Files
Your Name a27af69f27 docs(nextpm): rewrite around Modbus RTU mode
The previous doc described only the proprietary 0x81 UART protocol, but
all current AirCarto firmwares (NebuleAir Pro 4G, ModuleAir light) talk
to the NextPM in Modbus RTU. Document the actual register mapping
(PM at 0x38/0x44 for 10s/60s averages, T/HR at 0x6B/0x6A, status at
0x13, 5 granulometric channels at 0x80-0x88), the LSW-MSW word order,
and both reading strategies (per-register and bulk read). Move the
proprietary 0x81 protocol to an annex; drop Python/C examples (to be
published in separate docs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 13:52:50 +02:00

175 lines
12 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.
# NextPM (Tera Sensor)
Capteur de particules **Tera Sensor NextPM** : mesure PM1, PM2.5, PM10 (µg/m³), 5 canaux de comptage granulométrique, et intègre un capteur de température et d'humidité interne. Communication **UART**.
Le capteur expose deux modes de communication mutuellement exclusifs :
- **Modbus RTU** (utilisé par défaut sur les intégrations AirCarto, dont le NebuleAir Pro 4G) — décrit dans le corps de cette doc.
- **Protocole propriétaire Tera Sensor** (préambule `0x81`) — résumé en annexe à la fin.
## Caractéristiques
| Paramètre | Valeur |
|-------------------------|-----------------------------------------------------|
| Fabricant / modèle | Tera Sensor — NextPM |
| Grandeurs | PM1, PM2.5, PM10 (µg/m³), 5 canaux #, T (°C), HR (%) |
| Plage PM | 0 1000 µg/m³ |
| Alimentation | 5 V, ~100 mA pic, ~35 mA en veille |
| Interface | UART 3V3 (logique CMOS 3.3 V tolérante 5 V TX) |
| Vitesse UART | 115200 baud, **8E1** en Modbus (8N1 en propriétaire) |
| Rafraîchissement | 10 s côté capteur |
| Datasheet | https://www.tera-sensor.com/ |
## Câblage
Connecteur JST-PH 4 broches du NextPM (vue côté capteur) :
| Pin capteur | Fil usuel | Fonction | MCU (exemple) |
|-------------|-----------|-----------------------|------------------|
| 1 | Rouge | VCC 5 V | 5V |
| 2 | Noir | GND | GND |
| 3 | Blanc | RX capteur (← TX MCU) | UART TX du MCU |
| 4 | Vert | TX capteur (→ RX MCU) | UART RX du MCU |
> Le NextPM est en 3V3 côté logique : si le MCU est en 3V3 (nRF9151, ESP32, Raspberry Pi…), connecter directement. Pour un MCU 5 V, prévoir un level shifter sur TX→RX.
## Protocole Modbus RTU
### Paramètres de la liaison
- 115200 baud, **8 bits, parité paire (EVEN), 1 stop**.
- Slave address par défaut : `0x01` (configurable côté capteur).
- Function code utilisé : `0x03` (Read Holding Registers).
- Checksum : **CRC-16 Modbus** (poly `0xA001`), placé en fin de trame, **little-endian** (LSB d'abord).
- Temps de traitement côté capteur : prévoir un délai de **~200 ms** entre l'envoi de la requête et la lecture de la réponse.
### Format de trame
**Requête** (8 octets pour une lecture de registres) :
```
+--------+--------+----------+----------+--------+
| ADDR | FUNC | REGADDR | QTY | CRC |
| 1B | 1B | 2B | 2B | 2B |
+--------+--------+----------+----------+--------+
big-endian big-endian little-endian
```
**Réponse** :
```
+--------+--------+--------+--------------------+--------+
| ADDR | FUNC | BCNT | DATA | CRC |
| 1B | 1B | 1B | 2 × QTY octets | 2B |
+--------+--------+--------+--------------------+--------+
```
`BCNT` = nombre d'octets de données = `2 × QTY`. Chaque registre fait 2 octets, big-endian.
En cas d'erreur, le capteur répond avec `FUNC | 0x80` suivi d'un code d'exception Modbus standard.
### Stratégies de lecture
Deux approches pratiquées sur les firmwares AirCarto, toutes deux valides :
- **Lecture par registre** (ex. ModuleAir) : une requête Modbus par valeur (PM1, PM2.5, PM10, T, HR, status). Plus simple à implémenter avec une lib Modbus standard (`ModbusMaster` Arduino, `pymodbus`…).
- **Lecture en bloc** (ex. NebuleAir Pro 4G) : une seule requête lit 85 registres à partir de `0x0038`, et le firmware extrait localement chaque champ. Évite les courses entre mesures et minimise les échanges UART quand on veut tout récupérer (PM + 5 canaux + T/HR) :
```
Requête bloc complet : 01 03 00 38 00 55 <CRC_lo> <CRC_hi>
```
### Mapping des registres
Le NextPM expose les concentrations PM avec **plusieurs fenêtres de moyennage glissant** (10 s, 60 s, et — selon firmware — 900 s), chacune occupant un bloc de 6 registres consécutifs (PM1 / PM2.5 / PM10 sur 2 registres uint32 chacun).
| Registre (déc) | Registre (hex) | Champ | Taille | Décodage | Unité |
|----------------|----------------|------------------------------------|------------|------------------------|------------|
| 19 | 0x0013 | Status capteur | 1 registre | `value & 0xFF` | bitfield |
| 5657 | 0x0038 | PM1 — moyenne 10 s | 2 registres (uint32) | `value / 1000` | µg/m³ |
| 5859 | 0x003A | PM2.5 — moyenne 10 s | 2 registres (uint32) | `value / 1000` | µg/m³ |
| 6061 | 0x003C | PM10 — moyenne 10 s | 2 registres (uint32) | `value / 1000` | µg/m³ |
| 6869 | 0x0044 | PM1 — moyenne 60 s | 2 registres (uint32) | `value / 1000` | µg/m³ |
| 7071 | 0x0046 | PM2.5 — moyenne 60 s | 2 registres (uint32) | `value / 1000` | µg/m³ |
| 7273 | 0x0048 | PM10 — moyenne 60 s | 2 registres (uint32) | `value / 1000` | µg/m³ |
| 106 | 0x006A | Humidité relative | 1 registre | `value / 100` | %HR |
| 107 | 0x006B | Température interne | 1 registre | `value / 100` (signed) | °C |
| 128129 | 0x0080 | Canal 1 — particules 0.20.5 µm | 2 registres (uint32) | comptage brut | # |
| 130131 | 0x0082 | Canal 2 — particules 0.51.0 µm | 2 registres (uint32) | comptage brut | # |
| 132133 | 0x0084 | Canal 3 — particules 1.02.5 µm | 2 registres (uint32) | comptage brut | # |
| 134135 | 0x0086 | Canal 4 — particules 2.55.0 µm | 2 registres (uint32) | comptage brut | # |
| 136137 | 0x0088 | Canal 5 — particules 5.010 µm | 2 registres (uint32) | comptage brut | # |
> Une fenêtre de moyennage 900 s (15 min) existe sur le mode propriétaire (commande `0x13`) ; le bloc lu en `0x38..0x8C` la couvre probablement entre `0x4A` et `0x55`, mais l'adresse exacte n'a pas été confirmée par lecture directe — vérifier datasheet avant usage.
**Encodage des uint32 sur 2 registres :** chaque registre Modbus est big-endian sur ses 2 octets, mais l'ordre des deux registres est **LSW d'abord, MSW ensuite** (« little-endian word order »). Reconstruction :
```
value = (MSW << 16) | LSW
```
avec `LSW` = registre N et `MSW` = registre N+1.
### Bitfield du registre status (`0x0013`)
Octet de poids faible :
| Bit | Signification |
|-----|-------------------------------------------------|
| 0 | Fan default (1 = vitesse dégradée / anormale) |
| 1 | Memory error |
| 2 | Sensor laser default |
| 3 | T/RH sensor default |
| 4 | Sleep mode |
| 57 | Réservés |
Un status `0x00` indique un fonctionnement nominal. Loguer la valeur à chaque cycle de mesure et la remonter au backend si elle est non nulle.
> Les adresses et tailles de registres ci-dessus correspondent aux versions firmware NextPM utilisées par AirCarto en 2026. Toujours recroiser avec la datasheet Tera Sensor la plus récente avant d'intégrer une nouvelle révision hardware/firmware.
## Mise en œuvre recommandée
1. Choisir la fenêtre de moyennage selon le cas d'usage :
- **10 s** (`0x0038`) — réactivité, débogage, exposition courte. Cas typique : NebuleAir Pro 4G.
- **60 s** (`0x0044`) — stabilité, transmission réseau périodique. Cas typique : ModuleAir light.
- **900 s** — historisation longue durée (à confirmer datasheet).
2. Choisir la stratégie de lecture (cf. plus haut) : registre par registre si on n'a besoin que des PM, lecture en bloc si on veut aussi les 5 canaux et T/HR.
3. Cycle de lecture aligné sur le rafraîchissement interne du capteur (10 s) — il est inutile d'interroger plus vite.
4. Ignorer les **2 premières minutes** après mise sous tension : le ventilateur monte en régime et les concentrations sont sous-estimées.
5. Toujours valider le CRC-16 (la plupart des libs Modbus le font) avant d'utiliser la trame ; en cas d'échec, écarter la mesure et incrémenter un compteur d'erreurs.
6. Si toutes les valeurs lues sont nulles ou si la requête timeout, écrire en base une ligne « erreur » (ex. PM = 0, status = `0xFF`) plutôt que sauter le cycle, pour pouvoir distinguer une absence de capteur d'une vraie mesure à 0.
## Pièges connus
- **Parité Modbus** : le NextPM exige du 8E1, alors que la majorité des UART sont configurés en 8N1 par défaut. Une confusion sur la parité produit un silence radio total côté capteur.
- **Word order des uint32** : `(MSW << 16) | LSW` (LSW d'abord). Inverser donne des valeurs énormes parfois plausibles, à surveiller en intégration.
- **Masse commune** : si le checksum/CRC échoue de manière intermittente, vérifier la masse (GND) entre MCU et NextPM — flottement de GND observé sur câbles longs.
- **Démarrage à froid** : prévoir 30 s de stabilisation après mise sous tension ou sortie de veille avant la première lecture exploitée.
## Annexe : protocole propriétaire `0x81`
Le NextPM supporte également un protocole UART propriétaire Tera Sensor, **non utilisé par les firmwares AirCarto actuels** mais documenté dans la datasheet officielle :
- UART 115200 **8N1** (pas de parité).
- Toutes les trames commencent par le préambule `0x81`.
- Checksum simple sur 1 octet : `(256 somme(octets précédents)) mod 256`.
- Commandes (extrait) : `0x11` / `0x12` / `0x13` lecture concentrations 10/60/900 s, `0x14` lecture T/HR, `0x15` sleep, `0x16` wake, `0x17` set fan speed, `0x21` set clock, `0x41` firmware version.
- L'octet `STATE` est inclus inline dans la réponse, juste après le code de commande.
Pour une intégration complète dans ce mode, se référer à la datasheet Tera Sensor — ce mode et le mode Modbus RTU s'excluent mutuellement.
## Références
- Datasheet Tera Sensor : https://www.tera-sensor.com/ (demander la dernière révision PDF).
- Code de référence AirCarto en mode Modbus :
- Lecture en bloc (Python, Raspberry Pi) : `nebuleair_pro_4g/NPM/get_data_modbus_v3.py` sur [gitea.aircarto.fr](https://gitea.aircarto.fr/PaulVua).
- Lecture par registre (C++/Arduino, ESP32) : `moduleair_light/src/sensors.cpp` sur [github.com/aircarto](https://github.com/aircarto).
## Historique
| Date | Révision | Changement |
|------------|----------|------------------------------------------------------------------------|
| 2026-04-23 | v1 | Création de la doc (mode propriétaire `0x81`). |
| 2026-05-04 | v2 | Réécriture autour du mode Modbus RTU effectivement utilisé en prod ; ajout du mapping de registres et des 5 canaux ; mode propriétaire déplacé en annexe ; exemples de code retirés (à publier dans des docs séparées). |
| 2026-05-04 | v2.1 | Ajout de la fenêtre de moyennage 60 s (`0x0044`/`0x0046`/`0x0048`) constatée sur ModuleAir light ; explicite les deux stratégies de lecture (registre par registre vs bloc complet). |