Searching string in char array

Hi all,

I’m developing some code on GSM/GPRS module. My needs to get an HTTP answer from a web site, using InetGSM library.
All work fine and I get an char array containing HTTP response.
I want to search in it two keywords that limit string I need.
In example I have something like that

<my_start/>here the string I need<my_end/>

To do this I used this (semplified) code.
When I assign char array msg to variable resultHTTP, it seems not working, infact string length is 0.

What’s wrong with this assignement?
Thanks

Andrea

String first_match = "<my_start/>";
String second_match = "<my_end/>";
String resultHTTP;

int pos_start;
int pos_end;

#define HTTP_SIZE 400
char msg[HTTP_SIZE];

void setup() 
{

	resultHTTP.reserve(400);

	//init GSM
	
	getHTTPResponse()

}

void getHTTPResponse()
{
    pos_start = 0;
    pos_end = 0;
		  
    numdata=inet.httpGET("www.google.com", 80, "/", msg, 400);
      
    Serial.println("\nNumber of data received:");
    Serial.println(numdata);  
    Serial.println("\nData received:"); 
    Serial.println(msg); 
  
	resultHTTP = String(msg);
    
	Serial.println(resultHTTP.length); // THIS IS THE PROBLEM and result = 0!!!
	
	pos_start = resultHTTP.indexOf(first_match) + first_match.length() + 2;      
	pos_end = resultHTTP.indexOf(second_match);

	Serial.print("pos_start ");
	Serial.println(pos_start);
	Serial.print("pos_end ");
	Serial.println(pos_end);

	resultHTTP = resultHTTP.substring(pos_start, pos_end);

	Serial.println(resultHTTP);      
}

does this code

    Serial.println("\nNumber of data received:");
    Serial.println(numdata); 
    Serial.println("\nData received:");
    Serial.println(msg);

demostrates you "msg" is not empty?

looking at http://arduino.cc/en/Tutorial/StringConstructors I am not sure you can pass a vector in this way doing a sort of cast "String(msg);" try to use this "string.toCharArray(buf, len)"

Read this before posting a programming question

Please edit your post, select the code, and put it between [code][/code] tags.

You can do that by hitting the # button above the posting area.

#define HTTP_SIZE 400
char msg[HTTP_SIZE];

There goes 400 bytes of the 2048 that you have.

String resultHTTP;
   resultHTTP.reserve(400);

There goes another 400 bytes of the 2048 that you have.

   resultHTTP = String(msg);

You are making a useless copy of the data in message, then another copy operation is performed to copy the data to the resultHTTP variable.

For NO good reason. It is quite easy to deal with the char array that the httpGet() method gives you, if you'd just try.

Quit pissing memory away like you have 4G. You have 2K.

Thanks all for answers.

@cantore
Yes, Serial.println(msg); instruction print on debugger windows HTML response.

Debugging I found something strange: I modified the code like this, just to see what in char array.

Serial.println("-- array --");
Serial.println(sizeof(msg));
  int i;
  for (i = 0; i < sizeof(msg) - 1; i++){
     Serial.print(i, DEC);
     Serial.print(" = ");
     Serial.write(msg[i]);
     Serial.println();
   }

What I see in debugger is quite strange for me (I’m a beginner): it seems array has filled all on 3rd position.

-- array --
4
0 =
1 = 
2 =
HTTP/1.0 302 Moved Temporarily
Content-Length: 212      
Location: http://www.google.it/
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Set-Cookie: PREF=ID=add8b737e7a5a3fd:FF=0:TM=1351678154:LM=1351678154:S=yrHuZ3WKP1NbG9A8; expires=Fri, 31-Oct-2014 10:09:14 GMT; path=/; domain=.google.com
Set-Cookie: NID=65=Tfaq-gRDnvz6ksS-IZnAEB3YZdb7b2VJww4dxNpFAlYXWl_Ldg_Nfs2THasu3fo0aa2F65XVAsUIfKR90o--pWrtEAYXPWu107A6o6Aykc8Mc-4OeeQT6pqBnnS_5Cas; expires=Thu, 02-May-2013 10:09:14 GMT; path=/; domain=.google.com; HttpOnly
P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
Date: Wed, 31 Oct 2012 10:09:14 GMT
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Connection: close

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"><TITLE>302 Moved</TITLE></HEAD><BODY><H1>302 Moved</H1>The document has moved <A HREF="http://www.google.it/">here</A>. </BODY></HTML>
CLOSED

