diff --git a/README.md b/README.md index ff4b27a..6a33ef3 100755 --- a/README.md +++ b/README.md @@ -181,6 +181,103 @@ And set the base URL for Sara R4 communication: ``` +## UDP Payload Miotiq — Structure 100 bytes + +| Bytes | Taille | Nom | Format | Description | +|-------|--------|-----|--------|-------------| +| 0-7 | 8 | device_id | ASCII | Identifiant unique du capteur | +| 8 | 1 | signal_quality | uint8 | Qualite signal modem (AT+CSQ) | +| 9 | 1 | protocol_version | uint8 | Version protocole (0x01) | +| 10-11 | 2 | pm1 | uint16 BE | PM1.0 en ug/m3 (x10) | +| 12-13 | 2 | pm25 | uint16 BE | PM2.5 en ug/m3 (x10) | +| 14-15 | 2 | pm10 | uint16 BE | PM10 en ug/m3 (x10) | +| 16-17 | 2 | temperature | int16 BE | Temperature en C (x100, signe) | +| 18-19 | 2 | humidity | uint16 BE | Humidite en % (x100) | +| 20-21 | 2 | pressure | uint16 BE | Pression en hPa | +| 22-23 | 2 | noise_cur_leq | uint16 BE | Bruit LEQ en dB(A) (x10) | +| 24-25 | 2 | noise_cur_level | uint16 BE | Bruit instantane en dB(A) (x10) | +| 26-27 | 2 | noise_max | uint16 BE | Bruit max en dB(A) (x10) | +| 28-29 | 2 | envea_no2 | uint16 BE | NO2 en ppb | +| 30-31 | 2 | envea_h2s | uint16 BE | H2S en ppb | +| 32-33 | 2 | envea_nh3 | uint16 BE | NH3 en ppb | +| 34-35 | 2 | envea_co | uint16 BE | CO en ppb | +| 36-37 | 2 | envea_o3 | uint16 BE | O3 en ppb | +| 38-39 | 2 | npm_ch1 | uint16 BE | NPM canal 1 (5-channel) | +| 40-41 | 2 | npm_ch2 | uint16 BE | NPM canal 2 (5-channel) | +| 42-43 | 2 | npm_ch3 | uint16 BE | NPM canal 3 (5-channel) | +| 44-45 | 2 | npm_ch4 | uint16 BE | NPM canal 4 (5-channel) | +| 46-47 | 2 | npm_ch5 | uint16 BE | NPM canal 5 (5-channel) | +| 48-49 | 2 | mppt_temperature | int16 BE | Temperature MPPT en C (x10, signe) | +| 50-51 | 2 | mppt_humidity | uint16 BE | Humidite MPPT en % (x10) | +| 52-53 | 2 | battery_voltage | uint16 BE | Tension batterie en V (x100) | +| 54-55 | 2 | battery_current | int16 BE | Courant batterie en A (x100, signe) | +| 56-57 | 2 | solar_voltage | uint16 BE | Tension solaire en V (x100) | +| 58-59 | 2 | solar_power | uint16 BE | Puissance solaire en W | +| 60-61 | 2 | charger_status | uint16 BE | Status chargeur MPPT | +| 62-63 | 2 | wind_speed | uint16 BE | Vitesse vent en m/s (x10) | +| 64-65 | 2 | wind_direction | uint16 BE | Direction vent en degres | +| 66 | 1 | error_flags | uint8 | Erreurs systeme (voir detail) | +| 67 | 1 | npm_status | uint8 | Registre status NextPM | +| 68 | 1 | device_status | uint8 | Etat general du boitier | +| 69 | 1 | version_major | uint8 | Version firmware major | +| 70 | 1 | version_minor | uint8 | Version firmware minor | +| 71 | 1 | version_patch | uint8 | Version firmware patch | +| 72-99 | 28 | reserved | — | Reserve (initialise a 0xFF) | + +### Consommation data (UDP Miotiq uniquement) + +Taille par paquet : 100 bytes payload + 8 bytes UDP header + 20 bytes IP header = **128 bytes** + +| | Toutes les 60s | Toutes les 10s | +|---|---|---| +| Paquets/jour | 1 440 | 8 640 | +| Par jour | ~180 KB | ~1.08 MB | +| Par mois | ~5.3 MB | ~32.4 MB | +| Par an | ~63.6 MB | ~388.8 MB | + +> Note : ces chiffres ne comptent que l'UDP vers Miotiq. Les envois HTTP (AirCarto) et HTTPS (uSpot) consomment des donnees supplementaires. + +### Byte 66 — error_flags + +| Bit | Masque | Description | +|-----|--------|-------------| +| 0 | 0x01 | RTC deconnecte | +| 1 | 0x02 | RTC reset (annee 2000) | +| 2 | 0x04 | BME280 erreur | +| 3 | 0x08 | NPM erreur | +| 4 | 0x10 | Envea erreur | +| 5 | 0x20 | Bruit erreur | +| 6 | 0x40 | MPPT erreur | +| 7 | 0x80 | Vent erreur | + +### Byte 67 — npm_status + +| Bit | Masque | Description | +|-----|--------|-------------| +| 0 | 0x01 | Sleep mode | +| 1 | 0x02 | Degraded mode | +| 2 | 0x04 | Not ready | +| 3 | 0x08 | Heater error | +| 4 | 0x10 | THP sensor error | +| 5 | 0x20 | Fan error | +| 6 | 0x40 | Memory error | +| 7 | 0x80 | Laser error | + +### Byte 68 — device_status + +| Bit | Masque | Description | +|-----|--------|-------------| +| 0 | 0x01 | Modem reboot au cycle precedent | +| 1 | 0x02 | WiFi connecte | +| 2 | 0x04 | Hotspot actif | +| 3 | 0x08 | Pas de fix GPS | +| 4 | 0x10 | Batterie faible | +| 5 | 0x20 | Disque plein | +| 6 | 0x40 | Erreur base SQLite | +| 7 | 0x80 | Boot recent (uptime < 5 min) | + +--- + # Notes ## Wifi Hotspot (AP) diff --git a/VERSION b/VERSION index bc80560..26ca594 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.0 +1.5.1 diff --git a/changelog.json b/changelog.json index 841b67d..0e3e75d 100644 --- a/changelog.json +++ b/changelog.json @@ -1,5 +1,21 @@ { "versions": [ + { + "version": "1.5.1", + "date": "2026-03-18", + "changes": { + "features": [ + "Payload UDP Miotiq: bytes 69-71 firmware version (major.minor.patch)", + "README: documentation complete de la structure des 100 bytes UDP" + ], + "improvements": [], + "fixes": [], + "compatibility": [ + "Necessite mise a jour du parser Miotiq pour decoder les bytes 69-71 (firmware version)" + ] + }, + "notes": "Le capteur envoie maintenant sa version firmware dans chaque trame UDP. Cote serveur, bytes 69/70/71 = major/minor/patch. Documentation payload complete ajoutee au README." + }, { "version": "1.5.0", "date": "2026-03-18", diff --git a/loop/SARA_send_data_v2.py b/loop/SARA_send_data_v2.py index 976413b..d0d7704 100755 --- a/loop/SARA_send_data_v2.py +++ b/loop/SARA_send_data_v2.py @@ -386,6 +386,16 @@ class SensorPayload: """Set device status flags (byte 68)""" self.payload[68] = status & 0xFF + def set_firmware_version(self, version_str): + """Set firmware version bytes 69-71 (major.minor.patch)""" + try: + parts = version_str.strip().split('.') + self.payload[69] = int(parts[0]) & 0xFF + self.payload[70] = int(parts[1]) & 0xFF + self.payload[71] = int(parts[2]) & 0xFF + except (IndexError, ValueError): + pass # leave as 0xFF if VERSION file is malformed + def get_bytes(self): """Get the complete 100-byte payload""" return bytes(self.payload) @@ -1126,6 +1136,13 @@ try: error_flags |= ERR_RTC_RESET payload.set_error_flags(error_flags) + # ---- Firmware version (bytes 69-71) ---- + try: + with open("/var/www/nebuleair_pro_4g/VERSION", "r") as f: + payload.set_firmware_version(f.read()) + except FileNotFoundError: + pass + if send_miotiq: print('

➡️SEND TO MIOTIQ

', end="")