Come realizzare un datalogger con la NodeMCU ESP32 collegata ad un cloud Django su Raspberry

Introduzione

Vedremo in questo articolo come usare una Raspberry e delle API REST per ricevere e visualizzare, tramite una applicazione Django, dei dati rilevati da una NodeMCU ESP32.

Abbiamo già visto in precedenti articoli (Termoigrometro con orologio e display LCD su Arduino UNO, Come realizzare un datalogger con la NodeMCU ESP8266 collegata all’Arduino Cloud, Come fare un datalogger bluetooth per temperatura e umidità con Arduino MEGA) come realizzare dei datalogger per misurare temperatura ed umidità ambientali e visualizzarli in vari modi (display LCD, Arduino Cloud, bluetooth). Questa volta li visualizzaremo su una dashboard funzionante su una Raspberry.

Cosa sono le API REST?

La comunicazione tra la ESP32 e la Raspberry avverrà tramite delle “funzioni” esposte sulla rete WiFi dalla Raspberry. La ESP32 quindi farà periodicamente le misurazioni e le invierà alla Raspberry tramite una API REST. Ma cosa sono queste API REST?

Le API REST sono delle interfacce che consentono alle applicazioni web di comunicare fra loro.
Tramite queste interfacce (basate sulla classica architettura client-server), un client può inviare una certa richiesta ad un server il quale risponde con l’informazione desiderata o con una informazione di stato. Il client può, per esempio, chiedere di leggere dati, di modificarli, crearli o cancellarli.
Per questo motivo abbiamo 4 tipi:

  • GET – per ottenere una informazione specifica
  • POST – per inserire una nuova informazione
  • PUT – per modificare una informazione esistente
  • DELETE – per cancellare una informazione esistente

Il nostro ESP32 userà solo il tipo POST.

Cosa faremo in questo progetto?

Questa volta, oltre alla temperatura e all’umidità misureremo anche la pressione atmosferica e l’intensità luminosa. Queste grandezze fisiche sono rilevate da una NodeMCU ESP32 con appositi sensori (in particolare un DHT22 per temperatura ed umidità, un BMP180 per la pressione atmosferica e un BH1750 per l’intensità luminosa) ed inviate, via WiFi, ad intervalli regolari ad una Raspberry PI che le riceve, come già anticipato, tramite un’API REST. I dati sono inviati dalla ESP32 tramite questa API verso la Raspberry in formato Json con il formato seguente:

{
    "temperature": "56.48",
    "humidity": "19.82",
    "brightness": "93.82",
    "atmosphericPressure": "22.34"
}

L’API, che è di tipo POST, ha invece questo aspetto:

http://IP_RASPBERRY:8000/api/measurement-items/

L’API in questione è parte di una applicazione web realizzata in Django che gira sulla Raspberry. Tale applicazione si occupa di ricevere i dati e di scriverli su un database realizzato con Sqlite. Una seconda applicazione Django, sempre installata sulla Raspberry, si occupa di estrarre i dati dal database e mostrarli su una semplice dashboard interrogabile tramite browser ad una URL di questo tipo:

http://IP_RASPBERRY:8000/view/showall

Le due applicazioni Django sono state testate su una Raspberry Pi 1 Model B e su una Raspberry PI 3 Model B.

La dashboard avrà un aspetto di questo tipo (i dati rappresentati qui sono casuali):

Esempio di dashboard mostrato dalla Raspberry
Esempio di dashboard mostrato dalla Raspberry

In pratica realizzaremo una architettura come quella visualizzata nell’immagine seguente:

Architettura del sistema
Architettura del sistema

Di quali componenti abbiamo bisogno?

La lista dei componenti non è particolarmente lunga:

  • una breadboard per connettere la NodeMCU ESP32 agli altri componenti
  • alcuni fili DuPont (maschio – maschio, maschio – femmina, femmina – femmina)
  • un sensore DHT22
  • un resistore da 4.7kΩ
  • un sensore BMP180
  • un sensore BH1750
  • una SD card di capienza da 8GB a 32GB
  • un eventuale dongle WiFi USB per la Raspberry
  • e, ovviamente, una Raspberry !

La SD card che ho usato per gli esperimenti è da 16GB.

Come già detto il progetto Django è stato testato su una Raspberry Pi 1 Model B e su una Raspberry PI 3 Model B ma non è escluso che funzioni anche su altri modelli di Raspberry.

