0

I am using software serials on my Arduino nano to read data from my esp01 in functionality testing for the software serial. I simply make the esp01 to send a string "OK" to an Arduino's software rx serial port (in this case pin 9) , and then the Arduino prints it on the monitor via its hardware serial (USB).

I've found that when I allocate space for the string in Arduino using char str[2], the message seems to be correct. However, with char* str = (char*)malloc(2), there appear to be an unkown character tai ling the string "OK"

Below is the code I use

#include<SoftwareSerial.h>


SoftwareSerial s(9,10);
void setup() {
  pinMode(2,OUTPUT);
  Serial.begin(9600); 
  s.begin(9600);      // link to esp01
}

void loop() {
  
  if(s.available() > 0){
    char txt[2];
    int indx = 0;

   // read input
    while(s.available() > 0 && indx <=SIZE-1){
        char buff;
        buff=  s.read();
        txt[indx] =  buff;
        Serial.println(indx);
        indx++;
     
    }
   // bink an LED for debugging
   if((String)txt == "OK"){
    digitalWrite(2,HIGH);
    delay(500);
    digitalWrite(2,LOW);
    delay(500);
   }

   //reset index
   if(indx ==2){
     
     Serial.println(txt);
     Serial.print("Size of string:");
     Serial.println(sizeof(txt));
    indx = 0;
   }
  } // if(s.available())
  
}

here is the print-out of the serial monitor

enter image description here

That tailing square is the myster for me.

Any help would be grateful!!

Boris

3
  • 1
    To store two characters you need three slots in the array. You forgot about the null terminator to mark the end of the string. So depending on where the array is in memory there may or may not be extra junk behind it that gets printed because the string isn’t terminated.
    – Delta_G
    Commented Jul 8, 2020 at 17:58
  • 1
    you need 3 positions. 'O 'K' '\0'
    – Juraj
    Commented Jul 8, 2020 at 17:58
  • Re “while(s.available() > 0 && indx <=SIZE-1)”: where did you define SIZE? Commented Jul 9, 2020 at 8:26

2 Answers 2

1

First, a few general notes:

  • The difference between char str[2] and char* str = (char*) malloc(2) is irrelevant to you problem. It changes the place where the array is stored, the meaning of sizeof str, and malloc() will blow up your RAM if you call it repeatedly and forget to free().
  • In your code, sizeof(txt) is the constant 2: the actual size in bytes of the array txt, irrespective of how much relevant data it holds.
  • You want txt and indxto remember their contents across calls to loop(). You should either make them global or qualify them as static.
  • Avoid the String class if at all possible. Here, since you use it only to compare two strings, you can use the traditional strcmp() function instead.

Now, the bulk of my answer is just about expanding on what has already been said about properly terminating strings.

A string is a variable-length list of characters. Whenever you are dealing with a list that has a variable length, you need to somehow keep track of this length. There are two common ways to do so:

  1. either you store the length in a variable,
  2. or you put a sentinel value (a value that would be invalid within the list) after the last element to mark the end of the list.

Both methods are valid, and I am going to illustrate both of them below. However, in the case of strings, the C++ language uses method 2, with the ASCII “NUL” character (numeric value 0) as a sentinel.

Method 1:

void loop() {
  static char txt[2];
  static size_t indx = 0;

  while (s.available()) {
    // read input
    char c = s.read();
    txt[indx++] = c;

    // report
    Serial.print("Received: '");
    Serial.print(c);
    Serial.print("', string: \"");
    Serial.write(txt, indx);
    Serial.print("\", size: ");
    Serial.println(indx);
    if (indx == 2 && memcmp(txt, "OK", 2) == 0) {
      Serial.println("Correct message");
    }

    // reset index
    if (indx >= sizeof txt) {
      indx = 0;
    }
  } // if(s.available())
}

Some things to note:

  • The indx variable holds the length of the string. It also happens to be the index of the next available cell in the txt array, if any.
  • The string is printed using the write(const char *buffer, size_t size) method, which accepts a size as a second argument. Unlike print(const char *), it doesn't depend on the string being NUL-terminated.
  • In order to check that we received “OK”, we have to check for both the correct length (indx == 2) and correct contents. The contents is checked using the memcmp() function, which can compare arbitrary byte arrays and, again, does not depend on NUL-termination.
  • An important loop invariant here is idx < sizeof txt. It is true at initialization and is restored by the “reset index” section at the end of the loop. The “read input” section depends on this invariant to not overflow the buffer.

Method 2:

void loop() {
  static char txt[3];
  static size_t indx = 0;

  while (s.available()) {
    // read input
    char c = s.read();
    txt[indx++] = c;
    txt[indx] = '\0';

    // report
    Serial.print("Received: '");
    Serial.print(c);
    Serial.print("', string: \"");
    Serial.print(txt);
    Serial.print("\", size: ");
    Serial.println(indx);
    if (strcmp(txt, "OK") == 0) {
      Serial.println("Correct message");
    }

    // reset index
    if (indx >= sizeof txt - 1) {
      indx = 0;
      txt[indx] = '\0';
    }
  } // if(s.available())
}

Again, some notes:

  • This is actually an hybrid of methods 1 and 2, as the indx variable is still used to keep track of where to insert the next incoming character. This just happens to be more convenient and efficient than putting that character into its own length-1 string and using strcat().
  • The array is now one cell longer, to make room for the terminating NUL.
  • There is another loop invariant now: correct string termination (txt[indx] == '\0'). This invariant has to be enforced both on reading and on resetting.
  • The string being NUL-terminated, it can now be printed with print() and compared with strcmp(). The string literal "OK" is guaranteed to be NUL-terminated by the compiler.

This second method is generally safer because there are a lot of functions and methods in Arduino and C++ that expect strings to be NUL-terminated. If your strings are not, you have to be extra careful to not use any of these string-oriented functions.

0

As others have pointed out, C strings are null-terminated. So to hold 2 characters of content you need a 3-byte array. Your code that reads the chacters into the buffer also needs to be changed. (That code isn't adding the required terminating null.)

You should probably rewrite your code to use the strncat() function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.