# 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](https://docs.platformio.org/en/latest/core/installation/) ≥ 6.x - USB-C cable to the ESP32-S3-Zero (native USB CDC) ### First flash ```bash # 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`: ```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.