Ethernet Shield and HTML forms

Hey everyone,

I am using an arduino + ethernet shield to control a pan/tilt camera from a web browser. Currently it works, very basically and I wanted some advice to make the improvements I want. At the moment when user clicks "up" the arduino server receives a string and uses indexOf() to find what was sent and execute, send HIGH to the electronics and activate the appropriate motor. In it's current iteration it has to be manually set back to low, but I'd rather it send "up" (and therefore HIGH) only while the button is held down. Furthermore it should be able to count how long it is held down. Any ideas would be greatly appreciated!

Post what you have done already.

Read this before posting a programming question

I’ve attached the arduino .ino which verifies in arduino 1.0

EtherCam.ino (3.46 KB)

To save people downloading it:

//Adam Henrichs 22/05/2012
#include <SPI.h>
#include <Client.h>
#include <Ethernet.h>
#include <Udp.h>
#include <Servo.h>

//Pin Assignments
const int upPin =5;
const int downPin =6;
const int leftPin =7;
const int rightPin =8;

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

String readString = String(30); //string for fetching data from address

void setup()
{
  Ethernet.begin(mac, ip, gateway, subnet);
  Serial.begin(9600);
  
  pinMode(upPin, OUTPUT);
  pinMode(downPin, OUTPUT);
  pinMode(leftPin, OUTPUT);
  pinMode(rightPin, OUTPUT);
}

void loop()
{
  
digitalWrite(upPin, LOW);
digitalWrite(downPin, LOW);
digitalWrite(leftPin, LOW);
digitalWrite(rightPin, LOW);

// 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 (c == '\n') {
          if (readString.indexOf("?") <0)
          {
            //do nothing
          }
          else
          {
             if(readString.indexOf("UP=UP") >0)
               {
                 cameraup();
               }
             else if(readString.indexOf("DN=DN") >0)
               {
                 cameradown();
               }
             else if(readString.indexOf("LT=LT") >0)
               {
                 cameraleft();
               }
             else if(readString.indexOf("RT=RT") >0)
               {
                 cameraright();
               }
             else if(readString.indexOf("ST=ST") >0)
               {
                 camerastop();
               }
          }
          // now output HTML data starting with standard header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          //set background to green
          client.print("<body style=background-color:green>");
          client.println("<hr />");
          client.println("<center>");
          client.println("<h1>Camera control</h1>");
          client.println("<form method=get name=SERVO>");
          client.println("<input type=submit value=UP name=UP style=\"width:100px\">
");
          client.println("<input type=submit value=LT name=LT style=\"width:100px\"><input type=submit value=ST name=ST style=\"width:100px\"><input type=submit value=RT name=RT style=\"width:100px\">
");
          client.println("<input type=submit value=DN name=DN style=\"width:100px\">");
          client.println("</form>");
          client.println("</center>");
          client.println("</body></html>");
          //clearing string for next read
          readString="";
          //stopping client
          client.stop();
            }
          }
        }
      }
}
void movetiltupstep()
{
  digitalWrite(upPin, HIGH);
}
void movetiltdownstep()
{
  digitalWrite(downPin, HIGH);
}
void movepanleftstep()
{
  digitalWrite(leftPin, HIGH);
}
void movepanrightstep()
{
  digitalWrite(rightPin, HIGH);
}
void center()
{
  digitalWrite(rightPin, LOW);
  digitalWrite(leftPin, LOW);
  digitalWrite(upPin, LOW);
  digitalWrite(downPin, LOW);
}
}

Now:

String readString = String(30); //string for fetching data from address

That doesn’t create a 30-character string, it creates a string with “30” in it.

    if (readString.length() < 100)

Just as well because you are reading up to 100 characters.

The String class tends to fragment memory. You are better off reading into a char array, and then doing “strstr” on it.

In it’s current iteration it has to be manually set back to low, but I’d rather it send “up” (and therefore HIGH) only while the button is held down.

I doubt if you can do that, without some fancy Java on the web page. The form is transmitted in one batch.

Thanks for the correction and suggestion. Unfortunately I've never Java-d although I'm fairly certain the arduino can't store too fancy a page due to size restrictions anyway.

You will want to look at the F macro. What with using the String class, and the other literal strings you have, running out of RAM is almost certain. eg.

          client.println(F("<form method=get name=SERVO>"));

I'm fairly certain the arduino can't store too fancy a page due to size restrictions anyway.

You can work around that by using the SD card reader included in the Ethernet shield. Store the fancy stuff on the SD card and just "pipe" it through the Arduino if the browser requests it. Not extraordinarily fast but probably enough for your usage (single user I guess).