Realizzazione del progetto

Lo schema elettrico

Prima di realizzare il circuito vero e proprio del datalogger 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

Il pinout del BMP180 è questo:

Pinout del BMP180
Pinout del BMP180

Il pinout del BH1750 è questo:

Pinout del BH1750
Pinout del BH1750

I sensori BMP180 e BH1750 sono collegati ai GPIO 21 (SDA) e 22 (SCL) della ESP32. Questi pin fanno parte di un bus di comunicazione a due fili che si chiama I2C. Questo bus prevede la presenza di un dispositivo master (nel nostro caso la ESP32) che gestisce la comunicazione e uno o più dispositivi slave (che da noi sono i due sensori). I dispositivi slave hanno tutti un proprio indirizzo esadecimale in modo da far capire al master da quale dispositivo proviene un determinato messaggio o per fare in modo che il master mandi un messaggio allo slave giusto. In questo modo si evitano conflitti fra i messaggi, dato che viaggiano sullo stesso bus.

Il sensore BMP180 ha indirizzo 0xEF in lettura e 0xEE in scrittura mentre l’indirizzo del sensore BH1750 può essere settato tramite il pin ADDR. In particolare, avremo che:

  • l’indirizzo è 0x23 se la tensione al pin ADDR è inferiore a 0.7 Vcc
  • l’indirizzo è 0x5C se la tensione al pin ADDR è maggiore di 0.7V

Fortunatamente gli indirizzi dei due dispositivi non coincidono quindi non avremo collisioni dei messaggi.

In generale, nel caso due o più dispositivi abbiano lo stesso indirizzo (non modificabile) non sarà possibile collegarli allo stesso bus I2C.

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

Di seguito lo schema di montaggio realizzato con Fritzing:

Schema di collegamento
Schema di collegamento

Come puoi vedere, l’alimentazione per i tre sensori è presa dall’uscita 3.3V del NodeMCU (pin 3V3). E’ necessario alimentare i sensori con 3.3V in modo che anche la loro 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!!

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 la libreria HttpClient by Adrian McEwen:

Installa la libreria HttpClient sul progetto PlatformIO
Installa la libreria HttpClient sul progetto PlatformIO

Installa ora la libreria ArduinoJson by Benoit Blanchon:

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

Installa ora la libreria Adafruit BMP085 Library by Adafruit:

Installa la libreria BMP085 sul progetto PlatformIO
Installa la libreria BMP085 sul progetto PlatformIO

Infine installa la libreria BH1750 by claws:

Installa la libreria BH1750 sul progetto PlatformIO
Installa la libreria BH1750 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 = 
	beegee-tokyo/DHT sensor library for ESPx@^1.18
	amcewen/HttpClient@^2.2.0
	bblanchon/ArduinoJson@^6.20.1
	https://github.com/tzapu/WiFiManager.git
	adafruit/Adafruit BMP085 Library@^1.2.2
	claws/BH1750@^1.3.0

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 "DHTesp.h"
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <WiFiManager.h>
#include <WiFiClientSecure.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <SPI.h>
#include <BH1750.h>
#include <math.h>

Poi prosegue con la creazione degli oggetti relativi ai sensori di pressione, luminosità, temperatura e umidità. Viene anche definito il GPIO14 come collegamento dati per il DHT22:

Adafruit_BMP085 bmp;  
BH1750 brightnessMeasure;


DHTesp dht;
#define  DHT22_PIN 14   

Successivamente vengono definite le variabili interne:

double temperature;
double humidity;
double brightness;
double atmosphericPressure;

unsigned long measureDelay = 600000;                //    NOT LESS THAN 2000!!!!!    BETTER 600000  (10 MINUTES)
unsigned long lastTimeRan;

Nota che la variabile measureDelay (che definisce ogni quanto tempo devono essere fatte le misurazioni ed inviati i dati) non deve essere inferiore a 2000ms (corrispondenti a 2s) perché il DHT22 impiega circa 2 secondi per fare una misurazione.

A questo punto creiamo il client WiFi e definiamo la struttura dell’API:

WiFiClientSecure client;

//Your Domain name with URL path or IP address with path
const char* serverName = "http://192.168.1.190:8000/api/measurement-items/";

