Go Down

Topic: Webserver & external files from SD card, no show... (Read 6 times) previous topic - next topic



I'm experimenting with a webserver. Everything (UNO, 5100 Ethernetshield) runs fine as long as I put all the html code inline in the sketch.
When I try to load the html from a SD card only the text shows. I shamelessly copied the following sketch from another post on this forum (http://arduino.cc/forum/index.php/topic,158718.0.html), I only reduced the buffer sizes to 64.
I tried other examples too, i.e. the one from this post: http://arduino.cc/forum/index.php?topic=156244.0 and other from around the web, but every time only text shows up. No javascript loads, no external css files load etc.

This is the code I tried before giving in and ask for help:

Code: [Select]
#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

// MAC address from Ethernet shield sticker under board
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0xE7, 0xAC };
IPAddress ip(192, 168, 16, 100); // IP address, may need to change depending on network
byte dn[] = {8, 8, 8, 8 };
byte gw[] = { 192, 168, 16, 254 };
byte mask[] = { 255, 255, 255, 0 };
EthernetServer server(80);  // create a server at port 80

File webFile;
char header[64];
char filename[64];
int txtlen;
uint8_t buf[64];
int aantal;

void setup()
    pinMode(10, OUTPUT);
    Ethernet.begin(mac, ip, dn, gw, mask);
    server.begin();           // start to listen for clients
    Serial.begin(9600);       // for debugging
    // initialize SD card
    Serial.println("Initializing SD card...");
    if (!SD.begin(4)) {
        Serial.println("ERROR - SD card initialization failed!");
        return;    // init failed
    Serial.println("SUCCESS - SD card initialized.");

void loop()
    EthernetClient client = server.available();  // try to get client

    txtlen = 0;
    if (client) {  // got client?
        boolean currentLineIsBlank = true;
        while (client.connected()) {
            if (client.available()) {   // client data available to read
                char c = client.read(); // read 1 byte (character) from client
                if (txtlen < 63) {
                  header[txtlen++] = c;
                  header[txtlen] = 0;
                // last line of client request is blank and ends with \n
                // respond to client only after last line received
                if (c == '\n' && currentLineIsBlank) {
                  webFile = SD.open("index.htm");            // open web file
                  if (!webFile) {
                    Serial.println("File not found!!");
                    client.println("HTTP/1.1 404 NOT FOUND");
                  else {
                    // send a standard http response header
                    client.println("HTTP/1.1 200 OK");
                    client.println("Content-Type: text/html");
  client.println("Connection: close");
                    // send web page
                  if (webFile) {
                        while(aantal = webFile.available()) {
                            if (aantal > 63) {
                              webFile.read(buf, 64);
                              client.write(buf, 64);
                            else {
                              webFile.read(buf, aantal);                             
                              client.write(buf, aantal); // send web page to client
                // every line of text received from the client ends with \r\n
                if (c == '\n') {
                    // last character on line of received text
                    // starting new line with next character read
                    currentLineIsBlank = true;
                else if (c != '\r') {
                    // a text character was received from client
                    currentLineIsBlank = false;
            } // end if (client.available())
        } // end while (client.connected())
        delay(100);      // give the web browser time to receive the data
        client.stop(); // close the connection
    } // end if (client)

and this is the html:

Code: [Select]

<meta http-equiv="Content-Language" content="nl">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>This picture is from the Arduino</title>


<p><font size="5">This picture is from the Arduino webserver:</font></p>
<img border="0" src="picture.jpg" width="349" height="262"></p>



Both picture.jpg and index.htm are in the root of the SD card. The card initializes fine, the index.htm is found and is shown in the browser. But where the picture should be just the 'missing image' symbol is shown.
I'm guessing it is a memory problem, but I have no idea how to find out. Or is it something to do with mime-types? I'm lost here...


Code: [Select]
                char c = client.read(); // read 1 byte (character) from client
You don't seem to care what the client is asking for. At least not to the extent of printing the client's request.

The client initially asks for the page. You supply that. The page includes an img tag, so the client says "Aha, I need to ask for that, too.". It does, but you don't care. You do not respond properly to that request. Ergo, the image is not supplied, and the image does not appear.

Not exactly rocket surgery to figure out what to do. Respond (appropriately) to the client request, without assuming that you know what the client is asking for.


Thanks, that makes perfect sense. But even though it's not rocket science to figure out what to do, figuring out how to do it is beyond me at the moment. (As is the reason this code appears to work for others, but that's another topic).
I've looked for tutorials and examples for hosting files from a SD card, but so far I've not found much (except for the forementioned examples that don't seem to work for me). I don't want anybody to write the code for me, but maybe a pointer to a tutorial dealing with this problem?


I don't know of such a tutorial, but I think that if you Serial.print(c), you'll see the problem, and perhaps the solution. Suppose the client sends "GET / HTTP 1.0" to the Arduino. Currently, you ignore what the client asks for, and send a reply. As it happens, that is the correct response, in this case.

But, the page you send back has an image tag in it. So, the client sees this, and makes another request -
"GET /picture.jpg HTTP 1.0". You don't care what the client asked for, so you send the base page again. This does not satisfy the request, so the client discards the data.

What you need to do is collect the client request in a string (a NULL terminated array of chars), which you are doing, and then parse that request to see what the client is asking for. If the client IS asking for / or /index.htm, send that. If the client is asking for /picture.jpg, send that.

zoomkat has posted some code that reads image data from an SD card and sends it back to a client.


OK, that also explains why the code I copied probably worked in it's original form. It had an 'extract filename' function:

Code: [Select]
void ExtractFileName()
  int i, j;
  while (header[i++] != ' ') {
  while (header[i] != ' ') {
    filename[j++] = header[i++];
  filename[j] = 0;
  if (filename[0] == '/' && filename[1] == 0) {
    strcpy(filename, "index.htm");

And in stead of just opening SD.open("index.htm") it had:

Code: [Select]
                  Serial.print("Extracted filename is: ");
                  webFile = SD.open(filename);            // open web file

However I kept getting "Extracted filename is /index.htm"  "file not found".
I now put back that code and now I see the picture appear. As far as I know the code now is identical to what I was doing before (giving me the constant "file not found") so I don't quite understand what went wrong the first time. As it turns out it's a good thing though.
Your explanation put me back on track, I now (sort of) understand what this code is doing and why it's there. Time to dive deeper into this.

Thanks a lot,


Go Up