4cc5b19f0c
Define el protocolo completo de la tarjeta concentrador NMEA2000-USB: - Sentencias NMEA 0183 estandar emitidas por el ESP32 hacia los puertos OUT - Sentencias propietarias $PARP para comandos del autopilot - Protocolo de transferencia de mando entre estaciones (puente, cockpit, flybridge) - Mapeo completo NMEA 0183 <-> NMEA 2000 PGNs - Seguridad: validacion de checksum, permisos por estacion, alarmas Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.6 KiB
8.6 KiB
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,<modo>,<setpoint>,<heading>,<rudder>,<commander>*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,<desde>,<hacia>*XX
$PARP,CMDREQUEST,<estacion>*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,<COMANDO>,<parametro>,<station_id>*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
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
- Checksum inválido → descartar silenciosamente, no ejecutar
- Comando de estación sin mando → descartar, emitir
$PARP,STATUScon commander actual - Heading fuera de rango (< 0° o > 359.9°) → normalizar a rango válido
- Pérdida de datos del backbone (> 3s sin PGNs) → emitir alarma
$PARP,STATUS,ALARM,... - Desconexión USB-IN → no afecta la operación; el mando permanece asignado
- 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