docs(co2): ajouter ISO_17 (CO2, ppm) au protocole NebuleAir Pro 4G

- iso-pollutant-codes.md: code ISO_17 = CO2 (ppm), sentinelle 0xFFFF
- udp-miotiq.md: ISO_17 consomme les 2 octets reserved (offset 81),
  trame inchangee a 83 octets, retrocompat (0 = anciens fw)
- json-payload.md: tableau polluants, note sentinelle, exemple

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-06-01 17:44:25 +02:00
parent a27af69f27
commit ace447c933
3 changed files with 12 additions and 3 deletions

View File

@@ -12,6 +12,7 @@ Les codes vont théoriquement de `ISO_01` à `ISO_99`. Seuls ceux effectivement
| `ISO_04` | CO — monoxyde de carbone | ppb | | |
| `ISO_05` | H₂S — sulfure d'hydrogène | ppb | | |
| `ISO_08` | O₃ — ozone | ppb | | |
| `ISO_17` | CO₂ — dioxyde de carbone | ppm | | En **ppm** (et non ppb comme les gaz traces). `uint16`, sentinelle `0xFFFF` = non équipé. |
| `ISO_21` | NH₃ — ammoniac | ppb | | |
| `ISO_24` | PM10 | µg/m³ | `x/10` | Particules ≤ 10 µm |
| `ISO_39` | PM2.5 | µg/m³ | `x/10` | Particules fines ≤ 2.5 µm |

View File