So could be the string assignement go wrong for this…

Any ideas

So could be the string assignement go wrong for this…

No. It’s likely that you are doing something (else) wrong.

Serial.println(sizeof(msg));

Like here, where you want strlen() to get the number of characters in the array, not sizeof() to get the size of the array.

  for (i = 0; i < sizeof(msg) - 1; i++){

Same problem here.

Of course, by itself, that code snippet tells us nothing. Post ALL of your code.

Hi Pauls,
here is the full code.

My requirement is to get the number between keywords “content-lenght” and “location” and put it in a string.

I’m using Arduino UNO with SHIELD GSM/GPRS (SIM900 mounted).

Thanks
Andrea

#include "SIM900.h"
#include <SoftwareSerial.h>
#include "inetGSM.h"

InetGSM inet;

String first_match = "Content-Length:";
String second_match = "Location";
String resultHTTP;

int pos_start;
int pos_end;

const unsigned long requestInterval = 10000;  // delay between requests
unsigned long lastAttemptTime = 0;            // last time you connected to the server, in milliseconds

#define HTTP_SIZE 50
char msg[HTTP_SIZE];
int numdata;
int i=0;
boolean started=false;

void setup() 
{
  resultHTTP.reserve(50);

  Serial.begin(9600);
  Serial.println("Initializing GSM Shield");
  //Start configuration of shield with baudrate.
  //For http uses is raccomanded to use 4800 or slower.
  if (gsm.begin(2400)){
    Serial.println("status=READY");
    started=true;  
  }
  else Serial.println("status=IDLE");
  
  if(started){
    //GPRS attach, put in order APN, username and password.
    //If no needed auth let them blank.
    if (inet.attachGPRS("web.omnitel.it", "", ""))
      Serial.println("status=ATTACHED");
    else Serial.println("status=ERROR");
    delay(1000);
 
    gsm.SimpleWriteln("AT+CIFSR");
    delay(5000);
    gsm.WhileSimpleRead();

    getHTTPResponse();

  }
};


void getHTTPResponse()
{
  
  Serial.println("getHTTPResponse");
  pos_start = 0;
  pos_end = 0;
  
  numdata=inet.httpGET("www.google.com", 80, "/", msg, 50);
  resultHTTP = String(msg);
      
  pos_start = resultHTTP.indexOf(first_match) + first_match.length() + 2;      
  pos_end = resultHTTP.indexOf(second_match);
  
  Serial.print("pos_start ");
  Serial.println(pos_start);
  Serial.print("pos_end ");
  Serial.println(pos_end);
	   
  resultHTTP = resultHTTP.substring(pos_start, pos_end);
  Serial.println(resultHTTP);
  
  lastAttemptTime = millis();

}

void loop() 
{
  
  serialswread();

  if (millis() - lastAttemptTime > requestInterval)
  {   
    getHTTPResponse();
  }
 }

void serialswread(){
  gsm.SimpleRead();
}

If you are reading header values, they are each on a line by themselves until there is a double cr/lf (blank line), denoting end-of-header. I would read each header line, then use strncmp to see if this is the right line. Then get the value after "Content-Length:".

My requirement is to get the number between keywords "content-lenght" and "location" and put it in a string.

There is absolutely no reason to use the String class to do this. Get rid of it.

Where, in that code, is the snippet you were asking about?

The data from the request is held in msg. The strstr() function does EXACTLY the same thing that the indexOf method does, and doesn't require abusing SRAM to accomplish.

Once you know where the first string starts (that's effectively what strstr() tells you), and where the second string starts, a little pointer arithmetic will tell you where the first string ends (add the length of the string to the pointer to the start of the 1st string to point to the first character after the first string), and the data of interest starts. A little more pointer arithmetic (subtract the pointer to the second string from the modified pointer to the first string) will tell you where the end of the data is.

char *ptrOne = strstr(msg, "Content-Length:");
if(ptrOne)
{
   ptrOne += strlen("Content-Length:"); // Point to first character after content length part
   char *ptrTwo = strstr(ptrOne, "Location");
   if(ptrTwo)
   {
      int len = ptrTwo - ptrOne;
   }
}

Once you know the length of the portion of interest, you can copy that data, starting at ptrOne, somewhere else.

Here is code that uses the SD output to parse into lines and recover bytes (numbers), but the theory is the same.

Maybe you can get some idea how to parse your header info with it. No String stuff, just a character array. Just keep reading lines until the start of the line matches "Content-Length:".

Hi Pauls,

it doesn't work.

I put your code right after msg variable assignement and I modified it like this

 Serial.println("myength:");
    char *ptrOne = strstr(msg, "Content-Length:");
    if(ptrOne)
    {
      Serial.println("step 1");
       ptrOne += strlen("Content-Length:"); // Point to first character after content length part
       char *ptrTwo = strstr(ptrOne, "Location");
       if(ptrTwo)
       {
          Serial.println("step 2");

         int len = ptrTwo - ptrOne;
          Serial.println(len);
       }
    }

The only thing I see in serial output is "myength:".
I think that after

numdata=inet.httpGET("www.google.com", 80, "/", msg, 50);

msg array doesn't contain any data. In notice, in my debugging, that only calling

serialswread();

in loop function, make data stream flow into msg variable.
I'm not sure of this, and taking a look to libraries, I don't understand how it works.

http://code.google.com/p/gsm-shield-arduino/

Thanks

I think that after...msg array doesn't contain any data.

Why don't you confirm that? Print both numdata and msg.

numdata return 0 and msg doesn't print nothing.
After that the function in loop gsm.SimpleRead(); print all HTTP response.

Infact, in SIM900.cpp you found

void SIMCOM900::SimpleRead()
{
	char datain;
	if(_cell.available()>0){
		datain=_cell.read();
		if(datain>0){
			Serial.print(datain);
		}
	}
}

so is for this that I'm not sure that msg variable contain data.
The library seems read and print data, but msg variable doesn't contain it.

What happen if HTTP response is bigger than 50 as passed to the function?
Data should be truncated in msg, isn't it?

Thanks

Infact, in SIM900.cpp you found

Useless crap. All that the function does is dump the data to the serial monitor. That's useless.

Unless there is another function that actually collects the data into an array, you will need to create one.

Thanks Pauls,

I'll write my own function and I'll take care of your suggestions about not using strings.

Andrea

I’ll write my own function and I’ll take care of your suggestions about not using strings.

You WANT to use strings. You do NOT want to use Strings. There is a world of difference in that one little letter.

Squizza, time spent now just learning about C strings and string.h functions will save you a lot of time and headaches in future.

Here is a start point to build/experiment from:

// link to AVR LibC string.h library
// http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html
#include <string.h>

char mywords[ 32 ];

void setup()
{
  Serial.begin( 9600 );

  strcpy( mywords, "Hello World" );
  Serial.println( mywords );

  char *substring;
  substring = strstr( mywords, "World" );
  if ( substring )
  {
    Serial.println( substring );
  }
  else
  {
    Serial.println( "World not found." );
  }
}

void loop()
{
}

I would point you to the Arduino tutorials->examples page but some Idiot loaded a bunch of C++ String examples there – maybe that’s why so many new users think that C++ String objects are the way to go. Sure, you -can- use them if you don’t do much but as a practice they are Bad for such small environments.