Parsing GET Variables

Hello Community,

I've now used every possible way to get my problem solved, but unfortunately i didn't work.

I want to parse GET Variables. And they come from an Buffer of Ethercard. My Shield uses the Ethercard libary by the way.

So in the Ethernode example it looks like this

   if (pos) {
        bfill = ether.tcpOffset();
        char* data = (char *) Ethernet::buffer + pos;
#if SERIAL
        Serial.println(data);
#endif
        // receive buf hasn't been clobbered by reply yet
        if (strncmp("GET / ", data, 6) == 0)

and it works, but i have much more possibilities of values after /. I had several ideas to cut the string and parsing out the valuename(s) and value(s) itself.

Maybe someone could support me?

kind regards Alf91

I had several ideas to cut the string and parsing out the valuename(s) and value(s) itself.

Maybe someone could support me?

Implement one of them. Test it. If it doesn't work, post what you tried, what you actually got, and what you expected (that is what was your input and what was your output). If I were doing it, I'd be using strtok() with = as the delimiter to get a name, then with & as the delimiter to get the value, then repeat, alternating delimiters until the whole string was parsed.

could you maybe post a kind of an example? I know it's not the mention of requesting support and starting to demand help with codesnippets but i'm now nearly crying because nothing seems to work. Or better - i can not evaluate if it works because my tries all based on an serial output of the arduino and everytime it's starting near to a session were i will throw my laptop out of my window.

could you maybe post a kind of an example?

Sure, but I need something to build on. What does your code to read the data from the client look like?

Here's a snippet to get you started:

// Parse QUERY_STRING at char *s
// Does not unencode a urlencoded string

char c, *k, *v;
do
{  k = s;
   while ((c = *s) && (c != '=')) ++s;
   if (c == '=')
   {  *s = '\0';
      v = ++s;
      while ((c = *s) && (c != '&')) ++s;
      *s++ = '\0';

      /* process (k->key,v->value) pair */
   }
} while (c);

Simple updated code from the past that extracts data from a GET request for controlling two servos.

  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString);

          //readString looks like "GET /?-1500-1500 HTTP/1.1"

          if (readString.length() >0) {
            Serial.println(readString);

            servo1 = readString.substring(7, 11);
            servo2 = readString.substring(12, 16);

            Serial.println(servo1);
            Serial.println(servo2);

            myservo1.writeMicroseconds(servo1.toInt());
            myservo2.writeMicroseconds(servo2.toInt());

            readString="";
          } 
          ///////////////////

Thank you for the snippets but with my humble know how i didn’t understand much.

@ PaulS

Heres the Code I’m starting with:

#include <EtherCard.h>

// ethernet interface mac address, must be unique on the LAN
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
static byte myip[] = { 192,168,254,203 };

byte Ethernet::buffer[500];
BufferFiller bfill;

void setup () {
  if (ether.begin(sizeof Ethernet::buffer, mymac,28) == 0)
    Serial.println( "Failed to access Ethernet controller");
  ether.staticSetup(myip);
}

static word homePage() {
  long t = millis() / 1000;
  word h = t / 3600;
  byte m = (t / 60) % 60;
  byte s = t % 60;
  bfill = ether.tcpOffset();
  bfill.emit_p(PSTR(
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Pragma: no-cache\r\n"
    "\r\n"
    "<meta http-equiv='refresh' content='1'/>"
    "<title>RBBB server</title>" 
    "<h1>$D$D:$D$D:$D$D</h1>"),
      h/10, h%10, m/10, m%10, s/10, s%10);
  return bfill.position();
}