Nota che l’API contiene l’indirizzo IP (192.168.1.190) che verrà in seguito definito per la Raspberry (cioè questo è l’indirizzo IP della nostra Raspberry all’interno della rete). Vedremo in seguito come settare questo IP fisso sulla Raspberry in modo che il router gli assegni sempre lo stesso. Nulla vieta, ovviamente, di scegliere un altro IP (nel caso questo fosse già occupato da un altro dispositivo).

Successivamente viene definita la funzione round2 che serve a troncare i valori provenienti dai sensori alla seconda cifra decimale:

double round2(double value) {
   return (int)(value * 100 + 0.5) / 100.0;
}

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); 

Vengono attivati il bus I2C e il sensore di luminosità:

Wire.begin();           

brightnessMeasure.begin();

Viene poi fatto un controllo sulla presenza del sensore di pressione:

if (!bmp.begin()) {
    Serial.println("Could not find a valid BMP085/BMP180 sensor, check wiring!");      
    while (1) {}
  }

Se il sensore non è presente viene stampato un messaggio di errore e la board si blocca in un loop infinito dato dal while (1). Se invece il sensore è presente e correttamente funzionante, viene inizializzato.

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

  //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 :)");
  }

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

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

Subito dopo inizia la funzione loop.

Inizialmente viene definito ed inizializzato il client HTTP, che ci servirà per inviare i dati:

  HTTPClient http;

  http.begin(serverName);

Poi inizia un blocco if che contiene svariate 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();
brightness = brightnessMeasure.readLightLevel();
atmosphericPressure = bmp.readPressure() / 100.0;             

lastTimeRan = millis();

Subito dopo viene creato il documento Json contenente i dati da inviare alla Raspberry:

http.addHeader("Content-Type", "application/json");

DynamicJsonDocument jsonToSend(1024);
jsonToSend["temperature"] = round2(temperature);            
jsonToSend["humidity"] = round2(humidity);
jsonToSend["brightness"] = round2(brightness);
jsonToSend["atmosphericPressure"] = round2(atmosphericPressure);

String jsonToSendStr;
serializeJson(jsonToSend, jsonToSendStr);

Viene poi inviato il documento Json appena creato alla API col metodo POST e stampato sul Serial Monitor il codice HTTP ricevuto dalla Raspberry (se la trasmissione ha avuto esito corretto il codice è 200):

int httpResponseCode = http.POST(jsonToSendStr);

Serial.println("\nHTTP response code: " + String(httpResponseCode));

Infine viene terminato il client:

http.end();

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.

Preparazione della Raspberry

Per poter installare l’applicazione Django, il cui compito è quello di raccogliere e salvare su database i dati trasmessi dalla ESP32 e mostrarli sulla dashboard, sulla Raspberry è necessario fare alcuni passi preliminari ed installare alcuni software.

Iniziamo subito con l’installazione del sistema operativo.

Il sistema operativo scelto è una distribuzione fatta apposta per funzionare su tutti i tipi di Raspberry, anche le più datate. Come già specificato altrove, i test sono stati fatti su una Raspberry Pi 1 Model B e su una Raspberry PI 3 Model B.

Se la Raspberry non ha connessione Wireless nativa puoi usare un dongle WiFi da inserire in una delle sue prese USB.

Scarichiamo e installiamo il sistema operativo su SD card

Puoi scaricare l’immagine che ho usato io dal link qui sotto:

Altrimenti, se desideri l’ultima versione, vai all’indirizzo https://www.raspberrypi.com/software/operating-systems/

e portati alla sezione Raspberry Pi OS (Legacy). Scaricherai una versione che non ha ambiente grafico in modo che sia la più leggera possibile:

La distribuzione scelta
La distribuzione scelta

Il file scaricato sarà compresso in formato xz. Per decomprimerlo su Linux dovrai prima installare il tool:

sudo dnf install xz           su CentOS/RHEL/Fedora Linux.
sudo apt install xz-utils     su Ubuntu/Debian

e poi dare la riga di comando:

xz -d -v filename.xz

dove filename.xz è il nome del file che hai appena scaricato contenente il sistema operativo.

Su Windows sarà sufficiente utilizzare uno fra questi tool: 7-Zip, winRAR, WinZip.

Il risultato sarà un file con estensione img che è l’immagine da flashare sulla SD card della Raspberry.

Per flashare l’immagine sulla SD card userai il tool Balena Etcher che funziona sia su Linux che su Windows che su MACOS.

Il suo utilizzo è molto semplice: è sufficiente selezionare l’immagine da flashare, la SD card di destinazione e premere il pulsante Flash.

