The Arduino MIDI Library: send and receive notes over serial
The MIDI Library from FortySevenEffects is how an Arduino becomes a MIDI instrument or controller: it builds and parses MIDI 1.0 messages (note on and off, control change, program change, pitch bend, SysEx) on a serial port running at MIDI鈥檚 31250 baud, through a 5-pin DIN socket with an optocoupler on the input side.
Built in on Mokxi: #include <MIDI.h> and it builds, with nothing to install. Free, and no account needed.
What MIDI Library does
MIDI_CREATE_DEFAULT_INSTANCE() makes an object called MIDI on the board鈥檚 first serial port. On an Uno that is the same Serial the USB monitor uses, so a MIDI sketch and Serial.print() debugging cannot share it; boards with a second port use MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI) instead.
Reading works through handlers. You register functions such as setHandleNoteOn(), call MIDI.read() on every pass of loop(), and the library calls the right handler for each message. By default begin() also turns on Thru, echoing everything that comes in back out, which surprises people whose controller suddenly doubles every note.
| Library Manager name | MIDI Library |
|---|---|
| Author | Francois Best, lathoub |
| Include | #include <MIDI.h> |
| Boards | every board Mokxi compiles for but ATtiny85 |
| In the simulator | Runs, with a gap the note below names. |
Install it
It builds on every board Mokxi compiles for but ATtiny85.
In Mokxi it is built in: #include <MIDI.h> and the sketch compiles, with no download, no zip and no Library Manager.
On a real board, install "MIDI Library" by Francois Best, lathoub from the Arduino IDE鈥檚 Library Manager (Sketch, Include Library, Manage Libraries), or add MIDI Library to lib_deps in PlatformIO. Mokxi follows the calls of release 5.0.2, so a sketch written here builds there too.
What to know in the simulator: There is no MIDI instrument part, so the bytes reach the serial monitor raw. A transport class of the sketch鈥檚 own works as upstream.
ESP32-C3 路 Arduino Uno 路 Arduino Nano 路 Arduino Mega 2560 路 Arduino Leonardo 路 SparkFun Pro Micro 路 Arduino Micro 路 ESP32-C6 路 Raspberry Pi Pico 路 Raspberry Pi Pico W 路 Seeed XIAO SAMD21 路 STM32 路 STM32 Nucleo-F411RE 路 STM32 Blue Pill 路 micro:bit V2 路 Arduino Uno R4 路 ESP32 DevKit 路 ESP32-S3-DevKitC-1 路 ESP8266 NodeMCU 路 Arduino Pro Mini 3.3 V 路 Seeed XIAO ESP32-C3 路 ESP32-C3 SuperMini 路 Seeed XIAO ESP32-C6 路 ESP32-DevKitC V4 路 LOLIN32 Lite 路 Wemos D1 R32 路 Cheap Yellow Display 路 TTGO T-Display 路 Seeed XIAO ESP32-S3 路 Arduino Nano ESP32 路 Arduino Nano RP2040 Connect 路 Seeed XIAO RP2040 路 Waveshare RP2040-Zero 路 Arduino Uno R4 WiFi
The calls you will use
In the order a sketch meets them.
MIDI_CREATE_DEFAULT_INSTANCE()
Declares the MIDI object on the default serial port. Put it at the top of the sketch, outside any function.
MIDI.begin(MIDI_CHANNEL_OMNI)
Starts the port at 31250 baud and listens on every channel. Pass 1 to 16 to listen on one channel only.
MIDI.sendNoteOn(60, 127, 1)
Sends middle C at full velocity on channel 1. Follow it with sendNoteOff(60, 0, 1) or the note hangs.
MIDI.sendNoteOn(60, 127, 1);
delay(500);
MIDI.sendNoteOff(60, 0, 1);MIDI.sendControlChange(7, analogRead(A0) / 8, 1)
Sends a control change, here controller 7 (volume) from a potentiometer scaled to 0 to 127.
MIDI.sendProgramChange(5, 1)
Asks the receiving synth to switch to patch 5 on channel 1.
MIDI.setHandleNoteOn(handleNoteOn)
Registers void handleNoteOn(byte channel, byte pitch, byte velocity), called from read() for each incoming note.
MIDI.read()
Reads what has arrived, runs the matching handler and returns true when a message was read. Call it on every pass of loop().
MIDI.turnThruOff()
Stops echoing incoming messages to the output, which begin() turns on by default.
Example: MIDI messages sent and read back through a loopback transport
On the Arduino Uno R3. It runs in the browser exactly as printed.
#include <MIDI.h>
// A transport of the sketch's own: what it writes, it reads back.
struct Loopback {
static const bool thruActivated = false;
uint8_t buffer[64];
uint8_t head = 0, tail = 0;
void begin() {}
bool beginTransmission(midi::MidiType) { return true; }
void write(byte value) { buffer[tail++ % 64] = value; }
void endTransmission() {}
byte read() { return buffer[head++ % 64]; }
unsigned available() { return (uint8_t)(tail - head); }
};
Loopback loopback;
midi::MidiInterface<Loopback> MIDI(loopback);
void handleNoteOn(byte channel, byte note, byte velocity) {
Serial.print("Note On ");
Serial.print(note);
Serial.print(" velocity ");
Serial.print(velocity);
Serial.print(" channel ");
Serial.println(channel);
}
void handleControlChange(byte channel, byte number, byte value) {
Serial.print("CC ");
Serial.print(number);
Serial.print(" = ");
Serial.print(value);
Serial.print(" channel ");
Serial.println(channel);
}
void handlePitchBend(byte channel, int bend) {
Serial.print("Bend ");
Serial.print(bend);
Serial.print(" channel ");
Serial.println(channel);
}
void setup() {
Serial.begin(9600);
MIDI.setHandleNoteOn(handleNoteOn);
MIDI.setHandleControlChange(handleControlChange);
MIDI.setHandlePitchBend(handlePitchBend);
MIDI.begin(MIDI_CHANNEL_OMNI);
MIDI.sendNoteOn(60, 100, 1);
MIDI.sendControlChange(midi::ChannelVolume, 90, 2);
MIDI.sendPitchBend(-4096, 3);
MIDI.sendNoteOn(64, 0, 1); // velocity 0: read as a Note Off
}
void loop() {
if (MIDI.read() && MIDI.getType() == midi::NoteOff) {
Serial.print("Note Off ");
Serial.println(MIDI.getData1());
}
}Common errors and how to fix them
The serial monitor shows garbage once MIDI.begin() runs
MIDI takes over the serial port at 31250 baud, which the monitor cannot show as text. Debug on a second port, or use a USB MIDI board such as the Leonardo with MIDIUSB, or a serial to MIDI bridge on the computer.
Every note is played twice, or a loop of notes builds up
Thru is on by default and echoes incoming messages back out, which a DAW then sends back again. Call MIDI.turnThruOff() after begin() unless you want the echo.
Handlers never run
MIDI.read() must be called often, on every pass of loop(), with no long delay(). Also check that begin() listens on the channel the sender uses, or use MIDI_CHANNEL_OMNI.
Keep going
More communication libraries
Try MIDI Library without installing anything
Open the example, change the sketch, and keep your version in a free account.