Problem Receiving SysEx Messages

Hello all,

This is my first Arduino project and first time posting and just so you understand, I’m not a programmer, more of a musician!

So here’s where I need help:

I’m working on a MIDI controller to control a proprietary soft synth. I’m using a Mega 2560 R3 with a 74HC4067 multiplexer to control my 22 buttons and 24 pots matching the same on the soft synth UI and I got them to work perfectly. I also have 3 LEDs matching the 3 LEDs on the synth.

That’s where it gets more difficult for me...
One of the 22 buttons is called Group and it controls two LEDs called Group A and Group B.
A second button called Panel controls the third LED. When I launch the synth normally only Group A LED is on while Group B and Panel LEDs are off.

Now, Group A and Group B LEDs can turn On or Off without me pressing the Group button if for example I press the Program button to select a new sound and that sound has been programmed let’s say with only Group B. In that case Group A LED would turn Off and Group B would turn On.

That’s why the creator of the synth made it so the state of the LEDs is sent out from the synth as Sysex messages and not just from the pressing of the Group button. There are eight messages to cover the eight states that are possible:

SOX ID Sub Com Lng Data EOX - n = MIDI channel
—————————————————————————————-
F0 29 5n 10 01 00 F7 - all 3 LEDs off
F0 29 5n 10 01 01 F7 - Group A LED on
F0 29 5n 10 01 02 F7 - Group B LED on
F0 29 5n 10 01 03 F7 - Group A + Group B LEDs on
F0 29 5n 10 01 04 F7 - Second Panel LED on
F0 29 5n 10 01 05 F7 - Group A + Second Panel LED on
F0 29 5n 10 01 06 F7 - Group B + Second Panel LED on
F0 29 5n 10 01 07 F7 - Group A + Group B + Second Panel LED on

Since these are static Sysex messages it should be fairly easy to create an if or a case statement to listen to the message that comes in, do a compare and match and turn on the appropriate LEDs.

I can’t for the life of me figure it out. Here’s the code that I’m playing with:


//**************** My Code *********************
//******Only SysEx Portion of the Code *********

#include <Control_Surface.h>

#define ledA 48
#define ledB 49
#define ledP 50

uint8_t sysex1[] = {0xF0, 0x29, 0x51, 0x10, 0x01, 0x00, 0xF7}; // all 3 LEDs off
uint8_t sysex2[] = {0xF0, 0x29, 0x51, 0x10, 0x01, 0x01, 0xF7}; // Gr A LED on
uint8_t sysex3[] = {0xF0, 0x29, 0x51, 0x10, 0x01, 0x02, 0xF7}; // Gr B LED on
uint8_t sysex4[] = {0xF0, 0x29, 0x51, 0x10, 0x01, 0x03, 0xF7}; // Gr A + B LEDs on
uint8_t sysex5[] = {0xF0, 0x29, 0x51, 0x10, 0x01, 0x04, 0xF7}; //  2nd Panel LED on
uint8_t sysex6[] = {0xF0, 0x29, 0x51, 0x10, 0x01, 0x05, 0xF7}; // Gr A + 2nd Panel LED on
uint8_t sysex7[] = {0xF0, 0x 29, 0x 51, 0x10, 0x01, 0x06, 0xF7}; // Group B + Second Panel LED on
uint8_t sysex8[] = {0xF0, 0x 29, 0x 51, 0x10, 0x01, 0x07, 0xF7}; // Group A + Group B + Second Panel LED
 
// Instantiate the MIDI over USB interface:
USBMIDI_Interface midi; 
 
