sprint-0: fundaciones VMS-Sailor
Sprint 0 completo del producto VMS-Sailor (Vessel Management System integrado para buques 30-40m). Brief de referencia en VMS_Sailor_v2_Parte_*.md (intacto). Core (vmssailor.core, 95.17% coverage, 99 tests verde): - ShipCoord: sistema naval x_pp/y_cl/z_bl frozen - Vessel, Deck, Bulkhead - Equipment, EquipmentModel, Sensor, EquipmentSpec - Tag, AlarmConfig, TagBinding, Scaling - CardInstance, Bus, Topology con validacion 21 puntos I/O AR-NMEA-IO-v1.0 - Alarm, PermissiveRule, Condition - Project agregado raiz con validacion cross-entity - Persistencia portable .vmsproj (SQLite) con roundtrip verificable Biblioteca curada seed (vmssailor.library): - systems_catalog.json completo (catalogo maestro Parte 1 sec 7) - 2 vessels: Sunseeker 76, Ferretti 850 - 2 motores: MTU 12V 2000 M96, Volvo D13-900 - 1 genset: Northern Lights M65C13 - yacht_motor_planeo.yaml (reglas heuristicas) - TODO marcado data_source=seed_estimate - requiere validacion datasheets Tools: - vms-validate-library: CLI valida biblioteca completa - vms-generate-test-project: CLI demo + verificacion roundtrip persistencia Design System + 8 mockups HTML estaticos: - docs/design_system.md (paleta Deep Ocean, gradientes, typography, motion) - docs/brand/ (logo + variantes SVG) - docs/mockups/splash, studio_main, runtime_overview, runtime_mimic_fuel (P&ID animado), runtime_alarms, runtime_trim (panel estrella con horizonte artificial), mobile_overview, mobile_trim - docs/mockups/index.html (galeria) Firmware (Sprint 12+ implementacion): - firmware/ar_nmea_io_v1/src/config/pinout.h con macros GPIO Decisiones autonomas documentadas en docs/decisions_sprint0.md. Stack: Python 3.11 + uv + Pydantic v2 + SQLite stdlib + hatchling + pytest 9 + ruff + mypy. Sin PySide6, FastAPI, Flutter ni firmware funcional (entran en sprints siguientes). Criterio de aceptacion Sprint 0: cumplido. - uv sync: OK - pytest: 99/99 verde - cov vmssailor.core: 95.17% (objetivo >=80%) - ruff: clean - vms-validate-library: OK - vms-generate-test-project: INTEGRIDAD OK Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,401 @@
|
||||
# VMS-Sailor · Brief para Claude Code · Parte 4 de 6
|
||||
|
||||
## Hardware AR-NMEA-IO-v1.0 + Firmware ESP32
|
||||
|
||||
> Esta parte detalla la tarjeta de I/O y su firmware. Asume que ya leíste Partes 1-3.
|
||||
|
||||
---
|
||||
|
||||
## 1. Descripción física de la tarjeta
|
||||
|
||||
La tarjeta `AR-NMEA-IO-v1.0` es hardware diseñado por Álvaro, una sola SKU física que se replica idénticamente para todos los proyectos. Lo que varía entre proyectos es la **configuración** descargada desde el VMS, no el hardware.
|
||||
|
||||
### Capacidades fijas
|
||||
|
||||
| Recurso | Cantidad | Detalle |
|
||||
|---|---|---|
|
||||
| Salidas digitales (DO) | 10 | MOSFET IRLML6344TRPBF, opto PC817, diodo flyback SS14, 30V/5A |
|
||||
| Entradas digitales (DI) | 5 | Optoacopladas PC817, contacto o 24VDC, resistencia limitadora 4.7kΩ |
|
||||
| Entrada frecuencia (RPM) | 1 | Optoacoplada, pickup magnético / tacómetro / inductivo |
|
||||
| Entradas analógicas (AI) | 4 | Con divisores y filtros V3061, ADC del ESP32 (12 bits + oversampling) |
|
||||
| Microcontrolador | 1 | ESP32-DOWD (módulo ESP32 DevKit china) |
|
||||
| Bus serie | RS485 | Transceiver SN65HVD1781 (TI), ESD ±16kV, conector RJ45 |
|
||||
| Bus CAN | NMEA 2000 | Transceiver MCP2562T-E_MF (Microchip), ESD SP0502, conector estándar |
|
||||
| WiFi | Integrado | Del ESP32, usado para OTA y MQTT opcional |
|
||||
| USB | 1 | Para programación inicial (CH340 o CP2102 del DevKit) |
|
||||
| Alimentación | 12 VDC | Conector Phoenix de 2 vías, TVS SMET24A, ferrita B2U-1000P |
|
||||
| Reguladores | 2× MP2338 | Bucks 12V→5V y 5V→3.3V |
|
||||
|
||||
**Aislamiento galvánico:** TODOS los I/O (DI, DO, RPM) están aislados con optoacopladores PC817. Un fallo eléctrico en un sensor o actuador NO daña el ESP32.
|
||||
|
||||
**Total: 21 puntos I/O por tarjeta.**
|
||||
|
||||
### GPIOs del ESP32 usados
|
||||
|
||||
(Información extraída del esquemático real que me pasó Álvaro:)
|
||||
|
||||
- **DO**: GPIO15, GPIO5, GPIO17, GPIO16 (cuidado: este es Rx2), GPIO18, GPIO22, GPIO25, GPIO26, GPIO27, GPIO34 (cuidado: solo entrada normalmente)
|
||||
- **DI**: GPIO13, GPIO2, GPIO0, GPIO4, GPIO14 (algunos son boot-strap)
|
||||
- **AI**: GPIO33 (BAT), GPIO34 (SPARE2), GPIO32 (WATER), GPIO35 (OILP)
|
||||
- **RPM**: GPIO15 o similar (verificar en esquemático)
|
||||
- **RS485**: GPIO04 (DE/RE), pines TX/RX UART
|
||||
- **CAN**: GPIO21 (TX), GPIO23 (RX), GPIO22 (STBY)
|
||||
|
||||
**Nota para Claude Code:** Los pines exactos los verificaremos en el firmware. Algunos GPIO del ESP32 tienen restricciones (strapping, solo entrada, conflictos con boot). El firmware debe respetar las asignaciones reales del schematic.
|
||||
|
||||
---
|
||||
|
||||
## 2. Filosofía del firmware
|
||||
|
||||
### Universal y autoconfigurable
|
||||
|
||||
**Una sola SKU física = un solo firmware base = una sola variante de tarjeta.**
|
||||
|
||||
La tarjeta sale de fábrica con firmware idéntico. Su rol y configuración se descargan del VMS al conectarse al bus. Patrón **"plug-and-produce"** estándar industrial moderno (similar a Beckhoff EtherCAT, Wago I/O System).
|
||||
|
||||
### Lo que NO se programa por hardware
|
||||
|
||||
- Qué hace cada DO (eso lo decide el VMS)
|
||||
- Qué lee cada DI (eso lo decide el VMS)
|
||||
- Qué tipo de sensor entra en cada AI (eso lo decide el VMS, dentro de los rangos físicos que el divisor permite)
|
||||
- Dirección Modbus de la tarjeta (la asigna el VMS, junto con confirmación del dipswitch físico)
|
||||
- Filtros, alarmas locales, permissives locales (todo desde el VMS)
|
||||
|
||||
### Lo que SÍ va fijo en el firmware
|
||||
|
||||
- Pinout: qué GPIO corresponde a qué función (DO1=GPIO15, AI1=GPIO33, etc.)
|
||||
- Stack de protocolos: Modbus RTU, NMEA 2000, J1939, MQTT (siempre disponibles, se activan según config)
|
||||
- Stack de WiFi y OTA
|
||||
- Boot sequence con discovery broadcast
|
||||
- Cliente OTA seguro
|
||||
- Watchdog y recuperación ante fallos
|
||||
- Buffer offline (60s en flash)
|
||||
|
||||
---
|
||||
|
||||
## 3. Estructura del firmware
|
||||
|
||||
```
|
||||
firmware/ar_nmea_io_v1/
|
||||
├── platformio.ini # PlatformIO o ESP-IDF
|
||||
├── README.md
|
||||
├── src/
|
||||
│ ├── main.cpp # Entry point + boot sequence
|
||||
│ ├── config/
|
||||
│ │ ├── pinout.h # Definición física de GPIOs
|
||||
│ │ ├── card_identity.cpp # Serial + HWID de cada tarjeta
|
||||
│ │ ├── dipswitch_reader.cpp # Lee dipswitches físicos
|
||||
│ │ └── runtime_config.cpp # Aplica config descargada del VMS
|
||||
│ │
|
||||
│ ├── channels/ # Manejo de los 21 puntos I/O
|
||||
│ │ ├── do_handler.cpp # 10 DO
|
||||
│ │ ├── di_handler.cpp # 5 DI
|
||||
│ │ ├── rpm_handler.cpp # 1 entrada frecuencia
|
||||
│ │ └── ai_handler.cpp # 4 AI con oversampling
|
||||
│ │
|
||||
│ ├── filters/ # Procesamiento local
|
||||
│ │ ├── moving_average.cpp
|
||||
│ │ ├── median.cpp
|
||||
│ │ ├── deadband.cpp
|
||||
│ │ └── rate_limit.cpp
|
||||
│ │
|
||||
│ ├── protocols/
|
||||
│ │ ├── modbus_slave.cpp # Modbus RTU esclava
|
||||
│ │ ├── modbus_master.cpp # Modbus RTU maestra (si así se configura)
|
||||
│ │ ├── nmea2000_node.cpp # Publica y suscribe PGNs
|
||||
│ │ ├── j1939_listener.cpp # Escucha J1939 (motores)
|
||||
│ │ ├── mqtt_client.cpp # MQTT WiFi (opcional)
|
||||
│ │ └── discovery.cpp # Discovery broadcast al arrancar
|
||||
│ │
|
||||
│ ├── local_logic/ # Lógica local en la tarjeta
|
||||
│ │ ├── local_alarms.cpp # Alarmas locales con umbrales
|
||||
│ │ ├── local_permissives.cpp # Permissives locales críticos (E-stop)
|
||||
│ │ └── safety_outputs.cpp # Salidas de seguridad (cortar relé si E-stop)
|
||||
│ │
|
||||
│ ├── storage/
|
||||
│ │ ├── nvs_config.cpp # Configuración en NVS (flash)
|
||||
│ │ └── offline_buffer.cpp # Buffer 60s en flash si bus cae
|
||||
│ │
|
||||
│ ├── system/
|
||||
│ │ ├── watchdog.cpp
|
||||
│ │ ├── ota_client.cpp # Cliente OTA seguro
|
||||
│ │ ├── health_reporter.cpp # Reporta estado al maestro
|
||||
│ │ └── reboot_handler.cpp
|
||||
│ │
|
||||
│ └── utils/
|
||||
│ ├── crc.cpp
|
||||
│ ├── logger.cpp
|
||||
│ └── time_sync.cpp
|
||||
│
|
||||
├── common/ # Librerías compartidas con futuros firmwares
|
||||
│ ├── modbus_lib/
|
||||
│ ├── nmea2000_lib/
|
||||
│ └── ota_lib/
|
||||
│
|
||||
└── test/ # Tests del firmware (PlatformIO test framework)
|
||||
├── test_pinout.cpp
|
||||
├── test_modbus.cpp
|
||||
├── test_filters.cpp
|
||||
└── test_discovery.cpp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Boot sequence (plug-and-produce)
|
||||
|
||||
Cuando la tarjeta arranca por primera vez (o después de un reset):
|
||||
|
||||
### Paso 1 — Identidad
|
||||
|
||||
- Lee su serial number del NVS (asignado en fábrica)
|
||||
- Lee los dipswitches físicos para obtener su "slot number" en el bus (1-16)
|
||||
- Lee firmware version
|
||||
|
||||
### Paso 2 — Inicialización de hardware
|
||||
|
||||
- Inicializa GPIOs según `pinout.h`
|
||||
- Pone todos los DO en estado seguro (apagados)
|
||||
- Inicializa transceivers RS485 y CAN
|
||||
- Inicializa ADC para AI
|
||||
- Inicia WiFi en modo cliente (si hay credenciales guardadas) o AP (si no)
|
||||
|
||||
### Paso 3 — Discovery broadcast
|
||||
|
||||
Por RS485 envía un mensaje broadcast Modbus:
|
||||
```
|
||||
[broadcast addr 0]
|
||||
function: 65 (custom)
|
||||
payload: {
|
||||
serial: "ARC-2026-00153",
|
||||
hw_version: "1.0",
|
||||
fw_version: "1.4.2",
|
||||
dipswitch_slot: 5,
|
||||
capabilities: { do:10, di:5, rpm:1, ai:4 }
|
||||
}
|
||||
```
|
||||
|
||||
### Paso 4 — Espera config del maestro
|
||||
|
||||
- Si el maestro (VMS) responde con `CONFIG_PUSH` → aplica la configuración recibida
|
||||
- Si no responde en 10s → modo "huérfana": espera silencio, reintenta cada 30s
|
||||
|
||||
### Paso 5 — Config aplicada
|
||||
|
||||
La configuración recibida incluye:
|
||||
- Su rol: ESCLAVA con dirección Modbus N
|
||||
- Modo de bus: solo Modbus, solo NMEA 2000, dual, o bridge
|
||||
- Asignación de cada uno de los 21 puntos a un tag específico (con su escalado, tipo de señal, filtros)
|
||||
- Permissives locales (lista de condiciones if-then críticas)
|
||||
- Alarmas locales (umbrales que dispararán acciones locales sin esperar al VMS)
|
||||
- Frecuencia de reporte
|
||||
- Configuración WiFi para OTA y MQTT (opcional)
|
||||
|
||||
La guarda en NVS para persistencia.
|
||||
|
||||
### Paso 6 — Modo operativo
|
||||
|
||||
A partir de aquí, ciclo principal:
|
||||
- Lee canales según frecuencia configurada
|
||||
- Aplica filtros locales
|
||||
- Evalúa alarmas locales y permissives locales
|
||||
- Responde a queries Modbus del maestro
|
||||
- Publica PGNs NMEA 2000 si está habilitado
|
||||
- Envía health beacon cada 60s
|
||||
|
||||
---
|
||||
|
||||
## 5. Modos de operación de bus
|
||||
|
||||
Configurable por software desde el VMS:
|
||||
|
||||
### Modo "Modbus solo"
|
||||
|
||||
- Solo RS485 activo, CAN apagado
|
||||
- Esclava Modbus con dirección N
|
||||
- Útil para tarjetas en sistemas auxiliares (tanques, FiFi, A/A, luces)
|
||||
|
||||
### Modo "NMEA 2000 solo"
|
||||
|
||||
- Solo CAN activo, RS485 apagado (o solo escucha)
|
||||
- Publica PGNs estándar al backbone
|
||||
- Útil cuando la tarjeta es de un sistema que se quiere exponer al ecosistema marino (motor, genset)
|
||||
|
||||
### Modo "Dual" (recomendado para motores y gensets)
|
||||
|
||||
- RS485 + CAN ambos activos
|
||||
- Reporta al VMS por Modbus para control fino
|
||||
- Publica al backbone NMEA 2000 para que plotters Garmin/Raymarine vean datos del motor
|
||||
- Doble alcance, mismo dato
|
||||
|
||||
### Modo "Bridge"
|
||||
|
||||
- Escucha CAN/NMEA 2000 y traduce a Modbus
|
||||
- Útil para tener un equipo que solo habla NMEA 2000 (ej: sensor de viento Airmar) y que el VMS pueda leerlo
|
||||
|
||||
---
|
||||
|
||||
## 6. PGNs NMEA 2000 que publica la tarjeta
|
||||
|
||||
Cuando una tarjeta está configurada en modo NMEA 2000 (típicamente motores y gensets):
|
||||
|
||||
| PGN | Nombre | Datos publicados |
|
||||
|---|---|---|
|
||||
| 127488 | Engine Parameters Rapid | RPM, boost pressure, trim |
|
||||
| 127489 | Engine Parameters Dynamic | Presión aceite, temp aceite, temp refrigerante, voltaje alternador, % carga, horas |
|
||||
| 127493 | Transmission Parameters | Presión aceite caja, temperatura |
|
||||
| 127505 | Fluid Level | Tanques combustible/agua/aceite (si la tarjeta es de tanques) |
|
||||
| 127508 | Battery Status | Voltaje, corriente, temp batería |
|
||||
| 127257 | Attitude | (consume del backbone, no publica — viene del AR-ECDIS) |
|
||||
|
||||
Esto le da al cliente un beneficio gratis: el plotter del puente muestra automáticamente datos del motor sin configuración extra.
|
||||
|
||||
---
|
||||
|
||||
## 7. Procesamiento local en la tarjeta
|
||||
|
||||
Esto es lo que diferencia tu sistema de tarjetas Modbus pasivas:
|
||||
|
||||
### Filtros locales
|
||||
|
||||
Configurables por canal desde el VMS:
|
||||
- **Moving average**: promedia últimas N muestras
|
||||
- **Median**: filtra spikes (excelente para señales con ruido EMI)
|
||||
- **Deadband**: solo reporta si el cambio supera X
|
||||
- **Rate limit**: rechaza cambios bruscos (anti-glitch)
|
||||
|
||||
Beneficio: reduce tráfico Modbus 10× típicamente, y mejora calidad de la señal.
|
||||
|
||||
### Alarmas locales
|
||||
|
||||
La tarjeta tiene umbrales propios y puede activar una salida si una entrada se sale de rango, sin esperar al VMS. Ejemplo:
|
||||
|
||||
```
|
||||
IF AI1 (temp aceite) > 110°C
|
||||
THEN DO1 (relé alarma local) = ON
|
||||
AND envía mensaje urgente al VMS por Modbus
|
||||
```
|
||||
|
||||
Tiempo de reacción: <50ms (vs 200-500ms si pasa por el VMS).
|
||||
|
||||
### Permissives locales críticos
|
||||
|
||||
Cuando un permissive es de seguridad funcional (E-stop, sobre-velocidad), se ejecuta LOCAL en la tarjeta:
|
||||
|
||||
```
|
||||
IF DI2 (pulsador E-stop) == ACTIVO
|
||||
THEN DO1 (relé arranque motor) = OFF inmediato
|
||||
AND DO2 (parada emergencia) = ON
|
||||
AND envía evento al VMS
|
||||
```
|
||||
|
||||
Esto cumple con SIL (Safety Integrity Level) básico — el corte no depende de que el bus esté disponible.
|
||||
|
||||
---
|
||||
|
||||
## 8. OTA (Over-The-Air firmware update)
|
||||
|
||||
El ESP32 soporta OTA nativo y maduro. Stack del firmware:
|
||||
|
||||
### Flujo OTA
|
||||
|
||||
1. Álvaro publica nueva versión de firmware en el Studio (ej: 1.5.0)
|
||||
2. Al generar el `.vmspack` o `.vmsdelta`, incluye el `.bin`
|
||||
3. El Runtime, vía VPN o presencial, recibe el paquete
|
||||
4. El Admin del buque aprueba la actualización
|
||||
5. El Runtime envía el `.bin` a cada tarjeta vía Modbus o WiFi
|
||||
6. La tarjeta:
|
||||
- Verifica firma criptográfica del firmware (clave pública de Álvaro embebida)
|
||||
- Verifica que la versión es compatible
|
||||
- Guarda en partición OTA
|
||||
- Reinicia
|
||||
- Si arranca OK → confirma al VMS
|
||||
- Si falla → rollback automático a la partición anterior
|
||||
|
||||
### Seguridad OTA
|
||||
|
||||
- Firma RSA-2048 o Ed25519 del firmware
|
||||
- Verificación obligatoria antes de aplicar
|
||||
- Sin firma válida = rechazado
|
||||
- Rollback automático si nuevo firmware no responde en 60s
|
||||
|
||||
### Tiempo de actualización
|
||||
|
||||
- Por Modbus RTU a 115200 baud: ~2 min por tarjeta de 500KB
|
||||
- Por WiFi: ~10 segundos
|
||||
- Para 7 tarjetas, total ~5-15 minutos
|
||||
|
||||
---
|
||||
|
||||
## 9. Comunicación tarjeta ↔ VMS
|
||||
|
||||
### Mensajes Modbus custom (function codes propietarios)
|
||||
|
||||
Además del Modbus estándar (read holding, write coils, etc.), agregamos function codes custom para gestión:
|
||||
|
||||
| FC | Nombre | Propósito |
|
||||
|---|---|---|
|
||||
| 65 | DISCOVERY_BROADCAST | Tarjeta anuncia presencia |
|
||||
| 66 | CONFIG_PUSH | VMS empuja configuración |
|
||||
| 67 | CONFIG_ACK | Tarjeta confirma config aplicada |
|
||||
| 68 | HEALTH_BEACON | Tarjeta reporta su salud cada 60s |
|
||||
| 69 | OTA_BEGIN | Inicia transferencia firmware |
|
||||
| 70 | OTA_CHUNK | Bloque de firmware |
|
||||
| 71 | OTA_END | Finalizar y reiniciar |
|
||||
| 72 | LOCAL_ALARM_EVENT | Tarjeta notifica alarma local |
|
||||
| 73 | LOCAL_PERMISSIVE_TRIGGERED | Tarjeta notifica permissive ejecutado |
|
||||
|
||||
---
|
||||
|
||||
## 10. Identificación de tarjetas (reemplazo de tarjeta dañada)
|
||||
|
||||
Combinación A+C (decisión Álvaro):
|
||||
|
||||
### Dipswitches físicos
|
||||
|
||||
Cada tarjeta tiene 4-5 dipswitches que el técnico setea al instalarla. Indican el "slot number" en el bus (1-16). La tarjeta envía este número en su discovery.
|
||||
|
||||
### Confirmación en pantalla del Runtime
|
||||
|
||||
Cuando se conecta una tarjeta nueva (slot que estaba vacío o slot que estaba con otra tarjeta serial), el Runtime alerta:
|
||||
```
|
||||
Nueva tarjeta detectada:
|
||||
Serial: ARC-2026-00187
|
||||
Slot dipswitch: 5
|
||||
Reemplaza a: ARC-2026-00153 (Motor PORT) [confirmado por Álvaro o Técnico]
|
||||
|
||||
[Aceptar y reasignar rol] [Rechazar - no esperaba esta tarjeta]
|
||||
```
|
||||
|
||||
Si los dipswitches no coinciden con lo esperado, alarma: "Tarjeta con dipswitch 7 conectada en slot 5 - corregir dipswitch o reasignar en Studio".
|
||||
|
||||
---
|
||||
|
||||
## 11. Sprints relacionados con firmware
|
||||
|
||||
- **Sprint 0** (parte): definición de `pinout.h` y estructura básica del firmware
|
||||
- **Sprint 12**: firmware completo Modbus RTU esclava + discovery + config push + filtros locales
|
||||
- **Sprint 13**: NMEA 2000 publishing + J1939 listening
|
||||
- **Sprint 14**: OTA + health reporter + permissives locales + alarmas locales
|
||||
- **Sprint 15**: pruebas de stress, watchdog, recovery
|
||||
|
||||
**Nota:** El firmware ES parte del proyecto VMS-Sailor (mismo repositorio, mismo control de versiones). Va junto con el resto.
|
||||
|
||||
---
|
||||
|
||||
## 12. Stack tecnológico del firmware
|
||||
|
||||
| Componente | Tecnología |
|
||||
|---|---|
|
||||
| Framework | PlatformIO + Arduino framework O ESP-IDF (a decidir en Sprint 12) |
|
||||
| Lenguaje | C++ |
|
||||
| Modbus RTU | `eModbus` o `ArduinoModbus` |
|
||||
| NMEA 2000 | `NMEA2000_esp32` library |
|
||||
| J1939 | librería específica |
|
||||
| OTA | `ArduinoOTA` o `esp_ota` de ESP-IDF |
|
||||
| MQTT | `PubSubClient` o `AsyncMqttClient` |
|
||||
| Filesystem (NVS) | nativo ESP32 |
|
||||
| Tests | Unity + PlatformIO test runner |
|
||||
|
||||
---
|
||||
|
||||
**Fin de Parte 4 de 6.** Próxima: Parte 5 — VMS-Sailor Mobile (Flutter).
|
||||
Reference in New Issue
Block a user