Software

Install CircuitPython

The Adafruit Feather M4 ships with CircuitPython but lets go ahead and update it to the latest version. It's super easy with the circuitpython.org website, just click the link below to launch the page. There you can choose to install stable release or beta. 

Quick Start

  • Connect board to computer via a known good USB data cable and double press the reset button.
  • Download the CircuitPython UF2 and upload to the FEATHERBOOT drive.
  • Open CIRCUITPY drive and upload the required libraries (listed below) and code.py

Adafruit Circuit Python Libraries

Download the CircuitPython library bundle and unzip the folder. Create a new folder in the CIRCUITPY drive and name it "lib". The following libraries are required to run the code properly. Double check to ensure all of the files and folders are inside the lib folder on the CIRCUITPY drive.

  • adafruit_bus_device (directory)
  • adafruit_lis3dh.mpy
  • adafruit_neotrellis (directory)
  • adafruit_rgbled.py
  • adafruit_seesaw
  • neopixel.mpy
  • simpleio.mpy

Mu Python Editor

Check out Mu, it's a simple Python editor that works with Adafruit CircuitPython hardware. It's written in Python and works on Windows, MacOS, Linux and Raspberry Pi. The serial console is built right in so you get immediate feedback from your board's serial output!

# NeoTrellis Soundbox Remix - CircuitPython
# Noe and Pedro Ruiz, code by Mike Barela
# for Adafruit Industries, MIT License

import time
import os
import random
import board
from board import SCL, SDA
import digitalio
import busio
import audioio
import adafruit_rgbled
from adafruit_neotrellis.neotrellis import NeoTrellis
import adafruit_lis3dh

# Color definitions
OFF = (0, 0, 0)
RED = (25, 0, 0)
YELLOW = (25, 15, 0)
GREEN = (0, 25, 0)
CYAN = (0, 25, 25)
BLUE = (0, 0, 25)
PURPLE = (18, 0, 25)
WHITE = (127, 127, 127)

# Create the i2c object for the trellis
# If you get an error, your PropMaker Shield needs to be snappped on
i2c_bus = busio.I2C(SCL, SDA)

# Create the trellis
trellis = NeoTrellis(i2c_bus)

print("NeoTrellis created")

# Enable PWR Pin to enable NeoPixels, audio amplifier and RGB LED
# See https://learn.adafruit.com/adafruit-prop-maker-featherwing/pinouts
enable = digitalio.DigitalInOut(board.D10)
enable.direction = digitalio.Direction.OUTPUT
enable.value = True

# Set up RGB for switch RGB LED
RED_LED = board.D11
GREEN_LED = board.D12
BLUE_LED = board.D13
led = adafruit_rgbled.RGBLED(RED_LED, GREEN_LED, BLUE_LED)
led.color = GREEN

# Enable button use on PropMaker Wing Switch input
push_switch = digitalio.DigitalInOut(board.D9)
push_switch.switch_to_input(pull=digitalio.Pull.UP)

# Set up Accelerometer on I2C bus
int1 = digitalio.DigitalInOut(board.D5)
accel = adafruit_lis3dh.LIS3DH_I2C(i2c_bus, int1=int1)
# See https://circuitpython.readthedocs.io/projects/lis3dh/en/
#  latest/api.html for adjusting settings for the accelerometer
accel.range = adafruit_lis3dh.RANGE_4_G
# accel.set_tap(1, 80)  # Single tap, second value is sensitivity

# Set up playing audio on A0 and interruptable playing
myaudio = audioio.AudioOut(board.A0)
audio_file = None

def play_file(audio_filename):
    global audio_file  # pylint: disable=global-statement
    if myaudio.playing:
        myaudio.stop()
    if audio_file:
        audio_file.close()
    audio_file = open("/sounds/"+audio_filename, "rb")
    wav = audioio.WaveFile(audio_file)
    print("Playing "+audio_filename+".")
    myaudio.play(wav)

# Process wav files in the flash drive sounds directory
wavefiles = [file for file in os.listdir("/sounds/")
             if (file.endswith(".wav") and not file.startswith("._"))]
if len(wavefiles) < 1:
    print("No wav files found in sounds directory")
else:
    print("Audio files found: ", wavefiles)

PUSH_COLOR = GREEN
ANIM_COLOR = WHITE

COLORS = ["RED", "YELLOW", "GREEN", "CYAN", "BLUE", "PURPLE", "WHITE"]
COLOR_TUPLES = [RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE]

buttons = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
button_colors = [OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF,
                 OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF]
shuffled_colors = list(button_colors)
Shuffled = False