// Custom MIDI callback that actions incoming SysEx messages:
struct MyMIDI_Callbacks : MIDI_Callbacks {
 public:
  // This callback function is called when a SysEx message is received:
  void onSysExMessage(Parsing_MIDI_Interface &midi) override {
    // Get the message from the MIDI interface:
SysExMessage sysex = midi.getSysExMessage();

void setup() {
  Serial.begin(115200);
  midi.begin();
  midi.setCallbacks(callback);
pinMode(ledA, OUTPUT);
pinMode(ledB, OUTPUT);
pinMode(ledP, OUTPUT);
}

void loop(){
If (sysex = = uint8_t sysex1){
     digitalWrite(ledA, LOW);
     digitalWrite(ledB, LOW);
     digitalWrite(ledP, LOW);
   } 
   else If (sysex = = uint8_t sysex2){
     digitalWrite(ledA, HIGH);
     digitalWrite(ledB, LOW);
     digitalWrite(ledP, LOW);
   }
   else If (sysex = = uint8_t sysex3){
     digitalWrite(ledA, LOW);
     digitalWrite(ledB, HIGH);
     digitalWrite(ledP, LOW);
   }
   else If (sysex = = uint8_t sysex4){
     digitalWrite(ledA, HIGH);
     digitalWrite(ledB, HIGH);
     digitalWrite(ledP, LOW);
   } 
   else If (sysex = = uint8_t sysex5){
     digitalWrite(ledA, LOW);
     digitalWrite(ledB, LOW);
     digitalWrite(ledP, HIGH);
   }
   else If (sysex = = uint8_t sysex6){
     digitalWrite(ledA, HIGH);
     digitalWrite(ledB, LOW);
     digitalWrite(ledP, HIGH);
   }
   else If (sysex = = uint8_t sysex7){
     digitalWrite(ledA, LOW);
     digitalWrite(ledB, HIGH);
     digitalWrite(ledP, HIGH);
   }
   else If (sysex = = uint8_t sysex8){
     digitalWrite(ledA, HIGH);
     digitalWrite(ledB, HIGH);
     digitalWrite(ledP, HIGH);
   }
}

// Read incoming MIDI data and call the callback if a new
 // SysEx message has been received.
  midi.update();
}

I must be missing something, there’s something I don’t understand so any help would be very much appreciated!

Then...I have to do the same with the 40 x 2 LCD whose information is also sent out via SysEx!

I'm no expert in Arduino code (more of a MIDI guy), but it appears to me that the decisions toward the end say:

" If (sysex = = uint8_t sysex3"

and I wonder if the syntax should be if (sysex == sysex3) {
My impression is that the compare is between the sysex data and the data sysex[n], so having the uint8_t might be mucking that up somehow. Early in the software I understand that uint8_t is assigning an unsigned integer, and that's not part of the variable name. Or you are using some other language and I am totally clueless.

It might be the formatting shown, but I don't believe there should be a space betweent the two ==.

Another possibility worth looking at is verifying the length of the data strings. Is one or the other really the same number of bytes to compare? They are really all identical except for the 6th byte, which is a direct match of the LEDs and the bits.

Hi RDeangelis,

Yes there might be something to it, I will double check the syntax and try again tomorrow morning by removing the uint8_t and see what comes.

For the length of the data string, all 8 of them are exactly the same except as you said the 6th byte which is the one related to the 8 LED options.

It looks like you don't finish the declaration of "MyMidiCallbacks" before you start defining the setup() function. That can't be right! You need two '}''s and a ';' to close the declaration before you can start defining a function.

// Custom MIDI callback that actions incoming SysEx messages:
struct MyMIDI_Callbacks : MIDI_Callbacks
{
  public:
    // This callback function is called when a SysEx message is received:
    void onSysExMessage(Parsing_MIDI_Interface &midi) override
    {
      // Get the message from the MIDI interface:
      SysExMessage sysex = midi.getSysExMessage();

      void setup()
      {
        Serial.begin(115200);
        midi.begin();
        midi.setCallbacks(callback);
        If (sysex = = uint8_t sysex1)
        {

The 'if' keyword is spelled 'if', not 'If'.
You can't compare arrays with '==' (which is spelled '==', not '= =').
Inserting 'uint8_t' there is a syntax error.
Since all of your messages are the same except for byte 5 you can check the first 5 and last 1. Then parse byte 5:

  digitalWrite(ledA, (sysex[5] & 1) != 0);
  digitalWrite(ledB, (sysex[5] & 2) != 0);
  digitalWrite(ledP, (sysex[5] & 4) != 0);

Try this:

#include <Control_Surface.h>

const pin_t ledA = 48;
const pin_t ledB = 49;
const pin_t ledP = 50;

const uint8_t sysex_prefix[] {0xF0, 0x29, 0x51, 0x10, 0x01};
const uint16_t sysex_len = 7; // expected length of the full sysex message
const uint16_t sysex_led_idx = 5; // byte of the message containing the LED states
const uint8_t ledA_mask = 0x01; // mask that selects the bit with the ledA state
const uint8_t ledB_mask = 0x02;
const uint8_t ledP_mask = 0x04;

bool sysex_matches(SysExMessage sysex) {
  return sysex.length == sysex_len &&                      // First check the length of the message,
         std::equal(std::begin(sysex_prefix), std::end(sysex_prefix), sysex.data); // then the data.
}

// Custom MIDI callback that updates the LEDs:
struct MyMIDI_Callbacks : MIDI_Callbacks {

  // This callback function is called whenever a SysEx message is received:
  void onSysExMessage(Parsing_MIDI_Interface &midi) override {
    // Get the message from the MIDI interface:
    SysExMessage sysex = midi.getSysExMessage();
    // Check if it matches the LED update SysEx message we expect:
    if (sysex_matches(sysex)) {
      // Turn on the right LEDs:
      const uint8_t led_data = sysex.data[sysex_led_idx];
      digitalWrite(ledA, led_data & ledA_mask ? HIGH : LOW);
      digitalWrite(ledB, led_data & ledB_mask ? HIGH : LOW);
      digitalWrite(ledP, led_data & ledP_mask ? HIGH : LOW);
    }
  }

};

// Instantiate the MIDI over USB interface:
USBMIDI_Interface midi;
// Instantiate the MIDI callbacks:
MyMIDI_Callbacks callback;

void setup() {
  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledP, OUTPUT);
  Serial.begin(115200);
  midi.begin();
  midi.setCallbacks(callback);
}

void loop() {
  // Read incoming MIDI data and call the callback if a new
  // SysEx message has been received.
  midi.update();
}

Keep in mind that the Arduino Mega does not support MIDI over USB natively, so when you instantiate a USBMIDI_Interface, this will be a standard MIDI interface on the Serial port (TX/RX pins) at 31,250 baud. Calling midi.begin() will undo your previous call to Serial.begin(115200).

If you want a MIDI interface on the Serial port which is connected to the USB interface at 115200 baud, use:

USBSerialMIDI_Interface midi { 115200 };

If you want a hardware MIDI interface on a specific serial port, e.g. Serial1, you can use:

HardwareSerialMIDI_Interface midi { Serial1, MIDI_BAUD };

The MIDI_BAUD parameter is optional, if you omit it, it'll use the standard MIDI baud rate.

1 Like

John, Pieter,

Thank you for you help. I've implemented Pieter's solution and though it compiles without errors, it's still not working. I've checked all my connections and made sure my LEDs are working correctly.

I have a suspicion that the SysEx format we defined is not what's received. I have Midi-OX but I'm not sure how to set it up to monitor what's coming out of my soft synth. The synth is on my laptop as well as Midi-OX and when I try to open both at the same time, Midi-OX says there's not enough memory and to shut down some application.

So what I did is created a SysEx file using Notepad and entered the following: F02951100100F7.
Then I loaded the file in Midi-OX's SysEx View and this is what I got: 46 30 32 29 35 31 31 30 30 31 30 30 46 37 0D 0A.

I have to idea where these numbers come from though I can see that 46 is F and 30 is 0, etc.
Is it possible that Midi-OX is converting from 7bit to 8bit?

To convince yourself that the code I posted works, replace the line USBMIDI_Interface midi with USBDebugMIDI_Interface midi. Then open the Serial monitor at 115200 baud, set the line ending to “Newline”, end paste the following message:

F0 29 51 10 01 01 F7

Then press enter to send it to the Arduino, and LED A turns on.
If you send

F0 29 51 10 01 00 F7

LED A will turn off.

(I've just tried that myself, and it works as expected.)

You'll have to find a way to get the correct message from your synth. If Midi-OX doesn't work, you could try https://www.roxxxtar.com/apps/miditool/ in your browser.
How are you sending MIDI to the Arduino. Like I mentioned before, the Arduino Mega doesn't support MIDI over USB, so you cannot send MIDI messages to it directly. You either need a hardware MIDI to USB interface, or use a software tool such as Hairless. In both cases, you need to make sure that the baud rates are correct, otherwise the Arduino will receive garbage.

Those are the ASCII codes for the characters you entered in Notepad ('F' == 0x46, '0' == 0x30, and so on). SysEx is a binary format, you cannot create SysEx files using a text editor.

Is it possible that Midi-OX is converting from 7bit to 8bit?

Highly unlikely.


Edit: Are you sure that 0x51 means MIDI channel 1? Usually, MIDI channels are zero-based, i.e. 0x50 would be channel 1, and 0x51 would be channel 2.

OK, I will do some tests with your suggestions. And for midi, if "1" is channel 2 that could be the reason it's not working. Time for testing!

Ok so I plugged my laptop into my MIDI interface with a USB-to-5pin IN-OUT cable and the interface is plugged into my Mac. I opened Roxxxtar MIDITool and this is what I see when I press the Group button:
STATUS: 0xF0 System Exclusive
Channel: (4) 240, 41, 80, 247

In decimal that's F0, 29, 50, F7. We're on the right track but there's no message in the middle. When I press the Panel button I get the exact same, no data between F0-F7. I checked the CC messages and they show up no problem. I'll contact the creator and check with him to see why the data is no there. I'll be back...

BTW, Pieter was right, channel 1 is 0!

Hi Pieter,

I finally got in touch with the creator and as I suspected I didn't have the version with full Sysex implementation...but now I do!

In the new version there's actually a logging function that can be turned on and it captures all the MIDI messages going out of the synth, so now I can see all the Sysex codes in all their glory!
I'm gonna start testing the code and see how this works.

To answer your question about how I do MIDI with the Mega, once I upload the code, I reset it with DualMoco to get it MIDI compliant. It's a pain though because I have to reset it again to access the IDE and upload my code. Is there a better way to do this with the Mega? Is there a way to be able to upload codes and without resetting use MIDI to test the synth with my controller?

Get a USBasp programmer and 10-to-6-pin adapter from eBay or Amazon. Should be about $5 total from China. Probably under $10 from a more local supplier. Then you can select USBasp under Tools->Programmer and use Sketch->Upload Using Programmer. This is also an easy way to install firmware on the ATmega16u2.

Thank you so much for the tip! I will get me one right away.

Why get a separate programmer if you can just get a microcontroller with native USB support?

Well late last night I was thinking maybe I should buy a Teensy 4.0, until John suggested the programmer. What do you guys think of the Teensy?

They're great little boards, outstanding performance, and perfect for doing MIDI over USB.

Thanks for the info, I will look into this. In the meantime, I had a chance to try the code you gave me and it's not working, and I think it's because now that I can see what's going on, there are a lot of different messages going through which would need to be parsed properly and your code doesn't account for that.

I looked over the Sysex messages from the synth and here's what it looks like after doing the following sequence of button pressing:

MIDI CC
B0 5E 7F (Press Tuning button - HIGH)

SYSEX to update LCD Screen
F0 29 50 11 4B 6 0E 8 20 44 45 54 55 3A 33 20...F7

MIDI CC (Press Group button - HIGH)
B0 61 7F

SYSEX to turn on LED-B and turn off LED-A
F0 29 50 10 1 2 F7

SYSEX to update LCD Screen
F0 29 50 11 24 6 0E 0E 30 20 4D 4F 3A 31 20 4D...F7

MIDI CC (Group button - LOW)
B0 61 0

MIDI CC (Press Group button - HIGH)
B0 61 7F

SYSEX to turn on LED-A and keep LED-B on
F0 29 50 10 1 3 F7

SYSEX to update LCD Screen
F0 29 50 11 2 6 0E F7

MIDI CC (Group button LOW)
B0 61 0

MIDI CC (Press Group button - HIGH)
B0 61 7F

SYSEX to turn LED-B off and keep LED-A on
F0 29 50 10 1 1 F7

SYSEX to update LCD Screen
F0 29 50 11 24 6 0E 0E 33 20 4D 4F 3A 31 20 4D...F7

MIDI CC (Group button LOW)
B0 61 0

How would you modify your code to parse out the SYSEX for the LEDs?
This would give me a chance to study the code and try to implement the SYSEX parsing for the LCD.

Again thank you all for your help!

Did you try the Debug MIDI interface?

The code I posted should correctly ignore all MIDI messages except the one that changes the state of the LEDs.
Did you change the 0x51 to 0x50 in sysex_prefix?

Good morning! So I changed the 0x51 to 0x50 and with the USBDebugMIDI and it's now working as expected. When I go back to the USBMIDI_Interface midi; it's not working. It must be in the MIDI call. In the code I did for all my CC messages that works well, I have USBMIDI_Interface midi; to instantiate a MIDI interface, then Control_Surface.begin(); in void setup and Control_Surface.loop(); in void loop();. This works through USB when I reset to DualMoco. How can this work with this LED code? This is what I have right now:

#include <Control_Surface.h>

const pin_t ledA = 48;
const pin_t ledB = 49;
const pin_t ledP = 50;

const uint8_t sysex_prefix[] {0xF0, 0x29, 0x50, 0x10, 0x01};
const uint16_t sysex_len = 7; // expected length of the full sysex message
const uint16_t sysex_led_idx = 5; // byte of the message containing the LED states
const uint8_t ledA_mask = 0x01; // mask that selects the bit with the ledA state
const uint8_t ledB_mask = 0x02;
const uint8_t ledP_mask = 0x04;

bool sysex_matches(SysExMessage sysex) {
  return sysex.length == sysex_len &&                      // First check the length of the message,
         std::equal(std::begin(sysex_prefix), std::end(sysex_prefix), sysex.data); // then the data.
}

// Custom MIDI callback that updates the LEDs:
struct MyMIDI_Callbacks : MIDI_Callbacks {

  // This callback function is called whenever a SysEx message is received:
  void onSysExMessage(Parsing_MIDI_Interface &midi) override {
    // Get the message from the MIDI interface:
    SysExMessage sysex = midi.getSysExMessage();
    // Check if it matches the LED update SysEx message we expect:
    if (sysex_matches(sysex)) {
      // Turn on the right LEDs:
      const uint8_t led_data = sysex.data[sysex_led_idx];
      digitalWrite(ledA, led_data & ledA_mask ? HIGH : LOW);
      digitalWrite(ledB, led_data & ledB_mask ? HIGH : LOW);
      digitalWrite(ledP, led_data & ledP_mask ? HIGH : LOW);
    }
  }

};

// Instantiate the MIDI over USB interface:
USBSerialMIDI_Interface midi { 115200 };
// Instantiate the MIDI callbacks:
MyMIDI_Callbacks callback;

void setup() {
  pinMode(ledA, OUTPUT);
  pinMode(ledB, OUTPUT);
  pinMode(ledP, OUTPUT);
  Serial.begin(115200);
  midi.begin();
  midi.setCallbacks(callback);
}

void loop() {
  // Read incoming MIDI data and call the callback if a new
  // SysEx message has been received.
  midi.update();
  }

I changed to USBSerialMIDI_Interface midi { 115200 }; Is this supposed to work through the USB connector? Do I have to reset with DualMoco? Do I have to comment MIDI.begin(); I've tried many of these combinations to no avail.

All I have plugged in is a USB cable. I do have a MIDI din connector on my breadboard if this can help.

Why?

What is the baud rate used by DualMoco? I don't think it's 115200. You could try 31250, but you should look it up in the DualMoco documentation.

No, you need to initialize the MIDI interface before you can use it.