Problems with TinyWebServer and BlinkLED

I wonder if someone could point me in the right direction.

I'm trying to get the example sketch, BlinkLED, working using the TinyWebServer library.
It all compiles nicely, but at runtime it appears to fail when it gets to any file.open commands. It's using the Sd2Card, SdVolume and SdFile classes, and I wonder if something has changed in those libraries since the original example was written to stop things working?
I'm pretty sure everything is correct hardware-wise as I can use the documented SD.h classes to read and write from the SD card.
Unfortunately, I'm very new to arduino and don't have the skills to migrate the BlinkLED example to use SD rather than SdFile.

Has anyone got any experience with this?

Help! :cold_sweat:

Here's the code I am running

// -*- c++ -*-
//
// Copyright 2010 Ovidiu Predescu <ovidiu@gmail.com>
// Date: December 2010
// Updated: 08-JAN-2012 for Arduno IDE 1.0 by <Hardcore@hardcoreforensics.com>
//

#include <pins_arduino.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Flash.h>
#include <SD.h>
#include <TinyWebServer.h>

/****************VALUES YOU CHANGE*************/
// The LED attached to PIN X on an Arduino board.
const int LEDPIN = 7;

// pin 4 is the SPI select pin for the SDcard
const int SD_CS = 4;

// pin 10 is the SPI select pin for the Ethernet
const int ETHER_CS = 10;

// Don't forget to modify the IP to an available one on your home network
byte ip[] = { 192, 168, 0, 160 };
/*********************************************/

static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// The initial state of the LED
int ledState = LOW;

void setLedEnabled(boolean state) {
  ledState = state;
  digitalWrite(LEDPIN, ledState);
}

inline boolean getLedState() { return ledState; }

boolean file_handler(TinyWebServer& web_server);
boolean blink_led_handler(TinyWebServer& web_server);
boolean led_status_handler(TinyWebServer& web_server);
boolean index_handler(TinyWebServer& web_server);

TinyWebServer::PathHandler handlers[] = {
  // Work around Arduino's IDE preprocessor bug in handling /* inside
  // strings.
  //
  // `put_handler' is defined in TinyWebServer
  {"/", TinyWebServer::GET, &index_handler },
  {"/upload/" "*", TinyWebServer::PUT, &TinyWebPutHandler::put_handler },
  {"/blinkled", TinyWebServer::POST, &blink_led_handler },
  {"/ledstatus" "*", TinyWebServer::GET, &led_status_handler },
  {"/" "*", TinyWebServer::GET, &file_handler },
  {NULL},
};

const char* headers[] = {
  "Content-Length",
  NULL
};

TinyWebServer web = TinyWebServer(handlers, headers);

boolean has_filesystem = true;
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;

void send_file_name(TinyWebServer& web_server, const char* filename) {
  if (!filename) {
    web_server.send_error_code(404);
    web_server << F("Could not parse URL");
  } else {
    TinyWebServer::MimeType mime_type
      = TinyWebServer::get_mime_type_from_filename(filename);
    web_server.send_error_code(200);
    web_server.send_content_type(mime_type);
    web_server.end_headers();
    if (file.open(&root, filename, O_READ)) {
      Serial << F("Read file "); Serial.println(filename);
      web_server.send_file(file);
      file.close();
    } else {
      web_server << F("Could not find file: ") << filename << "\n";
    }
  }
}

boolean file_handler(TinyWebServer& web_server) {
  char* filename = TinyWebServer::get_file_from_path(web_server.get_path());
  send_file_name(web_server, filename);
  free(filename);
  return true;
}

boolean blink_led_handler(TinyWebServer& web_server) {
  web_server.send_error_code(200);
  web_server.send_content_type("text/plain");
  web_server.end_headers();
  // Reverse the state of the LED.
  setLedEnabled(!getLedState());
  Client& client = web_server.get_client();
  if (client.available()) {
    char ch = (char)client.read();
    if (ch == '0') {
      setLedEnabled(false);
    } else if (ch == '1') {
      setLedEnabled(true);
    }
  }
  return true;
}

