diff --git a/docs/concentrador_protocol.md b/docs/concentrador_protocol.md new file mode 100644 index 0000000..9ae693a --- /dev/null +++ b/docs/concentrador_protocol.md @@ -0,0 +1,242 @@ +# Protocolo Concentrador NMEA2000-USB +**AR-Autopilot — Tarjeta Concentrador** +Versión 1.0 — 2026-05-23 + +--- + +## Arquitectura general + +``` +Software (J6412 / Tablet / PC) + │ + │ NMEA 0183 ASCII @115200 bps por USB (CH340N) + ▼ +┌─────────────────────────────────────┐ +│ ESP32 Concentrador │ +│ NMEA 0183 ←→ NMEA 2000 gateway │ +└─────────────────────────────────────┘ + │ + │ NMEA 2000 (CAN 250 kbps) + ▼ + Backbone del barco +``` + +- **USB → ESP32 (IN)** : sentencias NMEA 0183 que el software envía como comandos +- **ESP32 → USB (OUT)**: sentencias NMEA 0183 que el ESP32 emite con datos del backbone +- **Formato**: ASCII, terminado en `\r\n`, checksum NMEA estándar `*XX` +- **Velocidad**: 115200 bps en todos los puertos USB + +--- + +## Hardware — Puertos USB + +``` +UART1 TX → CH340N #1 → USB-OUT1 (puente — solo lectura) + → CH340N #2 → USB-OUT2 (cockpit — solo lectura) + → CH340N #3 → USB-OUT3 (flybridge — solo lectura) + → CH340N #4 → USB-OUT4 (reserva — solo lectura) + +UART2 RX ← CH340N #5 ← USB-IN1 (puente — manda comandos) + ← CH340N #6 ← USB-IN2 (cockpit — manda si tiene el mando) + ← CH340N #7 ← USB-IN3 (flybridge — manda si tiene el mando) + ← CH340N #8 ← USB-IN4 (reserva — futuro) +``` + +**IDs de estación:** +| ID | Estación | Prioridad | +|-----|-----------|-----------| +| 01 | Puente | Máxima — override sin confirmación | +| 02 | Cockpit | Normal | +| 03 | Flybridge | Normal | +| 04 | Reserva | Normal | + +--- + +## Sentencias NMEA 0183 estándar (salida ESP32 → USB-OUT) + +El ESP32 convierte los PGNs del backbone a estas sentencias y las emite +por todos los puertos OUT cada vez que recibe datos nuevos del bus. + +| Sentencia | Contenido | PGN fuente | Frecuencia | +|-----------|-----------|------------|------------| +| `$IIHDT,x.x,T*XX` | Heading verdadero | 127250 | 10 Hz | +| `$IIROT,x.x,A*XX` | Rate of turn (°/min) | 127251 | 10 Hz | +| `$IIVHW,,,x.x,N,x.x,K*XX` | Velocidad agua | 128259 | 1 Hz | +| `$IIDPT,x.x,0.0*XX` | Profundidad | 128267 | 1 Hz | +| `$GPGLL,x.x,N,x.x,W,hhmmss,A*XX` | Posición GPS | 129025 | 1 Hz | +| `$GPRMC,...*XX` | GPS completo (COG, SOG) | 129026 | 1 Hz | +| `$IIMTW,x.x,C*XX` | Temperatura agua | 130310 | 0.5 Hz | +| `$IIMWV,...*XX` | Viento | 130306 | 1 Hz | + +--- + +## Sentencias propietarias AR-Autopilot (salida ESP32 → USB-OUT) + +Prefijo `$PARP` (P=Proprietary, ARP=AR-Pilot). + +### Estado del autopilot (broadcast continuo, 2 Hz) + +``` +$PARP,STATUS,,,,,*XX +``` + +| Campo | Valores | +|-------|---------| +| modo | `STANDBY` `HEADING_HOLD` `TRACK` `ALARM` | +| setpoint | heading objetivo en grados (000.0–359.9) | +| heading | heading actual en grados | +| rudder | posición timón en grados (-35.0 a +35.0, + = estribor) | +| commander | ID estación con el mando (01–04, 00 = nadie) | + +Ejemplo: +``` +$PARP,STATUS,HEADING_HOLD,045.5,044.8,-3.2,01*4F +``` + +### Estado de transferencia de mando (broadcast al ocurrir) + +``` +$PARP,CMDTRANSFER,,*XX +$PARP,CMDREQUEST,*XX +``` + +Ejemplos: +``` +$PARP,CMDTRANSFER,01,02*3A ← el puente cedió el mando al cockpit +$PARP,CMDREQUEST,02*1B ← cockpit pide el mando (pendiente de confirmación) +``` + +--- + +## Sentencias propietarias AR-Autopilot (entrada USB-IN → ESP32) + +El ESP32 solo acepta estas sentencias de la estación que tiene el mando, +excepto `TAKECMD` y `REQCMD` que tienen reglas propias. + +### Formato general + +``` +$PARP,,,*XX\r\n +``` + +### Comandos de control del autopilot + +| Sentencia | Acción | → PGN NMEA 2000 | +|-----------|--------|-----------------| +| `$PARP,ENGAGE,000.0,01*XX` | Activar autopilot en heading actual | PGN 127237 mode=heading_hold | +| `$PARP,DISENGAGE,000.0,01*XX` | Desactivar autopilot | PGN 127237 mode=standby | +| `$PARP,SETHEADING,045.5,01*XX` | Fijar heading objetivo | PGN 127237 commanded_heading | +| `$PARP,PORTONE,000.0,01*XX` | Girar 1° a babor | PGN 127237 (setpoint -= 1°) | +| `$PARP,STBDONE,000.0,01*XX` | Girar 1° a estribor | PGN 127237 (setpoint += 1°) | +| `$PARP,PORTTEN,000.0,01*XX` | Girar 10° a babor | PGN 127237 (setpoint -= 10°) | +| `$PARP,STBDTEN,000.0,01*XX` | Girar 10° a estribor | PGN 127237 (setpoint += 10°) | + +### Comandos de transferencia de mando + +| Sentencia | Acción | Regla | +|-----------|--------|-------| +| `$PARP,REQCMD,000.0,02*XX` | Solicitar el mando | Avisa al commander actual; auto-transfer en 10s si no responde | +| `$PARP,RELCMD,000.0,01*XX` | Ceder el mando voluntariamente | Solo el commander actual | +| `$PARP,TAKECMD,000.0,01*XX` | Tomar el mando por override | Solo estación 01 (puente); inmediato, sin confirmación | +| `$PARP,ACKCMD,000.0,01*XX` | Confirmar o denegar solicitud de mando | Solo el commander actual | +| `$PARP,DENYCMD,000.0,01*XX` | Denegar solicitud de mando | Solo el commander actual | + +--- + +## Protocolo de transferencia de mando + +``` +Estado normal: Puente (01) tiene el mando + │ +Cockpit quiere mando: + Cockpit ──► $PARP,REQCMD,000.0,02*XX + ESP32 ──► broadcast $PARP,CMDREQUEST,02*XX (todos se enteran) + │ + ┌────────┴────────┐ + Puente confirma Puente deniega Puente no responde (10s) + │ │ │ + $PARP,RELCMD,02 $PARP,DENYCMD,02 auto-transfer + │ │ │ + commander = 02 commander = 01 commander = 02 + $PARP,CMDTRANSFER,01,02*XX $PARP,CMDTRANSFER,01,02*XX + +Override del puente (siempre posible): + Puente ──► $PARP,TAKECMD,000.0,01*XX + ESP32 ──► commander = 01 inmediatamente + ESP32 ──► broadcast $PARP,CMDTRANSFER,XX,01*XX +``` + +--- + +## Mapeo NMEA 0183 → NMEA 2000 + +| Sentencia IN | PGN generado | Descripción PGN | +|-------------|-------------|-----------------| +| `$PARP,ENGAGE` | 127237 | Heading/Track Control — mode: heading_hold | +| `$PARP,DISENGAGE` | 127237 | Heading/Track Control — mode: standby | +| `$PARP,SETHEADING` | 127237 | commanded_heading field | +| `$PARP,PORT*` / `$PARP,STBD*` | 127237 | commanded_heading ± delta | + +## Mapeo NMEA 2000 → NMEA 0183 + +| PGN recibido | Sentencia generada | +|-------------|-------------------| +| 127250 — Vessel Heading | `$IIHDT` | +| 127251 — Rate of Turn | `$IIROT` | +| 127237 — Heading/Track Control | `$PARP,STATUS` | +| 128259 — Speed Through Water | `$IIVHW` | +| 128267 — Water Depth | `$IIDPT` | +| 129025 — Position Rapid | `$GPGLL` | +| 129026 — COG & SOG | `$GPRMC` | +| 130310 — Water Temperature | `$IIMTW` | +| 130306 — Wind | `$IIMWV` | + +--- + +## Cálculo de checksum NMEA + +```python +def nmea_checksum(sentence: str) -> str: + """sentence: contenido entre $ y * (sin incluir ambos)""" + chk = 0 + for c in sentence: + chk ^= ord(c) + return f"{chk:02X}" + +# Ejemplo: +# sentence = "PARP,STATUS,HEADING_HOLD,045.5,044.8,-3.2,01" +# checksum = "4F" +# resultado = "$PARP,STATUS,HEADING_HOLD,045.5,044.8,-3.2,01*4F\r\n" +``` + +--- + +## Seguridad y validación en el ESP32 + +1. **Checksum inválido** → descartar silenciosamente, no ejecutar +2. **Comando de estación sin mando** → descartar, emitir `$PARP,STATUS` con commander actual +3. **Heading fuera de rango** (< 0° o > 359.9°) → normalizar a rango válido +4. **Pérdida de datos del backbone** (> 3s sin PGNs) → emitir alarma `$PARP,STATUS,ALARM,...` +5. **Desconexión USB-IN** → no afecta la operación; el mando permanece asignado +6. **Power-on**: `commander = 00` (nadie), autopilot en STANDBY + +--- + +## Ejemplo de sesión completa + +``` +[t=0s] OUT → $PARP,STATUS,STANDBY,000.0,182.3,+0.0,00*XX +[t=1s] IN1 ← $PARP,REQCMD,000.0,01*XX (puente toma el mando) +[t=1s] OUT → $PARP,CMDTRANSFER,00,01*XX +[t=2s] OUT → $PARP,STATUS,STANDBY,000.0,182.5,+0.0,01*XX +[t=3s] IN1 ← $PARP,ENGAGE,000.0,01*XX +[t=3s] OUT → $PARP,STATUS,HEADING_HOLD,182.5,182.5,+0.0,01*XX +[t=5s] IN1 ← $PARP,STBDTEN,000.0,01*XX +[t=5s] OUT → $PARP,STATUS,HEADING_HOLD,192.5,183.1,+2.5,01*XX +[t=10s] IN2 ← $PARP,REQCMD,000.0,02*XX (cockpit pide mando) +[t=10s] OUT → $PARP,CMDREQUEST,02*XX +[t=15s] IN1 ← $PARP,RELCMD,000.0,01*XX (puente lo cede) +[t=15s] OUT → $PARP,CMDTRANSFER,01,02*XX +[t=16s] IN2 ← $PARP,SETHEADING,200.0,02*XX +[t=16s] OUT → $PARP,STATUS,HEADING_HOLD,200.0,193.4,+3.8,02*XX +```