Scripts to control a strip of LPD8806 LEDs from a raspberry pi's GPIO

glow.py 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. #!/usr/bin/python
  2. # Testing the LED lights
  3. import random
  4. import time
  5. import argparse
  6. import struct
  7. # Set up command-line operations
  8. parser = argparse.ArgumentParser(description="Make the LPD8806 LEDs glow random colors")
  9. parser.add_argument('-s', '--step', type=int, default=1,
  10. help='Amount in RGB to increase/decrease at every interval. Lower value '
  11. 'will be slower and smoother, higher will be faster but jumpier.')
  12. parser.add_argument('-i', '--interval', type=float, default=0.1,
  13. help='Time in seconds to wait between every interval (step). Lower value '
  14. 'will be slower and more jagged and jumpy, higher will be faster and smoother.')
  15. parser.add_argument('-g', '--glow_pause', type=float, default=2,
  16. help='Time in seconds to wait at maximum glow')
  17. parser.add_argument('-d', '--dim_pause', type=float, default=1,
  18. help='Time in seconds to wait while LEDs are off')
  19. parser.add_argument('-m', '--minimum', type=int, default=0,
  20. help='The minimum value to fade out to, the "dim" setting. (0 - 255)')
  21. parser.add_argument('-n', '--no_dim', action='store_true', default=False,
  22. help='Instead of fading out to a dim minimum, only pause at max glow'
  23. 'and then fade to the next random color.')
  24. parser.add_argument('-L', '--min_color', type=int, default=0,
  25. help='Minimum of the range to select color values from. A higher value will'
  26. 'make brighter colors. (valid range: 0-255)')
  27. parser.add_argument('-H', '--max_color', type=int, default=255,
  28. help='Maximum of the range to select color values from. A lower value will'
  29. 'make dimmer colors. (valid range: 0-255)')
  30. parser.add_argument('-v', '--verbose', action='store_true', default=False,
  31. help='Display debug printouts of the current and target colors at every '
  32. 'sub-interval')
  33. # Set the spi file
  34. dev = "/dev/spidev0.0"
  35. # Open SPI device
  36. spidev = file(dev, "wb")
  37. # Number of LEDs in my strip
  38. height = 32
  39. # Calculate gamma correction table. This includes
  40. # LPD8806-specific conversion (7-bit color w/high bit set).
  41. gamma = bytearray(256)
  42. for i in range(256):
  43. gamma[i] = 0x80 | int(pow(float(i) / 255.0, 2.5) * 127.0 + 0.5)
  44. # Create a bytearray to display
  45. # R, G, B byte per pixel, plus extra '0' byte at end for latch.
  46. step = parser.parse_args().step
  47. interval = parser.parse_args().interval
  48. glow_pause = parser.parse_args().glow_pause
  49. dim_pause = parser.parse_args().dim_pause
  50. minimum = parser.parse_args().minimum
  51. min_color = parser.parse_args().min_color
  52. max_color = parser.parse_args().max_color
  53. no_dim = parser.parse_args().no_dim
  54. verbose = parser.parse_args().verbose
  55. if verbose: print "Allocating..."
  56. array = bytearray(height * 3 + 1)
  57. def test_gamma(strip, gamma):
  58. # First a test:
  59. for i, color in enumerate(gamma):
  60. print str(i) + ": " + str(color) + " (" + repr(struct.pack('>B', color)) + ")"
  61. for y in range(height):
  62. value = [0]*3
  63. y3 = y * 3
  64. strip[y3] = color
  65. strip[y3 + 1] = color
  66. strip[y3 + 2] = color
  67. spidev.write(strip)
  68. spidev.flush()
  69. time.sleep(0.5)
  70. def rgb_to_gamma(color, gamma):
  71. """Translate normal RGB colors to the first occurrence in the gamma array
  72. The LPD8806 skips over a lot of RGB colors by using the same color at many
  73. different indices. This method just takes a gamma index: color, finds what
  74. color byte the gamma array contains at that index, and then searches the
  75. array from the beginning to find the first occurrence of that color byte
  76. and returns that gamma index instead. That way, all representable colors can
  77. be reduced to the indexes in gamma that start new colors, thereby making
  78. equivalencies between colors that are actually equal a lot easier.
  79. Arguments:
  80. color -- triple tuple representing RGB respectively for the desired color
  81. gamma -- bytearray of 256 corrected colors to choose from for the LPD8806 LEDs
  82. """
  83. gamma_r = gamma.index(struct.pack('>B', gamma[color[0]]))
  84. gamma_g = gamma.index(struct.pack('>B', gamma[color[1]]))
  85. gamma_b = gamma.index(struct.pack('>B', gamma[color[2]]))
  86. return gamma_r, gamma_g, gamma_b
  87. def get_current_color(strip, gamma):
  88. """Extract gamma-indexed RBG tuple from the first LED in the strip.
  89. The strip is ordered in GRB format, but this method returns RGB. Also note
  90. that this method only checks the color of the first LED, and does not take
  91. into account the rest of the strip's colors.
  92. Arguments:
  93. strip -- bytearray of the latest written colors to the LED strip. Array is
  94. three times the length of the strip plus one for '0' byte latch.
  95. gamma -- bytearray of 256 corrected colors to choose from for the LPD8806 LEDs
  96. """
  97. gamma_r = gamma.index(struct.pack('>B', strip[1]))
  98. gamma_g = gamma.index(struct.pack('>B', strip[0]))
  99. gamma_b = gamma.index(struct.pack('>B', strip[2]))
  100. return gamma_r, gamma_g, gamma_b
  101. def fade_to_color(color, strip, gamma, step=1, interval=0.1, pause=1):
  102. """Increment/Decrement LED colors to the target color and pause.
  103. Will convert the RGB color to the gamma-indexed version internally.
  104. Arguments:
  105. color -- triple tuple representing RGB respectively for the desired color
  106. strip -- bytearray of the latest written colors to the LED strip. Array is
  107. three times the length of the strip plus one for '0' byte latch.
  108. gamma -- bytearray of 256 corrected colors to choose from for the LPD8806 LEDs
  109. Keyword Arguments:
  110. step -- amount in RGB to increment/decrement at each interval (default 1)
  111. interval -- time in seconds between each increment/decrement (default 0.1)
  112. pause -- time in seconds to wait once fade is complete (default 1)
  113. """
  114. if verbose: print "color desired: " + str(color)
  115. # Convert color to a gamma-indexed value that this method will certainly reach
  116. color = rgb_to_gamma(color, gamma)
  117. if verbose: print "gamma-indexed color: " + str(color)
  118. strip_length = (len(strip) - 1) / 3
  119. current = get_current_color(strip, gamma)
  120. counter = 0
  121. while (current != color):
  122. # Near the end of the gamma spectrum, some colors are missing, so skip over
  123. # them if they are encountered.
  124. skip_nums = [234, 235, 242, 243, 248, 249, 252, 253]
  125. skip_g = 1
  126. skip_r = 1
  127. skip_b = 1
  128. if current[1] in skip_nums: skip_g = 2
  129. if current[0] in skip_nums: skip_r = 2
  130. if current[2] in skip_nums: skip_b = 2
  131. # Fill strip with next color
  132. for y in range(strip_length):
  133. y3 = y * 3
  134. strip[y3] = gamma[current[1]] + \
  135. (cmp(color[1], current[1]) * skip_g)
  136. strip[y3 + 1] = gamma[current[0]] + \
  137. (cmp(color[0], current[0]) * skip_r)
  138. strip[y3 + 2] = gamma[current[2]] + \
  139. (cmp(color[2], current[2]) * skip_b)
  140. # Increment counter. If at next step, then write to spi and wait
  141. counter = counter + 1
  142. if counter % step == 0:
  143. spidev.write(strip)
  144. spidev.flush()
  145. time.sleep(interval)
  146. # Update the current color for the while condition comparison
  147. current = get_current_color(strip, gamma)
  148. if verbose: print str(current) + " | " + str(color)
  149. # Now write to the spi port!
  150. if verbose: print "Displaying..."
  151. # Firstly, clear the strip and set array to values that are known
  152. for y in range(height):
  153. value = [0]*3
  154. y3 = y * 3
  155. array[y3] = gamma[value[1]]
  156. array[y3 + 1] = gamma[value[0]]
  157. array[y3 + 2] = gamma[value[2]]
  158. spidev.write(array)
  159. spidev.flush()
  160. # Now, glow
  161. while True:
  162. color = tuple([random.randint(min_color, max_color) for x in range(3)])
  163. # Wrap in try/except block for graceful exiting via KeyboardInterrupt
  164. try:
  165. fade_to_color(color, array, gamma, step=step,
  166. interval=interval, pause=glow_pause)
  167. time.sleep(glow_pause)
  168. if not no_dim:
  169. fade_to_color((minimum, minimum, minimum), array, gamma, step=step,
  170. interval=interval, pause=dim_pause)
  171. time.sleep(dim_pause)
  172. except KeyboardInterrupt:
  173. # Clear out array
  174. for y in range(height):
  175. value = [0]*3
  176. y3 = y * 3
  177. array[y3] = gamma[value[1]]
  178. array[y3 + 1] = gamma[value[0]]
  179. array[y3 + 2] = gamma[value[2]]
  180. spidev.write(array)
  181. spidev.flush()
  182. exit(0)