Ethernet Relay Control

Hi all,

I’ve been a reading this forum for a little while, I’m now putting together my first Arduino Project and really enjoying the challenge. It uses a W5500 Ethernet Shield to open and close a relay. I plan to connect it to my shower water heater.

Using parts of code found on the form I’ve managed to create a web server that lets me to switch the relay open or closed. I’ve set the page to refresh every 5 seconds, and I’ve also created a circular DIV that turns red or green depending on whether the relay is set high or low. The refresh is so that the on/off indicator updates.

I have two questions for you.

1) When I visit the arduino’s URL the relay turns itself on without me clicking on. It would appear to be something to do with the IF statements in my red/green DIV indicating whether the relay is high or low. As when I comment them out.

If I select OFF on my server page, the relay turns back on once the page refreshes.
Would you have any suggestions on how I could do this better/correctly?

2) I’d love if I could get the page to not refresh, but still change the indicator circle colour when you click the ON or OFF buttons. But not sure how to achieve this.

Thanks for your suggestions.

#include <SPI.h>
#include <Ethernet2.h>


byte mac[] = { 0x90, 0xAD, 0xDA, 0x10, 0xA7, 0x79 };

IPAddress ip(192, 168, 0, 179);
EthernetServer server(84);

String readString; 


void setup()
  {
  pinMode(5, OUTPUT); 
  Ethernet.begin(mac, ip);
  server.begin();
  delay(1000);
  Serial.begin(9600); 
  Serial.println("IP address:"); 
  Serial.println(Ethernet.localIP());
  delay(1000);
  Serial.println("Server Port:");
  Serial.println("84");

  }

void loop(){
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (readString.length() < 100) 
        {
          readString += c; 
        } 
        if (c == '\n') {

          Serial.println(readString);


             if(readString.indexOf('?') >=0) { 
               client.println("HTTP/1.1");
               client.println();
               client.println();  
             }
             else {
          client.println("HTTP/1.1 200 OK"); 
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>Heating</TITLE>");
          client.println("<meta http-equiv=\"refresh\" content=\"5\">");
          client.println("</HEAD>");
          client.println("<BODY style=\"font-family:arial;colour:white;background-color:#E0E0E0;\">");
          client.println("<DIV style=\"height:100vh;text-align:center;\"><div style=\"position:relative;height:inherit;vertical-align:middle;\">");
          client.println("<H1>Heating</H1>");
          client.println("Current State:"); 
          client.println("

");
            if (digitalRead(5) == HIGH) 
                    {client.println("<DIV style=\"position:relative;text-align:center;display:inline-block;background-color:green;color:white!important;height:100px;width:100px;border-radius:50px;\"><div style=\"Height:100px;vertical-align:middle;\">ON</DIV></DIV>");}
            if (digitalRead(5) == LOW) 
                    {client.println("<DIV style=\"position:relative;text-align:center;display:inline-block;background-color:red;color:white!important;height:100px;width:100px;border-radius:50px;vertical-align:middle;\"><div style=\"Height:100px;vertical-align:middle;\">OFF</DIV></DIV>");}
          client.println("

"); 
          client.println("<a href=\"/?on\" target=\"inlineframe\">ON</a>"); 
          client.println("<a href=\"/?off\" target=\"inlineframe\">OFF</a>"); 
          client.println("<IFRAME name=inlineframe style=\"display:none\">");          
          client.println("</IFRAME>");
          client.println("</DIV></DIV>");
          client.println("</BODY>");
          client.println("</HTML>");
             }

          delay(1);
          client.stop();

          if(readString.indexOf("on") >0)
          {
            digitalWrite(5, HIGH);   
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)
          {
            digitalWrite(5, LOW);
            Serial.println("Led Off");
          }
          
          readString="";

        }
      }
    }
  }
}

Thanks for moving it.

  pinMode(5, OUTPUT);

Why don’t you give this pin a name, so we have some clue what is connected to it?

        if (readString.length() < 100)
        {
          readString += c;
        }

Since you have an upper limit on how much data to store, there is NO excuse for pissing away memory using the String class. Appending data to a String is far slower than storing it in the next position in a char array.

            if (digitalRead(5) == HIGH)
                    {client.println("<DIV style=\"position:relative;text-align:center;display:inline-block;background-color:green;color:white!important;height:100px;width:100px;border-radius:50px;\"><div style=\"Height:100px;vertical-align:middle;\">ON</DIV></DIV>");}
            if (digitalRead(5) == LOW)
                    {client.println("<DIV style=\"position:relative;text-align:center;display:inline-block;background-color:red;color:white!important;height:100px;width:100px;border-radius:50px;vertical-align:middle;\"><div style=\"Height:100px;vertical-align:middle;\">OFF</DIV></DIV>");}

Do you plan to add code for the other possible values that digitalRead() might return?

Why do you need to read the state of an output pin? You KNOW (or should) what state you wrote to it.

