Behaviour of Serial.available() == N

Hi, I have a small project to control a testrig in the lab with the MKR Vidor 4000 board.
I need to send different data to the Arduino.

  1. A message of n byte length
  2. A message of m byte length
if (Serial.available() == n){

    do some stuff
   
   Serial.read(); // clear incoming buffer 

}

if (Serial.available() == m){

     do some stuff
  
    Serial.read(); // clear incoming buffer
}

m is 32 bytes long.
If n is 2, the code works fine. (sending 2 bytes or 32 bytes)
If n = 4, the code doesn't work because the first if statement (n == 4) is executed even though I send 32 bytes to the Arduino. (sending 4 bytes or 32 bytes)

Can someone explain this behavior and have some suggestens to fix this behavior?

Edit: Data n and Data m have to be send independently from each other.

    Serial.read(); // clear incoming buffer

That will read a single byte, not empty the buffer

while (Serial.available())
{
  Serial.read();
}

will empty the buffer

Serial is slow; you're just lucky that it works when n == 2. When sending 32 bytes, you might have received 3 bytes initially which does not match 2 or 4 or 32 so nothing will happen. Next time you might have a few more bytes (e.g. the original 3 plus 4 extra) so no matches till you did send 32 bytes and your second 'if' will be true.

You will need to read a complete packet; it's the only way you know that a full message is received an what its length is. See e.g. Serial Input Basics - updated.

Please post a complete sketch that demonstrates the behaviour. Is the data binary or tekst?

1 Like

In the if statements all bytes will be read with Serial.read for simplicty I didn't show that.

if n = 2 two times Serial.read
if m= 32; 32 times Serial.read
etc. etc.

That my code, not optimised and bulky.

if (Serial.available() == 2){

    incomingByte33 = Serial.read(); /// PWM Data Spannung
    incomingByte34 = Serial.read(); /// PWM Data 

   // incomingByte35 = Serial.read(); /// PWM Data Strom
   // incomingByte36 = Serial.read(); /// PWM Data 

    Vset = incomingByte33 << 8 | incomingByte34 ;
  // Iset = incomingByte35 << 8 | incomingByte36 ;

}






    if (Serial.available() == 32) {
    // read the incoming byte:
    incomingByte1 = Serial.read();
    incomingByte2 = Serial.read();
    incomingByte3 = Serial.read();
    incomingByte4 = Serial.read();

    incomingByte5 = Serial.read();
    incomingByte6 = Serial.read();
    incomingByte7 = Serial.read();
    incomingByte8 = Serial.read();

    incomingByte9 = Serial.read();
    incomingByte10 = Serial.read();
    incomingByte11 = Serial.read();
    incomingByte12 = Serial.read();

    incomingByte13 = Serial.read();
    incomingByte14 = Serial.read();
    incomingByte15 = Serial.read();
    incomingByte16 = Serial.read();


    incomingByte17 = Serial.read();
    incomingByte18 = Serial.read();
    incomingByte19 = Serial.read();
    incomingByte20 = Serial.read();

    incomingByte21 = Serial.read();
    incomingByte22 = Serial.read();
    incomingByte23 = Serial.read();
    incomingByte24 = Serial.read();

    incomingByte25 = Serial.read();
    incomingByte26 = Serial.read();
    incomingByte27 = Serial.read();
    incomingByte28 = Serial.read();

    incomingByte29 = Serial.read();
    incomingByte30 = Serial.read();
    incomingByte31 = Serial.read();
    incomingByte32 = Serial.read();

    uint32_t myWord = incomingByte1 << 24 | incomingByte2 << 16 | incomingByte3 << 8 | incomingByte4 ; //first puls
    uint32_t myWord2 = incomingByte5 << 24 | incomingByte6 << 16 | incomingByte7 << 8 | incomingByte8 ; // pause
    uint32_t myWord3 = incomingByte9 << 24 | incomingByte10 << 16 | incomingByte11 << 8 | incomingByte12 ; // second puls
    uint32_t myWord4 = incomingByte13 << 24 | incomingByte14 << 16 | incomingByte15 << 8 | incomingByte16 ; // verriegelung

    uint32_t myWord5 = incomingByte17 << 24 | incomingByte18 << 16 | incomingByte19 << 8 | incomingByte20 ; //first puls
    uint32_t myWord6 = incomingByte21 << 24 | incomingByte22 << 16 | incomingByte23 << 8 | incomingByte24 ; // pause
    uint32_t myWord7 = incomingByte25 << 24 | incomingByte26 << 16 | incomingByte27 << 8 | incomingByte28 ; // second puls
    uint32_t myWord8 = incomingByte29 << 24 | incomingByte30 << 16 | incomingByte31 << 8 | incomingByte32 ; // verriegelung



  } 



}

