Chapitre 11 — Projet Intégré : Le Jardin Connecté

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.

11.1 Cahier des charges

Fonctionnalités requises

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

Contraintes

11.2 Architecture système

Vue d’ensemble

                          ┌──────────────────────────┐
                          │    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   │
            └──────────────┘  └──────────────┘  └──────────────┘

Architecture d’un nœud

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)                   │
└─────────────────────────────────────────────┘

11.3 Liste des composants

Par nœud capteur/actionneur

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 €

Serveur central

Composant Référence Prix approx.
Raspberry Pi 4 (2 Go) + alimentation 45 €
Carte micro-SD 32 Go Classe 10 8 €
Total serveur   ~53 €

11.4 Configuration du serveur

Installation de la pile logicielle

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

Script de pont MQTT → InfluxDB

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.

Configuration de Mosquitto

# /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_anonymous et configurez l’authentification par mot de passe ou certificat TLS.

11.5 Firmware ESP32 complet

Structure des fichiers sur l’ESP32

/
├── 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

Fichier de configuration

{
    "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
}

Boucle principale (main.py)

# 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()

Module capteurs

# 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

Module irrigation

# 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"

11.6 Alimentation solaire

Dimensionnement du panneau

É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.

Circuit de charge

[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.

Surveillance de la batterie

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

11.7 Robustesse et fiabilité

Watchdog Timer

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()

Gestion des erreurs capteurs

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

Tampon de données hors ligne

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

Mise à jour OTA (Over-The-Air)

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-ota ou implémentez un téléchargement fichier par fichier depuis un serveur HTTP.

11.8 Tableau de bord final

Disposition recommandée du dashboard Grafana

┌─────────────────────────────────────────────────────┐
│                  🌱 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)  │
└─────────────────────────────────────────────────────┘

Requête InfluxDB pour Grafana

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)

11.9 Calendrier de déploiement

É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

11.10 Maintenance et évolution

Maintenance mensuelle

Évolutions possibles

É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

Intégration avec l’écosystème

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)

11.11 Retour aux sciences fondamentales

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.

11.12 Récapitulatif final

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.


← Précédent : Collecte et Visualisation des Données

← Retour à la Table des Matières