Ecco come appare la sua interfaccia:

L'interfaccia del tool Balena Etcher
L’interfaccia del tool Balena Etcher

A sinistra viene impostata l’immagine da flashare, al centro la SD card da flashare, a destra il pulsante per iniziare l’operazione di flashing.

Alla fine della operazione la SD card conterrà due partizioni: boot e rootfs. Nel gestore dei dispositivi su Linux appare un menu del genere:

Menu dei dispositivi su Linux
Menu dei dispositivi su Linux

Anche Windows mostrerà un menu del genere: dal tuo file explorer, alla voce Questo computer vedrai le 2 partizioni.

Ora, con un editor di testo, crea sul tuo computer un file che chiamerai wpa_supplicant.conf e che editerai in questo modo:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=«your_ISO-3166-1_two-letter_country_code»
network={
     ssid="«your_SSID»"
     psk="«your_PSK»"
     key_mgmt=WPA-PSK
}

Dovrai sostituire le seguenti voci:

  • «your_ISO-3166-1_two-letter_country_code» con l’identificativo del tuo paese (per esempio per l’Italia è IT)
  • «your_SSID» con il nome SSID della tua rete WiFi
  • «your_PSK» con la password della rete WiFi

A questo punto, dovrai creare un file vuoto che chiamerai ssh (senza nessuna estensione).

Le nuove distribuzioni non hanno il classico utente pi con password raspberry quindi, per poter entrare in SSH, dobbiamo provvedere in altro modo.

Con una Raspberry funzionante dobbiamo creare un file di nome userconf che conterrà l’utente che vogliamo creare con una versione criptata della password che gli vogliamo attribuire. Il formato sara quindi username:password-hash.

Supponiamo di voler tenere l’utente pi, dobbiamo creare la password-hash. Supponiamo di voler creare l’hash della password raspberry, sempre nella Raspberry dove abbiamo creato il file userconf. Dobbiamo dare il comando, da shell, seguente:

echo "raspberry" | openssl passwd -6 -stdin

Questo comando restituirà l’hash della password raspberry. Per esempio potrebbe essere una stringa così:

$6$ROOQWZkD7gkLyZRg$GsKVikua2e1Eiz3UNlxy1jsUFec4j9wF.CQt12mta/6ODxYJEB6xuAZzVerM3FU2XQ27.1tp9qJsqqXtXalLY.

Questo è l’hash della password raspberry che ho calcolato sulla mia Raspberry.

Il nostro file userconf quindi conterrà la seguente stringa:

pi:$6$ROOQWZkD7gkLyZRg$GsKVikua2e1Eiz3UNlxy1jsUFec4j9wF.CQt12mta/6ODxYJEB6xuAZzVerM3FU2XQ27.1tp9qJsqqXtXalLY.

NOTA BENE: è necessario calcolare l’hash con una Raspberry perché l’hash calcolato col computer utilizza un altro algoritmo che non consentirebbe alla Raspberry che stiamo preparando di riconoscere la password.

Alternativamente puoi scaricare dal link qua sotto il file userconf che ho creato io per avere utente pi con password raspberry.

Ora apri la partizione boot sulla SD card e copia dentro i tre files wpa_supplicant.conf, ssh e userconf. Rimuovi in sicurezza la SD card dal computer e inseriscila nella Raspberry.

Accendi la Raspberry, aspetta qualche minuto. Per poterti loggare in ssh alla Raspberry, dovrai scoprire quale sia il suo IP (quello che il router le ha assegnato tramite DHCP).

Per fare questo è sufficiente dare il comando da una shell del pc:

ping raspberrypi.local 

valido sia su Linux che su Windows (previa installazione di Putty su Windows).

Nel mio PC la Raspberry risponde così:

Risposta della Raspberry al ping
Risposta della Raspberry al ping

Ciò mi fa capire che l’IP assegnato è 192.168.43.27.

Alternativamente puoi usare il tool Angry IP Scanner oppure puoi accedere alle impostazioni del tuo router per vedere i dispositivi collegati via WiFi e scoprire che IP ha la Raspberry.

Per poter loggarti sulla Raspberry in ssh dai il comando da shell:

con password raspberry. Su Windows è necessario Putty.

Una volta dentro la Raspberry dai i seguenti comandi per fare l’update del software:

sudo apt update
sudo apt upgrade

La password è sempre raspberry.