Is there a way to wait until the whole message is in the buffer?

The Data are sent from Matlab and converted into binary. Matlab gets an integer and converts the Data into binary by sending it.

Read each byte that becomes available and put it in an array. Stop when you have got the expected number of bytes and use the data in the array.

Ideally the message would have start and end markers so that you know that you have got a complete message, otherwise count the received bytes, but expect problems with synchronization of messages

Did you look at Serial input basics - updated as previously suggested

2 Likes

It's not the full code.

Maybe https://docs.arduino.cc/language-reference/en/functions/communication/serial/readBytes/ will work for you. Set it to 32 bytes; if only two bytes are received it will timeout after one or two seconds. You can check how many bytes are received by checking the return value.

You should design a solid protocol. Something like

[startByte][dataLength][data0]...[dataN][cs]

You will wait till you receive a startByte before you start reading the rest of the message. Next you read the dataLength so you know how many bytes to expect; this will exclude th checksum at the end. After that you can read the data into a buffer and calculate the checksum. In the last step you read the checksum from the serial port and verify that it matches the checksum that you calculated.

If there is an integer value that can not occur in the data (e.g. 0xFFFF), you can use two startBytes and you only start if both match.

Here is the full program.

#include <wiring_private.h>
#include "jtag.h"

#define TDI                               12
#define TDO                               15
#define TCK                               13
#define TMS                               14
#define MB_INT                            28
#define MB_INT_PIN                        31
#define SIGNAL_OUT                        41 //B5 L16
#define SIGNAL_IN                         33 //B2 N2

#define CLK                               0
#define ENABLE                            1
#define DATAPIN                           2

#define no_data    0xFF, 0xFF, 0xFF, 0xFF, \
          0xFF, 0xFF, 0xFF, 0xFF, \
          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
          0xFF, 0xFF, 0xFF, 0xFF, \
          0x00, 0x00, 0x00, 0x00  \

#define NO_BOOTLOADER   no_data
#define NO_APP        no_data
#define NO_USER_DATA    no_data

__attribute__ ((used, section(".fpga_bitstream_signature")))
const unsigned char signatures[4096] = {
  //#include "signature.ttf"
  NO_BOOTLOADER,

  0x00, 0x00, 0x08, 0x00,
  0xA9, 0x6F, 0x1F, 0x00,   // Don't care.
  0x20, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x65, 0x73, 0x2d, 0x65, 0x6d, 0x62, 0x61, 0x72, 0x71, 0x75, 0x65, 0x73, 0x2e, 0x66, 0x72, 0x20, 0x00, 0x00, 0xff, 0xf0, 0x0f,
  0x01, 0x00, 0x00, 0x00,   
  0x01, 0x00, 0x00, 0x00,   // Force

  NO_USER_DATA,
};
__attribute__ ((used, section(".fpga_bitstream")))
const unsigned char bitstream[] = {
  #include "Kurzschluss1.h" // Hier wird die FPGA file geladen.
};

int FPGAVal=HIGH;
const int SPEED=5;
const int FPGALED=6;


uint8_t incomingByte1 = 0;
uint8_t incomingByte2 = 0;
uint8_t incomingByte3 = 0;
uint8_t incomingByte4 = 0;

uint8_t incomingByte8 = 0;
uint8_t incomingByte7 = 0;
uint8_t incomingByte6 = 0;
uint8_t incomingByte5 = 0;