@@ -65,6 +65,9 @@ Le mapping complet vit dans [`formats/iso-pollutant-codes.md`](iso-pollutant-cod
| `ISO_21` | NH₃ | ppb |
| `ISO_04` | CO | ppb |
| `ISO_08` | O₃ | ppb |
| `ISO_17` | CO₂ | ppm |
> **CO₂ (`ISO_17`)** : encodé en **ppm bruts** (`uint16`), pas en ppb comme les gaz traces. Comme ce champ vient d'un descripteur Miotiq `hex2dec` (non signé), il ne peut pas porter la sentinelle `-1` : un capteur sans CO₂ envoie `0xFFFF` (65535) — ou `0` sur les firmwares qui émettaient encore l'ancien bloc `reserved`. Le backend traite `ISO_17 ∈ {0, 65535}` comme « CO₂ non disponible ». Voir [`udp-miotiq.md`](udp-miotiq.md).
Les codes ISO vont théoriquement de `ISO_01` à `ISO_99`. Seuls les polluants effectivement mesurés par le capteur sont présents dans le JSON.
@@ -198,6 +201,7 @@ Enum extensible — de nouvelles valeurs peuvent être ajoutées (7, 8, …) san
"signal_quality_unit": "-22 dB",
"command": 0,
"ISO_68": 0.8, "ISO_68_unit": "0,8 ugm3",
"ISO_17": 437, "ISO_17_unit": "437 ppm",
"ISO_54": 25.5, "ISO_54_unit": "25.5 °C",
"noise_cur_leq": 25.5, "noise_cur_leq_unit": "25,5 dB",
"noise_cur_level": 25.5, "noise_cur_unit": "25,5 dB",
@@ -251,3 +255,4 @@ Enum extensible — de nouvelles valeurs peuvent être ajoutées (7, 8, …) san
| 2026-04-23 | v2 | **Format officiel AirCarto 2026** : schéma plat, bitfields détaillés, compat firmware ancien. |
| 2026-04-27 | v3 | Champ `version` (offset 9 du binaire, hardcodé `0x01` côté firmware) renommé `command` et réaffecté à un type de trame : `0x00` = données, `0x01` = ping test. Ajout de la section « Commande / type de trame ». Versioning protocole reste sur `version_major/minor/patch`. |
| 2026-04-27 | v4 | Rétrocompatibilité : ping test déplacé de `0x01` vers `0x02`. Les firmwares déployés émettent déjà `0x01` (héritage de l'ancien champ `version`) ; les compter comme pings aurait masqué toutes leurs mesures. `0x00` et `0x01` sont désormais tous deux des trames de données normales, `0x02` est le déclencheur explicite du ping. |
| 2026-06-01 | v5 | Ajout du CO₂ (`ISO_17`, ppm) au tableau des polluants et à l'exemple NebuleAir_Pro. Sentinelle « non disponible » = `0` ou `65535` (`0xFFFF`) car le champ vient d'un descripteur Miotiq `hex2dec` non signé (pas de `-1` possible). |

View File

@@ -27,7 +27,7 @@ Un descripteur est une suite de lignes, une par champ. **Format officiel Miotiq*
| `length` | Taille du champ en **caractères hexadécimaux** (2 chars = 1 octet). |
| `variable name` | Identifiant logique du champ. Les codes polluants suivent [ISO 7168 AirCarto](iso-pollutant-codes.md). |
| `base function` | Fonction de décodage appliquée aux octets bruts. Voir tableau ci-dessous. |
| `units` | Unité physique finale (`ugm3`, `degC`, `%`, `hPa`, `ppb`, `dB`, `V`, `A`, `W`, `m/s`, `degrees`, `count`). Vide pour les champs d'état / versions. |
| `units` | Unité physique finale (`ugm3`, `degC`, `%`, `hPa`, `ppb`, `ppm`, `dB`, `V`, `A`, `W`, `m/s`, `degrees`, `count`). Vide pour les champs d'état / versions. |
| `equation` | Expression de transformation appliquée à la valeur décodée, où `x` est la valeur. Ex. `x/10`, `x/100`, `(x-32)*5/9`. Vide = pas de transformation. |
| `export to JSON` | Contrôle la sortie JSON côté Miotiq (voir ci-dessous). Valeurs : `Y` (défaut), `W`, `N`. |
@@ -102,7 +102,7 @@ Descripteur de référence :
8|latitude|hex2dec|degrees|x/1000000-90|
8|longitude|hex2dec|degrees|x/1000000-180|
2|misc|hex2dec|||
4|reserved|skip|||
4|ISO_17|hex2dec|ppm||
```
**Encodage `latitude` / `longitude`**`hex2dec` est **non-signé** côté Miotiq. Pour transmettre des coordonnées négatives sans ambiguïté, le firmware encode avec un offset fixe :
@@ -116,6 +116,8 @@ Miotiq applique l'équation inverse (`x/1000000-90`, `x/1000000-180`) et exporte
Les firmwares antérieurs à cette extension envoyaient déjà 83 octets (bloc `reserved` = 11 zéros), qui décodent désormais comme `lat=-90, lon=-180, misc=0`. Sur ces firmwares `device_status = 0xFF` (= champ non supporté — cf. [`json-payload.md`](json-payload.md#error_flags--bitfield-système-1-octet)) : un backend prudent traite donc la combinaison `device_status == 0xFF && (lat, lon) == (-90, -180)` comme « coords non disponibles ».
**CO₂ (`ISO_17`, offset 81)** — ce champ réutilise les 2 octets jadis `reserved` : la trame reste à **83 octets** (aucune trame déployée n'est rejetée). Le CO₂ est encodé en **ppm bruts** (`uint16`, pas d'équation, ambiant ≈ 420 ppm). Comme `reserved` était émis à `0x0000`, les firmwares sans capteur CO₂ décodent `ISO_17 = 0` ; les firmwares CO₂ utilisent la sentinelle `0xFFFF` (65535) quand le capteur est absent ou en défaut. Le backend traite donc `ISO_17 ∈ {0, 0xFFFF}` comme « CO₂ non disponible ».
Layout octet par octet :
| Offset | Taille | Champ | Unité | Scale | Notes |
@@ -160,7 +162,7 @@ Layout octet par octet :
| 72 | 4 | `latitude` | degrés | /1e6 90 | WGS84, offset unsigned. Voir encodage ci-dessus. Ignoré si `device_status.GPS_NO_FIX`. |
| 76 | 4 | `longitude` | degrés | /1e6 180 | WGS84, offset unsigned. Idem. |
| 80 | 1 | `misc` | | | Contexte de mesure 06 (voir [`json-payload.md`](json-payload.md#géolocalisation--contexte)) |
| 81 | 2 | `reserved` | | | À ignorer (évolution future du descripteur) |
| 81 | 2 | `ISO_17` | ppm | | CO₂. `uint16` ppm (pas d'équation). `0xFFFF` = capteur CO₂ absent ; `0` (anciens fw, ex-`reserved`) = non mesuré. |
| **83** | total | | | | |
### MobileAir (17 octets — legacy, pré-descripteur)
@@ -221,3 +223,4 @@ Valeurs manquantes codées `-1` (sentinelle legacy). À **ne pas reproduire** po
| 2026-04-24 | v6 | Intégration effective de `latitude` (4B), `longitude` (4B), `misc` (1B) dans le descripteur NebuleAir Pro 4G. Encodage offset unsigned (`raw = (deg + 90|180) * 1e6`, équation `x/1000000-90|180`) pour contourner l'absence de signed sur `hex2dec` Miotiq. « No fix » géré par le bit `GPS_NO_FIX` de `device_status`. Reste 2B `reserved`. |
| 2026-04-27 | v7 | Octet 9 renommé `version``command`. Ce champ était hardcodé `0x01` côté firmware (jamais une vraie version de protocole). Réaffecté à un type de trame : `0x00` = données mesure, `0x01` = ping test. Permet au firmware de déclencher une trame de bout en bout (capteur → Miotiq → backend) sans envoyer de mesures réelles. Versioning protocole assuré par `version_major/minor/patch` (offsets 69-71). |
| 2026-04-27 | v8 | Rétrocompatibilité : ping test déplacé de `0x01` vers `0x02`. Les firmwares déployés émettent déjà `0x01` (héritage du champ `version` hardcodé) ; les traiter comme ping aurait fait passer toutes leurs trames pour des diagnostics. `0x00` et `0x01` restent donc des trames de mesure normales, `0x02` devient le déclencheur explicite du ping. |
| 2026-06-01 | v9 | Ajout du CO₂ (`ISO_17`, ppm, `uint16`) sur NebuleAir Pro 4G, en consommant les 2 octets `reserved` (offset 81). Trame inchangée à 83 octets — aucune trame déployée rejetée. Plus de bloc `reserved`. Sentinelle `0xFFFF` = capteur absent ; `0` (ex-`reserved` des anciens fw) = non mesuré. `ISO_17` ajouté à [`iso-pollutant-codes.md`](iso-pollutant-codes.md). |