SD card image to webpage issue

Can't get my Uno + Ethernet shield to display a JPG on the webpage. I think I am missing some vital part?
It displays a box on the webpage, but no image. Tried different image files and SD cards.

What am I missing?

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

File myFile;

#define Output1 3
#define Output2 5
#define Output3 6 
#define Output4 7
#define Output5 8
#define Output6 9
#define Piezo A2
#define Reset A3
#define Local A4
//#define Confirm A5

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDA, 0x02 };

// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(80);

int hash1;
int data1;
int data2;
int data3;

int count;
int readposition;

String readString;

byte ip_1;          //Ip address pulled from SD card
byte ip_2;
byte ip_3;
byte ip_4;

//----------------------------------------------------------------------------------------------------------------------------- 

void setup()
{  

pinMode(Reset, INPUT); 
pinMode(Output1, OUTPUT); 
pinMode(Output2, OUTPUT); 
pinMode(Output3, OUTPUT);
pinMode(Output4, OUTPUT);
pinMode(Output5, OUTPUT);
pinMode(Output6, OUTPUT);
pinMode(Piezo, OUTPUT);
pinMode(Local, OUTPUT);
//pinMode(Confirm, INPUT);

  Serial.begin(9600);  
  
  //Factory_reset ();
  
  data1=EEPROM.read(551);                                   //Check to see if this is the first time this program has ever run
  if (data1==0) {First_run ();} 
//----------------------------------------------------------------------------------------------------------------------------- 
                                                                               
  while (!Serial) {                                         // Open serial communications and wait for port to open:
  }

  Serial.println();
  Serial.print(F("Checking for SD card... "));
  Serial.println();

  if (SD.begin(4)) {
    
     Serial.println(F("Found SD card"));
     delay(50);

  // Open the file for reading:
  myFile = SD.open("SiteData.txt");
  if (myFile) {
    Serial.println(F("SiteData.txt:"));
    Serial.println(F(" "));

    for (count=0;count<=500;count++) {                    //Clear all EEPROM text memory allocations 
      EEPROM.write(count,32);
    } 
    Serial.println(F("Existing data erased.  New data being loaded from SD card"));
    Serial.println(F(" "));

    // read from the file until there's nothing else in it:
    while (myFile.available()) {

       hash1=myFile.read();                               //Search for a hash
         if (hash1==35) {                                 //If a hash is found, get the line number of the data
             Serial.print(char(hash1));                   //Display the hash+line number
             data1=(myFile.read()-48);             
             data2=(myFile.read()-48);
                      
             data3=((data1*10)+data2);                    //Calculate which line number we are reading
             Serial.print(data3);
                       
             EEPROM.write((data3*30)-30,data3);           //Store the line number at its own line number address *30 then -30 (because there is no zero line number)
             
             for (count=0;count<=12;count++) {            //Skip the next 12 spaces/letters as they are the reference text.  Just print them to the serial monitor
                 Serial.print(char(myFile.read()));
                 }   
                    
             for (count=1;count<30;count++) {             //Read the next 30 letters or numbers and store them incrementally from the known line number start point
                 readposition=(myFile.position());        //Get the current read position
                 data1=(myFile.read());                          
                   if (data1==35) {                       //If it finds another hash at the beginning of the next line, then process ready for next line data   
                     myFile.seek(readposition-1);         //Move the read position back one byte                
                     break;                               //Exit this line read
                   }                  
                 Serial.print(char(data1));          
                 EEPROM.write(((data3*30)-30)+count,data1); 
                 if (count==29) {Serial.print('\n');}     //If it doesn't move to the next line due to finding the next hash, then force a new line                
             }
             if (data3>=16) {break;}                      //If all 16 files are found then exit
         }
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println(F("error opening SiteData.txt"));
  }

  }else {Serial.print(F("No SD card present, using memory stored data"));}

IPAddress ip(192, 168, 1, 75);
IPAddress gateway(192, 168, 1, 254);
IPAddress subnet(255, 255, 255, 0);

  //start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
}


void loop() {   

  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        // 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.1 200 OK"));                       //Send new page
          client.println(F("Content-Type: text/html"));
          client.println();        
          client.println(F("<html>"));         
          client.println(F("<body>"));
          
      client.println(F("<body style='background-color:black'>"));
//Box around screen here

      //client.println(F("<p style='color:yellow; text-align:center> <form action=''>User name:
<input type='text' name='userid'>
User password:
<input type='password' name='psw'></form>"));
      
      client.println(F("<h1 style='color:white; strong; text-align:center'; >SMART</h1>"));
      client.println(F("</p>"));

      client.println(F("<center><svg width='500' height='8'> <rect width='500' height='8' style='fill:rgb(255,0,0);stroke-width:10;stroke:rgb(255,0,0)' /> Sorry, your browser does not support inline SVG.</svg>"));


      client.println(F("</p>"));
      client.println(F("</p>"));

      client.println(F("<h2 style='color:yellow; strong; text-align:center'; >Remote Power Controller v1.0</h2>"));
      client.println(F("</p>")); 

      client.println(F("<form action='/action_page.php'>Quantity:<input type='number' name='1st range'min='0' max='255' step='1' value='192'><input type='submit'></form>"));

      client.println(F("<form action='/action_page.php'>Quantity (between 0 and 255):<input type='1st range' name='quantity' min='0' max='255'><input type='submit'></form>"));

      //client.println(F("<input type='number' placeholder='ip address' autocomplete='off' name='firstname'>
"));
      
      
      client.println(F("</p>"));
      
      client.println("Content-Type: image/jpeg");
      
      File myFile = SD.open("Smart.jpg");

      client.print(F("<img src='Smart.jpg' alt='PPHE' style='width:304px;height:228px;'>"));
         
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();
    
  }
}
             for (count=0;count<=12;count++) {            //Skip the next 12 spaces/lette

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 is NOT 12 numbers.

                 readposition=(myFile.position());

(What's) (with) (the) (useless) (parentheses) (?)

What am I missing?

Some code to actually send the image data.

Gee thanks for the really helpful reply

Don't remember asking how to count to 12 ... which is nothing to do with this question

And the parentheses are how I lifted them from someones elses code... and it works fine.

I know I am code missing... THATS THE QUESTION

The server gives the html for a requested page. The client analyses the html, sees e.g. an image tag and requests the image from the server.

So you need to be able to handle two requests; one for e.g. index.html and one for the image.

You can press in the browser and check the requests (in firefox you can find them under console and network tabs); not sure about other browsers.

which is nothing to do with this question

But, the code doesn't match the comment, so one of them should be fixed.

You need, as sterretje points out, to actually look at what the client is requesting, and supply the appropriate response, which MIGHT be nothing, as the client is also likely to request favicon.ico, which you do not need to respond to.

Well most of the code is simply lifted from other peoples examples. None of the examples appear to have any other relevant code to lift the image from the SD card.

I can lift the TXT files off the SD card fine and send them to the webpage. I did read somewhere that the SD card slot doesn't always work when using the ethernet shield, but then what s the point of having the SD card slot on the shield?

The image box size appears on the webpage (depending on the dimensions you give it)... but you get the little image missing icon within that dimensions box on the page.

PaulS:
You need, as sterretje points out, to actually look at what the client is requesting, and supply the appropriate response, which MIGHT be nothing, as the client is also likely to request favicon.ico, which you do not need to respond to.

OP:
You haven't seemed to let this advise soak in. I don't see any place in your code where you're looking at the request from the client and responding appropriately. Wait for the request for the image to come in, then open the file on the SD card and stream it to the client in response. Then, close the file.

No, I realise I am some vital piece of code missing... I am just not sure what it is.
I will continue to play around with it.

There's lots of Google hits for "arduino web server image from sd".

Perhaps: Displaying a Web Page with Image using the Arduino Ethernet Shield and SD Card

That is the page I lifted quite a bit of code from. Not sure what bit I have missed that is relevant to loading the image. I will try all that code as a stand-alone program and see if it works

Steve2018:
That is the page I lifted quite a bit of code from.

Where in your code is the equivalent of this functionality from that page?

// last line of client request is blank and ends with \n
                // respond to client only after last line received
                if (c == '\n' && currentLineIsBlank) {
                    // open requested web page file
                    if (StrContains(HTTP_req, "GET / ")
                                 || StrContains(HTTP_req, "GET /index.htm")) {
                        client.println("HTTP/1.1 200 OK");
                        client.println("Content-Type: text/html");
                        client.println("Connnection: close");
                        client.println();
                        webFile = SD.open("index.htm");        // open web page file
                    }
                    else if (StrContains(HTTP_req, "GET /page2.htm")) {
                        client.println("HTTP/1.1 200 OK");
                        client.println("Content-Type: text/html");
                        client.println("Connnection: close");
                        client.println();
                        webFile = SD.open("page2.htm");        // open web page file
                    }
                    else if (StrContains(HTTP_req, "GET /pic.jpg")) {
                        webFile = SD.open("pic.jpg");
                        if (webFile) {
                            client.println("HTTP/1.1 200 OK");
                            client.println();
                        }
                    }
                    if (webFile) {
                        while(webFile.available()) {
                            client.write(webFile.read()); // send web page to client
                        }
                        webFile.close();
                    }

Is that not lifting (after searching) a request from the files on the sd card after its been sent to the webpage?
Its looking for index.htm, page2.htm or pic.jpg.

I am sending my webpage details to the page within my code and they work fine.
I would have thought (obviously incorrectly) that there would be no difference in me directly sending the pic.jpg file like I have the rest of the webpage.

Anyway, I clearly don't understand that bit, so I will have to rethink my method.

Steve2018:
Is that not lifting (after searching) a request from the files on the sd card after its been sent to the webpage?

You don't send stuff to a web page. So I do not understand what you mean.

Steve2018:
Its looking for index.htm, page2.htm or pic.jpg.

You get a request for something; a html document, a css file, an image, whatever. You analyse the request and handle accordingly.

To put the example in perspective, when you visit arduino.cc, your browser sends a GET request to the server (GET https://www.arduino.cc/ if you look in the F12 window). This is a request for the root of the page as there is no filename specified; now web servers have some rules and one of them tells the webserver that if it receives a request without a filename, it should 'send' e.g. index.php (in the example that you don't understand, index.htm). You could also have requested arduino.cc/index.php which tells the browser that you want the specific page and the browser will 'send' that page.

Please note that arduino.cc uses dynamic web pages (php in this case) which will first be processed by the server to e.g. retrieve data from a database; the result of that processing is send to the browser (and hence send in the previous paragraph is between single quotes).

Now you click one of the links on that page, e.g. "buy an arduino"; the browser will now send a GET request to the server (GET https://store.arduino.cc/). This is similar (not exactly the same) to a request for your page2.htm.

Steve2018:
I am sending my webpage details to the page within my code and they work fine.

That does not mean it's the preferred / correct approach. It's far better to store your pages and pictures on sd card as it gives you the flexibility to add and modify without having to rewrite your code each time that you want to change something; simply modify the file on the sd card.

Steve2018:
I would have thought (obviously incorrectly) that there would be no difference in me directly sending the pic.jpg file like I have the rest of the webpage.

Of course there is a difference; the one is hard-coded in your code, the other one is a file.

Steve2018:
Anyway, I clearly don't understand that bit, so I will have to rethink my method.

No, you will have to understand that part. Rethinking will only help if you are not going to use images, style sheets, other pages etc.

You could also have requested arduino.cc/index.php which tells the browser that you want the specific page and the browser will 'send' that page.

In this context, 'send' means executing the PHP script, and sending the output that the script generates.

The example uses index.htm, instead of index.php, because the Arduino can't execute a PHP script, but it can serve up a static html page.

Steve2018:
Anyway, I clearly don't understand that bit, so I will have to rethink my method.

Instead of “rethinking” a new incorrect way to do it, why not try the method from the (presumably) working example that you incorrectly emulated?

gfvalvo:
Instead of “rethinking” a new incorrect way to do it, why not try the method from the (presumably) working example that you incorrectly emulated?

Because the sd card is not always going to be available to lift the website index file off of.
The entire static standard webpage is created in the Arduino code, and works with or without the sd card.
The only part that doesn't work, is the Logo image.

By the fact that I was client.printing meant I was simply sending items to a web page.... but I realise now that isn't the case

You can store the image in the memory of the Arduino. If it's small enough (a few K), it will probably fit in PROGMEM (depending on the size of the rest of the code).

You still need to honour the different requests for the page and the image.

sterretje:
You can store the image in the memory of the Arduino. If it's small enough (a few K), it will probably fit in PROGMEM (depending on the size of the rest of the code).

You still need to honour the different requests for the page and the image.

Right. The key point you're missing is that you must follow the Cclient / Server protocol. When the Client requests the main page, then your Server should send the HTML. When it requests the image, then stream the .jpg image. You must analyze the request and respond appropriately. The example code does that, yours doesn't.

The above is true regardless of where the data you're sending back resides -- SD Card, PROGMEM, RAM strings, etc. So, when the Client requests the image, stream it from the SD (if present). If the SD isn't there, stream a default image from PROGMEM.

Bottom line -- FOLLOW THE PROTOCOL!!!

I am sure I am going to get grief over this, but hey ho...

I have been playing around with various bits of code I have lifted from all over Google. Got my images to display from the SD card etc.

I wanted a login page - user name and password.
I used some code I found on here, and it works... but, it then shows the password and username in the URL at the top of the page. Not very useful.

Please don't pull it all apart... its a mess because I have been pulling it all over the place until I get my head around it all.

I have been designing the page layout in the code, and then when happy with it, putting into a htm file on the sd card and retrieving it.

This code does work, but if suffers all kinds of issues, as I don't understand HTML and basically I am learning it as I go + lifting code + trial and error. But, only one way to learn it.

This has become an experiment, as its far too unreliable to use (as it is). Especially as it seems to remember the login details and display them in the URL!

I have simply thrown all the data into EEPROM at the moment. I think it would be better to properly address that into memory with strings or something later. There is currently no end of data reading or anything.

Added a book on HTML to my Amazon basket... maybe that will help

Anyone know of a better way to achieve what I was after (apart from a college course)..

1/ Go to an ip address, and a first page load with 2x images and a user+password box.
2/ Correct login results in page 2 loading with either SD data or EEPROM data loaded.
3/ Incorrect login goes to Google.com

Had to bin some code - over limit. In fact... gotta wait 5 mins now until I can post the code

Annoying...  had to delete loads of code

void setup()
{ 

    
pinMode(10, OUTPUT);                                        // disable Ethernet chip
digitalWrite(10, HIGH);

  Serial.begin(9600);  
  
  //Factory_reset ();
  
  data1=EEPROM.read(551);                                   //Check to see if this is the first time this program has ever run
  if (data1==0) {First_run ();} 
                                 
  while (!Serial) {                                         // Open serial communications and wait for port to open:
  }

  Serial.println();
  Serial.print(F("Checking for SD card... "));
  Serial.println();

  if (SD.begin(4)) {
    
     Serial.println(F("SUCCESS: Found SD card"));
     Serial.println();
     delay(50);
  
  myFile = SD.open("SiteData.txt");                       // Open the file for reading:
  if (myFile) {
    Serial.println(F("Opening SiteData.txt:"));
    Serial.println(F(" "));

 //### deleted all the files saving here
    
    myFile.close();                                       // close the file:
  } else {
    
    Serial.println(F("Error opening SiteData.txt"));      // if the file didn't open, print an error:
    myFile.close();
  }

  }else {Serial.print(F("No SD card present, using memory stored data"));}

  delay(500);

  Serial.println();
  Serial.println("Obtaining webpage layout details and logos...");                              // Open Page1.html on SD card to get webpage layout details + logos
  if (SD.exists("Page1.htm")) {Serial.println("SUCCESS: Opening Page1.htm file.");}
  else {Serial.println("ERROR:  Can't find Page1.htm file!");}


IPAddress ip(192, 168, 1, 75);
IPAddress gateway(192, 168, 1, 254);
IPAddress subnet(255, 255, 255, 0);

Ethernet.begin(mac, ip);      //start the Ethernet connection and the server:
server.begin();               // start to listen for clients       
}

void loop() {   

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

    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
                // buffer first part of HTTP request in HTTP_req array (string)
                // leave last element in array as 0 to null terminate string (REQ_BUF_SZ - 1)
                if (req_index < (REQ_BUF_SZ - 1)) {
                    HTTP_req[req_index] = c;          // save HTTP request character
                    req_index++;
                }
                // print HTTP request character to serial monitor
                Serial.print(c);
                // last line of client request is blank and ends with \n
                // respond to client only after last line received
                if (c == '\n' && currentLineIsBlank) {
                    // open requested web page file
                    if (StrContains(HTTP_req, "GET / ")
                                 || StrContains(HTTP_req, "GET /login.htm")) {
                        client.println("HTTP/1.1 200 OK");
                        client.println("Content-Type: text/html");
                        client.println("Connnection: close");
                        client.println();
                        myFile = SD.open("login.htm");                                  //Open first web page file and obtain the names of the logo files
                    }
                    else if (StrContains(HTTP_req, "GET /Ourlogo.jpg")) {               //Display our logo
                        myFile = SD.open("Ourlogo.jpg");
                        if (myFile) {
                            client.println("HTTP/1.1 200 OK");
                            client.println();
                        }
                    }
                    else if (StrContains(HTTP_req, "GET /Sitelogo.jpg")) {              //Display the site logo
                        myFile = SD.open("Sitelogo.jpg");
                        if (myFile) {
                            client.println("HTTP/1.1 200 OK");
                            client.println();
                        }
                    }
                   
                    if (myFile) {
                        while(myFile.available()) {
                            client.write(myFile.read());                                // send web page to client
                        }
                        myFile.close();
                    }


          if (StrContains(HTTP_req, "User")) {
             if (StrContains(HTTP_req, "User=Steve")) {Serial.println("Found user");}
             if (StrContains(HTTP_req, "Pswrd=12345")) {Serial.println("Found password");}

             if ((StrContains(HTTP_req, "User=Steve")) and (StrContains(HTTP_req, "Pswrd=12345"))){
                 Serial.println("Access granted");
                 Access=true;
             }
          else
             {}      //Redirect to another website if wrong submission after 5 attempts.  Yet to be determined
          }



if (Access==true) {     client.println("HTTP/1.1 200 OK");                              //If access is granted, then populate the screen
                        client.println("Content-Type: text/html");
                        client.println("Connnection: close");
                        client.println();
                        myFile = SD.open("systemstatus.htm");                                 

                        if (myFile) {
                        while(myFile.available()) {
                            client.write(myFile.read());                                // send web page to client
                        }
                        myFile.close();
                        }

                        Access=false;
}

                   
                    req_index = 0;                                                    // reset buffer index and all buffer elements to 0
                    StrClear(HTTP_req, REQ_BUF_SZ);
                    break;
                }
                
                if (c == '\n') {                                                      // every line of text received from the client ends with \r\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(1);                                                                     // give the web browser time to receive the data
        client.stop();                                                                // close the connection
    }                                                                                 // end if (client)
}

// sets every element of str to 0 (clears array)
void StrClear(char *str, char length)
{
    for (int i = 0; i < length; i++) {
        str[i] = 0;
    }
}

// searches for the string sfind in the string str
// returns 1 if string found
// returns 0 if string not found
char StrContains(char *str, char *sfind)
{
    char found = 0;
    char index = 0;
    char len;

    len = strlen(str);
    
    if (strlen(sfind) > len) {
        return 0;
    }
    while (index < len) {
        if (str[index] == sfind[found]) {
            found++;
            if (strlen(sfind) == found) {
                return 1;
            }
        }
        else {
            found = 0;
        }
        index++;
    }

    return 0;
}


void Read_EEPROM () {

Serial.println(" ");                   

for (count=1;count<30;count++) {
   data2=EEPROM.read(count+(((data1*30)-30)));
   Serial.print(char(data2));
}
}