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

beat.py 7.0KB

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