Go Down

Topic: How to get substrings from char pointer array (Read 4130 times) previous topic - next topic

winkleink

I'm using the ethercard library (https://github.com/jcw/ethercard) to set the Arduino up as a web client so I can read data from a website.
All this is working correctly and the demo webClient sketch (https://github.com/jcw/ethercard/blob/master/examples/webClient/webClient.ino) works perfectly.
I have even change the web site and page to be access and the correct output is sent to the serial monitor.

My problem is that my knowledge of C and pointers is very limited.

I know that Ethernet::buffer is an array of bytes using pointers.

What I am trying to do is read the Google XML weather data from http://www.google.com//ig/api?weather=London
Extract the current temp_c, humidity, icon and wind_condition data and use them to create an output.

Where I am failing is how to parsing a char.  Any advice or pointers (sorry for pun) on how to do this would be appreciated.

Below is the webclient code I am working from

// Demo using DHCP and DNS to perform a web client request.
// 2011-06-08 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php

#include <EtherCard.h>

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

byte Ethernet::buffer[700];
static uint32_t timer;

char website[] PROGMEM = "www.google.com";

// called when the client request is complete
static void my_callback (byte status, word off, word len) {
  Serial.println(">>>");
  Ethernet::buffer[off+300] = 0;
  Serial.print((const char*) Ethernet::buffer + off);
  Serial.println("...");
}

void setup () {
  Serial.begin(57600);
  Serial.println("\n[webClient]");

  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
    Serial.println( "Failed to access Ethernet controller");
  if (!ether.dhcpSetup())
    Serial.println("DHCP failed");

  ether.printIp("IP: ", ether.myip);
  ether.printIp("GW: ", ether.gwip);
  ether.printIp("DNS: ", ether.dnsip);

  if (!ether.dnsLookup(website))
    Serial.println("DNS failed");
   
  ether.printIp("SRV: ", ether.hisip);
}

void loop () {
  ether.packetLoop(ether.packetReceive());
 
  if (millis() > timer) {
    timer = millis() + 5000;
    Serial.println();
    Serial.print("<<< REQ ");
    ether.browseUrl(PSTR("/foo/"), "bar", website, my_callback);
  }
}

dc42

Please use the # button when posting code, it makes it easier to read.

I found a library for parsing XML on arduino at http://john.crouchley.com/blog/archives/454. Alternatively, you can search for strings and characters inside a null terminated string using the strstr and strchr functions from the standard C library.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.


pravas

I generally use strstr() function:
char buffer[]="+CBC: 0,99,4190" ; // this is a return string by GSM modem  0,charge level, millivolt
char *ptr;  //temp pointer variable for pointer artihmetic
char *fieldPtr; //temp pointer  variable for pointer artihmetic
:
:
:
:
ptr=strstr(buffer,"+CBC:"); // if substring found in buffer it return address ofstart character of matched string
                      // if not matched it returned NULL
if(ptr!=NULL)
{
   ptr=strstr(ptr,",");
   Serial.println(ptr);         // will print :",99,4190"
                       // you can use pointer arithmetic to get the field
   while(ptr!=NULL)
   {
    ptr++;
    fieldPtr=ptr;
    ptr=strstr(ptr,",");
    *ptr='\0';
    Serial.println(fieldPtr);
   }
}
--------Output---------------------------------
,99,4190
99
4190

winkleink


I generally use strstr() function:
char buffer[]="+CBC: 0,99,4190" ; // this is a return string by GSM modem  0,charge level, millivolt
char *ptr;  //temp pointer variable for pointer artihmetic
char *fieldPtr; //temp pointer  variable for pointer artihmetic
:
:
:
:
ptr=strstr(buffer,"+CBC:"); // if substring found in buffer it return address ofstart character of matched string
                      // if not matched it returned NULL
if(ptr!=NULL)
{
   ptr=strstr(ptr,",");
   Serial.println(ptr);         // will print :",99,4190"
                       // you can use pointer arithmetic to get the field
   while(ptr!=NULL)
   {
    ptr++;
    fieldPtr=ptr;
    ptr=strstr(ptr,",");
    *ptr='\0';
    Serial.println(fieldPtr);
   }
}
--------Output---------------------------------
,99,4190
99
4190



pravas - Thanks.
This looks like the kind of thing I'm trying to do but for a web page rather than GPS.
I think some experimenting over the weekend will see if I understand what you are saying.

Albert.


pravas

Yes you can use the webpage XML data  into the buffer.

char buffer[]="------------------------------XML DATA-----------------------------------------"; //This is a character array buffer.

In logic you must Identify the token, FieldName, FieldValue etc.

Goodluck for your experiment..

gardner

The data returned from "http://www.google.com//ig/api?weather=London" is 1371 bytes, not counting headers.  It us unlikely you will be 100% successful with the approach of loading the whole response into a string and searching around in it with stsrstr().

I would structure this as a parser like below that uses a small buffer to recognize just one tag at a time, and then feed all the data you receive into it.  This allows you to avoid saving huge swaths of the response at once.  You just feed your data into handle_input() one character at a time and it does the rest.

Code: [Select]
char token[TOKEN_LEN];
uint8_t input_len = 0;

void handle_input(uint8_t c)
{
int temp;

    /* c = '<', then a new tag sequence is starting
     */
    if (c == '<') {
        input_len = 0;
        return;
    }

    /* c = '>', a tag sequence is complete.
     * Check if this is a specific tag
     */
    if (c == '>') {
       token[input_len] = 0;

       if (strncmp(token, "temp_c data=\"", 13) != 0)
           return;

       temp = atoi(token + 13);

// Now what?
//
Serial.print("got temperature ");
Serial.print(temp);

    }

    /* otherwise we have an input character
     */
    if (input_len <= TOKEN_LEN - 1)
       token[input_len++] = c;
}

winkleink

Thank you for the advice.
I have a feeling for me I'm  byting off more than I can chew for now.
I think I need to learn a bit more C++ before I can get my head around this.

Again, thanks for the advice.
Albert.

scarlsen

Hey, I have same problem with reading the array - have you found a solution ?
I can call a web page, and in my serial monitor can I read that I receive the correct data from the web server, but how can "work" with my received data ?
/Per

PaulS

Quote
but how can "work" with my received data ?

Collecting the data in an array is the first step. Then, define what ""work" with my received data" means.

There are plenty of examples around of parsing the data that a client sends to the Arduino server, or that an Arduino client fetches from a server.

Vendict

>>   *ptr='\0';

Please don't use this. Function atoi can work without cutting string with '\0'.
This command (  *ptr='\0'; ) sometimes (2-4 time on day) crash arduino. I was looking for this problem for several months.

GoForSmoke

http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=11425&page=1

Look at the state machine example. It doesn't buffer, it evaluates each char as it arrives.
Your data has a pattern. You can work it with a state machine as long as you can identify the states.

Note that the code below has room to add many operations.
There are many unused cycles between serial characters arriving.

Quote

Another way of processing incoming data, without blocking, is to set up a "state machine". Effectively this means looking at each byte in the input stream, and handling it depending on the current state.

As an example, say you had this coming into the serial port:


R4500S80G3



Where Rnnn is RPM, Snnnn is speed, and Gnnnn is the gear setting.

The state machine below switches state when it gets a letter "R", "S" or "G". Otherwise it processes incoming digits by multiplying the previous result by 10, and adding in the new one.

When switching states, if first handles the previous state. So for example, after getting R4500 when the "S" arrives, we call the ProcessRPM function, passing it 4500.

This has the advantage of handling long messages without even needing any buffer, thus saving RAM. You can also process message as soon as the state changes, rather than waiting for end-of-line.

Example state-machine code

Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

Go Up