Ce dernier chapitre rassemble tous les concepts du livre — chimie du sol, gestion de l’eau, biologie, capteurs, irrigation et données — dans un projet complet de jardin connecté. L’objectif est de construire un système autonome qui mesure, décide, arrose et rapporte, avec une architecture robuste et extensible.
| Fonction | Description | Chapitres |
|---|---|---|
| F1 Mesure du sol | Humidité, température, pH | Ch. 1, 2, 8 |
| F2 Mesure de l’environnement | Température air, humidité relative, luminosité | Ch. 4, 8 |
| F3 Irrigation automatique | Décision par seuils + bilan hydrique, multi-zones | Ch. 3, 9 |
| F4 Détection de pluie | Inhibition de l’arrosage, mesure pluviométrique | Ch. 3, 9 |
| F5 Collecte de données | Stockage local + envoi MQTT | Ch. 10 |
| F6 Visualisation | Dashboard Grafana, rapports automatiques | Ch. 10 |
| F7 Alertes | Gel, sécheresse, batterie faible, capteur HS | Ch. 9, 10 |
| F8 Autonomie énergétique | Panneau solaire + batterie, deep sleep | Ch. 7 |
┌──────────────────────────┐
│ Serveur (Raspberry Pi) │
│ ┌──────┐ ┌──────────┐ │
│ │MQTT │ │InfluxDB │ │
│ │Broker│ │ │ │
│ └──┬───┘ └────┬─────┘ │
│ │ │ │
│ ┌──┴───────────┴──┐ │
│ │ Grafana │ │
│ └─────────────────┘ │
└───────────┬───────────────┘
│ WiFi
┌─────────────────┼──────────────────┐
│ │ │
┌───────┴──────┐ ┌──────┴───────┐ ┌───────┴──────┐
│ Nœud Potager │ │ Nœud Pelouse │ │ Nœud Serre │
│ ESP32 #1 │ │ ESP32 #2 │ │ ESP32 #3 │
│ + capteurs │ │ + capteurs │ │ + capteurs │
│ + vanne │ │ + vanne │ │ + vanne │
│ + solaire │ │ + solaire │ │ + solaire │
└──────────────┘ └──────────────┘ └──────────────┘
Chaque nœud est autonome et contient :
┌─────────────────────────────────────────────┐
│ Nœud ESP32 │
│ │
│ [Panneau solaire 6V 2W] │
│ │ │
│ [TP4056 + protection] → [18650 3,7V] │
│ │ │
│ [Régulateur 3,3V] → [ESP32-WROOM-32] │
│ │ │
│ ┌────────────────┼──────────┐ │
│ │ │ │ │
│ [Capteur humidité] [DS18B20] [BH1750] │
│ (ADC GPIO34) (GPIO4) (I²C) │
│ │
│ [Module relais] → [Électrovanne 12V] │
│ (GPIO26) (alim. séparée) │
│ │
│ [Capteur pluie] (GPIO33) │
└─────────────────────────────────────────────┘
| Composant | Référence | Quantité | Prix approx. |
|---|---|---|---|
| ESP32-WROOM-32 DevKit | 38 broches | 1 | 6 € |
| Capteur humidité capacitif | v1.2 | 1 | 3 € |
| DS18B20 étanche | 1 m câble | 2 | 4 € |
| BH1750 (lumière) | Module GY-302 | 1 | 2 € |
| Capteur de pluie | Module FC-37 | 1 | 2 € |
| Module relais 5V | 2 canaux | 1 | 3 € |
| Électrovanne 12V NF | 1/2” BSP | 1 | 8 € |
| Alimentation 12V 1A | Adaptateur | 1 | 5 € |
| Panneau solaire 6V 2W | Mini panel | 1 | 8 € |
| Module TP4056 + protection | USB-C | 1 | 2 € |
| Batterie 18650 | 3400 mAh | 1 | 5 € |
| Boîtier étanche IP65 | 100×68×50 mm | 1 | 4 € |
| Connecteurs, câbles, résistances | Divers | 1 | 5 € |
| Total par nœud | ~57 € |
| Composant | Référence | Prix approx. |
|---|---|---|
| Raspberry Pi 4 (2 Go) | + alimentation | 45 € |
| Carte micro-SD 32 Go | Classe 10 | 8 € |
| Total serveur | ~53 € |
Sur le Raspberry Pi (Raspbian / Debian) :
# Mosquitto (broker MQTT)
sudo apt install -y mosquitto mosquitto-clients
# InfluxDB 2.x
wget https://dl.influxdata.com/influxdb/releases/\
influxdb2_2.7.1-1_arm64.deb
sudo dpkg -i influxdb2_2.7.1-1_arm64.deb
sudo systemctl enable influxdb
# Grafana
sudo apt install -y grafana
sudo systemctl enable grafana-server
# Python + dépendances
pip install paho-mqtt influxdb-client flask pandas
Ce script écoute tous les topics du jardin et écrit dans InfluxDB :
# code/mqtt_to_influx.py (extrait)
import paho.mqtt.client as mqtt
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
import json
INFLUX_URL = "http://localhost:8086"
INFLUX_TOKEN = "mon_token"
INFLUX_ORG = "jardin"
INFLUX_BUCKET = "jardin"
influx = InfluxDBClient(
url=INFLUX_URL, token=INFLUX_TOKEN,
org=INFLUX_ORG)
write_api = influx.write_api(
write_options=SYNCHRONOUS)
Le fichier complet code/mqtt_to_influx.py gère la décomposition des topics, la conversion des types, et les erreurs de connexion.
# /etc/mosquitto/conf.d/jardin.conf
listener 1883
allow_anonymous true
persistence true
persistence_location /var/lib/mosquitto/
log_dest file /var/log/mosquitto/mosquitto.log
Point pratique : En production, désactivez
allow_anonymouset configurez l’authentification par mot de passe ou certificat TLS.
/
├── boot.py # Configuration initiale
├── main.py # Point d'entrée
├── config.json # Paramètres (WiFi, seuils, MQTT)
├── capteurs.py # Lecture des capteurs
├── irrigation.py # Contrôle des vannes
├── reseau.py # WiFi + MQTT
├── journal.py # Logging local
└── data/
└── log.csv # Historique local
{
"wifi_ssid": "MonReseau",
"wifi_pass": "MotDePasse",
"mqtt_broker": "192.168.1.100",
"mqtt_prefix": "jardin/potager",
"zone": "potager",
"intervalle_mesure_min": 15,
"seuil_bas": 40,
"seuil_haut": 70,
"duree_arrosage_s": 300,
"heure_debut": 6,
"heure_fin": 21
}
# code/main_esp32.py (extrait)
import json, time, machine
from capteurs import lire_tous_capteurs
from irrigation import gerer_irrigation
from reseau import connecter_wifi, publier_mqtt
from journal import log
# Charger la configuration
with open("config.json") as f:
cfg = json.load(f)
def cycle():
# 1. Lire les capteurs
mesures = lire_tous_capteurs()
log(f"Mesures: {mesures}")
# 2. Décision d'irrigation
action = gerer_irrigation(mesures, cfg)
mesures["action"] = action
# 3. Envoi des données
try:
connecter_wifi(cfg["wifi_ssid"],
cfg["wifi_pass"])
publier_mqtt(cfg["mqtt_broker"],
cfg["mqtt_prefix"], mesures)
except Exception as e:
log(f"Erreur réseau: {e}")
# 4. Deep sleep
duree_us = cfg["intervalle_mesure_min"] * 60e6
machine.deepsleep(int(duree_us))
cycle()
# code/capteurs.py (extrait)
from machine import Pin, ADC, I2C
import onewire, ds18x20, time
def lire_tous_capteurs():
resultats = {}
# Humidité sol (capacitif, ADC)
adc = ADC(Pin(34))
adc.atten(ADC.ATTN_11DB)
brut = sum(adc.read() for _ in range(10)) // 10
resultats["humidite_sol"] = max(0, min(100,
(3200 - brut) * 100 // (3200 - 1400)))
# Température sol (DS18B20)
ds_pin = Pin(4)
ds = ds18x20.DS18X20(onewire.OneWire(ds_pin))
roms = ds.scan()
if roms:
ds.convert_temp()
time.sleep_ms(750)
resultats["temp_sol"] = ds.read_temp(roms[0])
return resultats
# code/irrigation.py (extrait)
from machine import Pin
import time as utime
relais = Pin(26, Pin.OUT, value=0)
def gerer_irrigation(mesures, cfg):
h = mesures.get("humidite_sol", 100)
t = mesures.get("temp_sol", 20)
# Protection gel
if t < 2:
relais.value(0)
return "HORS_GEL"
# Vérifier fenêtre horaire
heure = utime.localtime()[3]
if not (cfg["heure_debut"] <= heure
<= cfg["heure_fin"]):
return "HORS_HORAIRE"
# Décision
if h < cfg["seuil_bas"]:
relais.value(1)
utime.sleep(cfg["duree_arrosage_s"])
relais.value(0)
return "ARROSAGE"
return "OK"
Équation clé — Dimensionnement solaire :
P_panneau = E_journalière / (H_soleil × η_charge × η_régulateur)
Avec :
P_panneau = 0,24 / (3 × 0,85 × 0,90) ≈ 0,10 W minimum
Un panneau de 2W offre une marge ×20, ce qui compense les jours nuageux et la dégradation.
[Panneau 6V] → [TP4056] → [18650 3,7V] → [Régulateur LDO 3,3V] → [ESP32]
│ │
└── LED charge └── [Vanne via relais 5V/12V]
(alimentation séparée)
⚠️ Important : le TP4056 est conçu pour des panneaux ≤ 6V. Au-delà, utiliser un régulateur buck en amont. Ne jamais alimenter la vanne 12V depuis la batterie 3,7V — utiliser une alimentation secteur séparée ou une batterie 12V dédiée.
L’ESP32 peut lire la tension de la batterie via un diviseur de tension :
from machine import ADC, Pin
def lire_batterie():
"""Tension batterie via diviseur R1=100k, R2=100k"""
adc = ADC(Pin(35))
adc.atten(ADC.ATTN_11DB)
brut = adc.read()
# Tension réelle = brut * (3.3/4095) * 2
tension = brut * 3.3 / 4095 * 2
# Pourcentage (approximation linéaire)
pct = max(0, min(100,
int((tension - 3.0) / (4.2 - 3.0) * 100)))
return tension, pct
Le watchdog redémarre l’ESP32 si le code se bloque :
from machine import WDT
wdt = WDT(timeout=30_000) # 30 secondes
def cycle():
wdt.feed() # Réarmer le watchdog
# ... code du cycle ...
wdt.feed()
Un capteur défaillant ne doit pas bloquer le système :
def lire_capteur_safe(fonction, defaut=None):
"""Encapsule la lecture avec gestion d'erreur"""
try:
return fonction()
except Exception as e:
log(f"Erreur capteur: {e}")
return defaut
Si le WiFi est indisponible, les données sont stockées localement et envoyées au prochain cycle réussi :
def envoyer_tampon(broker, prefix):
"""Envoie les mesures en attente"""
try:
with open("/data/tampon.json") as f:
lignes = f.readlines()
for ligne in lignes:
data = json.loads(ligne)
publier_mqtt(broker, prefix, data)
os.remove("/data/tampon.json")
except OSError:
pass # Pas de tampon en attente
Pour mettre à jour le firmware sans accès physique :
# Vérification de mise à jour au démarrage
def verifier_maj(serveur_url):
import urequests
r = urequests.get(f"{serveur_url}/version.txt")
version_distante = r.text.strip()
r.close()
with open("version.txt") as f:
version_locale = f.read().strip()
return version_distante != version_locale
Point pratique : Pour l’OTA complet, utilisez la bibliothèque
micropython-otaou implémentez un téléchargement fichier par fichier depuis un serveur HTTP.
┌─────────────────────────────────────────────────────┐
│ 🌱 Jardin Connecté │
├───────────┬───────────┬───────────┬─────────────────┤
│ Potager │ Pelouse │ Serre │ Batterie │
│ 💧 45% │ 💧 38% │ 💧 62% │ 🔋 85% | 78% │
│ 🌡️ 22°C │ 🌡️ 21°C │ 🌡️ 26°C │ | 91% │
├───────────┴───────────┴───────────┴─────────────────┤
│ Historique Humidité (7 jours) │
│ 70%| ___ │
│ 60%| _/\__/ \ ___ │
│ 50%|___/ \__/ \___ │
│ 40%|--- seuil bas ----------- ___ │
│ 30%| │
│ └───┬───┬───┬───┬───┬───┬───┬ │
│ Lun Mar Mer Jeu Ven Sam Dim │
├──────────────────────────┬──────────────────────────┤
│ Arrosage (volume/jour) │ Température sol vs air │
│ ██ 15L │ sol: ──── air: ---- │
│ ██ 12L │ │
│ █ 8L │ ---- ---- │
│ ██ 14L │ ──── ──── │
├──────────────────────────┴──────────────────────────┤
│ Dernières alertes │
│ ⚠️ 22/03 15:30 — Potager: humidité < 25% │
│ ℹ️ 21/03 08:00 — Pelouse: arrosage inhibé (pluie) │
└─────────────────────────────────────────────────────┘
from(bucket: "jardin")
|> range(start: -7d)
|> filter(fn: (r) =>
r._measurement == "sol"
and r.zone == "potager"
and r._field == "humidite")
|> aggregateWindow(every: 1h, fn: mean)
| Étape | Durée | Description |
|---|---|---|
| 1. Prototypage | 1 week-end | Un nœud sur breadboard, test capteurs |
| 2. Firmware | 1 week-end | Code MicroPython, test boucle complète |
| 3. Serveur | 1 soirée | Installer Mosquitto + InfluxDB + Grafana |
| 4. Intégration | 1 week-end | Tester la chaîne complète en intérieur |
| 5. Boîtier | 1 soirée | Câblage définitif, boîtier étanche |
| 6. Déploiement | 1/2 journée | Installation au jardin, calibration |
| 7. Ajustement | 2 semaines | Observer, ajuster seuils et paramètres |
| 8. Extension | Progressif | Ajouter des nœuds, zones, capteurs |
| Évolution | Complexité | Bénéfice |
|---|---|---|
| Caméra timelapse (ESP32-CAM) | ★★☆ | Suivi visuel de la croissance |
| Station météo complète | ★★☆ | Anémomètre, pluviomètre, UV |
| Intégration Home Assistant | ★★☆ | Pilotage unifié domotique + jardin |
| Machine learning (prédiction) | ★★★ | Arrosage prédictif basé sur l’historique |
| Réseau LoRa (longue portée) | ★★★ | Parcelles éloignées sans WiFi |
| Analyse d’image (maladie) | ★★★ | Détection de maladies foliaires par IA |
| Contrôle de serre (chauffage, ventilation) | ★★☆ | Régulation climatique automatique |
Le jardin connecté peut s’intégrer dans un écosystème plus large :
[Jardin connecté] ←→ [Home Assistant] ←→ [Alexa / Google Home]
↕
[Automatisations]
(Si pluie → fermer stores)
(Si gel → allumer serre)
Ce projet intégré illustre comment les sciences du début du livre s’appliquent concrètement :
| Science | Application dans le projet |
|---|---|
| Chimie du sol (Ch. 1) | Interprétation des mesures de pH et conductivité |
| Physique de l’eau (Ch. 2) | Compréhension du potentiel hydrique mesuré |
| Bilan hydrique (Ch. 3) | Algorithme d’irrigation basé sur ET₀ |
| Photosynthèse (Ch. 4) | Optimisation de l’éclairage en serre avec capteur PAR |
| Microbiologie (Ch. 5) | Suivi de la température du compost comme indicateur d’activité |
| Compostage (Ch. 6) | Alertes de retournement basées sur la courbe de température |
| Électronique (Ch. 7) | Architecture matérielle du système |
| Capteurs (Ch. 8) | Choix et calibration des instruments |
| Irrigation (Ch. 9) | Automatisation des vannes et algorithmes |
| Données (Ch. 10) | Stockage, visualisation et analyse |
🌱 Le jardin comme laboratoire : chaque mesure est une expérience, chaque saison un jeu de données. Le jardin connecté n’est pas une fin en soi, mais un outil pour mieux observer, comprendre et respecter les processus naturels. La technologie est au service de la nature, pas l’inverse.
| Aspect | Choix recommandé |
|---|---|
| Microcontrôleur | ESP32-WROOM-32 (WiFi intégré) |
| Langage | MicroPython (prototypage rapide) |
| Capteurs essentiels | Humidité capacitif + DS18B20 + BH1750 |
| Actionneur | Électrovanne NF 12V + relais |
| Communication | MQTT via WiFi |
| Serveur | Raspberry Pi 4 |
| Base de données | InfluxDB 2.x |
| Visualisation | Grafana |
| Alimentation | Panneau solaire 6V + 18650 + TP4056 |
| Budget total (3 zones) | ~220 € (3 nœuds + serveur) |
💡 Dernier conseil : Le meilleur système est celui qu’on construit progressivement. Commencez par un capteur et un script, puis ajoutez de la complexité uniquement quand vous en avez besoin. Le jardin a ses propres rythmes — prenez le temps d’observer avant d’automatiser.