HTML Server Web Pin Control

Here's the idea. I know how to code HTML. I have an Uno with the Ethernet Shield. I have a micro SD card. I want to put the html code on the sd card. The arduino will act as the web server to host the website. When I use a browser on the local network to access the web server (by typing in the http://192.168.0.100 for example) the website I created will appear. It will have little buttons I can click which can turn pins on and off. How do you get the html code to work with the arduino code to turn the pins on and off?

Thanks so much.

That all seems basically feasible.

njdurkin:
How do you get the html code to work with the arduino code to turn the pins on and off?

You will need to learn how to run a web server, read files from an SD card, and turn pins on and off. You will find plenty of examples showing how to do those things separately, even if you can't find an existing solution that matches exactly what you're trying to achieve.

Simple web page control code:

//zoomkat 4-1-12
//simple button GET for servo and pin 5
//for use with IDE 1.0
//open serial monitor to see what the arduino receives
//use the \ slash to escape the " in the html, or use ' instead of " 
//address will look like http://192.168.1.102:84 when submited
//for use with W5100 based ethernet shields

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

#include <Servo.h> 
Servo myservo;  // create servo object to control a servo 

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

String readString; 

//////////////////////

void setup(){

  pinMode(5, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, gateway, subnet);
  server.begin();

  myservo.write(90); //set initial servo position if desired
  myservo.attach(7);  //the pin for the servo control
  //enable serial data print 
  Serial.begin(9600); 
  Serial.println("server servo/pin 5 test 1.0"); // so I can keep track of what is loaded
}

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();

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

          client.println("<H1>Zoomkat's simple Arduino button</H1>");
          
          client.println("<a href='/?on'>ON</a>"); 
          client.println("<a href='/?off'>OFF</a>"); 

          client.println("</BODY>");
          client.println("</HTML>");
 
          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on") >0)//checks for on
          {
            myservo.write(40);
            digitalWrite(5, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            myservo.write(140);
            digitalWrite(5, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
}

Okay I made some really good progress. What I'm stuck on now is trying to use images with arduino. I have an html file on the sd card which can be accessed from a browser on the network. All the image files are on the sd card but they do not display on the html file when called. When I open the html file from the sd card on the computer the page loads fine with all the images. I'm guessing arduino doesn't have the algorithms to display or communicate those images with a browser by default. Is there some library or some way to get those images to show?

Thanks.

/*
 * USERS OF ARDUINO 0023 AND EARLIER: use the 'SDWebBrowse.pde' sketch...
 * 'SDWebBrowse.ino' can be ignored.
 * USERS OF ARDUINO 1.0 AND LATER: **DELETE** the 'SDWebBrowse.pde' sketch
 * and use ONLY the 'SDWebBrowse.ino' file.  By default, BOTH files will
 * load when using the Sketchbook menu, and the .pde version will cause
 * compiler errors in 1.0.  Delete the .pde, then load the sketch.
 *
 * I can't explain WHY this is necessary, but something among the various
 * libraries here appears to be wreaking inexplicable havoc with the
 * 'ARDUINO' definition, making the usual version test unusable (BOTH
 * cases evaluate as true).  FML.
 */

/*
 * This sketch uses the microSD card slot on the Arduino Ethernet shield
 * to serve up files over a very minimal browsing interface
 *
 * Some code is from Bill Greiman's SdFatLib examples, some is from the
 * Arduino Ethernet WebServer example and the rest is from Limor Fried
 * (Adafruit) so its probably under GPL
 *
 * Tutorial is at http://www.ladyada.net/learn/arduino/ethfiles.html
 * Pull requests should go to http://github.com/adafruit/SDWebBrowse
 */

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

/************ ETHERNET STUFF ************/
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 80 };
EthernetServer server(80);

/************ SDCARD STUFF ************/
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))

