Webserver con Arduino: lo stato dei sensori su internet.

Visualizzare lo stato dei sensori del sistema domotico sul web – Il WebServer con Arduino, ethernet shield, HTML e AJAX

Webserver con Arduino, Ethernet shield, HTML e Ajax

Nella parte sinistra della foto è visibile il Webserver costituito da una scheda Arduino 2560 Mega e da un Ethernet shield.

Questo Webserver con Arduino, più che risolvere una reale esigenza, è stato un modo per giocare con le schede Arduino e le Etherne Shield. Volevo provare a visualizzare lo stato dei sensori della casa in una immagine grafica e su ogni piattaforma hardware (PC, Tablet, Cellulari ecc.); quindi la scelta è caduta su un ‘Ethernet Shield’.  Ho creato una prima versione del programma che utilizzava solo ‘HTML’. Funzionava, ma ad ogni refresh riaggiornava l’intera pagina con un fastidioso e non bello sfarfallio. Così ho utilizzato Html + Ajax per la pagina web e il software.

Ma dato che le routine per la funzionalità WebServer pesavano in termini occupazione di risorse (CPU e memoria) e quindi avrebbero rallentato il programma principale di gestione della casa descritto in questo sito; ho preferito confinare il programma in una scheda Arduino a parte, che si occupa di produrre la pagina web con i dati ricevuti via I2C.

In questo modo al programma di gestione della casa è demandato solo il compito di inviare un insieme di byte, rappresentante in ogni singolo bit lo stato di un sensore, verso l’Arduino dedicato come webserver. Quest’ultimo quindi visualizza o meno un cerchietto colorato sopra una immagine. Il risultato è di vedere una pianta con i pallini dei sensori attivi. Non proprio in real time, ma con un refresh di pochissimi secondi.

Dato che il programma WebServer è confinato in un Arduino a parte, è relativamente semplice copiarlo ed adattarlo in vostri progetti o per altri scopi. Diventa così facile controllare un negozio, un hangar, una fabbrica o un giocattolo. Ad esempio potete mettere su qualche sito web internet due foto della vostra casa, una con la porta aperta e l’altra chiusa e far vedere una delle due foto in accordo con lo stato del sensore della porta stessa. E’ un esempio che rende l’idea delle potenzialità del programma, ma è bene non mettete in internet lo stato di sensori importanti sotto il profilo della sicurezza.

L’immagine seguente da una idea del risultato grafico. Lo stato dei sensori sono rappresentati dai pallini che cambiano colore in blu o rosso. Un cerchio che si muove intorno al pallino attira immediatamente l’attenzione sull’ultimo cambio di stato. Il cerchio può essere rosso o blu a secondo dello stato del sensore. Infine ad ogni cambio stato di un sensore, su di esso appare una barra che esprime 2 minuti di tempo. In questo modo è possibile vedere a colpo d’occhio la sequenza dei sensori attivati al passaggio di una persona.

Casa domotica - Arduino webserver - Pianta della casa con lo stato dei sensori.jpg

L’immagine è fissa. Cliccando il seguente link è possibile vedere lo stato dei sensori in diretta (real-time) e può darvi una idea di come funziona. Ricordatevi che è una situazione in diretta (in realta è introdotto un deciso ritardo, ma cambia poco per le finalità di divulgazione) e pertanto è necessario aspettare qualche tempo per vedere i sensori cambiare stato di giorno e molto di più di notte (Il webserver è in Italia GMT+1).

 

