From 1d7dd633273bb7ebe594ea985d1c8c65e80e655a Mon Sep 17 00:00:00 2001 From: alro1965 Date: Mon, 18 May 2026 07:37:53 -0400 Subject: [PATCH] docs(sprint-1): proposed plan + firmware libraries research Two new documents to drive the Sprint 1 approval cycle when the user reviews in the morning. NO firmware code touched -- this is planning material only, per brief rule #1 "Antes de cada sprint, me presentas plan detallado y esperas mi OK. No improvises features." docs/sprint-1-plan.md - Sprint 1 objective (firmware boot + STANDBY + Modbus slave + NMEA 2000 consume of PGN 127250/127251 + watchdog). - 4 explicit technical decisions awaiting the user's go/no-go: 2.1 Framework: Arduino-as-ESP-IDF-component (recommended) 2.2 New libraries: NMEA2000-library, NMEA2000_esp32, eModbus (asked per brief rule #4 "No agregues dependencias sin preguntarme") 2.3 FreeRTOS core/priority mapping (PID isolated on Core 1) 2.4 Logging: ESP_LOG via UART0 - 7-phase breakdown over 3-4 weeks (mirrors brief's Sprint 1 scope). - Acceptance criteria, risks, and the inputs needed from the user before kickoff (hardware availability, schematic, NMEA 2000 bus). docs/firmware-libraries-research.md - Detailed rationale for each library choice with comparison tables. - Pinout / hardware references aligned with the existing firmware/ar_autopilot_v1/src/hal/pinout.h. - Draft platformio.ini outline (NOT yet written to the firmware directory -- needs user OK first). - All external sources cited for traceability. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/firmware-libraries-research.md | 246 +++++++++++++++++++++++++ docs/sprint-1-plan.md | 273 ++++++++++++++++++++++++++++ 2 files changed, 519 insertions(+) create mode 100644 docs/firmware-libraries-research.md create mode 100644 docs/sprint-1-plan.md diff --git a/docs/firmware-libraries-research.md b/docs/firmware-libraries-research.md new file mode 100644 index 0000000..e3ea0d3 --- /dev/null +++ b/docs/firmware-libraries-research.md @@ -0,0 +1,246 @@ +# Firmware libraries — research notes (Sprint 1 prep) + +Investigación realizada antes de planificar el Sprint 1 para elegir las +librerías del firmware ESP32. Documento de referencia — no normativo, solo +para que tengamos trazabilidad de por qué se eligió cada cosa. + +Fecha: 2026-05-18. + +--- + +## 1. NMEA 2000 stack + +### Decisión: `ttlappalainen/NMEA2000-library` + `ttlappalainen/NMEA2000_esp32` + +**Por qué es el estándar de facto:** + +- Es la stack NMEA 2000 más adoptada del ecosistema open-source para + microcontroladores. Casi todos los proyectos NMEA 2000 de aficionados + y muchos productos comerciales pequeños la usan. +- Cubre tanto consumo como publicación de PGN. +- Mantenida desde 2015, sigue activa. + +**Componentes:** + +| Repo | Rol | +|---|---| +| [`ttlappalainen/NMEA2000`](https://github.com/ttlappalainen/NMEA2000) | Stack core (parsing, PGNs, NMEA2000 class). Independiente de hardware | +| [`ttlappalainen/NMEA2000_esp32`](https://github.com/ttlappalainen/NMEA2000_esp32) | Driver CAN específico para ESP32. Incluye su propio low-level driver — no requiere libs adicionales | + +**Configuración por defecto:** + +- TX pin: `GPIO 16` +- RX pin: `GPIO 4` +- Override: definir `ESP32_CAN_TX_PIN` y `ESP32_CAN_RX_PIN` antes de + incluir los headers. + +**Framework support:** + +- ✅ Arduino framework +- ✅ ESP-IDF (añadido oficialmente en 2022) +- → Compatible con la decisión "Arduino-as-ESP-IDF-component" + +**Licencia:** MIT (compatible con producto comercial propietario). + +**Hardware externo necesario:** + +- Transceiver CAN físico: Microchip MCP2562, NXP TJA1051T, o similar. + Conecta los pines GPIO16/4 del ESP32 al bus diferencial CAN-H/CAN-L + del backbone NMEA 2000. El driver de la librería NO incluye el + hardware del transceiver — es un chip aparte en la tarjeta + AR-NMEA-IO. (Probablemente ya lo tienes en la v1.0 que diseñaste.) + +**PGN que vamos a consumir en Sprint 1:** + +| PGN | Nombre | Contenido relevante | +|---|---|---| +| 127250 | Vessel Heading | heading (rad), deviation, variation, reference (true/magnetic) | +| 127251 | Rate of Turn | rate (rad/s) | + +**Snippet de referencia para Sprint 1 (no es código final):** + +```cpp +#include // selects ESP32 driver automatically +#include + +void HandleHeading(const tN2kMsg &N2kMsg) { + unsigned char SID; + double Heading, Deviation, Variation; + tN2kHeadingReference HeadingReference; + if (ParseN2kHeading(N2kMsg, SID, Heading, Deviation, Variation, HeadingReference)) { + // Heading in radians; convert to degrees for our use + gLastHeadingDeg = Heading * 180.0 / M_PI; + gLastHeadingTimestamp = millis(); + } +} + +void setup() { + NMEA2000.SetN2kCANMsgBufSize(8); + NMEA2000.SetMsgHandler(HandleHeading); // registers for ALL PGNs; + // we filter inside the handler + NMEA2000.Open(); +} + +void loop() { + NMEA2000.ParseMessages(); // call frequently from a dedicated task +} +``` + +Esto es **orientativo**. En el sprint lo encapsulamos en una tarea +FreeRTOS dedicada (`nmea2000_rx_task` en Core 0). + +--- + +## 2. Modbus RTU server (slave) + +### Decisión: `eModbus/eModbus` v1.7.4 + +**Comparación rápida:** + +| Lib | Slave RTU | Async/Tasks FreeRTOS | Mantenimiento | Veredicto | +|---|---|---|---|---| +| `eModbus/eModbus` v1.7.4 (2025-06) | ✅ | ✅ tasks separados, no bloquea | Activo | **Elegida** | +| `emelianov/modbus-esp8266` (a.k.a. `modbus-esp32`) | ✅ | Parcial | Activo | Alternativa válida | +| `MrMoses1911/ModbusRTU_ESP32` | ✅ | No documentado | Menor adopción | Descartada | +| `ArduinoModbus` (oficial Arduino) | ✅ | No | Mantenida pero feature-pobre | Descartada | +| `bertmelis/esp32ModbusRTU` | ❌ solo cliente | ✅ | Activo | No aplica (cliente, no slave) | + +**Por qué eModbus:** + +- Único que documenta explícitamente "Modbus communication is done in + separate tasks" y API "non blocking / asynchronous" → el lazo PID no + se bloquea jamás por tráfico Modbus. +- Soporta RTU, ASCII y TCP — si en el futuro queremos pasar a Modbus + TCP por Ethernet/WiFi, es la misma librería. +- Versión estable reciente (junio 2025). +- Licencia MIT. + +**Punto a verificar en Fase 1.3:** + +⚠️ El README de eModbus no confirma explícitamente que la librería +maneje sola la línea **DE/RE** (Driver Enable / Receiver Enable) del +transceiver RS-485 MAX485 / MAX3485. Si no lo hace, escribimos un +wrapper de ~30 líneas que toggle el `PIN_RS485_DE` del pinout antes +y después de cada respuesta. + +**Framework support:** + +- ✅ Arduino framework (confirmado) +- ❓ ESP-IDF nativo (no confirmado explícitamente; lo verifico al + integrarlo) + +Si ESP-IDF nativo no está soportado: igual funciona desde el componente +Arduino dentro de un proyecto ESP-IDF (eso es el patrón +Arduino-as-ESP-IDF-component). + +**Mapa de registros propuesto** (a refinar en Sprint 1): + +| Tipo Modbus | Rango | Contenido | +|---|---|---| +| Holding (40001-) | 1-50 | Setpoints: heading_deseado, modo, comandos de operador | +| Input (30001-) | 1-50 | Estado: heading_actual, rudder_angle_actual, modo_activo, alarmas activas | +| Discrete (10001-) | 1-32 | Bits: engaged, alarms (uno por tipo), DI raw | +| Coils (00001-) | 1-32 | Comandos digitales: engage_request, disengage_request, alarm_ack | + +El mapa exacto se finaliza al inicio de Fase 1.3 y se genera en +single-source-of-truth desde un YAML. + +--- + +## 3. Framework: ESP-IDF vs Arduino vs híbrido + +### Decisión: Arduino-as-ESP-IDF-component + +**Resumen de la discusión:** + +| Aspecto | Arduino solo | ESP-IDF solo | Arduino-as-ESP-IDF-component | +|---|---|---|---| +| Manejo de FreeRTOS | Abstraído a single-thread | Nativo, control total | **Nativo (ESP-IDF), Arduino se compone encima** | +| Disponibilidad de libs NMEA2000/Modbus | ✅ inmediata | ⚠️ requiere wrap | ✅ inmediata | +| OTA con rollback automático | ❌ no | ✅ sí | ✅ sí | +| Secure boot + flash encryption | ⚠️ limitado | ✅ sí | ✅ sí | +| Determinismo PID real-time | ❌ unsuitable | ✅ excelente | ✅ excelente | +| Production-grade (TWDT, panic handlers, partitions, NVS) | Limitado | Completo | Completo | +| Complejidad inicial `platformio.ini` | Baja | Media | Media-alta (10 min de setup) | +| Curva de aprendizaje | Cortísima | Media | Media | + +**Por qué para AR-Autopilot:** + +El brief es taxativo: + +> **Regla #11:** El PID corre en firmware ESP32, no en el display. Esto +> es no-negociable: latencia determinística, seguridad ante caída del +> display, eficiencia. +> +> **Sección 6 (frecuencias):** Lazo PID interno (timón a posición): 50 +> Hz (20 ms) **Determinístico, sin jitter**. + +"Sin jitter" en 50 Hz exige FreeRTOS real con prioridades correctas, no +el modelo abstraído de Arduino. Y al mismo tiempo no queremos perder 2 +semanas reescribiendo NMEA2000_esp32 ni eModbus — son cientos de horas +de trabajo de la comunidad. + +**Solución oficial Espressif:** [Arduino as an ESP-IDF component](https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html). +Permite usar libs Arduino dentro de un proyecto ESP-IDF "puro". Es +exactamente lo que necesitamos. + +**Configuración en PlatformIO (resumen):** + +```ini +[env:esp32-arautopilot] +platform = espressif32@~6.7.0 +board = esp32dev ; ajustar al chip real (ESP32-DOWD) +framework = arduino, espidf ; ← aquí está el truco + +; Pinneamos versión del core Arduino para reproducibilidad +platform_packages = + framework-arduinoespressif32@~3.20017.0 + +lib_deps = + ttlappalainen/NMEA2000-library + ttlappalainen/NMEA2000_esp32 + https://github.com/eModbus/eModbus.git#v1.7.4.stable + +build_flags = + -DCORE_DEBUG_LEVEL=3 + -DCONFIG_FREERTOS_HZ=1000 + ; ... más flags al hacer el sprint + +monitor_speed = 115200 +``` + +**Riesgo conocido:** el modo dual `framework = arduino, espidf` a veces +se rompe entre versiones de PlatformIO. Mitigación: pinneamos versión +exacta del `platform` y de `framework-arduinoespressif32`. + +--- + +## 4. Hardware externo / transceivers (referencia) + +Pinout `firmware/ar_autopilot_v1/src/hal/pinout.h` ya declara los pines +lógicos. Para que el sistema físicamente funcione, la tarjeta +AR-NMEA-IO debe tener: + +| Función | Chip típico | Pin ESP32 | +|---|---|---| +| Transceiver CAN (NMEA 2000) | MCP2562, TJA1051T | GPIO16 (TX), GPIO4 (RX) — overridable | +| Transceiver RS-485 (Modbus) | MAX485, MAX3485, SP485E | GPIO21 (TX), GPIO22 (RX), GPIO23 (DE) | +| Acondicionamiento AI1 (rudder sensor) | Op-amp + divisor / módulo 4-20 mA loop | GPIO36 (ADC1_CH0) | +| Driver de potencia para DO1/DO2 (bomba) | Relé SSR o transistor + flyback | GPIO13, GPIO12 | + +Asumo que tu tarjeta v1.0 ya los lleva (es la misma usada en +VMS-Sailor). Si hay alguna diferencia, lo cuadramos al inicio de +Fase 1.2 con tu esquemático. + +--- + +## 5. Fuentes consultadas + +- [NMEA2000_esp32 — GitHub](https://github.com/ttlappalainen/NMEA2000_esp32) +- [NMEA2000 library — GitHub](https://github.com/ttlappalainen/NMEA2000) +- [NMEA2000_esp32 — PlatformIO Registry](https://registry.platformio.org/libraries/ttlappalainen/NMEA2000_esp32) +- [eModbus — GitHub](https://github.com/eModbus/eModbus) +- [Arduino as ESP-IDF component — Espressif docs](https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html) +- [ESP-IDF vs Arduino Framework for ESP32 — Hubble Network Community](https://hubble.com/community/comparisons/esp-idf-vs-arduino-framework-for-esp32-when-to-switch-and-why/) +- [ESP-IDF vs. Arduino for ESP32-S3: The Engineering Pivot to Production — Hoomanely Tech](https://tech.hoomanely.com/esp-idf-vs-arduino-for-esp32-s3-the-engineering-pivot-to-production/) +- [Modbus RTU slave (RS485) for ESP32 on Arduino platform — PlatformIO Community](https://community.platformio.org/t/modbus-rtu-slave-rs485-for-esp32-on-arduino-platform/16196) diff --git a/docs/sprint-1-plan.md b/docs/sprint-1-plan.md new file mode 100644 index 0000000..453b4ce --- /dev/null +++ b/docs/sprint-1-plan.md @@ -0,0 +1,273 @@ +# Sprint 1 — Firmware ESP32 base — Plan propuesto + +> **Status:** propuesto, esperando tu OK. +> **Brief reference:** sección 12, Sprint 1. +> **Duración estimada brief:** 3-4 semanas. +> **Pre-requisito:** Sprint 0 aprobado (tag `sprint-0-approved`). + +Este documento es **mi propuesta**. Antes de tocar firmware necesito tu OK +explícito (regla de oro #1 del brief). Léelo, corrige lo que no te encaje, +y luego me das luz verde o me pides cambios. + +--- + +## 1. Objetivo del sprint + +Levantar la **base** del firmware del controlador AR-Autopilot sobre la +tarjeta AR-NMEA-IO v1.0. Al final del sprint la tarjeta debe poder: + +- Arrancar en modo **STANDBY** (timón manual, sin control del piloto). +- Leer los 4 AI + 5 DI + 1 RPM, escribir los 10 DO declarados en + [`firmware/ar_autopilot_v1/src/hal/pinout.h`](../firmware/ar_autopilot_v1/src/hal/pinout.h). +- Hablar **Modbus RTU como esclavo** con el display dedicado (todavía + inexistente en código — usamos un cliente de prueba en PC). +- Consumir del bus **NMEA 2000** los PGN mínimos: **127250** (Heading) + y **127251** (Rate of Turn). Solo logging, no control. +- Reaccionar al pulsador físico de **disengage** (DI1): no hay nada + enganchado en Sprint 1, pero el evento se registra y se ven los + bits cambiar en Modbus. + +Lo que **NO** hace este sprint (importante — ver brief sección 12): +- No corre PID real. Hay un "stub" del PID que devuelve siempre 0. +- No publica al backbone NMEA 2000 (eso es Sprint 6). +- No tiene seguridad funcional completa (Sprint 6 también). +- No conoce los modos HEADING_HOLD / TRUE_COURSE / TRACK_KEEPING + más allá de exponerlos como enum por Modbus. + +--- + +## 2. Decisiones técnicas que pido aprobar + +Estas son las preguntas que necesito que me respondas **antes** de empezar. + +### Decisión 2.1 — Framework: ESP-IDF puro vs Arduino-as-ESP-IDF-component + +| Opción | Pros | Cons | Veredicto | +|---|---|---|---| +| **Arduino framework** puro | Más fácil. Las libs NMEA2000_esp32 y eModbus son nativas Arduino. Curva corta | Arduino abstrae FreeRTOS en un modelo single-threaded. Mal para nuestras 4 tareas concurrentes con prioridades distintas. Menos control sobre OTA con rollback, secure boot, deterministic timing | **No** para producto safety-critical | +| **ESP-IDF nativo** | Control total de FreeRTOS, deterministic real-time, OTA con rollback automático, secure boot/flash encryption, mejor para producción comercial | Las libs Modbus/NMEA2000 maduras son Arduino-first; hay que wrappear o reescribir partes | Limpio pero costoso | +| **Arduino-as-ESP-IDF-component** ⭐ | Combina lo mejor: ESP-IDF maneja el sistema (tareas, prioridades, OTA, security), Arduino se usa solo para las libs maduras (NMEA2000_esp32, eModbus). Patrón oficial Espressif | Configuración inicial un poco más complicada en `platformio.ini` | **Recomendado** | + +**Mi recomendación:** opción ⭐. El brief (regla #11) dice "El PID corre +en firmware ESP32, no en el display. Esto es no-negociable: latencia +determinística, seguridad ante caída del display, eficiencia." Eso +implica ESP-IDF/FreeRTOS real, no Arduino single-thread. Pero no +queremos perder semanas reescribiendo NMEA2000_esp32 ni eModbus. + +**Tu pregunta:** ¿Apruebas opción ⭐, o prefieres una de las otras dos? + +### Decisión 2.2 — Librerías nuevas a añadir a `platformio.ini` + +Por la regla #4 del brief ("No agregues dependencias sin preguntarme"), +te las pido por adelantado: + +| Lib | Versión / fuente | Por qué | Licencia | +|---|---|---|---| +| `ttlappalainen/NMEA2000-library` | latest stable, PIO registry | Base de toda la pila NMEA 2000. Sin esto no leemos heading | MIT | +| `ttlappalainen/NMEA2000_esp32` | latest stable, PIO registry | Driver CAN específico para ESP32 (TX=GPIO16, RX=GPIO4 por defecto). Incluye su propio driver CAN, no hace falta más | MIT | +| `eModbus/eModbus` | v1.7.4 (Jun 2025) | Modbus RTU server (slave) con tasks FreeRTOS asíncronos, no bloquea el lazo principal | MIT | + +Todas MIT, todas maduras, todas mantenidas. Las añado a `platformio.ini` +con versiones pinneadas (no `latest`) para reproducibilidad. + +**Tu pregunta:** ¿Las apruebo así o quieres otras alternativas? + +### Decisión 2.3 — Asignación de núcleos y prioridades FreeRTOS + +ESP32-DOWD tiene 2 núcleos. Propuesta de mapeo: + +| Núcleo | Tarea | Prioridad | Frecuencia | +|---|---|---|---| +| 1 (Core 1 / APP_CPU) | `pid_inner_loop_task` (Sprint 2; stub en Sprint 1) | 24 (alta) | 50 Hz, hard real-time | +| 1 | `rudder_sensor_task` (lectura AI1, mediana 5 muestras) | 23 | 100 Hz | +| 1 | `safety_monitor_task` (limit switches, watchdog feed) | 22 | 50 Hz | +| 0 (Core 0 / PRO_CPU) | `nmea2000_rx_task` (CAN polling, parsing PGN) | 15 | event-driven | +| 0 | `modbus_slave_task` (gestiona eModbus internals) | 14 | event-driven | +| 0 | `health_reporter_task` (uptime, RAM libre, IDFs varios) | 5 | 1 Hz | + +**Aislamiento explícito:** el lazo PID **nunca** comparte núcleo con +comunicaciones. Si el bus NMEA 2000 satura, el control no se ve afectado. + +**Tu pregunta:** ¿Mapeo OK o quieres modificar? + +### Decisión 2.4 — Estrategia de logging en firmware + +Para depurar sin display, necesito ver qué pasa adentro de la ESP32. +Opciones: + +1. **Solo `ESP_LOG` por UART0** (consola serie por USB). Estándar + ESP-IDF. Cero overhead cuando se desactiva. (**Recomendado**) +2. **Log circular en RAM + dump por Modbus a petición**. Más sofisticado, + útil para post-mortem en campo, pero complejidad innecesaria en + Sprint 1. +3. **Log a SD card.** La tarjeta AR-NMEA-IO no tiene SD según el brief; + descartado. + +**Mi recomendación:** opción 1 para Sprint 1. La 2 se podría añadir en +Sprint 6 (Seguridad y alarmas) si te parece útil. + +**Tu pregunta:** ¿Opción 1? + +--- + +## 3. Entregables del sprint + +``` +firmware/ar_autopilot_v1/ +├── platformio.ini # nuevo: configurado con framework + libs aprobadas +├── sdkconfig.defaults # nuevo: tuning ESP-IDF (FreeRTOS tick, etc.) +├── src/ +│ ├── main.cpp # nuevo: arranca tareas FreeRTOS, entra en loop tonto +│ ├── pid/ +│ │ ├── pid_outer.cpp # stub: devuelve setpoint=0, log +│ │ └── pid_inner.cpp # stub: devuelve PWM=0, log +│ ├── modes/ +│ │ └── standby.cpp # nuevo: única lógica de modo en Sprint 1 +│ ├── safety/ +│ │ ├── watchdog.cpp # nuevo: TWDT habilitado a 2 s +│ │ └── override_handler.cpp # nuevo: DI1 → siempre fuerza STANDBY +│ ├── protocols/ +│ │ ├── modbus_slave.cpp # nuevo: servidor Modbus mínimo +│ │ ├── modbus_registers.h # nuevo: mapa de registros expuestos +│ │ └── nmea2000_consumer.cpp # nuevo: lee 127250 + 127251, loguea +│ ├── hal/ +│ │ ├── pinout.h # ya existe (Sprint 0) +│ │ ├── rudder_sensor.cpp # nuevo: lee AI1 con filtro mediana +│ │ ├── rudder_actuator.cpp # nuevo: stub que escribe DO1/DO2 pero comando=0 +│ │ └── di_do.cpp # nuevo: helpers para todos los DI/DO +│ └── system/ +│ └── health_reporter.cpp # nuevo: log periódico de salud +├── include/ # headers públicos +├── test/ +│ ├── test_modbus_registers/ # Unity: validar mapa de registros +│ ├── test_filters/ # Unity: mediana de 5 muestras +│ └── test_pinout/ # Unity: que los pines no se pisen +└── tools/ + └── modbus_client_test.py # script Python para probar el slave desde PC +``` + +Y **en el lado Python:** + +``` +arautopilot/ +├── core/ +│ └── modbus_registers.py # nuevo: mismo mapa que firmware, generado / espejo +├── shared/ +│ └── modbus_register_map.py # constantes single-source-of-truth para ambos lados +└── tests/ + └── test_modbus_register_map.py # nuevo: sanity check sobre el mapa +``` + +> **Importante:** el mapa de registros Modbus tiene que ser +> **single-source-of-truth**. Mi propuesta: definirlo en YAML en +> `firmware/ar_autopilot_v1/modbus_registers.yaml` y generar tanto +> `modbus_registers.h` (C++) como `modbus_registers.py` (Python) con +> un script en `tools/`. Así no se desincronizan jamás. Te lo presento +> mejor cuando arranquemos. + +--- + +## 4. Plan de fases dentro del sprint + +| Fase | Días | Objetivo | Demo al final | +|---|---|---|---| +| 1.1 | 1-2 | `platformio.ini` + esqueleto FreeRTOS + LED de vida | Tarjeta arranca, parpadea LED, log por UART0 | +| 1.2 | 3-4 | HAL: lectura de los 9 inputs, escritura de los 10 outputs | Script Python por UART manda comandos manuales, se ven los LEDs/relés | +| 1.3 | 5-7 | Modbus RTU slave + mapa de registros generado | Cliente Modbus desde PC lee estado, escribe setpoint dummy | +| 1.4 | 8-10 | NMEA 2000 consumer: PGN 127250, 127251 | Conectada al simulador NMEA 2000 (o a tu AR-ECDIS si está), aparecen heading y ROT en los registros Modbus | +| 1.5 | 11-13 | Watchdog + DI1 override + modo STANDBY explícito | DI1 pulsador funciona, watchdog reinicia si se cuelga firmware | +| 1.6 | 14-15 | Tests Unity + pruebas integradas + documentación | `pio test` verde, `docs/firmware.md` con instrucciones de flash | +| 1.7 | 16-20 | Buffer + integración con `arautopilot.studio.simulator` (vessel + sensor sim) | Demo end-to-end: simulador → ESP32 → cliente Modbus en PC | + +--- + +## 5. Criterios de aceptación del Sprint 1 + +(Equivalente a lo que hicimos en Sprint 0) + +1. ✅ `pio run` compila sin warnings en modo `release` y `debug`. +2. ✅ `pio test` pasa todos los tests Unity del firmware. +3. ✅ `pytest` sigue verde en el lado Python (80+ tests existentes + nuevos del mapa Modbus). +4. ✅ `bash scripts/dev.sh check` (lint+typecheck+test Python) verde. +5. ✅ Demo manual documentada en `docs/sprint-1-demo.md`: + - Conexión por USB → terminal serie a 115200 baudios → ver logs + - Cliente Modbus Python (`tools/modbus_client_test.py`) → lee + estado y bits de I/O + - Simulador NMEA 2000 → publica PGN 127250 con heading 045°, + el firmware lo loguea y lo expone en el registro Modbus +6. ✅ Watchdog: si descomento un `vTaskDelay(10000)` en `pid_outer_loop_task`, + el firmware se resetea solo y vuelve a STANDBY con alarma loggeada. +7. ✅ Tag `sprint-1` en `main`. + +--- + +## 6. Riesgos identificados + +| Riesgo | Mitigación | +|---|---| +| **Tarjeta AR-NMEA-IO física no disponible** durante el sprint | Sprint 1 se puede empezar sin hardware: PlatformIO permite compilación + tests sin board. Los tests Unity corren en host. Las pruebas integradas se postergan hasta tener tarjeta. **Tu input:** ¿la tarjeta está físicamente disponible o trabajo "ciego" hasta tenerla? | +| **GPIOs del pinout.h no coinciden con la tarjeta real** | El header del Sprint 0 dice claramente "PLACEHOLDERS aligned with the AR-NMEA-IO schematic. They will be confirmed and locked in Sprint 1 against the real board revision." Antes de Fase 1.2 te pido el esquemático o el manual de la tarjeta | +| **El bus NMEA 2000 del entorno de pruebas no está disponible** | Plan B: simulador software (`tools/n2k_simulator.py`) que inyecte PGN 127250 sintéticos a la ESP32 vía un transceiver USB-CAN (Canable, Yacht Devices YDNU-02) | +| **eModbus no maneja DE/RE automáticamente** | Lo verifico en Fase 1.3. Si no, escribo un wrapper de 30 líneas que use el `PIN_RS485_DE` del pinout | +| **Conflicto de IRQ entre CAN driver y UART de logging** | Asignamos a núcleos separados (decisión 2.3). El log va por UART0 que es PRO_CPU; CAN driver corre interrupts en APP_CPU sin solapamiento | +| **Arduino-as-ESP-IDF-component se rompe entre versiones** | Pinneamos versión exacta de `framework-arduinoespressif32` en `platformio.ini`, no `latest` | + +--- + +## 7. Lo que necesito de ti antes de empezar + +1. **Apruebas/modificas las 4 decisiones técnicas** de la sección 2. +2. **Tag de Sprint 0 aprobado:** + ```powershell + git tag -a sprint-0-approved -m "Sprint 0 reviewed and approved" + ``` +3. **Confirmas disponibilidad del hardware** (tarjeta AR-NMEA-IO física): + - Si la tienes: trabajamos contra ella desde Fase 1.2. + - Si no: trabajamos en bench (todos los tests Unity + simulador) y + la integración real se pospone para cuando llegue. +4. **Esquemático o pinout real** de la AR-NMEA-IO v1.0, para confirmar + los GPIOs placeholder de `pinout.h`. Si está en repositorio + separado, dame la ruta. Si está en papel, una foto sirve. +5. **Acceso a un bus NMEA 2000 funcional** para pruebas (o lo hacemos + con un simulador software y un adaptador USB-CAN). + +--- + +## 8. Plan de comunicación durante el sprint + +Mismo modelo que Sprint 0 (resultó bien): + +- **Inicio de cada fase**: te resumo qué voy a hacer y espero OK. +- **Final de cada fase**: commit + breve resumen + demo de qué se + puede tocar/ver. +- **Si encuentro algo no previsto** (riesgo nuevo, decisión técnica + con trade-off real), paro y te pregunto antes de improvisar. +- **Tests siempre verdes antes de cerrar fase**. Si rompo algo, lo + arreglo antes de pedirte review. + +--- + +## 9. Estado actual del repo (al momento de escribir este plan) + +``` +$ git log --oneline +8d4a698 polish(sprint-0): clean code per ruff + mypy strict +700756c sprint-0: foundations -- data model, seed library, tests, demo +``` + +(Y el commit del runner dev que viene a continuación de este plan.) + +Working tree limpio, 80/80 tests verde, ruff limpio, mypy strict limpio. + +--- + +## 10. Fin + +Cuando termines de leer: + +- Si todo bien: tag `sprint-0-approved` + "dale Sprint 1" → arranco + por Fase 1.1. +- Si hay que cambiar algo: dime qué decisión te encaja distinto y + reescribo el plan. No empiezo nada hasta que coincidamos. + +— Claude