You are pissing away a LOT of SRAM by not using the F() macro.

Hey Paul,

The pin is connected to a relay. My intention is to use it to turn on and off my water heater for the shower.

PaulS:

        if (readString.length() < 100)

{
          readString += c;
        }



Since you have an upper limit on how much data to store, there is NO excuse for pissing away memory using the String class. Appending data to a String is far slower than storing it in the next position in a char array.

This is the bit of your post that I don’t understand, so it interests me.
How would I alternatively story it in the next position in a char array?

Why do you need to read the state of an output pin? You KNOW (or should) what state you wrote to it.

As it’s an ethernet server it’s accessible by a number of people.
Reading the state of the output pin compiles slightly different html to the client, depending on whether it’s high or low state.

A DIV on the page loads as green if it’s already been turned on, or red if it’s off.

You are pissing away a LOT of SRAM by not using the F() macro.

I just managed to figure this F() out, it’s saving a tone of memory, thanks for the suggestion!

The pin is connected to a relay.

So, it should have a name, like maybe, I don't, relayPin.

How would I alternatively story it in the next position in a char array?

   someCharArray[nextPosition++] = c;
   someCharArray[nextPostion] = '\0';

(Assuming, of course, that you have a char array named someCharArray and an index variable called nextPosition, initialized to 0.)

Reading the state of the output pin compiles slightly different html to the client, depending on whether it's high or low state.

It is not necessary to read the state of pin. The ONLY thing that can change the state of the pin is the Arduino sketch, which CAN keep track of the state of each pin.

I just managed to figure this F() out, it's saving a tone of memory, thanks for the suggestion!

Good. Some progress. Now, about those Strings...

Have managed to get through a couple of these things.

So, it should have a name, like maybe, I don't, relayPin.

I've added in

int Relay1 = 5;

so this is now

pinMode(Relay1, OUTPUT);

Why do you need to read the state of an output pin? You KNOW (or should) what state you wrote to it.

And I've also removed digitalRead(Relay1) == HIGH and changed it to

   if (Relay1 == HIGH)                 // old // (digitalRead(Relay1) == HIGH)
                        {client.println(F("checked>"));}
                      else //(Relay1 == LOW)              // old // (digitalRead(Relay1) == LOW)
                        {client.println(F(">"));}

Which is a much subtler way of amending the HTML than I had initially used.

I'm still trying to figure out how to replace readString and store data in a char array. Don't get it yet, but at least from researching it I understand a hell of a lot more than I used to.

And I've also removed Code: [Select]

digitalRead(Relay1) == HIGH

and changed it to Code: [Select]

if (Relay1 == HIGH) // old // (digitalRead(Relay1) == HIGH) {client.println(F("checked>"));} else //(Relay1 == LOW) // old // (digitalRead(Relay1) == LOW) {client.println(F(">"));}

Which is a much subtler way of amending the HTML than I had initially used.

You assigned Relay1 a value of 5. Under what circumstances will 5 ever equal 1?

THAT is why I suggest that you use Pin in the name of all variables that contain pin numbers. You would not compare relayPin1 to HIGH, would you?

I see what you mean. I had called it Relay 1, with the intention of there being Relay2, etc in the future. But I can see why using pin is a good idea.

denski: I see what you mean. I had called it Relay 1, with the intention of there being Relay2, etc in the future. But I can see why using pin is a good idea.

There is nothing wrong with relayPin1 and relayPin2, with relayState1 and relayState2. If you have more than a few, arrays are better, but for two or three, numeric suffixes are acceptable.

Ok cool, so what I'm looking at for multiple relays (say 4x) is something like.

 char relayArray[4] = {5, 6, 7, 10}

and then to call them I'm using either

for() //for incrementing eg KnightRider tutorial

or

relayArray[0] //to call pin etc5

You missed the point mentioned in PaulS comment #6.

if (Relay1 == HIGH)

As Relay1 is 5, is there any chance of passing this condition?

sarouje: You missed the point mentioned in PaulS comment #6.

if (Relay1 == HIGH)

As Relay1 is 5, is there any chance of passing this condition?

I had been testing this today and and couldn't get it to work, so your comment helps understand why. Upon re-reading my code, I get what you are saying completely.

I’ve been trying to figure out how to add the GET request to a char array, and process it that way, rather than through a string, as suggest. So far I’ve failed, but admittedly, it’s lead to lots of other interesting discoveries.

At the moment, I’ve limited the code to simply print the GET request in serial monitor, once I can get that I think setting a pin to high should be straight forward.

What I’ve managed so far is to get serial to print G, adding GG and then GGG on each HTTP request.

Really interested to get to the bottom of this, so thanks for taking a look.

// Heat Switch v3.2 - by Denski
// 14 October 2016


#include <SPI.h>
#include <Ethernet2.h>

#define REQ_BUF_SZ   90


byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
IPAddress ip(192, 168, 0, 179);
EthernetServer server(84);
//String readString;
int pinRelay1 = 5;
char HTTP_req[REQ_BUF_SZ] = {0}; 
char req_index = 0;              



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

void setup()
{
  pinMode(pinRelay1, OUTPUT); 

  Ethernet.begin(mac, ip);

  server.begin();

  Serial.begin(9600);
  Serial.println(F("Heat Switch V3.2b\n"));
  Serial.print(F("Heating Module IP address: ")); // so I can keep track of what is loaded
  Serial.println(Ethernet.localIP());
  Serial.println(F("Heating Module Server Port: 84")); // so I can keep track of what is loaded
}

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

void loop()
{ EthernetClient client = server.available(); 
  if (client)
  { while (client.connected())
    { if (client.available())
      { char c = client.read(); 

        if (req_index < (REQ_BUF_SZ - 1)) {
                    HTTP_req[req_index] = c;          
                    req_index++;
                } 
                // The above results in G followed by GG followed by GGG each time I send a HTTP request, 
                //I understand this is the result of the ++ but thought I'd leave it in there for now.
        
        if (c == '\n');
          ///////////////////////
        { Serial.println("Printing \"HTTP_req\"");
          Serial.println(HTTP_req); //print to serial monitor for debuging
          //so far I can get it to print G, which I assume it the first letter of the GET request.
          
          if (HTTP_req.indexOf('?') >= 0) //This gives errors, I assume because HTTP_req is not a string
                //error: request for member 'indexOf' in 'HTTP_req', which is of non-class type 'char [90]'
          { client.println(F("HTTP/1.1"));
            client.println();
            client.println();
          }
          else
          { client.println(F("HTTP/1.1 200 OK\n" 
                             "Content-Type: text/html\n\n"
            
                             "<HTML>"
                             "<HEAD>"
                              // Javascript
                             "<script language=\"javascript\" type=\"text/javascript\">"
                             "function CheckSwitch()"
                             "{var heatingstate = document.getElementById(\"Relay1\").checked;"
                             "{if (heatingstate == true)"
                             "{window.open(\"/?on1\", \"inlineframe\");}"
                             "else"
                             "{window.open(\"/?off1\", \"inlineframe\");}"
                             "}"
                             "}"
                             "</script>"
                             "<TITLE>HEATING</TITLE>"
                             "</HEAD>"
                             "<body>"

                             "<h1>HEATING</h1>"
                             "<form method=\"get\">"
                             "<label class=\"switch\">"
                             "<input type=\"checkbox\" id=\"Relay1\" name=\"Relay1\" onclick=\"CheckSwitch()\" "));
            if (digitalRead(pinRelay1) == HIGH)
            {
              client.print(F("checked>"));
            }
            else
            {
              client.print(F(">"));
            }
            client.println(F(
                             "</label>"
                             "</form>"
                             "
"
                             "<IFRAME id=\"inlineframe\" name=\"inlineframe\" style=\"display:none\">"
                             "</IFRAME>"
                             "</BODY>"
                             "</HTML>"));
          }
          delay(1);
          //stopping client
          client.stop();

        }
      }
    }
  }
}

A quick addition - something that occurred to me just there.

I've been looking for a solution that stores my HTTP request "GET /?on1/" as one position in array.

HTTP_ req = "GET /?on1/"

Should I actually be looking at it as storing each letter of the request in a different position of the array?

Should I actually be looking at it as storing each letter of the request in a different position of the array?

Yes.

Ok, rather than ask you for the answer, would you have any recommended reading for me? perhaps a sketch that uses it already, and I can apply it across. I've been looking, but haven't come across anything that does the job yet.

denski: Ok, rather than ask you for the answer, would you have any recommended reading for me? perhaps a sketch that uses it already, and I can apply it across. I've been looking, but haven't come across anything that does the job yet.

Robin2 has a tutorial that deals with reading serial data, and saving it in an array. Reading client data is EXACTLY the same process.

http://forum.arduino.cc/index.php?topic=396450.0

Fantastic, thanks. I'll post the sketch once I amend it for client.read().

Hey,

Working through Robin2's example to understand how it works.

I'm guessing here, but does rc = serial.read(); take the data in one digit at a time, adding it to each new index position of receivedChars[ndx] until it sees the endMarker?

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
    
    while (Serial.available() > 0 && newData == false) 
    {rc = Serial.read();

        if (rc != endMarker) 
        {receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) 
            {ndx = numChars - 1;
            }
        }

I'm guessing here, but does rc = serial.read(); take the data in one digit at a time

One character at a time. The function has no idea that the character is a digit.

The rest of your statement is true.

Your use of curly braces is horrid, though. Not a single recognized style allows for ANYTHING following the {, except when initializing an array. Using a recognized style will save you a lot of grief.

While it is possible to add the NULL terminator when the end marker arrives, I prefer to keep the array NULL terminated at all times.

     receivedChars[ndx] = rc;
     receivedChars[ndx+1] = '\0';