Realizziamo un sistema di monitoraggio miniale per il fotovoltaico con una ESP8266 e un display TFT, aggiornando i dati direttamente da Home Assistant
In questo nuovo progetto abbiamo messo insieme le nostre conoscenze di programmazione su ESP8266 e Home Assistant, sfruttando il servizio MQTT per recuperare alcune informazioni sull’impianto fotovoltaico che avevamo realizzato qualche tempo fa e mostrarle su un display TFT da 2,4″. Abbiamo riciclato una Wemos D1 Mini e il display shield della Lolin per creare questo piccolo sistema di monitoraggio.
Componenti
Il nostro sistema di monitoraggio ci permetterà di leggere i dati direttamente da un display TFT della Lolin da 2,4″ che abbiamo visionato tempo fa [LINK], ma che potete sostituire con un comunissimo display TFT, badando al fatto che questa shield non richiede collegamenti con jumper, ma solo l’innesto nel retro della Wemos D1 Mini, una piccola ESP8266 con connessione Wi-Fi, nonché il piccolo motore che preleverà i dati da Home Assistant tramite MQTT, li elaborerà e li mostrerà sul display.
Per alimentare il blocco Wemos D1 Mini e Display basterà un semplicissimo connettore USB Mini, collegato all’apposito cavo e ad un alimentatore da smartphone, ricordandovi che la scheda e il display lavorano da 3,3V, ma possono essere alimentati comodamente a 5V dagli appositi pin.
In ultimo, il tutto va assemblato dentro una scocca stampata con la stampante 3D, realizzata con PLA color legno.
Lista componenti Amazon:
- Wemos D1 Mini: https://amzn.to/4aYAWB8
- Display TFT SPI 2.4″ ILI9341: https://amzn.to/4bXAFiT
- USB Mini: https://amzn.to/3yV3DkR
Lista componenti Aliexpress:
- Wemos D1 Mini: https://s.click.aliexpress.com/e/_DF4yGKB
- Display Lolin 2.4″ Shield: https://s.click.aliexpress.com/e/_DlB74AJ
- oppure Display TFT SPI 2.4″ ILI9341: https://it.aliexpress.com/item/32919729730.html
- USB Mini: https://s.click.aliexpress.com/e/_DEkwdX9
Collegamenti
I collegamenti da fare sono estremamente facili, ma facilitare la vita a chiunque abbia un display TFT da 2,4 e non voglia acquistare la shield della Lolin, vi lasceremo più giù i collegamenti da effettuare. Iniziamo con la shield della Lolin, la quale prevede solamente, come già detto, l’innesto della Wemos D1 Mini. Per alimentarli, un piccolo connettore USB Mini, dove il pin Vc va collegato al pin 5V della Wemos D1 Mini e il pin Ground all’omonimo pin della Wemos D1 Mini.
Proseguendo con i collegamenti tra la Wemos e un comune display TFT da 2,4″, applicabili a tutti i display dotati di chip ILI9341 della ILITEK, sono i seguenti:
Wemos D1 Mini | Display ILI9341 |
---|---|
5V | Vcc |
5V | LED |
G | GND |
D2 | CS |
D3 | RST |
D4 | RS |
D5 | CLK |
D7 | SDA |
Codice ESP8266
Prima di passare al codice, scaricabile con tutte le librerie e i file yaml a questo LINK, premettiamo che il display lavora solo con determinate librerie, ossia la Adafruit_GFX [LINK] e la Adafruit_ILI9341 [LINK], da scaricare a aggiungere alla IDE di Arduino. Fatto ciò, andiamo al codice iniziando ad inserire le seguenti librerie:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "NTPClient.h"
#include "WiFiUdp.h"
Queste librerie ci consentono di collegarci alla rete Wi-Fi di casa ottenendo un indirizzo IP, per poi collegarci al broker MQTT di Home Assistant, inviando i dati che poi elaboreremo. In particolare, la librerie ArduinoJson ci permette di deserializzare una stringa che riceveremo da Home Assistant, contenente i dati sul fotovoltaico. Per quanto riguarda il display, oltre alle già citate librerie, aggiungiamo anche la SPI per la gestione dell’omonima comunicazione.
#include <Adafruit_ILI9341.h>
#include <Adafruit_GFX.h>
#include <SPI.h>
sun.c non è una libreria, ma un semplice file che contiene un’immagine che adopereremo sul display. Abbiamo visto come creare queste immagini usando la funzione DrawBitmap [LINK].
#include "sun.c"
Definiamo i pin per il funzionamento del display TFT.
#define TFT_CS D0
#define TFT_DC D8
#define TFT_RST -1
#define TS_CS D3
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
Successivamente inseriamo i parametri di rete, tra i quali SSID della rete Wi-Fi e la relativa password, senza dimenticarci di inserire anche l’indirizzo IP del broker MQTT (l’indirizzo del PC su cui è installato Home Assistant), la porta, l’username e la password, tutte informazioni che trovare sul Home Assistant se avete attivato l’add-on Moquitto broker.
const char* ssid = "Nome_rete_wifi";
const char* password = "password_wifi";
const char* mqtt_server = "xxx.xxx.xxx.xxx";
const int mqtt_server_port = 1883;
const char* mqttUser = "homeassistant";
const char* mqttPassword = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
Dichiariamo prima gli oggetti utili per il programma per connettersi alla rete Wi-Fi, sincronizzarsi con il server MQTT e scambiarsi dati, poi le variabili per i dati che andremo ad elaborare. Queste sono variabili che ci serviranno per compare i dati ricevuti ed eventualmente stamparli se ci sono variazioni.
WiFiClient espClient;
PubSubClient client(espClient);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
float prevPower = -1.0;
float prevEnergyD = -1.0;
float prevEnergyM = -1.0;
float prevCo2 = -1.0;
Nel VOID SETUP, avviamo il monitor seriale, inizializziamo il display e ne settiamo la rotazione ci colleghiamo alla rete Wi-Fi, ottenendo un indirizzo IP.
Serial.begin(115200);
tft.begin();
tft.setRotation(3);
delay(1000);
Serial.println();
Serial.print("Connessione a ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("Connessione WiFi stabilita");
Serial.println("Indirizzo IP: ");
Serial.println(WiFi.localIP());
Procediamo creando la grafica del display, ossia lo sfondo nero, una cornice bianca, qualche linea per creare delle sezioni separate e i testi che accompagneranno i valori.
tft.fillScreen(ILI9341_BLACK);
tft.drawRoundRect(0, 0, 320, 240, 5, ILI9341_WHITE);
tft.drawBitmap(40, 10, gImage_sun, 80, 80, ILI9341_YELLOW);
tft.setCursor(70, 150);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(4);
tft.println("W");
tft.setCursor(20, 200);
tft.setTextColor(ILI9341_CYAN);
tft.setTextSize(2);
tft.println("Produzione");
tft.setCursor(20, 215);
tft.setTextColor(ILI9341_CYAN);
tft.setTextSize(2);
tft.println("Istantanea");
tft.drawRect(160, 8, 1, 226, ILI9341_WHITE);
tft.drawRect(160, 80, 156, 1, ILI9341_WHITE);
tft.drawRect(160, 160, 156, 1, ILI9341_WHITE);
tft.setCursor(170, 10);
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.println("Energia Oggi");
tft.setCursor(170, 90);
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.println("Energia Mese");
tft.setCursor(170, 170);
tft.setTextColor(0x8410);
tft.setTextSize(2);
tft.println("CO2");
Concludiamo il VOID SETUP avviando la connessione al server MQTT, settando i parametri e chiamando una funzione che a breve andiamo a vedere.
client.setServer(mqtt_server, mqtt_server_port);
client.setCallback(callback);
La funzione ci permette di leggere la stringa inviataci tramite Home Assistant sul monitor seriale.
Serial.print("Info impianto [");
Serial.print(topic);
Serial.print("] ");
// Copia il payload in una stringa
String payloadStr;
for (unsigned int i = 0; i < length; i++) {
payloadStr += (char)payload[i];
}
Serial.println(payloadStr);
Creiamo delle variabili per produzione istantanea, energia prodotta giornalmente e mensilmente, poi per la CO2 non emessa. Queste variabili andranno ad ospitare i valori che estrarremo dalla stringa. Estraiamo i valori dalla stringa e stampiamoli su monitor seriale.
float power = 0.0, energyD = 0.0, energyM = 0.0, co2 = 0.0;
int powerIndex = payloadStr.indexOf("Power ");
int energyDIndex = payloadStr.indexOf("EnergyD ");
int energyMIndex = payloadStr.indexOf("EnergyM ");
int co2Index = payloadStr.indexOf("CO2 ");
if (powerIndex != -1) {
power = payloadStr.substring(powerIndex + 6, payloadStr.indexOf(" ", powerIndex + 6)).toFloat();
}
if (energyDIndex != -1) {
energyD = payloadStr.substring(energyDIndex + 8, payloadStr.indexOf(" ", energyDIndex + 8)).toFloat();
}
if (energyMIndex != -1) {
energyM = payloadStr.substring(energyMIndex + 8, payloadStr.indexOf(" ", energyMIndex + 8)).toFloat();
}
if (co2Index != -1) {
co2 = payloadStr.substring(co2Index + 4).toFloat();
}
// Stampa i valori estratti
Serial.print("Power: ");
Serial.println(power);
Serial.print("EnergyD: ");
Serial.println(energyD);
Serial.print("EnergyM: ");
Serial.println(energyM);
Serial.print("CO2: ");
Serial.println(co2);
Per evitare continui refresh del display e ottenere uno fastidioso sfarfallio nell’aggiornamento dei dati, scriviamo il codice affinché il display aggiorni solo i dati che cambiamo. Questo è possibile confrontando i dati ricevuti con quelli precedentemente.
bool powerChanged = (power != prevPower);
bool energyDChanged = (energyD != prevEnergyD);
bool energyMChanged = (energyM != prevEnergyM);
bool co2Changed = (co2 != prevCo2);
// Aggiorna solo se i valori sono cambiati
if (powerChanged || energyDChanged || energyMChanged || co2Changed) {
// Aggiorna valori precedenti
prevPower = power;
prevEnergyD = energyD;
prevEnergyM = energyM;
prevCo2 = co2;
// Pulisci solo l'area del testo che cambia
if (powerChanged) {
tft.fillRect(10, 100, 150, 40, ILI9341_BLACK); // Pulisci l'area del testo di Power
// Scrivi il nuovo valore di Power
tft.setCursor(10, 100);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(4);
tft.println(power);
}
if (energyDChanged) {
tft.fillRect(170, 45, 120, 20, ILI9341_BLACK); // Pulisci l'area del testo di EnergyD
// Scrivi il nuovo valore di EnergyD
tft.setCursor(170, 45);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.print(energyD);
tft.println(" kWh");
}
if (energyMChanged) {
tft.fillRect(170, 125, 120, 20, ILI9341_BLACK); // Pulisci l'area del testo di EnergyM
// Scrivi il nuovo valore di EnergyM
tft.setCursor(170, 125);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.print(energyM);
tft.println(" kWh");
}
if (co2Changed) {
tft.fillRect(170, 200, 120, 20, ILI9341_BLACK); // Pulisci l'area del testo di CO2
// Scrivi il nuovo valore di CO2
tft.setCursor(170, 200);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.print(co2);
tft.println(" kg");
}
}
Infine, andiamo nel VOID LOOP dove verifichiamo se la connessione via MQTT sia attiva. In caso contrario il dispositivo si ricollegherà, richiamando la funzione reconnect().
if (!client.connected()) {
reconnect();
}
client.loop();
timeClient.update();
Vi riportiamo anche questa funzione, permettendo alla Wemos di collegarsi nuovamente al server MQTT ogni 5 secondi.
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str(), mqttUser, mqttPassword)) {
Serial.println("connected");
client.subscribe("home/lolin");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
Codice Home Assistant
Lato Home Assistant dobbiamo prima di tutto andare a creare una entità che chiamiamo sensor.co2_fotovoltaico, con il quale calcoleremo i valori di CO2 non emessa, moltiplicando il valore dell’energia prodotta dall’impianto per 0.53, valore che corrisponde ai chilogrammi di anidride carbonica per ogni kWh di energia. Entriamo con il File editor nel configuration.yaml e copiamo quanto segue, modificando ovviamente il sensore che si occupa del calcolo dell’energia prodotta dal fotovoltaico. Non abbiamo impostato un numero massimo di cifre visibili dopo la virgola, ma basta modificare quanto segue per ottenere 2 o meno cifre decimali.
sensor:
- platform: template
sensors:
co2_fotovoltaico:
friendly_name: "CO2 non emessa"
unit_of_measurement: "kg"
value_template: "{{ states('sensor.fotovoltaico') | float * 0.53 }}"
Una volta salvato e riavviamo Home Assistant, passiamo a creare l’automazione che si occuperà di inviare i dati alla Wemos D1 Mini. L’automazione, di cui pubblichiamo lo yaml, permetterà di inviare i dati alla ESP8266 ogni volta che il valore della potenza istantanea cambia. Questi valori sono presi dal Sonoff POW Ring che abbiamo utilizzato per leggere la produzione del fotovoltaico.
alias: Invio Lolin MQTT
description: ""
trigger:
- platform: state
entity_id:
- sensor.sonoff_1002263197_power
condition: []
action:
- service: mqtt.publish
data:
topic: home/lolin
payload: >-
Power {{ states("sensor.sonoff_1002263197_power") }} , EnergyD {{
states("sensor.sonoff_1002263197_energy_day") }} , EnergyM {{
states("sensor.sonoff_1002263197_energy_month") }} , CO2 {{
states("sensor.co2_fotovoltaico") }}
mode: single
Il risultato finale prevede un aggiornamento dei dati che continua finché l’impianto produce. Infatti, la sera, quando non c’è sole, i valori vanno a zero e non vengono aggiornati fino al mattino seguente.