void loop(){
    word len = ether.packetReceive();
    word pos = ether.packetLoop(len);
    // check if valid tcp data is received
    if (pos) {
        bfill = ether.tcpOffset();
        char* data = (char *) Ethernet::buffer + pos;
#if SERIAL
        Serial.println(data);
#endif
        // receive buf hasn't been clobbered by reply yet
        if (strncmp("GET / ", data, 6) == 0)
            homePage(bfill);

Its from the RBB Server Example and the EtherNode sketch.

My biggest problem is to understand how i can extract various values out of an GET Request which looks like this:

GET /mode=blink&rate=30&color=yellow for example.

Anyway, thank you for your help guys

        char* data = (char *) Ethernet::buffer + pos;

So, data contains something like:
GET /mode=blink&rate=30&color=yellow

if (strncmp("GET / ", data, 6) == 0)

If this is true, then you can use strtok() to extract tokens.

char *token = strtok(data, "/"); // Get everything up to the /
if(token) // If we got something
{
   char *name = strtok(NULL, "="); // Get the first name. Use NULL as first argument to keep parsing same string
   while(name)
   {
      char *valu = strtok(NULL, "&");
      if(valu)
      {
         // Do something with name and valu
         name = strtok(NULL, "="); // Get the next name
      }
   }
}

Use NUL as first argument to keep parsing same string

NULL is here meant to be like a pointer on "token" right?

Does the while loop extract every name and value out of "token" and in this loop i have with the values to start other programms?

like

 char *name = strtok(NULL, "="); // Get the first name. Use NULL as first argument to keep parsing same string
   while((name) == mode)
   {
      char *valu = strtok(NULL, "&");
      if((valu) == blink) //Thats only if i know what the value can be else it would be if(value) 
      {
         // Do something with name and valu
         name = strtok(NULL, "="); // Get the next name
      }
   }

Is that interpretation right? Couldn't evaluate it by myself because im not at home til tomorrow

The strtok() function takes, as the first argument, a pointer to a string to parse. If you pass NULL as the pointer to the string to parse, strrok() keeps parsing the string it was parsing. If you pass it other than NULL, it starts parsing the new string.

Does the while loop extract every name and value out of "token" and in this loop i have with the values to start other programms?

No, it extracts every name and every matching value out of data.

If data contains "GET / mode=blink&rate=30&color=yellow", token points to "GET / ". Then, the call to strtok() with NULL as the first argument will continue parsing "mode=blink&rate=30&color=yellow", and name will point to "mode". Then, strtok() will parse "blink&rate=30&color=yellow", and value will point to "blink".

On the next iteration of the while loop, name will point to "color" and value will point to "yellow".

so how i can implement the if construct?

I'm unfortunately not able to understand where i have to check if name = something and valu = something and how i implement that without interrupting the parsing of the rest of "token".

So, is my example interpretation of your code right? At the moment my opinion is that i have to compare in the while loop name with a name ( if i know the name for example in mode=blink)

There is code here in reply #3 that has a parse for two variables passed by a GET form request. Maybe that give you some ideas. http://arduino.cc/forum/index.php/topic,94861.0.html

So i tried it with this code :

#include <EtherCard.h>

// ethernet interface mac address, must be unique on the LAN
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
static byte myip[] = { 192,168,0,203 };

byte Ethernet::buffer[500];
BufferFiller bfill;

void setup () {
  if (ether.begin(sizeof Ethernet::buffer, mymac,28) == 0)
    Serial.println( "Failed to access Ethernet controller");
  ether.staticSetup(myip);
  pinMode(22,OUTPUT);
}


void loop () {
  word len = ether.packetReceive();
  word pos = ether.packetLoop(len);
  
  char* data = (char *) Ethernet::buffer + pos;
  
  if(strncmp("GET / ", data, 6) == 0) {
 
 char *token = strtok(data, "/"); // Get everything up to the /




if(token) // If we got something
{
   char *name = strtok(NULL, "="); // Get the first name. Use NULL as first argument to keep parsing same string
   while(name == "mode")
   {
      char *valu = strtok(NULL, "&");
      if(valu == "blink")
      {
        digitalWrite(22, HIGH); //YES PORT 22. It's an arduino port.
        // Do something with name and valu
         name = strtok(NULL, "="); // Get the next name
      }
   }
}

} 
  }

didn’t work. what’s my fault?

   while(name == "mode")

name is a pointer to a string. It will NEVER equal a constant string. They are completely different memory locations, so they will never be equal. Look at strcmp().

Ok, so now it makes more sense for me, think i understood something. Thank you for the help.

So, in my example GET / mode=blink&rate=30&color=yellow
there is a need after parsing the first name and valu to know the second and maybe the third in some situations.

But when

   char *name = strtok(NULL, "="); // Get the first name. Use NULL as first argument to keep parsing same string
   while(name)
   {
      char *valu = strtok(NULL, "&");
      if(valu)
      {
         // Do something with name and valu
         name = strtok(NULL, "="); // Get the next name
      }
   }
}

then name is after the second strtok the second name and that so i need to save the first.
Maybe to count name1 name2 with something like i++?
Or is there an possibility to parse it into an array? ( Like in php when you make an database query and you have $valuname[content1, content2] at the end)

I would not depend on the order of those variables. I would stick to the format I used in my code. Instead of searching for "r=" and "t=" as in my example, search for "mode=", "rate=", then add "color=" for the third variable retrieval.

You don't need to use while loops, if they are not appropriate. You can parse, or not, the rest of the string, based on what you find in any token.

but how would it work in my example? i mean how can i parse which mode, and what the options?

but how would it work in my example? i mean how can i parse which mode, and what the options?

Try something yourself. If it doesn't work, come on back.

youre right, i wil try some. but by the way;

what does if(strncmp("123" NULL, 3) ==0) means. Is an compare with ==0 that it is boolean true? Thought that would be 1 for true?! Or does it means its an compare with NULL?