boolean led_status_handler(TinyWebServer& web_server) {
  web_server.send_error_code(200);
  web_server.send_content_type("text/plain");
  web_server.end_headers();
  Client& client = web_server.get_client();
  client.println(getLedState(), DEC);
  return true;
}

boolean index_handler(TinyWebServer& web_server) {
  send_file_name(web_server, "INDEX.HTM");
  return true;
}

void file_uploader_handler(TinyWebServer& web_server,
			   TinyWebPutHandler::PutAction action,
			   char* buffer, int size) {
  static uint32_t start_time;
  static uint32_t total_size;

  switch (action) {
  case TinyWebPutHandler::START:
    start_time = millis();
    total_size = 0;
    if (!file.isOpen()) {
      // File is not opened, create it. First obtain the desired name
      // from the request path.
      char* fname = web_server.get_file_from_path(web_server.get_path());
      if (fname) {
	Serial << F("Creating ") << fname << "\n";
	file.open(&root, fname, O_CREAT | O_WRITE | O_TRUNC);
	free(fname);
      }
    }
    break;

  case TinyWebPutHandler::WRITE:
    if (file.isOpen()) {
      file.write(buffer, size);
      total_size += size;
    }
    break;

  case TinyWebPutHandler::END:
    file.sync();
    Serial << F("Wrote ") << file.fileSize() << F(" bytes in ")
	   << millis() - start_time << F(" millis (received ")
           << total_size << F(" bytes)\n");
    file.close();
  }
}

void setup() {
  Serial.begin(115200);
  Serial << F("Free RAM: ") << FreeRam() << "\n";

  pinMode(LEDPIN, OUTPUT);
  setLedEnabled(false);

  pinMode(SS_PIN, OUTPUT);	// set the SS pin as an output
                                // (necessary to keep the board as
                                // master and not SPI slave)
  digitalWrite(SS_PIN, HIGH);	// and ensure SS is high

  // Ensure we are in a consistent state after power-up or a reset
  // button These pins are standard for the Arduino w5100 Rev 3
  // ethernet board They may need to be re-jigged for different boards
  pinMode(ETHER_CS, OUTPUT); 	// Set the CS pin as an output
  digitalWrite(ETHER_CS, HIGH); // Turn off the W5100 chip! (wait for
                                // configuration)
  pinMode(SD_CS, OUTPUT);       // Set the SDcard CS pin as an output
  digitalWrite(SD_CS, HIGH); 	// Turn off the SD card! (wait for
                                // configuration)

  // initialize the SD card.
  Serial << F("Setting up SD card...\n");
  // Pass over the speed and Chip select for the SD card
  if (!card.init(SPI_FULL_SPEED, SD_CS)) {
    Serial << F("card failed\n");
    has_filesystem = false;
  }
  // initialize a FAT volume.
  if (!volume.init(&card)) {
    Serial << F("vol.init failed!\n");
    has_filesystem = false;
  }
  if (!root.openRoot(&volume)) {
    Serial << F("openRoot failed");
    has_filesystem = false;
  }

  if (has_filesystem) {
    // Assign our function to `upload_handler_fn'.
    TinyWebPutHandler::put_handler_fn = file_uploader_handler;
  }

  // Initialize the Ethernet.
  Serial << F("Setting up the Ethernet card...\n");
  Ethernet.begin(mac, ip);

  // Start the web server.
  Serial << F("Web server starting...\n");
  web.begin();

  Serial << F("Ready to accept HTTP requests.\n");
}

void loop() {
  if (has_filesystem) {
    web.process();
  }
}

Which Arduino are you using? Using the SD class eats up more than 1/4 of the memory on a 328-based Arduino. The Ethernet classes need memory. The TinyWebServer class needs memory. The Flash class needs memory, too, but is NOT necessary.