Visualizzazione sensori con HTML-Ajax (Nota: tale link è spesso attivo, ma non sempre. Quando attivo, i cerchietti colorati si accendono e spengono quasi in real-time (refresh 2 secondi). Alcuni cerchietti cambiano in modo casuale per non permettere ad estranei di risalire al reale stato della casa. La casualità è implementata in una routine sullo sketch non presente nella versione scaricabile da questa pagina web.

 

Nel link qui sopra, l’immagine di sfondo non rappresenta nessuna casa reale. Da solo un riferimento per la posizione del cerchietto rosso. Ad esempio in vostri progetti, potete mettere lo sfondo di un gatto e fare in modo che il pallino in prossimità dei baffi indichi una finestra aperta, quello sull’orecchio vi indica la luce accesa ecc. In altre parole associate immagine e posizione dei cerchietti in modo che abbiano un significato solo per voi. Va bene anche, come nel link sopra, un disegno di una pianta apparentemente verosimile ma che in realtà è completamente inventata.

Come realizza questo Webserver con Arduino:
Prendete due Arduino collegati in I2C tra loro e metteteci i due programmi qui sotto. Il primo è il Webserver. L’altro è il programma di test che invia i dati. Il prograrmma di test permette di vedere il cambio stato dei sensori sulla pagina web. Modificandolo potete includere e visualizzare su internet i vostri sensori.

Sketch Webserver con Arduino
Visualizzare lo stato dei sensori di Arduino in una pagina HTML-Ajax

Il programma quindi è il seguente. Da considerare che gestisce una casa. Ma è possibile adattarlo per controllare un negozio, un hangar, un fabbrica o un giocattolo a secondo delle vostre esigenze. Questa versione software prevede solo il cambio di colore dei pallini a secondo lo stato del sensore. Non visualizza animazione e le barre del tempo sopra ai sensori.

/*————————————————————————-*/
/* WebServerwithHTMLandAjax */
/* shows representative images of the state of the sensors in an htmlpage */
/*————————————————————————-*/
/* myelectronichome.altervista.org */
/*————————————————————————-*/

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

int address = 55; //address I2C
byte Stato_sensori[10]; //from I2C receive 10 byte

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //mac
IPAddress ip(192,168,1,111); // IP address
EthernetServer server(80);

String HTTP_req;

void setup()
{
Serial.begin(115200); // for diagnostics
Ethernet.begin(mac, ip); // initializeEthernetdevice
server.begin(); // start to listen for clients
Serial.println(“WebServer_html-Ajax: Start”);

Serial.print(“WebServerAddress: “);
Serial.println(Ethernet.localIP());

//Section Wire
Wire.begin(address);
Wire.onReceive(receiveEvent);

Stato_sensori[0] = 0;
Stato_sensori[1] = 0;
Stato_sensori[2] = 0;
Stato_sensori[3] = 0;
Stato_sensori[4] = 0;
Stato_sensori[5] = 0;
Stato_sensori[6] = 0;
Stato_sensori[7] = 0;
Stato_sensori[8] = 0;
Stato_sensori[9] = 0;

}

void loop()
{
EthernetClient client = server.available(); // try to get client
if (client) {
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
HTTP_req += c;
if (c == ‘\n’ && currentLineIsBlank) {
// send a standard http response header
client.println(“HTTP/1.1 200 OK”);
client.println(“Content-Type: text/html”);
client.println(“Connection: keep-alive”);
client.println();
//AJAXrequest for switch state
if (HTTP_req.indexOf(“MyElectronicHome”) > -1) {
GetSensorState(client);
}
else
{ // HTTP request for web page
// send web page – contains JavaScriptwithAJAXcalls
client.println(“<!DOCTYPEhtml>”);
client.println(“<html>”);
client.println(“<head>”);
client.println(“<title>MyElectronicHome: Sensor</title>”);
client.println(“<script>”);
client.println(“function GetSensorState() {“);
client.println(“nocache = \” &nocache=\” + Math.random() * 1000000;”);
client.println(“var request = new XMLHttpRequest();”);
client.println(“request.onreadystatechange = function() {“);
client.println(“if (this.readyState == 4) {“);
client.println(“if (this.status == 200) {“);
client.println(“if (this.responseText != null) {“);
client.println(“document.getElementById(\”Sensor\”).innerHTML = this.responseText;”);
client.println(“}}}}”);
client.println(“request.open(\”GET\”, \”MyElectronicHome\” + nocache, true);”);
client.println(“request.send(null);”);
client.println(“setTimeout(‘GetSensorState()’, 2000);”); //2000 for 2 seconds refresh.
client.println(“}”);
client.println(“</script>”);
client.println(“</head>”);
client.println(“<body onload=\”GetSensorState()\”>”);
client.println(“<h1>MyElectronicHome</h1>”);
client.println(“<DIV id=\”casa\” STYLE=\”position:absolute; left:30px; top:140px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/casa.gif\”> </DIV>”);
client.println(“<p id=\”Sensor\” STYLE=\”position:absolute; left:0px; top:60px\”> Please Wait…</p>”);
client.println(“</body>”);
client.println(“</html>”);
}
HTTP_req = “”; // finishedwithrequest, empty string
break;
}
// every line of text received from the client endswith\r\n
if (c == ‘\n’) {
currentLineIsBlank = true;
}
else if (c != ‘\r’) {
currentLineIsBlank = false;
}
} // end if (client.available())
} // end while (client.connected())
delay(1); // give the web browser time to receive the data
client.stop(); // close the connection
} // end if (client)
}

