MIDI Library (MIDI.h) - Issues with receiving MIDI inputs

Initially, I wrote this code for the Arduino UNO, and it worked by reading MIDI inputs from my digital piano and executing the conditional functions correctly (I sent MIDI inputs using the Hairless MIDI program (The Hairless MIDI<->Serial Bridge).

image

However, the Arduino UNO had limitations in reading multiple notes pressed simultaneously. Therefore, I decided to use the Arduino Leonardo. But with the same code, it doesn't read the MIDI inputs. How can I make it read? Do I need to change the code or use another library?

My Code:

#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();

void setup() {
  MIDI.begin();
  Serial.begin(9600);
  Serial.setTimeout(10);
}

void loop() {
  if (MIDI.read()) {
    //My functions, but this part doesn't matter because it works correctly.
  }
}

If I've missed any important information, please let me know.

What limitations did you encounter ? Can you post the UNO code ?

On a leonardo, the Serial port has native USB, so you can actually turn it into a USB midi device. For receiving Midi from your digital piano, you can connect to the hwSerial 1 (Serial1) on pins 0 & 1 instead of any swSerial you are using on the UNO.
This is of course the limitation that you may run into on an UNO, but before that causes issues you will need to play an insane amount of notes at the default 115200 baudrate that is supported by hairless, don;t use 9600.

But anyway, if you switch to the Leonardo, you should use "MIDIUSB.h" and you can use this as a guide on how the functions are implemented. (again you don't need hairless anymore)

#include "MIDIUSB.h"

#define LED 3  // that pin is PWM capable and has no extra function
#define POT A2
#define CHANNEL (7 - 1)  // midi channel 6 equals 5 in this system.

// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, (uint8_t) (0x90 | channel), pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
  MidiUSB.flush();
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, (uint8_t) (0x80B | channel), pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
  MidiUSB.flush();
}

void setup() {
  pinMode(LED, OUTPUT);
  analogWrite(LED, 120);
  delay(1000);
  analogWrite(LED, 0);
}

// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, (uint8_t) (0xB0 | channel), control, value};
  MidiUSB.sendMIDI(event);
  MidiUSB.flush();
}


void loop() {
  static uint32_t moment = millis();
  //static bool noteon = true;
  static uint8_t volume = 0;
  uint8_t analog = analogRead(POT) >> 3;
  if ((volume != analog) && (millis() - moment > 20)) {
    moment = millis();
    volume = analog;
    controlChange(CHANNEL, 7, volume);
  }

  /*if (millis() - moment > 2000) {
    moment = millis();
    if (noteon) noteOn(0, 48, 100);  // pin C2
    else noteOff(0, 48, 64);
    noteon = !noteon;
    }*/

  midiEventPacket_t rx = MidiUSB.read();
  while (rx.header != 0) {    
    uint8_t type = rx.byte1 >> 4;
    uint8_t channel = rx.byte1 & 0xF;
    uint8_t value1 = rx.byte2;
    uint8_t value2 = rx.byte3;
    if (type == 0x9) analogWrite(LED, value2 * 2);
    if (type == 0x8) analogWrite(LED, 0);
    rx = MidiUSB.read();
  }

  //controlChange(0, 10, 65); // Set the value of controller 10 on channel 0 to 65
}

For the incoming signals on Serial1 you can create a midi instance like this

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, midi);

and then all you need to do is combine them

I agree that swSerial is not the best for receiving and transmitting Midi, but it should not be an issue when you are playing notes on a piano, more so when receiving and forwarding CC messages and midi sync.

The limitation I found was that the Arduino UNO cannot read very well when more than one note is pressed at the same time. For instance, when I play a chord of 3 or more notes, some notes are not registered by it. The code I posted is what I used and it worked on the UNO; now, I was trying to run it on the Leonardo.

So, if I use this library, will the Arduino Leonardo receive MIDI messages directly through the USB cable that connects it to my PC? Because my piano sends MIDI messages through a cable like this

and then the message was sent to the Arduino through Hairless.

No i don't think so, that means i am not a 100% sure what you mean, but if your piano connects to your PC through a USB cable, what do you need the Arduino for ?

