monitor pin plus web server at the same time possible

Hi,

I'm an Arduino novice and have a question:

I am planning to use an R3 Arduino Uno plus Ethernet shield for monitoring a digital pin, where each pulse changes the monitored device from ON to OFF and back. The current state is planned to be made available by making the Arduino a web server, which publishes a very simple HTML page showing the current state.

Is this possible, or does it require parallel processing, which the Arduino is not supporting?

My concern is that while the web server is serving the simple HTML page (taking a few seconds?), the digital pin is not monitored, and should a pulse occur on the digital pin during serving the HTML page, the state change would not be detected.

Possible to do or not?

Or are there better approaches not requiring parallel processing?

Thank you for sharing your expertise.

Is this possible,

Easy, even.

My concern is that while the web server is serving the simple HTML page (taking a few seconds?), the digital pin is not monitored

That's what interrupts are for (if what you are doing is READING the state of the pin).

Possible to do or not?

Possible.

Thank you PaulS, good to know it is possible.

PaulS:
That’s what interrupts are for (if what you are doing is READING the state of the pin).

To be precise, this is what is required:

  • when there is a pulse on a digital input pin, a pulse is written on a digital output pin AND the HTML page is updated with the device state ON or OFF
  • when there is an incoming HTML request to switch the device, a pulse is written on the digital output pin AND the HTML page is updated with the device state ON or OFF
  • in parallel, all client requests to view the HTML page need to be served and show the current device state.

In short, the Arduino allows switching a device ON and OFF using:

  • a touch panel (manual)
  • remote clients via HTTP (automated)

Am I on the right path when looking at the example at

and expand it by using Interrupt?

Obviously, I have to read up on all of that first.

Some client/server test code that might be of use.

//zoomkat 10-02-14, combined client and server
//simple button GET with iframe code
//for use with IDE 1.0
//open serial monitor and send a g to test client GET and
//see what the arduino client/server receives
//web page buttons make pins high/low
//use the ' in html instead of " to prevent having to escape the "
//address will look like http://192.168.1.102:84 when submited
//for use with W5100 based ethernet shields

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

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //assign arduino mac address
byte ip[] = {192, 168, 1, 102 }; // ip in lan assigned to arduino
byte gateway[] = {192, 168, 1, 1 }; // internet access via router
byte subnet[] = {255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port arduino server will use
EthernetClient client;
char serverName[] = "checkip.dyndns.com"; // (DNS) dyndns web page server
//byte serverName[] = { 208, 104, 2, 86 }; // (IP) zoomkat web page server IP address

String readString; //used by server to capture GET request 

//////////////////////

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

  //pinMode(5, OUTPUT); //pin 5 selected to control
  Ethernet.begin(mac,ip,gateway,gateway,subnet); 
  server.begin();
  Serial.begin(9600); 
  Serial.println(F("server/client 1.0 test 9/02/14")); // keep track of what is loaded
  Serial.println(F("Send a g in serial monitor to test client")); // what to do to test client
}

