Help hosting web server based on tutorial

Hello, I just got an arduino, and I am trying to follow tutorial (http://www.instructables.com/id/Arduino-Web-LED) to control an RGB led over ethernet. I have gotten this to work perfectly using Yaler, however I would like to migrate away from that. Just as a learning experience I would like to get rid of all of the Yaler stuff and only use a webserver hosted directly from the Arduino, but still have the same look to the page to control the led. I need help getting rid of all of the Yaler stuff. I am pretty new to this, and don’t really understand the code well enough to be able to make major changes like that. The code I have so far is pretty much directly from the tutorial. I have the led and everything hardware wise setup fine, just need help with the code.

Original code:

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

const char html[] =
  "<html><head>"
    "<script type=\"text/javascript\">"
    "var r;"
    "try {"
      "r = new XMLHttpRequest();"
    "} catch (e) {"
      "try {"
        "r = new ActiveXObject('Microsoft.XMLHTTP');"
      "} catch (e) {}"
    "}"
    "function set (c) {"
      "r.open('PUT', './led/' + c, false);"
      "r.send(null);"
    "}"
    "</script>"
    "<style type=\"text/css\">"
      ".b {width:112; height:112}"
      ".g {color:lightgrey}"
    "</style>"
  "</head>"
  "<body><table height=\"100%\" width=\"100%\">"
    "<tr><td align=\"center\" valign=\"middle\">"
      "<p>"
        "<input type=\"button\" class=\"b\" style=\"background-color:#ff0000\" onclick=\"set('ff0000')\"/>&nbsp;&nbsp;"
        "<input type=\"button\" class=\"b\" style=\"background-color:#00ff00\" onclick=\"set('00ff00')\"/>&nbsp;&nbsp;"
        "<input type=\"button\" class=\"b\" style=\"background-color:#0000ff\" onclick=\"set('0000ff')\"/>"
      "</p>"
      "<p>HTML served from <a href=\"\">this</a> Arduino, made accessible by <a href=\"http://www.yaler.org/\">Yaler</a>.</p>"
    "</td></tr>"
  "</table></body></html>";

const char http200[] = "HTTP/1.1 200 OK";
const char contentLength[] = "Content-Length: ";
const char connectionClose[] = "Connection: close";
const char contentTypeTextHtml[] = "Content-Type: text/html";
const char contentTypeTextPlain[] = "Content-Type: text/plain";

const byte RECEIVING = 0, READ_CR = 1, READ_CRLF = 2, READ_CRLFCR = 3, DONE = 4;
const byte YALER_RECEIVING = 0, YALER_UPGRADING = 1, YALER_TIMEOUT = 2, YALER_UPGRADED = 3;

const char yalerId[] = "my-arduino"; // TODO: change
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // TODO: change
byte server[] = { 46, 137, 106, 125 }; // try.yaler.net

boolean isPut;
byte state;
int count;
byte ledIndex;
byte ledUriOffset;
int htmlLength;

byte yalerState;
int yalerCount;

byte rPin = 5;
byte gPin = 3;
byte bPin = 6;
byte led[] = {0, 0, 0};

void setColor (byte r, byte g, byte b) {
  // SparkFun LED
  analogWrite(rPin, r);
  analogWrite(gPin, g); 
  analogWrite(bPin, b);

  // Adafruit LED
  //analogWrite(rPin, 255 - r);
  //analogWrite(gPin, 255 - g); 
  //analogWrite(bPin, 255 - b);  
}

byte byteFromHexChar (char ch) {
  byte result;
  if ((ch >= '0') && (ch <= '9')) {
    result = ch - '0';
  } else if ((ch >= 'a') && (ch <= 'f')) {
    result = 10 + (ch - 'a');
  } else if ((ch >= 'A') && (ch <= 'F')) {
    result = 10 + (ch - 'A');
  } else {
    result = 0;
  }
  return result;
}

void parseRequestChar (char ch) {
  // PUT /<yaler-id>/led/ff0000 ... \r\n\r\n
  // GET /<yaler-id>/led ... \r\n\r\n
  if (count == 0) {
    isPut = ch == 'P';
  } else if ((count >= ledUriOffset) && (count < ledUriOffset + 6)) {
    byte d = byteFromHexChar(ch);
    if ((count - ledUriOffset) % 2 == 0) {
      led[ledIndex] = d;
    } else {
      led[ledIndex] = led[ledIndex] * 16 + d;
      ledIndex++;
    }
  }
  if (state == RECEIVING) {
    if (ch == '\r') {
      state = READ_CR;
    }
  } else if (state == READ_CR) {
    // assert ch == '\n'
    state = READ_CRLF;
  } else if (state == READ_CRLF) {
    if (ch == '\r') {
      state = READ_CRLFCR;
    } else {
      state = RECEIVING;
    }
  } else if (state == READ_CRLFCR) {
    // assert ch == '\n'
    state = DONE;
  }
  count++;
}

void parseYalerResponseChar (char ch) {
  // HTTP/1.1 101 ... \r\n\r\n
  // HTTP/1.1 204 ... \r\n\r\n
  if (yalerState == YALER_RECEIVING) {
    if (yalerCount == 9) { // sizeof("HTTP/1.1 ?") - 1;
      if (ch == '1') { // 101
        yalerState = YALER_UPGRADING;
      } else { // 204
        // assert ch == '2'
        yalerState = YALER_TIMEOUT;
      }
    }
  } else if (yalerState == YALER_UPGRADING) {
    if (yalerCount == 56) { // sizeof("HTTP/1.1 101 ... \r\n\r\n") - 1
      yalerState = YALER_UPGRADED;
    }
  }
  yalerCount++;
}

void sendYalerPostRequest (EthernetClient c) {
  c.print("POST /");
  c.print(yalerId);
  c.println(" HTTP/1.1");
  c.println("Upgrade: PTTH/1.0");
  c.println("Connection: Upgrade");
  c.println("Host: www.yaler.net");
  c.print(contentLength);
  c.println("0");
  c.println();
  c.flush();
}

void receiveYalerResponse (EthernetClient c) {
  yalerCount = 0;
  yalerState = YALER_RECEIVING;
  while (c.connected() && (c.available() <= 0)) {} // Yaler sends 101 or 204 in < 30s
  while (c.connected() && (c.available() > 0) &&
    (yalerState != YALER_UPGRADED) &&
    (yalerState != YALER_TIMEOUT)) 
  {
    char ch = c.read();
    parseYalerResponseChar(ch);
  }
}

void sendPutResponse (EthernetClient c) {
  c.println(http200);
  c.println(contentTypeTextPlain);
  c.print(contentLength);
  c.println("3");
  c.println(connectionClose);
  c.println();
  c.print("200");
  c.flush();
}

void sendGetResponse (EthernetClient c) {
  c.println(http200);
  c.println(contentTypeTextHtml);
  c.print(contentLength);
  c.println(htmlLength);
  c.println(connectionClose);
  c.println();
  c.print(html);
  c.flush();
}

void receiveRequest (EthernetClient c) {
  count = 0;
  ledIndex = 0;
  state = RECEIVING;
  while (c.connected() && (c.available() > 0) && (state != DONE)) {
    char ch = c.read();
    //Serial.print(ch);
    parseRequestChar(ch);
  }
}

void setup() {
  //Serial.begin(9600);
  //Serial.println("setup");
  pinMode(rPin, OUTPUT);
  pinMode(gPin, OUTPUT);
  pinMode(bPin, OUTPUT);
  setColor(255, 255, 255);
  Ethernet.begin(mac);
  htmlLength = sizeof(html) - 1;
  ledUriOffset = sizeof("PUT /") + sizeof(yalerId) + sizeof("/led/") - 3 * 1;
  setColor(0, 0, 0);
}

void loop() {
  EthernetClient client;
  client.connect(server, 80);
  if (client.connected()) {
    //Serial.println("connected");
    sendYalerPostRequest(client);
    receiveYalerResponse(client);
    if (yalerState == YALER_UPGRADED) {
      //Serial.println("upgraded");
      receiveRequest(client);
      if (state == DONE) {
        if (isPut) {
          setColor(led[0], led[1], led[2]);
          sendPutResponse(client);
        } else {
          sendGetResponse(client);
        }
      }
    } else {
      //Serial.println("timeout");
    }
    client.stop();
  }
}

Post exceeds 9500 chars… will post more

My code, attempted to remove Yaler:

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

const char html[] =
  "<html><head>"
    "<script type=\"text/javascript\">"
    "var r;"
    "try {"
      "r = new XMLHttpRequest();"
    "} catch (e) {"
      "try {"
        "r = new ActiveXObject('Microsoft.XMLHTTP');"
      "} catch (e) {}"
    "}"
    "function set (c) {"
      "r.open('PUT', './led/' + c, false);"
      "r.send(null);"
    "}"
    "</script>"
    "<style type=\"text/css\">"
      ".b {width:112; height:112}"
      ".g {color:lightgrey}"
    "</style>"
  "</head>"
  "<body><table height=\"100%\" width=\"100%\">"
    "<tr><td align=\"center\" valign=\"middle\">"
      "<p>"
        "<input type=\"button\" class=\"b\" style=\"background-color:#ff0000\" onclick=\"set('ff0000')\"/>&nbsp;&nbsp;"
        "<input type=\"button\" class=\"b\" style=\"background-color:#00ff00\" onclick=\"set('00ff00')\"/>&nbsp;&nbsp;"
        "<input type=\"button\" class=\"b\" style=\"background-color:#0000ff\" onclick=\"set('0000ff')\"/>"
      "</p>"
      "<p>HTML served from <a href=\"\">this</a> Arduino, made accessible by <a href=\"http://www.yaler.org/\">Yaler</a>.</p>"
    "</td></tr>"
  "</table></body></html>";

const char http200[] = "HTTP/1.1 200 OK";
const char contentLength[] = "Content-Length: ";
const char connectionClose[] = "Connection: close";
const char contentTypeTextHtml[] = "Content-Type: text/html";
const char contentTypeTextPlain[] = "Content-Type: text/plain";

const byte RECEIVING = 0, READ_CR = 1, READ_CRLF = 2, READ_CRLFCR = 3, DONE = 4;

const char yalerId[] = "my-arduino"; 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
IPAddress ip(172,16,40,120); 

boolean isPut;
byte state;
int count;
byte ledIndex;
byte ledUriOffset;
int htmlLength;

byte rPin = 5;
byte gPin = 3;
byte bPin = 6;
byte led[] = {0, 0, 0};

void setColor (byte r, byte g, byte b) {
  // SparkFun LED
  analogWrite(rPin, r);
  analogWrite(gPin, g); 
  analogWrite(bPin, b);
}

byte byteFromHexChar (char ch) {
  byte result;
  if ((ch >= '0') && (ch <= '9')) {
    result = ch - '0';
  } else if ((ch >= 'a') && (ch <= 'f')) {
    result = 10 + (ch - 'a');
  } else if ((ch >= 'A') && (ch <= 'F')) {
    result = 10 + (ch - 'A');
  } else {
    result = 0;
  }
  return result;
}

void parseRequestChar (char ch) {
  if (count == 0) {
    isPut = ch == 'P';
  } else if ((count >= ledUriOffset) && (count < ledUriOffset + 6)) {
    byte d = byteFromHexChar(ch);
    if ((count - ledUriOffset) % 2 == 0) {
      led[ledIndex] = d;
    } else {
      led[ledIndex] = led[ledIndex] * 16 + d;
      ledIndex++;
    }
  }
  if (state == RECEIVING) {
    if (ch == '\r') {
      state = READ_CR;
    }
  } else if (state == READ_CR) {
    // assert ch == '\n'
    state = READ_CRLF;
  } else if (state == READ_CRLF) {
    if (ch == '\r') {
      state = READ_CRLFCR;
    } else {
      state = RECEIVING;
    }
  } else if (state == READ_CRLFCR) {
    // assert ch == '\n'
    state = DONE;
  }
  count++;
}

void sendPutResponse (EthernetClient c) {
  c.println(http200);
  c.println(contentTypeTextPlain);
  c.print(contentLength);
  c.println("3");
  c.println(connectionClose);
  c.println();
  c.print("200");
  c.flush();
}

void sendGetResponse (EthernetClient c) {
  c.println(http200);
  c.println(contentTypeTextHtml);
  c.print(contentLength);
  c.println(htmlLength);
  c.println(connectionClose);
  c.println();
  c.print(html);
  c.flush();
}

void receiveRequest (EthernetClient c) {
  count = 0;
  ledIndex = 0;
  state = RECEIVING;
  while (c.connected() && (c.available() > 0) && (state != DONE)) {
    char ch = c.read();
    //Serial.print(ch);
    parseRequestChar(ch);
  }
}

void setup() {
  //Serial.begin(9600);
  //Serial.println("setup");
  pinMode(rPin, OUTPUT);
  pinMode(gPin, OUTPUT);
  pinMode(bPin, OUTPUT);
  setColor(255, 255, 255);
  Ethernet.begin(mac, ip);
  htmlLength = sizeof(html) - 1;
  ledUriOffset = sizeof("PUT /") + sizeof("/led/") - 3 * 1;
  setColor(0, 0, 0);
}

void loop() {
  EthernetClient client;
  client.connect(server, 80);
  if (client.connected()) {
      receiveRequest(client);
      if (state == DONE) {
        if (isPut) {
          setColor(led[0], led[1], led[2]);
          sendPutResponse(client);
        } else {
          sendGetResponse(client);
        }
      }
    } else {
      //Serial.println("timeout");
    }
    client.stop();
  }
}

