IoT, Arduino, REST e Cloud

21 luglio 2014

Nelle lezioni precedenti abbiamo posto le basi per il progetto che realizziamo in questa ultima parte della guida: un semplice sistema di logging della temperatura nella nostra stanza che sia in grado di acquisire periodicamente i dati ed inviandoli nel Cloud. Per farlo ci serviremo di cose già viste: la connessione a Internet tramite Ethernet shield (e relative librerie software) e il driver per il sensore di temperatura.

Partiamo con lo sviluppo dal lato del device sulla “Arduino Uno”, assumendo che il backend fornisca un’interfaccia HTTP REST sulla quale sia possibile eseguire unicamente un’operazione di POST in corrispondenza del path:

/api/temperature/<id_device>

In questo modo, la nostra board avrà un identificativo univoco (immaginando una rete di sensori) ed invierà il valore della temperatura acquisita all’interno del body della propria richiesta in formato JSON e quindi anche come semplice stringa.

Poiché l’applicazione si basa su quanto abbiamo già realizzato nelle lezioni precedenti, relativamente alla connessione ad Internet ed alla realizzazione di un driver per il sensore di temperatura TMP102, focalizziamo la nostra attenzione sul loop principale e sulla funzione che permette di inviare i dati nel Cloud.

void loop() {
  // lettura e trasmissione della temperatura
  float celsius = tmp102.getTemperature();
  Serial.print("Celsius : ");
  Serial.println(celsius);
  
  // converto la temperatura in una stringa
  char data[10];
  dtostrf(celsius, 4, 2, data);
  Serial.println(data);
  
  // assumiamo che il nostro sensore abbia ID = 1
  // (nell'ambito di una potenziale rete di sensori)
  postTmp(server, 80, "/api/temperature/1", data);
  
  delay(60000);
}

In particolare, viene acquisito il valore della temperatura dal sensore e convertito in una stringa attraverso la funzione dtostrf (fornita nella standard library) dopodiché viene invocata la funzione postTmp(server, port, path, data) con i seguenti parametri :

  • L’indirizzo del server al quale connettersi;
  • La porta su cui il servizio è in ascolto (trattandosi di HTTP REST è per default la porta 80);
  • Il path della risorsa (URI) sul qualche eseguiamo la richiesta secondo l’architettura REST;
  • Il valore da trasmettere all’interno del body;

Tale funzione esegue la connessione al server ed in caso di successo invia la richiesta HTTP trasmettendo nell’ordine:

  1. La request line con il “verbo” POST ed il path della risorsa, oltre alla versione del protocollo HTTP;
  2. L’header “Host” con l’indirizzo e la porta del server;
  3. L’header “Connection” con la richiesta di chiudere la connessione al termine del trasferimento;
  4. L’header “Content-Type” specificando che nel body ci saranno dei dati in formato JSON. Va sottolineato che secondo la specifica, anche una semplice stringa è già di per se un oggetto JSON purché sia delimitata dai doppi apici;
  5. L’header “Content-Length” con la quantità di byte contenuti nel body;
  6. Il body contenente il valore della temperatura delimitato da doppi apici in modo da rappresentare una stringa JSON;

Al termine dell’invio della richiesta, il client si mette in attesa della risposta per poi acquisirla ed infine chiudere la connessione con il server. Sarebbe opportuno implementare un parser HTTP per poter interpretare la risposta ricevuta dal server almeno per quanto riguarda lo “status code” contenuto nella response line per poter valutare o meno se la richiesta è stata eseguita con successo. In questa sede, ci interessa esclusivamente trasferire la risposta sul nostro PC attraverso la porta seriale.

void postTmp(char *host, int port, char *path, char *data) {
  
  char httpBuffer[HTTP_BUFFER_SIZE]; 
  
  Serial.println("Connessione in corso...");
  
  if (ethclient.connect(host, port))
  {
    Serial.println("Connessione avvenuta!");
    
    // invio request line
    sprintf(httpBuffer, "POST %s HTTP/1.1", path);
    Serial.println(httpBuffer);
    ethclient.println(httpBuffer);
    
    // invio header "Host"
    sprintf(httpBuffer, "Host: %s:%d", server, port);
    Serial.println(httpBuffer);
    ethclient.println(httpBuffer);
    
    // invio header "Connection"
    Serial.println("Connection: close");
    ethclient.println("Connection: close");
    
    // invio header "Content-Type", JSON per le ASP.NET Web API
    Serial.println("Content-Type: application/json; charset=utf-8");
    ethclient.println("Content-Type: application/json; charset=utf-8");
    
    // invio "Content-Length" (dimensione dati + 2 sommando i
    // doppi apici per il formato JSON delle ASP.NET Web API)
    sprintf(httpBuffer, "Content-Length: %u", strlen(data) + 2);
    Serial.println(httpBuffer);
    ethclient.println(httpBuffer);
    
    // linea vuota tra headers HTTP e Body
    Serial.println();
    ethclient.println();
    
    // invio Body
    sprintf(httpBuffer, "\"%s\"", data);
    Serial.println(httpBuffer);
    ethclient.print(httpBuffer);
    
    delay(1000);
    
    // ci sono byte disponibili
    while (ethclient.available())
    {
      // leggo il prossimo e lo trasmetto sulla seriale
      char c = ethclient.read();
      Serial.print(c);
    }
    
    // se il server ha chiusto la connessione
    if (!ethclient.connected())
    {
      // stop del client
      ethclient.stop();
    }
  }
}

