Pollice Verde: gestione dell'orto e del frutteto con irrigazione calcolata
App PWA che dice cosa fare nell'orto e, soprattutto, quanta acqua dare a ogni coltura, calcolandolo sul meteo reale invece che su un calendario fisso. Motore idrico (metodo FAO-56) - il terreno come serbatoio: capacità dal tipo di suolo per la profondità radicale, prelievo giornaliero pari a ET0 × Kc, ricarica da pioggia efficace e irrigazioni al netto dell'efficienza del metodo - bilancio ricostruito su 21 giorni reali e proiettato su 10 di previsione, così si sa anche fra quanti giorni servirà il prossimo turno e quando saltarlo - soglia di intervento dipendente dal metodo: con la goccia si bagna al 55% della riserva utile, con l'aspersione all'85%, a mano si sfrutta tutto - correzioni per pacciamatura, serra e coltura in vaso Dati - 58 schede fra ortaggi, frutta e aromatiche con coefficienti colturali, profondità radicali e fattori di deplezione da FAO-56, più calendario, sesti, consociazioni, cure e problemi tipici - ricerca che capisce i nomi dialettali: cocomero, popone, petonciano, ramerino - meteo ed evapotraspirazione da Open-Meteo, senza chiave API, con cache offline Automazione - generatore di programma per centraline a settori: ritmo di regime da impostare e giorni esatti con le piogge già scontate - agente Node che custodisce lo stato, esegue i turni un settore alla volta e registra nel diario quello che ha davvero erogato - driver per ESP32, Shelly Gen1/Gen2, Tasmota, più modalità simulata predefinita - sonde Watermark: veto sui turni, riancoraggio del bilancio su misura reale, confronto fra sonda e calcolo - sicurezze: tetti su minuti e litri, blocchi gelo/vento/pioggia, chiusura all'avvio e all'uscita, doppia chiusura a tempo lato dispositivo, un settore per volta, sospensione dei settori con sonda muta o guasta Firmware ESP32 incluso, con schema di collegamento e istruzioni di posa. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GFnqupGt8T5HNNhPPHCjKs
This commit is contained in:
commit
b412d51658
46 files changed
+10608
No files matched your search
@@ -0,0 +1,123 @@
|
||||
# Firmware ESP32 per Pollice Verde
|
||||
|
||||
Trasforma un ESP32 e una scheda a relè nella centralina comandata dall'app.
|
||||
|
||||
## Materiale
|
||||
|
||||
| Pezzo | Indicativo |
|
||||
|---|---|
|
||||
| ESP32 DevKit v1 | 8 € |
|
||||
| Scheda a 4 relè con optoisolatore | 7 € |
|
||||
| Trasformatore 24 V AC per elettrovalvole (se non ne hai già uno) | 15 € |
|
||||
| Alimentatore 5 V per l'ESP32 | 6 € |
|
||||
| Scatola stagna IP65 | 10 € |
|
||||
| Sonda Watermark 200SS (una per settore) | 35 € |
|
||||
| Resistenza 10 kΩ 1% (una per sonda) | pochi centesimi |
|
||||
|
||||
Le elettrovalvole da giardino sono quasi sempre a **24 V AC**. Il relè commuta
|
||||
quella tensione, non la 230 V: il comune del trasformatore va a tutte le valvole,
|
||||
e ogni relè chiude il ritorno di una valvola. Se il tuo impianto usa tensione di
|
||||
rete, fallo vedere a un elettricista prima di metterci le mani.
|
||||
|
||||
## Collegamenti
|
||||
|
||||
```
|
||||
ESP32 GPIO 26 → IN1 della scheda relè (canale 0)
|
||||
ESP32 GPIO 27 → IN2 (canale 1)
|
||||
ESP32 GPIO 14 → IN3 (canale 2)
|
||||
ESP32 GPIO 12 → IN4 (canale 3)
|
||||
ESP32 5V → VCC scheda relè
|
||||
ESP32 GND → GND scheda relè
|
||||
|
||||
Trasformatore 24V AC ─┬─ comune di tutte le elettrovalvole
|
||||
└─ COM di ogni relè
|
||||
Ogni NO del relè ─────── filo della rispettiva elettrovalvola
|
||||
```
|
||||
|
||||
Il numero di canale che scrivi nella scheda dell'aiuola, dentro l'app, è la
|
||||
posizione nell'array `PIN_RELE`: il primo pin è il canale 0.
|
||||
|
||||
### Sonde Watermark
|
||||
|
||||
Ogni sonda va in serie a una resistenza da 10 kΩ, fra due pin di eccitazione, e
|
||||
l'ADC legge il punto di mezzo:
|
||||
|
||||
```
|
||||
GPIO 32 ──┬── filo 1 della sonda 0
|
||||
│
|
||||
sonda 0 (Watermark 200SS)
|
||||
│
|
||||
├── GPIO 34 (lettura ADC)
|
||||
│
|
||||
resistenza 10 kΩ
|
||||
│
|
||||
GPIO 33 ──┘
|
||||
|
||||
GPIO 25 / 35 / GPIO 4 → stessa cosa per la sonda 1
|
||||
```
|
||||
|
||||
I fili della Watermark non hanno polarità. GPIO 34 e 35 sono di **solo ingresso**
|
||||
sull'ESP32 ed è il motivo per cui sono stati scelti per la lettura.
|
||||
|
||||
**La cosa da non sbagliare: i Watermark si leggono in corrente alternata.** Una
|
||||
tensione continua elettrolizza il gesso e in poche settimane la sonda è da
|
||||
buttare. Il firmware lo fa già: ogni misura è due letture a polarità invertita da
|
||||
50 microsecondi l'una, e fra una misura e l'altra i pin restano entrambi a zero.
|
||||
Non aumentare `MICROSECONDI_ECCITAZIONE` e non leggere più spesso di ogni
|
||||
quarto d'ora.
|
||||
|
||||
### Dove infilare la sonda
|
||||
|
||||
A **metà della profondità delle radici** — 15-20 cm per l'orto, 30-40 cm per un
|
||||
albero — e a 15-20 cm di lato dal gocciolatore, mai sotto: sotto il gocciolatore
|
||||
misureresti il bulbo bagnato, non il terreno che la pianta esplora davvero.
|
||||
|
||||
Fai un foro con una punta di legno del diametro della sonda, bagna la sonda in
|
||||
acqua per qualche minuto prima di inserirla, e **fai fango**: versa un po' di
|
||||
terra stemperata nel foro prima di calare la sonda, così il gesso resta a
|
||||
contatto pieno col terreno. Una sonda con aria attorno legge sempre secco.
|
||||
|
||||
A fine stagione, sui terreni che gelano, tirale fuori.
|
||||
|
||||
## Caricamento
|
||||
|
||||
1. Arduino IDE → *Preferenze* → URL aggiuntive:
|
||||
`https://espressif.github.io/arduino-esp32/package_esp32_index.json`
|
||||
2. *Gestore schede* → installa **esp32** by Espressif.
|
||||
3. Apri `pollice-verde-esp32.ino`, scrivi rete Wi-Fi e password in cima.
|
||||
4. Scheda: *ESP32 Dev Module*. Carica.
|
||||
5. Apri il monitor seriale a 115200: stampa l'indirizzo IP da mettere in
|
||||
*Impostazioni → Centralina → Indirizzo del dispositivo*.
|
||||
|
||||
Assegna all'ESP32 un **IP fisso** dal router, altrimenti prima o poi cambierà e
|
||||
l'agente non lo troverà più.
|
||||
|
||||
## Sicurezze già nel firmware
|
||||
|
||||
- **Chiusura a tempo autonoma.** Ogni apertura porta con sé la sua durata: la
|
||||
scheda chiude allo scadere anche se l'agente è morto o la rete è caduta.
|
||||
- **Tetto di 3 ore** per singola apertura, non aggirabile dall'esterno.
|
||||
- **Un solo canale alla volta**, perché la pressione di un impianto domestico
|
||||
non basta per due settori insieme (disattivabile con `UNO_ALLA_VOLTA`).
|
||||
- **Relè chiusi prima di configurare i pin**, così un riavvio dell'ESP32 non
|
||||
apre le valvole per un istante.
|
||||
- **Riconnessione Wi-Fi** ogni 30 secondi senza interrompere un turno in corso.
|
||||
|
||||
## Prova prima di collegare l'acqua
|
||||
|
||||
Carica il firmware, alimenta solo l'ESP32 e la scheda relè, e da un browser:
|
||||
|
||||
```
|
||||
http://<ip-esp32>/stato
|
||||
http://<ip-esp32>/valvola?canale=0&secondi=10
|
||||
http://<ip-esp32>/sensori
|
||||
```
|
||||
|
||||
Devi sentire il relè scattare e, dopo dieci secondi, scattare di nuovo in
|
||||
chiusura. Solo quando tutti i canali fanno così, collega le valvole.
|
||||
|
||||
Per le sonde, la prova che vale: immergi la sonda in un bicchiere d'acqua e
|
||||
aspetta dieci minuti. `/sensori` deve leggere **0-5 kPa**. Tirala fuori, lasciala
|
||||
asciugare all'aria un giorno, e deve salire **oltre i 100 kPa**. Se resta
|
||||
inchiodata su un valore o dice sempre `scollegata`, il problema è nel cablaggio,
|
||||
non nel terreno.
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Pollice Verde — firmware per ESP32 con scheda a relè
|
||||
* ────────────────────────────────────────────────────
|
||||
* Espone tre indirizzi HTTP sulla rete di casa:
|
||||
*
|
||||
* GET /stato → JSON con lo stato dei canali
|
||||
* GET /valvola?canale=0&secondi=1200 → apre il canale 0 per 20 minuti
|
||||
* GET /valvola?canale=0&secondi=0 → chiude subito il canale 0
|
||||
* GET /stop → chiude tutto
|
||||
* GET /sensori → lettura delle sonde Watermark, in kPa
|
||||
* GET /sensori?tempC=21 → idem, con compensazione di temperatura
|
||||
*
|
||||
* Il pezzo importante è che la durata la conosce la scheda, non solo l'agente:
|
||||
* se l'agente si spegne, se cade la rete o se qualcuno stacca il Raspberry a
|
||||
* metà turno, il canale si chiude comunque allo scadere del tempo. Un allagamento
|
||||
* notturno è il tipo di guasto che non si vuole scoprire la mattina dopo.
|
||||
*
|
||||
* ⚠️ Le elettrovalvole da giardino sono a 24 V AC o 9 V a impulso: il relè
|
||||
* commuta quella tensione, NON la 230 V. Se il tuo impianto usa la rete
|
||||
* elettrica, falla vedere a un elettricista prima di toccarla.
|
||||
*
|
||||
* Librerie: nessuna oltre a quelle del core ESP32 (WiFi, WebServer).
|
||||
*/
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h>
|
||||
|
||||
// ── Da personalizzare ───────────────────────────────────────────────────────
|
||||
const char* WIFI_SSID = "NOME-DELLA-TUA-RETE";
|
||||
const char* WIFI_PASSWORD = "PASSWORD-DELLA-TUA-RETE";
|
||||
|
||||
// Pin dei relè, uno per canale. L'ordine qui è l'ordine dei canali: il primo
|
||||
// elemento è il canale 0, che è quello da scrivere nella scheda dell'aiuola.
|
||||
const int PIN_RELE[] = {26, 27, 14, 12};
|
||||
const int NUM_CANALI = sizeof(PIN_RELE) / sizeof(PIN_RELE[0]);
|
||||
|
||||
// Quasi tutte le schede a relè cinesi sono attive a livello basso: il relè
|
||||
// scatta quando il pin va a 0. Se la tua fa il contrario, metti false.
|
||||
const bool RELE_ATTIVO_BASSO = true;
|
||||
|
||||
// Nessun canale può restare aperto più di così, qualunque cosa chieda l'agente.
|
||||
const unsigned long MASSIMO_SECONDI = 3 * 60 * 60; // 3 ore
|
||||
|
||||
// Un solo settore per volta: la pressione di un impianto domestico non basta
|
||||
// per due valvole aperte insieme. Metti false se il tuo impianto lo regge.
|
||||
const bool UNO_ALLA_VOLTA = true;
|
||||
|
||||
// ── Sonde Watermark ─────────────────────────────────────────────────────────
|
||||
// Ogni sonda va letta con un partitore: la sonda in serie a una resistenza nota,
|
||||
// il tutto fra due pin di eccitazione, e l'ADC che legge il punto di mezzo.
|
||||
//
|
||||
// La cosa da NON sbagliare: i Watermark si leggono in **corrente alternata**.
|
||||
// Una tensione continua elettrolizza il gesso e in poche settimane la sonda è
|
||||
// da buttare. Per questo ogni misura si fa due volte a polarità invertita, per
|
||||
// pochi microsecondi, e si media. Fra una misura e l'altra i pin restano a zero.
|
||||
const int PIN_ECCITAZIONE_A[] = {32, 25}; // un pin per sonda
|
||||
const int PIN_ECCITAZIONE_B[] = {33, 4}; // il pin opposto della stessa sonda
|
||||
const int PIN_LETTURA[] = {34, 35}; // ingressi ADC (solo pin di solo-ingresso)
|
||||
const int NUM_SONDE = sizeof(PIN_LETTURA) / sizeof(PIN_LETTURA[0]);
|
||||
|
||||
const float RESISTENZA_NOTA = 10000.0; // ohm, in serie alla sonda
|
||||
const float TENSIONE_ECCITAZIONE = 3.3;
|
||||
const int MICROSECONDI_ECCITAZIONE = 50; // brevissimo: più a lungo, più si consuma la sonda
|
||||
const float TEMPERATURA_SUOLO_PREDEFINITA = 24.0; // °C, se l'agente non ne passa una
|
||||
// ────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
WebServer server(80);
|
||||
unsigned long chiusuraPrevista[8]; // millis() a cui chiudere, 0 = chiuso
|
||||
|
||||
void scriviRele(int canale, bool acceso) {
|
||||
int livello = acceso ? (RELE_ATTIVO_BASSO ? LOW : HIGH)
|
||||
: (RELE_ATTIVO_BASSO ? HIGH : LOW);
|
||||
digitalWrite(PIN_RELE[canale], livello);
|
||||
}
|
||||
|
||||
void chiudi(int canale) {
|
||||
scriviRele(canale, false);
|
||||
chiusuraPrevista[canale] = 0;
|
||||
}
|
||||
|
||||
void chiudiTutto() {
|
||||
for (int i = 0; i < NUM_CANALI; i++) chiudi(i);
|
||||
}
|
||||
|
||||
void apri(int canale, unsigned long secondi) {
|
||||
if (secondi > MASSIMO_SECONDI) secondi = MASSIMO_SECONDI;
|
||||
if (UNO_ALLA_VOLTA) {
|
||||
for (int i = 0; i < NUM_CANALI; i++)
|
||||
if (i != canale && chiusuraPrevista[i] != 0) chiudi(i);
|
||||
}
|
||||
scriviRele(canale, true);
|
||||
chiusuraPrevista[canale] = millis() + secondi * 1000UL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Misura la resistenza di una sonda con doppia eccitazione a polarità invertita.
|
||||
* Restituisce la media delle due letture, in ohm.
|
||||
*/
|
||||
float leggiResistenza(int sonda) {
|
||||
float somma = 0;
|
||||
for (int verso = 0; verso < 2; verso++) {
|
||||
int alto = verso == 0 ? PIN_ECCITAZIONE_A[sonda] : PIN_ECCITAZIONE_B[sonda];
|
||||
int basso = verso == 0 ? PIN_ECCITAZIONE_B[sonda] : PIN_ECCITAZIONE_A[sonda];
|
||||
|
||||
digitalWrite(alto, HIGH);
|
||||
digitalWrite(basso, LOW);
|
||||
delayMicroseconds(MICROSECONDI_ECCITAZIONE);
|
||||
int grezzo = analogRead(PIN_LETTURA[sonda]);
|
||||
digitalWrite(alto, LOW); // subito a riposo: niente continua sulla sonda
|
||||
|
||||
float volt = (grezzo / 4095.0) * TENSIONE_ECCITAZIONE;
|
||||
// Nel verso invertito il partitore si legge specchiato.
|
||||
float frazione = verso == 0 ? volt / TENSIONE_ECCITAZIONE
|
||||
: 1.0 - (volt / TENSIONE_ECCITAZIONE);
|
||||
if (frazione <= 0.001) frazione = 0.001;
|
||||
if (frazione >= 0.999) frazione = 0.999;
|
||||
somma += RESISTENZA_NOTA * (frazione / (1.0 - frazione));
|
||||
|
||||
delay(90); // riposo fra i due versi
|
||||
}
|
||||
return somma / 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Da resistenza a centibar (= kPa). Equazioni di Shock, Barnum e Seddigh (1998),
|
||||
* le stesse usate da Irrometer: la relazione non è lineare e cambia forma su tre
|
||||
* intervalli, e dipende dalla temperatura del terreno.
|
||||
*/
|
||||
float resistenzaInKpa(float ohm, float tempC) {
|
||||
float kohm = ohm / 1000.0;
|
||||
float kpa;
|
||||
|
||||
if (ohm > 550.0) {
|
||||
if (ohm > 8000.0) {
|
||||
kpa = -2.246 - 5.239 * kohm * (1.0 + 0.018 * (tempC - 24.0)) -
|
||||
0.06756 * kohm * kohm * pow((1.0 + 0.018 * (tempC - 24.0)), 2.0);
|
||||
} else if (ohm > 1000.0) {
|
||||
kpa = (-3.213 * kohm - 4.093) / (1.0 - 0.009733 * kohm - 0.01205 * tempC);
|
||||
} else {
|
||||
kpa = (kohm * 23.156 - 12.736) * (1.0 + 0.018 * (tempC - 24.0));
|
||||
}
|
||||
} else {
|
||||
// Sotto i 550 ohm il terreno è saturo: la sonda non distingue più niente.
|
||||
kpa = 0.0;
|
||||
}
|
||||
|
||||
if (kpa < 0.0) kpa = 0.0;
|
||||
if (kpa > 239.0) kpa = 239.0; // fondo scala del sensore
|
||||
return kpa;
|
||||
}
|
||||
|
||||
/** Diagnostica del collegamento, per non scambiare un cavo staccato per siccità. */
|
||||
const char* saluteSonda(float ohm) {
|
||||
if (ohm > 60000.0) return "scollegata"; // circuito aperto: filo staccato
|
||||
if (ohm < 200.0) return "in corto"; // due fili che si toccano, o acqua nel morsetto
|
||||
if (ohm > 35000.0) return "fuori scala"; // oltre il campo utile del Watermark
|
||||
return "ok";
|
||||
}
|
||||
|
||||
String jsonSensori(float tempC) {
|
||||
String s = "{\"temperatura\":" + String(tempC, 1) + ",\"sonde\":[";
|
||||
for (int i = 0; i < NUM_SONDE; i++) {
|
||||
if (i) s += ",";
|
||||
float ohm = leggiResistenza(i);
|
||||
float kpa = resistenzaInKpa(ohm, tempC);
|
||||
s += "{\"sensore\":" + String(i) +
|
||||
",\"ohm\":" + String(ohm, 0) +
|
||||
",\"kpa\":" + String(kpa, 1) +
|
||||
",\"salute\":\"" + String(saluteSonda(ohm)) + "\"}";
|
||||
}
|
||||
s += "]}";
|
||||
return s;
|
||||
}
|
||||
|
||||
String jsonStato() {
|
||||
String s = "{\"firmware\":\"pollice-verde-1.0\",\"canali\":[";
|
||||
unsigned long ora = millis();
|
||||
for (int i = 0; i < NUM_CANALI; i++) {
|
||||
if (i) s += ",";
|
||||
unsigned long restanti = 0;
|
||||
if (chiusuraPrevista[i] != 0 && chiusuraPrevista[i] > ora)
|
||||
restanti = (chiusuraPrevista[i] - ora) / 1000UL;
|
||||
s += "{\"canale\":" + String(i) +
|
||||
",\"aperto\":" + (chiusuraPrevista[i] != 0 ? "true" : "false") +
|
||||
",\"secondiRestanti\":" + String(restanti) + "}";
|
||||
}
|
||||
s += "],\"ip\":\"" + WiFi.localIP().toString() + "\"}";
|
||||
return s;
|
||||
}
|
||||
|
||||
void rispondi(int codice, const String& corpo) {
|
||||
server.sendHeader("Access-Control-Allow-Origin", "*");
|
||||
server.send(codice, "application/json", corpo);
|
||||
}
|
||||
|
||||
void gestisciValvola() {
|
||||
if (!server.hasArg("canale") || !server.hasArg("secondi")) {
|
||||
rispondi(400, "{\"errore\":\"servono canale e secondi\"}");
|
||||
return;
|
||||
}
|
||||
int canale = server.arg("canale").toInt();
|
||||
long secondi = server.arg("secondi").toInt();
|
||||
|
||||
if (canale < 0 || canale >= NUM_CANALI) {
|
||||
rispondi(400, "{\"errore\":\"canale inesistente\"}");
|
||||
return;
|
||||
}
|
||||
if (secondi <= 0) {
|
||||
chiudi(canale);
|
||||
} else {
|
||||
apri(canale, (unsigned long)secondi);
|
||||
}
|
||||
rispondi(200, jsonStato());
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Prima cosa all'accensione: tutti i relè chiusi, PRIMA di impostarli come
|
||||
// uscite. Al contrario, un ESP32 che riparte apre le valvole per un istante.
|
||||
for (int i = 0; i < NUM_CANALI; i++) {
|
||||
digitalWrite(PIN_RELE[i], RELE_ATTIVO_BASSO ? HIGH : LOW);
|
||||
pinMode(PIN_RELE[i], OUTPUT);
|
||||
scriviRele(i, false);
|
||||
chiusuraPrevista[i] = 0;
|
||||
}
|
||||
|
||||
// Pin delle sonde: a riposo entrambi bassi, così fra una lettura e l'altra
|
||||
// non passa corrente e il gesso non si consuma.
|
||||
for (int i = 0; i < NUM_SONDE; i++) {
|
||||
pinMode(PIN_ECCITAZIONE_A[i], OUTPUT);
|
||||
pinMode(PIN_ECCITAZIONE_B[i], OUTPUT);
|
||||
digitalWrite(PIN_ECCITAZIONE_A[i], LOW);
|
||||
digitalWrite(PIN_ECCITAZIONE_B[i], LOW);
|
||||
pinMode(PIN_LETTURA[i], INPUT);
|
||||
}
|
||||
analogSetAttenuation(ADC_11db); // fondo scala ~3,3 V
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
Serial.print("Mi collego al Wi-Fi");
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println();
|
||||
Serial.print("Pronto. Indirizzo da mettere in Pollice Verde: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
server.on("/stato", []() { rispondi(200, jsonStato()); });
|
||||
server.on("/sensori", []() {
|
||||
float t = server.hasArg("tempC") ? server.arg("tempC").toFloat()
|
||||
: TEMPERATURA_SUOLO_PREDEFINITA;
|
||||
rispondi(200, jsonSensori(t));
|
||||
});
|
||||
server.on("/valvola", gestisciValvola);
|
||||
server.on("/stop", []() {
|
||||
chiudiTutto();
|
||||
rispondi(200, jsonStato());
|
||||
});
|
||||
server.onNotFound([]() { rispondi(404, "{\"errore\":\"non trovato\"}"); });
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
server.handleClient();
|
||||
|
||||
// Il vero guardiano: chiude i canali scaduti anche se nessuno lo chiede.
|
||||
unsigned long ora = millis();
|
||||
for (int i = 0; i < NUM_CANALI; i++) {
|
||||
if (chiusuraPrevista[i] != 0 && (long)(ora - chiusuraPrevista[i]) >= 0) {
|
||||
Serial.println("Tempo scaduto: chiudo il canale " + String(i));
|
||||
chiudi(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Se il Wi-Fi cade non chiudiamo subito (un turno in corso deve finire), ma
|
||||
// proviamo a riconnetterci: senza rete l'agente non può più dare lo stop.
|
||||
static unsigned long ultimoControllo = 0;
|
||||
if (ora - ultimoControllo > 30000) {
|
||||
ultimoControllo = ora;
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.println("Wi-Fi caduto, riprovo");
|
||||
WiFi.disconnect();
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user