Serra intelligente con Arduino: sensori, irrigazione e codice
Serra intelligente con Arduino: DHT22, umidità del terreno e LDR; ventola, riscaldatore, pompa e LED con relè e MOSFET, isteresi, LCD e codice ESP32.
Una serra intelligente con Arduino è un sistema di automazione in cui un Arduino o un ESP32 misura temperatura, umidità dell'aria, umidità del terreno e luce nella serra e aziona da solo la ventola, il riscaldatore, la pompa dell'acqua e le luci di coltivazione. È uno dei temi preferiti per le fiere della scienza, i concorsi scolastici e le lezioni di tecnologia, perché il risultato si vede: quando la terra si asciuga parte la pompa, quando dentro fa caldo gira la ventola, la sera si accendono i LED. In più, tra un modellino ricavato da un contenitore portaoggetti e una vera mini serra sul balcone non c'è quasi nessuna differenza di circuito o di codice.
In questa guida partiamo da ciò di cui le piante hanno davvero bisogno e poi costruiamo il modellino passo dopo passo: la lista dei componenti e il costo indicativo, il collegamento dei sensori (DHT22, sensore capacitivo di umidità del terreno, LDR) e degli attuatori (ventola, riscaldatore, pompa e LED tramite relè e un MOSFET), il piano di alimentazione e la tabella dei pin. Poi spieghiamo la logica di soglia e isteresi che impedisce ai relè di scattare in continuazione, il codice Arduino completo senza delay() e il display LCD 16x2. Nelle ultime sezioni trovi come controllare la serra dallo smartphone con un ESP32, una versione MicroPython per il Raspberry Pi Pico, consigli per presentazioni scolastiche e fiere della scienza e gli errori più comuni.





Come funziona una serra intelligente?
Ogni serra intelligente segue lo stesso ciclo in quattro passi: misura, decidi, agisci, mostra. I sensori misurano l'ambiente: quanti gradi ci sono dentro, quanto è umida l'aria, la terra si è asciugata, c'è abbastanza luce? Arduino confronta questi valori con le regole che hai stabilito: «se la temperatura supera i 30 °C, accendi la ventola», «se l'umidità del terreno scende sotto il 35%, irriga». Gli attuatori eseguono la decisione: i relè accendono e spengono ventola, riscaldatore e pompa, mentre un MOSFET regola la luminosità della striscia LED. Infine il display LCD e lo smartphone ti mostrano lo stato attuale della serra. I computer di controllo climatico delle serre professionali funzionano esattamente con questo ciclo; usano solo più sensori e motori più grandi.
| Cosa si misura? | Sensore | Attuatore | Regola (esempio) |
|---|---|---|---|
| Temperatura dell'aria | DHT22 | Ventola e riscaldatore | La ventola parte a 30 °C, il riscaldatore si accende a 16 °C |
| Umidità relativa | DHT22 | Ventola | La ventola parte all'85% e si ferma al 75% |
| Umidità del terreno | Sensore capacitivo di umidità del terreno | Mini pompa a immersione | Sotto il 35% irriga a impulsi di 3 secondi |
| Luce | LDR | Striscia LED (MOSFET) | Quando fa buio, i LED restano accesi al massimo 4 ore |
| Acqua nel serbatoio | Interruttore a galleggiante | Blocco della pompa | Se il serbatoio è vuoto, la pompa non parte mai |
Di cosa hanno bisogno le piante? Temperatura, umidità e luce ideali
Prima di scrivere le regole devi sapere a cosa punti. La maggior parte delle piante vuole temperature diverse di giorno e di notte; un leggero raffreddamento notturno aiuta la pianta a riposare. Se l'umidità dell'aria resta a lungo sopra l'85%, l'acqua condensa sulle foglie e le malattie fungine come muffe e peronospora si diffondono in fretta; per questo in serra la ventola non serve solo a rinfrescare, ma anche a eliminare l'umidità. Per la luce, la maggior parte delle piantine di ortaggi vuole 14–16 ore al giorno, ma ha bisogno anche di un periodo di buio di almeno 6–8 ore; lasciare i LED accesi tutta la notte fa più male che bene. I valori qui sotto sono una buona guida per iniziare; adattali a ciò che coltivi.
| Pianta | Giorno | Notte | Umidità relativa | Nota |
|---|---|---|---|---|
| Pomodoro | 21–27 °C | 15–18 °C | 60–75% | Sopra i 32 °C i fiori possono cadere |
| Peperone | 21–29 °C | 16–20 °C | 60–75% | Più sensibile al freddo del pomodoro |
| Lattuga e spinaci | 15–20 °C | 10–15 °C | 60–70% | Con caldo e giornate lunghe va presto a seme |
| Fragola | 18–24 °C | 10–13 °C | 60–75% | Con umidità alta aumenta il rischio di muffa grigia |
| Piantine e germinazione | 20–25 °C | 18–20 °C | 70–85% | La temperatura del terriccio conta più di quella dell'aria |
Costruire il modellino della serra
Il contenitore più semplice ed economico è una scatola portaoggetti di plastica trasparente con coperchio: una da 40–60 litri basta per due o tre vasi, è impermeabile e si fora facilmente con un taglierino o con una punta riscaldata. Se vuoi un aspetto più realistico, costruisci un telaio con listelli di legno e rivestilo con lastre trasparenti di plexiglas o PETG da 2–3 mm; una serra con il tetto a due falde fa molto più effetto in una presentazione. Circa 50×35×40 cm è una buona misura per iniziare. Qualunque contenitore scegli, il coperchio o una parete laterale deve potersi aprire: dovrai metterci le mani per innaffiare, spostare il sensore e sistemare i cavi.
- Elettronica fuori: Dentro la serra c'è molta umidità e al mattino l'acqua condensa sulle pareti. Metti Arduino, modulo relè e alimentatore in una scatola separata fuori dalla serra; dentro restano solo sensori, ventola, riscaldatore, tubo della pompa e LED.
- Ventilazione: L'aria calda sale, quindi monta la ventola nella parte alta di una delle pareti corte, in modo che spinga l'aria fuori. Nella parte bassa della parete opposta apri una presa d'aria grande quanto la ventola e coprila con una zanzariera; così l'aria fresca entra dal basso e quella calda e umida esce dall'alto.
- Riscaldatore: Monta il riscaldatore vicino al fondo, su una lastra di alluminio o una piastrella di ceramica, ad almeno 5 cm dalle pareti di plastica e dalle piante. Salendo, l'aria calda circola in tutta la serra.
- Illuminazione LED: Incolla la striscia LED sotto il coperchio, 15–30 cm sopra le piante. Se la incolli su un sottile profilo di alluminio, dissipa meglio il calore e l'adesivo non si stacca col tempo.
- Sensori: Metti il DHT22 all'altezza delle piante, dove non lo colpiscano il sole diretto né la luce diretta dei LED. Interra il sensore di umidità nella zona delle radici, lontano dal bordo del vaso. La LDR non deve vedere i LED: sistemala fuori dalla serra, sul tetto, rivolta verso il cielo.
- Serbatoio dell'acqua: Un contenitore da 1–2 litri basta per la pompa e il galleggiante. Metti dei sottovasi sotto i vasi; l'acqua in eccesso si raccoglie lì invece di allagare il fondo della scatola.
Lista dei componenti e costo
| Componente | Q.tà | Funzione | Fascia di prezzo |
|---|---|---|---|
| Arduino Uno R3 o ESP32 DevKit | 1 | Il cervello della serra | Media |
| Modulo temperatura e umidità DHT22 (AM2302) | 1 | Temperatura e umidità dell'aria | Bassa |
| Sensore capacitivo di umidità del terreno (v1.2) | 1 (uno per vaso) | Umidità del terreno | Bassa |
| LDR (GL5528) e resistenza da 10 kΩ | 1 + 1 | Luce ambiente | Molto bassa |
| Interruttore a galleggiante per il livello dell'acqua | 1 | Blocca la pompa a serbatoio vuoto | Bassa |
| Modulo 4 relè 5 V (con fotoaccoppiatore e JD-VCC) | 1 | Ventola, riscaldatore e pompa | Bassa |
| MOSFET a canale N logic level (IRLZ44N), resistenze da 220 Ω e 100 kΩ | 1 set | Luminosità della striscia LED (PWM) | Molto bassa |
| Mini pompa a immersione 3–6 V e 1 m di tubo in silicone | 1 | Irrigazione | Bassa |
| Ventola da PC 12 V 80 mm | 1–2 | Ventilazione ed eliminazione dell'umidità | Bassa |
| Riscaldatore PTC 12 V (10–30 W) e fusibile termico | 1 + 1 | Riscaldamento | Bassa |
| Striscia LED 12 V (per coltivazione o bianco freddo), circa 1 m | 1 | Illuminazione supplementare | Bassa |
| Display LCD I2C 16x2 | 1 | Display di stato | Bassa – media |
| Alimentatore 12 V 5 A e convertitore step-down LM2596 | 1 + 1 | Alimentazione | Media |
| Diodo 1N5819 o 1N4007, condensatore da 470 µF, morsetti, cavetti jumper | – | Protezione e collegamenti | Molto bassa |
| Scatola trasparente con coperchio o lastre di plexiglas, vasi, sottovasi | – | Struttura della serra | Media |
I prezzi cambiano molto da un venditore all'altro, quindi meglio un budget indicativo che cifre esatte. Esclusa la scheda, la versione base (DHT22, sensore di umidità del terreno, LDR, relè a 2 canali, pompa e ventola da 5 V) costa di solito circa 25–40 €. La versione completa, con riscaldatore, striscia LED, LCD, alimentatore 12 V, LM2596, galleggiante e contenitore, si aggira sui 70–120 €; la maggior parte del budget va in scheda, alimentatore, display e contenitore. Se hai uno starter kit Arduino, scheda, breadboard, cavetti, LDR, resistenze e spesso anche l'LCD ce li hai già. Con un budget ridotto, rimanda riscaldatore e LED a una seconda fase: irrigazione e ventilazione da sole sono già un progetto utile e funzionante.
Sensori: DHT22, sensore di umidità del terreno e LDR
Per la serra ti consigliamo il DHT22 (AM2302). Costa un po' più del DHT11, ma misura la temperatura da −40 a 80 °C con ±0,5 °C e l'umidità su tutto l'intervallo 0–100% con circa ±2%; il DHT11 è preciso solo a ±2 °C tra 0 e 50 °C e non vede l'umidità sopra il 90%. Dato che una serra può superare il 90% di umidità al mattino e scendere sotto zero d'inverno, il DHT22 è molto più adatto. Il sensore fornisce al massimo una lettura ogni 2 secondi, per questo il codice lo legge ogni 2,5 secondi. Se il sole o i LED lo colpiscono direttamente, il suo involucro si scalda e segna qualche grado più dell'aria; un cappuccio bianco forato che gli faccia ombra evita il problema. Collegamenti e libreria sono spiegati nell'articolo sensore di temperatura e umidità DHT11 e DHT22.