Configuriamo la timezone

Per configurare la timezone dai il comando:

sudo raspi-config

alla shell della Raspberry. Supponiamo di voler impostare il fuso orario di Roma.

Apparirà una schermata così:

Schermata iniziale del comando sudo raspi-config
Schermata iniziale del comando sudo raspi-config

Seleziona l’opzione sulla localizzazione e dai Ok:

Selezionata l'opzione sulla localizzazione
Selezionata l’opzione sulla localizzazione

Seleziona poi l’opzione sulla timezone e dai Ok:

Selezionata l'opzione della timezone
Selezionata l’opzione della timezone

Seleziona ora l’area geografica e dai Ok:

Selezionata l'area geografica
Selezionata l’area geografica

Infine seleziona la città e dai Ok:

Selezionata la città
Selezionata la città

Ecco fatto!

Riavvia la Raspberry dando il comando:

sudo reboot

e, dopo pochi minuti, rientra in ssh come hai fatto prima.

Dai il comando

date

La Raspberry dovrebbe ora mostrare data e ora corrette.

Impostiamo l’IP statico

Per fare in modo che la Raspberry abbia sempre lo stesso indirizzo IP, dobbiamo impostarlo in modo che sia statico. Nei miei test l’ho impostato a 192.168.1.190. Se non facessimo così, il router le assegnerebbe un IP diverso ad ogni riavvio il che ci costringerebbe ogni volta a cambiare l’indirizzo IP dell’API nello sketch dell’ESP32 e l’URL che dobbiamo usare sul browser per vedere la dashboard.

Procederemo in due passi:

  • imposteremo l’IP fisso nella Raspberry
  • imposteremo il router in modo che riservi quell’indirizzo alla nostra Raspberry

Per il primo punto, dai il comando:

nano /etc/dhcpcd.conf

per aprire il file dhcpcd.conf ed editarlo.

Alla fine del file dovrai aggiungere un blocco del genere:

interface [INTERFACE]
static_routers=[ROUTER IP]
static domain_name_servers=[DNS IP]
static ip_address=[STATIC IP ADDRESS YOU WANT]/24

dove:

  • [INTERFACE] è il nome dell’interfaccia WiFi (nel nostro caso sarà wlan0)
  • [ROUTER IP] è l’indirizzo del nostro router (in genere è qualcosa del tipo 192.168.0.1 oppure 192.168.1.1). Lo puoi trovare entrando nell’interfaccia di amministrazione del tuo modem/router
  • [DNS IP] è l’indirizzo del server DNS, che in genere coincide con il parametro [ROUTER IP] del modem/router
  • [STATIC IP ADDRESS YOU WANT] è l’indirizzo IP che vogliamo assegnare come IP fisso alla Raspberry

Quindi, supposto che [ROUTER IP] = [DNS IP] = 192.168.1.1 e che [STATIC IP ADDRESS YOU WANT] = 192.168.1.190, il blocco avrà un aspetto del genere:

interface wlan0
static_routers=192.168.1.1
static domain_name_servers=192.168.1.1
static ip_address=192.168.1.190/24

Riavvia la Raspberry sempre col comando

sudo reboot

e poi accedi nuovamente in ssh, questa volta con IP 192.168.1.190.

Come secondo passo imposteremo il router in modo che riservi l’indirizzo 192.168.1.190 alla nostra Raspberry. Ogni modem/router è diverso dagli altri ma più o meno si somigliano. Mostrerò qui come appare il mio.

Per entrare digito l’indirizzo 192.168.1.1 (perche il mio modem ha questo IP) sul browser e, dopo aver dato la password di amministratore, arrivo alla schermata principale. Da qui devo cercare la schermata relativa al controllo degli accessi.

Aggiunta di un IP statico per la Raspberry
Aggiunta di un IP statico per la Raspberry

Ci sarà un pulsante di aggiunta di un IP statico: aggiungi l’IP scelto abbinato al MAC address della scheda WiFi della Raspberry. Ti consiglio comunque di consultare il manuale di istruzioni del tuo modem/router per questa operazione.

Verifica ora che la Raspberry si connetta alla rete dando il comando:

ping www.google.com

Se ottieni la risposta al ping la rete è connessa. Se ottieni un messaggio del tipo “Network is unreachable” dai il comando

sudo route add default gw [ROUTER IP]  