void loop(){
  // check for serial input
  if (Serial.available() > 0) 
  {
    byte inChar;
    inChar = Serial.read();
    if(inChar == 'g')
    {
      sendGET(); // call client sendGET function
    }
  }  

  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.print(readString); //print to serial monitor for debuging 

            //now output HTML data header
          if(readString.indexOf('?') >=0) { //don't send new page
            client.println(F("HTTP/1.1 204 Zoomkat"));
            client.println();
            client.println();  
          }
          else {   
            client.println(F("HTTP/1.1 200 OK")); //send new page on browser request
            client.println(F("Content-Type: text/html"));
            client.println();

            client.println(F("<HTML>"));
            client.println(F("<HEAD>"));
            client.println(F("<TITLE>Arduino GET test page</TITLE>"));
            client.println(F("</HEAD>"));
            client.println(F("<BODY>"));

            client.println(F("<H1>Zoomkat's simple Arduino 1.0 button</H1>"));

            // DIY buttons
            client.println(F("Pin5"));
            client.println(F("<a href=/?on2 target=inlineframe>ON</a>")); 
            client.println(F("<a href=/?off3 target=inlineframe>OFF</a>

")); 

            client.println(F("Pin6"));
            client.println(F("<a href=/?on4 target=inlineframe>ON</a>")); 
            client.println(F("<a href=/?off5 target=inlineframe>OFF</a>

")); 

            client.println(F("Pin7"));
            client.println(F("<a href=/?on6 target=inlineframe>ON</a>")); 
            client.println(F("<a href=/?off7 target=inlineframe>OFF</a>

")); 

            client.println(F("Pin8"));
            client.println(F("<a href=/?on8 target=inlineframe>ON</a>")); 
            client.println(F("<a href=/?off9 target=inlineframe>OFF</a>

")); 

            client.println(F("Pins"));
            client.println(F("&nbsp;<a href=/?off2468 target=inlineframe>ALL ON</a>")); 
            client.println(F("&nbsp;<a href=/?off3579 target=inlineframe>ALL OFF</a>")); 

            client.println(F("<IFRAME name=inlineframe style='display:none'>"));          
            client.println(F("</IFRAME>"));

            client.println(F("</BODY>"));
            client.println(F("</HTML>"));
          }

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

          ///////////////////// control arduino pin
          if(readString.indexOf('2') >0)//checks for 2
          {
            digitalWrite(5, HIGH);    // set pin 5 high
            Serial.println(F("Led 5 On"));
            Serial.println();
          }
          if(readString.indexOf('3') >0)//checks for 3
          {
            digitalWrite(5, LOW);    // set pin 5 low
            Serial.println(F("Led 5 Off"));
            Serial.println();
          }
          if(readString.indexOf('4') >0)//checks for 4
          {
            digitalWrite(6, HIGH);    // set pin 6 high
            Serial.println(F("Led 6 On"));
            Serial.println();
          }
          if(readString.indexOf('5') >0)//checks for 5
          {
            digitalWrite(6, LOW);    // set pin 6 low
            Serial.println(F("Led 6 Off"));
            Serial.println();
          }
          if(readString.indexOf('6') >0)//checks for 6
          {
            digitalWrite(7, HIGH);    // set pin 7 high
            Serial.println(F("Led 7 On"));
            Serial.println();
          }
          if(readString.indexOf('7') >0)//checks for 7
          {
            digitalWrite(7, LOW);    // set pin 7 low
            Serial.println(F("Led 7 Off"));
            Serial.println();
          }     
          if(readString.indexOf('8') >0)//checks for 8
          {
            digitalWrite(8, HIGH);    // set pin 8 high
            Serial.println(F("Led 8 On"));
            Serial.println();
          }
          if(readString.indexOf('9') >0)//checks for 9
          {
            digitalWrite(8, LOW);    // set pin 8 low
            Serial.println(F("Led 8 Off"));
            Serial.println();
          }         

          //clearing string for next read
          readString="";

        }
      }
    }
  }
} 

//////////////////////////
void sendGET() //client function to send and receive GET data from external server.
{
  if (client.connect(serverName, 80)) {
    Serial.println(F("connected"));
    client.println(F("GET / HTTP/1.1"));
    client.println(F("Host: checkip.dyndns.com"));
    client.println(F("Connection: close"));
    client.println();
  } 
  else {
    Serial.println(F("connection failed"));
    Serial.println();
  }

  while(client.connected() && !client.available()) delay(1); //waits for data
  while (client.connected() || client.available()) { //connected or data available
    char c = client.read(); //gets byte from ethernet buffer
    readString += c; //places captured byte in readString
  }

  //Serial.println();
  client.stop(); //stop client
  Serial.println(F("client disconnected."));
  Serial.println(F("Data from server captured in readString:"));
  Serial.println();
  Serial.print(readString); //prints readString to serial monitor 
  Serial.println();
  Serial.println(F("End of readString"));
  Serial.println(F("=================="));
  Serial.println();
  readString=""; //clear readString variable

}

Thank you zoomkat.

a) Your code combines a client and a server on the Arduino, right? I thought I only need the Arduino to be a server, because it does not initiate any requests over Ethernet. It only receives requests, and responds to them. Or am I overlooking something?

b) Your code does not utilise any interrupts, correct?

a) Your code combines a client and a server on the Arduino, right? I thought I only need the Arduino to be a server, because it does not initiate any requests over Ethernet. It only receives requests, and responds to them. Or am I overlooking something?

The code includes both client and server functions. If you do not need to initiate an external connection from the arduino, then you do not need the client code.

b) Your code does not utilise any interrupts, correct?

Not directly, but that is not to say interrupts are not used somewhere in the libraries being used. Be aware that your web page may only show the correct state when it is refreshed. Within seconds another client could change the status again.

To be precise, this is what is required: - when there is a pulse on a digital input pin, a pulse is written on a digital output pin AND the HTML page is updated with the device state ON or OFF

Not possible. Any number (including 0) of clients may have requested that the server send some data. The server has a poor memory. Once it gets done sending the data to a client, it forgets that the client has ever existed. Even if it didn't, it is NOT possible for a server to push data to a client. The client MUST pull the data.

