Reading an Instruction from Serial

I am attempting to send instructions to the arduino by serial.

I want to be able to receive strings like ‘F100’, ‘R050’, ‘P015’, ‘S000’.

The first character (F, R, P, S) is to call a function, and the next 3 digits are a value - for example ‘F100’ means Forward 100units.

How can I break this string into the character and the integer?

This is what I have so far:

char buf[3];

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


//Read Serial when there are enough bytes
boolean read_bytes(int num){
  if(Serial.available() >= num){
    for(int i = 0; i < num; i++){
      buf[i] = Serial.read();
    }
    return true;
  }
  else{
    return false;
  }
}


void loop() {
  //Check Serial
  if (read_bytes(4))
  {
    //We have an instruction
    //Do something
    Serial.println(buf);
  }
}

How can I break this string into the character and the integer?

Relatively easy, but...

char buf[3];

Will hold 2 characters plus a trailing NULL.

  if (read_bytes(4))

Is going to try to put 5 elements in that 3 element array.

The read_bytes function is not adding the terminating NULL;

If you correct those issues:

char cmd = buf[0];
buf[0] = '0';
int val = atoi(buf);

If you send Q674, cmd will contain 'Q" and val will be 674.

Thanks that worked brilliantly, although I did need to keep read_bytes(4)) otherwise it didn't work.

Which led me on to the next question, what ways could I implement some sort of error correction - for example if only part of a command is received 'F01' it will mess things up, and I will need to send another character to get things back in sync.

although I did need to keep
Code:
read_bytes(4))otherwise it didn’t work.

But you did increase the size of your buffer, didn’t you?

But you did increase the size of your buffer, didn't you?

Yep: char buf[5];

what ways could I implement some sort of error correction - for example if only part of a command is received ‘F01’ it will mess things up, and I will need to send another character to get things back in sync.

If F01G456H999 is received on the serial port, your current code will read F01G from the serial port. I’m not sure how you think that reading one more character will get things back in sync.

If, on the other hand, you send , then the start and ends of a packet can be detected, and a corrupt packet detected.

#define SOP '<'
#define EOP '>'

bool started = false;
bool ended = false;

char inData[80];
byte index;

void setup()
{
   Serial.begin(57600);
   // Other stuff...
}

void loop()
{
  // Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {
    char inChar = Serial.read();
    if(inChar == SOP)
    {
       index = 0;
       inData[index] = '\0';
       started = true;
       ended = false;
    }
    else if(inChar == EOP)
    {
       ended = true;
       break;
    }
    else
    {
      if(index < 79)
      {
        inData[index] = inChar;
        index++;
        inData[index] = '\0';
      }
    }
  }

  // We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
    // The end of packet marker arrived. Process the packet

    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
  }
}

Where it says “Process the packet” is where you get the command and value from the packet, and do something with them.

This code is more robust, in that it only does something when a complete packet arrives, is non-blocking, and allows for any number of digits in the value portion of the packet.

You might take the view that the serial characters come in 1 at a time and process them as they come in rather than add each to a buffer, wait for the buffer to fill and THEN process them. Guess which is faster and uses less RAM?

Simple servo test code that captures a data string, parses out the desired data from the string, and then converts the data into a number.

// zoomkat 12-13-11 serial servo (2) test
// for writeMicroseconds, use a value like 1500
// for IDE 1.0
// Powering a servo from the arduino usually DOES NOT WORK.
// two servo setup with two servo commands
// send eight character string like 15001500 or 14501550
// use serial monitor to test

#include <Servo.h> 
String readString, servo1, servo2;
Servo myservo1;  // create servo object to control a servo 
Servo myservo2;

void setup() {
  Serial.begin(9600);
  myservo1.attach(6);  //the pin for the servo control 
  myservo2.attach(7);
  Serial.println("two-servo-test-1.0"); // so I can keep track of what is loaded
}

void loop() {

  while (Serial.available()) {
    delay(3);  //delay to allow buffer to fill 
    if (Serial.available() >0) {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //makes the string readString
    } 
  }

  if (readString.length() >0) {
      Serial.println(readString); //see what was received
      
      // expect a string like 07002100 containing the two servo positions      
      servo1 = readString.substring(0, 4); //get the first four characters
      servo2 = readString.substring(4, 8); //get the next four characters 
      
      Serial.println(servo1);  //print to serial monitor to see results
      Serial.println(servo2);

      int n1 = servo1.toInt();
      int n2 = servo2.toInt();
      
      myservo1.writeMicroseconds(n1); //set servo position 
      myservo2.writeMicroseconds(n2);
    readString="";
  } 
}

Hey, zoomkat. You know better than that!

Good habits on a PC are not always good habits on a microcontroller. But people will write to their own level of understanding/training.

Some day I may put a kitchen sink on the front of a bicycle because of the conveeeeeeenience. Besides, it will go with the bathtub on the back.

This code is more robust, in that it only does something when a complete packet arrives, is non-blocking, and allows for any number of digits in the value portion of the packet.

Great - this works well for me thank you.

I added the following so that I can have a Key/Value type of packet each of any length:

#define MOP '|'
// We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
    // The end of packet marker arrived. Process the packet
    Serial.print("Packet received: ");
    Serial.println(inData);

    String cmd;
    
    for (int i =0; i < index; i++)
    {
      if (inData[i] != MOP)
      {
        cmd+= inData[i];
        inData[i] = '0';
      }
      else if (inData[i] == MOP)
      {
        inData[i] = '0';
        break;
      }
    }
    
    int value = atoi(inData);
    
    Serial.print("Command: ");
    Serial.print(cmd);
    Serial.print(" Value: ");
    Serial.println(value);
    
    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';

Any suggested improvements?

Any suggested improvements?

Yes. Ditch the String class. It is not needed, and is a resource hog.

Delete the processing of MOP, during the read of the serial data (which, logically, should be =, anyway). Then, when the complete packet arrives, use strtok() to get the key (cmd) and value tokens, and process them.

Delete the processing of MOP, during the read of the serial data (which, logically, should be =, anyway). Then, when the complete packet arrives, use strtok() to get the key (cmd) and value tokens, and process them.

So this will split the packet wherever a "=" is found, and then I guess I could push the values into an array within the while loop

    char delims[] = "=";
    char *result = NULL;
    result = strtok( inData, delims );
    while( result != NULL ) {
        Serial.println(result);
        result = strtok( NULL, delims );
    }

To take it a step further how would I best allow for multiple key value pairs? eg: &key=value&key=value, would I use some sort of nested strtok() function? or is it be better to just have smaller packets with a single key/value?

To take it a step further how would I best allow for multiple key value pairs?

Change the delimiter array to include &, and another call to strtok(), to get the value, before the while loop, and another one in the loop, to get the next value. You'd then need if/else if statements to determine where to store the value, based on the key.