Go Down

Topic: {Solved} Help With PROGMEM Webserver Example (Read 1 time) previous topic - next topic


Apr 16, 2012, 01:48 am Last Edit: Apr 23, 2012, 04:31 am by FullFwd Reason: 1

I have been using the progmem web-server example found at:http://arduino.cc/playground/Code/WebServer. Out of the two examples shown, the one I am referencing is the first one, higher up on the page than the other.  All that is needed to get the example server working is change the IP address to an unused one one your network(in my case, plug the Arduino uno into your router, and upload the code.

Libraries it is using:
#include <SPI.h>
#include <Ethernet.h>
#include <avr/pgmspace.h>
#include <string.h>

This example web-server displays a title webpage with links across the top to other pages.  What I am trying to do seems simple, but I haven't been able to find a way, both through searching and forum scouring. 

What I want to do:
On the first http page, shown in the code as "Page 1",  There is a bit of text that says "Nothing... yet.".

I am trying to change that text "Nothing... yet." to either "On" or "Off" based on the reading from an analogread command in a function that I am adding lower in the code.

But, because the text ""Nothing... yet." is inside of an array (or array pointer), I cant change it. 

I have looked into changing elements in arrays, but this web-server example uses arrays in a way that I can't locate which place(or element) in the array it is, to change the text.

This is the idea of how I want to change it:

Example code(unchanged):
Code: [Select]
PROGMEM prog_char content_page1[] = "<hr /><h3>Content of Page 1</h3><p>Nothing... yet.</p><br /><form action=\"/login\" method=\"POST\"><input type=\"text\" name=\"prova\"><input type=\"submit\" value=\"post\"></form>";

Example code(with my code):
Code: [Select]
PROGMEM prog_char content_page1[] = "<hr /><h3>Content of Page 1</h3><p>onOrOff();</p><br /><form action=\"/login\" method=\"POST\"><input type=\"text\" name=\"prova\"><input type=\"submit\" value=\"post\"></form>";

