Come realizzare un server API REST con la ESP32

Introduzione

In questo articolo vedremo come realizzare un server per API REST utilizzando una NodeMCU ESP32. Abbiamo già visto un esempio di cosa siano le API REST e come utilizzarle nell’articolo Come realizzare un datalogger con la NodeMCU ESP32 collegata ad un cloud Django su Raspberry. In pratica, le API REST sono un modo per far comunicare dispositivi diversi fra loro oppure per consentirci di interagirci.

Con questo tutorial proveremo a creare un server web con la ESP32 in modo che questa esponga un set di API REST. Possiamo utilizzare queste API REST per interagire con la ESP32 in modo da ricevere o mandare dei dati.

Concentreremo la nostra attenzione sullo sviluppo di API REST che utilizzano i metodi GET e POST. In particolare, il metodo POST invierà i dati alla ESP32 tramite un file Json. Implementeremo due diverse API:

  • una GET che ci restituirà i valori di interesse settati in alcune variabili
  • una POST che ci consentirà di mandare verso la ESP32 dei valori che verranno memorizzati in opportune variabili e modificheranno il funzionamento del dispositivo

Per avere un esempio pratico delle potenzialità di questo tipo di comunicazione con la ESP32, useremo un sensore DHT22 per misurare la temperatura e l’umidità ambientali e due diodi LED rossi che comanderemo in due modi diversi:

  • un LED potrà essere solo acceso o spento
  • l’altro LED potrà essere regolato in luminosità da un valore minimo (LED spento) ad un valore massimo (LED acceso alla massima luminosità) passando per i valori intermedi

Con l’API GET potremo leggere i valori di temperatura ed umidità rilevati dal DHT22 e le variabili che controllano lo stato dei due LED (una booleana che comanda il led che può essere solo acceso o spento e l’altra intera per comandare la luminosità dell’altro LED).

L’API di tipo GET avrà questo aspetto:

http://IP_ESP32/getValues

mentre l’API di tipo POST ha questo aspetto:

http://IP_ESP32/setStatus

ed invierà alla nostra ESP32 un file Json fatto così:

{
	"led1Status": "34",
	"led2Status":  false
}

dove IP_ESP32 è l’indirizzo IP assegnato alla board dal router, led1Status rappresenta il valore di luminosità da regolare per uno dei due LED mentre led2Status serve ad accendere/spegnere l’altro LED.

Per interagire con la ESP32 tramite le API REST useremo un programma che si chiama Postman, di cui vedremo l’uso più avanti.

Anche per questo tutorial realizzeremo il progetto utilizzando l’IDE Platformio.

Di quali componenti abbiamo bisogno?

La lista dei componenti non è particolarmente lunga:

Realizzazione del progetto

Lo schema elettrico

Prima di realizzare il circuito vero e proprio diamo un’occhiata al pinout della board:

Pinout della ESP32
Pinout della ESP32

Useremo il GPIO 14 per collegare il sensore DHT22.

Il pinout del sensore DHT22 è il seguente:

Il sensore DHT22 e la sua piedinatura
Il sensore DHT22 e la sua piedinatura

Per collegare i LED useremo invece i GPIO 16 e 17.

Il LED collegato al pin 17 è quello che può essere solo acceso o spento. Il GPIO17 verrà messo allo stato HIGH per accendere il LED e allo stato LOW per spegnerlo.

Il LED collegato al pin 16 è quello la cui luminosità può essere regolata. Per ottenere questo risultato verrà pilotato dall’ESP32 tramite un segnale PWM il cui duty cycle varia al variare del valore contenuto in una variabile apposita, valore che va da 0 (LED spento) a 255 (LED completamente acceso) passando per i valori intermedi.

I comandi per gestire i due LED verranno mandati all’ESP32 tramite l’API REST di tipo POST.

A questo punto puoi procedere alla realizzazione del circuito seguendo lo schema di collegamento più sotto. Purtroppo la NodeMCU ESP32 è troppo larga per stare sulla breadboard, motivo per cui sarà collegata con dei fili volanti al resto del circuito.

I due LED sono collegati alla ESP32 tramite due resistori da 100Ω per limitare la corrente che li attraversa ed evitare di bruciarli (e di bruciare le due uscite digitali).

Il LED ha due terminali (chiamati anodo e catodo) e, come tutti i diodi, è un componente che ha una sua polarità: fa passare la corrente quando è polarizzato direttamente (cioè la tensione all’anodo è maggiore di quella al catodo) e blocca la corrente quando è polarizzato inversamente (cioè la tensione all’anodo è minore di quella al catodo). La tensione tra anodo e catodo, che indicheremo con Vd, varia a seconda del colore della luce emessa. In particolare abbiamo che:

  • Vd = 1.8 V per il LED rosso
  • Vd = 1.9 V per il LED giallo
  • Vd = 2 V per il LED verde
  • Vd = 2 V per il LED arancio
  • Vd = 3 V per il LED blu
  • Vd = 3 V per il LED bianco

