{Solved} Help With PROGMEM Webserver Example


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):

PROGMEM prog_char content_page1[] = "<hr /><h3>Content of Page 1</h3><p>Nothing... yet.</p>
<form action=\"/login\" method=\"POST\"><input type=\"text\" name=\"prova\"><input type=\"submit\" value=\"post\"></form>";

Example code(with my code):

PROGMEM prog_char content_page1[] = "<hr /><h3>Content of Page 1</h3><p>onOrOff();</p>
<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.

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.

#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;

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.

<FORM action="/makeSched0" method="get">
<LABEL for="start"> %{start}: </LABEL>
<INPUT type="text" id="start" name="start">

<LABEL for="end">%{end}: </LABEL>
<INPUT type="text" id="end" name="end">

<LABEL for="setvol">%{setvol}: </LABEL>
<INPUT type="text" id="setvol" name="setvol">

<LABEL for="rate">%{rate}: </LABEL>
<INPUT type="text" id="rate" name="rate">

<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.

8_light_webserver_control.ino (25.4 KB)

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

*                                                              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.print(" [***N***] ");
  else if(analogRead(4)<200)   
  {client.print(" [___N___] ");}
  {client.print(" [***NW***] ");
  else if(analogRead(3)<200)   
  {client.print(" [___NW___] ");
  {client.print(" [***W***] ");
  else if(analogRead(2)<200)   
  {client.print(" [___W___] ");}
  {client.print(" [***SW***] ");
  else if(analogRead(1)<200)   
  {client.print(" [___SW___] ");}

  {client.print(" [***S***] ");
  else if(analogRead(0)<200)   
  {client.print(" [___S___] ");}
  {client.print(" [***SE***] ");
  else if(digitalRead(8)==0)   
  {client.print(" [___SE___] ");}
  {client.print(" [***E***] ");
  else if(digitalRead(7)==0)   
  {client.print(" [___E___] ");}
  //***********************************************End Monitor********************************************************

  // send the body for the requested page

  // send POST variables

  // send footer