Need help and guidance to understand how to parse character array

First time poster and a real newbee here. I’ve been searching through the net trying to understand how to parse some data. I can’t get my head around it… I’m using an example to receive data via I2C. I receive the data fine and the array prints out as expected but I would like to parse the array that is delimited by commas. I’ve seen examples that use atoi, strok but I don’t understand them… yet.

The received message looks like 255,255,255 (any one of the triplets can be a number from 0 to 255). I want to assign the first number to red, second to green and third to blue as follows:

red = 255
green = 255
blue = 255

Any help or comments greatly appreciated.

Thanks.
Don

// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this

// Created 29 March 2006

// This example code is in the public domain.

#include <Wire.h>

char message[14];
int red;
int green;
int blue;

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop() {
  delay(100);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
  int idx=0;
  while (1 < Wire.available()) {  // loop through all but the last
    message[idx] = Wire.read();   // receive byte as a character and store in array
    idx++;
  }
  message[idx] = Wire.read();   // receive last byte
  Serial.println(message);      // print out entire message from array
 
}

Hello and welcome,

To understand strtok, see here.

Here is another example: http://ideone.com/XNGIgr

There are other ways, such as sscanf (easier to use in your case, but uses more memory)

Here is an example: http://ideone.com/EdYO1D

(Edit: oops, fixed the strtok example.. )

Ok I was doing some reading from Arduino Cookbook, 2nd Edition and found some code that splits the data by the comma’s and prints the individual number but I need to figure out how to assign each number to the red, green & blue variables.

// Wire Slave Receiver
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Receives data as an I2C/TWI slave device
// Refer to the "Wire Master Writer" example for use with this

// Created 29 March 2006

// This example code is in the public domain.

#include <Wire.h>

char message[14];
int red;
int green;
int blue;
int commaposition;
const int MAX_STRING_LEN = 12;
char stringBuffer[MAX_STRING_LEN+1];

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop() {

  char *str;
  char *p;
  strncpy(stringBuffer, message, MAX_STRING_LEN);
  Serial.println(stringBuffer); 

    for(str = strtok_r(stringBuffer, ",", &p);
    str;
    str = strtok_r(NULL, ",", &p)
    )
  {
    Serial.println(str);
  }
    
    delay(1000);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
  
  int idx=0;
  
  while (1 < Wire.available()) {  // loop through all but the last
    message[idx] = Wire.read();   // receive byte as a character and store in array
    idx++;
  }
  message[idx] = Wire.read();   // receive last byte
  // Serial.println(message);      // print out entire message from array
 
}

Or you could do it manually:-

char message[14] = "80,160,255";
byte red;
byte green;
byte blue;

void setup()
{
    Serial.begin(9600);
    char tmpBuffer[3][4];
    byte iLen = strlen(message);
    byte bufIndex = 0;
    byte bufCharIndex = 0;
    for (byte i = 0; i < iLen; i++)
    {
        char c = message[i];
        if (c != ',')
        {
            tmpBuffer[bufIndex][bufCharIndex] = c;
            bufCharIndex++;
        }
        else
        {
            bufIndex++;
            bufCharIndex = 0;
        }
    }
    red = atoi(tmpBuffer[0]);
    green = atoi(tmpBuffer[1]);
    blue = atoi(tmpBuffer[2]);

    Serial.println(red);
    Serial.println(green);
    Serial.println(blue);
}

void loop(){}

OldSteve:
Or you could do it manually:-

char message[14] = "80,160,255";

byte red;
byte green;
byte blue;

void setup()
{
    Serial.begin(9600);
    char tmpBuffer[3][4];
    byte iLen = strlen(message);
    byte bufIndex = 0;
    byte bufCharIndex = 0;
    for (byte i = 0; i < iLen; i++)
    {
        char c = message[i];
        if (c != ‘,’)
        {
            tmpBuffer[bufIndex][bufCharIndex] = c;
            bufCharIndex++;
        }
        else
        {
            bufIndex++;
            bufCharIndex = 0;
        }
    }
    red = atoi(tmpBuffer[0]);
    green = atoi(tmpBuffer[1]);
    blue = atoi(tmpBuffer[2]);

Serial.println(red);
    Serial.println(green);
    Serial.println(blue);
}

void loop(){}

Thanks Steve!

donmerch: I would like to parse the array that is delimited by commas.

First, you need to get the print out of receiveEvent. It is called from an Interrupt Service Routine, and you must do as little as possible. Doing something that takes too long (or even blocks, like print) is a no-no. Your receiveEvent should only set a flag when the message is received. You can check that flag in loop().

Using the Stringclass is also forbidden in an ISR, and strongly discouraged elsewhere. I'm glad you're not using it.

Second, C strings (aka char arrays) are NUL-terminated. That means they are supposed to have a zero byte at the end, so you have to do that after the last byte is read (see code below)

volatile bool messageReceived = false;

void loop()
{
  if (messageReceived) {
    messageReceived = false; // clear it for next time
    Serial.println(message); // print out entire message from array
  }
}

void receiveEvent(int howMany) {
  uint8_t idx=0;  // 8 bits is enough for this... smaller and faster.
  while (Wire.available()) {  // loop through *all* ...
    message[idx] = Wire.read();   // receive byte as a character and store in array
    idx++;
  }
  message[idx] = '\0'; // ... and NUL-terminate

  messageReceived = true;     //  tell 'loop' we got something
}

For parsing the array in loop(), you could use strtol:

void loop()
{
  if (messageReceived) {
    messageReceived = false; // clear it for next time

    char *nextStart;
    red   = strtol( message, &nextStart, 10 );  // nextStart points to the comma after this
    nextStart++; // skip the comma
    green = strtol( nextStart, &nextStart, 10 );
    blue  = strtol( &nextStart[1], NULL, 10 );  // a different way to skip the comma (&nextStart not passed in as 2nd arg)

//    Serial.println(message);      // print out entire message from array
    Serial.print( F("Parsed ") );
    Serial.print( red   );  Serial.print( ',' );
    Serial.print( green );  Serial.print( ',' );
    Serial.println( blue  );
  }
}

Cheers, /dev

@donmerch, you may find the concepts (and the code) in Serial Input Basics useful. There is also a parse example.

...R