uint8_t incomingByte9 = 0;
uint8_t incomingByte10 = 0;
uint8_t incomingByte11 = 0;
uint8_t incomingByte12 = 0;

uint8_t incomingByte13 = 0;
uint8_t incomingByte14 = 0;
uint8_t incomingByte15 = 0;
uint8_t incomingByte16 = 0;

uint8_t incomingByte17 = 0;
uint8_t incomingByte18 = 0;
uint8_t incomingByte19 = 0;
uint8_t incomingByte20 = 0;

uint8_t incomingByte21 = 0;
uint8_t incomingByte22 = 0;
uint8_t incomingByte23 = 0;
uint8_t incomingByte24 = 0;

uint8_t incomingByte25 = 0;
uint8_t incomingByte26 = 0;
uint8_t incomingByte27 = 0;
uint8_t incomingByte28 = 0;

uint8_t incomingByte29 = 0;
uint8_t incomingByte30 = 0;
uint8_t incomingByte31 = 0;
uint8_t incomingByte32 = 0;


uint32_t delaytime=50;

uint8_t incomingByte33 = 0;
uint8_t incomingByte34 = 0;
uint16_t Vset = 0;
uint16_t Iset = 0;
uint8_t incomingByte35 = 0;
uint8_t incomingByte36 = 0;

uint16_t v_ist = 0;
uint16_t i_ist = 0;

// the setup function runs once when you press reset or power the board

void printBin(uint32_t aByte) {
  for (int16_t aBit = 31; aBit >= 0; aBit--)
    Serial.write(bitRead(aByte, aBit) ? '1' : '0');
}

void printBin2(byte aByte) {
  for (int8_t aBit = 7; aBit >= 0; aBit--)
    Serial.write(bitRead(aByte, aBit) ? '1' : '0');
}

void setup() {

  int ret;
  uint32_t ptr[1];

  //enableFpgaClock();
  pinPeripheral(30, PIO_AC_CLK);
  clockout(0, 1);
  delay(1000);
  
  //Init Jtag Port
  ret = jtagInit();
  mbPinSet();

  // Load FPGA user configuration
  ptr[0] = 0 | 3;
  mbEveSend(ptr, 1);

  // Give it delay
  delay(1000);

  // Configure onboard LED Pin as output
  pinMode(LED_BUILTIN, INPUT);
  // my code
  pinMode(CLK,OUTPUT);
  pinMode(ENABLE,OUTPUT);
  pinMode(DATAPIN,OUTPUT);
  ///////////////////////////////////
  // Disable all JTAG Pins (usefull for USB BLASTER connection)
  pinMode(TDO, INPUT);
  pinMode(TMS, INPUT);
  pinMode(TDI, INPUT);
  pinMode(TCK, INPUT);

  // Configure other share pins as input too
  pinMode(SIGNAL_IN, INPUT);  // oSAM_INTstat
  pinMode(MB_INT_PIN, INPUT);
  pinMode(MB_INT, INPUT);
  Serial.begin(9600);
  pinMode(SPEED,INPUT);
  pinMode(FPGALED,INPUT);
  pinMode(18,OUTPUT); //A3
  pinMode(19,OUTPUT); //A4 Analoger ausgang
  //digitalWrite(SPEED,FPGAVal);
  pinMode(20,INPUT); // A5
  pinMode(16,INPUT); // A1


  digitalWrite(CLK,LOW);
  digitalWrite(ENABLE,LOW);
  //digitalWrite(LED_BUILTIN,LOW);
  digitalWrite(DATAPIN,LOW);


}


