Serial Read - Serial Print Problem

Hello, sorry to bother you guys, but im running out of ideas.
I bet im not the first on this situation, but I have searched earth and sky and havent gotten a solution yet.
My problem is easy (for simplicity i post short code)

I have this function, wich reads Serial input, sotres it into a char array, and then, as my project requires some decodification, I post back what I got.

Try to follow me.

this is the function that reads and stores the input into a “char array”

char inData[200];
char inChar;
int index;

void setup() {
Serial.begin(9600);
Serial.println(“Waiting Serial”);

}

void loop() {
read_String();
Serial.println(inData);
}

void read_String() {
Serial.flush();
while(!Serial.available()){;} //wait until there is an answer
index=0;
while(Serial.available() > 0) // Don’t read unless
// there you know there is data
{
if(index < 199) // One less than the size of the array
{
inChar = Serial.read(); // Read a character
inData[index] = inChar; // Store it
index++; // Increment where to write next
inData[index] = ‘\0’; // Null terminate the string
}
}
}

But, here are the "but"s…

I need to “decode” the string, I tried:

*if(strcomp(inData,“OK”)==0){…} //if inData = OK, return will be 0, it works only if OK is sent every single time, but if i send something else, of course, “IF” condition is false, but then i send back “OK”, it never gets to be true again, I dont know why.

//tried with IF
if(inData[0] == ‘O’ && inData[1] ==‘K’){ … } //but for some reason, inData[1] wont work…

So i decided to run some tests…

I tried

if(inData[0]==‘O’){…} //Worked perfectly!!

but then, if I go

if(inData[1]==‘K’){…} // even tho 2nd char is K, it wont work, I tried sending KKKK, KKKKK, aKdawdawd, etc… and wont work

so, I decided to see, what am I storing into inData…

When I use

Serial.println(inData);

the answer is not a line displaying the inData content, instead, is a line, first byte, new line, second byte, etc etc…

lets say I send “Hello”… answer will be

H
e
l
l
o

If I use Serial.print(inData) instead…
of course,
I send “Hello” i read “Hello”
but “I NEED A NEW LINE RIGHT AFTER I READ THE INPUT”

so if I do:

Serial.print(inData);
Serial.println(" ");

Answer will be:

H

e

l

l

o

Like I said, I tried with strcmp, strcpy, String String(inData), Serial.write(inData), but all the time I have the same answer.

I also tried to do something like

while(inData*!=’\0’){*
Serial.print(inData*);*
i++;
}
//and here a new line OUTSIDE the “while”…
Serial.println(" ");
but still, answer will be:
H
e
l
l
o
Im just runngin out of ideas. ANY, any, HELP WILL BE APRECIATED! :smiley:

Maybe read this: http://www.gammon.com.au/serial

In its simplest form a way to capture a String of characters sent from the serial monitor. More useful is to add a delimiting end character to the string of characters sent.

// zoomkat 7-30-11 serial I/O string test
// type a string in serial monitor. then send or enter
// for IDE 0019 and later

String readString;

void setup() {
  Serial.begin(9600);
  Serial.println("serial test 0021"); // so I can keep track of what is loaded
}

void loop() {

  while (Serial.available()) {
    delay(2);  //delay to allow byte to arrive in input buffer
    char c = Serial.read();
    readString += c;
  }

  if (readString.length() >0) {
    Serial.println(readString);

    readString="";
  } 
}

You don't need to use delay() to "help" serial data arrive. If you follow my method here you can receive data without delays. Also the "delay" method assumes the data will arrive continually (ie. with only a 2 mS gap) and will fail if there is a 3 mS gap.

Try a different approach, more blink without delay style:

index = 0;
byte dataStream[200];  // format? do you have to give it a size? I suppose you might.

void setup(){
Serial.begin(some_speed);
}
void loop(){
  if (Serial.available()>0){
  dataStream[index] = Serial.read();
    if (dataStream[index] == '\n'){ // or whatever the terminating character is
    for (x=0; x<=index; x=x+1){
    Serial.print(dataStream[x]);
    Serial.println("");
    index = 0;
    }
    else {index=index+1;}
  }
//
// do other stuff until more data comes in
//
} // end loop

@zoomkat Indeed as Nick pointed out the delay(2) in your code can go wrong quite easily. Change the baud rate to 4800/2400/1200 and it will fail to meet expectations. For a simple test it might work, but I think it is time to update your test sketch 21 ;)

// zoomkat 7-30-11 serial I/O string test
// type a string in serial monitor. then send or enter
// for IDE 0019 and later

String readString;

void setup() 
{
  Serial.begin(9600);
  Serial.println("serial test 0022");
}

