Parsing Serial Data and separating it into variables

Hello, I am working on a project that needs to use the serial channel. the data that is sent over is separated by commas so it looks like this: 200,0,0,1234,567,890,1000,2000,3000,4000,5000
(the numbers in there are made up )
I would like to take the second number and save that to a variable, how would I do that?

I saw something about strtok() but I do not know what that is.

Thanks

Read this tutorial and focus on example 5.

You should also google the strtok() function since that is what you need and is part of standard C/C++

Hi,

here's my code based on the tutorial in the link that you've been sent above.

I use a slightly modified version of example 5 and while my effort isn't great it works for my application.

You may find it a starting point.

// https://forum.arduino.cc/index.php?topic=396450.0
// Serial Input Basics - updated
// Apr 25, 2016, 05:12 pm Last Edit: Apr 26, 2016, 11:27 am by Robin2
// Using Robins 'void RecvWithStartEndMarkers(') & 'void ParseData()'
// Example 5 - Receive with start- and end-markers combined with parsing

const byte NumChars = 32;
char ReceivedChars[NumChars];
char TempChars[NumChars];       		      // temporary array for use when parsing

 // variables to hold the parsed data
char MessageFromPC[NumChars] = {0};
int IntegerFromPC1 = 0;
int IntegerFromPC2 = 0;
boolean NewData = false;

 //myStuff for counting pulses
byte Pulse1=0;			                      // output 1st pulses
byte LongPulse=0;				                  // long or short pulse before last stream	
byte Pulse2=0;			                      // output 2nd pulses
byte Number;				                      
byte I;
byte OutPort=8;
String myReturn = "";                     // send string back to VB
byte PC=0;

//============

void setup() 
{
    Serial.begin(9600);
    digitalWrite(LED_BUILTIN, LOW);
    pinMode(LED_BUILTIN, OUTPUT);		      // initialize ports, LEDs off
    
    digitalWrite(OutPort, LOW);
    pinMode(OutPort, OUTPUT);
}

//============

void loop()
{
    RecvWithStartEndMarkers();			      // test for serial input
    if (NewData == true)			            // input received
    {
        strcpy(TempChars, ReceivedChars); // this temporary copy is necessary to protect the original data
					                                // because strtok() used in parseData() replaces the commas with \0
        ParseData();				              // split the data
        ReplyToInput();				            // handshake / reply to input
        NewData = false;			            // reset new data
    }
}

//============

void RecvWithStartEndMarkers()
{
    static boolean RecvInProgress = false;
    static byte ndx = 0;			// index
    char StartMarker = '<';
    char EndMarker = '>';
    char rc;					// received data

    while (Serial.available() > 0 && NewData == false)
    {
        rc = Serial.read();			          // test for received data

        if (RecvInProgress == true)
        {					// found some!!
            if (rc != EndMarker)			    // <> end marker
            {
                ReceivedChars[ndx] = rc;	// 1st array position=data
                ndx++;				            // next index	
                if (ndx >= NumChars)			// if index>= number of chars
                {	
                    ndx = NumChars - 1;		// index -1
                }
            }
            else				                  // end marker found
            {
                ReceivedChars[ndx] = '\0'; // terminate the string	
                RecvInProgress = false;
                ndx = 0;				          // reset index
                NewData = true;			      // new data received flag
            }
        }

        else if (rc == StartMarker)			  // signal start of new data
        {
        RecvInProgress = true;
        }
    }
}

//============

void ParseData()	      			            // split the data into its parts
{    					                            // Serial.println(MessageFromPC); ECHO data received
    char * StrTokIndx; 				            // this is used by strtok() as an index

    StrTokIndx = strtok(TempChars,",");   // get the first control word
    //strcpy(MessageFromPC, StrTokIndx); 	// copy it to messageFromPC
    PC = atoi(StrTokIndx);
    
    StrTokIndx = strtok(NULL, ","); 		  // this continues after 1st ',' in the previous call
    IntegerFromPC1 = atoi(StrTokIndx);    // convert this part to the first integer

    StrTokIndx = strtok(NULL, ","); 		  // this continues after 2nd ',' in the previous call
    LongPulse = atoi(StrTokIndx);     		// convert this part to the first integer

    StrTokIndx = strtok(NULL, ",");
    IntegerFromPC2 = atof(StrTokIndx);    // last integer
}

