Files
aircarto-protocols/formats/json-payload.md
Your Name 33c4472350 docs: merge parsers/ into formats/ and drop misleading JSON wrapper
Parsers and formats are tightly linked (a parser produces a format) and
the split made cross-links heavy for a single parser file. Also removed
the confusing "Enveloppe JSON" block in udp-miotiq.md that mixed the
raw webhook wrapper with what the backend actually consumes — the
decoded payload schema lives in json-payload.md and is now referenced
directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:02:10 +02:00

13 KiB
Raw Blame History

Format JSON canonique — mesures capteurs AirCarto (2026)

JSON envoyé par les capteurs AirCarto (ou par le webhook Miotiq en leur nom) au serveur central.

Endpoint

POST https://api.aircarto.com/receive_data?device_type=<modèle>
Content-Type: application/json

Le type de capteur est passé en query string. Modèles supportés :

device_type Description
NebuleAir Station fixe NebuleAir
NebuleAir_Pro Station fixe NebuleAir Pro (4G)
ModuleAir Module AirCarto
ModuleAir_Pro Module AirCarto Pro (4G)
MobileAir Capteur mobile

Règles générales

  • Tous les champs sont optionnels sauf device_id. Un capteur n'envoie que les champs qu'il mesure.
  • Valeur sentinelle -1 : donnée non disponible ou capteur non renseigné (ex. pas de GPS fix, pas de pression).
  • Valeur sentinelle 255 (0xFF) sur error_flags, npm_status, device_status uniquement : le firmware du capteur est antérieur à l'introduction du champ. À interpréter comme « non disponible », pas comme « toutes les erreurs actives ». Les nouveaux firmwares initialisent ces octets et envoient une valeur ≤ 254.
  • Champs <nom>_unit : optionnels, ajoutés par Miotiq quand la colonne units du descripteur est remplie et que l'export JSON est Y (voir udp-miotiq.md). Format : "<valeur> <unité>". Le backend peut les ignorer, la valeur canonique est toujours le champ sans suffixe.

Identification

Champ Type Description
device_id string Identifiant unique, représentation hexadécimale de 16 caractères (8 octets ASCII). Convertir hex → ASCII pour obtenir le numéro de série imprimé sur le boîtier. Ex. "4430353234313938""D0524198".
signal_quality int Qualité du signal réseau (dB, souvent RSSI négatif).
version int Version du protocole de communication (pas la version firmware — voir version_* plus bas).

Polluants (codes ISO LCSQA)

Le mapping complet vit dans formats/iso-pollutant-codes.md. Liste rappel :

Champ Grandeur Unité
ISO_68 PM1 µg/m³
ISO_39 PM2.5 µg/m³
ISO_24 PM10 µg/m³
ISO_54 Température °C
ISO_55 Humidité %
ISO_53 Pression hPa
ISO_03 NO₂ ppb
ISO_05 H₂S ppb
ISO_21 NH₃ ppb
ISO_04 CO ppb
ISO_08 O₃ ppb

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.

Bruit

Champ Unité Description
noise_cur_leq dB Niveau sonore équivalent continu (Leq) courant.
noise_cur_level dB Niveau sonore instantané courant.
max_noise dB Niveau sonore maximal sur la période.

Comptage particulaire NPM (Naneos Partector)

Champ Unité Description
npm_ch1 count Comptage canal 1.
npm_ch2 count Comptage canal 2.
npm_ch3 count Comptage canal 3.
npm_ch4 count Comptage canal 4.
npm_ch5 count Comptage canal 5.
npm_temp °C Température interne du module NPM.
npm_humidity % Humidité interne du module NPM.
npm_status int Statut NPM — bitfield, voir ci-dessous.

Alimentation

Champ Unité Description
battery_voltage V Tension batterie.
battery_current A Courant batterie.
solar_voltage V Tension panneau solaire.
solar_power W Puissance panneau solaire.
charger_status int Code de statut du chargeur MPPT.

Vent

Champ Unité Description
wind_speed m/s Vitesse du vent.
wind_direction degrés Direction du vent, 0360 (0 = Nord).

Diagnostic & firmware

error_flags — bitfield système (1 octet)

Erreurs matérielles détectées par le capteur. 255 (0xFF) = firmware ancien, champ non supporté.

Bit Masque Nom Signification
0 0x01 RTC_DISCONNECTED Module RTC DS3231 non détecté (I2C).
1 0x02 RTC_RESET RTC en date par défaut (année 2000).
2 0x04 BME280_ERROR Capteur BME280 non détecté ou erreur.
3 0x08 NPM_ERROR Capteur NextPM non détecté ou erreur.
4 0x10 ENVEA_ERROR Capteurs Envea non détectés ou erreur.
5 0x20 NOISE_ERROR Capteur bruit NSRT MK4 non détecté.
6 0x40 MPPT_ERROR Chargeur solaire MPPT non détecté.
7 0x80 WIND_ERROR / CO2_ERROR NebuleAir : vent non détecté. ModuleAir : CO₂ non détecté.

Exemple : error_flags = 5 → RTC déconnecté + BME280 en erreur.

npm_status — bitfield NextPM (1 octet)

Registre d'état interne du capteur NextPM. Copie du byte STATE de la trame UART NextPM (voir sensors/nextpm.md). 255 = firmware ancien.

