Skip to content

Commit 7ac3475

Browse files
authored
Merge pull request #1522 from firepixie/master
first commit
2 parents 4a66208 + 290b9e3 commit 7ac3475

File tree

1 file changed

+257
-0
lines changed

1 file changed

+257
-0
lines changed

Sunflower_Mobile/code.py

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
"""
2+
LED Sunflower Mobile with Circuit Playground Bluefruit
3+
Full tutorial:
4+
https://learn.adafruit.com/sound-reactive-sunflower-baby-crib-mobile-with-bluetooth-control
5+
Adafruit invests time and resources providing this open source code.
6+
Please support Adafruit and open source hardware by purchasing
7+
products from Adafruit!
8+
Written by Erin St Blaine & Dan Halbert for Adafruit Industries
9+
Copyright (c) 2020-2021 Adafruit Industries
10+
Licensed under the MIT license.
11+
All text above must be included in any redistribution.
12+
13+
"""
14+
15+
import array
16+
import math
17+
import audiobusio
18+
import board
19+
import neopixel
20+
from digitalio import DigitalInOut, Direction, Pull
21+
22+
from adafruit_bluefruit_connect.packet import Packet
23+
from adafruit_bluefruit_connect.button_packet import ButtonPacket
24+
from adafruit_bluefruit_connect.color_packet import ColorPacket
25+
from adafruit_ble import BLERadio
26+
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
27+
from adafruit_ble.services.nordic import UARTService
28+
29+
from adafruit_led_animation.helper import PixelMap
30+
from adafruit_led_animation.sequence import AnimationSequence
31+
from adafruit_led_animation.group import AnimationGroup
32+
from adafruit_led_animation.animation.sparkle import Sparkle
33+
from adafruit_led_animation.animation.rainbow import Rainbow
34+
from adafruit_led_animation.animation.rainbowchase import RainbowChase
35+
from adafruit_led_animation.animation.rainbowcomet import RainbowComet
36+
from adafruit_led_animation.animation.chase import Chase
37+
from adafruit_led_animation.animation.comet import Comet
38+
from adafruit_led_animation.animation.solid import Solid
39+
from adafruit_led_animation.color import colorwheel
40+
from adafruit_led_animation.color import (
41+
BLACK,
42+
RED,
43+
ORANGE,
44+
BLUE,
45+
PURPLE,
46+
WHITE,
47+
)
48+
49+
YELLOW = (25, 15, 0)
50+
51+
# Setup BLE
52+
ble = BLERadio()
53+
uart = UARTService()
54+
advertisement = ProvideServicesAdvertisement(uart)
55+
56+
# Color of the peak pixel.
57+
PEAK_COLOR = (100, 0, 255)
58+
# Number of total pixels - 10 build into Circuit Playground
59+
NUM_PIXELS = 30
60+
61+
62+
fairylights = DigitalInOut(board.A4)
63+
fairylights.direction = Direction.OUTPUT
64+
fairylights.value = True
65+
66+
# Exponential scaling factor.
67+
# Should probably be in range -10 .. 10 to be reasonable.
68+
CURVE = 2
69+
SCALE_EXPONENT = math.pow(10, CURVE * -0.1)
70+
71+
# Number of samples to read at once.
72+
NUM_SAMPLES = 160
73+
74+
brightness_increment = 0
75+
76+
# Restrict value to be between floor and ceiling.
77+
def constrain(value, floor, ceiling):
78+
return max(floor, min(value, ceiling))
79+
80+
81+
# Scale input_value between output_min and output_max, exponentially.
82+
def log_scale(input_value, input_min, input_max, output_min, output_max):
83+
normalized_input_value = (input_value - input_min) / \
84+
(input_max - input_min)
85+
return output_min + \
86+
math.pow(normalized_input_value, SCALE_EXPONENT) \
87+
* (output_max - output_min)
88+
89+
90+
# Remove DC bias before computing RMS.
91+
def normalized_rms(values):
92+
minbuf = int(mean(values))
93+
samples_sum = sum(
94+
float(sample - minbuf) * (sample - minbuf)
95+
for sample in values
96+
)
97+
98+
return math.sqrt(samples_sum / len(values))
99+
100+
101+
def mean(values):
102+
return sum(values) / len(values)
103+
104+
105+
def volume_color(volume):
106+
return 200, volume * (255 // NUM_PIXELS), 0
107+
108+
109+
# Main program
110+
111+
# Set up NeoPixels and turn them all off.
112+
pixels = neopixel.NeoPixel(board.A1, NUM_PIXELS, brightness=0.1, auto_write=False)
113+
pixels.fill(0)
114+
pixels.show()
115+
116+
mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA,
117+
sample_rate=16000, bit_depth=16)
118+
119+
# Record an initial sample to calibrate. Assume it's quiet when we start.
120+
samples = array.array('H', [0] * NUM_SAMPLES)
121+
mic.record(samples, len(samples))
122+
# Set lowest level to expect, plus a little.
123+
input_floor = normalized_rms(samples) + 30
124+
# OR: used a fixed floor
125+
# input_floor = 50
126+
127+
# You might want to print the input_floor to help adjust other values.
128+
print(input_floor)
129+
130+
# Corresponds to sensitivity: lower means more pixels light up with lower sound
131+
# Adjust this as you see fit.
132+
input_ceiling = input_floor + 100
133+
134+
peak = 0
135+
136+
# Cusomize LED Animations ------------------------------------------------------
137+
rainbow = Rainbow(pixels, speed=0, period=6, name="rainbow", step=2.4)
138+
rainbow_chase = RainbowChase(pixels, speed=0.1, size=5, spacing=5, step=5)
139+
chase = Chase(pixels, speed=0.2, color=ORANGE, size=2, spacing=6)
140+
rainbow_comet = RainbowComet(pixels, speed=0.1, tail_length=30, bounce=True)
141+
rainbow_comet2 = RainbowComet(
142+
pixels, speed=0.1, tail_length=104, colorwheel_offset=80, bounce=True
143+
)
144+
rainbow_comet3 = RainbowComet(
145+
pixels, speed=0, tail_length=25, colorwheel_offset=80, step=4, bounce=False
146+
)
147+
strum = RainbowComet(
148+
pixels, speed=0.1, tail_length=25, bounce=False, colorwheel_offset=50, step=4
149+
)
150+
sparkle = Sparkle(pixels, speed=0.1, color=BLUE, num_sparkles=10)
151+
sparkle2 = Sparkle(pixels, speed=0.5, color=PURPLE, num_sparkles=4)
152+
off = Solid(pixels, color=BLACK)
153+
154+
# Animations Playlist - reorder as desired. AnimationGroups play at the same time
155+
animations = AnimationSequence(
156+
157+
rainbow_comet2, #
158+
rainbow_comet, #
159+
chase, #
160+
rainbow_chase, #
161+
rainbow, #
162+
163+
AnimationGroup(
164+
sparkle,
165+
strum,
166+
),
167+
AnimationGroup(
168+
sparkle2,
169+
rainbow_comet3,
170+
),
171+
off,
172+
auto_clear=True,
173+
auto_reset=True,
174+
)
175+
176+
177+
MODE = 1
178+
LASTMODE = 1 # start up in sound reactive mode
179+
i = 0
180+
# Are we already advertising?
181+
advertising = False
182+
183+
while True:
184+
animations.animate()
185+
if not ble.connected and not advertising:
186+
ble.start_advertising(advertisement)
187+
advertising = True
188+
189+
# Are we connected via Bluetooth now?
190+
if ble.connected:
191+
# Once we're connected, we're not advertising any more.
192+
advertising = False
193+
# Have we started to receive a packet?
194+
if uart.in_waiting:
195+
packet = Packet.from_stream(uart)
196+
if isinstance(packet, ColorPacket):
197+
# Set all the pixels to one color and stay there.
198+
pixels.fill(packet.color)
199+
pixels.show()
200+
MODE = 2
201+
elif isinstance(packet, ButtonPacket):
202+
if packet.pressed:
203+
if packet.button == ButtonPacket.BUTTON_1:
204+
animations.activate(1)
205+
elif packet.button == ButtonPacket.BUTTON_2:
206+
MODE = 1
207+
animations.activate(2)
208+
elif packet.button == ButtonPacket.BUTTON_3:
209+
MODE = 1
210+
animations.activate(3)
211+
elif packet.button == ButtonPacket.BUTTON_4:
212+
MODE = 4
213+
214+
elif packet.button == ButtonPacket.UP:
215+
pixels.brightness = pixels.brightness + 0.1
216+
pixels.show()
217+
if pixels.brightness > 1:
218+
pixels.brightness = 1
219+
elif packet.button == ButtonPacket.DOWN:
220+
pixels.brightness = pixels.brightness - 0.1
221+
pixels.show()
222+
if pixels.brightness < 0.1:
223+
pixels.brightness = 0.1
224+
elif packet.button == ButtonPacket.RIGHT:
225+
MODE = 1
226+
animations.next()
227+
elif packet.button == ButtonPacket.LEFT:
228+
animations.activate(7)
229+
animations.animate()
230+
231+
if MODE == 2:
232+
animations.freeze()
233+
if MODE == 4:
234+
animations.freeze()
235+
pixels.fill(YELLOW)
236+
mic.record(samples, len(samples))
237+
magnitude = normalized_rms(samples)
238+
# You might want to print this to see the values.
239+
#print(magnitude)
240+
241+
# Compute scaled logarithmic reading in the range 0 to NUM_PIXELS
242+
c = log_scale(constrain(magnitude, input_floor, input_ceiling),
243+
input_floor, input_ceiling, 0, NUM_PIXELS)
244+
245+
# Light up pixels that are below the scaled and interpolated magnitude.
246+
#pixels.fill(0)
247+
for i in range(NUM_PIXELS):
248+
if i < c:
249+
pixels[i] = volume_color(i)
250+
# Light up the peak pixel and animate it slowly dropping.
251+
if c >= peak:
252+
peak = min(c, NUM_PIXELS - 1)
253+
elif peak > 0:
254+
peak = peak - 0.01
255+
if peak > 0:
256+
pixels[int(peak)] = PEAK_COLOR
257+
pixels.show()

0 commit comments

Comments
 (0)