Help parse (char *) data

Thanks to this forum I’ve been able to transmit and receive data using the RFM69 library. Now that I have characters in a char object I’m wanting to parse out and assign each item to string variables. All the posts I’ve seen are taking a char of data to compare with the input, typically Serial. In my case, my data is not from Serial. I get that char cannot be compared to (char *)radio.DATA. They’re different types. I’m struggling to find a method to cast (char *)radio.DATA as char so that I can compare string objects. The data I’m getting from my transmitter is

wx,1.8,6.4,0.0,33,99.0,188.6,99.1,31.6,61.1,270.0,3.0

Looking at the char documentation I don’t see many methods to help, the closest is Serial.println() and that’s not helping.

Using this as a guide, this is my code so far:

#include <RFM69.h>    //get it here: https://www.github.com/lowpowerlab/rfm69
#include <SPI.h>
const int numChars = 65;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

int counter = 0;
int lastIndex = 0;
String input;
void setup() {
  while (!Serial); // wait until serial console is open, remove if not tethered to computer.   Serial.println("Feather RFM69HCW Receiver");
  // Initialize radio
  radio.initialize(FREQUENCY,NODEID,NETWORKID);
}
void loop() {
  //check if something was received (could be an interrupt from the radio)
  if (radio.receiveDone())
  {
if ((char *)radio.DATA)
    {
      for(int i=0; i < 61; ++i) {
        input[i] = Serial.println((char *)radio.DATA);
      }
      Serial.print("input= ");
      Serial.print(input);
      Serial.print("\r");

  }
    } 
  }
  radio.receiveDone(); //put radio in RX mode
}

As I said, I get that this stunt I tried to pull here to try to iteratively pass each character from the DATA into a string is wrong. As you would know, it’s just passing the DATA to Serial 61 times. But
input is not the same type as this DATA.
What can I do to cast this into something I can parse? Perhaps there’s some methods in this char * that will let me parse. Or just convert it into a string and use those methods? I think that Arduino and C++ share some syntax but not methods so I’m unsure which direction to go.
Thanks for pointers

How do you know that you have the data? I don’t have the hardware to test this, so is there a buffer in the radio object that is holding the data? It appears that radio.DATA is a property, not a method (or function). What is the method that the radio object makes available to read the data it has?

econjack:
How do you know that you have the data? I don't have the hardware to test this, so is there a buffer in the radio object that is holding the data? It appears that radio.DATA is a property, not a method (or function). What is the method that the radio object makes available to read the data it has?

From RMF69.h it looks like '.DATA' is a public array of uint8_t. The examples cast it as a pointer to the actual type of data being transferred (i.e. struct *, char *, etc).

Have a look at the parse example in Serial Input Basics

...R

If all of this:
“wx,1.8,6.4,0.0,33,99.0,188.6,99.1,31.6,61.1,270.0,3.0”

is in a string: an array of char terminated with a trailing ‘\0’, the the strtok function is what you need. This is a standard C/C++ library function in libc, which comes as standard with pretty much all C compilers.

The documentation for #include <string.h> is here.

There is no substitute for simply reading that page and getting a feel for what functions are available in the standard library.

Yes, thank you for the pointer to this library. I was not finding any methods to what can be done with the char * type. Using your suggestion to use char * strtok() I referred back to the Serial Basics use of parsing that Robin2 pointed.

I'm trying to get past the compile errors so using this simplest version. Since this all should be using the char * type I'm still getting compile errors that don't yield results that seem meaningful to me in my Google searches. Mostly because of my ignorance of pointers and what can be done with them.

RFM69 radio = RFM69(RFM69_CS, RFM69_IRQ, IS_RFM69HCW, RFM69_IRQN);
const int numChars = 65;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;
int counter = 0;
int lastIndex = 0;
char* mytype[2] = {0};
char* winds[10] = {0};
char* windg[10] = {0};
char* rain[10] = {0};
char* btemp[10] = {0};
char* bp[10] = {0};
char* ba[10] = {0};
char* bsea[10] = {0};
char* temp[10] = {0};
char* humid[10] = {0};
char* windd[10] = {0};
char* windv[10] = {0};
char* endMarker = ">";

