--- title: Firmware v3.1 — Modo B / $PHLG en banda — Design Brief & Execution Prompt tags: [project-drogheda, firmware, epct, modo-b, phlg, telemetria, design-brief, prompt] project: Project Drogheda author: Helgen CTO + Claude (decisión registrada 2026-08-08) date: 2026-08-08 status: Active — EL prompt para la sesión de implementación de la telemetría en banda (v3.1 núcleo EPCT) authority: ejecuta la decisión A de `Informe-Tecnico-V3-V4` §4.7–4.8 (v1.2); hardware canónico = watchdog-electrical-design-state.md; hallazgos F1–F9 = firmware-review-v2.3-EinarBoard.md --- # Firmware v3.1 (núcleo EPCT) — encender la telemetría en banda > **Cómo usar este doc.** Es la instrucción autocontenida para convertir la decisión > «**A = `$PHLG` tal cual, en banda por Modo B, es la palabra canónica de telemetría**» > (Leo, 2026-08-08) en software corriendo sobre V3 (EinarBoard 1.0, vía adaptador inversor) > y V4 (HBOX v4). Una sesión (humana o Claude) debe poder arrancar solo con este archivo. > Reglas vigentes: hostsim en verde antes de tocar hardware; **banco antes de campo**; > el NC de K1 es la garantía de hardware — ningún cambio puede debilitarla. ## 0. La decisión y su estado de partida | Hecho | Estado hoy (v2.3 flota / v3.0 escrito) | |---|---| | Flujo NMEA al servidor | **Sin alterar** — posición pura; K1 desenergizado (PB5 sin manejar), `ENABLE_PHLG_RELAY=0` | | `$PHLG` | Se construye y emite **cada 30 s solo al log USB** (21 campos v2.3 / 25 v3.0, checksum XOR correcto) | | Camino Modo B en cobre | Listo y verificado en **ambas** tarjetas (PA10 tap · PA9→NO · PB5→Q6→bobina · NC fail-safe) | | Transparencia del módem | **Probada** 2026-06-22: `$PUBX` inyectada llegó byte-idéntica al servidor | | Palabra canónica | **DECIDIDA 2026-08-08: opción A** — la misma `$PHLG` del log USB, sin recortar ni re-empacar (una sola palabra en USB y aire; append-only se mantiene) | Consecuencia inmediata útil: **el decodificador del backend puede desarrollarse y probarse HOY** contra capturas USB de banco — son los mismos bytes que llegarán por aire. ## 1. Fases (cada una con compuerta propia; 1 y 2 pueden correr en paralelo) | Fase | Qué | Compuerta de salida | |---|---|---| | **1 — Servidor primero** (cero riesgo, sin tocar firmware) | `htracker`: extender `PHLG_FIELDS` a los 24 campos de datos v3.0; parser tolerante por longitud (20 = v2.3, 24 = v3.0+, extras = ignorar); validar checksum XOR; sentinelas −127/−1 → null; almacenar por unidad (llave `cell_id`) + panel/alertas (`limp=1`, `rec` subiendo, `cflt=1`, `nmea_age>60000`, `tmax≥75` o celda `≥55`) | Decodifica al 100 % un log USB real de banco (v2.3 **y** v3.0) | | **2 — Prerrequisitos F4** (firmware, sin encender nada) | SHT40 no bloqueante en dos fases + rings UART de 256 B (§3.1–3.2) | hostsim verde; TLM intacta; sin `delay()` en el lazo | | **3 — Relevo Modo B en banco V3** | `ENABLE_PHLG_RELAY=1` + driver PB5 (§3.3–3.5) en una 1.0 de banco (adaptador inversor) | Los 5 gates de banco de §4 en verde | | **4 — Piloto en vivo** | Una unidad transmitiendo; Fase 1 ya desplegada | `$PHLG` visible en el panel; RTK fix≥4 sostenido con relevo insertado; consumo medido | | **5 — V4** | Mismo código bajo `TARGET_HBOXV4` (PA9/PA10/PB5 = nets idénticas); después v3.2 = espejo de la MISMA palabra por W5500/TCP | bring-up V4 + gates §4 repetidos | ## 2. Diseño de la inserción (política de K1) - **Insertar** solo en la transición BOOTING→RUNNING (después de «Boot complete»), **una vez por sesión de energía**: orden estricto `RELAY.begin(115200)` → `digitalWrite(PB5, HIGH)`. El transfer del G5V-1 (~ms) puede morder una sentencia en el switchover — aceptable una vez por sesión; jamás conmutar por-sentencia. - **Retirar** en TODA salida de RUNNING — dentro de `setPower(false)` (cubre `beginPowerCycle` y SLEEP): `digitalWrite(PB5, LOW)` (NC restituye al instante) → `RELAY.end()` → `pinMode(PA9, INPUT)` (flotar TX, espejo del patrón PA2 — no retroalimentar el riel). - **Fallas**: cuelgue del MCU → IWDG resetea → GPIO a default → PB5 cae → **el NC restituye el camino por hardware** (R41 10 k garantiza Q6 apagado durante el reset). Nada que programar: documentar como la historia de seguridad y probarla (gate G4). - **Presupuesto**: bobina ~30 mA de `SYS5V` mientras está insertado. En respaldo queremos la telemetría viva (¡ahí viaja `vbat`!) — se queda insertado; 30 mA contra 12 Ah es ruido. Datos: ~95 B/30 s ≈ **~0.3 MB/mes/unidad** — despreciable. ## 3. Cambios de código (archivo único `blackpillrtkcode_v3.ino` → v3.1) 1. **SHT40 en dos fases** (F4): sustituir `readSHT40()` bloqueante por máquina de dos estados dentro de `thermalService()`: pasada N dispara (`Wire` write `0xFD`, marca `sht40TrigMs`), pasada N+1 (≥10 ms después; THERMAL_PERIOD=2 s sobra) cosecha los 6 bytes. Cero `delay()` en el lazo. TMP102 queda igual (free-running, lectura corta). 2. **Rings UART de 256 B**: `build_opt.h` en la carpeta del sketch con `-DSERIAL_RX_BUFFER_SIZE=256 -DSERIAL_TX_BUFFER_SIZE=256` (vía STM32duino; equivalente `--build-property` en arduino-cli). Aplica a GNSS **y** RELAY. 3. **Driver de inserción**: `#define PIN_BYP_CTRL PB5`; en `setup()` `OUTPUT` + `LOW` **antes** de cualquier otra cosa; helper `setModoB(bool)` con el orden de §2; llamada ON en la transición a RUNNING, OFF dentro de `setPower(false)`. 4. **Flags**: `ENABLE_PHLG_RELAY 1` en el build EinarBoard; el pump (`pumpRelay()`) y la cola `pendingPhlg` **ya existen y son correctos** (reenvío byte a byte + inyección solo tras ≥8 ms de línea ociosa, solo en RUNNING) — no reescribir, solo encender. 5. **Target V4**: introducir `TARGET_HBOXV4` (tercer target excluyente): hereda TODO el EPCT de `TARGET_EINARBOARD`; difiere en medidor/RGB→PCF8574 y en **no tocar PB12–15** (SPI2 del W5500). El Modo B es idéntico — cero cambios. 6. **hostsim**: nuevo escenario `relay`: alimentar 10 min de GGA sintéticas a 1 Hz por el stub, verificar (a) salida byte-exacta salvo líneas `$PHLG` completas en fronteras de línea, (b) ninguna inyección con <8 ms de hueco, (c) `setModoB(false)` al salir de RUNNING. Correr toda la red existente (ladder/sps/rtkloss/gga/phlg/charge/gauge) sin regresión. 7. **Docs**: CHANGELOG v3.1; LOGGING-SPEC §5b marcar «relay ON por default en EinarBoard»; PINOUT.md (la reescritura F7 pendiente absorbe esto). ## 4. Compuertas de banco (todas en una 1.0 con adaptador, antes de piloto) - **G1 — Integridad byte-exacta**: capturar simultáneo F9P TX2 y módem DIN (o segunda USB-UART en el nodo DIN) 10 min @1 Hz → diff = solo líneas `$PHLG` íntegras, ninguna GGA partida ni byte alterado. - **G2 — Sin regresión RTK/VRS**: con relevo insertado y caster real, la unidad alcanza y sostiene fix ≥4 (la GGA de cortesía al caster sigue siendo válida). - **G3 — Llegada al servidor**: repetir la prueba `$PUBX` pero con `$PHLG` real → Fase 1 la decodifica en el panel. - **G4 — Fail-safe**: forzar cuelgue (bucle infinito de prueba) → IWDG resetea → verificar con osciloscopio/continuidad que PB5 cae y el NC restituye ANTES del re-arranque; el flujo de posición nunca se detiene. - **G5 — Presupuesto de lazo**: con relevo activo, `TLM` sigue a 2 s, IWDG jamás dispara, sin `rxbad` creciente (los rings de 256 aguantan la ráfaga de época GNSS). ## 5. Riesgos / notas - **F4 es prerrequisito duro** — encender el relevo sin él corrompe el uplink GGA del caster VRS (10 ms bloqueados ≈ 115 bytes vs ring de 64). El orden de fases no es opcional. - El switchover de K1 muerde ~1 sentencia una vez por sesión — el backend ya tolera pérdida de época (heartbeat 1 Hz); documentado, no mitigado. - v2.3 en flota **no cambia**: unidades sin re-flashear siguen transmitiendo posición pura por el NC — el backend debe seguir aceptando flujo sin `$PHLG` indefinidamente. - La opción C (Modbus, Bloque M de V4) queda como **espejo por consulta** para el cliente — no sustituye la palabra canónica; su mapa de registros sigue gateado por firma del cliente. - No tocar: la doctrina de recuperación, el formato `$PHLG` (append-only), ni el NC de K1.