77

I'm using two Arduinos to sent plain text strings to each other using NewSoftSerial and an RF transceiver.

Each string is perhaps 20-30 characters in length. How do I convert Serial.read() into a string so I can do if x == "testing statements", etc.?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Joe
  • 2,223
  • 4
  • 18
  • 13
  • Please do check my answer below, it's far more straightforward/simple than the answer you chose – Ihab Feb 19 '14 at 05:27

15 Answers15

120

Unlimited string readed:

String content = "";
char character;
    
while(Serial.available()) {
     character = Serial.read();
     content.concat(character);
}
      
if (content != "") {
     Serial.println(content);
}
kabr8
  • 341
  • 1
  • 2
  • 19
user1415516
  • 1,237
  • 1
  • 8
  • 3
  • Way more reliable on an Arduino Leonardo than any of the other reading methods. Might be an issue on RAM usage due to concat, but if the sketch can take it, it looks like the best way of doing this. – Daniel F Dec 23 '12 at 15:25
  • 23
    Very useful and simple. Though, I found I had to put a small delay between the read of each character over serial - otherwise it printed each character on a separate line rather than concatenating together. `void setup() { Serial.begin(9600); // Initialize serial port } void loop() { String content = ""; char character; while(Serial.available()) { character = Serial.read(); content.concat(character); delay (10); } if (content != "") { Serial.println(content); } } ` – So Over It Mar 16 '13 at 07:56
  • 4
    If you want to move this code to a separate function called from `loop()`, it is VERY important that you `return(content);` rather than `Serial.println()`-ing it. Otherwise, you'll drop into an infinite loop, as it will catch whatever the function prints to Serial, and try to re-process it. – depwl9992 Aug 15 '13 at 19:54
  • if in any case it still did not worked, try the `content.trim()` method – Yakob Ubaidi Jul 25 '14 at 17:40
  • 2
    You must clear content after printing it, otherwise the if block will be executed again, even if new serial data hasn't arrived. – Md. Minhazul Haque May 23 '16 at 23:50
65

From Help with Serial.Read() getting string:

char inData[20]; // Allocate some space for the string
char inChar = -1; // Where to store the character read
byte index = 0; // Index into array; where to store the character

void setup() {
    Serial.begin(9600);
    Serial.write("Power On");
}

char Comp(char* This) {
    while (Serial.available() > 0) // Don't read unless there
                                   // you know there is data
    {
        if(index < 19) // 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
        }
    }

    if (strcmp(inData, This) == 0) {
        for (int i=0; i<19; i++) {
             inData[i] = 0;
        }
        index = 0;
        return(0);
    }
    else {
        return(1);
    }
}

