I have a number of microcontrollers now that use 3.3V logic:
So far, I’ve look at MIDI OUT functionality, so now its time to look at MIDI IN, based on the circuit from my Simple MIDI Monitor. This project shows how to build your own MIDI IN circuit, but if that is too much trouble, there are some Ready-Made MIDI Modules that are 3.3V friendly too (e.g. the Hobbytronics one).
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:
- Getting Started with the Raspberry Pi Pico: Inputs and Outputs
- Adafruit Circuit Playground Express Tutorial
- Notes and Volts MIDI Circuit Analysis
If you are new to microcontrollers, see the Getting Started pages.
- Raspberry Pi Pico or Adafruit Circuit Playground Express
- Either: 3.3V compatible Ready-Made MIDI Module; Or:
- H11L1 (or 6N138) optoisolator
- 1N914 diode
- 1x 220Ω resistor
- 1x 470Ω resistor
- 4.7k resistor (for 6N138)
- 5 pin din socket
- Breadboard or stripboard and jumper wires
If you are using a ready-made module, naturally you can skip this part. This DIY circuit is basically the same circuit as used for 5V microcontrollers.
Version 1 of the circuit used a 6N138 as that is what I had around at the time. The second version uses the H11L1 which seems to be preferred for 3V operation. Here are a few things to note:
- First, the actual MIDI side is independent of the supply voltage for the microcontroller. The MIDI specification for a MIDI IN deliberately isolates the MIDI side from the processing side, so the MIDI side (a 220Ω resistor and diode) is the same as 5V operation.
- I’ve seen advice to avoid the 6N138 for 3.3V operation, even though a lot of circuits on the Internet seem to still suggest it can be used, and recommendations to use the H11L1 (or H11L2) instead. Using these parts will also save a resistor!
- Some optoisolators might work with a 3.3V power supply, but many 6N138 circuits seem to assume a 5V supply even if 3.3V logic is used. It hasn’t been clear to me from reading datasheets why this is the case – I’ve seen data sheets that talk of a “detector supply voltage” from -0.5V to 7V… So, note:
- My own 6N138 does indeed seem to work fine powered from a 3.3V supply – i.e. both the orange and red in the frist circuit connected to 3.3V – which means I only need one power link to the MIDI board.
- I am not an electronics person, so “official” circuits (i.e. those in products) seem to prefer the H11L1.
- Feel free to see how you go if you wish, but remember the warning!
- The connection to the RX pin needs to be pulled HIGH to 3.3V whereas for 5V microcontrollers this would typically be pulled HIGH to 5V.
- If you get things wrong and end up putting 5V into your 3.3V IO pin you will damage your board, so once again, remember the warning!
There is a great explanatory tutorial of how the circuit works at Notes and Volts. This is described for 5V operation and an Arduino, but the principles are exactly the same here.
They key feature we need to know here is that when the optoisolator is inactive (when TX is HIGH, i.e. logic state 1), the pin 6-RX junction is tied HIGH via the pull-up resistor to 3.3V. But when the optoisolator is active (logic 0 from the transmitter), the pin 6-RX junction is connected to pin 5 which thus ties the microcontroller RX Pin LOW. The critical part for this discussion is choosing the pull-up resistor as this is required to limit the current going through the optoisolator.
Typically, output current seems to often specify a maximum of tens of mA (you might need to check the data sheet for the exact make and type of optoisolator you are using, typical values for a 6N138 are around 60mA). Now, once again I have to say I am not an electronics person, but I believe this is the critical value here for us, so we need a pull-up resistor that keeps this current below this value. So far I’ve see values of 270Ω, 470Ω and 1k used here in circuits I’ve found whilst researching.
I went with 470Ω as I’ve seen it used on the official MIDI interface circuits for the Adafruit MIDI Feather Wing and the Teensy microcontroller (v3 onwards). With a 3.3V supply this means there is a current of around 7mA. We should remember that as well as not being too high for the optoisolator, this also has to be sourced via whatever is providing our 3.3V supply, which will probably be the microcontroller.
Here is how we connect this up to the Raspberry Pi Pico then using the “version 1 circuit”. Note this is using the second UART (UART 1, not UART 0) so it connected to the RX pin on GP7. I’m pulling 5V from the VBUS pin which will be 5V when USB powered (although as mentioned above, it looks like I could get away with just using the 3.3V supply with my 6N138). Remember, if you manage to get 5V connected to a 3.3V pin, you will break things!
And here is how to connect it up to the Adafruit Circuit Playground Express using version 1 of the circuit. Once again, 5V comes from the VOUT pin which, when USB powered, is 5V (again, mine also works with just the 3.3V supply).
There is a MIDI handler library for CircuitPython, but not for Micropython. However, at the time of writing the Circuit Python release for the Raspberry Pi Pico still doesn’t support the hardware serial port (UART).
Raspberry Pi and Micropython MIDI
I’ve implemented a simple MIDI receiver that can support the idea of MIDI Running Status, using the algorithm described here: http://midi.teragonaudio.com/tech/midispec/run.htm
The basic idea is that I poll the UART using the uart.any() function and if there is data read it one byte at a time and pass it off to a MIDI handler function. uart.read() returns a byte object, but we are only reading one byte (length = 1), so just pass the first one into the doMidi function.
while True: if (uart.any()): doMidi(uart.read(1))
The doMidi function then runs through the algorithm from the above site:
- Buffer is cleared (ie, set to 0) at power up.
- Buffer stores the status when a Voice Category Status (ie, 0x80 to 0xEF) is received.
- Buffer is cleared when a System Common Category Status (ie, 0xF0 to 0xF7) is received.
- Nothing is done to the buffer when a RealTime Category message is received.
- Any data bytes are ignored when the buffer is 0.
Once it has a complete noteOn or noteOff message it calls one of the doMidiNoteOn or doMidiNoteOff handlers to act accordingly. In my case I turn the LED on for noteOn messages and turn it off for noteOff messages. They also print to the console.
Find it on GitHub here as SimpleMidiMonitor.py.
Adafruit CPX And Circuit Python MIDI
Circuit Python has a MIDI library provided by Adafruit as part of their Circuit Python Bundle which can be downloaded from here. This supports MIDI sending and receiving. The resulting ZIP file must be expanded and the required libraries copied over to your CircuitPython device, in this case, copying the lib\adafruit_midi folder over to the lib folder on the CPX.
All the examples for the MIDI library seem to assume a USB MIDI connection, but using the library with the serial port is a relatively simple matter (once you’ve worked out the syntax) of passing in an object that implements a read(len) function as the midi_in parameter. In this case, that means the object returned from a busio.UART call.
The only quirk is that with no further instruction, the MIDI handling is very slow as the uart.read() function will block by default with a timeout of 1 second. The way to make it more responsive is to specify a much shorter timeout. In the example below, I’m using 0.001S or 10mS.
Like the Pi/MicroPython version I use the LED to show note On and note Off events, and print the received message to the console.
import board import digitalio import busio import adafruit_midi from adafruit_midi.note_off import NoteOff from adafruit_midi.note_on import NoteOn led = digitalio.DigitalInOut(board.D13) led.direction = digitalio.Direction.OUTPUT uart = busio.UART(tx=board.TX, rx=board.RX, baudrate=31250, timeout=0.001) midi = adafruit_midi.MIDI(midi_in=uart) while True: msg = midi.receive() if (isinstance(msg, NoteOn)): led.value = True print ("Note On: \t",msg.note,"\t",msg.velocity) if (isinstance(msg, NoteOff)): led.value = False print ("Note Off:\t",msg.note,"\t",msg.velocity)
I still find it a challenge that there are two environments for these boards, and that there seems to be very little cross-over at present between them. I guess when the Circuit Python support for the Raspberry Pi Pico finally catches up with the CPX version then that might start to gain the edge over MicroPython. I do need to go and check out Adafruit Blinka… that may well eventually hold the key…
At least in hardware terms, I now have a MIDI IN and MIDI OUT for my Pico and CPX. The next stage in the hardware development will be to create some kind of plug-in-able “shield” type board for MIDI support.
Software wise, it would be good to do something a little more interesting than just flash the LED!