dove [ROUTER IP] è il gateway che nel nostro caso è l’IP del router, cioè 192.168.1.1

Installiamo alcuni indispensabili tool e l’ambiente virtuale

Procediamo ora installando alcuni tool indispensabili.

Innanzitutto installerai il programma di gestione del database sqlite digitando il comando:

sudo apt install sqlite3

Questo programma ci servirà in seguito quando useremo lo script getdata.sh.

Poi dobbiamo installare il comando pip3 che ci consente di installare ulteriori pacchetti e librerie per Python (dato che Django è realizzato con questo linguaggio).

Sulla shell di Raspberry dai il comando:

sudo apt install python3-pip

che installerà pip3.

A questo punto devi installare il comando che crea l’ambiente virtuale dando il seguente comando:

sudo pip3 install virtualenv

Virtualenv è un ambiente virtuale che rimane isolato dal resto del sistema operativo. Una volta creato, ci consente di installare al suo interno pacchetti e librerie della versione che ci serve senza che queste entrino in conflitto con quelle di sistema. In questo modo possiamo creare un ambiente in cui ogni progetto può avere le sue librerie e i suoi requisiti (con versioni differenti) senza rischiare che vadano in conflitto fra loro e con quelle di sistema.

Ora crea una cartella di nome testDjango (o quello che preferisci) ed entraci dentro con i comandi:

mkdir testDjango
cd testDjango

Una volta all’interno della cartella testDjango puoi creare l’ambiente virtuale che chiamerai myenv dando il comando:

virtualenv myenv

Dentro la cartella testDjango verrà creata la cartella myenv con, all’interno, tutti i files necessari per il nostro ambiente virtuale.

Ora non rimane che attivarlo dando il comando:

source myenv/bin/activate

Noterai che il prompt dei comandi cambierà assumendo questo aspetto:

(myenv) pi@raspberrypi:~/testDjango $ 

La scritta (myenv) sta a significare che l’ambiente virtuale myenv è attivo.

NOTA BENE: per disattivare l’ambiente virtuale è sufficiente dare il comando

deactivate

A questo punto dovrai caricare la cartella con l’applicazione Django dentro la cartella testDjango. Per farlo, spegni la Raspberry, togli la SD card e inseriscila nel PC.

Scarica il progetto dal link qui sotto:

Apri la partizione rootfs della SD card ed entra nella cartella /home/pi. Entra poi nella cartella testDjango, copia il progetto lì dentro e decomprimilo. Dovresti ritrovarti una cartella restMeasurements con i files di progetto dentro. Estrai la SD card dal PC, inseriscila nella Raspberry, avvia la board e loggati di nuovo con ssh.

Entra nella cartella testDjango:

cd testDjango

Attiva l’ambiente virtuale:

source myenv/bin/activate

A questo punto entra nella cartella del progetto col comando:

cd restMeasurements

Questa cartella, oltre ai file di progetto, contiene il file requirements.txt che ha al suo interno l’elenco dei pacchetti (con le giuste versioni) da installare per far funzionare l’applicazione.

Il file appare così:

asgiref==3.4.1
Django==3.2.6
djangorestframework==3.12.4
pytz==2021.1
sqlparse==0.4.1

Installerai ora questi pacchetti con le versioni indicate all’interno dell’ambiente virtuale dando il comando:

pip3 install -r requirements.txt

Siccome qui pip3 è un comando dell’ambiente virtuale (e non di sistema), i pacchetti verranno installati al suo interno, senza interferire con quelli di altri ambienti virtuali o con quelli di sistema.

Dando il comando

pip3 list

si otterrà la lista dei pacchetti installati in questo ambiente virtuale:

Package             Version
------------------- -------
asgiref             3.4.1
Django              3.2.6
djangorestframework 3.12.4
pip                 23.0.1
pytz                2021.1
setuptools          67.4.0
sqlparse            0.4.1
typing_extensions   4.5.0
wheel               0.38.4

Sempre con l’ambiente virtuale attivato, ripulisci il database in questo modo:

entra nella cartella restMeasurements

cd ~/testDjango/restMeasurements/

elimina il file contenente il database

rm -f db.sqlite3

ricrea il database

python manage.py migrate

A questo punto rimane un’ altra piccola modifica: dobbiamo settare l’applicazione Django in modo che riconosca l’IP assegnato alla Raspberry (nel caso in esempio 192.168.1.190) e dobbiamo impostare la timezone.

