Soundboard Code

Navigating the NeoTrellis

To get your NeoTrellis M4 set up to run the soundboard code, follow these steps:

1) Don't forget to update the bootloader for NeoTrellis from the NeoTrellis M4 guide

2) Install the latest CircuitPython for NeoTrellis from the NeoTrellis M4 guide

3) Get the latest 4.0 library pack, unzip it, and drag the libraries you need over into the /lib folder on CIRCUITPY. The latest library package includes support for NeoTrellis.

For this project you will need the following libraries:

  • adafruit_trellism4.mpy
  • adafruit_fancyled folder
  • neopixel.mpy
  • adafruit_matrixkeypad.mpy

Add Audio

Ensure your created sound clips are numbered 01.wav to 32.wav (don't forget those leading zeroes!). They all must be 16 Bit, 22KHz WAV files in mono (single channel audio) or stereo (2 channel audio) and cannot be mixed mono & stereo.

Connect your NeoTrellis to your computer via a known good USB cable. Your operating system will show a new "thumb drive" named CIRCUITPY is available. If you do not see this happening, visit the NeoTrellis M4 introductory tutorial to ensure CircuitPython has been loaded onto the device.

Next, in your file explorer/Finder, create a folder on the CIRCUITPY flash drive labeled /startrek. You can then drag and drop your audio files into that directory (ensuring the files are named from 01.wav through 32.wav). You can change the name of the folder later in the code if you want.

Soundboard Code

Onto the final step, the code itself!

Copy from the link below and put it in CIRCUITPY root directory. You can work with this code in any text editing application, or open and save with Mu if you prefer. 

Also copy the linked file into CIRCUITPY root directory.

import time
import board
import audioio
import adafruit_fancyled.adafruit_fancyled as fancy
import adafruit_trellism4
from color_names import * # pylint: disable=wildcard-import,unused-wildcard-import


SAMPLE_FOLDER = "/startrek/"  # the name of the folder containing the samples
# This soundboard can select up to *32* sound clips! each one has a filename
# which will be inside the SAMPLE_FOLDER above, and a *color* in a tuple ()
SAMPLES = [("01.wav", RED),
           ("02.wav", ORANGE),
           ("03.wav", YELLOW),
           ("04.wav", GREEN),
           ("05.wav", TEAL),
           ("06.wav", BLUE),
           ("07.wav", PURPLE),
           ("08.wav", PINK),
           ("09.wav", RED),
           ("10.wav", ORANGE),
           ("11.wav", YELLOW),
           ("12.wav", GREEN),
           ("13.wav", TEAL),
           ("14.wav", BLUE),
           ("15.wav", PURPLE),
           ("16.wav", PINK),
           ("17.wav", RED),
           ("18.wav", ORANGE),
           ("19.wav", YELLOW),
           ("20.wav", GREEN),
           ("21.wav", TEAL),
           ("22.wav", BLUE),
           ("23.wav", PURPLE),
           ("24.wav", PINK),
           ("25.wav", RED),
           ("26.wav", ORANGE),
           ("27.wav", YELLOW),
           ("28.wav", GREEN),
           ("29.wav", TEAL),
           ("30.wav", BLUE),
           ("31.wav", PURPLE),
           ("32.wav", PINK)]

# For the intro, pick any number of colors to make a fancy gradient!
# the color for the selected sample

# Our keypad + neopixel driver
trellis = adafruit_trellism4.TrellisM4Express(rotation=0)

# Play the welcome wav (if its there)
with audioio.AudioOut(board.A1, right_channel=board.A0) as audio:
        f = open("welcome.wav", "rb")
        wave = audioio.WaveFile(f)
        swirl = 0  # we'll swirl through the colors in the gradient
        while audio.playing:
            for i in range(32):
                palette_index = ((swirl+i) % 32) / 32
                color = fancy.palette_lookup(INTRO_SWIRL, palette_index)
                # display it!
                trellis.pixels[(i%8, i//8)] = color.pack()
            swirl += 1
        # Clear all pixels
        # just hold a moment
    except OSError:
        # no biggie, they probably deleted it

# Parse the first file to figure out what format its in
channel_count = None
bits_per_sample = None
sample_rate = None
with open(SAMPLE_FOLDER+SAMPLES[0][0], "rb") as f:
    wav = audioio.WaveFile(f)
    print("%d channels, %d bits per sample, %d Hz sample rate " %
          (wav.channel_count, wav.bits_per_sample, wav.sample_rate))

    # Audio playback object - we'll go with either mono or stereo depending on
    # what we see in the first file
    if wav.channel_count == 1:
        audio = audioio.AudioOut(board.A1)
    elif wav.channel_count == 2:
        audio = audioio.AudioOut(board.A1, right_channel=board.A0)
        raise RuntimeError("Must be mono or stereo waves!")

# Clear all pixels

# turn on maybe play all of the buttons
for i, v in enumerate(SAMPLES):
    filename = SAMPLE_FOLDER+v[0]
        with open(filename, "rb") as f:
            wav = audioio.WaveFile(f)
                  "%d channels, %d bits per sample, %d Hz sample rate " %
                  (wav.channel_count, wav.bits_per_sample, wav.sample_rate))
            if wav.channel_count != channel_count:
            if wav.bits_per_sample != bits_per_sample:
            if wav.sample_rate != sample_rate:
            trellis.pixels[(i%8, i//8)] = v[1]
            if PLAY_SAMPLES_ON_START:
                while audio.playing:
    except OSError:
        # File not found! skip to next

def stop_playing_sample(playback_details):
    print("playing: ", playback_details)
    trellis.pixels[playback_details['neopixel_location']] = playback_details['neopixel_color']
    playback_details['voice'] = None

current_press = set()
currently_playing = {'voice' : None}
last_samplenum = None
while True:
    pressed = set(trellis.pressed_keys)
    #if pressed:
    #    print("Pressed:", pressed)

    just_pressed = pressed - current_press
    just_released = current_press - pressed

    #if just_pressed:
    #    print("Just pressed", just_pressed)
    for down in just_pressed:
        sample_num = down[1]*8 + down[0]
            filename = SAMPLE_FOLDER+SAMPLES[sample_num][0]
            f = open(filename, "rb")
            wav = audioio.WaveFile(f)

            # is something else playing? interrupt it!
            if currently_playing['voice'] != None:

            trellis.pixels[down] = WHITE
            # voice, neopixel tuple, color, and sample, file handle
            currently_playing = {
                'voice': 0,
                'neopixel_location': down,
                'neopixel_color': SAMPLES[sample_num][1],
                'sample_num': sample_num,
                'file': f}
        except OSError:
            pass # File not found! skip to next

    #if just_released:
    #    print("Just released:", just_released)

    # check if any samples are done
    if not audio.playing and currently_playing['voice'] != None:

    time.sleep(0.01)  # a little delay here helps avoid debounce annoyances
    current_press = pressed
RED = 0xFF0000
MAROON = 0x800000
ORANGE = 0xFF8000
OLIVE = 0x808000
GREEN = 0x008000
TEAL = 0x008080
BLUE = 0x0000FF
NAVY = 0x000080
PURPLE = 0x800080
PINK = 0xFF0080
BLACK = 0x000000

Your CIRCUITPY flash drive should now include the following files:

  1. main program
  2. color definition file
  3. /lib directory containing the necessary CircuitPython 4.x libraries
  4. /startrek directory with 32 wav file audio clips names 01.wav, 02.wav, . . ., 32.wav

Boot up: Color!

Once you have all your files set up, your board should reboot and start running your code automatically.

The boot-up sequence illuminates all neopixels in a rainbow pattern, then goes dark for a second before coming back on in an array of Star Trek-inspired colors. 

Making Changes

If you'd like to change the code, you can do so as follows:

The line INTRO_SWIRL = [PINK, TEAL, YELLOW] can be changed to be any three color names listed in You can even put more color values in that file and refer to them in your code.

In the definition of SAMPLES near the top of the code - it lists a file name and the color of the button. Feel free to color the buttons how you wish using the color names in

While Star Trek used lots of primary colors (as an early color TV program), your other soundboards may be different. Say, different shades of green for Teenage Mutant Turtles, etc.

Images from Star Trek, copyright by the license holder, reproduced under fair use as outlined in 17 U.S.C. 107 Educational Use.


You can use the serial capability in Mu or a serial terminal program to connect to the NeoTrellis M4 to interact with the CircuitPython prompt. You can see error messages, restart the program, etc. If you use a terminal program, set it to the COM port that appears when the device is plugged in. In Windows, Device Manager will show a keyboard when NeoTrellis is plugged in. Right click, Properties, Hardware will show the COM port. Connect at 9600 baud.

Problem: I don't see the CIRCUITPY drive when I plug the NeoTrellis M4 into my computer

Solution: Ensure CircuitPython is installed via this guide.


Problem: The code does not run.

Solution: Check the files on the CIRCUITPY drive. /lib should have the latest CircuitPython 4.x libraries, /startrek holds the 32 sound files, and should be in the main (root) directory.


Problem: Not enough disk space for all the files.

Solution: You cannot use long stereo clips or songs for each button, there is not enough flash memory space. You can reduce the number of files below 32. You can trim your clips to make them shorter. Or you can ensure all your clips are mono instead of stereo. Advanced users can remove libraries in /lib for sensors that are not used by your device to free up small amounts of flash but please do not delete any libraries used by the program or low level input/output libraries.


Problem: My board isn't booting up!

Solution: Make sure your /lib folder is set up with the libraries in the 4.0 latest release and includes trellism4.mpy in its contents.


Problem: I'm not hearing any sounds!

Solution: Check that all your files are formatted as Stereo 16-bit, 22,050Hz, PCM

This guide was first published on Nov 14, 2018. It was last updated on Nov 14, 2018. This page (Soundboard Code) was last updated on Dec 12, 2018.