Di seguito lo schema di montaggio realizzato con Fritzing:

Schema di collegamento
Schema di collegamento

Come puoi vedere, l’alimentazione del sensore è presa dall’uscita 3.3V del NodeMCU (pin 3V3). E’ necessario alimentarlo con 3.3V in modo che anche la sua uscita sia 3.3V in quanto i pin digitali del NodeMCU non accettano tensioni superiori a 3.3V.

ATTENZIONE: nel NodeMCU ESP32 la tensione massima tollerata dagli ingressi digitali è pari a 3.3V. Qualsiasi tensione superiore lo danneggerebbe irreparabilmente!!

Come facciamo ad identificare l’anodo e il catodo del LED? Lo facciamo osservando i suoi terminali. Il più lungo corrisponde all’anodo. Inoltre il corpo del LED presenta un appiattimento in un punto del bordo che indica che il terminale vicino è il catodo.

Quindi, se un LED non si accende è possibile che sia stato collegato al contrario. In questo caso, per farlo funzionare, è sufficiente invertirne i collegamenti.

Come si calcola la resistenza da collegare al LED?

Nota Bene: questo paragrafo tratta il calcolo della resistenza di limitazione in maniera teorica e richiede un minimo di conoscenza delle basi dell’Elettrotecnica. Pertanto non è fondamentale per la comprensione del resto del progetto e può essere saltato dal lettore non interessato a tali aspetti teorici.

Come abbiamo già detto, il resistore tra il generico GPIO e il LED serve a limitare la corrente che attraversa il LED. Ma come possiamo calcolare il suo valore di resistenza? Ci viene in soccorso la Legge di Ohm la quale dice che la differenza di potenziale ai capi di un resistore (cioè la tensione misurata agli estremi del resistore) è proporzionale alla corrente I che lo attraversa e la costante di proporzionalità è proprio il valore di resistenza del resistore R:

V2 - V1 = RI

Nota Bene: per amor di precisione bisogna puntualizzare che mentre il resistore è il componente fisico (l’oggetto vero e proprio), la resistenza è il suo valore. Quindi è improprio (anche se accade di frequente) chiamare il resistore col termine resistenza.

Possiamo vedere la Legge di Ohm su un semplice circuito costituito da un generatore di tensione (il cerchio a sinistra) e un resistore:

Rappresentazione della Legge di Ohm
Rappresentazione della Legge di Ohm

La tensione (o differenza di potenziale) V2 – V1 impressa dal generatore di tensione sul resistore è uguale al prodotto di R per I.

Vediamo ora uno schema leggermente più complesso dove sono presenti il solito generatore di tensione, il resistore e un LED rosso:

Circuito per il calcolo del resistore di limitazione della corrente sul LED
Circuito per il calcolo del resistore di limitazione della corrente sul LED

Nel nostro caso la Vg rappresenta la tensione presente all’uscita digitale della ESP32 quando è HIGH ed è pari quindi a 3.3V.

La Vd è la tensione ai capi del diodo (tra anodo e catodo) quando questo è polarizzato direttamente (cioè quando fa scorrere la corrente). Avendo scelto un LED rosso, sappiamo, dalla tabella precedente, che Vd = 1.8V.

Dobbiamo determinare il valore R del resistore. Abbiamo ancora una incognita: il valore della corrente I che deve scorrere nel circuito quando il pin è in stato HIGH.

Nota Bene: quando il pin digitale è nello stato LOW la sua tensione (cioè la Vg) è nulla, ne consegue che anche la corrente I nel circuito è nulla.

I LED in genere non sopportano correnti maggiori di 20mA, quindi imponiamo una corrente massima di 15mA per stare sul sicuro.

Per la Legge di Kirchhoff alle maglie (detta anche Legge di Kirchhoff delle tensioni) , abbiamo che:

Vg - Vr - Vd = 0

Da cui ricaviamo che:

Vr = Vg - Vd 

Passando ai valori reali, abbiamo che:

Vr = 3.3V - 1.8V

Ne risulta che:

Vr = 1.5V

Ma, per la Legge di Ohm, abbiamo che:

Vr = RI

da cui:

R = Vr / I

Sostituendo i valori reali:

R = 1.5V / 0.015A

Ne deriva un valore di R pari a 100Ω.

Lo sketch