// the loop function runs over and over again forever
void loop() {

/*
Serial.print(';');
v_ist=analogRead(20); //A5
Serial.print(v_ist);
Serial.print(',');
i_ist=analogRead(16); //A1
Serial.print(i_ist);
Serial.print('.');
Serial.print("\n");
*/


if (Serial.available() == 2){

// Data that sends the MCU

    incomingByte33 = Serial.read(); /// PWM Data Spannung
    incomingByte34 = Serial.read(); /// PWM Data 

   // incomingByte35 = Serial.read(); /// PWM Data Strom
   // incomingByte36 = Serial.read(); /// PWM Data  

    Vset = incomingByte33 << 8 | incomingByte34 ;
  // Iset = incomingByte35 << 8 | incomingByte36 ;

    analogWriteResolution(10);
    analogWrite(18,Vset); // A3 ausgang
    analogWrite(19,Iset); // A4 ausgang


    Serial.read(); // clear incoming buffer


}




 // Data send to the FPGA

    if (Serial.available() == 32) {
    // read the incoming byte:
    incomingByte1 = Serial.read();
    incomingByte2 = Serial.read();
    incomingByte3 = Serial.read();
    incomingByte4 = Serial.read();

    incomingByte5 = Serial.read();
    incomingByte6 = Serial.read();
    incomingByte7 = Serial.read();
    incomingByte8 = Serial.read();

    incomingByte9 = Serial.read();
    incomingByte10 = Serial.read();
    incomingByte11 = Serial.read();
    incomingByte12 = Serial.read();

    incomingByte13 = Serial.read();
    incomingByte14 = Serial.read();
    incomingByte15 = Serial.read();
    incomingByte16 = Serial.read();


    incomingByte17 = Serial.read();
    incomingByte18 = Serial.read();
    incomingByte19 = Serial.read();
    incomingByte20 = Serial.read();

    incomingByte21 = Serial.read();
    incomingByte22 = Serial.read();
    incomingByte23 = Serial.read();
    incomingByte24 = Serial.read();

    incomingByte25 = Serial.read();
    incomingByte26 = Serial.read();
    incomingByte27 = Serial.read();
    incomingByte28 = Serial.read();

    incomingByte29 = Serial.read();
    incomingByte30 = Serial.read();
    incomingByte31 = Serial.read();
    incomingByte32 = Serial.read();



    uint32_t myWord = incomingByte1 << 24 | incomingByte2 << 16 | incomingByte3 << 8 | incomingByte4 ; //first puls
    uint32_t myWord2 = incomingByte5 << 24 | incomingByte6 << 16 | incomingByte7 << 8 | incomingByte8 ; // pause
    uint32_t myWord3 = incomingByte9 << 24 | incomingByte10 << 16 | incomingByte11 << 8 | incomingByte12 ; // second puls
    uint32_t myWord4 = incomingByte13 << 24 | incomingByte14 << 16 | incomingByte15 << 8 | incomingByte16 ; // verriegelung

    uint32_t myWord5 = incomingByte17 << 24 | incomingByte18 << 16 | incomingByte19 << 8 | incomingByte20 ; //first puls
    uint32_t myWord6 = incomingByte21 << 24 | incomingByte22 << 16 | incomingByte23 << 8 | incomingByte24 ; // pause
    uint32_t myWord7 = incomingByte25 << 24 | incomingByte26 << 16 | incomingByte27 << 8 | incomingByte28 ; // second puls
    uint32_t myWord8 = incomingByte29 << 24 | incomingByte30 << 16 | incomingByte31 << 8 | incomingByte32 ; // verriegelung


// Custom protocol to send Data to the FPGA
            digitalWrite(ENABLE,LOW);  // Start des protocol
            delay(10);
            for (int i=0;i<32; i++){


                  
                  digitalWrite(DATAPIN,bitRead(myWord,i));
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,HIGH);
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,LOW);

            }


            for (int i=0;i<32; i++){


                  
                  digitalWrite(DATAPIN,bitRead(myWord2,i));
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,HIGH);
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,LOW);

            } 

            for (int i=0;i<32; i++){


                  
                  digitalWrite(DATAPIN,bitRead(myWord3,i));
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,HIGH);
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,LOW);

            }


            for (int i=0;i<32; i++){


                  
                  digitalWrite(DATAPIN,bitRead(myWord4,i));
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,HIGH);
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,LOW);

            } 

            
            for (int i=0;i<32; i++){


                  
                  digitalWrite(DATAPIN,bitRead(myWord5,i));
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,HIGH);
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,LOW);

            }


            for (int i=0;i<32; i++){


                  
                  digitalWrite(DATAPIN,bitRead(myWord6,i));
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,HIGH);
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,LOW);

            } 

            for (int i=0;i<32; i++){


                  
                  digitalWrite(DATAPIN,bitRead(myWord7,i));
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,HIGH);
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,LOW);

            }


            for (int i=0;i<32; i++){


                  
                  digitalWrite(DATAPIN,bitRead(myWord8,i));
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,HIGH);
                  delayMicroseconds(delaytime);
                  digitalWrite(CLK,LOW);

            }    








            digitalWrite(ENABLE,HIGH); // Ende des protocols
            delay(delaytime);
            digitalWrite(DATAPIN,LOW);


            // clear incoming buffer
            Serial.read();




  } 



}
uint8_t incomingByte1 = 0;
uint8_t incomingByte2 = 0;
uint8_t incomingByte3 = 0;
uint8_t incomingByte4 = 0;
etc, etc