Bit Masque Nom Signification
0 0x01 SLEEP_STATE Capteur en veille.
1 0x02 DEGRADED_STATE Erreur mineure, précision réduite.
2 0x04 NOT_READY Démarrage en cours (~15 s).
3 0x08 HEAT_ERROR Humidité > 60 % pendant > 10 min.
4 0x10 TRH_ERROR T/HR interne hors spécification.
5 0x20 FAN_ERROR Ventilateur hors plage.
6 0x40 MEMORY_ERROR Accès mémoire impossible.
7 0x80 LASER_ERROR Aucune particule > 240 s, erreur laser.

Exemple : npm_status = 40HEAT_ERROR + FAN_ERROR.

device_status — bitfield boîtier (1 octet)

État général du boîtier capteur. 255 = firmware ancien.

Bit Masque Nom Signification
0 0x01 SARA_REBOOTED Modem SARA a rebooté (hardware) au cycle précédent.
1 0x02 WIFI_CONNECTED Device connecté en WiFi (mode atelier).
2 0x04 HOTSPOT_ACTIVE Hotspot WiFi actif (mode configuration).
3 0x08 GPS_NO_FIX Pas de position GPS valide.
4 0x10 BATTERY_LOW Tension batterie sous seuil critique.
5 0x20 DISK_FULL Espace disque critique (< 5 %).
6 0x40 DB_ERROR Erreur d'accès à la base SQLite locale.
7 0x80 BOOT_RECENT Device redémarré récemment (uptime < 5 min).

Exemple : device_status = 145 (0x91) → modem reboot + batterie faible + boot récent.

Version firmware

Champ Type Description
version_major int Numéro majeur (X.y.z).
version_minor int Numéro mineur (x.Y.z).
version_patch int Numéro de patch (x.y.Z).

Reconstitution : f"{version_major}.{version_minor}.{version_patch}" → ex. "1.2.3".

Géolocalisation & contexte

Champ Type Unité Description
latitude number degrés Latitude GPS WGS84 (décimal).
longitude number degrés Longitude GPS WGS84 (décimal).
misc int Contexte de mesure, voir table ci-dessous.
misc Contexte
0 Aucun
1 Mesure en intérieur
2 Mesure en extérieur
3 Mesure en voiture
4 Mesure en piéton
5 Mesure en vélo
6 Mesure en transport en commun

Exemple complet (NebuleAir_Pro)

{
  "device_id": "4430353234313938",
  "signal_quality": -22,
  "signal_quality_unit": "-22 dB",
  "version": 1,
  "ISO_68": 0.8,  "ISO_68_unit": "0,8 ugm3",
  "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",
  "max_noise":       25.5, "max_noise_unit":       "25,5 dB",
  "npm_ch1": 255, "npm_ch1_unit": "255 nb",
  "npm_ch2": 255, "npm_ch2_unit": "255 nb",
  "npm_ch3": 255, "npm_ch3_unit": "255 nb",
  "npm_ch4": 255, "npm_ch4_unit": "255 nb",
  "npm_ch5": 255, "npm_ch5_unit": "255 nb",
  "battery_voltage": 25.5, "battery_voltage_unit": "25,5 V",
  "battery_current": 25.5, "battery_current_unit": "25,5 A",
  "solar_voltage":   25.5, "solar_voltage_unit":   "25,5 V",
  "solar_power":    255,   "solar_power_unit":     "255 W",
  "npm_temp":       25.5,  "npm_temp_unit":        "25,5 °C",
  "npm_humidity":   25.5,  "npm_humidity_unit":    "25,5 %",
  "wind_speed":     25.5,  "wind_speed_unit":      "25,5 m/s",
  "wind_direction": 255,   "wind_direction_unit":  "255 degrees",
  "charger_status": 255,   "charger_status_unit":  "255",
  "error_flags":   0,
  "npm_status":    0,
  "device_status": 0,
  "version_major": 1,
  "version_minor": 2,
  "version_patch": 3,
  "latitude":  43.2964,
  "longitude": 5.36978,
  "misc": 2
}

Notes d'intégration

Côté capteur / firmware

  • Envoyer uniquement les champs mesurés par ton modèle. Omettre les autres, ou les remplir à -1 si leur présence est structurellement attendue par un parser amont (ex. descripteur Miotiq à taille fixe).
  • Initialiser error_flags, npm_status, device_status à 0. Les laisser à 0xFF uniquement si tu ne sais pas renseigner (= firmware non migré), pour que le backend interprète bien « non disponible ».

Côté backend

  • Ignorer toute valeur -1 (ne pas la stocker comme une mesure).
  • Ignorer error_flags, npm_status, device_status si == 255 — c'est un firmware ancien, l'absence de diagnostic n'est pas une alarme.
  • Les _unit sont purement informatifs / de debug. La valeur métier est toujours le champ sans suffixe.
  • device_id est hex dans le JSON ; convertir en ASCII (bytes.fromhex(v).decode('ascii')) pour afficher le numéro de série lisible.

Historique

Date Révision Changement
2026-04-23 v1 Version initiale inventée (schéma imbriqué avec token/ts/measurements) — remplacée.
2026-04-23 v2 Format officiel AirCarto 2026 : schéma plat, bitfields détaillés, compat firmware ancien.