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

Hi Guys, Due to possible memory issues, it was recommended that I stay away from String in my sketches. This lead me to Serial.read(). I've read the Serial Input Basics - update several times and tinkered with the posted sketches. I especially appreciated Example 3. I found this post Print Char Array in Arduino and started messing with with the sketch to make sure I understand what is happening. I'm struggling to understand why the Serial.read() data of abc prints out as a⸮⸮. Here is my practice sketch:

//int ch[] = {'a','b','c'};
char ch[3];

void setup() {
  Serial.begin(9600);
  Serial.println("Enter abc:");
  while (Serial.available() == 0) {}
  for (int j = 0; j < ch[3]; j++) {
    ch[j] = Serial.read();    
  }
  
  int len = sizeof(ch)/(sizeof(ch[0]));  // incase variable length
  Serial.print("len of ch[] = ");
  Serial.println(len);     //prints 3
  for (int i = 0; i < len; i++) {
    Serial.print(ch[i]);  //prints: a⸮⸮
        //prints: 97-1-1 using "int ch[3]" instead of "char ch[3]"
  }
  Serial.println(" "); 
  Serial.println(ch); // prints a⸮⸮⸮   // maybe need to add a '\0' to end of Serial.read() data?
}

void loop() {
}

The problem is probably obvious to many of you but, I just don't get it. Why won't it print out abc? Instead, I get a⸮⸮.

Thank you for the insight.

Michael

If one character is available, read many into an area that can hold three.

char ch[3];  //an array with 3 elements numbered 0, 1 and 2
  for (int j = 0; j < ch[3]; j++)

There is no element 3 of the array and what were you expecting that it would hold ?

I know you gave me the answer in one short sentence but, I don't get it. I should have told you that I'm a novice developer if it wasn't obvious.
When I look at other examples, I feel like my code does the same thing. Serial.read() reads in one character at a time. So, as the for loop increments j, ch[j] = Serial.read(); should be populating the 3 locations in the array. Sorry I'm so thick.

I was thinking that ch[3] array contains positions 0, 1, 2. By my way of thinking, j < ch[3] means match on 0, 1 & 2 but, not 3 (I.e. less than 3). Am I missing your point?

No, you match on the content of ch[3], which is not defined because it is a value that is outside of your array.

When you use 3 instead of ch[3] in your for-loop [edit] and you fix the Serial.available issue mentioned above [/edit], things should work as expected.

Alternatively, you can move the following line up and use len.

If you have read the Serial Input Basics, why would you not use that code rather than use something else?

No, it would still read three characters, when only one is available.

Very true, but you already pointed that out.

Then, OP (@michael-1-1-1970 ) may go with the following test codes:

while (Serial.available() != 3) 
  {}
  for (int j = 0; j < 3; j++) 
  {
    ch[j] = Serial.read();
    Serial.print(ch[j]);
  }
  while(1);

I would prefer

while (Serial.available() <= 3) 

but I would not use any while for Serial input.

Good point. I'm not copying it because I don't just want it to work, I want to understand it.

You have declared the following array in the global space:

char ch[3];

There are three locations/members and these are named as ch[0], ch[1], and ch[2]. Being in global space they all contain 0. What does ch[3] contain which is not a member of your array?

I moved the array into the void setup(). I'm not sure why that matters but, it wouldn't work before in the smaller sketch shown below.

Regarding ch[3]. It would contain a random value because I didn't use it. Regarding j < ch[2], I would think this would stop at ch[1] because ch[2] is not less than j? Regardless, I changed j < ch[2] to j < 3 and I get exactly the same results.

Thank you for continuing to try and help me understand what I'm not figuring out.

The red marked characters are the characters read (0xFF),
when no characters are available.

1 Like

This is the main problem. ch[3] means load value located in RAM one byte behind the officially defined array ch[0], ch[1], ch[2]
which can be any kind of value

example:
ch[3] might deliver as value 129
this means your for-loop is running

for (int j = 0; j < 129; j++)

it should be

for (int j = 0; j < 3; j++)

do you see the difference to j < ch[ 3 ] ?

if you use a for-loop the for-loop will run 3 times totally independend from how many characters are really in the receivebuffer
You check

which becomes false if only one character is in the receive-buffer

then the for-loop does run 129 iterations even if only one character is in the receive-buffer

and even in this version
then the for-loop does run 3 iterations even if only one character is in the receive-buffer

StefanL38 . I understand your point "ch[3] might deliver as value 129". That makes sence. Thank you. I changed it to j < 3 and now have better results but, I'm still getting those weird characters.

It looks like ch[j] = Serial.read() is not copying the next character from the maduino buffer when j gets incremented to 1. Crazy because I thought Serial.read() read in 1 character at a time and then the buffer slides down (I.e. after ch[0] is read, ch[1] becomes 1st in line). At least in what I've read and learned from YouTube... In my code this simply isn't working.

read more carefully

this will becomes false if only ONE character is in the receive-buffer

and then this happends:

one character available ==> prints the character
no character available => prints the weird character

That may be a bad move because the array and its contents will now only be available in the setup() function unless that is what you want. It is OK with the sketch you just posted a picture of (why not post it in code tags) but will cause problems if you need to access it outside of setup()

I see too that you have not got the message about using the value of an array element as a parameter in the for loop. What will be in ch[2] when the for loop starts. HINT : local variables like your ch array are initialised to random values unless specific values are defined

Despite everyone’s amazing patience and willingness to try and guide my thinking toward finding the solution on my own, I just don’t completely get it. I think I do understand that I can’t use ch[3] in for (int j = 0; j < ch[3]; j++). I made these changes.

Then there is my use of while (Serial.available() == 0) {} to force entry into the for loop. I don’t get why it matters if I type in 1 or 20 characters in order to drop into the for loop. I apologize for this especially after StefanL38 gave what is probably an awesome outline of why this area of my code isn’t working. Please don’t think ill of me for deciding to find another way. Lets table this topic for now as I’m bit embarrassed.

So far in my learning journey I found double linked lists to be easier to comprehend than working with char and Serial.read! heh heh. From the volume of results in my google searches, this is an area of C that a lot of learning developers struggle with.

I’m not giving up but, the anxiety of not understanding has gotten to me. I just found other code that might do what I want and I seem to understand it (Example 2).

I don’t want you guys to think I’m a quitter. I just need to let this go for awhile as the excessive struggle isn’t healthy for my enthusiasm to learn. Thank you for all your help!!!