#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 #include // ============================================================================= // 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 = 9; ///< Chip Select constexpr uint8_t EPD_DC = 8; ///< Data/Command constexpr uint8_t EPD_RST = 7; ///< Reset constexpr uint8_t EPD_BUSY = 6; ///< Busy signal (active LOW) constexpr uint8_t EPD_SCK = 10; ///< 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; constexpr uint32_t SCAN_DURATION = 10; ///< seconds per scan window 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 ""; } }