I'm using an Arduino Uno R3. The original project http://www.webweavertech.com/ovidiu/weblog/archives/000484.html appears to use the same hardware.
FreeRam() is returning 554 at runtime.

FreeRam() is returning 554 at runtime.

Before or after opening a file?

Before

bigstoo:
Before

Since opening a file needs a 512 byte buffer, it doesn't seem likely that 554 bytes free at some undefined point in the code is going to be sufficient.

Thank you so much for your help. I guess between when that sketch was originally developed and now, one or more of the libraries (and their memory use) must have increased in size.

I've now abandoned using that sketch, and am now trying a sketch found at Arduino Webserver Input and Output. Unfortunately, that isn't going smoothly either.

When I try to connect to my Arduino via HTTP, all I get is the infamous ÿ returned over and over.
It occurs at this point in the code

webFile = SD.open("index.htm");        // open web page file
                        if (webFile) {
                            while(webFile.available()) {
                                client.write(webFile.read()); // send web page to client
                            }
                            webFile.close();
                        }

I have replaced client.write(webFile.read()); with Serial.write(webFile.read()); and it is putting the correct file contents to the terminal but the very first character is ÿ.

I can think of some quick-and-dirty ways to check if webFile.read() is returning ÿ, but is there a "proper" way of dealing with this problem?

Each client.write() sends a packet. Sending one character per packet, when the packet can hold a lot more than that, is wasteful.

webFile.read() can read multiple values from a file, and store them in an array (of specified size). It also returns the number of bytes read.

client.write() has an overload that takes an array and a number of values in the array.

A little re-write will speed things up immensely.

Next, the write() methods are for binary data. Are you really reading binary data from the SD card? Are you really sending binary data to the client? If not, use the print() method.

but the very first character is ÿ.

There must be something in the file (or something outside the snippet that you posted) that causes that.

Before worrying about it, though, try using an array in the read() and print() calls. The ÿ might be resolved without you needing to do anything.

webFile.read() can read multiple values from a file, and store them in an array (of specified size). It also returns the number of bytes read.

How do I do that? From the documentation SD - Arduino Reference, it suggests I can only read one byte at a time.

Next, the write() methods are for binary data. Are you really reading binary data from the SD card? Are you really sending binary data to the client? If not, use the print() method.

Yes, I believe so. I've tried using Serial.print() and that gave me a string of integers, so I guess it's binary data

There must be something in the file (or something outside the snippet that you posted) that causes that.

Evidently, ÿ is equivalent to integer -1, or 0xFF. That's what would be returned by webFile.read() if there was nothing to read, according to the above documentation. Thing is, the line before, webFile.available() is suggesting there's plenty to read (1053 bytes according to my debugging).

How do I do that? From the documentation SD - Arduino Reference, it suggests I can only read one byte at a time.

But, looking at the header file:

  virtual int read();
  virtual int peek();
  virtual int available();
  virtual void flush();
  int read(void *buf, uint16_t nbyte);

So:

byte stuff[32];
byte count = 32;

int actual = webFile.read(stuff, count);
client.write(stuff, actual);

Yes, I believe so. I've tried using Serial.print() and that gave me a string of integers, so I guess it's binary data

I doubt it, with a name like index.htm. But, webFile.read() returns an int, so it can return -1 when there is nothing to read. So, Serial.print() is being called with an int, which causes conversion to a string.

char c = webFile.read();
Serial.print(c);

Will likely show different results.

Evidently, ÿ is equivalent to integer -1, or 0xFF. That's what would be returned by webFile.read() if there was nothing to read, according to the above documentation. Thing is, the line before, webFile.available() is suggesting there's plenty to read (1053 bytes according to my debugging).

There may be something outside the snippet you posted that is having an influence. But, you can see from my last snippet that I rarely call one function with the output of another. It's too hard to debug, and too easy to mix types inadvertently. I'd suggest that you not do it, either.

And, of course, my way could become:

int c = webFile.read();
if(c != -1)
   Serial.print((char)c);