//============

 void ReplyToInput()                      // Handshaking with VB code.
{
    if (PC==1)				                    // test OK?
    {
        Pulse1=0;				
        LongPulse=0;
        Pulse2=0;      
        Serial.print(String(PC) + ">");   // acknowledge
    }

    if (PC==2)				                    // receive pulse strings
    {
        Pulse1=IntegerFromPC1;
        Pulse2=IntegerFromPC2;			      // LongPulse =  0 or 1
        myReturn=" - " + String(Pulse1) + " - " + String(LongPulse) + " - " + String(Pulse2) + ">";
        Serial.print(PC + myReturn);      // ask if UNO received correctly

    }

    if (PC==3)				                    // VB Confirms OK to pulse out
    {
        PulsesOut();				              // call pulse out using Pulse1 & pulse2
        Serial.print(PC + myReturn);      // tell VB that it's done 
     }   

    if (PC==4)				                    // VB says to abort			
    {
        Pulse1=0;
        LongPulse=0;        
        Pulse2=0;
        Serial.print(PC + myReturn);      // confirm to VB
    }
}

//============

void PulsesOut()
{
    Number=Pulse1;
    SendPulses();

    delay(180);
 
    if (LongPulse==1)
    {
        digitalWrite(LED_BUILTIN, HIGH);   		// turn the LED on (HIGH is the voltage level)
        digitalWrite(OutPort, HIGH);         	// turn on OptoIsolator LED
        delay(100);                      			// wait for 100 mS
    
        digitalWrite(LED_BUILTIN, LOW);    		// turn the LED off by making the voltage LOW
        digitalWrite(OutPort, LOW);           // turn off OptoIsolator LED
        delay(50);                       			// wait for 50mS
    }

    Number=Pulse2;
    SendPulses();
}

//============

void SendPulses()
{
    for (I = 0; I < Number; I++) 
    {
        digitalWrite(LED_BUILTIN, HIGH);  		// turn the LED on (HIGH is the voltage level)
        digitalWrite(OutPort, HIGH);          // turn on OptoIsolator LED
        delay(50);                       			// wait for 50 mS
    
        digitalWrite(LED_BUILTIN, LOW);    		// turn the LED off by making the voltage LOW
        digitalWrite(OutPort, LOW);           // turn off OptoIsolator LED
        delay(50);                       			// wait for 50mS
    }
}

As I'm sending three numbers representing pulses to be sent into a jukebox I don't want to send incorrect numbers that could mess up the mechanism (parts are expensive). So I have 'messages' forwards and back between my VB front end and the Uno to confirm that the right numbers have been received. If it's OK the Uno pulses the output, through a opto isolator that pulses the -37 volts into the Jukebox.

Peter

If you can receive the string into a NULL-terminated char buffer (i.e. C-string) you can use something like this to parse out the values:

const char
    szTest[] = "200,153954,0,1234,567,890,1000,2000,3000,4000,5000";
    
void setup() 
{
    long
        value;
    char
        *pszStr;
        
    Serial.begin( 9600 );

    //set the pointer to the start of the string
    pszStr = szTest;

    //get first number from list
    value = atol( pszStr );
    Serial.println( value );

    //then loop through, looking for commas and the numbers that follow
    while( 1 )
    {
        //find the next ','
        pszStr = strchr( pszStr, ',' );

        //if not found, we're done
        if( pszStr == NULL )
            break;
        else
        {
            //move the pointer one char past the ',' and convert
            //what's there
            value = atol( ++pszStr );
            Serial.println( value );
                            
        }//else
        
    }//while

    Serial.println( "\nAll done." );
        
}//setup

void loop() 
{    
}//loop

I used atol() but atoi() could also work depending on the size of the values you're sending.

Thanks so much for both of your responses, I don't quite know how to use the jukebox code but I am away from home so I will take a look at it when I have access to the ide, as for the second response what would the output be

You could use a serial transfer library to make things easier.

If you use the library, you can take any element of the receive buffer array and save it to a variable in your code.

Hi,

the important parts of my program are 'void RecvWithStartEndMarkers()' and 'void ParseData()' that came from 'Serial Input Basics - updated - Introductory Tutorials - Arduino Forum'.

