Once you've finished setting up your Feather nRF52840 Sense 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: 2021 Liz Clark for Adafruit Industries # # SPDX-License-Identifier: MIT """Adapted from the FFT Example: Waterfall Spectrum Analyzer by Jeff Epler https://learn.adafruit.com/ulab-crunch-numbers-fast-with-circuitpython/overview """ import array import board import audiobusio import busio from ulab import numpy as np try: from ulab.utils import spectrogram except ImportError: from ulab.scipy.signal import spectrogram import adafruit_is31fl3741 from adafruit_is31fl3741.adafruit_rgbmatrixqt import Adafruit_RGBMatrixQT # Manually declare I2c (not board.I2C()) to access 1 MHz speed for i2c = busio.I2C(board.SCL, board.SDA, frequency=1000000) # Declare is31 w/buffering preferred (low RAM will fall back on unbuffered) is31 = Adafruit_RGBMatrixQT(i2c, allocate=adafruit_is31fl3741.PREFER_BUFFER) # In buffered mode, MUST use show() to refresh matrix (see line 94) # brightness for the RGBMatrixQT # set to about 20% is31.set_led_scaling(0x19) is31.global_current = 0x03 is31.enable = True # fmt: off # array of colors for the LEDs # goes from purple to red # gradient generated using https://colordesigner.io/gradient-generator heatmap = [0xb000ff,0xa600ff,0x9b00ff,0x8f00ff,0x8200ff, 0x7400ff,0x6500ff,0x5200ff,0x3900ff,0x0003ff, 0x0003ff,0x0047ff,0x0066ff,0x007eff,0x0093ff, 0x00a6ff,0x00b7ff,0x00c8ff,0x00d7ff,0x00e5ff, 0x00e0ff,0x00e6fd,0x00ecf6,0x00f2ea,0x00f6d7, 0x00fac0,0x00fca3,0x00fe81,0x00ff59,0x00ff16, 0x00ff16,0x45ff08,0x62ff00,0x78ff00,0x8bff00, 0x9bff00,0xaaff00,0xb8ff00,0xc5ff00,0xd1ff00, 0xedff00,0xf5eb00,0xfcd600,0xffc100,0xffab00, 0xff9500,0xff7c00,0xff6100,0xff4100,0xff0000, 0xff0000,0xff0000] # fmt: on # size of the FFT data sample fft_size = 64 # setup for onboard mic mic = audiobusio.PDMIn( board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000, bit_depth=16 ) # use some extra sample to account for the mic startup samples_bit = array.array("H", [0] * (fft_size + 3)) # sends visualized data to the RGB matrix with colors def waves(data, y): offset = max(0, (13 - len(data)) // 2) for x in range(min(13, len(data))): is31.pixel(x + offset, y, heatmap[int(data[x])]) # main loop def main(): # value for audio samples max_all = 10 # variable to move data along the matrix scroll_offset = 0 # setting the y axis value to equal the scroll_offset y = scroll_offset while True: # record the audio sample mic.record(samples_bit, len(samples_bit)) # send the sample to the ulab array samples = np.array(samples_bit[3:]) # creates a spectogram of the data spectrogram1 = spectrogram(samples) # spectrum() is always nonnegative, but add a tiny value # to change any zeros to nonzero numbers spectrogram1 = np.log(spectrogram1 + 1e-7) spectrogram1 = spectrogram1[1 : (fft_size // 2) - 1] # sets range of the spectrogram min_curr = np.min(spectrogram1) max_curr = np.max(spectrogram1) # resets values if max_curr > max_all: max_all = max_curr else: max_curr = max_curr - 1 min_curr = max(min_curr, 3) # stores spectrogram in data data = (spectrogram1 - min_curr) * (51.0 / (max_all - min_curr)) # sets negative numbers to zero data = data * np.array((data > 0)) # resets y y = scroll_offset # runs waves to write data to the LED's waves(data, y) # updates scroll_offset to move data along matrix scroll_offset = (y + 1) % 9 # writes data to the RGB matrix is31.show() main()
After downloading the Project Bundle, plug your Feather nRF52840 Sense 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 Feather nRF52840 Sense's CIRCUITPY drive.
- lib folder
- code.py
Your Feather nRF52840 Sense CIRCUITPY drive should look like this after copying the lib folder and the code.py file.
The code is a port of the Waterfall Spectrum Analyzer CircuitPython code written by Jeff Epler for the ulab: Crunch Numbers Fast in CircuitPython Learn Guide. Be sure to check out that guide for more information on the ulab CircuitPython library.
The main portion of the code remains the same. Audio is sampled via the onboard PDM microphone. The sampled data is then visualized with color. The difference is that instead of displaying that visualization with a screen, as seen on the CLUE board, it is displayed with the LED's on the matrix.
# setup for onboard mic mic = audiobusio.PDMIn(board.MICROPHONE_CLOCK, board.MICROPHONE_DATA, sample_rate=16000, bit_depth=16) # use some extra sample to account for the mic startup samples_bit = array.array('H', [0] * (fft_size+3)) # sends visualized data to the RGB matrix with colors def waves(data, y): offset = max(0, (13-len(data))//2) for x in range(min(13, len(data))): is31.pixel(x+offset, y, heatmap[int(data[x])])
In the loop, you'll find the ulab
CircuitPython library in action, using numpy
to crunch the sampled data into a pretty, colorful light show.
while True: # record the audio sample mic.record(samples_bit, len(samples_bit)) # send the sample to the ulab array samples = np.array(samples_bit[3:]) # creates a spectogram of the data spectrogram1 = spectrogram(samples) # spectrum() is always nonnegative, but add a tiny value # to change any zeros to nonzero numbers spectrogram1 = np.log(spectrogram1 + 1e-7) spectrogram1 = spectrogram1[1:(fft_size//2)-1] # sets range of the spectrogram min_curr = np.min(spectrogram1) max_curr = np.max(spectrogram1) # resets values if max_curr > max_all: max_all = max_curr else: max_curr = max_curr-1 min_curr = max(min_curr, 3) # stores spectrogram in data data = (spectrogram1 - min_curr) * (51. / (max_all - min_curr)) # sets negative numbers to zero data = data * np.array((data > 0)) # resets y y = scroll_offset # runs waves to write data to the LED's waves(data, y) # updates scroll_offset to move data along matrix scroll_offset = (y + 1) % 9 # writes data to the RGB matrix is31.show()
It's important for data visualizations to be as speedy and reactive as possible for the best effect. To do this for the IS31FL3741 matrix, I2C is overclocked to communicate using 1 MHz speed. Additionally, the FFT data sample size is set to 64.
# Manually declare I2c (not board.I2C()) to access 1 MHz speed for i2c = busio.I2C(board.SCL, board.SDA, frequency=1000000) # Declare is31 w/buffering preferred (low RAM will fall back on unbuffered) is31 = Adafruit_RGBMatrixQT(i2c, allocate=adafruit_is31fl3741.PREFER_BUFFER) # In buffered mode, MUST use show() to refresh matrix (see line 94) # size of the FFT data sample fft_size = 64
After speed, the colors used to visualize the data come into play. The selected gradient in the code is a rainbow effect ranging from purple to red through the color spectrum. You can experiment with creating your own gradients too. There are quite a few tools available online for this. For this project, colordesigner.io was used.
The color gradient is stored in the heatmap
array and consists of 52 hexadecimal color codes.
# array of colors for the LEDs # goes from purple to red # gradient generated using https://colordesigner.io/gradient-generator heatmap = [0xb000ff,0xa600ff,0x9b00ff,0x8f00ff,0x8200ff, 0x7400ff,0x6500ff,0x5200ff,0x3900ff,0x0003ff, 0x0003ff,0x0047ff,0x0066ff,0x007eff,0x0093ff, 0x00a6ff,0x00b7ff,0x00c8ff,0x00d7ff,0x00e5ff, 0x00e0ff,0x00e6fd,0x00ecf6,0x00f2ea,0x00f6d7, 0x00fac0,0x00fca3,0x00fe81,0x00ff59,0x00ff16, 0x00ff16,0x45ff08,0x62ff00,0x78ff00,0x8bff00, 0x9bff00,0xaaff00,0xb8ff00,0xc5ff00,0xd1ff00, 0xedff00,0xf5eb00,0xfcd600,0xffc100,0xffab00, 0xff9500,0xff7c00,0xff6100,0xff4100,0xff0000, 0xff0000,0xff0000]
Page last edited January 22, 2025
Text editor powered by tinymce.