# Time to process the filenames using the special file name syntax
# Currently nn-color-name.wav where nn = 2 digit number 0 to 15
# color is lower or upper case color name from above and
# name can be anything. BUT these all must be separated by a "-"
# Example 02-blue-firetruck.wav is valid. Note leading 0 for 0 to 9
wavnames = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
shuffled_names = list(wavnames)  # Duplicate list, wavnames is our reference
shuffled = False
for soundfile in wavefiles:
    print("Processing "+soundfile)
    pos = int(soundfile[0:2])
    if pos >= 0 and pos < 16:      # Valid filenames start with 00 to 15
        wavnames[pos] = soundfile  # Store soundfile in proper index
        shuffled_names[pos] = soundfile
        skip = soundfile[3:].find('-') + 3
        user_color = soundfile[3:skip].upper()  # Detect file color
        print("For file "+soundfile+", color is "+user_color+".")
        file_color = COLOR_TUPLES[COLORS.index(user_color)]
        button_colors[pos] = file_color
        shuffled_colors[pos] = file_color
    else:
        print("Filenames must start with a number from 00 to 15 - "+soundfile)

# this will be called when button events are received
def blink(event):
    # turn the LED on when a rising edge is detected
    if event.edge == NeoTrellis.EDGE_RISING:  # Trellis button pushed
        print("Button "+str(event.number)+" pushed")
        if event.number > 15:
            print("Event number out of range: ", event.number)
        trellis.pixels[event.number] = WHITE
        if shuffled_names[event.number] != "":
            play_file(shuffled_names[event.number])

    # turn the LED off when a rising edge is detected (button released)
    elif event.edge == NeoTrellis.EDGE_FALLING:
        trellis.pixels[event.number] = shuffled_colors[event.number]

for i in range(16):
    # activate rising edge events on all keys
    trellis.activate_key(i, NeoTrellis.EDGE_RISING)
    # activate falling edge events on all keysshuff
    trellis.activate_key(i, NeoTrellis.EDGE_FALLING)
    # set all keys to trigger the blink callback
    trellis.callbacks[i] = blink

    # cycle the LEDs on startup
    trellis.pixels[i] = ANIM_COLOR
    time.sleep(.05)

# On start, set the pixels on trellis to the file name colors chosen
for i in range(16):
    trellis.pixels[i] = shuffled_colors[i]
    time.sleep(.05)

while True:
    # call the sync function call any triggered callbacks
    trellis.sync()

    # Check push switch, reset trellis buttons randomization if pressed
    if not push_switch.value:
        myaudio.stop()  # Stop any audio playing
        print("RGB Switch Push - reset shuffle if needed")
        shuffled_names = list(wavnames)     # Reset with clean lists
        shuffled_colors = list(button_colors)
        for i in range(16):
            trellis.pixels[i] = shuffled_colors[i]
            time.sleep(.05)
        Shuffled = False
        led.color = GREEN
    # Check accelerometer
    if accel.shake(shake_threshold=15):   # Change shake(val) to tapped
        myaudio.stop()  # Stop any audio playing
        print("Unit Tapped - shuffle sound files to random buttons")
        shuffled_names = list(wavnames)       # Copy lists
        shuffled_colors = list(button_colors)
        for i in range(len(wavnames)):  # Do the shuffling
            random_i = random.randrange(len(wavnames))
            # Swap current name with a random slot
            name = shuffled_names[random_i]
            shuffled_names[random_i] = shuffled_names[i]
            shuffled_names[i] = name
            number = shuffled_colors[random_i]
            shuffled_colors[random_i] = shuffled_colors[i]
            shuffled_colors[i] = number
        for i in range(16):
            trellis.pixels[i] = shuffled_colors[i]
            time.sleep(.05)
        print(shuffled_names)
        print(shuffled_colors)
        shuffled = True
        led.color = RED
    # the trellis can only be read every 17 milliseconds or so
    time.sleep(.019)

Audio Files

The sounds used in this project are sampled from popular music and movies. The pack contains various sounds that are already in the supported audio format. The file names must contain a two digital number followed by a color and single phrase description (00-15). For example:

  • 00-red-bird.wav
  • 01-blue-cat.wav
  • 02-green-dog.wav

The two digit number represents the button pad on the NeoTrellis. Located on the top of the PCB are labels with numbers 1-16. For example, the file name with "00" will be mapped to the first NeoPixel #1.  A total of 16 audio files can be used. 

Audio files must be in a directory named "sounds". 

Adafruit CircuitPython supports 16-bit, Mono, 22.050kHz .wav audio format. See this guide to help format any audio files you might want to use in this project besides the files provided.

NeoPixel Colors

Supported colors are in the list below. Optionally create custom colors using RGB values. List of colors are located in the code on line 17.

Download: file
# Color definitions
OFF = (0, 0, 0)
RED = (25, 0, 0)
YELLOW = (25, 15, 0)
GREEN = (0, 25, 0)
CYAN = (0, 25, 25)
BLUE = (0, 0, 25)
PURPLE = (18, 0, 25)
WHITE = (127, 127, 127)

Additional colors must be added to the array on line 87 and 88.

Download: file
COLORS = ["RED", "YELLOW", "GREEN", "CYAN", "BLUE", "PURPLE", "WHITE"]
COLOR_TUPLES = [RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE]
This guide was first published on Jul 16, 2019. It was last updated on Jul 16, 2019. This page (Software) was last updated on Nov 13, 2019.