The page you are serving up implies that there are servos involved. The code that reacts to the form submit buttons includes the Servo library but creates no instances of the Servo class. So, there are no servos involved. Just electric motors that you turn on and off. There is going to be a significant lag time between submitting the form with the UP button and submitting the form with the STOP button.

Perhaps the thing to do is to make the up, down, left, and right buttons move the camera for a fixed period of time, and get rid of the stop button. Perhaps add a field (or slider, if your browser supports sliders) to the form to define that time. Or add 4 more buttons for large steps left, right, up, and down, and have the 4 current buttons be small steps up, down, left, and right.

In any case, explicitly defining the width (style) of form objects is unnecessary, and simply makes the form larger (and slower to load).

@ Pylon - thanks for the info I'll look in to it, you're right that speed is not a concern, the camera just needs to be remotely movable and set to the desired position for observation.

@PaulS - yes there is some servo code left in there as this is a frankenstein'd version of a similar project I did that did use servos, I'll clean it up today! Thanks for the advice on the HTML, I haven't used it before and working mostly off examples in that regard. I am interested in the idea of only activating for a set time on button push. Would that be as simple as HIGH > delay > LOW?

Thanks everyone for your input!

*Edit, got my test bits together and HIGH > delay > LOW does indeed work! The manufacture has included a rough degrees/second for the motors too so I am considering including a rough position update after being told where it started. What I don't know how to do between the HTML and arduino is have the arduino receive the initial positions from the browser and save them as a variable - any ideas?

If you use servos for the pan/tilt, you can direct the cam via a web page like below. Other web page controls can be made using javascript "onclick" functions and similar where a start command is sent when a mouse page button down press is made, and the stop command is sent when the mouse button is released.

http://web.comporium.net/~shb/wc2000-PT-script.htm

What I don’t know how to do between the HTML and arduino is have the arduino receive the initial positions from the browser and save them as a variable - any ideas?

Developing the code is an iterative process. First, you have the Arduino send what it knows as part of the form. Then, you analyze what the Arduino gets pack when a submit button is pressed. Next, you develop the code to parse that response.

Let us know what part(s) you need help with.

I have set up some submit buttons where I can enter text and ‘submit’ and they turn up like this;

http://131.181.87.145/?vpos=6&hpos=7

where 6 and 7 are the entered numbers. I’m not sure what method there is to pluck out that 6 and 7 and save them.

Your link does not work. Below is some html code that performs get request based on mouse click actions. The contained data in the query_string is for an apache server another servo controller, but could be modified for an arduino.

<HTML> 
<HEAD> 
<TITLE>SSC-32 Web Control Panel</TITLE> 
</HEAD> 
<BODY> 
<H2>SSC-32 Web Control Panel 

(Continous Rotation)</H2>
 
Buttons to control two servos (4 and 5).


Code set to rotate servos that are


on opposite sides of a bot.


Hold button down to actuate.


Release button to stop.





<input type="button" value="LF" 
onmousedown="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1600$0D');" 
onmouseup="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1500$0D');"/> 

<input type="button" value="LR" 
onmousedown="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1400$0D');" 
onmouseup="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1500$0D');"/> 

<input type="button" value="SL" 
onmousedown="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1400$235P1400$0D');" 
onmouseup="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1500$235P1500$0D');"/> 

<input type="button" value="AF" 
onmousedown="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1600$235P1400$0D');" 
onmouseup="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1500$235P1500$0D');"/> 

<input type="button" value="AR" 
onmousedown="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1400$235P1600$0D');" 
onmouseup="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1500$235P1500$0D');"/> 

<input type="button" value="SR" 
onmousedown="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1600$235P1600$0D');" 
onmouseup="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$234P1500$235P1500$0D');"/> 

<input type="button" value="RF" 
onmousedown="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$235P1400$0D');" 
onmouseup="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$235P1500$0D');"/> 

<input type="button" value="RR" 
onmousedown="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$235P1600$0D');" 
onmouseup="location.href 
('http://127.0.0.1:80/cgi-bin/echoo.bat?$235P1500$0D');"/> 





Zoomkat 2009
 
</body> 
</html>

Just for clarification it's not meant to be a link, just illustrative of what my client sends the arduino :p. I'm keeping it local network only

Ok so I’m trying to use the TextFinder based on a method found elsewhere

 else if(finder.find("vpos="))
            {
              vpos = finder.getValue();
              Serial.print(vpos);
            }

