This code uses the Adafruit Bluefruit app's Control Pad and Color Picker features. The Color Picker will send a solid color to the mobile, and the Control Pad will allow you to cycle between the modes or set up quick-select buttons for your favorite modes. You can also control the brightness with the up and down arrow buttons.
To use with CircuitPython, you need to first install a few libraries, into the lib folder on your CIRCUITPY drive. Then you need to update code.py with the example script.
Thankfully, we can do this in one go. In the example below, click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, open the directory Sunflower_Mobile/ and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your CIRCUITPY drive.
Your CIRCUITPY drive should now look similar to the following image:
# SPDX-FileCopyrightText: 2021 Erin St Blaine for Adafruit Industries # SPDX-FileCopyrightText: 2021 Dan Halbert for Adafruit Industries # # SPDX-License-Identifier: MIT """ LED Sunflower Mobile with Circuit Playground Bluefruit Full tutorial: https://learn.adafruit.com/sound-reactive-sunflower-baby-crib-mobile-with-bluetooth-control Adafruit invests time and resources providing this open source code. Please support Adafruit and open source hardware by purchasing products from Adafruit! Written by Erin St Blaine & Dan Halbert for Adafruit Industries Copyright (c) 2020-2021 Adafruit Industries Licensed under the MIT license. All text above must be included in any redistribution. """ import array import math import audiobusio import board import neopixel from digitalio import DigitalInOut, Direction, Pull from adafruit_bluefruit_connect.packet import Packet from adafruit_bluefruit_connect.button_packet import ButtonPacket from adafruit_bluefruit_connect.color_packet import ColorPacket from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService from adafruit_led_animation.helper import PixelMap from adafruit_led_animation.sequence import AnimationSequence from adafruit_led_animation.group import AnimationGroup from adafruit_led_animation.animation.sparkle import Sparkle from adafruit_led_animation.animation.rainbow import Rainbow from adafruit_led_animation.animation.rainbowchase import RainbowChase from adafruit_led_animation.animation.rainbowcomet import RainbowComet from adafruit_led_animation.animation.chase import Chase from adafruit_led_animation.animation.comet import Comet from adafruit_led_animation.animation.solid import Solid from adafruit_led_animation.color import colorwheel from adafruit_led_animation.color import ( BLACK, RED, ORANGE, BLUE, PURPLE, WHITE, ) YELLOW = (25, 15, 0) # Setup BLE ble = BLERadio() uart = UARTService() advertisement = ProvideServicesAdvertisement(uart) # Color of the peak pixel. PEAK_COLOR = (100, 0, 255) # Number of total pixels - 10 build into Circuit Playground NUM_PIXELS = 30 fairylights = DigitalInOut(board.A4) fairylights.direction = Direction.OUTPUT fairylights.value = True # Exponential scaling factor. # Should probably be in range -10 .. 10 to be reasonable. CURVE = 2 SCALE_EXPONENT = math.pow(10, CURVE * -0.1) # Number of samples to read at once. NUM_SAMPLES = 160 brightness_increment = 0 # Restrict value to be between floor and ceiling. def constrain(value, floor, ceiling): return max(floor, min(value, ceiling)) # Scale input_value between output_min and output_max, exponentially. def log_scale(input_value, input_min, input_max, output_min, output_max): normalized_input_value = (input_value - input_min) / \ (input_max - input_min) return output_min + \ math.pow(normalized_input_value, SCALE_EXPONENT) \ * (output_max - output_min) # Remove DC bias before computing RMS. def normalized_rms(values): minbuf = int(mean(values)) samples_sum = sum( float(sample - minbuf) * (sample - minbuf) for sample in values ) return math.sqrt(samples_sum / len(values)) def mean(values): return sum(values) / len(values) def volume_color(volume): return 200, volume * (255 // NUM_PIXELS), 0 # Main program # Set up NeoPixels and turn them all off. pixels = neopixel.NeoPixel(board.A1, NUM_PIXELS, brightness=0.1, auto_write=False) pixels.fill(0) pixels.show() mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000, bit_depth=16) # Record an initial sample to calibrate. Assume it's quiet when we start. samples = array.array('H', [0] * NUM_SAMPLES) mic.record(samples, len(samples)) # Set lowest level to expect, plus a little. input_floor = normalized_rms(samples) + 30 # OR: used a fixed floor # input_floor = 50 # You might want to print the input_floor to help adjust other values. print(input_floor) # Corresponds to sensitivity: lower means more pixels light up with lower sound # Adjust this as you see fit. input_ceiling = input_floor + 100 peak = 0 # Cusomize LED Animations ------------------------------------------------------ rainbow = Rainbow(pixels, speed=0, period=6, name="rainbow", step=2.4) rainbow_chase = RainbowChase(pixels, speed=0.1, size=5, spacing=5, step=5) chase = Chase(pixels, speed=0.2, color=ORANGE, size=2, spacing=6) rainbow_comet = RainbowComet(pixels, speed=0.1, tail_length=30, bounce=True) rainbow_comet2 = RainbowComet( pixels, speed=0.1, tail_length=104, colorwheel_offset=80, bounce=True ) rainbow_comet3 = RainbowComet( pixels, speed=0, tail_length=25, colorwheel_offset=80, step=4, bounce=False ) strum = RainbowComet( pixels, speed=0.1, tail_length=25, bounce=False, colorwheel_offset=50, step=4 ) sparkle = Sparkle(pixels, speed=0.1, color=BLUE, num_sparkles=10) sparkle2 = Sparkle(pixels, speed=0.5, color=PURPLE, num_sparkles=4) off = Solid(pixels, color=BLACK) # Animations Playlist - reorder as desired. AnimationGroups play at the same time animations = AnimationSequence( rainbow_comet2, # rainbow_comet, # chase, # rainbow_chase, # rainbow, # AnimationGroup( sparkle, strum, ), AnimationGroup( sparkle2, rainbow_comet3, ), off, auto_clear=True, auto_reset=True, ) MODE = 1 LASTMODE = 1 # start up in sound reactive mode i = 0 # Are we already advertising? advertising = False while True: animations.animate() if not ble.connected and not advertising: ble.start_advertising(advertisement) advertising = True # Are we connected via Bluetooth now? if ble.connected: # Once we're connected, we're not advertising any more. advertising = False # Have we started to receive a packet? if uart.in_waiting: packet = Packet.from_stream(uart) if isinstance(packet, ColorPacket): # Set all the pixels to one color and stay there. pixels.fill(packet.color) pixels.show() MODE = 2 elif isinstance(packet, ButtonPacket): if packet.pressed: if packet.button == ButtonPacket.BUTTON_1: animations.activate(1) elif packet.button == ButtonPacket.BUTTON_2: MODE = 1 animations.activate(2) elif packet.button == ButtonPacket.BUTTON_3: MODE = 1 animations.activate(3) elif packet.button == ButtonPacket.BUTTON_4: MODE = 4 elif packet.button == ButtonPacket.UP: pixels.brightness = pixels.brightness + 0.1 pixels.show() if pixels.brightness > 1: pixels.brightness = 1 elif packet.button == ButtonPacket.DOWN: pixels.brightness = pixels.brightness - 0.1 pixels.show() if pixels.brightness < 0.1: pixels.brightness = 0.1 elif packet.button == ButtonPacket.RIGHT: MODE = 1 animations.next() elif packet.button == ButtonPacket.LEFT: animations.activate(7) animations.animate() if MODE == 2: animations.freeze() if MODE == 4: animations.freeze() pixels.fill(YELLOW) mic.record(samples, len(samples)) magnitude = normalized_rms(samples) # You might want to print this to see the values. #print(magnitude) # Compute scaled logarithmic reading in the range 0 to NUM_PIXELS c = log_scale(constrain(magnitude, input_floor, input_ceiling), input_floor, input_ceiling, 0, NUM_PIXELS) # Light up pixels that are below the scaled and interpolated magnitude. #pixels.fill(0) for i in range(NUM_PIXELS): if i < c: pixels[i] = volume_color(i) # Light up the peak pixel and animate it slowly dropping. if c >= peak: peak = min(c, NUM_PIXELS - 1) elif peak > 0: peak = peak - 0.01 if peak > 0: pixels[int(peak)] = PEAK_COLOR pixels.show()
Text editor powered by tinymce.