incoming Midi really muddy/ dirty/ data loss with a well known 4n28 circuit.

This circuit

NOTE: According to several dozens of posts all over the web, the circuit started to work properly when you take out the 100k resistor off pin 8. This is indeed that case and I started receiving the messages. Who knows maybe the same optocouplers made by different companies? since the OP usually says his circuit works too even with the 100k in place.

I have also tried the circuit from instructables using the 6n138 optocoupler. I get the same problem consistently.

The problem: It appears that some of the messages do not make it to the controller OR they do but there's some interference or noise. The source of the issue is unknown as at one point it works just fine (led on pin 13 lights up when I press the Middle C note and turns off when I let go of the middle C note) and periodically it doesn't. I can stil see the RX onbopard led flashing so the midi timecode messages and note messages ARE coming in. It appears though that the data may be different or too muddy to interpreted by the arduino.

I've tested this with 2 midi controllers and an MIDI interface, sending data from daw. Same effect can be observed everywhere.

I also tried this with using a power supply instead of powering the Uno via usb (as usb power is a rad more noisy) - same deal.

2 different midi cables too.

This is the code:

byte commandByte;
byte noteByte;
byte velocityByte;

byte noteOn = 144;
byte noteOff = 128;


void setup(){
  Serial.begin(31250);
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(13,LOW);
  digitalWrite(12,LOW);
}   

void loop(){
 
    while (Serial.available() > 2){
      commandByte = Serial.read();//read first byte
      noteByte = Serial.read();//read next byte
      velocityByte = Serial.read();//read final byte
    }
      
      if (commandByte == noteOn){//if note on message
        //check if note == 60 and velocity > 0
        if (noteByte == 60){
          digitalWrite(13,HIGH);//turn on led
        }
        if (noteByte == 61){
          digitalWrite(12,HIGH);//turn on led
        }
      }

      else if (commandByte == noteOff){//if note on message
        //check if note == 60 and velocity > 0
        if (noteByte == 60){
          digitalWrite(13,LOW);//turn on led
        }
        if (noteByte == 61){
          digitalWrite(12,LOW);//turn on led
        }
      }
}

I know the code works.

Video cuts out a bit too early. I was saying that First I check if there is a note On and then I check which note was pressed since it all comes in one message of [status, data byte, data byte].

So I had 2 more variations of the code (most of which I got from here or instructubles):

  1. Just turning the led if the Note On message comes in, regardless of which note - that works with 100% accuracy but it just triggers front any note, I needed to do something else for every note.

  2. Since the data comes in in packets, I simply check which note it is as soon as the serial data comes in. Since this would only change IF a message came it, I just made a Boolean that toggled between on and off state whenever the midi message with this specific note came in. It technically did the trick. I was actually able to get the two leds to light up separately since the midi messages send 2 messages (one for when key is pressed and another for when key is let go, both containing the "which key" data), however there was is still an occasion when a message would not come in or interpreted right and the led would effectively invert. So it seems that I get slightly better results if I'm using only one piece of data for checks. As soon as I start checking if the incoming message is a note On message AND if this message is a specific note - that's when this issue become more frequent.
    =========================================================

PS: Here are some things I've been finding and testing myself:

cbradburne:
Ok, back so soon!!

I forgot to mention, I also had issues reading MIDI while I still had a USB cable plugged in to the Arduino I guess because it was conflicting with the Rx pin.

So I'd definitely recommend you try running your Arduino from a battery or another PSU, not USB.

Cheers,
Col

It seems to me that your video proves that your code doesn't work.
Post all your code (in code tags).

Pete

