Files
aircarto-protocols/sensors/nextpm.md

184 lines
8.2 KiB
Markdown
Raw 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 en masse (µg/m³) et en nombre (#/cm³), intègre un capteur de température et d'humidité embarqué. Communication **UART**.
## Caractéristiques
| Paramètre | Valeur |
|------------------------|--------------------------------------------------|
| Fabricant / modèle | Tera Sensor — NextPM |
| Grandeurs | PM1, PM2.5, PM10 (µg/m³ et #/cm³), 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 par défaut | 115200 baud, 8N1 |
| 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…), connecter directement. Pour un MCU 5 V, prévoir un level shifter sur TX→RX.
## Protocole UART
- 115200 bauds, 8 bits, pas de parité, 1 stop.
- Toutes les trames commencent par **`0x81`** (préambule).
- Checksum = `(256 - somme(octets précédents)) mod 256`, placé en dernier octet.
- Délai de traitement côté capteur : 1530 ms typique ; prévoir timeout de lecture de 200 ms.
### Structure des trames
**Requête MCU → capteur** (sans données) :
```
+------+------+------+
| 0x81 | CMD | CS |
+------+------+------+
```
**Requête MCU → capteur** (avec données) :
```
+------+------+----------+------+
| 0x81 | CMD | DATA... | CS |
+------+------+----------+------+
```
**Réponse capteur → MCU** :
```
+------+------+-------+------------+------+
| 0x81 | CMD | STATE | DATA... | CS |
+------+------+-------+------------+------+
```
`STATE` (1 octet) est un bitfield d'état :
| 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 `STATE == 0x00` indique un fonctionnement nominal.
### Commandes principales
| Commande | Code | Taille requête | Taille réponse | Notes |
|-------------------------------|-------|----------------|----------------|----------------------------------------------|
| Read concentrations 10 s | 0x11 | 3 | 16 | Moyenne glissante 10 s |
| Read concentrations 60 s | 0x12 | 3 | 16 | Moyenne glissante 60 s (plus stable) |
| Read concentrations 900 s | 0x13 | 3 | 16 | Moyenne glissante 15 min |
| Read T/RH | 0x14 | 3 | 10 | Température et humidité |
| Sleep (fan off) | 0x15 | 3 | 4 | Passe en veille, ventilateur coupé |
| Wake / fan on | 0x16 | 3 | 4 | Sort de veille |
| Set fan speed | 0x17 | 4 | 4 | 1 octet supplémentaire (% vitesse) |
| Set clock | 0x21 | 9 | 4 | Horodatage interne |
| Read firmware version | 0x41 | 3 | variable | Retourne une chaîne ASCII |
> Les codes et tailles ci-dessus sont issus de l'intégration de référence. **Toujours recroiser avec la datasheet Tera Sensor la plus récente** avant d'implémenter une nouvelle version firmware — certains registres ont changé entre révisions hardware.
### Format de la réponse `0x11` / `0x12` / `0x13`
16 octets de DATA, big-endian :
| Offset | Taille | Champ | Unité | Décodage |
|--------|--------|-----------------------|---------------|-------------------------|
| 0 | 2 | PM1 number | #/cm³ | valeur brute |
| 2 | 2 | PM2.5 number | #/cm³ | valeur brute |
| 4 | 2 | PM10 number | #/cm³ | valeur brute |
| 6 | 2 | PM1 mass | µg/m³ × 10 | `raw / 10.0` |
| 8 | 2 | PM2.5 mass | µg/m³ × 10 | `raw / 10.0` |
| 10 | 2 | PM10 mass | µg/m³ × 10 | `raw / 10.0` |
### Format de la réponse `0x14`
| Offset | Taille | Champ | Unité | Décodage |
|--------|--------|---------------|--------------|---------------------------|
| 0 | 2 | Température | °C × 100 | `raw / 100.0` (signed) |
| 2 | 2 | Humidité | %HR × 100 | `raw / 100.0` |
## Exemple de code
### Construction de la requête (C)
```c
#include <stdint.h>
#include <stddef.h>
static uint8_t nextpm_checksum(const uint8_t *buf, size_t len) {
uint32_t sum = 0;
for (size_t i = 0; i < len; i++) sum += buf[i];
return (uint8_t)(256 - (sum & 0xFF));
}
// Envoie une commande sans données (0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x41)
size_t nextpm_build_cmd(uint8_t cmd, uint8_t *out) {
out[0] = 0x81;
out[1] = cmd;
out[2] = nextpm_checksum(out, 2);
return 3;
}
```
### Décodage de la réponse 0x11 / 0x12 / 0x13 (Python)
```python
import struct
def decode_pm(frame: bytes) -> dict:
if len(frame) != 16 or frame[0] != 0x81:
raise ValueError("trame NextPM invalide")
cmd, state = frame[1], frame[2]
data = frame[3:15]
cs_expected = (256 - sum(frame[:15])) & 0xFF
if cs_expected != frame[15]:
raise ValueError("checksum NextPM incorrect")
pm1_n, pm25_n, pm10_n, pm1_m, pm25_m, pm10_m = struct.unpack(">HHHHHH", data)
return {
"state": state,
"pm1_num": pm1_n,
"pm25_num": pm25_n,
"pm10_num": pm10_n,
"pm1": pm1_m / 10.0,
"pm25": pm25_m / 10.0,
"pm10": pm10_m / 10.0,
}
```
## Mise en œuvre recommandée
1. Au boot : `0x16` (wake) puis laisser 30 s de stabilisation du flux avant de lire.
2. Utiliser `0x12` (moyenne 60 s) pour l'envoi réseau standard — meilleur compromis bruit/latence.
3. Loguer `STATE` à chaque lecture ; remonter au backend si `STATE != 0`.
4. En cas de cycle veille/mesure (ex. applications sur batterie) : `0x15` (sleep), attendre la prochaine fenêtre, `0x16`, 30 s stabilisation, lire, renvoyer en sleep.
## Pièges connus
- Les **2 premières minutes** après le wake sont à ignorer (le ventilateur monte en régime, concentrations sous-estimées).
- Si le checksum est faux une fois sur deux : vérifier la masse commune (GND) entre MCU et NextPM — flottement du GND observé sur certains câbles longs.
- Le NextPM renvoie parfois un octet `0x00` avant le préambule si la ligne UART n'était pas propre au démarrage : implémenter une resynchronisation sur `0x81` côté parser MCU.
## Références
- Datasheet : https://www.tera-sensor.com/ (demander la dernière révision PDF)
- Code firmware de référence AirCarto : `nebuleair_pro_4g` et `esp32_NPM_only` sur [gitea.aircarto.fr](https://gitea.aircarto.fr/PaulVua).
## Historique
| Date | Révision | Changement |
|------------|----------|---------------------------------|
| 2026-04-23 | v1 | Création de la doc. |