void loop()
{
    if (Comp("m1 on") == 0) {
        Serial.write("Motor 1 -> Online\n");
    }
    if (Comp("m1 off") == 0) {
        Serial.write("Motor 1 -> Offline\n");
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
magma
  • 8,432
  • 1
  • 35
  • 33
  • if this did not work, try to add `inData.trim`() incase we are using the arduino console, there will be newline char. This http://stackoverflow.com/questions/24961402/how-to-compare-string-from-serial-read works for me – Yakob Ubaidi Jul 25 '14 at 17:39
  • 1
    This shouldn't work. The second "if" statement inside the loop will never trigger because you've already read the serial data from the first "if" comparison. – Levi Roberts Feb 03 '15 at 21:52
57

You can use Serial.readString() and Serial.readStringUntil() to parse strings from Serial on the Arduino.

You can also use Serial.parseInt() to read integer values from serial.

int x;
String str;
    
void loop() 
{
     if(Serial.available() > 0)
     {
        str = Serial.readStringUntil('\n');
        x = Serial.parseInt();
     }
}

The value to send over serial would be my string\n5 and the result would be str = "my string" and x = 5

kabr8
  • 341
  • 1
  • 2
  • 19
Ihab
  • 2,225
  • 1
  • 20
  • 32
  • 2
    I was having strange voltage fluctuations when trying to read the Serial input in to a character buffer manually, but using `Serial.readStringUntil()` fixed it all up for me, and made the code more readable! Thanks! – Eddie Fletcher Jan 27 '16 at 00:31
  • 2
    I tested in, and yes it is easier. However compared to character buffer, this operation is significantly requires more time and latency. – Toni Tegar Sahidi Oct 09 '16 at 01:03
  • Interesting, can you share more details about your findings and how much difference in time we are talking about here ? If you have detailed numbers, I would love it if you would share them with us. Thank you ! – Ihab Oct 09 '16 at 18:30
13

I was asking the same question myself and after some research I found something like that.

It works like a charm for me. I use it to remote control my Arduino.

// Buffer to store incoming commands from serial port
String inData;
    
void setup() {
    Serial.begin(9600);
    Serial.println("Serial conection started, waiting for instructions...");
}
    
void loop() {
    while (Serial.available() > 0)
    {
        char recieved = Serial.read();
        inData += recieved; 
    
        // Process message when new line character is recieved
        if (recieved == '\n')
        {
            Serial.print("Arduino Received: ");
            Serial.print(inData);
                
            // You can put some if and else here to process the message juste like that:

            if(inData == "+++\n"){ // DON'T forget to add "\n" at the end of the string.
              Serial.println("OK. Press h for help.");
            }   

    
            inData = ""; // Clear recieved buffer
        }
    }
}
kabr8
  • 341
  • 1
  • 2
  • 19
ladislas
  • 3,037
  • 19
  • 27
5

This would be way easier:

char data [21];
int number_of_bytes_received;

if(Serial.available() > 0)
{
    number_of_bytes_received = Serial.readBytesUntil (13,data,20); // read bytes (max. 20) from buffer, untill <CR> (13). store bytes in data. count the bytes recieved.
    data[number_of_bytes_received] = 0; // add a 0 terminator to the char array
} 

bool result = strcmp (data, "whatever");
// strcmp returns 0; if inputs match.
// http://en.cppreference.com/w/c/string/byte/strcmp


if (result == 0)
{
   Serial.println("data matches whatever");
} 
else 
{
   Serial.println("data does not match whatever");
}
kabr8
  • 341
  • 1
  • 2
  • 19
mrv
  • 311
  • 1
  • 4
  • 8
3

The best and most intuitive way is to use serialEvent() callback Arduino defines along with loop() and setup().

I've built a small library a while back that handles message reception, but never had time to opensource it. This library receives \n terminated lines that represent a command and arbitrary payload, space-separated. You can tweak it to use your own protocol easily.

First of all, a library, SerialReciever.h:

#ifndef __SERIAL_RECEIVER_H__
#define __SERIAL_RECEIVER_H__
    
class IncomingCommand {
  private:
    static boolean hasPayload;
  public:
    static String command;
    static String payload;
    static boolean isReady;
    static void reset() {
      isReady = false;
      hasPayload = false;
      command = "";
      payload = "";
    }
    static boolean append(char c) {
      if (c == '\n') {
        isReady = true;
        return true;
      }
      if (c == ' ' && !hasPayload) {
        hasPayload = true;
        return false;
      }
      if (hasPayload)
        payload += c;
      else
        command += c;
      return false;
    }
};
    
boolean IncomingCommand::isReady = false;
boolean IncomingCommand::hasPayload = false;
String IncomingCommand::command = false;
String IncomingCommand::payload = false;
    
#endif // #ifndef __SERIAL_RECEIVER_H__

To use it, in your project do this:

#include <SerialReceiver.h>
    
void setup() {
  Serial.begin(115200);
  IncomingCommand::reset();
}
    
void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    if (IncomingCommand::append(inChar))
      return;
  }
}

To use the received commands:

void loop() {
  if (!IncomingCommand::isReady) {
    delay(10);
    return;
  }
executeCommand(IncomingCommand::command, IncomingCommand::payload); // I use registry pattern to handle commands, but you are free to do whatever suits your project better.
    
IncomingCommand::reset();
kabr8
  • 341
  • 1
  • 2
  • 19
Blazer
  • 39
  • 1
2

If you want to read messages from the serial port and you need to deal with every single message separately I suggest separating messages into parts using a separator like this:

String getMessage()
{
  String msg=""; //the message starts empty
  byte ch; // the character that you use to construct the Message 
  byte d='#';// the separating symbol 

  if(Serial.available())// checks if there is a new message;
  {
      while(Serial.available() && Serial.peek()!=d)// while the message did not finish
      {
          ch=Serial.read();// get the character
          msg+=(char)ch;//add the character to the message
          delay(1);//wait for the next character
      }
     ch=Serial.read();// pop the '#' from the buffer
     if(ch==d) // id finished
         return msg;
     else
         return "NA";
  }
  else
      return "NA"; // return "NA" if no message;
}

This way you will get a single message every time you use the function.

Spoody
  • 2,852
  • 1
  • 26
  • 36
Qurashi
  • 1,449
  • 16
  • 27
2

Here is a more robust implementation that handles abnormal input and race conditions.

  • It detects unusually long input values and safely discards them. For example, if the source had an error and generated input without the expected terminator; or was malicious.
  • It ensures the string value is always null terminated (even when buffer size is completely filled).
  • It waits until the complete value is captured. For example, transmission delays could cause Serial.available() to return zero before the rest of the value finishes arriving.
  • Does not skip values when multiple values arrive quicker than they can be processed (subject to the limitations of the serial input buffer).
  • Can handle values that are a prefix of another value (e.g. "abc" and "abcd" can both be read in).

It deliberately uses character arrays instead of the String type, to be more efficient and to avoid memory problems. It also avoids using the readStringUntil() function, to not timeout before the input arrives.

The original question did not say how the variable length strings are defined, but I'll assume they are terminated by a single newline character - which turns this into a line reading problem.

int read_line(char* buffer, int bufsize)
{
  for (int index = 0; index < bufsize; index++) {
    // Wait until characters are available
    while (Serial.available() == 0) {
    }

    char ch = Serial.read(); // read next character
    Serial.print(ch); // echo it back: useful with the serial monitor (optional)

    if (ch == '\n') {
      buffer[index] = 0; // end of line reached: null terminate string
      return index; // success: return length of string (zero if string is empty)
    }

    buffer[index] = ch; // Append character to buffer
  }

  // Reached end of buffer, but have not seen the end-of-line yet.
  // Discard the rest of the line (safer than returning a partial line).

  char ch;
  do {
    // Wait until characters are available
    while (Serial.available() == 0) {
    }
    ch = Serial.read(); // read next character (and discard it)
    Serial.print(ch); // echo it back
  } while (ch != '\n');

  buffer[0] = 0; // set buffer to empty string even though it should not be used
  return -1; // error: return negative one to indicate the input was too long
}

Here is an example of it being used to read commands from the serial monitor:

const int LED_PIN = 13;
const int LINE_BUFFER_SIZE = 80; // max line length is one less than this

void setup() {
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  Serial.print("> ");

  // Read command

  char line[LINE_BUFFER_SIZE];
  if (read_line(line, sizeof(line)) < 0) {
    Serial.println("Error: line too long");
    return; // skip command processing and try again on next iteration of loop
  }

  // Process command

  if (strcmp(line, "off") == 0) {
      digitalWrite(LED_PIN, LOW);
  } else if (strcmp(line, "on") == 0) {
      digitalWrite(LED_PIN, HIGH);
  } else if (strcmp(line, "") == 0) {
    // Empty line: no command
  } else {
    Serial.print("Error: unknown command: \"");
    Serial.print(line);
    Serial.println("\" (available commands: \"off\", \"on\")");
  }
}
Hoylen
  • 16,076
  • 5
  • 30
  • 16
2
String content = "";
char character;

if(Serial.available() >0){
    //reset this variable!
    content = "";

    //make string from chars
    while(Serial.available()>0) {
        character = Serial.read();
        content.concat(character);
}
    //send back   
    Serial.print("#");
    Serial.print(content);
    Serial.print("#");
    Serial.flush();
}
kabr8
  • 341
  • 1
  • 2
  • 19
flamaniac
  • 46
  • 4
  • Warning: Sometimes your string will be send in two or more pieces. Put some "end of message" char to check that if you concate all chars from massage. – flamaniac Sep 26 '14 at 10:45
2

Credit for this goes to magma. Great answer, but here it is using c++ style strings instead of c style strings. Some users may find that easier.

String string = "";
char ch; // Where to store the character read

void setup() {
    Serial.begin(9600);
    Serial.write("Power On");
}
    
boolean Comp(String par) {
    while (Serial.available() > 0) // Don't read unless
                                       // there you know there is data
    {
        ch = Serial.read(); // Read a character
        string += ch; // Add it
    }
    
    if (par == string) {
       string = "";
       return(true);
    }
    else {
       //dont reset string
       return(false);
    }
}
    
void loop()
{
    if (Comp("m1 on")) {
        Serial.write("Motor 1 -> Online\n");
    }
    if (Comp("m1 off")) {
        Serial.write("Motor 1 -> Offline\n");
    }
}
kabr8
  • 341
  • 1
  • 2
  • 19
j_v_wow_d
  • 511
  • 2
  • 11
1

If you're using concatenate method then don't forget to trim the string if you're working with if else method.

Sanoj Kashyap
  • 5,020
  • 4
  • 49
  • 75
1

Use string append operator on the serial.read(). It works better than string.concat()

char r;
string mystring = "";

while(serial.available()){
    r = serial.read();
    mystring = mystring + r; 
}

After you are done saving the stream in a string(mystring, in this case), use SubString functions to extract what you are looking for.

kabr8
  • 341
  • 1
  • 2
  • 19
Sarosh
  • 33
  • 7
0

I could get away with this:

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

void loop() {
  String message = "";
  while (Serial.available())
    message.concat((char) Serial.read());
  if (message != "")
    Serial.println(message);
}
Bengt
  • 14,011
  • 7
  • 48
  • 66
0

Many great answers, here is my 2 cents with exact functionality as requested in the question.

Plus it should be a bit easier to read and debug.

Code is tested up to 128 chars of input.

Tested on Arduino uno r3 (Arduino IDE 1.6.8)

Functionality:

  • Turns Arduino onboard led (pin 13) on or off using serial command input.

Commands:

  • LED.ON
  • LED.OFF

Note: Remember to change baud rate based on your board speed.

// Turns Arduino onboard led (pin 13) on or off using serial command input.

// Pin 13, a LED connected on most Arduino boards.
int const LED = 13;

// Serial Input Variables
int intLoopCounter = 0;
String strSerialInput = "";

// the setup routine runs once when you press reset:
void setup() 
{
  // initialize the digital pin as an output.
  pinMode(LED, OUTPUT);

  // initialize serial port
  Serial.begin(250000); // CHANGE BAUD RATE based on the board speed.

  // initialized
  Serial.println("Initialized.");
}

// the loop routine runs over and over again forever:
void loop() 
{
  // Slow down a bit. 
  // Note: This may have to be increased for longer strings or increase the iteration in GetPossibleSerialData() function.
  delay(1);
  CheckAndExecuteSerialCommand();  
}

void CheckAndExecuteSerialCommand()
{
  //Get Data from Serial
  String serialData = GetPossibleSerialData();
  bool commandAccepted = false;

  if (serialData.startsWith("LED.ON"))
  {
    commandAccepted = true;
    digitalWrite(LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  }
  else if  (serialData.startsWith("LED.OFF"))
  {
    commandAccepted = true;
    digitalWrite(LED, LOW);    // turn the LED off by making the voltage LOW
  }
  else if (serialData != "")
  {
    Serial.println();
    Serial.println("*** Command Failed ***");
    Serial.println("\t" + serialData);
    Serial.println();
    Serial.println();
    Serial.println("*** Invalid Command ***");
    Serial.println();
    Serial.println("Try:");
    Serial.println("\tLED.ON");
    Serial.println("\tLED.OFF");
    Serial.println();
  }

  if (commandAccepted)
  {
    Serial.println();
    Serial.println("*** Command Executed ***");
    Serial.println("\t" + serialData);
    Serial.println();
  }
}

String GetPossibleSerialData()
{
  String retVal;
  int iteration = 10; // 10 times the time it takes to do the main loop
  if (strSerialInput.length() > 0)
  {
    // Print the retreived string after looping 10(iteration) ex times
    if (intLoopCounter > strSerialInput.length() + iteration)
    {
        retVal = strSerialInput;
        strSerialInput = "";
        intLoopCounter = 0;
    } 
    intLoopCounter++;
  }

  return retVal;
}

void serialEvent()
{  
  while (Serial.available())
  {    
    strSerialInput.concat((char) Serial.read());
  } 
}
Zunair
  • 1,085
  • 1
  • 13
  • 21
0

This always works for me :)

String _SerialRead = "";
    
void setup() {
  Serial.begin(9600);
}
    
void loop() {
  while (Serial.available() > 0)        //Only run when there is data available
 {
    _SerialRead += char(Serial.read()); //Here every received char will be
                                        //added to _SerialRead
    if (_SerialRead.indexOf("S") > 0)   //Checks for the letter S
    {
      _SerialRead = "";                 //Do something then clear the string
    }
  }
}
kabr8
  • 341
  • 1
  • 2
  • 19
TheJonaMr
  • 9
  • 2