To deal with the strange -1, if it continues to happen.

I'm coming dangerously close to the limit of my ability :stuck_out_tongue:

I had a go using

byte stuff[32];
byte count = 32;

int actual = webFile.read(stuff, count);
client.write(stuff, actual);

in my code, but it just outputted gobbledegook. I could see some of the file contents in there, but there was plenty of nonsense too. It also seemed to make the Arduino keep resetting. (out of memory, perhaps?)

I've just managed to get it working using

c = webFile.read();
if (c != -1) client.write(c);

You are quite right. It isn't binary data, I am as you say, just mixing up types. Is there any advantage of using client.write(c); over client.print((char)c); or vice-versa?

As I say, I've now got it running, so thank you very much for your help. I fear the little Uno may be running dangerously low on memory though because I can only seem to load a page once. If I try to open a second window it just hang. The whole program, at the moment does a lot more than I need it to so I may have some joy in hacking out the bits that aren't needed.

As I say, I've now got it running, so thank you very much for your help. I fear the little Uno may be running dangerously low on memory though because I can only seem to load a page once. If I try to open a second window it just hang. The whole program, at the moment does a lot more than I need it to so I may have some joy in hacking out the bits that aren't needed.

You could be running out of memory. The SD class uses more than 1/4 of the memory on a Uno. The Ethernet classes, the TinyWebServer class, the HardwareSerial class, etc. all need some memory. Have a peak at: Arduino Playground - HomePage

Posting ALL of your code would be good. There may be things you could do more efficiently, memory-wise.

I'm only using the SPI, SD and Ethernet libraries now so I was hoping that might help.

I'm in the middle of rehashing it now, taking out any of the stuff the original author put in that I don't need. I'll post it on here when I'm done.

Strangely, though, the author has noted in the comments that it was designed to run on an Arduino Uno. I wonder of some of the libraries might have got bigger in the last year or since it was written.

I've got it working... kind of. The only problem I have left to sort out, is that it will only serve up one web page.
After a reset, the first browser can connect fine and do it's thing. However, if you close the browser or try to access it from any other devices, nothing loads.

Anyway, here's the code.

/*--------------------------------------------------------------
  Program:      websrv_relay

  Description:  Arduino web server that displays the state of a relay
                and allows control of it.
                The web page is stored on the micro SD card.
  
  Hardware:     Arduino Uno and official Arduino Ethernet
                shield. Should work with other Arduinos and
                compatible Ethernet shields.
                2Gb micro SD card formatted FAT16.
                Relay connected to Pin ?.
                
  Software:     Developed using Arduino 1.0.5-r2 software
                Should be compatible with Arduino 1.0 +
                SD card contains web page called index.htm
  
  References:   - WebServer example by David A. Mellis and 
                  modified by Tom Igoe
                - SD card examples by David A. Mellis and
                  Tom Igoe
                - Ethernet library documentation:
                  http://arduino.cc/en/Reference/Ethernet
                - SD Card library documentation:
                  http://arduino.cc/en/Reference/SD
                - Original author
                  W.A. Smith, http://startingelectronics.com  

  Date:         29 Jan 2014
 
  Author:       Stuart Mayor
--------------------------------------------------------------*/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ   30  // This was set to 60. I have halved it to see if it has any
                         // effect on memory use

// MAC address from Ethernet shield sticker under board
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 160); // IP address, may need to change depending on network
EthernetServer server(80);  // create a server at port 80
File webFile;               // the web page file on the SD card
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer
boolean Relay_state = 0; // stores the states of the LEDs
int c;