Creiamo il progetto PlatformIO

Abbiamo già visto la procedura di creazione di un progetto PlatformIO nell’articolo Come creare un progetto per NodeMCU ESP8266 con PlatformIO.

Anche se si riferisce alla board ESP8266, la procedura è simile.
Semplicemente, nella scelta della piattaforma, dovrai scegliere la AZ-Delivery ESP-32 Dev Kit C V4.

Delle librerie indicate installa, seguendo la procedura, la libreria DHT sensor library for ESPx by Bernd Giesecke (che viene usata per leggere i dati trasmessi dal sensore di temperatura e umidità DHT22) e lascia perdere le altre due (WiFiManager e UniversalTelegramBot).

La libreria WiFiManager la installeremo in altro modo più tardi.

Installa ora la libreria ArduinoJson by Benoit Blanchon:

Installa la libreria ArduinoJson sul progetto PlatformIO
Installa la libreria ArduinoJson sul progetto PlatformIO

Ci rimane da installare la libreria WiFiManager che avevamo lasciato in sospeso. La installeremo editando il file platformio.ini e aggiungendo la voce https://github.com/tzapu/WiFiManager.git alla sezione lib_deps. Il file avrà quindi un aspetto del genere:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = 
	bblanchon/ArduinoJson@^6.21.0
	https://github.com/tzapu/WiFiManager.git
	beegee-tokyo/DHT sensor library for ESPx@^1.18

Nota la libreria WiFiManager aggiunta direttamente in questo file.

Ovviamente puoi scaricare il progetto dal link seguente:

Sostituisci il file main.cpp del progetto che hai creato con quello presente nel file zip.

Vediamo ora come funziona lo sketch.

Lo sketch inizia con l’inclusione delle librerie necessarie:

#include <Arduino.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include "DHTesp.h"
#include <WiFiManager.h>

Viene poi creato l’oggetto dht che serve a leggere il sensore e definito il GPIO a cui collegarlo:

DHTesp dht;
#define  DHT22_PIN 14   

Si definisce poi il webserver che rimane in ascolto sulla porta 80:

WebServer server(80);

e vengono definite le variabili del progetto:

float temperature;
float humidity;
int led1Status = 3;
bool led2Status = false;

Vengono poi definiti il pin in cui collegare il LED controllato col PWM e i parametri di funzionamento del segnale PWM:

// set the pin for the PWM LED
const int ledPinPWM = 16;  // 16 corresponds to GPIO16

// set the PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;

Poi si definisce il pin per il LED che può essere solo acceso o spento:

const int ledPinBool = 17;  // 17 corresponds to GPIO17

Le variabili:

unsigned long measureDelay = 3000;                //    NOT LESS THAN 2000!!!!!   
unsigned long lastTimeRan;

temporizzano le letture da parte del DHT22 (in questo caso la lettura avviene ogni 3 secondi). È consigliabile non scendere sotto questo valore in quanto il DHT22 impiega circa 2 secondi per fare una lettura.

Successivamente viene creato il documento Json:

StaticJsonDocument<1024> jsonDocument;
char buffer[1024];

La funzione handlePost gestisce l’API di tipo POST ed estrae dal Json ricevuto dalla POST i valori led1Status e led2Status e li salva nelle rispettive variabili:

void handlePost() {
  if (server.hasArg("plain") == false) {
    //handle error here
  }
  String body = server.arg("plain");
  deserializeJson(jsonDocument, body);
  
  // Get RGB components
  led1Status = jsonDocument["led1Status"];
  led2Status = jsonDocument["led2Status"];

  // Respond to the client
  server.send(200, "application/json", "{}");
}

Le funzioni createJson, addJsonObject e getValues creano il documento Json che viene inviato dalla ESP32 in risposta all’API GET inserendoci i valori presenti nelle variabili temperature, humidity, led1Status e led2Status:

void createJson(char *name, float value, char *unit) {  
  jsonDocument.clear();
  jsonDocument["name"] = name;
  jsonDocument["value"] = value;
  jsonDocument["unit"] = unit;
  serializeJson(jsonDocument, buffer);  
}
 
void addJsonObject(char *name, float value, char *unit) {
  JsonObject obj = jsonDocument.createNestedObject();
  obj["name"] = name;
  obj["value"] = value;
  obj["unit"] = unit; 
}

void getValues() {
  Serial.println("Get all values");
  jsonDocument.clear(); // Clear json buffer
  addJsonObject("temperature", temperature, "°C");
  addJsonObject("humidity", humidity, "%");
  addJsonObject("led1Status", led1Status, "%");
  addJsonObject("led2Status", led2Status, "boolean");

  serializeJson(jsonDocument, buffer);
  server.send(200, "application/json", buffer);
}