void error_P(const char* str) {
  PgmPrint("error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
  while(1);
}

void setup() {
  Serial.begin(9600);
 
  PgmPrint("Free RAM: ");
  Serial.println(FreeRam());  
  
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  pinMode(10, OUTPUT);                       // set the SS pin as an output (necessary!)
  digitalWrite(10, HIGH);                    // but turn off the W5100 chip!

  if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!");
  
  // initialize a FAT volume
  if (!volume.init(&card)) error("vol.init failed!");

  PgmPrint("Volume is FAT");
  Serial.println(volume.fatType(),DEC);
  Serial.println();
  
  if (!root.openRoot(&volume)) error("openRoot failed");

  // list file in root with date and size
  PgmPrintln("Files found in root:");
  root.ls(LS_DATE | LS_SIZE);
  Serial.println();
  
  // Recursive list of all directories
  PgmPrintln("Files found in all dirs:");
  root.ls(LS_R);
  
  Serial.println();
  PgmPrintln("Done");
  
  // Debugging complete, we start the server!
  Ethernet.begin(mac, ip);
  server.begin();
}

void ListFiles(EthernetClient client, uint8_t flags) {
  // This code is just copied from SdFile.cpp in the SDFat library
  // and tweaked to print to the client output in html!
  dir_t p;
  
  root.rewind();
  client.println("<ul>");
  while (root.readDir(&p) > 0) {
    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) break;

    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') continue;

    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;

    // print any indent spaces
    client.print("<li><a href=\"");
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print((char)p.name[i]);
    }
    client.print("\">");
    
    // print file name with possible blank fill
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print((char)p.name[i]);
    }
    
    client.print("</a>");
    
    if (DIR_IS_SUBDIR(&p)) {
      client.print('/');
    }

    // print modify date/time if requested
    if (flags & LS_DATE) {
       root.printFatDate(p.lastWriteDate);
       client.print(' ');
       root.printFatTime(p.lastWriteTime);
    }
    // print size if requested
    if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {
      client.print(' ');
      client.print(p.fileSize);
    }
    client.println("</li>");
  }
  client.println("</ul>");
}

// How big our line buffer should be. 100 is plenty!
#define BUFSIZ 100

void loop()
{
  char clientline[BUFSIZ];
  int index = 0;
  
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    
    // reset the input buffer
    index = 0;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;
          
          // continue to read more data!
          continue;
        }
        
        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;
        
        // Print it out for debugging
        Serial.println(clientline);
        
        // Look for substring such as a request to get the root file
        if (strstr(clientline, "GET / ") != 0) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          // print all the files, use a helper to keep it clean
          client.println("<h2>Files:</h2>");
          ListFiles(client, LS_SIZE);
        } else if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;
          
          filename = clientline + 5; // look after the "GET /" (5 chars)
          // a little trick, look for the " HTTP/1.1" string and 
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
          
          // print the file we want
          Serial.println(filename);

          if (! file.open(&root, filename, O_READ)) {
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>File Not Found!</h2>");
            break;
          }
          
          Serial.println("Opened!");
                    
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/plain");
          client.println();
          
          int16_t c;
          while ((c = file.read()) > 0) {
              // uncomment the serial to debug (slow!)
              //Serial.print((char)c);
              client.print((char)c);
          }
          file.close();
        } else {
          // everything else is a 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>File Not Found!</h2>");
        }
        break;
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}

Are these static images? If so, why force the the Arduino to work even harder when you can just have the images hosted on one of the many free image hosting sites and have the Arduino Web Server just send the URL and let the client web browser do all the heavy lifting?

The problem is that this local network is not connected to the internet in anyway (not even the client browser because it is connected to the arduino network only). So while I would love to do that, there is no way. Sorry that I did not specify before.

The only other solution I can think of is to have the image files on the client device and have the webserver point the client to look in its own directory for the images. Is that even possible?

Either I'm dyslexic, or you have two user names...

durkinnj:
Okay I made some really good progress. What I'm stuck on now is trying to use images with arduino. I have an html file on the sd card which can be accessed from a browser on the network. All the image files are on the sd card but they do not display on the html file when called. When I open the html file from the sd card on the computer the page loads fine with all the images. I'm guessing arduino doesn't have the algorithms to display or communicate those images with a browser by default. Is there some library or some way to get those images to show?

