Files
nebuleair_pro_4g/CCS811/README.md
PaulVua 13c266d694 v1.11.0: CCS811 en daemon + fix filtrage + I2C 10kHz requis
Vérif terrain sur pro100 : à 100 kHz le CCS811 renvoie des valeurs corrompues
0x8000 (32768) par clock-stretching, et le modèle oneshot-reset-toutes-les-10s
ne donne que le 1er échantillon post-init (garbage). Refonte :

- CCS811/daemon.py: service long-running (Type=simple, Restart=always). Init 1x,
  boucle lecture/écriture 10s, filtre eCO2 dans [400,8192], re-init auto sur
  erreurs I2C répétées. Remplace write_data.py (supprimé).
- CCS811/get_data.py: lit la dernière ligne data_CCS811 au lieu du capteur
  (évite la collision I2C avec le daemon -> corruption observée).
- setup_services.sh: service daemon + self-heal suppression de l'ancien .timer;
  activation hors boucle timers.
- launcher.php: .timer -> .service (map statut + allowedServices x2).
- update_firmware.sh: redémarre le daemon à l'OTA.
- doc: README (archi daemon + I2C 10kHz confirmé requis), CLAUDE.md, changelog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 16:08:03 +02:00

102 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CCS811 — Capteur qualité d'air (eCO2 / TVOC)
Capteur de gaz **MOX** (oxyde métallique) AMS CCS811. Connecté en **I2C**.
## ⚠ À lire avant de câbler
Le CCS811 **n'est pas** un capteur CO2 NDIR comme le Senseair S88. C'est un capteur
de COV (composés organiques volatils) qui mesure :
- **TVOC** (Total Volatile Organic Compounds) — en **ppb**. C'est la mesure réellement
utile / fiable du capteur, et celle qui nous intéresse ici.
- **eCO2** (CO2 *équivalent*) — en **ppm**, plage 4008192. Valeur *calculée* à partir
du TVOC par un algorithme interne, ce **n'est pas** une mesure directe du CO2. Pour
un vrai CO2, utiliser le S88. On stocke quand même l'eCO2 (gratuit, vient de la même
lecture) mais ne pas le confondre avec une mesure NDIR.
## ⚠ Clock-stretching I2C sur Raspberry Pi
Le CCS811 utilise massivement le **clock-stretching** I2C. Le contrôleur I2C matériel
du Raspberry Pi (BSC) gère **mal** le clock-stretching (bug matériel documenté). Sans
mitigation, les lectures échouent typiquement en `OSError` / `Remote I/O error`.
**Mitigation** : ralentir le bus I2C dans `/boot/firmware/config.txt` :
```
dtparam=i2c_arm_baudrate=10000
```
(10 kHz au lieu de 100 kHz par défaut.) Reboot ensuite.
**Confirmé nécessaire sur le terrain** (nebuleair-pro100, juin 2026) : à 100 kHz le
CCS811 renvoie des valeurs corrompues 0x8000+ (32768) par intermittence et finit en
état d'erreur. À 10 kHz c'est stable. Ce réglage n'est pas géré par le repo (fichier
hors `/var/www`), il doit être posé à la main sur chaque capteur équipé d'un CCS811.
Vérifier la présence du capteur :
```bash
sudo i2cdetect -y 1 # doit montrer 5a (ou 5b selon la broche ADDR)
```
## Adresse I2C
- **0x5A** : ADDR à GND — défaut des breakouts **Adafruit**. Valeur par défaut du firmware.
- **0x5B** : ADDR à VDD — défaut des breakouts **SparkFun** / modules génériques.
Configurable dans `admin.html` (clé config `CCS811_address`, dropdown 0x5A / 0x5B).
## Câblage I2C
| CCS811 | Raspberry Pi |
|---|---|
| VCC / VIN | 3.3V |
| GND | GND |
| SDA | SDA (GPIO2) |
| SCL | SCL (GPIO3) |
| WAK / nWAKE | GND (réveil permanent ; sinon laisser le module gérer) |
| ADDR | GND → 0x5A, VDD → 0x5B |
⚠ La plupart des breakouts CCS811 sont en **3.3V** logique. Ne pas alimenter en 5V
sans level-shifter sauf si le module embarque son propre régulateur + shifter.
## Burn-in / conditionnement
- **Burn-in initial** : ~48 h de fonctionnement continu avant des valeurs stables (1ère mise en service).
- **Warm-up** à chaque démarrage : ~20 min pour des valeurs fiables. Au démarrage le
capteur renvoie souvent eCO2=400 ppm / TVOC=0 ppb (valeurs de repos).
## Implémentation NebuleAir
**Architecture : daemon, PAS un timer oneshot** (contrairement aux autres capteurs).
Le CCS811 doit être initialisé **une seule fois** puis lu en continu :
- chaque (ré)init fait un reset + app_start, et les premiers échantillons juste après
sont du garbage (eCO2 = 0, ou valeurs 0x8000+ = 32768 dues au clock-stretching) ;
- un cycle reset toutes les 10 s empêche l'algorithme de baseline de se construire.
Composants :
- `CCS811/daemon.py` — service long-running (`nebuleair-ccs811-data.service`,
`Type=simple`, `Restart=always`). Init une fois, puis boucle : toutes les 10 s,
lit un échantillon **valide** (eCO2 ∈ [400, 8192], le reste est jeté) et l'écrit
dans `data_CCS811 (timestamp, eCO2, TVOC)`. Re-init automatique du capteur après
plusieurs erreurs I2C consécutives.
- `CCS811/get_data.py` — bouton "Get Data" du web. **Ne lit PAS le capteur** (ça
entrerait en collision I2C avec le daemon et corromprait la sonde) : renvoie la
**dernière ligne** de `data_CCS811`. Affiche `{"eCO2","TVOC","timestamp"}` ou
`{"error": "..."}`.
Librairie Python : `adafruit-circuitpython-ccs811` (dans `requirements.txt`,
installée par `installation_part1.sh` ET par `update_firmware.sh`). La table est
créée par `sqlite/create_db.py` et self-healée par `daemon.py`
(CREATE TABLE IF NOT EXISTS) — garder les deux schémas synchro.
Activation : `admin.html` → case "Send VOC sensor data (CCS811)".
### Pistes d'amélioration (non implémentées)
Le CCS811 supporte une compensation température/humidité (`SET_ENV_DATA`). Comme le
boîtier embarque déjà un BME280, on pourrait lui pousser temp/hum périodiquement
pour améliorer la précision. Non fait pour garder le daemon simple.