It is possible, using meta tags, to have the server's data tell the client that it needs to periodically pull the data again.

Thank you Zoomkat, very helpful points I need to consider.

PaulS: Not possible. Any number (including 0) of clients may have requested that the server send some data. The server has a poor memory. Once it gets done sending the data to a client, it forgets that the client has ever existed. Even if it didn't, it is NOT possible for a server to push data to a client. The client MUST pull the data.

It is possible, using meta tags, to have the server's data tell the client that it needs to periodically pull the data again.

Thank you PaulS, important points I wasn't aware of.

Luckily I am in control of how the clients access the Arduino. I can make sure that the client software always pulls the data (again) before making any switching requests back to the Arduino, whilst the Arduino can take care of switching conflict resolution. For example, Client A requests to switch the device ON (which it only does when it believes it is OFF), but for whatever action(s) took place in the meantime, the Arduino detects that the device is already ON and can respond to the client that a conflict has been detected, and that the device is already in the requested ON state.

Thanks for making me think about this. I now believe the Uno and Ethernet Shield are a good fit for my requirements.

Thank you all for your great help to evaluate Arduino capabilities for my above requirements.

I’ve now drafted my very first sketch. It is not yet tested (waiting for hardware), but it may still be useful for others.

Comments on the code are most welcome.

/*
  Web Server

 A simple web server using an Arduino Ethernet shield that:
 - returns the current state of the spa pool (ON or OFF) to clients using html
   Example: 192.168.1.251/requestState
 - accepts instruction from clients to turn the spa on by writing a pulse
   Example: 192.168.1.251/switchOn
 - accepts instruction from clients to turn the spa off by writing a pulse
   Example: 192.168.1.251/switchOff
 - interrupts when a RISING pulse is detected on pinIN, which is propagated to pinOut with a RISING pulse
 - keeps track of each switching to memorise the current state of the spa (starting with OFF after power up)

The clients are connecting using automated software. I.e no regular direct user interaction is expected.
 

 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 * Digital input attached to pins 2
 * Digital output attached to pin 3

 created 18 Dec 2009
   by David A. Mellis
 with input from PaulS, Zoomkat and other online sketches
 modified 17 Sept 2015
   by jpt60 

 */

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

// Enter a MAC address and IP address for your controller below.
// The MAC address is on a sticker on the Ethernet Shield
// The IP address is dependent on the local network and is static 192.168.1.251
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 251);

// 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 pinIN = 2;
int pinOUT = 3;
volatile int spaState = 0;

String HTTP_req;          // stores the HTTP GET request


void setup() {
  // Initiate pins
  pinMode(pinIN,INPUT);
  pinMode(pinOUT, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(pinIN),SwitchSPA,RISING);
  
  // Open serial communications and wait for port to open:
  Serial.begin(9600);

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


void loop() {
  // 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();  // read 1 byte (character) from client
        Serial.write(c);  // write each byte (character) to serial
        HTTP_req += c;  // append c to the HTTP request object
        //decypher the http request, which must begin with one of the following:
        // "GET /requestState HTTP/" for URL "192.168.1.251/requestState"
        // "GET /switchOn HTTP/" for URL "192.168.1.251/switchOn"
        // "GET /switchOff HTTP/" for URL "192.168.1.251/switchOff"

        
        // 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 the appropriate reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header

          
          
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println();

          if (HTTP_req.indexOf("/requestState HTTP/") > -1) {              // requestState
            //send spaState web page
            client.println("<!DOCTYPE HTML>");
            client.println("<html>");
            //output the value of the spaState (1 for On or 0 for Off)
            client.print("State=");
            client.println(spaState);
            client.println("</html>");
            break;
            }
            else if (HTTP_req.indexOf("/switchOn HTTP/") > -1) {              // switchOn  
                    //switch the spa On
                    if (spaState = 0) {
                      SwitchSPA();
              
                      //send switchedOn html
                      client.println("<!DOCTYPE HTML>");
                      client.println("<html>");
                      //output the action (On)
                      client.println("Action=switchedOn");
                      client.println("</html>");
                      break;
                    }
                    else {          //conflict: spa is already on
                      //send conflict html
                      client.println("<!DOCTYPE HTML>");
                      client.println("<html>");
                      //output the conflict
                      client.println("Conflict=alreadyOn");
                      client.println("</html>");
                      break;                          
                    }
                 }  
                 else if (HTTP_req.indexOf("/switchOff HTTP/") > -1) {              // switchOff
                        //switch the spa Off
                        if (spaState = 1) {
                          SwitchSPA();
                          
                          //send switchedOn html
                          client.println("<!DOCTYPE HTML>");
                          client.println("<html>");
                          //output the action (Off)
                          client.println("Action=switchedOff");
                          client.println("</html>");
                          break;
                        }
                        else {          //conflict: spa is already off
                          //send conflict html
                          client.println("<!DOCTYPE HTML>");
                          client.println("<html>");
                          //output the conflict
                          client.println("Conflict=alreadyOff");
                          client.println("</html>");
                          break;                                  
                        }
                      }  
  
                      else {                                                      // Invalid HTTP Request
                        //send 404 Not Found response for all other HTTP requests
                        client.println("HTTP/1.1 404 Not Found");
                        client.println("Content-Type: text/html");
                        client.println("Connection: close");  // the connection will be closed after completion of the response
                        client.println();
                        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;
               }
      }
    }

    HTTP_req = "";    // finished with request, empty string
    
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
    Ethernet.maintain();
  }
}


