
Description:
There are many pages online offering to show you how to host a website from an Arduino Ethernet, and control the Arduino from that hosted website, but those mainly share the same method: sending a request by changing the website url address.
In this tutorial, I will be using the second available HTML method: sending a request by actually sending a DATA packet.
Video
Hardware:
Arduino Ethernet: http://www.jayconsystems.com/product_detail.php?prod_id=172
Ardumoto Shield V2: http://www.jayconsystems.com/product_detail.php?prod_id=114
LED wired to pins D6 and D7
DC motor using channel B of the Ardumoto Shield V2
Result
Here's a screenshot of what I get with the code in this tutorial.
Hosted by an Arduino! That's pretty neat, and this tutorial will get you to that!

Advantages or this method
- You can send any data type (including files), and are not limited to ASCII
- You can send any data length and are not limited by the url size
- Your data is Hidden, can be encrypted, and is difficult to hack (has to be used for applications using passwords)
Drawbacks or this method
- The server (here the Arduino) processing is more complex
- The processing depends on the web browser used, as different browsers send different request headers
Important: This tutorial is working for Firefox and will need to be adapted for any other browser. But I will explain how to modify it to get it working with your favorite browser!!
Code
If you want to get to business immediately, here is the whole code.
It is well commented but the whole thing might still be confusing, so I will explain every part more in detail in the rest of this tutorial
- Code: Select all
#include <SPI.h>
#include "Ethernet.h"
// Ethernet library configuration
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 123 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
EthernetServer server(80); //server port
// HMTL processing variables
String readString = ""; //string to get incoming data
char c;
char buffer[10];
int dataLength =0;
// Buffer containing processed hmtl data
char data[50];
char datamotor[5];
int index=3;
// Arduino controls variables
// Motor control Ardumoto B PWM D5 Direction D8
int motor_PINpwm = 5;
int motor_PINdir = 8;
int motor_VALpwm = 0;
int motor_VALdir = LOW;
int LEDpinlow = 6; //simulated ground
int LEDpin = 7;
int LEDval=0;
// Setup function
void setup()
{
Ethernet.begin(mac, ip, gateway); //start Ethernet
Serial.begin(9600);
// Arduino controls initialization
pinMode(motor_PINdir, OUTPUT);
pinMode(LEDpinlow, OUTPUT);
digitalWrite(LEDpinlow, LOW);
pinMode(LEDpin, OUTPUT);
}
// Loop function
void loop()
{
EthernetClient client = server.available();
if (client)
{
while (client.connected())
{
while (client.available()) // Receive client data
{
Serial.print(".");
c = client.read(); //read char by char HTTP request
readString +=c;
//Serial.print(c); //output chars to serial port
// If first request upon connexion, the 3 first characters will be "GET"
// If "GET" is caught, skip the request info
if( readString.equals("GET"))
{
Serial.println("");
Serial.println("GET caught, skipping request and printing HTML");
break;
}
// Otherwise, if the request contains data,
// the first characters will be "POST"
// We then skip the request header and this "if" becomes our main function
if( readString.equals("POST"))
{
Serial.println("");
Serial.println("POST caught, skipping header and acquiring DATA");
// 320 is arbitrary. The actual length that has to be skipped depends on
// several user settings ( browser, language, addons...)
// the skipped length has not to be too long to skip relevant data
// and not to short to waste computing time
for(int i=0; i<320; i++)
{
c = client.read();
//Serial.print(c); // UNCOMMENT FOR DEBUG
}
//Searches for "Length: "
while(c != 'L')
{
c = client.read();
//Serial.print(c); // UNCOMMENT FOR DEBUG
}
// Skip "Length: "
for (int i=0; i<7; i++)
{
c = client.read();
//Serial.print(c); // UNCOMMENT FOR DEBUG
}
// Read the data package length
readString="";
c = client.read();
while(c != '\n')
{
readString += c;
//Serial.print(c);
c = client.read();
}
// convert data read from String to int
readString.toCharArray(buffer, readString.length());
dataLength = atoi(buffer);
Serial.println("");
Serial.print("dataLength: ");
Serial.println(dataLength);
// gets DATA
client.read(); // skips additional newline
client.read();
for (int i=0; i<dataLength; i++)
{
data[i] = client.read();
}
Serial.println("");
Serial.print("data: ");
Serial.println(data);
readString ="";
// Handle data
// Sort cases depending on the first 2 characters
char tempbuff[3];
tempbuff[0] = data[0];
tempbuff[1] = data[1];
switch (atoi(tempbuff)) //convert to int
{
case 1:
Serial.println("In case 1");
while(data[index] !='&')
{ datamotor[index-3] = data[index];
index++; }
motor_VALpwm = atoi(datamotor);
if(motor_VALpwm > 255){motor_VALpwm = 255;}
if(motor_VALpwm < 0){motor_VALpwm = 0;}
Serial.println("PWM value: ");
Serial.println(motor_VALpwm);
index++;
if( atoi(&data[index]) == 0)
{ motor_VALdir = LOW; }
if( atoi(&data[index]) == 1)
{ motor_VALdir = HIGH; }
Serial.println("PWM direction: ");
Serial.println(motor_VALdir);
digitalWrite(motor_PINdir, motor_VALdir);
analogWrite(motor_PINpwm, motor_VALpwm);
break;
case 2:
Serial.println("In case 2");
if (LEDval == 0)
{LEDval =1;}
else
{LEDval=0;}
digitalWrite(LEDpin, LEDval);
Serial.print("LED status: ");
Serial.println(LEDval);
break;
}
Serial.println("Out of switch");
}
}
// HTML CODE
client.println("<!DOCTYPE html>");
client.println("<html>");
client.println("<body background=\"http://img256.imageshack.us/img256/5867/52577012.jpg\"</body>");
client.println("<font color=white><h1 align=center>Jayconsystems Arduino Ethernet presentation</font></h1>");
client.println("<P style=text-align:center><img src=\"http://img52.imageshack.us/img52/4299/logovgn.png\" width=600 height=150 /></P>");
client.println("<hr />");
client.println("<hr />");
client.println("<form name=input method=post>");
String tempSTR = "<font color=white> Motor speed: <input type=text name=01 value=";
tempSTR += motor_VALpwm;
tempSTR += " maxlength=3 /><br />";
client.println(tempSTR);
client.println("<input type=submit name=0 value=\"DIRECTION 1\" />");
client.println("<input type=submit name=1 value=\"DIRECTION 2\" />");
client.println("</form> <br /> <br />");
client.println("<dd><dd><dd>");
if(LEDval == 1)
{
client.println("<img src=\"http://img688.imageshack.us/img688/4420/greencw.png\" width=20 height=20 />");
}
if(LEDval == 0)
{
client.println("<img src=\"http://img507.imageshack.us/img507/8505/blackko.png\" width=20 height=20 />");
}
client.println("<form name=input method=post>");
client.println("<input type=hidden name=02 />");
client.println("<input type=submit value=\" LED \" />");
client.println("</form> <br />");
client.println("</body>");
client.println("</html>");
Serial.println("__________");
delay(100);
client.stop();
}
}
// Reinitializing variables
readString =""; // Reinitialize String
for (int i=0; i<10; i++)
{buffer[i] = '\0';}
for (int i=0; i<50; i++)
{data[i] = '\0';}
for (int i=0; i<5; i++)
{datamotor[i] = '\0';}
dataLength =0;
index = 3;
}
Code detailed
Ethernet configuration
- Code: Select all
#include <SPI.h>
#include "Ethernet.h"
Those are the basic include to get the Arduino Ethernet Library working
- Code: Select all
// Ethernet library configuration
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 123 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
EthernetServer server(80); //server port
The physical mac address is set by the code. Just make sure to change at least one of those values if you use 2 Arduinos on your network.
The IP in lan is also set by this code.
The gateway is the local address of my router
The EthernetServer server(80) means that a server is open on port 80 (standard port for HTTP)
Browser request message handler
- Code: Select all
Serial.println("");
Serial.println("POST caught, skipping header and acquiring DATA");
// 320 is arbitrary. The actual length that has to be skipped depends on
// several user settings ( browser, language, addons...)
// the skipped length has not to be too long to skip relevant data
// and not to short to waste computing time
for(int i=0; i<320; i++)
{
c = client.read();
//Serial.print(c); // UNCOMMENT FOR DEBUG
}
//Searches for "Length: "
while(c != 'L')
{
c = client.read();
//Serial.print(c); // UNCOMMENT FOR DEBUG
}
// Skip "Length: "
for (int i=0; i<7; i++)
{
c = client.read();
//Serial.print(c); // UNCOMMENT FOR DEBUG
}
This is the part that depends on the browser and user.
You can uncomment the 3 lines labeled "// UNCOMMENT FOR DEBUG" to output on serial the browser-dependent header and request.
Then you will have to adapt that code on that info, by skipping irrelevant information and starting the data handling at specific detected characters.
If there's a high demand, I will work on a header analyzer that detects the browser and accordingly skips unrelevant data.
Caught Data management
Whenever a request is sent from the browser by clicking a button, one data value will be first detected.
For performance, I then organized data handler with switch/case.
Important: In my code, every case has to be a 2-numbers code. You can then with my code handle 99 different buttons.
(case 1 is actually case 01 but the 0 here doesn't matter)
I will just analyze the most complicated case of this code, the request controlling the motor:
The data sent by the browser will look like: data: "01=50&0=DIRECTION+1"
I will detail later that I gave the button "DIRECTION 1" the html name "0" and "DIRECTION 2" the html name "1"
The part that interests us here is "01=50&0".
It means: case 01 (motor) = 50 (speed value, ranging from 0 to 255) & 0 (direction 1).
A request demanding a 100% speed on direction 2 would then be: "01=255&1=DIRECTION+2"
Let's analyze the data handler then:
// I am there catching the speed value:
while(data[index] !='&')
{ datamotor[index-3] = data[index];
index++; }
// I am here converting the read buffered ASCII characters into the actual integer
motor_VALpwm = atoi(datamotor);
// Ensuring that limit values aren't exceeded
if(motor_VALpwm > 255){motor_VALpwm = 255;}
if(motor_VALpwm < 0){motor_VALpwm = 0;}
// Printing values
Serial.println("PWM value: ");
Serial.println(motor_VALpwm);
// Incrementing the data index to skip the "&" character
index++;
// Reading the next character. 0 means direction 1, 1 means direction 2
if( atoi(&data[index]) == 0)
{ motor_VALdir = LOW; }
if( atoi(&data[index]) == 1)
{ motor_VALdir = HIGH; }
Serial.println("PWM direction: ");
Serial.println(motor_VALdir);
// Updating the motor controls
digitalWrite(motor_PINdir, motor_VALdir);
analogWrite(motor_PINpwm, motor_VALpwm);
// Exiting the switch
break;
Website HTML construction
If you're still following me to that point, I got good news, the hardest part is done!!
The only thing left is the html output to refresh the website!
The goal of this tutorial is not to teach you html, there are plenty of great websites for that.
I will just explain the buttons:
Motor control:
//This is the "method" I was talking about at the beginning of this tutorial. The 2 different methods are "get" //and "post", and we are here using "post"
//Every user-input has to be a "form" content. You can put almost anything within the form.
//Our first form includes a user-input field and 2 buttons.
client.println("<form name=input method=post>");
// I give the form name "01". This can be whatever you want, but remember that the data analyzer is only //coded for 2 digits values and then the "0" is important here.
// I created a temp String to be able to print the current motor speed in the field, which is the
// "motor_VALpwm" variable
String tempSTR = "<font color=white> Motor speed: <input type=text name=01 value=";
tempSTR += motor_VALpwm;
tempSTR += " maxlength=3 /><br />";
client.println(tempSTR);
// We are still in the "form" and now I create the 2 buttons. The value is the text of the buttons. This can also
// be an image. The name of the buttons is what matters. Here 0 and 1, for 2 different cases
// (direction 1 and direction 2)
client.println("<input type=submit name=0 value=\"DIRECTION 1\" />");
client.println("<input type=submit name=1 value=\"DIRECTION 2\" />");
// And now we close the form
client.println("</form> <br /> <br />");
LED control:
This one is way easier.
We create one form with the name "02" (for case "02"), hide the user input field, and just put one button
client.println("<form name=input method=post>");
client.println("<input type=hidden name=02 />");
client.println("<input type=submit value=\" LED \" />");
client.println("</form> <br />");
Conclusion:
Alright, I guess that's it!
I admit that it's a pretty tricky one, there are plenty of ways to get lost between Arduino code and html, but I hope that I was of a great help!
Don't hesitate to ask questions here and share with us the great projects this tutorial helped you realize!!
_______________________
EDIT:
11/13/2012: Jonkapteijn points out that the header might have to be changed
http://www.jayconsystems.com/forum/viewtopic.php?f=10&t=90&p=917#p916
Try replacing:
- Code: Select all
client.println("<body background=\"http://img256.imageshack.us/img256/5867/52577012.jpg\"</body>");
With:
- Code: Select all
client.println("<body background=\"http://img256.imageshack.us/img256/5867/52577012.jpg\">");