And errors encountered if trying to compile that:

HelloTriColorLedYalerService.ino: In function 'void loop()':
HelloTriColorLedYalerService:161: error: 'server' was not declared in this scope
HelloTriColorLedYalerService.ino: At global scope:
HelloTriColorLedYalerService:177: error: expected declaration before '}' token

Thank you in advance for your help! :slight_smile:

remove Yaller

the Yaler stuff

I have no idea what either Yaler or Yaller is, regardless of how you spell it, but you might like to have a look at this tutorial.

Edit.... fyi, my server turns an led off and on.

JimboZA:

remove Yaller

the Yaler stuff

I have no idea what either Yaler or Yaller is, regardless of how you spell it, but you might like to have a look at this tutorial.

My appologies, it is “Yaler” and it is a relay service that can be used to access the board from anywhere by going to http//try.yaler.net/“whatever is set to <const char yalerId = “my-arduino”;>” so the page would be accessible without the IP address. I would like to remove this service, and host the page and connect directly with an IP address. Just for learning purposes, Yaler is not needed.

Thank you for your response, but I have seen that tutorial also. The way that the POST/GET methods are integrated in the other tutorial, I’m not sure how to make it work on a different server.

Any chance I could get a copy of the source code for your program?

Have you got enough RAM for all that HTML?

