create String from chars read in serial port

Hi, Im reading in data over serial on my UNO, I was trying out the readString function but if I send a large amount of data it seems to top out and stop reading, I guess tops out the buffer, What im doing is reading in chars and adding them together in a String variable, this works fine for the large amount of data I receive but I cant do comparisons if(String =="ABC") etc. but the this doesn't work as the data is printed out a char at a time, when printing to the serial port I don't print a line for readability but when I want to do comparisons I cant as its a char. Printing using println will print the individual characters with a new line and carriage return separating each character.

In my case I want to check for "SD" in the data but its only characters, I know this is probably a rudimental question but I've been trying to do a few things to get it to work with no real success.

Either that or is there a way to allow the readString to work with an infinite size of data? Sorry for posting a silly question

  // add any incoming characters to the String:
   if (Serial.available() > 0) { //was while
     char inChar = Serial.read();
     txtMsg += inChar;

  if(txtMsg !="")

{

  Serial.println(txtMsg);

   txtMsg.trim();
  if(txtMsg.startsWith("SD"))
  {
if(Serial.available()>0)
{
  rec=Serial.readString();
}
    
 Serial.println("PRINTING");
 Serial.println(rec);
    }

     
  }
 txtMsg="";
 rec="";
}

}

Either that or is there a way to allow the readString to work with an infinite size of data?

That doesn't work even on a supercomputer as the string is stored in memory and there is no infinite memory on any computer. I guess what you're trying to find is the find() method of the Stream class (HardwareSerial is a subclass of it). You can specify a pattern to search for in the input stream, so the characters are read until the search string is found.

If that doesn't answer your question I didn't understand it. Please define in more detail.

BTW: The UNO has a RAM (working memory) size of 2kB. Every string you define in your sketch without the F() macro will also consume RAM, so be careful not to waste it.

Hi Pylon, Sorry not an infinite string length, I'm reading data from an SD card shield that has is sent as a String to the terminal so time date, a temperature reading etc., the problem is that I have a CSV file with multiple lines of data in it. when I print the entire contents and try to read it on the Uno it starts reading through it just fine using the readString function but it suddenly stops where it kind of just stops printing the string halfway through the sentence sort of like "11:12:03, 23/12/2017, 32.05C" and the last line will be like "11:12:24, 23/12/20" however, the adding chars together method works fine for the data. This has a new line on it as I send the string using println for each line.

I think the readString maxes out, when the reader UNO is doing its serial reading of the string is there a way of clearing the buffer on each received string that isn't to cumbersome? I would rather work with the strings as I want to be able to do comparisons using .beginsWith and .endsWith etc Thanks guys

On embedded processors with limited resources (or any computer with finite resources :wink: ), it is much more efficient to process the data as it is received, instead of accumulating all the data and then processing it all at once.

Although it is easier to understand (and usually read) the latter technique, you may not have enough RAM or CPU time to do it that way.

The former technique usually requires some kind of Finite-state Machine. Here is a very simple FSM that watches for “SD” in the input stream:

unsigned int matchCount = 0;

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

void loop()
{
  if (Serial.available()) {
    char c = Serial.read();

    if (c == 'S') {
      matchCount = 1;

    } else if ((matchCount == 1) && (c == 'D')) {
      Serial.println( F("Got it!") ); // F macro saves RAM!
      matchCount = 0; // reset FSM

    } else { // other char resets FSM
      matchCount = 0;
    }
  }
}

The “state variable” is matchCount. It “remembers” how many characters have matched so far. Whenever an ‘S’ is received, it goes to state 1. If a ‘D’ is received and it has matched the ‘S’ already (i.e., matchCount is 1), it knows that “SD” was received. If not, it restarts the FSM.

Notice that it does not require any RAM besides the state variable.

BTW, this also avoids the nasty String class. There are many reasons to avoid String. Here is a good explanation: The Evils of Arduino Strings

If you really need to hold on to characters, use a char array:

    char buffer[10];

These are called “C strings”, with a lower-case ‘c’. Some tutorials here and here. Or just ask another question…

I see from your reply that you also want to compare string values. You can use strcmp to do that:

    if (strcmp( buffer, "SD" )) {

… or strstr:

    if (strstr( buffer, "SD" ) == &buffer[0]) {  // same as "beginsWith"

or

    if (strstr( buffer, "SD" ) == &buffer[ len-2 ]) { // same as "endsWith"

… where len is:

    size_t len = strlen( buffer );

There are many, many string functions (more than the String class can provide), so it can be overwhelming at first.

For this kind of usage, your FSM may get reset when a CR/LF is received (end of line), or it may watch for ‘,’ characters to delimit each field. When it has received a certain number of commas, it could accumulate a few characters into the buffer:

     unsigned int fieldCount = 1;
      unsigned int charCount  = 0;
      char         buffer[10];
const size_t       BUFFER_SIZE = sizeof(buffer);

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

void loop()
{
  if (Serial.available()) {
    char c = Serial.read();

    if (c < ' ') { // This matches all control characters, like CR or LF
      // reset FSM
      fieldCount = 1;
      charCount  = 0;

    } else if (c == ',') {

      // All of field 2 received?
      if (fieldCount == 2) {
        buffer[ charCount ] = '\0'; // NUL-terminate the C string
        Serial.print( F("Field 2 = '") ); // F macro saves RAM!
        Serial.print( buffer );
        Serial.println( '\'' );
      }

      fieldCount ++; // step to next field
      charCount = 0;

    } else if (fieldCount == 2) {

      // Save another character, if there's room
      if (charCount < BUFFER_SIZE-1) {
        buffer[ charCount++ ] = c;
      }

    }
  }
}

When the end of field 2 is reached, it prints what it has received.

Or… you can save one line in a buffer, then parse it with strtok:

     unsigned int charCount  = 0;
      char         buffer[60]; 
const size_t       BUFFER_SIZE = sizeof(buffer);

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

void loop()
{
  if (Serial.available()) {
    char c = Serial.read();

    if (c < ' ') { // This matches all control characters, like CR or LF
      // The entire line is ready

      if (charCount > 0) { // skip empty lines
        buffer[ charCount ] = '\0'; // NUL-terminate the C string

        // Display the entire line we saved in 'buffer'
        Serial.print( F("line = '") ); // F macro saves RAM!
        Serial.print( buffer );
        Serial.println( '\'' );

        // Use strtok to parse the line by one field at a time.
        char        *field      = strtok( buffer, "," );
        unsigned int fieldCount = 1;

        while (field != nullptr) {
          // Display each field
          Serial.print( F("Field ") );
          Serial.print( fieldCount );
          Serial.print( F(" = '") );
          Serial.print( field );
          Serial.println( '\'' );

          // Did we receive the special keyword in field 2?
          if ((fieldCount == 2) && (strcmp( field, "SD" ) == 0)) {
            Serial.println( F("Got it!") );
          }

          // get next field
          fieldCount++;
          field = strtok( nullptr, "," );
        }

        // reset FSM
        charCount  = 0;
      }

    } else {

      // Save another character, if there's room
      if (charCount < BUFFER_SIZE-1) {
        buffer[ charCount++ ] = c;
      }
    }
  }
}

This saves up to 60 characters from one line, then uses strtok to process one field at a time in a loop. So many choices! :slight_smile:

Cheers,
/dev

Wow absolutely marvellous, massive thank you. I really appreciate it. Thanks you ;D :D :D :D :D :D

You’re welcome. Minor correction for “endsWith” above.