void SwitchSPA() {
     PulseSPA();   // write a pulse on pinOUT

     //memorise the current state of the spa
     //  0 when powering up
     //  each pulse changes the state to the other state, from 0 to 1 and vice versa
     if (spaState == 0)
     {
        // set the spaState to 1 (On) if it is 0 (Off)
        spaState = 1;
     }
     else
     {
        // set the spaState to 0 (Off) if it is 1 (On)
        spaState = 0;
     }
}



void PulseSPA() {
  //sends a 1/2 second pulse on pinOut to switch the SPA to the opposite state
  digitalWrite(pinOUT, HIGH);   // sets the pinOUT to HIGH
  delay(500);                  // waits for half a second
  digitalWrite(pinOUT, LOW);    // sets the pinOUT to LOW
}
     if (spaState == 0)
     {
        // set the spaState to 1 (On) if it is 0 (Off)
        spaState = 1;
     }
     else
     {
        // set the spaState to 0 (Off) if it is 1 (On)
        spaState = 0;
     }

10 lines of code (counting the comments) or

   spaState = !spaState;

1 line of code...

SwichSPA() is called when the external interrupt happens. That calls PulseSPA() which calls delay(). When SwitchSPA() is called by the interrupt, interrupts are disabled. delay() relies on interrupts.

You NEVER use delay() in an interrupt service routine.

What is providing the external interrupt? A human controlled switch does not need interrupts to be read.

Thank you PaulS, very helpful.

PaulS:   spaState = !spaState;

1 line of code...

Great, I didn't realise the !spaState also works for int variables. Much better!

PaulS: SwichSPA() is called when the external interrupt happens. That calls PulseSPA() which calls delay(). When SwitchSPA() is called by the interrupt, interrupts are disabled. delay() relies on interrupts.

You NEVER use delay() in an interrupt service routine.

Understood, must find a way without using delay().

PaulS: What is providing the external interrupt?

The external interrupt comes from the spa pools control panel. Every time the button it pressed, a pulse comes in on pinIn.

PaulS: A human controlled switch does not need interrupts to be read.

How can I ensure the pinIn is read while the Arduino web server might be serving a client? By the time the web server is finished with the client, the pulse is probably finished and missed. I think I am not following fully.

The problem: The Interrupt Service Routine (ISR) must not contain a delay(), but writing a pulse to propagate the signal seems to require a delay().

I believe I found an answer to having an external interrupt intercepting AND PROPAGATING a pulse on digital pins whilst potentially serving web clients: The ISR itself does not propagte the pulse, which is deferred to the loop().

This has however the disadvantage of the web clients’ switching taking priority over the manual switching. I.e. the manual switch won’t be actioned until the web client is fully completed. I assume serving the very small htmls won’t hold up the pulse for long …?

Perhaps there are better solutions to this?

/*
  Web Server

 A simple web server using an Arduino Ethernet shield that:
 - returns the current state of the spa pool (ON or OFF) to clients using html
   Example: 192.168.1.251/requestState
 - accepts instruction from clients to turn the spa on by writing a pulse
   Example: 192.168.1.251/switchOn
 - accepts instruction from clients to turn the spa off by writing a pulse
   Example: 192.168.1.251/switchOff
 - interrupts when a RISING pulse is detected on pinIN, which is propagated to pinOut with a RISING pulse (writing the pulse is deferred)
 - keeps track of each switching to memorise the current state of the spa (starting with OFF after power up)

The clients are connecting using automated software. I.e no regular direct user interaction is expected.


 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 * Digital input attached to pins 2
 * Digital output attached to pin 3

 created 18 Dec 2009
   by David A. Mellis
 with input from PaulS, Zoomkat and other online sketches
 modified 17 Sept 2015
   by jpt60

 */

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