I’ve tried this which works but only after submitting twice, on first click it sends http://131.181.87.145/?vpos=&hpos= and second click it sends http://131.181.87.145/?vpos=56&hpos=43 (or whatever numbers I’ve put in). I have a feeling this has something to do with how the form is set up although I am not sure how to change it.

*Edit, forgot to include the form section

          client.print("<body style=background-color:ivory>");
          client.println("<center>");
          client.println("<h1><FONT COLOR=blue>Observation Camera Control</FONT></h1>");
          client.println("</center>");
          client.println("<form method=get name=POSI>");
          client.println("<FONT COLOR=blue>Initial Vertical Position:</FONT><input type=text name=vpos>
");
          client.println("<FONT COLOR=blue>Initial Horizontal Position:</FONT><input type=text name=hpos><input type=submit value=Submit>
");
          client.println("</form>");  
          client.println("<center>");
          client.println("<form method=get name=MOTOR>");
          client.println("<input type=submit value=UP name=UP>
");
          client.println("<input type=submit value=LT name=LT><input type=submit value=RT name=RT>
");
          client.println("<input type=submit value=DN name=DN>");
          client.println("</form>");
          client.println("</center>");
          client.println("</body></html>");

*Edit, forgot to include the form section

What you forgot to include was all the code.

Here is the complete code;

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

int upPin =5;
int downPin =6;
int leftPin =7;
int rightPin =8;
int vpos;
int hpos;
double current_vertical = 0;
double current_horizontal = 0;

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xAD }; //physical mac address
byte ip[] = { 
  131, 181, 87, 145 }; // ip of arduino in lan
byte gateway[] = { 
  131, 181, 87, 129 }; // internet access via router
byte subnet[] = { 
  255, 255, 255, 128 }; //subnet mask
EthernetServer server(80); //server port

String readString; 

void setup()
{
  Ethernet.begin(mac, ip, gateway, subnet);
  Serial.begin(9600);

  pinMode(upPin, OUTPUT);
  pinMode(downPin, OUTPUT);
  pinMode(leftPin, OUTPUT);
  pinMode(rightPin, OUTPUT);
}

void loop()
{

  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    TextFinder finder(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 (c == '\n') {
          if (readString.indexOf("?") <0)
          {
            //do nothing
          }
          else
          {
            if(readString.indexOf("UP=UP") >0)
            {
              cameraup();
            }
            else if(readString.indexOf("DN=DN") >0)
            {
              cameradown();
            }
            else if(readString.indexOf("LT=LT") >0)
            {
              cameraleft();
            }
            else if(readString.indexOf("RT=RT") >0)
            {
              cameraright();
            }
            else if(finder.find("vpos="))
            {
              vpos = finder.getValue();
              Serial.print(vpos);
//              finder.find("hpos=");
//              hpos = finder.getValue();
//              Serial.print(hpos);
            }            
          }
          client.print("<body style=background-color:ivory>");
          client.println("<center>");
          client.println("<h1><FONT COLOR=blue>Observation Camera Control</FONT></h1>");
          client.println("</center>");
          client.println("<form method=get name=POSI>");
          client.println("<FONT COLOR=blue>Initial Vertical Position:</FONT><input type=text name=vpos>
");
          client.println("<FONT COLOR=blue>Initial Horizontal Position:</FONT><input type=text name=hpos><input type=submit value=Submit>
");
          client.println("</form>");  
          client.println("<center>");
          client.println("<form method=get name=MOTOR>");
          client.println("<input type=submit value=UP name=UP>
");
          client.println("<input type=submit value=LT name=LT><input type=submit value=RT name=RT>
");
          client.println("<input type=submit value=DN name=DN>");
          client.println("</form>");
          client.println("</center>");
          client.println("</body></html>");
          //clearing string for next read
          readString="";
          //stopping client
          client.stop();
        }
      }
    }
  }
}
void cameraup()
{
  digitalWrite(upPin, HIGH);
  delay(500);
  digitalWrite(upPin, LOW);
  current_vertical = current_vertical + 1.5;
}
void cameradown()
{
  digitalWrite(downPin, HIGH);
  delay(500);
  digitalWrite(downPin, LOW);
  current_vertical = current_vertical - 1.5;
}
void cameraleft()
{
  digitalWrite(leftPin, HIGH);
  delay(500);
  digitalWrite(leftPin, LOW);
  current_horizontal = current_horizontal - 1.5;
}
void cameraright()
{
  digitalWrite(rightPin, HIGH);
  delay(500);
  digitalWrite(rightPin, LOW);
  current_horizontal = current_horizontal + 1.5;
}