Trying to read 2 chars at a time from Serial but can't get the right code.

i am basically trying to expand on the example code from Robin2's "Serial Input Basic" thread;

// Example 1 - Receiving single characters

char receivedChar;
boolean newData = false;

void setup() {
    Serial.begin(9600);
    Serial.println("<Arduino is ready>");
}

void loop() {
    recvOneChar();
    showNewData();
}

void recvOneChar() {
    if (Serial.available() > 0) {
        receivedChar = Serial.read();
        newData = true;
    }
}

void showNewData() {
    if (newData == true) {
        Serial.print("This just in ... ");
        Serial.println(receivedChar);
        newData = false;
    }
}

i tried to expand it but i'm not getting the right method to read in two characters at a time -Robin's thread shows how to use start and end markers, but if we know for sure how many characters we want/need - can't we determine that by looping the right number of times ?

like so;

char receivedChar[2];
boolean gotInput = false;
boolean proceed = false;

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print("Enter TWO numbers : ");
  while (!proceed) {
    recv2char ();    
    processinput();
  }
  Serial.print("THE NUMBERS are ");
  Serial.print(receivedChar[0]);
  Serial.print(" and ");
  Serial.println(receivedChar[1]);
}

void recv2char () {
  byte i=0;
  while (i < 2) {
    if (Serial.available() > 0) {
      receivedChar[i] = Serial.read();
    }
    i++;
    gotInput = true;
  }
}

void processinput() {
  if (gotInput) {
    Serial.print("processing...");
    gotInput = false;
    proceed = true;
  }
}

i'm obviously failing in recv2char ()

You have a wrong posistioned counter:

void recv2char () {
  byte i=0;
  while (i < 2) {
    if (Serial.available() > 0) {
      receivedChar[i] = Serial.read();
      i++; //Only increment after something was received
    }
    //i++; Not here!
  }
  gotInput = true; //Move this to here, you do not need to set it for each byte received
}

If you want to receive more than one character why are you using the example recvOneChar().

Use the second example recvWithEndMarker()

...R
Serial Input Basics

Define what you expect from this program. The sketch might be correct but that depends a lot on your expectations.

Robin's thread shows how to use start and end markers, but if we know for sure how many characters we want/need - can't we determine that by looping the right number of times ?

You either know for sure how many characters you want or you have to determine but your sentence contradict itself.

pylon:
Define what you expect from this program. The sketch might be correct but that depends a lot on your expectations.

You either know for sure how many characters you want or you have to determine but your sentence contradict itself.

i guess i should have clarified it should be TWO - single digit numbers.

Danois90:
You have a wrong posistioned counter:

void recv2char () {

byte i=0;
  while (i < 2) {
    if (Serial.available() > 0) {
      receivedChar[i] = Serial.read();
      i++; //Only increment after something was received
    }
    //i++; Not here!
  }
  gotInput = true; //Move this to here, you do not need to set it for each byte received
}

brilliant - that's where i went wrong, thanks a bunch !!

I don't believe the code in Reply #4 is a reliable way to receive 2 characters.

There is a big conceptual jump from receiving a single character to receiving more than one character. If you wish to receive multiple characters (even 2) the order in which they are received matters and you need a system that ensures you know which is which. That is the purpose of using an end-marker or. better still, a start- and an end-marker

...R