// Enter a MAC address and IP address for your controller below.
// The MAC address is on a sticker on the Ethernet Shield
// The IP address is dependent on the local network and is static 192.168.1.251
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 251);

// 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 pinIN = 2;
int pinOUT = 3;
volatile int spaState = 0;
volatile int manualWantedState = 0;
volatile bool manualInterrupt = false;


String HTTP_req;          // stores the HTTP GET request


// ISR: Interrupt Service Routine
//-------------------------------
void ManualSwitchDetect() {
  manualInterrupt = true;
  manualWantedState = !spaState;   //manually pressing the spa button means a person wants the opposite of the current spa state
}


// SETUP
//-------------------------------
void setup() {
  // Initiate pins
  pinMode(pinIN, INPUT);
  pinMode(pinOUT, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(pinIN), ManualSwitchDetect, RISING);   //attach as early as possible to avoid queing of interrupt before attaching the interrupt

  // Open serial communications and wait for port to open:
  Serial.begin(9600);

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

// LOOP
//-------------------------------
void loop() {
  // listen for incoming clients
  EthernetClient client = server.available();   // if an incoming client connects, there will be bytes available to read:
  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();  // read 1 byte (character) from client
        Serial.write(c);  // write each byte (character) to serial
        HTTP_req += c;  // append the character in c to the HTTP request object
        //decypher the http request, which must begin with one of the following:
        // "GET /requestState HTTP/" for URL "192.168.1.251/requestState"
        // "GET /switchOn HTTP/" for URL "192.168.1.251/switchOn"
        // "GET /switchOff HTTP/" for URL "192.168.1.251/switchOff"


        // 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 the appropriate reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header



          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println();

          if (HTTP_req.indexOf("/requestState HTTP/") > -1) {              // requestState
            //send spaState web page
            client.println("<!DOCTYPE HTML>");
            client.println("<html>");
            //output the value of the spaState (1 for On or 0 for Off)
            client.print("State=");
            client.println(spaState);
            client.println("</html>");
            break;
          }
          else if (HTTP_req.indexOf("/switchOn HTTP/") > -1) {              // switchOn
            //switch the spa On
            if (spaState = 0) {
              SwitchSPA();

              //send switchedOn html
              client.println("<!DOCTYPE HTML>");
              client.println("<html>");
              //output the action (On)
              client.println("Action=switchedOn");
              client.println("</html>");
              break;
            }
            else {          //conflict: spa is already on
              //send conflict html
              client.println("<!DOCTYPE HTML>");
              client.println("<html>");
              //output the conflict
              client.println("Conflict=alreadyOn");
              client.println("</html>");
              break;
            }
          }
          else if (HTTP_req.indexOf("/switchOff HTTP/") > -1) {              // switchOff
            //switch the spa Off
            if (spaState = 1) {
              SwitchSPA();

              //send switchedOn html
              client.println("<!DOCTYPE HTML>");
              client.println("<html>");
              //output the action (Off)
              client.println("Action=switchedOff");
              client.println("</html>");
              break;
            }
            else {          //conflict: spa is already off
              //send conflict html
              client.println("<!DOCTYPE HTML>");
              client.println("<html>");
              //output the conflict
              client.println("Conflict=alreadyOff");
              client.println("</html>");
              break;
            }
          }

          else {                                                      // Invalid HTTP Request
            //send 404 Not Found response for all other HTTP requests
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println("Connection: close");  // the connection will be closed after completion of the response
            client.println();
            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;
        }
      }
    }

    HTTP_req = "";    // finished with request, empty string

    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
    Ethernet.maintain();
  }
  //web client procssing finished here

  //check wether a manual interrupt occurred
  if (manualInterrupt == true) {
    if (manualWantedState != spaState) {
      SwitchSPA();
    }
    else {
      //A web client has already switched the SPA to the very same state as what the manual pressing would have without a competing web client
    }
    manualInterrupt == false;
  }
}

// SwitchSPA
//-------------------------------

void SwitchSPA() {
  PulseSPA();   // write a pulse on pinOUT

  //memorise the current state of the spa
  //  0 when powering up
  //  each pulse toggles the spaState, from 0 to 1 and vice versa
  spaState = !spaState;
}

// PulseSPA
//-------------------------------
void PulseSPA() {
  //sends a 1/2 second pulse on pinOut to switch the SPA to the opposite state
  digitalWrite(pinOUT, HIGH);   // sets the pinOUT to HIGH
  delay(500);                  // waits for half a second
  digitalWrite(pinOUT, LOW);    // sets the pinOUT to LOW
}