REST API arguments - is there a better way?


#1

I just hit a weird little snag related to posting arguments to a core.

Most of the examples I’ve seen show REST API accessible functions dealing with arguments like so:

int myFunction(String args) {
  int arg1 = args.substring(0,1));
  String arg2 = args.substring(3,8);
  // ... 
  return 1;
}

The problem I’m running into is that my first argument might vary in length, as might my second.

What I’d like to be able to do is pass in a traditional query string as an argument, then parse each of its keys and values. Question is - is there an existing Spark API way of doing this, or should I write a query string parser?


#2

Hey @buffington. I think that the best way to do this right now is to parse the string in firmware. I did this using the indexOf(",") command to find the location of the first comma (loc1) in the string to save the first argument, then starting from loc1 to find the second comma, etc. Probably not the most graceful way of doing it, but it worked for me.

In this sample function, I’m controlling an RGBW LED and parsing the incoming string with 5 values separated by 4 commas–a target value for each color RGBW, and a fade duration.

I agree it would be great to avoid having to parse in the firmware–I’ll pass on the feedback to the team and add to the backlog if we can think of a way to make the experience more pleasant.

int set_leds(String command) {

  fading = true;

  int loc1 = 0;
  int loc2 = 0;
  int loc3 = 0;
  int loc4 = 0;

  loc1 = command.indexOf(",");
  targetRed = command.substring(0,loc1).toInt();

  loc2 = command.indexOf(",",loc1+1);
  targetGreen = command.substring(loc1+1,loc2).toInt();

  loc3 = command.indexOf(",",loc2+1);
  targetBlue = command.substring(loc2+1,loc3).toInt();

  loc4 = command.indexOf(",",loc3+1);
  targetWhite = command.substring(loc3+1,loc4).toInt();

  duration=command.substring(loc4+1).toInt();

#3

Ladies and Gentlemen, it’s Michael Buffington!!!

I had a similar issue: sending individual lines of text to a 16x2 LCD, comma-delimited. I just indexOf’d the comma but it would be awkward for more data.

I’d love to see a string parser for query args.


#4

String parser is a good idea since we’ll all be doing the same types of things to parse these incoming strings. I’ll add it to our backlog.

We’ve been using comma delimiting for our examples (for example: D0,HIGH). Any other suggestions for a common structure for arguments?


#5

Having a default delimiter is great but it’s helpful to be able to specify an arbitrary one.


#6

I’d love to See special support for JSON if possible. One could post a hierarchy one json structure and get all key/values as a map maybe.


#7

JSON’s pretty heavy for an embedded system, we’ll probably stick with something simple like just splitting a string into an array of strings based on a user-defined delimiter (where the default is a comma).


#8

This was one of the reasons I had asked about Wiring vs. Arduino support in an other thread, since Wiring does sport a function splitString ( http://wiring.org.co/reference/splitString_.html ) that takes a character parameter as delimiter and “returns” a vector of substrings, where Arduino does not.
I just don’t understand why this is not a method of String-object in Wiring.

On the other hand there would be a C/C++ pendant for this kind of task with “char * strtok ( char * str, const char * delimiters );” that allows for multiple delimiters and which should be part of “string.h” even for GCC, or not?

Edit: I’ve just tested if strtok() compiles in the online IDE, and it does. I’m just not sure, if it would work as expected, since I’ve not got my Cores, yet :frowning:


#9

+1 support for this addition


#10

Late post. I can’t find in the docs if this is resolved or not, though.

I think @ScruffR is onto something with strtok. The following code works dandy for me:

/*
 * This funtion will turn on a selected digital output pin.
 * Usage - "args=ON,7" will enable pin 7
 */
int parseArgs(String command) {
    int    ledPin = -1;
    char * params = new char[command.length() + 1];

    strcpy(params, command.c_str());
    char * param1 = strtok(params, ",");
    char * param2 = strtok(NULL, ",");

    if(param1 != NULL &&  /* Make sure parameters are present */
       param2 != NULL &&
       !strcmp(param1, "ON")) /* strcmp will return 0 if the args match */
    {
      ledPin = atoi(param2);
      if(ledPin >= 0 && ledPin < 8) /* Check for a valid digital pin */
      {
        pinMode(ledPin, OUTPUT);
        digitalWrite(ledPin, HIGH);
        return 0;
      }
    }
    return -1;
}

That code seems cleaner and a bit more flexible to me, but I think that’s just a matter of personal preference. It may even be a bit overkill if you have a small and clearly defined set of commands.

Hope this helps.


Cloud JSON Parsing
#11

Hi @metis,

I thought I was the only “old school” strtok() guy here. I also use strtok to parse XML structure reading an RSS feed based on the double-quote characters. My code depends on the order of the parameters, but it could be rewritten easily to use the values instead. The string to parse looks like:

<header param1="value1" param2="value2" .../>

My code looks like:

    int fieldn = 1;
    char * p = strtok(dataStr, "\"");
    while (p != NULL) {
        //get the values for 
        if (fieldn == 2) {  // value for 1st param
           ...
        } else if (fieldn == 6) {  // value for 3rd param (skip 2nd)
           ...
        } else if (fieldn == 8) {   // value for 4th
           ...
        } else if ( fieldn == 10) {  // and the 5th
           ...
        }
        fieldn++;
        p = strtok(NULL, "\"");
    }


Parsing XML data from Openweather map?
Spark v0.2.2 out of memory
#12

Hi, i’m using your example, but i get this error:
invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
Do you have any idea?


#13

You need to copy data (which is a const char* and hence can’t be altered) into an own char array in order to use strtok(), since that function alters the string.


#14

So basically you’re saying that it’s impossible?


#15

Nope, that’s not what I said!

Read again:

const char* constStr = "This is an immutable string which can't be used direct"; 
char tempStr[64];
strncpy(tempStr, constStr, sizeof(tempStr)); // pull a mutable copy
strtok(tempStr, " "); // this works now

I edited above post and put the insertion in brakets to make it clearer


#16

Like this?

void myHandler(const char *event, const char *data) {
  // Handle the integration response

    const char* constStr = data; 
char tempStr[64];
strncpy(tempStr, constStr, sizeof(tempStr)); // pull a mutable copy
strtok(tempStr, " "); // this works now

    int fieldn = 1;
    char * p = strtok(data, "\"");
    while (p != NULL) {
        //get the values for 
        if (fieldn == 2) {  // value for 1st param
           ...
        } else if (fieldn == 6) {  // value for 3rd param (skip 2nd)
           ...
        } else if (fieldn == 8) {   // value for 4th
           ...
        } else if ( fieldn == 10) {  // and the 5th
          Serial.println(p);
        }
        fieldn++;
        p = strtok(NULL, "\"");
    }
    
}

#17

You just copied the code of BKO and mangled it with my reply there :confused:
Do you understand what that code is supposed to do?

And it’s not quite what you seem to be after :wink:


#18

I just received the particle photon a few days ago, and i’m also starting to approach to serious programming :confounded: . But it’s very important for me to finish this project, so could you be so kind to post the correct code? I would be very thankful!!
:relaxed:


#19

like @bko pointed out, you are trying to create a copy of what the data and/or even pointers are pointing to.

void myHandler(const char *event, const char *data) // pointers to so-called c-string arrays 'event' and 'data' are passed to the handler function
{
  char myData[256] = ""; //create a string array big enough to capture what's pointed to by 'data'
  strncpy(myData, data, sizeof(myData));  // copy whats pointed to by 'data' to 'myData', this function will not overflow the size of myData
  ...
  // parse to get what you need from myData;
  ...
}

#20

We see ourselves more as enablers and educators not as coder-bots.
I’ve pointed you in your other thread to some functions.

You just need to investigate and look at the samples in the provided links.


One additional hint for above code could be the use of strlen()

void myHandler(const char *event, const char *data) // pointers to so-called c-string arrays 'event' and 'data' are passed to the handler function
{
  char myData[strlen(data)+1] = ""; //create a string array big enough to capture what's pointed to by 'data'
  strncpy(myData, data, sizeof(myData));  // copy whats pointed to by 'data' to 'myData', this function will not overflow the size of myData
  ...
  // parse to get what you need from myData;
  ...
}