void loop() 
{
  int c = 0; // serial read returns an int, not a char

  if (Serial.available() )   // if serial.Available > 0 then the new char is already in the buffer so a delay is not needed
  {
    c = Serial.read();
    readString += c;  // collect the output
  }

  if (c == '\n' || c == '#' ) // some end of input encountered (as the Serial monitor 'does not send '\n   I added a # as end of input too;
  {
    Serial.println(readString);
    readString = "";
  } 
}

as alternative you can also write this sample without Serial.available()

String readString;

void setup() 
{
  Serial.begin(9600);
  Serial.println("serial test 0023");
}

void loop() 
{
  int c = 0; // serial read returns an int, not a char; initialized to 0 to keep compiler happy

  if ((c = Serial.read()) != -1)  // if I can read a byte
  {
    readString += c;  // I add it the buffer

    if (c == '\n') // if end of input encountered
    {
      Serial.println(readString);  // I print my buffer
     readString = "";  // and clean it
    } 
  }

  // do other things

}

I'll be back on the original question asap

Give this a try to search for the OK in the input

char inData[200];
int index = 0;

void setup() 
{
  Serial.begin(9600);
  Serial.println("Waiting Serial");
  Serial.flush();
}

void loop() 
{
  read_String();
}


void read_String() 
{
  int c = 0; 
  if (Serial.available() == 0) return;  // if no char to process skip
  
  c = Serial.read();
  inData[index++] = c;
  inData[index] = '\0';  // keep end of string right
  
  if (strstr(inData, "OK") != NULL) // http://www.cplusplus.com/reference/cstring/strstr/
  {
    Serial.println("0");
    index = 0;
  } 

  if (strstr(inData, "FOO") != NULL) // 2nd test
  {
    Serial.println("42");
    index = 0;
  } 

  if (index == 199)  // prevent buffer overflow
  {
    index = 0;
  }
}

Remove Serial.flush() until you read the reference page for it.

It doesn't do what you think it does.

http://arduino.cc/en/Serial/Flush

Also the "delay" method assumes the data will arrive continually (ie. with only a 2 mS gap) and will fail if there is a 3 mS gap.

Probably goes to show you fail to validate your assumptions. Change the 2ms to 3ms and the code still works the same.

For a simple test it might work, but I think it is time to update your test sketch 21

Not really. It works under the conditions described. I tried your code and it didn’t work. Why? You assume the person knows that they will have to change the default setting in the serial monitor to make it work. Unvalidated assumptions is why airplanes fall out of the sky and such.

zoomkat:
Probably goes to show you fail to validate your assumptions. Change the 2ms to 3ms and the code still works the same.

This is nonsense. Changing the code to delay for 3 mS is not what I am talking about.

What I am saying is that serial is an asynchronous protocol. That is, there is no guarantee that bytes will arrive at a particular rate. The baud rate merely says that for a particular byte the bits will arrive at a certain rate.

For example the sending end might send:

A <3 seconds gap) B <4 seconds gap) C <2 seconds gap)

Your proposed code would definitely fail there.

The only valid way of handling serial input is to look for a delimiter (for example, newline) and then act when that is received.

zoomkat:
Unvalidated assumptions is why airplanes fall out of the sky and such.

Exactly. And you are assuming the bytes arrive at a certain rate.

zoomkat:

For a simple test it might work, but I think it is time to update your test sketch 21

Not really. It works under the conditions described. I tried your code and it didn't work. Why? You assume the person knows that they will have to change the default setting in the serial monitor to make it work. Unvalidated assumptions is why airplanes fall out of the sky and such.

Thanks, I tested it again, and you're right it didn't work as I forgot to cast the int c to char. (caused by the behavior of the string class I seldom use, but still my fault ;) The code works without the delay(2) independent of the time between bytes. For the rest it just did work, didn't make any user assumptions there AFAIK. The modified sketch below still uses 9600 baud, the default setting. So please explain more explicit what you assume that my assumptions were :)

(and yes I agree 90% about the planes, the other 10% is about why planes do not get into the air in the first place ;)

// zoomkat 7-30-11 serial I/O string test
// type a string in serial monitor. then send or enter
// for IDE 0019 and later

String readString;

void setup() 
{
  Serial.begin(9600);
  Serial.println("serial test 0022");
}

void loop() 
{
  int c = 0; // serial read returns an int, not a char

  if (Serial.available() )   // if serial.Available > 0 then the new char is already in the buffer so a delay is not needed
  {
    c = Serial.read();
    readString += (char)c;  // collect the output
  }
  
  if (c == '\n' || c == '#' ) // some end of input encountered (as the Serial monitor 'does not send '\n   I added a # as end of input too;
  {
    Serial.println(readString);
    readString = "";
  } 
}