Any chance I could get a copy of the source code for your program?

Sure, attached, with appropriate attribution to the kat who zooms… It includes reading an LM35 on an analog pin, and a DS1302 Real Time Clock.

You’ll obviously need to check the part at the top re the lan ip, gateway etc, as well as server port.

I use ddns from dyn btw, so don’t have to worry about the wan ip changing everytime the dog barks.

Jim

zk_webserver_buttons_with_LM_and_RTC.ino (5.87 KB)

AWOL:
Have you got enough RAM for all that HTML?

There are 32KB storage that it is stored on. That works fine.

JimboZA:

Any chance I could get a copy of the source code for your program?

Sure, attached, with appropriate attribution to the kat who zooms.... It includes reading an LM35 on an analog pin, and a DS1302 Real Time Clock.

You'll obviously need to check the part at the top re the lan ip, gateway etc, as well as server port.

I use ddns from dyn btw, so don't have to worry about the wan ip changing everytime the dog barks.

Jim

Thank you! I will definitely use this to help learn different functions that the Arduino can do.

There are 32KB storage that it is stored on

On which processor?

AWOL:

There are 32KB storage that it is stored on

On which processor?

Using Arduino Uno with ATMEGA328P-PU chip. Not sure if that is what you mean?

That processor doesn't have 32kB of RAM, which is where that HTML is placed