This screams "use an array" to me

5 Likes

You are right, thats only quick and dirty code. I will optimize the code when everything works fine. bitRead() only works for 32 bits. The problem is the Data must be send independently.

For example.
The first if statement set the voltage and current of a 1500V voltage source.

The second statement sends double pulse Data to the FPGA.

For example I have to set the voltage of the voltage source. After the voltage has built up in the capacitor (1 to 5 seconds) then i can send Data to the FPGA.

If the 32 byte messages comes over the wire at a nice, even baud rate, it will be consumed 2 bytes at a time by the first if statements as it misinterprets the component bytes of the 32 byte message.

9600 baud is about 1 bytes per millisecond, so ~2ms into receiving the 32 byte message, it will process the first 2 bytes.

1 Like

if (Serial.available() == 2)
Will be true if the buffer has 2 or 32 bytes or anything inbetween.
You need a new/different communication scheme

That's a very bad strategy. Changing to arrays should be done first.

2 Likes

Why would if(Serial.available() == 2) be true for any value other than 2 ?

1 Like

No. It is the number available, not a test to see if at least that many are.

a7

Right!

I had to look. It is plausible interpretation of "available" to mean an equal or greater number of characters are in the buffer.

This would obvsly be a less useful function, as users are always able to >= it for themselves.

a7

Here's another way to look at it. If you did have 32 bytes available in your serial buffer, how would your program know whether it's a single 32-byte message or 16 separate 2-byte messages? Is there anything in the protocol that says "I'm a 1500V voltage source control message" or "I'm an FPGA control message"?

Like @sterretje mentioned, you should design a protocol that makes it clear what message is being sent. After all, serial data is sent one byte at a time, so unless you designed the protocol otherwise, you can't really tell if you received a full 2-byte message or if you received only the first two bytes of a 32-byte message.

That would be quite a stretch and abuse of overloading to the point of obfuscation. To make this:

   if (Serial.available() == 2) {

   }

behave as you suggest, the available() function would have to return a new object type. Then you'd have to overload operator==() to behave like >=. You could do it, but like I said, obfuscation:

class PretendStream {
  private:
    int charactersInBuffer {3};

    class Available {
      private:
        friend class PretendStream;
        Available(PretendStream &s) : theStream(s) {}
        PretendStream &theStream;

      public:
        bool operator==(int i) {
          return theStream.charactersInBuffer >= i;
        }
    };

  public:
    Available available() {
      return Available{*this};
    }
};

PretendStream strm;

void setup() {
  Serial.begin(115200);
  delay(2000);

  if (strm.available() == 2) {
    Serial.println("Greater than or equal to 2 characters");
  } else {
    Serial.println("NOT Greater than or equal to 2 characters");
  }

  if (strm.available() == 5) {
    Serial.println("Greater than or equal to 5 characters");
  } else {
    Serial.println("NOT Greater than or equal to 5 characters");
  }
}

void loop() {
}
**OUTPUT:**
Greater than or equal to 2 characters
NOT Greater than or equal to 5 characters

I was not suggesting overloading ==.

I just meant if you want to know there are exactly N characters, compare the return value for equality to N.

If you want to know at least N characters are available, use >=.

So that is why available() does what it does, because we can handle it either way we want to.

a7