The code works without the delay(2) independent of the time between bytes. For the rest it just did work, didn’t make any user assumptions there AFAIK. The modified sketch below still uses 9600 baud, the default setting. So please explain more explicit what you assume that my assumptions were

Extremely lame spin control. Then why did you change this:

if (c == '\n') //

to this so your code would work without changing the default serial monitor settings? :wink:

 if (c == '\n' || c == '#' )

This is nonsense. Changing the code to delay for 3 mS is not what I am talking about.

I copied your comment exactly as you wrote it. If you were actually thinking something different, then perhaps you need to collect thoughts before you post.

Exactly. And you are assuming the bytes arrive at a certain rate.

I assume the bytes arrive as they are sent from the serial monitor. As best as I can tell, when characters are typed into the serial monitor text box and the enter key is depressed, the bytes are not sent like you describe below.

A <3 seconds gap) B <4 seconds gap) C <2 seconds gap)

Your proposed code would definitely fail there.

Your code proposal may also fail if there is noise on the the tx/rx lines, the tx/rx lines are not connected, the arduino is not powered up, the cat peeded on the arduino, etc. If one wants, one can make up unbounded failure modes. YMMV

zoomkat: I assume the bytes arrive as they are sent from the serial monitor.

The original post never said so.

I have this function, wich reads Serial input, sotres it into a char array, and then, as my project requires some decodification, I post back what I got.

He said it "reads Serial input". I thought you were opposed to assumptions?


zoomkat:

This is nonsense. Changing the code to delay for 3 mS is not what I am talking about.

I copied your comment exactly as you wrote it. If you were actually thinking something different, then perhaps you need to collect thoughts before you post.

I'll try again. Your posted code processes one byte, then waits 2 mS to allow for another byte to arrive. If the next byte takes 3 mS to arrive, Serial.available() will return 0 and you will exit the loop. However you are now printing data that has not fully arrived.

To be fair, what you actually had was:

  while (Serial.available()) {
    delay(2);  //delay to allow byte to arrive in input buffer
    char c = Serial.read();
    readString += c;
  }

So, you know there is data available, but you wait 2 mS. Why? Just to waste time? I am presuming you are doing it to allow for the next byte to arrive (not the current one, you know that is there). So the whole loop is designed around the assumption that a 2 mS delay is adequate, and that the sending end won't pause briefly (eg. to take a reading, process an interrupt, etc.)

... which means that the sketch is obliged to stop and wait for the whole message to dribble in, and can't do anything else in the meantime. It also means that the receive timing is based on hard-coded values which have to be chosen by trial and error - if the serial port is slower than you expect then the algorithm will time out prematurely; if it is faster then the code will fail to receive bytes as quickly as they arrive and would leave them to accumulate in the receive buffer, potentially resulting in a buffer overflow and lost bytes.

Although it can obviously be made to work under the right circumstances it's just not a good general way of handling serial input and I think you're doing novices a disservice by recommending this approach to them.

The preferred way to handle serial input is to read each byte as soon as it becomes available and then determine whether the complete message has been received, as Nick's tutorial explains. It's simple, robust and works regardless of the serial port settings and regardless of the timing of the incoming byte stream.

The 'delay loop' approach has no advantages that I can see, and so many disadvantages that it simply doesn't make sense.

Quote from: zoomkat on Today at 05:14:32 pm I assume the bytes arrive as they are sent from the serial monitor.

The original post never said so.

So what do you not understand about "// type a string in serial monitor. then send or enter"?

So, you know there is data available, but you wait 2 mS. Why? Just to waste time?

If you are still stumped on that subject, well that is not my issue.

So the whole loop is designed around the assumption that a 2 mS delay is adequate, and that the sending end won't pause briefly (eg. to take a reading, process an interrupt, etc.)

As written, the code works when something like the OP was typing in the serial monitor and sent at 9600 baud. I even mentioned in my original post that using a delimiter would be better. The code simply demonstrates that the characters can be captured into a String to be used for useful things. If the OP has specific needs for his serial operations, then those needs can be addressed.

zoomkat:

Quote from: zoomkat on Today at 05:14:32 pm I assume the bytes arrive as they are sent from the serial monitor.

The original post never said so.

So what do you not understand about "// type a string in serial monitor. then send or enter"?

OK, I give up. The [u]original[/u] post never said so.

You are posting code that works under carefully constructed circumstances. You even ticked me off for making assumptions.

Oh well, never mind.

@OP: I am trying to solve your problem. Zoomkat is going out on a tangent. If his tangent works for you, well and good.

So what do you not understand about "// type a string in serial monitor. then send or enter"?

OK, I give up. The original post never said so.

Not much I can say. :roll_eyes:

http://forum.arduino.cc/index.php?topic=191199.msg1413505#msg1413505