# Error Flags — UDP Payload Miotiq (Bytes 66-68)

## Principe

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

```
Byte  9     : command (0x00 = data normal, 0x01 = legacy/ancien protocol_version, 0x02 = ping test)
Bytes 0-8   : device_id (8 bytes) + signal_quality (1 byte)
Bytes 10-65 : donnees capteurs
Byte  66    : error_flags (erreurs systeme)
Byte  67    : npm_status (status NextPM)
Byte  68    : device_status (etat general du boitier)
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)
```

---

## Byte 66 — Error Flags (erreurs systeme)

Chaque bit represente une erreur detectee par le script d'envoi (`SARA_send_data_v2.py`).

| Bit | Masque | Nom               | Description                                      | Source                        |
|-----|--------|-------------------|--------------------------------------------------|-------------------------------|
| 0   | 0x01   | RTC_DISCONNECTED  | Module RTC DS3231 non detecte sur le bus I2C      | timestamp_table → 'not connected' |
| 1   | 0x02   | RTC_RESET         | RTC en date par defaut (annee 2000)               | timestamp_table → annee 2000  |
| 2   | 0x04   | BME280_ERROR      | Capteur BME280 non detecte ou erreur de lecture   | data_BME280 → valeurs a 0     |
| 3   | 0x08   | NPM_ERROR         | Capteur NextPM non detecte ou erreur communication| data_NPM → toutes valeurs a 0 |
| 4   | 0x10   | ENVEA_ERROR       | Capteurs Envea non detectes ou erreur serie       | data_envea → valeurs a 0      |
| 5   | 0x20   | NOISE_ERROR       | Capteur bruit NSRT MK4 non detecte ou erreur      | data_noise → valeurs a 0      |
| 6   | 0x40   | MPPT_ERROR        | Chargeur solaire MPPT non detecte ou erreur       | data_MPPT → valeurs a 0       |
| 7   | 0x80   | WIND_ERROR        | Capteur vent non detecte ou erreur                | data_windMeter → valeurs a 0  |

### Detection des erreurs

Les scripts de collecte (`get_data_modbus_v3.py`, `get_data_v2.py`, etc.) ecrivent des **0**
en base SQLite quand un capteur ne repond pas. Le script d'envoi (`SARA_send_data_v2.py`)
lit ces valeurs et peut detecter l'erreur quand toutes les valeurs d'un capteur sont a 0.

Pour le RTC, le champ `timestamp_table` contient directement `'not connected'` ou une date
en annee 2000 quand le module est deconnecte ou reinitialise.

### Exemples de valeurs

| Valeur dec | Hex  | Signification                         |
|------------|------|---------------------------------------|
| 0          | 0x00 | Aucune erreur                         |
| 1          | 0x01 | RTC deconnecte                        |
| 2          | 0x02 | RTC reset (annee 2000)                |
| 5          | 0x05 | RTC deconnecte + BME280 erreur        |
| 9          | 0x09 | RTC deconnecte + NPM erreur           |
| 255        | 0xFF | Toutes les erreurs (cas extreme)      |

---

## Byte 67 — NPM Status (registre interne NextPM)

Le capteur NextPM possede un registre de status sur 9 bits (registre Modbus).
On stocke les 8 bits bas dans le byte 67. Ce registre est lu directement depuis
le capteur via Modbus, pas depuis SQLite.

| Bit | Masque | Nom             | Description                                                           | Severite       |
|-----|--------|-----------------|-----------------------------------------------------------------------|----------------|
| 0   | 0x01   | SLEEP_STATE     | Capteur en veille (commande sleep). Seule la lecture status autorisee  | Info           |
| 1   | 0x02   | DEGRADED_STATE  | Erreur mineure confirmee. Mesures possibles mais precision reduite    | Warning        |
| 2   | 0x04   | NOT_READY       | Demarrage en cours (15s apres mise sous tension). Mesures non fiables | Info           |
| 3   | 0x08   | HEAT_ERROR      | Humidite relative > 60% pendant > 10 minutes                         | Warning        |
| 4   | 0x10   | TRH_ERROR       | Capteur T/RH interne hors specification                               | Warning        |
| 5   | 0x20   | FAN_ERROR       | Vitesse ventilateur hors plage (tourne encore)                        | Warning        |
| 6   | 0x40   | MEMORY_ERROR    | Acces memoire impossible, fonctions internes limitees                 | Warning        |
| 7   | 0x80   | LASER_ERROR     | Aucune particule detectee pendant > 240s, possible erreur laser       | Warning        |

Note : le bit 8 du registre NextPM (DEFAULT_STATE — ventilateur arrete apres 3 tentatives)
ne tient pas dans un byte. Si necessaire, il peut etre combine avec le bit 0 (SLEEP_STATE)
car les deux indiquent un capteur inactif.

