Ethernet PHP control - I want to stay in a loop and check for a new string

Hi Paul.

Thanks for the input. That makes sense, so each time the arduino asks the controller if it has x y z string waiting for it?

so, I want to check for the animation string from the ethernet controller, then run the animation loop until another string arrives - which I have to check for.

I'm using 'if' statements to check for certain strings, and they will only be true once then the string will be gone, so how could I make the 'if' loop run continuously and check for other strings without running the rest of the loop() function running?

I hope that makes sense, maybe I am being daft and over complicating this matter?

The main code is below, currently if you go into the stars() function it appears to run only twice, but I can't see where the flag is being set back to false? - I'm sure there must be a less clunky way of doing this without messing around with flags?

//////////////////////
//INCLUDES
//////////////////////
#include <SPI.h>
#include <Ethernet.h>
#include "Tlc5940.h"
//////////////////////
//END OF INCLUDES
//////////////////////


//////////////////////
//DEFINES
//////////////////////
int TLCDelay = 50;  //Delay after setting TLC
int brightness = 4095;  //Initial brightness to display
int dimStep = 0;
boolean flag = false;  //flag for animation or single shot, false = single shot
int pinNumber = 0;

// number of items in an array
#define NUMITEMS(arg) ((unsigned int) (sizeof (arg) / sizeof (arg [0])))

//int vals [] = { 1000, 1000, 500, 500, 250, 200, 175, 175, 100, 75, 40, 5};
int vals [] = { 
  2047, 1024, 512, 256, 128, 64, 16, 16, 32 };
int numVal = NUMITEMS (vals); //number of values in array


//////////////////////
//END OF DEFINES
//////////////////////


//////////////////////
//ETHERNET SETUP
//////////////////////
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 
  192, 168, 0, 177 }; // ip in lan
byte gateway[] = { 
  192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 0 }; //subnet mask
EthernetServer server(80); //server port
String readString;
//////////////////////
//END OF ETHERNET SETUP
//////////////////////


//////////////////////
//START OF MODES
//////////////////////
void allOn(){
  Tlc.clear();
  digitalWrite(7, HIGH);
  Tlc.setAll(brightness);
  Tlc.update();
  delay(TLCDelay);
}

void sides(){
  Tlc.clear();
  digitalWrite(7, HIGH);
  for (int i = 0; i < 4; i++)
  {
    Tlc.set(i, brightness);
  }
  for (int i = 7; i < 11; i++)
  {
    Tlc.set(i, brightness);
  }
  Tlc.update();
  delay(TLCDelay);
}

void gradient(){
  Tlc.clear();
  for (int i = 0; i < 4; i++)
    Tlc.set(i, 4095);

  for (int i = 4; i < 8; i++)
    Tlc.set(i, 1000);

  for (int i = 8; i < 12; i++)
    Tlc.set(i, 250);

  for (int i = 12; i < 16; i++)
    Tlc.set(i, 100);

  Tlc.update();
  delay(TLCDelay);
}

void frontRow(){
  Tlc.clear();
  Tlc.set(0, brightness);
  Tlc.set(4, brightness);
  Tlc.set(7, brightness);
  Tlc.update();
  delay(TLCDelay);
}

void middle(){
  Tlc.clear();
  Tlc.set(4, brightness);
  Tlc.set(5, brightness);
  Tlc.set(6, brightness);
  Tlc.update();
  delay(TLCDelay);
}

void alternate(){
  Tlc.clear();
  Tlc.set(0, brightness);
  Tlc.set(2, brightness);
  Tlc.set(5, brightness);
  Tlc.set(7, brightness);
  Tlc.set(9, brightness);
  Tlc.update();
  delay(TLCDelay);
}

void stars(){
  Tlc.clear();
  Tlc.set(pinNumber, brightness);
  Tlc.update();
  pinNumber++;
  delay(100);
  delay(TLCDelay);
  flag = true;
}

//////////////////////
//END OF MODES
//////////////////////


//////////////////////
//START OF DIMMING CONTROL
//////////////////////
void up(){

  if ((brightness + vals[dimStep]) < 4095)
  {
    brightness = (brightness + vals[(dimStep - 1)]) ; //set new brightness level
    dimStep--;
  }

  Serial.println(brightness);
  Tlc.setAll(brightness);
  Tlc.update();
  delay(TLCDelay);
}

