{"id":50901348,"url":"https://github.com/navado/e-boat-controls","last_synced_at":"2026-06-16T03:03:58.124Z","repository":{"id":341930675,"uuid":"736049484","full_name":"navado/e-boat-controls","owner":"navado","description":"DYI two-board electric-boat motor control system.","archived":false,"fork":false,"pushed_at":"2026-03-03T23:48:35.000Z","size":115,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"develop","last_synced_at":"2026-03-04T03:45:08.078Z","etag":null,"topics":["ai-generated-code","arduino","e-drive","stm32"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/navado.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-12-26T20:58:01.000Z","updated_at":"2026-03-03T16:31:29.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/navado/e-boat-controls","commit_stats":null,"previous_names":["navado/e-boat-controls"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/navado/e-boat-controls","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navado%2Fe-boat-controls","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navado%2Fe-boat-controls/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navado%2Fe-boat-controls/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navado%2Fe-boat-controls/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/navado","download_url":"https://codeload.github.com/navado/e-boat-controls/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navado%2Fe-boat-controls/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34388669,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-generated-code","arduino","e-drive","stm32"],"created_at":"2026-06-16T03:03:52.244Z","updated_at":"2026-06-16T03:03:58.110Z","avatar_url":"https://github.com/navado.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Controls for DYI electric boat\n\n[![CI](https://github.com/navado/e-boat-pannel/actions/workflows/ci.yml/badge.svg)](https://github.com/navado/e-boat-pannel/actions/workflows/ci.yml)\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE)\n\nFirmware for a three-node electric-boat motor control system. A **sensor node** (Arduino Nano) reads hall-effect RPM pulses, a throttle potentiometer, and battery voltage, then drives four relays and a PWM throttle signal. A **panel node** (Arduino Nano or STM32 Bluepill) provides an SPI-connected 128×64 LCD display and four tactile buttons for the operator. An optional **throttle device** (STM32 Bluepill) adds an advanced control interface — potentiometer or quadrature encoder — with five PID-based control modes and NMEA 2000 / NMEA 0183 GPS integration. All three nodes communicate over a shared serial bus using a checksummed NMEA-0183-style protocol.\n\n---\n\n## Table of Contents\n\n1. [System Architecture](#system-architecture)\n2. [Hardware Bill of Materials](#hardware-bill-of-materials)\n3. [Wiring Diagrams](#wiring-diagrams)\n   - [Sensor Node (Arduino Nano)](#sensor-node--arduino-nano)\n   - [Panel Node — STM32 Bluepill](#panel-node--stm32-bluepill)\n   - [Panel Node — Arduino Nano (alternative)](#panel-node--arduino-nano-alternative)\n   - [Throttle Device — STM32 Bluepill](#throttle-device--stm32-bluepill)\n   - [System Interconnect (ASCII)](#system-interconnect)\n   - [System Interconnect (Mermaid)](#system-connection-diagram)\n4. [Communication Protocol](#communication-protocol)\n5. [Throttle Control Modes](#throttle-control-modes)\n6. [Throttle Table](#throttle-table)\n7. [Safety Interlocks](#safety-interlocks)\n8. [Building the Firmware](#building-the-firmware)\n9. [Running the Tests](#running-the-tests)\n10. [Project Layout](#project-layout)\n11. [License](#license)\n\n---\n\n## System Architecture\n\n```\n  ┌──────────────────────────┐        ┌──────────────────────────────────┐\n  │       PANEL NODE         │        │          SENSOR NODE             │\n  │  (STM32 Bluepill or Nano)│        │         (Arduino Nano)           │\n  │                          │  UART  │                                  │\n  │  [BTN_A] Throttle UP  ─┐ │◄──────►│  ┌─ Relay: main power lock       │\n  │  [BTN_B] Throttle DOWN─┤ │        │  ├─ Relay: reverse direction      │\n  │  [BTN_C] Power toggle ─┤ │        │  ├─ Relay: regenerative braking   │\n  │  [BTN_D] Regen toggle ─┘ │        │  └─ Relay: spare                 │\n  │                          │        │                                  │\n  │  ┌──────────────────┐    │        │  PWM ──► Motor controller        │\n  │  │  ST7565 128×64   │    │        │                                  │\n  │  │    LCD display   │    │        │  Hall sensor ──► RPM counter     │\n  │  └──────────────────┘    │        │  Throttle pot ──► ADC           │\n  └──────────────────────────┘        │  48 V bus ──► Voltage divider   │\n                                      └──────────────┬───────────────────┘\n  ┌──────────────────────────┐                       │ shared UART bus\n  │    THROTTLE DEVICE       │◄──────────────────────┘\n  │  (STM32 Bluepill)        │\n  │                          │\n  │  Pot / Encoder ──► position                                          │\n  │  Mode encoder  ──► RPM/PWR/SOG/SOW/RNG                              │\n  │  GPS (NMEA 0183/2000) ──► SOG, SOW, COG                             │\n  │  RGB LED + Buzzer ──► status                                         │\n  └──────────────────────────┘\n```\n\n### Serial bus messages\n\n| Direction | Message | Trigger |\n|-----------|---------|---------|\n| Panel → Sensor | `$ENCMD,pow:on/off,rev:on/off,reg:on/off,thr:\u003c0-255\u003e*HH` | Any button press |\n| Sensor → all | `$ENINF,\u003cT\u003e,\u003crpm\u003e,\u003cpow\u003e,\u003crev\u003e,\u003creg\u003e,\u003cthr\u003e,\u003cvthr\u003e,\u003cvcc\u003e,...*HH` | Every 10 s |\n| Throttle → Sensor | `$THRCMD,mode:\u003c0-4\u003e,target:\u003cval\u003e,thr:\u003c0-255\u003e*HH` | On position change |\n| Throttle → all | `$THRINF,\u003cT\u003e,\u003cmode\u003e,\u003ctarget\u003e,\u003csog_kn10\u003e,\u003csow_kn10\u003e,\u003ccog_deg\u003e*HH` | Periodically |\n| Throttle → Panel | `$PANCTL,pow:\u003con/off\u003e*HH` | On panel power toggle |\n\n---\n\n## Hardware Bill of Materials\n\n| Component | Qty | Notes |\n|-----------|-----|-------|\n| Arduino Nano (ATmega328P) | 2 | One for sensor, one for Nano-panel variant |\n| STM32 Bluepill (F103C8) | 1–2 | Panel and/or throttle device; J-Link or ST-Link for first flash |\n| ST7565-based 128×64 LCD | 1 | ERC12864 or compatible, 4-wire SPI |\n| Tactile push-buttons | 4–6 | Normally open, 10 kΩ pull-down resistors |\n| Hall-effect sensor | 1 | Open-drain NPN; 1 kΩ pull-up to 5 V |\n| N-channel MOSFETs / relay board | 4 | 5 V coil, rated for motor current |\n| Resistors: 1 kΩ, 100 kΩ | 1 each | Throttle pot voltage divider |\n| Resistors: 470 kΩ, 20 kΩ | 1 each | 48 V battery voltage divider |\n| RC low-pass filter (1 kΩ + 10 µF) | 1 | Smooths PWM throttle signal |\n| Quadrature encoder (optional) | 1 | With push-button; for throttle device encoder variant |\n| RGB LED (common-cathode) | 1 | Status indicator on throttle device |\n| Passive buzzer | 1 | Audio feedback on throttle device |\n| SN65HVD230 CAN transceiver (optional) | 1 | For NMEA 2000 GPS input |\n| 48 V lithium battery pack | 1 | Nominal voltage; confirm relay ratings |\n\n---\n\n## Wiring Diagrams\n\n### Sensor Node — Arduino Nano\n\n```\nArduino Nano\n─────────────────────────────────────────────────────────────\nPin  Label        Direction  Connected to\n─────────────────────────────────────────────────────────────\nD0   RX            IN         Shared bus TX  (Panel or Throttle device)\nD1   TX            OUT        Shared bus RX\nD2   RELAY_SPARE   OUT        Relay board IN4 (spare)\nD4   RELAY_REGEN   OUT        Relay board IN3 (regenerative braking enable)\nD5   T1/RPM        IN         Hall sensor OUT  ──[1 kΩ pull-up to 5 V]── VCC\n                               (falling-edge counted by Timer 1; 6 pulses/rev)\nD6   THROTTLE_OUT  OUT        RC filter ──[1 kΩ]──┬── Motor controller throttle IN\n                                                   └──[10 µF]── GND\nD7   RELAY_REV     OUT        Relay board IN2 (motor direction reversal)\nD8   RELAY_LOCK    OUT        Relay board IN1 (main power lock)\nD13  LED_BRD       OUT        On-board LED (heartbeat blink)\nA0   THROTTLE_IN   IN         Throttle feedback divider:\n                               48 V bus ──[470 kΩ]──┬── A0\n                                                    └──[20 kΩ]── GND\nA1   VCC_SENS_IN   IN         Battery voltage divider:\n                               48 V bus ──[470 kΩ]──┬── A1\n                                                    └──[20 kΩ]── GND\nA2   BTN_A         IN         Button A (Throttle UP)   ──[10 kΩ pull-down]── GND\nA3   BTN_B         IN         Button B (Throttle DOWN) ──[10 kΩ pull-down]── GND\nA4   BTN_C         IN         Button C (Power toggle)  ──[10 kΩ pull-down]── GND\nA5   BTN_D         IN         Button D (Regen toggle)  ──[10 kΩ pull-down]── GND\nA6   CURR_SENS_IN  IN         Shunt amplifier output (50 mV/A)\n─────────────────────────────────────────────────────────────\n\nRelay board wiring (each relay independently):\n  IN_n ──── Arduino output pin (active HIGH)\n  COM  ──── 48 V bus or motor lead\n  NO   ──── load (motor lead / power bus)\n  NC   ──── leave disconnected (or use for safety tie-down)\n\nHall sensor wiring:\n  VCC  ──── 5 V\n  GND  ──── GND\n  OUT  ──[1 kΩ pull-up]── 5 V\n        └──── D5 (T1)\n```\n\n---\n\n### Panel Node — STM32 Bluepill\n\n```\nSTM32 Bluepill (F103C8)\n─────────────────────────────────────────────────────────────\nPin   Label          Direction  Connected to\n─────────────────────────────────────────────────────────────\nA9    USART1 TX      OUT        Shared serial bus RX\nA10   USART1 RX      IN         Shared serial bus TX\nA0    RS_PIN         OUT        LCD RS  (register select / data-cmd)\nA1    SCL_PIN        OUT        LCD SCL (serial clock)\nA2    SDO_PIN        OUT        LCD SDO (serial data, MOSI)\nA3    BACKLIGHT_PIN  OUT        LCD backlight anode  ──[100 Ω]── LED\nB0    BTN_D          IN         Button D (Regen toggle)  ──[10 kΩ pull-down]── GND\nB1    BTN_C          IN         Button C (Power toggle)  ──[10 kΩ pull-down]── GND\nB10   BTN_B          IN         Button B (Throttle DOWN) ──[10 kΩ pull-down]── GND\nB11   BTN_A          IN         Button A (Throttle UP)   ──[10 kΩ pull-down]── GND\nPC13  LED_BRD        OUT        On-board LED (heartbeat, active LOW on Bluepill)\nPC14  CS_PIN         OUT        LCD CS  (chip select, active LOW)\nPC15  RST_PIN        OUT        LCD RST (reset, active LOW)\n3.3   VCC            —          LCD VCC (3.3 V)\nGND   GND            —          LCD GND, button common\n─────────────────────────────────────────────────────────────\n\nLCD ERC12864 (ST7565) SPI wiring:\n  VDD ──── 3.3 V\n  GND ──── GND\n  CS  ──── PC14\n  RST ──── PC15\n  RS  ──── A0\n  SCL ──── A1\n  SDO ──── A2\n  BLA ──[100 Ω]──── A3 (PWM backlight)\n  BLK ──── GND\n\nJ-Link / ST-Link programming header (SWD):\n  SWDIO ──── PA13\n  SWCLK ──── PA14\n  GND   ──── GND\n  3.3 V ──── 3.3 V  (do NOT power from programmer if board is externally powered)\n\nLevel shifting (STM32 3.3 V ↔ Nano 5 V):\n  STM32 TX → Sensor RX : direct connection (Nano RX tolerates 3.3 V)\n  Sensor TX → STM32 RX : 10 kΩ / 20 kΩ voltage divider or dedicated level shifter\n─────────────────────────────────────────────────────────────\n```\n\n---\n\n### Panel Node — Arduino Nano (alternative)\n\n```\nArduino Nano (panel variant, compile with -D PANNEL_NANO)\n─────────────────────────────────────────────────────────────\nPin   Label          Direction  Connected to\n─────────────────────────────────────────────────────────────\nD0    RX             IN         Shared serial bus TX\nD1    TX             OUT        Shared serial bus RX\nD2    SCL_PIN        OUT        LCD SCL\nD3    SDO_PIN        OUT        LCD SDO (MOSI)\nD4    CS_PIN         OUT        LCD CS  (active LOW)\nD5    RST_PIN        OUT        LCD RST (active LOW)\nD6    BACKLIGHT_PIN  OUT        LCD backlight ──[100 Ω]── LED\nD7    RS_PIN         OUT        LCD RS  (register select)\nD9    BTN_D          IN         Button D ──[10 kΩ pull-down]── GND\nD10   BTN_C          IN         Button C ──[10 kΩ pull-down]── GND\nD11   BTN_B          IN         Button B ──[10 kΩ pull-down]── GND\nD12   BTN_A          IN         Button A ──[10 kΩ pull-down]── GND\nD13   LED_BRD        OUT        On-board LED (heartbeat)\n5 V   VCC            —          LCD VCC (check LCD voltage spec; add 3.3 V LDO if needed)\nGND   GND            —          LCD GND, button common\n─────────────────────────────────────────────────────────────\n```\n\n---\n\n### Throttle Device — STM32 Bluepill\n\nThe throttle device replaces or augments panel button-based throttle control with a high-resolution input device and closed-loop PID control modes.\n\n```\nSTM32 Bluepill (F103C8) — Throttle Controller\n─────────────────────────────────────────────────────────────\nPin   Label           Direction  Connected to\n─────────────────────────────────────────────────────────────\nA9    USART1 TX       OUT        Shared serial bus RX  (Sensor + Panel)\nA10   USART1 RX       IN         Shared serial bus TX\nA2    USART2 TX       OUT        (unused / debug)\nA3    USART2 RX       IN         NMEA 0183 GPS  (4800 / 9600 baud, $GPRMC / $GPVTG)\n\n— Throttle input (choose ONE variant at build time) ——————————\nPA0   THROTTLE_POT    IN         Potentiometer wiper  (-D THROTTLE_POT)\n                                   3.3 V ──[pot]──┬── PA0\n                                                  └──[GND]\nPB0   THROTTLE_ENC_A  IN         Quadrature encoder A  (-D THROTTLE_ENC)\nPB1   THROTTLE_ENC_B  IN         Quadrature encoder B\n                                   (5 V encoder: use 3.3 V level shifter or 10k divider)\n\n— Mode-selector encoder ——————————————————————————————————————\nPB3   MODE_ENC_A      IN         Mode selector encoder A\nPB4   MODE_ENC_B      IN         Mode selector encoder B\nPB5   MODE_ENC_BTN    IN         Encoder push-to-select / calibrate centre\n\n— Control buttons (active LOW, INPUT_PULLUP) —————————————————\nPB6   BTN_ENGINE      IN         Engine ON / OFF\nPB7   BTN_PANEL       IN         Panel power ON / OFF  (long-press interlock)\n\n— Status outputs —————————————————————————————————————————————\nPA1   LED_R           OUT        RGB LED — Red   channel (PWM TIM2_CH2)\nPA6   LED_G           OUT        RGB LED — Green channel (PWM TIM3_CH1)\nPA7   LED_B           OUT        RGB LED — Blue  channel (PWM TIM3_CH2)\nPA8   BUZZER          OUT        Passive buzzer  (tone() / PWM TIM1_CH1)\nPC13  LED_BRD         OUT        Onboard LED (active LOW)\n\n— NMEA 2000 CAN (optional, requires SN65HVD230 transceiver) —\nPA11  CAN_RX          IN         CAN bus RX\nPA12  CAN_TX          OUT        CAN bus TX\n\nJ-Link / ST-Link SWD:\n  PA13 SWDIO,  PA14 SWCLK,  GND,  3.3 V\n─────────────────────────────────────────────────────────────\n```\n\n**RGB LED status colours**\n\n| Colour | Meaning |\n|--------|---------|\n| Green (solid) | Engine ON, throttle active |\n| Blue (solid) | Engine OFF / idle |\n| Yellow (pulsing) | Mode change in progress |\n| Red | Safety interlock active or fault |\n\n---\n\n### System Interconnect\n\n```\n  ┌──────────────────────────┐              ┌──────────────────────────┐\n  │       PANEL NODE         │              │       SENSOR NODE        │\n  │   (STM32 or Nano)        │    UART      │     (Arduino Nano)       │\n  │                          │  115200 8N1  │                          │\n  │  USART TX ───────────────┼─────────────►│ RX (D0)                  │\n  │  USART RX ◄──────────────┼──────────────┤ TX (D1)                  │\n  │                          │              │                          │\n  └──────────────────────────┘              └──────────┬───────────────┘\n                                                       │ 115200 8N1\n  ┌──────────────────────────┐                         │ (same RS-232 bus)\n  │    THROTTLE DEVICE       │◄────────────────────────┘\n  │   (STM32 Bluepill)       │\n  │                          │◄──── NMEA 0183 GPS (Serial2, PA2/PA3)\n  └──────────────────────────┘◄──── NMEA 2000 CAN  (PA11/PA12, optional)\n\n  Notes:\n  ─ All three nodes share one RS-232 bus (sensor D0/D1).\n  ─ Panel and Throttle device connect as additional talkers/listeners.\n  ─ STM32 I/O is 3.3 V; Nano sensor is 5 V:\n      TX (STM32) → Nano RX : direct (Nano tolerates 3.3 V)\n      TX (Nano)  → STM32 RX: 10 kΩ / 20 kΩ divider or level shifter\n  ─ Motor controller, relays and 48 V power are on the sensor node only.\n```\n\n---\n\n### System Connection Diagram\n\n```mermaid\nflowchart TD\n    GPS[\"GPS receiver\\n(NMEA 0183 / 2000)\"]\n    THR[\"Throttle Device\\n(STM32 Bluepill)\\nEncoder / Pot\\nRGB LED · Buzzer\"]\n    PAN[\"Panel Node\\n(STM32 / Nano)\\n128×64 LCD\\n4 buttons\"]\n    SEN[\"Sensor Node\\n(Arduino Nano)\"]\n    MOT[\"Motor Controller\\n(PWM throttle)\"]\n    REL[\"Relay Board\\n4× relays\\nPower · Rev · Regen · Spare\"]\n    BAT[\"48 V Battery\"]\n    HALL[\"Hall Sensor\\n(RPM)\"]\n    POT[\"Throttle Pot\\n(feedback ADC)\"]\n\n    GPS  --\u003e|NMEA 0183 Serial2\\nor NMEA 2000 CAN| THR\n    THR  \u003c--\u003e|THRCMD / THRINF\\n115200 8N1 UART| SEN\n    PAN  \u003c--\u003e|ENCMD / ENINF\\n115200 8N1 UART| SEN\n    THR  --\u003e|PANCTL| PAN\n\n    SEN  --\u003e|analogWrite PWM| MOT\n    SEN  --\u003e|digitalWrite| REL\n    REL  --\u003e|contacts| MOT\n    BAT  --\u003e|voltage divider ADC| SEN\n    HALL --\u003e|falling-edge counter T1| SEN\n    POT  --\u003e|ADC A0| SEN\n```\n\n---\n\n## Communication Protocol\n\nMessages use an NMEA-0183-inspired framing:\n\n```\n$\u003cPAYLOAD\u003e*\u003cXX\u003e\\n\n │              │└─ Newline (0x0A)\n │              └── Two hex digits: XOR checksum of all bytes in PAYLOAD\n └── PAYLOAD: comma-separated fields\n```\n\n### Panel → Sensor: `ENCMD`\n\n```\n$ENCMD,pow:\u003con|off\u003e,rev:\u003con|off\u003e,reg:\u003con|off\u003e,thr:\u003c0-255\u003e*XX\n```\n\n| Field | Values | Meaning |\n|-------|--------|---------|\n| `pow` | `on` / `off` | Main power relay |\n| `rev` | `on` / `off` | Reverse relay |\n| `reg` | `on` / `off` | Regenerative-braking relay |\n| `thr` | 0–255 | PWM throttle value from throttle table |\n\n### Sensor → all: `ENINF`\n\n```\n$ENINF,\u003cT\u003e,\u003crpm\u003e,\u003cpow\u003e,\u003crev\u003e,\u003creg\u003e,\u003cthr\u003e,\u003cvthr\u003e,\u003cvcc\u003e,\u003ccurr_ma\u003e,\u003cpower_w\u003e,\u003cwater_kn10\u003e,\u003cslip_pct10\u003e*XX\n```\n\n| Field | Type | Meaning |\n|-------|------|---------|\n| `T` | ms | Uptime timestamp |\n| `rpm` | uint | Motor RPM (TCNT1 / 6) |\n| `pow` | 0/1 | Power relay state |\n| `rev` | 0/1 | Reverse relay state |\n| `reg` | 0/1 | Regen relay state |\n| `thr` | 0–255 | Active throttle PWM value |\n| `vthr` | 0–5000 | Throttle ADC mapped 0–5000 mV |\n| `vcc` | mV | Battery voltage |\n| `curr_ma` | mA | Motor current (shunt amplifier) |\n| `power_w` | W | Motor shaft power (V × I) |\n| `water_kn10` | kn×10 | Speed through water (from throttle device) |\n| `slip_pct10` | %×10 | Propeller slip (signed; +ve = slipping) |\n\n### Throttle → Sensor: `THRCMD`\n\n```\n$THRCMD,mode:\u003c0-4\u003e,target:\u003cval\u003e,thr:\u003c0-255\u003e*XX\n```\n\n| Field | Values | Meaning |\n|-------|--------|---------|\n| `mode` | 0–4 | Control mode index (see table below) |\n| `target` | varies | Mode target: RPM, W, or knots×10 |\n| `thr` | 0–255 | Computed PWM after PID |\n\n### Throttle → all: `THRINF`\n\n```\n$THRINF,\u003cT\u003e,\u003cmode\u003e,\u003ctarget\u003e,\u003csog_kn10\u003e,\u003csow_kn10\u003e,\u003ccog_deg\u003e*XX\n```\n\n| Field | Type | Meaning |\n|-------|------|---------|\n| `T` | ms | Uptime |\n| `mode` | 0–4 | Active control mode |\n| `target` | varies | Current mode target value |\n| `sog_kn10` | kn×10 | Speed over ground (from GPS) |\n| `sow_kn10` | kn×10 | Speed through water (from impeller/NMEA) |\n| `cog_deg` | degrees | Course over ground |\n\n### Throttle → Panel: `PANCTL`\n\n```\n$PANCTL,pow:\u003con|off\u003e*XX\n```\n\nControls the panel LCD backlight / wake state remotely from the throttle device.\n\n### Timing\n\n| Event | Period |\n|-------|--------|\n| Button scan | 100 ms |\n| Throttle / voltage ADC read | 100 ms |\n| RPM counter latch | 1 s |\n| `ENINF` telemetry transmit | 10 s |\n| `ENCMD` command transmit | On change |\n| `THRCMD` throttle command | On position change |\n| `THRINF` status broadcast | 1 s |\n\n---\n\n## Throttle Control Modes\n\nThe throttle device selects its active mode via the mode-selector encoder. The sensor node reads `mode` and `target` from `THRCMD` / `THRINF` and applies them through the PID controller.\n\n| Mode | Value | Target unit | PID feedback | Description |\n|------|-------|-------------|--------------|-------------|\n| `RPM` | 0 | RPM | `engine_state.rpm` | Direct RPM target; simplest mode |\n| `PWR` | 1 | Watts | computed `V × I` | Constant power; battery-friendly |\n| `SOG` | 2 | knots × 10 | GPS `sog_kn10` | Constant speed over ground |\n| `SOW` | 3 | knots × 10 | impeller `sow_kn10` | Constant speed through water |\n| `RNG` | 4 | — | efficiency ratio | Maximum range; minimises power for given SOW |\n\n---\n\n## Throttle Table\n\nSpeed index 5 is neutral. Below 5 is reverse, above 5 is forward. The PWM values match the motor controller's input range.\n\n```\nIndex │  0    1    2    3    4  │  5  │  6    7    8    9   10\n──────┼─────────────────────────┼─────┼──────────────────────────\nPWM   │ 245  224  185  145  110 │  1  │ 110  145  185  224  254\nMode  │◄────── REVERSE ─────────┤STOP │──────── FORWARD ────────►\n```\n\nLong-press on throttle buttons jumps ±4 steps; short-press moves ±1 step.\n\nThe `throttle_table[]` array in `models.cpp` maps the 11-step index to a PWM value written to `THROTTLE_OUT` (D6) via `analogWrite()`.\n\n---\n\n## Safety Interlocks\n\nThe sensor firmware enforces the following guards against unsafe state transitions:\n\n| Command | Blocked when |\n|---------|-------------|\n| Power OFF | Throttle PWM \u003e 1 (motor still spinning) |\n| Reverse toggle | Throttle \u003e 1 **or** regen is active |\n| Regen toggle | Throttle \u003e 1 **or** reverse is active |\n| Throttle change | Power is off **or** regen is active |\n\nThe panel firmware adds a second layer:\n\n| Button action | Blocked when |\n|--------------|-------------|\n| Power toggle (BTN_C) | Speed ≠ neutral **or** regen is active |\n| Regen toggle (BTN_D) | Power is off **or** speed ≠ neutral |\n| Throttle change | Power is off **or** regen is active |\n\n---\n\n## Building the Firmware\n\n### Prerequisites\n\n- [PlatformIO Core](https://docs.platformio.org/en/latest/core/installation/index.html) (CLI or IDE plugin)\n- For STM32 targets: J-Link or ST-Link debugger\n\n```bash\ncd devices/e-drive-sensor\n\n# Sensor node (Arduino Nano)\npio run -e sensor\n\n# Panel node — STM32 Bluepill\npio run -e stm-panel\n\n# Panel node — Arduino Nano\npio run -e avr-pannel\n\n# Throttle device — potentiometer input\npio run -e throttle-stm\n\n# Throttle device — quadrature encoder input\npio run -e throttle-stm-enc\n```\n\n### Uploading\n\n```bash\n# Arduino Nano via USB:\npio run -e sensor         --target upload\npio run -e avr-pannel     --target upload\n\n# STM32 Bluepill via J-Link:\npio run -e stm-panel      --target upload\npio run -e throttle-stm   --target upload\npio run -e throttle-stm-enc --target upload\n```\n\n### Build flags reference\n\n| Flag | Effect |\n|------|--------|\n| `-D SENSOR` | Sensor node firmware |\n| `-D PANNEL_STM32` | Panel node (Bluepill) |\n| `-D PANNEL_NANO` | Panel node (Nano) |\n| `-D THROTTLE_STM32` | Throttle device firmware |\n| `-D THROTTLE_POT` | Use potentiometer input |\n| `-D THROTTLE_ENC` | Use quadrature encoder input |\n| `-D LOG_LEVEL=\u003c0-4\u003e` | Log verbosity: 0=DEBUG … 3=ERROR, 4=NONE |\n| `-D PROP_PITCH_MM=\u003cmm\u003e` | Propeller pitch for slip calculation (default 600 mm) |\n| `-D NMEA2000` | Enable NMEA 2000 CAN bus GPS input |\n\n---\n\n## Running the Tests\n\nThe test suite targets pure logic (tokenizer, checksum, command parser, throttle model) and requires only a C++14 compiler — no embedded hardware or PlatformIO native platform download needed.\n\n```bash\ncd devices/e-drive-sensor\n\ng++ -std=c++14 -I include -I test \\\n    -D NATIVE_TEST -D LOG_LEVEL=4 \\\n    -Wall -Wextra -Werror \\\n    src/comms.cpp src/models.cpp test/test_main.cpp \\\n    -o /tmp/e_boat_tests \u0026\u0026 /tmp/e_boat_tests\n```\n\nExpected output:\n\n```\nPASS: test_tokenize_basic\nPASS: test_tokenize_single_token\n...\n-----------------------\n57 Tests  0 Failures\nOK\n```\n\nAlternatively, once PlatformIO's native platform is downloaded:\n\n```bash\npio test -e native\n```\n\n---\n\n## Project Layout\n\n```\ne-boat-pannel/\n└── devices/\n    └── e-drive-sensor/         # Unified firmware project\n        ├── platformio.ini      # Six build environments\n        ├── include/\n        │   ├── pinout.h        # Pin assignments for all board variants\n        │   ├── models.h        # State structs, throttle table, PID, slip calc\n        │   ├── comms.h         # Protocol types, log levels, function declarations\n        │   ├── buttons.h       # Button debounce state machine \u0026 encoder types\n        │   └── ui.h            # LCD display function declarations\n        ├── src/\n        │   ├── main-sensor.cpp   # Sensor node: relays, RPM, ADC, PWM throttle\n        │   ├── main-pannel.cpp   # Panel node: buttons, LCD display, ENCMD sender\n        │   ├── main-throttle.cpp # Throttle device: encoder/pot, PID modes, GPS\n        │   ├── comms.cpp         # Serial framing, tokenizer, XOR checksum\n        │   ├── models.cpp        # State init, throttle table, PID, prop slip\n        │   ├── buttons.cpp       # Button debounce FSM, quadrature encoder decoding\n        │   └── ui.cpp            # u8g2-based LCD rendering\n        └── test/\n            ├── test_main.cpp   # 57 unit tests (Unity framework)\n            ├── unity.h         # Bundled minimal Unity test runner\n            ├── Arduino.h       # Shim: redirects to mock_arduino.h\n            └── mock_arduino.h  # Arduino API stub for native compilation\n```\n\n---\n\n## License\n\nGNU General Public License v3.0 — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnavado%2Fe-boat-controls","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnavado%2Fe-boat-controls","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnavado%2Fe-boat-controls/lists"}