### Exemples de valeurs

| Valeur dec | Hex  | Signification                              |
|------------|------|--------------------------------------------|
| 0          | 0x00 | Capteur OK, mesures fiables                |
| 4          | 0x04 | Demarrage en cours (NOT_READY)             |
| 8          | 0x08 | Erreur humidite (HEAT_ERROR)               |
| 32         | 0x20 | Erreur ventilateur (FAN_ERROR)             |
| 128        | 0x80 | Possible erreur laser (LASER_ERROR)        |
| 40         | 0x28 | HEAT_ERROR + FAN_ERROR                     |

---

## 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

Le script `NPM/get_data_modbus_v3.py` doit etre modifie pour :
1. Lire le registre de status du NextPM (adresse Modbus a determiner)
2. Stocker le status byte dans une nouvelle colonne SQLite (ex: `npm_status` dans `data_NPM`)

### Etape 2 : Construire les flags dans SARA_send_data_v2.py

```python
# Constantes error_flags (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

# 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

if rtc_status == "disconnected":
    error_flags |= ERR_RTC_DISCONNECTED
if rtc_status == "reset":
    error_flags |= ERR_RTC_RESET
if PM1 == 0 and PM25 == 0 and PM10 == 0:
    error_flags |= ERR_NPM
# ... autres capteurs

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

```python
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
```

---

## Parser Miotiq

```
16|device_id|string|||W
2|signal_quality|hex2dec|dB||
2|version|hex2dec|||W
4|ISO_68|hex2dec|ugm3|x/10|
4|ISO_39|hex2dec|ugm3|x/10|
4|ISO_24|hex2dec|ugm3|x/10|
4|ISO_54|hex2dec|degC|x/100|
4|ISO_55|hex2dec|%|x/100|
4|ISO_53|hex2dec|hPa||
4|noise_cur_leq|hex2dec|dB|x/10|
4|noise_cur_level|hex2dec|dB|x/10|
4|max_noise|hex2dec|dB|x/10|
4|ISO_03|hex2dec|ppb||
4|ISO_05|hex2dec|ppb||
4|ISO_21|hex2dec|ppb||
4|ISO_04|hex2dec|ppb||
4|ISO_08|hex2dec|ppb||
4|npm_ch1|hex2dec|count||
4|npm_ch2|hex2dec|count||
4|npm_ch3|hex2dec|count||
4|npm_ch4|hex2dec|count||
4|npm_ch5|hex2dec|count||
4|npm_temp|hex2dec|°C|x/10|
4|npm_humidity|hex2dec|%|x/10|
4|battery_voltage|hex2dec|V|x/100|
4|battery_current|hex2dec|A|x/100|
4|solar_voltage|hex2dec|V|x/100|
4|solar_power|hex2dec|W||
4|charger_status|hex2dec|||
4|wind_speed|hex2dec|m/s|x/10|
4|wind_direction|hex2dec|degrees||
2|error_flags|hex2dec|||
2|npm_status|hex2dec|||
2|device_status|hex2dec|||
2|version_major|hex2dec|||
2|version_minor|hex2dec|||
2|version_patch|hex2dec|||
22|reserved|skip|||
```

---

## Lecture cote serveur (exemple Python)

```python
# Byte 66 — erreurs systeme
error_flags = int(parsed_error_flags)

rtc_disconnected = bool(error_flags & 0x01)
rtc_reset        = bool(error_flags & 0x02)
bme280_error     = bool(error_flags & 0x04)
npm_error        = bool(error_flags & 0x08)
envea_error      = bool(error_flags & 0x10)
noise_error      = bool(error_flags & 0x20)
mppt_error       = bool(error_flags & 0x40)
wind_error       = bool(error_flags & 0x80)

# Byte 67 — status NextPM
npm_status = int(parsed_npm_status)

npm_sleep     = bool(npm_status & 0x01)
npm_degraded  = bool(npm_status & 0x02)
npm_not_ready = bool(npm_status & 0x04)
npm_heat_err  = bool(npm_status & 0x08)
npm_trh_err   = bool(npm_status & 0x10)
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")
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")
```

---

## Notes

- Les bytes 66-68 sont initialises a 0x00 dans le constructeur SensorPayload
  (0x00 = aucune erreur/aucun flag). Les bytes 69-71 restent a 0xFF si le
  fichier VERSION est absent ou malformed.
- Le NPM status n'est pas encore lu par `get_data_modbus_v3.py`. Il faut d'abord
  ajouter la lecture du registre de status Modbus et le stocker en SQLite.
- Les flags du byte 66 sont determines par le script d'envoi en analysant les
  valeurs lues depuis SQLite (toutes a 0 = capteur en erreur).
