Once you've finished setting up your QT Py RP2040 with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries # SPDX-License-Identifier: MIT import time import array import math import board import audiobusio import simpleio import neopixel # neopixel setup pixel_pin = board.A0 pixel_num = 16 pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.1, auto_write=False) # function to average mic levels def mean(values): return sum(values) / len(values) # function to return mic level 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)) # mic setup mic = audiobusio.PDMIn(board.TX, board.A1, sample_rate=16000, bit_depth=16) samples = array.array('H', [0] * 160) # variable to hold previous mic level last_input = 0 # neopixel colors GREEN = (0, 127, 0) RED = (127, 0, 0) YELLOW = (127, 127, 0) OFF = (0, 0, 0) # array of colors for VU meter colors = [GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, YELLOW, YELLOW, YELLOW, YELLOW, YELLOW, RED, RED] # begin with pixels off pixels.fill(OFF) pixels.show() while True: # take in audio mic.record(samples, len(samples)) # magnitude holds the value of the mic level magnitude = normalized_rms(samples) # uncomment to see the levels in the REPL # print((magnitude,)) # mapping the volume range (125-500) to the 16 neopixels # volume range is optimized for music. you may want to adjust the range # based on the type of audio that you're trying to show mapped_value = simpleio.map_range(magnitude, 125, 500, 0, 16) # converting the mapped range to an integer input_val = int(mapped_value) # if the mic input has changed from the last input value... if last_input != input_val: for i in range(input_val): # if the level is lower... if last_input > input_val: for z in range(last_input): # turn those pixels off pixels[z] = (OFF) # turn on pixels using the colors array pixels[i] = (colors[i]) pixels.show() # update last_input last_input = input_val time.sleep(0.01)
After downloading the Project Bundle, plug your QT Py RP2040 into the computer's USB port. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the QT Py RP2040's CIRCUITPY drive.
- lib folder
- code.py
Your QT Py RP2040 CIRCUITPY drive should look like this after copying the lib folder and the code.py file.
First, the strip of 16 NeoPixels are setup.
# neopixel setup pixel_pin = board.A0 pixel_num = 16 pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.1, auto_write=False)
Then the PDM mic is setup, along with two functions that assist with returning usable data from the PDM mic.
# function to average mic levels def mean(values): return sum(values) / len(values) # function to return mic level 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)) # mic setup mic = audiobusio.PDMIn(board.TX, board.A1, sample_rate=16000, bit_depth=16) samples = array.array('H', [0] * 160)
Four colors for the NeoPixels are setup. The colors
array holds the colors in the order that they will appear on the VU meter.
# neopixel colors GREEN = (0, 127, 0) RED = (127, 0, 0) YELLOW = (127, 127, 0) OFF = (0, 0, 0) # array of colors for VU meter colors = [GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, YELLOW, YELLOW, YELLOW, YELLOW, YELLOW, RED, RED] # begin with pixels off pixels.fill(OFF) pixels.show()
In the loop, data is received by the PDM mic and is held in the variable magnitude
. magnitude
is scaled to fit into the range of 0
to 16
using map_range()
. This allows the 16 NeoPixels to represent the range of data coming from the PDM mic.
# take in audio mic.record(samples, len(samples)) # magnitude holds the value of the mic level magnitude = normalized_rms(samples) # uncomment to see the levels in the REPL # print((magnitude,)) # mapping the volume range (125-500) to the 16 neopixels # volume range is optimized for music. you may want to adjust the range # based on the type of audio that you're trying to show mapped_value = simpleio.map_range(magnitude, 125, 500, 0, 16) # converting the mapped range to an integer input_val = int(mapped_value)
The NeoPixels are updated if the data from the PDM mic changes. This is checked by comparing last_input
and input_val
.
A for
statement is used to iterate through the range of the current input data from the PDM mic. The index, represented with i
, is used to turn on the correct number of pixels and the defined colors from the colors
array.
# if the mic input has changed from the last input value... if last_input != input_val: for i in range(input_val): # if the level is lower... if last_input > input_val: for z in range(last_input): # turn those pixels off pixels[z] = (OFF) # turn on pixels using the colors array pixels[i] = (colors[i]) pixels.show() # update last_input last_input = input_val time.sleep(0.01)
Page last edited January 22, 2025
Text editor powered by tinymce.