I used 'Example 5 - Receive with start- and end- markers combined with parsing'.

You would need to add some more variables in 'ParseData' as you're sending more numbers to it.

I included all of it so you can pick out any bits that you want to use.

Bear in mind that it's my first real C++ progam and it needs some polishing to tidy it up, but it does work as it stands.

Peter

Let me assume that the data are coming over the serial port as ASCII characters with a preamble data byte (say: 0x03) and a postamble data byte (',' = comma = 0x2C). The possible solution to retrieve the decimal numbers from the received frame is (tested using MEGA as sender and NANO as receiver using soft serial ports):

MEGA (sender codes):

#include<SoftwareSerial.h>
SoftwareSerial SUART(10, 11);  //SRX = 10, STX = 11
char myData[] = "200,0,0,1234,567,890,1000,2000,3000,4000,5000";
void setup()
{
  Serial.begin(9600);
  SUART.begin(9600);
}

void loop()
{
  SUART.write(0x03);
  SUART.print(myData);
  SUART.write(',');
  //------------------
  delay(1000);
}

NANO (receiver codes):

#include<SoftwareSerial.h>
SoftwareSerial SUART(10, 11);  // SRX = DPin3, STX = DPin - 4
bool flag = false;
char myData[10];
byte counter = 0;
int intData[11];

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

void loop()
{
  byte n = SUART.available();
  if (n != 0)
  {
    if (flag =  false)
    {
      byte x = SUART.read();
      if (x == 0x03)
      {
        flag = true;     //received preamble code: 0x03
      }
    }
    else
    {
      do
      {
        byte m = SUART.readBytesUntil(',', myData, 10); //receive/store every data item separtaed by comma
        myData[m] = '\0';  //nul charcater
        intData[counter] = atoi(myData);  //retrieve original decimal number
        Serial.println(intData[counter]);
        counter++;
      }
      while (counter != 11);
      flag = false;
      counter = 0;
    }
    Serial.println("========================");
  }
}

Screen shot (NANO):
smnx.png

smnx.png

Alive.

GolamMostafa:
NANO (receiver codes):
changed by luberth if(flag =true){ to if(flag == true){

#include<SoftwareSerial.h>

SoftwareSerial SUART(10, 11);  // SRX = DPin3, STX = DPin - 4
bool flag = false;
char myData[10];
byte counter = 0;
int intData[11];

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

void loop()
{
 byte n = SUART.available();
 if (n != 0)
 {
   if (flag == false)      //changed to == was =   luberth
   {
     byte x = SUART.read();
     if (x == 0x03)
     {
       flag = true;     // received preamble code: 0x03
     }
   }
   else
   {
     do
     {
       byte m = SUART.readBytesUntil(',', myData, 10); //receive/store every data item separtaed by comma
       myData[m] = '\0';  //nul charcater
       intData[counter] = atoi(myData);  //retrieve original decimal number
       Serial.println(intData[counter]);
       counter++;
     }
     while (counter != 11);
     flag = false;
     counter = 0;
   }
   Serial.println("========================");
 }
}




the recieve example does not work as expected

if (flag = false){
should be if (flag ==false){
spend some time figuring out why the first variable 200 whas not recieved it was always 0
my modifeid version of recieve

#include<SoftwareSerial.h>
SoftwareSerial SUART(10, 11);  // SRX = DPin3, STX = DPin - 4
char myData[10];
byte counter = 0;


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

void loop()
{

  if (SUART.available() != 0) {

      if (SUART.read() == 0x03) {  //recieved start flag
      do {
        byte m = SUART.readBytesUntil(',', myData, 10); //receive/store every data item seperated by comma
        myData[m] = '\0';  //nul charcater
        Serial.print(counter);
        Serial.print(" => ");
                   if (counter==5){
                        int test=atoi(myData);
                        Serial.print(" intval = "); 
                        Serial.print(test);
                        Serial.print("  ");
                   }
        Serial.println(atoi(myData));
        
        counter++;
      }
      while (counter != 11);
      counter = 0;
    }
    Serial.println("========================");
  }
}

would like to do 2 way communicate ( mega 2560 touch lcd settings <==> esp8266 webpage settings )
for my thermostat arduino esp8266 (robotdyn mega wifi)
would this be possible with this example?