void parseData() {

    // split the data into its parts
    
  char * strtokIndx; // this is used by strtok() as an index
  
  strtokIndx = strtok((char *)radio.DATA,",");      // get the first part - the string
  strcpy(mytype, strtokIndx); // copy it to mytype
  
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  winds = strcpy(winds, strtokIndx);     // convert this part to a text
  
  strtokIndx = strtok(NULL, ","); 
  windg = strcpy(windg, strtokIndx);     // convert this part to a text

}

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

My errors:

receiver_sam_simpler.ino: In function 'void parseData()':
receiver_sam_simpler:57: error: cannot convert 'char**' to 'char*' for argument '1' to 'char* strcpy(char*, const char*)'
receiver_sam_simpler:60: error: cannot convert 'char**' to 'char*' for argument '1' to 'char* strcpy(char*, const char*)'
receiver_sam_simpler:63: error: cannot convert 'char**' to 'char*' for argument '1' to 'char* strcpy(char*, const char*)'
cannot convert 'char**' to 'char*' for argument '1' to 'char* strcpy(char*, const char*)'

OK, assuming that argument '1' isn't zero based, that suggests that my char * for each variable is being converted into char **? What does that mean? It appears that strcpy is a valid char * method so I should be able to use it. The Serial Basics link is assuming String? Or char? I ask because I've not been able to figure this char * out.
Why is this not in a separate post? Because this seems to me to all part of parsing out char * and it might help others in this context.
Ultimately my goal is to take this data and interpolate it into a string that will be sent to std input on the computer.
"curl localhost '-d{"windspeed": " + winds +"}'" etc.

All those char* in the beginning should be just char. You have declared arrays of pointers. Also, for each you need to allocate one byte more, e.g.

char myType[3];

Usually strtok() is used in a very precise way, similar to:

void setup() {

  char testStr[] = "wx,1.8,6.4,0.0,33,99.0,188.6,99.1,31.6,61.1,270.0,3.0";
  char *ptr;

  Serial.begin(9600);
  
  Serial.print("Test string: ");
  Serial.println(testStr);  
  ptr = strtok (testStr,",");   // Look for commas...
  while (ptr != NULL)
  {
    Serial.println(ptr);
    ptr = strtok (NULL, ",");   // Continue to look...
  }
}

void loop() {

}

Try to use it in your code along these lines

econjack:
Usually strtok() is used in a very precise way, similar to:

That is similar to the parse example in Serial Input Basics except that my version "knows" that there are just 3 pieces of data whereas your's is open-ended.

...R

Yes, it's similar to Serial Basic but I'm unsure how to assign each to a variable OR access them in an array. As it goes around in a loop, it's spitting back out the first ptr variable as the function docs say it would.
An array would be fine but then I'm still dealing with assigning a char * to an incompatible type?

An array would be fine but then I'm still dealing with assigning a char * to an incompatible type?

See reply #6 :wink:

sam452:
Yes, it's similar to Serial Basic but I'm unsure how to assign each to a variable OR access them in an array.

Post the code that represents your best attempt.

...R

reply #6 implies that data that is (char *) is equivalent to char? In my limited knowledge, I don’t find that to be true. Because I’m wrong, I’m posting here, and reading lots of search posts, to make attempts. In econjack’s reply I don’t see any way to safely assign each iteration of ptr to a variable. So I’m attempting to make an array of char * to catch them and then build my command.

#include <SPIFlash.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_IS31FL3731.h>
#include <RFM69.h>    //get it here: https://www.github.com/lowpowerlab/rfm69
#include <SPI.h>

RFM69 radio = RFM69(RFM69_CS, RFM69_IRQ, IS_RFM69HCW, RFM69_IRQN);
const int numChars = 65;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

char * radio.DATA =  "wx,1.8,6.4,0.0,33,99.0,188.6,99.1,31.6,61.1,270.0,3.0";

