skip navigational linksPJRC
Shopping Cart Checkout Shipping Cost Download Website
Home MP3 Player 8051 Tools All Projects PJRC Store Site Map
You are here: Teensy Teensyduino USB MIDI

PJRC Store
Teensy 3.2, $19.80
Teensy LC, $11.65
Teensy 2.0, $16.00
Teensy++ 2.0, $24.00
Main Page
Teensy 3.2 / 3.1
Getting Started
How-To Tips
Code Library


When you select "MIDI" from the Tools->USB Type menu, the Teensy becomes a USB MIDI (Musical Instrument Digital Interface) device, capable of sending and receiving MIDI messages.

MIDI can be used for non-music applications, such a controlling a large number of lights from a PC. Environments like Puredata and Max/MSP can give you access to MIDI for almost any purpose. MIDI is optimized to simultaneously transmit and receive large numbers of short messages with minimal delay.

Teensyduino implements a "class compliant" MIDI device, which can work with the built-in drivers on all major operating systems.

Transmitting Messages

These functions allow you to transmit all of the standard MIDI messages.

  usbMIDI.sendNoteOn(note, velocity, channel)
  usbMIDI.sendNoteOff(note, velocity, channel)
  usbMIDI.sendPolyPressure(note, pressure, channel)
  usbMIDI.sendControlChange(control, value, channel)
  usbMIDI.sendProgramChange(program, channel)
  usbMIDI.sendAfterTouch(pressure, channel)
  usbMIDI.sendPitchBend(value, channel)
  usbMIDI.sendSysEx(length, array)

MIDI messages are grouped together into USB packets. Up to 16 messages can transmit at once! They are held for a brief time, not more than 1 ms, to facilitate grouping. You can use the send_now() function to immediately allow any buffered messagse to transmit


USB bandwidth is shared among devices. Usually there is very little delay, unless other USB devices are using all the available USB bandwidth.

Transmit Examples

Philip Cunningham's MIDI Controller reads 6 knobs and sends Control Change messages.

Arcade Button MIDI Controller (instructables)

This simple sketch shows how to use the Bounce library together with MIDI. A longer example can be found in File > Examples > Teensy > USB_MIDI > Buttons.

#include <Bounce.h>  // Bounce library makes button change detection easy
const int channel = 1;

Bounce button1 = Bounce(1, 5);  // 5 = 5 ms debounce time
Bounce button2 = Bounce(2, 5);  // which is appropriate for good
Bounce button3 = Bounce(3, 5);  // quality mechanical pushbuttons
void setup() {
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);

