Serial read hungs the arduino. Array of buffers.

Hi.

I'm trying to read the MIDI data that comes from a mixer. The data word i'm receiving is 3 Bytes on every move i do to the fader, thus, that's a lot of bytes coming thru the Serial of the arduino at a given time.

I reall don't know how to handle so much data coming into the arduino. If i move up to 5 faders at the same time, things are ok, but that's the limit. As soon i touch the 6th and move the 6 at the same time (Just to see the limit of the buffer), the LCD and the Main() hangs.

Is there any info i can read about dealing with large amount of data at the serial buffer? Increasing the 128Bytes buffer? Maybe create an array of buffers, or delaying the readings, or flushing the buffer... any real idea will be accepted, or FAQ or manual.

If anyone needs the code, i will post... but is just a first draft.

Thank you as usual.
knob2001

Without seeing your code, it's pointless conjecturing.

The limit is not the size of the buffer. The limit is the time it takes to you to read the buffer. If your program spends too much time in other tasks than reading the buffer you will have a buffer overrun.

If I am right, MIDI is something like 31Kb/s this gives a byte every 320µs. When you move many faders at the same time you receive a large amount of data at that rate. This don't give you many time to process your data.

Hi.

Thank you for the answers. This is the first draft of the sketch... don't be very cruel with it :wink:

It's a very easy code, just the tricky stuff of the Serial.Read is driving me a little mad. Whatever idea is very wellcomed!

Ok, the code so far:

/*
Some comments and variables may appear in spanish, sorry.
 Byte word receiving: 01 02 03 / B0 (Always) - Channel - Level
*/

#include <stdio.h> // For let me use the itoa - atoi conversions
#include <stdlib.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(13, 12, 11, 10, 9, 8); //Custom Pins for LCD. 11-10-9-8 are DB7 to BD4

// Too much junk around here, but i will clean it later
byte midiByte;
int x;
int q;
byte bufferByte1[3];
byte byteNivel;
byte bytefader;
int estado[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Fader State array. There are 15 fader on the main mixer.
byte n;
byte z;
int bytefaderInt;
int midifaderDec;
int pinFader;
int modoMixer;
int byteMax;
int h;




// Setup

void setup() {

 Serial.begin(31250); // MIDI rate
 Serial.flush();

 lcd.begin(16, 2); // Starts LCD
 lcd.setCursor(0,1);
 lcd.print("Fader:");
 lcd.setCursor(0,0);
 lcd.print("Level:");
 lcd.setCursor(7,0);
 lcd.print("AF:"); // Debugs level position


 pinMode(2, OUTPUT);  // optocoupler 2-3
 pinMode(3, OUTPUT);
 digitalWrite(3, LOW); // GND optocoupler
 digitalWrite(2, HIGH); // 5v optocoupler
 pinMode(4, INPUT); // DIP MIDI 1-4 Not used right now, but later
 pinMode(5, INPUT);
 pinMode(6, INPUT);
 pinMode(7, INPUT);
 digitalWrite(4, HIGH); // DIP MIDI pullup resistors
 digitalWrite(5, HIGH);
 digitalWrite(6, HIGH);
 digitalWrite(7, HIGH);
 // -------------------------------------------------------------
 pinMode(14, OUTPUT); // Using analog as digital due lack of available pins
 pinMode(15, OUTPUT);
 pinMode(16, OUTPUT);
 pinMode(17, OUTPUT);
 pinMode(18, OUTPUT);
 digitalWrite(14, HIGH);
 digitalWrite(15, HIGH);
 digitalWrite(16, LOW);
 digitalWrite(17, LOW);
 digitalWrite(18, LOW);

 
 modoMixer = 0; // Setups: Yamaha 01v = 0 /
Yamaha LS-9 = 1
 byteMax = 3; // MIDI Byte words:  01v = 3 / LS-9 = 18
 //
}

void loop () {

 readSerial(); // Call the read()

 if (modoMixer == 0) { // MODE YAMAHA 01v

 byteNivel = bufferByte1[3]; // Level Byte  (3)
 bytefader= bufferByte1[2]; // Fader Byte  (2)
 char bytefaderDec [50]; // For debugging. To be removed.
 itoa ( bytefader,bytefaderDec,10); // bytefaderDec for later
 bytefaderInt = atoi (bytefaderDec); // bytefaderInt for later

 //  LCD debugs begin here.
 lcd.setCursor(7,1);
 lcd.print (bytefaderDec);
 lcd.print(" ");
 lcd.setCursor(7,0);
 lcd.print(byteNivel, DEC); // 
 lcd.print(" ");
 // ---------------------

 pinFader = asignaPin(bytefaderInt); // Assign Pin to Faders

 if (bytefaderInt < 15) // I really need the main faders of the mixer
 { if (byteNivel > 0){ // If i touch the fader, then the Relay will be latched

                     onAutofader(); // Closes circuit
                     } else {
                     offAutofader(); // Open circuit
                            }

 delayMicroseconds(300); // Delay to handle the buffer. I don't really think this works as expected.

             } // Ends fader mode

       }//Ends mixer mode
}// Ends loop


byte readSerial(){ // Function read

 if (Serial.available() > 0) {

  // I create a buffer to store the 3 Bytes
  // Maybe a Byte1 = Serial.Read();
  // Byte2 = Serial.Read();
  // Byte3 = Serial.Read();
  // Is faster than the actual function with the buffer?

   q++;
   midiByte = Serial.read();
   bufferByte1[q]=midiByte;
   return bufferByte1[q];
  
   if (q > byteMax){ // byteMax is the LEN of the mixer's word. 
     q=0; // Array to 0. Prepare for the next 3 Bytes word

           }
                           }
 else { // Just be sure to init again the Array
   q=0; 
   Serial.flush(); // Flush the Serial again. Again, i don't know if this works.
      }
             }


int onAutofader(){ // Close circuit

 if (estado[bytefaderInt]==0){ // If it's already open, don't touch it.

    estado[bytefaderInt] = 1; // Change to open
    //cd.setCursor(11,0); // Debug Stuff
    //lcd.print("ON ");

    digitalWrite(pinFader, LOW); // Do the magic on the pin

 }

}

int offAutofader(){ // Close relay

                     estado[bytefaderInt] = 0; // Fader is off
                     //lcd.setCursor(11,0); // Debug Stuff
                     //lcd.print("OFF");


                     digitalWrite(pinFader, HIGH); // Closes the ciruit

                 }  }




