Moving from Serial.readstring() to Serial.read() for strings

I don’t understand half this stuff either but as I read the code it says;

Create an array of 3 random things (char type)
Then, in setup which is linear and only runs once:
Set up the serial port
Print “enter abc” to it
Then while there is nothing entered just hang about.
Then (crucially) as soon as anything is entered (given the speed of the uC that will only be a single character) move into the for loop
In the for loop;
Loop 3 times (after you corrected this) and each time through loop assign the available char to the char array. So, since only 1 character will have been read by the time the for loop is activated it will assign this to position 0 in the array and then it has nothing to assign to position 1 or 2.
You then go on to correctly print what you assigned into the char array

That is my take on it but, as I say, I know nothing

@pmagowan

your description is 100% correct.

@michael-1-1-1970
in the serial monitor you get
grafik
This clearly shows your for-loop iterates 3 times.

physical analogon to your code
imagine a stampmachine
the stamp machine can stamp sheets of paper at a high speed
the feeding mechanism for the sheets has only a slow speed
the stamp-machine starts stamping as soon as a sheet of paper is in position
0.1 seconds first sheet is in position
0.2 seconds first sheet is stamped
0.3 seconds first sheet is removed
0.4 seconds machine stamps the second time but no sheet is in position
0.5 seconds machine stamps the third time but no sheet is in position
2.0 seconds second sheet is in position (stays empty)
3.0 seconds third sheet is in position (stays empty)

This analogon explains what is happening
You try to read in the next byte at a time where no second / third byte has arrived
hence the strange character in the serial monitor
grafik

best regards Stefan

OMG. You guys are the best! Thank you pmagowan and Stefan. Definitely much more clear. I learned a lot on this thread and I'm grateful to you guys. Stefan, your analogy was awesome so I put in a delay(200); and I got abc. I'm going to go with different code but, your patience paid off for me.

char ch[3];

void setup() {
  Serial.begin(9600);
  Serial.println("Enter abc:");
  int len = sizeof(ch)/(sizeof(ch[0]));  // incase variable length
  while (Serial.available() == 0) {}
  delay(200);
  for (int j = 0; j < len; j++) {
    ch[j] = Serial.read();    
  }
  Serial.print("len of ch[] = ");
  Serial.println(len);     //prints 3
  for (int i = 0; i < len; i++) {
    Serial.print(ch[i]);  
  }
}
void loop() {
}

The above prints out in the serial monitor:

Enter abc:
len of ch[] = 3
abc

I am glad to hear that you got it working, but just adding a delay() when a single character is available is really just like putting a sticking plaster on a wound

At the very least, if you know that you will be receiving 3 characters then why not wait until 3 characters are available and then read them ?

If the input is going to be read in loop() rather than setup() there is an even better way to do it

Do you think that your sketch of post #23 is going to work to receive/print abc if I replace your delay(200) line by this line: delayMicroseconds(100)? If not, then why not?

Programming is art and science and not guess work. Yes! We do trial-and-error; but, finally, the conclusion must have justification.

I like what you did. Adding a delay shows you understand a problem and have a solution. It is not the solution I would have chosen but everyone thinks differently.

You have a line of code in a while loop which waits for a serial input. You told it to wait for any input and it is doing this brilliantly. Like an eager puppy it waits and the instant there is an input it is off into the for loop faster than abc!

The problem is you actually want it to wait for 3 inputs. Your solution is to put a delay in after the first input and hope that during this delay all 3 inputs are there. You can probably predict that this has some drawbacks and may not work if someone is a slow typer for example.

You could just tell the code to wait for 3 inputs before it jumps into the for loop. Then it doesn’t matter how long you take. The uC puppy will sit patiently until all 3 are in and then it is off doing the for loop.

Hey UKHeliBob, regarding your comment “… why not wait until 3 characters are available and then read them?”. Due to my limited understanding of C, I didn’t realize I was doing this. Hopefully the code I posted below avoids “sticking plaster on a wound” although I like the analogy.

GolamMostafa I agree that “Programming is art and science and not guess work. ” Yes, I guess a lot. Most of the time guessing doesn’t work. Sometimes I get lucky. Yes, delay(200) was a guess. :confused: My learning curve is nearly straight up right now. I’m drinking from a fire house…

pmagowan regarding “I like what you did.” Thanks. What I understand: The Arduino can’t shift the buffer as fast as the software can read individual characters out so, I slowed down the software. Without StefanL38’s explanation, I’d still be pacing the floor talking to myself.

Here’s my new code. Notice no delay(200):

char ch[3];
int len = sizeof(ch)/(sizeof(ch[0]));  // incase becomes variable
static byte i = 0;
char endMarker = '\r';
char rc;
boolean entered = false;