///////Further Down in the Code/////////
void PROGMEM onOrOff() {
if(analogRead(5)<500)  //965 = on, 0 = off
        return "On";
          return "Off";

This is the idea that I have to change it, not the code that must be used.  I know this code wouldn't work anyways. 

I have tried numerous ways to try and change that text, but no success. 

I have even added code that printed every single char from a certain array(that I chose) out to serial when ever I would hit a certain button, and even with that, I couldn't find the http data chars "Nothing... yet.". I didn't try EVERY single array tho.

I take any help, ideas, examples, etc.

I really have no preference on the method, I can alter it after I get it working.

I am stuck.


Apr 16, 2012, 04:21 am Last Edit: Apr 16, 2012, 04:34 am by Delta_G Reason: 1
You can't make a function call inside of a string.  

Here I'm doing something similar, only mine is reading from an SD card and not PROGMEM.  It hosts some int values to a page on a form that allows you to change the values.  Just an example.

The varHandler function parses the HTML as it is read to the client and the reqHandler function parses GET / requests from the client.

The id tags that I used for the variables look like %{id} where id is a character string that is unique to some action or variable.  That just worked for me, you can use any kind of identifiers you want, but you have to have some way to mark out the text that needs to be replaced with something realtime.  

Code: [Select]

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x04, 0x00, 0xA9 }; //physical mac address
byte ip[] = { 192, 168, 1, 222 }; // ip in lan
byte gateway[] = { 192, 168, 1, 51 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

char read_str[100];

char name[] = "Test_Page";
int start_time = 112;
int end_time = 234;
int setvol;
int rate;


void setup(){

 pinMode(4, OUTPUT); //SS pin for SD card
 pinMode(SS, OUTPUT);
 //start Ethernet
 Ethernet.begin(mac, ip, gateway, subnet);

 //enable serial data print
 Serial.println("EtherNet Server testing");

 if (!SD.begin(4)) Serial.println("Card Failure");
 else Serial.println("Card Success");

void loop(){
 // Create a client connection
 EthernetClient client = server.available();
 if (client) {
   int index = 0;
   while (client.connected()) {
     if (client.available()) {
       char c = client.read();
       // As long as we're not getting new line keep reading in characters
       if (c != '\n' && c != '\r')
         if (index < 99) // Stop adding data if too long (the good part will be up front for us anyway)
           read_str[index++] = c;

       //HTTP request has ended (got '/n' or '/r')
       // Cap the string with a NULL
       read_str[index] = NULL;

       Serial.println(read_str); //print to serial monitor for debuging
       // if a GET with anything other than a space behind it
       // then parse the request
       char* req_ptr;
       if ((req_ptr = strstr(read_str, "GET /")) >0 && *(req_ptr + 5) != ' ')
         req_ptr += 5;  //Skip over the GET /
         // Chop of the HTTP 1.1 part (Make the H = NULL to end the string there)
         (strstr(read_str, "HTTP"))[0] = NULL;  
         // Parse the GET / request in read_str

       client.println("HTTP/1.1 200 OK"); //send new page
       client.println("Content-Type: text/html");
       // could easily change the file name based on what
       // we got when we parsed the Get request
       pageFromFile(client, "main_p.txt");

       break;  // out of the while loop
   //stopping client
   //clearing string for next read

void pageFromFile(EthernetClient client, char* file_name)
 File page_file = SD.open(file_name);

 if (page_file)
   while (page_file.available())
     char c = page_file.read();
     if (c == '%') // Marks one of my variable insertions
       varHandler(client, page_file);

   client.println("FILE ERROR");

void varHandler(EthernetClient client, File file)
 // parses the .txt file for variable names and replaces them with associated variable values
 char name_buffer[10];  // inbedded variable names shouldn't be more than 9 characters long
 char* buf_ptr = name_buffer;

 if (file.peek() != '{')  // Not one of my variable names
   client.print('%');  // It was really a percent sign we read

 while (file.available())
   char c = file.read();
   if (c == '{') continue;  //no need to save the start marker
   if (c == '}') break;  // end of name marker (breaks while)

   *(buf_ptr++) = c;

 *buf_ptr = NULL;  // Add a NULL to the end to close the string
 // We can add as many names and values as we want here
 // Recognizing a name can get you any result you want
 // from being replaced with values as here to triggering
 // function calls that sync the action with the page
 if (strcmp(name_buffer, "name") == 0) client.print(name);

 else if (strcmp(name_buffer, "start") == 0) client.print(start_time);

 else if (strcmp(name_buffer, "end") == 0) client.print(end_time);

 else if (strcmp(name_buffer, "setvol") == 0) client.print(setvol);

 else if (strcmp(name_buffer, "rate") == 0) client.print(rate);

void reqHandler(char* req_string)
 // Here is only one, but we could have any number of possible actions
 // initiated through GET requests
 if (strstr(req_string, "makeSched0") > 0)  // a submission from the form
   // parse out the variables
   // these variables are all expected to be there
   start_time = pullNumber(req_string, "start");
   end_time = pullNumber(req_string, "end");
   setvol = pullNumber(req_string, "setvol");
   rate = pullNumber(req_string, "rate");

int pullNumber(char* req_string, char* var_name)
 // pull an integer of unknown length out of a string and convert to int
 // string from url will look like "start=123&end=345&setvol=35&rate=9 "
 int ret_val = 0;

 char* var_ptr = strstr(req_string, var_name) + strlen(var_name) + 1; // add one for the = sign
 //  keep pulling characters as long as you're getting digits
 for( ; ((*var_ptr >= '0') && (*var_ptr <= '9')) ; ++var_ptr)
   ret_val *= 10;
   ret_val += (*var_ptr - '0');
 return ret_val;


Apr 16, 2012, 04:23 am Last Edit: Apr 16, 2012, 04:30 am by Delta_G Reason: 1
This is the .txt file from the SD card that the example above reads off to the client.  It could just as easily be in PROGMEM.

Code: [Select]

<FORM action="/makeSched0" method="get">
<LABEL for="start"> %{start}: </LABEL>
<INPUT type="text" id="start" name="start"><BR>
<LABEL for="end">%{end}: </LABEL>
<INPUT type="text" id="end" name="end"><BR>
<LABEL for="setvol">%{setvol}: </LABEL>
<INPUT type="text" id="setvol" name="setvol"><BR>
<LABEL for="rate">%{rate}: </LABEL>
<INPUT type="text" id="rate" name="rate"><BR>
<INPUT type="submit" value="Send"> <INPUT type="reset">


I am still having trouble trying to find exactly in this webserver example where to tag each individual char, in real time, as it goes out.  I know that I would have to put a %{} around my HTML text, but where in this code do I setup the varHandler and reqHandler like you have.

Just to repeat what I have said before, All I want to do is change a button label or text label to "on" or "off" depending on what the corresponding analogRead or digitalRead is getting.  I would use an simpler webserver/PROGMEM example for my needs, but this is the only one I could find.  The buttons work right now, and turn on pins, but I have no way to know which pins are "on" or "off".

Full code is attached: its too big to post.


No, you don't necessarily need the %{ }, you just need some way to mark those items as needing further work and not just going straight into the character array as written.

I'm not sure you can put those functions into your program, I was just posting that example so you could see how it works.

The program is reading characters one at a time off the SD card and writing them to the client.  If it sees a % sign, then instead of writing it to the client it goes and runs that other function varHandler.  That function reads in until it sees the } closing bracket and puts everything inside there into a character array.  Then it compares that array against a list to see what variable it should substitute into the message going to the client. 

If you'll notice, the names I'm using for those variables in the HTML doc and in the program are different in a couple of cases.  The processor doesn't know your variables by name, so you have to write code to figure out which variable you want in there.  That's what all those strcmp calls are doing.  They're inside if statements comparing the thing I got out of the braces to some hard coded strings and if it finds a match running the code in the if statement.  In this case, that code is printing some variables to the client.  It could be doing anything. 

Probably the easiest thing to do in your case would be to just break up that long string into two different print statements and print your variable in between them.


I ended up doing something like splitting up the print statements.  I inserted my monitoring code in between where the title is printed to HTML and where the Body is printed to HTML, and it ended up looking pretty good. 

***FYI*** for future readers though...If you want to add any more code to this monitoring section, either use PROGMEM or the SD card to store the html, because at this point, this program is pretty close to its limit.

Thank you Delta_G

Code: [Select]
*                                                              Send Pages
void PROGMEM sendPage(EthernetClient client,struct HTTP_DEF http_def) {

  // send HTML header
  // contentPrinter(client,(char*)pgm_read_word(&(contents_main[CONT_HEADER])));

  // send menu

  // send title
*                                                            Monitor (Analog and Digital channel checks)

  {client.print(" [***NE***] ");
  else if(analogRead(5)<200)   
  {client.print(" [___NE___] ");}
  //client.println("<br />");

  {client.print(" [***N***] ");
  else if(analogRead(4)<200)   
  {client.print(" [___N___] ");}
  //client.println("<br />");

  {client.print(" [***NW***] ");
  else if(analogRead(3)<200)   
  {client.print(" [___NW___] ");
  //client.println("<br />");

  {client.print(" [***W***] ");
  else if(analogRead(2)<200)   
  {client.print(" [___W___] ");}
  //client.println("<br />");
  {client.print(" [***SW***] ");
  else if(analogRead(1)<200)   
  {client.print(" [___SW___] ");}
  //client.println("<br />");

  {client.print(" [***S***] ");
  else if(analogRead(0)<200)   
  {client.print(" [___S___] ");}
  //client.println("<br />");
  {client.print(" [***SE***] ");
  else if(digitalRead(8)==0)   
  {client.print(" [___SE___] ");}
  //client.println("<br />");

  {client.print(" [***E***] ");
  else if(digitalRead(7)==0)   
  {client.print(" [___E___] ");}
  client.println("<br />");   
  //***********************************************End Monitor********************************************************

  // send the body for the requested page

  client.print("<br />");
  // send POST variables

  // send footer

Go Up

Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

via Egeo 16
Torino, 10131