La parte cloud

Possiamo collegare alla nostra board qualunque servizio RESTful e utilizzare diverse tecnologie per lo storage dei dati. Nel nostro caso implementiamo la parte cloud servendoci di un’applicazione ASP.NET MVC 4 Web API, che ci fornisce l’infrastruttura di servizi REST.

Inoltre, tale servizio può essere pubblicato su Windows Azure. Per farlo sarà sufficiente creare un account e la versione trial, gratis per un mese (comprende tutti i servizi con 150€ di spesa inclusa). Dovremo quindi creare un progetto Cloud con Visual Studio ed aggiungere ad esso un template di applicazione ASP.NET MVC 4 Web API.

La prima operazione da fare consiste nel creare un nuovo Controller che possiamo chiamare TemperatureController in modo che il path su cui eseguire le richieste sia del tipo /api/temperature/ secondo la modalità di routing del framework ASP.NET MVC.

Visual Studio : creazione di un nuovo Windows Azure Cloud Service

Visual Studio : aggiungere progetto ASP.NET MVC 4 Web API

Il file autogenerato già mette a disposizione i principali verbi HTTP (GET, POST, PUT e DELETE) mappati su semplicissimi metodi del Controller invocati opportunamente dal framework ASP.NET MVC in base al path ed ai parametri forniti (che insieme costituiscono le cosiddette regole di routing).

Non è previsto il metodo POST invocato con un parametro id che possiamo aggiungere in modo da garantire le richieste sul path /api/temperature/<id_device>

// POST api/temperature/5
public void Post(int id, [FromBody]string value)
{
	if (this.temperatures == null)
	{
		this.temperatures = new SortedDictionary<int, decimal>();
	}

	if (this.temperatures.ContainsKey(id))
		this.temperatures[id] = Convert.ToDecimal(value);
	else
		this.temperatures.Add(id, Convert.ToDecimal(value));
}

In questa guida, non ci interessa realizzare un servizio completamente funzionante ma ci basta avere una semplice collection indicizzata sulla base dell’id del device che contenga al suo interno l’ultimo valore della temperatura acquisita per ciascuno di essi.

Compiliamo la soluzione e pubblichiamo su Windows Azure. Al termine della pubblicazione (una decina di minuti di elaborazione), il nostro servizio sarà disponibile online ed attraverso il management portal di Windows Azure possiamo ricavare il VIP (Virtual IP) assegnato all’istanza in esecuzione. Oltre all’indirizzo IP virtuale possiamo utilizzare il l’URL assegnato al nostro servizio che sarà del tipo <nomeapp>.cloudapp.net; ad esempio arduinowebapi.cloudapp.net.

Windows Azure Management Portal: Public Virtual IP (VIP) Address

Utilizziamo questo indirizzo IP per assegnarlo alla variabile “server” del nostro sketch che a questo punto possiamo compilare ed eseguire sulla nostra board. Avviando il Serial Monitor, vedremo scorrere le informazioni di debug che indicano l’esecuzione della richiesta HTTP verso il server e tutto ciò che costituisce la risposta di quest’ultimo; lo status code sarà tipicamente il 204 che indica un’operazione avvenuta con successo (allo stesso modo del famoso codice 200) ma senza alcun contenuto nel body.

Serial Monitor: invio della temperatura via HTTP REST al servizio Web API

In pochissimi minuti ed con pochissimi passi, abbiamo messo in piedi un servizio RESTful perfettamente funzionante in esecuzione addirittura nel Cloud senza preoccuparci di tutte le problematiche di hosting e di deploy. Siamo quindi riusciti a far raggiungere la nuvola (“azzurra” in questo caso) alla nostra “Arduino Uno” !

Tutte le lezioni

1 ... 7 8 9

 

 

1 ... 7 8 9

Se vuoi aggiornamenti su IoT, Arduino, REST e Cloud inserisci la tua e-mail nel box qui sotto:
Tags:
 
X
Se vuoi aggiornamenti su IoT, Arduino, REST e Cloud

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy