Serre connectée avec Arduino : capteurs, arrosage et code
Serre connectée avec Arduino : DHT22, humidité du sol et LDR, ventilateur, chauffage, pompe et LED pilotés par relais et MOSFET, hystérésis, LCD et ESP32.
Une serre connectée avec Arduino est un système d'automatisation dans lequel un Arduino ou un ESP32 mesure la température, l'humidité de l'air, l'humidité du sol et la lumière de la serre, puis pilote tout seul le ventilateur, le chauffage, la pompe à eau et l'éclairage horticole. C'est l'un des sujets préférés des fêtes de la science, des concours scolaires et des cours de technologie, parce que le résultat se voit : quand la terre sèche, la pompe démarre ; quand il fait chaud à l'intérieur, le ventilateur tourne ; le soir, les LED s'allument. En plus, entre une maquette fabriquée dans une boîte de rangement et une vraie mini-serre sur ton balcon, il n'y a presque aucune différence de circuit ni de code.
Dans ce guide, on commence par ce dont les plantes ont réellement besoin, puis on construit la maquette pas à pas : la liste du matériel et le coût approximatif, le câblage des capteurs (DHT22, capteur capacitif d'humidité du sol, LDR) et des actionneurs (ventilateur, chauffage, pompe et LED via des relais et un MOSFET), le plan d'alimentation et le tableau des broches. Ensuite, on explique la logique seuil et hystérésis qui empêche les relais de cliqueter sans arrêt, le code Arduino complet sans delay() et l'écran LCD 16x2. Dans les dernières parties, tu trouveras la surveillance depuis ton téléphone avec un ESP32, une version MicroPython pour le Raspberry Pi Pico, des conseils pour les présentations scolaires et les concours, et les erreurs les plus courantes.





Comment fonctionne une serre connectée ?
Toute serre connectée suit la même boucle en quatre étapes : mesurer, décider, agir, afficher. Les capteurs mesurent l'environnement : quelle température fait-il à l'intérieur, l'air est-il humide, la terre a-t-elle séché, y a-t-il assez de lumière ? L'Arduino compare ces valeurs aux règles que tu as fixées : « si la température dépasse 30 °C, mets le ventilateur en marche », « si l'humidité du sol passe sous 35 %, arrose ». Les actionneurs appliquent la décision : les relais allument et éteignent le ventilateur, le chauffage et la pompe, et un MOSFET règle la luminosité du ruban LED. Enfin, l'écran LCD et ton téléphone t'affichent l'état actuel de la serre. Les ordinateurs de gestion climatique des serres professionnelles fonctionnent exactement avec cette boucle ; ils utilisent simplement plus de capteurs et de plus gros moteurs.
| Que mesure-t-on ? | Capteur | Actionneur | Règle (exemple) |
|---|---|---|---|
| Température de l'air | DHT22 | Ventilateur et chauffage | Le ventilateur démarre à 30 °C, le chauffage s'allume à 16 °C |
| Humidité relative | DHT22 | Ventilateur | Le ventilateur démarre à 85 % et s'arrête à 75 % |
| Humidité du sol | Capteur capacitif d'humidité du sol | Mini-pompe immergée | Arrose par impulsions de 3 secondes sous 35 % |
| Lumière | LDR | Ruban LED (MOSFET) | À la tombée de la nuit, les LED restent allumées 4 heures au maximum |
| Eau du réservoir | Interrupteur à flotteur | Verrouillage de la pompe | Si le réservoir est vide, la pompe ne démarre jamais |
De quoi les plantes ont-elles besoin ? Température, humidité et lumière cibles
Avant d'écrire les règles, il faut savoir ce que tu vises. La plupart des plantes veulent des températures différentes le jour et la nuit ; un léger rafraîchissement nocturne aide la plante à se reposer. Si l'humidité de l'air reste longtemps au-dessus de 85 %, l'eau se condense sur les feuilles et les maladies fongiques comme la moisissure et le mildiou se propagent vite ; c'est pourquoi, dans une serre, le ventilateur ne sert pas seulement à rafraîchir mais aussi à évacuer l'humidité. Côté lumière, la plupart des semis de légumes veulent 14 à 16 heures par jour, mais ils ont aussi besoin d'une période d'obscurité d'au moins 6 à 8 heures ; laisser les LED allumées toute la nuit fait plus de mal que de bien. Les valeurs ci-dessous sont un bon point de départ ; adapte-les à ce que tu cultives.
| Plante | Jour | Nuit | Humidité relative | Remarque |
|---|---|---|---|---|
| Tomate | 21–27 °C | 15–18 °C | 60–75 % | Au-dessus de 32 °C, les fleurs peuvent tomber |
| Poivron | 21–29 °C | 16–20 °C | 60–75 % | Plus sensible au froid que la tomate |
| Laitue et épinard | 15–20 °C | 10–15 °C | 60–70 % | Monte vite en graines par temps chaud et jours longs |
| Fraise | 18–24 °C | 10–13 °C | 60–75 % | Le risque de pourriture grise augmente avec l'humidité |
| Semis et germination | 20–25 °C | 18–20 °C | 70–85 % | La température du substrat compte plus que celle de l'air |
Fabriquer la maquette de serre
Le boîtier le plus simple et le moins cher est une boîte de rangement en plastique transparent avec couvercle : une boîte de 40 à 60 litres suffit pour deux ou trois pots, elle est étanche et se perce facilement au cutter ou avec une pointe chauffée. Pour un rendu plus réaliste, monte une ossature en tasseaux de bois et habille-la de plaques transparentes en plexiglas ou en PETG de 2 à 3 mm ; une serre avec un toit à deux pentes fait bien plus d'effet lors d'une présentation. Environ 50×35×40 cm est une bonne taille de départ. Quel que soit le boîtier, le couvercle ou une paroi latérale doit pouvoir s'ouvrir : tu vas passer la main à l'intérieur pour arroser, déplacer le capteur et ranger les câbles.
- L'électronique dehors : L'intérieur d'une serre est humide et l'eau se condense sur les parois le matin. Place l'Arduino, le module relais et l'alimentation dans un boîtier séparé, à l'extérieur de la serre ; à l'intérieur, il ne reste que les capteurs, le ventilateur, le chauffage, le tuyau de la pompe et les LED.
- Ventilation : L'air chaud monte : fixe donc le ventilateur dans la partie haute d'une des petites parois, en extraction vers l'extérieur. Dans le bas de la paroi opposée, découpe une entrée d'air de la taille du ventilateur et couvre-la d'une moustiquaire ; l'air frais entre par le bas, l'air chaud et humide sort par le haut.
- Chauffage : Fixe le chauffage près du fond, sur une plaque d'aluminium ou un carreau de céramique, à au moins 5 cm des parois en plastique et des plantes. L'air chaud circule dans toute la serre en montant.
- Éclairage LED : Colle le ruban LED sous le couvercle, 15 à 30 cm au-dessus des plantes. Collé sur un fin profilé en aluminium, le ruban dissipe mieux sa chaleur et l'adhésif ne se décolle pas avec le temps.
- Capteurs : Place le DHT22 à hauteur des plantes, à l'abri du soleil direct et de la lumière directe des LED. Enfonce le capteur d'humidité dans la zone des racines, loin du bord du pot. La LDR ne doit pas voir les LED : installe-la hors de la serre, sur le toit, tournée vers le ciel.
- Réservoir d'eau : Un récipient de 1 à 2 litres suffit pour la pompe et le flotteur. Mets des soucoupes sous les pots ; l'eau en trop s'y accumule au lieu d'inonder le fond de la boîte.
Liste du matériel et coût
| Composant | Qté | Rôle | Niveau de prix |
|---|---|---|---|
| Arduino Uno R3 ou ESP32 DevKit | 1 | Le cerveau de la serre | Moyen |
| Module température et humidité DHT22 (AM2302) | 1 | Température et humidité de l'air | Faible |
| Capteur capacitif d'humidité du sol (v1.2) | 1 (un par pot) | Humidité du sol | Faible |
| LDR (GL5528) et résistance de 10 kΩ | 1 + 1 | Lumière ambiante | Très faible |
| Interrupteur à flotteur pour niveau d'eau | 1 | Bloque la pompe quand le réservoir est vide | Faible |
| Module 4 relais 5 V (à optocoupleur, avec JD-VCC) | 1 | Ventilateur, chauffage et pompe | Faible |
| MOSFET canal N à niveau logique (IRLZ44N), résistances de 220 Ω et 100 kΩ | 1 lot | Luminosité du ruban LED (PWM) | Très faible |
| Mini-pompe immergée 3–6 V et 1 m de tuyau en silicone | 1 | Arrosage | Faible |
| Ventilateur de PC 12 V 80 mm | 1–2 | Ventilation et évacuation de l'humidité | Faible |
| Chauffage PTC 12 V (10–30 W) et fusible thermique | 1 + 1 | Chauffage | Faible |
| Ruban LED 12 V (horticole ou blanc froid), environ 1 m | 1 | Éclairage d'appoint | Faible |
| Écran LCD I2C 16x2 | 1 | Affichage d'état | Faible – moyen |
| Alimentation 12 V 5 A et convertisseur abaisseur LM2596 | 1 + 1 | Alimentation | Moyen |
| Diode 1N5819 ou 1N4007, condensateur 470 µF, borniers, fils de connexion | – | Protection et connexions | Très faible |
| Boîte transparente avec couvercle ou plaques de plexiglas, pots, soucoupes | – | Structure de la serre | Moyen |
Les prix varient beaucoup d'un vendeur à l'autre, voici donc un budget approximatif plutôt que des chiffres exacts. Sans la carte, la version de base (DHT22, capteur d'humidité du sol, LDR, relais 2 canaux, pompe et ventilateur 5 V) coûte en général 25 à 40 €. La version complète, avec chauffage, ruban LED, LCD, alimentation 12 V, LM2596, flotteur et boîtier, tourne autour de 70 à 120 € ; l'essentiel du budget part dans la carte, l'alimentation, l'écran et le boîtier. Si tu as un kit de démarrage Arduino, la carte, la platine d'essai, les fils, la LDR, les résistances et souvent l'écran LCD sont déjà là. Avec un petit budget, garde le chauffage et les LED pour une deuxième étape : l'arrosage et la ventilation forment déjà à eux seuls un projet utile qui fonctionne.
Capteurs : DHT22, capteur d'humidité du sol et LDR
Pour une serre, on te conseille le DHT22 (AM2302). Il coûte un peu plus cher que le DHT11, mais il mesure la température de −40 à 80 °C à ±0,5 °C et l'humidité sur toute la plage de 0 à 100 % à environ ±2 % ; le DHT11 n'est précis qu'à ±2 °C entre 0 et 50 °C et ne voit pas l'humidité au-delà de 90 %. Comme une serre peut dépasser 90 % d'humidité le matin et passer sous zéro en hiver, le DHT22 convient beaucoup mieux. Le capteur fournit au plus une mesure toutes les 2 secondes, c'est pourquoi le code le lit toutes les 2,5 secondes. Si le soleil ou les LED éclairent directement le capteur, son boîtier chauffe et il affiche quelques degrés de plus que l'air ; un capuchon blanc percé qui lui fait de l'ombre évite ce problème. Le câblage et la bibliothèque sont expliqués dans l'article capteur de température et d'humidité DHT11 et DHT22.

Pour l'humidité du sol, choisis un capteur capacitif. Les capteurs résistifs bon marché (YL-69, FC-28) ont deux pointes métalliques en contact direct avec la terre ; alimentés en permanence, ils se corrodent en quelques semaines à cause de l'électrolyse et ne conviennent pas à un système qui doit tourner des mois. L'électrode d'un capteur capacitif est revêtue : aucun métal ne touche la terre. Sa sortie est analogique et baisse à mesure que la terre devient humide : sur un Uno, un capteur tenu dans l'air donne environ 500 à 600, plongé dans l'eau environ 250 à 300. Ces valeurs varient d'un capteur à l'autre ; avant de téléverser le programme principal, relève donc dans le moniteur série les valeurs de ton capteur dans l'air et dans l'eau, puis inscris-les dans les constantes SOIL_DRY_RAW et SOIL_WET_RAW. Le code convertit ces deux valeurs en 0–100 % avec map(). Plus de détails dans l'article capteur d'humidité du sol.
Pour la lumière, la LDR (GL5528) de ton kit suffit. Câble-la en pont diviseur de tension avec une résistance de 10 kΩ : 5V → LDR → A1 → 10 kΩ → GND. Quand la lumière augmente, la résistance de la LDR baisse et la valeur sur A1 monte ; le code la convertit elle aussi en 0–100 % à partir de deux valeurs que tu mesures dans le noir et en plein soleil. Une LDR n'est pas un luxmètre précis, mais elle répond de façon fiable à la question « fait-il nuit, est-ce couvert, fait-il soleil ? ». Pour une mesure de lumière plus précise, tu peux passer à un capteur de lumière I2C comme le BH1750. Utilise l'outil Pont diviseur de tension pour le calcul et l'article capteur de lumière LDR pour les détails.
Actionneurs : relais ou MOSFET ?
Une broche d'Arduino ne fournit qu'environ 20 mA, alors que le ventilateur, le chauffage, la pompe et le ruban LED consomment des centaines de milliampères, voire plusieurs ampères. C'est pourquoi chaque charge est commandée par un relais ou par un MOSFET. Un relais est un interrupteur mécanique : il allume et éteint une charge dans un circuit complètement séparé, en alternatif comme en continu, mais il ne fait que tout ou rien, il claque à chaque commutation et ses contacts ont une durée de vie limitée. Un MOSFET est un interrupteur électronique silencieux ; il ne fonctionne qu'avec des charges en continu, mais il peut commuter des centaines de fois par seconde et permet donc de régler luminosité et vitesse en PWM. Dans ce projet, on utilise un module 4 relais pour le chauffage, le ventilateur et la pompe, et un MOSFET pour la luminosité du ruban LED.
| Charge | Valeur typique | Commande | Pourquoi ? |
|---|---|---|---|
| Chauffage PTC 12 V | 10–30 W, environ 1–2,5 A | Relais (IN2) | Courant élevé, un simple tout ou rien suffit |
| Ventilateur 12 V 80 mm | 0,1–0,2 A | Relais (IN1) ou MOSFET | Le relais est simple ; un MOSFET permet aussi de régler la vitesse en PWM |
| Mini-pompe immergée 3–6 V | 0,1–0,25 A, plus au démarrage | Relais (IN3) ou MOSFET avec diode | Fonctionne par impulsions courtes ; il faut isoler les parasites du moteur |
| Ruban LED 12 V (5050, 60 LED/m) | Environ 1,2 A par mètre | MOSFET à niveau logique | Réglage de luminosité en PWM et fonctionnement silencieux |

Selon la hauteur du tuyau, une mini-pompe immergée débite environ 1 à 1,5 litre par minute ; une impulsion de 3 secondes apporte donc 50 à 75 mL d'eau au pot. C'est pourquoi on ne fait pas tourner la pompe en continu « jusqu'à ce que la terre soit humide », mais par impulsions courtes : arroser 3 secondes, attendre 60 secondes que l'eau se diffuse dans la terre, puis mesurer à nouveau. Si le capteur était relu avant que l'eau ne l'atteigne, la terre paraîtrait encore sèche et la pompe tournerait jusqu'à faire déborder le pot. Une pompe qui tourne à sec chauffe et grille en quelques minutes ; le flotteur du réservoir la bloque quand l'eau vient à manquer. À chaque arrêt, la bobine du moteur produit une pointe de tension inverse ; une diode montée en inverse aux bornes de la pompe (diode de roue libre 1N5819 ou 1N4007, la bague côté +) protège de ces parasites le contact du relais comme l'Arduino. Le choix de la pompe et les façons de la commander sont détaillés dans l'article mini-pompe à eau.
Pour chauffer, utilise un chauffage PTC 12 V. Dans les éléments céramiques PTC (coefficient de température positif), la résistance augmente avec la chaleur, si bien que leur température de surface se limite d'elle-même à une valeur fixe ; c'est bien plus sûr qu'un fil résistif nu qui rougit. Dans un boîtier en plastique, choisis un modèle basse température dont la surface plafonne vers 80 °C. Un chauffage de 24 W consomme 2 A sous 12 V d'après P = U × I ; pendant les premières secondes, à froid, le courant est un peu plus élevé. Pour l'éclairage, tu peux prendre un ruban LED horticole rouge et bleu ou un ruban LED blanc froid ; pour la photosynthèse, les plantes utilisent surtout la lumière rouge (vers 660 nm) et bleue (vers 450 nm). Commande le ruban avec un MOSFET à niveau logique comme l'IRLZ44N : la grille (gate) se relie à D9 par une résistance de 220 Ω et à GND par une résistance de 100 kΩ ; le + du ruban va au 12 V, le − au drain du MOSFET et la source à GND. Les modules MOSFET standard comme l'IRF520 ne conduisent pas complètement avec une tension de grille de 5 V et chauffent avec des charges de plusieurs ampères ; les détails sont dans l'article utiliser un MOSFET avec Arduino. Pour calculer courant et puissance, tu peux utiliser l'outil Loi d'Ohm.
Plan d'alimentation : lignes 12 V et 5 V
Les problèmes les plus fréquents dans les serres connectées (« l'Arduino redémarre quand la pompe tourne », « des caractères bizarres s'affichent sur le LCD ») viennent de l'alimentation. La solution la plus propre consiste à tirer deux lignes d'une seule alimentation 12 V 5 A. La ligne 12 V alimente directement le chauffage, les ventilateurs et le ruban LED, et va aussi au connecteur jack de l'Arduino Uno. Ce même 12 V entre dans un convertisseur abaisseur LM2596 réglé sur 5 V ; les bobines des relais, la pompe et le LCD sont alimentés par cette ligne 5 V. Si tu retires le cavalier JD-VCC du module relais, le courant des bobines vient entièrement du LM2596 et le côté Arduino n'allume plus que les LED des optocoupleurs. Les capteurs (DHT22, capteur d'humidité du sol et pont diviseur de la LDR), eux, s'alimentent sur la broche 5V de l'Arduino : analogRead() mesure par rapport au 5 V de la carte, donc alimenter les capteurs analogiques avec cette même tension garde des mesures stables. Relie impérativement les GND de l'alimentation, du LM2596 et de l'Arduino.
| Charge | Ligne | Courant approx. |
|---|---|---|
| Chauffage PTC (24 W) | 12 V | 2 A (plus au premier instant, à froid) |
| Ruban LED (1 m) | 12 V | 1–1,2 A |
| Deux ventilateurs de 80 mm | 12 V | 0,2–0,4 A |
| Arduino Uno et capteurs (par le jack) | 12 V | Environ 0,06–0,08 A |
| Entrée du LM2596 (avec 1 A côté 5 V) | 12 V | Environ 0,5 A |
| Module relais (trois relais collés) | 5 V | 0,2–0,25 A |
| Mini-pompe immergée | 5 V | 0,1–0,25 A (plus au démarrage) |
| LCD I2C (rétroéclairage allumé) | 5 V | 20–30 mA |
Même si tout fonctionne en même temps, le courant total tourne autour de 4 à 4,5 A ; une alimentation 12 V 5 A (60 W) laisse donc assez de marge. En pratique, le chauffage et le ventilateur de température ne tournent jamais ensemble, car un large écart sépare leurs seuils. Avant de brancher le LM2596 au circuit, mesure sa sortie au multimètre et règle-la sur 5,0 V avec son potentiomètre ; à la sortie de la boîte, il peut laisser passer presque les 12 V. Tu peux alimenter l'Arduino par USB pendant le téléversement et par le jack une fois installé ; brancher les deux en même temps ne pose pas de problème pour l'Uno. Plus de détails dans le guide alimentation de l'Arduino.
Câblage et tableau des broches
Le tableau ci-dessous indique les broches utilisées par les trois programmes de ce guide. Sur l'Uno, on a relié le MOSFET qui commande le ruban LED à D9, qui gère la PWM ; sur l'Uno, la PWM n'existe que sur D3, D5, D6, D9, D10 et D11. Le flotteur et les relais n'ont pas besoin de PWM. Comme le LCD n'utilise que le bus I2C (A4 et A5), il occupe très peu de broches ; sur l'Uno, A2, A3 et quelques broches numériques restent libres pour un deuxième capteur de sol ou un bouton. Sur l'ESP32, on a branché les capteurs analogiques sur des broches ADC1 (GPIO 34 et 35), qui continuent de fonctionner quand le Wi-Fi est actif. Tu peux voir le brochage de ta carte dans l'outil Pinout.
| Connexion | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| DHT22 DATA | D2 | GPIO 4 | GP15 |
| Flotteur (autre fil à GND) | D3 | GPIO 13 | GP14 |
| Relais IN3 : pompe | D4 | GPIO 25 | GP18 |
| Relais IN1 : ventilateur | D7 | GPIO 26 | GP16 |
| Relais IN2 : chauffage | D8 | GPIO 27 | GP17 |
| Grille du MOSFET (via 220 Ω) : LED | D9 | GPIO 18 | GP19 |
| AOUT du capteur d'humidité du sol | A0 | GPIO 34 | GP26 |
| Point milieu du pont diviseur de la LDR | A1 | GPIO 35 (pont alimenté en 3V3) | GP27 (pont alimenté en 3V3) |
| LCD I2C SDA / SCL | A4 / A5 | GPIO 21 / 22 | GP4 / GP5 |
| Alimentation des capteurs | 5V | 3V3 | 3V3 |
Seuil et hystérésis : pourquoi les relais claquent-ils ?
Imagine que tu commandes le ventilateur avec un seuil unique : « en marche au-dessus de 30 °C, sinon arrêt ». Quand la température oscille entre 29,9 et 30,1 °C (ce qui arrive très souvent à cause du bruit du capteur), le relais s'enclenche et se déclenche à chaque mesure. Les contacts du relais et le moteur du ventilateur s'usent, et avec le chauffage, les câbles et l'alimentation encaissent inutilement chaque mise en marche. La solution, c'est l'hystérésis : tu utilises deux seuils différents pour la mise en marche et l'arrêt. Le ventilateur démarre à 30 °C mais ne s'arrête que lorsque la température est redescendue à 27 °C. Dans cette zone morte de 3 degrés, le système garde son dernier état. Les thermostats d'ambiance des chaudières et les réfrigérateurs fonctionnent sur le même principe.
| Commande | Mise en marche | Arrêt | Zone morte |
|---|---|---|---|
| Ventilateur (température) | ≥ 30 °C | ≤ 27 °C | 3 °C |
| Ventilateur (humidité) | ≥ 85 % | ≤ 75 % | 10 % |
| Chauffage | ≤ 16 °C | ≥ 19 °C | 3 °C |
| Arrosage | Sol ≤ 35 % | Sol ≥ 50 % | 15 % |
| Éclairage LED | Lumière ≤ 25 % | Lumière ≥ 35 % ou après 4 heures | 10 % |
La largeur de la zone morte est un compromis : trop étroite, le relais commute encore souvent ; trop large, la température s'éloigne trop de la cible. Environ 2 à 3 °C pour la température, 8 à 10 % pour l'humidité et 10 à 15 % pour l'humidité du sol constituent un bon départ. On a aussi laissé un large écart entre 19 °C, où le chauffage s'arrête, et 30 °C, où le ventilateur démarre, pour que le chauffage et le ventilateur de température ne travaillent jamais l'un contre l'autre. Dans le code, l'hystérésis tient dans une petite fonction hysteresis() pour ne pas la réécrire à chaque commande : si le seuil de marche est supérieur au seuil d'arrêt, cela signifie « allumer en montée » (ventilateur) ; s'il est inférieur, « allumer en descente » (chauffage, arrosage, LED). Si tu veux aussi lisser les pics des données du capteur, jette un œil à l'article filtrer les données des capteurs.
Structure du code : mesurer, décider, agir, afficher
La règle la plus importante du code de la serre, c'est qu'aucune tâche n'en attend une autre. Si tu attendais une minute entière avec delay(60000) après la pompe, pendant ce temps le ventilateur ne tournerait pas et l'écran ne se mettrait pas à jour. C'est pourquoi le code est fait de petites fonctions qui mesurent le temps avec millis(), chacune chargée d'une seule tâche ; loop() les appelle à tour de rôle des milliers de fois par seconde, et chaque fonction n'agit que lorsque son moment est venu. L'arrosage est une petite machine à états à trois états : attendre (IDLE), arroser (PUMPING) et laisser l'eau s'infiltrer (SOAKING). Tous les réglages sont des constantes en haut du code ; tu n'as pas à fouiller le programme pour changer un seuil. Tu trouveras la logique de cette approche dans les articles millis() et delay() et machine à états avec Arduino. Le code comprend aussi ces mesures de sécurité :
- Démarrage sûr : La valeur « arrêt » est écrite sur les broches des relais avant l'appel à pinMode() ; les relais ne collent donc pas, même un instant, au démarrage de l'Arduino.
- Panne de capteur : Si le DHT22 ne peut pas être lu trois fois de suite, le code passe en état de sécurité : il coupe le chauffage, met le ventilateur en marche et affiche « Sensor hatasi » (erreur de capteur) sur le LCD. Un système qui ne connaît pas la température ne doit jamais laisser le chauffage allumé.
- Protection du réservoir : Quand le flotteur indique un réservoir vide, la pompe ne démarre pas du tout, et si elle tournait, elle s'arrête aussitôt.
- Protection contre le débordement : Si l'humidité du sol n'atteint pas 50 % après six impulsions dans un même cycle d'arrosage, le capteur est peut-être sorti de la terre ou le tuyau s'est détaché ; le code arrête l'arrosage et affiche « Sulama hatasi » (défaut d'arrosage). L'alerte disparaît d'elle-même quand la terre est de nouveau humide.
- Enregistrement des données : Toutes les 10 secondes, le code écrit les mesures dans le moniteur série sous forme de ligne séparée par des virgules (CSV) ; tu peux copier ces lignes et tracer des graphiques dans Excel.
Montage et test pas à pas
- Prépare le boîtier : découpe les trous du ventilateur, de l'entrée d'air, du tuyau et des câbles, et installe le boîtier électronique à l'extérieur de la serre.
- Monte les lignes d'alimentation : branche l'alimentation 12 V sur un bornier, règle le LM2596 sur 5,0 V au multimètre et relie tous les GND.
- Branche le LCD I2C et affiche un texte. Si l'écran reste vide, essaie le programme scanner et le potentiomètre de contraste de l'article LCD I2C.
- Ajoute les capteurs un par un et teste-les dans le moniteur série : note les valeurs du DHT22, la valeur brute du capteur de sol dans l'air et dans l'eau, et celle de la LDR dans le noir et au soleil.
- Teste d'abord les relais sans charge en observant leurs LED et en écoutant le clic. Branche ensuite le ventilateur, la pompe (réservoir plein) et en dernier le chauffage.
- Branche le ruban LED via le MOSFET et change la valeur d'analogWrite() pour voir la luminosité varier.
- Téléverse le programme principal, inscris tes valeurs d'étalonnage dans les constantes et laisse tourner le système quelques heures sous surveillance : arrosage intempestif, relais qui claque sans arrêt ou composant qui chauffe trop ?
Code complet pour l'Arduino Uno
Pour compiler le code, installe depuis le gestionnaire de bibliothèques la DHT sensor library d'Adafruit (avec sa dépendance Adafruit Unified Sensor) et la bibliothèque LiquidCrystal I2C de Frank de Brabander ; Wire est fournie avec l'IDE Arduino. L'adresse du LCD est le plus souvent 0x27, et 0x3F sur certains modules. Avant de téléverser, inscris les valeurs d'étalonnage que tu as mesurées dans les constantes SOIL_DRY_RAW, SOIL_WET_RAW, LDR_DARK_RAW et LDR_BRIGHT_RAW. Si ton flotteur s'ouvre au lieu de se fermer en présence d'eau, mets TANK_OK à HIGH ; si ton module relais est actif à l'état haut, inverse les valeurs de RELAY_ON et 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}| Fonction | Rôle | Fréquence |
|---|---|---|
| readSensors() | Lit le DHT22, l'humidité du sol, la LDR et le flotteur ; convertit les valeurs analogiques en 0–100 % à partir de la moyenne de 10 mesures | Toutes les 2,5 secondes |
| controlClimate() | Pilote le ventilateur avec une hystérésis de température ou d'humidité et le chauffage avec une hystérésis de température ; passe en état de sécurité si le capteur tombe en panne | À chaque tour de boucle |
| controlLight() | Allume les LED en PWM à la tombée de la nuit et les éteint au plus tard 4 heures après | À chaque tour de boucle |
| controlPump() | Machine à états de l'arrosage : arroser 3 s, attendre 60 s, mesurer à nouveau ; avec protection du réservoir et anti-débordement | À chaque tour de boucle |
| updateLcd() | Fait défiler trois pages : mesures de l'air, humidité du sol et lumière, état des appareils | Toutes les 3 secondes |
| logCsv() | Écrit mesures et états dans le moniteur série sous forme de ligne CSV | Toutes les 10 secondes |
La première page du LCD affiche la température et l'humidité de l'air, la deuxième l'humidité du sol et le niveau de lumière, la troisième l'état des appareils : « F:1 I:0 P:0 L:1 » signifie que le ventilateur tourne, que le chauffage (I = ısıtıcı) et la pompe sont arrêtés et que les LED sont allumées. La ligne du bas affiche l'un des messages « Sistem normal » (tout va bien), « Depo bos! » (réservoir vide), « Sulama hatasi » (défaut d'arrosage) ou « Sensor hatasi » (erreur de capteur) ; tu peux facilement traduire ces textes dans le code. Une fois compilé, le code occupe environ 31 % de la mémoire programme de l'Uno et 25 % de sa RAM ; il reste donc largement la place pour un deuxième capteur de sol, un buzzer ou un module horloge RTC. Comme l'Uno n'a pas de véritable horloge, la règle des LED fonctionne en « 4 heures au maximum après la tombée de la nuit », un bon moyen d'allonger les courtes journées d'hiver. Si tu veux allumer les LED à heures fixes chaque jour, ajoute un module RTC ou passe à la version ESP32 ci-dessous.
Surveiller et arroser depuis ton téléphone avec un ESP32

L'Uno n'a pas de Wi-Fi. Si tu veux voir l'état de la serre sur ton téléphone et arroser à distance, remplace le cerveau par un ESP32. Le code ci-dessous transpose la même logique de commande sur l'ESP32 et lance un petit serveur web. Avec ton téléphone sur le même réseau Wi-Fi, ouvre dans le navigateur l'adresse IP affichée dans le moniteur série : tu vois la température, l'humidité, l'humidité du sol, la lumière, l'état du réservoir et si chaque appareil est allumé ou éteint ; la page se recharge toute seule toutes les 10 secondes. Le bouton Water now (arroser maintenant) fait tourner la pompe une fois pendant 3 secondes s'il y a de l'eau dans le réservoir. Au démarrage, l'ESP32 récupère l'heure sur Internet (NTP) ; ainsi, les LED ne s'allument qu'entre 6 h et 20 h et seulement s'il fait sombre, et la nuit les plantes se reposent.
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}Voici ce qui change par rapport au code de l'Uno : l'ADC de l'ESP32 est sur 12 bits et renvoie des valeurs de 0 à 4095, il faut donc refaire l'étalonnage ; comme les capteurs sont alimentés en 3V3, les valeurs brutes seront elles aussi différentes. Le fuseau horaire se règle dans la ligne configTime() ; on a mis 3 × 3600 secondes pour la Turquie (UTC+3). En France, remplace cette ligne par configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org"), qui gère aussi l'heure d'été. S'il n'y a pas d'Internet ou que l'heure n'a pas encore été récupérée, la règle des LED revient à celle de l'Uno, « 4 heures au maximum ». La page n'a pas de mot de passe : n'importe qui sur ton réseau domestique pourrait lancer la pompe ; ajouter un mot de passe simple avec la fonction authenticate() de la bibliothèque WebServer est une bonne habitude. Si tu veux aussi un LCD sur l'ESP32, copie telle quelle la fonction updateLcd() du code de l'Uno ; SDA est sur GPIO 21 et SCL sur GPIO 22.
| Méthode | Accès hors de la maison | Difficulté | Idéal pour |
|---|---|---|---|
| Serveur web sur l'ESP32 (le code de ce guide) | Non, même réseau uniquement | Moyenne | Présentation scolaire, démonstration sans Internet |
| Blynk IoT | Oui | Facile | Jauges, graphiques et notifications prêts à l'emploi |
| Bot Telegram | Oui | Moyenne | Alertes « réservoir vide » et « température trop élevée » |
| Google Sheets | Oui | Moyenne | Enregistrement des données sur plusieurs semaines et graphiques |
| MQTT et Home Assistant | Oui, configuration nécessaire | Avancée | Vraie domotique pour la maison et le jardin |
Pour une présentation scolaire, le serveur web de l'ESP32 est le choix le plus sûr : il ne dépend d'aucun service cloud et fonctionne avec son propre réseau même sans Internet dans la salle. Si tu veux aussi consulter la serre hors de chez toi, Blynk IoT, avec ses jauges et graphiques tout prêts, est la voie la plus simple. Pour recevoir un message sur ton téléphone quand le réservoir est vide ou que la température devient dangereuse, consulte le guide bot Telegram avec ESP32 ; pour récolter des semaines de données pour ton rapport, ESP32 et Google Sheets ; et pour relier la serre à une vraie domotique, MQTT et Home Assistant.
Version MicroPython pour le Raspberry Pi Pico
Tu peux aussi gérer la même serre avec un Raspberry Pi Pico en MicroPython. Le firmware MicroPython du Pico intègre déjà un pilote DHT : aucun fichier supplémentaire à copier. Les entrées analogiques sont uniquement GP26, GP27 et GP28, et read_u16() renvoie des valeurs de 0 à 65535 ; mesure tes valeurs d'étalonnage sur cette échelle. Comme le Pico fonctionne en 3,3 V, alimente les capteurs en 3V3 et le module relais par VBUS (le 5 V de l'USB) ou par la ligne du LM2596. Le code ci-dessous contient toutes les règles du ventilateur, du chauffage, des LED et de l'arrosage, et affiche les valeurs dans la console de Thonny. Si tu enregistres le fichier sur le Pico sous le nom main.py, la carte fonctionne même sans être reliée à un ordinateur. Avec un Pico W doté du Wi-Fi, tu peux ajouter la méthode de l'article serveur web avec le Pico W pour suivre les valeurs depuis ton téléphone.
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)Conseils pour la présentation à l'école ou en concours
La serre connectée est un sujet si populaire que les jurys voient des dizaines de maquettes semblables. Ce qui te fera sortir du lot, ce n'est pas seulement de montrer que le système marche, c'est de répondre à une question par des mesures. Pour une fête de la science ou une exposition scolaire, l'objectif, la méthode et le résultat attendu doivent être clairs ; les concours de projets scientifiques, comme le concours CGénial, attendent une expérience comparative avec des données. Une bonne question de recherche pour cette serre : « Combien d'eau un arrosage piloté par l'humidité du sol économise-t-il par rapport à un arrosage chaque jour à heure fixe, et quel effet cela a-t-il sur la croissance de la plante ? »
- Forme deux groupes : Répartis des plants de la même espèce et de la même taille en deux groupes. Arrose l'un avec le système à capteurs et l'autre à la main, avec une quantité fixe chaque jour ; la lumière et la température doivent être identiques pour les deux groupes.
- Mesure et note : Tiens un tableau avec la quantité d'eau hebdomadaire (en mesurant le réservoir, en mL), la hauteur des plants (cm) et le nombre de feuilles. La sortie CSV du code ou un journal dans Google Sheets te donne directement le graphique de température et d'humidité.
- Dessine un schéma fonctionnel : Les capteurs à gauche, l'Arduino au milieu, les actionneurs à droite ; légende les flèches avec des phrases-règles (« sol < 35 % → pompe »). Le jury ne lira pas ton code, mais il se souviendra de ce schéma.
- Explique l'hystérésis : Deux graphiques montrant combien de fois en une heure un ventilateur à seuil unique et un ventilateur avec hystérésis s'allument et s'éteignent sont le meilleur moyen de montrer au jury le côté ingénierie de ton projet.
- Écris un scénario de démonstration : Réchauffe le DHT22 dans ta main pour lancer le ventilateur, plante le capteur de sol dans un pot sec pour montrer les impulsions de la pompe, cache la LDR avec ta main pour allumer les LED. Chaque démonstration doit durer 10 à 20 secondes.
- Relie-le à la vie réelle : Parle de vrais problèmes comme la consommation d'eau et d'énergie des serres, les maladies fongiques dues à l'excès d'humidité et la charge de travail des maraîchers ; montre chiffres à l'appui auquel ton système répond.
- Prévois un plan B : Filme le système en marche ; emporte des fils, une pompe et une alimentation de rechange ; note sur une feuille tes valeurs d'étalonnage et tes paramètres Wi-Fi.
- Prépare les questions du jury : Aie des réponses courtes à « Que se passe-t-il si un capteur tombe en panne ? », « Que deviennent les plantes en cas de coupure de courant ? » et « Comment passerais-tu à une vraie serre ? ». L'état de sécurité et la protection contre le débordement du code sont les meilleures réponses.
Erreurs fréquentes et solutions
| Symptôme | Cause probable | Solution |
|---|---|---|
| L'Arduino redémarre ou le LCD s'affole quand la pompe tourne | Pompe et relais alimentés par la broche 5V de l'Arduino, parasites du moteur | Alimente la pompe et les bobines des relais par le LM2596, relie les GND, ajoute une diode inverse et un condensateur de 470 µF sur la pompe |
| Les relais claquent sans arrêt | Seuil unique ou mesures bruitées | Utilise des seuils de marche et d'arrêt distincts (hystérésis) et fais la moyenne des mesures analogiques |
| L'eau continue de couler après l'arrêt de la pompe | Effet siphon : l'extrémité du tuyau est sous le niveau d'eau du réservoir | Place le réservoir plus bas ou perce un petit trou d'air en haut du tuyau |
| L'humidité du sol affiche toujours 0 % ou toujours 100 % | Mauvaises valeurs d'étalonnage ou AOUT sur la mauvaise broche | Mesure la valeur brute du capteur dans l'air et dans l'eau, puis inscris-la dans les constantes |
| Les LED clignotent | La LDR voit la lumière des LED | Déplace la LDR hors de la serre, tournée vers le ciel |
| Le DHT22 renvoie nan | Mauvaise broche, pas de résistance de tirage ou lecture plus fréquente que toutes les 2 secondes | Vérifie la broche, utilise un module ou ajoute 10 kΩ entre DATA et VCC |
| La température semble trop élevée | Le soleil ou les LED éclairent directement le capteur | Abrite-le sous un capuchon blanc percé et éloigne-le du chauffage |
| Gouttes d'eau et moisissures sur les parois | Humidité trop élevée, ventilation insuffisante | Baisse le seuil d'humidité, agrandis l'entrée d'air, arrose moins |
| Sur l'ESP32, la mesure du sol se dérègle quand le Wi-Fi démarre | Le capteur est sur une broche ADC2 | Déplace-le sur une broche ADC1 entre GPIO 32 et 39 |
| Avec un ESP32, le relais ne retombe jamais | Un HIGH de 3,3 V ne coupe pas un module 5 V actif à l'état bas | Sépare JD-VCC ou utilise un module relais commandable en 3,3 V |
Une serre connectée réunit dans un seul projet la lecture de capteurs, l'étalonnage, la décision avec hystérésis, la commande de relais et de MOSFET, le plan d'alimentation et la surveillance sans fil, et au bout du compte elle fait pousser quelque chose de vraiment vivant : une plante. Commence par l'arrosage et la ventilation, teste chaque élément séparément, puis ajoute le chauffage, les LED et le téléphone. Dans l'application BlueGrays, tu trouveras pas à pas, avec schémas et code, des projets pour mesurer la température et l'humidité avec un DHT11, lire la lumière avec une LDR, une station d'arrosage intelligente et un enregistreur de température avec DHT22.
Questions fréquentes
Quel matériel faut-il pour une serre connectée avec Arduino ?
Pour une serre connectée de base, il suffit d'un Arduino Uno ou d'un ESP32, d'un capteur de température et d'humidité DHT22, d'un capteur capacitif d'humidité du sol, d'une LDR avec une résistance de 10 kΩ, d'un module relais, d'une mini-pompe immergée et d'un ventilateur. La version complète ajoute un chauffage PTC 12 V, un ruban LED commandé par un MOSFET, un LCD I2C, un interrupteur à flotteur, une alimentation 12 V 5 A et un LM2596. Pour la structure, une boîte de rangement transparente avec couvercle est la solution la plus simple et la moins chère.
Comment fonctionne une serre connectée ?
Une serre connectée fonctionne selon une boucle mesurer, décider, agir, afficher. Les capteurs mesurent la température, l'humidité, l'humidité du sol et la lumière ; l'Arduino compare ces valeurs à des seuils et allume ou éteint le ventilateur, le chauffage, la pompe et les LED via des relais ou un MOSFET. Les résultats s'affichent sur un LCD et, avec un ESP32, sur ton téléphone.
DHT11 ou DHT22 pour une serre ?
Choisis le DHT22. Le DHT11 n'est précis qu'à ±2 °C entre 0 et 50 °C et ne voit pas l'humidité au-delà de 90 %, alors qu'une serre peut dépasser 90 % d'humidité le matin et passer sous zéro en hiver. Le DHT22 mesure de −40 à 80 °C à ±0,5 °C et l'humidité à environ ±2 %, et son code est presque identique à celui du DHT11.
Combien coûte une serre connectée avec Arduino ?
Les prix dépendent du vendeur, mais sans la carte, la version de base (DHT22, capteur d'humidité du sol, LDR, relais, pompe et ventilateur) coûte en général 25 à 40 €. La version complète avec chauffage, ruban LED, LCD, alimentation 12 V et boîtier revient à environ 70 à 120 €. Si tu as déjà un kit de démarrage Arduino, le coût baisse nettement.
Comment surveiller ma serre depuis mon téléphone ?
Le plus simple est de remplacer le cerveau par un ESP32 et de lancer un serveur web ; depuis le navigateur de ton téléphone, sur le même réseau Wi-Fi, tu vois les valeurs et tu lances la pompe. Pour un accès hors de chez toi, tu peux utiliser Blynk IoT, pour les alertes un bot Telegram et pour l'enregistrement à long terme Google Sheets.
Comment présenter une serre connectée à un concours scientifique ?
Au lieu de montrer seulement une maquette qui marche, pose une question de recherche mesurable ; par exemple, mesure pendant plusieurs semaines, avec deux groupes de plantes, combien d'eau l'arrosage piloté par capteur économise par rapport à un arrosage programmé. Présente la consommation d'eau, la hauteur des plants et les courbes de température et d'humidité sous forme de tableaux et de graphiques, explique le système avec un schéma fonctionnel et prépare un court scénario pour la démonstration en direct.
Projets à réaliser après cet article
Voir tous les projets
Température et humidité avec DHT11
Quelle température fait-il dans ta chambre, et l'air est-il humide ou sec ? Le capteur DHT11 mesure la température et l'humidité, et l'Arduino affiche les valeurs sur un écran LCD à deux lignes, mises

Mesurer l'intensité lumineuse avec une LDR
Envie de voir en chiffres à quel point ta chambre est lumineuse ? Le LDR, une petite pièce sensible à la lumière, mesure la lumière ambiante, et l'Arduino écrit la valeur sur l'écran de ton ordinateur

Mesure de l'intensité lumineuse avec le module LDR ESP32
Suivi en temps réel sur le moniteur série de la valeur brute de l'ADC du module LDR et du pourcentage relatif de luminosité calculé à partir de références dans l'obscurité et à la lumière. Ce pourcentage n'est pas une mesure en lux. Permet de comprendre le principe de calibrage du module. Il est préférable de réaliser cet exercice avant le projet 10, car il sert à déterminer la valeur seuil.

Station d'irrigation intelligente
Un jardinier qui arrose tes plantes tout seul pour qu'elles ne sèchent pas pendant tes vacances. Un capteur d'humidité mesure à quel point la terre est mouillée ; quand elle devient sèche, l'Arduino f

ESP32 DHT22 Température et humidité (avec écran LCD)
Il s'agit d'un projet permettant de mesurer la température ambiante et le taux d'humidité à l'aide d'un capteur DHT22 et d'afficher ces données en temps réel sur un écran LCD I2C 16x2. Les données du capteur sont lues à l'aide du protocole one-wire grâce à la bibliothèque Adafruit DHT. Ce projet constitue la base des stations météo IoT, de la domotique ou des systèmes de surveillance de l'environnement dans les serres.

Enregistreur de Température ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
Construis un enregistreur de température horodaté avec ESP32 ! Lis la température/humidité du DHT22 chaque minute, obtiens l'heure réelle du DS1307 RTC et enregistre sur carte microSD au format CSV. L'OLED affiche en direct la température (grande police), le pourcentage d'humidité, l'horodatage et le compteur. Le fichier /temp_log.csv sur la carte SD s'ouvre dans Excel, Google Sheets ou Python pandas pour analyse — parfait pour surveillance de serre, suivi de température à domicile, c

Éclairage Intelligent ESP32 (PIR + LDR + Relais + OLED)
Transformez votre couloir, votre salle de bain ou votre garde-manger en éclairage intelligent avec l'ESP32 ! En utilisant un capteur de mouvement PIR, un capteur de lumière LDR et un module relais, nous construisons le scénario classique "la lumière s'allume lorsqu'un mouvement est détecté dans l'obscurité" — si l'environnement est déjà lumineux, le mouvement N'ACTIVE PAS la lumière, économisant ainsi de l'énergie pendant la journée. La lumière reste allumée pendant 30 secondes ; si le mouvement
Autres articles
Application BlueGrays
Réalise ce montage pas à pas sur ton téléphone
Schéma, code et liste des composants sur un seul écran. Plus de 200 projets Arduino, ESP32 et Pico à réaliser.
Scanne avec ton téléphone
Pointe l’appareil photo vers le code : la boutique d’applications de ton téléphone s’ouvre.
ou ouvre la boutique