Entra nella cartella measurement_cart

cd measurement_cart/

per editare il file settings.py dando il comando

nano settings.py

cerca la voce

ALLOWED_HOSTS

e aggiungi alla lista l’IP della Raspberry.

Quindi se quella riga all’inizio è così:

ALLOWED_HOSTS = ['127.0.0.1', 'localhost']

dovrà diventare così:

ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '192.168.1.190']

Poi portati nella sezione TIME_ZONE e metti la timezone appropriata. Per esempio, la timezone per l’Italia è Europe/Rome. Quindi questa sezione, nel caso dell’Italia, avrà questo aspetto:

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'Europe/Rome'

USE_I18N = True

USE_L10N = True

USE_TZ = True

Adesso sei pronto ad avviare il server. Torna alla cartella restMeasurements col comando:

cd ~/testDjango/restMeasurements/

e avvia il server:

python manage.py runserver 0.0.0.0:8000

Se tutto è andato bene compariranno delle scritte così:

(myenv) pi@raspberrypi:~/testDjango/restMeasurements $ python manage.py runserver 0.0.0.0:8000
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
March 03, 2023 - 09:11:53
Django version 3.2.6, using settings 'measurement_cart.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

Questo significa che il server è in ascolto sulla porta 8000.

Adesso vai sul tuo browser e digita l’URL

http://192.168.1.190:8000/view/showall

Ti apparirà questa schermata:

Schermata iniziale della dashboard
Schermata iniziale della dashboard

Se premi il pulsante Refresh ti appariranno i dati (al momento il database è vuoto quindi appariranno solo le legende dei grafici):

La dashboard senza dati
La dashboard senza dati

A questo punto accendi il datalogger con ESP32 che dovresti aver già collegato alla WiFi. Se tutto va bene la Raspberry dovrebbe iniziare a raccogliere i dati e salvarli sul database e mostrarli ogni volta che premi il pulsante Refresh.

NOTA BENE: la board ESP32 invierà il primo dato dopo un tempo pari a quello impostato nella variabile measureDelay. Se, per esempio, questo tempo è pari a 10 minuti, la prima misurazione verrà effettuata 10 minuti dopo l’accensione della ESP32.

Questa è la mia dashboard dopo un giorno di registrazione dei dati misurati ogni 10 minuti:

La dashboard con i dati di una giornata
La dashboard con i dati di una giornata

Per fermare il server (che quindi non riceverà più i dati dall’ESP32 nè li mostrerà sulla dashboard) basterà premere il tasto CTRL seguito dalla lettera C.

Un problema un po’ fastidioso dovuto al modo con cui abbiamo avviato il server finora è dato dal fatto che se, col server attivo, chiudiamo la shell, il server verrà chiuso e l’applicazione non funzionerà più. Ma esiste un modo per far sì che continui a funzionare anche chiudendo la shell modificando leggermente il comando di avvio in questo modo:

nohup python manage.py runserver 0.0.0.0:8000 &

Come estrarre i dati salvati dal database per successive elaborazioni

Talvolta ci può servire estrarre i dati dal database in modo da metterli, per esempio, su un foglio di calcolo per elaborarli ulteriormente con qualche tool ad hoc.

Ci viene in aiuto un piccolo script che si trova nella cartella restMeasurements e che si chiama getdata.sh.

Quindi, trovandoci in quella cartella e dando il comando così:

./getdata.sh

questo genererà un file, chiamato data.csv, in formato CSV contenente tutti i record del database memorizzati fino a quel momento.

Ecco quindi un esempio di come appare il contenuto di questo file:

