diff --git a/loop/SARA_send_data_v2.py b/loop/SARA_send_data_v2.py index 91881c3..0832a2e 100755 --- a/loop/SARA_send_data_v2.py +++ b/loop/SARA_send_data_v2.py @@ -151,6 +151,16 @@ payload_json = { aircarto_profile_id = 0 uSpot_profile_id = 1 +# Error flags constants (byte 66) +ERR_RTC_DISCONNECTED = 0x01 +ERR_RTC_RESET = 0x02 +ERR_BME280 = 0x04 +ERR_NPM = 0x08 +ERR_ENVEA = 0x10 +ERR_NOISE = 0x20 +ERR_MPPT = 0x40 +ERR_WIND = 0x80 + # database connection conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") cursor = conn.cursor() @@ -358,6 +368,18 @@ class SensorPayload: if direction is not None: self.payload[64:66] = struct.pack('>H', int(direction)) + def set_error_flags(self, flags): + """Set system error flags (byte 66)""" + self.payload[66] = flags & 0xFF + + def set_npm_status(self, status): + """Set NextPM status register (byte 67)""" + self.payload[67] = status & 0xFF + + def set_device_status(self, status): + """Set device status flags (byte 68)""" + self.payload[68] = status & 0xFF + def get_bytes(self): """Get the complete 100-byte payload""" return bytes(self.payload) @@ -1090,6 +1112,14 @@ try: ''' + # ---- Build error_flags (byte 66) ---- + error_flags = 0x00 + if rtc_status == "disconnected": + error_flags |= ERR_RTC_DISCONNECTED + if rtc_status == "reset": + error_flags |= ERR_RTC_RESET + payload.set_error_flags(error_flags) + if send_miotiq: print('

➡️SEND TO MIOTIQ

', end="") @@ -1125,9 +1155,20 @@ try: print(response_SARA_1) else: print("⛔There were issues with the modem CSD PSD reinitialize process") + print("🔄 PDP reset failed → escalating to hardware reboot") # Clignotement LED rouge en cas d'erreur led_thread = Thread(target=blink_led, args=(24, 5, 0.5)) led_thread.start() + #Send notification (WIFI) + send_error_notification(device_id, "UDP socket creation failed + PDP reset failed -> hardware reboot") + #Hardware Reboot + hardware_reboot_success = modem_hardware_reboot() + if hardware_reboot_success: + print("✅Modem successfully rebooted and reinitialized") + else: + print("⛔There were issues with the modem reboot/reinitialize process") + #end loop + sys.exit() #Retreive Socket ID socket_id = None diff --git a/loop/error_flags.md b/loop/error_flags.md index 42de8ba..40539ae 100644 --- a/loop/error_flags.md +++ b/loop/error_flags.md @@ -1,13 +1,14 @@ -# Error Flags — UDP Payload Miotiq (Bytes 66-67) +# Error Flags — UDP Payload Miotiq (Bytes 66-68) ## Principe -Les bytes 66 et 67 de la payload UDP (100 bytes) sont utilises comme registres d'erreurs. -Chaque bit represente un etat d'erreur independant. Plusieurs erreurs peuvent -etre signalees simultanement. +Les bytes 66, 67 et 68 de la payload UDP (100 bytes) sont utilises comme registres d'erreurs +et d'etat. Chaque bit represente un etat independant. Plusieurs flags peuvent +etre actifs simultanement. - **Byte 66** : erreurs systeme (RTC, capteurs) - **Byte 67** : status NextPM (registre interne du capteur) +- **Byte 68** : status device (etat general du boitier) ## Position dans la payload @@ -15,7 +16,8 @@ etre signalees simultanement. Bytes 0-65 : donnees capteurs (existant) Byte 66 : error_flags (erreurs systeme) Byte 67 : npm_status (status NextPM) -Bytes 68-99 : reserved (initialises a 0xFF) +Byte 68 : device_status (etat general du boitier) +Bytes 69-99 : reserved (initialises a 0xFF) ``` --- @@ -91,6 +93,35 @@ car les deux indiquent un capteur inactif. --- +## Byte 68 — Device Status (etat general du boitier) + +Flags d'etat du device, determines par le script d'envoi (`SARA_send_data_v2.py`). +Ces flags donnent du contexte sur l'etat general du boitier pour le diagnostic a distance. + +| Bit | Masque | Nom | Description | Source | +|-----|--------|----------------------|----------------------------------------------------------------|-------------------------------------| +| 0 | 0x01 | SARA_REBOOTED | Le modem a ete reboot hardware au cycle precedent | flag fichier ou SQLite | +| 1 | 0x02 | WIFI_CONNECTED | Le device est connecte en WiFi (atelier/maintenance) | nmcli device status | +| 2 | 0x04 | HOTSPOT_ACTIVE | Le hotspot WiFi est actif (configuration en cours) | nmcli device status | +| 3 | 0x08 | GPS_NO_FIX | Pas de position GPS valide | config_table latitude/longitude | +| 4 | 0x10 | BATTERY_LOW | Tension batterie sous seuil critique | data_MPPT → battery_voltage | +| 5 | 0x20 | DISK_FULL | Espace disque critique sur la Pi (< 5%) | os.statvfs ou shutil.disk_usage | +| 6 | 0x40 | DB_ERROR | Erreur d'acces a la base SQLite | try/except sur connexion SQLite | +| 7 | 0x80 | BOOT_RECENT | Le device a redemarre recemment (uptime < 5 min) | /proc/uptime | + +### Exemples de valeurs + +| Valeur dec | Hex | Signification | +|------------|------|--------------------------------------------------| +| 0 | 0x00 | Tout est normal | +| 1 | 0x01 | Modem reboot au cycle precedent | +| 2 | 0x02 | WiFi connecte (probablement en atelier) | +| 6 | 0x06 | WiFi + hotspot actifs (configuration en cours) | +| 128 | 0x80 | Boot recent (uptime < 5 min) | +| 145 | 0x91 | Modem reboot + batterie faible + boot recent | + +--- + ## Implementation ### Etape 1 : Lire le status NPM depuis le capteur @@ -112,6 +143,16 @@ ERR_NOISE = 0x20 ERR_MPPT = 0x40 ERR_WIND = 0x80 +# Constantes device_status (byte 68) +DEV_SARA_REBOOTED = 0x01 +DEV_WIFI_CONNECTED = 0x02 +DEV_HOTSPOT_ACTIVE = 0x04 +DEV_GPS_NO_FIX = 0x08 +DEV_BATTERY_LOW = 0x10 +DEV_DISK_FULL = 0x20 +DEV_DB_ERROR = 0x40 +DEV_BOOT_RECENT = 0x80 + # Construction byte 66 error_flags = 0x00 @@ -128,6 +169,27 @@ payload.set_error_flags(error_flags) # Construction byte 67 (lu depuis SQLite, ecrit par get_data_modbus_v3.py) npm_status = get_npm_status_from_db() # 0-255 payload.set_npm_status(npm_status) + +# Construction byte 68 +device_status = 0x00 + +if sara_was_rebooted(): # flag fichier persistant + device_status |= DEV_SARA_REBOOTED +if check_wifi_connected(): # nmcli device status + device_status |= DEV_WIFI_CONNECTED +if check_hotspot_active(): # nmcli device status + device_status |= DEV_HOTSPOT_ACTIVE +if latitude == 0.0 and longitude == 0.0: # config_table + device_status |= DEV_GPS_NO_FIX +if battery_voltage < 11.0: # data_MPPT seuil a ajuster + device_status |= DEV_BATTERY_LOW +if check_disk_usage() > 95: # shutil.disk_usage + device_status |= DEV_DISK_FULL +# DEV_DB_ERROR: set dans le try/except de la connexion SQLite +if get_uptime_seconds() < 300: # /proc/uptime + device_status |= DEV_BOOT_RECENT + +payload.set_device_status(device_status) ``` ### Etape 3 : Ajouter les methodes dans SensorPayload @@ -140,6 +202,10 @@ def set_error_flags(self, flags): def set_npm_status(self, status): """Set NextPM status register (byte 67)""" self.payload[67] = status & 0xFF + +def set_device_status(self, status): + """Set device status flags (byte 68)""" + self.payload[68] = status & 0xFF ``` --- @@ -180,7 +246,8 @@ def set_npm_status(self, status): 4|wind_direction|hex2dec|degrees|| 2|error_flags|hex2dec||| 2|npm_status|hex2dec||| -30|reserved|skip||| +2|device_status|hex2dec||| +28|reserved|skip||| ``` --- @@ -212,6 +279,18 @@ npm_fan_err = bool(npm_status & 0x20) npm_mem_err = bool(npm_status & 0x40) npm_laser_err = bool(npm_status & 0x80) +# Byte 68 — status device +device_status = int(parsed_device_status) + +sara_rebooted = bool(device_status & 0x01) +wifi_connected = bool(device_status & 0x02) +hotspot_active = bool(device_status & 0x04) +gps_no_fix = bool(device_status & 0x08) +battery_low = bool(device_status & 0x10) +disk_full = bool(device_status & 0x20) +db_error = bool(device_status & 0x40) +boot_recent = bool(device_status & 0x80) + # Alertes if rtc_disconnected: alert("RTC module deconnecte — verifier pile/cables I2C") @@ -219,6 +298,12 @@ if npm_fan_err: alert("NextPM: ventilateur hors plage — maintenance requise") if npm_laser_err: alert("NextPM: possible erreur laser — verifier le capteur") +if battery_low: + alert("Batterie faible — verifier alimentation solaire") +if disk_full: + alert("Espace disque critique — verifier logs/DB") +if sara_rebooted: + alert("Modem reboot hardware au cycle precedent — instabilite reseau") ``` ---