2026-06-18 15:18:12 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:18:12 +02:00

Marine Navigation Display

Waterproof marine e-ink navigation display based on the Waveshare ESP32-S3-Zero and a 4.2" monochrome e-paper screen, acting as a BLE client to the Marine Gateway.


Hardware Bill of Materials

Component Model
Microcontroller Waveshare ESP32-S3-Zero
Display Waveshare 4.2" E-Ink GDEY042T81 (400×300 px)
Touch buttons 3× capacitive pads on Touch pins 1, 2, 3
Status LED Built-in WS2812B NeoPixel (GPIO 21)
Power 3.3 V regulated (e.g. waterproof LiPo + TP4056)

Wiring

E-Ink Display (SPI — HSPI bus)

ESP32-S3 GPIO E-Ink Pin Signal
10 CLK SPI Clock
11 DIN SPI MOSI
9 CS Chip Select
8 DC Data / Command
7 RST Reset
6 BUSY Busy (active LOW)
3V3 VCC Power
GND GND Ground

If you rewire these pins, update the Pins::EPD_* constants in include/Config.h.

Touch Buttons

Connect a metallic rod or pad to each touch GPIO. No pull-up or external components are required — the ESP32-S3 native touch peripheral handles everything.

Button GPIO Action
NEXT Touch 1 Next page (short press)
PREV Touch 2 Previous page (short press)
ACTION Touch 3 Force full e-ink refresh
NEXT + PREV Hold 3 s → toggle WiFi AP mode

Project Structure

marine-display/
├── platformio.ini              # Build system configuration
├── partitions/
│   └── custom_ota.csv          # Custom flash partition table (OTA + LittleFS)
├── scripts/
│   └── gen_littlefs.py         # Pre-build script (PlatformIO extra_script)
├── data/                       # LittleFS root — upload with: pio run -t uploadfs
│   └── version.txt             # Auto-generated at build time
├── include/
│   ├── Config.h                # All constants, UUIDs, data structures
│   ├── ConfigManager.h         # Persistent configuration API
│   ├── BleManager.h            # BLE central client API
│   ├── DisplayManager.h        # E-Ink display driver API
│   ├── WifiManager.h           # WiFi AP + web server API
│   ├── TouchManager.h          # Capacitive touch API
│   └── StatusLed.h             # NeoPixel status LED API
└── src/
    ├── main.cpp                # Application entry point
    ├── ConfigManager.cpp
    ├── BleManager.cpp
    ├── DisplayManager.cpp
    ├── WifiManager.cpp
    ├── TouchManager.cpp
    └── StatusLed.cpp

Partition Layout

Name Type Size Purpose
nvs data/nvs 20 KB NimBLE bonds, Preferences
otadata data/ota 8 KB OTA slot selector
app0 app/ota_0 1.5 MB Running firmware
app1 app/ota_1 1.5 MB OTA target partition
littlefs data/spiffs 1 MB Config JSON + web assets
coredump data/coredump 64 KB Crash dump

Software Architecture

main.cpp
  │
  ├── ConfigManager   LittleFS + ArduinoJson
  │     └── /config.json  (pages, tiles, AP credentials)
  │
  ├── BleManager      NimBLE-Arduino (central role)
  │     ├── Scan → connect → pair → subscribe (5 characteristics)
  │     ├── Notify callbacks → parse JSON → update BoatState (mutex-protected)
  │     └── sendAutopilotCmd / sendAdminCmd
  │
  ├── DisplayManager  GxEPD2_BW
  │     ├── Full refresh  — page change, boot, timer (5 min)
  │     └── Partial refresh — value area only, per-tile change detection
  │
  ├── WifiManager     ESPAsyncWebServer
  │     ├── GET  /           → embedded HTML config page
  │     ├── GET  /api/config → config JSON
  │     ├── POST /api/config → apply new config
  │     ├── GET  /api/state  → live BoatState JSON
  │     ├── POST /api/ble/cmd → forward BLE command
  │     ├── POST /api/ble/passkey → provide BLE pairing PIN
  │     └── POST /update     → OTA firmware upload
  │
  ├── TouchManager    ESP32-S3 native touchRead()
  │     └── Short press / combo detection (NEXT+PREV → AP mode)
  │
  └── StatusLed       Adafruit NeoPixel
        └── Colour + blink pattern encodes BLE/WiFi status

Mode switching

The ESP32-S3 radio is shared between BLE and WiFi. The display operates in one mode at a time:

Boot
 ├── NEXT+PREV held? → WiFi AP mode
 └── Normal boot     → BLE mode

BLE mode:    BLE scanning → connect → live data display
WiFi AP mode: AP started → web server → config / OTA

