WiFly Wireless SD Shield Upload JPEG to Browser Issue

Hello,

I am attempting to upload a JPEG image from the SD card on my Wireless SD shield: (http://arduino.cc/en/Main/ArduinoWirelessShield)

I have the RN-XV WiFly module attached to this shield, with a high-gain 2.4 GHZ antenna.

My issue is that the image does not upload to a web browser. When I connect to the WiFly module via web browser, the module's LED suggest that it is sending the image to my computer, and the browser's address bar shows that it is receiving data. However, when the file transfer is complete, I get an icon showing a broken image.

Please help! Code below:


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

WiFlyServer server(80);
WiFlyClient client;

char* ssid = "Wingman007";
char* passphrase = "yellowlab";
const int chipSelect = 4;
int serverPort = 80;
boolean reading = false;
File dataFile;

void setup() {
Serial.begin(9600);
WiFly.setUart(&Serial);
WiFly.begin();
WiFly.join(ssid, passphrase);
Serial.println(WiFly.ip());
server.begin();
pinMode(10, OUTPUT);

if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;

Serial.println("SD CARD INIITIALIZED");
}
}

void loop()
{
WiFlyClient client = server.available();
if (client) {
// an http request ends with a blank line
boolean current_line_is_blank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
// if we've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so we can send a reply
if (c == '\n' && current_line_is_blank) {
// send a standard http response header

dataFile = SD.open("Deer2.jpg");

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

int16_t c;
while ((c = dataFile.read()) >= 0) {
client.print((char)c);
}

dataFile.close();

break;
}
if (c == '\n') {
// we're starting a new line
current_line_is_blank = true;
}
else if (c != '\r') {
// we've gotten a character on the current line
current_line_is_blank = false;
}
}
}
// give the web browser time to receive the data
delay(10);
client.stop();
}
}

There are a number of issues with your code. First, you are sending a packet for each byte read from the file. That's like using a pickup truck per letter to deliver the mail. The pickup truck (packet) can hold a lot more than one letter.

Second, you are casting c, which is a char, to a char. Doesn't that seem a trifle silly?

The most important problem, though, is that you stop reading from the file when you encounter a NULL. JPEG files are binary files that can contain embedded NULLs. Stopping when you encounter one is wrong.

Thank you for the reply PaulS.

Could you please elaborate on methods regarding the first issue (sending larger packets)? Any hints?

Regarding your second point, here is another method I have tried that yields the same results (no image in the web browser):

NEW METHOD:
while (dataFile.available()){
client.print(dataFile.read());
}

OLD METHOD:
int16_t c;
while ((c = dataFile.read()) >= 0) {
client.print((char)c);
}

The NEW METHOD should read the information from the JPEG file until the file is not longer "available". Correct?

Is there anything wrong with this part of the code? Am I missing any header information?
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: image/jpeg");
client.println();

Could you please elaborate on methods regarding the first issue (sending larger packets)? Any hints?

The SD class has a method to read more than one byte at a time. Same name, but it takes an array and a size.

byte filestuff[32];
byte cnt = dataFile.read(filestuff, 32);

The function will read up to 32 bytes at one time. It returns the actual number read.

Regarding your second point, here is another method I have tried that yields the same results (no image in the web browser):

Storing the byte in a variable that is 4 times larger than needed is not the answer. All you needed to do was get rid of the useless cast.

Is there anything wrong with this part of the code? Am I missing any header information?

Yes. There needs to be a Content-Length statement, too.

You need to determine the size of the jpeg file. The dataFile.available() function tells you that. Then, you loop to read that number of bytes (hopefully more than one at a time), regardless of what those bytes actually are.

Thank you for the code recommendations, I will include them in the future.

As far as the "Content-Length", I am not seeing success with that and I think I am not implementing it correctly. Here is the updated code:

void loop()
{
  WiFlyClient client = server.available();
  if (client) {
              // an http request ends with a blank line
    boolean current_line_is_blank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
              // if we've gotten to the end of the line (received a newline
              // character) and the line is blank, the http request has ended,
              // so we can send a reply
        if (c == '\n' && current_line_is_blank) {
              // send a standard http response header

          dataFile = SD.open("Deer2.jpg"); //open the JPEG image file on the SD card

          client.println("HTTP/1.1 200 OK");
          client.println("Content-Length: 4656"); //future improvement to determine file length of any file. For now, I        know the size of this image file to be 4656 bytes.
          client.println("Content-Type: image/jpeg");
          client.println();
          
         //more header info here?

          while (dataFile.available()){ //future improvement to include reading 32 bytes at once
            client.print(dataFile.read());
          }
          dataFile.close();

          break;
        }
        if (c == '\n') {
          // we're starting a new line
          current_line_is_blank = true;
        } 
        else if (c != '\r') {
          // we've gotten a character on the current line
          current_line_is_blank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(10);
    client.stop();
  }
}

Am I still missing header info? Am I placing the "Content-Length" in the wrong position?

Again, I simply want to open a JPEG file on the SD card through a web browser. I am able to get the WiFly to begin sending the file, and the browser begins to load it, but in the end I get a broken-image icon/"errors in the file". I can open and view this file directly through the SD card on my computer, so the file is not corrupt.

Thank you for the assistance!

below is some test code that uploads a jpg file to IE. Note that the ("Content-Type: image/jpeg") line is commented out. I found the .jpg file uploaded better just using the standard ("Content-Type: text/html") header.

//zoomkat 12/26/12
//SD server test code
//open serial monitor to see what the arduino receives
//address will look like http://192.168.1.102:84 when submited
//for use with W5100 based ethernet shields

#include <SD.h>
#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(){

  Serial.begin(9600);

  // disable w5100 while setting up SD
  pinMode(10,OUTPUT);
  digitalWrite(10,HIGH);
  Serial.print("Starting SD..");
  if(!SD.begin(4)) Serial.println("failed");
  else Serial.println("ok");

  Ethernet.begin(mac, ip, gateway, gateway, subnet);

  //delay(2000);
  server.begin();
  Serial.println("Ready");

}

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("Content-Type: image/jpeg");
          //client.println("Content-Type: image/gif");
          //client.println("Content-Type: application/x-javascript");
          //client.println("Content-Type: text");
          
          client.println();

          //File myFile = SD.open("boom.htm");
          File myFile = SD.open("HYPNO.JPG");
          //File myFile = SD.open("BLUEH_SL.GIF");
          //File myFile = SD.open("SERVOSLD.HTM");
          if (myFile) {
            //Serial.println("test.txt:");
            // read from the file until there's nothing else in it:
            while (myFile.available()) {
              client.write(myFile.read());
            }
            // close the file:
            myFile.close();

          }
            delay(1);
            //stopping client
            client.stop();
            readString="";
          //}
        }
      }
    }
  } 
}

Thank you for the code Zoomkat! Still no success, however I did try it using the text/html content type which allowed me to see the JPEG characters. I compared them to the characters in the actual JPEG file (converted the .jpg to a .txt) and noticed that some spaces were missing from the transferred file. All of the characters where there in the correct order, however it looks like some ? were not correctly transferred (see attached image). The browser states "image contains errors".

Any additional information would be greatly appreciated. From what I have seen, the process after connecting to a client (web browser) should be:

  • Send new page header (HTTP OK)
  • Send content type (image/jpeg)
  • Open file on SD card
  • Print content of file to browser until the file is no longer available
  • Close file, close connection to client

Transfer Issue.jpg