La funzione setupApi esegue il routing delle API getValues e setStatus associandole alle funzioni getValues e handlePost:

void setupApi() {
  server.on("/getValues", getValues);
  server.on("/setStatus", HTTP_POST, handlePost);
 
  // start server
  server.begin();
}

A questo punto inizia la funzione setup:

Viene attivata la porta seriale:

Serial.begin(115200);
  // This delay gives the chance to wait for a Serial Monitor     without blocking if none is found
delay(1500); 

A seguire, si trova la parte che gestisce la connessione WiFi:

  WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
  // it is a good practice to make sure your code sets wifi mode how you want it.



  //WiFiManager, Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wm;

  // reset settings - wipe stored credentials for testing
  // these are stored by the esp library
  // wm.resetSettings();

  // Automatically connect using saved credentials,
  // if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
  // if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
  // then goes into a blocking loop awaiting configuration and will return success result

  bool res;
  // res = wm.autoConnect(); // auto generated AP name from chipid
  // res = wm.autoConnect("AutoConnectAP"); // anonymous ap
  res = wm.autoConnect("AutoConnectAP","password"); // password protected ap

  if(!res) {
      Serial.println("Failed to connect");
      ESP.restart();
  } 
  else {
      //if you get here you have connected to the WiFi    
      Serial.println("Connected...yeey :)");
  }

e viene definito il collegamento del DHT22 alla ESP32 tramite il GPIO14:

dht.setup(DHT22_PIN, DHTesp::DHT22); // Connect DHT sensor to GPIO 14 

Viene chiamata la funzione setupApi che esegue il routing delle API:

setupApi();

Infine vengono configurati i canali per i due LED:

// configure LED PWM functionalitites
ledcSetup(ledChannel, freq, resolution);
  
// attach the channel to the GPIO to be controlled
ledcAttachPin(ledPinPWM, ledChannel);

pinMode(ledPinBool, OUTPUT);

Subito dopo inizia la funzione loop.

Inizialmente viene attivato il server:

server.handleClient();

Poi inizia un blocco if che contiene alcune funzioni al suo interno.

La condizione all’interno dell’if determina la cadenza temporale con cui vengono fatte le misurazioni ed inviati i dati:

if (millis() > lastTimeRan + measureDelay)  {

Quindi gli eventi descritti avvengono ogni measureDelay ms grazie a questa condizione.

Proseguendo nell’if, vediamo che vengono effettuate le misurazioni delle grandezze fisiche di nostro interesse e viene aggiornata la variabile lastTimeRan col valore corrente in ms:

humidity = dht.getHumidity();
temperature = dht.getTemperature();

lastTimeRan = millis();

Subito dopo vengono settati gli stati dei due LED coerentemente con i valori contenuti nelle variabili led1Status e led2Status, settate tramite l’API POST:

ledcWrite(ledChannel, led1Status);

if(led2Status) {
  digitalWrite(ledPinBool, HIGH);
} else {
  digitalWrite(ledPinBool, LOW);
} 

A questo punto non ti resta che caricare lo sketch sulla board e procedere con la sua connessione alla rete WiFi.

Come connettere la board ad Internet

Dopo aver caricato lo sketch sulla board, apri il Serial Monitor per vedere i messaggi provenienti dal dispositivo.

Per prima cosa la board va in modalità Access Point e ci fornirà un indirizzo IP che useremo a breve. Questa operazione serve per connettere la board ad Internet senza dover inserire nel codice i parametri della rete WiFi (SSID e password).

La board ci fornisce il suo indirizzo IP
La board ci fornisce il suo indirizzo IP

In questo caso l’indirizzo IP è 192.168.4.1.

A questo punto la ESP32 è in modalità Access Point (con SSID AutoConnectAP) e dobbiamo connettere il nostro computer alla rete AutoConnectAP. Se andiamo nel menu delle reti del nostro computer, dovremmo vedere anche la rete AutoConnectAP nell’elenco delle reti wireless.

Lista delle reti WiFi disponibili
Lista delle reti WiFi disponibili

Connetti il computer alla rete AutoConnectAP. Quindi vai sul tuo browser e inserisci l’IP precedentemente fornito dalla ESP32 (che in questo esempio è 192.168.4.1)

Vedrai una schermata come questa:

La schermata del browser per scegliere la rete
La schermata del browser per scegliere la rete

Clicca il bottone ConfigureWiFi. Ti mostrerà le reti disponibili:

Lista delle reti disponibili
Lista delle reti disponibili

Scegli la SSID della tua rete:

Scegli la tua rete
Scegli la tua rete

Inserisci la password della tua rete e clicca il bottone save:

Inserisci la password
Inserisci la password

La risposta della board
La risposta della board

Il modulo ESP32 conserva memorizzati i parametri di accesso anche se lo spegni, li ricorderà al riavvio e si ricollegherà automaticamente senza dover ripetere questa procedura. Solo se lo resetti rimuovendo il commento da questa riga

// wm.resetSettings();

perderà i parametri di connessione.

Nota Bene: il dispositivo può memorizzare solo una rete. Se successivamente lo colleghi a un’altra rete, dimenticherà le impostazioni della rete precedente.

Testiamo il progetto sulle API REST

Una volta che la ESP32 è stata connessa alla rete WiFi ci fornirà tramite il Serial Monitor di PlatformIO il suo indirizzo IP, come visibile nella figura seguente:

Ricaviamo l'IP della board
Ricaviamo l’IP della board

In questo caso l’IP assegnato dal router alla board è 192.168.1.9. Tale IP ci servirà per comporre le API REST.

Per interagire con la board abbiamo bisogno di un software apposito che si chiama Postman. Dopo aver installato il programma, siamo pronti ad usarlo.

Ecco come si presenta la sua schermata iniziale:

Schermata iniziale di Postman
Schermata iniziale di Postman

Nella finestra principale si trova una barra in cui dovrai inserire l’API.

Alla sinistra di questa barra c’è un menù a tendina che consente di scegliere il tipo di API (per esempio GET, POST, PUT…).

Scegli ora il tipo GET e inserisci l’API GET che avrà formato:

http://IP_ESP32/getValues

Per esempio, in questo caso, essendo l’IP assegnato 192.168.1.9, l’URL dell’API sarà:

http://192.168.1.9/getValues

Ovviamente tu dovrai mettere l’indirizzo IP assegnato alla tua ESP32.

Premi il tasto Send sulla destra.

L’API restituirà un file Json che riporta i valori rilevati dal DHT22 e lo stato dei due LED, come mostrato nella immagine seguente:

Risultato dell'API GET
Risultato dell’API GET

Proviamo ora l’API POST per inviare i dati alla ESP32.

Seleziona, nel menù a tendina, il tipo POST.

Inserisci nella barra l’URL:

http://IP_ESP32/setStatus

che in questo particolare caso diventa:

http://192.168.1.9/setStatus

Ovviamente tu dovrai mettere l’indirizzo IP assegnato alla tua ESP32.

Prima di premere il tasto Send, dovrai selezionare la voce Body che sta sotto la barra dell’URL. Poi seleziona la voce raw (sotto Body) e poi, sul menù a tendina sulla destra, seleziona la voce JSON al posto di Text, come mostrato in foto:

Selezione dei parametri per l'API di tipo POST
Selezione dei parametri per l’API di tipo POST

Nella finestra sottostante inserisci il Json:

{
	"led1Status": "20",
	"led2Status":  false
}

Ecco come dovrebbe apparirti la finestra di Postman:

La finestra di Postman con l'API POST
La finestra di Postman con l’API POST

Modificando il valore della variabile led1Status (da 0 a 255) potrai variare la luminosità del LED corrisondente.

Modificando il valore led2Status (false o true) potrai spegnere o accendere l’altro LED.

Come per la GET, per mandare il comando è necessario premere il tasto Send.

Osservazioni finali

Hai potuto vedere come interagire con la ESP32 tramite le API REST. Per farlo hai usato il programma Postman. Ma si potrebbe interagire anche in altro modo, come per esempio con uno script Python che interroga la board automaticamente sempre tramite API REST. Quindi disporre di un programma che interagisce, dando comandi o ricevendo dati, in maniera del tutto autonoma e indipendente.

Ma tali comandi possono anche essere impartiti da un altro dispositivo come un’altra ESP32, realizzando così una vera e propria comunicazione fra dispositivi (senza intervento manuale da parte nostra) come già fatto nell’articolo Come realizzare un datalogger con la NodeMCU ESP32 collegata ad un cloud Django su Raspberry dove la comunicazione avviene tra una ESP32 e una Raspberry utilizzando sempre un’API REST.

Newsletter

Se vuoi essere aggiornato sui nuovi articoli, iscriviti alla newsletter. Prima dell’iscrizione alla newsletter leggi la pagina Privacy Policy (UE)

Se ti vuoi disiscrivere dalla newsletter clicca sul link che troverai nella mail della newsletter.

Inserisci il tuo nome
Inserisci la tua email
0 0 votes
Valutazione articolo
guest
0 Commenti
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
Torna in alto