# Jardin LIBRE, documentation technique

# Partie I

## [![Logo Arles-Gnu-Linux.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/QK1smy7vGFF949My-logo-arles-gnu-linux.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/QK1smy7vGFF949My-logo-arles-gnu-linux.png)[![Logo_Verrerie.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/0Ku7RADhbj3BHGoj-logo-verrerie.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/0Ku7RADhbj3BHGoj-logo-verrerie.png)

## **Découverte du projet et du Raspberry Pi**

[![cc-by-sa-icon-white-svg.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/IFaydKnyXpC4MMko-cc-by-sa-icon-white-svg.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/IFaydKnyXpC4MMko-cc-by-sa-icon-white-svg.png)

### <span style="color:rgb(115,100,100);">Qu'est ce qu'un Raspberry PI ?</span>

<span style="white-space:pre-wrap;">Le </span>**Raspberry Pi**<span style="white-space:pre-wrap;"> est un ordinateur monocarte de petite taille et à faible coût, développé par la Fondation Raspberry Pi. Il est conçu pour promouvoir l'enseignement de l'informatique </span>[![raspberry-1.jpg](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/hGg33ueZPET9Ovgg-raspberry-1.jpg)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/hGg33ueZPET9Ovgg-raspberry-1.jpg)<span style="white-space:pre-wrap;">et des compétences en programmation. Le Raspberry Pi est équipé d'un processeur ARM, de mémoire RAM, et de divers ports pour la connectivité, tels que HDMI, USB, et GPIO (General Purpose Input/Output). Il fonctionne avec plusieurs systèmes d'exploitation, le plus courant étant </span>**Raspberry Pi OS**, une distribution GNU/Linux. Grâce à sa polyvalence et son coût abordable, le Raspberry Pi est largement utilisé dans des projets éducatifs, des applications IoT (Internet des objets), des projets de bricolage électronique, et même comme centre multimédia ou serveur léger.

[![raspberry-pi-5.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/Q8DJgbWsEJOD6L8T-raspberry-pi-5.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/Q8DJgbWsEJOD6L8T-raspberry-pi-5.png)

<span style="white-space:pre-wrap;">Le </span>**Raspberry Pi 5**<span style="white-space:pre-wrap;"> est un nano-ordinateur puissant, économe en énergie et totalement silencieux. Plus rapide que ses prédécesseurs, il est idéal pour faire tourner en continu notre </span>**station de jardin libre**<span style="white-space:pre-wrap;">. Grâce à ses ports </span>**GPIO**, il peut piloter capteurs et relais (température, humidité, pression, arrosage…), tout en hébergeant une page web locale et un wiki, même hors connexion Internet. Compact, robuste et compatible avec l’écosystème libre, c’est la pierre angulaire de notre démarche numérique écoresponsable.

### <span style="color:rgb(115,100,100);">Le projet</span>

<span style="white-space:pre-wrap;">Au cours de ces ateliers, nous explorerons les bases de l'électronique et de la programmation pour créer une station de </span>**jardin LIBRE fonctionnelle**. Que vous soyez débutant ou que vous ayez déjà des connaissances, ces ateliers sont conçus pour vous offrir une expérience enrichissante et pratique.

**Ce que vous apprendrez :**

- Électronique de base : Découvrez les composants électroniques et apprenez à les utiliser pour construire des projets interactifs.
- <span style="white-space:pre-wrap;">Programmation : Initiez-vous à la programmation avec Python pour contrôler et lire les données de capteurs comme la </span>**DHT22**<span style="white-space:pre-wrap;"> et la </span>**BMP280**.
- <span style="white-space:pre-wrap;">Développement web : Créez une interface utilisateur avec </span>**HTML5**<span style="white-space:pre-wrap;"> et </span>**CSS**<span style="white-space:pre-wrap;"> pour afficher les données de votre station météo en temps réel.</span>

### <span style="color:rgb(115,100,100);white-space:pre-wrap;">Installation de Raspberry Pi OS </span>

[![raspberrypios-1.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/CCt0i1xZHF9kkWzx-raspberrypios-1.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/CCt0i1xZHF9kkWzx-raspberrypios-1.png)

Le Raspberry Pi OS est un système d'exploitation libre basé sur Debian, spécialement conçu pour les ordinateurs Raspberry Pi. Il offre une interface graphique conviviale et une large gamme d'outils pour la programmation et l'apprentissage, le rendant idéal pour les projets éducatifs et les applications de bricolage.

####   


1. <span style="white-space:pre-wrap;">Téléchargement de l'outil Raspberry Pi imager : </span>**https://www.raspberrypi.com/software/**
2. Choisissez Raspberry Pi OS lite (sans interface graphique).
3. Dans la section général, vous pouvez configurer : 
    1. - Le nom de la machine (hostname).
        - Votre nom d'utilisateur et mot de passe.
        - La configuration de votre wifi.
        - Les locales (clavier et langue).
4. Dans la section services, vous pouvez activer le SSH.

[![raspberrypiimager-1.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/gH2ga9IZK5hKC7uw-raspberrypiimager-1.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/gH2ga9IZK5hKC7uw-raspberrypiimager-1.png)[<span style="color:rgb(0,0,0);white-space:pre-wrap;"> </span>![raspberrypiwifi-1.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/mo3uQ301EtrK17oj-raspberrypiwifi-1.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/mo3uQ301EtrK17oj-raspberrypiwifi-1.png)

[![raspberrypissh-1.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/gGPgBpU46YxxW5Tn-raspberrypissh-1.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/gGPgBpU46YxxW5Tn-raspberrypissh-1.png)

**SSH (Secure Shell)**<span style="white-space:pre-wrap;"> est un protocole sécurisé permettant de se connecter à distance à un ordinateur (comme un Raspberry Pi) via le terminal. Il chiffre les échanges et permet de contrôler, configurer ou transférer des fichiers entre machines, sans avoir besoin d’un écran ou clavier branché localement.</span>

### <span style="color:rgb(115,100,100);">Branchement de la sonde DHT22</span>

[![gpio.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/eevz4CIWLDG1JAbB-gpio.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/eevz4CIWLDG1JAbB-gpio.png)

- <span style="white-space:pre-wrap;">Connecter la broche </span>**VCC**<span style="white-space:pre-wrap;"> de la sonde à une broche </span>**3.3V**<span style="white-space:pre-wrap;"> du Raspberry Pi (</span>**broche 1**)
- <span style="white-space:pre-wrap;">Connecter la broche </span>**GND**<span style="white-space:pre-wrap;"> de la sonde à une broche </span>**GND**<span style="white-space:pre-wrap;"> du Raspberry Pi (</span>**broche 6**)
- <span style="white-space:pre-wrap;">Connecter la broche </span>**DATA**<span style="white-space:pre-wrap;"> de la sonde à une broche </span>**GPIO**<span style="white-space:pre-wrap;"> du Raspberry Pi (</span>**broche 7 - GPIO 4**)

[![gpio-board.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/PkUAJFp2daguoPsd-gpio-board.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/PkUAJFp2daguoPsd-gpio-board.png)

[![connexion-dht22.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/swM7vN8G3XEMAUQh-connexion-dht22.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/swM7vN8G3XEMAUQh-connexion-dht22.png)

# Partie II

## <span style="color:rgb(55,190,140);">**Récupération des données** </span><span style="color:rgb(55,190,140);">**avec python en mode interactif**</span>

### <span style="color:rgb(115,100,100);">Qu’est-ce que Python ?</span>

<span style="color:rgb(115,100,100);"><span style="font-family:'Linux Libertine';">![python.jpeg](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/SVMBEXuylRuTMWoJ-python.jpeg)</span></span>

- Python est un langage de programmation populaire, connu pour sa simplicité et sa lisibilité.
- Il est largement utilisé dans divers domaines, comme le développement web, l'analyse de données, et l'intelligence artificielle.

#### <span style="color:rgb(110,115,50);">Pourquoi Python ?</span>

- Facile à apprendre et à utiliser, surtout pour les débutants.
- Grande communauté de soutien et de nombreuses ressources disponibles en ligne.
- Utilisé dans de nombreux projets éducatifs et scientifiques.

#### <span style="color:rgb(110,115,50);">Découverte du terminal</span>

Un terminal est une interface où l'on peut taper des commandes pour interagir avec l'ordinateur.

#### <span style="color:rgb(110,115,50);">Nos premières commandes Python</span>

Dans le terminal, tapez python3 et appuyez sur Entrée. Cela lancera l'interpréteur Python en mode interactif.

Commande print :

La commande print affiche du texte à l'écran.

```python
print (“Python, c'est cool !”)
```

### <span style="color:rgb(115,100,100);">Constantes et variables</span>

- Une **constante** est un emplacement de stockage nommé pour des données qui ne changent pas au cours de l'exécution d'un programme.
- Une **variable** est un emplacement de stockage nommé pour des données qui peuvent changer au cours de l'exécution d'un programme.

```python
prenom = “Olivier”
```

### <span style="color:rgb(115,100,100);">Installation de la bibliothèque adafruit-circuitpython-dht sur le Raspberry Pi</span>

Les bibliothèques permettent d'étendre les fonctionnalités de Python.

Pour éviter les conflits entre bibliothèques, nous allons créer un environnement virtuel isolé pour gérer les dépendances de notre projet.

Pour créer un environnement virtuel, nous devons installer le paquet python3-venv :

```bash
sudo apt update
sudo apt install python3-venv
```

[![install-python3-venv-on-ubuntu.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/hxHRrlfBqnATogoE-install-python3-venv-on-ubuntu.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/hxHRrlfBqnATogoE-install-python3-venv-on-ubuntu.png)

Pour créer un environnement virtuel (par exemple pour le projet jardin LIBRE), taper la commande suivante :

```bash
python3 -m venv jardin
```

Tapez la commande suivante pour installer pip, le gestionnaire de paquets Python :

```bash
sudo apt update 
sudo apt install python3-pip
```

Pour pouvoir installer la bibliothèque, commençons pas entrer dans notre environnement virtuel :

```bash
source jardin/bin/activate
```

Tapez les commandes suivantes pour installer la bibliothèque adafruit-circuitpython-dht :

```bash
pip3 install adafruit-circuitpython-dht
pip3 install lgpio
pip3 install gpiod
```

### <span style="color:rgb(115,100,100);">lgpio : la bibliothèque GPIO de nouvelle génération</span>

📡 Elle permet un **accès bas niveau rapide et fiable** aux broches GPIO.

🧰 Elle est utilisée en coulisse par certaines bibliothèques Adafruit pour gérer les **signaux numériques très précis** du capteur DHT.

Pour quitter un environnement virtuel, taper la commande suivante :

```bash
deactivate
```

### <span style="color:rgb(115,100,100);">Lecture et affichage des données</span>

Démarrer le mode interactif de Python :

```bash
python3
```

Importer la bibliothèque adafruit-circuitpython-dht afin d’interagir avec la sonde DHT22 :

```python
import adafruit_dht
```

Importer la bibliothèque board :

La bibliothèque board sert à **nommer les broches physiques du microcontrôleur ou du Raspberry Pi de façon claire et universelle**.

```python
import board
```

Définition de notre capteur (ici, pour le GPIO4) :

```python
dhtDevice = adafruit_dht.DHT22(board.D4)
```

Lire les données :

```python
humidity = dhtDevice.humidity
temperature = dhtDevice.temperature
```

**humidity** et **temperature** sont deux variables, elles servent à stocker les valeurs.

Afficher les données :

```python
humidity
temperature
```

[![dht22-pi5.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/7xiPu0OoUCp7mUjr-dht22-pi5.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/7xiPu0OoUCp7mUjr-dht22-pi5.png)

# Partie III

## **<span style="color:rgb(55,190,140);">Création d'un script python</span>**

### <span style="color:rgb(115,100,100);">Pourquoi construire un script ?</span>

Lors du chapitre précédent, nous avons découvert comment lire les données du capteur DHT22 en mode interactif dans le terminal Python. Ce mode est idéal pour expérimenter, tester et comprendre le fonctionnement de la sonde. Mais il a ses limites : chaque mesure doit être lancée manuellement, les calculs doivent être refaits à la main, et les résultats ne sont pas sauvegardés.

Avec un script **Python autonome**, on passe à l'étape supérieure : notre station de jardin devient **automatisée** et **réutilisable**. Le code tourne en boucle, effectue les relevés à intervalle régulier, calcule automatiquement le point de rosée et l’humidex, puis affiche les résultats joliment formatés. C’est une première vraie brique vers une **station pour le jardin libre et autonome**, que l’on peut faire évoluer ensuite (enregistrement des données, affichage web, alertes, etc.). On quitte l’expérimentation manuelle pour poser les bases d’un **service automatisé, éthique, et maîtrisé de bout en bout**.

<span style="text-decoration:underline;">**Bref : on libère notre station.**</span>

### <span style="color:rgb(115,100,100);">Création du script</span>

Créer un script et éditer le avec la commande suivante :

```bash
nano station.py
```

### <span style="color:rgb(115,100,100);">Importation</span>

Commençons par importer la **bibliothèque adafruit-circuitpython-dht**, qui permet de lire les données des capteurs DHT (température et humidité) :

```python
import adafruit_dht
```

Importons aussi la **bibliothèque board** qui sert à indiquer l'emplacement de la sonde :

```python
import board
```

Imports supplémentaires :

- **time** pour gérer les temporisations entre les mesures.
- **math** pour les calculs scientifiques.
- **datetime** pour afficher la date et l'heure des relevés.

```python
import time
import math
from datetime import datetime
```

**Différences entre import et from ... import ...**

Quand on écrit import math, on importe toute la bibliothèque, et on accède à ses fonctions avec le préfixe math.nom\_fonction (ex : math.log()).

Quand on écrit from datetime import datetime, on importe directement **une fonction ou une classe précise**, ce qui permet de l’utiliser sans préfixe (ex : datetime.now() au lieu de datetime.datetime.now()).

**La première forme est plus explicite et lisible dans les grands scripts, la seconde est plus concise quand on utilise souvent la même fonction.**

### <span style="color:rgb(115,100,100);">Déclaration du capteur</span>

```python
dhtDevice = adafruit_dht.DHT22(board.D4)
```

Cette ligne permet de créer **un objet Python qui représente le capteur DHT22** connecté à la broche **GPIO 4** du Raspberry Pi.

**À noter** : Ici, le capteur apparaît sous une forme de variable et non pas de constante car c'est un objet dont l'état peut évoluer (ex. : erreurs, fermeture avec .exit())

### <span style="color:rgb(115,100,100);">Les fonctions</span>

Dans le script, on trouve deux blocs qui commencent par def. Ce sont des **fonctions**, c’est-à-dire des morceaux de code **réutilisables** qui effectuent une tâche bien précise :

```python
def calculer_point_de_rosee(temperature, humidity):

...

def calculer_humidex(temperature, point_de_rosee):

---
```

Une fonction, c’est comme une **boîte à outils** : on lui donne des **données en entrée** (ici, la température et l’humidité pour le point de rosée), et elle nous renvoie un **résultat calculé** (le point de rosée ou l’humidex). On peut **réutiliser** cette fonction autant de fois qu’on veut, sans devoir réécrire le calcul à chaque fois.

**Les fonctions permettent de structurer le code, de le rendre plus clair, plus facile à maintenir et à faire évoluer. C’est un pas de plus vers un code propre, compréhensible.**

```python
def calculer_point_de_rosee(temperature, humidity):
    # Formule pour calculer le point de rosée
    alpha = 17.27
    beta = 237.7
    gamma = (alpha * temperature) / (beta + temperature) + math.log(humidity / 100.0)
    point_de_rosee = (beta * gamma) / (alpha - gamma)
    return point_de_rosee
```

Fonction pour calculer le **point de rosée**, une mesure liée à la condensation de l’humidité (formule d’**August-Roche-Magnus**).

Le mot-clé **return** sert à **renvoyer le résultat** d'une fonction. C’est ce que la fonction renvoie à celui qui l’a appelée. Sans **return**, la fonction ferait les calculs, mais ne transmettrait rien de ses résultats !

```python
def calculer_humidex(temperature, point_de_rosee):
    # Formule pour calculer l'humidex
    humidex = temperature + (5/9) * (6.11 * math.exp(5417.7530 * ((1/273.16) - (1/273.15 + point_de_rosee))) - 10)
    return humidex
```

Fonction pour calculer l’**humidex**, un indice qui combine température et point de rosée pour donner une idée de la **température ressentie**.

### <span style="color:rgb(115,100,100);">Boucle infinie</span>

Dans notre script, nous utilisons une **boucle infinie** grâce à la ligne :

```python
while True:
```

Cela signifie que le bloc de code qui suit va s’exécuter en **boucle, sans fin**, tant qu’on n’interrompt pas manuellement le programme. Cette technique est parfaite pour une **station météo autonome** : elle va relever les données, les afficher, attendre un peu… puis recommencer, encore et encore. C’est ce qui permet de transformer notre Raspberry Pi en véritable service météo libre et auto-hébergé, toujours actif tant que la machine est allumée.

#### <span style="color:rgb(110,115,50);">Les variables</span>

```python
humidity = dhtDevice.humidity
temperature = dhtDevice.temperature
```

on utilise **deux variables**, humidity et temperature. Une variable, c’est comme une **boîte** dans laquelle on peut **stocker une valeur** pour la réutiliser plus tard, contrairement aux constantes, ici la **valeur sera amenée à bouger**. La fonction interroge le capteur et renvoie deux valeurs : l’humidité et la température mesurées. On les **dépose directement dans deux variables**, pour pouvoir les afficher et/ou faire des calculs.

**En nommant les variables de façon claire, on rend le code plus compréhensible et plus facile à relire.**

#### <span style="color:rgb(110,115,50);">Validité de la lecture</span>

Une **condition** permets de vérifier si **la lecture de la sonde est valable**. Si la lecture est correcte, le programme continue normalement, sinon, un message d'erreur s'affiche.

```python
if humidity is not None and temperature is not None:
    # Suite du programme
else:
    # Message en cas d'erreur
```

#### <span style="color:rgb(110,115,50);">Date et heure</span>

Dans notre station météo, on veut savoir quand chaque mesure a été prise. Pour ça, on utilise le module datetime, et plus précisément les lignes suivantes :

```python
now = datetime.now()
date_heure = now.strftime("%d-%m-%Y %H:%M:%S")
```

- datetime.now() récupère **la date et l’heure actuelles** du système.
- strftime() permet de **formater** cette date dans un style plus lisible : ici, jour-mois-année heure:minute:seconde.

#### <span style="color:rgb(110,115,50);">Calcul du point de rosée et de l'humidex</span>

Après avoir lu la température et l'humidité, on utilise les fonctions **définies plus haut** pour effectuer deux calculs :

```python
point_de_rosee = calculer_point_de_rosee(temperature, humidity)
humidex = calculer_humidex(temperature, point_de_rosee)
```

Ces lignes montrent comment on **réutilise nos fonctions** calculer\_point\_de\_rosee() et calculer\_humidex() en leur passant des **variables en paramètres**. Les résultats obtenus sont ensuite stockés dans **deux nouvelles variables**, point\_de\_rosee et humidex.

**Cela montre la force des fonctions : une fois écrites, on peut les appeler facilement autant de fois qu’on veut, ce qui rend notre code modulaire, réutilisable et lisible**

#### <span style="color:rgb(110,115,50);">Couleurs ANSI</span>

Nous pouvons ajouter en haut de notre script une série de **constantes** avec les couleurs :

On parle de constante car ces valeurs **ne changent pas pendant l’exécution du script**. On les écrit en **majuscules** pour le signaler clairement (c’est une convention en Python).

**L’intérêt ? C’est plus lisible, plus facile à modifier si on change de capteur ou de broche, et ça évite de répéter ces valeurs partout dans le code. On améliore donc la clarté et la maintenabilité du programme.**

```python
 RED = "\033[91m"
 GREEN = "\033[92m"
 YELLOW = "\033[93m"
 BLUE = "\033[94m"
 MAGENTA = "\033[95m"
 RESET = "\033[0m"
```

#### <span style="color:rgb(110,115,50);">Affichage des données</span>

Nous utilisons les **couleurs** pour rendre l'affichage **plus clair et agréable à lire dans le terminal**.

```python
print(f"{BLUE}Date et heure:{RESET} {date_heure}")
print(f"{GREEN}Température:{RESET} {temperature:.1f}°C")
print(f"{YELLOW}Humidité:{RESET} {humidity:.1f}%")
print(f"{RED}Point de rosée:{RESET} {point_de_rosee:.1f}°C")
print(f"{MAGENTA}Humidex:{RESET} {humidex:.1f}")
print("----")
```

Les variables numériques sont affichés avec **une seule décimale** grâce à la syntaxe {temperature:.1f}. Ce format permet d’avoir des valeurs **précises mais lisibles**, sans afficher une longue série de chiffres inutiles. Chaque ligne correspond à une donnée spécifique.

#### <span style="color:rgb(110,115,50);">Erreur de lecture de la sonde</span>

En cas d’échec de la lecture, un message d’erreur est affiché. C’est important pour diagnostiquer les soucis matériels ou de câblage.

```python
else:
    print("Échec de la lecture du capteur")
```

#### <span style="color:rgb(110,115,50);">Pause entre les lectures</span>

Mettons une pause de **20 secondes** entre deux lectures, pour éviter de surcharger le capteur et ralentir le flux d’informations.

```python
time.sleep(20)
```

### <span style="color:rgb(115,100,100);">Script complet</span>

```python
#Importation
import adafruit_dht
import adafruit_bmp280
import board
import busio
import time
import math
from datetime import datetime

#Déclaration du capteur
dhtDevice = adafruit_dht.DHT22(board.D4)

i2c = busio.I2C(board.SCL, board.SDA)

bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76)

#Couleurs
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
MAGENTA = "\033[95m"
RESET = "\033[0m"

#Calcul du point de rosée
def calculer_point_de_rosee(temperature, humidity):
    # Formule pour calculer le point de rosée
    alpha = 17.27
    beta = 237.7
    gamma = (alpha * temperature) / (beta + temperature) + math.log(humidity / 100.0)
    point_de_rosee = (beta * gamma) / (alpha - gamma)
    return point_de_rosee

#Calcul de l'humidex
def calculer_humidex(temperature, point_de_rosee):
    # Formule pour calculer l'humidex
    humidex = temperature + (5/9) * (6.11 * math.exp(5417.7530 * ((1/273.16) - (1/273.15 + point_de_rosee))) - 10)
    return humidex

#Boucle et affichage
while True:
    humidity = dhtDevice.humidity
    temperature = dhtDevice.temperature
    pression = bmp280.pressure
    if humidity is not None and temperature is not None:
        now = datetime.now()
        date_heure = now.strftime("%d-%m-%Y %H:%M:%S")
        point_de_rosee = calculer_point_de_rosee(temperature, humidity)
        humidex = calculer_humidex(temperature, point_de_rosee)
        print(f"{BLUE}Date et heure:{RESET} {date_heure}")
        print(f"{GREEN}Température:{RESET} {temperature:.1f}°C")
        print(f"{YELLOW}Humidité:{RESET} {humidity:.1f}%")
        print(f"{RED}Point de rosée:{RESET} {point_de_rosee:.1f}°C")
        print(f"{MAGENTA}Humidex:{RESET} {humidex:.1f}")
        print("----")

    else:
        print("Échec de la lecture du capteur")

#Pause de 20 secondes
time.sleep(20)
```

# Partie IV

## **<span style="color:rgb(55,190,140);">Utilisation de la fonction round()</span>**

Nous allons modifier notre script python pour introduire l'utilisation de la fonction Python round() pour **arrondir les valeurs numériques** (température, humidité, point de rosée, humidex) à **une seule décimale**.

### <span style="color:rgb(115,100,100);">Avant</span>

Les variables temperature ou humidity pouvaient contenir des valeurs longues comme 23.67893452, ce qui :

- encombrait la sortie dans le terminal ou sur la page web,
- n’était pas lisible pour l’utilisateur final,
- et ne servait à rien dans un contexte grand public où une précision de 0,1 est largement suffisante.

**Pour avoir un affichage correct, nous utilisons le code suivant : print(f“{temperature:.1f}”). Celui-ci arrondi bien notre résultat à un chiffre après la virgule, mais il n'arrondi que l'affichage.**

### <span style="color:rgb(115,100,100);">Après</span>

Grâce à round(variable, 1), on obtient des valeurs comme 23.7, ce qui :

- améliore la **clarté visuelle** des résultats,
- simplifie l’envoi des données vers une interface web ou une base de données,
- et réduit les erreurs d’interprétation dans les calculs suivants.

**round() arrondit réellement la valeur stockée, contrairement à l’ancienne méthode. Cela permet donc une meilleure réutilisation des données : les fonctions ou interfaces web utilisent des valeurs déjà simplifiées.**

### <span style="color:rgb(115,100,100);">Changement dans le code</span>

```python
print(f"{GREEN}Température :{RESET} {round(temperature, 1)}°C")
print(f"{YELLOW}Humidité :{RESET} {round(humidity, 1)}%")
print(f"{RED}Point de rosée :{RESET} {round(point_de_rosee, 1)}°C")
print(f"{MAGENTA}Indice humidex :{RESET} {round(humidex, 1)}")
```

Ce changement, bien que minime à première vue, marque une **étape importante vers la structuration professionnelle** du script et prépare le terrain pour la future séparation des modules (capteur, API, interface web).

# Partie V

## **<span style="color:rgb(55,190,140);">Logique métier</span>**

Nous sommes prêt à structurer **plus proprement notre application** en séparant :

- la **partie capteurs** (lecture, calculs...) -&gt; **Logique métier**
- et l’**application web** (routes, affichage)

### <span style="color:rgb(115,100,100);">Création d'un fichier capteur.py</span>

Dans une démarche de développement propre, modulaire et réutilisable, on **sépare la logique métier** (la lecture des données et les calculs) de **l’interface utilisateur** (site web, affichage).

Le fichier capteur.py **ne contient plus de boucle** while True ni de traitement direct. Il est composé **exclusivement de fonctions**, que l’on pourra appeler **depuis une autre application**, ici app.py (notre serveur Flask).

Ce script agit comme **une boîte à outils**, il regroupe :

- la lecture du capteur DHT22,
- le calcul du point de rosée,
- le calcul de l’indice humidex,
- la récupération de la date et heure actuelles.

### <span style="color:rgb(115,100,100);">Code</span>

```python
#importations
import adafruit_dht
import board
import math
from datetime import datetime


def lire_donnees_capteur():
    try:
        dhtDevice = adafruit_dht.DHT22(board.D4)
        humidity = dhtDevice.humidity
        temperature = dhtDevice.temperature
        dhtDevice.exit()
        if humidity is not None and temperature is not None:
            return round(humidity, 1), round(temperature, 1)
        else:
            return None, None

    except RuntimeError as error:
        print("Erreur de lecture :", error)
        return None, None

    except Exception as error:
        dhtDevice.exit()
        raise error

def calculer_point_de_rosee(temperature, humidity):
    #Formule pour calculer le point de rosée
    alpha = 17.27
    beta = 237.7
    gamma = (alpha * temperature) / (beta + temperature) + math.log(humidity / 100)
    point_de_rosee = (beta * gamma) / (alpha - gamma)
    return round(point_de_rosee, 1)

def calculer_humidex(temperature, point_de_rosee):
    #Formule pour calculer l'indice humidex
    humidex = temperature + (5/9) * (6.11 * math.exp(5417.7530 * ((1/273.16) - (1/273.15 + point_de_rosee))) - 10)
    return round(humidex, 1)

def recuperer_date_heure():
    return datetime.now().strftime("%d-%m-%Y %H:%M:%S")
```

### <span style="color:rgb(115,100,100);">Commentaires sur la fonction lire\_donnees\_capteur() :</span>

- Cette fonction permet de lire la température et l’humidité à partir du capteur **DHT22** connecté au **GPIO4** du Raspberry Pi.
- Elle renvoie ces deux valeurs sous forme de nombres arrondis à une décimale.
- Si la lecture échoue, elle retourne None, None.

```python
try:
```

- On démarre un bloc qui va tenter de réaliser la lecture
- Si une erreur survient, Python basculera dans le bloc except.

```python
dhtDevice = adafruit_dht.DHT22(board.D4)
```

- Cette ligne crée un objet capteur, ici un DHT22, branché sur le GPIO4 (représenté par board.D4).
- 💡 👉 C’est une étape indispensable pour communiquer avec le capteur. L'objet est créé directement dans la fonction pour éviter tous risques de blocage du capteur.

```python
humidity = dhtDevice.humidity temperature = dhtDevice.temperature
```

Ces lignes interrogent le capteur pour récupérer :

- humidity → le taux d’humidité.
- temperature → la température.

```python
dhtDevice.exit()
```

- Cette commande est **très importante** : elle **libère les ressources GPIO** utilisées par le capteur.
- Cela évite les erreurs fréquentes sur Raspberry Pi telles que : “Lost access to message queue”.

```python
if humidity is not None and temperature is not None:
```

- On vérifie que le capteur a bien répondu avec des données valides.
- Les capteurs DHT peuvent parfois échouer à donner une valeur.

```python
return round(humidity, 1), round(temperature, 1)
```

Si les valeurs sont valides :

- On les arrondit à une décimale pour un affichage plus lisible.
- Puis on les renvoie sous la forme de deux nombres.

```python
else: return None, None
```

Si le capteur n’a pas répondu correctement, la fonction renvoie None pour les deux mesures.

```python
except RuntimeError as error:
```

- Gestion des erreurs courantes dues aux capteurs DHT (perte temporaire de lecture).
- On affiche l’erreur mais le programme continue de fonctionner.

```python
except Exception as error:
```

- Gestion des erreurs plus graves (problème matériel, GPIO bloqué, etc.).
- On ferme proprement le capteur avec dhtDevice.exit() avant de relancer l’erreur (raise error) pour éventuellement arrêter le programme.

# Partie VI

## **<span style="color:rgb(55,190,140);">Interface utilisateur</span>**

### <span style="color:rgb(115,100,100);">La bibliothèque flask</span>

Flask est un micro-framework web en Python, simple et léger. Il permet de créer rapidement un petit site web ou une API sans dépendances complexes.

Dans notre cas, Flask permet de :

- transformer votre Raspberry Pi en serveur web local ;
- créer une interface web pour afficher les données météo en temps réel ;
- séparer le traitement (dans capteur.py) de l’affichage (dans app.py).

#### <span style="color:rgb(110,115,50);">Installation de flask</span>

```bash
pip3 install flask
```

#### <span style="color:rgb(110,115,50);">Importation de flask</span>

Importons flask au début de notre code :

```python
from flask import Flask, render_template_string
```

- Flask : la classe qui permet de **créer l’application web**.
- render\_template\_string : permet de **générer une page HTML directement dans le script** (sans créer de fichier .html pour le moment).

#### <span style="color:rgb(110,115,50);">Importation des données de l'application métier</span>

Dans notre fichier app.py, nous avons besoin d'accéder aux données produites par nos capteurs (température, humidité, etc.) et aux fonctions de calcul (point de rosée, humidex…).

Ces fonctions ne sont **pas écrites directement dans app.py**, mais dans un autre fichier appelé capteur.py. Ce fichier est ce qu'on appelle notre **application métier** : il contient toute la **logique de calcul et de lecture**.

```python
from capteur import (
    lire_donnees_capteur,
    calculer_point_de_rosee,
    calculer_humidex,
    recuperer_date_heure
)
```

- from capteur : on indique qu’on veut importer **depuis le fichier capteur.py**.
- import (…) : on précise **quelles fonctions on veut utiliser** dans ce fichier.

C’est un peu comme si on disait :

*« Va chercher dans la boîte capteur.py ces outils bien précis, et rends-les disponibles ici. »*

#### <span style="color:rgb(110,115,50);">Création de l’application Flask</span>

```python
# Création de l'application Flask
app = Flask(__name__)
```

Cette ligne est essentielle. Elle signifie :

- On crée une instance de notre application Flask.
- name est une variable spéciale en Python qui contient le nom du fichier. Flask s’en sert pour retrouver le chemin vers les fichiers associés (comme les templates HTML ou les fichiers CSS).

Autrement dit :

👉 « Je démarre une application web avec ce fichier comme point d’entrée. »

#### <span style="color:rgb(110,115,50);">Définir une route dans Flask</span>

```python
@app.route('/')
def index():
```

@app.route('/') : Indique que la fonction juste en dessous va être exécutée quand un utilisateur accède à l’adresse « / », c’est-à-dire la racine du site, la page d’accueil.

def index(): C’est une fonction Python classique, appelée ici index. Elle contiendra le code qui définit ce que l'on affiche ou retourne quand on visite cette page.

### <span style="color:rgb(115,100,100);">Récupération des données</span>

```python
humidity, temperature = lire_donnees_capteur()
if humidity is not None and temperature is not None:
    point_de_rosee = calculer_point_de_rosee(temperature, humidity)
    humidex = calculer_humidex(temperature, point_de_rosee)
    date_heure = recuperer_date_heure()
```

nous mettons en œuvre **la logique métier définie dans notre fichier capteur.py** :

- **lire\_donnees\_capteur()** : interroge le capteur DHT22 et renvoie les valeurs d’humidité et de température.
- **calculer\_point\_de\_rosee()** : calcule le point à partir duquel l’humidité de l’air commence à se condenser.
- **calculer\_humidex()** : estime la température ressentie selon l’humidité ambiante.
- **recuperer\_date\_heure()** : renvoie l’heure exacte de la mesure.

### <span style="color:rgb(115,100,100);">Génération simple d’une page HTML avec une f-string</span>

```html
html = f"""
<h1>Données météo locales</h1>
<ul>
  <li>Date et heure : {date_heure}</li>
  <li>Température : {temperature} °C</li>
  <li>Humidité : {humidity} %</li>
  <li>Point de rosée : {point_de_rosee} °C</li>
  <li>Humidex : {humidex}</li>
  </ul>
"""
```

Nous créons **une chaîne de caractères contenant du HTML**, dans laquelle nous insérons directement les **valeurs des variables Python** (température, humidité, etc.).

- La **balise h1** sert à afficher titre de niveau 1.
- La **balise ul** sert à afficher une liste à puce, les **balises li** correspondent aux éléments de la liste.
- La **balise p** sert à afficher un paragraphe.

#### <span style="color:rgb(110,115,50);">Pourquoi le f devant la chaîne ?</span>

Le f signifie que c’est une **f-string** (ou formatted string literal en anglais).

Cela permet d’écrire **des variables à l’intérieur de la chaîne** entre des accolades {}.

C’est une **méthode claire et moderne pour mélanger texte et variables** dans une même ligne.

#### <span style="color:rgb(110,115,50);">Et si la lecture échoue ?</span>

```python
else:
    html = "<p>Erreur de lecture du capteur.</p>"
```

Ce code gère le cas où le capteur ne renvoie pas de valeurs valides. Dans ce cas, on prépare un message simple à afficher sur la page web.

#### <span style="color:rgb(110,115,50);">Affichage de la page web et démarrage de l'application</span>

```python
return render_template_string(html)
```

Ce code indique à Flask d'**afficher le HTML généré comme contenu de la page web**.

```python
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
```

Ce code permet de lancer l'application :

- **host='0.0.0.0'** : rend l'application accessible sur le réseau.
- **port=5000** : port utilisé pour acceder au site.

### <span style="color:rgb(115,100,100);">Code</span>

```python
#importations
from flask import Flask, render_template_string
from capteur import (
    lire_donnees_capteur,
    calculer_point_de_rosee,
    calculer_humidex,
    recuperer_date_heure
)

# Définition de l'application Flask
app = Flask(__name__)

@app.route('/')
def index():
    humidity, temperature = lire_donnees_capteur()
    if humidity is not None and temperature is not None:
        point_de_rosee = calculer_point_de_rosee(temperature, humidity)
        humidex = calculer_humidex(temperature, point_de_rosee)
        date_heure = recuperer_date_heure()

        html = f"""
        <h1>Données météo locales</h1>
        <ul>
            <li>Date et heure : {date_heure}</li>
            <li>Température : {temperature} °C</li>
            <li>Humidité : {humidity} %</li>
            <li>Point de rosée : {point_de_rosee} °C</li>
            <li>Humidex : {humidex}</li>
        </ul>
        """
 
    else:
        html = "<p>Erreur de lecture du capteur.</p>"

    return render_template_string(html)
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
```

[![capture-station.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/s0mf3Z69I2HCu6cu-capture-station.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/s0mf3Z69I2HCu6cu-capture-station.png)

# Partie VII

## **<span style="color:rgb(55,190,140);">Présentation de la sonde BMP280</span>**

Le **BMP280** est un petit capteur environnemental, capable de mesurer :

- La **pression atmosphérique** (en hPa)
- La **température de l'air** (en °C)

C’est un capteur léger, peu gourmand en énergie et parfaitement adapté aux stations météo autonomes.

### <span style="color:rgb(115,100,100);">Câblage du BMP280 sur le Raspberry Pi</span>

Le BMP280 communique avec le Raspberry Pi via le **protocole I2C**, qui utilise **2 fils de données**, plus l'alimentation et la masse.

<table id="bkmrk-broche-bmp280" style="border-collapse:collapse;width:100%;height:149px;"><colgroup><col style="width:33.3333%;"></col><col style="width:33.3333%;"></col><col style="width:33.3333%;"></col></colgroup><thead><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">Broche BMP280</td><td class="align-center" style="height:29.8px;">Broche Raspberry PI</td><td class="align-center" style="height:29.8px;">Rôle</td></tr></thead><tbody><tr style="height:29.8px;"><td>VCC

</td><td>Broche 17 (3.3V)</td><td>Alimentation</td></tr><tr style="height:29.8px;"><td>GND</td><td>Broche 9 (GND)</td><td>Masse</td></tr><tr style="height:29.8px;"><td>SCL</td><td>Broche 5 (GPIO3)</td><td>Horloge (Clock)</td></tr><tr style="height:29.8px;"><td>SDA</td><td>Broche 3 (GPIO2)</td><td>Données (Data)</td></tr></tbody></table>

### <span style="color:rgb(115,100,100);">Activation du protocole I2C sur le Raspberry Pi</span>

Le protocole I2C est désactivé par défaut sur Raspberry Pi OS. Il faut l’activer manuellement.

**Ouvrir la configuration du Raspberry Pi :**

```bash
sudo raspi-config
```

**[![raspi-config.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/pYzhv5Vk42pLGjHp-raspi-config.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/pYzhv5Vk42pLGjHp-raspi-config.png)**

**Aller dans le menu :**

```
3 Interface Options
I2C → Enable
```

[![interface-option.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/GNHBJyYtVCdf4gfT-interface-option.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/GNHBJyYtVCdf4gfT-interface-option.png)

**Redémarrer le Raspberry Pi :**

```bash
sudo reboot
```

### <span style="color:rgb(115,100,100);">Vérifier si le BMP280 est détecté</span>

Installer les **outils I2C** :

```bash
sudo apt install i2c-tools
```

**Scanner le bus I2C** pour vérifier que le capteur est bien détecté :

```bash
i2cdetect -y 1
```

Exemple de retour :

[![scan-i2c.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/s82PK8rb3GFfOJ5G-scan-i2c.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/s82PK8rb3GFfOJ5G-scan-i2c.png)

👉 Ici, on voit bien 76, ce qui indique que le BMP280 est bien détecté.

### <span style="color:rgb(115,100,100);">Test de la sonde en mode interactif</span>

Dans votre **environnement virtuel Python**, commencer par installer la **bibliothèque pour la sonde BMP280** :

```bash
 pip3 install adafruit-circuitpython-bmp280
```

Test en mode interactif :

```python
python3
 
import board
import busio
import adafruit_bmp280

i2c = busio.I2C(board.SCL, board.SDA)
bm280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76)

print(bme280.pressure)
print(bme280.temperature)
```

Si le scan de votre bus i2c à donné 77 comme résultat, remplacer address=0x76 par address=0x77

# Partie VIII

## **<span style="color:rgb(55,190,140);">Ajout de la sonde BMP280 aux scripts</span>**

Nous allons modifier nos scripts existants pour tester la sonde bmp280 aussi bien dans le **terminal** que sur le **serveur web (flask)**.

### <span style="color:rgb(115,100,100);">Modification du script station.py</span>

```python
#Importation
import adafruit_dht
import adafruit_bmp280
import board
import busio
import time
import math
from datetime import datetime

#Déclaration du capteur
dhtDevice = adafruit_dht.DHT22(board.D4)

i2c = busio.I2C(board.SCL, board.SDA)

bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76)

#Couleurs
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
MAGENTA = "\033[95m"
CYAN = "\033[96m"
RESET = "\033[0m"

#Calcul du point de rosée
def calculer_point_de_rosee(temperature, humidity):
    # Formule pour calculer le point de rosée
    alpha = 17.27
    beta = 237.7
    gamma = (alpha * temperature) / (beta + temperature) + math.log(humidity / 100.0)
    point_de_rosee = (beta * gamma) / (alpha - gamma)
    return point_de_rosee

#Calcul de l'humidex
def calculer_humidex(temperature, point_de_rosee):
    # Formule pour calculer l'humidex
    humidex = temperature + (5/9) * (6.11 * math.exp(5417.7530 * ((1/273.16) - (1/273.15 + point_de_rosee))) - 10)
    return humidex

#Boucle et affichage
while True:
    humidity = dhtDevice.humidity
    temperature = dhtDevice.temperature
    pression = bmp280.pressure
    if humidity is not None and temperature is not None:
        now = datetime.now()
        date_heure = now.strftime("%d-%m-%Y %H:%M:%S")
        point_de_rosee = calculer_point_de_rosee(temperature, humidity)
        humidex = calculer_humidex(temperature, point_de_rosee)
        print(f"{BLUE}Date et heure:{RESET} {date_heure}")
        print(f"{GREEN}Température:{RESET} {round(temperature, 1)}°C")
        print(f"{YELLOW}Humidité:{RESET} {round(humidity, 1)}%")
        print(f"{RED}Point de rosée:{RESET} {round(point_de_rosee, 1)}°C")
        print(f"{MAGENTA}Humidex:{RESET} {round(humidex, 1)}")
        print(f"{CYAN}Pression atmosphérique:{RESET} {round(pression, 2)} hPa")
        print("----")
    else:
        print("Échec de la lecture du capteur")

    #Pause de 20 secondes
    time.sleep(20)
```

### <span style="color:rgb(115,100,100);">Modification du script capteur.py</span>

```python
#importations
import adafruit_dht
import adafruit_bmp280
import board
import busio #Bibliothèque permettant d'ouvrir une communication sur un bus matériel
import math
from datetime import datetime

#Initialisation du bus I2C
i2c = busio.I2C(board.SCL, board.SDA)

#Initialisation de la sonde
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76)

def lire_donnees_capteur():
    try:
        dhtDevice = adafruit_dht.DHT22(board.D4)
        humidity = dhtDevice.humidity
        temperature = dhtDevice.temperature
        dhtDevice.exit()
        if humidity is not None and temperature is not None:
            return round(humidity, 1), round(temperature, 1)
        else:
            return None, None

    except RuntimeError as error:
        print("Erreur de lecture :", error)
        return None, None

    except Exception as error:
        dhtDevice.exit() #Libérer le GPIO même en cas de crash
        raise error #Le programme crashe, car c'est une erreur critique

def calculer_point_de_rosee(temperature, humidity):
    #Formule pour calculer le point de rosée
    alpha = 17.27
    beta = 237.7
    gamma = (alpha * temperature) / (beta + temperature) + math.log(humidity / 100)
    point_de_rosee = (beta * gamma) / (alpha - gamma)
    return round(point_de_rosee, 1)

def calculer_humidex(temperature, point_de_rosee):
    #Formule pour calculer l'indice humidex
    humidex = temperature + (5/9) * (6.11 * math.exp(5417.7530 * ((1/273.16) - (1/273.15 + point_de_rosee))) - 10)
    return round(humidex, 1)

def lire_pression():
    try:
        pression = bmp280.pressure
        return round(pression, 1)
    except Exception as error:
        print("Erreur de lecture BMP280 :", error)
        return None

def recuperer_date_heure():
    return datetime.now().strftime("%d-%m-%Y %H:%M:%S")
```

### <span style="color:rgb(115,100,100);">Modification du script app.py</span>

```python
#importations
from flask import Flask, render_template_string
from capteur import (
    lire_donnees_capteur,
    calculer_point_de_rosee,
    calculer_humidex,
    recuperer_date_heure,
    lire_pression
)

# Définition de l'application Flask
app = Flask(__name__)

@app.route('/')
def index():
    humidity, temperature = lire_donnees_capteur()
    if humidity is not None and temperature is not None:
        point_de_rosee = calculer_point_de_rosee(temperature, humidity)
        humidex = calculer_humidex(temperature, point_de_rosee)
        date_heure = recuperer_date_heure()
        pression = lire_pression()

        html = f"""
        <h1>Données météo locales</h1>
        <ul>
            <li>Date et heure : {date_heure}</li>
            <li>Température : {temperature} °C</li>
            <li>Humidité : {humidity} %</li>
            <li>Point de rosée : {point_de_rosee} °C</li>
            <li>Humidex : {humidex}</li>
            <li>Pression atmosphérique : {pression} hPa</li>
        </ul>
        """
    else:
        html = "<p>Erreur de lecture du capteur.</p>"

    return render_template_string(html)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
```

# Partie IX

## **<span style="color:rgb(55,190,140);">Mise en couleur dans le terminal avec Rich</span>**

### <span style="color:rgb(115,100,100);">Pourquoi améliorer l'affichage dans un terminal ?</span>

Lorsque notre station de jardin renvoie des données météo (température, humidité, pression…), il est utile d'avoir un affichage **lisible** et **coloré** pour mieux distinguer les différentes mesures. Jusqu'ici, nous utilisions des **codes ANSI** pour ajouter un peu de couleur dans le terminal.

Mais pour un rendu plus **propre**, **lisible**, **personnalisable** et **moderne**, la bibliothèque rich est une excellente alternative.

### <span style="color:rgb(115,100,100);">Couleurs ANSI vs Rich</span>

<table id="bkmrk-%F0%9F%A7%B1-ansi-%28codes-couleu" style="border-collapse:collapse;width:100%;height:178.8px;"><colgroup><col style="width:50%;"></col><col style="width:50%;"></col></colgroup><thead><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">**🧱 ANSI (codes couleurs bruts)**</td><td class="align-center" style="height:29.8px;">**🌈 Rich (interface haut niveau)**</td></tr></thead><tbody><tr style="height:29.8px;"><td>Utilise des codes comme `\\033\[91m`</td><td>Utilise des noms lisibles : `“red”`, `“green”`, `“cyan”`…</td></tr><tr style="height:29.8px;"><td>Peu de contrôle sur le style</td><td>Affichage centré, aligné, stylisé facilement</td></tr><tr style="height:29.8px;"><td>Difficile à maintenir et à lire</td><td>Code clair, moderne, accessible</td></tr><tr style="height:29.8px;"><td>8 couleurs de base</td><td>Plus de 140 couleurs disponibles</td></tr><tr style="height:29.8px;"><td>Pas d'affichage enrichi</td><td>Icônes, tableaux, mise en page possible</td></tr></tbody></table>

### <span style="color:rgb(115,100,100);">Liste de couleurs Rich utiles</span>

Voici quelques couleurs bien contrastées à utiliser dans un terminal (parmi les 140 disponibles) :

- red
- green
- blue
- yellow
- magenta
- cyan
- white
- black
- orange1
- violet
- deep\_sky\_blue1
- spring\_green2
- dark\_orange3
- turquoise2
- light\_salmon1ark\_orange3
- turquoise2
- light\_salmon1
- chartreuse2
- sky\_blue1
- gold3
- plum4
- aquamarine1
- medium\_violet\_red
- khaki1
- grey50

⚠️ Certaines couleurs ne sont visibles correctement que sur les terminaux qui supportent le mode 256 couleurs.

### <span style="color:rgb(115,100,100);">Installation de la bibliothèque Rich</span>

```bash
pip install rich
```

### <span style="color:rgb(115,100,100);">Initialisation de Rich</span>

```python
from rich.console import Console
from rich.text import Text
```

- **Console** est l'outil de base de la bibliothèque **Rich**. C’est lui qui gère l’affichage dans le terminal.
- **Text** permet de **styliser dynamiquement** des portions de texte dans une même ligne (**gras**, *italique*, <span style="text-decoration:underline;">souligné</span>…).

#### <span style="color:rgb(110,115,50);">Création de l'objet console :</span>

```python
console = Console()
```

Cette ligne permet de créer un objet console qui va gérer l'affichage dans le terminal. C’est avec cet objet qu’on peut utiliser toutes les fonctions de rich.

### <span style="color:rgb(115,100,100);">Affichage des données</span>

```python
# Importation des bibliothèques permettant de gérer couleurs et mise en page
from rich.console import Console
from rich.text import Text

# Déclaration de l'objet console pour gérer les couleurs et mise en page
console = Console()

console.print(f"[bold cyan]Date et heure :[/bold cyan] {date_heure}")
console.print(f"[bold red]Température :[/bold red] {round(temperature, 1)}°C")
console.print(f"[bold blue]Humidité :[/bold blue] {round(humidity, 1)}%")
console.print(f"[bold magenta]Point de rosée :[/bold magenta] {round(point_de_rosee, 1)}°C")
console.print(f"[bold yellow]Humidex :[/bold yellow] {round(humidex, 1)}")
console.print(f"[bold green]Pression atmosphérique :[/bold green] {round(pression, 2)} hPa")
```

- console.print(…) : méthode principale pour **afficher du texte enrichi** (couleur, gras, emoji…).
- \[bold red\]Température :\[/bold red\] : indique que le mot “**Température** :” sera **affiché en rouge gras**.

[![couleur-rich.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/as5I4BhIWVTgpzO0-couleur-rich.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/as5I4BhIWVTgpzO0-couleur-rich.png)

# Partie X

## <span style="color:rgb(55,190,140);">**Icônes Unicode dans les scripts Python**</span>

### <span style="color:rgb(115,100,100);">Pourquoi les utiliser ?</span>

Les caractères Unicode (émojis et pictogrammes) permettent :

- d’améliorer la **lisibilité** de vos affichages terminal ;
- de rendre les données plus **intuitives** ;
- de créer des **interfaces plus conviviales**, même en mode texte.

### <span style="color:rgb(115,100,100);">Comment afficher un caractère Unicode en python ?</span>

Chaque icône a un **code Unicode** qu’on peut insérer en Python comme ceci :

```python
print("\U0001F4C5")  # Affiche 📅 (calendrier)
```

⚠️ Il faut **8 caractères hexadécimaux** après \\U (majuscule). Complétez avec des zéros si besoin.

### <span style="color:rgb(115,100,100);">Écrire un caractère Unicode avec son clavier (Ubuntu) ?</span>

Sur le **système Ubuntu** (et plus généralement sous tout système Linux basé sur X11), vous pouvez **taper un caractère Unicode** grâce à une combinaison de touches très simple :

- Appuyer sur **Ctrl + Shift + u** → un u souligné apparaît.
- Taper ensuite le code hexadécimal Unicode (par exemple 1f4c5 pour 📅).
- Appuyer sur Entrée ou Espace → le caractère est inséré.

### <span style="color:rgb(115,100,100);">Liste d'icônes par thème</span>

#### <span style="color:rgb(110,115,50);">🌦️ Météo &amp; Environnement :</span>

<table id="bkmrk-ic%C3%B4ne-signification-" style="border-collapse:collapse;width:100%;height:238.4px;"><colgroup><col style="width:33.3333%;"></col><col style="width:33.3333%;"></col><col style="width:33.3333%;"></col></colgroup><thead><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">**Icône**</td><td class="align-center" style="height:29.8px;">**Signification**</td><td class="align-center" style="height:29.8px;">**Code Unicode**</td></tr></thead><tbody><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🌡️</td><td style="height:29.8px;">Thermomètre / température</td><td>1f321</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">💧</td><td style="height:29.8px;">Goutte d’eau / humidité</td><td>1f4a7</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">❄️</td><td style="height:29.8px;">Flocon de neige / point de rosée</td><td>2746</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🔥</td><td style="height:29.8px;">Flamme / chaleur / humidex</td><td>1f525</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🌬️</td><td style="height:29.8px;">Vent / pression atmosphérique</td><td>1f32c</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🌱</td><td style="height:29.8px;">Jeune pousse / jardin / sol</td><td>1f331</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🌻</td><td style="height:29.8px;">Tournesol / jardin</td><td>1f33b</td></tr></tbody></table>

#### <span style="color:rgb(110,115,50);">📅 Temps &amp; Organisation :</span>

<table id="bkmrk-ic%C3%B4ne-signification--1" style="border-collapse:collapse;width:100%;height:119.2px;"><colgroup><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col></colgroup><thead><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">**Icône**</td><td class="align-center" style="height:29.8px;">**Signification**</td><td class="align-center" style="height:29.8px;">**Code Unicode**</td></tr></thead><tbody><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">📅</td><td>Calendrier</td><td>1f4c5</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🕒</td><td>Horloge / heure</td><td>1f552</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🗓️</td><td>Calendrier à feuillets</td><td>1f5d3</td></tr></tbody></table>

#### <span style="color:rgb(110,115,50);">🧪 Capteurs &amp; Science :</span>

<table id="bkmrk-ic%C3%B4ne-signification--2" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col></colgroup><thead><tr><td class="align-center" style="height:29.8px;">**Icône**</td><td class="align-center" style="height:29.8px;">**Signification**</td><td class="align-center" style="height:29.8px;">**Code Unicode**</td></tr></thead><tbody><tr><td class="align-center">🧪</td><td>Expérience / capteur</td><td>1f9ea</td></tr><tr><td class="align-center">🧬</td><td>ADN / science</td><td>1f9ec</td></tr><tr><td class="align-center">⚗️</td><td>Alambic / mesure scientifique</td><td>2697</td></tr></tbody></table>

#### <span style="color:rgb(110,115,50);">🖥️ Informatique et électronique :</span>

<table id="bkmrk-ic%C3%B4ne-signification--3" style="border-collapse:collapse;width:100%;height:178.8px;"><colgroup><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col></colgroup><thead><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">**Icône**</td><td class="align-center" style="height:29.8px;">**Signification**</td><td class="align-center" style="height:29.8px;">**Code Unicode**</td></tr></thead><tbody><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🖥️</td><td>Ordinateur</td><td style="height:29.8px;">1f5a5</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">💾 </td><td>Sauvegarde / stockage</td><td style="height:29.8px;">1f4be</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🔌</td><td>Électricité / câblage</td><td style="height:29.8px;">1f50c</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🧰</td><td>Boîte à outils / montage</td><td style="height:29.8px;">1f9f0</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🧲</td><td>Électronique / aimant</td><td style="height:29.8px;">1f9f2</td></tr></tbody></table>

#### <span style="color:rgb(110,115,50);">🛠️ Outils &amp; Fabrication :</span>

<table id="bkmrk-ic%C3%B4ne-signification--4" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col></colgroup><thead><tr><td class="align-center" style="height:29.8px;">**Icône**</td><td class="align-center" style="height:29.8px;">**Signification**</td><td class="align-center" style="height:29.8px;">**Code Unicode**</td></tr></thead><tbody><tr><td class="align-center">🛠️ </td><td>Outils / montage</td><td>1f6e0</td></tr><tr><td class="align-center">🔧</td><td>Clé à molette</td><td>1f527</td></tr><tr><td class="align-center">🔨</td><td>Marteau</td><td>1f528</td></tr><tr><td class="align-center">🪛</td><td>Tournevis</td><td>1fa9b</td></tr></tbody></table>

#### <span style="color:rgb(110,115,50);">🎓 Apprentissage &amp; Partage :</span>

<table id="bkmrk-ic%C3%B4ne-signification--5" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col></colgroup><thead><tr><td class="align-center" style="height:29.8px;">**Icône**</td><td class="align-center" style="height:29.8px;">**Signification**</td><td class="align-center" style="height:29.8px;">**Code Unicode**</td></tr></thead><tbody><tr><td class="align-center">🎓</td><td>Apprentissage / formation</td><td>1f393</td></tr><tr><td class="align-center">🧑‍🏫</td><td>Animateur / médiateur</td><td>1f9d1 ou 200d ou 1f3eb</td></tr><tr><td class="align-center">📚</td><td>Savoirs / wiki / doc</td><td>1f4da</td></tr><tr><td class="align-center">📝</td><td>Note / documentation</td><td>1f4dd</td></tr></tbody></table>

#### <span style="color:rgb(110,115,50);">🎉 Final &amp; Événement :</span>

<table id="bkmrk-ic%C3%B4ne-signification--6" style="border-collapse:collapse;width:100%;height:119.2px;"><colgroup><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col><col style="width:33.3731%;"></col></colgroup><thead><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">**Icône**</td><td class="align-center" style="height:29.8px;">**Signification**</td><td class="align-center" style="height:29.8px;">**Code Unicode**</td></tr></thead><tbody><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🎉</td><td style="height:29.8px;">Fête / restitution</td><td style="height:29.8px;">1f389</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🏆</td><td style="height:29.8px;">Diplôme / récompense</td><td style="height:29.8px;">1f3c6</td></tr><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">🤝</td><td style="height:29.8px;">Partenariat / inclusion</td><td style="height:29.8px;">1f91d</td></tr></tbody></table>

Vous pouvez retrouver l'ensemble des émojis en symbole ici : [https://www.unicode.org/emoji/charts/full-emoji-list.html](https://www.unicode.org/emoji/charts/full-emoji-list.html)

⚠️ Le chargement peut être très très long !

### <span style="color:rgb(115,100,100);">Code</span>

```python
console.print(f"\U0001f4c5      [bold cyan]Date et heure :[/bold cyan] {date_heure}")
console.print(f"\U0001f321      [bold red]Température :[/bold red] {round(temperature, 1)}°C")
console.print(f"\U0001f4a7      [bold blue]Humidité :[/bold blue] {round(humidity, 1)}%")
console.print(f"\U00002746      [bold magenta]Point de rosée :[/bold magenta] {round(point_de_rosee, 1)}°C")
console.print(f"\U0001f525      [bold yellow]Humidex :[/bold yellow] {round(humidex, 1)}")
console.print(f"\U0001f32c      [bold green]Pression atmosphérique :[/bold green] {round(pression, 2)} hPa")
```

[![affichage_emojis.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/mxUlflR1ZTOHjx9M-affichage-emojis.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/mxUlflR1ZTOHjx9M-affichage-emojis.png)

# Partie XI

## **<span style="color:rgb(55,190,140);">Comprendre la breadboard (plaque de prototypage rapide)</span>**

### <span style="color:rgb(115,100,100);">Qu'est-ce qu'une breadboard ?</span>

Une **breadboard**, ou **plaque d’essai sans soudure**, est un outil essentiel pour tester et assembler des circuits électroniques **sans avoir à souder**. Elle permet de réaliser des montages rapidement, de les modifier facilement et de bien comprendre comment circulent les signaux et l’énergie dans un circuit.

Elle est composée :

- de **colonnes verticales** de chaque côté (repérées souvent par + et -), on y connecte généralement le + (**3,3V ou 5V**) d’un côté et le **GND** (**masse**) de l’autre,
- de **lignes horizontales** centrales, connectées 5 par 5, où l’on insère les **composants et capteurs** (DHT22, BMP280, etc.).

### <span style="color:rgb(115,100,100);">Pourquoi utiliser une breadboard pour notre station de jardin ?</span>

<span style="color:rgb(115,100,100);">[![breadboard-400-punti.jpeg](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/zX91AzYtKdFfR5FC-breadboard-400-punti.jpeg)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/zX91AzYtKdFfR5FC-breadboard-400-punti.jpeg)</span>

- 🔌 **D’alimenter plusieurs capteurs en parallèle** avec une seule broche 3.3V ou GND : chaque ligne de la breadboard partage l’électricité à tous les composants branchés dessus.
- 🌐 **De partager le protocole I2C (SDA/SCL)** : le bus I2C permet à plusieurs capteurs de communiquer sur les mêmes broches, tant qu’ils ont une adresse différente (comme le BMP280 sur 0x76 ou 0x77).
- 🛠️ **De prototyper, tester et modifier facilement** votre montage sans soudure.
- 🚀 **De déporter les capteurs** pour qu’ils soient mieux placés dans le jardin, tout en gardant le Raspberry Pi au sec dans un boîtier.

Plutôt que de brancher chaque composant directement sur les broches du Raspberry Pi (ce qui deviendrait vite ingérable), **on déporte les connexions sur la breadboard**, qui agit comme **un répartiteur**.

### <span style="color:rgb(115,100,100);">Montage des capteurs sur la breadboard</span>

La breadboard jour le rôle de hub de connexion entre le Raspberry Pi et les sondes :

[![breadboard.jpg](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/hRviPCDSbMlsZShF-breadboard.jpg)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/hRviPCDSbMlsZShF-breadboard.jpg)

- **3.3V** (alimentation) du Raspberry Pi est envoyé sur **la ligne d’alimentation rouge** de la breadboard.
- **GND** (masse) du Raspberry Pi est envoyé sur **la ligne noire (ou bleue)** de la breadboard.
- Les **GPIO** (pour la DHT22) et **SDA/SCL** (pour la BMP280) sont envoyés sur des **rangées centrales de la breadboard**.

#### <span style="color:rgb(110,115,50);">DHT22 :</span>

- VCC (alimentation) → ligne rouge (3.3V)
- GND → ligne bleue (GND)
- Data → GPIO4 (broche 7 du Pi) via une rangée de la breadboard

#### <span style="color:rgb(110,115,50);"> BMP280 (I2C) :</span>

- VCC → ligne rouge (3.3V)
- GND → ligne bleue
- SDA → broche 3 du Pi (GPIO2) via une rangée de la breadboard
- SCL → broche 5 du Pi (GPIO3) via une rangée de la breadboard

Tous ces fils passent par la **breadboard**, qui facilite leur organisation et future extension (ajout de relais, convertisseur, etc.).

# Partie XII

## <span style="color:rgb(55,190,140);">**À quoi sert un convertisseur analogique-numérique (ADC) ?**</span>

**Le Raspberry Pi**, contrairement à un Arduino, **n’a pas d’entrée analogique**. Il ne peut lire que **des valeurs numériques** (0 ou 1, HIGH ou LOW). Or, certains capteurs, comme **la sonde d’humidité du sol Gravity SEN0193**, peuvent envoyer **une tension variable** représentant un niveau d’humidité, et non un simple “sec” ou “humide”.

👉 C’est là qu’intervient **le convertisseur analogique-numérique (ADC)**, comme le **MCP3008**.

### <span style="color:rgb(115,100,100);">Pourquoi préférer une lecture analogique pour la sonde SEN0193 ?</span>

<span style="color:rgb(115,100,100);">[![mcp3008-ip-convertisseur-analogique-numerique.jpg](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/vn2tk31LLmQER9KQ-mcp3008-ip-convertisseur-analogique-numerique.jpg)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/vn2tk31LLmQER9KQ-mcp3008-ip-convertisseur-analogique-numerique.jpg)</span>La sonde Gravity SEN0193, selon le modèle peut fonctionner en deux modes :

- **Numérique** : elle renvoie simplement 1 (sol sec) ou 0 (sol humide). C’est tout.
- **Analogique** : elle renvoie une **valeur continue** entre 0 et 3.3V, que le MCP3008 convertit en une valeur entre **0 et 1023**.

#### <span style="color:rgb(110,115,50);">Avantages de l’analogique :</span>

- Permet un **suivi plus fin et progressif de l’humidité** (utile pour déclencher l’arrosage à un seuil précis).
- Donne la **possibilité de créer des graphiques, des seuils personnalisés**, des alertes…
- Ouvre la voie à une gestion **intelligente et économe de l’eau**.

### <span style="color:rgb(115,100,100);">Présentation du MCP3008</span>

Le **MCP3008** est une petite puce qui permet de convertir jusqu’à **8 signaux analogiques** en valeurs numériques que le Raspberry Pi peut comprendre, via le protocole SPI.

Il se connecte au Raspberry Pi **via la breadboard** pour simplifier les branchements.

### <span style="color:rgb(115,100,100);">Branchement du MCP3008</span>

<span style="color:rgb(115,100,100);">[![mcp3008.jpeg](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/sBpIKUcjyfLKBSYb-mcp3008.jpeg)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/sBpIKUcjyfLKBSYb-mcp3008.jpeg)</span>La **puce MCP3008** doit être placée sur le breadboard **à cheval sur la “tranchée centrale”**, de manière à ce que **chaque broche soit positionnée sur une rangée indépendante**, ce qui permet un câblage propre et organisé.

<table id="bkmrk-mcp3008-fonction-bra" style="border-collapse:collapse;width:100%;height:298px;"><colgroup><col style="width:33.3333%;"></col><col style="width:33.3333%;"></col><col style="width:33.3333%;"></col></colgroup><thead><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">MCP3008</td><td class="align-center" style="height:29.8px;">Fonction</td><td class="align-center" style="height:29.8px;">Branchement sur la breadboard</td></tr></thead><tbody><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">16 (VDD)</td><td style="height:29.8px;text-align:justify;">Alimentation</td><td style="height:29.8px;text-align:justify;">Jumper vers la ligne rouge (3.3V)</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">15 (VREF)</td><td style="height:29.8px;text-align:justify;">Référence</td><td style="height:29.8px;text-align:justify;">Jumper vers la ligne rouge (3,3V)</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">14 (AGND)</td><td style="height:29.8px;text-align:justify;">Masse analogique</td><td style="height:29.8px;text-align:justify;">Jumper vers la ligne noire (GND)</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">13 (CLK)</td><td style="height:29.8px;text-align:justify;">Horloge SPI</td><td style="height:29.8px;text-align:justify;">GPIO11 du Raspberry Pi</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">12 (DOUT)</td><td style="height:29.8px;text-align:justify;">Données vers le Raspberry Pi</td><td style="height:29.8px;text-align:justify;">GPIO9 du Raspberry Pi</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">11 (DIN)</td><td style="height:29.8px;text-align:justify;">Données du Raspberry Pi</td><td style="height:29.8px;text-align:justify;">GPIO10 du Raspberry Pi</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">10 (CS)</td><td style="height:29.8px;text-align:justify;">Chip select</td><td style="height:29.8px;text-align:justify;">GPIO5 du Raspberry Pi</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">9 (DGND)</td><td style="height:29.8px;text-align:justify;">Masse numérique</td><td style="height:29.8px;text-align:justify;">Jumper vers la ligne noire (GND)</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">1 (CH0)</td><td style="height:29.8px;text-align:justify;">Canal analogique 0</td><td style="height:29.8px;text-align:justify;">Sortie A0 de la sonde</td></tr></tbody></table>

<table id="bkmrk-broche-nom-r%C3%B4le-%2F-ex" style="border-collapse:collapse;width:100%;height:382px;"><colgroup><col style="width:16.9249%;"></col><col style="width:18.5936%;"></col><col style="width:64.4815%;"></col></colgroup><thead><tr style="height:29.8px;"><td class="align-center" style="height:29.8px;">Broche</td><td class="align-center" style="height:29.8px;">Nom</td><td class="align-center" style="height:29.8px;">Rôle / Explication</td></tr></thead><tbody><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">16</td><td style="height:29.8px;text-align:justify;">VDD</td><td style="text-align:justify;height:29.8px;">Tension d’alimentation **du circuit numérique** (généralement **3,3V ou 5V**).</td></tr><tr style="height:46.6px;"><td style="height:46.6px;text-align:justify;">15</td><td style="height:46.6px;text-align:justify;">VREF</td><td style="text-align:justify;height:46.6px;">Tension de **référence** pour la conversion analogique. **On la relie à VDD**. La précision des mesures dépend de cette valeur.</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">14</td><td style="height:29.8px;text-align:justify;">AGND</td><td style="text-align:justify;height:29.8px;">**Masse (GND)** pour la partie **analogique** (capteurs). À relier au GND du circuit.</td></tr><tr style="height:46.6px;"><td style="height:46.6px;text-align:justify;">13</td><td style="height:46.6px;text-align:justify;">CLK</td><td style="text-align:justify;height:46.6px;">**Horloge SPI** : le Raspberry Pi envoie un signal ici pour cadencer les échanges de données.</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">12</td><td style="height:29.8px;text-align:justify;">DOUT</td><td style="text-align:justify;height:29.8px;">**Data OUT** : les **données numériques sortent** du MCP3008 vers le Raspberry Pi.</td></tr><tr style="height:46.6px;"><td style="height:46.6px;text-align:justify;">11</td><td style="height:46.6px;text-align:justify;">DIN</td><td style="text-align:justify;height:46.6px;">**Data IN** : le Raspberry Pi **envoie des commandes vers le MCP3008** (ex : “lis le canal 0”).</td></tr><tr style="height:46.6px;"><td style="height:46.6px;text-align:justify;">10</td><td style="height:46.6px;text-align:justify;">CS</td><td style="text-align:justify;height:46.6px;">**Chip Select** (ou CE = Chip Enable). Sert à dire “je parle maintenant à ce composant SPI”.</td></tr><tr style="height:29.8px;"><td style="height:29.8px;text-align:justify;">9</td><td style="height:29.8px;text-align:justify;">DGND</td><td style="text-align:justify;height:29.8px;">**Masse (GND)** pour la partie **numérique** (Raspberry Pi). À relier au GND.</td></tr><tr style="height:46.6px;"><td style="height:46.6px;text-align:justify;">1 à 8</td><td style="height:46.6px;text-align:justify;">CH0 à CH7</td><td style="text-align:justify;height:46.6px;">**Canaux analogiques d’entrée** (pour capteurs). Le MCP3008 peut lire **jusqu’à 8 capteurs analogiques**. CH0 est le plus utilisé.</td></tr></tbody></table>

- <div style="text-align:justify;">🟥 VDD + VREF = alimentent la puce et définissent la précision.</div>
- <div>🟦 AGND + DGND = masses nécessaires pour les parties analogiques et numériques.</div>
- <div>📡 CLK, DOUT, DIN, CS = communication SPI avec le Raspberry Pi.</div>
- <div style="text-align:justify;">🌱 CH0 à CH7 = brancher ici les capteurs analogiques, comme l’humidité du sol SEN0193.</div>

### <span style="color:rgb(115,100,100);">Le protocole SPI</span>

C’est un **protocole de communication** utilisé pour faire dialoguer un microcontrôleur (comme le Raspberry Pi) avec des composants externes

C’est un **bus rapide**, synchrone (horloge partagée), **plein-duplex** (on peut envoyer et recevoir en même temps).

Le protocole SPI utilise 4 fils :

<table id="bkmrk-nom-du-fil-fonction-" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:17.0479%;"></col><col style="width:49.579%;"></col><col style="width:33.3731%;"></col></colgroup><thead><tr><td class="col0 leftalign align-center">Nom du fil</td><td class="col1 leftalign align-center">Fonction</td><td class="col2 align-center">Correspondance sur le Raspberry Pi</td></tr></thead><tbody><tr><td style="text-align:justify;">`MOSI`</td><td style="text-align:justify;">Master Out Slave In : le Pi envoie les données vers le capteur</td><td style="text-align:justify;">GPIO10 (Pin 19)</td></tr><tr><td style="text-align:justify;">`MISO`</td><td style="text-align:justify;">Master In Slave Out : le Pi lit les données du capteur</td><td style="text-align:justify;">GPIO9 (Pin 21)</td></tr><tr><td style="text-align:justify;">`SCLK` ou `CLK`</td><td style="text-align:justify;">Clock : le signal d’horloge synchronise les échanges</td><td style="text-align:justify;">GPIO11 (Pin 23)</td></tr><tr><td style="text-align:justify;">`CS` ou `CE`</td><td style="text-align:justify;">Chip Select : active le capteur concerné</td><td style="text-align:justify;">Par exemple GPIO5 (Pin 29)</td></tr></tbody></table>

### <span style="color:rgb(115,100,100);">Activer le protocole SPI</span>

```bash
sudo raspi-config
```

<span style="color:rgb(115,100,100);">[![interface-option.png](https://doc.arles-linux.org//uploads/images/gallery/2025-08/scaled-1680-/GNHBJyYtVCdf4gfT-interface-option.png)](https://doc.arles-linux.org//uploads/images/gallery/2025-08/GNHBJyYtVCdf4gfT-interface-option.png)</span>

 Aller dans l'interface d'administration du Raspberry Pi :

- <div>Aller dans Interfaces</div>
- <div>Activer SPI</div>
- <div>Redémarrer</div>

### <span style="color:rgb(115,100,100);">Installation de la bibliothèque</span>

 Pour pouvoir installer la bibliothèque, commençons pas entrer dans notre environnement virtuel :

```bash
source meteo/bin/activate
```

 Installation de la bibliothèque :

```bash
pip3 install Adafruit_CircuitPython_MCP3xxx
```

# Partie XIII

## **<span style="color:rgb(55,190,140);">Présentation de la sonde d’humidité du sol Gravity SEN0193</span>**

### <span style="color:rgb(115,100,100);">Description</span>

La **Gravity SEN0193** est une **sonde capacitive d’humidité du sol** développée par DFRobot. Elle mesure **l’humidité du sol** de façon **capacitive**, ce qui est **plus fiable** et **plus durable** que les anciennes sondes résistives (qui s’oxydent avec le temps).

### <span style="color:rgb(115,100,100);">Avantages</span>


<div class="level2" id="bkmrk-%F0%9F%8C%BF-capacitive-%3A-ne-s%E2%80%99">- <div class="li" style="text-align:justify;">🌿 Capacitive : ne s’oxyde pas, dure plus longtemps que les modèles bas de gamme.</div>
- <div class="li">📐 Compacte : facile à insérer dans un pot ou un bac.</div>
- <div class="li" style="text-align:justify;">🔌 Compatible Raspberry Pi via convertisseur ADC.</div>

</div>### <span style="color:rgb(115,100,100);">Branchement de la sonde</span>

 Voici le schéma logique de câblage pour la sonde sur le MCP3008 déjà alimenté et installé :

<table id="bkmrk-broche-de-la-sonde-%C3%80" style="border-collapse:collapse;width:100%;"><colgroup><col style="width:50.0596%;"></col><col style="width:50.0596%;"></col></colgroup><thead><tr><td class="col0 align-center">Broche de la sonde</td><td class="col1 leftalign align-center">À connecter sur…</td></tr></thead><tbody><tr><td>`VCC`</td><td>ligne 3,3V (pin 1 du Raspberry Pi) via breadboard</td></tr><tr><td>`GND`</td><td>ligne GND (pin 6 du Raspberry Pi) via breadboard</td></tr><tr><td>`A0` (sortie)</td><td>une des entrées **CH0 à CH7** du MCP3008 (ex: **CH0**)</td></tr></tbody></table>

### <span style="color:rgb(115,100,100);">Test de la sonde en mode interactif</span>

```python
#Importation des bibliothèques nécessaires
import busio # Pour initialiser et utiliser le bus SPI (communication avec le MCP3008)
import digitalio  # Pour gérer les entrées/sorties numériques (notamment la broche CS du SPI)
import board  # Pour accéder aux broches physiques du Raspberry Pi via des noms symboliques

# Importation de la bibliothèque du convertisseur analogique/numérique MCP3008
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

#Initialisation du bus SPI matériel (horloge, entrée et sortie de données)
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)

#Définir la broche utilisée pour sélectionner le MCP3008 (CS = Chip Select)
# Ici on utilise la broche physique D5 (GPIO5), mais on peut en choisir une autre
cs = digitalio.DigitalInOut(board.D5)

#Création d’une instance du MCP3008 relié via SPI
mcp = MCP.MCP3008(spi, cs)

#Définir une entrée analogique sur le canal CH0 (où est branchée la sonde d’humidité Gravity)
capteur_1 = AnalogIn(mcp, MCP.P0)  # Capteur 1 branché sur CH0
# capteur_2 = AnalogIn(mcp, MCP.P1)  # Capteur 2 (à décommenter si besoin)
# capteur_3 = AnalogIn(mcp, MCP.P2)  # Capteur 3...

#Affichage des valeurs
capteur_1.value
capteur_1.voltage
```

# Partie XIV

## **<span style="color:rgb(55,190,140);">Ajout de la sonde d’humidité du sol aux scripts</span>**

### <span style="color:rgb(115,100,100);">Modification du script station.py</span>

```python
# ==========
# Bibliothèques pour les cpteurs DHT22 (température, humidité) et BMP280 (pression atmosphérique)
# ==========
import adafruit_dht
import adafruit_bmp280

# ==========
# Bibliothèques pour le convertisseur analogique-numérique MCP3008
# qui permet de lire la valeur de la sonde d'humidité du sol (analogique - numérique)
# ==========
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

# ==========
# Bibliothèque pour gérer la broche CS (chip select) utilisée avec le protocole SPI
# ==========
import digitalio

# ==========
# Bibliothèque pour gérer les bus de communication (I2C pour BMP280, SPI pour MCP3008)
# ==========
import busio

# ==========
# Bibliothèque qui simplifie l'accès auc broches GPIO (par exemple board.D4)
# ==========
import board

# ==========
# Bibliothèques pour affichage en couleur et mise en page dans le terminal
# ==========
from rich.console import Console
from rich.text import Text

# ==========
# # Bibliothèques Python standards
# ==========
import time # Gérer les pauses entre deux lectures
import math # Calculs mathématiques (point de rosée, humidex)
from datetime import datetime # Obtenir la date et l'heure actuelles

# ==========
# Déclaration des capteurs :
# ==========

# Capteur DHT22 branché sur la broche D4 (température et humidité de l'air)
dhtDevice = adafruit_dht.DHT22(board.D4)

# Capteur BMP280 branché en I2C (pression atmosphérique)
i2c = busio.I2C(board.SCL, board.SDA)
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76)

# Convertisseur MCP3008 branché en SPI (permet de lire la sonde Gravity analogique)
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
cs = digitalio.DigitalInOut(board.D5)
mcp = MCP.MCP3008(spi, cs)

# ==========
# Fonctions de calcul météo
# ==========

def calculer_point_de_rosee(temperature, humidity):
    # Calcule le point de rosée à partir de la température et de l'humidité relative.
    # Le point de rosée indique à quelle température l'air devient saturé en humidité (condensation)
    alpha = 17.27
    beta = 237.7
    gamma = (alpha * temperature) / (beta + temperature) + math.log(humidity / 100.0)
    point_de_rosee = (beta * gamma) / (alpha - gamma)
    return point_de_rosee

def calculer_humidex(temperature, point_de_rosee):
    # Calcule l'indice Humidex à partir de la température et du point de rosée.
    # L'humidex est un indicateur de confort thermique développé au Canada
    # Il combine la température et le point de rosée pour extimer la sensation de chaleur ressentie par le corps humain :
    # Un humidex de 30 indique une chaleur lourde
    # Au-delà de 40, la chaleur devient dangereuse pour la santé
    humidex = temperature + (5/9) * (6.11 * math.exp(5417.7530 * ((1/273.16) - (1/273.15 + point_de_rosee))) - 10)
    return humidex

def convertir_tension_en_pourcentage(voltage):
# Fonction permettant de convertir une tension mesurée par la sonde d'humidité du sol (0V - 3.3V) en un pourcentage d'humidité du sol (0% - 100%)
    valeur_normalisee = voltage / 3.3
    # 1. Normaliser la valeur : rapport entre la tension mesurée et la tension max (3.3V), donne un résultat entre 0 (0V) et 1 (3,3V)
    valeur_inversee = 1 - valeur_normalisee
    # 2. Inverser l'échelle car la sonde donne 0V = sol humide, 3.3V = sol sec, l'inversion donnera donc un résultat entre 1 (trés humide, 0V) et 0 (trés sec, 3.3V)
    pourcentage = valeur_inversee * 100
    # 3. Conversion en pourcentage
    pourcentage_final = max(0, min(100, pourcentage))
    # 4. Sécuriser pour rester dans l'interval [0, 100], utile en cas de bruit électrique ou si la tension dépasse légèrement les bornes
    return pourcentage_final

# Déclaration de l'objet console pour gérer les couleurs et mise en page
console = Console()

# ==========
# Boucle principale
# ==========
# Lecture des capteurs toutes les 20 secondes
# Calcul des indicateurs (point de rosée, humidex, humidité du sol)
# Affichage avec couleurs et icônes
while True:
    humidity = dhtDevice.humidity
    temperature = dhtDevice.temperature
    capteur_humidite_1 = AnalogIn(mcp, MCP.P0)
    voltage = capteur_humidite_1.voltage
    pression = bmp280.pressure
    if humidity is not None and temperature is not None:
        now = datetime.now()
        date_heure = now.strftime("%d-%m-%Y %H:%M:%S")
        point_de_rosee = calculer_point_de_rosee(temperature, humidity)
        humidex = calculer_humidex(temperature, point_de_rosee)
        humidity_pct = convertir_tension_en_pourcentage(voltage)
        console.print(f"\U0001f4c5	[bold cyan]Date et heure :[/bold cyan] {date_heure}")
        console.print(f"\U0001f321	[bold red]Température :[/bold red] {round(temperature, 1)}°C")
        console.print(f"\U0001f4a7 	[bold blue]Humidité :[/bold blue] {round(humidity, 1)}%")
        console.print(f"\U00002746 	[bold magenta]Point de rosée :[/bold magenta] {round(point_de_rosee, 1)}°C")
        console.print(f"\U0001f525	[bold yellow]Humidex :[/bold yellow] {round(humidex, 1)}")
        console.print(f"\U0001f32c	[bold green]Pression atmosphérique :[/bold green] {round(pression, 2)} hPa")
        console.print(f"\U0001f331	[bold navajo_white1]Capteur d'humidité du sol 1 (tension) :[/bold navajo_white1] {round(voltage, 1)} V")
        console.print(f"\U0001f9ea	[bold gold3]Capteur d'humidité du sol 1 (résultat exprimé en pourcentage) :[/bold gold3] {round(humidity_pct, 1)}%")
        print("----")
    else:
        print("Échec de la lecture du capteur")

    #Pause de 20 secondes
    time.sleep(20)
```

#### <span style="color:rgb(110,115,50);">Conversion de la tension en pourcentage d’humidité du sol</span>

La sonde d’humidité du sol **Gravity** est un capteur **analogique** :  
elle envoie une **tension électrique comprise entre 0 et 3,3V**, proportionnelle à l’humidité détectée.

- **0 V → sol très humide (100% d’humidité)**
- **3,3 V → sol très sec (0% d’humidité)**

Or, une tension exprimée en volts n’est pas très parlante pour un utilisateur.

La fonction Python **convertir\_tension\_en\_pourcentage(voltage)** traduit directement cette tension en un pourcentage d’humidité du sol, beaucoup plus intuitif à lire.

La bibliothèque du MCP3008 permet aussi de récupérer une **valeur brute** (par ex. entre 0 et 65535).  
Mais cette valeur dépend directement du convertisseur, pas du capteur, et elle est **moins parlante** pour l’utilisateur.  
C’est pourquoi nous ne retenons que :

- la **tension en volts** (utile pour les tests techniques),
- et le **pourcentage d’humidité**, compréhensible par tout le monde.

### <span style="color:rgb(115,100,100);">Modification du script capteur.py</span>

```python
# =============================
# Bibliothèques pour les cpteurs DHT22 (température, humidité) et BMP280 (pression atmosphérique)
# =============================
import adafruit_dht
import adafruit_bmp280

# ==========
# Bibliothèques pour le convertisseur analogique-numérique MCP3008
# qui permet de lire la valeur de la sonde d'humidité du sol (analogique - numérique)
# ==========
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

# ==========
# Bibliothèque pour gérer la broche CS (chip select) utilisée avec le protocole SPI
# ==========
import digitalio

# ==========
# Bibliothèque pour gérer les bus de communication (I2C pour BMP280, SPI pour MCP3008)
# =========
import busio

# ==========
# Bibliothèque qui simplifie l'accès auc broches GPIO (par exemple board.D4)
# ==========
import board

# ==========
# Bibliothèques Python standards
# ==========
import math
from datetime import datetime

# ==========
# Déclaration des capteurs :
# ==========

# Capteur BMP280 branché en I2C (pression atmosphérique)
i2c = busio.I2C(board.SCL, board.SDA)
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c, address=0x76)

# =============================
# Fonctions de lecture capteurs
# =============================

# Fonction de lecture de la sonde DHT22
def lire_donnees_capteur():
    try:
        dhtDevice = adafruit_dht.DHT22(board.D4)
        # Initialisation de la sonde DHT22
        humidity = dhtDevice.humidity
        # Récupération de l'humidité
        temperature = dhtDevice.temperature
        # Récupération de la temperature
        dhtDevice.exit()
        if humidity is not None and temperature is not None:
            return round(humidity, 1), round(temperature, 1)
        else:
            return None, None

    except RuntimeError as error:
        print("Erreur de lecture :", error)
        return None, None

    except Exception as error:
        dhtDevice.exit()
        # Libérer le GPIO même en cas de crash
        raise error
        # Le programme crashe, car c'est une erreur critique

# Fonction de lecture de la sonde BMP280
def lire_pression():
    try:
        pression = bmp280.pressure
        return round(pression, 1)
    except Exception as error:
        print("Erreur de lecture BMP280 :", error)
        return None

# ==========
# Fonctions de calcul météo
# ==========
def calculer_point_de_rosee(temperature, humidity):
    # Calcule le point de rosée à partir de la température et de l'humidité relative.
    # Le point de rosée indique à quelle température l'air devient saturé en humidité (condensation)
    alpha = 17.27
    beta = 237.7
    gamma = (alpha * temperature) / (beta + temperature) + math.log(humidity / 100)
    point_de_rosee = (beta * gamma) / (alpha - gamma)
    return round(point_de_rosee, 1)

def calculer_humidex(temperature, point_de_rosee):
    # Calcule l'indice Humidex à partir de la température et du point de rosée.
    # L'humidex est un indicateur de confort thermique développé au Canada
    # Il combine la température et le point de rosée pour extimer la sensation de chaleur ressentie par le corps humain :
    # Un humidex de 30 indique une chaleur lourde
    # Au-delà de 40, la chaleur devient dangereuse pour la santé
    humidex = temperature + (5/9) * (6.11 * math.exp(5417.7530 * ((1/273.16) - (1/273.15 + point_de_rosee))) - 10)
    return round(humidex, 1)

# =============================
# Humidité du sol (via MCP3008)
# =============================
def lire_humidite_sol():
    """
    Lecture de la sonde d’humidité du sol (canal CH0 du MCP3008).
    Retourne (tension en Volts, pourcentage estimé d’humidité).
    """

    try:
        # Convertisseur MCP3008 branché en SPI (permet de lire la sonde Gravity analogique)
        spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
        cs = digitalio.DigitalInOut(board.D5)
        mcp = MCP.MCP3008(spi, cs)

        capteur_humidite = AnalogIn(mcp, MCP.P0)
        # Capteur branché sur CH0

        voltage = capteur_humidite.voltage

        # Conversion en pourcentage (0V = 100% humidité, 3.3V = 0%)
        valeur_normalisee = voltage / 3.3
        valeur_inversee = 1 - valeur_normalisee
        pourcentage = valeur_inversee * 100
        pourcentage_final = max(0, min(100, round(pourcentage, 1)))

        return round(voltage, 2), pourcentage_final

    except Exception as e:
        print("Erreur lecture MCP3008 :", e)
        return None, None

    finally:
        # Libération propre de la broche CS
        cs.deinit()

# =============================
# Récupération de la date et de l'heure
# =============================
def recuperer_date_heure():
```

#### <span style="color:rgb(110,115,50);">Pourquoi utiliser</span> `try/except/finally` <span style="color:rgb(110,115,50);">?</span>

- **`try`** : on exécute le code normal (lecture de la sonde).
- **`except`** : si une erreur survient (mauvais branchement, problème de communication SPI, etc.), le programme n’arrête pas brutalement : on affiche un message d’erreur et on continue.
- **`finally`** : cette partie est toujours exécutée, qu’il y ait une erreur ou non.  
    Ici, elle sert à **libérer la broche CS (`cs.deinit()`)**, exactement comme on libère la sonde **DHT22 avec `.exit()`**.

### <span style="color:rgb(115,100,100);">Modification du script app.py</span>

```python
# =============================
# Importations
# =============================
from flask import Flask, render_template_string
from capteur import (
    lire_donnees_capteur,
    calculer_point_de_rosee,
    calculer_humidex,
    recuperer_date_heure,
    lire_pression,
    lire_humidite_sol
)

# =============================
# Application Flask
# =============================
app = Flask(__name__)

@app.route('/')
def index():
    humidity, temperature = lire_donnees_capteur()
    if humidity is not None and temperature is not None:
        point_de_rosee = calculer_point_de_rosee(temperature, humidity)
        humidex = calculer_humidex(temperature, point_de_rosee)
        date_heure = recuperer_date_heure()
        pression = lire_pression()
        voltage_sol, humidite_sol = lire_humidite_sol()

        html = f"""
        <h1>Données météo locales</h1>
        <ul>
            <li>Date et heure : {date_heure}</li>
            <li>Température : {temperature} °C</li>
            <li>Humidité : {humidity} %</li>
            <li>Point de rosée : {point_de_rosee} °C</li>
            <li>Humidex : {humidex}</li>
            <li>Pression atmosphérique : {pression} hPa</li>
        </ul>
        <h2>Humidité du sol de la sonde 1 :</h2>
        <ul>
            <li>Humidité du sol (tension) : {voltage_sol} V</li>
            <li>Humidité du sol (approx.) : {humidite_sol} %</li>
        </ul>
        """
    else:
        html = "<p>Erreur de lecture du capteur.</p>"

    return render_template_string(html)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
```

# Partie XV

## **<span style="color:rgb(55,190,140);">Serveur web Apache2 et Dokuwiki</span>**

### <span style="color:rgb(115,100,100);">Qu’est-ce qu’Apache ?</span>

**Apache HTTP Server** est l’un des serveurs web les plus utilisés au monde.  
Un serveur web est un logiciel qui **écoute les requêtes HTTP** (venant d’un navigateur comme Firefox ou Chromium) et qui **renvoie des pages web**.

En d’autres termes, Apache est l’intermédiaire qui permet à vos fichiers (pages HTML, scripts PHP, wiki, etc.) d’être consultés via une simple adresse dans un navigateur :  
👉 `http://raspberry.local/wiki`

### <span style="color:rgb(115,100,100);">Pourquoi Apache est important pour DokuWiki ?</span>

- **DokuWiki** est un moteur de wiki léger, qui fonctionne avec **PHP** mais **sans base de données**.
- Cela le rend particulièrement adapté à notre station autonome : pas besoin de MySQL ou MariaDB, juste du stockage en fichiers plats.
- Pour que PHP puisse fonctionner et afficher correctement les pages du wiki, il a besoin d’un serveur web comme **Apache**.

En résumé :

- Flask = pour la météo et les capteurs.
- Apache + PHP = pour héberger DokuWiki.

Les deux peuvent coexister sur le Raspberry Pi.

### <span style="color:rgb(115,100,100);">Installation d’Apache et PHP sur Raspberry Pi OS</span>

Exécutez ces commandes :

```bash
sudo apt update
sudo apt install apache2 php libapache2-mod-php
```

- **Apache2** → le serveur web.
- **PHP** → le langage qui fait tourner DokuWiki.
- **libapache2-mod-php** → module qui permet à Apache d’interpréter le code PHP.

### <span style="color:rgb(115,100,100);">Installation de DokuWiki</span>

<span style="color:rgb(115,100,100);">Téléchargez et installez la dernière version stable de DokuWiki :</span>

```bash
cd /var/www/html/
sudo wget https://download.dokuwiki.org/src/dokuwiki/dokuwiki-stable.tgz
sudo tar xvf dokuwiki-stable.tgz
sudo mv dokuwiki_nom_dossier wiki (Attention ici, le nom dépendra de la version de dokuwiki)
sudo chown -R www-data:www-data /var/www/html/wiki
sudo systemctl restart apache2
```

### <span style="color:rgb(115,100,100);">Accéder au wiki</span>

Une fois installé, ouvrez un navigateur connecté au Wi-Fi de la station et rendez-vous sur :

👉 `http://IP_RASPBERRY/wiki`

Vous aurez alors accès à l’interface d’installation de DokuWiki et pourrez commencer à créer la documentation du jardin.

✅ **Avantage majeur** : tout fonctionne **hors-ligne**.  
Les participant·es peuvent consulter et enrichir la documentation du jardin **même sans Internet**.