void loop() {
  // Note On messages when each button is pressed
  if (button1.fallingEdge()) {
    usbMIDI.sendNoteOn(60, 99, channel);  // 60 = C4
  if (button2.fallingEdge()) {
    usbMIDI.sendNoteOn(61, 99, channel);  // 61 = C#4
  if (button3.fallingEdge()) {
    usbMIDI.sendNoteOn(62, 99, channel);  // 62 = D4
  // Note Off messages when each button is released
  if (button1.risingEdge()) {
    usbMIDI.sendNoteOff(60, 0, channel);  // 60 = C4
  if (button2.risingEdge()) {
    usbMIDI.sendNoteOff(61, 0, channel);  // 61 = C#4
  if (button3.risingEdge()) {
    usbMIDI.sendNoteOff(62, 0, channel);  // 62 = D4

  // MIDI Controllers should discard incoming MIDI messages.
  while ( {

Transmit Issues

When using MIDI-OX on Windows, together with a sketch that transmits but never receives, open only the input device. From the menu Options > MIDI Devices, select "USB Audio Device" in the MIDI Inputs box. Do NOT select "USB Audio Device" in the MIDI Outputs box. Uncheck the "Automatically attach Inputs to Outputs during selection" box, then click OK.

Receiving Messages with Read & Callback Functions

The easiest way to receive MIDI messages is with functions that are automatically called when each type of message is received. Usually you would create one or more of these:

  void OnNoteOn(byte channel, byte note, byte velocity)
  void OnNoteOff(byte channel, byte note, byte velocity)
  void OnVelocityChange(byte channel, byte note, byte velocity)
  void OnControlChange(byte channel, byte control, byte value)
  void OnProgramChange(byte channel, byte program)
  void OnAfterTouch(byte channel, byte pressure)
  void OnPitchChange(byte channel, int pitch)

For each function you create, you must use the corresponding "setHandle" functions to tell usbMIDI to call it when that message type is read.


Once everything is set up, you then need the "read" function to actually read data. The callback functions are only called when you use read. There are 2 choices for read:;         // All Channels

If you give a specific channel number to read, messages to other channels are ignored, which is easier than having to check the channel number in your callback function.;  // One Specific Channel (1 to 16)

Here is a simple example which controls a LED by MIDI note messages:

// USB MIDI receive example, Note on/off -> LED on/off
// contributed by Alessandro Fasan

int ledPin = 13;

void OnNoteOn(byte channel, byte note, byte velocity)
  digitalWrite(ledPin, HIGH);

void OnNoteOff(byte channel, byte note, byte velocity)
  digitalWrite(ledPin, LOW);

void setup()
  pinMode(ledPin, OUTPUT);
  usbMIDI.setHandleNoteOn(OnNoteOn) ;
  digitalWrite(ledPin, HIGH);
  digitalWrite(ledPin, LOW);

void loop()

Receiving Messages with Read & Query Functions

Instead of callback functions, you can also check the return value from read(). When it returns true, a new message has arrived. The "get" functions allow you to access information about the most recently received MIDI message.

The types are: 0 = Note Off, 1 = Note On, 2 = Velocity Change, 3 = Control Change, 4 = Program Change, 5 = After Touch, 6 = Pitch Bend, 7 = System Exclusive

The getChannel function gives you the channel number, from 1 to 16. If you requested only a specific channel when calling read, there is no need to use this.
The actual message data can be accessed with getData1 and getData2.
For system exclusive messages, the message data is available as an array. Use getData1 to find the number of bytes stored in the array.

Connecting Many Buttons & Knobs/Sliders

Often MIDI controllers need more pushbuttons and knobs or sliders than can be connected directly. The inexpensive 74HC4051 or 74HCT4051 chip can be used to connect 8 inputs to one pin. It works for analog or digital signals. Three digital pins are needed to control the 74HC4051, but those same 3 pins can control many chips.

8X Input Expansion with 74HC4051 Chips

To use these 74HC4051 chips, first select the desired channel using the 3 control pins. Then wait a brief time for the signals to propagate through the chips. 50 µs is plenty. Then you can read the pins. This process must be repeated 8 times to read all the signals.

  // select 74HC4051 channel 5 (of 0 to 7)
  digitalWrite(1, HIGH);
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  // allow 50 us for signals to stablize
  // read the signals routed to pins 10, 19, 20
  // through channel 5 of each 74HC4051 chip
  knobPin19channel5 = analogRead(19);
  knobPin20channel5 = analogRead(20);

A delay is required between 74HC4051 change and analogRead(). Any extra capacitance can greatly increase the required delay. This forum thread shows how to calculate the delay, and what problems to expect if the delay is not long enough.

For newer Teensy boards with 3.3V signals, 74HCT4051 chips are best. Power the 74HCT4051 from 5V. If 74HC4051 (without the "T") are used, they should be powered by 3.3V when used with a Teensy that has 3.3V signals, or by 5V when used with a Teensy having 5V signals.

Working with Noisy Analog Readings

Ideally analog data would be noise-free, but in a large and complex MIDI controller with many long wires, often some noise is unavoidable. Hysteresis can be used when converting from 10 to 7 bit data to reject noise.

TODO: examples using digital low-pass filtering and other techniques...

Awesome MIDI Projects

Using force sensitive resistors (FSR) for velocity sensitive notes with poly aftertouch.

Footsy - foot pedal controller