void setup()
{
    // disable Ethernet chip
    pinMode(10, OUTPUT);
    digitalWrite(10, HIGH);
    
    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.");
    // check for index.htm file
    if (!SD.exists("index.htm")) {
        Serial.println("ERROR - Can't find index.htm file!");
        return;  // can't find index file
    }
    Serial.println("SUCCESS - Found index.htm file.");
    
    // Relay
    pinMode(7, OUTPUT);
    
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    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
                // limit the size of the stored received HTTP request
                // 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++;
                }
                // last line of client request is blank and ends with \n
                // respond to client only after last line received
                if (c == '\n' && currentLineIsBlank) {
                    // send a standard http response header
                    client.println("HTTP/1.1 200 OK");
                    // remainder of header follows below, depending on if
                    // web page or XML page is requested
                    // Ajax request - send XML file
                    if (StrContains(HTTP_req, "ajax_inputs")) {
                        // send rest of HTTP header
                        client.println("Content-Type: text/xml");
                        client.println("Connection: keep-alive");
                        client.println();
                        SetRelay();
                        // send XML file containing input states
                        XML_response(client);
                    }
                    else {  // web page request
                        // send rest of HTTP header
                        client.println("Content-Type: text/html");
                        client.println("Connection: keep-alive");
                        client.println();
                        // send web page
                        webFile = SD.open("index.htm");        // open web page file
                        if (webFile) {
                            while(webFile.available()) {
                             c = webFile.read();
                             if (c != -1) client.write(c);
                         //       client.write(webFile.read()); // send web page to client
                            }
                            webFile.close();
                        }
                    }
                    // display received HTTP request on serial port
                    //Serial.print(HTTP_req);
                    // reset buffer index and all buffer elements to 0
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ);
                    break;
                }
                // 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(1);      // give the web browser time to receive the data
        client.stop(); // close the connection
    } // end if (client)
}

// checks if received HTTP request is switching on/off LEDs
// also saves the state of the LEDs
void SetRelay(void)
{
    // Relay (pin 7)
    if (StrContains(HTTP_req, "Relay=1")) {
        Relay_state = 1;  // save Relay state
        digitalWrite(7, HIGH);
    }
    else if (StrContains(HTTP_req, "Relay=0")) {
        Relay_state = 0;  // save LED state
        digitalWrite(7, LOW);
    }
}

// send the XML file with relay state
void XML_response(EthernetClient cl)
{
    
    cl.print("<?xml version = \"1.0\" ?>");
    // Relay state
    cl.print("<inputs>");
    cl.print("<Relay>");
    if (Relay_state) {
        cl.print("on");
    }
    else {
        cl.print("off");
    }
    cl.print("</Relay>");
    cl.println("</inputs>");
        
}

// 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;
}

Serial.println("Initializing SD card...");
would use less of your precious SRAM as
Serial.println(F("Initializing SD card..."));

Do this everywhere you have string literals.

Further to my last post. It appears to fall over at this point webFile = SD.open("index.htm");        // open web page file.

The first time round this loop it works fine, but the second time round I get a boolean negative for webFile, ie it failed to open the file. I don't see any change in FreeRam() on different times round the loop though.

Well. I have it working now. Not perfect, but working.

I have opted to replace webFile.close(); with webFile.seek(0); and just keep the file open rather then keep opening it every time a new HTTP client connects.
I wonder if the problems are to do with the way SPI is working - with Ethernet somehow conflicting with SD.

Anyway. Happy bunny now. I'll post the final code below, in case anyone else finds it useful.

/*--------------------------------------------------------------
 Program:      websrv_relay
 
 Description:  Arduino web server that displays the state of a relay
               and allows control of it.
               The web page is stored on the micro SD card.
 
 Hardware:     Arduino Uno and official Arduino Ethernet
               shield. Should work with other Arduinos and
               compatible Ethernet shields.
               2Gb micro SD card formatted FAT16.
               Relay connected to Pin ?.
 
 Software:     Developed using Arduino 1.0.5-r2 software
               Should be compatible with Arduino 1.0 +
               SD card contains web page called index.htm
 
 References:   - WebServer example by David A. Mellis and 
                 modified by Tom Igoe
               - SD card examples by David A. Mellis and
                 Tom Igoe
               - Ethernet library documentation:
                 http://arduino.cc/en/Reference/Ethernet
               - SD Card library documentation:
                 http://arduino.cc/en/Reference/SD
               - Original author
                 W.A. Smith, http://startingelectronics.com  
 
 Date:         29 Jan 2014
 
 Author:       Stuart Mayor
 --------------------------------------------------------------*/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ   60