I do not have that issue at all ! but then i think hairless should be run at 115200bps and not at 9600bps what you are doing, but it means basically that your code (which you didn't post at all) is most likely faulty.

Well it does matter, because it clearly doesn't work correctly.

For one thing, if you forward a message and the incoming speed (31250) is higher than the outgoing speed (9600) you will get into trouble. Of course the other issue is that you are making use of swSerial for reception, and it is possible that is part of the problem, but just a few chords playing should not be the point that this issue shows up.

I also prefer to us a hardware UART for reception, those are way more reliable and seldom have issues.

Before i give anymore advice you need to show me how you have wired things up with the UNO and how you plan to do it with the Leonardo. And mainly how are you connecting the Piano to the Arduino ! i get that you connect the Arduino thru a USB cable to the PC, but how atre you connecting the Piano to the Arduino and what do you intend to use the Arduino for specifically.

I said it doesn't matter, because what's inside should be executed as soon as it receives any MIDI message; it could display any message, and it's unrelated to MIDI. However, it doesn't even seem to execute this function because apparently it's not receiving the messages. But here's an example of a code that worked on the Arduino UNO: essentially, if it receives a MIDI message, it should turn on LED '55' on my strip, what happens immediately upon connecting my piano through Hairless to the Arduino UNO, as the piano constantly emits MIDI messages.

image

// Led Imports
#include <FastLED.h>
#define LED_PIN 7 //Led strip connected to pin 7
#define NUM_LEDS 60 // number of leds that will be used
CRGB leds[NUM_LEDS];

//MIDI Imports
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();

void setup() {
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);

  MIDI.begin();
  Serial.begin(9600);
  Serial.setTimeout(10);
}

int r, g, b;
void setColors() {
  r = 255;
  g = 0;
  b = 0;
}
void loop() {
  setColors();
  if (MIDI.read()) { // if receive a midi message
        leds[55] = CRGB(r, g, b); // then light up the led in position "55"
       FastLED.show();
  }
}

The only thing I did was replace the Arduino UNO with the Leonardo and try to run it in the same way. It lights up the LED correctly if I use only the commands from the FastLED library, but I can't make it receive MIDI inputs (I managed to make it send inputs, but that's not relevant to me).

I'll try my best to explain how I configured this.

So basically, my piano should send the MIDI message to my computer; then, Hairless will route the MIDI from the Piano to the Arduino board. The Arduino board will read this and send the message to the strip to light up the LED

This does not do what you think it does on a Leonardo:

You should instantiate the MIDI interface yourself, e.g.

MIDI_CREATE_INSTANCE(decltype(Serial), Serial,  MIDI);

It is receiving the message, or at least it would be if you would not send a ledstrip signal straight away, which turns off interrupts and disable any message from coming in from that moment onwards.

You could have saved us both quite a bit of time by including the function.

And when it does so it, it disables the reception of any midi messages for the duration of the ledstrip transmission.
With this in mind, the leonardo is not going to work for you either, it is not going to make any difference.

However what you want to do can be done on an ESP32 or possibly a Teensy. I can do it on an ESP32. I can receive the midi from hairless and transmit the ledstrip signal thru I2S in the background using the Neopixelbus library.

i guess it could also be done on a nodeMCU, but using a UART method on TX1 (GPIO 2) also with Neopixelbus.

On an AVR i do n't think it can be done, being that the only way to transmit the signal is bit-banged, which disables interrupts during which you can not receive any midi bytes (and only a 1 byte FIFO)

The Leonardo will not fix this, but again, had you shared you r code initially completely i could have told you this already.

Yeah i got it. Okay, you need a different board, a 30-pin ESP32s devkit would be my choice.

You could also try and re-write the code to first receive multiple midi commands before starting to execute them, but there will always be a lapse in which reception will just not happen, and that may always be a moment when you play another note.

You can easily prove that you are actually receiving the notes, by commenting out

FastLED.show();

and instead ping the builtin LED for every midi command received (or turn it on when a command is received and turn it off when no command has been received for a period of time.

OMG!! that solved all my problems!! thank you very much!! also Merry Christmas!!

1 Like

The other comment solved my problem, but thank you so much for taking time to try to help me with this!! Merry Christmas!!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.