# 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 ``` ### 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 | | 56–57 | 0x0038 | PM1 — moyenne 10 s | 2 registres (uint32) | `value / 1000` | µg/m³ | | 58–59 | 0x003A | PM2.5 — moyenne 10 s | 2 registres (uint32) | `value / 1000` | µg/m³ | | 60–61 | 0x003C | PM10 — moyenne 10 s | 2 registres (uint32) | `value / 1000` | µg/m³ | | 68–69 | 0x0044 | PM1 — moyenne 60 s | 2 registres (uint32) | `value / 1000` | µg/m³ | | 70–71 | 0x0046 | PM2.5 — moyenne 60 s | 2 registres (uint32) | `value / 1000` | µg/m³ | | 72–73 | 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 | | 128–129 | 0x0080 | Canal 1 — particules 0.2–0.5 µm | 2 registres (uint32) | comptage brut | # | | 130–131 | 0x0082 | Canal 2 — particules 0.5–1.0 µm | 2 registres (uint32) | comptage brut | # | | 132–133 | 0x0084 | Canal 3 — particules 1.0–2.5 µm | 2 registres (uint32) | comptage brut | # | | 134–135 | 0x0086 | Canal 4 — particules 2.5–5.0 µm | 2 registres (uint32) | comptage brut | # | | 136–137 | 0x0088 | Canal 5 — particules 5.0–10 µ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 | | 5–7 | 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). |