Files
aircarto-protocols/sensors/nextpm.md

8.2 KiB
Raw Blame History

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)

#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)

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

Historique

Date Révision Changement
2026-04-23 v1 Création de la doc.