If the web server is serving out binary resources then the web client and server need to agree what encoding scheme to use, and the server then needs to implement that encoding scheme. (The 'agreeing' part could consist of you simply choosing an encoding scheme that you know your browser supports.)

Yeah sorry. Honestly don't know how that happened. How do I choose? What do I do? I'm confused.

You also have a PC. A PC can run almost any program you want!

You can run a full on server on you PC. The code is free. Look at Apache the webs most widely used server. You could also look at "wildcat" a Java server and many others.

Mark

Thanks for the input but unfortunately my project cannot include my computer. The arduino must host the webserver.

Okay so I think my problem will be solved if I can just get the browser to open an an html file as an html file instead of a text document because right now when I open a .htm file it opens it as a text file in the browser.

In which case your options are so limited by the RAM limits of, say the UNO, 2k bytes. That you need a re-think!.

Mark

All I'm doing is hosting a very simple pin control web server not websites.

I tried a lot of different browsers. It seems like there are two types. One type: firefox, opera, that can open files from your browser by using file://localhost/sdcard/file.ext or some similar command but will not open the html file as a webpage but instead as a text document with all the html code. Another type: dolphin that will open the html file as a webpage but cannot navigate and open files on the local disk. I need the browser to do both so that I can 1; view the html file as a webpage and 2; use images on the local disk as part of that webpage.

Otherwise I'm going to need arduino to host and display the images somehow.

UPDATE: I found out that dolphin has a command to view files that is very different but it can be done and it does open the html file as a webpage but it won't use the files on the sdcard of the mobile device.

How do I get a browser on my phone like firefox to view the .htm file as a webpage and not a text document?

How the web browser interprets the page is determined by the page encoding indicated in the HTTP response. If your HTML page is being rendered as plain text then you are sending the wrong content-type.

The reason you are seeing .html files treated as plain text is because the code tells the browser that is what it wants:

          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/plain");
          client.println();

I believe you'll find Content-Type: text/html will make the browser render the html the way you want.

-br

Why are the files on the sdcard seen with different names? When I have a name like indexabc.html arduino loads it as indexh~1.htm and that's how I access it. Why is this happening?

Your SD card is probably formatted with a FAT file system, which cannot handle .html as a normal extension, since normal extensions back when FAT was invented were limited to 3 characters. Files with more than 8 characters in the filename or three characters in the extension get this special treatment.

I'm guessing the tilde thing isn't happening to files that fit the 8.3 limit.

You could name your file .htm and see if it helps.

-br

That did help. Thank you.

Here is my problem. If I open this file on the sdcard of my phone it opens the webpage fine and displays the images perfectly fine. If I have the same exact file on the sdcard of my ethernet shield on the arduino it does not load the images. Why is this happening? Sorry to throw these html problems at you guys.

Here is what the serial monitor is displaying:

Free RAM: 831
Volume is FAT16

Files found in root:
INDEX.HTM      2012-12-19 18:06:36 1363

Files found in all dirs:
INDEX.HTM

Done
GET /INDEX.HTM HTTP/1.1
INDEX.HTM
Opened!
GET /favicon.ico HTTP/1.1
favicon.ico

The problem is the file:// url. There is a great difference between http:// and file:// urls. The main issue here is that a file:// url is only valid on the device where the resource named by the url lives.

To fetch a resource like an image across the network, it needs to be on an http:// server and the referring HTML code needs to use the appropriate http:// url to fetch it.

-br

Well I don't know if you've read any previous posts but the problem is that this local network is not connected to the internet in anyway (not even the client browser because it is connected to the arduino network only). So while I would love to have the images hosted elsewhere, I can't.

The only other solution I can think of is to have the image files on the client device and have the webserver point the client to look in its own directory for the files. I'm kind of stuck here :confused:

The only other way to go with this is to actually connect the arduino to the internet. I honestly do not know how to do this as a mobile application. I'll have to use a broadband device either usb or mobile hotspot and somehow connect the arduino to it but how?