pi@raspberrypi:~/testDjango/restMeasurements $ cat data.csv 
id,temperature,humidity,timestamp,atmosphericPressure,brightness
1,16.9,53.8,"2023-02-25 06:04:25.741774",989.31,0
2,17.1,53.5,"2023-02-25 06:14:25.710678",989.17,0
3,17.1,53.9,"2023-02-25 06:24:25.862770",989.17,33.33
4,16.9,53.7,"2023-02-25 06:34:25.756406",989.13,0
5,17,54.7,"2023-02-25 06:44:27.664863",988.78,0
6,16.9,55,"2023-02-25 06:54:25.939375",988.77,0
7,16.9,55.1,"2023-02-25 07:04:25.719414",988.74,0.83
8,16.9,54.8,"2023-02-25 07:14:25.713820",988.46,3.33
9,16.9,54.6,"2023-02-25 07:24:25.861219",988.52,2.5
10,16.9,55.8,"2023-02-25 07:34:25.808413",988.43,2.5
11,16.8,55.6,"2023-02-25 07:44:25.712195",988.4,3.33
12,16.8,55.5,"2023-02-25 07:54:25.650290",988.37,0.83
13,16.8,55.4,"2023-02-25 08:04:25.828069",988.35,0.83
14,16.6,53.3,"2023-02-25 08:14:25.697565",988.35,0.83
15,16.7,55,"2023-02-25 08:24:25.713511",988.33,0.83
16,16.7,54.3,"2023-02-25 08:34:25.848961",988.59,0.83
17,16.8,54.6,"2023-02-25 08:44:25.628505",988.64,0.83
18,16.8,55.4,"2023-02-25 08:54:25.718198",988.64,1.67
19,16.8,55.2,"2023-02-25 09:04:25.823875",988.35,1.67
20,16.9,56.4,"2023-02-25 09:14:25.850034",988.05,2.5
21,16.9,55.1,"2023-02-25 09:24:25.649983",987.93,1.67
.....
.....

Questo file è stato visualizzato dando il comando:

cat data.csv

Puoi quindi togliere la SD card dalla Raspberry (ovviamente spenta) e metterla nel tuo PC per estrarre il file e copiartelo su una cartella per poi aprirlo con un foglio di calcolo, elaborarlo, calcolare statistiche e così via.

NOTA BENE: Django memorizza i dati nel database utilizzando il fuso orario UTC che corrisponde all’orario di Greenwich. Quindi i timestamp estratti con lo script e presenti nel file data.csv rispecchiano questo orario e non tengono conto della timezone.

Gli orari presenti invece nei grafici vengono corretti al volo con la giusta timezone (impostata precedentemente nel file settings.py) prima di essere visualizzati.

L’immagine per la Raspberry già pronta

La procedura precedente per il setup della Raspberry con l’installazione e l’impostazione dei programmi necessari non è particolarmente complessa ma mi rendo conto che per un principiante potrebbe rappresentare un ostacolo ed impedirgli di completare il progetto.

A tale proposito ho preparato una immagine creata con la stessa procedura descritta sopra e liberamente scaricabile dal link qui sotto:

In questo modo avrai a disposizione l’ambiente operativo già funzionante senza dover seguire la procedura precedente.

Dovrai solo scaricare l’immagine, decomprimerla e flasharla sulla SD card usando il tool Balena Etcher (anche se Balena Etcher è comunque in grado di decomprimere autonomamente files compressi in formato gz).

Una volta che l’immagine è flashata sulla SD card, apri la partizione rootfs e vai alla cartella /etc/wpa_supplicant/. Apri il file wpa_supplicant.conf che ha questo aspetto:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=«your_ISO-3166-1_two-letter_country_code»
network={
     ssid="«your_SSID»"
     psk="«your_PSK»"
     key_mgmt=WPA-PSK
}

Con un editor di testo dovrai sostituire le seguenti voci:

  • «your_ISO-3166-1_two-letter_country_code» con l’identificativo del tuo paese (per esempio per l’Italia è IT)
  • «your_SSID» con il nome SSID della tua rete WiFi
  • «your_PSK» con la password della rete WiFi

e poi salvare il file.

È possibile che per editare e salvare il file debba avere i privilegi di amministratore.

Altrernativamente puoi inserire la SD card nella Raspberry, dare alimentazione e collegare la Raspberry al router con un cavo LAN, trovare l’IP assegnatole dal router con uno dei metodi spiegati sopra e loggarti in ssh. A questo punto dai il comando:

sudo nano /etc/wpa_supplicant/wpa_supplicant.conf

e potrai editare il file come spiegato prima. La password è raspberry.

Scollega il cavo LAN, spegni e riaccendi la Raspberry. Se tutto va bene si dovrebbe ricollegare automaticamente in WiFi.

In questa immagine l’IP è settato su 192.168.1.190 quindi dovrai settare corrispondementemente il tuo router/modem in modo che possa riservare tale l’IP alla Raspberry.

Considera che anche se all’inizio potresti trovare difficoltà a connetterti via WiFi, puoi sempre collegarla al router con un cavo LAN ed entrare in ssh con l’IP fornito dal router e apportare le eventuali correzioni necessarie nella impostazione del WiFi.

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