void setup() {
  Serial.begin(9600);
  Serial.println("Enter abc:");
  while (1) {
    while (Serial.available() == 0) {}
    rc = Serial.read();
    if(rc == endMarker && entered != true){
      break;
    }
    else if (rc != endMarker){
      entered = true; //don't enter the if
      ch[i] = rc;
      i++; 
      if (i > len) {
        i = len - 1;
      }
    }
    else {
      ch[i] = '\0'; // terminate the string
      break;
    }
  }
  for (int i = 0; i <= len; i++) {
    Serial.write(ch[i]);
  }
}

void loop() {
}

I basically get the same results. Feed back is appreciated.

I have not looked at the sketch in detail (it looks like one of Ronin2's examples) but why do this in setup() using while(1) when you could do it in loop() and let loop() do the repetition ?

1. When you enter abc in the InputBox of the Serial Monitor with Bd = 9600, the message takes about 3.125 ms (1/9600 * 3 * 10) time to arrive to the UNO and get stored in the following (Fig-1) FIFO (first-in first-out) buffer.
serialBuff
Figure-1:

2. When you execute the following codes to wait until a character arrives from SM, a arrives within 1.04 ms (1/9600*10). Then the waiting loop is exited.

while (Serial.available() == 0) {}

3. And then you call the following delay() function to wait for 200ms. It is only 2.08 ms within which the characters b, c arrive into the buffer.

delay(200);

4. Now, all three characters are in the buffer; as a result, your following for() loop works well to print abc on the SM.

 for (int j = 0; j < len; j++) 
{
    ch[j] = Serial.read();    
}

5. If you change delay(200) of Step-3 into delay(1), you will miss the characters -- b and c.

6. So, it is recommended that you don't insert delay() function; rather, you keep checking that all three characters have arrived into the buffer and then read them out for printing. Or, you may read the characters one-by-one as when they are available and show them on the Serial Monitor.

It’s always a bad idea to try to second guess the timing of an asynchronous process

Deal with the data as it comes - never delay.

UKHeliBob Yes, it is Robin2’s example #2. Terrible of me to pretty much copy and paste but, I was tired of spinning my wheels and guessing. Robin’s post explained what the code was doing in a way I understood. I’m reading books on C, blogs, etc. and watching YouTube videos. All are very helpful but, I still don’t understand a lot of the example code that I find on-line. I’m sure you are correct but, I simply needed the code because I don’t have the experience with C needed to figure how to “let loop() do the repetition”.

GolamMostafa, Thank you for the detailed explanation and the diagram. Your point #6 is well taken but, without the code to reinforce your points, I just don’t get it. Hopefully the code I posted in post #27 is a smarter way of getting the characters out of the buffer. No delay(200).

J-M-L I understand now. In my desire to make things work, I made a bad decision. Hopefully post # 27 is a better strategy. Thank you.

indeed - no delay is the way to go !

+1 on @UKHeliBob's comment to make this a function rather than stick that into the setup()

1. Upload the following sketch in UNO.

char myArray[10];

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

void loop() 
{
  byte n = Serial.available(); //there is at least one char
  if( n != 0)
  {
    byte m = Serial.readBytesUntil('\n', myArray, 10); 
    //the above line waits to receive/store all characters until Newline is detected
    //or 10 characters are received or default 1-sec time out happens;
   //whatever happens at the earliest.
    //m = number of characters stored in myArray[] except Newline character ('\n' = 0x0A)

    myArray[m] = '\0'; //insert null-character
    Serial.println(myArray);
  }
}

2. Open Serial Monitor at Bd = 9600.
3. Select Newline option for the Line ending tab of Serial Monitor(Fig-1).


Figure-1:

4. Enter abc in the InputBox of Serial Monitor.
5. Click on the Send button.
6. Check that abc has appeared on the OutputBox of SM.
7. Repeat the process for other strings.

GolamMostafa, this is awesome. Thank you so much! I have never run into Serial.readBytesUntil() before. New to me. This code is so brief. My college professor would have commented "parsimonious code". That was a good thing when we heard this. I will save it and reuse whenever necessary. I hope everyone has a great weekend.

Thank you so much for the good words!

Have fun!

Hello Golam, Curious about something. In this snippet of code:
byte m = Serial.readBytesUntil('\n', myArray, 10); I understand that m contains the total. Does it start at zero? In other words, if m = 2, that means 0 and 1. This is why we can use 'm' to put '\0' at the end?
myArray[m] = '\0';

Thank you for any insight.

Yes but You’ll unfortunately have a bug if 10 characters are received …