330 lines
14 KiB
C++
330 lines
14 KiB
C++
#pragma once
|
||
|
||
/**
|
||
* @file Config.h
|
||
* @brief Central configuration constants, data structures and enumerations
|
||
* for the Marine Navigation Display.
|
||
*
|
||
* All hardware pin assignments, BLE UUIDs, display layout parameters and
|
||
* application-level structs are defined here so that every module shares a
|
||
* single source of truth.
|
||
*/
|
||
|
||
#include <Arduino.h>
|
||
#include <stdint.h>
|
||
|
||
// =============================================================================
|
||
// Firmware version
|
||
// =============================================================================
|
||
#ifndef APP_VERSION
|
||
#define APP_VERSION "1.0.0"
|
||
#endif
|
||
|
||
// =============================================================================
|
||
// Hardware — SPI pins for the 4.2" GxEPD2 e-ink display
|
||
// Waveshare ESP32-S3-Zero default SPI wiring
|
||
// =============================================================================
|
||
namespace Pins {
|
||
// ── E-Ink display ─────────────────────────────────────────────────────────
|
||
constexpr uint8_t EPD_CS = 7; ///< Chip Select
|
||
constexpr uint8_t EPD_DC = 6; ///< Data/Command
|
||
constexpr uint8_t EPD_RST = 5; ///< Reset
|
||
constexpr uint8_t EPD_BUSY = 4; ///< Busy signal (active LOW)
|
||
constexpr uint8_t EPD_SCK = 12; ///< SPI clock (HSPI SCK)
|
||
constexpr uint8_t EPD_MOSI = 11; ///< SPI MOSI (HSPI MOSI)
|
||
|
||
// ── Capacitive touch buttons (ESP32-S3 native touch) ─────────────────────
|
||
// Override via platformio.ini build_flags if needed.
|
||
constexpr uint8_t TOUCH_NEXT = TOUCH_PIN_NEXT; ///< Next page / +
|
||
constexpr uint8_t TOUCH_PREV = TOUCH_PIN_PREV; ///< Previous page / -
|
||
constexpr uint8_t TOUCH_ACTION = TOUCH_PIN_ACTION; ///< Action / full refresh
|
||
|
||
// ── NeoPixel RGB status LED ───────────────────────────────────────────────
|
||
constexpr uint8_t LED = STATUS_LED_PIN;
|
||
}
|
||
|
||
// =============================================================================
|
||
// Touch input parameters
|
||
// =============================================================================
|
||
namespace Touch {
|
||
constexpr uint16_t THRESHOLD = 40; ///< Raw touch value below = touched
|
||
constexpr uint32_t DEBOUNCE_MS = 50; ///< Minimum ms between events
|
||
constexpr uint32_t LONG_PRESS_MS = 1500; ///< Hold duration for long-press
|
||
constexpr uint32_t AP_COMBO_MS = 3000; ///< Hold NEXT+PREV to enter AP mode
|
||
}
|
||
|
||
// =============================================================================
|
||
// BLE — Marine Gateway protocol UUIDs
|
||
// Base: 4D475743-xxxx-4E41-5649-474154494F4E
|
||
// =============================================================================
|
||
namespace BleUUID {
|
||
// ── Services ──────────────────────────────────────────────────────────────
|
||
constexpr const char* NAV_SVC = "4d475743-0001-4e41-5649-474154494f4e";
|
||
constexpr const char* WIND_SVC = "4d475743-0002-4e41-5649-474154494f4e";
|
||
constexpr const char* AUTOPILOT_SVC = "4d475743-0003-4e41-5649-474154494f4e";
|
||
constexpr const char* PERF_SVC = "4d475743-0004-4e41-5649-474154494f4e";
|
||
constexpr const char* ADMIN_SVC = "4d475743-0005-4e41-5649-474154494f4e";
|
||
|
||
// ── Characteristics (read / notify) ───────────────────────────────────────
|
||
constexpr const char* NAV_DATA = "4d475743-0101-4e41-5649-474154494f4e";
|
||
constexpr const char* WIND_DATA = "4d475743-0201-4e41-5649-474154494f4e";
|
||
constexpr const char* AUTOPILOT_DATA = "4d475743-0301-4e41-5649-474154494f4e";
|
||
constexpr const char* AUTOPILOT_CMD = "4d475743-0302-4e41-5649-474154494f4e";
|
||
constexpr const char* PERF_DATA = "4d475743-0401-4e41-5649-474154494f4e";
|
||
constexpr const char* ADMIN_DATA = "4d475743-0501-4e41-5649-474154494f4e";
|
||
constexpr const char* ADMIN_CMD = "4d475743-0502-4e41-5649-474154494f4e";
|
||
}
|
||
|
||
// =============================================================================
|
||
// BLE runtime parameters
|
||
// =============================================================================
|
||
namespace BleConfig {
|
||
constexpr const char* DEVICE_NAME = BLE_DEVICE_NAME;
|
||
// Optional fixed BLE address ("aa:bb:cc:dd:ee:ff"). Keep empty to disable.
|
||
constexpr const char* TARGET_ADDR = "";
|
||
// Fallback name fragment (case-insensitive) for peripherals that do not
|
||
// expose the full local name in every advertisement packet.
|
||
constexpr const char* NAME_FALLBACK = "MarineGateway";
|
||
// Some firmwares expose only a 16-bit service UUID in advertising payload.
|
||
// 0xFE9F observed in field logs.
|
||
constexpr uint16_t FALLBACK_SVC16 = 0xFE9F;
|
||
constexpr uint32_t DEFAULT_PASSKEY = 123456; ///< default pairing PIN used if UI has not overridden it
|
||
constexpr uint32_t SCAN_DURATION = 10000; ///< scan window in milliseconds
|
||
constexpr uint32_t RECONNECT_MS = 5000; ///< delay before reconnect attempt
|
||
constexpr uint32_t STALE_TIMEOUT = 15; ///< seconds before data is marked stale
|
||
}
|
||
|
||
// =============================================================================
|
||
// Display layout
|
||
// 400×300 px divided into a 2×2 grid → 4 tiles
|
||
// =============================================================================
|
||
namespace Display {
|
||
constexpr uint16_t WIDTH = 400;
|
||
constexpr uint16_t HEIGHT = 300;
|
||
constexpr uint8_t TILE_COLS = 2;
|
||
constexpr uint8_t TILE_ROWS = 2;
|
||
constexpr uint8_t TILE_COUNT = TILE_COLS * TILE_ROWS;
|
||
constexpr uint16_t TILE_W = WIDTH / TILE_COLS; // 200 px
|
||
constexpr uint16_t TILE_H = HEIGHT / TILE_ROWS; // 150 px
|
||
constexpr uint8_t BORDER_PX = 2; ///< separator line width
|
||
constexpr uint8_t LABEL_H = 22; ///< px reserved for the tile label
|
||
constexpr uint32_t FULL_REFRESH_INTERVAL_MS = 300000; ///< forced full refresh every 5 min
|
||
}
|
||
|
||
// =============================================================================
|
||
// Available data fields that can be assigned to a tile
|
||
// =============================================================================
|
||
enum class DataField : uint8_t {
|
||
NONE = 0,
|
||
// Navigation
|
||
LAT, ///< GPS Latitude
|
||
LON, ///< GPS Longitude
|
||
SOG, ///< Speed Over Ground (kn)
|
||
COG, ///< Course Over Ground (°)
|
||
STW, ///< Speed Through Water (kn)
|
||
HDG_MAG, ///< Magnetic Heading (°)
|
||
DEPTH, ///< Depth (m)
|
||
// Wind
|
||
AWS, ///< Apparent Wind Speed (kn)
|
||
AWA, ///< Apparent Wind Angle (°)
|
||
TWS, ///< True Wind Speed (kn)
|
||
TWA, ///< True Wind Angle (°)
|
||
TWD, ///< True Wind Direction (°)
|
||
// Autopilot
|
||
AP_MODE, ///< Autopilot mode string
|
||
AP_TARGET, ///< Autopilot target heading (°)
|
||
AP_RUDDER, ///< Rudder angle (°)
|
||
// Performance
|
||
VMG, ///< Velocity Made Good (kn)
|
||
POLAR_PCT, ///< Polar efficiency (%)
|
||
TARGET_STW, ///< Polar target speed (kn)
|
||
// Admin / System
|
||
UPTIME, ///< Device uptime (s)
|
||
WIFI_MODE, ///< WiFi mode string
|
||
FREE_HEAP, ///< Free heap (bytes)
|
||
_COUNT ///< Sentinel — keep last
|
||
};
|
||
|
||
// =============================================================================
|
||
// Tile configuration (persisted in NVS / LittleFS)
|
||
// =============================================================================
|
||
struct TileConfig {
|
||
DataField field = DataField::NONE;
|
||
char label[16] = ""; ///< User-defined label override (empty = auto)
|
||
};
|
||
|
||
// =============================================================================
|
||
// Page: a set of 4 tiles
|
||
// =============================================================================
|
||
constexpr uint8_t MAX_PAGES = 8;
|
||
|
||
struct PageConfig {
|
||
char name[24] = "";
|
||
TileConfig tiles[Display::TILE_COUNT];
|
||
};
|
||
|
||
// =============================================================================
|
||
// Application-wide configuration (serialised to LittleFS as JSON)
|
||
// =============================================================================
|
||
struct AppConfig {
|
||
uint8_t pageCount = 1;
|
||
uint8_t activePage = 0;
|
||
PageConfig pages[MAX_PAGES];
|
||
|
||
// WiFi AP credentials (used when the display itself opens an AP)
|
||
char apSSID[32] = "MarineDisplay";
|
||
char apPassword[32] = "marine123";
|
||
};
|
||
|
||
// =============================================================================
|
||
// Live data snapshot — filled by BleManager, read by DisplayManager
|
||
// =============================================================================
|
||
struct NavData {
|
||
float lat = NAN;
|
||
float lon = NAN;
|
||
float sog = NAN;
|
||
float cog = NAN;
|
||
float stw = NAN;
|
||
float hdgMag = NAN;
|
||
float depth = NAN;
|
||
uint32_t updatedAt = 0; ///< millis() of last valid update
|
||
};
|
||
|
||
struct WindData {
|
||
float aws = NAN;
|
||
float awa = NAN;
|
||
float tws = NAN;
|
||
float twa = NAN;
|
||
float twd = NAN;
|
||
uint32_t updatedAt = 0;
|
||
};
|
||
|
||
struct AutopilotData {
|
||
char mode[16] = "";
|
||
char status[16] = "";
|
||
float headingTarget = NAN;
|
||
float windTarget = NAN;
|
||
float rudder = NAN;
|
||
float lockedHeading = NAN;
|
||
uint32_t updatedAt = 0;
|
||
};
|
||
|
||
struct PerfData {
|
||
float vmg = NAN;
|
||
float polarPct = NAN;
|
||
float targetStw = NAN;
|
||
bool polarLoaded = false;
|
||
uint32_t updatedAt = 0;
|
||
};
|
||
|
||
struct AdminData {
|
||
uint32_t uptimeS = 0;
|
||
uint64_t datetimeUtc = 0;
|
||
char wifiMode[8] = "";
|
||
char wifiSSID[33] = "";
|
||
char ip[16] = "";
|
||
uint32_t freeHeap = 0;
|
||
uint32_t updatedAt = 0;
|
||
};
|
||
|
||
/**
|
||
* @brief Aggregated snapshot of all BLE-received data.
|
||
* Access must be guarded by the SemaphoreHandle in AppState.
|
||
*/
|
||
struct BoatState {
|
||
NavData nav;
|
||
WindData wind;
|
||
AutopilotData autopilot;
|
||
PerfData perf;
|
||
AdminData admin;
|
||
};
|
||
|
||
// =============================================================================
|
||
// Application state — shared between all modules (extern in AppState.cpp)
|
||
// =============================================================================
|
||
namespace AppState {
|
||
enum class BleStatus : uint8_t {
|
||
IDLE,
|
||
SCANNING,
|
||
CONNECTING,
|
||
CONNECTED,
|
||
DISCONNECTED,
|
||
ERROR
|
||
};
|
||
|
||
enum class WifiStatus : uint8_t {
|
||
OFF,
|
||
AP_ACTIVE,
|
||
STA_CONNECTING,
|
||
STA_CONNECTED
|
||
};
|
||
}
|
||
|
||
// =============================================================================
|
||
// NeoPixel LED colour palette for status indication
|
||
// =============================================================================
|
||
namespace LedColor {
|
||
constexpr uint32_t OFF = 0x000000;
|
||
constexpr uint32_t BLE_SCANNING = 0x0000FF; ///< Blue pulsing
|
||
constexpr uint32_t BLE_CONNECTED= 0x00FF00; ///< Solid green
|
||
constexpr uint32_t BLE_ERROR = 0xFF0000; ///< Red
|
||
constexpr uint32_t WIFI_AP = 0xFF8800; ///< Amber
|
||
constexpr uint32_t WIFI_STA = 0x00FFFF; ///< Cyan
|
||
constexpr uint32_t OTA = 0xFF00FF; ///< Magenta pulsing
|
||
}
|
||
|
||
// =============================================================================
|
||
// Utility: readable name for a DataField
|
||
// =============================================================================
|
||
inline const char* fieldName(DataField f) {
|
||
switch (f) {
|
||
case DataField::LAT: return "Latitude";
|
||
case DataField::LON: return "Longitude";
|
||
case DataField::SOG: return "SOG";
|
||
case DataField::COG: return "COG";
|
||
case DataField::STW: return "STW";
|
||
case DataField::HDG_MAG: return "Heading";
|
||
case DataField::DEPTH: return "Depth";
|
||
case DataField::AWS: return "AWS";
|
||
case DataField::AWA: return "AWA";
|
||
case DataField::TWS: return "TWS";
|
||
case DataField::TWA: return "TWA";
|
||
case DataField::TWD: return "TWD";
|
||
case DataField::AP_MODE: return "AP Mode";
|
||
case DataField::AP_TARGET: return "AP Target";
|
||
case DataField::AP_RUDDER: return "Rudder";
|
||
case DataField::VMG: return "VMG";
|
||
case DataField::POLAR_PCT: return "Polar %";
|
||
case DataField::TARGET_STW:return "Tgt STW";
|
||
case DataField::UPTIME: return "Uptime";
|
||
case DataField::WIFI_MODE: return "WiFi Mode";
|
||
case DataField::FREE_HEAP: return "Free Heap";
|
||
default: return "---";
|
||
}
|
||
}
|
||
|
||
inline const char* fieldUnit(DataField f) {
|
||
switch (f) {
|
||
case DataField::LAT:
|
||
case DataField::LON:
|
||
case DataField::COG:
|
||
case DataField::HDG_MAG:
|
||
case DataField::TWD:
|
||
case DataField::AP_TARGET:
|
||
case DataField::AP_RUDDER:
|
||
case DataField::AWA:
|
||
case DataField::TWA: return "°";
|
||
case DataField::SOG:
|
||
case DataField::STW:
|
||
case DataField::AWS:
|
||
case DataField::TWS:
|
||
case DataField::VMG:
|
||
case DataField::TARGET_STW:return "kn";
|
||
case DataField::DEPTH: return "m";
|
||
case DataField::POLAR_PCT: return "%";
|
||
case DataField::UPTIME: return "s";
|
||
case DataField::FREE_HEAP: return "B";
|
||
default: return "";
|
||
}
|
||
}
|