Thank you! I will definitely use this to help learn different functions that the Arduino can do.

If you need a hand understanding some of that, shout.

You may have noticed when you offed and onned my LED, if you looked at the url line in your browser, that it added /?on8 or /?off to my url. That was a consequence of the strings /?on8 or /?off being the hrefs of the hyperlink where you clicked. View the source in your browser when my page is loaded: ctrlU in chrome for instance. Then the Arduino sketch looks for them here: if(readString.indexOf("on8") >0) and here: if(readString.indexOf("off") >0)

AWOL:
That processor doesn’t have 32kB of RAM, which is where that HTML is placed

The HTML code is in the program itself. I’m sorry, I don’t understand what you are asking… I am able to run it like a webserver, and it worked with Yaler, so I am not sure what is happening.

JimboZA:

Thank you! I will definitely use this to help learn different functions that the Arduino can do.

If you need a hand understanding some of that, shout.

You may have noticed when you offed and onned my LED, if you looked at the url line in your browser, that it added /?on8 or /?off to my url. That was a consequence of the strings /?on8 or /?off being the hrefs of the hyperlink where you clicked. View the source in your browser when my page is loaded: ctrlU in chrome for instance. Then the Arduino sketch looks for them here: if(readString.indexOf("on8") >0) and here: if(readString.indexOf("off") >0)