Per l'umidità del terreno scegli un sensore capacitivo. I sensori resistivi economici (YL-69, FC-28) hanno due punte metalliche a contatto diretto con la terra e, se alimentati di continuo, si corrodono in poche settimane per elettrolisi; non vanno bene per un sistema che deve funzionare per mesi. Nel sensore capacitivo l'elettrodo è rivestito e nessun metallo tocca la terra. L'uscita è analogica e scende man mano che la terra si bagna: su un Uno, un sensore tenuto in aria legge circa 500–600, immerso in acqua circa 250–300. Questi numeri cambiano da sensore a sensore; prima di caricare il programma principale, leggi quindi nel monitor seriale i valori del tuo sensore in aria e in acqua e scrivili nelle costanti SOIL_DRY_RAW e SOIL_WET_RAW. Il codice converte questi due valori in 0–100% con map(). Trovi altri dettagli nell'articolo sensore di umidità del terreno.
Per la luce basta la LDR (GL5528) del tuo kit. Collegala come partitore di tensione con una resistenza da 10 kΩ: 5V → LDR → A1 → 10 kΩ → GND. Quando la luce aumenta, la resistenza della LDR scende e il valore su A1 sale; anche questo il codice lo converte in 0–100% partendo da due numeri che misuri al buio e in pieno sole. Una LDR non è un luxmetro preciso, ma risponde in modo affidabile alla domanda «è buio, è nuvoloso, c'è il sole?». Se vuoi misurare la luce con più precisione, puoi passare a un sensore di luce I2C come il BH1750. Usa lo strumento Partitore di tensione per il calcolo e l'articolo sensore di luce LDR per i dettagli.
Attuatori: relè o MOSFET?
Un pin di Arduino può fornire solo circa 20 mA, mentre ventola, riscaldatore, pompa e striscia LED assorbono centinaia di milliampere o addirittura diversi ampere. Per questo ogni carico si comanda tramite un relè o un MOSFET. Il relè è un interruttore meccanico: accende e spegne un carico in un circuito del tutto separato, in alternata o in continua, ma sa solo accendere o spegnere, fa clic a ogni commutazione e i suoi contatti hanno una vita limitata. Il MOSFET è un interruttore elettronico silenzioso; funziona solo con carichi in continua, ma può commutare centinaia di volte al secondo e quindi regolare luminosità e velocità con il PWM. In questo progetto usiamo un modulo a 4 relè per riscaldatore, ventola e pompa e un MOSFET per la luminosità della striscia LED.
| Carico | Valore tipico | Comando | Perché? |
|---|---|---|---|
| Riscaldatore PTC 12 V | 10–30 W, circa 1–2,5 A | Relè (IN2) | Corrente alta, basta acceso/spento |
| Ventola 12 V 80 mm | 0,1–0,2 A | Relè (IN1) o MOSFET | Il relè è semplice; con un MOSFET si può anche regolare la velocità in PWM |
| Mini pompa a immersione 3–6 V | 0,1–0,25 A, di più all'avvio | Relè (IN3) o MOSFET con diodo | Lavora a impulsi brevi; i disturbi del motore vanno isolati |
| Striscia LED 12 V (5050, 60 LED/m) | Circa 1,2 A al metro | MOSFET logic level | Regolazione della luminosità in PWM e funzionamento silenzioso |

