From dad953acdc130f5f177dff7b835d58c3d47e264a Mon Sep 17 00:00:00 2001 From: PaulVua Date: Tue, 2 Jun 2026 16:55:54 +0200 Subject: [PATCH] v1.12.0: transmission CO2 S88 dans le payload UDP Miotiq (byte 81) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Débloque le WIP S88->Miotiq. Spec serveur reçue: byte 81-82 = CO2 (ISO_17, uint16 ppm). Source = Senseair S88 (vrai CO2 NDIR). - SensorPayload.set_co2() -> bytes 81-82 (uint16, clamp 0..65535) - lecture data_S88 (derniere ligne) si config S88 active, puis set_co2 - defaut 0xFFFF = capteur absent - canal UDP Miotiq uniquement (CSV/JSON non touches) - error_flags.md: byte-map a jour (81-82 = CO2) CCS811 (TVOC/eCO2) NON transmis: pas encore de champ dans la spec Miotiq. Co-Authored-By: Claude Opus 4.8 (1M context) --- VERSION | 2 +- changelog.json | 15 +++++++++++++++ loop/SARA_send_data_v2.py | 26 +++++++++++++++++++++++--- loop/error_flags.md | 3 ++- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 1cac385..0eed1a2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.0 +1.12.0 diff --git a/changelog.json b/changelog.json index 612d85f..e658158 100644 --- a/changelog.json +++ b/changelog.json @@ -1,5 +1,20 @@ { "versions": [ + { + "version": "1.12.0", + "date": "2026-06-02", + "changes": { + "features": [ + "Transmission du CO2 S88 dans le payload UDP Miotiq (débloque le WIP). Nouvelle méthode SensorPayload.set_co2() -> bytes 81-82 (uint16 ppm, champ ISO_17 de la spec Miotiq). SARA_send_data_v2.py lit data_S88 (dernière ligne) si config S88 active et empaquette le CO2. Défaut 0xFFFF = capteur absent (octets initialisés à 0xFF). Canal UDP Miotiq uniquement (pas CSV/JSON pour l'instant). error_flags.md: cartographie d'octets mise à jour (81-82 = CO2)." + ], + "improvements": [], + "fixes": [], + "compatibility": [ + "Source du champ CO2 (ISO_17) = Senseair S88 (vrai CO2 NDIR). Le CCS811 n'alimente PAS ce champ (son eCO2 est dérivé, pas un vrai CO2). Le CCS811 (TVOC + eCO2) n'a pas encore de champ dans la spec serveur Miotiq -> transmission CCS811 toujours en attente d'extension de la spec côté serveur." + ] + }, + "notes": "Spec Miotiq reçue: payload 83 octets, byte 81-82 = CO2 ISO_17 uint16 ppm. Packing vérifié (793 -> 0x0319). À faire ensuite pour le CCS811: demander à Miotiq d'ajouter un champ TVOC (code ISO COV) + eCO2." + }, { "version": "1.11.0", "date": "2026-06-02", diff --git a/loop/SARA_send_data_v2.py b/loop/SARA_send_data_v2.py index 2fa562c..06d5d47 100755 --- a/loop/SARA_send_data_v2.py +++ b/loop/SARA_send_data_v2.py @@ -243,9 +243,10 @@ send_uSpot = config.get('send_uSpot', False) #envoi sur M npm_5channel = config.get('npm_5channel', False) #5 canaux du NPM envea_cairsens= config.get('envea', False) wind_meter= config.get('windMeter', False) -bme_280_config = config.get('BME280', False) +bme_280_config = config.get('BME280', False) mppt_charger = config.get('MPPT', False) NOISE_sensor = config.get('NOISE', False) +s88_sensor = config.get('S88', False) #update device id in the payload json payload_json["nebuleairid"] = device_id @@ -373,7 +374,14 @@ class SensorPayload: self.payload[62:64] = struct.pack('>H', int(speed * 10)) if direction is not None: self.payload[64:66] = struct.pack('>H', int(direction)) - + + def set_co2(self, co2_ppm): + """Set CO2 (bytes 81-82, uint16 ppm) — ISO_17 in the Miotiq spec. + Source: Senseair S88 (true NDIR CO2). Default stays 0xFFFF (constructor + fills 0xFF) = sensor absent. NOT for CCS811 eCO2 (a derived value).""" + if co2_ppm is not None: + self.payload[81:83] = struct.pack('>H', max(0, min(int(co2_ppm), 65535))) + def set_error_flags(self, flags): """Set system error flags (byte 66)""" self.payload[66] = flags & 0xFF @@ -1073,7 +1081,19 @@ try: payload.set_noise( cur_leq=cur_LEQ, # current LEQ (dBA) cur_level=cur_level # current level (dBA) - ) + ) + + # S88 CO2 sensor (Senseair S88, NDIR) -> byte 81-82 (ISO_17, uint16 ppm) + if s88_sensor: + print("➡️Getting S88 CO2 value") + cursor.execute("SELECT * FROM data_S88 ORDER BY rowid DESC LIMIT 1") + last_row = cursor.fetchone() + if last_row and last_row[1] is not None: + co2_ppm = last_row[1] + print(f"CO2 (S88): {co2_ppm} ppm") + payload.set_co2(co2_ppm) + else: + print("No S88 data available in the database.") #print("Verify SARA connection (AT)") diff --git a/loop/error_flags.md b/loop/error_flags.md index 7197577..753ba3b 100644 --- a/loop/error_flags.md +++ b/loop/error_flags.md @@ -23,7 +23,8 @@ Bytes 69-71 : firmware version (major.minor.patch) Bytes 72-75 : latitude (uint32, x/1000000-90) Bytes 76-79 : longitude (uint32, x/1000000-180) Byte 80 : misc (contexte de mesure) -Bytes 81-99 : reserved (initialises a 0xFF) +Bytes 81-82 : CO2 (ISO_17, uint16 ppm — Senseair S88, NDIR ; 0xFFFF = absent, 0 = non mesure) +Bytes 83-99 : reserved (initialises a 0xFF) ``` ---