pin fw 0.5.1 + docs; htracker patch for Gibran

This commit is contained in:
Leopoldo Rodríguez 2026-09-18 13:12:09 -06:00
parent e2bf432a32
commit 63bf5cc82f
7 changed files with 204 additions and 16 deletions

View File

@ -0,0 +1,152 @@
--- a/retranslator.py
+++ b/retranslator.py
@@ -712,6 +712,24 @@
'tair', 'rh', 'tbat', 'fan_rpm', 'fan_pwm', 'fan_fault',
)
_PHLG_FIELDS_V3_EXTRA = ('tntc', 'chg', 'cflt', 'rxbad')
+# fw 0.5.1-fm (EinarBoard v5, bring-up/field-minimum) — append-only, campos 25..29:
+# up_s segundos desde el arranque del MCU (si baja ⇒ reinicio)
+# pwr VEH (5 V del vehículo presente) | BATT (en respaldo LTC4040)
+# v5min mínimo instantáneo del riel 5 V en la ventana de 30 s (caídas de ms por ráfaga LTE)
+# vbat_min mínimo instantáneo del paquete en la misma ventana
+# bkp_n respaldos de batería desde el arranque
+_PHLG_FIELDS_V5_EXTRA = ('up_s', 'pwr', 'v5min', 'vbat_min', 'bkp_n')
+PHLG_V5MIN_WARN_V = 4.60 # zona de disparo PFI del LTC4040 (divisor 270k/100k ⇒ 4.44 V)
+PHLG_VBAT_LOW_V = 3.50
+
+# ── $PHEV — eventos de la unidad, en banda (fw 0.5.1+) ──────────────────────
+# $PHEV,<up_s>,<CODIGO>,<detalle>*CS — una línea por transición de estado
+# (STACK_ON/OFF, BACKUP_START/END, RECOVERY, LIMP, TEMP_CUT/REARM, V5_LOW/LOST/OK,
+# IGN_ON/OFF, SLEEP, MODOB_ON/OFF, BOOT). Se guardan los últimos PHEV_MAX por
+# unidad y se exponen en el status JSON como 'events' para el monitor.
+PHEV_MAX = 200
+DEVICE_EVENTS: dict[str, deque] = {} # cell_id → deque de {'ts','up_s','code','detail'}
+_PHEV_WARN = {'RECOVERY', 'LIMP', 'TEMP_CUT', 'V5_LOW', 'SLEEP', 'BACKUP_START'}
def _nmea_checksum_ok(sentence: str) -> bool:
@@ -740,9 +758,33 @@
names = list(_PHLG_FIELDS_V2)
if len(vals) >= len(_PHLG_FIELDS_V2) + len(_PHLG_FIELDS_V3_EXTRA):
names += list(_PHLG_FIELDS_V3_EXTRA)
+ if len(vals) >= len(names) + len(_PHLG_FIELDS_V5_EXTRA):
+ names += list(_PHLG_FIELDS_V5_EXTRA)
return dict(zip(names, vals))
+def _parse_phev(sentence: str) -> dict | None:
+ """$PHEV,<up_s>,<CODIGO>,<detalle> → dict, o None si el checksum/forma no cuadra."""
+ if not _nmea_checksum_ok(sentence):
+ return None
+ parts = sentence[1:].partition('*')[0].split(',', 3)
+ if len(parts) < 3 or parts[0] != 'PHEV':
+ return None
+ try:
+ up_s = int(parts[1])
+ except ValueError:
+ return None
+ return {'up_s': up_s, 'code': parts[2], 'detail': parts[3] if len(parts) > 3 else ''}
+
+
+def _note_event(cell_id: str, ev: dict, now_ts: float) -> None:
+ """Guarda el evento y lo registra en el log (WARNING para los que piden acción)."""
+ ev = dict(ev); ev['ts'] = now_ts
+ DEVICE_EVENTS.setdefault(cell_id, deque(maxlen=PHEV_MAX)).append(ev)
+ msg = f"[{cell_id}] $PHEV up={ev['up_s']}s {ev['code']} {ev['detail']}".rstrip()
+ (log.warning if ev['code'] in _PHEV_WARN else log.info)(msg)
+
+
def _parse_pubx00(sentence: str) -> dict | None:
"""$PUBX,00 (u-blox propietaria): hAcc/vAcc/edad-DGPS/sats usados.
Índices verificados contra capturas reales de la flota (2026-08-10)."""
@@ -785,17 +827,58 @@
f"(¿cargador pausado por NTC?).")
except ValueError:
pass
+ # ── Reglas de diagnóstico (fw 0.4.0+ / 0.5.1+) ──────────────────────────
+ # 1. reset_cause=IWDG ⇒ el MCU se colgó (watchdog). 2. up_s bajó ⇒ reinicio
+ # (contamos reinicios/hora: ≥2 = bucle de arranque). 3. recoveries subió ⇒
+ # el F9P dejó de hablar. 4. v5min en zona PFI mientras sigue reportando.
+ # 5. pwr=BATT ⇒ en respaldo (con cargador OFF el paquete es de un solo uso).
+ if health.get('reset_cause') == 'IWDG' and (not prev or prev.get('reset_cause') != 'IWDG'):
+ log.warning(f"[{cell_id}] $PHLG reinicio por WATCHDOG (IWDG): el MCU se colgó.")
+ try:
+ up_now = int(health.get('up_s') or -1)
+ up_prev = int(prev.get('up_s') or -1) if prev else -1
+ if 0 <= up_now < up_prev:
+ reboots = health['_reboots'] = [t for t in (prev.get('_reboots') or []) if now_ts - t < 3600] + [now_ts]
+ lvl = log.warning if len(reboots) >= 2 else log.info
+ lvl(f"[{cell_id}] $PHLG reinicio detectado (up {up_prev}s → {up_now}s), "
+ f"{len(reboots)} en la última hora{' — ¿BUCLE DE ARRANQUE?' if len(reboots) >= 2 else ''}.")
+ elif prev and prev.get('_reboots'):
+ health['_reboots'] = [t for t in prev['_reboots'] if now_ts - t < 3600]
+ rec_now = int(health.get('recoveries') or 0)
+ rec_prev = int(prev.get('recoveries') or 0) if prev else 0
+ if rec_now > rec_prev:
+ log.warning(f"[{cell_id}] $PHLG recovery #{rec_now}: la unidad cicló la etapa RTK (F9P sin NMEA / RTK perdido).")
+ v5min = float(health.get('v5min') or 99)
+ if v5min < PHLG_V5MIN_WARN_V and float(health.get('v5v') or 0) > 3.8:
+ log.warning(f"[{cell_id}] $PHLG riel 5 V cayó a {v5min:.2f} V en los últimos 30 s "
+ f"(zona PFI del LTC4040): ajustar la fuente del gabinete a ~5.3 V.")
+ if health.get('pwr') == 'BATT' and (not prev or prev.get('pwr') != 'BATT'):
+ log.warning(f"[{cell_id}] $PHLG en RESPALDO de batería (vbat={health.get('vbat')} V, bkp #{health.get('bkp_n')}).")
+ if health.get('pwr') == 'VEH' and prev and prev.get('pwr') == 'BATT':
+ log.info(f"[{cell_id}] $PHLG regresó a alimentación del vehículo.")
+ except (ValueError, TypeError):
+ pass
health['ts'] = now_ts
DEVICE_HEALTH[cell_id] = health
+def _events_snapshot(cell_id: str, now: float, n: int = 20) -> list:
+ """Últimos n eventos $PHEV de la unidad (más reciente al final), con edad."""
+ evs = DEVICE_EVENTS.get(cell_id)
+ if not evs:
+ return []
+ return [{'age_s': round(now - e['ts'], 1), 'up_s': e['up_s'], 'code': e['code'], 'detail': e['detail']}
+ for e in list(evs)[-n:]]
+
+
def _health_snapshot(cell_id: str, now: float) -> dict | None:
"""Copia del último $PHLG con su edad, para el status JSON (None si nunca)."""
h = DEVICE_HEALTH.get(cell_id)
if not h:
return None
- out = {k: v for k, v in h.items() if k != 'ts'}
+ out = {k: v for k, v in h.items() if k != 'ts' and not k.startswith('_')}
out['age_s'] = round(now - h['ts'], 1)
+ out['reboots_1h'] = len(h.get('_reboots') or [])
return out
@@ -916,6 +999,7 @@
'jump_rejects': device_jump_rejects.get(cid, 0),
# ── Telemetría en banda, si la unidad la manda ───────────
'health': _health_snapshot(cid, now), # $PHLG | None
+ 'events': _events_snapshot(cid, now), # $PHEV (fw 0.5.1+) | []
'hacc_m': info.get('hacc_m'), # $PUBX,00
'vacc_m': info.get('vacc_m'),
'diff_age_s': info.get('diff_age_s'),
@@ -1436,6 +1520,22 @@
device_total_online_s, device_last_seen_ever)
_note_health(cell_id, health, now_ts)
+ # ── $PHEV — eventos de la unidad, en banda (firmware 0.5.1+) ──
+ elif raw.startswith('$PHEV'):
+ ev = _parse_phev(raw)
+ if ev:
+ entry = addr_to_cell_id.get(addr)
+ if not entry:
+ continue
+ cell_id = entry[0]
+ now_ts = time.time()
+ addr_to_cell_id[addr] = (cell_id, now_ts)
+ _mark_seen(cell_id, now_ts, device_last_seen,
+ device_first_seen, device_packet_count,
+ device_disconnected, device_first_connected,
+ device_total_online_s, device_last_seen_ever)
+ _note_event(cell_id, ev, now_ts)
+
# ── $PUBX,00 — precisión estimada del F9P (configs nuevas) ────
elif raw.startswith('$PUBX'):
pubx = _parse_pubx00(raw)

