302 lines
9.6 KiB
Markdown
302 lines
9.6 KiB
Markdown
# 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.
|