// MAC address from Ethernet shield sticker under board
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 160); // IP address, may need to change depending on network
EthernetServer server(80);  // create a server at port 80
File webFile;               // the web page file on the SD card
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer
boolean Relay_state = 0; // stores the states of the Relay
int html;

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

  Serial.begin(9600);       // for debugging

  // initialize SD card
  Serial.println(F("Initializing SD card..."));
  if (!SD.begin(4)) {
    Serial.println(F("ERROR - SD card initialization failed!"));
    return;    // init failed
  }
  Serial.println(F("SUCCESS - SD card initialized."));
  // check for index.htm file
  if (!SD.exists("index.htm")) {
    Serial.println(F("ERROR - Can't find index.htm file!"));
    return;  // can't find index file
  }
  Serial.println(F("SUCCESS - Found index.htm file."));

  // Relay
  pinMode(7, OUTPUT);

  Ethernet.begin(mac, ip);  // initialize Ethernet device
  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
        // limit the size of the stored received HTTP request
        // 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++;
        }
        // last line of client request is blank and ends with \n
        // respond to client only after last line received
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println(F("HTTP/1.1 200 OK"));
          // remainder of header follows below, depending on if
          // web page or XML page is requested
          // Ajax request - send XML file
          if (StrContains(HTTP_req, "ajax_inputs")) {
            // send rest of HTTP header
            client.println(F("Content-Type: text/xml"));
            client.println(F("Connection: keep-alive"));
            client.println();
            SetRelay();
            // send XML file containing input states
            XML_response(client);
          }
          else {  // web page request
            // send rest of HTTP header
            client.println(F("Content-Type: text/html"));
            client.println(F("Connection: keep-alive"));
            client.println();
            // send web page
            if (!webFile) {
              Serial.println(F("Attempting to open index.htm"));
              webFile = SD.open("index.htm");        // open web page file
            }  
            if (webFile) {
              while(webFile.available()) {
                html = webFile.read();
                if (html != -1) client.write(html); // send web page to client
              }
              webFile.seek(0); // Try keeping the file in memory instead
            } 
          }

          // display received HTTP request on serial port
          Serial.print(F("Free RAM:")); 
          Serial.println(FreeRam());
          Serial.print(F("HTTP Req:")); 
          Serial.println(HTTP_req);
          // reset buffer index and all buffer elements to 0
          req_index = 0;
          StrClear(HTTP_req, REQ_BUF_SZ);
          break;
        }
        // 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(1);      // give the web browser time to receive the data
    client.stop(); // close the connection
  } // end if (client)
}

// checks if received HTTP request is switching on/off LEDs
// also saves the state of the LEDs
void SetRelay(void)
{
  // Relay (pin 7)
  if (StrContains(HTTP_req, "Relay=1")) {
    Relay_state = 1;  // save Relay state
    digitalWrite(7, HIGH);
  }
  else if (StrContains(HTTP_req, "Relay=0")) {
    Relay_state = 0;  // save LED state
    digitalWrite(7, LOW);
  }
}

// send the XML file with relay state
void XML_response(EthernetClient cl)
{

  cl.print("<?xml version = \"1.0\" ?>");
  // Relay state
  cl.print("<inputs>");
  cl.print("<Relay>");
  if (Relay_state) {
    cl.print("on");
  }
  else {
    cl.print("off");
  }
  cl.print("</Relay>");
  cl.println("</inputs>");

}

// 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;
}

I suggest you to use Mirabedini/TinyWebServer on github. In this version I try to process and handle POST and GET method parameters that is sent to server by browser. So you can response to user base on the parameter's value that sent from user's browser. It is better to view and test PostGetSD example of this package;