View File

@ -0,0 +1,36 @@
# Parches para htracker (rama `feature/test500unidades`)
Parches propuestos al backend que **no** se aplican desde aquí: Gibran es el dueño de `htracker`.
Se generan contra la última `origin/feature/test500unidades` (`retranslator.py` no cambió entre
`d8dc4cf` y `2cdcb5e`) y se prueban con el parser real antes de proponerlos.
## 2026-09-18 — `retranslator_phlg-v5-extra_phev_alertas.patch` (fw 0.5.1-fm)
Aplicar en el clon del servidor o en el Mac:
```bash
cd htracker && git checkout feature/test500unidades && git pull --ff-only
git apply --check ../../02-Architecture-Software/htracker-patches/2026-09-18_retranslator_phlg-v5-extra_phev_alertas.patch
git apply ../../02-Architecture-Software/htracker-patches/2026-09-18_retranslator_phlg-v5-extra_phev_alertas.patch
python3 -m py_compile retranslator.py
```
Qué hace (solo `retranslator.py`, 152 líneas; `web_monitor.py` queda para Gibran):
1. **`$PHLG` de 29 campos** (`_PHLG_FIELDS_V5_EXTRA = up_s, pwr, v5min, vbat_min, bkp_n`). Append-only:
las tramas de 21 (v2.x) y 25 (v3.x) campos siguen parseando igual.
2. **`$PHEV,<up_s>,<CODIGO>,<detalle>*CS`** — eventos en banda (STACK_ON/OFF, BACKUP_START/END, RECOVERY,
LIMP, TEMP_CUT/REARM, V5_LOW/LOST/OK, IGN_ON/OFF, SLEEP, MODOB_ON/OFF, BOOT). Se guardan los últimos 200 por
unidad (`DEVICE_EVENTS`) y salen en el status JSON como `events` (últimos 20, con `age_s`).
3. **Reglas de diagnóstico** en `_note_health` (van al log como WARNING → Loki/Grafana):
- `reset_cause=IWDG` → el MCU se colgó.
- `up_s` bajó → reinicio; ≥ 2 en una hora → «¿BUCLE DE ARRANQUE?». `reboots_1h` sale en el snapshot.
- `recoveries` subió → la unidad cicló la etapa RTK.
- `v5min < 4.60 V` con el riel presente → zona PFI del LTC4040: ajustar la fuente del gabinete a ~5.3 V.
- `pwr=BATT` → en respaldo (cargador OFF en 0.x: el paquete es de un solo uso); regreso a `VEH` en INFO.
Probado con el parser real: tramas de 24 y 29 campos, `$PHEV`, checksum malo rechazado, y las cinco reglas disparan.
Sugerencias para `web_monitor.py` (Gibran): columna «Eventos» (últimos `events`), badge `reboots_1h`,
`v5min` junto a `v5v` en el medidor de riel, `pwr=BATT` en amarillo, y graficar el RSSI que ya viene en
`$GPTXT,NET,H00xx,TELCEL,<rssi>,…`.

