Arduino – Accendiamo un led da remoto

Ormai siamo sempre più connessi con il mondo che ci circonda e con lo smartphone siamo in grado di controllare qualsiasi oggetto in casa, dalle luci agli elettrodomestici fino ad arrivare all’auto che usiamo per spostarci.
Oggi in quest’articolo voglio mostrare come sia possibile comunicare con Arduino da remoto attraverso una pagina web raggiungibile da qualsiasi parte nel mondo, ovviamente dobbiamo essere muniti di una connessione ad internet cosa che oggigiorno è sempre più presente.

Il progetto di cui oggi vi parlo è molto semplice, realizzeremo una piccola pagina web che ci permetterà di accendere e spegnere un led da remoto. Nonostante il progetto sembra non avere grande utilità al suo interno troveremo le informazioni base per comunicare con Arduino e quindi realizzare qualsiasi applicazione domotica ci verrà in mente.

Cosa ci occorre:

  • Arduino Uno o simili
  • Arduino Ethernet Shield
  • Servizio di hosting dove caricare la pagina web (es: http://it.altervista.org/)

Pagina Web

La pagina che andremo a realizzare è la seguente

ArduinoLedController
Pagina Web – Arduino Led Controller

Partiamo subito con il vedere tutta la struttura del codice e poi ci soffermeremo ad analizzare le parti più importanti.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<title>Arduino Led Controller</title>
<script>
$(document).ready(function() {
	$("#action").submit(function(evt) {
		evt.preventDefault();
		var dataString = $(this).find("button[type=submit]:focus" ).val();
		$.ajax({
               url:"Address IP of Arduino!",
               type:"get",
      		   data: {'status':dataString},
      		   success:function(response) {
          		 		if (response==1)
				 			$("#lbl").html("LED ON").css("color","green");
          		 		else 
          		 			$("#lbl").html("LED OFF").css("color","red");
      		   },
      		   error:function(data) {
          		 		alert("Error");
      		   }
		});
	});
});
</script>
</head>
<body>
	<div class="container">
		<div class="jumbotron" style="text-align: center;">
			<h1>Arduino Led Controller</h1>
		</div>
	</div>
	<div style="text-align: center;">
	<label id="lbl"style="font-size:20px;"></label>
	<form id='action'><br>
		<p class="btn-group btn-group-lg" role="group">
			<!-- create a button group -->
			<button type="submit" id="status_led" class="btn btn-primary"
				 value="1">ON</button>
			<button type="submit" id="status_led" class="btn btn-primary"
				 value="0">OFF</button>	
		</p>
	</form>
	</div>
</body>
</html>

Notiamo subito che la pagina è costituita semplicemente da due pulsanti ON e OFF che servono appunto per accendere e spegnere il led su Arduino.

<button type="submit" id="status_led" class="btn btn-primary" value="1">ON</button>
<button type="submit" id="status_led" class="btn btn-primary" value="0">OFF</button>

Questi pulsanti o meglio button sono di tipo submit infatti sono inseriti all’interno di una form identificata da id = “action”. Entrambi i button hanno id=”status_led” con valori però differenti per ON e per OFF.

L’invio dei dati della form spetta allo script javasript, posto nel head del codice HTML, che si occupa di prelevare il valore del button premuto ed effettuare una chiamata ajax sfruttando la libreria jQuery.

Quando si fa click su un pulsante viene invocata la funzione di submit che come prima cosa esegue il comando

evt.preventDefault();

il quale inibisce il normale funzionamento della form permettendo così allo script di decidere come comportarsi, in questo caso si ricava subito il valore del pulsante premuto salvandolo nella variabile dataString

var dataString = $(this).find("button[type=submit]:focus" ).val();

successivamente si effettua la chiamata ajax che grazie alla libreria jQuery risulta essere molto più veloce e, soprattutto, molto più semplice.

$.ajax({
        url:"Address IP of Arduino!",
        type:"get",
        data: {'status':dataString},
        success:function(response) {
          	  if (response==1)
          	      $("#lbl").html("LED ON").css("color","green");
          	  else 
          	      $("#lbl").html("LED OFF").css("color","red");
        },
        error:function(data) {
          	  alert("Error");
        }
});

All’interno della chiamata ajax possiamo notare diversi parametri:

  • url: (default: URL della pagina corrente) URL della risorsa alla quale viene inviata la richiesta; se omessa verrà contattata la pagina corrente.
  • type: (default: GET) E’ utilizzato per specificare il tipo di richiesta da effettuare, principalmente POST o GET; sono utilizzabili anche altri metodi HTTP (come ad es. PUT, DELETE, …) ma non tutti i browser li supportano.
  • data: Contiene i dati che devono essere inviati alla risorsa che elabora la richiesta; il formato può essere sotto forma di oggetto (contenente delle coppie chiave/valore) oppure sotto forma di semplice stringa &key1=val1&key2=val2.
  • success: Consente di specificare una funzione che verrà eseguita al successo della chiamata.
  • error: Consente di specificare una funzione che verrà eseguita in caso di errore nell’effettuare la chiamata.

Per il parametro data  è stato utilizzato il formato JSON costituito dalla chiave status e il valore del button premuto, mentre nel parametro success è stato inserito un controllo basato sulla risposta ricevuta da Arduino, se response==1 allora viene stampato LED ON nel caso sia verrà stampato LED OFF, tutto questo in poche righe di codice grazie a jQuery.

success:function(response) {
	if (response==1)
		$("#lbl").html("LED ON").css("color","green");
	else 
		$("#lbl").html("LED OFF").css("color","red");
}

Sketch Arduino

Ora passiamo al codice da caricare su Arduino necessario per leggere la richiesta fatta dalla pagina web e inviare la relativa risposta.

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

// Create an array of bytes to specify the mac address
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// Create an array of bytes to specify the IP address
IPAddress ip(192, 168, 1, 100);
// Creates a server object that listens on port specified
EthernetServer server(80);
// Create a client object
EthernetClient client;
// dataRX contains the request HTTP of the client
String dataRX = "";
// checkString check if dataRX contains the word "status"
int checkString = 0;
// statusLed contains the status of the led
// received from the client (0 or 1)
int statusLed = 0;

// readRequest check if there are data be read
bool readRequest(EthernetClient& client) {
  dataRX = "";
  // An HTTP request ends with a blank line
  bool currentLineIsBlank = true;
  // Check continuously that the client is connected
  while (client.connected()) {
    // Check if there are bytes available for reading
    if (client.available()) {
      char c = client.read();
      dataRX += c;
      // If the sending of the HTTP request is completed,
      // then the server sends the response
      if (c == '\n' && currentLineIsBlank) {
        Serial.print(dataRX);
        return true;
      } else if (c == '\n') {
        currentLineIsBlank = true;     // new row
      } else if (c != '\r') {
        currentLineIsBlank = false;    // character of new line
      }
    }
  }
  return false;
}

void writeResponse() {
  // Header of the response HTTP
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text / html");
  client.println("Access-Control-Allow-Origin: *");
  client.println("Connection: close");
  client.println();
  checkString = dataRX.indexOf("status");
  if (checkString >= 0) {
    statusLed = dataRX.charAt(checkString + 7);
    if (statusLed == 49) {
      digitalWrite(9, HIGH);
      client.println("1");
    }
    else if (statusLed == 48) {
      digitalWrite(9, LOW);
      client.println("0");
    }
  }
}

void setup() {
  Ethernet.begin(mac, ip);  // Initialize the shield with the mac and ip
  server.begin();           // Initialize the server object
  Serial.begin(9600);       // Initialize serial communications with the PC
  pinMode(9, OUTPUT);       // Define pin 9 as OUTPUT
  digitalWrite(9, LOW);     // Initialize the digital pin 9 LOW
}

void loop() {
  // Gets a client that is connected to the
  // server and has data available for reading
  client = server.available();
  if (client) {
    bool success = readRequest(client);
    if (success) {
      writeResponse();
    }
    // Wait 1 ms so that the response arrivals the browser of the client
    delay(1);
    // Close connection
    client.stop();
  }
}

Anche se il codice è veramente molto commentato voglio soffermarmi principalmente sulla funzione readRequest(EthernetClient& client) che si occupa di leggere la richiesta inviata dal client, ossia dalla pagina web descritta nel paragrafo precedente.

Per meglio comprendere il comportamento della funzione dobbiamo prima vedere come è fatta una richiesta HTTP. 

GET /default.htm HTTP/1.1\r\n
Host: rhynoruv\r\n
Accept: */*\r\n
\r\n

Qui sopra è mostrato un esempio di richiesta HTTP di tipo  GET e subito balza agli occhi la presenza dei caratteri \r e \n alla fine di ogni riga ma soprattutto alla fine della richiesta.
Quindi per leggerla correttamente tutta bisogna lavorare proprio su questi caratteri, e la variabile bool currentLineIsBlank  ci aiuta proprio in questo, infatti sarà true se la riga corrente è vuota mentre false se è presente un carattere diverso da \rAlla fine della richiesta ci troveremo nella situazione in cui l’ultimo carattere letto sarà \n currentLineIsBlank sarà vero per cui la funzione ritornerà true .

Per concludere parliamo della funzione writeResponse() preposta all’invio della risposta alla pagina web che ne ha fatto richiesta. Si parte subito con l’invio dell’header composto dalle seguenti righe:

HTTP/1.1 200 OK
Content-Type: text / html
Access-Control-Allow-Origin: *
Connection: close

e si conclude con una riga che cambia in funzione della richiesta effettuata dalla pagina web, ossia se abbiamo premuto sul pulsante ON (value=1), il carattere ricevuto sarà 49 per cui accenderemo il led e invieremo la stringa 1, in caso di carattere 48, significativo del fatto che abbiamo premuto OFF (value=0), allora spegneremo il led e invieremo la stringa 0.

checkString = dataRX.indexOf("status");
if (checkString >= 0) {
  statusLed = dataRX.charAt(checkString + 7);
  if (statusLed == 49) {
    digitalWrite(9, HIGH);
    client.println("1");
  }
  else if (statusLed == 48) {
    digitalWrite(9, LOW);
    client.println("0");
  }
}

Da notare nell’header la presenza della riga Access-Control-Allow-Origin: * fondamentale per poter ricevere lato client, ossia la pagina web che ha fatto la richiesta, la risposta di Arduino.
L’utilizzo di questa riga è necessario perché viene fatta una richiesta cross-origin HTTP, in quanto la pagina web e Arduino si trovano su 2 domini differenti e visto che per motivi di sicurezza, i browser limitano le richieste HTTP cross-origine avviate dagli script, con questa intestazione si fa in modo che la risorsa (Arduino) sia accessibile da qualsiasi dominio in modo cross-site.
La notazione usata in questo codice è quella più semplice perché permette l’accesso alla risorsa da qualsiasi dominio se invece si vuole limitare l’accesso solo ad uno in particolare si deve sostituire l’asterisco (*) con il nome di tale dominio.

 Anche questo articolo è terminato spero possa tornare utile a chi sta cercando di trovare un modo per controllare Arduino da remoto, qui sotto trovate il link per scaricare il progetto.

Download
Download