lunedì 16 novembre 2009

Arduino WebServer

Arduino Web Server
How to read analog values and control digital output on the web

We will setup in few steps an Arduino WebServer that communicate with a web Browser on Internet. It will accept commands and respond sending to the client the status of its analog inputs.


1. DNS
If you want to interrogate Arduino from the internet is very useful to have a dynamic DNS. I used the free service at http://www.dyndns.com/ to register the name arduino.hobby-site.org



2. ROUTER
Your router must be informed that the request on the port 777 must be redirected inernally to the IP of your Arduino, and the port 77.
The numbers used here are just examples; if you want you can choice the "well know" port 80 for the web server in both the fields "From port" and "To Port". Obviously in this case you must also set "const int SRVPort=80; " in the Arduino code.





3. ARDUINO
Here is the sketch for Arduino. It is partially copied from the standard sample, and partially from the useful DHCP library by Jordan Terrell - blog.jordanterrell.com

/*
* Web Server
* A simple web server that shows the value of the analog input pins.
*/

#include <ethernet.h>
#include <wstring.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 178, 177 };
byte gateway[] = { 192, 168, 178, 1 };
byte subnet[] = { 255, 255, 255, 0 };
const int SRVPort=77;
#define maxLength 30
String inString = String(maxLength); // allocate a new String
unsigned long time;

Server server(SRVPort);

void setup()
{
inString="";
for (int x = 2; x <=7; x++) pinMode(x, OUTPUT); Ethernet.begin(mac, ip, gateway, subnet); server.begin(); Serial.begin(9600); Serial.print("Listening on IP "); printArray(&Serial, ".", ip, 4, 10); Serial.print(" and port="); byte SRV[]={SRVPort}; printArray(&Serial, ".", SRV, 1, 10); } void loop() { char inChar; Client client = server.available(); if (client) { // an http request ends with a blank line boolean current_line_is_blank = true; while (client.connected()) { if (client.available()) { inChar = client.read(); Serial.print(inChar); // keep just the first chars of the HTTP request, discard headers. if (inString.length()<maxLength) inString.append(inChar); // if we've gotten to the end of the line (received a newline // character) and the line is blank, the http request has ended, // so we can send a reply if (inChar == '\n' && current_line_is_blank) { // send a standard http response header client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); ElaborateCmd(&inString); SendOutput(&client); inString=""; break; } } if (inChar == '\n') { // we're starting a new line current_line_is_blank = true; } else if (inChar != '\r') { // we've gotten a character on the current line current_line_is_blank = false; } } } // give the web browser time to receive the data delay(1); client.stop(); } void printArray(Print *output, char* delimeter, byte* data, int len, int base) { char buf[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; for(int i = 0; i < len; i++) { if(i != 0) output->print(delimeter);
output->print(itoa(data[i], buf, base));
}
}

void interpret(uint8_t ch) {
if (ch == 'A') digitalWrite(2, HIGH);
if (ch == 'a') digitalWrite(2, LOW);
if (ch == 'B') digitalWrite(3, HIGH);
if (ch == 'b') digitalWrite(3, LOW);
if (ch == 'C') digitalWrite(4, HIGH);
if (ch == 'c') digitalWrite(4, LOW);
if (ch == 'D') digitalWrite(5, HIGH);
if (ch == 'd') digitalWrite(5, LOW);
if (ch == 'E') digitalWrite(6, HIGH);
if (ch == 'e') digitalWrite(6, LOW);
if (ch == 'F') digitalWrite(7, HIGH);
if (ch == 'f') digitalWrite(7, LOW);

// since pins 0 and 1 are used we have the following for digital IO:
if (ch == 'G') for (int x = 2; x <= 7; x++) { digitalWrite(x, HIGH); }
if (ch == 'g') for (int x = 2; x <= 7; x++) { digitalWrite(x, LOW); }
}

void ElaborateCmd(String *inString)
{
if (inString->length() > 12)
if (inString->startsWith("GET /WriteD"))
interpret(inString->charAt(11));
}

void SendOutput(Client *client)
{
//prints time since program started
time = millis();
client->print("ARDUINO is running during the last: ");
client->print(time);
client->println(" ms<hr />");

// output the value of each analog input pin
for (int i = 0; i < 6; i++) {
client->print("analog input ");
client->print(i);
client->print(" is ");
client->print(analogRead(i));
client->println("<br />");
}

// print the digital output commands available
for (int x = 2; x <=7; x++) { client->print("<a href='WriteD");
client->print('A'+x-2, BYTE);
client->print("'>Led ");
client->print(x);
client->println(" ON</a><br />");
}
for (int x = 2; x <=7; x++)
{
client->print("<a href='WriteD");
client->print('a'+x-2, BYTE);
client->print("'>Led ");
client->print(x);
client->println(" off</a><br />");
}
client->println("<a href='WriteDG'>ALL Leds ON</a>|<a href='WriteDg'>ALL Leds off</a>");
}


4. HTTP
When the program is running, Arduino send a message like this on the serial, to let you now that everythink is ready and ok:
Listening on IP 192.168.178.177 and port=77

Point your browser to:
http://arduino.hobby-site.org:777/
This means to ask a "GET" to the web server.

GET / HTTP/1.1
Host:
arduino.hobby-site.org:777
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; it; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 4.0.20506)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: it-it,it;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://
arduino.hobby-site.org:777/WriteDg

Arduino code DOES NOT check for HTTP valid codes, just waits for a '\n' (new line), that is a blank line. This indicates that the request is ended and Arduino replies:

HTTP/1.1 200 OK
Content-Type: text/html
analog input 0 is 366
analog input 1 is 364
analog input 2 is 332
analog input 3 is 303
analog input 4 is 323
analog input 5 is 324


That inside your browser you will see as follow:




5. NEXT
From this basic structure you can now implement a communication protocol between Arduino and your remote browser.
For example addressing
http://arduino.hobby-site.org:777/ReadA1
will read only Analog1
and
http://arduino.hobby-site.org:777/WriteDA
will turn DigitalPort 8 on, WriteDa turn DigitalPort 8 off, ...

The code below already make Arduino send a list of commands that are recognized by Arduino.

Nessun commento:

Posta un commento