@ -1 +1 @@
Subproject commit a82ca4ace53106dd7f1d18ffb1fb92a1f4b0c65d Subproject commit ab4d82973c2de361d454e489738f5487452cf812

View File

@ -37,7 +37,7 @@ y dejarla flasheada con el firmware de arranque entregado por ingeniería.
| 1 | Fuente de banco 0–30 V con límite de corriente ajustable | | 1 | Fuente de banco 0–30 V con límite de corriente ajustable |
| 1 | Cable USB-C ↔ USB-C (o USB-A ↔ USB-C) **con datos** (no solo carga) | | 1 | Cable USB-C ↔ USB-C (o USB-A ↔ USB-C) **con datos** (no solo carga) |
| 1 | Laptop con **STM32CubeProgrammer** instalado (o `dfu-util` en terminal) | | 1 | Laptop con **STM32CubeProgrammer** instalado (o `dfu-util` en terminal) |
| 1 | Archivo de firmware: **`hbox-v5-bringup_0.5.0.bin`** — descarga: **https://drogheda.helgen.online/descargas/** (repo: `04-Source-Code/firmware/hbox_v5_bringup/release/`, SHA-256 inicia `98656ddc`) | | 1 | Archivo de firmware: **`hbox-v5-bringup_0.5.1.bin`** — descarga: **https://drogheda.helgen.online/descargas/** (repo: `04-Source-Code/firmware/hbox_v5_bringup/release/`, SHA-256 inicia `e1f35108`) |
| 1 | Jumper 2.54 mm para P11 (interruptor de corte) | | 1 | Jumper 2.54 mm para P11 (interruptor de corte) |
| 1 | Lupa o lámpara con aumento | | 1 | Lupa o lámpara con aumento |
| — | Hoja de registro (sección 7) | | — | Hoja de registro (sección 7) |
@ -183,7 +183,7 @@ Solo para tarjetas que pasaron las secciones 1 a 4.
2. Arriba a la derecha, en el selector de conexión elegir **USB**. Pulsar el botón de refrescar; 2. Arriba a la derecha, en el selector de conexión elegir **USB**. Pulsar el botón de refrescar;
debe aparecer `USB1`. Pulsar **Connect**. debe aparecer `USB1`. Pulsar **Connect**.
3. Ir a la pestaña **Erasing & Programming** (icono de flecha hacia abajo, lado izquierdo). 3. Ir a la pestaña **Erasing & Programming** (icono de flecha hacia abajo, lado izquierdo).
4. **File path** → seleccionar `hbox-v5-bringup_0.5.0.bin`. 4. **File path** → seleccionar `hbox-v5-bringup_0.5.1.bin`.
5. **Start address** = `0x08000000`. Marcar **Verify programming** y **Run after programming**. 5. **Start address** = `0x08000000`. Marcar **Verify programming** y **Run after programming**.
6. Pulsar **Start Programming**. Esperar "File download complete" y "Verify… successful". 6. Pulsar **Start Programming**. Esperar "File download complete" y "Verify… successful".
7. Pulsar **Disconnect**. 7. Pulsar **Disconnect**.
@ -192,7 +192,7 @@ Solo para tarjetas que pasaron las secciones 1 a 4.
```bash ```bash
dfu-util -l # debe listar [0483:df11] dfu-util -l # debe listar [0483:df11]
dfu-util -a 0 -s 0x08000000:leave -D hbox-v5-bringup_0.5.0.bin dfu-util -a 0 -s 0x08000000:leave -D hbox-v5-bringup_0.5.1.bin
``` ```
### 5d. Verificación después del flasheo ### 5d. Verificación después del flasheo
@ -206,7 +206,7 @@ dfu-util -a 0 -s 0x08000000:leave -D hbox-v5-bringup_0.5.0.bin
| **D17** (LED azul) + LED de panel en P8 (si hay) | **Parpadean** a 1 Hz (MCU ejecutando el firmware) | | **D17** (LED azul) + LED de panel en P8 (si hay) | **Parpadean** a 1 Hz (MCU ejecutando el firmware) |
| **DB1 → DB4** (LEDs del indicador de batería) | Se encienden **en secuencia** una vez, luego se apagan; después R, G, B en P14 si hay LED RGB conectado | | **DB1 → DB4** (LEDs del indicador de batería) | Se encienden **en secuencia** una vez, luego se apagan; después R, G, B en P14 si hay LED RGB conectado |
| Laptop | Aparece un **puerto serie** nuevo ("STMicroelectronics Virtual COM Port" / `usbmodem…`) | | Laptop | Aparece un **puerto serie** nuevo ("STMicroelectronics Virtual COM Port" / `usbmodem…`) |
| Terminal serie a **115200 baud** | Encabezado `hbox-v5-bringup 0.5.0` (cualquier otra version: re-flashear), luego líneas `[PASS]` / `[FAIL]`, y al final **`>>> BOARD OK`** | | Terminal serie a **115200 baud** | Encabezado `hbox-v5-bringup 0.5.1` (cualquier otra version: re-flashear), luego líneas `[PASS]` / `[FAIL]`, y al final **`>>> BOARD OK`** |
3. Si el terminal se abrió tarde y no se vio el reporte: teclear **`r`** en el terminal → repite 3. Si el terminal se abrió tarde y no se vio el reporte: teclear **`r`** en el terminal → repite
todas las pruebas (y vuelve a encender los LEDs en secuencia). todas las pruebas (y vuelve a encender los LEDs en secuencia).

View File

@ -53,7 +53,7 @@ si el módulo ya está asignado.
| Ítem | Notas | | Ítem | Notas |
|---|---| |---|---|
| Gabinete HBOX con panel perforado (GX16-5, 3 × SMA, RJ45, prensaestopas) | según el diseño mecánico vigente | | Gabinete HBOX con panel perforado (GX16-5, 3 × SMA, RJ45, prensaestopas) | según el diseño mecánico vigente |
| EinarBoard **V5-NN** aprobada, con firmware de bring-up **0.5.0 o superior** | ver acta de tarjeta | | EinarBoard **V5-NN** aprobada, con firmware de bring-up **0.5.1 o superior** | ver acta de tarjeta |
| simpleRTK2B Pro **ARD-NNN** y módem 4G NTRIP Master **RTK-NNNN** aprobados e integrados | con sus bolsas | | simpleRTK2B Pro **ARD-NNN** y módem 4G NTRIP Master **RTK-NNNN** aprobados e integrados | con sus bolsas |
| Fuente **Mean Well SD-15B-05** (24 V → 5 V) | va dentro del HBOX | | Fuente **Mean Well SD-15B-05** (24 V → 5 V) | va dentro del HBOX |
| Pack de batería **1S4P** con NTC (3 hilos → P4) | conectar al final | | Pack de batería **1S4P** con NTC (3 hilos → P4) | conectar al final |