void GetSensorState(EthernetClient cl)
{
//on byte received reads bit and printhtmlistruction
//Attention: when there are a big amount of cl.print, withoout F(“….”) macro the program will stop.
int j1=0 ;
int j2=0 ;

//on J1 the byte, on j2 the bit of the byte
j1=0;j2=0; if (bitRead(Stato_sensori[j1],j2)==1) {cl.print(F(“<DIV id=\”0\” STYLE=\”position:absolute; left:0px; top:00px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/rosso.gif\”> </DIV> “));}
j1=0;j2=1; if (bitRead(Stato_sensori[j1],j2)==1) {cl.print(F(“<DIV id=\”1\” STYLE=\”position:absolute; left:0px; top:00px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/rosso.gif\”> </DIV>;”));}
j1=0;j2=2; if (bitRead(Stato_sensori[j1],j2)==1) {cl.print(F(“<DIV id=\”2\” STYLE=\”position:absolute; left:0px; top:00px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/rosso.gif\”> </DIV> “));}
j1=0;j2=3; if (bitRead(Stato_sensori[j1],j2)==1) {cl.print(F(“<DIV id=\”3\” STYLE=\”position:absolute; left:00px; top:00px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/rosso.gif\”> </DIV> “));}
//Many lines are not shown but they must continue numbering J1 and J2
j1=1;j2=7; if (bitRead(Stato_sensori[j1],j2)==1) {cl.print(F(“<DIV id=\”15\” STYLE=\”position:absolute; left:110px; top:420px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/rosso.gif\”> </DIV>”));}
//Many lines are not shown but they must continue numbering J1 and J2
j1=2;j2=0; if (bitRead(Stato_sensori[j1],j2)==1) {cl.print(F(“<DIV id=\”16\” STYLE=\”position:absolute; left:320px; top:170px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/rosso.gif\”> </DIV>”));}
//Many lines are not shown but they must continue numbering J1 and J2
j1=2;j2=7; if (bitRead(Stato_sensori[j1],j2)==1) {cl.print(F(“<DIV id=\”23\” STYLE=\”position:absolute; left:80px; top:200px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/rosso.gif\”> </DIV>”));}
//Many lines are not shown but they must continue numbering J1 and J2
j1=9;j2=1; if (bitRead(Stato_sensori[j1],j2)==1) {cl.print(F(“<DIV id=\”73\” STYLE=\”position:absolute; left:230px; top:880px\”> <img src=\”http://myelectronichome.altervista.org/joomla/images/arduinowebserver/rosso.gif\”> </DIV>”));}
}

void receiveEvent(int howMany)
{
//receive I2C byte from otherArduino
Serial.println(“Start receive”) ;
int index_x=0;
while (Wire.available() != 0)
{
byte ricevuto;
ricevuto = Wire.read();
Stato_sensori[index_x] = ricevuto;
index_x = index_x + 1;
}

Serial.println(“Data received…”) ;

}

Programma di test.

Questo programma invia semplicemente un insieme di byte al secondo Arduino. Notare che invia lo stesso byte 10 volte. Le istruzioni in rosso scuro sono quelle da inserire in vostri progetti. Questo programma ma messo in una seconda scheda Arduino collegata via I2C.

#include <Wire.h>
int address = 55; //indirizzo I2C

void setup()
{
Wire.begin(); // join i2c bus
}

byte x = 0;

void loop()
{
Wire.beginTransmission(address);

//Send byte x more times
Wire.write(x); // sends one byte 1
Wire.write(x); // sends one byte 2
Wire.write(x); // sends one byte 3
Wire.write(x); // sends one byte 4
Wire.write(x); // sends one byte 5
Wire.write(x); // sends one byte 6
Wire.write(x); // sends one byte 7
Wire.write(x); // sends one byte 8
Wire.write(x); // sends one byte 9
Wire.write(x); // sends one byte 10

Wire.endTransmission(); // stop transmitting

//Display X byte
Serial.println(x,BIN);

x++;
if (x>255) {x=0;}
delay(2000);

}

Oltre ai programmi vi servono due iimmagini, una come sfondo e l’altra contenete un solo cerchietto rosso (come questo  ad esempio) da mettere in un sito web. La seconda immagine (il pallino rosso) sarà sovrapposta allo sfondo in base allo stato del sensore e, se copre un pallino blu disegnato nello sfondo, si ottiene il gioco di visualizzazione blu-rosso a secondo dello stato del sensore. Il gioco è possibile ottenerlo anche sovrapponendo alternativamente pallini blu e rossi allo sfondo sebbene così a parità di effetto richieda più risorse (maggiori righe di programma).

Particolarità

Per l’immagine grafica è possibile fare una griglia con il programma gratuito Photofiltre (dal menu cliccare filter, other, grid generator e fare una griglia di lato 10). Disegnate la vostra mappa e colorare in rosso i quadrati della griglia dove sopra volete inserirci il pallino del sensore. Una volta fatta occorre aggiornare le coordinate (esempio: left:110px; top:420px) dei pallini sullo sketch Webserver in modo che questi si collochino sopra al quadratino riempito di rosso. Una volta determinate le coordinate di tutti i pallini si può cambiare l’immagine di sfondo con una a vostro piacere.

Non mettete la pianta esatta della casa, ma una immagine diversa che vi serva a voi per riferimento ma che rimanga incomprensibie agli altri (es. una foto di un gatto il cui pallino sopra l’occhio rappresenta il cancello aperto).

Si è scelto Ajax perche il refresh automatico di una pagina html avrebbe comportato un vistoso e fastidioso sfarfallio.

Le stringhe del client.ethernet sono memorizzate a livello di programma tramite la macro F(). Se il numero di sensori da visualizzare è elevato e non si usa tale macro, il programma si bloccava dopo pochi minuti.

L’indirizzo ip del web server cambia continuamente se nella ethernet shield è presente una schedina di memoria.

Le immagine sono memorizzate su un sito esterno gratutio, nel mio caso sui server Altervista.

Se con il vostro provider telefonico non avete un contratto particolare, il vostro indirizzo ip in internet non è fisso e cambia ogni tanto. Per ovviare, potete usare la funzionalità DDNS del vostro router in congiunzione con uno dei tanti servizi DDNS gratuiti in internet. Nel mio caso ho utilizzato dlinkdns.com. Una volta registrati ed aver configurato propiamente il router, questi siti vi permettono di raggiungere l’indirizzo variabile del vostro sistema tramite un url fisso.

Se trovate utile il programma e lo utilizzerete, vi chiedo la cortesia di inviarmi una email/cartolina (anche anonima) per dirmi che lo avete fatto con successo.