void recvOneChar() {
    if (Serial.available() > 0) {

If receiving one character requires that there be more than 0 characters to read, isn't it reasonable to think that reading two characters requires that there be more than 1 character to read?

Robin2:
That is the purpose of using an end-marker...

yep, (i.e. a NewLine character)

@BabyGeezer, what does the actual data stream look like, just two bytes? If so, not good...

PaulS:

void recvOneChar() {

if (Serial.available() > 0) {



If receiving one character requires that there be more than 0 characters to read, isn't it reasonable to think that reading two characters requires that there be more than 1 character to read?

good point; i have always seen it as (Serial.available() > 0) and never though to make it Serial.available() > n)

thanks for that, it might even make the sketch more compact.

so it would be ;

    if (Serial.available() > 1) {
      receivedChar[0] = Serial.read();
      receivedChar[1] = Serial.read();
}

and we can dispense with the counter altogether !

BulldogLowell:
@BabyGeezer, what does the actual data stream look like, just two bytes? If so, not good...

why "not good" ? - that the data is only two bytes, or that i choose to read the stream two bytes at a time.

it probably sounds like an inefficient program getting two bytes from the user at a time instead of setting up a more comprehensive input process, but i'm learning it step-by-step, so i want to know how to do it "simple" first.

Reliable stream I/O requires both ends to know how much data is send per transmission or that each transmission has an end marker. You could also consider this approach, which may be handy:

unsigned int SerialRead(char *buf, unsigned int count, unsigned int timeout = 0)
{
  unsigned long ms = millis();
  unsigned int pos = 0;
  while ( (pos < count) && ( (timeout==0) || (millis()-ms<timeout) ) ) {
    if (Serial.available() > 0) buf[pos++] = Serial.read();
    else delay(5); //Wait for data, can be omitted
  }
  return pos; //return the number of characters read
}

This will continue to read until "count" characters has been read into "buf" or "timeout" milliseconds has elapsed. To disable timeout, set it to 0 (the default).

What method you choose to use, depends on the task to solve. There might be good practice and bad practice, but in the end the following takes the cake: If it works, it works! :wink:

BabyGeezer:
why "not good" ? -

What happens if, on one occasion, the Arduino starts listening just as the second byte is sent? It will think it is the first byte because it has no means to know the difference.

Or what will happen if 3 bytes are sent? It will treat the 3rd byte as the first byte of the next pair.

The receiver has no control over the data that is sent so it must be designed to cope with problems.

...R

PS ... a timeout as suggested in Reply #10 can be useful but I would not implement it in the blocking style of that example. I would use IF rather than WHILE - which may require other changes.

Robin2:
What happens if, on one occasion, the Arduino starts listening just as the second byte is sent? It will think it is the first byte because it has no means to know the difference.

i can't think of a scenario where this will occur - my sketch only runs once so perhaps i would not encounter this.

Robin2:
Or what will happen if 3 bytes are sent? It will treat the 3rd byte as the first byte of the next pair.

yes, this case i have experienced, and i have remedied it by "flushing" as per your method;

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

Robin2:
The receiver has no control over the data that is sent so it must be designed to cope with problems.

this is an important thing to keep in mind, thanks.

i do get that begin and end markers are best practice for precision, but for the case of just two characters (even if looping to get four sets of same), i thought i could just "read them in" and then giving the user one more step to confirm the full set is okay before proceeding to pass it to the program, rather than checking each set as they are entered.

here is my working code now...
(i have now bypassed the processinput()

char receivedChar[2];
// boolean gotInput = false;
boolean proceed = false;

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println("Enter the 2 (single-digit) numbers : ");
  while (Serial.available() > 0) {  Serial.read();  }
  while (!proceed) {
    recv2char ();    
//    processinput();
      Serial.println("flushing...");  // without this it does NOT 'flush' ?
  }
  Serial.print("THE NUMBERS are ");
  Serial.print(receivedChar[0]);
  Serial.print(" and ");
  Serial.println(receivedChar[1]);
  //  Serial.println("Done");
  proceed = false;
  receivedChar[0]=0;
  receivedChar[1]=0;
}

/*
void processinput() {
  if (gotInput) {
    Serial.println("processing...");
    gotInput = false;
    proceed = true;
  }
}
*/

void recv2char () {
  byte i=0;
  while (i < 2) {
    if (Serial.available() > 0) {
      receivedChar[i] = Serial.read();
      i++; 
    }
  }
  proceed = true;
}