int counter = 0;
int lastIndex = 0;
char mytype[3] = {0};
char winds[10] = {0};
char windg[10] = {0};
char rain[10] = {0};
char btemp[10] = {0};
char bp[10] = {0};
char ba[10] = {0};
char bsea[10] = {0};
char temp[10] = {0};
char humid[10] = {0};
char windd[10] = {0};
char windv[10] = {0};
char *endMarker = ">";
char *ptr[12]; //incompatible types in assignment of 'char*' to 'char* [12]

void setup() { 
  // Initialize radio
  radio.initialize(FREQUENCY,NODEID,NETWORKID);
}

void loop() {
  //check if something was received (could be an interrupt from the radio)
  if (radio.receiveDone())
  {
    //print message received to serial
//    Serial.print((char*)radio.DATA);
   char * mytokens;
   char *p, *i;
   char *wx = {};

       //check if received message
    if ((char *)radio.DATA)
    {
      //parseData();
      //showParsedData();
      
      ptr = strtok((char *)radio.DATA, ",");
//      if ptr == "wx" & sizeof(ptr) > 1 { // because I'm in a loop and the strtok() clears out all but the first value in a repeat until new DATA comes in. I need to only do this when I get new DATA in to parse
        while (ptr != NULL)
         {
           Serial.println(ptr);
           ptr = strtok(NULL, ",");
           wx << ptr //invalid operands of types 'char*' and 'char*' to binary 'operator<<' so another mismatch
         }
         //char command = 'curl localhost -D {"windspeed": ' + ptr[1] + '"windgust:" ' + ptr[2];
//      }


  }
    } 
    //delay(100); 
  }
  radio.receiveDone(); //put radio in RX mode
  Serial.flush(); //make sure all serial data is clocked out before sleeping the MCU
}

void parseData() {

    // split the data into its parts
    ptr = strtok((char *)radio.DATA, ",");
    while (ptr != NULL)
    {
      ptr = strtok(NULL, ",");
    }

}

void showParsedData() {
// Serial.print("Wind Speed: ");
// Serial.println(winds);
// Serial.print("Wind Gust: ");
// Serial.println(windg);
  Serial.println(ptr);
}

So making ptr an array of char * returns
incompatible types in assignment of ‘char*’ to 'char* [12]
In my reading, it appears I cannot make an empty array. I have to instantiate all its members when I do so and I cannot add to it like I can in dynamic languages. And how to do it in this while loop escapes me. Thanks for your pointers and advice, sam

What happens if you remove all the asterisks that you have associated with char variables. I can't recall the last time I needed one.

...R

Removing it from
char endMarker = “<”; returns invalid conversion from ‘const char*’ to ‘char’. But this compiles when I leave char *endMarker.

As mentioned near the top reply #2, the (char *)radio.DATA from RFM69 is what’s driving this choice of aligning everything with char *.

In my reply #5 is where I attempted to assign the strtok() to char * variables.

If I were to reincorporate reply #5 assign to variables and remove the * from nearly all the char variables for each token I get the error

incompatible types in assignment of 'char*' to 'char [10]'

thx, sam

sam452:
. In econjack’s reply I don’t see any way to safely assign each iteration of ptr to a variable. So I’m attempting to make an array of char * to catch them and then build my command.

Clearly I’m missing something because saving the results from strtok() is pretty simple:

void setup() {

  char testStr[] = "wx,1.8,6.4,0.0,33,99.0,188.6,99.1,31.6,61.1,270.0,3.0";
  char *ptr;
  char *result[20];   // Large enough to hold each datum
  int count;
  int i;
  
  Serial.begin(9600);
  
  Serial.print("Test string: ");
  Serial.println(testStr);  

  i = 0;
  ptr = strtok (testStr,",");   // Look for commas...
  while (ptr != NULL)
  {
    result[i++] = ptr;
    ptr = strtok (NULL, ",");
  }
  count = i;
  for (i = 0; i < count; i++) {
    Serial.println(result[i]);
  }
}

void loop() {

}

Yes, I affirm that it works. The context is what threw me. When the while() loop is finished I can make the assignments.