A seconda dell'altezza del tubo, una mini pompa a immersione eroga circa 1–1,5 litri al minuto, quindi un impulso di 3 secondi porta nel vaso 50–75 mL d'acqua. Per questo non facciamo girare la pompa di continuo «finché la terra non è bagnata», ma a impulsi brevi: irriga 3 secondi, aspetta 60 secondi che l'acqua si distribuisca nella terra, poi misura di nuovo. Se il sensore venisse riletto prima che l'acqua lo raggiunga, la terra sembrerebbe ancora asciutta e la pompa andrebbe avanti finché il vaso non trabocca. Una pompa che gira a secco si scalda e si guasta in pochi minuti; il galleggiante nel serbatoio la blocca quando l'acqua finisce. Ogni volta che il motore si ferma, la sua bobina genera un picco di tensione inversa; un diodo collegato in inversa ai capi della pompa (diodo di ricircolo 1N5819 o 1N4007, con la fascetta verso il +) protegge da questi disturbi sia il contatto del relè sia Arduino. Scelta della pompa e modi per comandarla sono spiegati nell'articolo mini pompa dell'acqua.
Per riscaldare usa un riscaldatore PTC da 12 V. Negli elementi ceramici PTC (coefficiente di temperatura positivo) la resistenza aumenta con il calore, per cui la loro temperatura superficiale si limita da sola a un valore fisso; è molto più sicuro di un filo resistivo nudo che diventa incandescente. In un contenitore di plastica scegli un modello a bassa temperatura, con la superficie limitata a circa 80 °C. Un riscaldatore da 24 W assorbe 2 A a 12 V secondo P = V × I; nei primi secondi, da freddo, la corrente è un po' più alta. Per l'illuminazione puoi usare una striscia LED per coltivazione rosso-blu o una striscia LED bianco freddo; per la fotosintesi le piante usano soprattutto la luce rossa (circa 660 nm) e blu (circa 450 nm). Comanda la striscia con un MOSFET logic level come l'IRLZ44N: il gate va a D9 tramite una resistenza da 220 Ω e a GND tramite una da 100 kΩ; il + della striscia va ai 12 V, il − al drain del MOSFET e il source a GND. I moduli MOSFET standard come l'IRF520 non conducono del tutto con 5 V sul gate e si scaldano con carichi di diversi ampere; i dettagli sono nell'articolo usare un MOSFET con Arduino. Per calcolare corrente e potenza puoi usare lo strumento Legge di Ohm.
Piano di alimentazione: linee a 12 V e 5 V
I problemi più frequenti nelle serre intelligenti («Arduino si riavvia quando parte la pompa», «sull'LCD compaiono caratteri strani») dipendono dall'alimentazione. La soluzione più pulita è ricavare due linee da un unico alimentatore da 12 V 5 A. La linea a 12 V alimenta direttamente riscaldatore, ventole e striscia LED e va anche al jack DC dell'Arduino Uno. Gli stessi 12 V entrano in un convertitore step-down LM2596 regolato a 5 V; bobine dei relè, pompa e LCD si alimentano da questa linea a 5 V. Se togli il ponticello JD-VCC dal modulo relè, la corrente delle bobine arriva tutta dall'LM2596 e il lato Arduino accende solo i LED dei fotoaccoppiatori. I sensori (DHT22, sensore di umidità del terreno e partitore della LDR) alimentali invece dal pin 5V dello stesso Arduino: analogRead() misura rispetto ai 5 V della scheda, quindi alimentare i sensori analogici con la stessa tensione mantiene stabili le letture. Collega sempre tra loro i GND dell'alimentatore, dell'LM2596 e di Arduino.
| Carico | Linea | Corrente indicativa |
|---|---|---|
| Riscaldatore PTC (24 W) | 12 V | 2 A (di più nel primo istante, da freddo) |
| Striscia LED (1 m) | 12 V | 1–1,2 A |
| Due ventole da 80 mm | 12 V | 0,2–0,4 A |
| Arduino Uno e sensori (dal jack DC) | 12 V | Circa 0,06–0,08 A |
| Ingresso dell'LM2596 (con 1 A sul lato 5 V) | 12 V | Circa 0,5 A |
| Modulo relè (tre relè eccitati) | 5 V | 0,2–0,25 A |
| Mini pompa a immersione | 5 V | 0,1–0,25 A (di più all'avvio) |
| LCD I2C (retroilluminazione accesa) | 5 V | 20–30 mA |
Anche se tutto funziona insieme, la corrente totale si aggira sui 4–4,5 A, quindi un alimentatore da 12 V 5 A (60 W) lascia abbastanza margine. In pratica riscaldatore e ventola della temperatura non funzionano mai insieme, perché tra le loro soglie c'è un ampio intervallo. Prima di collegare l'LM2596 al circuito, misura la sua uscita con il multimetro e regolala a 5,0 V con il trimmer; appena tolto dalla confezione può lasciar passare quasi tutti i 12 V. Puoi alimentare Arduino via USB mentre carichi il codice e dal jack DC una volta installato; averli collegati entrambi insieme non è un problema per l'Uno. Altri dettagli nella guida alimentazione di Arduino.
Collegamenti e tabella dei pin
La tabella qui sotto mostra i pin usati dai tre programmi di questa guida. Sull'Uno abbiamo collegato il MOSFET che comanda la striscia LED a D9, che supporta il PWM; sull'Uno il PWM c'è solo su D3, D5, D6, D9, D10 e D11. Galleggiante e relè non hanno bisogno del PWM. Dato che l'LCD usa solo il bus I2C (A4 e A5), occupa pochissimi pin; sull'Uno restano liberi A2, A3 e alcuni pin digitali per un secondo sensore del terreno o un pulsante. Sull'ESP32 abbiamo collegato i sensori analogici ai pin ADC1 (GPIO 34 e 35), che funzionano anche con il Wi-Fi acceso. Puoi vedere la piedinatura della tua scheda nello strumento Pinout.
| Collegamento | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| DHT22 DATA | D2 | GPIO 4 | GP15 |
| Galleggiante (l'altro capo a GND) | D3 | GPIO 13 | GP14 |
| Relè IN3: pompa | D4 | GPIO 25 | GP18 |
| Relè IN1: ventola | D7 | GPIO 26 | GP16 |
| Relè IN2: riscaldatore | D8 | GPIO 27 | GP17 |
| Gate del MOSFET (tramite 220 Ω): LED | D9 | GPIO 18 | GP19 |
| AOUT del sensore di umidità del terreno | A0 | GPIO 34 | GP26 |
| Punto centrale del partitore della LDR | A1 | GPIO 35 (partitore da 3V3) | GP27 (partitore da 3V3) |
| LCD I2C SDA / SCL | A4 / A5 | GPIO 21 / 22 | GP4 / GP5 |
| Alimentazione dei sensori | 5V | 3V3 | 3V3 |
Soglia e isteresi: perché i relè sbattono?
Immagina di comandare la ventola con una soglia unica: «accendila se la temperatura supera i 30 °C, altrimenti spegnila». Quando la temperatura oscilla tra 29,9 e 30,1 °C (cosa molto frequente per il rumore del sensore), il relè si accende e si spegne a ogni lettura. Si consumano i contatti del relè e il motore della ventola, e con il riscaldatore cavi e alimentatore vengono sollecitati inutilmente a ogni accensione. La soluzione è l'isteresi: usi due soglie diverse per accendere e per spegnere. La ventola parte a 30 °C ma si ferma solo quando la temperatura è scesa a 27 °C. In questa banda morta di 3 gradi il sistema mantiene il suo ultimo stato. I termostati ambiente delle caldaie e i frigoriferi funzionano con la stessa logica.
| Controllo | Si accende | Si spegne | Banda morta |
|---|---|---|---|
| Ventola (temperatura) | ≥ 30 °C | ≤ 27 °C | 3 °C |
| Ventola (umidità) | ≥ 85% | ≤ 75% | 10% |
| Riscaldatore | ≤ 16 °C | ≥ 19 °C | 3 °C |
| Irrigazione | Terreno ≤ 35% | Terreno ≥ 50% | 15% |
| Illuminazione LED | Luce ≤ 25% | Luce ≥ 35% o dopo 4 ore | 10% |
L'ampiezza della banda morta è un compromesso: se è troppo stretta il relè commuta ancora spesso, se è troppo larga la temperatura si allontana troppo dall'obiettivo. Circa 2–3 °C per la temperatura, 8–10% per l'umidità e 10–15% per l'umidità del terreno sono un buon punto di partenza. Abbiamo anche lasciato un ampio intervallo tra i 19 °C a cui si spegne il riscaldatore e i 30 °C a cui parte la ventola, così riscaldatore e ventola della temperatura non lavorano mai l'uno contro l'altra. Nel codice l'isteresi è raccolta in una piccola funzione hysteresis(), per non doverla riscrivere a ogni controllo: se la soglia di accensione è maggiore di quella di spegnimento significa «accendi quando sale» (ventola); se è minore, «accendi quando scende» (riscaldatore, irrigazione, LED). Se vuoi anche smussare i picchi nei dati del sensore, dai un'occhiata all'articolo filtrare i dati dei sensori.
Struttura del codice: misura, decidi, agisci, mostra
La regola più importante nel codice della serra è che nessun compito aspetti un altro. Se dopo la pompa aspettassi un minuto intero con delay(60000), in quel tempo non girerebbe la ventola e il display non si aggiornerebbe. Per questo il codice è fatto di piccole funzioni che misurano il tempo con millis(), ognuna responsabile di un solo compito; loop() le chiama a turno migliaia di volte al secondo e ogni funzione fa qualcosa solo quando è il suo momento. L'irrigazione è una piccola macchina a stati con tre stati: attendi (IDLE), irriga (PUMPING) e lascia assorbire l'acqua (SOAKING). Tutte le impostazioni sono costanti all'inizio del codice, quindi per cambiare una soglia non devi cercare in tutto il programma. La logica di questo approccio è spiegata negli articoli millis() e delay() e macchina a stati con Arduino. Il codice include anche queste misure di sicurezza:
- Avvio sicuro: Il valore di «spento» viene scritto sui pin dei relè prima di chiamare pinMode(); così all'accensione di Arduino i relè non scattano nemmeno per un istante.
- Guasto del sensore: Se il DHT22 non si riesce a leggere per tre volte di fila, il codice passa a uno stato sicuro: spegne il riscaldatore, accende la ventola e mostra «Sensor hatasi» (errore sensore) sull'LCD. Un sistema che non conosce la temperatura non deve mai lasciare acceso il riscaldatore.
- Protezione del serbatoio: Quando il galleggiante segnala il serbatoio vuoto, la pompa non parte affatto e, se stava girando, si ferma subito.
- Protezione contro il traboccamento: Se in un ciclo di irrigazione l'umidità del terreno non arriva al 50% dopo sei impulsi, forse il sensore è uscito dalla terra o il tubo si è staccato; il codice ferma l'irrigazione e mostra «Sulama hatasi» (guasto irrigazione). L'avviso sparisce da solo quando la terra torna bagnata.
- Registrazione dei dati: Ogni 10 secondi il codice scrive le letture nel monitor seriale come riga separata da virgole (CSV); puoi copiare queste righe e creare grafici in Excel.
Montaggio e collaudo passo dopo passo
- Prepara il contenitore: apri i fori per ventola, presa d'aria, tubo e cavi, e sistema la scatola dell'elettronica fuori dalla serra.
- Prepara le linee di alimentazione: collega l'alimentatore 12 V a un morsetto, regola l'LM2596 a 5,0 V con il multimetro e unisci tutti i GND.
- Collega l'LCD I2C e scrivi un testo sul display. Se non mostra nulla, prova lo sketch scanner e il trimmer del contrasto dell'articolo LCD I2C.
- Aggiungi i sensori uno alla volta e provali nel monitor seriale: annota i valori del DHT22, il valore grezzo del sensore del terreno in aria e in acqua e quello della LDR al buio e al sole.
- Prova prima i relè senza carico, guardando i loro LED e ascoltando il clic. Poi collega la ventola, la pompa (con il serbatoio pieno) e per ultimo il riscaldatore.
- Collega la striscia LED tramite il MOSFET e cambia il valore di analogWrite() per vedere variare la luminosità.
- Carica il programma principale, inserisci i tuoi valori di taratura nelle costanti e fai funzionare il sistema per qualche ora sotto controllo: irriga quando non deve, c'è un relè che scatta di continuo o un componente che si scalda troppo?
Codice completo per Arduino Uno
Per compilare il codice installa dal Gestore librerie la DHT sensor library di Adafruit (insieme alla sua dipendenza Adafruit Unified Sensor) e la libreria LiquidCrystal I2C di Frank de Brabander; Wire è già inclusa nell'IDE di Arduino. L'indirizzo dell'LCD di solito è 0x27, su alcuni moduli 0x3F. Prima di caricare il codice, scrivi i valori di taratura che hai misurato nelle costanti SOIL_DRY_RAW, SOIL_WET_RAW, LDR_DARK_RAW e LDR_BRIGHT_RAW. Se il tuo galleggiante si apre con l'acqua invece di chiudersi, imposta TANK_OK su HIGH; se il tuo modulo relè è attivo alto, scambia i valori di RELAY_ON e RELAY_OFF.
1#include <Wire.h>
2#include <LiquidCrystal_I2C.h>
3#include <DHT.h>
4
5// Pinler / Pins
6const byte DHT_PIN = 2, FLOAT_PIN = 3, PUMP_RELAY = 4, FAN_RELAY = 7, HEATER_RELAY = 8;
7const byte LED_PIN = 9; // MOSFET gate (PWM)
8const byte SOIL_PIN = A0, LDR_PIN = A1;
9
10// Kalibrasyon: kendi ölçümlerini yaz / Calibration: write your own readings
11const int SOIL_DRY_RAW = 520, SOIL_WET_RAW = 260; // havada ve suda / in air and in water
12const int LDR_DARK_RAW = 60, LDR_BRIGHT_RAW = 900; // karanlık ve güneş / dark and sunlight
13
14// Eşikler (açma, kapama) / Thresholds (on, off)
15const float FAN_ON_C = 30, FAN_OFF_C = 27; // sıcaklıkta fan / fan on heat
16const float HUM_ON = 85, HUM_OFF = 75; // nemde fan / fan on humidity
17const float HEAT_ON_C = 16, HEAT_OFF_C = 19; // ısıtıcı / heater
18const float SOIL_DRY = 35, SOIL_OK = 50; // sulama başla, dur / start, stop watering
19const float DARK_ON = 25, DARK_OFF = 35; // ışık % / light %
20
21// Zamanlar / Timing
22const unsigned long READ_MS = 2500; // DHT22: en az 2 sn / at least 2 s
23const unsigned long PUMP_MS = 3000; // tek sulama darbesi / one watering pulse
24const unsigned long SOAK_MS = 60000; // suyun yayılması / let the water soak in
25const byte MAX_PULSES = 6; // fazlası arıza / more means a fault
26const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL; // günü 4 saat uzat / extend the day by 4 h
27const byte LED_LEVEL = 200; // LED parlaklığı 0-255 / LED brightness 0-255
28const byte RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
29const byte TANK_OK = LOW; // şamandıra suyla kapanır / float closes with water
30
31DHT dht(DHT_PIN, DHT22);
32LiquidCrystal_I2C lcd(0x27, 16, 2);
33
34float tempC = NAN, hum = NAN, soil = 0, light = 0;
35byte dhtErrors = 0, pulses = 0, page = 0;
36bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
37bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
38unsigned long lastRead = 0, lastLcd = 0, lastLog = 0, pumpAt = 0, darkSince = 0;
39
40enum PumpState { IDLE, PUMPING, SOAKING };
41PumpState pumpState = IDLE;
42
43// onAt > offAt: yükselince aç (fan) / turn on when rising (fan)
44// onAt < offAt: düşünce aç (ısıtıcı) / turn on when falling (heater)
45bool hysteresis(bool state, float value, float onAt, float offAt) {
46 if (onAt > offAt) {
47 if (value >= onAt) return true;
48 if (value <= offAt) return false;
49 } else {
50 if (value <= onAt) return true;
51 if (value >= offAt) return false;
52 }
53 return state; // ölü bölgede durumu koru / keep state in the dead band
54}
55
56void setRelay(byte pin, bool on) {
57 digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
58}
59
60float readPercent(byte pin, int raw0, int raw100) {
61 long sum = 0;
62 for (byte i = 0; i < 10; i++) sum += analogRead(pin); // 10 ölçüm ortalaması / average of 10
63 long pct = map(sum / 10, raw0, raw100, 0, 100);
64 return constrain(pct, 0, 100);
65}
66
67void setup() {
68 Serial.begin(9600);
69 setRelay(PUMP_RELAY, false); pinMode(PUMP_RELAY, OUTPUT); // açılışta kapalı / off at boot
70 setRelay(FAN_RELAY, false); pinMode(FAN_RELAY, OUTPUT);
71 setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
72 pinMode(LED_PIN, OUTPUT);
73 pinMode(FLOAT_PIN, INPUT_PULLUP);
74 dht.begin();
75 lcd.init();
76 lcd.backlight();
77 lcd.print(F("BlueGrays Sera"));
78 Serial.println(F("sn,sicaklik,nem,toprak,isik,fan,isitici,pompa,led")); // CSV başlığı / CSV header
79 delay(2000); // DHT22 açılış süresi / DHT22 start-up time
80 readSensors();
81 lastRead = millis();
82}
83
84void loop() {
85 unsigned long now = millis();
86 if (now - lastRead >= READ_MS) {
87 lastRead = now;
88 readSensors();
89 }
90 controlClimate();
91 controlLight(now);
92 controlPump(now);
93 updateLcd(now);
94 logCsv(now);
95}
96
97// 1) Ölç / Measure
98void readSensors() {
99 float t = dht.readTemperature();
100 float h = dht.readHumidity();
101 if (isnan(t) || isnan(h)) {
102 if (dhtErrors < 255) dhtErrors++; // okuma hatası / read error
103 } else {
104 tempC = t;
105 hum = h;
106 dhtErrors = 0;
107 }
108 soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
109 light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
110 tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
111}
112
113// 2) Fan ve ısıtıcı / Fan and heater
114void controlClimate() {
115 if (dhtErrors >= 3) { // sensör yok: güvenli durum / no sensor: safe state
116 heaterOn = false;
117 fanByTemp = true;
118 } else {
119 fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
120 fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
121 heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
122 }
123 fanOn = fanByTemp || fanByHum;
124 setRelay(FAN_RELAY, fanOn);
125 setRelay(HEATER_RELAY, heaterOn);
126}
127
128// 3) LED aydınlatma / LED grow light
129void controlLight(unsigned long now) {
130 bool wasDark = isDark;
131 isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
132 if (isDark && !wasDark) darkSince = now; // akşam oldu / dusk detected
133 ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS);
134 analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
135}
136
137// 4) Sulama: darbe + bekleme / Watering: pulse + soak
138void controlPump(unsigned long now) {
139 needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
140 if (!needWater) {
141 pulses = 0;
142 pumpFault = false; // toprak ıslandı / soil is wet again
143 }
144 switch (pumpState) {
145 case IDLE:
146 if (needWater && tankOk && !pumpFault) {
147 if (pulses >= MAX_PULSES) {
148 pumpFault = true; // nem yükselmiyor / moisture is not rising
149 } else {
150 pumpState = PUMPING;
151 pumpAt = now;
152 pulses++;
153 }
154 }
155 break;
156 case PUMPING:
157 if (now - pumpAt >= PUMP_MS || !tankOk) {
158 pumpState = SOAKING;
159 pumpAt = now;
160 }
161 break;
162 case SOAKING:
163 if (now - pumpAt >= SOAK_MS) pumpState = IDLE;
164 break;
165 }
166 setRelay(PUMP_RELAY, pumpState == PUMPING);
167}
168
169// 5) LCD: üç sayfa / three pages
170void updateLcd(unsigned long now) {
171 if (now - lastLcd < 3000) return;
172 lastLcd = now;
173 page = (page + 1) % 3;
174 lcd.clear();
175 if (page == 0) {
176 lcd.print(F("Sicaklik: "));
177 if (isnan(tempC)) lcd.print(F("--")); else lcd.print(tempC, 1);
178 lcd.print('C');
179 lcd.setCursor(0, 1);
180 lcd.print(F("Hava nemi: %"));
181 if (isnan(hum)) lcd.print(F("--")); else lcd.print(hum, 0);
182 } else if (page == 1) {
183 lcd.print(F("Toprak: %"));
184 lcd.print(soil, 0);
185 lcd.setCursor(0, 1);
186 lcd.print(F("Isik: %"));
187 lcd.print(light, 0);
188 } else {
189 lcd.print(F("F:")); lcd.print(fanOn);
190 lcd.print(F(" I:")); lcd.print(heaterOn);
191 lcd.print(F(" P:")); lcd.print(pumpState == PUMPING);
192 lcd.print(F(" L:")); lcd.print(ledOn);
193 lcd.setCursor(0, 1);
194 if (dhtErrors >= 3) lcd.print(F("Sensor hatasi"));
195 else if (!tankOk) lcd.print(F("Depo bos!"));
196 else if (pumpFault) lcd.print(F("Sulama hatasi"));
197 else lcd.print(F("Sistem normal"));
198 }
199}
200
201// 6) Seri port CSV kaydı / Serial CSV log
202void logCsv(unsigned long now) {
203 if (now - lastLog < 10000) return;
204 lastLog = now;
205 Serial.print(now / 1000); Serial.print(',');
206 Serial.print(tempC, 1); Serial.print(',');
207 Serial.print(hum, 0); Serial.print(',');
208 Serial.print(soil, 0); Serial.print(',');
209 Serial.print(light, 0); Serial.print(',');
210 Serial.print(fanOn); Serial.print(',');
211 Serial.print(heaterOn); Serial.print(',');
212 Serial.print(pumpState == PUMPING); Serial.print(',');
213 Serial.println(ledOn);
214}| Funzione | Compito | Ogni quanto? |
|---|---|---|
| readSensors() | Legge DHT22, umidità del terreno, LDR e galleggiante; converte i valori analogici in 0–100% con la media di 10 letture | Ogni 2,5 secondi |
| controlClimate() | Comanda la ventola con isteresi di temperatura o di umidità e il riscaldatore con isteresi di temperatura; passa a uno stato sicuro se il sensore si guasta | A ogni ciclo |
| controlLight() | Accende i LED in PWM quando fa buio e li spegne al massimo dopo 4 ore | A ogni ciclo |
| controlPump() | Macchina a stati dell'irrigazione: irriga 3 s, attendi 60 s, misura di nuovo; con protezione del serbatoio e contro il traboccamento | A ogni ciclo |
| updateLcd() | Alterna tre pagine: valori dell'aria, umidità del terreno e luce, stato dei dispositivi | Ogni 3 secondi |
| logCsv() | Scrive letture e stati nel monitor seriale come riga CSV | Ogni 10 secondi |
La prima pagina dell'LCD mostra temperatura e umidità dell'aria, la seconda umidità del terreno e livello di luce, la terza lo stato dei dispositivi: «F:1 I:0 P:0 L:1» significa che la ventola gira, che riscaldatore (I = ısıtıcı) e pompa sono spenti e che i LED sono accesi. La riga in basso mostra uno dei messaggi «Sistem normal» (tutto regolare), «Depo bos!» (serbatoio vuoto), «Sulama hatasi» (guasto irrigazione) o «Sensor hatasi» (errore sensore); puoi tradurre facilmente questi testi nel codice. Compilato, il codice occupa circa il 31% della memoria di programma dell'Uno e il 25% della sua RAM, quindi c'è molto spazio per aggiungere un secondo sensore del terreno, un buzzer o un modulo orologio RTC. Poiché l'Uno non ha un vero orologio, la regola dei LED funziona come «al massimo 4 ore dopo il tramonto», un buon modo per allungare le brevi giornate invernali. Se vuoi accendere i LED ogni giorno a orari fissi, aggiungi un modulo RTC o passa alla versione con ESP32 qui sotto.
Controllare e irrigare dallo smartphone con un ESP32

L'Uno non ha il Wi-Fi. Se vuoi vedere lo stato della serra sullo smartphone e irrigare a distanza, sostituisci il cervello con un ESP32. Il codice seguente porta la stessa logica di controllo sull'ESP32 e avvia un piccolo web server. Con lo smartphone sulla stessa rete Wi-Fi, apri nel browser l'indirizzo IP che compare nel monitor seriale: vedrai temperatura, umidità, umidità del terreno, luce, stato del serbatoio e se ogni dispositivo è acceso o spento; la pagina si ricarica da sola ogni 10 secondi. Il pulsante Water now (irriga ora) fa girare la pompa una volta per 3 secondi se nel serbatoio c'è acqua. All'avvio l'ESP32 prende l'ora da Internet (NTP); così i LED si accendono solo tra le 6:00 e le 20:00 e solo se è buio, e di notte le piante riposano.
1#include <WiFi.h>
2#include <WebServer.h>
3#include <DHT.h>
4#include <time.h>
5
6const char* ssid = "AgAdi"; // Wi-Fi adın / your Wi-Fi name
7const char* password = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
8
9// Pinler / Pins
10const int DHT_PIN = 4, FLOAT_PIN = 13, PUMP_RELAY = 25, FAN_RELAY = 26, HEATER_RELAY = 27;
11const int LED_PIN = 18; // MOSFET gate (PWM)
12const int SOIL_PIN = 34, LDR_PIN = 35; // ADC1: Wi-Fi açıkken çalışır / works with Wi-Fi on
13
14// Kalibrasyon (0-4095): kendin ölç / Calibration (0-4095): measure your own
15const int SOIL_DRY_RAW = 2800, SOIL_WET_RAW = 1200;
16const int LDR_DARK_RAW = 200, LDR_BRIGHT_RAW = 3800;
17
18// Eşikler (açma, kapama) / Thresholds (on, off)
19const float FAN_ON_C = 30, FAN_OFF_C = 27, HUM_ON = 85, HUM_OFF = 75;
20const float HEAT_ON_C = 16, HEAT_OFF_C = 19, SOIL_DRY = 35, SOIL_OK = 50;
21const float DARK_ON = 25, DARK_OFF = 35;
22const int LIGHT_FROM = 6, LIGHT_TO = 20; // LED yalnız 06:00-20:00 / LEDs only 06:00-20:00
23
24const unsigned long READ_MS = 2500, PUMP_MS = 3000, SOAK_MS = 60000;
25const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL; // saat yoksa yedek kural / fallback without a clock
26const int MAX_PULSES = 6, LED_LEVEL = 200;
27const int RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
28const int TANK_OK = LOW; // şamandıra suyla kapanır / float closes with water
29
30DHT dht(DHT_PIN, DHT22);
31WebServer server(80);
32
33float tempC = NAN, hum = NAN, soil = 0, light = 0;
34int dhtErrors = 0, pulses = 0;
35bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
36bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
37unsigned long lastRead = 0, pumpAt = 0, darkSince = 0;
38
39enum PumpState { IDLE, PUMPING, SOAKING };
40PumpState pumpState = IDLE;
41
42bool hysteresis(bool state, float value, float onAt, float offAt) {
43 if (onAt > offAt) { // yükselince aç / turn on when rising
44 if (value >= onAt) return true;
45 if (value <= offAt) return false;
46 } else { // düşünce aç / turn on when falling
47 if (value <= onAt) return true;
48 if (value >= offAt) return false;
49 }
50 return state;
51}
52
53void setRelay(int pin, bool on) {
54 digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
55}
56
57float readPercent(int pin, int raw0, int raw100) {
58 long sum = 0;
59 for (int i = 0; i < 16; i++) sum += analogRead(pin); // 16 ölçüm ortalaması / average of 16
60 long pct = map(sum / 16, raw0, raw100, 0, 100);
61 return constrain(pct, 0, 100);
62}
63
64int hourNow() {
65 struct tm t;
66 if (!getLocalTime(&t, 0)) return -1; // saat henüz yok / time not set yet
67 return t.tm_hour;
68}
69
70void readSensors() {
71 float t = dht.readTemperature();
72 float h = dht.readHumidity();
73 if (isnan(t) || isnan(h)) {
74 dhtErrors++;
75 } else {
76 tempC = t;
77 hum = h;
78 dhtErrors = 0;
79 }
80 soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
81 light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
82 tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
83}
84
85void controlClimate() {
86 if (dhtErrors >= 3) { // sensör yok: güvenli durum / no sensor: safe state
87 heaterOn = false;
88 fanByTemp = true;
89 } else {
90 fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
91 fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
92 heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
93 }
94 fanOn = fanByTemp || fanByHum;
95 setRelay(FAN_RELAY, fanOn);
96 setRelay(HEATER_RELAY, heaterOn);
97}
98
99void controlLight(unsigned long now) {
100 bool wasDark = isDark;
101 isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
102 if (isDark && !wasDark) darkSince = now;
103 int h = hourNow();
104 if (h >= 0) ledOn = isDark && h >= LIGHT_FROM && h < LIGHT_TO; // saat kuralı / clock rule
105 else ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS); // yedek kural / fallback rule
106 analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
107}
108
109void controlPump(unsigned long now) {
110 needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
111 if (!needWater) {
112 pulses = 0;
113 pumpFault = false;
114 }
115 if (pumpState == IDLE && needWater && tankOk && !pumpFault) {
116 if (pulses >= MAX_PULSES) {
117 pumpFault = true; // nem yükselmiyor / moisture is not rising
118 } else {
119 pumpState = PUMPING;
120 pumpAt = now;
121 pulses++;
122 }
123 } else if (pumpState == PUMPING && (now - pumpAt >= PUMP_MS || !tankOk)) {
124 pumpState = SOAKING;
125 pumpAt = now;
126 } else if (pumpState == SOAKING && now - pumpAt >= SOAK_MS) {
127 pumpState = IDLE;
128 }
129 setRelay(PUMP_RELAY, pumpState == PUMPING);
130}
131
132String row(const char* name, const String& value) {
133 return String("<tr><td>") + name + "</td><td><b>" + value + "</b></td></tr>";
134}
135
136String onOff(bool on) {
137 return on ? "ON" : "OFF";
138}
139
140void handleRoot() {
141 String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>"
142 "<meta name='viewport' content='width=device-width,initial-scale=1'>"
143 "<meta http-equiv='refresh' content='10'><title>BlueGrays Sera</title>"
144 "<style>body{font-family:sans-serif;max-width:420px;margin:auto;padding:12px}"
145 "td{padding:8px;border-bottom:1px solid #ddd}"
146 "a{display:block;margin-top:16px;padding:16px;border-radius:10px;background:#2e7d32;"
147 "color:#fff;text-align:center;text-decoration:none;font-size:18px}</style>"
148 "</head><body><h2>BlueGrays Sera</h2><table>";
149 html += row("Sıcaklık / Temperature", String(tempC, 1) + " °C");
150 html += row("Hava nemi / Humidity", "%" + String(hum, 0));
151 html += row("Toprak nemi / Soil", "%" + String(soil, 0));
152 html += row("Işık / Light", "%" + String(light, 0));
153 html += row("Fan", onOff(fanOn));
154 html += row("Isıtıcı / Heater", onOff(heaterOn));
155 html += row("Pompa / Pump", onOff(pumpState == PUMPING));
156 html += row("LED", onOff(ledOn));
157 html += row("Depo / Tank", tankOk ? "OK" : "BOŞ / EMPTY");
158 if (pumpFault) html += row("Uyarı / Warning", "Sulama hatası / Watering fault");
159 html += "</table><a href='/water'>Şimdi sula / Water now</a></body></html>";
160 server.send(200, "text/html", html);
161}
162
163void handleWater() { // elle tek darbe / one manual pulse
164 if (pumpState == IDLE && tankOk) {
165 pumpState = PUMPING;
166 pumpAt = millis();
167 }
168 server.sendHeader("Location", "/");
169 server.send(303);
170}
171
172void setup() {
173 Serial.begin(115200);
174 setRelay(PUMP_RELAY, false); pinMode(PUMP_RELAY, OUTPUT);
175 setRelay(FAN_RELAY, false); pinMode(FAN_RELAY, OUTPUT);
176 setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
177 pinMode(LED_PIN, OUTPUT);
178 pinMode(FLOAT_PIN, INPUT_PULLUP);
179 dht.begin();
180 WiFi.begin(ssid, password);
181 while (WiFi.status() != WL_CONNECTED) {
182 delay(500);
183 Serial.print('.');
184 }
185 Serial.println();
186 Serial.println(WiFi.localIP()); // bu adresi telefonda aç / open this address on your phone
187 configTime(3 * 3600, 0, "pool.ntp.org"); // Türkiye UTC+3; kendi saat dilimini yaz / set your own time zone
188 server.on("/", handleRoot);
189 server.on("/water", handleWater);
190 server.begin();
191 readSensors();
192 lastRead = millis();
193}
194
195void loop() {
196 server.handleClient();
197 unsigned long now = millis();
198 if (now - lastRead >= READ_MS) {
199 lastRead = now;
200 readSensors();
201 }
202 controlClimate();
203 controlLight(now);
204 controlPump(now);
205}Ecco cosa cambia rispetto al codice dell'Uno: l'ADC dell'ESP32 è a 12 bit e restituisce valori da 0 a 4095, quindi devi rifare la taratura; dato che alimenti i sensori da 3V3, anche i valori grezzi saranno diversi. Il fuso orario si imposta nella riga configTime(); abbiamo messo 3 × 3600 secondi per la Turchia (UTC+3). In Italia sostituisci la riga con configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org"), che gestisce anche l'ora legale. Se non c'è Internet o l'ora non è ancora stata ricevuta, la regola dei LED torna a quella dell'Uno, «al massimo 4 ore». La pagina non ha password, quindi chiunque sulla tua rete di casa potrebbe avviare la pompa; aggiungere una password semplice con la funzione authenticate() della libreria WebServer è una buona abitudine. Se vuoi un LCD anche sull'ESP32, puoi copiare così com'è la funzione updateLcd() dal codice dell'Uno; SDA è GPIO 21 e SCL è GPIO 22.
| Metodo | Accesso fuori casa | Difficoltà | Ideale per |
|---|---|---|---|
| Web server sull'ESP32 (il codice di questa guida) | No, solo sulla stessa rete | Media | Presentazione scolastica, dimostrazione senza Internet |
| Blynk IoT | Sì | Facile | Indicatori, grafici e notifiche pronti all'uso |
| Bot di Telegram | Sì | Media | Avvisi «serbatoio vuoto» e «temperatura troppo alta» |
| Fogli Google | Sì | Media | Registrazione dei dati per settimane e grafici |
| MQTT e Home Assistant | Sì, richiede configurazione | Avanzata | Vera domotica per casa e giardino |
Per una presentazione scolastica il web server dell'ESP32 è la scelta più sicura: non dipende da nessun servizio cloud e funziona con la sua rete anche se in sala manca Internet. Se vuoi controllare la serra anche fuori casa, Blynk IoT con i suoi indicatori e grafici pronti è la strada più semplice. Per ricevere un messaggio sullo smartphone quando il serbatoio si svuota o la temperatura diventa pericolosa, leggi la guida al bot di Telegram con ESP32; per raccogliere settimane di dati per la tua relazione, ESP32 con Fogli Google; e per collegare la serra a una vera domotica, MQTT e Home Assistant.
Versione MicroPython per Raspberry Pi Pico
Puoi gestire la stessa serra anche con un Raspberry Pi Pico in MicroPython. Il firmware MicroPython del Pico include già un driver DHT, quindi non devi copiare altri file. Gli ingressi analogici sono solo GP26, GP27 e GP28, e read_u16() restituisce valori da 0 a 65535; misura i tuoi numeri di taratura su questa scala. Poiché il Pico lavora a 3,3 V, alimenta i sensori da 3V3 e il modulo relè da VBUS (i 5 V dell'USB) o dalla linea dell'LM2596. Il codice seguente contiene tutte le regole di ventola, riscaldatore, LED e irrigazione e scrive i valori nella shell di Thonny. Se salvi il file sul Pico con il nome main.py, la scheda funziona anche senza essere collegata al computer. Se usi il Pico W con Wi-Fi, puoi aggiungere il metodo dell'articolo web server con Pico W per seguire i valori dallo smartphone.
1from machine import Pin, ADC, PWM
2import dht
3import time
4
5sensor = dht.DHT22(Pin(15)) # GP15: DHT22 DATA
6soil_adc = ADC(26) # GP26: toprak nem AOUT / soil moisture AOUT
7ldr_adc = ADC(27) # GP27: LDR bölücü (3V3) / LDR divider (3V3)
8tank = Pin(14, Pin.IN, Pin.PULL_UP) # şamandıra: suyla 0 / float switch: 0 with water
9fan = Pin(16, Pin.OUT, value=1) # aktif LOW röleler kapalı başlar / active-LOW relays start off
10heater = Pin(17, Pin.OUT, value=1)
11pump = Pin(18, Pin.OUT, value=1)
12led = PWM(Pin(19)) # MOSFET gate
13led.freq(1000)
14
15SOIL_DRY_RAW, SOIL_WET_RAW = 50000, 22000 # kendin ölç / measure your own
16LDR_DARK_RAW, LDR_BRIGHT_RAW = 2000, 60000
17EXTRA_LIGHT_MS = 4 * 3600 * 1000 # günü 4 saat uzat / extend the day by 4 h
18
19
20def percent(adc, raw0, raw100):
21 raw = sum(adc.read_u16() for _ in range(10)) // 10
22 pct = (raw - raw0) * 100 // (raw100 - raw0)
23 return max(0, min(100, pct))
24
25
26def hysteresis(state, value, on_at, off_at):
27 if on_at > off_at: # yükselince aç / turn on when rising
28 if value >= on_at:
29 return True
30 if value <= off_at:
31 return False
32 else: # düşünce aç / turn on when falling
33 if value <= on_at:
34 return True
35 if value >= off_at:
36 return False
37 return state # ölü bölge / dead band
38
39
40def relay(pin, on):
41 pin.value(0 if on else 1)
42
43
44t = h = None
45errors = pulses = 0
46fan_t = fan_h = heat_on = need_water = dark = led_on = False
47pump_state, pump_at, dark_since = "idle", 0, 0
48soil = light = 0
49tank_ok = True
50time.sleep(2) # DHT22 açılış süresi / DHT22 start-up time
51last_read = time.ticks_add(time.ticks_ms(), -2500)
52
53while True:
54 now = time.ticks_ms()
55 if time.ticks_diff(now, last_read) >= 2500: # 2,5 sn'de bir ölç / measure every 2.5 s
56 last_read = now
57 try:
58 sensor.measure()
59 t, h = sensor.temperature(), sensor.humidity()
60 errors = 0
61 except OSError:
62 errors += 1 # okuma hatası / read error
63 soil = percent(soil_adc, SOIL_DRY_RAW, SOIL_WET_RAW)
64 light = percent(ldr_adc, LDR_DARK_RAW, LDR_BRIGHT_RAW)
65 tank_ok = tank.value() == 0
66
67 if errors >= 3 or t is None: # güvenli durum / safe state
68 heat_on, fan_t = False, True
69 else:
70 fan_t = hysteresis(fan_t, t, 30, 27)
71 fan_h = hysteresis(fan_h, h, 85, 75)
72 heat_on = hysteresis(heat_on, t, 16, 19)
73 relay(fan, fan_t or fan_h)
74 relay(heater, heat_on)
75
76 was_dark = dark
77 dark = hysteresis(dark, light, 25, 35)
78 if dark and not was_dark:
79 dark_since = now # akşam oldu / dusk detected
80 led_on = dark and time.ticks_diff(now, dark_since) < EXTRA_LIGHT_MS
81 led.duty_u16(50000 if led_on else 0)
82
83 need_water = hysteresis(need_water, soil, 35, 50)
84 if not need_water:
85 pulses = 0
86 print("T", t, "N", h, "Toprak", soil, "Isik", light, "Pompa", pump_state,
87 "Depo OK" if tank_ok else "Depo BOS", "HATA" if pulses >= 6 else "")
88
89 # Sulama: 3 sn darbe, 60 sn bekleme / Watering: 3 s pulse, 60 s soak
90 if pump_state == "idle" and need_water and tank_ok and pulses < 6:
91 pump_state, pump_at = "pump", now
92 pulses += 1
93 elif pump_state == "pump" and (time.ticks_diff(now, pump_at) >= 3000 or not tank_ok):
94 pump_state, pump_at = "soak", now
95 elif pump_state == "soak" and time.ticks_diff(now, pump_at) >= 60000:
96 pump_state = "idle"
97 relay(pump, pump_state == "pump")
98 time.sleep_ms(100)Consigli per la presentazione a scuola o alla fiera della scienza
La serra intelligente è un tema così diffuso che le giurie vedono decine di modellini simili. A farti emergere non sarà solo mostrare che il sistema funziona, ma rispondere a una domanda con delle misure. In una fiera della scienza obiettivo, metodo e risultato atteso devono essere chiari; nei concorsi di ricerca ci si aspetta un esperimento comparativo con dati. Una buona domanda di ricerca per questa serra è: «Quanta acqua fa risparmiare l'irrigazione basata sull'umidità del terreno rispetto a quella fatta ogni giorno alla stessa ora, e che effetto ha sulla crescita della pianta?»
- Forma due gruppi: Dividi piantine della stessa specie e della stessa altezza in due gruppi. Irriga un gruppo con il sistema a sensori e l'altro a mano con una quantità fissa ogni giorno; luce e temperatura devono essere uguali per entrambi.
- Misura e registra: Tieni una tabella con la quantità d'acqua settimanale (misurando il serbatoio, in mL), l'altezza delle piante (cm) e il numero di foglie. L'uscita CSV del codice o un registro su Fogli Google ti danno già pronto il grafico di temperatura e umidità.
- Disegna uno schema a blocchi: Sensori a sinistra, Arduino al centro, attuatori a destra; etichetta le frecce con le frasi delle regole («terreno < 35% → pompa»). La giuria non leggerà il tuo codice, ma si ricorderà di questo schema.
- Spiega l'isteresi: Due grafici che mostrano quante volte in un'ora si accendono e si spengono una ventola a soglia unica e una con isteresi sono il modo migliore per far vedere alla giuria il lato ingegneristico del progetto.
- Scrivi un copione per la dimostrazione dal vivo: Scalda il DHT22 nel palmo per far partire la ventola, sposta il sensore del terreno in un vaso asciutto per mostrare gli impulsi della pompa, copri la LDR con la mano per accendere i LED. Ogni dimostrazione deve durare 10–20 secondi.
- Collegalo alla vita reale: Parla di problemi reali come il consumo di acqua ed energia nelle serre, le malattie fungine causate dall'eccesso di umidità e il carico di lavoro degli agricoltori; mostra con i numeri a quale di questi risponde il tuo sistema.
- Prepara un piano B: Fai un video del sistema in funzione; porta cavi, una pompa e un alimentatore di scorta; scrivi su un foglio i valori di taratura e i dati del Wi-Fi.
- Preparati alle domande della giuria: Tieni pronte risposte brevi a «Cosa succede se un sensore si guasta?», «Cosa succede alle piante se manca la corrente?» e «Come lo porteresti in una serra vera?». Lo stato sicuro e la protezione contro il traboccamento del codice sono le risposte migliori.
Errori comuni e soluzioni
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| Arduino si riavvia o l'LCD impazzisce quando parte la pompa | Pompa e relè alimentati dal pin 5V di Arduino, disturbi del motore | Alimenta pompa e bobine dei relè dall'LM2596, unisci i GND, aggiungi un diodo in inversa e un condensatore da 470 µF sulla pompa |
| I relè scattano in continuazione | Si usa una soglia unica o le letture sono rumorose | Usa soglie diverse per accensione e spegnimento (isteresi) e fai la media delle letture analogiche |
| L'acqua continua a scorrere dopo lo stop della pompa | Effetto sifone: l'estremità del tubo è sotto il livello dell'acqua del serbatoio | Abbassa il serbatoio o pratica un piccolo foro d'aria in cima al tubo |
| L'umidità del terreno è sempre 0% o sempre 100% | Valori di taratura sbagliati o AOUT sul pin sbagliato | Misura il valore grezzo del sensore in aria e in acqua e scrivilo nelle costanti |
| I LED si accendono e si spengono di continuo | La LDR vede la luce dei LED | Sposta la LDR fuori dalla serra, rivolta verso il cielo |
| Il DHT22 restituisce nan | Pin sbagliato, nessuna resistenza di pull-up o letture più frequenti di una ogni 2 secondi | Controlla il pin, usa un modulo o aggiungi 10 kΩ tra DATA e VCC |
| La temperatura sembra più alta del dovuto | Il sole o la luce dei LED colpiscono direttamente il sensore | Fagli ombra con un cappuccio bianco forato e allontanalo dal riscaldatore |
| Gocce d'acqua e muffa sulle pareti | Umidità troppo alta, ventilazione insufficiente | Abbassa la soglia di umidità, allarga la presa d'aria, irriga meno |
| Sull'ESP32 la lettura del terreno impazzisce quando parte il Wi-Fi | Il sensore è su un pin ADC2 | Spostalo su un pin ADC1 tra GPIO 32 e 39 |
| Con l'ESP32 il relè non si diseccita mai | Un HIGH da 3,3 V non spegne un modulo da 5 V attivo basso | Separa JD-VCC o usa un modulo relè comandabile a 3,3 V |
Una serra intelligente unisce in un solo progetto lettura dei sensori, taratura, decisioni con isteresi, comando di relè e MOSFET, piano di alimentazione e monitoraggio senza fili, e alla fine fa crescere qualcosa di davvero vivo: una pianta. Comincia da irrigazione e ventilazione, prova ogni parte da sola e poi aggiungi riscaldatore, LED e smartphone. Nell'app BlueGrays trovi, passo dopo passo e con schemi e codice, progetti per misurare temperatura e umidità con un DHT11, leggere la luce con una LDR, una stazione di irrigazione intelligente e un registratore di temperatura con DHT22.
Domande frequenti
Cosa serve per una serra intelligente con Arduino?
Per una serra intelligente di base bastano un Arduino Uno o un ESP32, un sensore di temperatura e umidità DHT22, un sensore capacitivo di umidità del terreno, una LDR con una resistenza da 10 kΩ, un modulo relè, una mini pompa a immersione e una ventola. La versione completa aggiunge un riscaldatore PTC da 12 V, una striscia LED comandata da un MOSFET, un LCD I2C, un interruttore a galleggiante, un alimentatore da 12 V 5 A e un LM2596. Come struttura, una scatola portaoggetti trasparente con coperchio è la soluzione più semplice ed economica.
Come funziona una serra intelligente?
Una serra intelligente funziona con un ciclo misura, decidi, agisci, mostra. I sensori misurano temperatura, umidità, umidità del terreno e luce; Arduino confronta questi valori con delle soglie e accende o spegne ventola, riscaldatore, pompa e LED tramite relè o un MOSFET. I risultati compaiono su un LCD e, con un ESP32, anche sullo smartphone.
Meglio DHT11 o DHT22 per una serra?
Scegli il DHT22. Il DHT11 è preciso solo a ±2 °C tra 0 e 50 °C e non vede l'umidità sopra il 90%, mentre in serra l'umidità può superare il 90% al mattino e la temperatura scendere sotto zero d'inverno. Il DHT22 misura da −40 a 80 °C con ±0,5 °C e l'umidità con circa ±2%, e il suo codice è quasi identico a quello del DHT11.
Quanto costa una serra intelligente con Arduino?
I prezzi dipendono dal venditore, ma esclusa la scheda la versione base (DHT22, sensore di umidità del terreno, LDR, relè, pompa e ventola) costa di solito circa 25–40 €. La versione completa con riscaldatore, striscia LED, LCD, alimentatore 12 V e contenitore arriva a circa 70–120 €. Se hai già uno starter kit Arduino, il costo scende parecchio.
Come controllo la serra dallo smartphone?
Il modo più semplice è sostituire il cervello con un ESP32 e avviare un web server; dal browser dello smartphone, sulla stessa rete Wi-Fi, vedi i valori e avvii la pompa. Per l'accesso fuori casa puoi usare Blynk IoT, per gli avvisi un bot di Telegram e per la registrazione dei dati a lungo termine Fogli Google.
Come presento una serra intelligente alla fiera della scienza?
Invece di mostrare solo un modellino che funziona, poni una domanda di ricerca misurabile; per esempio misura per alcune settimane, con due gruppi di piante, quanta acqua fa risparmiare l'irrigazione a sensori rispetto a quella a orario fisso. Presenta consumo d'acqua, altezza delle piante e andamento di temperatura e umidità con tabelle e grafici, spiega il sistema con uno schema a blocchi e prepara un breve copione per la dimostrazione dal vivo.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
Temperatura e umidità con DHT11
Quanti gradi ci sono nella tua stanza, e l'aria è umida o secca? Il sensore DHT11 misura temperatura e umidità, e l'Arduino scrive i valori su uno schermo LCD a due righe, aggiornandoli ogni paio di s

Misurare l'intensità luminosa con LDR
Vuoi vedere quanto è luminosa la tua stanza sotto forma di numero? L'LDR, un piccolo componente sensibile alla luce, misura la luce ambiente e l'Arduino scrive il valore sullo schermo del computer ogn

Lettura dell'intensità luminosa con il modulo LDR ESP32
Monitoraggio in tempo reale sul Serial Monitor del valore grezzo dell'ADC del modulo LDR e della percentuale relativa di luce calcolata dai valori di riferimento al buio e alla luce. Questa percentuale non è una misurazione in lux. Spiega il principio di calibrazione del modulo. È consigliabile eseguirlo prima del Progetto 10, poiché serve a determinare il valore di soglia.

Stazione di irrigazione intelligente
Un giardiniere che annaffia le tue piante da solo, così non si seccano mentre sei in vacanza. Un sensore di umidità misura quanto è bagnata la terra; quando diventa secca, l'Arduino accende la pompa d

ESP32 DHT22 Temperatura e umidità (con display LCD)
Si tratta di un progetto che misura la temperatura e l'umidità dell'ambiente tramite il sensore DHT22 e le visualizza in tempo reale su un display LCD I2C 16x2. I dati vengono letti dal sensore utilizzando il protocollo one-wire tramite la libreria Adafruit DHT. Questo progetto costituisce la base per stazioni meteorologiche IoT, sistemi di automazione domestica intelligente o sistemi di monitoraggio ambientale nelle serre. La comunicazione con il DHT22 avviene tramite il pin GPIO 4 dell'ESP32.

Data Logger di Temperatura ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
Costruisci un data logger di temperatura con timestamp usando ESP32! Leggi temperatura/umidità dal DHT22 ogni minuto, ottieni l'ora reale dal DS1307 RTC e registra su scheda microSD in formato CSV. L'OLED mostra dal vivo temperatura (font grande), percentuale di umidità, timestamp e contatore di log. Il file /temp_log.csv sulla scheda SD si apre in Excel, Google Sheets o Python pandas per l'analisi — perfetto per il monitoraggio di serre, tracciamento della temperatura domestica, raccolta

Illuminazione Intelligente ESP32 (PIR + LDR + Relè + OLED)
Trasforma il tuo corridoio, bagno o dispensa in illuminazione intelligente con ESP32! Utilizzando un sensore di movimento PIR, un sensore di luce LDR e un modulo relè, costruiamo lo scenario classico "la luce si accende quando viene rilevato movimento al buio" — se l'ambiente è già luminoso, il movimento NON attiva la luce, risparmiando energia durante il giorno. La luce rimane accesa per 30 secondi; se il movimento continua, il timer si azzera e la luce rimane accesa più a lungo. Il display OLE
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 200 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