int asignaPin(int f){ // I have to assign the pinout for the different faders
 int pinAssign;

 switch (f){ // Begin. Just 4 for testing. F is the Fader i touch.

   case 1:
   pinAssign = 14;
   return pinAssign;
   case 2:
   pinAssign = 15;
   return pinAssign;
   case 3:
   pinAssign = 17;
   return pinAssign;
   case 4:
   pinAssign = 19;
   return pinAssign;

   default:
   break;
 }

 }

If I am right, MIDI is something like 31Kb/s this gives a byte every 320µs. When you move many faders at the same time you receive a large amount of data at that rate. This don't give you many time to process your data.
Posted by: Groove

You are right. But i used to think the serial buffer would drop some bytes when an overrun occurs, not hangs the whole Loop()

Regards,
knob2001

delayMicroseconds(300); // Delay to handle the buffer. I don't really think this works as expected.

?

Your "readSerial" returns as soon as it has read one byte, if there is a byte available - is that what you intended?
You've also declared it as type "byte", yet you don't use the return value, nor do you return any value if there is no data available.
"asignaPin" doesn't return any value in the default case, and the whole routine could be replaced with a simple lookup table (except for the default case)

This:

  if (q > byteMax){ // byteMax is the LEN of the mixer's word.
     q=0; // Array to 0. Prepare for the next 3 Bytes word

can never be executed, since it is preceeded by a "return"

Before cutting and pasting from the IDE, could you maybe use the IDE's auto-format tool?
Can I suggest you concentrate on reliably reading midi messages, before doing all the ticky stuff with the LCD?

Hi.

The whole idea is:

As soon as the serial buffer receives one byte, put in on the first position of the array. So on up to the third byte (On one model of the yamaha, the other is Sysex messages... 18 Bytes). Then take the next 3 bytes.
That's all the serial read i need.

The Delay was added later becouse i thought the LCD was the problem. It gives some time to refresh it...

The readSerial function is byte, i think this is something from the beggining. Sice i'm using the bufferByte1 instead, should i declare it as void to don't waste memory and/or cycles? Could it be the hang reason?

And the default case of AsignaPin is nothing with a pourpose. I don't have many pins available... > 4 do nothing. Thought it was a good idea better than return a useless variable.

The LCD will not be there, is just a debbuger monitor. I don't know how to monitor a MIDI 31250 bauds rate as the serial monitor just let me put the usual serial rates (maybe becouse the internal crystal doesn't handle other rates?... i'm still learning all if this)

The reading of the midi... i thought i knew how to do it. At least the whole thing works up to 5 faders flawless. I'm forcing it to 6 faders and crazy movements (you will never mix audio with 6 faders at the same time, up & down as crazy)

If you know any manual to understand better the serial.Read would be great!

Again, i love to learn. Everything you say is really helpfull.
Regards
knob2001

P.D. I don't have the compiler here at home on this computer... sorry about the code format.

I'd really have a good look at "readSerial", maybe get it to loop until it has read three bytes.

If "Serial.avaliable ()" returns zero, then there's really no need to flush the serial buffer; it's already empty - or it was when you called it...it may not still be by the time you call "flush".
Just because the buffer is empty, is no reason to reset the buffer pointer. Remember your program can potentially execute "loop" hundreds and hundreds of times in the time it takes to transmit one midi character.

I'm sorry, I'm not any kind of midi user, but there are so many potential bear-traps in that code, you really need to get a clear idea of what it is you want to do.

There are no performance penalties for not returning values, or not using values that are returned, but it makes your code and your intentions less clear.

Hi.

It seems that removing the "return" that was ending the function just before letting the array returns to 0 (every 3 bytes), things are working perfect. No more hangs even with 16 faders at the same time!

Also i removed a lot of useless stuff like the LCD delay and those unwanted int functions without any data return.

It's not a beautiful code, but for my c++ knowlegde i guess it's a good start. I think with time i would be ready to do things more elegant, but until then... at least it works.

Anyway, thank you very much...
Regards
knob2001