void down(){
  if (dimStep < numVal)
  {
    brightness = (brightness - vals[dimStep]); //set new brightness level
    dimStep++;
  }

  Serial.println(brightness);
  Tlc.setAll(brightness);
  Tlc.update();
  delay(TLCDelay);
}
//////////////////////
//END OF DIMMING CONTROL
//////////////////////


void setup(){

  Tlc.init(0);

  pinMode(7, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();
  Serial.begin(9600);

}



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

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string
          readString += c;
          //Serial.print(c);
        }

        //if HTTP request has ended
        if (c == '\n') {

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

          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();
          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<meta name='apple-mobile-web-app-capable' content='yes' />");
          client.println("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />");
          client.println("<link rel='stylesheet' type='text/css' href='https://dl.dropbox.com/u/33809233/a.css' />");
          client.println("<TITLE>Home Automation</TITLE>");
          client.println("<meta name=\"viewport\" content=\"user-scalable = yes\" />"); //?
          client.println("<meta name=\"viewport\" content=\"width=device-width, height=device-height, initial-scale=1, maximum-scale=1\" />");
          client.println("</HEAD>");
          client.println("<BODY>");
          client.println("<H1>Home Automation</H1>");
          client.println("<hr />");
          client.println("
");
          client.println("<a href=\"/?lighton\"\">All On</a>");
          client.println("<a href=\"/?lightoff\"\">All Off</a>
");
          client.println("
");
          client.println("<hr />");
          client.println("
");
          client.println("<a href=\"/?up\"\">Up</a>");
          client.println("<a href=\"/?down\"\">Down</a>
");
          client.println("
");
          client.println("<hr />");
          client.println("
");
          client.println("<a href=\"/?sides\"\">Sides</a>");  
          client.println("<a href=\"/?gradient\"\">Gradient</a>
");
          client.println("
");
          client.println("
");
          client.println("<a href=\"/?frontRow\"\">Front Row</a>");
          client.println("<a href=\"/?alternate\"\">Alternate</a>");
          client.println("<a href=\"/?middle\"\">Middle</a>
");
          client.println("
");
          client.println("<hr />");
          client.println("
");
          client.println("<a href=\"/?stars\"\">Stars</a>
");
          client.println("</BODY>");
          client.println("</HTML>");

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

        
          if(readString.indexOf("?lighton") >0)
          {
            allOn();
            flag = false;
          }
          else if(readString.indexOf("?lightoff") >0)
          {
            Tlc.setAll(0);
            Tlc.update();
            delay(TLCDelay);
            flag = false;
          }
          else if(readString.indexOf("?sides") >0)
          {
            sides();
            flag = false;
          }
          else if(readString.indexOf("?gradient") >0)
          {
            gradient();
            flag = false;
          }
          else if(readString.indexOf("?up") >0)
          {
            up();
          }
          else if(readString.indexOf("?down") >0)
          {
            down();
          }
          else if(readString.indexOf("?frontRow") >0)
          {
            frontRow();
            flag = false;
          }
          else if(readString.indexOf("?middle") >0)
          {
            middle();
            flag = false;
          }
          else if(readString.indexOf("?alternate") >0)
          {
            alternate();
            flag = false;
          }
          else if(readString.indexOf("?stars") >0 || flag == true)
          {
            stars();
          }



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

        }
      }
    }
  }
}


/*
            digitalWrite(7, LOW);
 //Tlc.set(1, 0);
 //Tlc.set(15, 0);
 for (int i = 4095; i > (0-1) ; i--)
 {
 Tlc.set(15, i);
 Tlc.set(10, i);
 Tlc.update();
 delay(1);
 }
 Tlc.set(9, 0);
 Tlc.update();
 delay(100);
 
 */

That makes sense, so each time the arduino asks the controller if it has x y z string waiting for it?

The Arduino is a server, when running your code. It doesn't ask anything anything. It responds when asked.

What might be confusing is the nature of the questions asked by the clients. What a client is asking the server is "Can you do something (with this data)?". That isn't really any different from the client asking "What is the temperature?" or "What are all the URLs on the Arduino site containing "PROGMEM"?" The supplied data doesn't change the fact that the client is asking the server a question.

I'm using 'if' statements to check for certain strings, and they will only be true once then the string will be gone

If you mean that the if statement will only resolve to true once per client request, that is probably true. The client data doesn't go away just because you checked whether it contains a particular string, though. So, I'm not entirely sure that I understand the question.

I hope that makes sense, maybe I am being daft and over complicating this matter?

I'm not sure that either assertion is true. Without seeing what the client asks, it's hard to understand what you want to do. From looking at the server page, though, it appears that each request from the client is a separate event, not something that could be used to put together any kind of animation. That is, the client request defines one frame of a movie, not a whole movie.

Hi again Paul

Sorry I was babbling a bit last night and not making much sense!

Basically, this is what my code does currently...
check for string
if string arrived - turn something either on or off
run loop again to check for strings (if statements)

This is what I would like to do...
check for string
if string arrived - turn something on or off, or start animation sequence
continue running animation sequence and run main loop again to check for strings (if statements)

I think I have over complicated the matter, I have an idea. If I put all the if statements to test for strings into a function I could sit in a loop to run the animation and call the function to check for other strings, if any are true it would exit the loop?

Still not sure on the best way of doing it though

continue running animation sequence and run main loop again to check for strings (if statements)

There is still the assumption, incorrect, that the client can send more strings. It can't. It sends all the data needed to satisfy one request at one time. The "animation" you are looking for comes from the same client making multiple requests in the proper order with minimum delay between requests. There is nothing that the Arduino can do to get multiple requests from the same client any faster than the client makes requests.

If I understand you, you wish to have an animation (something like a screensaver) running until a network request comes in. I take it that you "poll" something to see if the request has come in. You then want to process the single request of adjusting some LEDs I think. You then want to go back to the animation. Is that correct?

If so, then you insert your poll of the network controller somewhere inside the animation process like inside a loop or procedure that frequently executes. When you detect something, just handle it from within the animation or float some kind of signal up to the main level loop() to handle it.

afremont:
If I understand you, you wish to have an animation (something like a screensaver) running until a network request comes in. I take it that you "poll" something to see if the request has come in. You then want to process the single request of adjusting some LEDs I think. You then want to go back to the animation. Is that correct?

If so, then you insert your poll of the network controller somewhere inside the animation process like inside a loop or procedure that frequently executes. When you detect something, just handle it from within the animation or float some kind of signal up to the main level loop() to handle it.

This is pretty much what I'm trying to do, sorry for being rubbish at explaining myself :~ lol I will try the approach you mentioned, many thanks :slight_smile:

OK, right I'm trying this idea on my basic working code but having some trouble.

The command enters the flash function, in which I have an infinite while loop. The LED flashes as I would expect and the program stays in the loop, but when I press another button on the browser I get no response from the arduino and get a 404 error.

I'm not sure where I'm going wrong here, I'm calling the checkString function on each loop?

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

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 
  192, 168, 0, 177 }; // ip in lan
byte gateway[] = { 
  192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 0 }; //subnet mask
EthernetServer server(80); //server port

String readString;

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

void checkString(){
  if(readString.indexOf("?lighton") >0)//checks for on
  {
    digitalWrite(7, HIGH);    // set pin 4 high
  }
  else if(readString.indexOf("?lightoff") >0)//checks for off
  {
    digitalWrite(7, LOW);    // set pin 4 low
  }
  else if(readString.indexOf("?flash") >0)
  {
    Serial.println("test");
    flash();
  }
  //clearing string for next read
  readString="";
}


void flash(){
  int i = 0;
  while (i == 0){
    digitalWrite(7, LOW);
    delay(100);
    digitalWrite(7, HIGH);
    delay(100);
    checkString();  
  }
}



void setup(){

  pinMode(7, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();
  Serial.begin(9600);

}

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

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string
          readString += c;
          //Serial.print(c);
        }

        //if HTTP request has ended
        if (c == '\n') {

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

          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<meta name='apple-mobile-web-app-capable' content='yes' />");
          client.println("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />");
          client.println("<link rel='stylesheet' type='text/css' href='http://homeautocss.net84.net/a.css' />");
          client.println("<TITLE>Home Automation</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");
          client.println("<H1>Home Automation</H1>");
          client.println("<hr />");
          client.println("
");
          client.println("<a href=\"/?flash\"\">Flash</a>");
          client.println("<a href=\"/?lighton\"\">Turn On Light</a>");
          client.println("<a href=\"/?lightoff\"\">Turn Off Light</a>
");        

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

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

        }
      }
    }
  }
}

Any reason you are using 192.168.0.177 instead of 192.168.1.177 for the arduino IP address?

byte ip[] = { 
  192, 168, 0, 177 }; // ip in lan
byte gateway[] = { 
  192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 0 }; //subnet mask

You are calling checkString() from inside flash(), but then that recursively calls flash() again. I don't think that's what you want. Are the other two functions working as you expect?

I don't want to be a jerk, but I think the problem here is the design. In my opinion, you need to get the arduino to do the flashing for you as a background operation, that way you just turn it on or off same as the other functions. Don't let that discourage you, because we can probably get it to work without doing that yet by reversing your approach.

Perhaps we could look at the problem from the standpoint that you spend most time flashing the LED and then checking for a client connection each loop. It's not the best way to do it, but it should work. You can't really call checkString() unless you have a new string to look at and that only happens when you get a new client connection. I also notice that you spit back the HTML form before processing the request, this precludes you from sending some feedback to the user about what happened should you later want to.

Save your existing sketch somewhere new, because we are about to perform major surgery on its structure. It's time to simplify your loop() by calling functions instead of having all that code in it. Here is some pseudo code for what I'm talking about:

loop()
  if(flashing)
     turn on LED
     delay(100)
     turn off LED
     delay(100)
   endif
   checkForClient()
end loop()

checkForClient()
  if(client)
     read request
     handle request
  endif
end checkForClient()

It's not the most responsive way to do it, but it should get the job done. Client processing will be delayed up to 200mS while the LED is toggled. Does this make any sense?

Hi afremont, thanks for the advice!

I will have a go at re-structuring the code now and let you know how I get on :slight_smile:

Just trying this out at the moment, does not work though. The LED starts flashing but again after that the arduino does not respond?

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

boolean flashing = false;

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 
  192, 168, 0, 177 }; // ip in lan
byte gateway[] = { 
  192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 0 }; //subnet mask
EthernetServer server(80); //server port

String readString;


void checkFor(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string
          readString += c;
          //Serial.print(c);
        }

        //if HTTP request has ended
        if (c == '\n') {

          /////////////// 
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println(); 
          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<meta name='apple-mobile-web-app-capable' content='yes' />");
          client.println("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />");
          client.println("<link rel='stylesheet' type='text/css' href='http://homeautocss.net84.net/a.css' />");
          client.println("<TITLE>Home Automation</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");
          client.println("<H1>Home Automation</H1>");
          client.println("<hr />");
          client.println("
");  
          client.println("<a href=\"/?flashing\"\">Flashing</a>");      
          client.println("<a href=\"/?lighton\"\">Turn On Light</a>");

          client.println("<a href=\"/?lightoff\"\">Turn Off Light</a>
");         
          client.println("</BODY>");
          client.println("</HTML>");

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

          ///////////////////// control arduino pin
          if(readString.indexOf("?lighton") >0)//checks for on
          {
            digitalWrite(7, HIGH);    // set pin 4 high
          }
          else if(readString.indexOf("?lightoff") >0)//checks for off
          {
            digitalWrite(7, LOW);    // set pin 4 low
          }
          else if(readString.indexOf("?flashing") >0)
          {
            flashing = true;
          }


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


}


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

void setup(){

  pinMode(7, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();

}

void loop(){

  if (flashing == true) 
  {
    digitalWrite(7, LOW);
    delay(100);
    digitalWrite(7, HIGH);
    delay(100);
    checkFor();
  }
  checkFor();

}

ok, this seems to work finally.

But it seems clunky having to set 'flash = false' on the other if statements, especially when I end up with more animation loops and commands to turn outputs on and off.

Is there a better way to do it?

//simple button GET server code to control servo and arduino pin 5
//for use with IDE 1.0
//open serial monitor to see what the arduino receives
//use the \ slash to escape the " in the html
//for use with W5100 based ethernet shields
//Powering a servo from the arduino usually DOES NOT WORK.
//note that the below bug fix may be required
// http://code.google.com/p/arduino/issues/detail?id=605

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

boolean flash = false;

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 
  192, 168, 0, 177 }; // ip in lan
byte gateway[] = { 
  192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 0 }; //subnet mask
EthernetServer server(80); //server port

String readString;

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

void setup(){

  pinMode(7, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();
  Serial.begin(9600);

}

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

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string
          readString += c;
          Serial.print(c);
        }

        //if HTTP request has ended
        if (c == '\n') {

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

          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<meta name='apple-mobile-web-app-capable' content='yes' />");
          client.println("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />");
          client.println("<link rel='stylesheet' type='text/css' href='http://homeautocss.net84.net/a.css' />");
          client.println("<TITLE>Home Automation</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");
          client.println("<H1>Home Automation</H1>");
          client.println("<hr />");
          client.println("
");
          client.println("<a href=\"/?flashing\"\">Flashing</a>");
          client.println("<a href=\"/?lighton\"\">Turn On Light</a>");
          client.println("<a href=\"/?lightoff\"\">Turn Off Light</a>
");        

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

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

          ///////////////////// control arduino pin
          if(readString.indexOf("?lighton") >0)//checks for on
          {
            flash = false;
            digitalWrite(7, HIGH);    // set pin 4 high
          }
          else if(readString.indexOf("?lightoff") >0)//checks for off
          {
            flash = false;
            digitalWrite(7, LOW);    // set pin 4 low
            
          }

          else if(readString.indexOf("?flashing") >0)
          {
            flash = true;
          }

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

        }
      }
    }
  }

  if (flash == true)
  {
    digitalWrite(7, HIGH);
    delay(100);
    digitalWrite(7, LOW);
    delay(100);
  }


}

It may be a matter of taste, but after writing an entire HTML page in code I would enjoy adding some logic to the program ;). If flash = false is the default state, you can just write it before the string comparisons, e.g.

flash = false;
if(readString.indexOf("?lighton") >0)
... // just set the PIN value, leave flash in the default state
else if(readString.indexOf("?flashing") >0)
{
  flash = true;
}

In order to make the logic more readable I would put the code for the HTML output in a function of its own. It is just presentation and it's probably going to grow in size, but it gets in the middle of the most important function in the loop.

Hi Spatula

The only issue I can see with that method is won't flash will get set to false each time through the loop, I need it to stay true until something else gets turned on - does that make sense?

Thanks for the advice regarding the html code in a function, I will take a look at that :slight_smile:

You are right - if you get a string that doesn't contain any of the strings you are checking, the flash state will be set to false. So it's probably better to set flash = false within each if block, as you did.

I don't know whether you actually tested the code using a browser. I wonder how it reacts to your sending the response and closing the connection right after reading the first line with the query string (ignoring the rest - host, user-agent, ...). Just curious.

I have been running the code and it runs fine, unless I send requests very fast or the arduino is in a delay function and not ready to receive then I get a page not found error and the arduino needs a reset which can be annoying?

OK, many thanks for the input. At least the way I'm structuring the code using lots of separate functions I can call on them easily.

I have just tried to create a function for just the html request, but it won't compile because 'client' was not declared in the scope, how could I make it a global variable, or should I move the rest of the request stuff in with the html function?

Here's the code I have at the moment, needs a lot of work yet.. it won't fit in this post :frowning:

https://dl.dropbox.com/u/33809233/TLC_AND_PHP_CONTROL_COMBINED.ino

how could I make it a global variable

You shouldn't.

or should I move the rest of the request stuff in with the html function?

Why not pass the function a reference to the client?

Using the Additional Options link, and attaching the code is much better than using dropbox.

I pondered over what you meant by passing a reference to the client in the function, adding EthernetClient client = server.available(); to the start works fine. Is that what you meant?

Many thanks!

I pondered over what you meant by passing a reference to the client in the function, adding EthernetClient client = server.available(); to the start works fine. Is that what you meant?

To the start of what?

If you added client as a global variable, that won't work. Each time a client connects, a new client instance is created. You can pass that client instance to a function, using a reference (&).

void loop()
{
   EthernetClient client = server.available();

   myFun(client);
}

voif myFun(EthernetClient &joe)
{
   if(joe.connected())
   {
      // Find out what joe wants
   }
}