Raspberry Pi Pico Multi MIDI Router

In this project I’ve combined my Raspberry Pi Pico MIDI Channel Merger and my Raspberry Pi Pico MIDI Channel Router to create a more general 6-IN, 6-OUT MIDI routing device.  This project uses the two hardware UARTs on a Raspberry Pi Pico along with four “PIO” UARTs to give the six MIDI IN and OUT ports.

  • Part 1 shows the basic principles and code using jumper links.
  • Part 2 builds a proto-board Multi-MIDI router.
  • Part 3 adds optional USB MIDI host and device support.
  • Part 4 provides the design for a custom PCB.
  • Part 5 is the build guided for the custom PCB.

The idea is revisited for enhancements in: Raspberry Pi Pico Multi MIDI Router – Revisited.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

These are the key tutorials for the main concepts used in this project:

If you are new to microcontrollers, see the Getting Started pages.

Parts list

The Circuit

PiPicoMultiMIDI-PIO_bb

The basic idea is shown above.  There will be two MIDI interfaces connected to the two hardware serial ports (UART0 and UART1) on pins 1,2 and 6,7, accessible via GP0, GP1 and GP4, GP5 respectively.

Then the Raspberry Pi Pico’s PIO system will be used to provide four consecutive “RX” serial links on pins 9-12 (GP6-9) and four consecutive “TX” serial links on pins 14-17 (GP10-13).

In terms of their use with MIDI interfaces, the hardware and PIO serial links are largely interchangeable, so a MIDI interface can be connected to any of the six RX lines or six TX lines.

The Code

The code is an amalgamation of the following two projects:

The first shows how to use the PIO system as eight serial TX links.  The second shows how to use the PIO system as eight serial RX links.

Key to the operation is a “MIDI routing table” which specifies which ports should be connected to which ports.  The ports are numbered as follows:

  • Port 0 = hardware serial port (UART) 0 – GP0, 1.
  • Port 1 = hardware serial port (UART) 1 – GP4, 5.
  • Port 2 = PIO RX and TX link 0 – GP6 and GP10.
  • Port 3 = PIO RX and TX link 1 – GP7 and GP11.
  • Port 4 = PIO RX and TX link 2 – GP8 and GP12.
  • Port 5 = PIO RX and TX link 3 – GP9 and GP12.

The routing table itself is described in code at the top of the file.

# Define the MIDI routing table specifying groups of
# [MIDI CH, MIDI CMD, source port, destination port]
#
# NB: -1 for CH, CMD or SRC means "any"
#
MIDIRT = [
[-1, -1, 2, 2], # Anything on port 2 to port 2
[-1, -1, 3, 3], # Anything on port 3 to port 3
[-1, -1, 4, 4], # Anything on port 4 to port 4
[-1, -1, 5, 0], # Anything on port 5 to port 0
[-1, -1, 0, 1], # Anything on port 0 to port 1
[-1, -1, 0, 5], # Anything on port 0 to port 5
[-1, -1, 1, 1], # Anything on port 1 to port 1
[-1, -1, 1, 5], # Anything on port 1 to port 5
[6, 0x80, 0, 4], # NoteOff on CH 6 on port 0 to port 5
[6, 0x90, 0, 4], # NoteOn on CH 6 on port 0 to port 5
]

This is the table used with the demonstration, for which there are full details later.

The MIDI routing table has the concept of a “wildcard” match.  Setting any of the values to “-1” means “don’t care”.  So the first route, for example: [-1, -1, 2, 2] means the following:

  • MIDI channel = -1 = ANY
  • MIDI command = -1 = ANY
  • Source port = 2 = PIO RX port 2 (GP6)
  • Destination port = 2 = PIO TX port 2 (GP10)

The routing algorithm will find all routes that match and pass on the traffic, so it is perfectly possible to copy traffic to multiple destinations. I did initially wonder about only looking for the most specific matches, which would have required multiple searches through the routing table, but in the end opted for multiple-matching.

At present the code provides debug output to show which routes are matched and where the data ends up getting sent. There is a general “midi send” function that works out if the destination is a hardware or PIO port and then uses the appropriate final “sending” routine to do it.

I had to update my SimpleMIDIDecoder slightly to allow me to pass in an “instance” number when it is set up which can then be used later on to work out which MIDI handler is being used in the callback functions.

