Skip to content

Commit 842b42b

Browse files
Disco Tie added
1 parent 36220d6 commit 842b42b

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed

Disco_Tie/code.py

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
"""
2+
LED Disco Tie with Bluetooth
3+
=========================================================
4+
Give your suit an sound-reactive upgrade with Circuit
5+
Playground Bluefruit & Neopixels. Set color and animation
6+
mode using the Bluefruit LE Connect app.
7+
8+
Author: Collin Cunningham for Adafruit Industries, 2019
9+
"""
10+
11+
import time
12+
import array
13+
import math
14+
import audiobusio
15+
import board
16+
import neopixel
17+
from adafruit_ble.uart_server import UARTServer
18+
from adafruit_bluefruit_connect.packet import Packet
19+
from adafruit_bluefruit_connect.color_packet import ColorPacket
20+
from adafruit_bluefruit_connect.button_packet import ButtonPacket
21+
22+
uart_server = UARTServer()
23+
24+
# User input vars
25+
mode = 0 # 0=audio, 1=rainbow, 2=larsen_scanner, 3=solid
26+
user_color= (127,0,0)
27+
speed = 5.0 # for larsen scanner
28+
29+
# Audio meter vars
30+
PEAK_COLOR = (100, 0, 255)
31+
NUM_PIXELS = 10
32+
CURVE = 2
33+
SCALE_EXPONENT = math.pow(10, CURVE * -0.1)
34+
NUM_SAMPLES = 160
35+
36+
# Restrict value to be between floor and ceiling.
37+
def constrain(value, floor, ceiling):
38+
return max(floor, min(value, ceiling))
39+
40+
# Scale input_value between output_min and output_max, exponentially.
41+
def log_scale(input_value, input_min, input_max, output_min, output_max):
42+
normalized_input_value = (input_value - input_min) / \
43+
(input_max - input_min)
44+
return output_min + \
45+
math.pow(normalized_input_value, SCALE_EXPONENT) \
46+
* (output_max - output_min)
47+
48+
# Remove DC bias before computing RMS.
49+
def normalized_rms(values):
50+
minbuf = int(mean(values))
51+
samples_sum = sum(
52+
float(sample - minbuf) * (sample - minbuf)
53+
for sample in values
54+
)
55+
56+
return math.sqrt(samples_sum / len(values))
57+
58+
def mean(values):
59+
return sum(values) / len(values)
60+
61+
def volume_color(volume):
62+
return 200, volume * (255 // NUM_PIXELS), 0
63+
64+
# Set up NeoPixels and turn them all off.
65+
pixels = neopixel.NeoPixel(board.A1, NUM_PIXELS, brightness=0.1, auto_write=False)
66+
pixels.fill(0)
67+
pixels.show()
68+
69+
mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA,
70+
sample_rate=16000, bit_depth=16)
71+
72+
# Record an initial sample to calibrate. Assume it's quiet when we start.
73+
samples = array.array('H', [0] * NUM_SAMPLES)
74+
mic.record(samples, len(samples))
75+
# Set lowest level to expect, plus a little.
76+
input_floor = normalized_rms(samples) + 10
77+
# Corresponds to sensitivity: lower means more pixels light up with lower sound
78+
input_ceiling = input_floor + 500
79+
peak = 0
80+
81+
def wheel(pos):
82+
# Input a value 0 to 255 to get a color value.
83+
# The colours are a transition r - g - b - back to r.
84+
if pos < 0 or pos > 255:
85+
r = g = b = 0
86+
elif pos < 85:
87+
r = int(pos * 3)
88+
g = int(255 - pos*3)
89+
b = 0
90+
elif pos < 170:
91+
pos -= 85
92+
r = int(255 - pos*3)
93+
g = 0
94+
b = int(pos*3)
95+
else:
96+
pos -= 170
97+
r = 0
98+
g = int(pos*3)
99+
b = int(255 - pos*3)
100+
return (r, g, b)
101+
102+
def rainbow_cycle(wait):
103+
for j in range(255):
104+
for i in range(NUM_PIXELS):
105+
pixel_index = (i * 256 // NUM_PIXELS) + j
106+
pixels[i] = wheel(pixel_index & 255)
107+
pixels.show()
108+
time.sleep(wait)
109+
110+
def audio_meter():
111+
mic.record(samples, len(samples))
112+
magnitude = normalized_rms(samples)
113+
global peak
114+
115+
# Compute scaled logarithmic reading in the range 0 to NUM_PIXELS
116+
c = log_scale(constrain(magnitude, input_floor, input_ceiling),
117+
input_floor, input_ceiling, 0, NUM_PIXELS)
118+
119+
# Light up pixels that are below the scaled and interpolated magnitude.
120+
pixels.fill(0)
121+
for i in range(NUM_PIXELS):
122+
if i < c:
123+
pixels[i] = volume_color(i)
124+
# Light up the peak pixel and animate it slowly dropping.
125+
if c >= peak:
126+
peak = min(c, NUM_PIXELS - 1)
127+
elif peak > 0:
128+
peak = peak - 1
129+
if peak > 0:
130+
pixels[int(peak)] = PEAK_COLOR
131+
pixels.show()
132+
133+
pos = 0 # position
134+
direction = 1 # direction of "eye"
135+
136+
def larsen_set(index, color):
137+
if index < 0:
138+
return
139+
else:
140+
pixels[index] = color
141+
142+
def larsen(wait):
143+
global pos
144+
global direction
145+
146+
color_dark = (int(user_color[0]/8), int(user_color[1]/8),
147+
int(user_color[2]/8))
148+
color_med = (int(user_color[0]/2), int(user_color[1]/2),
149+
int(user_color[2]/2))
150+
151+
larsen_set(pos - 2, color_dark)
152+
larsen_set(pos - 1, color_med)
153+
larsen_set(pos, user_color)
154+
larsen_set(pos + 1, color_med)
155+
156+
if (pos + 2) < NUM_PIXELS:
157+
# Dark red, do not exceed number of pixels
158+
larsen_set(pos + 2, color_dark)
159+
160+
pixels.write()
161+
time.sleep(wait)
162+
163+
# Erase all and draw a new one next time
164+
for j in range(-2, 2):
165+
larsen_set(pos + j, (0, 0, 0))
166+
if (pos + 2) < NUM_PIXELS:
167+
larsen_set(pos + 2, (0, 0, 0))
168+
169+
# Bounce off ends of strip
170+
pos += direction
171+
if pos < 0:
172+
pos = 1
173+
direction = -direction
174+
elif pos >= (NUM_PIXELS - 1):
175+
pos = NUM_PIXELS - 2
176+
direction = -direction
177+
178+
def solid():
179+
global user_color
180+
pixels.fill(user_color)
181+
pixels.show()
182+
183+
def map_value(value, in_min, in_max, out_min, out_max):
184+
return out_min + (out_max - out_min) * ((value - in_min)
185+
/ (in_max - in_min))
186+
187+
def change_speed(val):
188+
global speed
189+
new_speed = speed + val
190+
if new_speed > 10.0:
191+
new_speed = 10.0
192+
elif new_speed < 1.0:
193+
new_speed = 1.0
194+
speed = new_speed
195+
print("set speed " + str(speed))
196+
197+
while True:
198+
# While BLE is *not* connected
199+
if not uart_server.connected:
200+
# OK to call again even if already advertising
201+
uart_server.start_advertising()
202+
203+
# While BLE is connected
204+
else:
205+
if uart_server.in_waiting:
206+
packet = Packet.from_stream(uart_server)
207+
208+
# Received ColorPacket
209+
if isinstance(packet, ColorPacket):
210+
print("color received: " + str(packet.color))
211+
user_color = packet.color
212+
213+
# Received ButtonPacket
214+
elif isinstance(packet, ButtonPacket):
215+
if packet.pressed:
216+
if packet.button == ButtonPacket.UP:
217+
change_speed(1)
218+
elif packet.button == ButtonPacket.DOWN:
219+
change_speed(-1)
220+
elif packet.button == ButtonPacket.BUTTON_1:
221+
mode = 0
222+
elif packet.button == ButtonPacket.BUTTON_2:
223+
mode = 1
224+
elif packet.button == ButtonPacket.BUTTON_3:
225+
mode = 2
226+
elif packet.button == ButtonPacket.BUTTON_4:
227+
mode = 3
228+
229+
# Determine animation based on mode
230+
if mode == 0:
231+
audio_meter()
232+
elif mode == 1:
233+
rainbow_cycle(0.001)
234+
elif mode == 2:
235+
larsen(map_value(speed, 10.0, 0.0, 0.01, 0.3))
236+
elif mode == 3:
237+
solid()

0 commit comments

Comments
 (0)