Browse Source

Add beat.py, wrote this a long time ago

Tyler Hallada 8 years ago
parent
commit
393b92529d
1 changed files with 206 additions and 0 deletions
  1. 206 0
      beat.py

+ 206 - 0
beat.py

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