The decoder is now initialised six times as follows:

md = []
for i in range(HW_NUM_UARTS+RX_NUM_UARTS):
    # Set up one MIDI decoder per hardware UARTs
    md_t = SimpleMIDIDecoder.SimpleMIDIDecoder(i)
    md_t.cbNoteOn (doMidiNoteOn)
    md_t.cbNoteOff (doMidiNoteOff)
    md_t.cbThru (doMidiThru)
    md.append(md_t)

Which means that the callback functions will receive an “index” parameter that determines the source of the MIDI messages, which can then be passed into the MIDI router which will return a list of destination ports to pass the messages on to.

def doMidiNoteOn(ch,cmd,note,vel,src):
    ledOn()
    dst = midiRouter(ch, cmd, src)
    if dst:
        for d in dst:
            midi_send(d, cmd, ch, note, vel)

The main scanning routine will check each hardware serial port, then all four PIO RX links and if there is any data, pass it on to the appropriate SimpleMIDIDecoder instance.

while True:
    for i in range(HW_NUM_UARTS):
        if (hw_uarts[i].any()):
            md[i].read(hw_uarts[i].read(1)[0])

    for i in range(RX_NUM_UARTS):
        if (rx_uarts[i].rx_fifo()):
            md[HW_NUM_UARTS+i].read(rx_uarts[i].get() >> 24)

Find it on GitHub here alongside the updated SimpleMIDIDecoder and the MIDITester.

Demonstration

In order to attempt to show the routing in action, I’m using the following (somewhat artificial) setup.

PiPicoMultiMIDI-Test_bb

The hardware and PIO ports are allocated as follows:

Referring back to the demonstration routing table, there are three routes at the start linking PIO RX ports 2 to 4 straight through to PIO RX ports 2 to 4.  This means that three of the “test” Picos will each end up “playing” one of the Arduino Nano tone boards.

PIO RX port 5 is linked to UART port 0 (TX), so the fourth “test” Pico will be “playing” the PC USB MIDI IN port.

UART port 0 (RX) is linked to both UART port 1 (TX) and PIO port 5 (TX), as is UART port 1 (RX).  This means that both the PC USB MIDI OUT and the toy MIDI controller keyboard will be playing the MT-32 and showing the output on the MIDI visualiser.

Finally there is a more specific route matching NoteOn and NoteOff messages only on MIDI channel 6 on UART port 0 (RX) and sending them to PIO TX port 5.  This means that setting the PC to send on MIDI channel 6 will also show on the MIDI visualiser (I forgot to show this in the video, but it works!).

This is somewhat contrived, but in the video you can see the following happening:

  • Playing a range of MIDI channels from MIDIOX on the PC, each being routed to the MT-32 and thus playing a different sound depending on the MT-32 configuration, whilst activating the MIDI visualisation.
  • Playing the MIDI controller keyboard plays the MT-32 on MIDI channel 1 and also activates the MIDI visualisation.
  • When the “test” Picos are connected, notes start to be played on the Arduino tone boards.
  • Finally when all tones are being played, the MIDI controller keyboard is still quite happily playing the MT-32 and the MIDI visualiser.
  • I forgot to show the PC sending on MIDI channel 6 to show the more specific NoteOn/NoteOff route in action…

Whilst all this is going on, the matched routes are being printed to the serial console, which can just about be seen in the bottom left corner of the video.

IMG_5631

Closing Thoughts

This seems to be working pretty well!  I’ve not tested it exhaustively so if you give it a try, do let me know how you get on!  At the very least, you’ll need to specify a more useful routing table to match what you need it to do.

If I was more of a python/object oriented person, I’d probably try to “wrap up” the PIO RX/TX links into a software library that mirrors the way uart works, but for PIO links.  As it stands, the code is there to be optimised, but it all seems to work for now.

What I’d really like is a Raspberry Pi Pico version of these Teensy MIDI boards from Deftaudio on Tindie. I have a basic stripboard design, so I might try to prototype one, but it would be really nice to have it on a proper PCB!

Kevin

One thought on “Raspberry Pi Pico Multi MIDI Router

  1. I can do the pcb easily. I’m currently using atmega2560 with the controlSurface library for this purpose, and I want to migrate to pi pico

    Like

Leave a comment