I changed it a little bit so it looks a bit like this:

#ifdef useLED          
          client.println("Click to <a href=\"/?ron\">turn RED led ON</a>"); 
          client.println("
");
          client.println("Click to <a href=\"/?roff\">turn RED led OFF</a>");
          client.println("
");
          client.println("
");
          client.println("Click to <a href=\"/?gon\">turn GREEN led ON</a>"); 
          client.println("
");
          client.println("Click to <a href=\"/?goff\">turn GREEN led OFF</a>"); 
          client.println("
");
          client.println("
");
          client.println("Click to <a href=\"/?bon\">turn BLUE led ON</a>"); 
          client.println("
");
          client.println("Click to <a href=\"/?boff\">turn BLUE led OFF</a>"); 
#endif

and further down

          ///////////////////// control arduino pin
          if(readString.indexOf("ron") >0)//checks for on
          {
            digitalWrite(rLed, HIGH);    // set pin  high
          }
          if(readString.indexOf("roff") >0)//checks for off
          {
            digitalWrite(rLed, LOW);    // set pin 5 low
          }
          if(readString.indexOf("gon") >0)//checks for on
          {
            digitalWrite(gLed, HIGH);    // set pin  high
          }
          if(readString.indexOf("goff") >0)//checks for off
          {
            digitalWrite(gLed, LOW);    // set pin 5 low
          }
          if(readString.indexOf("bon") >0)//checks for on
          {
            digitalWrite(bLed, HIGH);    // set pin  high
          }
          if(readString.indexOf("boff") >0)//checks for off
          {
            digitalWrite(bLed, LOW);    // set pin 5 low
          }
          //clearing string for next read
          readString="";

Hopefully this will allow control of the RGB led. I am about to compile it and I will get back to see if it works. Thank you!

Edit: It did exactly what I was looking for. I like that method of using /?ron /?roff to be able to toggle the pins. Very simple, much more then what I was trying to do before. Thanks!

The HTML code is in the program itself.

Yes, and during crt0 it gets copied to your very limited RAM.
It would be better if you left it in program memory.

AWOL:

The HTML code is in the program itself.

Yes, and during crt0 it gets copied to your very limited RAM.
It would be better if you left it in program memory.

How would I be able to do that?

My final working code:

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

#define useLED

// Configure network settings
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // MAC Address
byte ip[] = { 
  172, 16, 40, 120}; // IP Address
byte gateway[] = { 
  172, 16, 40, 1 }; // Gateway (router IP)
byte subnet[] = { 
  255, 255, 248, 0 }; // Subnet Mask
EthernetServer server(80); // Port to run the webserver on, default 80, change to anything between 1025-65535, and connect with http://server-ip:port

String readString;
// Using RGB led sparkfun.com/products/105
int rLed = 3;
int gLed = 5;
int bLed = 6;

void setup(){
  pinMode(rLed, OUTPUT);
  pinMode(gLed, OUTPUT);
  pinMode(bLed, OUTPUT);
  digitalWrite(rLed, LOW);
  digitalWrite(gLed, LOW); 
  digitalWrite(bLed, LOW); 


// to be safe, disabling SD explicitly
    pinMode(4, OUTPUT);
    digitalWrite(4, HIGH);


  //start Ethernet
  Ethernet.begin(mac, ip, gateway, gateway, subnet);
  server.begin();
}

void loop(){
  // Create a client connection
  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; 
          //Serial.print(c);
        } 

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

          // Serial.println(readString); //print to serial monitor for debuging 

          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          // client.println("Refresh: 600");  // refresh the page automatically every 10 mins
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>ArduinoMation test page</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");

          client.println("<H1>Testing automation via web</H1>");

          client.println("<H3>Thanks to Arduino forum guys for help</H1>");
          
#ifdef useLED          
          client.println("Click to <a href=\"/?ron\">turn RED led ON</a>"); 
          client.println("
");
          client.println("Click to <a href=\"/?roff\">turn RED led OFF</a>");
          client.println("
");
          client.println("
");
          client.println("Click to <a href=\"/?gon\">turn GREEN led ON</a>"); 
          client.println("
");
          client.println("Click to <a href=\"/?goff\">turn GREEN led OFF</a>"); 
          client.println("
");
          client.println("
");
          client.println("Click to <a href=\"/?bon\">turn BLUE led ON</a>"); 
          client.println("
");
          client.println("Click to <a href=\"/?boff\">turn BLUE led OFF</a>"); 
#endif
          client.println("<H4>Hardware:</H4>");
          client.println("Arduino UNO");
          client.println("
");
          client.println("Ethernet shield"); 
          client.println("
");
          client.println("
");
          client.println("
");
          client.println("Code edited from JimboZA from forum.arduino.cc   Credit is given for his great help!");
          client.println("</BODY>");
          client.println("</HTML>");

          delay(1);
          client.stop();

#ifdef useLED
          // Control Arduino Pins
          if(readString.indexOf("ron") >0)
          {
            digitalWrite(rLed, HIGH);
          }
          if(readString.indexOf("roff") >0)
          {
            digitalWrite(rLed, LOW);
          }
          if(readString.indexOf("gon") >0)
          {
            digitalWrite(gLed, HIGH);
          }
          if(readString.indexOf("goff") >0)
          {
            digitalWrite(gLed, LOW);
          }
          if(readString.indexOf("bon") >0)
          {
            digitalWrite(bLed, HIGH);
          }
          if(readString.indexOf("boff") >0)
          {
            digitalWrite(bLed, LOW);
          }
          //clearing string for next read
          readString="";
#endif
        }
      }
    }
  }
}

Thanks for the help! JimboZA, credit was given for the code example!

client.println(F("Click to <a href=\"/?ron\">turn RED led ON</a>"));

Etc, etc

AWOL:

client.println(F("Click to <a href=\"/?ron\">turn RED led ON</a>"));

Etc, etc

Would I put all of the HTML into one variable and just call that variable? I still don’t understand…

You can't call a variable, unless the variable is a function pointer.
The F() macro ensure the string stays in program memory and doesn't waste RAM.

AWOL:
You can't call a variable, unless the variable is a function pointer.
The F() macro ensure the string stays in program memory and doesn't waste RAM.

Oh okay, I didn't even notice the F(). Thank you, I will use that!

cquick197:
How would I be able to do that?

Google "Arduino progmem".