View File

@ -1,13 +1,13 @@
# HBOX v5 — Prueba final de unidad ensamblada con firmware 0.5.0 (field-minimum + telemetría) # HBOX v5 — Prueba final de unidad ensamblada con firmware 0.5.1 (field-minimum + telemetría y eventos)
**Alcance:** unidad completa (EinarBoard V5 + ArduSimple + módem 4G + batería + antenas) dentro del HBOX, antes de embarcar. **Alcance:** unidad completa (EinarBoard V5 + ArduSimple + módem 4G + batería + antenas) dentro del HBOX, antes de embarcar.
**Firmware:** `hbox-v5-bringup_0.5.0.bin` (SHA-256 `98656ddc…`) — descarga en <https://drogheda.helgen.online/descargas/>. **Firmware:** `hbox-v5-bringup_0.5.1.bin` (SHA-256 `e1f35108…`) — descarga en <https://drogheda.helgen.online/descargas/>.
**Duración:** ~45 min por unidad (P1–P6). Registrar todo en el acta de ensamble (`HBOX5-Exx-Axxx-Rxxxx`). **Duración:** ~45 min por unidad (P1–P6). Registrar todo en el acta de ensamble (`HBOX5-Exx-Axxx-Rxxxx`).
!!! warning "Lo que 0.5.0 NO hace" !!! warning "Lo que 0.5.1 NO hace"
**No carga la batería** (cargador apagado). El paquete es de un solo uso: sostiene la unidad cuando se va el 5 V del vehículo **No carga la batería** (cargador apagado). El paquete es de un solo uso: sostiene la unidad cuando se va el 5 V del vehículo
hasta 3.40 V y después la unidad solo funciona con alimentación del vehículo. Las unidades regresan a laptop (DFU en J11, 2 min) hasta 3.40 V y después la unidad solo funciona con alimentación del vehículo. Las unidades regresan a laptop (DFU en J11, 2 min)
cuando salga el firmware de producción. Anotar en el acta: *«fw 0.5.0-fm, cargador OFF»*. cuando salga el firmware de producción. Anotar en el acta: *«fw 0.5.1-fm, cargador OFF»*.
## 0. Preparación ## 0. Preparación
- Consola: cable USB-C en **J11**, PuTTY/CoolTerm **115200**. Cada línea es `STAT`, `EVT` o `$PHLG`; teclear `l` para la leyenda. - Consola: cable USB-C en **J11**, PuTTY/CoolTerm **115200**. Cada línea es `STAT`, `EVT` o `$PHLG`; teclear `l` para la leyenda.
@ -20,7 +20,7 @@ Energizar 24 V (con ignición) y **no tocar la consola**.
| Esperado | Si falla | | Esperado | Si falla |
|---|---| |---|---|
| Banner `hbox-v5-bringup 0.5.0` · `modo=AUTO` · `inputs at boot: v5=5.2–5.4V(ok) ign=ON` | otra versión → re-flashear; `ign=OFF` → arnés pin 3 / divisor 22k-10k | | Banner `hbox-v5-bringup 0.5.1` · `modo=AUTO` · `inputs at boot: v5=5.2–5.4V(ok) ign=ON` | otra versión → re-flashear; `ign=OFF` → arnés pin 3 / divisor 22k-10k |
| `---- RESULT: 12 PASS / 0 FAIL ----` · `>>> BOARD OK` | cualquier `[FAIL]` → tarjeta a revisión, no embarcar | | `---- RESULT: 12 PASS / 0 FAIL ----` · `>>> BOARD OK` | cualquier `[FAIL]` → tarjeta a revisión, no embarcar |
| `EVT … STACK ON reason=auto` **sin teclear nada** (≈10 s tras el banner) | no enciende → `STAT` muestra `v5=` bajo 4.5 o `modo=MAN` (teclear `A`) | | `EVT … STACK ON reason=auto` **sin teclear nada** (≈10 s tras el banner) | no enciende → `STAT` muestra `v5=` bajo 4.5 o `modo=MAN` (teclear `A`) |
| `EVT … NMEA primera trama` < 3 s después · `EVT FAN ON (vehiculo: 5V+ign)` | sin NMEA → ArduSimple no arranca: medir SW_5V (MOD1 pin 5) ≈ 5 V | | `EVT … NMEA primera trama` < 3 s después · `EVT FAN ON (vehiculo: 5V+ign)` | sin NMEA → ArduSimple no arranca: medir SW_5V (MOD1 pin 5) ≈ 5 V |
@ -31,11 +31,11 @@ Dejar la unidad 10 min encendida con el módem registrándose (LEDs del módem /
| Esperado | Si falla | | Esperado | Si falla |
|---|---| |---|---|
| `STAT … v5=5.2x–5.3x(ok)` **todo el tiempo**; nunca `(BAJO)` ni `EVT V5 BAJO` | `BAJO` → el riel cae con la ráfaga LTE: SD-15B ajustado a ~5.3 V en P2 (memo 24-Jul, PFI LTC4040) | | `STAT … v5=5.2x–5.3x(ok)` **todo el tiempo**; nunca `(BAJO)` ni `EVT V5 BAJO`; **`v5min` ≥ 4.9 V** en todas las STAT | `BAJO` o `v5min` < 4.6 → el riel cae con la ráfaga LTE: SD-15B ajustado a ~5.3 V en P2 (memo 24-Jul, PFI LTC4040) |
| `tbat`/`tair`/`tmcu` estables (< 45 °C en banco) · `i2cerr=0` | `i2cerr` subiendo → bus I²C / soldadura U3/U4/U13 | | `tbat`/`tair`/`tmcu` estables (< 45 °C en banco) · `i2cerr=0` | `i2cerr` subiendo → bus I²C / soldadura U3/U4/U13 |
| `$PHLG,0.5.0-fm,RUN,…` cada 10 s, `rec=0`, sin `LIMP` | `rec` > 0 sin causa → NMEA intermitente: revisar MOD1 / UART1 F9P | | `$PHLG,0.5.1-fm,RUN,…` cada 10 s, `rec=0`, sin `LIMP` | `rec` > 0 sin causa → NMEA intermitente: revisar MOD1 / UART1 F9P |
| **Modo B:** ≈10 s tras la primera NMEA, `EVT MODOB ON`; luego `STAT … modoB=ON relay=` (sube cientos/s) `phlg_aire=` (+1 cada 30 s) | `modoB=off` permanente → sin NMEA en PA3 o `B` bloqueado; `relay=0` con `modoB=ON` → K1 / PA10 | | **Modo B:** ≈10 s tras la primera NMEA, `EVT MODOB ON`; luego `STAT … modoB=ON relay=` (sube cientos/s) `phlg_aire=` (+1 cada 30 s) | `modoB=off` permanente → sin NMEA en PA3 o `B` bloqueado; `relay=0` con `modoB=ON` → K1 / PA10 |
| **Servidor:** en ≤ 2 min la unidad `H00xx` muestra la columna **Salud** (fw `0.5.0-fm`, vbat, temperaturas) y sigue reportando posición 1 Hz | posición sí pero sin Salud → el módem no reenvía la `$PHLG`: revisar `$$$AS+` (modo transparente) | | **Servidor:** en ≤ 2 min la unidad `H00xx` muestra la columna **Salud** (fw `0.5.1-fm`, vbat, temperaturas, `v5min`) y en el log del servidor los `$PHEV` (BOOT, STACK_ON, MODOB_ON) y sigue reportando posición 1 Hz | posición sí pero sin Salud → el módem no reenvía la `$PHLG`: revisar `$$$AS+` (modo transparente) |
## P3 — Respaldo de batería (6 min) — **la prueba que nunca se había hecho en v5** ## P3 — Respaldo de batería (6 min) — **la prueba que nunca se había hecho en v5**
Con la etapa ON: **desconectar el 24 V** (se va el 5 V del SD-15B). Esperar 5 min. Reconectar. Con la etapa ON: **desconectar el 24 V** (se va el 5 V del SD-15B). Esperar 5 min. Reconectar.
@ -65,7 +65,7 @@ Con la etapa ON y > 90 s de encendida: **retirar el ArduSimple del socket** (o s
## P6 — Apagado y acta (2 min) ## P6 — Apagado y acta (2 min)
- Quitar ignición: `EVT IGN OFF`, `EVT FAN OFF (bateria: termostato)`; la etapa sigue ON mientras haya 5 V (el SD-15B sigue con 24 V). - Quitar ignición: `EVT IGN OFF`, `EVT FAN OFF (bateria: termostato)`; la etapa sigue ON mientras haya 5 V (el SD-15B sigue con 24 V).
- Quitar 24 V: si la batería está, entra `BACKUP` (P3); dejarla hasta `SLEEP` **no** es necesario — reconectar y apagar con `P`. - Quitar 24 V: si la batería está, entra `BACKUP` (P3); dejarla hasta `SLEEP` **no** es necesario — reconectar y apagar con `P`.
- Guardar el log. En el acta: versión (`0.5.0-fm`), `uid=` del banner, resultado P1–P5, `vbat` final, y la nota *cargador OFF*. - Guardar el log. En el acta: versión (`0.5.1-fm`), `uid=` del banner, resultado P1–P5, `vbat` final, y la nota *cargador OFF*.
## Lectura rápida del LED RGB (para el técnico de campo) ## Lectura rápida del LED RGB (para el técnico de campo)
| Color | Significado | | Color | Significado |

@ -1 +1 @@
Subproject commit caee07fde9ab9c807356806bb2f22a4a169c9678 Subproject commit ade43bbc14ed1da637d82b93dd473d4dd5d71e17