interpreting HTML in combination with SD

Hey guys,

I have an arduino uno and an ethernetshield. I am running a little Webserver that gives me the value of an analog sensor. So far so good, once I include SD.h the html sent to the client is corrupt and the output in the browser is not as expected.

Attached are 2 screenshots. One showing how I expect it to look like and the other shows how it looks like once I include SD.h

the code looks as follows:

THANKS A LOT IN ADVANCE

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

byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,178,179);

const int chipSelect = 4;
int outputPin = 6;
EthernetServer server(80);

void setup() {
Serial.begin(57600);
pinMode(10, OUTPUT);
Ethernet.begin(mac, ip);
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
pinMode(outputPin, OUTPUT);

}

void loop() {
int sensorValue = analogRead(A0);
float humidityPerCent = 100-(sensorValue /10.23);

EthernetClient client = server.available();
if (client) {
Serial.println("new client");
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
if (c == '\n' && currentLineIsBlank) {
client.println( "");
client.println( "");
client.println( "");
client.println( "The Garduino");
client.println( "");
client.println( "body, input, select, textarea");
client.println( "{color: #888;font: 16pt "Source Sans Pro", sans-serif; width: 75%; margin:0 auto;}");
client.println( ".gradient { height: 50px; margin: 0 0 50px 0;");
//*** THE GRADIENTS SHOULD BE DEPENDING ON THE BOUNDARY VALUES***
client.println( "background: -webkit-linear-gradient(left, #69482D 10%, #1B701B 45%, #1B701B 55%, #1E315E 90%);"); /* For Safari 5.1 to 6.0 /
client.println( "background: -o-linear-gradient(right, #69482D 10%, #1B701B 45%, #1B701B 55%, #1E315E 90%);"); /
For Opera 11.1 to 12.0 /
client.println( "background: -moz-linear-gradient(right, #69482D 10%, #1B701B 45%, #1B701B 55%, #1E315E 90%);"); /
For Firefox 3.6 to 15 /
client.println( "background: linear-gradient(to right, #69482D 10%,#1B701B 45%, #1B701B 55%, #1E315E 90%);}"); /
Standard syntax (must be last) /
client.println( ".box{float: left !important; height: 50px;}");
//
** THE BOXES WIDTHS SHOULD BE DEPENDING ON THE BOUNDARY VALUES***
client.println( ".box.dry{width: 40%;}");
client.println( ".box.good{width: 20%; border-left:2px solid #888; border-right:2px solid #888;}");
client.println( ".box.humid{width: 37%}");
client.println( ".level{text-align: right; height: 50px; border-right: 2.5px solid #444;}");
client.println( ".level.bad{border-right:2.5px solid #c33;}");
client.println( "");
client.println( "");
client.println( "");
client.println( "

The Garduino measures the humidity
of the ficus benjaminus

");
//*** LOOP ALL POTENTIAL SENSORS ***
client.println( "top soil sensor");
client.println( "<div class ="gradient">");
client.println( "<div class="box dry"> <div class="box good"> <div class="box humid"> ");
client.print( "<div class="level bad" style="width:");
client.print( humidityPerCent);
client.print( "%;">");
client.print( humidityPerCent);
client.print( "%");
client.println( "");
//*** END LOOP ***
client.println( "");
client.println("");
break;
}
if (c == '\n') {
currentLineIsBlank = true;
}
else if (c != '\r') {
currentLineIsBlank = false;
}
}
}
delay(1);
client.stop();
Serial.println("client disconnected");
}
}

You are not sending a response header.

// add this line
        client.println(F("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n"));

        client.println( "<!DOCTYPE HTML>");
        client.println( "<html>");
        client.println( "<head>");

edit: Plus what PaulS mentions below. Use the F() function as I did with the header.

I'd hazard a guess that there is a 99.99+% probability that you are using more SRAM than your Arduino has.

You can keep string literals out of SRAM by using the F() macro:
client.println(F( "" ));

Below is an example of a web page bundled in a single F() macro. bundling like this can be broken in to parts if variables need to be printed in the page.

//example of web page bundled in single F() macro
//get submit box code
//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 a '
//address will look like http://192.168.1.102:84 when submitted
//for use with W5100 based ethernet shields
//note that the below bug fix may be required
// http://code.google.com/p/arduino/issues/detail?id=605

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

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
 pinMode(6, OUTPUT); //pin selected to control
 pinMode(7, OUTPUT); //pin selected to control
 pinMode(8, OUTPUT); //pin selected to control
 //start Ethernet
 Ethernet.begin(mac, ip, gateway, gateway, subnet);
 server.begin();

 //enable serial data print
 Serial.begin(9600);
 Serial.println(F("server text box test1")); // 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); //see what was captured

         //now output HTML data header

client.print(F(  //start F() macro
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"
"<HTML>"
"<HEAD>"
"<meta name='apple-mobile-web-app-capable' content='yes' />"
"<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />"
"<TITLE>JAVA Page</TITLE>"
"</HEAD>"
"<BODY>"
"<H1>JAVA</H1>"
"<hr />"
"
"
"<FORM ACTION='/' method=get >"
"Enter Code: <INPUT TYPE=TEXT NAME='LED' VALUE='' SIZE='25' MAXLENGTH='50'>
"
"
"
"<input type=submit value='5 ON' style=width:100px;height:45px onClick=location.href='/?on8;'><input type=submit value='5 OFF' style=width:100px;height:45px onClick=location.href='/?off9;'>
"
"<input type=submit value='6 ON' style=width:100px;height:45px onClick=location.href='/?on8;'><input type=submit value='6 OFF' style=width:100px;height:45px onClick=location.href='/?off9;'>
"
"<input type=submit value='7 ON' style=width:100px;height:45px onClick=location.href='/?on8;'><input type=submit value='7 OFF' style=width:100px;height:45px onClick=location.href='/?off9;'>
"
"<input type=submit value='8 ON' style=width:100px;height:45px onClick=location.href='/?on8;'><input type=submit value='8 OFF' style=width:100px;height:45px onClick=location.href='/?off9;'>
"
"</FORM>"
"</BODY>"
"</HTML>"
));   //end F() macro

         delay(1);
         //stopping client
         client.stop();

         /////////////////////
         if(readString.indexOf("5") >0)//checks for on
         {
           digitalWrite(5, HIGH);    // set pin 5 high
           Serial.println(F("Led On"));
         }
         if(readString.indexOf("50") >0)//checks for off
         {
           digitalWrite(5, LOW);    // set pin 5 low
           Serial.println(F("Led Off"));
         }
         
         if(readString.indexOf("6") >0)//checks for on
         {
           digitalWrite(6, HIGH);    // set pin 6 high
           Serial.println(F("Led 6 On"));
         }
         if(readString.indexOf("60") >0)//checks for off
         {
           digitalWrite(6, LOW);    // set pin 6 low
           Serial.println(F("Led 6 Off"));
         }
         
         if(readString.indexOf("7") >0)//checks for on
         {
           digitalWrite(7, HIGH);    // set pin 7 high
           Serial.println(F("Led On"));
         }
         if(readString.indexOf("70") >0)//checks for off
         {
           digitalWrite(7, LOW);    // set pin 7 low
           Serial.println(F("Led Off"));
         }
         
         if(readString.indexOf("8") >0)//checks for on
         {
           digitalWrite(8, HIGH);    // set pin 8 high
           Serial.println(F("Led On"));
         }
         if(readString.indexOf("80") >0)//checks for off
         {
           digitalWrite(8, LOW);    // set pin 8 low
           Serial.println(F("Led Off"));
         }
         //clearing string for next read
         readString="";

       }
     }
   }
 }
}

Hey guys,

thanks a lot, it has really been a SRAM problem as PaulS guessed or at least it is now working fine with the F() macro.

Is there a way to check the SRAM and print it to the serial console or something? I don't want to struggle around that problem again and agin ...

In best case I want to make myself a "checklist" if code is compiling but the results are not as expected check a) .... b) ... and so on

@surferTim: it works with as well as without your line of code, I cannot see any difference in the result.

Below is the code for those of you interested in how the code looks now.

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


// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,178,179);

// On the Ethernet Shield, CS is pin 4. Note that even if it's not used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library functions will not work.
const int chipSelect = 4;

// define the pin where the pump is attached to
int outputPin = 6;
int nTicks = 0;
int nBoundaryLow = 40;
int nBoundaryHigh = 60;

// (port 80 is default for HTTP):
EthernetServer server(80);

void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(57600);

// SD initializing
  Serial.println("Initializing SD card...");
  // make sure that the default chip select pin is set to output, even if you don't use it:
  pinMode(10, OUTPUT);
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

// ethernet initializing => start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());

// other initializations
  pinMode(outputPin, OUTPUT);

}


void loop() {
  nTicks ++;
  delay(1000);
  //read the sensor value
  int sensorValue = analogRead(A0);
  // convert it to percentage => the value rage is 0-1023 => divide by 10,23
  float humidityPerCent = 100-(sensorValue /10.23);
  //Serial.println(String(100-(sensorValue /10.23)));
  //First we write the values to the arduino

  // open the file. note that only one file can be open at a time, so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.print(nTicks);
    dataFile.print("; ");
    dataFile.print(sensorValue);
    dataFile.print("; ");
    dataFile.println(humidityPerCent);
    dataFile.close();
    Serial.println(String(nTicks) + "=>" + String(humidityPerCent));
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  }
  
 //This is for the Webserver to nicely display the status
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline character) and the line is blank, the http request has ended,so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
        client.println(F("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n"));
        client.println(F( "<!DOCTYPE HTML>"));
        client.println(F( "<html>"));
        client.println(F( "<head>"));
        client.println(F( "<title>The Garduino</title>")); 
        client.println(F( "<style>"));
        client.println(F( "body, input, select, textarea"));
        client.println(F( "{color: #888;font: 16pt \"Source Sans Pro\", sans-serif; width: 75%; margin:0 auto;}"));
        client.println(F( ".gradient { height: 50px; margin: 0 0 50px 0;"));
        //*** THE GRADIENTS SHOULD BE DEPENDING ON THE BOUNDARY VALUES***
          client.print(F( "background: -webkit-linear-gradient(left, #69482D 10%, #1B701B 45%, #1B701B 55%, #1E315E 90%);")); /* For Safari 5.1 to 6.0 */
          client.println(F( "background: -o-linear-gradient(right, #69482D 10%, #1B701B 45%, #1B701B 55%, #1E315E 90%);")); /* For Opera 11.1 to 12.0 */
          client.println(F( "background: -moz-linear-gradient(right, #69482D 10%, #1B701B 45%, #1B701B 55%, #1E315E 90%);")); /* For Firefox 3.6 to 15 */
          client.println(F( "background: linear-gradient(to right, #69482D 10%,#1B701B 45%, #1B701B 55%, #1E315E 90%);}")); /* Standard syntax (must be last) */
        client.println(F( ".box{float: left !important; height: 50px;}"));
         client.print(F( ".box.dry{width: "));
         client.print(nBoundaryLow);
        client.println(F( "%;}"));
         client.print(F( ".box.good{width: "));
         client.print(nBoundaryHigh-nBoundaryLow);
        client.println(F("%; border-left:2px solid #888; border-right:2px solid #888;}"));
        client.print(F( ".box.humid{width: "));
        client.print(100-nBoundaryHigh-3);
        client.println(F("%}"));
        client.println(F( ".level{text-align: right; height: 50px; border-right: 2.5px solid #444;}"));
        client.println(F( ".level.bad{border-right:2.5px solid #c33;}"));
        client.println(F( "</style>"));
        client.println(F( "</head>"));
        client.println(F( "<body>"));
        client.println(F( "<h1>The Garduino measures the humidity
of the ficus benjaminus</h1>"));
        //*** LOOP ALL POTENTIAL SENSORS ***
        client.println(F( "top soil sensor"));
        client.println(F( "<div class =\"gradient\">"));
        client.println(F( "<div class=\"box dry\">&nbsp;</div><div class=\"box good\">&nbsp;</div><div class=\"box humid\">&nbsp;</div>"));
        client.print(F( "<div class=\"level bad\" style=\"width:"));
        client.print( humidityPerCent);
        client.print(F( "%;\">"));
        client.print(humidityPerCent);
        client.print(F( "%</div>"));
        client.println(F( "</div>"));
        //*** END LOOP ***
        client.println(F( "</body>"));
        client.println(F("</html>"));
        break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
}

With this function, you can measure the free SRAM on your board:

int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

thanks riesens! It worked fine for me, but in the meanwhile I upgraded to the 1.5x Version of the IDE which shows the used and left spaces. Many thanks.

The only weired and "funny" thing is, that if I use a "wrong" Power Plug (12V) my webserver sometimes gives back the weired output as mentioned in my first post and once I plug it to the USB Port it runs fine.

@surferTim: it works with as well as without your line of code, I cannot see any difference in the result.

With the header, the request is correct. Without it, it is not correct, even though with some web browsers it may work ok.