yes just did

    while (Serial.available() > 2){

This is probably the problem. If the MIDI data comes in fast enough, there might be 4 characters (or more) in the queue. Assume that there are 4. The loop reads the first 3 as you would expect but then the loop repeats and reads the 4th char as the command byte. Then it reads two more characters which aren't there. In this case Serial.read() returns -1 which fouls up the rest of your code because noteByte won't be 60 or 61.

There are various ways of fixing this. One would be to remove the while loop and use this instead:

     while(Serial.available() == 0);
      commandByte = Serial.read();//read first byte
      while(Serial.available() == 0);
      noteByte = Serial.read();//read next byte
      while(Serial.available() == 0);
      velocityByte = Serial.read();//read final byte

BTW. Don't update your original message. It makes the thread hard to follow. Just add another post on the end.

Pete

el_supremo:

    while (Serial.available() > 2){

This is probably the problem. If the MIDI data comes in fast enough, there might be 4 characters (or more) in the queue. Assume that there are 4. The loop reads the first 3 as you would expect but then the loop repeats and reads the 4th char as the command byte. Then it reads two more characters which aren't there. In this case Serial.read() returns -1 which fouls up the rest of your code because noteByte won't be 60 or 61.

There are various ways of fixing this. One would be to remove the while loop and use this instead:

     while(Serial.available() == 0);

commandByte = Serial.read();//read first byte
      while(Serial.available() == 0);
      noteByte = Serial.read();//read next byte
      while(Serial.available() == 0);
      velocityByte = Serial.read();//read final byte




BTW. Don't update your original message. It makes the thread hard to follow. Just add another post on the end.

Pete

Got it, will post updates in new comments.

I've tried several renditions trying to debug it. I've done if statements and just read messages. But for consistency sake I tried your adjustment too. Thing is that these pieces of code came from indestructibles or here on Arduino forums where plenty of people reported the code working just fine.. just my luck?

Here's the update:

I also tried this which (seemingly) made things a little more consistent. I check if the serial buffer has at least 3 bytes available since MIDI note message comes in 3 packs, command byte, data byte for note and another data byte for velocty.

if (Serial.available() == 3) {
      commandByte = Serial.read();//read first byte
      noteByte = Serial.read();//read next byte
      velocityByte = Serial.read();//read final byte
}

So logically this means that the 3 bytes would always record in packs only when all three are available. This should be fool proof as far as my understanding goes. The issue, however persists BUT the behavior seems to be a tad different:

I've also tried using a switch statement for checking which note it is, considering that all the uno has to do is toggle digital pins on and off AND that you only get One note at a time event if a chord is sent then a switch statement with break; function would keep everything minimal enough.

byte commandByte;
byte noteByte;
byte velocityByte;

byte noteOn = 144;
byte noteOff = 128;

//light up led at pin 13 when receiving noteON message with note = 60

void setup(){
  Serial.begin(31250);
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(13,LOW);
  digitalWrite(12,LOW);
}   

void loop(){
 
      if (Serial.available() == 3){
        commandByte = Serial.read();//read first byte
        noteByte = Serial.read();//read next byte
        velocityByte = Serial.read();//read final byte
      }
      
      if (commandByte == noteOn){//if note on message
      switch (noteByte) {
          case 60:
            digitalWrite(13,HIGH);//turn on led
            break;
          case 61:
            digitalWrite(12,HIGH);//turn on led
            break;
          default: 
            // if nothing else matches, do the default
            // default is optional
          break;
        }
      }


      if (commandByte == noteOff){//if note on message
      switch (noteByte) {
          case 60:
            digitalWrite(13,LOW);//turn on led
            break;
          case 61:
            digitalWrite(12,LOW);//turn on led
            break;
          default: 
            // if nothing else matches, do the default
            // default is optional
          break;
        }
      } 
    //} // end of if serial available
} // end of void loop

I've attached an LCD display to display the midi 3 bytes. These numbers don't make sense to me as I'm just expecting the values that the MIDI manual says I'm supposed to receive. Hopefully this sheds some light on what the issue is.

I'm not sure if you're supposed to convert the data into some other data type. I just printed the noteByte and other two variables as they were.

Ok provided that there are indeed ONLY 3 bytes of data per each midi note on message (excluding the clock signal?) I've gotten a 20x4 display to actually show the binary data receiving from the source:

If you have a clock message as well as the notes, your code won't handle it because the clock message isn't 3 bytes long.
Use this code which dumps MIDI messages (except for Sysex which it won't handle properly). Each MIDI command byte has the high order bit set and none of the other bytes do, so it is easy to synchronize with the command byte. This just reads characters one at a time. If the character has the high bit set, print a line feed first. Then print the byte in hex. In this way, the first character on the line is always the command byte.

void loop(void)
{
  // temporary string for sprintf
  char s_tmp[4];

  while(Serial.available() == 0);
  char midi_char = Serial.read();
  sprintf(s_tmp,"%02X ",midi_char);
  // If this is a command byte, start a new line
  if(midi_char & 0x80)Serial.println();
  Serial.print(midi_char);
}

Try it, and cut and paste the output so I can see what it is really seeing.

Pete

Wait, but I'm using the RX pin to read the midi data, that's the only line with a UART chip. I'd have to use softwareSerial and that's not been kind to me lately. Give me a second I'll try to get something together.

I'll keep on this issue for a little more just to see if we can figure this out, you seem to know far more about this as you can come up with ways of bug testing it.

I found Hiduino project which, for all intents and purposes, achieves exactly what I need and even omits the use of midi cables. If I won't figure this serial midi stuff out, I'll go ahead and start figuring things out with Hiduino. It makes the Leonardo show up as a USB MIDI device so you can send stuff directly to in from your DAW

If anything, perhaps I could get myself an Ethernet shield as there are applications that allow you to forward midi data over Etherner from your pc.

That or use the MIDI>Serial program that sends midi data to the arduino via it's usb uart port. but it seems pretty dated and broken at this point.

Hiduino, making Leonardo appear as a midi device in your daw is the best solution though.

I'm using the RX pin to read the midi data

Yes, and the Tx pin will be used to write the serial debugging stuff to the serial monitor. I don't think they'll collide.
Try it.
Worry about the rest when you've got your code reading the MIDI data properly. It isn't going to matter what hardware you eventually use if you haven't sorted that out.

Pete

el_supremo:
Yes, and the Tx pin will be used to write the serial debugging stuff to the serial monitor. I don't think they'll collide.
Try it.
Worry about the rest when you've got your code reading the MIDI data properly. It isn't going to matter what hardware you eventually use if you haven't sorted that out.

Pete

Yes the issues is that the Baud rate for MIDI is 31250 and Serial Monitor Does not have the baud rate option. I trued using Putty terminal to get a reading as you can set any baud rate there. Results were unreadable.

I connected a DUO that has multiple rx-tx ports. This is the code:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial1.begin(31250);
}

void loop(void)
{
  // temporary string for sprintf
  char s_tmp[4];

  while(Serial1.available() == 0);
  char midi_char = Serial1.read();
  sprintf(s_tmp,"%02X ",midi_char);
  // If this is a command byte, start a new line
  if(midi_char & 0x80)Serial.println();
  Serial.print(midi_char);
}

Results of the serial monitor when pressing random keys on the keyboard.

Fairly certain that this is unreadable data. So something is wrong with the communication. somewhere

  Serial.print(midi_char);

Arrghhhh. My bad. This should be:

  Serial.print(s_tmp);

Pete

There are so many solutions I've found to get this done, even an isntructuble. The problem is that most of those solutions are essentially you loading a custom fw onto the controller responsible for communication of the atmega processor with through usb. And all I have are arduino replicas that use a CH340 usb to serial chip instead of the actual atmega chip.. Guess I deserve it for not buying originals.

I do have one leonardo though. Would be great if I can just have it appear as the midi device in my daw.

One moment, reading what you just wrote. response on page 2

Eurica, I've made progress!

pressing the middle C key with varying velocities.:

F0 
F0 
F0 00 38 10 
F0 
F0 00 38 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 38 40 
F0 
F0 00 38 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 38 40 
F0 00 38 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 38 40 
F0 00 38 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 38 40 
F0 
F0 00 38 00
F0
F0
F0 00 38 10
F0
F0 00 38 00
F0

This is partially correct. The "38 10" and "38 00" correspond to what would be sent for middle C Note on followed by Note off. But the "F0 00" and the other F0 are wrong. F0 is the start byte for a SYSEX message.
Note on should be generating "90 38 10" (when on MIDI Channel 1). Note off should generate either "90 38 00" (i.e. a Note on with velocity of zero) or "80 38 00".

Which device are you using to generate those messages?

Pete

I've tried 3 different devices:

Korg Microkorg with midi out:

F0 
F0 
F0 
F0 
F0 00 38 3E 
F0 
F0 00 38 00 
F0 
F0 00 38 00 
F0 
F0 00 38 00 
F0 
F0 00 38 08 
F0 00 38 00 
F0 
F0 
F0 00 38 02 
F0 
F0 00 38 00 
F0 
F0 00 38 04 
F0 
F0 00 38 00 
F0 
F0

output from daw via MiDi Express 128

00 00 
FE 00 00 
FE 00 02 
FE 00 02 
FE 00 00 
FE 00 00 
FE 00 06 
FE 00 06 
FE 00 00 
FE 00 00 
FE 00 3C 
FE 00 3C 
FE 00 00 
FE 00 00 
FE 00 00 
FE 00 00 
FE 00 00 
FE 00 00 
FE

Old Rolad D-10 keyboard midi:

F0 
F0 
F0 00 00 04 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 00 00 20 72 00 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 04 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 00 04 00 20 72 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 00 04 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 00 00 00 20 72 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 00 00 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 00 00 20 72 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 02 00 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 02 00 20 72 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 00 08 30 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 08 00 20 72 00 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 00 0E 00 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
FC 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 
F0 00 0E 00 20 72 00 
F0 
F0

Doesn't the clock signal transmit via sys_ex messages? It is worth noting that MIDI timecode is disabled in daw so that's probably why we're only seeing note messages from the MOTU Midi Experss 128. The other two are keyboards with on-board synthesis/ sampling engines and I know Microkorg can send out it's own clock signal ans I'm pretty sure the D-10 can do too. So I guess it's fairly straight forward to say that F0 is clock signal tick?

Now are these HEX values?

Any way to filter out the system messages if they are the cause of the errors.

Yes, they are all hex.

Doesn't the clock signal transmit via sys_ex messages?

I don't think so. I think it is F8 (below)

F0 is the start of a SYSEX message. When this is sent there must be a corresponding F7 to indicate the end of the sysex message.
F8 is the "Timing Clock. Sent 24 times per quarter note when synchronization is required". I've never used it.
FC means "Stop the current sequence."
FE is Active Sense. My SY77 sends this continuously roughly 5 times a second. BUT it should not have any data bytes after it.

I'm beginning to come around to the idea that it is the 4N28 interface. There are various reports of people who've had problems with this particular optocoupler when used for MIDI. This site has a couple of suggested workarounds - use at your own risk. I've also seen one site where they used a 2.2K instead of the 3.3K in your diagram.

Pete

el_supremo:
Yes, they are all hex.
I don't think so. I think it is F8 (below)

F0 is the start of a SYSEX message. When this is sent there must be a corresponding F7 to indicate the end of the sysex message.
F8 is the "Timing Clock. Sent 24 times per quarter note when synchronization is required". I've never used it.
FC means "Stop the current sequence."
FE is Active Sense. My SY77 sends this continuously roughly 5 times a second. BUT it should not have any data bytes after it.

I'm beginning to come around to the idea that it is the 4N28 interface. There are various reports of people who've had problems with this particular optocoupler when used for MIDI. This site has a couple of suggested workarounds - use at your own risk. I've also seen one site where they used a 2.2K instead of the 3.3K in your diagram.

Pete

Ok will try the resistor workaround and I'm going to quickly put together a 6n138 mini input circuit, be right back with more values from that circuit. Thank you for all the help btw, I've actually learned a lot.

So just in case, Can I just take the Hex values from midi documentation and compare them to the noteByte comandByte and velocityByte or is there some sort of conversion that needs to take place.