Overview
Drum patterns and colored lights! Use your grid controller with your software synth and keep the LEDs in sync with the GUI. The NeoTrellis MIDI Feedback Controller sends MIDI messages containing note information in order to program a trigger sequencer -- but here's the cool part: when the controller receives MIDI note messages back from the software it interprets them as NeoPixel commands!
Inspired by the excellent, Arduino-based design by PatchworkBoy, this simpler, CircuitPython-based approach will get you started quickly controlling four 16-step drum trigger sequences in a VCV Rack. You can then modify it to fit your needs.
Page last edited March 08, 2024
Text editor powered by tinymce.
Assemble the Controller
Follow this page for the assembly of the NeoTrellis tiles.
NOTE: there is no need to solder wires to pads on the board if you use the Feather RP2040 as you can use a STEMMA to STEMMA QT cable instead.
I2C Address Jumpers
Solder the I2C address jumpers as shown here. This gives you addresses 0x2E, 0x2F, 0x30, and 0x31
Case Build
This guide page shows how to assemble the acrylic case for your NeoTrellis 8x8.
NOTE: You can skip the steps involving switches and battery as this will be used as a plugged-in MIDI controller over USB.
Page last edited March 08, 2024
Text editor powered by tinymce.
Code the Controller
Text Editor
Adafruit recommends using the Mu editor for using your CircuitPython code with the Feather. You can get more info in this guide.
Alternatively, you can use any text editor that saves text files.
CircuitPython Installation
First make sure you are running the latest version of Adafruit CircuitPython for your board.
Project Files
Download the Project Bundle linked below and copy them onto your CIRCUITPY drive.
Libraries and code.py
You'll need to download the .zip file in the Download Project Bundle button below and unzip the file to get all of the needed files.
Copy the lib directory as well as the code.py file to your Feather's CIRCUITPY drive.
# SPDX-FileCopyrightText: 2022 John Park & @todbot Tod Kurt for Adafruit Industries
# SPDX-License-Identifier: MIT
# NeoTrellis 8x8 MIDI Trigger Sequencer
# Does bi-directional MIDI with VCV Rack + Stoermelder MIDI-CAT module
# (can also work with with other apps, such as DAWs that send MIDI for LED controll)
# This is a simplified version of https://github.com/PatchworkBoy/TrowasoftControl
# Requirements:
# -VCV Rack 2, w/ MIDI-CAT
# -sequencer such as Trowa TrigSeq or Count Modula
# -MIDI mappings on pads on "momentary"
import time
import board
import busio
from adafruit_neotrellis.neotrellis import NeoTrellis
from adafruit_neotrellis.multitrellis import MultiTrellis
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
note_base = 36
note_vel = 127
pad_midi_channel = 0 # add one for "real world" MIDI channel number, e.g. 0=1
led_midi_channel = 2 # see ^
pad_ager_secs = 0.1 # how many seconds to wait for MIDI-CAT to fail
pad_ager_secs_max = 1.0 # seconds max to wait for MIDI-CAT before giving up
# holds whether pad is currently lit or not, when it was pressed before being ack'd
# tuple of (pad lit?, pad_press_time_or_zero_if_ackd)
pad_states = [(False,0)] * 64
midi_usb = adafruit_midi.MIDI( midi_in=usb_midi.ports[0],
midi_out=usb_midi.ports[1] )
i2c = busio.I2C(board.SCL, board.SDA)
trelli = [ # adjust these to match your jumper settings if needed
[NeoTrellis(i2c, False, addr=0x2E), NeoTrellis(i2c, False, addr=0x2F)],
[NeoTrellis(i2c, False, addr=0x30), NeoTrellis(i2c, False, addr=0x31)]
]
trellis = MultiTrellis(trelli)
OFF = 0x000000
RED = 0x100000
YELLOW = 0x100c00
GREEN = 0x000c00
CYAN = 0x000303
BLUE = 0x000010
PURPLE = 0x130010
colors = [OFF, RED, YELLOW, GREEN, CYAN, BLUE, PURPLE]
color_table = [ # you can make custom color sections for clarity
1, 1, 1, 1, 5, 5, 5, 5,
1, 1, 1, 1, 5, 5, 5, 5,
1, 1, 1, 1, 5, 5, 5, 5,
1, 1, 1, 1, 5, 5, 5, 5,
4, 4, 4, 4, 6, 6, 6, 6,
4, 4, 4, 4, 6, 6, 6, 6,
4, 4, 4, 4, 6, 6, 6, 6,
4, 4, 4, 4, 6, 6, 6, 6
]
# convert an x,y (0-7,0-7) to 0-63
def xy_to_pos(x,y):
return x+(y*8)
# convert 0-63 to x,y
def pos_to_xy(pos):
return (pos%8, pos//8)
# callback when pads are pressed
def handle_pad(x, y, edge):
pos = xy_to_pos(x,y)
note_val = pos + note_base
if edge == NeoTrellis.EDGE_RISING:
(pad_on, pad_time) = pad_states[pos] # get pad state & press time
print(pad_time)
pad_on = not pad_on # toggle state
pad_states[pos] = (pad_on, time.monotonic()) # and save it w/ new press time
print("handle_pad: x,y,pos",x,y, pos, note_val, pad_on)
if pad_on:
noteon = NoteOn(note_val, note_vel, channel=pad_midi_channel)
midi_usb.send(noteon)
else:
noteoff = NoteOff(note_val, note_vel, channel=pad_midi_channel)
midi_usb.send(noteoff)
# called periodically in main loop to receive MIDI msgs
# saves to pad_state with LED on/off and 0 to indicate pad press acknowledgement
def midi_receive():
msg_in = midi_usb.receive()
if msg_in is None:
return
#print("msg_in:", msg_in.channel, msg_in.note, msg_in.velocity)
if msg_in.channel == led_midi_channel:
pos = msg_in.note - note_base
x,y = pos_to_xy(pos)
if isinstance(msg_in, NoteOn):
print("midi_receive: LED ON ",pos, x,y)
pad_states[pos] = (True, 0) # save pad state with press time 0 = acknowledgdd
trellis.color(x,y, colors[color_table[pos]])
if isinstance(msg_in, NoteOff):
print("midi_receive: LED OFF ",pos, x,y)
pad_states[pos] = (False, 0) # save pad state with press time 0 = acknowledged
trellis.color(x,y, 0x000000)
# attempts to fix the MIDI-CAT bug by retoggling a pad if no LED msg sent
def send_pad_toggle(pad_on,pos):
print("send_pad_toggle",pad_on,pos)
note_val = pos + note_base
noteon = NoteOn(note_val, note_vel, channel=pad_midi_channel)
noteoff = NoteOff(note_val, note_vel, channel=pad_midi_channel)
if pad_on:
midi_usb.send(noteoff)
midi_usb.send(noteon)
else:
midi_usb.send(noteon)
midi_usb.send(noteoff)
# scan list of pads, if any haven't been acknowledged with LED msgs, toggle them
def pad_ager():
for i in range(len(pad_states)):
(pad_on, pad_time) = pad_states[i]
if (pad_time > 0 and
time.monotonic() - pad_time > pad_ager_secs and
time.monotonic() - pad_time < pad_ager_secs_max):
send_pad_toggle(pad_on,i)
# set up actions for each pad and do a startup light show
for y_pad in range(8):
for x_pad in range(8):
trellis.activate_key(x_pad, y_pad, NeoTrellis.EDGE_RISING)
trellis.activate_key(x_pad, y_pad, NeoTrellis.EDGE_FALLING)
trellis.set_callback(x_pad, y_pad, handle_pad)
trellis.color( x_pad, y_pad, PURPLE)
time.sleep(0.005)
for y_pad in range(8): # finish light show
for x_pad in range(8):
trellis.color(x_pad, y_pad, OFF)
time.sleep(0.005)
print("Ready. Be sure VCV Rack2 w/ MIDI-CAT is running and configured")
last_debug_time = 0
while True:
trellis.sync()
midi_receive()
pad_ager()
if time.monotonic() - last_debug_time > 5.0:
last_debug_time = time.monotonic()
# print("pads:", ''.join(['1' if i else '0' for (i,t) in pad_states]))
Next, you set up VCV Rack to work with the NeoTrellis 8x8 MIDI Feedback Controller.
Page last edited March 08, 2024
Text editor powered by tinymce.
Rack Setup
VCV Rack is a virtual modular synthesizer that is free and open source. It can be used to patch together modules for sound design and music creation all by itself, and it can also be used with MIDI controllers.
You'll use it with the excellent Stoermelder MIDI-CAT and Trowasoft trigSeq modules so that the NeoTrellis 8x8 can both send and receive MIDI messages. This is a simplified version of the excellent system created by PatchworkBoy here.
MIDI messages sent from the NeoTrellis to Rack will be used to change the sequencer trigger patterns of three drums and a bass synth.
In the other direction, MIDI messages from Rack to the NeoTrellis will be used to keep the NeoPixel lighting in sync with the on-screen GUI.
VCV Rack and Plugins Required
- VCVRack2 + Fundamentals
- Audible Instruments (Mutable Instruments) Plaits
- Stoermelder PACK-ONE Dev Builds
- Trowasoft-VCV
Download and install VCV Rack 2 for your operating system. This will include the Fundamental modules as well.
Then, from the Rack Library, install Audible Instruments Macro Oscillator 2 (software version of the esteemed Mutable Instruments Plaits module).
Download Stoermelder PACK-ONE. Then, put the .vcvplugin file in the /Documents/Rack/plugins directory.
Finally, download the Trowasoft-VCV dev build. Extract the zip, then place the folder in the /Documents/Rack/plugins directory.
MIDI Device Select
When you first open the VCV Rack patch file you may find that the MIDI-CAT module has (No device) listed for both the outgoing and incoming MIDI devices.
Click these each and set them to the name of the board in your NeoTrellis 8x8 -- either Feather RP2040 or Feather M4, depending.
Once the controller is selected in the MIDI-CAT module, hit Reset and then Play in the Pulses module to start the trigger sequencers.
Now, you can add triggers to any of the four drum sequencers with the NeoTrellis and the VCV Rack GUI will match. Same goes for the other direction! Click any triggers in the Trowasoft trigSeq modules and the NeoTrellis will adjust the NeoPixels to match.
Acknowledgements
This project would not have been possible without the excellent work done by PatchworkBoy in creating the TrowasoftControl code, workflow, and example patch seen here.
Page last edited March 08, 2024
Text editor powered by tinymce.