Toggle at runtime: hold NEXT+PREV for 3 seconds.

Display Layout

┌────────────────────┬────────────────────┐
│ ████ TILE 0 ████  │ ████ TILE 1 ████  │
│                    │                    │
│       5.2          │       47.2°        │
│        kn          │                    │
├────────────────────┼────────────────────┤
│ ████ TILE 2 ████  │ ████ TILE 3 ████  │
│                    │                    │
│       10.1         │       85 %         │
│        kn          │                    │
└────────────────────┴────────────────────┘
  400 px wide × 300 px tall
  Each tile: 200 × 150 px
  Label bar: 22 px (inverted — white text on black)
  Value area: 128 px tall — large 24pt font, centred

Refresh strategy

Event Refresh type Duration
Data value changed Partial (value area only) ~200 ms
Page change Full ~1500 ms
Manual (ACTION button) Full ~1500 ms
Timer (every 5 min) Full ~1500 ms

BLE Protocol

The display connects to the Marine Gateway (MarineGateway) as a BLE central client. Full protocol documentation is in BLE_Client_Documentation.md.

Pairing

  1. The Marine Gateway displays a 6-digit PIN on its dashboard.
  2. Submit the PIN via the web UI at http://192.168.4.1/BLE Pairing PIN, or via the Serial console (planned).
  3. The bond is saved; subsequent reconnections are automatic.

Subscribed characteristics

Service Characteristic Update rate
Navigation NavData 1 Hz
Wind WindData 1 Hz
Autopilot AutopilotData 1 Hz
Sail Performance PerformanceData 1 Hz
Admin AdminData 1 Hz

Building and Flashing

Requirements

  • PlatformIO Core ≥ 6.x
  • USB-C cable to the ESP32-S3-Zero (native USB CDC)

First flash

# Clone / open the project
cd marine-display

# Build and flash firmware
pio run -t upload

# Upload the LittleFS filesystem (web UI assets + default config placeholder)
pio run -t uploadfs

OTA update (subsequent flashes)

  1. Ensure the device is in WiFi AP mode (hold NEXT+PREV 3 s or at boot).
  2. Connect your computer to the MarineDisplay WiFi network.
  3. Open http://192.168.4.1/ in a browser.
  4. Scroll to Firmware Update, select the .bin file, click Flash Firmware.

The firmware binary is at .pio/build/esp32s3_marine_display/firmware.bin after a build.

Alternatively, configure OTA upload in platformio.ini:

upload_protocol = espota
upload_port     = 192.168.4.1

Then use pio run -t upload over WiFi.


Configuration Web UI

Connect to MarineDisplay WiFi (default password: marine123) and open http://192.168.4.1/ in a browser.

Tile configuration

Each display page has 4 tiles arranged in a 2×2 grid. For each tile, select:

  • Data Field — which BLE data to display (SOG, TWA, VMG, Depth, etc.)
  • Label override — custom short label (max 15 chars); leave blank for auto

Up to 8 pages can be configured. Navigate with NEXT/PREV buttons.

Gateway control

From the web UI you can:

  • Switch the Marine Gateway to STA mode (connect it to your boat WiFi)
  • Restart the Marine Gateway remotely
  • Submit a BLE pairing PIN

Customisation

Adding a new data field

  1. Add an entry to the DataField enum in include/Config.h.
  2. Add the field to fieldName() and fieldUnit() inline functions.
  3. Add the field extraction in DisplayManager::_fieldToString().
  4. Add parsing in the appropriate BleManager::_parseXxx() method and struct.

Changing the display model

Replace GxEPD2_420_GDEY042T81 in include/DisplayManager.h with the correct GxEPD2 model class for your panel. Update DISPLAY_WIDTH / DISPLAY_HEIGHT in platformio.ini accordingly.

Changing pin assignments

All pin constants live in include/Config.h under the Pins:: namespace, or can be overridden via build_flags in platformio.ini.


Troubleshooting

Symptom Likely cause Fix
Display shows only splash BLE not connected Wait for scan; check gateway is advertising
Values show --- NMEA data stale (>10 s) Check gateway serial input
Ghosting on display Partial refresh accumulation Press ACTION button for full refresh
Can't see web UI Not in AP mode Hold NEXT+PREV 3 s
OTA fails Wrong .bin file Use firmware.bin from .pio/build/
Touch not responding Threshold too high/low Adjust Touch::THRESHOLD in Config.h
BLE bond stale Old bond data Clear NVS: pio run -t erase then re-flash

Licence

MIT — see LICENSE file.

S
Description
No description provided
Readme
92 KiB
Languages
C++ 99.1%
Python 0.9%