The BLM badge comes with six side-lit RGB NeoPixel LEDs and a sound sensor (microphone). This CircuitPython example combines those two features to have fun with sound reactive LED colors. All the hardware needed for this example is built-in to the BLM Badge, so no soldering or external components are necessary!

This section will walk you through loading the necessary CircuitPython libraries, then provide and explain a code example for sound reactive LEDs. You should have already installed CircuitPython on your board and have a CIRCUITPY drive available on your computer.

CircuitPython Library Installation

For this example, you'll need to copy two libraries to your BLM badge: NeoPixel and Adafruit Pypixelbuf.

First, download the Adafruit CircuitPython Library Bundle from circuitpython.org. Open the resulting file, and then open the lib folder contained within.

Find the following files:

  • adafruit_pypixelbuf.mpy
  • neopixel.mpy

Copy the two files listed above to the lib folder on your CIRCUITPY drive.

Before you continue, make sure that the lib folder on your CIRCUITPY drive contains the adafruit_pypixelbuf.mpy and neopixel.mpy files.

Sound Reactive LED Example

Save the following as code.py on your CIRCUITPY drive.

import array
import math
import board
import audiobusio
import neopixel
from adafruit_pypixelbuf import colorwheel

# Increase this number to use this example in louder environments. As you increase the number, it
# increases the level of sound needed to change the color of the LEDs. 5 is good for quiet up to
# workshop-level settings. If you plan to be in a louder setting, increase this number to maintain
# the same behavior as in a quieter setting.
magnitude_color_modifier = 5

pixels = neopixel.NeoPixel(board.NEOPIXEL, 6, auto_write=False)
mic = audiobusio.PDMIn(
    board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000, bit_depth=16
)


def normalized_rms(values):
    """Normalized Root Mean Square. Removes DC bias before computing RMS."""
    mean_values = int(sum(values) / len(values))
    return math.sqrt(
        sum(float(sample - mean_values) * (sample - mean_values) for sample in values)
        / len(values)
    )


audio_samples = []  # Create an empty list for sample values
while True:
    sample_array = array.array("H", [0] * 32)
    mic.record(sample_array, len(sample_array))
    normalized_samples = normalized_rms(sample_array)  # Calculate normalized sample value
    audio_samples.append(normalized_samples)  # Add normalized values to the audio samples list
    audio_samples = audio_samples[-10:]  # Keep only the 10 most recent values in samples list
    magnitude = sum(audio_samples) / len(audio_samples)  # The average of the last 10 audio samples
    print(magnitude)
    # Fill NeoPixels with color based on scaled magnitude
    pixels.fill(colorwheel(min(255, (magnitude / magnitude_color_modifier))))
    pixels.show()

Make a loud noise to see the LED color react!

First you import the necessary libraries and modules. Note that there are modules being imported that you did not need to copy to the lib folder in the previous step. That's because those modules are built-in to CircuitPython.

Then there is the magnitude_color_modifier variable. This is used to scale the sound magnitude value down to a color value. The default is 5 which is a good value for quiet settings like around the house or at your desk. If you intend to use this example in a louder setting such as a workshop or public event, you should consider increasing this number. Basically, if you see that the colors of the LEDs are not changing across the full rainbow, it may be due to an increased volume. You can increase this number to compensate.

Next is the hardware setup and the normalized_rms helper. This example uses the NeoPixels and the sound sensor, so you initialise these towards the beginning of your code. The helper function is used to provide a type of averaged sample sound level rather than focusing on very rapid peaks.

Before the loop, you create an empty list to hold audio sample values. This will be used in the loop.

You begin the loop by creating an array to hold 32 raw samples. Then, you use the mic to record raw signal values into the array. Next, you use the normalized_rms helper to normalize the values in the array.

You take the normalized sample values and add them to the audio_samples list. You'll use this list to create a rolling average of the normalized values. Since you are calculating a rolling average, you keep only the 10 most recent values in the audio_samples list at any given point in time. Then, you calculate the desired magnitude by averaging the values. This is done by adding up the 10 values (sum(audio_samples)) and dividing them by 10 (len(audio_samples)). This section of code is required because otherwise the values can be very jumpy which causes the LED color to flicker. So, we calculate a rolling average to smooth out the value and color changes.

Then, you print the magnitude to the serial console. Connect to the serial console to see the output if you desire. You can watch the values change as you make noise near the board.

Finally, you fill the pixels with color based on the magnitude where it ranges from 0 = red, up to green, up to blue, and back to red, with every color in between.

That's what goes into coding sound reactive NeoPixel LEDs on the BLM badge using CircuitPython!

This guide was first published on Mar 24, 2021. It was last updated on 2021-03-24 12:20:00 -0400.

This page (Sound Reactive NeoPixels) was last updated on Mar 24, 2021.

Text editor powered by tinymce.