diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e536f0d..8f0a47f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,9 @@ jobs: arduino-cli lib install "SensorManager" arduino-cli lib install "SdFat" arduino-cli lib install "DHT11" + arduino-cli lib install "NextionControl" + arduino-cli lib install "TinyGPSPlus" + arduino-cli lib install "DS1302" - name: Compile SmartFuseBox firmware run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7134cab..d74ef54 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,9 @@ jobs: arduino-cli lib install "SerialCommandManager" arduino-cli lib install "SensorManager" arduino-cli lib install "SdFat" + arduino-cli lib install "NextionControl" + arduino-cli lib install "TinyGPSPlus" + arduino-cli lib install "DS1302" - name: Generate FirmwareVersion.h run: | diff --git a/Docs/Commands.md b/Docs/Commands.md index e0f4b24..77eff0a 100644 --- a/Docs/Commands.md +++ b/Docs/Commands.md @@ -1,12 +1,50 @@ -# Communication Commands -The following sections indicate the types of commands that can be sent to the Boat Control Panel, with examples and purpose. +# SmartFuseBox Command Reference + +## Overview + +This document describes every command the SmartFuseBox (SFB) firmware understands. +Commands travel over two transports: + +| Transport | Format | Direction | +|---|---|---| +| **Serial / USB** | `:=;=\n` | Bidirectional | +| **WiFi REST** | `POST /api//?=&…` | Client → SFB | + +### Command-group prefix summary + +| Prefix | Group | Handler | WiFi route | +|---|---|---|---| +| `F` | System | `SystemCommandHandler` | `/api/system` | +| `C` | Configuration | `ConfigCommandHandler` / `ConfigNetworkHandler` | `/api/config` | +| `R` | Relay control & config | `RelayCommandHandler` / `RelayNetworkHandler` | `/api/relay` | +| `H` | Sound / fog-horn signals | `SoundCommandHandler` / `SoundNetworkHandler` | `/api/sound` | +| `W` | Warnings | `WarningCommandHandler` / `WarningNetworkHandler` | `/api/warning` | +| `S` | Sensor config & telemetry | `SensorConfigCommandHandler` / `SensorCommandHandler` / `SensorNetworkHandler` | `/api/sensor` | +| `M` | MQTT configuration | `MQTTConfigCommandHandler` | `/api/config` | +| `N` | Nextion display config | `NextionConfigCommandHandler` / `ConfigNetworkHandler` | `/api/config` | +| `E` | External (remote) sensor config | `ExternalSensorConfigCommandHandler` / `ExternalSensorNetworkHandler` | `/api/externalsensor` | +| `T` | Timer / scheduler | `SchedulerCommandHandler` / `SchedulerNetworkHandler` | `/api/timer` | +| `ACK` | Acknowledgement | `AckCommandHandler` | — | + +### General conventions + +- All commands and parameters are **case-sensitive**. +- Serial frame format: `CMD:key=val;key=val\n` — delimiter `:`, param separator `;`, key/value separator `=`. +- Up to **5 parameters** per command on the serial transport. +- Responses follow the same frame format; multi-value responses send multiple frames. +- Every mutating command that requires a **reboot** to take effect returns `reboot=1` in its ACK. +- Use `C0` (Save Settings) to persist in-memory changes to EEPROM before powering off. +- `255` / `0xFF` means **disabled / not fitted** for any pin field. + +--- + +## System Commands — `F` -## System Commands These are commands used to configure the system settings and can only be sent from a computer, they are not used for internal communication. | Command | Example | Purpose | |---|---|---| -| `F0` — Heart beat | `F0:w=0x00` (SFB) or `F0:w=0x00;t=1733328600` (BCP) | Send at rated intervals to monitor connection health. If no ACK received, indicates connection loss. **Params:** `w=` - bitmask of active local warnings (hex format). `t=` - (BCP only) Unix timestamp for continuous time sync. Smart Fuse Box automatically updates clock from `t=` parameter. | +| `F0` — Heart beat | `F0:w=0x00` or `F0:w=0x00;t=1733328600` | Send at rated intervals to monitor connection health. If no ACK received, indicates connection loss. **Params:** `w=` - bitmask of active local warnings (hex format). `t=` - Unix timestamp for continuous time sync. Smart Fuse Box automatically updates clock from `t=` parameter. | | `F1` — System Initialized | `F1` | Sent by the system when initialization is complete to signal readiness. No params. Used to notify connected devices or software that the control panel is ready for operation. | | `F2` — Free Memory | `F2` | When received will return the amount of free memory. | | `F3` — Cpu Usage | `F3` | When received will return the current CPU usage. No params. | @@ -21,380 +59,413 @@ These are commands used to configure the system settings and can only be sent fr | `F12` — OTA Check / Apply | `F12` or `F12:apply=1` | Trigger an OTA firmware check against the latest GitHub release. Without params (or `apply=0`) checks only and returns current status. With `apply=1` downloads and applies the update if one is available (or queues apply if a check is already in progress). Response params: `v=` current firmware version, `av=` available version tag (empty if none found yet), `s=` OTA state string. **SFB only** — requires `OTA_AUTO_UPDATE`, ESP32 and WiFi in client mode. | | `F13` — OTA Status | `F13` | Query current OTA state without triggering a check. Response params: `v=` current firmware version, `av=` available version tag, `s=` OTA state string (`idle`, `checking`, `available`, `downloading`, `rebooting`, `failed`, `uptodate`), `auto=<0\|1>` whether auto-apply is enabled. **SFB only** — requires `OTA_AUTO_UPDATE`, ESP32 and WiFi in client mode. | -**Heartbeat Behavior:** -- Sent every 1 second (1000ms) from both devices -- Boat Control Panel (BCP) includes time sync: `F0:w=0x00;t=1733328600` -- Smart Fuse Box (SFB) includes only warnings: `F0:w=0x00` -- Connection timeout occurs after 3 seconds without response -- Time synchronization is automatic - no separate F6 commands needed during normal operation -- F6 command can still be used for manual time updates or initial configuration -- -### Wifi System Commands (SFB) -Only F0 is supported via wifi. -Route: /api/system/{command} -Example: Return free memory = /api/system/F2 - -### OTA Behavior (F12 / F13) -- Automatic periodic check runs every 24 hours (first check fires 24 h after boot to allow WiFi to fully connect). -- F12 without params (or `apply=0`) triggers an immediate check and broadcasts the result — it does **not** apply the update automatically. -- F12 with `apply=1` downloads and applies immediately if an update is available; if a check is still in progress the apply is queued. -- Auto-apply on each periodic check is **off by default**; enable it by setting bit `OtaFlagAutoApply (0x01)` in `SystemHeader::reserved[0]`. -- State transitions broadcast an `F12` command automatically as they occur (e.g. `checking` → `available` → `downloading` → `rebooting`). -- Debug and error messages are broadcast via the debug channel with source `OTA` at each key milestone: API query, tag found/not found, checksum fetch, binary size, download progress (25 / 50 / 75%), hash verification, and reboot. -- F13 is read-only — it never triggers any OTA activity. -- OTA is only active when `OTA_AUTO_UPDATE` is defined, the board is an ESP32, `WIFI_SUPPORT` is defined, and WiFi is in connected client mode. +**OTA behaviour (F12 / F13):** +- Automatic periodic check every 24 hours (first check fires 24 h after boot). +- `F12` without params (or `apply=0`) triggers a check only — it does **not** apply automatically. +- `F12:apply=1` downloads and applies immediately; if a check is in progress the apply is queued. +- Auto-apply is **off by default**; enable via `OtaFlagAutoApply (0x01)` in `SystemHeader::reserved[0]`. +- State transitions broadcast an `F12` automatically as they occur. +- `F13` is read-only — it never triggers any OTA activity. +- OTA is only active when `OTA_AUTO_UPDATE` is defined, the board is an ESP32, `WIFI_SUPPORT` is defined, and WiFi is in connected client mode. ------- +### WiFi system commands +Route: `/api/system/{command}` +Example: `GET /api/system/F2` +--- -## Configuration Commands -These are commands used to configure the system settings and can only be sent from a computer, they are not used for internal communication. +## Configuration Commands — `C` | Command | Example | Purpose | |---|---|---| -| `C0` — Save settings | `C0` | Persist current in-memory config to EEPROM. Responds `SAVED` on success; error `EEPROM commit failed` on failure. No params. | -| `C1` — Get settings | `C1` | Request full config. Device replies with multiple commands: `C3 `, `C4 sck=;mosi=;miso=` (SPI bus pins), `C5 :` for each home-slot mapping, `C7 v=` for vessel type, `C9 v=` for sound delay, `C10`-`C17` for WiFi/Bluetooth settings (SFB only), `C20 v=` for timezone, `C21 ` for MMSI, `C22 ` for call sign, `C23 ` for home port, `R6 =` for each relay name, `R7 =` for each button color, `R8 =` for default relay states, `R9 =` for linked relay pairs (active pairs only), `R10 =` for non-default action types only, `R11 =` for relay pins, `S0` for all sensor config entries (one `S0` response per configured sensor), then `OK`. No params. | -| `C2` — Reset settings | `C2` | Request full reset of all config settings. No params. | -| `C3` — Rename boat | `C3:Sea Wolf` or `C3:name=SeaWolf` | Set the boat name. `:` pair (value is used as boat name. Empty name → error. Name is truncated to configured max length. | -| `C4` — SPI pins | `C4:sck=12;mosi=11;miso=13` | Set the SD card SPI bus pins. Param format: `sck=;mosi=;miso=`. All three keys are required. Use `255` (PinDisabled) to clear a pin. Returned by `C1` with the current stored values. Call `C0` to persist. | -| `C5` — Map home button (BCP) | `C5:1=3` (map) — `C5:1=255` (unmap) | Map a home-page slot to a relay. Param format: `:`. `button` must be 0..3 (`HOME_BUTTONS`). `relay` must be 0..7 or `255` to clear/unmap. | -| ~~`C6`~~ — ~~Set relay button color~~ | — | **Retired.** Use `R7` — Relay Set Button Color instead. | -| `C7` — Set vessel type | `C7:v=1` | Set the vessel type. Param format: `v=`. Possible values for `` are: 0 (Motor), 1 (Sail), 2 (Fishing), 3 (Yacht). Uses enum values as defined in `Config.h`. Invalid or missing value → error. | -| ~~`C8`~~ — ~~Sound relay button~~ | — | **Retired.** Use `R10` — Relay Set Action Type with `actionType=1` (Horn) instead. | -| `C9` — Sound delay Start | `C9:v=0xFF` | Sets the delay before the sound is started in milliseconds, allows other processing to continue so as sounds are not cut off. Invalid or missing value → error. | -| `C10` — Bluetooth Enabled (SFB) | `C10:v=1` | Sets the enabled state of bluetooth, 0 disabled, 1 enabled, if disabling then a restart is required. Invalid or missing value → error. | -| `C11` — Wifi Enabled (SFB) | `C11:v=1` | Sets the enabled state of wifi, 0 disabled, 1 enabled, if disabling then a restart is required. Invalid or missing value → error. | -| `C12` — Wifi Access Mode (SFB) | `C12:v=1` | Sets access mode, 0 is AP and 1 is client | -| `C13` — Wifi SSID (SFB) | `C13:RouterName` | Set's the wifi SSID, only available if access mode is client. Param format: SSID value directly (no v= prefix). | -| `C14` — Wifi Password (SFB) | `C14:Password123` | Set's the wifi password, only available if access mode is client. Param format: password value directly (no v= prefix). | -| `C15` — Wifi Port (SFB) | `C15:v=80` | Set's the wifi port, this is the port used to listen on, default value is 80. Invalid or zero port → error. | -| `C16` — Wifi connection state (SFB) | `C16` | Wifi connection state, no params, returns WifiConnectionState value. Note: Not implemented in ConfigCommandHandler - use SystemCommandHandler. | -| `C17` — Wifi AP Ip address (SFB) | `C17:192.168.4.1` | Sets the IP address when using access point mode, default is 192.168.4.1. Param format: IP address directly (no v= prefix). | -| ~~`C18`~~ — ~~Initial relay on~~ | — | **Retired.** Use `R8` — Relay Set Default State instead. | -| ~~`C19`~~ — ~~Link Relays~~ | — | **Retired.** Use `R9` — Relay Link instead. | -| `C20` — Timezone Offset | `C20:v=-5` or `C20:v=8` | Set timezone offset from UTC in hours. Param format: `v=`. Valid range: -12 to +14. Invalid offset → error. | -| `C21` — MMSI | `C21:123456789` | Set Maritime Mobile Service Identity (MMSI) number. Param format: MMSI value directly (no v= prefix). Must be exactly 9 digits. Invalid length → error. | -| `C22` — Call Sign | `C22:ABCD123` | Set vessel call sign. Param format: call sign value directly (no v= prefix). Truncated to configured max length (ConfigCallSignLength). | -| `C23` — Home Port | `C23:Miami` | Set vessel home port. Param format: port name directly (no v= prefix). Truncated to configured max length (ConfigHomePortLength). | -| `C24` — LED Color | `C24:t=0;c=0;r=255;g=50;b=213` | Set LED RGB color. Param format: `t=;c=;r=;g=;b=`. `type` must be 0 (day) or 1 (night). `colorset` must be 0 (good) or 1 (bad). RGB values 0-255. | -| `C25` — LED Brightness | `C25:t=0;b=75` | Set LED brightness. Param format: `t=;b=`. `type` must be 0 (day) or 1 (night). `brightness` must be 0-100. | -| `C26` — LED Auto Switch | `C26:v=true` or `C26:v=1` | Enable/disable auto day/night switching. Param format: `v=`. `value` must be true/false or 1/0. | -| `C27` — LED Enable States | `C27:g=true;w=true;s=false` | Enable/disable individual LEDs. Param format: `g=;w=;s=`. Each value must be true/false or 1/0. `g`=GPS LED, `w`=Warning LED, `s`=System LED. | -| `C28` — Control Panel Tones | `C28:t=0;h=400;d=500;p=0;r=30000` | Configure control panel tones, 2 categories, good and bad, good is only used at startup to indicate all is working, bad is used in response to warnings. t=0 or 1 (0 good or 1 bad), h = tone Hz, d = duration in milliseconds, p = preset 0 custom tone/duration, 1 = submaring ping, 2 = double beep, 3 = rising chirp, 4 = descending alert, 5 = nautical bell, 0xFF = no sound. r = repeat interval in milliseconds where 0 is do not repeat, this is only used for bad sounds. | -| `C29` — Reload Config from SD (SFB) | `C29` | **Local only (Serial/USB)**. Reload configuration from SD card config.txt file... | -| `C30` — Export Config to SD (SFB) | `C30` | **Local only (Serial/USB)**. Export current in-memory configuration to SD card... | -| `C31` — SD Card Initialize Speed (SFB) | `C31:v=4` | Set SD card SPI initialization speed in MHz. Param format: `v=`. Valid values: 4, 8, 12, 16, 20, 24. Default is 4 MHz. **Note:** Higher speeds (16+ MHz) should only be used with high-quality SD cards that explicitly support high-speed SPI mode. Using speeds that are too high for your SD card may result in initialization failures or data corruption. If experiencing SD card issues, try reducing the speed to 4 or 8 MHz. | -| ~~`C32`~~ — ~~Light Sensor Night Relay~~ | — | **Retired.** Use `R10` — Relay Set Action Type with `actionType=2` (NightRelay) instead. | -| `C33` — Light Sensor Daytime Threshold (SFB) | `C33:v=512` | Set the analogue light level threshold above which daytime is detected. Param format: `v=`. Valid range: 0–1023. Default is 512. Readings from the rolling average of the last 10 samples are compared against this value; 3 consecutive consistent readings are required before the day/night state changes (see S16). | - -### MQTT Configuration Commands (SFB) -These commands configure MQTT connectivity for Home Assistant and other MQTT-based integrations. +| `C0` — Save Settings | `C0` | Persist current in-memory config to EEPROM. Responds `SAVED` on success. No params. | +| `C1` — Get Settings | `C1` | Request full config dump. SFB replies with multiple frames covering all groups (see below). No params. | +| `C2` — Reset Settings | `C2` | Full reset of all config to factory defaults. No params. | +| `C3` — Rename Location | `C3:Sea Wolf` | Set the location name. Value directly, no `v=` prefix. Empty name → error. | +| `C4` — SPI Pins | `C4:sck=12;mosi=11;miso=13` | Set SD card SPI bus pins. Params: `sck=;mosi=;miso=`. Use `255` to clear any pin. Call `C0` to persist. | +| `C5` — Map Home Button | `C5:1=3` (map) — `C5:1=255` (unmap) | Map a home-page slot (0..3) to a relay (0..7), or `255` to unmap. | +| `C6` — XpdzTone Pin | `C6:v=12` | Set XpdzTone buzzer/piezo pin. `v=255` to disable. Call `C0` to persist. | +| `C7` — Location Type | `C7:v=1` | Set location type: `0`=Motor, `1`=Sail, `2`=Fishing, `3`=Yacht. | +| `C8` — Hw479Rgb Pins | `C8:r=12;g=13;b=14` | Set RGB LED warning indicator pins. Use `255` for any pin not fitted. Call `C0` to persist. | +| `C9` — Sound Start Delay | `C9:v=500` | Delay in milliseconds before sound starts, allowing other processing to complete. | +| `C10` — Bluetooth Enabled | `C10:v=1` | Enable (`1`) / disable (`0`) Bluetooth. Restart required. | +| `C11` — WiFi Enabled | `C11:v=1` | Enable (`1`) / disable (`0`) WiFi. Restart required. | +| `C12` — WiFi Access Mode | `C12:v=1` | Access mode: `0`=AP, `1`=Client. | +| `C13` — WiFi SSID | `C13:RouterName` | Set WiFi SSID (client mode only). Value directly, no `v=` prefix. | +| `C14` — WiFi Password | `C14:Password123` | Set WiFi password. Value directly, no `v=` prefix. | +| `C15` — WiFi Port | `C15:v=80` | Set listen port. Default `80`. Zero or invalid → error. | +| `C16` — WiFi Connection State | `C16` | Returns current `WifiConnectionState` value. Read-only, no params. | +| `C17` — WiFi AP IP Address | `C17:192.168.4.1` | Set AP-mode IP address. Value directly, no `v=` prefix. | +| `C18` — RTC Pins | `C18:dat=4;clk=5;rst=6` | Set DS1302 RTC pins. Use `255` for any pin not fitted. Call `C0` to persist. | +| ~~`C19`~~ | — | **Retired.** Use `N` commands (Nextion Display Configuration) instead. | +| `C20` — Timezone Offset | `C20:v=-5` | Set UTC timezone offset in hours. Valid range: −12 to +14. | +| `C21` — MMSI | `C21:123456789` | Set 9-digit Maritime Mobile Service Identity. Value directly. | +| `C22` — Call Sign | `C22:ABCD123` | Set location call sign. Value directly, truncated to max length. | +| `C23` — Home Port | `C23:Miami` | Set location home port. Value directly, truncated to max length. | +| `C24` — LED Color | `C24:t=0;c=0;r=255;g=50;b=213` | Set LED RGB color. `t`: `0`=day, `1`=night. `c`: `0`=good, `1`=bad. RGB values 0–255. | +| `C25` — LED Brightness | `C25:t=0;b=75` | Set LED brightness 0–100. `t`: `0`=day, `1`=night. | +| `C26` — LED Auto Switch | `C26:v=1` | Enable/disable automatic day/night LED switching. | +| `C27` — LED Enable States | `C27:g=1;w=1;s=0` | Enable/disable individual LEDs. `g`=GPS, `w`=Warning, `s`=System. | +| `C28` — Control Panel Tones | `C28:t=0;h=400;d=500;p=0;r=30000` | Configure tones. `t`: `0`=good, `1`=bad. `h`=Hz, `d`=duration ms. `p`=preset: `0`=custom, `1`=submarine ping, `2`=double beep, `3`=rising chirp, `4`=descending alert, `5`=nautical bell, `255`=silent. `r`=repeat interval ms (bad only, `0`=no repeat). | +| `C29` — Reload Config from SD | `C29` | **Serial only.** Reload configuration from SD card `config.txt`. | +| `C30` — Export Config to SD | `C30` | **Serial only.** Export current config to SD card. | +| `C31` — SD Card Init Speed | `C31:v=4` | Set SD card SPI init speed in MHz. Valid: 4, 8, 12, 16, 20, 24. Default 4 MHz. Higher speeds should only be used with cards that support high-speed SPI; reduce to 4–8 MHz if experiencing init failures. | +| `C32` — SD Card CS Pin | `C32:v=10` | Set SD card chip-select GPIO pin. Use `255` to disable. Call `C0` to persist. | +| `C33` — Light Sensor Threshold | `C33:v=512` | Analogue threshold (0–1023) above which daytime is detected. Default 512. Uses a rolling average of the last 10 samples; 3 consecutive consistent readings required before day/night state changes (see `S16`). | + +**C1 response sequence:** +SFB sends back: `C3` (name), `C4` (SPI pins), `C5` (home-button mappings), `C7` (location type), `C9` (sound delay), `C10`–`C17` (WiFi/Bluetooth — SFB only), `C18` (RTC pins), `C20`–`C23` (location identity), `C24`–`C27` (LED config), `C28` (tones), `N1`–`N6` (Nextion config), `R6` per relay (names), `R7` per relay (colours), `R8` per relay (default states), `R9` (linked pairs), `R10` (action types), `R11` (pins), `S0` per sensor (sensor config), then `ACK:C1=ok`. + +Common errors: `Missing param`, `Empty name`, `Invalid boat type`, `Invalid offset (-12 to +14)`, `MMSI must be 9 digits`, `Invalid speed (4, 8, 12, 16, 20, or 24 MHz)`, `Config not available`, `EEPROM commit failed`. + +### WiFi configuration commands +Route: `/api/config/{command}` +Example: `POST /api/config/C6?v=12` + +--- + +## Nextion Display Configuration Commands — `N` + +Each field is its own command so no message exceeds the 5-parameter serial limit. +**Reboot required** for changes to take effect. Every mutating ACK carries `reboot=1`. +Serial and network share the same handler — N-commands travel via `/api/config` on the network transport. | Command | Example | Purpose | |---|---|---| -| `M0` — MQTT Enabled | `M0:v=1` | Enable/disable MQTT. Param format: `v=`. `value` must be 0 (disabled) or 1 (enabled). Query without params returns current state. | -| `M1` — MQTT Broker | `M1:192.168.1.100` | Set MQTT broker address. Param format: broker address directly (no v= prefix). Supports IP addresses or hostnames. Query without params returns current broker. | -| `M2` — MQTT Port | `M2:v=1883` | Set MQTT broker port. Param format: `v=`. Default is 1883. Query without params returns current port. | -| `M3` — MQTT Username | `M3:myuser` | Set MQTT username for authentication. Param format: username directly (no v= prefix). Optional - leave empty for no authentication. Query returns "Username" or empty string if no username has been set. | -| `M4` — MQTT Password | `M4:mypassword` | Set MQTT password for authentication. Param format: password directly (no v= prefix). Optional - leave empty for no authentication. Query returns "Password" or empty string if no password has been setM4. | -| `M5` — MQTT Device ID | `M5:boat_seawolf_01` | Set unique MQTT device identifier. Param format: device ID directly (no v= prefix). Used in topic paths: `smartfusebox//...`. Query without params returns current device ID. | -| `M6` — HA Discovery | `M6:v=1` | Enable/disable Home Assistant auto-discovery. Param format: `v=`. `value` must be 0 (disabled) or 1 (enabled). When enabled, publishes discovery messages for automatic entity configuration. Query without params returns current state. | -| `M7` — Keep Alive Interval | `M7:v=60` | Set MQTT keep-alive interval in seconds. Param format: `v=`. Default is 60 seconds. Controls PING frequency. Query without params returns current interval. | -| `M8` — MQTT Connection State | `M8` | Query MQTT connection state. Returns `v=0` (disconnected/disabled) or `v=1` (connected). Read-only command, no parameters accepted. | -| `M9` — Discovery Prefix | `M9:homeassistant` | Set Home Assistant discovery prefix. Also used as the base topic prefix for all MQTT messages. Param format: prefix directly (no v= prefix). Default is "homeassistant". This prefix is used in discovery topic paths: `/switch//relay/config` and state/command topics: `//relay//state`. Query without params returns current prefix. | - -**MQTT Topic Structure:** -- Relay control: `//relay//set` (command), `//relay//state` (state) -- Sensor data: `//sensor//state` -- System info: `//system//state` -- Warnings: `//warning/active/state` -- Discovery: `/switch//relay/config` - -**Example with default prefix (homeassistant):** -- State topic: `homeassistant/smartfusebox_01/relay/0/state` -- Command topic: `homeassistant/smartfusebox_01/relay/0/set` -- Discovery topic: `homeassistant/switch/smartfusebox_01/relay0/config` - -**Example with custom prefix (myboat):** -- State topic: `myboat/boat_seawolf/relay/0/state` -- Command topic: `myboat/boat_seawolf/relay/0/set` -- Discovery topic: `myboat/switch/boat_seawolf/relay0/config` - -**Setup Example:** +| `N0` — Get Nextion Config | `N0` | Returns current config as individual `N1`–`N6` responses. No params. | +| `N1` — Enabled | `N1:v=1` | Enable (`1`) / disable (`0`) the Nextion display. Call `C0` to persist. | +| `N2` — Hardware Serial | `N2:v=1` | Serial mode: `1`=HardwareSerial, `0`=SoftwareSerial (uses rxPin/txPin). Call `C0` to persist. | +| `N3` — RX Pin | `N3:v=16` | Set RX pin. Use `255` to clear. Call `C0` to persist. | +| `N4` — TX Pin | `N4:v=17` | Set TX pin. Use `255` to clear. Call `C0` to persist. | +| `N5` — Baud Rate | `N5:v=9600` | Set serial baud rate. Call `C0` to persist. | +| `N6` — UART Number | `N6:v=1` | ESP32 UART peripheral index. Valid values: `1` or `2` (UART0 reserved for USB/debug). Call `C0` to persist. | + +**WiFi route:** `/api/config/{command}` +**Example:** `POST /api/config/N1?v=1` + +**Setup example:** ``` -M0:v=1 # Enable MQTT -M1:192.168.1.100 # Set broker IP -M5:boat_seawolf_01 # Set device ID -C0 # Save to EEPROM +N1:v=1 # Enable Nextion +N2:v=1 # Hardware serial +N3:v=16 # RX pin +N4:v=17 # TX pin +N5:v=9600 # Baud rate +N6:v=1 # UART1 +C0 # Save to EEPROM ``` -Common MQTT error responses: `Config not available`, `Invalid port`, `Invalid keep-alive interval`, `Invalid value (true/false or 0/1)`. +--- + +## External Sensor Configuration Commands — `E` +These commands configure the `RemoteSensorsConfig` array used to construct `RemoteSensor` instances dynamically at boot. Because each sensor record has more fields than the 5-parameter serial limit, add/update is split across two commands (`E1` for core fields, `E2` for MQTT detail fields). -Common error responses you may see: `Missing param`, `Missing params`, `Missing name`, `Empty name`, `Index out of range`, `Button out of range`, `Slot out of range`, `Relay out of range (or 255 to clear)`, `Invalid value (0 or 1)`, `Invalid mode (0=AP, 1=Client)`, `Only available in Client mode`, `Invalid port`, `Invalid offset (-12 to +14)`, `MMSI must be 9 digits`, `Invalid boat type`, `No available link slots`, `EEPROM commit failed`, `Unknown config command`, `Invalid type (0=day, 1=night)`, `Brightness must be 0-100`, `Invalid value (true/false or 0/1)`, `Missing params (t,r,g,b)`, `Missing params (t,b)`, `Missing params (g,w,s)`, `Missing params (sck,mosi,miso)`, `Invalid speed (4, 8, 12, 16, 20, or 24 MHz)`, `Config not available`, `Invalid port`, `Invalid keep-alive interval`. +**Reboot required** for changes to take effect. Every mutating ACK carries `reboot=1`. +| Command | Example | Purpose | +|---|---|---| +| `E0` — Get All | `E0` | Returns two frames per configured sensor (core fields then MQTT fields). No params. | +| `E1` — Set Core Fields | `E1:i=0;id=3;n=Gps;mn=Latitude;ms=latitude` | Set identity and core MQTT fields for sensor at index `i`. Params: `i=` (0-based, max `ConfigMaxSensors`); `id=` (`SensorIdList` enum value); `n=` (≤20 chars — also used as the serial command ID that external devices push updates to); `mn=` (≤15 chars); `ms=` (≤15 chars). Call `C0` to persist. | +| `E2` — Set MQTT Fields | `E2:i=0;mt=latitude;md=;mu=°;bin=0` | Set MQTT type/device-class/unit fields for sensor at index `i`. Params: `i=`; `mt=` (≤15 chars); `md=` (≤15 chars, empty to clear); `mu=` (≤10 chars, empty to clear); `bin=<0\|1>` (binary sensor flag). Call `C0` to persist. | +| `E3` — Remove | `E3:0` | Remove sensor at index. Remaining entries are compacted down. Call `C0` to persist. | +| `E4` — Rename | `E4:0=GpsUnit` | Rename sensor at index. Param format: `=`. Call `C0` to persist. | -### Wifi Configuration Commands (SFB) -Route: /api/config/{command} -Example: Set relay button color = /api/config/R7?2=4 +**`E0` response format (two frames per sensor):** +``` +E0:i=;id=;n=;mn=;ms= +E0:i=;mt=;md=;mu=;bin=<0|1> +``` +**`SensorIdList` values for the `id` parameter:** ------- +| Value | Name | +|---|---| +| `0` | WaterSensor | +| `1` | Dht11Sensor | +| `2` | LightSensor | +| `3` | GpsSensor | +| `4` | SystemSensor | +| `5` | BinaryPresenceSensor | +**How external sensors receive value updates:** +After boot the `name` field doubles as the sensor's serial command ID. An external device pushes readings by sending the name as a command: +``` +Gps:lat=51.507351;lon=-0.127758 +``` +`SensorCommandHandler` routes this to the matching `RemoteSensor` which stores the values for MQTT publishing and status queries. -## Acknowledgement Commands -These commands are used in response to receiving a command. +**WiFi route:** `/api/externalsensor/{command}` +**GET** `/api/externalsensor/` returns a JSON status block with all configured sensors. +**Example:** `POST /api/externalsensor/E1?i=0&id=3&n=Gps&mn=Latitude&ms=latitude` -| Command | Example | Purpose | -|---|---|---| -| `ACK` — Acknowledgement | `ACK:R3=Index out of range` | Indicates that the R3 command was processed and the index specified was out of range. | -| `ACK` — Acknowledgement | `ACK:R3=ok` | Indicates that the R3 command was processed successfully. | +**Setup example (GPS sensor with two channels):** +``` +E1:i=0;id=3;n=Gps;mn=Latitude;ms=latitude +E2:i=0;mt=latitude;md=;mu=°;bin=0 +E1:i=1;id=3;n=Gps;mn=Longitude;ms=longitude +E2:i=1;mt=longitude;md=;mu=°;bin=0 +C0 # Save to EEPROM — reboot to activate +``` -### Wifi Acknowledgement Commands -Not supported +Common errors: `Config not available`, `Invalid sensor index`, `Invalid or missing parameters`, `Missing sensor index`, `Missing parameters`, `Missing name`. ------- +--- +## Acknowledgement — `ACK` -## Relay Control Commands (SFB) -These commands are used to control the relays on the Boat Control Panel. Commands can be sent from a computer or generated internally by the Boat Control Panel. +Every command returns an ACK frame on the serial transport: -| Command | Example | Purpose | -|---|---|---| -| `R0` — Turn All Off | `R0` | Turns off all relays. No params. | -| `R1` — Turn All On | `R1` | Turns on all relays. No params. | -| `R2` — Retrieve States | `R2` | Retrieve the state of all relays. | -| `R3` — Relay State Set | `R3:3=1` (turn on relay 3) — `R3:5=0` (turn off relay 5) | Set the state of a specific relay. Param format: `=`. `idx` must be 0..7 (`RELAY_COUNT`). `state` must be `0` (off) or `1` (on). | -| `R4` — Relay State Get | `R4:3` (retrieves status of relay 3) — `R4:5` (returns status of relay 5). Param format: ``. `idx` must be 0..7 (`RELAY_COUNT`). | -| `R5` — Get All Relay Config | `R5` | Returns full relay configuration for all relays via a series of `R6`–`R11` responses. No params. | -| `R6` — Relay Rename | `R6:2=Bilge` or `R6:2=Bilge\|Bilge Pump` | Rename a relay. Param format: `=` or `=`. `idx` must be 0..7 (`RELAY_COUNT`). If no pipe character or long name is provided, the short name is used for both. Short name is truncated to 5 chars (used on home page), long name is truncated to 20 chars (used on buttons page). Missing name → error. | -| `R7` — Relay Set Button Color | `R7:2=4` (set relay 2 to Red when on) — `R7:2=255` (clear color) | Set the button image/color shown for a relay when it is active (on). Param format: `=`. `relay` must be 0..7 (`RELAY_COUNT`). `color` must be 0..5 (maps to BTN_COLOR_BLUE..BTN_COLOR_YELLOW) or `255` to clear. | -| `R8` — Relay Set Default State | `R8:3=1` (default on) — `R8:3=0` (default off) | Set the power-on default state of a relay. Param format: `=`. `relay` must be 0..7 (`RELAY_COUNT`), `value` must be 0 (off by default) or 1 (on by default). | -| `R9` — Relay Link | `R9:3=4` (link) — `R9:3=255` (unlink) | Link two relays together so toggling one also toggles the other. Param format: `=`. `relay` must be 0..7 (`RELAY_COUNT`). `linkedrelay` must be 0..7 or `0xFF` (255) to unlink. | -| `R10` — Relay Set Action Type | `R10:2=0` (Default) — `R10:2=1` (Horn) — `R10:2=2` (NightRelay) | Set the action type for a relay. Param format: `=`. `relay` must be 0..7 (`RELAY_COUNT`). `actionType`: `0`=Default, `1`=Horn (replaces C8 — the relay that activates the sound/horn system), `2`=NightRelay (replaces C32 — activates at night per light sensor). Only one relay may be assigned Horn and one NightRelay at a time; assigning a new relay clears the previous one automatically. | -| `R11` — Relay Set Pin | `R11:2=15` | Set the GPIO pin number for a relay. Param format: `=`. `relay` must be 0..7 (`RELAY_COUNT`). `pin` must be a valid non-zero GPIO pin number. | +| Example | Meaning | +|---|---| +| `ACK:R3=ok` | Command `R3` processed successfully. | +| `ACK:R3=Index out of range` | Command `R3` failed with the given error message. | +WiFi commands return JSON (`{"success":true}` or `{"success":false,"message":"…"}`) rather than ACK frames. -### Wifi Relay Commands (SFB) -Route: /api/relay/{command} -Example: Set relay state to on = /api/relay/R3?3=1 -Returns JSON formatted response with all relay states. +--- ------- +## Relay Control & Configuration Commands — `R` +| Command | Example | Purpose | +|---|---|---| +| `R0` — Turn All Off | `R0` | Turn off all relays. No params. | +| `R1` — Turn All On | `R1` | Turn on all relays. No params. | +| `R2` — Retrieve States | `R2` | Return the current state of all relays. | +| `R3` — Set Relay State | `R3:3=1` | Set relay `idx` on (`1`) or off (`0`). Param: `=`. `idx` 0..7. | +| `R4` — Get Relay State | `R4:3` | Return state of relay at `idx`. Param: ``. | +| `R5` — Get All Relay Config | `R5` | Returns full relay config as a series of `R6`–`R11` responses. No params. | +| `R6` — Relay Rename | `R6:2=Bilge` or `R6:2=Bilge\|Bilge Pump` | Rename relay `idx`. Param: `=` or `=`. Short ≤5 chars (home page), long ≤20 chars (buttons page). | +| `R7` — Set Button Color | `R7:2=4` or `R7:2=255` (clear) | Set active (on) button colour for relay `idx`. `0`=Blue, `1`=Green, `2`=Orange, `3`=Purple, `4`=Red, `5`=Yellow, `255`=clear. | +| `R8` — Set Default State | `R8:3=1` | Power-on default state for relay `idx`: `0`=off, `1`=on. | +| `R9` — Link Relays | `R9:3=4` (link) — `R9:3=255` (unlink) | Link two relays so toggling one toggles the other. `255` to unlink. | +| `R10` — Set Action Type | `R10:2=0` / `R10:2=1` / `R10:2=2` | Set relay action type: `0`=Default, `1`=Horn (activates sound system), `2`=NightRelay (activates at night per light sensor). Only one relay per type at a time. | +| `R11` — Set Pin | `R11:2=15` | Set GPIO pin for relay `idx`. Must be a valid non-zero pin number. | + +### WiFi relay commands +Route: `/api/relay/{command}` +Example: `POST /api/relay/R3?3=1` -## Sensor Commands -All sensor-related commands use the `S` prefix, mirroring how `R` commands own both relay runtime and relay configuration. `S0`–`S6` are sensor configuration commands; `S7`–`S22` are sensor telemetry commands. +--- -### Sensor Configuration Commands (SFB) -These commands configure the persisted sensor definitions stored in EEPROM (`SensorsConfig`). `SensorFactory` uses these definitions to construct live sensor instances at boot — **a reboot is required for any change to take effect**. Every mutating ACK carries `reboot=1` to make this explicit. +## Sound / Fog-Horn Signal Commands — `H` | Command | Example | Purpose | |---|---|---| -| `S0` — Get All Sensor Config | `S0` | Returns one `S0` response per configured sensor entry. Response format: `i=;t=;n=;p0=;p1=;o0=;o1=;en=<0\|1>`. No params. | -| `S1` — Add / Update Sensor | `S1:i=0;t=1;o0=0;o1=0` | Add or update the sensor at index `i` (0-based, up to `ConfigMaxSensors`). Param format: `i=;t=;o0=;o1=`. `t` is the `SensorIdList` enum value. `o0`, `o1` are optional (default to `PinDisabled` / 0). ACK carries `reboot=1`. | -| `S2` — Remove Sensor | `S2:0` | Remove (disable and clear) the sensor at the given index. Param format: ``. Clears name, pins, options and sets `enabled=false`. ACK carries `reboot=1`. | -| `S3` — Rename Sensor | `S3:0=Bilge Pump` | Rename the sensor at the given index. Param format: `=`. Name is truncated to `ConfigMaxSensorNameLength`. Empty name → error. ACK carries `reboot=1`. | -| `S4` — Set Sensor Pin | `S4:i=0;s=0;v=34` | Set a specific pin slot for a sensor. Param format: `i=;s=;v=`. `slot` must be 0..`ConfigMaxSensorPins-1`. `pin` is the GPIO pin number; use `PinDisabled` (255) to clear. ACK carries `reboot=1`. | -| `S5` — Set Sensor Enabled | `S5:0=1` | Enable or disable a sensor. Param format: `=<0\|1>`. ACK carries `reboot=1`. | -| `S6` — Set Sensor Option | `S6:i=0;s=0;v=1` | Set an `options1` value for a sensor. Param format: `i=;s=;v=`. `slot` must be within `options1` array bounds. `value` is a signed byte. ACK carries `reboot=1`. | +| `H0` — Cancel All | `H0` | Cancel all active sound signals immediately. No params. | +| `H1` — Is Active | `H1` | Returns active status as `=` pairs. No params. | +| `H2` — SOS / Danger | `H2` | Activates SOS horn sound (`...---...`) until cancelled. No params. | +| `H3` — Fog Signal | `H3` | Activates fog signal (location-type dependent) every 2 minutes until cancelled. No params. | +| `H4` — Manoeuvre Starboard | `H4` | One short blast. No params. | +| `H5` — Manoeuvre Port | `H5` | Two short blasts. No params. | +| `H6` — Manoeuvre Astern | `H6` | Three short blasts. No params. | +| `H7` — Manoeuvre Danger | `H7` | Five short rapid blasts. No params. | +| `H8` — Overtake Starboard | `H8` | Two prolonged + one short blast. No params. | +| `H9` — Overtake Port | `H9` | Two prolonged + two short blasts. No params. | +| `H10` — Overtake Consent | `H10` | One prolonged + one short + one prolonged + one short blast. No params. | +| `H11` — Overtake Danger | `H11` | Five short rapid blasts. No params. | +| `H12` — Test | `H12` | Test the horn/buzzer. No params. | + +### WiFi sound commands +Route: `/api/sound/{command}` +Example: `POST /api/sound/H3` + +--- -**Sensor type values (`t` parameter for S1):** +## Warning Commands — `W` -| Value | Name | Notes | +| Command | Example | Purpose | |---|---|---| -| `0` | Water Sensor | Uses `p0`=data pin, `p1`=power pin | -| `1` | Dht11 | Uses `p0`=data pin | -| `2` | Light Sensor | Uses `p0`=analogue pin; `o0=1` enables digital mode | -| `3` | System Sensor | No pin required | -| `5` | Binary Presence Sensor | `p0`=sensor pin; `o0`=active state (1=HIGH, 0=LOW), `o1`=onDetected action (SchedulerActionType value); use `S4` to set `p1`=onDetected relay/pin payload, `p2`=onClear relay/pin payload; use `S6` (option=1, slot=0) to set onClear action (SchedulerActionType value) | +| `W0` — Warnings Active | `W0:v=3` | Sent by SFB; indicates count of currently active warnings. | +| `W1` — List Warnings | `W1` | Retrieve warning list. Returns one `W2` frame per warning. No params. | +| `W2` — Warning Status | `W2:0x05=1` | Status of a single warning type. Param: `=<0\|1>`. | +| `W3` — Clear Warnings | `W3` | Clear all active warnings. No params. | +| `W4` — Set Warning Status | `W4:0x06=1` | Raise (`1`) or clear (`0`) a specific warning. Param: `=<0\|1>`. | -Common sensor config error responses: `Config not available`, `Invalid sensor index`, `Missing sensor index`, `Missing parameters`, `Missing name`, `Invalid pin slot`, `Invalid option slot`. +### WiFi warning commands +Route: `/api/warning/{command}` +Example: `GET /api/warning/W1` +--- -### Sensor Telemetry Commands -These commands carry live sensor readings. They are sent automatically by the SFB when sensor values change or at their configured polling interval. +## MQTT Configuration Commands — `M` (SFB only) -| Command | Example | Purpose | -|---|---|---| -| `S7` — Temperature | `S7:v=72.5` | Send temperature sensor data. Param format: `v=`. | -| `S8` — Humidity | `S8:v=55.2` | Send humidity sensor data. Param format: `v=`. | -| `S9` — Bearing | `S9:v=128` | Send bearing sensor data. Param format: `v=`. | -| `S10` — Direction | `S10:v=NNW` | Send direction sensor data. Param format: `v=`. | -| `S11` — Speed | `S11:v=3.4` | Send speed sensor data. Param format: `v=`. | -| `S12` — Compass Temp | `S12:v=23.4` | Send compass temperature sensor data. Param format: `v=`. | -| `S13` — Water Level | `S13:v=3.4` | Send water level sensor data. Param format: `v=`. | -| `S14` — Water Pump Active | `S14:v=1` | Send water pump active status. Param format: `v=`, 0 = off, 1 = on. | -| `S15` — Horn Active | `S15:v=1` | Send horn active status. Param format: `v=`, 0 = off, 1 = on. | -| `S16` — Light Sensor | `S16:v=1` | Send light sensor daytime value. Param format: `v=`, 0 = night, 1 = day. See C33 for threshold configuration. | -| `S17` — GPS Lat/Long | `S17:lat=51.507351;lon=-0.127758` | Send GPS latitude and longitude coordinates. Param format: `lat=;lon=`. Values are in decimal degrees with 6 decimal places precision. | -| `S18` — GPS Altitude | `S18:v=125.50` | Send GPS altitude in meters. Param format: `v=`. Value is in meters with 2 decimal places precision. | -| `S19` — GPS Speed | `S19:v=15.75;course=245.30;dir=NNW` | Send GPS speed in km/h with optional course/heading in degrees and optional direction. Param format: `v=;course=;dir=`. Speed is in kilometers per hour with 2 decimal places precision. Course is in degrees (0-360) with 2 decimal places precision, where 0° is North, 90° is East, 180° is South, and 270° is West. Direction is optional cardinal direction string (N, NE, E, SE, S, SW, W, NW, etc.). | -| `S20` — GPS Satellites | `S20:v=8` | Send GPS satellite count. Param format: `v=`. Value is the number of satellites currently connected to the GPS module. | -| `S21` — GPS Distance | `S21:v=1.23` | Send GPS distance moved. Param format: `v=`. Value is the total distance travelled according to the GPS module. | -| `S22` — Binary Presence | `S22:v=1;name=PIR` | Sent when a `BinaryPresenceSensor` pin state changes. Param format: `v=<0|1>;name=`. `v=1` = detected (pin matches configured active state), `v=0` = clear. `name` is the sensor name as configured. | - - -### Wifi Sensor Commands (SFB) -Route: /api/sensor/ -Example: Get all sensor values = /api/sensor/ - ------- - -## Warning Commands -These commands are used to send warning data from the control panel to link/computer. WarningType corresponds to the enum in WarningManager.h. +Requires `MQTT_SUPPORT` to be defined. M-commands share the `/api/config` network route. | Command | Example | Purpose | |---|---|---| -| `W0` — Warnings Active | `W0:v=3` | Number of active warnings 3 in this example. Param format: `=`. | -| `W1` — List Warnings | `W1` | Retrieve warning list. Param format: No parameters. | -| `W2` — Warning Status | `W2:0x05=1` | Sends warning status for each warning. Param format: `=`. | -| `W3` — Clear Warnings | `W3` | Clears all warning data. Param format: No Parameters. | -| `W4` — Warning Set Status | `W4:0x06=1` | Adds or removes a warning to the list of warnings. Param format: `=`. | +| `M0` — MQTT Enabled | `M0:v=1` | Enable (`1`) / disable (`0`) MQTT. Query without params returns current state. | +| `M1` — Broker Address | `M1:192.168.1.100` | Set broker hostname or IP. Value directly, no `v=` prefix. | +| `M2` — Broker Port | `M2:v=1883` | Set broker port. Default 1883. | +| `M3` — Username | `M3:myuser` | Set MQTT username. Leave empty for unauthenticated. | +| `M4` — Password | `M4:mypassword` | Set MQTT password. | +| `M5` — Device ID | `M5:boat_seawolf_01` | Set unique device identifier used in all topic paths: `//…`. | +| `M6` — HA Discovery | `M6:v=1` | Enable/disable Home Assistant auto-discovery messages. | +| `M7` — Keep-Alive | `M7:v=60` | PING interval in seconds. Default 60. | +| `M8` — Connection State | `M8` | Returns `v=0` (disconnected) or `v=1` (connected). Read-only. | +| `M9` — Discovery Prefix | `M9:homeassistant` | Base topic prefix. Default `homeassistant`. Used in all discovery and state topics. | + +**MQTT topic structure:** +- Relay state / command: `//relay//state` / `.../set` +- Sensor data: `//sensor//state` +- System metrics: `//system//state` +- Warnings: `//warning/active/state` +- HA discovery: `/switch//relay/config` + +**Setup example:** +``` +M0:v=1 # Enable MQTT +M1:192.168.1.100 # Broker IP +M5:boat_seawolf_01 # Device ID +C0 # Save to EEPROM +``` +### WiFi MQTT commands +Route: `/api/config/{command}` (shares the config route) -### Wifi Warning Commands (SFB) -Only W1 is supported via wifi. -Route: /api/warning/{command} -Example: Get all active warnings = /api/warning/W1 -Returns JSON formatted response with warning status and active warnings if any. +--- ------- +## Sensor Commands — `S` +`S0`–`S6` are configuration commands (persisted in EEPROM, **reboot required**). +`S7`–`S22` are live telemetry commands (sensor readings). -## Sound Signal Commands (Fog Horn) (SFB) -These commands are used to activate, query or deactivate signal sounds (Fog Horn Sounds). +### Sensor Configuration — `S0`–`S6` | Command | Example | Purpose | |---|---|---| -| `H0` — Cancel All | `H0` | Cancels any sound signals immediately.. Param format: No Parameters. | -| `H1` — Is Active | `H1` | Retrieves active status of sound signals in form of =. | -| `H2` — Danger | `H2` | Activates SOS horn sound until cancelled ...---... Param format: No Parameters. | -| `H3` — Fog sound | `H3` | Activates Fog sound (depending on boat type) every 2 minutes until cancelled. Param format: No Parameters. | -| `H4` — Maneuver Starboard | `H4` | Activates maneuver starboard sound. Param format: No Parameters. | -| `H5` — Maneuver Port | `H5` | Activates maneuver port sound. Param format: No Parameters. | -| `H6` — Maneuver Astern | `H6` | Activates maneuver astern sound. Param format: No Parameters. | -| `H7` — Maneuver Danger | `H7` | Activates maneuver danger sound. Param format: No Parameters. | -| `H8` — Overtake Starboard | `H8` | Activates overtake starboard sound. Param format: No Parameters. | -| `H9` — Overtake Port | `H9` | Activates overtake port sound. Param format: No Parameters. | -| `H10` — Overtake Consent | `H10` | Activates overtake consent sound. Param format: No Parameters. | -| `H11` — Overtake Danger | `H11` | Activates overtake danger sound. Param format: No Parameters. | -| `H12` — Test | `H12` | Tests the signal sound. Param format: No Parameters. | - +| `S0` — Get All Sensor Config | `S0` | Returns one `S0` frame per configured sensor: `i=;t=;n=;p0=;p1=;o0=;o1=;en=<0\|1>`. No params. | +| `S1` — Add / Update Sensor | `S1:i=0;t=1;o0=0;o1=0` | Add or update sensor at index `i`. Params: `i=;t=;o0=;o1=`. `t` = `SensorIdList` enum value. ACK carries `reboot=1`. | +| `S2` — Remove Sensor | `S2:0` | Remove (clear) sensor at index. Param: ``. ACK carries `reboot=1`. | +| `S3` — Rename Sensor | `S3:0=Bilge Pump` | Rename sensor at index. Param: `=`. ACK carries `reboot=1`. | +| `S4` — Set Sensor Pin | `S4:i=0;s=0;v=34` | Set pin slot `s` (0..`ConfigMaxSensorPins-1`) for sensor `i` to GPIO `v`. Use `255` to clear. ACK carries `reboot=1`. | +| `S5` — Set Sensor Enabled | `S5:0=1` | Enable (`1`) / disable (`0`) sensor at index. ACK carries `reboot=1`. | +| `S6` — Set Sensor Option | `S6:i=0;s=0;v=1` | Set `options1[s]` for sensor `i` to signed byte `v`. ACK carries `reboot=1`. | -### Wifi Sound Signal Commands (SFB) -Route: /api/sound/{command} -Example: Overtake danger = /api/sound/H11 -Returns JSON formatted response with sound status and active sound if any. +**Sensor type values (`t` for `S1`):** ------- +| Value | Name | Pins / Options | +|---|---|---| +| `0` | Water Sensor | `p0`=data pin, `p1`=power pin | +| `1` | DHT11 | `p0`=data pin | +| `2` | Light Sensor | `p0`=analogue pin; `o0=1` enables digital mode | +| `3` | GPS Sensor | Requires GPS serial configured via `setGpsSerial()` | +| `4` | System Sensor | No pin required | +| `5` | Binary Presence Sensor | `p0`=sensor pin; `o0`=active state (1=HIGH, 0=LOW); `o1`=onDetected action; use `S4` to set `p1`=onDetected payload, `p2`=onClear payload; use `S6` (slot=1) to set onClear action | +Common sensor config errors: `Config not available`, `Invalid sensor index`, `Missing sensor index`, `Missing parameters`, `Missing name`, `Invalid pin slot`, `Invalid option slot`. -## Timer / Scheduler Commands (SFB) -These commands manage scheduled events stored in EEPROM. Up to 20 events can be stored, if EEPROM is < 1KB only 6 events can be stored. Each event has a **trigger** (what fires it), an optional **condition** (a guard that must be true), and an **action** (what happens). +### Sensor Telemetry — `S7`–`S22` | Command | Example | Purpose | |---|---|---| -| `T0` — List events | `T0` | Returns the total event count and the enabled state of every slot. Response: `T0:count=3;s=1,0,1,0,...` where `s` is a comma-separated list of enabled states for all 20 slots. | -| `T1` — Get event | `T1:v=2` | Returns full detail of event at index `v` (0..19) using the same format as `T2`. Returns error if slot is empty. | -| `T2` — Set event | `T2:i=0;e=1;t=1,18,30,0,0;c=0,0,0,0,0;a=2,2,0,0,0` | Add or update the event at index `i` (0..19). Param format: `i=;e=;t=;c=;a=`. Each of `t`, `c`, `a` is a comma-separated list where the first value is the type enum and the remaining four are the payload bytes. Save with `C0` to persist to EEPROM. | -| `T3` — Delete event | `T3:v=2` | Clears the event at index `v` (0..19) and marks the slot as empty. Save with `C0` to persist. | -| `T4` — Enable / disable | `T4:i=2;v=1` | Enable (`v=1`) or disable (`v=0`) the event at index `i` without deleting it. Save with `C0` to persist. | -| `T5` — Clear all | `T5` | Removes all 20 scheduled events. Save with `C0` to persist. | -| `T6` — Trigger now | `T6:v=2` | Immediately executes the action of the event at index `v`, bypassing trigger and condition checks. Useful for testing. Does not affect enabled state. | - -**Parameter format for `t`, `c`, `a`:** Each is a 5-value comma-separated string: `,,,,`. The first value is the type enum; the four byte values are the payload interpreted according to that type. This compact format keeps the total parameter count within the system limit of 5. +| `S7` — Temperature | `S7:v=72.5` | Temperature reading. | +| `S8` — Humidity | `S8:v=55.2` | Humidity reading. | +| `S9` — Bearing | `S9:v=128` | Compass bearing in degrees. | +| `S10` — Direction | `S10:v=NNW` | Cardinal direction string. | +| `S11` — Speed | `S11:v=3.4` | Speed reading. | +| `S12` — Compass Temp | `S12:v=23.4` | Compass module temperature. | +| `S13` — Water Level | `S13:v=3.4` | Water level reading. | +| `S14` — Water Pump Active | `S14:v=1` | Water pump state: `0`=off, `1`=on. | +| `S15` — Horn Active | `S15:v=1` | Horn state: `0`=off, `1`=on. | +| `S16` — Light Sensor | `S16:v=1` | Daytime state: `0`=night, `1`=day. See `C33` for threshold. | +| `S17` — GPS Lat/Long | `S17:lat=51.507351;lon=-0.127758` | GPS coordinates in decimal degrees (6 d.p.). | +| `S18` — GPS Altitude | `S18:v=125.50` | Altitude in metres (2 d.p.). | +| `S19` — GPS Speed | `S19:v=15.75;course=245.30;dir=NNW` | Speed km/h, course degrees (0–360), optional cardinal direction. | +| `S20` — GPS Satellites | `S20:v=8` | Number of connected satellites. | +| `S21` — GPS Distance | `S21:v=1.23` | Total distance travelled. | +| `S22` — Binary Presence | `S22:v=1;name=PIR` | Pin-change event: `v=1` detected, `v=0` clear. `name` = configured sensor name. | + +### WiFi sensor commands +Route: `/api/sensor/` +Example: `GET /api/sensor/` --- -### Trigger Types (`t` first value) -Relay indices are 0-based (0..`RELAY_COUNT-1`). Multi-byte values use little-endian byte order (low byte first). +## Timer / Scheduler Commands — `T` -| Type | Name | Payload bytes b0..b3 | +Up to 20 scheduled events stored in EEPROM (6 max on platforms with < 1 KB EEPROM). Each event has a **trigger**, an optional **condition**, and an **action**. + +| Command | Example | Purpose | |---|---|---| -| `0` | None | Unused — event never fires automatically | -| `1` | TimeOfDay | `b0`=hour (0–23), `b1`=minute (0–59), `b2..b3`=unused | -| `2` | Sunrise | `b0..b1`=int16 offset minutes (positive=after, negative=before), `b2..b3`=unused | -| `3` | Sunset | `b0..b1`=int16 offset minutes (positive=after, negative=before), `b2..b3`=unused | -| `4` | Interval | `b0..b1`=uint16 interval in minutes, `b2..b3`=unused | -| `5` | DayOfWeek | `b0`=bitmask (bit0=Mon, bit1=Tue, bit2=Wed, bit3=Thu, bit4=Fri, bit5=Sat, bit6=Sun), `b1..b3`=unused | -| `6` | Date | `b0`=day (1–31), `b1`=month (1–12), `b2..b3`=unused | +| `T0` — List Events | `T0` | Returns `count=;s=<0,1,0,…>` — enabled state for every slot. | +| `T1` — Get Event | `T1:v=2` | Returns full detail of event at index `v` in `T2` format. Error if slot empty. | +| `T2` — Set Event | `T2:i=0;e=1;t=1,18,30,0,0;c=0,0,0,0,0;a=2,2,0,0,0` | Add/update event at index `i`. Params: `i=;e=;t=;c=;a=`. Each field is 5 comma-separated values (type + 4 payload bytes). Call `C0` to persist. | +| `T3` — Delete Event | `T3:v=2` | Clear event at index `v`. Call `C0` to persist. | +| `T4` — Enable / Disable | `T4:i=2;v=1` | Enable (`v=1`) or disable (`v=0`) event at index `i`. Call `C0` to persist. | +| `T5` — Clear All | `T5` | Remove all 20 events. Call `C0` to persist. | +| `T6` — Trigger Now | `T6:v=2` | Execute the action of event `v` immediately, bypassing trigger and condition. Useful for testing. | -**DayOfWeek bitmask examples:** Mon–Fri = `31`, weekends = `96`, all days = `127`, Monday only = `1` +**Trigger types (`t` first value):** ---- +| Type | Name | Payload `b0..b3` | +|---|---|---| +| `0` | None | Never fires automatically | +| `1` | TimeOfDay | `b0`=hour (0–23), `b1`=minute (0–59) | +| `2` | Sunrise | `b0..b1`=int16 offset minutes (+after, −before) | +| `3` | Sunset | `b0..b1`=int16 offset minutes | +| `4` | Interval | `b0..b1`=uint16 interval minutes | +| `5` | DayOfWeek | `b0`=bitmask (bit0=Mon…bit6=Sun). Mon–Fri=31, weekends=96, all days=127 | +| `6` | Date | `b0`=day (1–31), `b1`=month (1–12) | -### Condition Types (`c` first value) -Evaluated at trigger time. If the condition is not met the action is skipped. Use `c=0,0,0,0,0` for no condition. +**Condition types (`c` first value):** -| Type | Name | Payload bytes b0..b3 | +| Type | Name | Payload `b0..b3` | |---|---|---| -| `0` | None | Unused — action always fires when trigger matches | +| `0` | None | Always fires | | `1` | TimeWindow | `b0`=start_hour, `b1`=start_min, `b2`=end_hour, `b3`=end_min | -| `2` | DayOfWeek | `b0`=bitmask (same bit layout as trigger DayOfWeek), `b1..b3`=unused | -| `3` | IsDark | Unused — true when current time is between sunset and sunrise | -| `4` | IsDaylight | Unused — true when current time is between sunrise and sunset | -| `5` | RelayIsOn | `b0`=relay index (0..`RELAY_COUNT-1`), `b1..b3`=unused | -| `6` | RelayIsOff | `b0`=relay index (0..`RELAY_COUNT-1`), `b1..b3`=unused | - ---- +| `2` | DayOfWeek | `b0`=bitmask (same layout as trigger DayOfWeek) | +| `3` | IsDark | True between sunset and sunrise | +| `4` | IsDaylight | True between sunrise and sunset | +| `5` | RelayIsOn | `b0`=relay index | +| `6` | RelayIsOff | `b0`=relay index | -### Action Types (`a` first value) +**Action types (`a` first value):** -| Type | Name | Payload bytes b0..b3 | +| Type | Name | Payload `b0..b3` | |---|---|---| -| `0` | None | Unused — trigger fires but nothing happens | -| `1` | RelayOn | `b0`=relay index, `b1..b3`=unused | -| `2` | RelayOff | `b0`=relay index, `b1..b3`=unused | -| `3` | RelayToggle | `b0`=relay index, `b1..b3`=unused | -| `4` | RelayPulse | `b0`=relay index, `b1..b2`=uint16 duration seconds (little-endian), `b3`=unused | -| `5` | AllRelaysOn | Unused | -| `6` | AllRelaysOff | Unused | -| `7` | SetPinHigh | `b0`=GPIO pin number, `b1..b3`=unused — sets the pin HIGH (OUTPUT mode). Used by scheduler and `BinaryPresenceSensor` actions. | -| `8` | SetPinLow | `b0`=GPIO pin number, `b1..b3`=unused — sets the pin LOW (OUTPUT mode). Used by scheduler and `BinaryPresenceSensor` actions. | - ---- - -### Examples - -**"At 18:30 turn Relay 3 off"** (relay index 2, 0-based): +| `0` | None | Nothing happens | +| `1` | RelayOn | `b0`=relay index | +| `2` | RelayOff | `b0`=relay index | +| `3` | RelayToggle | `b0`=relay index | +| `4` | RelayPulse | `b0`=relay index, `b1..b2`=uint16 duration seconds (little-endian) | +| `5` | AllRelaysOn | — | +| `6` | AllRelaysOff | — | +| `7` | SetPinHigh | `b0`=GPIO pin number | +| `8` | SetPinLow | `b0`=GPIO pin number | + +**Examples:** ``` +# At 18:30 turn Relay 3 off (relay index 2, 0-based) T2:i=0;e=1;t=1,18,30,0,0;c=0,0,0,0,0;a=2,2,0,0,0 C0 -``` -**"10 minutes after sunset turn Relay 2 on"** (relay index 1, offset +10 min): -``` +# 10 minutes after sunset turn Relay 2 on (relay index 1, offset +10 min) T2:i=1;e=1;t=3,10,0,0,0;c=0,0,0,0,0;a=1,1,0,0,0 C0 -``` -**"At sunrise, only on weekdays, toggle Relay 1"** (relay index 0, Mon–Fri bitmask=31): -``` +# At sunrise on weekdays only, toggle Relay 1 (relay index 0, Mon–Fri bitmask=31) T2:i=2;e=1;t=2,0,0,0,0;c=2,31,0,0,0;a=3,0,0,0,0 C0 -``` -**"Every 30 minutes, pulse Relay 5 for 10 seconds"** (relay index 4, interval=30 min, duration=10 sec): -``` +# Every 30 minutes, pulse Relay 5 for 10 seconds (relay index 4, interval=30, duration=10) T2:i=3;e=1;t=4,30,0,0,0;c=0,0,0,0,0;a=4,4,10,0,0 C0 ``` -Common error responses: `Scheduler not supported`, `Index out of range`, `Missing params`, `Invalid trigger type`, `Invalid condition type`, `Invalid action type`, `Invalid enabled value (0 or 1)`, `Slot is empty`. +Common errors: `Index out of range`, `Missing params`, `Invalid trigger type`, `Invalid condition type`, `Invalid action type`, `Invalid enabled value (0 or 1)`, `Slot is empty`. -### Wifi Timer Commands (SFB) -Route: /api/timer/{command} -Example: Get all events = /api/timer/T0 -Returns JSON formatted response with event data. +### WiFi timer commands +Route: `/api/timer/{command}` +Example: `GET /api/timer/T0` ------- +--- +## Notes -## Please Note - All commands and parameters are case-sensitive. -- Commands must be sent in the exact format as specified, including any required delimiters. -- Commands specific to Boat Control Panel have (BCP) indicated next to them. -- Commands specific to Smart Fuse Box have (SFB) indicated next to them. -- All wifi commands return JSON formatted responses. +- Commands must be sent in the exact format shown, including delimiters. +- All WiFi responses are JSON formatted. +- Changes to sensor config (`S`), external sensor config (`E`), and Nextion display config (`N`) require a **reboot** to take effect. +- Always call `C0` (Save Settings) after making config changes you want to survive a power cycle. + diff --git a/SmartFuseBox/AckCommandHandler.cpp b/SmartFuseBox/AckCommandHandler.cpp index 0cb1ac7..b6ec37e 100644 --- a/SmartFuseBox/AckCommandHandler.cpp +++ b/SmartFuseBox/AckCommandHandler.cpp @@ -19,16 +19,23 @@ #include "SystemFunctions.h" #include "ConfigController.h" +#if defined(NEXTION_DISPLAY_DEVICE) +#include +#include "BasePage.h" +#endif const char AckCommand[] = "ACK"; -#if defined(BOAT_CONTROL_PANEL) -AckCommandHandler::AckCommandHandler(BroadcastManager* broadcastManager, NextionControl* nextionControl, WarningManager* warningManager) - : BaseBoatCommandHandler(broadcastManager, nextionControl, warningManager) -#elif defined(FUSE_BOX_CONTROLLER) -AckCommandHandler::AckCommandHandler(BroadcastManager* broadcastManager, WarningManager* warningManager) - : SharedBaseCommandHandler(broadcastManager, warningManager), _configController(nullptr) +AckCommandHandler::AckCommandHandler(BroadcastManager* broadcastManager, +#if defined(NEXTION_DISPLAY_DEVICE) + NextionControl* nextionControl, +#endif + WarningManager* warningManager) + : BaseNextionCommandHandler(broadcastManager, +#if defined(NEXTION_DISPLAY_DEVICE) + nextionControl, #endif + warningManager) { } @@ -92,18 +99,8 @@ bool AckCommandHandler::handleCommand(SerialCommandManager* sender, const char* // only process known ACK keys if you need to take action -#if defined(BOAT_CONTROL_PANEL) - if (SystemFunctions::commandMatches(params[0].key, SystemHeartbeatCommand) && strcmp(params[0].value, AckSuccess) == 0) - { - // Heartbeat acknowledgement - processHeartbeatAck(sender, params[0].key, params[0].value); - } - else if (strcmp(params[0].key, WarningsList) == 0 && strcmp(params[0].value, AckSuccess) == 0) - { - // Warnings list acknowledgement - merge remote warnings - processWarningsListAck(sender, params[0].key, params[0].value, params, paramCount); - } - else if (strcmp(params[0].key, RelayRetrieveStates) == 0 && strcmp(params[0].value, AckSuccess) == 0) +#if defined(NEXTION_DISPLAY_DEVICE) + if (strcmp(params[0].key, RelayRetrieveStates) == 0 && strcmp(params[0].value, AckSuccess) == 0) { // Relay state acknowledgement - handle both formats: // 1. ACK:R2=ok (just acknowledgement, no relay state - paramCount == 1) @@ -180,7 +177,7 @@ bool AckCommandHandler::handleCommand(SerialCommandManager* sender, const char* sendDebugMessage(F("Invalid F2 ACK format free memory"), AckCommand); } } -#elif defined(FUSE_BOX_CONTROLLER) +#else processConfigAck(sender, params[0].key, params[0].value); #endif diff --git a/SmartFuseBox/AckCommandHandler.h b/SmartFuseBox/AckCommandHandler.h index b994b01..8305258 100644 --- a/SmartFuseBox/AckCommandHandler.h +++ b/SmartFuseBox/AckCommandHandler.h @@ -21,13 +21,17 @@ #include "Local.h" #include "ConfigManager.h" -#include "SharedBaseCommandHandler.h" +#include "BaseNextionCommandHandler.h" + +#if defined(NEXTION_DISPLAY_DEVICE) +#include +#endif // Forward declarations class ConfigController; -class AckCommandHandler : public SharedBaseCommandHandler +class AckCommandHandler : public BaseNextionCommandHandler { private: bool processConfigAck(SerialCommandManager* sender, const char* key, const char* value); @@ -35,7 +39,11 @@ class AckCommandHandler : public SharedBaseCommandHandler ConfigController* _configController; public: - explicit AckCommandHandler(BroadcastManager* broadcastManager, WarningManager* warningManager); + explicit AckCommandHandler(BroadcastManager* broadcastManager, +#if defined(NEXTION_DISPLAY_DEVICE) + NextionControl* nextionControl, +#endif + WarningManager* warningManager); // Set the config sync manager (optional - needed for config sync feature) void setConfigController(ConfigController* configController); diff --git a/SmartFuseBox/Astronomy.h b/SmartFuseBox/Astronomy.h new file mode 100644 index 0000000..fad8e2d --- /dev/null +++ b/SmartFuseBox/Astronomy.h @@ -0,0 +1,164 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +enum class MoonPhase { + NewMoon = 0, + WaxingCrescent, + FirstQuarter, + WaxingGibbous, + FullMoon, + WaningGibbous, + LastQuarter, + WaningCrescent +}; + +// Buffer sizes for moon phase strings with a little extra space +constexpr uint8_t BufferSizeMoonPhaseName = 32; +constexpr uint8_t BufferSizeMoonPhaseDescription = 128; +constexpr uint8_t BufferSizeMoonPhaseSeaDescription = 64; + +// Moon phase names, minimum size is 16 bytes to fit longest string +static const char moon_phase_0[] PROGMEM = "New Moon"; +static const char moon_phase_1[] PROGMEM = "Waxing Crescent"; +static const char moon_phase_2[] PROGMEM = "First Quarter"; +static const char moon_phase_3[] PROGMEM = "Waxing Gibbous"; +static const char moon_phase_4[] PROGMEM = "Full Moon"; +static const char moon_phase_5[] PROGMEM = "Waning Gibbous"; +static const char moon_phase_6[] PROGMEM = "Last Quarter"; +static const char moon_phase_7[] PROGMEM = "Waning Crescent"; + +static const char* const MoonPhaseNames[] PROGMEM = { + moon_phase_0, moon_phase_1, moon_phase_2, moon_phase_3, + moon_phase_4, moon_phase_5, moon_phase_6, moon_phase_7 +}; + +// Moon phase descriptions, minimum size is 128 bytes to fit longest string +static const char moon_desc_0[] PROGMEM = "The Moon is between Earth and Sun.\r\nThe illuminated side faces away from\r\nus, so it's effectively invisible."; +static const char moon_desc_1[] PROGMEM = "A sliver of the moon becomes visible\r\nas it starts to wax."; +static const char moon_desc_2[] PROGMEM = "Half of the moon is illuminated as\r\n it continues to wax."; +static const char moon_desc_3[] PROGMEM = "More than half of the moon is\r\nilluminated, approaching full moon."; +static const char moon_desc_4[] PROGMEM = "The entire face of the moon is\r\nilluminated."; +static const char moon_desc_5[] PROGMEM = "More than half of the moon is\r\nilluminated, starting to wane."; +static const char moon_desc_6[] PROGMEM = "Half of the moon is illuminated as\r\nit continues to wane."; +static const char moon_desc_7[] PROGMEM = "A sliver of the moon remains\r\nvisible as it nears new moon."; + +static const char* const MoonPhaseDescriptions[] PROGMEM = { + moon_desc_0, moon_desc_1, moon_desc_2, moon_desc_3, + moon_desc_4, moon_desc_5, moon_desc_6, moon_desc_7 +}; + +// Moon phase sea descriptions, minimum size is 64 bytes to fit longest string +static const char moon_sea_0[] PROGMEM = "Darker nights, minimal\r\nmoonlight."; +static const char moon_sea_1[] PROGMEM = "Increasing night visibility\r\nafter sunset."; +static const char moon_sea_2[] PROGMEM = "Strong visual reference in\r\nthe evening sky."; +static const char moon_sea_3[] PROGMEM = "Bright nights, increasing\r\ntidal range."; +static const char moon_sea_4[] PROGMEM = "Brightest nights, strongest\r\nspring tides."; +static const char moon_sea_5[] PROGMEM = "Bright late-night and\r\nearly-morning light."; +static const char moon_sea_6[] PROGMEM = "Rises around midnight,\r\nvisible in early morning."; +static const char moon_sea_7[] PROGMEM = "Low pre-dawn light, calming\r\nvisual indicator of cycle reset."; + +static const char* const MoonPhaseSeaDescriptions[] PROGMEM = { + moon_sea_0, moon_sea_1, moon_sea_2, moon_sea_3, + moon_sea_4, moon_sea_5, moon_sea_6, moon_sea_7 +}; + +// average length of lunar month in days +static constexpr double SYNODIC_MONTH = 29.53058867; + +class Astronomy +{ +public: + /** + * @brief Return moon age in days (0 = new moon, ~29.53 = next new moon). + * @param unixTimestamp Seconds since epoch (UTC) + * @return Age in days (floating) + */ + static float getMoonAgeDays(unsigned long unixTimestamp) + { + // Convert Unix time to Julian Day (JD) + // JD for unix epoch: 2440587.5 + double jd = unixTimestamp / 86400.0 + 2440587.5; + // Reference new moon near Jan 6, 2000 -> JD 2451550.1 (commonly used reference) + double daysSinceRef = jd - 2451550.1; + double fraction = fmod(daysSinceRef / SYNODIC_MONTH, 1.0); + if (fraction < 0.0) fraction += 1.0; + double age = fraction * SYNODIC_MONTH; + return (float)age; + } + + /** + * @brief Calculate approximate MoonPhase from unix timestamp. + * Uses a simple astronomical approximation based on Julian date and average synodic month. + * @param unixTimestamp Seconds since epoch (UTC) + * @return MoonPhase enum value + */ + static MoonPhase getMoonPhaseFromUnix(unsigned long unixTimestamp) + { + float age = getMoonAgeDays(unixTimestamp); + // Map age into 8 equal sectors + double phaseFraction = age / SYNODIC_MONTH; // 0..1 + int index = (int)(phaseFraction * 8.0 + 0.5) % 8; + + switch (index) + { + case 0: return MoonPhase::NewMoon; + case 1: return MoonPhase::WaxingCrescent; + case 2: return MoonPhase::FirstQuarter; + case 3: return MoonPhase::WaxingGibbous; + case 4: return MoonPhase::FullMoon; + case 5: return MoonPhase::WaningGibbous; + case 6: return MoonPhase::LastQuarter; + default: return MoonPhase::WaningCrescent; + } + } + + // Copy the name from PROGMEM into a RAM buffer (safe copy) + static size_t getMoonPhaseName(MoonPhase phase, char* buffer, size_t bufferSize) + { + if (!buffer || bufferSize == 0) return 0; + uint8_t idx = static_cast(phase); + PGM_P p = reinterpret_cast(pgm_read_ptr(&MoonPhaseNames[idx])); + strncpy_P(buffer, p, bufferSize); + buffer[bufferSize - 1] = '\0'; + return strlen(buffer); + } + + static size_t getMoonPhaseDescription(MoonPhase phase, char* buffer, size_t bufferSize) + { + if (!buffer || bufferSize == 0) return 0; + uint8_t idx = static_cast(phase); + PGM_P p = reinterpret_cast(pgm_read_ptr(&MoonPhaseDescriptions[idx])); + strncpy_P(buffer, p, bufferSize); + buffer[bufferSize - 1] = '\0'; + return strlen(buffer); + } + + static size_t getMoonPhaseSeaDescription(MoonPhase phase, char* buffer, size_t bufferSize) + { + if (!buffer || bufferSize == 0) return 0; + uint8_t idx = static_cast(phase); + PGM_P p = reinterpret_cast(pgm_read_ptr(&MoonPhaseSeaDescriptions[idx])); + strncpy_P(buffer, p, bufferSize); + buffer[bufferSize - 1] = '\0'; + return strlen(buffer); + } +}; diff --git a/SmartFuseBox/BaseNextionCommandHandler.cpp b/SmartFuseBox/BaseNextionCommandHandler.cpp new file mode 100644 index 0000000..9dd7636 --- /dev/null +++ b/SmartFuseBox/BaseNextionCommandHandler.cpp @@ -0,0 +1,32 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "Local.h" +#include "BaseNextionCommandHandler.h" + +BaseNextionCommandHandler::BaseNextionCommandHandler( + BroadcastManager* broadcaster, +#if defined(NEXTION_DISPLAY_DEVICE) + NextionControl* nextionControl, +#endif + WarningManager* warningManager) + : SharedBaseCommandHandler(broadcaster, warningManager) +#if defined(NEXTION_DISPLAY_DEVICE) + , _nextionControl(nextionControl) +#endif +{ +} diff --git a/SmartFuseBox/BaseNextionCommandHandler.h b/SmartFuseBox/BaseNextionCommandHandler.h new file mode 100644 index 0000000..8c9a2a2 --- /dev/null +++ b/SmartFuseBox/BaseNextionCommandHandler.h @@ -0,0 +1,94 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +#if defined(NEXTION_DISPLAY_DEVICE) +#include +#endif + +#include "SharedBaseCommandHandler.h" +#include "WarningManager.h" + +/** + * @brief Base class for command handlers that interact with boat-specific systems. + * + * This class extends SharedBaseCommandHandler with additional boat-specific dependencies + * and helper methods used by command handlers that need to: + * - Send commands, debug messages, and errors via BroadcastManager (inherited) + * - Notify the current Nextion display page of updates + * - Access the warning management system + * - Parse common data formats (booleans, digits) + * + * Handlers like AckCommandHandler, SensorCommandHandler, and WarningCommandHandler + * should inherit from this class to get access to these shared capabilities. + * + * For handlers that only need broadcasting (like some shared command handlers), + * inherit from SharedBaseCommandHandler instead. + * For handlers that don't need any of these dependencies (like ConfigCommandHandler), + * inherit directly from BaseCommandHandler. + */ +class BaseNextionCommandHandler : public SharedBaseCommandHandler +{ +protected: + /** + * @brief Constructor with boat-specific dependencies. + * + * @param broadcaster Manager for broadcasting commands/debug/errors + * @param nextionControl Controller for interacting with Nextion display + * @param warningManager Manager for system warnings (can be nullptr if not needed) + */ + BaseNextionCommandHandler( + BroadcastManager* broadcaster, +#if defined(NEXTION_DISPLAY_DEVICE) + NextionControl* nextionControl, +#endif + WarningManager* warningManager = nullptr + ); + + /** + * @brief Notify the current display page of an external update. + * + * This is a convenience wrapper that safely gets the current page from + * NextionControl and calls handleExternalUpdate on it. + * + * @param updateType Type of update (cast to uint8_t from PageUpdateType enum) + * @param data Optional pointer to update-specific data structure + */ + void notifyCurrentPage(uint8_t updateType, const void* data) + { +#if defined(NEXTION_DISPLAY_DEVICE) + if (!_nextionControl) + return; + + BaseDisplayPage* p = _nextionControl->getCurrentPage(); + + if (!p) + return; + + p->handleExternalUpdate(updateType, data); +#endif + } + +#if defined(NEXTION_DISPLAY_DEVICE) + // Protected member variables for derived classes to access + // Note: _broadcaster is inherited from SharedBaseCommandHandler + NextionControl* _nextionControl; +#endif +}; diff --git a/SmartFuseBox/BasePage.cpp b/SmartFuseBox/BasePage.cpp new file mode 100644 index 0000000..d62c4ef --- /dev/null +++ b/SmartFuseBox/BasePage.cpp @@ -0,0 +1,80 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "BasePage.h" + +#if defined(NEXTION_DISPLAY_DEVICE) +#include +#include + +BasePage::BasePage(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BaseDisplayPage(serialPort), + _config(nullptr), + _commandMgrComputer(commandMgrComputer), + _warningManager(warningMgr) +{ +} + +BasePage::~BasePage() +{ + // Base destructor - no cleanup needed currently +} + +void BasePage::configSet(Config* config) +{ + _config = config; +} + +void BasePage::configUpdated() +{ + // Default implementation - override in derived classes if needed +} + +uint8_t BasePage::getButtonColor(uint8_t buttonIndex, bool isOn, uint8_t maxButtons) +{ + Config* config = getConfig(); + if (!config || buttonIndex >= maxButtons) + { + // Default: grey off, blue on + return isOn ? ImageButtonColorBlue : ImageButtonColorGrey; + } + + if (isOn) + { + // Check if a custom color is configured for this button + uint8_t configuredColor = config->relay.relays[buttonIndex].buttonImage; + if (configuredColor != DefaultValue && + configuredColor >= ImageButtonColorBlue && + configuredColor <= ImageButtonColorYellow) + { + return configuredColor; + } + + // Default ON color is blue + return ImageButtonColorBlue; + } + else + { + // OFF state always uses grey + return ImageButtonColorGrey; + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/BasePage.h b/SmartFuseBox/BasePage.h new file mode 100644 index 0000000..6e4b944 --- /dev/null +++ b/SmartFuseBox/BasePage.h @@ -0,0 +1,199 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include +#include "Config.h" +#include "INavigationDelegate.h" +#include "WarningManager.h" +#include "NextionIds.h" + +// Update type constants for external updates +enum class PageUpdateType : uint8_t +{ + None = 0x00, + Warning = 0x01, + RelayState = 0x02, + HeartbeatAck = 0x03, + Temperature = 0x04, + Humidity = 0x05, + Bearing = 0x06, + Direction = 0x07, + Speed = 0x08, + CompassTemp = 0x09, + WaterLevel = 0x0A, + WaterPumpActive = 0x0B, + SoundSignal = 0x0C, + CpuUsage = 0x0D, + MemoryUsage = 0x0E, + GpsLatitude = 0x0F, + GpsLongitude = 0x10, + GpsSatellites = 0x11, + GpsAltitude = 0x12, + GpsDistance = 0x13, + Daytime = 0x14, + HornActive = 0x15 +}; + +// Data structure for relay state updates +struct RelayStateUpdate { + uint8_t relayIndex; // 0-based relay index (0..7) + bool isOn; // true = relay on, false = relay off +}; + +struct FloatStateUpdate { + float value; +}; + +struct DoubleStateUpdate { + double value; +}; + +struct UInt8Update { + uint8_t value; +}; + +struct UInt16Update { + uint16_t value; +}; + +struct BoolStateUpdate { + bool value; +}; + +// Data structure for string/text updates (e.g., direction like "NNW", "SE") +struct CharStateUpdate { + static const uint8_t MaxLength = 16; // Sufficient for compass directions, status strings, etc. + uint8_t length; // Actual length of the string (not including null terminator) + char value[MaxLength]; // Fixed-size buffer for the string (null-terminated) +}; + +/** + * @class BasePage + * @brief Intermediate base class for all nextion display pages. + * + * Provides common functionality and shared state for all pages in the boat + * control panel application, including warning state management, configuration + * access, and other shared behaviors. + */ +class BasePage : public BaseDisplayPage +{ +private: + // Shared configuration pointer + Config* _config; + + // Command managers shared across all pages + SerialCommandManager* _commandMgrComputer; + + // Warning manager (shared across all pages) + WarningManager* _warningManager; + + // Navigation delegate — set by NextionFactory after construction. + INavigationDelegate* _navDelegate = nullptr; + +protected: + + /** + * @brief Constructor for boat pages. + * @param serialPort Pointer to the Nextion serial stream + * @param warningMgr Pointer to the shared WarningManager + * @param commandMgrComputer Pointer to the SerialCommandManager for computer communication (optional) + */ + explicit BasePage(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); + + /** + * @brief Virtual destructor for proper cleanup + */ + virtual ~BasePage(); + + /** + * @brief Get the configuration pointer. + * @return Pointer to the configuration, or nullptr if not set + */ + Config* getConfig() const { return _config; } + + /** + * @brief Get the computer command manager. + * @return Pointer to the computer SerialCommandManager, or nullptr if not set + */ + SerialCommandManager* getCommandMgrComputer() const { return _commandMgrComputer; } + + /** + * @brief Get the warning manager. + * @return Pointer to the WarningManager + */ + WarningManager* getWarningManager() const { return _warningManager; } + + /** + * @brief Fire a "go to next page" navigation event. + * Safe to call even if no delegate has been set. + */ + void navigateNext() { if (_navDelegate) _navDelegate->navigateNext(getPageId()); } + + /** + * @brief Fire a "go to previous page" navigation event. + * Safe to call even if no delegate has been set. + */ + void navigatePrevious() { if (_navDelegate) _navDelegate->navigatePrevious(getPageId()); } + + /** + * @brief Fire a "go to specific page" navigation event. + * The delegate silently ignores the call if the target page is not active. + * @param targetPageId Nextion page ID to navigate to. + */ + void navigateTo(uint8_t targetPageId) { if (_navDelegate) _navDelegate->navigateTo(targetPageId); } + + /** + * @brief Get the appropriate button color based on state. + * @return Color index for the button + */ + uint8_t getButtonColor(uint8_t buttonIndex, bool isOn, uint8_t maxButtons); + +public: + + /** + * @brief Set the configuration pointer for this page. + * @param config Pointer to the global configuration + */ + void configSet(Config* config); + + /** + * @brief Called when configuration has been updated. + * + * Override in derived classes to respond to configuration changes. + * Default implementation does nothing. This is public so external + * command handlers can notify pages when config changes. + */ + virtual void configUpdated(); + + /** + * @brief Inject the navigation delegate. + * Called by NextionFactory immediately after page construction. + */ + void setNavigationDelegate(INavigationDelegate* delegate) { _navDelegate = delegate; } +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/BroadcastManager.cpp b/SmartFuseBox/BroadcastManager.cpp index b5c9b67..4453f6a 100644 --- a/SmartFuseBox/BroadcastManager.cpp +++ b/SmartFuseBox/BroadcastManager.cpp @@ -18,43 +18,17 @@ #include "BroadcastManager.h" #include "BaseCommandHandler.h" -constexpr uint64_t UpdateIntervalMs = 60000; - -BroadcastManager::BroadcastManager(SerialCommandManager* computerSerial, SerialCommandManager* linkSerial) - : _computerSerial(computerSerial), _linkSerial(linkSerial), _nextUpdateTime(0) -{ -} - -void BroadcastManager::update(uint64_t now) +BroadcastManager::BroadcastManager(SerialCommandManager* computerSerial) + : _computerSerial(computerSerial) { - // Perform periodic updates every 1 minute: send configuration values to serial interfaces - if (now > _nextUpdateTime) - { - _nextUpdateTime = now + UpdateIntervalMs; - - Config* config = ConfigManager::getConfigPtr(); - - if (!config) - return; - char buffer[10]; - snprintf_P(buffer, sizeof(buffer), PSTR("v=%u"), config->sound.hornRelayIndex); - sendCommand(ConfigSoundRelayId, buffer, true); - snprintf_P(buffer, sizeof(buffer), PSTR("v=%u"), static_cast(config->location.locationType)); - sendCommand(ConfigBoatType, buffer, true); - } } -void BroadcastManager::sendCommand(const char* command, const char* params, bool linkOnly) +void BroadcastManager::sendCommand(const char* command, const char* params) { - if (_computerSerial && !linkOnly) + if (_computerSerial) { _computerSerial->sendCommand(command, params); } - - if (_linkSerial) - { - _linkSerial->sendCommand(command, params); - } } void BroadcastManager::sendCommand(const char* command, const char* message, const char* identifier, StringKeyValue* params, uint8_t argLength) @@ -63,10 +37,6 @@ void BroadcastManager::sendCommand(const char* command, const char* message, con { _computerSerial->sendCommand(command, message, identifier, params, argLength); } - if (_linkSerial) - { - _linkSerial->sendCommand(command, message, identifier, params, argLength); - } } void BroadcastManager::sendCommand(const char* command, StringKeyValue* params, uint8_t argLength) diff --git a/SmartFuseBox/BroadcastManager.h b/SmartFuseBox/BroadcastManager.h index 48ea6ae..9fcbc10 100644 --- a/SmartFuseBox/BroadcastManager.h +++ b/SmartFuseBox/BroadcastManager.h @@ -22,34 +22,20 @@ #include "SystemDefinitions.h" /** - * @brief Centralized manager for broadcasting commands to multiple serial ports. - * - * Simplifies sending commands/debug/errors to both computer (debug) and link (relay controller) - * serial connections simultaneously. + * @brief Centralized manager for broadcasting commands to the computer serial port. */ class BroadcastManager { private: - SerialCommandManager* _computerSerial; - SerialCommandManager* _linkSerial; - - uint64_t _nextUpdateTime; + SerialCommandManager* _computerSerial; public: - /** - * @brief Construct a broadcast manager with two serial command managers. - * - * @param computerSerial Pointer to computer serial (debug) manager (can be nullptr) - * @param linkSerial Pointer to link serial (relay controller) manager (can be nullptr) - */ - BroadcastManager(SerialCommandManager* computerSerial, SerialCommandManager* linkSerial); - - /** - * @brief allows for specific updates at regulated intervals. - * - * @param now current time in milliseconds - */ - void update(uint64_t now); + /** + * @brief Construct a broadcast manager. + * + * @param computerSerial Pointer to computer serial (debug) manager (can be nullptr) + */ + explicit BroadcastManager(SerialCommandManager* computerSerial); /** * @brief Send a command to all registered serial managers. @@ -57,7 +43,7 @@ class BroadcastManager * @param command The command string to send * @param params Optional parameters string (default: empty string) */ - void sendCommand(const char* command, const char* params = "", bool linkOnly = false); + void sendCommand(const char* command, const char* params = ""); /** * @brief Send a command to all registered serial managers. @@ -99,9 +85,4 @@ class BroadcastManager * @brief Get pointer to computer serial manager (for selective operations). */ SerialCommandManager* getComputerSerial() const { return _computerSerial; } - - /** - * @brief Get pointer to link serial manager (for selective operations). - */ - SerialCommandManager* getLinkSerial() const { return _linkSerial; } }; \ No newline at end of file diff --git a/SmartFuseBox/Config.h b/SmartFuseBox/Config.h index a617003..17bc655 100644 --- a/SmartFuseBox/Config.h +++ b/SmartFuseBox/Config.h @@ -127,7 +127,8 @@ constexpr uint8_t ConfigVersion2 = 2; constexpr uint8_t ConfigVersion3 = 3; constexpr uint8_t ConfigVersion4 = 4; constexpr uint8_t ConfigVersion5 = 5; -constexpr uint8_t ConfigVersion = ConfigVersion5; +constexpr uint8_t ConfigVersion6 = 6; +constexpr uint8_t ConfigVersion = ConfigVersion6; constexpr uint8_t ConfigHomeButtons = 4; constexpr uint8_t ConfigMaxNameLength = 31; // max characters (inc null) @@ -280,6 +281,30 @@ struct SensorsConfig { int8_t reserved2[2]; } __attribute__((packed)); +// XpdzTone: buzzer/piezo tone alert configuration +struct XpdzTone { + uint8_t pin; + uint8_t reserved[4]; +} __attribute__((packed)); + +// Hw479Rgb: RGB LED warning indicator pin configuration, PinDisabled = not fitted +struct Hw479Rgb { + uint8_t rPin; + uint8_t gPin; + uint8_t bPin; + uint8_t reserved1[3]; + int8_t reserved2[2]; +} __attribute__((packed)); + +// RtcConfig: DS1302 real-time clock pin configuration, PinDisabled = not fitted +struct RtcConfig { + uint8_t dataPin; // DS1302 DAT pin; + uint8_t clockPin; // DS1302 CLK pin + uint8_t resetPin; // DS1302 RST/CE pin + uint8_t reserved1[3]; + int8_t reserved2[2]; +} __attribute__((packed)); + struct SpiPins { uint8_t sckPin; uint8_t misoPin; @@ -294,6 +319,37 @@ struct NetworkAuthConfig { uint8_t reserved[4]; } __attribute__((packed)); +struct NextionConfig { + bool enabled; + bool isHardwareSerial; // if false, use SoftwareSerial on rxPin/txPin + uint8_t rxPin; + uint8_t txPin; + uint32_t baudRate; + uint8_t uartNum; // ESP32: UART peripheral index — valid values 1 or 2 only (UART0 is reserved for USB/debug) + uint8_t reserved[4]; +} __attribute__((packed)); + +struct RemoteSensorConfig +{ + SensorIdList sensorId; + char name[21]; // friendly name for UI + char mqttName[16]; + char mqttSlug[16]; + char mqttTypeSlug[16]; + char mqttDeviceClass[16]; + char mqttUnit[11]; + bool mqttIsBinary; + uint8_t reserved[8]; +} __attribute__((packed)); + + +struct RemoteSensorsConfig { + uint8_t count; + RemoteSensorConfig sensors[ConfigMaxSensors]; + uint8_t reserved1[2]; + int8_t reserved2[2]; +} __attribute__((packed)); + struct Config { uint8_t version; SystemConfig system; @@ -309,6 +365,11 @@ struct Config { SensorsConfig sensors; SpiPins spiPins; NetworkAuthConfig auth; + XpdzTone xpdzTone; + Hw479Rgb hw479Rgb; + RtcConfig rtc; + NextionConfig nextion; + RemoteSensorsConfig remoteSensors; uint8_t reserved1[2]; int8_t reserved2[2]; uint16_t checksum; // always last diff --git a/SmartFuseBox/ConfigCommandHandler.cpp b/SmartFuseBox/ConfigCommandHandler.cpp index f01c7a1..93f550c 100644 --- a/SmartFuseBox/ConfigCommandHandler.cpp +++ b/SmartFuseBox/ConfigCommandHandler.cpp @@ -97,10 +97,21 @@ bool ConfigCommandHandler::handleCommand(SerialCommandManager* sender, const cha sender->sendCommand(ConfigMapHomeButton, buffer); } + // C6 XpdzTone pin + snprintf(buffer, sizeof(buffer), "v=%u", config->xpdzTone.pin); + sender->sendCommand(ConfigXpdzTonePin, buffer); + // C7 Boat type snprintf(buffer, sizeof(buffer), "v=%d", static_cast(config->location.locationType)); sender->sendCommand(ConfigBoatType, buffer); + // C8 Hw479Rgb pins + snprintf(buffer, sizeof(buffer), "r=%u;g=%u;b=%u", + config->hw479Rgb.rPin, + config->hw479Rgb.gPin, + config->hw479Rgb.bPin); + sender->sendCommand(ConfigHw479RgbPins, buffer); + // C9 Sound start delay snprintf(buffer, sizeof(buffer), "v=%u", static_cast(config->sound.startDelayMs)); sender->sendCommand(ConfigSoundStartDelay, buffer); @@ -145,6 +156,32 @@ bool ConfigCommandHandler::handleCommand(SerialCommandManager* sender, const cha sender->sendCommand(ConfigWifiApIpAddress, buffer); } + // C18 RTC pins + snprintf(buffer, sizeof(buffer), "dat=%u;clk=%u;rst=%u", + config->rtc.dataPin, + config->rtc.clockPin, + config->rtc.resetPin); + sender->sendCommand(ConfigRtcPins, buffer); + + // N1–N6 Nextion display config (broadcast as individual commands) + snprintf(buffer, sizeof(buffer), "v=%u", config->nextion.enabled ? 1u : 0u); + sender->sendCommand(NextionEnabled, buffer); + + snprintf(buffer, sizeof(buffer), "v=%u", config->nextion.isHardwareSerial ? 1u : 0u); + sender->sendCommand(NextionHardwareSerial, buffer); + + snprintf(buffer, sizeof(buffer), "v=%u", config->nextion.rxPin); + sender->sendCommand(NextionRxPin, buffer); + + snprintf(buffer, sizeof(buffer), "v=%u", config->nextion.txPin); + sender->sendCommand(NextionTxPin, buffer); + + snprintf(buffer, sizeof(buffer), "v=%lu", config->nextion.baudRate); + sender->sendCommand(NextionBaudRate, buffer); + + snprintf(buffer, sizeof(buffer), "v=%u", config->nextion.uartNum); + sender->sendCommand(NextionUartNum, buffer); + // C20 Timezone offset snprintf(buffer, sizeof(buffer), "v=%d", config->system.timezoneOffset); sender->sendCommand(ConfigTimeZoneOffset, buffer); @@ -265,6 +302,27 @@ bool ConfigCommandHandler::handleCommand(SerialCommandManager* sender, const cha result = ConfigResult::InvalidParameter; } } + else if (SystemFunctions::commandMatches(command, ConfigXpdzTonePin)) + { + // C6 - Set XpdzTone pin + // Format: C6:v= (use 255/PinDisabled to disable) + if (paramCount >= 1) + { + uint8_t pin; + if (!getParamValueU8t(params, paramCount, "v", pin)) + { + result = ConfigResult::InvalidParameter; + } + else + { + result = _configController->setXpdzTonePin(pin); + } + } + else + { + result = ConfigResult::InvalidParameter; + } + } else if (SystemFunctions::commandMatches(command, ConfigSpiPins)) { if (paramCount >= 3) @@ -299,6 +357,29 @@ bool ConfigCommandHandler::handleCommand(SerialCommandManager* sender, const cha result = ConfigResult::InvalidParameter; } } + else if (SystemFunctions::commandMatches(command, ConfigHw479RgbPins)) + { + // C8 - Set Hw479Rgb RGB LED pins + // Format: C8:r=;g=;b= (use 255/PinDisabled to disable) + if (paramCount >= 3) + { + uint8_t rPin, gPin, bPin; + if (!getParamValueU8t(params, paramCount, "r", rPin) || + !getParamValueU8t(params, paramCount, "g", gPin) || + !getParamValueU8t(params, paramCount, "b", bPin)) + { + result = ConfigResult::InvalidParameter; + } + else + { + result = _configController->setHw479RgbPins(rPin, gPin, bPin); + } + } + else + { + result = ConfigResult::InvalidParameter; + } + } else if (SystemFunctions::commandMatches(command, ConfigSoundStartDelay)) { if (paramCount == 1) @@ -414,6 +495,29 @@ bool ConfigCommandHandler::handleCommand(SerialCommandManager* sender, const cha result = ConfigResult::InvalidParameter; } } + else if (SystemFunctions::commandMatches(command, ConfigRtcPins)) + { + // C18 - Set RtcConfig DS1302 pins + // Format: C18:dat=;clk=;rst= (use 255/PinDisabled for any unfit pin) + if (paramCount >= 3) + { + uint8_t dataPin, clockPin, resetPin; + if (!getParamValueU8t(params, paramCount, "dat", dataPin) || + !getParamValueU8t(params, paramCount, "clk", clockPin) || + !getParamValueU8t(params, paramCount, "rst", resetPin)) + { + result = ConfigResult::InvalidParameter; + } + else + { + result = _configController->setRtcPins(dataPin, clockPin, resetPin); + } + } + else + { + result = ConfigResult::InvalidParameter; + } + } else if (SystemFunctions::commandMatches(command, ConfigTimeZoneOffset)) { // C20 - Set timezone offset (hours from UTC, -12 to +14) @@ -537,7 +641,9 @@ bool ConfigCommandHandler::handleCommand(SerialCommandManager* sender, const cha // g=GPS LED, w=Warning LED, s=System LED if (paramCount >= 3) { - bool gps, warning, system; + bool gps = false; + bool warning = false; + bool system = false; if (!getParamValueBool(params, paramCount, "g", gps) || !getParamValueBool(params, paramCount, "w", warning) || @@ -870,11 +976,12 @@ const char* const* ConfigCommandHandler::supportedCommands(size_t& count) const { static const char* cmds[] = { ConfigSaveSettings, ConfigGetSettings, ConfigResetSettings, - ConfigRename, ConfigMapHomeButton, - ConfigSpiPins, - ConfigBoatType, ConfigSoundStartDelay, + ConfigRename, ConfigMapHomeButton, ConfigXpdzTonePin, + ConfigSpiPins, + ConfigBoatType, ConfigHw479RgbPins, ConfigSoundStartDelay, ConfigBluetoothEnable, ConfigWifiEnable, ConfigWifiMode, ConfigWifiSSID, ConfigWifiPassword, ConfigWifiPort, ConfigWifiState, ConfigWifiApIpAddress, + ConfigRtcPins, #if defined(MQTT_SUPPORT) MqttConfigEnable, MqttConfigBroker, MqttConfigPort, MqttConfigUsername, MqttConfigPassword, MqttConfigDeviceId, MqttConfigHADiscovery, MqttConfigKeepAlive, diff --git a/SmartFuseBox/ConfigController.cpp b/SmartFuseBox/ConfigController.cpp index c97319d..468bad6 100644 --- a/SmartFuseBox/ConfigController.cpp +++ b/SmartFuseBox/ConfigController.cpp @@ -22,13 +22,10 @@ #include "IWifiController.h" #include "SystemFunctions.h" -#if defined(FUSE_BOX_CONTROLLER) // from NextionIds.h constexpr uint8_t ImageButtonColorBlue = 2; constexpr uint8_t ImageButtonColorYellow = 7; -#endif - ConfigController::ConfigController(SoundController* soundController, IBluetoothRadio* bluetoothRadio, @@ -483,4 +480,96 @@ ConfigResult ConfigController::setLightSensorThreshold(const uint16_t threshold) _config->lightSensor.daytimeThreshold = threshold; return ConfigResult::Success; +} + +// C6: Set XpdzTone pin +ConfigResult ConfigController::setXpdzTonePin(const uint8_t pin) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + _config->xpdzTone.pin = pin; + return ConfigResult::Success; +} + +// C8: Set Hw479Rgb RGB LED pins +ConfigResult ConfigController::setHw479RgbPins(const uint8_t rPin, const uint8_t gPin, const uint8_t bPin) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + _config->hw479Rgb.rPin = rPin; + _config->hw479Rgb.gPin = gPin; + _config->hw479Rgb.bPin = bPin; + return ConfigResult::Success; +} + +// C18: Set RtcConfig DS1302 pins +ConfigResult ConfigController::setRtcPins(const uint8_t dataPin, const uint8_t clockPin, const uint8_t resetPin) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + _config->rtc.dataPin = dataPin; + _config->rtc.clockPin = clockPin; + _config->rtc.resetPin = resetPin; + return ConfigResult::Success; +} + +// C19 (N commands): Set NextionConfig display configuration — individual setters +ConfigResult ConfigController::setNextionEnabled(const bool enabled) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + _config->nextion.enabled = enabled; + return ConfigResult::Success; +} + +ConfigResult ConfigController::setNextionHardwareSerial(const bool isHardwareSerial) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + _config->nextion.isHardwareSerial = isHardwareSerial; + return ConfigResult::Success; +} + +ConfigResult ConfigController::setNextionRxPin(const uint8_t rxPin) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + _config->nextion.rxPin = rxPin; + return ConfigResult::Success; +} + +ConfigResult ConfigController::setNextionTxPin(const uint8_t txPin) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + _config->nextion.txPin = txPin; + return ConfigResult::Success; +} + +ConfigResult ConfigController::setNextionBaudRate(const uint32_t baudRate) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + _config->nextion.baudRate = baudRate; + return ConfigResult::Success; +} + +ConfigResult ConfigController::setNextionUartNum(const uint8_t uartNum) +{ + if (_config == nullptr) + return ConfigResult::InvalidConfig; + + if (uartNum != 1 && uartNum != 2) + return ConfigResult::InvalidParameter; + + _config->nextion.uartNum = uartNum; + return ConfigResult::Success; } \ No newline at end of file diff --git a/SmartFuseBox/ConfigController.h b/SmartFuseBox/ConfigController.h index 136ce7b..fcf1f03 100644 --- a/SmartFuseBox/ConfigController.h +++ b/SmartFuseBox/ConfigController.h @@ -81,4 +81,13 @@ class ConfigController ConfigResult setSdCardCsPin(const uint8_t csPin); ConfigResult setSpiPins(const uint8_t sckPin, const uint8_t mosiPin, const uint8_t misoPin); ConfigResult setLightSensorThreshold(const uint16_t threshold); + ConfigResult setXpdzTonePin(const uint8_t pin); + ConfigResult setHw479RgbPins(const uint8_t rPin, const uint8_t gPin, const uint8_t bPin); + ConfigResult setRtcPins(const uint8_t dataPin, const uint8_t clockPin, const uint8_t resetPin); + ConfigResult setNextionEnabled(const bool enabled); + ConfigResult setNextionHardwareSerial(const bool isHardwareSerial); + ConfigResult setNextionRxPin(const uint8_t rxPin); + ConfigResult setNextionTxPin(const uint8_t txPin); + ConfigResult setNextionBaudRate(const uint32_t baudRate); + ConfigResult setNextionUartNum(const uint8_t uartNum); }; \ No newline at end of file diff --git a/SmartFuseBox/ConfigManager.cpp b/SmartFuseBox/ConfigManager.cpp index c16d614..2689dec 100644 --- a/SmartFuseBox/ConfigManager.cpp +++ b/SmartFuseBox/ConfigManager.cpp @@ -32,21 +32,21 @@ SystemHeader ConfigManager::_hdr; void ConfigManager::begin() { - // On ESP platforms, EEPROM needs begin(size) + // On ESP platforms, EEPROM needs begin(size) #if defined(ESP8266) || defined(ESP32) size_t requiredBytes = sizeof(SystemHeader) + sizeof(Config); - EEPROM.begin(requiredBytes); + EEPROM.begin(requiredBytes); #endif } uint16_t ConfigManager::calcChecksum(const Config& c) { - // simple 16-bit sum over bytes excluding checksum field - const uint8_t* p = reinterpret_cast(&c); - size_t bytes = sizeof(Config) - sizeof(c.checksum); - uint32_t sum = 0; - for (size_t i = 0; i < bytes; ++i) sum += p[i]; - return static_cast(sum & 0xFFFF); + // simple 16-bit sum over bytes excluding checksum field + const uint8_t* p = reinterpret_cast(&c); + size_t bytes = sizeof(Config) - sizeof(c.checksum); + uint32_t sum = 0; + for (size_t i = 0; i < bytes; ++i) sum += p[i]; + return static_cast(sum & 0xFFFF); } bool ConfigManager::load() @@ -85,6 +85,11 @@ bool ConfigManager::load() migrateV4toV5(); migrated = true; } + else if (_cfg.version == ConfigVersion5) + { + migrateV5toV6(); + migrated = true; + } else { // Version is below the oldest known migration (e.g. 0x00 on some blank boards). @@ -125,148 +130,195 @@ bool ConfigManager::load() void ConfigManager::migrateV1toV2() { - // V1 -> V2: SchedulerSettings was appended immediately before checksum. - // Every V1 field occupies the same byte position in V2, so no data moves are needed. - // The first two bytes of scheduler overlap where the V1 checksum was written; - // the remainder of scheduler extends into EEPROM that V1 never touched. - // Zero the entire scheduler section so all events start disabled and the - // reserved bytes are clean before the new checksum is calculated by save(). - memset(&_cfg.scheduler, 0x00, sizeof(_cfg.scheduler)); - _cfg.version = ConfigVersion2; + // V1 -> V2: SchedulerSettings was appended immediately before checksum. + // Every V1 field occupies the same byte position in V2, so no data moves are needed. + // The first two bytes of scheduler overlap where the V1 checksum was written; + // the remainder of scheduler extends into EEPROM that V1 never touched. + // Zero the entire scheduler section so all events start disabled and the + // reserved bytes are clean before the new checksum is calculated by save(). + memset(&_cfg.scheduler, 0x00, sizeof(_cfg.scheduler)); + _cfg.version = ConfigVersion2; } void ConfigManager::migrateV2toV3() { - // V2 -> V3: SensorsConfig block appended before the top-level reserved bytes. - // All existing V2 fields occupy the same byte positions, so no data moves are needed. - // Zero the new sensors section so all entries start disabled and pins are cleared - // before the new checksum is calculated by save(). - - memset(&_cfg.sensors, 0x00, sizeof(_cfg.sensors)); - - for (uint8_t i = 0; i < ConfigMaxSensors; ++i) - { - memset(_cfg.sensors.sensors[i].pins, PinDisabled, ConfigMaxSensorPins); - } - - _cfg.version = ConfigVersion3; + // V2 -> V3: SensorsConfig block appended before the top-level reserved bytes. + // All existing V2 fields occupy the same byte positions, so no data moves are needed. + // Zero the new sensors section so all entries start disabled and pins are cleared + // before the new checksum is calculated by save(). + + memset(&_cfg.sensors, 0x00, sizeof(_cfg.sensors)); + + for (uint8_t i = 0; i < ConfigMaxSensors; ++i) + { + memset(_cfg.sensors.sensors[i].pins, PinDisabled, ConfigMaxSensorPins); + } + + _cfg.version = ConfigVersion3; } void ConfigManager::migrateV3toV4() { - // V3 -> V4: RelayActionType field added to RelayEntry (consumes 1 reserved byte). - // Promote hornRelayIndex -> Horn actionType, nightRelayIndex -> NightRelay actionType. - for (uint8_t i = 0; i < ConfigRelayCount; ++i) - { - _cfg.relay.relays[i].actionType = RelayActionType::Default; - } - - if (_cfg.sound.hornRelayIndex < ConfigRelayCount) - { - _cfg.relay.relays[_cfg.sound.hornRelayIndex].actionType = RelayActionType::Horn; - } - - if (_cfg.lightSensor.nightRelayIndex < ConfigRelayCount) - { - _cfg.relay.relays[_cfg.lightSensor.nightRelayIndex].actionType = RelayActionType::NightRelay; - } - - _cfg.version = ConfigVersion4; + // V3 -> V4: RelayActionType field added to RelayEntry (consumes 1 reserved byte). + // Promote hornRelayIndex -> Horn actionType, nightRelayIndex -> NightRelay actionType. + for (uint8_t i = 0; i < ConfigRelayCount; ++i) + { + _cfg.relay.relays[i].actionType = RelayActionType::Default; + } + + if (_cfg.sound.hornRelayIndex < ConfigRelayCount) + { + _cfg.relay.relays[_cfg.sound.hornRelayIndex].actionType = RelayActionType::Horn; + } + + if (_cfg.lightSensor.nightRelayIndex < ConfigRelayCount) + { + _cfg.relay.relays[_cfg.lightSensor.nightRelayIndex].actionType = RelayActionType::NightRelay; + } + + _cfg.version = ConfigVersion4; } void ConfigManager::migrateV4toV5() { - // V4 -> V5: introduced user configurable SPI pins - _cfg.spiPins.misoPin = PinDisabled; - _cfg.spiPins.mosiPin = PinDisabled; - _cfg.spiPins.sckPin = PinDisabled; - _cfg.auth.enabled = false; - _cfg.auth.version = 0; - _cfg.auth.apiKey[0] = '\0'; - _cfg.auth.hmacKey[0] = '\0'; - memset(_cfg.auth.reserved, 0x00, sizeof(_cfg.auth.reserved)); - - _cfg.version = ConfigVersion5; - _cfg.system.rebootOnSave = false; - _cfg.sdCard.csPin = PinDisabled; + // V4 -> V5: introduced user configurable SPI pins + _cfg.spiPins.misoPin = PinDisabled; + _cfg.spiPins.mosiPin = PinDisabled; + _cfg.spiPins.sckPin = PinDisabled; + _cfg.auth.enabled = false; + _cfg.auth.version = 0; + _cfg.auth.apiKey[0] = '\0'; + _cfg.auth.hmacKey[0] = '\0'; + memset(_cfg.auth.reserved, 0x00, sizeof(_cfg.auth.reserved)); + + _cfg.version = ConfigVersion5; + _cfg.system.rebootOnSave = false; + _cfg.sdCard.csPin = PinDisabled; +} + +void ConfigManager::migrateV5toV6() +{ + // V5 -> V6: XpdzTone, Hw479Rgb and RtcConfig added; all pins default to PinDisabled + _cfg.xpdzTone.pin = PinDisabled; + + _cfg.hw479Rgb.rPin = PinDisabled; + _cfg.hw479Rgb.gPin = PinDisabled; + _cfg.hw479Rgb.bPin = PinDisabled; + memset(_cfg.hw479Rgb.reserved1, 0x00, sizeof(_cfg.hw479Rgb.reserved1)); + memset(_cfg.hw479Rgb.reserved2, 0x00, sizeof(_cfg.hw479Rgb.reserved2)); + + _cfg.rtc.dataPin = PinDisabled; + _cfg.rtc.clockPin = PinDisabled; + _cfg.rtc.resetPin = PinDisabled; + memset(_cfg.rtc.reserved1, 0x00, sizeof(_cfg.rtc.reserved1)); + memset(_cfg.rtc.reserved2, 0x00, sizeof(_cfg.rtc.reserved2)); + + _cfg.nextion.enabled = false; + _cfg.nextion.isHardwareSerial = true; + _cfg.nextion.baudRate = 19200; + _cfg.nextion.uartNum = 2; + _cfg.nextion.rxPin = PinDisabled; + _cfg.nextion.txPin = PinDisabled; + + _cfg.remoteSensors.count = 0; + _cfg.remoteSensors.reserved1[0] = 0x00; + _cfg.remoteSensors.reserved1[1] = 0x00; + _cfg.remoteSensors.reserved2[0] = 0x00; + _cfg.remoteSensors.reserved2[1] = 0x00; + + for (uint8_t i = 0; i < ConfigMaxSensors; ++i) + { + _cfg.remoteSensors.sensors[i].sensorId = SensorIdList::None; + // Clear fixed-size string buffers + memset(_cfg.remoteSensors.sensors[i].name, 0x00, sizeof(_cfg.remoteSensors.sensors[i].name)); + memset(_cfg.remoteSensors.sensors[i].mqttName, 0x00, sizeof(_cfg.remoteSensors.sensors[i].mqttName)); + memset(_cfg.remoteSensors.sensors[i].mqttSlug, 0x00, sizeof(_cfg.remoteSensors.sensors[i].mqttSlug)); + memset(_cfg.remoteSensors.sensors[i].mqttTypeSlug, 0x00, sizeof(_cfg.remoteSensors.sensors[i].mqttTypeSlug)); + memset(_cfg.remoteSensors.sensors[i].mqttDeviceClass, 0x00, sizeof(_cfg.remoteSensors.sensors[i].mqttDeviceClass)); + memset(_cfg.remoteSensors.sensors[i].mqttUnit, 0x00, sizeof(_cfg.remoteSensors.sensors[i].mqttUnit)); + _cfg.remoteSensors.sensors[i].mqttIsBinary = false; + memset(_cfg.remoteSensors.sensors[i].reserved, 0x00, sizeof(_cfg.remoteSensors.sensors[i].reserved)); + } + + _cfg.version = ConfigVersion6; } bool ConfigManager::save() { - // prepare checksum - _cfg.version = ConfigVersion; - _cfg.checksum = 0; - _cfg.checksum = calcChecksum(_cfg); + // prepare checksum + _cfg.version = ConfigVersion; + _cfg.checksum = 0; + _cfg.checksum = calcChecksum(_cfg); - EEPROM.put(sizeof(SystemHeader), _cfg); + EEPROM.put(sizeof(SystemHeader), _cfg); #if defined(ESP8266) || defined(ESP32) - bool ok = EEPROM.commit(); - return ok; + bool ok = EEPROM.commit(); + return ok; #else - // AVR writes immediately; assume OK - return true; + // AVR writes immediately; assume OK + return true; #endif } void ConfigManager::resetToDefaults() { - // Erase to safe defaults - memset(&_cfg, 0x00, sizeof(_cfg)); - _cfg.version = ConfigVersion; - - // Default boat name - strncpy_P(_cfg.location.name, DefaultBoatName, ConfigMaxNameLength - 1); - _cfg.location.name[ConfigMaxNameLength - 1] = '\0'; // Ensure null termination - - for (uint8_t i = 0; i < ConfigRelayCount; ++i) - { - char shortBuf[ConfigShortRelayNameLength]{ 0 }; - snprintf_P(shortBuf, sizeof(shortBuf), RelayNameShort, (unsigned)i); - strncpy(_cfg.relay.relays[i].shortName, shortBuf, ConfigShortRelayNameLength - 1); - _cfg.relay.relays[i].shortName[ConfigShortRelayNameLength - 1] = '\0'; - - char longBuf[ConfigLongRelayNameLength]{ 0 }; - snprintf_P(longBuf, sizeof(longBuf), RelayNameLong, (unsigned)i); - strncpy(_cfg.relay.relays[i].longName, longBuf, ConfigLongRelayNameLength - 1); - _cfg.relay.relays[i].longName[ConfigLongRelayNameLength - 1] = '\0'; - - _cfg.relay.relays[i].buttonImage = 0x02; // default color blue - _cfg.relay.relays[i].defaultState = false; // default off (relay closed) - _cfg.relay.relays[i].pin = PinDisabled; // no pin assigned - } - - _cfg.sdCard.csPin = PinDisabled; - - // Reset linked relay table - for (uint8_t i = 0; i < ConfigMaxLinkedRelays; ++i) - { - _cfg.relay.linkedRelays[i][0] = PinDisabled; - _cfg.relay.linkedRelays[i][1] = PinDisabled; - } - - // Default home page mapping: first four relays visible in order - for (uint8_t i = 0; i < ConfigHomeButtons; ++i) - { - _cfg.relay.homePageMapping[i] = i; // map slot i -> relay i - } + // Erase to safe defaults + memset(&_cfg, 0x00, sizeof(_cfg)); + _cfg.version = ConfigVersion; + + // Default boat name + strncpy_P(_cfg.location.name, DefaultBoatName, ConfigMaxNameLength - 1); + _cfg.location.name[ConfigMaxNameLength - 1] = '\0'; // Ensure null termination + + for (uint8_t i = 0; i < ConfigRelayCount; ++i) + { + char shortBuf[ConfigShortRelayNameLength]{ 0 }; + snprintf_P(shortBuf, sizeof(shortBuf), RelayNameShort, (unsigned)i); + strncpy(_cfg.relay.relays[i].shortName, shortBuf, ConfigShortRelayNameLength - 1); + _cfg.relay.relays[i].shortName[ConfigShortRelayNameLength - 1] = '\0'; + + char longBuf[ConfigLongRelayNameLength]{ 0 }; + snprintf_P(longBuf, sizeof(longBuf), RelayNameLong, (unsigned)i); + strncpy(_cfg.relay.relays[i].longName, longBuf, ConfigLongRelayNameLength - 1); + _cfg.relay.relays[i].longName[ConfigLongRelayNameLength - 1] = '\0'; + + _cfg.relay.relays[i].buttonImage = 0x02; // default color blue + _cfg.relay.relays[i].defaultState = false; // default off (relay closed) + _cfg.relay.relays[i].pin = PinDisabled; // no pin assigned + } + + _cfg.sdCard.csPin = PinDisabled; + + // Reset linked relay table + for (uint8_t i = 0; i < ConfigMaxLinkedRelays; ++i) + { + _cfg.relay.linkedRelays[i][0] = PinDisabled; + _cfg.relay.linkedRelays[i][1] = PinDisabled; + } + + // Default home page mapping: first four relays visible in order + for (uint8_t i = 0; i < ConfigHomeButtons; ++i) + { + _cfg.relay.homePageMapping[i] = i; // map slot i -> relay i + } _cfg.location.locationType = LocationType::Other; _cfg.sound.hornRelayIndex = PinDisabled; // none - _cfg.lightSensor.nightRelayIndex = PinDisabled; // none - _cfg.lightSensor.daytimeThreshold = 512; - _cfg.sound.startDelayMs = 500; // 500ms + _cfg.lightSensor.nightRelayIndex = PinDisabled; // none + _cfg.lightSensor.daytimeThreshold = 512; + _cfg.sound.startDelayMs = 500; // 500ms - strncpy(_cfg.location.mmsi, "000000000", ConfigMmsiLength - 1); - _cfg.location.mmsi[ConfigMmsiLength - 1] = '\0'; - strncpy(_cfg.location.callSign, "NOCALL", ConfigCallSignLength - 1); - _cfg.location.callSign[ConfigCallSignLength - 1] = '\0'; - strncpy(_cfg.location.homePort, "Unknown", ConfigHomePortLength - 1); - _cfg.location.homePort[ConfigHomePortLength - 1] = '\0'; - _cfg.system.timezoneOffset = 0; // UTC - _cfg.system.rebootOnSave = false; + strncpy(_cfg.location.mmsi, "000000000", ConfigMmsiLength - 1); + _cfg.location.mmsi[ConfigMmsiLength - 1] = '\0'; + strncpy(_cfg.location.callSign, "NOCALL", ConfigCallSignLength - 1); + _cfg.location.callSign[ConfigCallSignLength - 1] = '\0'; + strncpy(_cfg.location.homePort, "Unknown", ConfigHomePortLength - 1); + _cfg.location.homePort[ConfigHomePortLength - 1] = '\0'; + _cfg.system.timezoneOffset = 0; // UTC + _cfg.system.rebootOnSave = false; - _cfg.network.bluetoothEnabled = false; + _cfg.network.bluetoothEnabled = false; _cfg.network.wifiEnabled = false; _cfg.network.accessMode = WifiMode::AccessPoint; @@ -278,15 +330,15 @@ void ConfigManager::resetToDefaults() strncpy(_cfg.network.apIpAddress, DefaultApIpAddress, sizeof(_cfg.network.apIpAddress) - 1); _cfg.network.apIpAddress[sizeof(_cfg.network.apIpAddress) - 1] = '\0'; - // Network auth defaults - _cfg.auth.enabled = false; - _cfg.auth.version = 0; - _cfg.auth.apiKey[0] = '\0'; - _cfg.auth.hmacKey[0] = '\0'; - memset(_cfg.auth.reserved, 0x00, sizeof(_cfg.auth.reserved)); + // Network auth defaults + _cfg.auth.enabled = false; + _cfg.auth.version = 0; + _cfg.auth.apiKey[0] = '\0'; + _cfg.auth.hmacKey[0] = '\0'; + memset(_cfg.auth.reserved, 0x00, sizeof(_cfg.auth.reserved)); #if defined(WIFI_SUPPORT) - _cfg.network.wifiEnabled = true; + _cfg.network.wifiEnabled = true; #endif #if defined(MQTT_SUPPORT) @@ -304,108 +356,121 @@ void ConfigManager::resetToDefaults() strncpy(_cfg.mqtt.discoveryPrefix, "homeassistant", ConfigMqttDiscoveryPrefixLength - 1); _cfg.mqtt.discoveryPrefix[ConfigMqttDiscoveryPrefixLength - 1] = '\0'; #else - _cfg.mqtt.enabled = false; + _cfg.mqtt.enabled = false; #endif - _cfg.sdCard.initializeSpeed = 4; - - _cfg.led.dayBrightness = 80; - _cfg.led.dayGoodColor[0] = 0; - _cfg.led.dayGoodColor[1] = 80; - _cfg.led.dayGoodColor[2] = 255; - _cfg.led.dayBadColor[0] = 255; - _cfg.led.dayBadColor[1] = 140; - _cfg.led.dayBadColor[2] = 0; - - _cfg.led.nightBrightness = 20; - _cfg.led.nightGoodColor[0] = 100; - _cfg.led.nightGoodColor[1] = 0; - _cfg.led.nightGoodColor[2] = 0; - _cfg.led.nightBadColor[0] = 255; - _cfg.led.nightBadColor[1] = 50; - _cfg.led.nightBadColor[2] = 0; - - _cfg.led.autoSwitch = true; - _cfg.led.gpsEnabled = true; - _cfg.led.warningEnabled = true; - _cfg.led.systemEnabled = true; - - // default sound config - _cfg.sound.goodPreset = static_cast(TonePreset::SubmarinePing); - _cfg.sound.goodToneHz = 1000; - _cfg.sound.goodDurationMs = 100; - _cfg.sound.badPreset = static_cast(TonePreset::DescendingAlert); - _cfg.sound.badToneHz = 500; - _cfg.sound.badDurationMs = 200; + _cfg.sdCard.initializeSpeed = 4; + + _cfg.led.dayBrightness = 80; + _cfg.led.dayGoodColor[0] = 0; + _cfg.led.dayGoodColor[1] = 80; + _cfg.led.dayGoodColor[2] = 255; + _cfg.led.dayBadColor[0] = 255; + _cfg.led.dayBadColor[1] = 140; + _cfg.led.dayBadColor[2] = 0; + + _cfg.led.nightBrightness = 20; + _cfg.led.nightGoodColor[0] = 100; + _cfg.led.nightGoodColor[1] = 0; + _cfg.led.nightGoodColor[2] = 0; + _cfg.led.nightBadColor[0] = 255; + _cfg.led.nightBadColor[1] = 50; + _cfg.led.nightBadColor[2] = 0; + + _cfg.led.autoSwitch = true; + _cfg.led.gpsEnabled = true; + _cfg.led.warningEnabled = true; + _cfg.led.systemEnabled = true; + + // default sound config + _cfg.sound.goodPreset = static_cast(TonePreset::SubmarinePing); + _cfg.sound.goodToneHz = 1000; + _cfg.sound.goodDurationMs = 100; + _cfg.sound.badPreset = static_cast(TonePreset::DescendingAlert); + _cfg.sound.badToneHz = 500; + _cfg.sound.badDurationMs = 200; _cfg.sound.badRepeatMs = 60000; - for (uint8_t i = 0; i < ConfigMaxSensors; ++i) - memset(_cfg.sensors.sensors[i].pins, PinDisabled, ConfigMaxSensorPins); + for (uint8_t i = 0; i < ConfigMaxSensors; ++i) + memset(_cfg.sensors.sensors[i].pins, PinDisabled, ConfigMaxSensorPins); + + // spi pins + _cfg.spiPins.misoPin = PinDisabled; + _cfg.spiPins.mosiPin = PinDisabled; + _cfg.spiPins.sckPin = PinDisabled; + + // XpdzTone defaults (pin unset; sensible tone parameters) + _cfg.xpdzTone.pin = PinDisabled; - // spi pins - _cfg.spiPins.misoPin = PinDisabled; - _cfg.spiPins.mosiPin = PinDisabled; - _cfg.spiPins.sckPin = PinDisabled; + // Hw479Rgb defaults (all pins unset) + _cfg.hw479Rgb.rPin = PinDisabled; + _cfg.hw479Rgb.gPin = PinDisabled; + _cfg.hw479Rgb.bPin = PinDisabled; - // compute checksum - _cfg.checksum = 0; - _cfg.checksum = calcChecksum(_cfg); + // RtcConfig defaults (all pins unset) + _cfg.rtc.dataPin = PinDisabled; + _cfg.rtc.clockPin = PinDisabled; + _cfg.rtc.resetPin = PinDisabled; + + // compute checksum + _cfg.checksum = 0; + _cfg.checksum = calcChecksum(_cfg); } Config* ConfigManager::getConfigPtr() { - return &_cfg; + return &_cfg; } size_t ConfigManager::availableEEPROMBytes() { - return EEPROM.length(); + return EEPROM.length(); } bool ConfigManager::loadHeader() { - EEPROM.get(0, _hdr); + EEPROM.get(0, _hdr); - if (_hdr.magic != SystemHeaderMagic || calcHeaderChecksum(_hdr) != _hdr.checksum) - { - memset(&_hdr, 0x00, sizeof(_hdr)); - _hdr.magic = SystemHeaderMagic; - _hdr.headerVersion = 1; - saveHeader(); - } + if (_hdr.magic != SystemHeaderMagic || calcHeaderChecksum(_hdr) != _hdr.checksum) + { + memset(&_hdr, 0x00, sizeof(_hdr)); + _hdr.magic = SystemHeaderMagic; + _hdr.headerVersion = 1; + saveHeader(); + } - return true; + return true; } bool ConfigManager::saveHeader() { - _hdr.checksum = 0; - _hdr.checksum = calcHeaderChecksum(_hdr); - EEPROM.put(0, _hdr); + _hdr.checksum = 0; + _hdr.checksum = calcHeaderChecksum(_hdr); + EEPROM.put(0, _hdr); #if defined(ESP8266) || defined(ESP32) - return EEPROM.commit(); + return EEPROM.commit(); #else - return true; + return true; #endif } void ConfigManager::resetCrashCounter() { - _hdr.crashCounter = 0; - _hdr.bootCount++; - saveHeader(); + _hdr.crashCounter = 0; + _hdr.bootCount++; + saveHeader(); } SystemHeader* ConfigManager::getHeaderPtr() { - return &_hdr; + return &_hdr; } uint16_t ConfigManager::calcHeaderChecksum(const SystemHeader& h) { - const uint8_t* p = reinterpret_cast(&h); - size_t bytes = sizeof(SystemHeader) - sizeof(h.checksum); - uint32_t sum = 0; - for (size_t i = 0; i < bytes; ++i) sum += p[i]; - return static_cast(sum & 0xFFFF); + const uint8_t* p = reinterpret_cast(&h); + size_t bytes = sizeof(SystemHeader) - sizeof(h.checksum); + uint32_t sum = 0; + for (size_t i = 0; i < bytes; ++i) sum += p[i]; + return static_cast(sum & 0xFFFF); } \ No newline at end of file diff --git a/SmartFuseBox/ConfigManager.h b/SmartFuseBox/ConfigManager.h index 3a27944..ceb3a19 100644 --- a/SmartFuseBox/ConfigManager.h +++ b/SmartFuseBox/ConfigManager.h @@ -32,6 +32,7 @@ class ConfigManager static void migrateV2toV3(); static void migrateV3toV4(); static void migrateV4toV5(); + static void migrateV5toV6(); static Config _cfg; static SystemHeader _hdr; diff --git a/SmartFuseBox/ConfigNetworkHandler.cpp b/SmartFuseBox/ConfigNetworkHandler.cpp index 62afa45..815c256 100644 --- a/SmartFuseBox/ConfigNetworkHandler.cpp +++ b/SmartFuseBox/ConfigNetworkHandler.cpp @@ -57,7 +57,28 @@ CommandResult ConfigNetworkHandler::handleRequest(const char* method, result = ConfigResult::InvalidParameter; } } - else if (SystemFunctions::commandMatches(command, ConfigSpiPins)) + else if (SystemFunctions::commandMatches(command, ConfigXpdzTonePin)) + { + // C6 - Set XpdzTone pin + // Format: C6:v= (use 255/PinDisabled to disable) + if (paramCount >= 1) + { + uint8_t pin; + if (!getParamValueU8t(params, paramCount, "v", pin)) + { + result = ConfigResult::InvalidParameter; + } + else + { + result = _configController->setXpdzTonePin(pin); + } + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, ConfigSpiPins)) { if (paramCount >= 3) { @@ -171,6 +192,29 @@ CommandResult ConfigNetworkHandler::handleRequest(const char* method, result = ConfigResult::InvalidParameter; } } + else if (SystemFunctions::commandMatches(command, ConfigHw479RgbPins)) + { + // C8 - Set Hw479Rgb RGB LED pins + // Format: C8:r=;g=;b= (use 255/PinDisabled to disable) + if (paramCount >= 3) + { + uint8_t rPin, gPin, bPin; + if (!getParamValueU8t(params, paramCount, "r", rPin) || + !getParamValueU8t(params, paramCount, "g", gPin) || + !getParamValueU8t(params, paramCount, "b", bPin)) + { + result = ConfigResult::InvalidParameter; + } + else + { + result = _configController->setHw479RgbPins(rPin, gPin, bPin); + } + } + else + { + result = ConfigResult::InvalidParameter; + } + } else if (SystemFunctions::commandMatches(command, ConfigSoundStartDelay)) { if (paramCount >= 1) @@ -315,6 +359,141 @@ CommandResult ConfigNetworkHandler::handleRequest(const char* method, result = ConfigResult::InvalidParameter; } } + else if (SystemFunctions::commandMatches(command, ConfigRtcPins)) + { + // C18 - Set RtcConfig DS1302 pins + // Format: C18:dat=;clk=;rst= (use 255/PinDisabled for any unfit pin) + if (paramCount >= 3) + { + uint8_t dataPin, clockPin, resetPin; + if (!getParamValueU8t(params, paramCount, "dat", dataPin) || + !getParamValueU8t(params, paramCount, "clk", clockPin) || + !getParamValueU8t(params, paramCount, "rst", resetPin)) + { + result = ConfigResult::InvalidParameter; + } + else + { + result = _configController->setRtcPins(dataPin, clockPin, resetPin); + } + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionGetConfig)) + { + // N0 - returns all nextion settings as a JSON-style response in responseBuffer + Config* config = ConfigManager::getConfigPtr(); + if (config == nullptr) + { + result = ConfigResult::InvalidConfig; + } + else + { + int len = snprintf(responseBuffer, bufferSize, + "en=%u;hw=%u;rx=%u;tx=%u;baud=%lu;uart=%u", + config->nextion.enabled ? 1u : 0u, + config->nextion.isHardwareSerial ? 1u : 0u, + config->nextion.rxPin, + config->nextion.txPin, + config->nextion.baudRate, + config->nextion.uartNum); + result = (len > 0 && len < static_cast(bufferSize)) + ? ConfigResult::Success : ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionEnabled)) + { + if (paramCount >= 1) + { + bool enabled; + if (!getParamValueBool(params, paramCount, "v", enabled)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionEnabled(enabled); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionHardwareSerial)) + { + if (paramCount >= 1) + { + bool hwSerial; + if (!getParamValueBool(params, paramCount, "v", hwSerial)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionHardwareSerial(hwSerial); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionRxPin)) + { + if (paramCount >= 1) + { + uint8_t pin; + if (!getParamValueU8t(params, paramCount, "v", pin)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionRxPin(pin); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionTxPin)) + { + if (paramCount >= 1) + { + uint8_t pin; + if (!getParamValueU8t(params, paramCount, "v", pin)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionTxPin(pin); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionBaudRate)) + { + if (paramCount >= 1) + { + uint32_t baud; + if (!getParamValueU32t(params, paramCount, "v", baud)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionBaudRate(baud); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionUartNum)) + { + if (paramCount >= 1) + { + uint8_t uartNum; + if (!getParamValueU8t(params, paramCount, "v", uartNum)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionUartNum(uartNum); + } + else + { + result = ConfigResult::InvalidParameter; + } + } else if (SystemFunctions::commandMatches(command, ConfigTimeZoneOffset)) { // C20 - Set timezone offset @@ -527,6 +706,205 @@ CommandResult ConfigNetworkHandler::handleRequest(const char* method, result = ConfigResult::InvalidParameter; } } + else if (SystemFunctions::commandMatches(command, ExternalSensorGetAll)) + { + // E0 — return all remote sensor config entries as JSON array + Config* config = ConfigManager::getConfigPtr(); + if (config == nullptr) + { + result = ConfigResult::InvalidConfig; + } + else + { + int written = snprintf(responseBuffer, bufferSize, + "\"count\":%u,\"sensors\":[", config->remoteSensors.count); + + for (uint8_t i = 0; i < config->remoteSensors.count && i < ConfigMaxSensors; i++) + { + const RemoteSensorConfig& e = config->remoteSensors.sensors[i]; + int n = snprintf(responseBuffer + written, bufferSize - written, + "%s{\"i\":%u,\"id\":%u,\"n\":\"%s\",\"mn\":\"%s\",\"ms\":\"%s\"," + "\"mt\":\"%s\",\"md\":\"%s\",\"mu\":\"%s\",\"bin\":%u}", + i > 0 ? "," : "", + i, + static_cast(e.sensorId), + e.name, + e.mqttName, + e.mqttSlug, + e.mqttTypeSlug, + e.mqttDeviceClass, + e.mqttUnit, + e.mqttIsBinary ? 1u : 0u); + if (n < 0 || written + n >= static_cast(bufferSize)) + break; + written += n; + } + + snprintf(responseBuffer + written, bufferSize - written, "]"); + result = ConfigResult::Success; + } + } + else if (SystemFunctions::commandMatches(command, ExternalSensorSetCore)) + { + // E1:i=;id=;n=;mn=;ms= + Config* config = ConfigManager::getConfigPtr(); + if (config == nullptr) + { + result = ConfigResult::InvalidConfig; + } + else + { + uint8_t idx, sensorId; + if (!getParamValueU8t(params, paramCount, "i", idx) || + !getParamValueU8t(params, paramCount, "id", sensorId) || + idx >= ConfigMaxSensors) + { + result = ConfigResult::InvalidParameter; + } + else + { + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + entry.sensorId = static_cast(sensorId); + + const char* n = getParamValue(params, paramCount, "n"); + if (n && n[0] != '\0') + { + strncpy(entry.name, n, sizeof(entry.name) - 1); + entry.name[sizeof(entry.name) - 1] = '\0'; + } + + const char* mn = getParamValue(params, paramCount, "mn"); + if (mn) + { + strncpy(entry.mqttName, mn, sizeof(entry.mqttName) - 1); + entry.mqttName[sizeof(entry.mqttName) - 1] = '\0'; + } + + const char* ms = getParamValue(params, paramCount, "ms"); + if (ms) + { + strncpy(entry.mqttSlug, ms, sizeof(entry.mqttSlug) - 1); + entry.mqttSlug[sizeof(entry.mqttSlug) - 1] = '\0'; + } + + if (idx >= config->remoteSensors.count) + config->remoteSensors.count = idx + 1; + + result = ConfigResult::Success; + } + } + } + else if (SystemFunctions::commandMatches(command, ExternalSensorSetMqtt)) + { + // E2:i=;mt=;md=;mu=;bin=<0|1> + Config* config = ConfigManager::getConfigPtr(); + if (config == nullptr) + { + result = ConfigResult::InvalidConfig; + } + else + { + uint8_t idx; + if (!getParamValueU8t(params, paramCount, "i", idx) || + idx >= ConfigMaxSensors || idx >= config->remoteSensors.count) + { + result = ConfigResult::InvalidParameter; + } + else + { + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + + const char* mt = getParamValue(params, paramCount, "mt"); + if (mt) + { + strncpy(entry.mqttTypeSlug, mt, sizeof(entry.mqttTypeSlug) - 1); + entry.mqttTypeSlug[sizeof(entry.mqttTypeSlug) - 1] = '\0'; + } + + const char* md = getParamValue(params, paramCount, "md"); + if (md) + { + strncpy(entry.mqttDeviceClass, md, sizeof(entry.mqttDeviceClass) - 1); + entry.mqttDeviceClass[sizeof(entry.mqttDeviceClass) - 1] = '\0'; + } + + const char* mu = getParamValue(params, paramCount, "mu"); + if (mu) + { + strncpy(entry.mqttUnit, mu, sizeof(entry.mqttUnit) - 1); + entry.mqttUnit[sizeof(entry.mqttUnit) - 1] = '\0'; + } + + bool isBinary; + if (getParamValueBool(params, paramCount, "bin", isBinary)) + entry.mqttIsBinary = isBinary; + + result = ConfigResult::Success; + } + } + } + else if (SystemFunctions::commandMatches(command, ExternalSensorRemove)) + { + // E3: + Config* config = ConfigManager::getConfigPtr(); + if (config == nullptr) + { + result = ConfigResult::InvalidConfig; + } + else if (paramCount < 1) + { + result = ConfigResult::InvalidParameter; + } + else + { + uint8_t idx = static_cast(atoi(params[0].value)); + if (idx >= ConfigMaxSensors || idx >= config->remoteSensors.count) + { + result = ConfigResult::InvalidParameter; + } + else + { + for (uint8_t j = idx; j + 1 < config->remoteSensors.count; j++) + config->remoteSensors.sensors[j] = config->remoteSensors.sensors[j + 1]; + + if (config->remoteSensors.count > 0) + config->remoteSensors.count--; + + memset(&config->remoteSensors.sensors[config->remoteSensors.count], 0, + sizeof(RemoteSensorConfig)); + + result = ConfigResult::Success; + } + } + } + else if (SystemFunctions::commandMatches(command, ExternalSensorRename)) + { + // E4:= + Config* config = ConfigManager::getConfigPtr(); + if (config == nullptr) + { + result = ConfigResult::InvalidConfig; + } + else if (paramCount < 1) + { + result = ConfigResult::InvalidParameter; + } + else + { + uint8_t idx = static_cast(strtoul(params[0].key, nullptr, 0)); + if (idx >= ConfigMaxSensors || idx >= config->remoteSensors.count || params[0].value[0] == '\0') + { + result = ConfigResult::InvalidParameter; + } + else + { + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + strncpy(entry.name, params[0].value, sizeof(entry.name) - 1); + entry.name[sizeof(entry.name) - 1] = '\0'; + result = ConfigResult::Success; + } + } + } else { result = ConfigResult::InvalidCommand; diff --git a/SmartFuseBox/DateTimeManager.cpp b/SmartFuseBox/DateTimeManager.cpp index fca80b5..88ebf4c 100644 --- a/SmartFuseBox/DateTimeManager.cpp +++ b/SmartFuseBox/DateTimeManager.cpp @@ -19,30 +19,31 @@ constexpr uint64_t DefaultTimestamp = 1735689600UL; -#if defined(BOAT_CONTROL_PANEL) RtcDS1302Driver DateTimeManager::_rtcDriver; -#endif uint64_t DateTimeManager::_syncedTimestamp = 0; uint64_t DateTimeManager::_syncedMillis = 0; bool DateTimeManager::_isSet = false; int8_t DateTimeManager::_timezoneOffset = 0; -void DateTimeManager::begin() +void DateTimeManager::begin(const RtcConfig& rtcConfig) { -#if defined(BOAT_CONTROL_PANEL) - _rtcDriver.begin(); - - if (_rtcDriver.isAvailable()) + if (rtcConfig.dataPin != PinDisabled && + rtcConfig.clockPin != PinDisabled && + rtcConfig.resetPin != PinDisabled) { - uint64_t rtcTimestamp = _rtcDriver.readTimestamp(); - if (rtcTimestamp > 0) + _rtcDriver.begin(rtcConfig.dataPin, rtcConfig.clockPin, rtcConfig.resetPin); + + if (_rtcDriver.isAvailable()) { - setDateTime(rtcTimestamp); - return; + uint64_t rtcTimestamp = _rtcDriver.readTimestamp(); + if (rtcTimestamp > 0) + { + setDateTime(rtcTimestamp); + return; + } } } -#endif setDateTime(DefaultTimestamp); } @@ -59,12 +60,10 @@ void DateTimeManager::setDateTime(uint64_t unixTimestamp) _syncedMillis = SystemFunctions::millis64(); _isSet = true; -#if defined(BOAT_CONTROL_PANEL) if (_rtcDriver.isAvailable()) { _rtcDriver.writeTimestamp(unixTimestamp); } -#endif } bool DateTimeManager::setDateTimeISO(const char* isoDateTime) @@ -369,7 +368,6 @@ void DateTimeManager::unixToDateTime(uint64_t unixTime, uint16_t& year, uint8_t& day = days + 1; } -#if defined(BOAT_CONTROL_PANEL) bool DateTimeManager::isRtcAvailable() { return _rtcDriver.isAvailable(); @@ -379,4 +377,3 @@ bool DateTimeManager::rtcDiagnostic(char* buffer, const uint8_t bufferLength) { return _rtcDriver.runDiagnostics(buffer, bufferLength); } -#endif diff --git a/SmartFuseBox/DateTimeManager.h b/SmartFuseBox/DateTimeManager.h index 08772d5..a731dfd 100644 --- a/SmartFuseBox/DateTimeManager.h +++ b/SmartFuseBox/DateTimeManager.h @@ -21,10 +21,9 @@ #include "Local.h" #include "SystemFunctions.h" +#include "Config.h" -#if defined(BOAT_CONTROL_PANEL) #include "RtcDS1302Driver.h" -#endif constexpr uint8_t DateTimeBufferLength = 20; // "YYYY-MM-DD HH:MM:SS" + null terminator constexpr uint8_t DateTimeISOBufferLength = 20; // "YYYY-MM-DDTHH:MM:SS" + null terminator @@ -73,12 +72,13 @@ class DateTimeManager { public: /** - * @brief Initialize the RTC hardware and read time from it. - * Call this once in setup() before any other DateTimeManager methods. - * If RTC is present and has valid time, loads it into DateTimeManager. - * If RTC is not present or time is invalid, uses default time. + * @brief Initialize with runtime RTC pin configuration from config. + * If all pins in rtcConfig are valid (not PinDisabled), attempts to + * initialize DS1302 and read time from hardware. Falls back to default + * timestamp if RTC is absent or time is invalid. + * @param rtcConfig RTC pin configuration from Config::rtc */ - static void begin(); + static void begin(const RtcConfig& rtcConfig); /** * @brief Set date/time to default value (January 1, 2025 00:00:00). @@ -209,9 +209,7 @@ class DateTimeManager static bool rtcDiagnostic(char* buffer, const uint8_t bufferLength); private: -#if defined(BOAT_CONTROL_PANEL) static RtcDS1302Driver _rtcDriver; -#endif static uint64_t _syncedTimestamp; static uint64_t _syncedMillis; static bool _isSet; diff --git a/SmartFuseBox/Environment.h b/SmartFuseBox/Environment.h new file mode 100644 index 0000000..442d349 --- /dev/null +++ b/SmartFuseBox/Environment.h @@ -0,0 +1,65 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +constexpr char comfortColdDamp[] PROGMEM = "Cold & Damp"; +constexpr char comfortCold[] PROGMEM = "Cold"; +constexpr char comfortComfortable[] PROGMEM = "Comfortable"; +constexpr char comfortWarm[] PROGMEM = "Warm"; +constexpr char comfortHotHumid[] PROGMEM = "Hot & Humid"; +constexpr char comfortHumid[] PROGMEM = "Humid"; +constexpr char comfortDry[] PROGMEM = "Dry"; +constexpr char comfortFair[] PROGMEM = "Fair"; + +enum class CondensationRisk +{ + Low, + Watch, + High +}; + + +class Environment +{ +public: + static double dewPoint(double tempC, double relativeHumidity) + { + const double a = 17.62; + const double b = 243.12; // °C + double gamma = (a * tempC) / (b + tempC) + log(relativeHumidity / 100.0); + return (b * gamma) / (a - gamma); + }; + + static double estimatedSurfaceTemp(double airTemp, bool night) + { + return night ? airTemp - 2.0 : airTemp - 0.5; + }; + + static CondensationRisk condensationRisk( + double airTemp, + double dewPt, + bool night) + { + double surfaceTemp = estimatedSurfaceTemp(airTemp, night); + double delta = surfaceTemp - dewPt; + + if (delta <= 0.5) return CondensationRisk::High; + if (delta <= 2.0) return CondensationRisk::Watch; + return CondensationRisk::Low; + } +}; \ No newline at end of file diff --git a/SmartFuseBox/ExternalSensorConfigCommandHandler.h b/SmartFuseBox/ExternalSensorConfigCommandHandler.h new file mode 100644 index 0000000..26d4fd6 --- /dev/null +++ b/SmartFuseBox/ExternalSensorConfigCommandHandler.h @@ -0,0 +1,291 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "BaseConfigCommandHandler.h" +#include "SystemDefinitions.h" +#include "Config.h" +#include "ConfigManager.h" + +/** + * @brief Command handler for remote/external sensor configuration (E-series commands). + * + * Manages the RemoteSensorsConfig array that drives dynamic RemoteSensor creation at + * boot. Because each RemoteSensorConfig has more fields than the 5-parameter limit, + * add/update is split across two commands. + * + * *** Important — reboot required *** + * RemoteSensor instances are constructed once during SmartFuseBoxApp::setup(). + * Changes here only affect persisted config; they take effect at next boot. + * Every mutating ACK carries a "reboot=1" field to make this explicit. + * + * Command reference: + * E0 — Get all external sensor config entries (broadcasts one E0 per entry) + * E1:i=;id=;n=;mn=;ms= + * — Set core fields for sensor at (0-based, up to ConfigMaxSensors) + * E2:i=;mt=;md=;mu=;bin=<0|1> + * — Set MQTT detail fields for sensor at + * E3: — Remove sensor at (clears entry, shifts count down) + * E4:= + * — Rename sensor at + */ +class ExternalSensorConfigCommandHandler : public virtual BaseCommandHandler, public BaseConfigCommandHandler +{ +private: + SerialCommandManager* _commandMgrComputer; + + void broadcastSensorConfig(SerialCommandManager* sender, uint8_t index, const RemoteSensorConfig& entry) + { + char buf[96]; + snprintf(buf, sizeof(buf), + "i=%u;id=%u;n=%s;mn=%s;ms=%s", + index, + static_cast(entry.sensorId), + entry.name, + entry.mqttName, + entry.mqttSlug); + if (_commandMgrComputer) + _commandMgrComputer->sendCommand(ExternalSensorGetAll, buf); + + snprintf(buf, sizeof(buf), + "i=%u;mt=%s;md=%s;mu=%s;bin=%u", + index, + entry.mqttTypeSlug, + entry.mqttDeviceClass, + entry.mqttUnit, + entry.mqttIsBinary ? 1u : 0u); + if (_commandMgrComputer) + _commandMgrComputer->sendCommand(ExternalSensorGetAll, buf); + } + +public: + explicit ExternalSensorConfigCommandHandler(SerialCommandManager* commandMgrComputer) + : _commandMgrComputer(commandMgrComputer) + { + } + + const char* const* supportedCommands(size_t& count) const override + { + static const char* cmds[] = { + ExternalSensorGetAll, + ExternalSensorSetCore, + ExternalSensorSetMqtt, + ExternalSensorRemove, + ExternalSensorRename + }; + count = sizeof(cmds) / sizeof(cmds[0]); + return cmds; + } + + bool handleCommand(SerialCommandManager* sender, const char* command, + const StringKeyValue params[], uint8_t paramCount) override + { + Config* config = ConfigManager::getConfigPtr(); + + if (config == nullptr) + { + sendAckErr(sender, command, F("Config not available")); + return true; + } + + if (SystemFunctions::commandMatches(command, ExternalSensorGetAll)) + { + for (uint8_t i = 0; i < config->remoteSensors.count && i < ConfigMaxSensors; i++) + { + broadcastSensorConfig(sender, i, config->remoteSensors.sensors[i]); + } + + sendAckOk(sender, command); + return true; + } + + if (SystemFunctions::commandMatches(command, ExternalSensorSetCore)) + { + // E1:i=;id=;n=;mn=;ms= + uint8_t idx, sensorId; + if (!getParamValueU8t(params, paramCount, "i", idx) || + !getParamValueU8t(params, paramCount, "id", sensorId)) + { + sendAckErr(sender, command, F("Invalid or missing parameters")); + return true; + } + + if (idx >= ConfigMaxSensors) + { + sendAckErr(sender, command, F("Invalid sensor index")); + return true; + } + + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + entry.sensorId = static_cast(sensorId); + + const char* name = getParamValue(params, paramCount, "n"); + if (name && name[0] != '\0') + { + strncpy(entry.name, name, sizeof(entry.name) - 1); + entry.name[sizeof(entry.name) - 1] = '\0'; + } + + const char* mqttName = getParamValue(params, paramCount, "mn"); + if (mqttName) + { + strncpy(entry.mqttName, mqttName, sizeof(entry.mqttName) - 1); + entry.mqttName[sizeof(entry.mqttName) - 1] = '\0'; + } + + const char* mqttSlug = getParamValue(params, paramCount, "ms"); + if (mqttSlug) + { + strncpy(entry.mqttSlug, mqttSlug, sizeof(entry.mqttSlug) - 1); + entry.mqttSlug[sizeof(entry.mqttSlug) - 1] = '\0'; + } + + if (idx >= config->remoteSensors.count) + config->remoteSensors.count = idx + 1; + + StringKeyValue reboot = makeParam("reboot", 1); + sendAckOk(sender, command, &reboot); + return true; + } + + if (SystemFunctions::commandMatches(command, ExternalSensorSetMqtt)) + { + // E2:i=;mt=;md=;mu=;bin=<0|1> + uint8_t idx; + if (!getParamValueU8t(params, paramCount, "i", idx)) + { + sendAckErr(sender, command, F("Invalid or missing parameters")); + return true; + } + + if (idx >= ConfigMaxSensors || idx >= config->remoteSensors.count) + { + sendAckErr(sender, command, F("Invalid sensor index")); + return true; + } + + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + + const char* mt = getParamValue(params, paramCount, "mt"); + if (mt) + { + strncpy(entry.mqttTypeSlug, mt, sizeof(entry.mqttTypeSlug) - 1); + entry.mqttTypeSlug[sizeof(entry.mqttTypeSlug) - 1] = '\0'; + } + + const char* md = getParamValue(params, paramCount, "md"); + if (md) + { + strncpy(entry.mqttDeviceClass, md, sizeof(entry.mqttDeviceClass) - 1); + entry.mqttDeviceClass[sizeof(entry.mqttDeviceClass) - 1] = '\0'; + } + + const char* mu = getParamValue(params, paramCount, "mu"); + if (mu) + { + strncpy(entry.mqttUnit, mu, sizeof(entry.mqttUnit) - 1); + entry.mqttUnit[sizeof(entry.mqttUnit) - 1] = '\0'; + } + + bool isBinary; + if (getParamValueBool(params, paramCount, "bin", isBinary)) + entry.mqttIsBinary = isBinary; + + StringKeyValue reboot = makeParam("reboot", 1); + sendAckOk(sender, command, &reboot); + return true; + } + + if (SystemFunctions::commandMatches(command, ExternalSensorRemove)) + { + // E3: + if (paramCount < 1) + { + sendAckErr(sender, command, F("Missing sensor index")); + return true; + } + + uint8_t idx = static_cast(atoi(params[0].value)); + + if (idx >= ConfigMaxSensors || idx >= config->remoteSensors.count) + { + sendAckErr(sender, command, F("Invalid sensor index")); + return true; + } + + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + entry.sensorId = static_cast(0); + memset(entry.name, 0, sizeof(entry.name)); + memset(entry.mqttName, 0, sizeof(entry.mqttName)); + memset(entry.mqttSlug, 0, sizeof(entry.mqttSlug)); + memset(entry.mqttTypeSlug, 0, sizeof(entry.mqttTypeSlug)); + memset(entry.mqttDeviceClass, 0, sizeof(entry.mqttDeviceClass)); + memset(entry.mqttUnit, 0, sizeof(entry.mqttUnit)); + entry.mqttIsBinary = false; + + // Compact the array: shift entries down over the removed slot + for (uint8_t j = idx; j + 1 < config->remoteSensors.count; j++) + config->remoteSensors.sensors[j] = config->remoteSensors.sensors[j + 1]; + + if (config->remoteSensors.count > 0) + config->remoteSensors.count--; + + // Zero the vacated last slot + RemoteSensorConfig& last = config->remoteSensors.sensors[config->remoteSensors.count]; + memset(&last, 0, sizeof(last)); + + StringKeyValue reboot = makeParam("reboot", 1); + sendAckOk(sender, command, &reboot); + return true; + } + + if (SystemFunctions::commandMatches(command, ExternalSensorRename)) + { + // E4:= + if (paramCount < 1) + { + sendAckErr(sender, command, F("Missing parameters")); + return true; + } + + uint8_t idx = static_cast(strtoul(params[0].key, nullptr, 0)); + + if (idx >= ConfigMaxSensors || idx >= config->remoteSensors.count) + { + sendAckErr(sender, command, F("Invalid sensor index")); + return true; + } + + if (params[0].value[0] == '\0') + { + sendAckErr(sender, command, F("Missing name")); + return true; + } + + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + strncpy(entry.name, params[0].value, sizeof(entry.name) - 1); + entry.name[sizeof(entry.name) - 1] = '\0'; + + StringKeyValue reboot = makeParam("reboot", 1); + sendAckOk(sender, command, &reboot); + return true; + } + + return false; + } +}; diff --git a/SmartFuseBox/ExternalSensorNetworkHandler.cpp b/SmartFuseBox/ExternalSensorNetworkHandler.cpp new file mode 100644 index 0000000..ecef94c --- /dev/null +++ b/SmartFuseBox/ExternalSensorNetworkHandler.cpp @@ -0,0 +1,261 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "Local.h" +#include "ExternalSensorNetworkHandler.h" + +#include "ConfigManager.h" +#include "ConfigController.h" +#include "SystemDefinitions.h" +#include "SystemFunctions.h" + +CommandResult ExternalSensorNetworkHandler::handleRequest(const char* method, + const char* command, + StringKeyValue* params, + uint8_t paramCount, + char* responseBuffer, + size_t bufferSize) +{ + (void)method; + + Config* config = ConfigManager::getConfigPtr(); + if (!config) + { + formatJsonResponse(responseBuffer, bufferSize, false, "Config not available"); + return CommandResult::error(InvalidConfiguration); + } + + ConfigResult result = ConfigResult::InvalidCommand; + + if (SystemFunctions::commandMatches(command, ExternalSensorGetAll) || command[0] == '\0') + { + // E0 — return all remote sensor config entries + int written = snprintf(responseBuffer, bufferSize, + "\"success\":true,\"count\":%u,\"sensors\":[", config->remoteSensors.count); + + for (uint8_t i = 0; i < config->remoteSensors.count && i < ConfigMaxSensors; i++) + { + const RemoteSensorConfig& e = config->remoteSensors.sensors[i]; + int n = snprintf(responseBuffer + written, bufferSize - written, + "%s{\"i\":%u,\"id\":%u,\"n\":\"%s\",\"mn\":\"%s\",\"ms\":\"%s\"," + "\"mt\":\"%s\",\"md\":\"%s\",\"mu\":\"%s\",\"bin\":%u}", + i > 0 ? "," : "", + i, + static_cast(e.sensorId), + e.name, + e.mqttName, + e.mqttSlug, + e.mqttTypeSlug, + e.mqttDeviceClass, + e.mqttUnit, + e.mqttIsBinary ? 1u : 0u); + if (n < 0 || written + n >= static_cast(bufferSize)) + break; + written += n; + } + + snprintf(responseBuffer + written, bufferSize - written, "]"); + return CommandResult::ok(); + } + else if (SystemFunctions::commandMatches(command, ExternalSensorSetCore)) + { + // E1:i=;id=;n=;mn=;ms= + uint8_t idx, sensorId; + if (!getParamValueU8t(params, paramCount, "i", idx) || + !getParamValueU8t(params, paramCount, "id", sensorId) || + idx >= ConfigMaxSensors) + { + result = ConfigResult::InvalidParameter; + } + else + { + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + entry.sensorId = static_cast(sensorId); + + const char* n = getParamValue(params, paramCount, "n"); + if (n && n[0] != '\0') + { + strncpy(entry.name, n, sizeof(entry.name) - 1); + entry.name[sizeof(entry.name) - 1] = '\0'; + } + + const char* mn = getParamValue(params, paramCount, "mn"); + if (mn) + { + strncpy(entry.mqttName, mn, sizeof(entry.mqttName) - 1); + entry.mqttName[sizeof(entry.mqttName) - 1] = '\0'; + } + + const char* ms = getParamValue(params, paramCount, "ms"); + if (ms) + { + strncpy(entry.mqttSlug, ms, sizeof(entry.mqttSlug) - 1); + entry.mqttSlug[sizeof(entry.mqttSlug) - 1] = '\0'; + } + + if (idx >= config->remoteSensors.count) + config->remoteSensors.count = idx + 1; + + result = ConfigResult::Success; + } + } + else if (SystemFunctions::commandMatches(command, ExternalSensorSetMqtt)) + { + // E2:i=;mt=;md=;mu=;bin=<0|1> + uint8_t idx; + if (!getParamValueU8t(params, paramCount, "i", idx) || + idx >= ConfigMaxSensors || idx >= config->remoteSensors.count) + { + result = ConfigResult::InvalidParameter; + } + else + { + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + + const char* mt = getParamValue(params, paramCount, "mt"); + if (mt) + { + strncpy(entry.mqttTypeSlug, mt, sizeof(entry.mqttTypeSlug) - 1); + entry.mqttTypeSlug[sizeof(entry.mqttTypeSlug) - 1] = '\0'; + } + + const char* md = getParamValue(params, paramCount, "md"); + if (md) + { + strncpy(entry.mqttDeviceClass, md, sizeof(entry.mqttDeviceClass) - 1); + entry.mqttDeviceClass[sizeof(entry.mqttDeviceClass) - 1] = '\0'; + } + + const char* mu = getParamValue(params, paramCount, "mu"); + if (mu) + { + strncpy(entry.mqttUnit, mu, sizeof(entry.mqttUnit) - 1); + entry.mqttUnit[sizeof(entry.mqttUnit) - 1] = '\0'; + } + + bool isBinary; + if (getParamValueBool(params, paramCount, "bin", isBinary)) + entry.mqttIsBinary = isBinary; + + result = ConfigResult::Success; + } + } + else if (SystemFunctions::commandMatches(command, ExternalSensorRemove)) + { + // E3: + if (paramCount < 1) + { + result = ConfigResult::InvalidParameter; + } + else + { + uint8_t idx = static_cast(atoi(params[0].value)); + if (idx >= ConfigMaxSensors || idx >= config->remoteSensors.count) + { + result = ConfigResult::InvalidParameter; + } + else + { + for (uint8_t j = idx; j + 1 < config->remoteSensors.count; j++) + config->remoteSensors.sensors[j] = config->remoteSensors.sensors[j + 1]; + + if (config->remoteSensors.count > 0) + config->remoteSensors.count--; + + memset(&config->remoteSensors.sensors[config->remoteSensors.count], 0, + sizeof(RemoteSensorConfig)); + + result = ConfigResult::Success; + } + } + } + else if (SystemFunctions::commandMatches(command, ExternalSensorRename)) + { + // E4:= + if (paramCount < 1) + { + result = ConfigResult::InvalidParameter; + } + else + { + uint8_t idx = static_cast(strtoul(params[0].key, nullptr, 0)); + if (idx >= ConfigMaxSensors || idx >= config->remoteSensors.count || params[0].value[0] == '\0') + { + result = ConfigResult::InvalidParameter; + } + else + { + RemoteSensorConfig& entry = config->remoteSensors.sensors[idx]; + strncpy(entry.name, params[0].value, sizeof(entry.name) - 1); + entry.name[sizeof(entry.name) - 1] = '\0'; + result = ConfigResult::Success; + } + } + } + + if (result == ConfigResult::Success) + return CommandResult::ok(); + + return CommandResult::error(static_cast(result)); +} + +void ExternalSensorNetworkHandler::formatStatusJson(IWifiClient* client) +{ + Config* config = ConfigManager::getConfigPtr(); + if (!config) + return; + + client->print("\"externalSensors\":{"); + client->print("\"count\":"); + client->print(config->remoteSensors.count); + client->print(",\"sensors\":["); + + for (uint8_t i = 0; i < config->remoteSensors.count && i < ConfigMaxSensors; i++) + { + const RemoteSensorConfig& e = config->remoteSensors.sensors[i]; + + if (i > 0) + client->print(","); + + client->print("{\"i\":"); + client->print(i); + client->print(",\"id\":"); + client->print(static_cast(e.sensorId)); + client->print(",\"n\":\""); + client->print(e.name); + client->print("\",\"mn\":\""); + client->print(e.mqttName); + client->print("\",\"ms\":\""); + client->print(e.mqttSlug); + client->print("\",\"mt\":\""); + client->print(e.mqttTypeSlug); + client->print("\",\"md\":\""); + client->print(e.mqttDeviceClass); + client->print("\",\"mu\":\""); + client->print(e.mqttUnit); + client->print("\",\"bin\":"); + client->print(e.mqttIsBinary ? "true" : "false"); + client->print("}"); + } + + client->print("]}"); +} + +void ExternalSensorNetworkHandler::formatWifiStatusJson(IWifiClient* client) +{ + formatStatusJson(client); +} diff --git a/SmartFuseBox/ExternalSensorNetworkHandler.h b/SmartFuseBox/ExternalSensorNetworkHandler.h new file mode 100644 index 0000000..b76f027 --- /dev/null +++ b/SmartFuseBox/ExternalSensorNetworkHandler.h @@ -0,0 +1,47 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "INetworkCommandHandler.h" +#include "BaseConfigCommandHandler.h" + +/** + * @brief Network handler for remote/external sensor configuration (E-series) via /api/externalsensor. + * + * Exposes the same E0–E4 operations as ExternalSensorConfigCommandHandler but over + * the WiFi REST interface. The GET route returns a JSON status block containing all + * configured external sensors. + */ +class ExternalSensorNetworkHandler : public INetworkCommandHandler, public BaseConfigCommandHandler +{ +public: + ExternalSensorNetworkHandler() = default; + + const char* getRoute() const override { return "/api/externalsensor"; } + + void formatWifiStatusJson(IWifiClient* client) override; + + void formatStatusJson(IWifiClient* client); + + CommandResult handleRequest(const char* method, + const char* command, + StringKeyValue* params, + uint8_t paramCount, + char* responseBuffer, + size_t bufferSize) override; +}; diff --git a/SmartFuseBox/GpsSensorHander.h b/SmartFuseBox/GpsSensorHandler.h similarity index 89% rename from SmartFuseBox/GpsSensorHander.h rename to SmartFuseBox/GpsSensorHandler.h index 2a16d54..b7d3ccb 100644 --- a/SmartFuseBox/GpsSensorHander.h +++ b/SmartFuseBox/GpsSensorHandler.h @@ -22,9 +22,7 @@ #include "SystemDefinitions.h" -#if defined(FUSE_BOX_CONTROLLER) #include "SmartFuseBoxConstants.h" -#endif #include "WarningManager.h" #include "WarningType.h" #include "BaseSensor.h" @@ -138,7 +136,7 @@ class GpsSensorHandler : public BaseSensor, public BroadcastLoggerSupport } // Convert to Unix timestamp using ISO format (setDateTimeISO handles the conversion) - char isoDateTime[20]; + char isoDateTime[32]; snprintf_P(isoDateTime, sizeof(isoDateTime), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), year, month, day, hour, minute, second); @@ -290,7 +288,7 @@ class GpsSensorHandler : public BaseSensor, public BroadcastLoggerSupport _lastFixTime = 0; } - unsigned long update() override + uint64_t update() override { if (!_gpsSerial || !_gps) { @@ -445,7 +443,6 @@ class GpsSensorHandler : public BaseSensor, public BroadcastLoggerSupport } } -#if defined(FUSE_BOX_CONTROLLER) void formatStatusJson(char* buffer, size_t size) override { char lat[16]; @@ -464,7 +461,6 @@ class GpsSensorHandler : public BaseSensor, public BroadcastLoggerSupport PSTR("\"gps\":{\"lat\":%s,\"lon\":%s,\"alt\":%s,\"speed\":%s,\"course\":%s,\"sats\":%lu,\"valid\":%s}"), lat, lon, alt, speed, course, _satellites, _hasValidFix ? "true" : "false"); } -#endif SensorIdList getSensorIdType() const override { @@ -497,9 +493,44 @@ class GpsSensorHandler : public BaseSensor, public BroadcastLoggerSupport const char* getDirection() const { return compassDirections[static_cast((_courseDeg + 11.25) / 22.5) % 16]; } double getTotalDistance() const { return _totalDistanceKm; } - void resetTotalDistance() + void resetTotalDistance() { - _totalDistanceKm = 0.0; - _firstFix = true; - } + _totalDistanceKm = 0.0; + _firstFix = true; + } + +#if defined(MQTT_SUPPORT) + uint8_t getMqttChannelCount() const override + { + return 6; + } + + MqttSensorChannel getMqttChannel(uint8_t channelIndex) const override + { + switch (channelIndex) + { + case 0: return { "GPS Latitude", "gps_latitude", "gps_latitude", nullptr, "°", false }; + case 1: return { "GPS Longitude", "gps_longitude", "gps_longitude", nullptr, "°", false }; + case 2: return { "GPS Altitude", "gps_altitude", "gps_altitude", nullptr, "m", false }; + case 3: return { "GPS Speed", "gps_speed", "gps_speed", nullptr, "km/h",false }; + case 4: return { "GPS Satellites", "gps_satellites", "gps_satellites", nullptr, nullptr,false }; + case 5: return { "GPS Fix", "gps_fix", "gps_fix", nullptr, nullptr,true }; + default: return { nullptr, nullptr, nullptr, nullptr, nullptr, false }; + } + } + + void getMqttValue(uint8_t channelIndex, char* buffer, size_t size) const override + { + switch (channelIndex) + { + case 0: dtostrf(_latitude, 1, 6, buffer); break; + case 1: dtostrf(_longitude, 1, 6, buffer); break; + case 2: dtostrf(_altitude, 1, 2, buffer); break; + case 3: dtostrf(_speedKmh, 1, 2, buffer); break; + case 4: snprintf(buffer, size, "%lu", _satellites); break; + case 5: snprintf(buffer, size, "%s", _hasValidFix ? "true" : "false"); break; + default: if (size > 0) buffer[0] = '\0'; break; + } + } +#endif }; \ No newline at end of file diff --git a/SmartFuseBox/INavigationDelegate.h b/SmartFuseBox/INavigationDelegate.h new file mode 100644 index 0000000..c888371 --- /dev/null +++ b/SmartFuseBox/INavigationDelegate.h @@ -0,0 +1,62 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include + +/** + * @brief Pure interface that pages use to request navigation. + * + * Pages fire navigation events through this interface rather than calling + * setPage() with hard-coded page IDs. The NavigationController implements + * this interface and resolves the correct target, skipping pages that are + * not active for the current LocationType. + */ +class INavigationDelegate +{ +public: + virtual ~INavigationDelegate() = default; + + /** + * @brief Navigate to the next page in the active page list. + * @param fromPageId The ID of the page firing the event. + */ + virtual void navigateNext(uint8_t fromPageId) = 0; + + /** + * @brief Navigate to the previous page in the active page list. + * @param fromPageId The ID of the page firing the event. + */ + virtual void navigatePrevious(uint8_t fromPageId) = 0; + + /** + * @brief Navigate directly to a specific page. + * + * If the requested page is not active (filtered out by LocationType) the + * call is silently ignored — pages never crash because a peer is absent. + * + * @param targetPageId The Nextion page ID to navigate to. + */ + virtual void navigateTo(uint8_t targetPageId) = 0; +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/Local.h b/SmartFuseBox/Local.h index 30272bf..8703d26 100644 --- a/SmartFuseBox/Local.h +++ b/SmartFuseBox/Local.h @@ -59,11 +59,10 @@ #undef CONFIGURE_SPI #endif -// ─── Controller Mode ────────────────────────────────────────────────────────── -// Undefine to use only the underlying components (WiFi, MQTT etc.) without -// the fuse box relay controller logic. -#define FUSE_BOX_CONTROLLER +// ─── Display Support ─────────────────────────────────────────────────────── +// Enable support for Nextion displays. +#define NEXTION_DISPLAY_DEVICE // ─── Optional Features ──────────────────────────────────────────────────────── // Remove the trailing underscore to enable each feature. diff --git a/SmartFuseBox/NavigationController.cpp b/SmartFuseBox/NavigationController.cpp new file mode 100644 index 0000000..b4a14ed --- /dev/null +++ b/SmartFuseBox/NavigationController.cpp @@ -0,0 +1,185 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "Local.h" +#include "NavigationController.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include "NextionIds.h" + +// --------------------------------------------------------------------------- +// Page order for the main next/previous chain. +// Maritime-only pages are included here; buildPageList() filters them out +// when the LocationType is not maritime. +// --------------------------------------------------------------------------- +static const uint8_t s_allPages[] = +{ + PageIdHome, + PageIdWarning, + PageIdRelay, + PageIdVhfRadio, // maritime + PageIdSoundSignals, // maritime + PageIdFlags, // maritime + PageIdCardinalMarkers, // maritime + PageIdBuoys, // maritime + PageIdMoonPhases, // maritime + PageIdVhfDistress, // maritime (reachable from VhfRadio button, not prev/next) + PageIdVhfChannels, // maritime (reachable from VhfRadio button, not prev/next) + PageIdSystem, + PageIdEnvironment, + PageIdAbout, + PageIdSettingsRelays, + PageIdSettings, +}; + +// Pages that are only shown for maritime LocationTypes. +static const uint8_t s_maritimePages[] = +{ + PageIdVhfRadio, + PageIdSoundSignals, + PageIdFlags, + PageIdCardinalMarkers, + PageIdBuoys, + PageIdMoonPhases, + PageIdVhfDistress, + PageIdVhfChannels, +}; + +// --------------------------------------------------------------------------- + +NavigationController::NavigationController(NextionControl* nextion, LocationType locationType) + : _nextion(nextion) +{ + buildPageList(locationType); +} + +// INavigationDelegate --------------------------------------------------------- + +void NavigationController::navigateNext(uint8_t fromPageId) +{ + if (_activePages.empty() || _nextion == nullptr) + return; + + size_t idx = indexOf(fromPageId); + + if (idx == SIZE_MAX) + { + // Current page not in the active list — go to the first active page. + setPage(_activePages[0]); + return; + } + + size_t next = (idx + 1) % _activePages.size(); + setPage(_activePages[next]); +} + +void NavigationController::navigatePrevious(uint8_t fromPageId) +{ + if (_activePages.empty() || _nextion == nullptr) + return; + + size_t idx = indexOf(fromPageId); + + if (idx == SIZE_MAX) + { + setPage(_activePages[0]); + return; + } + + size_t prev = (idx == 0) ? _activePages.size() - 1 : idx - 1; + setPage(_activePages[prev]); +} + +void NavigationController::navigateTo(uint8_t targetPageId) +{ + if (_nextion == nullptr) + return; + + // Silently ignore navigation to pages that are not active. + if (!isPageActive(targetPageId)) + return; + + setPage(targetPageId); +} + +// Public ----------------------------------------------------------------------- + +void NavigationController::rebuildForLocationType(LocationType locationType) +{ + buildPageList(locationType); +} + +bool NavigationController::isPageActive(uint8_t pageId) const +{ + return indexOf(pageId) != SIZE_MAX; +} + +// Private --------------------------------------------------------------------- + +void NavigationController::buildPageList(LocationType locationType) +{ + _activePages.clear(); + bool maritime = isMaritime(locationType); + + for (uint8_t id : s_allPages) + { + if (!maritime) + { + // Check if this page is maritime-only. + bool skip = false; + for (uint8_t m : s_maritimePages) + { + if (m == id) { skip = true; break; } + } + if (skip) + continue; + } + _activePages.push_back(id); + } +} + +size_t NavigationController::indexOf(uint8_t pageId) const +{ + for (size_t i = 0; i < _activePages.size(); ++i) + { + if (_activePages[i] == pageId) + return i; + } + return SIZE_MAX; +} + +bool NavigationController::isMaritime(LocationType lt) +{ + return lt == LocationType::Power || + lt == LocationType::Sail || + lt == LocationType::Fishing || + lt == LocationType::Yacht; +} + +void NavigationController::setPage(uint8_t pageId) const +{ + if (_nextion) + { + char cmd[16]; + snprintf(cmd, sizeof(cmd), "page %d", pageId); + _nextion->sendCommand(cmd); + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/NavigationController.h b/SmartFuseBox/NavigationController.h new file mode 100644 index 0000000..62ecc6a --- /dev/null +++ b/SmartFuseBox/NavigationController.h @@ -0,0 +1,91 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "INavigationDelegate.h" +#include "Config.h" + +/** + * @brief Centrally manages page navigation for the Nextion display. + * + * The controller holds the ordered list of page IDs that are active for the + * current LocationType. Pages fire events (next / previous / go-to) through + * INavigationDelegate; the controller resolves the correct target and calls + * NextionControl::setPage(). Pages never need to know their neighbours or + * whether a peer page exists for the current location. + * + * Maritime pages (Buoys, CardinalMarkers, Flags, Sound*, Vhf*) are only + * included in the active list when the LocationType is one of: + * Power, Sail, Fishing, Yacht + */ +class NavigationController : public INavigationDelegate +{ +private: + NextionControl* _nextion; + std::vector _activePages; // ordered list of active page IDs + + void buildPageList(LocationType locationType); + + // Returns the index of pageId in _activePages, or SIZE_MAX if not found. + size_t indexOf(uint8_t pageId) const; + + static bool isMaritime(LocationType lt); + + void setPage(uint8_t pageId) const; + +public: + /** + * @brief Construct the controller and build the initial page list. + * + * @param nextion Pointer to the NextionControl instance (non-owning). + * @param locationType The current vessel/location type from Config. + */ + explicit NavigationController(NextionControl* nextion, LocationType locationType); + + // INavigationDelegate + void navigateNext(uint8_t fromPageId) override; + void navigatePrevious(uint8_t fromPageId) override; + void navigateTo(uint8_t targetPageId) override; + + /** + * @brief Rebuild the active page list after a LocationType change. + * + * Call this whenever the user changes the location type in settings so + * that the page list is updated without a reboot. + * + * @param locationType The new location type. + */ + void rebuildForLocationType(LocationType locationType); + + /** + * @brief Check whether a page ID is present in the active list. + * @return true if the page is active for the current LocationType. + */ + bool isPageActive(uint8_t pageId) const; + +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/NextionConfigCommandHandler.h b/SmartFuseBox/NextionConfigCommandHandler.h new file mode 100644 index 0000000..7275367 --- /dev/null +++ b/SmartFuseBox/NextionConfigCommandHandler.h @@ -0,0 +1,219 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "BaseConfigCommandHandler.h" +#include "ConfigController.h" +#include "SystemDefinitions.h" + +/** + * @brief Command handler for Nextion display configuration (N-series commands). + * + * Each setting is exposed as its own command so no single message ever exceeds + * the 5-parameter limit imposed by SerialCommandManager. + * + * Command reference: + * N0 — Get all Nextion config (broadcasts N1–N6 with current values) + * N1:v=<0|1> — Set enabled state + * N2:v=<0|1> — Set serial mode (1=hardware, 0=software) + * N3:v= — Set RX pin (255/PinDisabled to clear) + * N4:v= — Set TX pin (255/PinDisabled to clear) + * N5:v= — Set baud rate + * N6:v=<1|2> — Set UART number (1 or 2 only; UART0 reserved for USB/debug) + */ +class NextionConfigCommandHandler : public virtual BaseCommandHandler, public BaseConfigCommandHandler +{ +private: + ConfigController* _configController; + + void broadcastNextionConfig(SerialCommandManager* sender) const + { + Config* config = ConfigManager::getConfigPtr(); + if (config == nullptr) + return; + + char buf[24]; + + snprintf(buf, sizeof(buf), "v=%u", config->nextion.enabled ? 1u : 0u); + sender->sendCommand(NextionEnabled, buf); + + snprintf(buf, sizeof(buf), "v=%u", config->nextion.isHardwareSerial ? 1u : 0u); + sender->sendCommand(NextionHardwareSerial, buf); + + snprintf(buf, sizeof(buf), "v=%u", config->nextion.rxPin); + sender->sendCommand(NextionRxPin, buf); + + snprintf(buf, sizeof(buf), "v=%u", config->nextion.txPin); + sender->sendCommand(NextionTxPin, buf); + + snprintf(buf, sizeof(buf), "v=%lu", config->nextion.baudRate); + sender->sendCommand(NextionBaudRate, buf); + + snprintf(buf, sizeof(buf), "v=%u", config->nextion.uartNum); + sender->sendCommand(NextionUartNum, buf); + } + +public: + explicit NextionConfigCommandHandler(ConfigController* configController) + : _configController(configController) + { + } + + const char* const* supportedCommands(size_t& count) const override + { + static const char* cmds[] = { + NextionGetConfig, + NextionEnabled, + NextionHardwareSerial, + NextionRxPin, + NextionTxPin, + NextionBaudRate, + NextionUartNum + }; + count = sizeof(cmds) / sizeof(cmds[0]); + return cmds; + } + + bool handleCommand(SerialCommandManager* sender, const char* command, + const StringKeyValue params[], uint8_t paramCount) override + { + if (SystemFunctions::commandMatches(command, NextionGetConfig)) + { + broadcastNextionConfig(sender); + sendAckOk(sender, command); + return true; + } + + if (_configController == nullptr) + { + sendAckErr(sender, command, F("Config not available")); + return true; + } + + ConfigResult result = ConfigResult::InvalidCommand; + + if (SystemFunctions::commandMatches(command, NextionEnabled)) + { + if (paramCount >= 1) + { + bool enabled; + if (!getParamValueBool(params, paramCount, "v", enabled)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionEnabled(enabled); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionHardwareSerial)) + { + if (paramCount >= 1) + { + bool hwSerial; + if (!getParamValueBool(params, paramCount, "v", hwSerial)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionHardwareSerial(hwSerial); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionRxPin)) + { + if (paramCount >= 1) + { + uint8_t pin; + if (!getParamValueU8t(params, paramCount, "v", pin)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionRxPin(pin); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionTxPin)) + { + if (paramCount >= 1) + { + uint8_t pin; + if (!getParamValueU8t(params, paramCount, "v", pin)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionTxPin(pin); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionBaudRate)) + { + if (paramCount >= 1) + { + uint32_t baud; + if (!getParamValueU32t(params, paramCount, "v", baud)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionBaudRate(baud); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + else if (SystemFunctions::commandMatches(command, NextionUartNum)) + { + if (paramCount >= 1) + { + uint8_t uartNum; + if (!getParamValueU8t(params, paramCount, "v", uartNum)) + result = ConfigResult::InvalidParameter; + else + result = _configController->setNextionUartNum(uartNum); + } + else + { + result = ConfigResult::InvalidParameter; + } + } + + switch (result) + { + case ConfigResult::Success: + sendAckOk(sender, command, ¶ms[0]); + break; + case ConfigResult::InvalidParameter: + sendAckErr(sender, command, F("Invalid parameter"), ¶ms[0]); + break; + case ConfigResult::InvalidConfig: + sendAckErr(sender, command, F("Config not available"), ¶ms[0]); + break; + default: + sendAckErr(sender, command, F("Unknown error"), ¶ms[0]); + break; + } + + return true; + } +}; diff --git a/SmartFuseBox/NextionFactory.h b/SmartFuseBox/NextionFactory.h new file mode 100644 index 0000000..3652062 --- /dev/null +++ b/SmartFuseBox/NextionFactory.h @@ -0,0 +1,226 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#if defined(ARDUINO_UNO_R4) || defined(ARDUINO_R4_MINIMA) +#include +#endif + +#include "ConfigManager.h" +#include "WarningManager.h" +#include "WarningType.h" +#include "NavigationController.h" +#include +#include "PageAbout.h" +#include "PageBuoys.h" +#include "PageCardinalMarkers.h" +#include "PageEnvironment.h" +#include "PageFlags.h" +#include "PageHome.h" +#include "PageMoonPhase.h" +#include "PageRelay.h" +#include "PageRelaySettings.h" +#include "PageSettings.h" +#include "PageSoundEmergency.h" +#include "PageSoundFog.h" +#include "PageSoundManeuvering.h" +#include "PageSoundOther.h" +#include "PageSoundOvertaking.h" +#include "PageSoundSignals.h" +#include "PageSplash.h" +#include "PageSystem.h" +#include "PageVhfChannels.h" +#include "PageVhfDistress.h" +#include "PageVhfRadio.h" +#include "PageWarning.h" + + +class NextionFactory +{ +public: + // Create and return an owning pointer to a configured NextionControl. + // The created page instances are kept in internal static storage so their + // lifetime matches the returned controller. Returns nullptr if Nextion is + // disabled in config or serial initialisation fails. + // locationType controls which pages are included in next/previous navigation. + static NextionControl* Create( + WarningManager* warningManager, + SerialCommandManager* commandMgrComputer, + SoundController* soundController, + RelayController* relayController, + LocationType locationType); + +private: + // Initialises and returns the serial port for the Nextion display. + // The port is initialised once and reused on subsequent calls. + // Board-conditional: + // ESP32 – HardwareSerial UART2 with runtime pin assignment. + // ARDUINO_UNO_R4 + // ARDUINO_R4_MINIMA – Fixed Serial1 peripheral (pins set by hardware). + // All boards – SoftwareSerial when isHardwareSerial == false. + static Stream* initSerial(const NextionConfig& cfg); +}; + +inline Stream* NextionFactory::initSerial(const NextionConfig& cfg) +{ + static Stream* s_serial = nullptr; + + if (s_serial != nullptr) + return s_serial; + + if (cfg.isHardwareSerial) + { +#if defined(ESP32) + // UART0 is reserved for USB/debug and must never be used for Nextion. + // Valid values are 1 or 2; anything else (including 0/unset) defaults to UART2. + const uint8_t uart = (cfg.uartNum == 1 || cfg.uartNum == 2) ? cfg.uartNum : 2; + static HardwareSerial s_hwSerial(uart); + s_hwSerial.begin(cfg.baudRate, SERIAL_8N1, cfg.rxPin, cfg.txPin); + s_serial = &s_hwSerial; +#elif defined(ARDUINO_UNO_R4) || defined(ARDUINO_R4_MINIMA) + // Hardware serial pins are fixed on the R4; Serial1 maps to the header. + // rxPin/txPin in config are ignored for hardware serial on this board. + Serial1.begin(cfg.baudRate); + s_serial = &Serial1; +#endif + } +#if defined(ARDUINO_UNO_R4) || defined(ARDUINO_R4_MINIMA) + else + { + // SoftwareSerial: available on all target boards. + // The static local is constructed with the config pins on the first call + // and reused on all subsequent calls. + static SoftwareSerial s_swSerial(cfg.rxPin, cfg.txPin); + s_swSerial.begin(cfg.baudRate); + s_serial = &s_swSerial; + } +#endif + + return s_serial; +} + +inline NextionControl* NextionFactory::Create( + WarningManager* warningManager, + SerialCommandManager* commandMgrComputer, + SoundController* soundController, + RelayController* relayController, + LocationType locationType) +{ + // Singleton: factory owns the instance for the program lifetime. + static std::unique_ptr s_instance; + + if (s_instance != nullptr) + return s_instance.get(); + + // Storage for owned page objects — static so pages outlive the controller. + static std::vector> s_pages; + static std::vector s_pagePtrs; + static std::unique_ptr s_navController; + + Config* config = ConfigManager::getConfigPtr(); + + if (config == nullptr || !config->nextion.enabled) + return nullptr; + +#if defined(ESP32) + // SoftwareSerial is not supported on ESP32. Raise a warning and bail out + // before attempting any serial initialisation. + if (!config->nextion.isHardwareSerial) + { + if (warningManager != nullptr) + warningManager->raiseWarning(WarningType::NextionInvalidConfig); + + return nullptr; + } +#endif + + Stream* serialPort = initSerial(config->nextion); + + // initSerial returns nullptr if the board/config combination is unsupported. + if (serialPort == nullptr) + { + if (warningManager != nullptr) + warningManager->raiseWarning(WarningType::NextionInvalidConfig); + + return nullptr; + } + + if (s_pages.empty()) + { + // Order must match the page numbering expected by the Nextion UI! + s_pages.emplace_back(std::make_unique(serialPort)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer, relayController)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer, relayController)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer, soundController)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer, soundController)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer, soundController)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer, soundController)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer, soundController)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer, soundController)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + s_pages.emplace_back(std::make_unique(serialPort, warningManager, commandMgrComputer)); + + // Build raw pointer array for NextionControl (non-owning). + s_pagePtrs.reserve(s_pages.size()); + for (auto& p : s_pages) + s_pagePtrs.push_back(p.get()); + } + + s_instance = std::unique_ptr( + new NextionControl(serialPort, s_pagePtrs.data(), s_pagePtrs.size())); + + // Create the navigation controller and inject it into all BasePage instances. + // PageSplash inherits BaseDisplayPage directly and is intentionally skipped. + s_navController = std::make_unique(s_instance.get(), locationType); + + // Inject navigation delegate into all pages that inherit from BasePage. + // PageSplash (index 0) intentionally derives from BaseDisplayPage and is + // skipped. We avoid dynamic_cast because RTTI is disabled in some build + // configurations (-fno-rtti). Use static_cast for pages we created and + // skip the splash page which does not implement BasePage. + for (size_t i = 1; i < s_pagePtrs.size(); ++i) + { + BasePage* bp = static_cast(s_pagePtrs[i]); + if (bp) + bp->setNavigationDelegate(s_navController.get()); + } + + return s_instance.get(); +} + +#endif \ No newline at end of file diff --git a/SmartFuseBox/NextionIds.h b/SmartFuseBox/NextionIds.h new file mode 100644 index 0000000..5f0b2bb --- /dev/null +++ b/SmartFuseBox/NextionIds.h @@ -0,0 +1,101 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +constexpr uint8_t PageIdSplash = 0; +constexpr uint8_t PageIdHome = 1; +constexpr uint8_t PageIdWarning = 2; +constexpr uint8_t PageIdRelay = 3; +constexpr uint8_t PageIdSoundSignals = 4; +constexpr uint8_t PageIdSoundManeuveringSignals = 5; +constexpr uint8_t PageIdSoundFogSignals = 6; +constexpr uint8_t PageIdSoundOvertaking = 7; +constexpr uint8_t PageIdSoundEmergency = 8; +constexpr uint8_t PageIdSoundOther = 9; +constexpr uint8_t PageIdSystem = 10; +constexpr uint8_t PageIdFlags = 11; +constexpr uint8_t PageIdCardinalMarkers = 12; +constexpr uint8_t PageIdBuoys = 13; +constexpr uint8_t PageIdAbout = 14; +constexpr uint8_t PageIdMoon = 15; +constexpr uint8_t PageIdMoonPhases = 15; +constexpr uint8_t PageIdVhfRadio = 16; +constexpr uint8_t PageIdVhfDistress = 17; +constexpr uint8_t PageIdVhfChannels = 18; +constexpr uint8_t PageIdSettingsRelays = 19; +constexpr uint8_t PageIdSettings = 20; +constexpr uint8_t PageIdEnvironment = 21; + +constexpr uint8_t InvalidButtonIndex = 0xFF; + +// Button color constants (Nextion picture IDs) +constexpr uint8_t ImageButtonColorBlue = 2; +constexpr uint8_t ImageButtonColorGreen = 3; +constexpr uint8_t ImageButtonColorGrey = 4; +constexpr uint8_t ImageButtonColorOrange = 5; +constexpr uint8_t ImageButtonColorRed = 6; +constexpr uint8_t ImageButtonColorYellow = 7; +constexpr uint8_t ImageButtonColorOffset = 12; // Offset for large button images + +// Image Ids for Nextion +constexpr uint8_t ImageCompass = 8; +constexpr uint8_t ImageSettings = 9; +constexpr uint8_t ImageWarning = 10; +constexpr uint8_t ImageBlank = 11; +constexpr uint8_t ImageBackButton = 12; +constexpr uint8_t ImageNextButton = 13; + + +constexpr uint8_t ImageMoonPhaseNew = 73; +constexpr uint8_t ImageMoonPhaseWaxingCrescent = 74; +constexpr uint8_t ImageMoonPhaseFirstQuarter = 75; +constexpr uint8_t ImageMoonPhaseWaxingGibbous = 76; +constexpr uint8_t ImageMoonPhaseFull = 77; +constexpr uint8_t ImageMoonPhaseWaningGibbous = 78; +constexpr uint8_t ImageMoonPhaseLastQuarter = 79; +constexpr uint8_t ImageMoonPhaseWaningCrescent = 80; + + +constexpr uint8_t ImageGreenCircle = 82; +constexpr uint8_t ImageOrangeCircle = 83; +constexpr uint8_t ImageRedCircle = 84; + +constexpr uint8_t ImageDownArrow = 85; +constexpr uint8_t ImageInwardArrow = 86; +constexpr uint8_t ImageUpArrow = 87; +constexpr uint8_t ImageQuestionMark = 88; + +constexpr uint8_t MoonImages[8] = { + ImageMoonPhaseNew, + ImageMoonPhaseWaxingCrescent, + ImageMoonPhaseFirstQuarter, + ImageMoonPhaseWaxingGibbous, + ImageMoonPhaseFull, + ImageMoonPhaseWaningGibbous, + ImageMoonPhaseLastQuarter, + ImageMoonPhaseWaningCrescent +}; + +// page +constexpr char PageOne[] = "page 1"; + +#endif \ No newline at end of file diff --git a/SmartFuseBox/PageAbout.cpp b/SmartFuseBox/PageAbout.cpp new file mode 100644 index 0000000..cc1dc55 --- /dev/null +++ b/SmartFuseBox/PageAbout.cpp @@ -0,0 +1,74 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageAbout.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +// Nextion Names/Ids on current Page +constexpr uint8_t BtnNextFlag = 3; // b3 +constexpr uint8_t BtnBack = 2; // b4 +constexpr uint8_t BtnSetup = 7; // b0 +constexpr uint8_t BtnRelaySetup = 9; // b1 + +constexpr unsigned long RefreshIntervalMs = 10000; + +PageAbout::PageAbout(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer) +{ +} + +void PageAbout::begin() +{ + +} + +void PageAbout::refresh(unsigned long now) +{ + (void)now; +} + +// Handle touch events for buttons +void PageAbout::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnSetup: + navigateTo(PageIdSettings); + break; + + case BtnRelaySetup: + navigateTo(PageIdSettingsRelays); + break; + + case BtnNextFlag: + navigateTo(PageIdHome); + break; + + case BtnBack: + navigatePrevious(); + break; + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageAbout.h b/SmartFuseBox/PageAbout.h new file mode 100644 index 0000000..fe212b0 --- /dev/null +++ b/SmartFuseBox/PageAbout.h @@ -0,0 +1,45 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include "NextionIds.h" +#include "BasePage.h" + +class PageAbout : public BasePage +{ +public: + PageAbout(Stream* serialPort, + WarningManager* warningMgr = nullptr, + SerialCommandManager* commandMgrComputer = nullptr); + +protected: + + uint8_t getPageId() const override { return PageIdAbout; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageBuoys.cpp b/SmartFuseBox/PageBuoys.cpp new file mode 100644 index 0000000..52f70aa --- /dev/null +++ b/SmartFuseBox/PageBuoys.cpp @@ -0,0 +1,139 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageBuoys.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include "SystemFunctions.h" + + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonNext = 3; +constexpr uint8_t ButtonPrevious = 2; +constexpr uint8_t BtnPrevBuoy = 4; +constexpr uint8_t BtnNextBuoy = 5; +constexpr char BuoyImageName[] = "buoyImg"; + +constexpr uint8_t BuoyFirst = 64; +constexpr uint8_t BuoyLast = 71; + +// Store buoy meanings in PROGMEM to save RAM +const char BuoyMeaning_1[] PROGMEM = "Wreck or Hazard.\r\nPlaced close to new wreck."; +const char BuoyMeaning_2[] PROGMEM = "Isolated Danger\r\nPlaced above or close to hazard."; +const char BuoyMeaning_3[] PROGMEM = "Lateral Marker Port.\r\nEdge of navigable channel."; +const char BuoyMeaning_4[] PROGMEM = "Lateral Marker Starboard.\r\nEdge of navigable channel."; +const char BuoyMeaning_5[] PROGMEM = "Preferred Channel Port.\r\nPass on port side."; +const char BuoyMeaning_6[] PROGMEM = "Preferred Channel Starboard.\r\nPass on starboard side."; +const char BuoyMeaning_7[] PROGMEM = "Safe Water.\r\nNavigable water or center line."; +const char BuoyMeaning_8[] PROGMEM = "Special Marker.\r\nSpecial area or feature."; + +const char* const BuoyMeanings[8] PROGMEM = { + BuoyMeaning_1, BuoyMeaning_2, BuoyMeaning_3, BuoyMeaning_4, + BuoyMeaning_5, BuoyMeaning_6, BuoyMeaning_7, BuoyMeaning_8 +}; + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageBuoys::PageBuoys(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer), + _currentBuoyIndex(BuoyFirst + 1) +{ +} + +void PageBuoys::begin() +{ + updateBuoyDisplay(); +} + +void PageBuoys::refresh(unsigned long now) +{ + (void)now; +} + +void PageBuoys::updateBuoyDisplay() +{ + setPicture(BuoyImageName, _currentBuoyIndex); + + uint8_t buoyIndex = _currentBuoyIndex - BuoyFirst; + + // Read the pointer from PROGMEM and convert to cha* + const char* meaningPtr = (const char*)pgm_read_ptr(&BuoyMeanings[buoyIndex]); + char buffer[100]; + strcpy_P(buffer, meaningPtr); + + sendText(F("buoyText"), buffer); +} + +void PageBuoys::cycleNextBuoy() +{ + if (_currentBuoyIndex >= BuoyLast) + { + _currentBuoyIndex = BuoyFirst; + } + else + { + _currentBuoyIndex++; + } + + updateBuoyDisplay(); +} + +void PageBuoys::cyclePrevBuoy() +{ + if (_currentBuoyIndex <= BuoyFirst) + { + _currentBuoyIndex = BuoyLast; + } + else + { + _currentBuoyIndex--; + } + + updateBuoyDisplay(); +} + +// Handle touch events for buttons +void PageBuoys::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnPrevBuoy: + cyclePrevBuoy(); + break; + + case BtnNextBuoy: + cycleNextBuoy(); + break; + + case ButtonNext: + navigateNext(); + return; + + case ButtonPrevious: + navigatePrevious(); + return; + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageBuoys.h b/SmartFuseBox/PageBuoys.h new file mode 100644 index 0000000..b62fa41 --- /dev/null +++ b/SmartFuseBox/PageBuoys.h @@ -0,0 +1,55 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + + + +class PageBuoys : public BasePage +{ +private: + uint8_t _currentBuoyIndex = 0; + void updateBuoyDisplay(); + void cycleNextBuoy(); + void cyclePrevBuoy(); +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdBuoys; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageBuoys(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageCardinalMarkers.cpp b/SmartFuseBox/PageCardinalMarkers.cpp new file mode 100644 index 0000000..2a17d92 --- /dev/null +++ b/SmartFuseBox/PageCardinalMarkers.cpp @@ -0,0 +1,61 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageCardinalMarkers.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonNext = 3; +constexpr uint8_t ButtonPrevious = 2; + +PageCardinalMarkers::PageCardinalMarkers(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer) +{ +} + +void PageCardinalMarkers::begin() +{ +} + +void PageCardinalMarkers::refresh(unsigned long now) +{ + (void)now; +} + +// Handle touch events for buttons +void PageCardinalMarkers::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonNext: + navigateNext(); + return; + + case ButtonPrevious: + navigatePrevious(); + return; + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageCardinalMarkers.h b/SmartFuseBox/PageCardinalMarkers.h new file mode 100644 index 0000000..538eb0a --- /dev/null +++ b/SmartFuseBox/PageCardinalMarkers.h @@ -0,0 +1,51 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + + + + +class PageCardinalMarkers : public BasePage +{ +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdCardinalMarkers; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageCardinalMarkers(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageEnvironment.cpp b/SmartFuseBox/PageEnvironment.cpp new file mode 100644 index 0000000..a26ba3b --- /dev/null +++ b/SmartFuseBox/PageEnvironment.cpp @@ -0,0 +1,351 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageEnvironment.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include "Environment.h" +#include "DateTimeManager.h" +#include "SunCalculator.h" +#include "SystemFunctions.h" + +const char TextLowRisk[] PROGMEM = "Low"; +const char TextWatch[] PROGMEM = "Watch"; +const char TextHighRisk[] PROGMEM = "High"; +const char TextNA[] PROGMEM = "N/A"; +const char TextColdDamp[] PROGMEM = "Cold & Damp"; +const char TextCold[] PROGMEM = "Cold"; +const char TextWarmSticky[] PROGMEM = "Warm & Sticky"; +const char TextHumid[] PROGMEM = "Humid"; +const char TextComfortable[] PROGMEM = "Comfortable"; +const char TextFair[] PROGMEM = "Fair"; + +constexpr char ControlSunriseTime[] = "t2"; +constexpr char ControlSunsetTime[] = "t4"; +constexpr char ControlTemperature[] = "t8"; +constexpr char ControlTemperatureIndicator[] = "p5"; +constexpr char ControlComfort[] = "t9"; +constexpr char ControlComfortIndicator[] = "p3"; +constexpr char ControlHumidity[] = "t5"; +constexpr char ControlHumidityIndicator[] = "p4"; +constexpr char ControlDewPoint[] = "t10"; +constexpr char ControlDewPointIndicator[] = "p1"; +constexpr char ControlCondensation[] = "t14"; +constexpr char ControlCondensationIndicator[] = "p0"; + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonPrevious = 2; + + +PageEnvironment::PageEnvironment(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer) +{ +} + +void PageEnvironment::onEnterPage() +{ + updateHumidity(); + updateTemperature(); + updateLatLon(); +} + +void PageEnvironment::begin() +{ + // not used just now +} + +void PageEnvironment::refresh(unsigned long now) +{ + (void)now; + +} + +// Handle touch events for buttons +void PageEnvironment::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonPrevious: + navigatePrevious(); + return; + } +} + +void PageEnvironment::handleExternalUpdate(uint8_t updateType, const void* data) +{ + // Call base class first to handle heartbeat ACKs + BasePage::handleExternalUpdate(updateType, data); + + if (updateType == static_cast(PageUpdateType::Temperature) && data != nullptr) + { + const FloatStateUpdate* update = static_cast(data); + _previousTemp = _lastTemp; + _lastTemp = update->value; + updateTemperature(); + } + else if (updateType == static_cast(PageUpdateType::Humidity) && data != nullptr) + { + const UInt16Update* update = static_cast(data); + _previousHumidity = _lastHumidity; + _lastHumidity = static_cast(update->value); + updateHumidity(); + } + else if (updateType == static_cast(PageUpdateType::GpsLatitude) && data != nullptr) + { + // FloatStateUpdate is used for GPS lat/lon notifications + const FloatStateUpdate* update = static_cast(data); + _lastLatitude = update->value; + updateLatLon(); + } + else if (updateType == static_cast(PageUpdateType::GpsLongitude) && data != nullptr) + { + const FloatStateUpdate* update = static_cast(data); + _lastLongitude = update->value; + updateLatLon(); + } +} + +void PageEnvironment::updateTemperature() +{ + if (isnan(_lastTemp)) + { + sendText(ControlTemperature, NoValueText); + setPicture(ControlTemperatureIndicator, ImageQuestionMark); + return; + } + + char cmd[14]; + dtostrf(_lastTemp, 4, 1, cmd); + + size_t len = strlen(cmd); + + // Append degree symbol (character 176) and "C" + if (len + 2 < sizeof(cmd)) + { + cmd[len] = (char)176; + cmd[len + 1] = '\0'; + strcat(cmd, CelsiusSuffix); + } + + // update direction indicator increasing/decreasing/same but only within 0.5 degree change + if (_lastTemp > _previousTemp) + { + setPicture(ControlTemperatureIndicator, ImageUpArrow); + } + else if (_previousTemp > _lastTemp) + { + setPicture(ControlTemperatureIndicator, ImageDownArrow); + } + else + { + setPicture(ControlTemperatureIndicator, ImageInwardArrow); + } + + sendText(ControlTemperature, cmd); + updateCondensation(); + updateDewPoint(); +} + +void PageEnvironment::updateHumidity() +{ + if (isnan(_lastHumidity)) + { + sendText(ControlHumidity, NoValueText); + setPicture(ControlHumidityIndicator, ImageQuestionMark); + return; + } + + char cmd[10]; + dtostrf(_lastHumidity, 4, 1, cmd); + + size_t len = strlen(cmd); + + if (len + strlen(PercentSuffix) < sizeof(cmd)) + { + strcpy(cmd + len, PercentSuffix); + } + + // update direction indicator increasing/decreasing/same + if (_lastHumidity > _previousHumidity) + { + setPicture(ControlHumidityIndicator, ImageUpArrow); + } + else if (_previousHumidity > _lastHumidity) + { + setPicture(ControlHumidityIndicator, ImageDownArrow); + } + else + { + setPicture(ControlHumidityIndicator, ImageInwardArrow); + } + + sendText(ControlHumidity, cmd); + updateCondensation(); + updateDewPoint(); +} + +void PageEnvironment::updateLatLon() +{ + if (isnan(_lastLatitude) || isnan(_lastLongitude)) + { + return; + } + + updateSunriseSunset(); +} + +void PageEnvironment::updateSunriseSunset() +{ + Config* config = getConfig(); + + if (config == nullptr) + { + return; + } + + SunTimes suntimes = SunCalculator::calculateSunTimes(_lastLatitude, _lastLongitude, + DateTimeManager::getYear(), + DateTimeManager::getMonth(), + DateTimeManager::getDay(), + config->system.timezoneOffset); + + char cmd[32]; + + if (!suntimes.isValid) + { + SystemFunctions::progmemToBuffer(TextNA, cmd, sizeof(cmd)); + sendText(ControlSunriseTime, cmd); + sendText(ControlSunsetTime, cmd); + return; + } + + + // Wrap hours to 0-23 range + int sunriseHour = (int)suntimes.sunrise; + if (sunriseHour < 0) sunriseHour += 24; + if (sunriseHour >= 24) sunriseHour -= 24; + + int sunriseMin = (int)((suntimes.sunrise - (int)suntimes.sunrise) * 60); + if (sunriseMin < 0) sunriseMin += 60; + + snprintf_P(cmd, sizeof(cmd), PSTR("%02d:%02d"), sunriseHour, sunriseMin); + sendText(ControlSunriseTime, cmd); + + int sunsetHour = (int)suntimes.sunset; + if (sunsetHour < 0) sunsetHour += 24; + if (sunsetHour >= 24) sunsetHour -= 24; + + int sunsetMin = (int)((suntimes.sunset - (int)suntimes.sunset) * 60); + if (sunsetMin < 0) sunsetMin += 60; + + snprintf_P(cmd, sizeof(cmd), PSTR("%02d:%02d"), sunsetHour, sunsetMin); + sendText(ControlSunsetTime, cmd); +} + +void PageEnvironment::updateCondensation() +{ + double dewPt = Environment::dewPoint(_lastTemp, _lastHumidity); + CondensationRisk risk = Environment::condensationRisk(_lastTemp, dewPt, false); + char buffer[24]; + + switch (risk) + { + case CondensationRisk::Low: + setPicture(ControlCondensationIndicator, ImageGreenCircle); + SystemFunctions::progmemToBuffer(TextLowRisk, buffer, sizeof(buffer)); + break; + case CondensationRisk::Watch: + setPicture(ControlCondensationIndicator, ImageOrangeCircle); + SystemFunctions::progmemToBuffer(TextWatch, buffer, sizeof(buffer)); + break; + case CondensationRisk::High: + setPicture(ControlCondensationIndicator, ImageRedCircle); + SystemFunctions::progmemToBuffer(TextHighRisk, buffer, sizeof(buffer)); + break; + } + + sendText(ControlCondensation, buffer); +} + +void PageEnvironment::updateDewPoint() +{ + if (isnan(_lastTemp) || isnan(_lastHumidity)) + { + sendText(ControlDewPoint, NoValueText); + setPicture(ControlDewPointIndicator, ImageQuestionMark); + return; + } + + double dewPoint = Environment::dewPoint(_lastTemp, _lastHumidity); + + char cmd[10]; + dtostrf(dewPoint, 4, 1, cmd); + + size_t len = strlen(cmd); + + // Append degree symbol (character 176) and "C" + if (len + 2 < sizeof(cmd)) + { + cmd[len] = (char)176; + cmd[len + 1] = '\0'; + strcat(cmd, CelsiusSuffix); + } + + sendText(ControlDewPoint, cmd); + + updateComfortDescription(dewPoint); +} + +void PageEnvironment::updateComfortDescription(double dewPoint) +{ + char buffer[24]; + + if (_lastTemp < 10 && _lastHumidity > 70) + { + SystemFunctions::progmemToBuffer(TextColdDamp, buffer, sizeof(buffer)); + } + else if (_lastTemp < 12) + { + SystemFunctions::progmemToBuffer(TextCold, buffer, sizeof(buffer)); + } + else if (_lastTemp > 26 && _lastHumidity > 65) + { + SystemFunctions::progmemToBuffer(TextWarmSticky, buffer, sizeof(buffer)); + } + else if (dewPoint > 18) + { + SystemFunctions::progmemToBuffer(TextHumid, buffer, sizeof(buffer)); + } + else if (_lastTemp >= 18 && _lastTemp <= 24 && _lastHumidity < 65) + { + SystemFunctions::progmemToBuffer(TextComfortable, buffer, sizeof(buffer)); + } + else + { + SystemFunctions::progmemToBuffer(TextFair, buffer, sizeof(buffer)); + } + + sendText(ControlComfort, buffer); +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageEnvironment.h b/SmartFuseBox/PageEnvironment.h new file mode 100644 index 0000000..caad8af --- /dev/null +++ b/SmartFuseBox/PageEnvironment.h @@ -0,0 +1,67 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + + + +class PageEnvironment : public BasePage +{ +private: + float _lastTemp = NAN; + float _previousTemp = NAN; + float _lastHumidity = NAN; + float _previousHumidity = NAN; + double _lastLatitude = NAN; + double _lastLongitude = NAN; + void updateTemperature(); + void updateHumidity(); + void updateLatLon(); + void updateSunriseSunset(); + void updateCondensation(); + void updateDewPoint(); + void updateComfortDescription(double dewPoint); + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdEnvironment; } + void begin() override; + void onEnterPage() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleExternalUpdate(uint8_t updateType, const void* data) override; + +public: + explicit PageEnvironment(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageFlags.cpp b/SmartFuseBox/PageFlags.cpp new file mode 100644 index 0000000..6eb8f6a --- /dev/null +++ b/SmartFuseBox/PageFlags.cpp @@ -0,0 +1,173 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageFlags.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include "SystemFunctions.h" + + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonNext = 3; +constexpr uint8_t ButtonPrevious = 2; +constexpr uint8_t BtnPrevFlag = 7; +constexpr uint8_t BtnNextFlag = 6; +constexpr char FlagImageName[] = "flagImg"; + +constexpr uint8_t FlagFirst = 21; +constexpr uint8_t FlagLast = 60; +constexpr uint8_t FlagLetterA = 31; +constexpr uint8_t FlagLetterZ = 56; + +// Store flag meanings in PROGMEM to save RAM +const char FlagMeaning_A[] PROGMEM = "Alfa: Diver down; keep\r\nwell clear at slow speed."; +const char FlagMeaning_B[] PROGMEM = "Bravo: I am taking in,\r\ndischarging, or carrying\r\ndangerous goods."; +const char FlagMeaning_C[] PROGMEM = "Charlie: Yes (affirmative)."; +const char FlagMeaning_D[] PROGMEM = "Delta: Keep clear of me;\r\nI am maneuvering\r\nwith difficulty."; +const char FlagMeaning_E[] PROGMEM = "Echo: I am altering my\r\ncourse to port."; +const char FlagMeaning_F[] PROGMEM = "Foxtrot: I am disabled;\r\ncommunicate with me."; +const char FlagMeaning_G[] PROGMEM = "Golf: I require a pilot.\r\n(Fishing: I am hauling nets.)"; +const char FlagMeaning_H[] PROGMEM = "Hotel: I have a pilot\r\non board."; +const char FlagMeaning_I[] PROGMEM = "India: I am altering my\r\ncourse to starboard."; +const char FlagMeaning_J[] PROGMEM = "Juliet: I am leaking\r\ndangerous goods."; +const char FlagMeaning_K[] PROGMEM = "Kilo: I wish to\r\ncommunicate with you."; +const char FlagMeaning_L[] PROGMEM = "Lima: You should stop\r\nyour vessel immediately."; +const char FlagMeaning_M[] PROGMEM = "Mike: My vessel is stopped;\r\nmaking no way."; +const char FlagMeaning_N[] PROGMEM = "November: No (negative)."; +const char FlagMeaning_O[] PROGMEM = "Oscar: Man overboard."; +const char FlagMeaning_P[] PROGMEM = "Papa: All persons should\r\nreport on board; vessel is\r\nabout to proceed to sea."; +const char FlagMeaning_Q[] PROGMEM = "Quebec: My vessel is\r\nhealthy and I request\r\nfree pratique."; +const char FlagMeaning_R[] PROGMEM = "Romeo: No ICS meaning\r\n(used for signals/\r\nport facilities only)."; +const char FlagMeaning_S[] PROGMEM = "Sierra: I am operating\r\nastern propulsion."; +const char FlagMeaning_T[] PROGMEM = "Tango: Keep clear of me;\r\nI am engaged in pair trawling."; +const char FlagMeaning_U[] PROGMEM = "Uniform: You are running\r\ninto danger."; +const char FlagMeaning_V[] PROGMEM = "Victor: I require assistance."; +const char FlagMeaning_W[] PROGMEM = "Whiskey: I require\r\nmedical assistance."; +const char FlagMeaning_X[] PROGMEM = "X-ray: Stop your intentions\r\nand watch for my signals."; +const char FlagMeaning_Y[] PROGMEM = "Yankee: My anchor is\r\ndragging."; +const char FlagMeaning_Z[] PROGMEM = "Zulu: I require a tug.\r\n(Fishing: I am shooting nets.)"; + +const char* const FlagMeanings[26] PROGMEM = { + FlagMeaning_A, FlagMeaning_B, FlagMeaning_C, FlagMeaning_D, + FlagMeaning_E, FlagMeaning_F, FlagMeaning_G, FlagMeaning_H, + FlagMeaning_I, FlagMeaning_J, FlagMeaning_K, FlagMeaning_L, + FlagMeaning_M, FlagMeaning_N, FlagMeaning_O, FlagMeaning_P, + FlagMeaning_Q, FlagMeaning_R, FlagMeaning_S, FlagMeaning_T, + FlagMeaning_U, FlagMeaning_V, FlagMeaning_W, FlagMeaning_X, + FlagMeaning_Y, FlagMeaning_Z +}; + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageFlags::PageFlags(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer), + _currentFlagIndex(FlagLetterA) +{ +} + +void PageFlags::begin() +{ + updateFlagDisplay(); +} + +void PageFlags::refresh(unsigned long now) +{ + (void)now; +} + +void PageFlags::updateFlagDisplay() +{ + setPicture(FlagImageName, _currentFlagIndex); + + // Check if the flag is within the letter range (A-Z) + if (_currentFlagIndex >= FlagLetterA && _currentFlagIndex <= FlagLetterZ) + { + uint8_t letterIndex = _currentFlagIndex - FlagLetterA; + + // Read the pointer from PROGMEM and convert to cha* + const char* meaningPtr = (const char*)pgm_read_ptr(&FlagMeanings[letterIndex]); + char buffer[128]; + strcpy_P(buffer, meaningPtr); + + sendText(F("flagText"), buffer); + } + else + { + // Not a letter flag, send empty string + sendText(F("flagText"), ""); + } +} + +void PageFlags::cycleNextFlag() +{ + if (_currentFlagIndex >= FlagLast) + { + _currentFlagIndex = FlagFirst; + } + else + { + _currentFlagIndex++; + } + + updateFlagDisplay(); +} + +void PageFlags::cyclePrevFlag() +{ + if (_currentFlagIndex <= FlagFirst) + { + _currentFlagIndex = FlagLast; + } + else + { + _currentFlagIndex--; + } + + updateFlagDisplay(); +} + +// Handle touch events for buttons +void PageFlags::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnPrevFlag: + cyclePrevFlag(); + break; + + case BtnNextFlag: + cycleNextFlag(); + break; + + case ButtonNext: + navigateNext(); + return; + + case ButtonPrevious: + navigatePrevious(); + return; + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageFlags.h b/SmartFuseBox/PageFlags.h new file mode 100644 index 0000000..ab588aa --- /dev/null +++ b/SmartFuseBox/PageFlags.h @@ -0,0 +1,55 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + + + +class PageFlags : public BasePage +{ +private: + uint8_t _currentFlagIndex = 0; + void updateFlagDisplay(); + void cycleNextFlag(); + void cyclePrevFlag(); +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdFlags; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageFlags(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageHome.cpp b/SmartFuseBox/PageHome.cpp new file mode 100644 index 0000000..443f4b9 --- /dev/null +++ b/SmartFuseBox/PageHome.cpp @@ -0,0 +1,670 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageHome.h" + +#if defined(NEXTION_DISPLAY_DEVICE) +#include "DateTimeManager.h" +#include "Astronomy.h" + + +// Nextion Names/Ids on current Home Page +constexpr float CompassTemperatureWarningValue = 35; +constexpr char ControlHumidity[] = "t3"; +constexpr char ControlTemperature[] = "t2"; +constexpr char ControlBearingText[] = "t6"; +constexpr char ControlBearingDirection[] = "t4"; +constexpr char ControlLonValue[] = "t5"; +constexpr char ControlLatValue[] = "t7"; +constexpr char ControlCurrTime[] = "t8"; +constexpr char ControlDistance[] = "t10"; +constexpr char ControlSpeed[] = "tSpeed"; +constexpr char ControlBoatName[] = "tBoatName"; +constexpr char ControlWarning[] = "pHomeWarning"; +constexpr char ControlMoonPhase[] = "pHomeMoon"; +const char SpeedUnitKnots[] PROGMEM = "%d kn/h"; +const char SpeedUnitKilometer[] PROGMEM = "%d km/h"; +const char DistanceUnitKnots[] PROGMEM = "%s nm"; +const char DistanceUnitKilometer[] PROGMEM = "%s km"; +const char BearingFormat[] PROGMEM = "%d°"; + +constexpr char HomeButtonPrefix[] = "b"; + +constexpr uint8_t Button1 = 1; // bHomeRelay1 +constexpr uint8_t Button2 = 2; // bHomeRelay2 +constexpr uint8_t Button3 = 3; // bHomeRelay3 +constexpr uint8_t Button4 = 4; // bHomeRelay4 +constexpr uint8_t ButtonTemperature = 6; //t2 +constexpr uint8_t ButtonHumidity = 7; //t3 +constexpr uint8_t ButtonSpeed = 9; //tSpeed / swap between kn/km +constexpr uint8_t ButtonVhf = 10; //p0 +constexpr uint8_t ButtonNext = 12; +constexpr uint8_t ButtonWarning = 13; +constexpr uint8_t ButtonMoon = 23; +constexpr uint8_t ButtonIdOffset = 1; // Offset to map button IDs to array indices + +constexpr unsigned long RefreshUpdateIntervalMs = 10000; +constexpr double GpsNoiseThresholdDegrees = 0.00001; + + +PageHome::PageHome(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + RelayController* relayController) + : BasePage(serialPort, warningMgr, commandMgrComputer), _relayController(relayController) +{ + +} + +void PageHome::begin() +{ + // If config already supplied before begin, apply it + if (getConfig()) + { + configUpdated(); + } + + for (uint8_t i = 1; i <= ConfigHomeButtons; ++i) + { + char cmd[15]; + snprintf_P(cmd, sizeof(cmd), PSTR("%sHomeRelay%d"), HomeButtonPrefix, i); + setPicture(cmd, ImageButtonColorGrey); + } + _compassTempAboveNorm = 0; +} + +void PageHome::onEnterPage() +{ + if (getConfig()) + { + configUpdated(); + } + + // Sync button states directly from RelayController + updateAllButtons(); + _lastRefreshTime = millis(); + + updateAllDisplayItems(); +} + +void PageHome::refresh(unsigned long now) +{ + updateAllDisplayItems(); + + // Periodically re-sync button states in case relays were changed elsewhere + if (now - _lastRefreshTime >= RefreshUpdateIntervalMs) + { + _lastRefreshTime = now; + updateAllButtons(); + } + + // Update warning display + WarningManager* warningMgr = getWarningManager(); + if (warningMgr) + { + if (warningMgr->hasWarnings()) + { + setPicture(ControlWarning, ImageWarning); + } + else + { + setPicture(ControlWarning, ImageBlank); + } + + // Update connection-related displays + if (warningMgr->isWarningActive(WarningType::ConnectionLost) || warningMgr->isWarningActive(WarningType::TemperatureSensorFailure)) + { + // Reset internal state to NaN to prevent flickering + _lastTemp = NAN; + _lastHumidity = NAN; + + sendText(ControlHumidity, NoValueText); + sendText(ControlTemperature, NoValueText); + } + } +} + +void PageHome::updateAllDisplayItems() +{ + updateTemperature(); + updateHumidity(); + updateBearing(); + updateSpeed(); + updateDirection(); + updateLatLon(); + updateDistance(); + updateTime(); + updateMoonPhase(); +} + +// Handle touch events for buttons +void PageHome::handleTouch(uint8_t compId, uint8_t eventType) +{ + Config* config = getConfig(); + + if (!config) + { + return; + } + + + + // Map component ID to button index + uint8_t buttonIndex = InvalidButtonIndex; + switch (compId) + { + case Button1: + case Button2: + case Button3: + case Button4: + buttonIndex = compId - ButtonIdOffset; // Get slot index (0-3) + + // Check if THIS button's mapped relay is the horn + if (config->sound.hornRelayIndex < DefaultValue && + _slotToRelay[buttonIndex] == config->sound.hornRelayIndex) + { + // This button controls the horn, go to sound page + navigateTo(PageIdSoundSignals); + return; + } + + break; // Continue to normal relay button handling + + case ButtonTemperature: + case ButtonHumidity: + navigateTo(PageIdEnvironment); + return; + + case ButtonSpeed: + _speedInKnots = !_speedInKnots; + updateSpeed(); + return; + + case ButtonVhf: + navigateTo(PageIdVhfRadio); + return; + + case ButtonNext: + navigateNext(); + return; + + case ButtonMoon: + navigateTo(PageIdMoonPhases); + return; + + case ButtonWarning: + navigateTo(PageIdWarning); + return; + + default: + return; + } + + // Check if we have a valid config and the button is mapped to a relay + if (buttonIndex >= ConfigHomeButtons) + return; + + uint8_t relayIndex = _slotToRelay[buttonIndex]; + + // Check if this button slot has a valid relay mapping + if (relayIndex == 0xFF || relayIndex >= ConfigRelayCount) + return; + + SerialCommandManager* commandMgrComputer = getCommandMgrComputer(); + + if (eventType == EventPress) + { + if (commandMgrComputer) + { + char debugMsg[64]; + snprintf_P(debugMsg, sizeof(debugMsg), PSTR("%s pressed"), config->relay.relays[relayIndex].shortName); + commandMgrComputer->sendDebug(debugMsg, F("HomePage")); + } + } + else if (eventType == EventRelease) + { + if (commandMgrComputer) + { + char debugMsg[64]; + snprintf_P(debugMsg, sizeof(debugMsg), PSTR("%s released"), config->relay.relays[relayIndex].shortName); + commandMgrComputer->sendDebug(debugMsg, F("HomePage")); + } + + // Toggle button state + _buttonOn[buttonIndex] = !_buttonOn[buttonIndex]; + + // Get the appropriate color based on the new state + uint8_t newColor = getButtonColor(buttonIndex, _buttonOn[buttonIndex], ConfigHomeButtons); + _buttonImage[buttonIndex] = newColor; + + // Update the button appearance + char cmd[15]; + snprintf_P(cmd, sizeof(cmd), PSTR("%sHomeRelay%d"), HomeButtonPrefix, buttonIndex + 1); + setPicture(cmd, newColor); + setPicture2(cmd, newColor); + + if (_relayController) + { + _relayController->setRelayState(relayIndex, _buttonOn[buttonIndex]); + } + } +} + +void PageHome::handleExternalUpdate(uint8_t updateType, const void* data) +{ + char debugMsg[64]; + snprintf_P(debugMsg, sizeof(debugMsg), PSTR("HomePage::handleExternalUpdate type=%u"), updateType); + SerialCommandManager* commandMgrComputer = getCommandMgrComputer(); + if (commandMgrComputer) + { + commandMgrComputer->sendDebug(debugMsg, F("HomePage")); + } + + // Call base class first to handle heartbeat ACKs + BasePage::handleExternalUpdate(updateType, data); + + if (updateType == static_cast(PageUpdateType::RelayState)) + { + updateAllButtons(); + } + else if (updateType == static_cast(PageUpdateType::Temperature) && data != nullptr) + { + const FloatStateUpdate* update = static_cast(data); + setTemperature(update->value); + } + else if (updateType == static_cast(PageUpdateType::Humidity) && data != nullptr) + { + const UInt16Update* update = static_cast(data); + setHumidity(static_cast(update->value)); + } + else if (updateType == static_cast(PageUpdateType::Bearing) && data != nullptr) + { + const FloatStateUpdate* update = static_cast(data); + setBearing(update->value); + } + else if (updateType == static_cast(PageUpdateType::Direction) && data != nullptr) + { + const CharStateUpdate* update = static_cast(data); + setDirection(update->value); + } + else if (updateType == static_cast(PageUpdateType::Speed) && data != nullptr) + { + const UInt16Update* update = static_cast(data); + setSpeed(static_cast(update->value)); + } + else if (updateType == static_cast(PageUpdateType::CompassTemp) && data != nullptr) + { + const FloatStateUpdate* update = static_cast(data); + setCompassTemperature(update->value); + } + else if (updateType == static_cast(PageUpdateType::GpsLatitude) && data != nullptr) + { + // FloatStateUpdate is used for GPS lat/lon notifications + const FloatStateUpdate* update = static_cast(data); + _lastLatitude = update->value; + updateLatLon(); + } + else if (updateType == static_cast(PageUpdateType::GpsLongitude) && data != nullptr) + { + const FloatStateUpdate* update = static_cast(data); + _lastLongitude = update->value; + updateLatLon(); + } + else if (updateType == static_cast(PageUpdateType::GpsDistance) && data != nullptr) + { + const FloatStateUpdate* update = static_cast(data); + _lastDistance = update->value; + updateDistance(); + } +} + +// --- Public setters --- +void PageHome::setTemperature(float tempC) +{ + if (isnan(_lastTemp) || _lastTemp != tempC) + { + _lastTemp = tempC; + updateTemperature(); + } +} + +void PageHome::setHumidity(float humPerc) +{ + if (isnan(_lastHumidity) || _lastHumidity != humPerc) + { + _lastHumidity = humPerc; + updateHumidity(); + } +} + +void PageHome::setBearing(float dir) +{ + if (isnan(_lastBearing) || _lastBearing != dir) + { + _lastBearing = dir; + updateBearing(); + } +} + +void PageHome::setSpeed(float speedKn) +{ + if (isnan(_lastSpeed) || _lastSpeed != speedKn) + { + _lastSpeed = speedKn; + updateSpeed(); + } +} + +void PageHome::setDirection(const char* dir) +{ + if (dir && strcmp(_lastDirection, dir) != 0) + { + strncpy(_lastDirection, dir, sizeof(_lastDirection) - 1); + _lastDirection[sizeof(_lastDirection) - 1] = '\0'; + updateDirection(); + } +} + +void PageHome::setCompassTemperature(float tempC) +{ + if (_lastCompassTemp != tempC) + { + _lastCompassTemp = tempC; + + if (tempC > CompassTemperatureWarningValue && _compassTempAboveNorm < 10) + { + _compassTempAboveNorm++; + + if (_compassTempAboveNorm > 5) + getWarningManager()->raiseWarning(WarningType::HighCompassTemperature); + } + else + { + _compassTempAboveNorm = 0; + + getWarningManager()->clearWarning(WarningType::HighCompassTemperature); + } + } +} + +// --- Private update methods --- +void PageHome::updateAllButtons() +{ + if (!_relayController) + return; + + CommandResult allStates = _relayController->getAllRelayStates(); + + for (uint8_t buttonIndex = 0; buttonIndex < ConfigHomeButtons; ++buttonIndex) + { + uint8_t relayIndex = _slotToRelay[buttonIndex]; + if (relayIndex == 0xFF || relayIndex >= ConfigRelayCount) + continue; + + bool isOn = allStates.isRelayOn(relayIndex); + _buttonOn[buttonIndex] = isOn; + _buttonImage[buttonIndex] = getButtonColor(buttonIndex, isOn, ConfigHomeButtons); + + char cmd[15]; + snprintf_P(cmd, sizeof(cmd), PSTR("%sHomeRelay%d"), HomeButtonPrefix, buttonIndex + 1); + setPicture(cmd, _buttonImage[buttonIndex]); + setPicture2(cmd, _buttonImage[buttonIndex]); + } +} + +void PageHome::updateTemperature() +{ + if (isnan(_lastTemp)) + { + sendText(ControlTemperature, NoValueText); + return; + } + + char cmd[10]; + dtostrf(_lastTemp, 4, 1, cmd); + + size_t len = strlen(cmd); + + // Append degree symbol (character 176) and "C" + if (len + 2 < sizeof(cmd)) // +2 for degree symbol and 'C' + { + cmd[len] = (char)176; // Degree symbol + cmd[len + 1] = '\0'; // Null terminate + strcat(cmd, CelsiusSuffix); // Append "C" + } + + sendText(ControlTemperature, cmd); +} + +void PageHome::updateHumidity() +{ + if (isnan(_lastHumidity)) + { + sendText(ControlHumidity, NoValueText); + return; + } + + char cmd[10]; + dtostrf(_lastHumidity, 4, 1, cmd); + + size_t len = strlen(cmd); + + if (len + strlen(PercentSuffix) < sizeof(cmd)) + { + strcpy(cmd + len, PercentSuffix); + } + + sendText(ControlHumidity, cmd); +} + +void PageHome::updateBearing() +{ + if (isnan(_lastBearing)) + { + sendText(ControlBearingText, NoValueText); + return; + } + + char buffer[10]; + // Use explicit single-byte degree symbol (0xB0) to avoid UTF-8 two-byte sequence + snprintf_P(buffer, sizeof(buffer), PSTR("%d%c"), (int)_lastBearing, (char)176); + sendText(ControlBearingText, buffer); +} + +void PageHome::updateSpeed() +{ + if (isnan(_lastSpeed)) + { + _lastSpeed = 0; + } + + char buffer[10]; + float displaySpeed = _lastSpeed; + const char* format = SpeedUnitKilometer; + + if (_speedInKnots) + { + // Convert km/h to knots (1 km/h = 0.539957 knots) + displaySpeed = _lastSpeed * 0.539957f; + format = SpeedUnitKnots; + } + + snprintf_P(buffer, sizeof(buffer), format, (int)displaySpeed); + sendText(ControlSpeed, buffer); +} + +void PageHome::updateLatLon() +{ + if (isnan(_lastLatitude)) + { + sendText(ControlLatValue, NoValueText); + _displayedLatitude = NAN; + } + else + { + // Only update display if change is significant or first time + bool shouldUpdate = isnan(_displayedLatitude) || + fabs(_lastLatitude - _displayedLatitude) > GpsNoiseThresholdDegrees; + + if (shouldUpdate) + { + _displayedLatitude = _lastLatitude; + + char latBuf[20]; + dtostrf(_lastLatitude, 10, 6, latBuf); + char* pLat = latBuf; + + while (*pLat == ' ') + pLat++; + + sendText(ControlLatValue, pLat); + } + } + + if (isnan(_lastLongitude)) + { + sendText(ControlLonValue, NoValueText); + _displayedLongitude = NAN; + } + else + { + // Only update display if change is significant or first time + bool shouldUpdate = isnan(_displayedLongitude) || + fabs(_lastLongitude - _displayedLongitude) > GpsNoiseThresholdDegrees; + + if (shouldUpdate) + { + _displayedLongitude = _lastLongitude; + + char lonBuf[20]; + dtostrf(_lastLongitude, 10, 6, lonBuf); + + char* pLon = lonBuf; + + while (*pLon == ' ') + pLon++; + + sendText(ControlLonValue, pLon); + } + } +} + +void PageHome::updateDistance() +{ + if (isnan(_lastDistance)) + { + _lastDistance = 0.00; + } + + char buffer[16]; + float displayDistance = _lastDistance; + const char* format = DistanceUnitKilometer; + + if (_speedInKnots) + { + // Convert km to knots (1 km = 0.539957 knots) + displayDistance = _lastDistance * 0.539957f; + format = DistanceUnitKnots; + } + + char num[10]; + dtostrf(displayDistance, 0, 1, num); + snprintf_P(buffer, sizeof(buffer), format, num); + sendText(ControlDistance, buffer); +} + +void PageHome::updateTime() +{ + char buf[DateTimeBufferLength + 5]; + if (!DateTimeManager::formatDateTimeReadable(buf, sizeof(buf))) + { + sendText(ControlCurrTime, NoValueText); + return; + } + + char timeBuf[13]; + if (strlen(buf) >= 19) + { + memcpy(timeBuf, buf + 11, 8); + timeBuf[8] = '\0'; + strcat(timeBuf, " UTC"); + } + else + { + // fallback: use whatever is available and append UTC + strncpy(timeBuf, buf, sizeof(timeBuf) - 5); + timeBuf[sizeof(timeBuf) - 5] = '\0'; + strcat(timeBuf, " UTC"); + } + + sendText(ControlCurrTime, timeBuf); +} + +void PageHome::updateMoonPhase() +{ + MoonPhase phase = Astronomy::getMoonPhaseFromUnix(DateTimeManager::getCurrentTime()); + setPicture(ControlMoonPhase, MoonImages[static_cast(phase)]); +} + +void PageHome::updateDirection() +{ + sendText(ControlBearingDirection, _lastDirection); +} + +void PageHome::configUpdated() +{ + Config* config = getConfig(); + + if (!config) + { + return; + } + + // update Nextion with config details + // Example: apply home page mapping and enabled mask to UI slots + for (uint8_t button = 0; button < ConfigHomeButtons; ++button) + { + char buffer[15]; + snprintf_P(buffer, sizeof(buffer), PSTR("%sHomeRelay%d"), HomeButtonPrefix, button + 1); + + uint8_t relayIndex = config->relay.homePageMapping[button]; + if (relayIndex <= 7) + { + _slotToRelay[button] = relayIndex; + + // set picture control (button image) - control names in your Nextion might differ + setPicture(buffer, _buttonImage[button]); + + // Use short name for home page display + sendText(buffer, config->relay.relays[relayIndex].shortName); + } + else + { + _slotToRelay[button] = 0xFF; + _buttonOn[button] = false; + _buttonImage[button] = ImageButtonColorGrey; + setPicture(buffer, _buttonImage[button]); + sendText(buffer, ""); + } + } + + // Update the boat name + sendText(ControlBoatName, config->location.name); +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageHome.h b/SmartFuseBox/PageHome.h new file mode 100644 index 0000000..93fa4d2 --- /dev/null +++ b/SmartFuseBox/PageHome.h @@ -0,0 +1,100 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "Config.h" +#include "BasePage.h" +#include "NextionIds.h" +#include "RelayController.h" + +class PageHome : public BasePage +{ +private: + RelayController* _relayController; + unsigned long _lastRefreshTime = 0; + float _lastTemp = NAN; + float _lastHumidity = NAN; + float _lastBearing = NAN; + float _lastSpeed = NAN; + char _lastDirection[5] = ""; + float _lastCompassTemp = NAN; + double _lastLatitude = NAN; + double _lastLongitude = NAN; + double _displayedLatitude = NAN; + double _displayedLongitude = NAN; + float _lastDistance = NAN; + + byte _compassTempAboveNorm = 0; + bool _speedInKnots = true; + bool _buttonOn[ConfigHomeButtons] = { false, false, false, false }; + byte _buttonImage[ConfigHomeButtons] = { ImageButtonColorGrey, ImageButtonColorGrey, ImageButtonColorGrey, ImageButtonColorGrey }; + const byte _buttonImageOn[ConfigHomeButtons] = { ImageButtonColorBlue, ImageButtonColorBlue, ImageButtonColorBlue, ImageButtonColorBlue }; + + uint8_t _slotToRelay[ConfigHomeButtons] = { 0xFF, 0xFF, 0xFF, 0xFF }; // map home slots 0..3 -> relay index 0..7 or 0xFF empty + + // Internal methods to update the display + void updateTemperature(); + void updateHumidity(); + void updateBearing(); + void updateSpeed(); + void updateDirection(); + void updateLatLon(); + void updateDistance(); + void updateTime(); + void updateMoonPhase(); + void updateAllDisplayItems(); + void updateAllButtons(); + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdHome; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void onEnterPage() override; + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleExternalUpdate(uint8_t updateType, const void* data) override; + +public: + explicit PageHome(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + RelayController* relayController); + + // Override configUpdated from BasePage + void configUpdated() override; + + // Setters for updating values + void setTemperature(float tempC); + void setHumidity(float humPerc); + void setBearing(float dir); + void setSpeed(float speedKn); + void setDirection(const char* dir); + void setCompassTemperature(float tempC); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageMoonPhase.cpp b/SmartFuseBox/PageMoonPhase.cpp new file mode 100644 index 0000000..240bd0f --- /dev/null +++ b/SmartFuseBox/PageMoonPhase.cpp @@ -0,0 +1,101 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageMoonPhase.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +#include "Astronomy.h" +#include "DateTimeManager.h" + + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonPrevious = 2; +constexpr char ControlMoonPhase[] = "p2"; +constexpr char ControlMoonPhaseName[] = "t1"; +constexpr char ControlMoonPhaseSeaDescription[] = "t2"; +constexpr char ControlMoonPhaseDescription[] = "t3"; + +constexpr unsigned long RefreshIntervalMs = 10000; +constexpr unsigned long AutoReturnMs = 120000; + +PageMoonPhase::PageMoonPhase(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer), + _pageEnterTime(0), _lastRefresh(0) +{ +} + +void PageMoonPhase::onEnterPage() +{ + // Called when page becomes active + _pageEnterTime = millis(); + _lastRefresh = millis(); + updateMoonDisplay(); +} + +void PageMoonPhase::begin() +{ + updateMoonDisplay(); +} + +void PageMoonPhase::refresh(unsigned long now) +{ + if (now - _lastRefresh >= RefreshIntervalMs) + { + _lastRefresh = now; + updateMoonDisplay(); + } + + // Auto-return to home after configured timeout + if (_pageEnterTime != 0 && (now - _pageEnterTime) >= AutoReturnMs) + { + navigateTo(PageIdHome); + } +} + +void PageMoonPhase::updateMoonDisplay() +{ + MoonPhase phase = Astronomy::getMoonPhaseFromUnix(DateTimeManager::getCurrentTime()); + setPicture(ControlMoonPhase, MoonImages[static_cast(phase)]); + + char buffer[BufferSizeMoonPhaseDescription]; + Astronomy::getMoonPhaseName(phase, buffer, sizeof(buffer)); + sendText(ControlMoonPhaseName, buffer); + Astronomy::getMoonPhaseSeaDescription(phase, buffer, sizeof(buffer)); + sendText(ControlMoonPhaseSeaDescription, buffer); + Astronomy::getMoonPhaseDescription(phase, buffer, sizeof(buffer)); + sendText(ControlMoonPhaseDescription, buffer); +} + +// Handle touch events for buttons +void PageMoonPhase::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonPrevious: + navigateTo(PageIdHome); + return; + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageMoonPhase.h b/SmartFuseBox/PageMoonPhase.h new file mode 100644 index 0000000..69c723b --- /dev/null +++ b/SmartFuseBox/PageMoonPhase.h @@ -0,0 +1,56 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + + + +class PageMoonPhase : public BasePage +{ +private: + void updateMoonDisplay(); + unsigned long _pageEnterTime; + unsigned long _lastRefresh; +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdMoon; } + void begin() override; + void onEnterPage() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageMoonPhase(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageRelay.cpp b/SmartFuseBox/PageRelay.cpp new file mode 100644 index 0000000..b77b3cc --- /dev/null +++ b/SmartFuseBox/PageRelay.cpp @@ -0,0 +1,230 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageRelay.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +// Nextion Names/Ids on current Home Page +constexpr uint8_t Button1 = 4; // b1 +constexpr uint8_t Button2 = 5; // b2 +constexpr uint8_t Button3 = 6; // b3 +constexpr uint8_t Button4 = 7; // b4 +constexpr uint8_t Button5 = 8; // b5 +constexpr uint8_t Button6 = 9; // b6 +constexpr uint8_t Button7 = 10; // b7 +constexpr uint8_t Button8 = 11; // b8 +constexpr uint8_t ButtonNext = 3; +constexpr uint8_t ButtonPrevious = 2; +constexpr uint8_t ButtonIdOffset = 4; // Offset to map button IDs to array indices + +constexpr char RelayButtonPrefix[] = "bRelay"; + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageRelay::PageRelay(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + RelayController* relayController) + : BasePage(serialPort, warningMgr, commandMgrComputer), _relayController(relayController) +{ + for (uint8_t i = 0; i < ConfigRelayCount; ++i) + { + _buttonImage[i] = ImageButtonColorGrey + ImageButtonColorOffset; + _buttonImageOn[i] = ImageButtonColorBlue + ImageButtonColorOffset; + } +} + +void PageRelay::begin() +{ + // If config already supplied before begin, apply it + if (getConfig()) + { + configUpdated(); + } + + for (uint8_t i = 0; i < ConfigRelayCount; ++i) + { + char variable[10]; + snprintf_P(variable, sizeof(variable), PSTR("%s%d"), RelayButtonPrefix, i + 1); + setPicture(variable, ImageButtonColorGrey + ImageButtonColorOffset); + setPicture2(variable, ImageButtonColorGrey + ImageButtonColorOffset); + } +} + +void PageRelay::onEnterPage() +{ + if (getConfig()) + { + configUpdated(); + } + + // Sync button states directly from RelayController + updateAllButtons(); + _lastRefreshTime = millis(); +} + +void PageRelay::refresh(unsigned long now) +{ + // Periodically re-sync button states in case relays were changed elsewhere + if (now - _lastRefreshTime >= RefreshIntervalMs) + { + _lastRefreshTime = now; + updateAllButtons(); + } +} + +// Handle touch events for buttons +void PageRelay::handleTouch(uint8_t compId, uint8_t eventType) +{ + Config* config = getConfig(); + + if (!config) + { + return; + } + + if (config->sound.hornRelayIndex < DefaultValue && + compId - 4 == config->sound.hornRelayIndex) + { + // relay button is configured to sound system (horn) and will be + // controlled via own command methods from sound pages + navigateTo(PageIdSoundSignals); + return; + } + + // Map component ID to button index + uint8_t buttonIndex = InvalidButtonIndex; + + switch (compId) + { + case Button1: + case Button2: + case Button3: + case Button4: + case Button5: + case Button6: + case Button7: + case Button8: + buttonIndex = compId - ButtonIdOffset; + break; + + case ButtonNext: + navigateNext(); + return; + + case ButtonPrevious: + navigatePrevious(); + return; + + default: + return; + } + + // Check if we have a valid config and the button is mapped to a relay + if (buttonIndex >= ConfigRelayCount) + { + return; + } + + uint8_t relayIndex = _slotToRelay[buttonIndex]; + + // Check if this button slot has a valid relay mapping + if (relayIndex == 0xFF || relayIndex >= ConfigRelayCount) + { + return; + } + + if (eventType == EventRelease) + { + if (_relayController) + { + bool newState = !_buttonOn[buttonIndex]; + _relayController->setRelayState(relayIndex, newState); + updateAllButtons(); + } + } +} + +void PageRelay::handleExternalUpdate(uint8_t updateType, const void* data) +{ + // Call base class first to handle heartbeat ACKs + BasePage::handleExternalUpdate(updateType, data); + + if (updateType == static_cast(PageUpdateType::RelayState)) + { + updateAllButtons(); + } +} + +void PageRelay::configUpdated() +{ + Config* config = getConfig(); + + if (!config) + { + return; + } + + for (uint8_t button = 0; button < ConfigRelayCount; ++button) + { + _slotToRelay[button] = button; + + // Initialize button to OFF state (grey) + _buttonOn[button] = false; + _buttonImage[button] = ImageButtonColorGrey + ImageButtonColorOffset; + + char relayPrefix[15]; + snprintf_P(relayPrefix, sizeof(relayPrefix), PSTR("%s%d"), RelayButtonPrefix, button + 1); + + setPicture(relayPrefix, ImageButtonColorGrey + ImageButtonColorOffset); + setPicture2(relayPrefix, ImageButtonColorGrey + ImageButtonColorOffset); + + sendText(relayPrefix, config->relay.relays[button].longName); + } +} + +void PageRelay::updateAllButtons() +{ + if (!_relayController) + return; + + CommandResult allStates = _relayController->getAllRelayStates(); + + for (uint8_t buttonIndex = 0; buttonIndex < ConfigRelayCount; ++buttonIndex) + { + uint8_t relayIndex = _slotToRelay[buttonIndex]; + if (relayIndex == 0xFF || relayIndex >= ConfigRelayCount) + continue; + + bool isOn = allStates.isRelayOn(relayIndex); + _buttonOn[buttonIndex] = isOn; + + uint8_t newColor = getButtonColor(buttonIndex, isOn, ConfigRelayCount) + ImageButtonColorOffset; + _buttonImage[buttonIndex] = newColor; + + char buttonName[15]; + snprintf_P(buttonName, sizeof(buttonName), PSTR("%s%d"), RelayButtonPrefix, buttonIndex + 1); + setPicture(buttonName, newColor); + setPicture2(buttonName, newColor); + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageRelay.h b/SmartFuseBox/PageRelay.h new file mode 100644 index 0000000..5ec3883 --- /dev/null +++ b/SmartFuseBox/PageRelay.h @@ -0,0 +1,67 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "Config.h" +#include "BasePage.h" +#include "NextionIds.h" +#include "RelayController.h" + + +class PageRelay : public BasePage +{ +private: + RelayController* _relayController; + unsigned long _lastRefreshTime = 0; + bool _buttonOn[ConfigRelayCount] = { false, false, false, false, false, false, false, false }; + byte _buttonImage[ConfigRelayCount] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + byte _buttonImageOn[ConfigRelayCount] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + uint8_t _slotToRelay[ConfigRelayCount] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + void updateAllButtons(); + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdRelay; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void onEnterPage() override; + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleExternalUpdate(uint8_t updateType, const void* data) override; + +public: + explicit PageRelay(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + RelayController* relayController); + + // Override configUpdated from BasePage + void configUpdated() override; +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageRelaySettings.cpp b/SmartFuseBox/PageRelaySettings.cpp new file mode 100644 index 0000000..6b84c47 --- /dev/null +++ b/SmartFuseBox/PageRelaySettings.cpp @@ -0,0 +1,694 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageRelaySettings.h" + +#if defined(NEXTION_DISPLAY_DEVICE) +#include "SystemFunctions.h" + +static const char SettingsError[] PROGMEM = "tRelayError"; +static const char SettingSelectedText[] PROGMEM = "tSelected"; +static const char PropertyButtonColor[] PROGMEM = "bco"; +static const char SettingRelayButton1[] PROGMEM = "b0"; +static const char SettingRelayButton2[] PROGMEM = "b1"; +static const char SettingRelayButton3[] PROGMEM = "b2"; +static const char SettingRelayButton4[] PROGMEM = "b4"; +static const char SettingRelayButton5[] PROGMEM = "b5"; +static const char SettingRelayButton6[] PROGMEM = "b6"; +static const char SettingRelayButton7[] PROGMEM = "b7"; +static const char SettingRelayButton8[] PROGMEM = "b8"; +static const char* const SettingRelayButtons[] PROGMEM = { + SettingRelayButton1, SettingRelayButton2, SettingRelayButton3, + SettingRelayButton4, SettingRelayButton5, SettingRelayButton6, + SettingRelayButton7, SettingRelayButton8 +}; + +// global variable of page that is currently being configured +static const char SettingsRelaySetupId[] PROGMEM = "relaySetupId"; +// global variable for maximum number of relays to select +static const char SettingsMaxSelectionCount[] PROGMEM = "relayMaximumSelectionCount"; + + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonPrevious = 3; +constexpr uint8_t ButtonNext = 2; + +constexpr uint16_t ButtonSelected = 5990; +constexpr uint16_t ButtonNotSelected = 65535; + +// home page button selection +constexpr uint8_t relayHomePageSetupId = 0; + +// always on button selection +constexpr uint8_t relayDefaultOn = 1; + +// allows user to have a dedicated horn relay +constexpr uint8_t relayHornRelay = 2; + +// associate button selection, two relays can be associated together so +// on together and off together, you can have two of these associations +constexpr uint8_t relayAssociate1 = 3; +constexpr uint8_t relayAssociate2 = 4; + +//page allowing users to select custom colors for each relay button that is on +constexpr uint8_t relayColorSelection = 5; + +// page allowing user to select night relay +constexpr uint8_t relayNightRelay = 6; + +PageRelaySettings::PageRelaySettings(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer), + _currentSettingsIndex(relayHomePageSetupId) +{ + _externalData[0] = '\0'; +} + +void PageRelaySettings::onEnterPage() +{ + Config* config = getConfig(); + + if (!config) + return; + + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[i])); + sendText(reinterpret_cast(buttonName), config->relay.relays[i].shortName); + } + + _currentSettingsIndex = relayHomePageSetupId; + loadConfigPage(); +} + +void PageRelaySettings::begin() +{ + // not used just now +} + +void PageRelaySettings::refresh(unsigned long now) +{ + (void)now; + //nothing to refresh currently +} + +// Handle touch events for buttons +void PageRelaySettings::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonPrevious: + if (_currentSettingsIndex > relayHomePageSetupId) + { + _currentSettingsIndex--; + } + break; + case ButtonNext: + if (validateExternalData()) + { + _currentSettingsIndex++; + } + break; + + default: + // Unknown component + return; + } + + loadConfigPage(); +} + +void PageRelaySettings::loadConfigPage() +{ + Config* config = getConfig(); + + if (!config) + return; + + clearSelection(); + + switch (_currentSettingsIndex) + { + case relayHomePageSetupId: + { + sendValue(FPSTR(SettingsRelaySetupId), relayHomePageSetupId); + + // Load the 4 selected relay indices and set their button colors + for (uint8_t i = 0; i < ConfigHomeButtons; i++) + { + uint8_t relayIndex = config->relay.homePageMapping[i]; + + if (relayIndex < ConfigRelayCount) + { + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[relayIndex])); + setComponentProperty(reinterpret_cast(buttonName), + FPSTR(PropertyButtonColor), ButtonSelected); + } + + _externalData[i] = '0' + relayIndex; + } + + _externalData[ConfigHomeButtons] = '\0'; + sendText(FPSTR(SettingSelectedText), _externalData); + + break; + } + + case relayDefaultOn: + { + sendValue(FPSTR(SettingsRelaySetupId), relayDefaultOn); + uint8_t selectionCount = 0; + + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + bool selButton = config->relay.relays[i].defaultState; + uint16_t buttonColor = selButton ? ButtonSelected : ButtonNotSelected; + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[i])); + + setComponentProperty(reinterpret_cast(buttonName), + FPSTR(PropertyButtonColor), buttonColor); + + if (selButton) + { + _externalData[selectionCount] = '0' + i; + selectionCount++; + } + } + + _externalData[selectionCount] = '\0'; + sendText(FPSTR(SettingSelectedText), _externalData); + + break; + } + + case relayHornRelay: + { + sendValue(FPSTR(SettingsRelaySetupId), relayHornRelay); + + uint8_t hornIndex = config->sound.hornRelayIndex; + + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + uint16_t buttonColor = (hornIndex == i) ? ButtonSelected : ButtonNotSelected; + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[i])); + setComponentProperty(reinterpret_cast(buttonName), + FPSTR(PropertyButtonColor), buttonColor); + } + + // Populate _externalData with current selection + if (hornIndex < ConfigRelayCount) + { + _externalData[0] = '0' + hornIndex; + _externalData[1] = '\0'; + } + else + { + _externalData[0] = '\0'; + } + + sendText(FPSTR(SettingSelectedText), _externalData); + + break; + } + + case relayAssociate1: + { + sendValue(FPSTR(SettingsRelaySetupId), relayAssociate1); + + uint8_t selectionCount = 0; + + // Iterate through all 8 relay buttons + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + bool isSelected = false; + + // Check if this relay is in the linked pair + for (uint8_t j = 0; j < ConfigLinkedRelayCount; j++) + { + if (config->relay.linkedRelays[0][j] == i && config->relay.linkedRelays[0][j] < ConfigRelayCount) + { + isSelected = true; + _externalData[selectionCount] = '0' + i; + selectionCount++; + break; + } + } + + uint16_t buttonColor = isSelected ? ButtonSelected : ButtonNotSelected; + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[i])); + setComponentProperty(reinterpret_cast(buttonName), + FPSTR(PropertyButtonColor), buttonColor); + } + + _externalData[selectionCount] = '\0'; + sendText(FPSTR(SettingSelectedText), _externalData); + + break; + } + + case relayAssociate2: + { + sendValue(FPSTR(SettingsRelaySetupId), relayAssociate2); + + uint8_t selectionCount = 0; + + // Iterate through all 8 relay buttons + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + bool isSelected = false; + + // Check if this relay is in the linked pair + for (uint8_t j = 0; j < ConfigLinkedRelayCount; j++) + { + if (config->relay.linkedRelays[1][j] == i && config->relay.linkedRelays[1][j] < ConfigRelayCount) + { + isSelected = true; + _externalData[selectionCount] = '0' + i; + selectionCount++; + break; + } + } + + uint16_t buttonColor = isSelected ? ButtonSelected : ButtonNotSelected; + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[i])); + setComponentProperty(reinterpret_cast(buttonName), + FPSTR(PropertyButtonColor), buttonColor); + } + + _externalData[selectionCount] = '\0'; + sendText(FPSTR(SettingSelectedText), _externalData); + + break; + } + + case relayColorSelection: + { + sendValue(FPSTR(SettingsRelaySetupId), relayColorSelection); + + // Load current button image selections and set button colors + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + uint8_t imageId = config->relay.relays[i].buttonImage; + + // Map image ID to character (1-5, default to white) + char colorChar; + uint16_t buttonColor; + + switch (imageId) + { + case ImageButtonColorBlue: + colorChar = '1'; + buttonColor = 1055; + break; + case ImageButtonColorGreen: + colorChar = '2'; + buttonColor = 2024; + break; + case ImageButtonColorOrange: + colorChar = '3'; + buttonColor = 64520; + break; + case ImageButtonColorRed: + colorChar = '4'; + buttonColor = 63488; + break; + case ImageButtonColorYellow: + colorChar = '5'; + buttonColor = 65504; + break; + default: + colorChar = '0'; + buttonColor = 65535; + break; + } + + _externalData[i] = colorChar; + + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[i])); + setComponentProperty(reinterpret_cast(buttonName), + FPSTR(PropertyButtonColor), buttonColor); + } + + _externalData[ConfigRelayCount] = '\0'; + sendText(FPSTR(SettingSelectedText), _externalData); + + break; + } + case relayNightRelay: + { + sendValue(FPSTR(SettingsRelaySetupId), relayNightRelay); + + uint8_t nightRelayIdx = config->lightSensor.nightRelayIndex; + + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + uint16_t buttonColor = (nightRelayIdx == i) ? ButtonSelected : ButtonNotSelected; + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[i])); + setComponentProperty(reinterpret_cast(buttonName), + FPSTR(PropertyButtonColor), buttonColor); + } + + if (nightRelayIdx < ConfigRelayCount) + { + _externalData[0] = '0' + nightRelayIdx; + _externalData[1] = '\0'; + } + else + { + _externalData[0] = '\0'; + } + + sendText(FPSTR(SettingSelectedText), _externalData); + + break; + } + default: + ConfigManager::save(); + navigateTo(PageIdAbout); + } +} + +void PageRelaySettings::handleText(const char* text) +{ + if (text == nullptr) + { + _externalData[0] = '\0'; + return; + } + + if (strlen(text) >= sizeof(_externalData)) + { + strncpy(_externalData, text, sizeof(_externalData) - 1); + _externalData[sizeof(_externalData) - 1] = '\0'; + } + else + { + snprintf_P(_externalData, sizeof(_externalData), PSTR("%s"), text); + } +} + +bool PageRelaySettings::validateExternalData() +{ + Config* config = getConfig(); + + if (!config) + { + sendText(FPSTR(SettingsError), F("Configuration error")); + return false; + } + + bool result = false; + + switch (_currentSettingsIndex) + { + case relayHomePageSetupId: + { + // Validate we have exactly ConfigHomeButtons characters + size_t len = strlen(_externalData); + if (len != ConfigHomeButtons) + { + sendText(FPSTR(SettingsError), F("Must select 4 relays")); + return false; + } + + // Validate each character is a valid relay index (0-7) + for (uint8_t i = 0; i < ConfigHomeButtons; i++) + { + if (_externalData[i] < '0' || _externalData[i] > '7') + { + sendText(FPSTR(SettingsError), F("Invalid relay index")); + return false; + } + } + + // Convert ASCII characters to numeric values and store + for (uint8_t i = 0; i < ConfigHomeButtons; i++) + { + config->relay.homePageMapping[i] = _externalData[i] - '0'; + } + + result = true; + break; + } + + case relayDefaultOn: + { + size_t len = strlen(_externalData); + + // Can have 0 to ConfigRelayCount selections + if (len > ConfigRelayCount) + { + sendText(FPSTR(SettingsError), F("Too many selections")); + return false; + } + + // Validate each character is a valid relay index (0-7) and check for duplicates + for (uint8_t i = 0; i < len; i++) + { + if (_externalData[i] < '0' || _externalData[i] > '7') + { + sendText(FPSTR(SettingsError), F("Invalid relay index")); + return false; + } + + // Check for duplicates + for (uint8_t j = i + 1; j < len; j++) + { + if (_externalData[i] == _externalData[j]) + { + sendText(FPSTR(SettingsError), F("Duplicate selection")); + return false; + } + } + } + + // Clear all default states first + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + config->relay.relays[i].defaultState = false; + } + + // Set selected relays to ON by default + for (uint8_t i = 0; i < len; i++) + { + uint8_t relayIndex = _externalData[i] - '0'; + config->relay.relays[relayIndex].defaultState = true; + } + + result = true; + break; + } + + case relayHornRelay: + { + // Allow empty selection (no horn relay) + if (strlen(_externalData) == 0) + { + config->sound.hornRelayIndex = 0xFF; // none + result = true; + break; + } + + // Validate single character + if (strlen(_externalData) != 1 || + _externalData[0] < '0' || _externalData[0] > '7') + { + sendText(FPSTR(SettingsError), F("Select 0 or 1 relay")); + return false; + } + + config->sound.hornRelayIndex = _externalData[0] - '0'; + result = true; + break; + } + + case relayAssociate1: + { + size_t len = strlen(_externalData); + + // Allow 0 or exactly 2 selections + if (len != 0 && len != ConfigLinkedRelayCount) + { + sendText(FPSTR(SettingsError), F("Select 0 or 2 relays")); + return false; + } + + // Validate relay indices and check for duplicates + for (uint8_t i = 0; i < len; i++) + { + if (_externalData[i] < '0' || _externalData[i] > '7') + { + sendText(FPSTR(SettingsError), F("Invalid relay index")); + return false; + } + + for (uint8_t j = i + 1; j < len; j++) + { + if (_externalData[i] == _externalData[j]) + { + sendText(FPSTR(SettingsError), F("Duplicate selection")); + return false; + } + } + } + + // Store selections or clear if none + if (len == 0) + { + // Clear linked relays + for (uint8_t i = 0; i < ConfigLinkedRelayCount; i++) + { + config->relay.linkedRelays[0][i] = 0xFF; + } + } + else + { + for (uint8_t i = 0; i < ConfigLinkedRelayCount; i++) + { + config->relay.linkedRelays[0][i] = _externalData[i] - '0'; + } + } + + result = true; + break; + } + + case relayAssociate2: + { + size_t len = strlen(_externalData); + + // Allow 0 or exactly 2 selections + if (len != 0 && len != ConfigLinkedRelayCount) + { + sendText(FPSTR(SettingsError), F("Select 0 or 2 relays")); + return false; + } + + // Validate relay indices and check for duplicates + for (uint8_t i = 0; i < len; i++) + { + if (_externalData[i] < '0' || _externalData[i] > '7') + { + sendText(FPSTR(SettingsError), F("Invalid relay index")); + return false; + } + + for (uint8_t j = i + 1; j < len; j++) + { + if (_externalData[i] == _externalData[j]) + { + sendText(FPSTR(SettingsError), F("Duplicate selection")); + return false; + } + } + } + + // Store selections or clear if none + if (len == 0) + { + // Clear linked relays + for (uint8_t i = 0; i < ConfigLinkedRelayCount; i++) + { + config->relay.linkedRelays[1][i] = 0xFF; + } + } + else + { + for (uint8_t i = 0; i < ConfigLinkedRelayCount; i++) + { + config->relay.linkedRelays[1][i] = _externalData[i] - '0'; + } + } + + result = true; + break; + } + + case relayColorSelection: + { + // Must have exactly 8 characters (one color per relay) + size_t len = strlen(_externalData); + if (len != ConfigRelayCount) + { + sendText(FPSTR(SettingsError), F("Must select all 8 colors")); + return false; + } + + // Convert color characters to image IDs and store + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + uint8_t imageId; + switch (_externalData[i]) + { + case '1': imageId = ImageButtonColorBlue; break; + case '2': imageId = ImageButtonColorGreen; break; + case '3': imageId = ImageButtonColorOrange; break; + case '4': imageId = ImageButtonColorRed; break; + case '5': imageId = ImageButtonColorYellow; break; + default: imageId = ImageButtonColorGrey; break; + } + + config->relay.relays[i].buttonImage = imageId; + } + + result = true; + break; + } + + case relayNightRelay: + { + if (strlen(_externalData) == 0) + { + config->lightSensor.nightRelayIndex = 0xFF; + result = true; + break; + } + + if (strlen(_externalData) != 1 || + _externalData[0] < '0' || _externalData[0] > '7') + { + sendText(FPSTR(SettingsError), F("Select 0 or 1 relay")); + return false; + } + + config->lightSensor.nightRelayIndex = _externalData[0] - '0'; + result = true; + break; + } + } + + if (result) + sendText(FPSTR(SettingsError), F("")); + + return result; +} + +void PageRelaySettings::clearSelection() +{ + for (uint8_t i = 0; i < ConfigRelayCount; i++) + { + const char* buttonName = reinterpret_cast(pgm_read_ptr(&SettingRelayButtons[i])); + setComponentProperty(reinterpret_cast(buttonName), + FPSTR(PropertyButtonColor), ButtonNotSelected); + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageRelaySettings.h b/SmartFuseBox/PageRelaySettings.h new file mode 100644 index 0000000..d6391f5 --- /dev/null +++ b/SmartFuseBox/PageRelaySettings.h @@ -0,0 +1,56 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" +#include "ConfigManager.h" + +class PageRelaySettings : public BasePage +{ +private: + uint8_t _currentSettingsIndex; + char _externalData[ConfigRelayCount + 1]; + bool validateExternalData(); + void loadConfigPage(); + void clearSelection(); +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSettingsRelays; } + void begin() override; + void onEnterPage() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleText(const char* text) override; +public: + explicit PageRelaySettings(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSettings.cpp b/SmartFuseBox/PageSettings.cpp new file mode 100644 index 0000000..c56835e --- /dev/null +++ b/SmartFuseBox/PageSettings.cpp @@ -0,0 +1,419 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageSettings.h" + +#if defined(NEXTION_DISPLAY_DEVICE) +#include "ConfigManager.h" +#include "SystemFunctions.h" + +static const char SettingsKeyboardInputLen[] PROGMEM = "keyboardInputLen"; +static const char SettingsKeyboardNumericOnly[] PROGMEM = "keyboardNumericOnly"; +static const char SettingsName[] PROGMEM = "tName"; +static const char SettingsError[] PROGMEM = "tError"; +static const char SettingsValue[] PROGMEM = "tValue"; +static const char SettingsNextButton[] PROGMEM = "b32"; +static const char SettingsNext[] PROGMEM = "Next"; +static const char SettingsSave[] PROGMEM = "Save"; + +static const char SettingsBoatName[] PROGMEM = "Boat Name"; +static const char SettingsCallSign[] PROGMEM = "Call Sign"; +static const char SettingsMmsiNumber[] PROGMEM = "MMSI Number"; +static const char SettingsHomePort[] PROGMEM = "Home Port"; +static const char ShortRelayDescription[] PROGMEM = "Short Relay Name %d"; +static const char LongRelayDescription[] PROGMEM = "Long Relay Name %d"; + + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonPrevious = 4; +constexpr uint8_t ButtonNext = 29; + +constexpr uint8_t ShowNumericKeyboard = 0; +constexpr uint8_t ShowAlphaKeyboard = 1; + +constexpr uint8_t SettingBoatName = 0; +constexpr uint8_t SettingBoatNameLen = 30; + +constexpr uint8_t SettingCallSign = 1; +constexpr uint8_t SettingCallSignLen = 10; + +constexpr uint8_t SettingMmsiNumber = 2; +constexpr uint8_t SettingMmsiNumberLen = 9; + +constexpr uint8_t SettingHomePortName = 3; + +constexpr uint8_t SettingShortRelayOffset = 4; +constexpr uint8_t SettingShortRelay1 = 4; +constexpr uint8_t SettingShortRelay2 = 5; +constexpr uint8_t SettingShortRelay3 = 6; +constexpr uint8_t SettingShortRelay4 = 7; +constexpr uint8_t SettingShortRelay5 = 8; +constexpr uint8_t SettingShortRelay6 = 9; +constexpr uint8_t SettingShortRelay7 = 10; +constexpr uint8_t SettingShortRelay8 = 11; + +constexpr uint8_t SettingLongRelayOffset = 12; +constexpr uint8_t SettingLongRelay1 = 12; +constexpr uint8_t SettingLongRelay2 = 13; +constexpr uint8_t SettingLongRelay3 = 14; +constexpr uint8_t SettingLongRelay4 = 15; +constexpr uint8_t SettingLongRelay5 = 16; +constexpr uint8_t SettingLongRelay6 = 17; +constexpr uint8_t SettingLongRelay7 = 18; +constexpr uint8_t SettingLongRelay8 = 19; + + +constexpr uint8_t LastSettings = 19; + +PageSettings::PageSettings(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer), + _currentSettingsIndex(SettingBoatName) +{ + _externalData[0] = '\0'; +} + +void PageSettings::onEnterPage() +{ + _currentSettingsIndex = SettingBoatName; + loadConfigPage(); +} + +void PageSettings::begin() +{ + // not used just now +} + +void PageSettings::refresh(unsigned long now) +{ + (void)now; + //nothing to refresh currently +} + +// Handle touch events for buttons +void PageSettings::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonPrevious: + if (_currentSettingsIndex > SettingBoatName) + { + _currentSettingsIndex--; + } + break; + case ButtonNext: + if (validateExternalData()) + { + _currentSettingsIndex++; + } + break; + + default: + // Unknown component + return; + } + + loadConfigPage(); +} + +void PageSettings::loadConfigPage() +{ + Config* config = getConfig(); + + switch (_currentSettingsIndex) + { + case SettingBoatName: + loadPage(FPSTR(SettingsBoatName), config->location.name, SettingBoatNameLen, ShowAlphaKeyboard, false); + break; + + case SettingCallSign: + loadPage(FPSTR(SettingsCallSign), config->location.callSign, SettingCallSignLen, ShowAlphaKeyboard, true); + break; + + case SettingMmsiNumber: + loadPage(FPSTR(SettingsMmsiNumber), config->location.mmsi, SettingMmsiNumberLen, ShowNumericKeyboard, true); + break; + + case SettingHomePortName: + loadPage(FPSTR(SettingsHomePort), config->location.homePort, ConfigHomePortLength - 1, ShowAlphaKeyboard, true); + break; + + case SettingShortRelay1: + case SettingShortRelay2: + case SettingShortRelay3: + case SettingShortRelay4: + case SettingShortRelay5: + case SettingShortRelay6: + case SettingShortRelay7: + case SettingShortRelay8: + { + char buffer[20]; + uint8_t relayIndex = _currentSettingsIndex - SettingShortRelayOffset; + + // Validate array bounds + if (relayIndex >= ConfigRelayCount) + { + sendText(FPSTR(SettingsError), F("Invalid relay index")); + return; + } + + // Display as 1-8 instead of 3-10 + snprintf_P(buffer, sizeof(buffer), ShortRelayDescription, relayIndex + 1); + + loadPage( + buffer, + config->relay.relays[relayIndex].shortName, + ConfigShortRelayNameLength - 1, + ShowAlphaKeyboard, + true); + + break; + } + + case SettingLongRelay1: + case SettingLongRelay2: + case SettingLongRelay3: + case SettingLongRelay4: + case SettingLongRelay5: + case SettingLongRelay6: + case SettingLongRelay7: + case SettingLongRelay8: + { + char buffer[20]; + uint8_t relayIndex = _currentSettingsIndex - SettingLongRelayOffset; + + // Validate array bounds + if (relayIndex >= ConfigRelayCount) + { + sendText(FPSTR(SettingsError), F("Invalid relay index")); + return; + } + + snprintf_P(buffer, sizeof(buffer), LongRelayDescription, relayIndex + 1); + loadPage( + buffer, + config->relay.relays[relayIndex].longName, + ConfigLongRelayNameLength - 1, + ShowAlphaKeyboard, + true); + + break; + } + + default: + ConfigManager::save(); + navigateTo(PageIdAbout); + } +} + +void PageSettings::loadPage(const char* settingName, const char* settingValue, uint8_t maxInputLength, uint8_t keyboardType, bool allowPrevious) +{ + sendValue(FPSTR(SettingsKeyboardInputLen), maxInputLength); + sendValue(FPSTR(SettingsKeyboardNumericOnly), keyboardType); + sendText(FPSTR(SettingsName), settingName); + sendText(FPSTR(SettingsError), F("")); + sendText(FPSTR(SettingsValue), settingValue); + + if (allowPrevious) + sendCommand(F("vis b3,1")); + else + sendCommand(F("vis b3,0")); + + if (_currentSettingsIndex == LastSettings) + sendText(FPSTR(SettingsNextButton), FPSTR(SettingsSave)); + else + sendText(FPSTR(SettingsNextButton), FPSTR(SettingsNext)); +} + +void PageSettings::loadPage(const __FlashStringHelper* settingName, const char* settingValue, uint8_t maxInputLength, uint8_t keyboardType, bool allowPrevious) +{ + sendValue(FPSTR(SettingsKeyboardInputLen), maxInputLength); + sendValue(FPSTR(SettingsKeyboardNumericOnly), keyboardType); + sendText(FPSTR(SettingsName), settingName); + sendText(FPSTR(SettingsError), F("")); + sendText(FPSTR(SettingsValue), settingValue); + + if (allowPrevious) + sendCommand(F("vis b3,1")); + else + sendCommand(F("vis b3,0")); + + if (_currentSettingsIndex == LastSettings) + sendText(FPSTR(SettingsNextButton), FPSTR(SettingsSave)); + else + sendText(FPSTR(SettingsNextButton), FPSTR(SettingsNext)); +} + +void PageSettings::handleText(const char* text) +{ + if (text == nullptr) + { + _externalData[0] = '\0'; + return; + } + + if (strlen(text) >= sizeof(_externalData)) + { + // Truncate to fit + strncpy(_externalData, text, sizeof(_externalData) - 1); + _externalData[sizeof(_externalData) - 1] = '\0'; + } + else + { + snprintf_P(_externalData, sizeof(_externalData), PSTR("%s"), text); + } +} + +bool PageSettings::validateExternalData() +{ + if (strlen(_externalData) == 0) + { + sendText(FPSTR("tError"), F("Can not be blank")); + return false; + } + + Config* config = getConfig(); + + if (!config) + { + sendText(FPSTR("tError"), F("Configuration error")); + return false; + } + + bool result = false; + + switch (_currentSettingsIndex) + { + case SettingBoatName: + strncpy(config->location.name, _externalData, ConfigMaxNameLength - 1); + config->location.name[ConfigMaxNameLength - 1] = '\0'; + result = true; + + break; + + case SettingCallSign: + strncpy(config->location.callSign, _externalData, ConfigCallSignLength - 1); + config->location.callSign[ConfigCallSignLength - 1] = '\0'; + result = true; + + break; + + case SettingMmsiNumber: + // MMSI number: must be exactly 9 digits + if (strlen(_externalData) != 9) + { + sendText(FPSTR("tError"), F("MMSI must be 9 digits")); + return false; + } + + if (!SystemFunctions::isAllDigits(_externalData)) + { + sendText(FPSTR("tError"), F("MMSI must be numeric")); + return false; + } + + strncpy(config->location.mmsi, _externalData, ConfigMmsiLength - 1); + config->location.mmsi[ConfigMmsiLength - 1] = '\0'; + result = true; + + break; + + case SettingHomePortName: + if (strlen(_externalData) > ConfigHomePortLength - 1) + { + sendText(FPSTR("tError"), F("Home port name too long")); + return false; + } + + strncpy(config->location.homePort, _externalData, ConfigHomePortLength - 1); + config->location.homePort[ConfigHomePortLength - 1] = '\0'; + result = true; + + break; + + case SettingShortRelay1: + case SettingShortRelay2: + case SettingShortRelay3: + case SettingShortRelay4: + case SettingShortRelay5: + case SettingShortRelay6: + case SettingShortRelay7: + case SettingShortRelay8: + { + if (strlen(_externalData) > ConfigShortRelayNameLength - 1) + { + sendText(FPSTR("tError"), F("Relay name too long")); + return false; + } + + uint8_t relayIndex = _currentSettingsIndex - SettingShortRelayOffset; + + if (relayIndex >= ConfigRelayCount) + { + sendText(FPSTR(SettingsError), F("Invalid relay index")); + return false; + } + + strncpy(config->relay.relays[relayIndex].shortName, _externalData, ConfigShortRelayNameLength - 1); + config->relay.relays[relayIndex].shortName[ConfigShortRelayNameLength - 1] = '\0'; + result = true; + + break; + } + + case SettingLongRelay1: + case SettingLongRelay2: + case SettingLongRelay3: + case SettingLongRelay4: + case SettingLongRelay5: + case SettingLongRelay6: + case SettingLongRelay7: + case SettingLongRelay8: + { + if (strlen(_externalData) > ConfigLongRelayNameLength - 1) + { + sendText(FPSTR("tError"), F("Relay name too long")); + return false; + } + + uint8_t relayIndex = _currentSettingsIndex - SettingLongRelayOffset; + + if (relayIndex >= ConfigRelayCount) + { + sendText(FPSTR(SettingsError), F("Invalid relay index")); + return false; + } + + strncpy(config->relay.relays[relayIndex].longName, _externalData, ConfigLongRelayNameLength - 1); + config->relay.relays[relayIndex].longName[ConfigLongRelayNameLength - 1] = '\0'; + result = true; + + break; + } + } + + if (result) + sendText(FPSTR("tError"), F("")); + + return result; +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSettings.h b/SmartFuseBox/PageSettings.h new file mode 100644 index 0000000..cec117f --- /dev/null +++ b/SmartFuseBox/PageSettings.h @@ -0,0 +1,58 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + +constexpr uint8_t MaximumDataLength = 31; // max length for external data incl null terminator + +class PageSettings : public BasePage +{ +private: + uint8_t _currentSettingsIndex; + char _externalData[MaximumDataLength]; + bool validateExternalData(); + void loadPage(const char* settingName, const char* settingValue, uint8_t maxInputLength, uint8_t keyboardType, bool allowPrevious); + void loadPage(const __FlashStringHelper* settingName, const char* settingValue, uint8_t maxInputLength, uint8_t keyboardType, bool allowPrevious); + void loadConfigPage(); +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSettings; } + void begin() override; + void onEnterPage() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleText(const char* text) override; +public: + explicit PageSettings(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundEmergency.cpp b/SmartFuseBox/PageSoundEmergency.cpp new file mode 100644 index 0000000..3e780a1 --- /dev/null +++ b/SmartFuseBox/PageSoundEmergency.cpp @@ -0,0 +1,70 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageSoundEmergency.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +// Nextion Names/Ids on current Page +constexpr uint8_t BtnSoS = 2; // b3 +constexpr uint8_t BtnBack = 3; // b4 + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageSoundEmergency::PageSoundEmergency(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController) + : BasePage(serialPort, warningMgr, commandMgrComputer), _soundController(soundController) +{ +} + +void PageSoundEmergency::begin() +{ + +} + +void PageSoundEmergency::refresh(unsigned long now) +{ + (void)now; +} + +// Handle touch events for buttons +void PageSoundEmergency::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnSoS: + if (_soundController) + { + _soundController->playSound(SoundType::Sos); + } + break; + + case BtnBack: + navigateTo(PageIdSoundSignals); + break; + + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundEmergency.h b/SmartFuseBox/PageSoundEmergency.h new file mode 100644 index 0000000..5aa02ac --- /dev/null +++ b/SmartFuseBox/PageSoundEmergency.h @@ -0,0 +1,55 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" +#include "SoundController.h" + + +class PageSoundEmergency : public BasePage +{ +private: + SoundController* _soundController; + unsigned long _lastRefreshTime = 0; + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSoundEmergency; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageSoundEmergency(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundFog.cpp b/SmartFuseBox/PageSoundFog.cpp new file mode 100644 index 0000000..d9a0a1b --- /dev/null +++ b/SmartFuseBox/PageSoundFog.cpp @@ -0,0 +1,69 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageSoundFog.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +// Nextion Names/Ids on current Page +constexpr uint8_t BtnFogHorn = 4; // b3 +constexpr uint8_t BtnBack = 5; // b4 + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageSoundFog::PageSoundFog(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController) + : BasePage(serialPort, warningMgr, commandMgrComputer), _soundController(soundController) +{ +} + +void PageSoundFog::begin() +{ + +} + +void PageSoundFog::refresh(unsigned long now) +{ + (void)now; +} + +// Handle touch events for buttons +void PageSoundFog::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnFogHorn: + if (_soundController) + { + _soundController->playSound(SoundType::Fog); + } + break; + + case BtnBack: + navigateTo(PageIdSoundSignals); + break; + + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundFog.h b/SmartFuseBox/PageSoundFog.h new file mode 100644 index 0000000..ade3d60 --- /dev/null +++ b/SmartFuseBox/PageSoundFog.h @@ -0,0 +1,55 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" +#include "SoundController.h" + + +class PageSoundFog : public BasePage +{ +private: + SoundController* _soundController; + unsigned long _lastRefreshTime = 0; + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSoundFogSignals; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageSoundFog(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundManeuvering.cpp b/SmartFuseBox/PageSoundManeuvering.cpp new file mode 100644 index 0000000..73f61bd --- /dev/null +++ b/SmartFuseBox/PageSoundManeuvering.cpp @@ -0,0 +1,95 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageSoundManeuvering.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + + + // Nextion Names/Ids on current Page +constexpr uint8_t BtnStarboard = 2; // b0 +constexpr uint8_t BtnAstern = 3; // b1 +constexpr uint8_t BtnPort = 4; // b2 +constexpr uint8_t BtnDanger = 5; // b3 +constexpr uint8_t BtnBack = 6; // b4 + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageSoundManeuvering::PageSoundManeuvering(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController) + : BasePage(serialPort, warningMgr, commandMgrComputer), _soundController(soundController) +{} + +void PageSoundManeuvering::begin() +{ + +} + +void PageSoundManeuvering::refresh(unsigned long now) +{ + (void)now; +} + +// Handle touch events for buttons +void PageSoundManeuvering::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnStarboard: + if (_soundController) + { + _soundController->playSound(SoundType::MoveStarboard); + } + break; + + case BtnAstern: + if (_soundController) + { + _soundController->playSound(SoundType::MoveAstern); + } + // Also send command to update active signal state on main page + break; + + case BtnPort: + if (_soundController) + { + _soundController->playSound(SoundType::MovePort); + } + break; + + case BtnDanger: + if (_soundController) + { + _soundController->playSound(SoundType::MoveDanger); + } + break; + + case BtnBack: + navigateTo(PageIdSoundSignals); + break; + + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundManeuvering.h b/SmartFuseBox/PageSoundManeuvering.h new file mode 100644 index 0000000..1e7fffb --- /dev/null +++ b/SmartFuseBox/PageSoundManeuvering.h @@ -0,0 +1,56 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "Config.h" +#include "BasePage.h" +#include "NextionIds.h" +#include "SoundController.h" + + +class PageSoundManeuvering : public BasePage +{ +private: + SoundController* _soundController; + unsigned long _lastRefreshTime = 0; + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSoundManeuveringSignals; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageSoundManeuvering(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundOther.cpp b/SmartFuseBox/PageSoundOther.cpp new file mode 100644 index 0000000..be4c7c3 --- /dev/null +++ b/SmartFuseBox/PageSoundOther.cpp @@ -0,0 +1,70 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageSoundOther.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +// Nextion Names/Ids on current Page +constexpr uint8_t BtnSoS = 3; // b3 +constexpr uint8_t BtnBack = 2; // b4 + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageSoundOther::PageSoundOther(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController) + : BasePage(serialPort, warningMgr, commandMgrComputer), _soundController(soundController) +{ +} + +void PageSoundOther::begin() +{ + +} + +void PageSoundOther::refresh(unsigned long now) +{ + (void)now; +} + +// Handle touch events for buttons +void PageSoundOther::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnSoS: + if (_soundController) + { + _soundController->playSound(SoundType::Test); + } + break; + + case BtnBack: + navigateTo(PageIdSoundSignals); + break; + + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundOther.h b/SmartFuseBox/PageSoundOther.h new file mode 100644 index 0000000..8b28b40 --- /dev/null +++ b/SmartFuseBox/PageSoundOther.h @@ -0,0 +1,55 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" +#include "SoundController.h" + + +class PageSoundOther : public BasePage +{ +private: + SoundController* _soundController; + unsigned long _lastRefreshTime = 0; + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSoundOther; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageSoundOther(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundOvertaking.cpp b/SmartFuseBox/PageSoundOvertaking.cpp new file mode 100644 index 0000000..1c1da06 --- /dev/null +++ b/SmartFuseBox/PageSoundOvertaking.cpp @@ -0,0 +1,95 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageSoundOvertaking.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + + +// Nextion Names/Ids on current Page +constexpr uint8_t BtnStarboard = 2; // b0 +constexpr uint8_t BtnConsent = 5; // b1 +constexpr uint8_t BtnPort = 3; // b2 +constexpr uint8_t BtnDanger = 4; // b3 +constexpr uint8_t BtnBack = 6; // b4 + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageSoundOvertaking::PageSoundOvertaking(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController) + : BasePage(serialPort, warningMgr, commandMgrComputer), _soundController(soundController) +{ +} + +void PageSoundOvertaking::begin() +{ + +} + +void PageSoundOvertaking::refresh(unsigned long now) +{ + (void)now; +} + +// Handle touch events for buttons +void PageSoundOvertaking::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnStarboard: + if (_soundController) + { + _soundController->playSound(SoundType::OvertakeStarboard); + } + break; + + case BtnConsent: + if (_soundController) + { + _soundController->playSound(SoundType::OvertakeConsent); + } + break; + + case BtnPort: + if (_soundController) + { + _soundController->playSound(SoundType::OvertakePort); + } + break; + + case BtnDanger: + if (_soundController) + { + _soundController->playSound(SoundType::OvertakeDanger); + } + break; + + case BtnBack: + navigateTo(PageIdSoundSignals); + break; + + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundOvertaking.h b/SmartFuseBox/PageSoundOvertaking.h new file mode 100644 index 0000000..c0373a4 --- /dev/null +++ b/SmartFuseBox/PageSoundOvertaking.h @@ -0,0 +1,55 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" +#include "SoundController.h" + + +class PageSoundOvertaking : public BasePage +{ +private: + SoundController* _soundController; + unsigned long _lastRefreshTime = 0; + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSoundOvertaking; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageSoundOvertaking(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundSignals.cpp b/SmartFuseBox/PageSoundSignals.cpp new file mode 100644 index 0000000..0408865 --- /dev/null +++ b/SmartFuseBox/PageSoundSignals.cpp @@ -0,0 +1,151 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageSoundSignals.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +// Nextion Names/Ids on current Page +constexpr uint8_t BtnManeuvering = 4; // b0 +constexpr uint8_t BtnFog = 6; // b2 +constexpr uint8_t BtnNarrowChannel = 5; // b1 +constexpr uint8_t BtnEmergency = 7; // b3 +constexpr uint8_t BtnOther = 8; // b4 +constexpr uint8_t BtnCancelAll = 9; // b5 +constexpr uint8_t ButtonNext = 3; +constexpr uint8_t ButtonPrevious = 2; +constexpr char CancelButton[] = "b5"; + +constexpr unsigned long RefreshIntervalMs = 10000; + + +PageSoundSignals::PageSoundSignals(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController) + : BasePage(serialPort, warningMgr, commandMgrComputer), _soundController(soundController) +{ +} + +void PageSoundSignals::begin() +{ + setPicture(CancelButton, ImageButtonColorGrey + ImageButtonColorOffset); + setPicture2(CancelButton, ImageButtonColorGrey + ImageButtonColorOffset); +} + +void PageSoundSignals::onEnterPage() +{ + if (_soundController) + { + updateButtonStates(_soundController->isPlaying()); + } + _lastRefreshTime = millis(); +} + +void PageSoundSignals::refresh(unsigned long now) +{ + if (now - _lastRefreshTime >= RefreshIntervalMs) + { + _lastRefreshTime = now; + if (_soundController) + { + updateButtonStates(_soundController->isPlaying()); + } + } +} + +// Handle touch events for buttons +void PageSoundSignals::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case BtnManeuvering: + navigateTo(PageIdSoundManeuveringSignals); + break; + + case BtnFog: + navigateTo(PageIdSoundFogSignals); + break; + + case BtnNarrowChannel: + navigateTo(PageIdSoundOvertaking); + break; + + case BtnEmergency: + navigateTo(PageIdSoundEmergency); + break; + + case BtnOther: + navigateTo(PageIdSoundOther); + break; + + case BtnCancelAll: + if (_soundController) + { + if (_soundController->isPlaying()) + { + _soundController->playSound(SoundType::None); + } + updateButtonStates(false); + } + break; + + case ButtonNext: + navigateNext(); + return; + + case ButtonPrevious: + navigatePrevious(); + return; + + default: + return; + } +} + +void PageSoundSignals::updateButtonStates(bool isActive) +{ + if (isActive) + { + setPicture(CancelButton, ImageButtonColorBlue + ImageButtonColorOffset); + setPicture2(CancelButton, ImageButtonColorBlue + ImageButtonColorOffset); + } + else + { + setPicture(CancelButton, ImageButtonColorGrey + ImageButtonColorOffset); + setPicture2(CancelButton, ImageButtonColorGrey + ImageButtonColorOffset); + } + +} + +void PageSoundSignals::handleExternalUpdate(uint8_t updateType, const void* data) +{ + // Call base class first to handle heartbeat ACKs + BasePage::handleExternalUpdate(updateType, data); + + if (updateType == static_cast(PageUpdateType::SoundSignal) && data != nullptr) + { + const BoolStateUpdate* update = static_cast(data); + updateButtonStates(update->value); + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSoundSignals.h b/SmartFuseBox/PageSoundSignals.h new file mode 100644 index 0000000..dd403df --- /dev/null +++ b/SmartFuseBox/PageSoundSignals.h @@ -0,0 +1,60 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "Config.h" +#include "BasePage.h" +#include "NextionIds.h" +#include "SoundController.h" + + +class PageSoundSignals : public BasePage +{ +private: + SoundController* _soundController; + unsigned long _lastRefreshTime = 0; + + void updateButtonStates(bool isActive); + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSoundSignals; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void onEnterPage() override; + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleExternalUpdate(uint8_t updateType, const void* data) override; + +public: + explicit PageSoundSignals(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer, + SoundController* soundController); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSplash.h b/SmartFuseBox/PageSplash.h new file mode 100644 index 0000000..5a70fba --- /dev/null +++ b/SmartFuseBox/PageSplash.h @@ -0,0 +1,76 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include "NextionIds.h" +#include +#include "ConfigManager.h" + +class PageSplash : public BaseDisplayPage +{ +public: + PageSplash(Stream* serialPort) + : BaseDisplayPage(serialPort) + {} + + uint8_t getPageId() const override { return PageIdSplash; } + + void onEnterPage() override + { + // No special actions needed on entering splash page + } + + void begin() override + { + Config* config = ConfigManager::getConfigPtr(); + + if (!config) + { + return; + } + + // initialize global variables + sendText(F("pageSplash.vaBoatName"), config->location.name); + sendText(F("pageSplash.vaHomeB1"), config->relay.relays[0].shortName); + sendText(F("pageSplash.vaHomeB2"), config->relay.relays[1].shortName); + sendText(F("pageSplash.vaHomeB3"), config->relay.relays[2].shortName); + sendText(F("pageSplash.vaHomeB4"), config->relay.relays[3].shortName); + + // Relay page global variables + sendText(F("pageSplash.vaRelayName1"), config->relay.relays[0].longName); + sendText(F("pageSplash.vaRelayName2"), config->relay.relays[1].longName); + sendText(F("pageSplash.vaRelayName3"), config->relay.relays[2].longName); + sendText(F("pageSplash.vaRelayName4"), config->relay.relays[3].longName); + sendText(F("pageSplash.vaRelayName5"), config->relay.relays[4].longName); + sendText(F("pageSplash.vaRelayName6"), config->relay.relays[5].longName); + sendText(F("pageSplash.vaRelayName7"), config->relay.relays[8].longName); + sendText(F("pageSplash.vaRelayName8"), config->relay.relays[7].longName); + } + + void refresh(unsigned long now) override + { + (void)now; + // No periodic refresh needed for splash page + } +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSystem.cpp b/SmartFuseBox/PageSystem.cpp new file mode 100644 index 0000000..d2c0179 --- /dev/null +++ b/SmartFuseBox/PageSystem.cpp @@ -0,0 +1,203 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageSystem.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include "SystemFunctions.h" +#include "SystemCpuMonitor.h" + +constexpr char ControlFuseBoxCpu[] = "t5"; +constexpr char ControlFuseBoxMemory[] = "t6"; +constexpr char ControlPanelCpu[] = "t7"; +constexpr char ControlPanelMemory[] = "t8"; + +constexpr uint8_t ButtonNext = 3; +constexpr uint8_t ButtonPrevious = 2; + + +constexpr unsigned long RefreshSystemIntervalMs = 500; + +constexpr char BytesSuffix[] = " bytes"; +const char MemoryFormat[] PROGMEM = "%d bytes"; +const char CpuFormat[] PROGMEM = "%d%%"; + +PageSystem::PageSystem(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer) +{ + +} + +void PageSystem::begin() +{ + +} + +void PageSystem::onEnterPage() +{ + _lastRefreshTime = millis(); + + updateAllDisplayItems(); +} + +void PageSystem::refresh(unsigned long now) +{ + updateAllDisplayItems(); + + if (now - _lastRefreshTime >= RefreshSystemIntervalMs) + { + SerialCommandManager* compMgr = getCommandMgrComputer(); + + if (compMgr) + { + compMgr->sendDebug(F("Sending F2/F3"), F("SystemPage")); + } + + _lastRefreshTime = now; + + setFuseBoxCpu(SystemCpuMonitor::getCpuUsage()); + setFuseBoxMemory(SystemFunctions::freeMemory()); + + updateControlPanelCpu(); + updateControlPanelMemory(); + } +} + +void PageSystem::updateAllDisplayItems() +{ + updateControlPanelCpu(); + updateControlPanelMemory(); + updateFuseBoxCpu(); + updateFuseBoxMemory(); +} + +// Handle touch events for buttons +void PageSystem::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonPrevious: + navigatePrevious(); + break; + + case ButtonNext: + navigateNext(); + return; + + default: + return; + } +} + +void PageSystem::handleExternalUpdate(uint8_t updateType, const void* data) +{ + // Call base class first to handle heartbeat ACKs + BasePage::handleExternalUpdate(updateType, data); + + if (updateType == static_cast(PageUpdateType::CpuUsage) && data != nullptr) + { + const UInt8Update* update = static_cast(data); + setFuseBoxCpu(update->value); + } + else if (updateType == static_cast(PageUpdateType::MemoryUsage) && data != nullptr) + { + const UInt16Update* update = static_cast(data); + setFuseBoxMemory(update->value); + } +} + +// --- Public setters --- + +void PageSystem::setFuseBoxCpu(uint8_t cpu) +{ + if (_lastFuseBoxCpu != cpu) + { + _lastFuseBoxCpu = cpu; + updateFuseBoxCpu(); + } +} + +void PageSystem::setFuseBoxMemory(uint16_t memory) +{ + if (_lastFuseBoxMemory != memory) + { + _lastFuseBoxMemory = memory; + updateFuseBoxMemory(); + } +} + + +// --- Private update methods --- +void PageSystem::updateControlPanelCpu() +{ + uint8_t cpu = SystemCpuMonitor::getCpuUsage(); + + if (cpu != _lastControlPanelCpu) + { + _lastControlPanelCpu = cpu; + char value[10]; + snprintf_P(value, sizeof(value), CpuFormat, cpu); + sendText(ControlPanelCpu, value); + } +} + +void PageSystem::updateControlPanelMemory() +{ + uint16_t memory = SystemFunctions::freeMemory(); + + if (memory != _lastControlPanelMemory) + { + _lastControlPanelMemory = memory; + char value[15]; + snprintf_P(value, sizeof(value), MemoryFormat, memory); + sendText(ControlPanelMemory, value); + } +} + +void PageSystem::updateFuseBoxCpu() +{ + if (_lastFuseBoxCpu == MaxUint8Value) + { + sendText(ControlFuseBoxCpu, NoValueText); + return; + } + + char buffer[12]; + snprintf_P(buffer, sizeof(buffer), CpuFormat, _lastFuseBoxCpu); + sendText(ControlFuseBoxCpu, buffer); +} + +void PageSystem::updateFuseBoxMemory() +{ + if (_lastFuseBoxMemory == MaxUint16Value) + { + sendText(ControlFuseBoxMemory, NoValueText); + return; + } + + char buffer[16]; + snprintf_P(buffer, sizeof(buffer), MemoryFormat, _lastFuseBoxMemory); + sendText(ControlFuseBoxMemory, buffer); +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageSystem.h b/SmartFuseBox/PageSystem.h new file mode 100644 index 0000000..9469aed --- /dev/null +++ b/SmartFuseBox/PageSystem.h @@ -0,0 +1,69 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "Config.h" +#include "BasePage.h" +#include "NextionIds.h" + +class PageSystem : public BasePage +{ +private: + unsigned long _lastRefreshTime = 0; + uint8_t _lastFuseBoxCpu = MaxUint8Value; + uint16_t _lastFuseBoxMemory = MaxUint16Value; + uint8_t _lastControlPanelCpu = MaxUint8Value; + uint16_t _lastControlPanelMemory = MaxUint16Value; + + // Internal methods to update the display + void updateControlPanelCpu(); + void updateControlPanelMemory(); + void updateFuseBoxCpu(); + void updateFuseBoxMemory(); + void updateAllDisplayItems(); + +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdSystem; } + void begin() override; + void refresh(unsigned long now) override; + + //optional overrides + void onEnterPage() override; + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleExternalUpdate(uint8_t updateType, const void* data) override; + +public: + explicit PageSystem(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); + + // Setters for updating values + void setFuseBoxCpu(uint8_t cput); + void setFuseBoxMemory(uint16_t memory); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageVhfChannels.cpp b/SmartFuseBox/PageVhfChannels.cpp new file mode 100644 index 0000000..ac891c7 --- /dev/null +++ b/SmartFuseBox/PageVhfChannels.cpp @@ -0,0 +1,63 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageVhfChannels.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonPrevious = 2; + +PageVhfChannels::PageVhfChannels(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer) +{ +} + +void PageVhfChannels::onEnterPage() +{ + // not used just now +} + +void PageVhfChannels::begin() +{ + // not used just now +} + +void PageVhfChannels::refresh(unsigned long now) +{ + (void)now; + //nothing to refresh currently +} + +// Handle touch events for buttons +void PageVhfChannels::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonPrevious: + navigateTo(PageIdVhfRadio); + return; + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageVhfChannels.h b/SmartFuseBox/PageVhfChannels.h new file mode 100644 index 0000000..7da08ad --- /dev/null +++ b/SmartFuseBox/PageVhfChannels.h @@ -0,0 +1,51 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + + + +class PageVhfChannels : public BasePage +{ +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdVhfChannels; } + void begin() override; + void onEnterPage() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageVhfChannels(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageVhfDistress.cpp b/SmartFuseBox/PageVhfDistress.cpp new file mode 100644 index 0000000..ebbc280 --- /dev/null +++ b/SmartFuseBox/PageVhfDistress.cpp @@ -0,0 +1,153 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageVhfDistress.h" + +#if defined(NEXTION_DISPLAY_DEVICE) +#include "Phonetic.h" +#include "SystemFunctions.h" + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonPrevious = 2; + +constexpr char ControlCallSign[] = "tMDCallSign"; +constexpr char ControlPosition[] = "tMDPosition"; + +// Maximum safe text length for Nextion text components (conservative limit) + +constexpr size_t MaxLineLength = 42; +static const char DistressCallSign[] PROGMEM = "This is %s, Call Sign %s,\r\nMMSI %s."; +static const char DistressPosition[] PROGMEM = "My position is %d degrees, %s minutes %s and %d degrees, %s minutes %s."; + +PageVhfDistress::PageVhfDistress(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer) +{ +} + +void PageVhfDistress::handleExternalUpdate(uint8_t updateType, const void* data) +{ + // Call base class first to handle heartbeat ACKs + BasePage::handleExternalUpdate(updateType, data); + + if (updateType == static_cast(PageUpdateType::GpsLatitude) && data != nullptr) + { + // FloatStateUpdate is used for GPS lat/lon notifications + const FloatStateUpdate* update = static_cast(data); + _lastLatitude = update->value; + updateDisplay(); + } + else if (updateType == static_cast(PageUpdateType::GpsLongitude) && data != nullptr) + { + const FloatStateUpdate* update = static_cast(data); + _lastLongitude = update->value; + updateDisplay(); + } +} + +void PageVhfDistress::onEnterPage() +{ + updateDisplay(); +} + +void PageVhfDistress::begin() +{ + +} + +void PageVhfDistress::refresh(unsigned long now) +{ + (void)now; +} + +void PageVhfDistress::updateDisplay() +{ + Config* config = getConfig(); + + if (!config) + { + return; + } + + const char* name = config->location.name[0] ? config->location.name : "UNKNOWN"; + const char* callSign = config->location.callSign[0] ? config->location.callSign : "N/A"; + const char* mmsi = config->location.mmsi[0] ? config->location.mmsi : "N/A"; + + char buffer[170]; // Large enough for both operations + + char phoneticCallSign[85]; + phoneticize(callSign, phoneticCallSign, sizeof(phoneticCallSign), ", "); + + char tempBuffer[160]; + snprintf_P(tempBuffer, sizeof(tempBuffer), DistressCallSign, name, phoneticCallSign, mmsi); + SystemFunctions::wrapTextAtWordBoundary(tempBuffer, buffer, sizeof(buffer), MaxLineLength); + + sendText(ControlCallSign, buffer); + + formatGpsPosition(_lastLatitude, _lastLongitude, buffer, sizeof(buffer)); + sendText(ControlPosition, buffer); +} + +// Handle touch events for buttons +void PageVhfDistress::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonPrevious: + navigateTo(PageIdVhfRadio); + return; + } +} + +void PageVhfDistress::formatGpsPosition(double latitude, double longitude, char* outBuf, size_t outBufSize) +{ + // Check for invalid GPS data + if (isnan(latitude) || isnan(longitude)) + { + snprintf_P(outBuf, outBufSize, PSTR("GPS position unavailable")); + return; + } + + // Calculate latitude components + double latAbs = latitude < 0.0 ? -latitude : latitude; + int latDeg = static_cast(latAbs); + double latMin = (latAbs - latDeg) * 60.0; + const char* latDir = latitude >= 0.0 ? "North" : "South"; + + // Calculate longitude components + double lonAbs = longitude < 0.0 ? -longitude : longitude; + int lonDeg = static_cast(lonAbs); + double lonMin = (lonAbs - lonDeg) * 60.0; + const char* lonDir = longitude >= 0.0 ? "East" : "West"; + + char latMinStr[8]; + char lonMinStr[8]; + dtostrf(latMin, 6, 3, latMinStr); + dtostrf(lonMin, 6, 3, lonMinStr); + + // Build the final string using only integer and string formatting + snprintf_P(outBuf, outBufSize, + DistressPosition, + latDeg, latMinStr, latDir, lonDeg, lonMinStr, lonDir); +} + + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageVhfDistress.h b/SmartFuseBox/PageVhfDistress.h new file mode 100644 index 0000000..91e57b9 --- /dev/null +++ b/SmartFuseBox/PageVhfDistress.h @@ -0,0 +1,57 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + + + +class PageVhfDistress : public BasePage +{ +private: + double _lastLongitude = NAN; + double _lastLatitude = NAN; + void formatGpsPosition(double latitude, double longitude, char* outBuf, size_t outBufSize); + void updateDisplay(); +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdVhfDistress; } + void begin() override; + void onEnterPage() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleExternalUpdate(uint8_t updateType, const void* data) override; + +public: + explicit PageVhfDistress(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageVhfRadio.cpp b/SmartFuseBox/PageVhfRadio.cpp new file mode 100644 index 0000000..9bce635 --- /dev/null +++ b/SmartFuseBox/PageVhfRadio.cpp @@ -0,0 +1,113 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageVhfRadio.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include "Config.h" +#include "Phonetic.h" +#include "SystemFunctions.h" + + +// Nextion Names/Ids on current Page +constexpr uint8_t ButtonPrevious = 2; +constexpr uint8_t ButtonNext = 3; +constexpr uint8_t ButtonDistress = 8; +constexpr uint8_t ButtonChannels = 9; + +constexpr char ControlBoatName[] = "tVhfBoatName"; +constexpr char ControlHomePort[] = "tVhfHomePort"; +constexpr char ControlMmsi[] = "tVhfMMSI"; +constexpr char ControlCallSign[] = "tVhfCallSign"; + +static const char BoatNameFmt[] PROGMEM = "Boat Name: %s"; +static const char HomePortFmt[] PROGMEM = "Home Port: %s"; +static const char MmsiFmt[] PROGMEM = "MMSI: %s"; +static const char CallSignFmt[] PROGMEM = "Call Sign: %s\r\n%s"; + + +PageVhfRadio::PageVhfRadio(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer) +{ +} + +void PageVhfRadio::onEnterPage() +{ + Config* config = getConfig(); + + if (!config) + return; + + // Called when page becomes active + char buffer[50]; + snprintf_P(buffer, sizeof(buffer), BoatNameFmt, config->location.name); + sendText(ControlBoatName, buffer); + snprintf_P(buffer, sizeof(buffer), HomePortFmt, config->location.homePort); + sendText(ControlHomePort, buffer); + snprintf_P(buffer, sizeof(buffer), MmsiFmt, config->location.mmsi); + sendText(ControlMmsi, buffer); + + char phoneticBuf[128]; + phoneticize(config->location.callSign, phoneticBuf, sizeof(phoneticBuf), ", "); + + char callSignBuf[160]; + snprintf_P(callSignBuf, sizeof(callSignBuf), CallSignFmt, config->location.callSign, phoneticBuf); + + char wrappedBuffer[134]; + SystemFunctions::wrapTextAtWordBoundary(callSignBuf, wrappedBuffer, sizeof(wrappedBuffer), 30); + + sendText(ControlCallSign, callSignBuf); +} + +void PageVhfRadio::begin() +{ + // not used just now +} + +void PageVhfRadio::refresh(unsigned long now) +{ + (void)now; + //nothing to refresh currently +} + +// Handle touch events for buttons +void PageVhfRadio::handleTouch(uint8_t compId, uint8_t eventType) +{ + (void)eventType; + + switch (compId) + { + case ButtonPrevious: + navigatePrevious(); + return; + case ButtonNext: + navigateNext(); + return; + case ButtonDistress: + navigateTo(PageIdVhfDistress); + return; + case ButtonChannels: + navigateTo(PageIdVhfChannels); + return; + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageVhfRadio.h b/SmartFuseBox/PageVhfRadio.h new file mode 100644 index 0000000..1a407a8 --- /dev/null +++ b/SmartFuseBox/PageVhfRadio.h @@ -0,0 +1,51 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include + +#include "BasePage.h" +#include "NextionIds.h" + + + +class PageVhfRadio : public BasePage +{ +protected: + // Required overrides + uint8_t getPageId() const override { return PageIdVhfRadio; } + void begin() override; + void onEnterPage() override; + void refresh(unsigned long now) override; + + //optional overrides + void handleTouch(uint8_t compId, uint8_t eventType) override; + +public: + explicit PageVhfRadio(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer = nullptr); +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageWarning.cpp b/SmartFuseBox/PageWarning.cpp new file mode 100644 index 0000000..2ab78f0 --- /dev/null +++ b/SmartFuseBox/PageWarning.cpp @@ -0,0 +1,205 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "PageWarning.h" + +#if defined(NEXTION_DISPLAY_DEVICE) +#include +#include + +// Nextion Names/Ids on Warning Page +constexpr uint8_t ButtonPrevious = 2; +constexpr uint8_t ButtonNext = 3; +constexpr char WarningListComponentName[] = "t1"; +constexpr char WarningHeader[] = "t2"; + +PageWarning::PageWarning(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer) + : BasePage(serialPort, warningMgr, commandMgrComputer), + _lastActiveWarnings(0), + _lastUpdateTime(0) +{ + // Initialize cached text as empty + _lastWarningText[0] = '\0'; +} + +void PageWarning::begin() +{ + _lastActiveWarnings = 0; + _lastUpdateTime = 0; + _lastWarningText[0] = '\0'; +} + +void PageWarning::onEnterPage() +{ + // Force update when entering the page by clearing cache + _lastActiveWarnings = 0; + _lastUpdateTime = 0; + _lastWarningText[0] = '\0'; + updateWarningDisplay(); +} + +bool PageWarning::buildWarningText(char* buffer, size_t bufferSize) +{ + WarningManager* warningMgr = getWarningManager(); + if (!warningMgr) + { + strncpy(buffer, "No Active Warnings", bufferSize - 1); + buffer[bufferSize - 1] = '\0'; + return false; + } + + size_t bytesWritten = 0; + bool firstWarning = true; + + // Iterate through all 32 possible bit positions in uint32_t + for (uint8_t bit = 0; bit < 32; bit++) + { + WarningType type = static_cast(1UL << bit); + + if (warningMgr->isWarningActive(type)) + { + // Get the warning string once + const char* warningString = getWarningString(bit); + + // Skip empty/undefined warnings + if (warningString[0] == '\0') + { + continue; + } + + // Add separator before subsequent warnings + if (!firstWarning) + { + // Check if we have room for "\r\n" + if (bytesWritten + 2 >= bufferSize - 1) + break; // Buffer full + + buffer[bytesWritten++] = '\r'; + buffer[bytesWritten++] = '\n'; + } + + // Copy warning string + size_t warningLen = strlen(warningString); + size_t remainingSpace = bufferSize - bytesWritten - 1; + + if (warningLen > remainingSpace) + warningLen = remainingSpace; // Truncate if needed + + strncpy(&buffer[bytesWritten], warningString, warningLen); + bytesWritten += warningLen; + + if (bytesWritten >= bufferSize - 1) + break; // Buffer full + + firstWarning = false; + } + } + + // Null-terminate + buffer[bytesWritten] = '\0'; + + // If no warnings are active, display a message + if (bytesWritten == 0) + { + strncpy(buffer, "No Active Warnings", bufferSize - 1); + buffer[bufferSize - 1] = '\0'; + } + + return true; +} + +void PageWarning::updateWarningDisplay() +{ + sendText(WarningHeader, "System Warnings"); + + // Build warning text into a temporary buffer + char newWarningText[MaxWarningTextLength]; + buildWarningText(newWarningText, MaxWarningTextLength); + + // Only send to Nextion if the text has changed + if (strcmp(newWarningText, _lastWarningText) != 0) + { + sendText(WarningListComponentName, newWarningText); + + // Update cache + strncpy(_lastWarningText, newWarningText, MaxWarningTextLength - 1); + _lastWarningText[MaxWarningTextLength - 1] = '\0'; + } +} + +void PageWarning::refresh(unsigned long now) +{ + WarningManager* warningMgr = getWarningManager(); + if (!warningMgr) + return; + + // Get current active warnings bitmap directly from WarningManager + uint32_t currentWarnings = warningMgr->getActiveWarningsMask(); + + // Check if warnings changed OR 10 seconds elapsed + bool warningsChanged = (currentWarnings != _lastActiveWarnings); + bool timeoutElapsed = (now - _lastUpdateTime >= UpdateIntervalMs); + + if (warningsChanged || timeoutElapsed) + { + updateWarningDisplay(); + _lastActiveWarnings = currentWarnings; + _lastUpdateTime = now; + } +} + +// Handle touch events for buttons +void PageWarning::handleTouch(uint8_t compId, uint8_t eventType) +{ + // Only handle release events + if (eventType != EventRelease) + { + return; + } + + // Map component ID to button index + switch (compId) + { + case ButtonPrevious: + navigatePrevious(); + break; + + case ButtonNext: + navigateNext(); + break; + } +} + +void PageWarning::handleExternalUpdate(uint8_t updateType, const void* data) +{ + // Call base class first to handle heartbeat ACKs + BasePage::handleExternalUpdate(updateType, data); + + // If warning state changed, force immediate update + if (updateType == static_cast(PageUpdateType::Warning)) + { + // Reset the cached state to force an update + _lastActiveWarnings = 0; + updateWarningDisplay(); + _lastUpdateTime = millis(); + } +} + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/PageWarning.h b/SmartFuseBox/PageWarning.h new file mode 100644 index 0000000..6b3036a --- /dev/null +++ b/SmartFuseBox/PageWarning.h @@ -0,0 +1,59 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "Local.h" + +#if defined(NEXTION_DISPLAY_DEVICE) + +#include +#include +#include "BasePage.h" + +class PageWarning : public BasePage +{ +private: + uint32_t _lastActiveWarnings; // Cache of last active warnings bitmap + unsigned long _lastUpdateTime; // Timestamp of last display update + static const unsigned long UpdateIntervalMs = 10000; // 10 seconds + + // Cache for warning text to prevent unnecessary Nextion updates + static constexpr size_t MaxWarningTextLength = 256; // Adjust based on your needs + char _lastWarningText[MaxWarningTextLength]; + + // Shared function to update warning display + void updateWarningDisplay(); + + // Helper to build warning text into a buffer + bool buildWarningText(char* buffer, size_t bufferSize); + +public: + PageWarning(Stream* serialPort, + WarningManager* warningMgr, + SerialCommandManager* commandMgrComputer); + + void begin() override; + void onEnterPage() override; + void refresh(unsigned long now) override; + void handleTouch(uint8_t compId, uint8_t eventType) override; + void handleExternalUpdate(uint8_t updateType, const void* data) override; + + uint8_t getPageId() const override { return PageIdWarning; } +}; + +#endif // NEXTION_DISPLAY_DEVICE diff --git a/SmartFuseBox/Phonetic.cpp b/SmartFuseBox/Phonetic.cpp new file mode 100644 index 0000000..f3e74f2 --- /dev/null +++ b/SmartFuseBox/Phonetic.cpp @@ -0,0 +1,125 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "Phonetic.h" + +#include +#include + +// NATO phonetic A-Z then 0-9 stored in PROGMEM using a fixed-width 2D array. +static const char phoneticTable[][10] PROGMEM = { + "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", + "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", + "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "Xray", + "Yankee", "Zulu", + "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" +}; + +static bool copy_phonetic_from_table(uint8_t idx, char* dest, size_t destSize) +{ + if (destSize == 0) + return false; + + strncpy_P(dest, phoneticTable[idx], destSize - 1); + dest[destSize - 1] = '\0'; + + return true; +} + +size_t phoneticize(const char* input, char* outBuf, size_t outBufSize, const char* separator) +{ + if (!input || !outBuf || outBufSize == 0 || !separator) + return 0; + + outBuf[0] = '\0'; + size_t outLen = 0; + bool first = true; + char tmp[16]; // temporary buffer for a single phonetic word + + for (const char* p = input; *p != '\0'; ++p) + { + char c = *p; + + // skip control chars except space, dash and dot which we'll spell out + if ((unsigned char)c < 32) + continue; + + // Map to phonetic + if (c >= 'a' && c <= 'z') + c = (char)toupper((unsigned char)c); + + bool found = false; + + if (c >= 'A' && c <= 'Z') + { + uint8_t idx = (uint8_t)(c - 'A'); + copy_phonetic_from_table(idx, tmp, sizeof(tmp)); + found = true; + } + else if (c >= '0' && c <= '9') + { + uint8_t idx = 26 + (uint8_t)(c - '0'); // digits start at 26 + copy_phonetic_from_table(idx, tmp, sizeof(tmp)); + found = true; + } + else + { + // Some common punctuation + if (c == '-') + { + strncpy_P(tmp, PSTR("Dash"), sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + found = true; + } + else if (c == '.') + { + strncpy_P(tmp, PSTR("Dot"), sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + found = true; + } + else if (c == ' ') + { + strncpy_P(tmp, PSTR("Space"), sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + found = true; + } + } + + if (!found) + continue; // skip characters we don't handle + + size_t wordLen = strlen(tmp); + size_t sepLen = first ? 0 : strlen(separator); + + if (outLen + sepLen + wordLen + 1 > outBufSize) + break; // not enough space, stop safely + + if (!first) + { + memcpy(outBuf + outLen, separator, sepLen); + outLen += sepLen; + } + + memcpy(outBuf + outLen, tmp, wordLen); + outLen += wordLen; + outBuf[outLen] = '\0'; + first = false; + } + + return outLen; +} \ No newline at end of file diff --git a/SmartFuseBox/Phonetic.h b/SmartFuseBox/Phonetic.h new file mode 100644 index 0000000..756be9e --- /dev/null +++ b/SmartFuseBox/Phonetic.h @@ -0,0 +1,25 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include + +// Convert `input` into phonetic words placed into `outBuf` (null-terminated). +// `outBufSize` is the total size of `outBuf`. `separator` defaults to ", ". +// Returns number of bytes written (excluding terminating NUL). +size_t phoneticize(const char* input, char* outBuf, size_t outBufSize, const char* separator = ", "); \ No newline at end of file diff --git a/SmartFuseBox/RelayCommandHandler.cpp b/SmartFuseBox/RelayCommandHandler.cpp index 5ed5fc5..7e491e2 100644 --- a/SmartFuseBox/RelayCommandHandler.cpp +++ b/SmartFuseBox/RelayCommandHandler.cpp @@ -21,8 +21,8 @@ #include "SystemFunctions.h" -RelayCommandHandler::RelayCommandHandler(SerialCommandManager* commandMgrComputer, SerialCommandManager* commandMgrLink, RelayController* relayController) - : _commandMgrComputer(commandMgrComputer), _commandMgrLink(commandMgrLink), _relayController(relayController), +RelayCommandHandler::RelayCommandHandler(SerialCommandManager* commandMgrComputer, RelayController* relayController) + : _commandMgrComputer(commandMgrComputer), _relayController(relayController), _config(nullptr) { } @@ -379,11 +379,6 @@ void RelayCommandHandler::configUpdated(Config* config) void RelayCommandHandler::broadcastRelayStatus(const char* cmd, const StringKeyValue* param) { - if (_commandMgrLink != nullptr) - { - sendAckOk(_commandMgrLink, cmd, param); - } - if (_commandMgrComputer != nullptr) { sendAckOk(_commandMgrComputer, cmd, param); diff --git a/SmartFuseBox/RelayCommandHandler.h b/SmartFuseBox/RelayCommandHandler.h index 25c4e69..8c08942 100644 --- a/SmartFuseBox/RelayCommandHandler.h +++ b/SmartFuseBox/RelayCommandHandler.h @@ -28,7 +28,6 @@ class RelayCommandHandler : public BaseCommandHandler { private: SerialCommandManager* _commandMgrComputer; - SerialCommandManager* _commandMgrLink; RelayController* _relayController; Config* _config; @@ -36,7 +35,7 @@ class RelayCommandHandler : public BaseCommandHandler void sendAllRelayConfig(SerialCommandManager* sender); public: - RelayCommandHandler(SerialCommandManager* commandMgrComputer, SerialCommandManager* commandMgrLink, RelayController* relayController); + RelayCommandHandler(SerialCommandManager* commandMgrComputer, RelayController* relayController); ~RelayCommandHandler(); bool handleCommand(SerialCommandManager* sender, const char* command, const StringKeyValue params[], uint8_t paramCount) override; diff --git a/SmartFuseBox/RgbLedFade.h b/SmartFuseBox/RgbLedFade.h new file mode 100644 index 0000000..a40d53f --- /dev/null +++ b/SmartFuseBox/RgbLedFade.h @@ -0,0 +1,175 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "Config.h" + +class RgbLedFade { +private: + uint8_t _rPin; + uint8_t _gPin; + uint8_t _bPin; + + uint8_t baseR = 0; + uint8_t baseG = 0; + uint8_t baseB = 0; + + float level = 0.0f; + float fadeStep = 0.01f; + int direction = 1; + + unsigned long lastUpdate = 0; + const unsigned long updateInterval = 10; // ms + + uint8_t _maxBrightness = 255; + uint8_t _minBrightness = 0; + + Config* _config = nullptr; + bool _isDayTime = true; + bool _isWarning = false; + + /** + * @brief Apply configuration based on current day/night and warning state + */ + void applyConfigColors() + { + if (!_config) return; + + const LedConfig& led = _config->led; + + // Select color based on day/night and warning state + const uint8_t* color; + uint8_t brightness; + + if (_isDayTime) + { + color = _isWarning ? led.dayBadColor : led.dayGoodColor; + brightness = led.dayBrightness; + } + else + { + color = _isWarning ? led.nightBadColor : led.nightGoodColor; + brightness = led.nightBrightness; + } + + // Apply color + setColor(color[0], color[1], color[2]); + + // Apply brightness (convert 0-100 to 0-255) + setMaxBrightness((brightness * 255) / 100); + } + +public: + RgbLedFade(uint8_t rPin, uint8_t gPin, uint8_t bPin) + : _rPin(rPin), _gPin(gPin), _bPin(bPin) + { + } + + void begin() + { + pinMode(_rPin, OUTPUT); + pinMode(_gPin, OUTPUT); + pinMode(_bPin, OUTPUT); + + // Initialize to off + analogWrite(_rPin, 0); + analogWrite(_gPin, 0); + analogWrite(_bPin, 0); + } + + void setColor(uint8_t r, uint8_t g, uint8_t b) + { + baseR = r; + baseG = g; + baseB = b; + } + + void setFadeSpeed(float step) + { + fadeStep = step; + } + + void setMaxBrightness(uint8_t brightness) + { + _maxBrightness = brightness; + } + + void setMinBrightness(uint8_t brightness) + { + _minBrightness = brightness; + } + + void setDayTime(bool isDaytime) + { + if (_isDayTime != isDaytime) + { + _isDayTime = isDaytime; + applyConfigColors(); + } + } + + void setWarning(bool isWarning) + { + if (_isWarning != isWarning) + { + _isWarning = isWarning; + applyConfigColors(); + } + } + + void configSet(Config* config) + { + _config = config; + applyConfigColors(); + } + + void refreshFromConfig() + { + applyConfigColors(); + } + + void update(unsigned long now) + { + if (now - lastUpdate < updateInterval) return; + lastUpdate = now; + + level += direction * fadeStep; + if (level >= 1.0f) + { + level = 1.0f; + direction = -1; + } + else if (level <= 0.0f) + { + level = 0.0f; + direction = 1; + } + + float gamma = pow(level, 2.2f); + float brightnessFactor = gamma * (_maxBrightness / 255.0f); + + if (brightnessFactor < (_minBrightness / 255.0f)) + { + brightnessFactor = _minBrightness / 255.0f; + } + + analogWrite(_rPin, (uint8_t)(baseR * brightnessFactor)); + analogWrite(_gPin, (uint8_t)(baseG * brightnessFactor)); + analogWrite(_bPin, (uint8_t)(baseB * brightnessFactor)); + } +}; diff --git a/SmartFuseBox/RtcDS1302Driver.cpp b/SmartFuseBox/RtcDS1302Driver.cpp new file mode 100644 index 0000000..e4047bc --- /dev/null +++ b/SmartFuseBox/RtcDS1302Driver.cpp @@ -0,0 +1,244 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "RtcDS1302Driver.h" +#include "Local.h" +#include "SystemDefinitions.h" +#include "ConfigManager.h" + +#include + +static Ds1302* rtc = nullptr; + +RtcDS1302Driver::RtcDS1302Driver() + : _available(false) +{ +} + +RtcDS1302Driver::~RtcDS1302Driver() +{ + delete rtc; + rtc = nullptr; +} + +bool RtcDS1302Driver::begin() +{ + // Attempt to initialise using runtime configuration if available. + Config* cfg = ConfigManager::getConfigPtr(); + if (cfg == nullptr) + { + _available = false; + return false; + } + + // If pins are not configured, report unavailable rather than silently disabling. + if (cfg->rtc.dataPin == PinDisabled || cfg->rtc.clockPin == PinDisabled || cfg->rtc.resetPin == PinDisabled) + { + _available = false; + return false; + } + + // Delegate to the pin-overload which performs full initialisation. + return begin(cfg->rtc.dataPin, cfg->rtc.clockPin, cfg->rtc.resetPin); +} + +bool RtcDS1302Driver::begin(uint8_t dataPin, uint8_t clockPin, uint8_t resetPin) +{ + if (dataPin == PinDisabled || clockPin == PinDisabled || resetPin == PinDisabled) + { + _available = false; + return false; + } + delete rtc; + rtc = new Ds1302(resetPin, clockPin, dataPin); + rtc->init(); + + // Ensure oscillator is running + if (rtc->isHalted()) + { + rtc->start(); + } + + // Attempt to read current time. The DS1302 stores a 2-digit year (0-99). + Ds1302::DateTime dt; + rtc->getDateTime(&dt); + uint16_t fullYear = 2000 + static_cast(dt.year); + + // If year is clearly invalid, set a sensible default (matches previous behaviour) + if (fullYear < 2025) + { + Ds1302::DateTime defaultDt; + defaultDt.year = static_cast(2025 - 2000); + defaultDt.month = 1; + defaultDt.day = 1; + defaultDt.hour = 0; + defaultDt.minute = 0; + defaultDt.second = 0; + defaultDt.dow = 0; + rtc->setDateTime(&defaultDt); + fullYear = 2025; + } + + (void)fullYear; // keep variable for readability; availability determined by successful read + _available = true; + return _available; +} + +bool RtcDS1302Driver::isAvailable() const +{ + return _available; +} + +unsigned long RtcDS1302Driver::readTimestamp() +{ + if (!_available) + return 0; + + if (!rtc) + return 0; + + Ds1302::DateTime rtcTime; + rtc->getDateTime(&rtcTime); + + // DS1302 returns 2-digit year (0..99) — convert to full year in 2000s + uint16_t year = 2000 + static_cast(rtcTime.year); + uint8_t month = rtcTime.month; + uint8_t day = rtcTime.day; + uint8_t hour = rtcTime.hour; + uint8_t minute = rtcTime.minute; + uint8_t second = rtcTime.second; + + const uint8_t daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + unsigned long days = 0; + + for (uint16_t y = 1970; y < year; y++) + { + days += 365; + if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) + days++; + } + + for (uint8_t m = 1; m < month; m++) + { + days += daysInMonth[m - 1]; + if (m == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) + days++; + } + + days += day - 1; + + unsigned long timestamp = days * 86400UL; + timestamp += hour * 3600UL; + timestamp += minute * 60UL; + timestamp += second; + + return timestamp; +} + +bool RtcDS1302Driver::writeTimestamp(unsigned long unixTimestamp) +{ + if (!_available) + return false; + + if (!rtc) + return false; + + const uint8_t daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + uint8_t second = unixTimestamp % 60; + unixTimestamp /= 60; + uint8_t minute = unixTimestamp % 60; + unixTimestamp /= 60; + uint8_t hour = unixTimestamp % 24; + unsigned long days = unixTimestamp / 24; + + uint16_t year = 1970; + while (true) + { + uint16_t daysInYear = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 366 : 365; + if (days < daysInYear) + break; + days -= daysInYear; + year++; + } + + bool isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); + uint8_t month = 1; + + while (month <= 12) + { + uint8_t monthDays = daysInMonth[month - 1]; + if (month == 2 && isLeapYear) + monthDays = 29; + if (days < monthDays) + break; + days -= monthDays; + month++; + } + + uint8_t day = static_cast(days) + 1; + + Ds1302::DateTime rtcTime; + rtcTime.year = static_cast(year - 2000); + rtcTime.month = month; + rtcTime.day = day; + rtcTime.hour = hour; + rtcTime.minute = minute; + rtcTime.second = second; + rtcTime.dow = 0; + rtc->setDateTime(&rtcTime); + return true; +} + +bool RtcDS1302Driver::runDiagnostics(char* buffer, const uint8_t bufferLength) +{ + if (!_available) + { + snprintf_P(buffer, bufferLength, PSTR("RTC not available")); + return false; + } + + if (!rtc) + { + snprintf_P(buffer, bufferLength, PSTR("RTC not available")); + return false; + } + + Ds1302::DateTime currentTime; + rtc->getDateTime(¤tTime); + + // Convert 2-digit year to full year + uint16_t year = 2000 + static_cast(currentTime.year); + + if (rtc->isHalted()) + { + snprintf_P(buffer, bufferLength, PSTR("RTC not running")); + return false; + } + + if (year < 2025 || year > 2099) + { + snprintf_P(buffer, bufferLength, PSTR("RTC time out of range: %d"), year); + return false; + } + + snprintf_P(buffer, bufferLength, PSTR("RTC OK: %04d-%02d-%02d %02d:%02d:%02d"), + year, currentTime.month, currentTime.day, + currentTime.hour, currentTime.minute, currentTime.second); + return true; +} diff --git a/SmartFuseBox/RtcDS1302Driver.h b/SmartFuseBox/RtcDS1302Driver.h new file mode 100644 index 0000000..50dec00 --- /dev/null +++ b/SmartFuseBox/RtcDS1302Driver.h @@ -0,0 +1,103 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include "Local.h" + +/** + * @class RtcDS1302Driver + * @brief Hardware abstraction layer for DS1302 Real-Time Clock. + * + * Provides a clean interface for DS1302 RTC operations including + * initialization, read/write, and diagnostics. Handles hardware-specific + * details and provides Unix timestamp interface for easy integration. + * + * Features: + * - DS1302 hardware initialization and configuration + * - Read/write Unix timestamps + * - Hardware diagnostics + * - Write protection management + * - Validity checking + * + * Usage: + * @code + * RtcDS1302Driver rtc; + * + * rtc.begin(); + * + * if (rtc.isAvailable()) + * { + * unsigned long timestamp = rtc.readTimestamp(); + * rtc.writeTimestamp(1735689600); + * } + * @endcode + */ +class RtcDS1302Driver +{ +public: + RtcDS1302Driver(); + ~RtcDS1302Driver(); + + /** + * @brief Initialize DS1302 hardware. + * Sets up communication, disables write protection, starts clock. + * @return true if initialization successful, false otherwise + */ + bool begin(); + + /** + * @brief Initialize DS1302 hardware using runtime pin values from config. + * @param dataPin DS1302 DAT pin + * @param clockPin DS1302 CLK pin + * @param resetPin DS1302 RST/CE pin + * @return true if initialization successful, false otherwise + */ + bool begin(uint8_t dataPin, uint8_t clockPin, uint8_t resetPin); + + /** + * @brief Check if RTC hardware is available and responding. + * @return true if hardware is present and has valid time + */ + bool isAvailable() const; + + /** + * @brief Read current time from RTC as Unix timestamp. + * @return Unix timestamp (seconds since epoch), or 0 if invalid/unavailable + */ + unsigned long readTimestamp(); + + /** + * @brief Write Unix timestamp to RTC. + * @param unixTimestamp Seconds since Unix epoch + * @return true if write successful, false otherwise + */ + bool writeTimestamp(unsigned long unixTimestamp); + + /** + * @brief Perform comprehensive RTC diagnostics. + * Tests hardware availability, validity, running state, write protection, and time range. + * @param buffer Buffer to store diagnostic message + * @param bufferLength Size of the buffer + * @return true if all diagnostics pass, false if any check fails + */ + bool runDiagnostics(char* buffer, const uint8_t bufferLength); + +private: + bool _available; +}; diff --git a/SmartFuseBox/SDCardConfigLoader.cpp b/SmartFuseBox/SDCardConfigLoader.cpp index f7cc9cb..65360a1 100644 --- a/SmartFuseBox/SDCardConfigLoader.cpp +++ b/SmartFuseBox/SDCardConfigLoader.cpp @@ -25,11 +25,9 @@ #include SdCardConfigLoader::SdCardConfigLoader(SerialCommandManager* computerSerial, - SerialCommandManager* linkSerial, ConfigController* configController, RelayController* relayController) : _computerSerial(computerSerial), - _linkSerial(linkSerial), _configController(configController), _relayController(relayController), _sdConfigPresent(false) @@ -423,16 +421,6 @@ bool SdCardConfigLoader::applyConfigCommand(const char* line) return true; } -void SdCardConfigLoader::syncConfigToLink() -{ - // Send C1 (get settings) to trigger config broadcast via LINK - // This will make the control panel receive the new config - if (_linkSerial) - { - _linkSerial->sendCommand("C1", ""); - } -} - void SdCardConfigLoader::logError(const char* message, const char* line) { if (_computerSerial) @@ -528,10 +516,6 @@ bool SdCardConfigLoader::loadConfigFromSd() { logInfo("Config saved to EEPROM"); - // Sync to control panel via LINK - logInfo("Syncing config to control panel..."); - syncConfigToLink(); - _sdConfigPresent = true; char summary[64]; diff --git a/SmartFuseBox/SDCardConfigLoader.h b/SmartFuseBox/SDCardConfigLoader.h index 67f2e3c..7c4a5f8 100644 --- a/SmartFuseBox/SDCardConfigLoader.h +++ b/SmartFuseBox/SDCardConfigLoader.h @@ -50,7 +50,7 @@ constexpr uint16_t SD_CONFIG_MAX_LINE_LENGTH = 128; * * Usage: * @code - * SdCardConfigLoader loader(&commandMgrComputer, &commandMgrLink, &configController); + * SdCardConfigLoader loader(&commandMgrComputer, &configController); * * void setup() * { @@ -66,7 +66,6 @@ class SdCardConfigLoader { private: SerialCommandManager* _computerSerial; - SerialCommandManager* _linkSerial; ConfigController* _configController; RelayController* _relayController; bool _sdConfigPresent; @@ -90,11 +89,6 @@ class SdCardConfigLoader */ bool applyConfigCommand(const char* line); - /** - * @brief Send config to LINK serial to sync control panel - */ - void syncConfigToLink(); - /** * @brief Log error message to serial * @param message Error message @@ -116,7 +110,6 @@ class SdCardConfigLoader * @param configController Configuration controller */ SdCardConfigLoader(SerialCommandManager* computerSerial, - SerialCommandManager* linkSerial, ConfigController* configController, RelayController* relayController); diff --git a/SmartFuseBox/ScreenDemoHandler.h b/SmartFuseBox/ScreenDemoHandler.h new file mode 100644 index 0000000..66dd1dd --- /dev/null +++ b/SmartFuseBox/ScreenDemoHandler.h @@ -0,0 +1,90 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include +#include +#include "NextionIds.h" +#include "Local.h" + +#if defined(SCREEN_DEMO_SUPPORT) + +constexpr char ScreenDemoCommand[] = "SCRNDMO"; +constexpr uint16_t DefaultDemoIntervalMs = 6000; + +class ScreenDemoHandler : public BaseCommandHandler +{ +private: + NextionControl* _nextionControl; + uint8_t _totalPages; + +public: + ScreenDemoHandler(NextionControl* nextionControl, uint8_t pageCount) + : _nextionControl(nextionControl), + _totalPages(pageCount) + { + } + + bool supportsCommand(const char* command) const override + { + return strcmp(command, ScreenDemoCommand) == 0; + } + + bool handleCommand(SerialCommandManager* sender, const char* command, const StringKeyValue params[], uint8_t paramCount) override + { + (void)command; + + uint16_t intervalMs = DefaultDemoIntervalMs; + for (uint8_t i = 0; i < paramCount; i++) + { + if (strcmp(params[i].key, "s") == 0) + { + unsigned long secs = strtoul(params[i].value, nullptr, 10); + if (secs > 0 && secs <= 60) + { + intervalMs = static_cast(secs * 1000UL); + } + } + } + + for (uint8_t pageId = PageHome; pageId < _totalPages; pageId++) + { + char pageCmd[12]; + snprintf_P(pageCmd, sizeof(pageCmd), PSTR("page %u"), pageId); + _nextionControl->sendCommand(String(pageCmd)); + delay(intervalMs); + } + + char pageCmd[12]; + snprintf_P(pageCmd, sizeof(pageCmd), PSTR("page %u"), PageHome); + _nextionControl->sendCommand(String(pageCmd)); + + sendAckOk(sender, ScreenDemoCommand); + return true; + } + + const char* const* supportedCommands(size_t& count) const override + { + count = 0; + return nullptr; + } +}; + +#endif \ No newline at end of file diff --git a/SmartFuseBox/SensorCommandHandler.cpp b/SmartFuseBox/SensorCommandHandler.cpp index 0f5aaf9..4f33e6c 100644 --- a/SmartFuseBox/SensorCommandHandler.cpp +++ b/SmartFuseBox/SensorCommandHandler.cpp @@ -19,19 +19,20 @@ #include "RemoteSensor.h" #include "SystemFunctions.h" -#if defined(BOAT_CONTROL_PANEL) -SensorCommandHandler::SensorCommandHandler(BroadcastManager* broadcastManager, - NextionControl* nextionControl, WarningManager* warningManager) - : BaseBoatCommandHandler(broadcastManager, nextionControl, warningManager) -#if defined(FUSE_BOX_CONTROLLER) - , _remoteSensors(nullptr), - _remoteSensorCount(0) +#if defined(NEXTION_DISPLAY_DEVICE) +#include "BasePage.h" #endif -#elif defined(FUSE_BOX_CONTROLLER) -SensorCommandHandler::SensorCommandHandler(BroadcastManager* broadcastManager, WarningManager* warningManager) - : SharedBaseCommandHandler(broadcastManager, warningManager), _remoteSensors(nullptr), _remoteSensorCount(0) +SensorCommandHandler::SensorCommandHandler(BroadcastManager* broadcastManager, +#if defined(NEXTION_DISPLAY_DEVICE) + NextionControl* nextionControl, +#endif + WarningManager* warningManager) + : BaseNextionCommandHandler(broadcastManager, +#if defined(NEXTION_DISPLAY_DEVICE) + nextionControl, #endif + warningManager), _remoteSensors(nullptr), _remoteSensorCount(0) { } @@ -120,8 +121,8 @@ bool SensorCommandHandler::getHornActive() const void SensorCommandHandler::setTemperature(float value) { _lastTemperature = value; -#if defined(BOAT_CONTROL_PANEL) - FloatStateUpdate update = { _lastTemperature }; +#if defined(NEXTION_DISPLAY_DEVICE) + FloatStateUpdate update = { _lastTemperature }; notifyCurrentPage(static_cast(PageUpdateType::Temperature), &update); #endif } @@ -129,8 +130,8 @@ void SensorCommandHandler::setTemperature(float value) void SensorCommandHandler::setHumidity(uint8_t value) { _lastHumidity = value; -#if defined(BOAT_CONTROL_PANEL) - UInt16Update update = { _lastHumidity }; +#if defined(NEXTION_DISPLAY_DEVICE) + UInt16Update update = { _lastHumidity }; notifyCurrentPage(static_cast(PageUpdateType::Humidity), &update); #endif } @@ -138,8 +139,8 @@ void SensorCommandHandler::setHumidity(uint8_t value) void SensorCommandHandler::setBearing(float value) { _lastBearing = value; -#if defined(BOAT_CONTROL_PANEL) - FloatStateUpdate update = { _lastBearing }; +#if defined(NEXTION_DISPLAY_DEVICE) + FloatStateUpdate update = { _lastBearing }; notifyCurrentPage(static_cast(PageUpdateType::Bearing), &update); #endif } @@ -147,8 +148,8 @@ void SensorCommandHandler::setBearing(float value) void SensorCommandHandler::setCompassTemperature(float value) { _lastCompassTemp = value; -#if defined(BOAT_CONTROL_PANEL) - FloatStateUpdate update = { _lastCompassTemp }; +#if defined(NEXTION_DISPLAY_DEVICE) + FloatStateUpdate update = { _lastCompassTemp }; notifyCurrentPage(static_cast(PageUpdateType::CompassTemp), &update); #endif } @@ -156,8 +157,8 @@ void SensorCommandHandler::setCompassTemperature(float value) void SensorCommandHandler::setSpeed(uint8_t value) { _lastSpeed = value; -#if defined(BOAT_CONTROL_PANEL) - UInt16Update update = { _lastSpeed }; +#if defined(NEXTION_DISPLAY_DEVICE) + UInt16Update update = { _lastSpeed }; notifyCurrentPage(static_cast(PageUpdateType::Speed), &update); #endif } @@ -165,8 +166,8 @@ void SensorCommandHandler::setSpeed(uint8_t value) void SensorCommandHandler::setWaterLevel(uint16_t value) { _lastWaterLevel = value; -#if defined(BOAT_CONTROL_PANEL) - UInt16Update update = { _lastWaterLevel }; +#if defined(NEXTION_DISPLAY_DEVICE) + UInt16Update update = { _lastWaterLevel }; notifyCurrentPage(static_cast(PageUpdateType::WaterLevel), &update); #endif } @@ -174,8 +175,8 @@ void SensorCommandHandler::setWaterLevel(uint16_t value) void SensorCommandHandler::setWaterPumpActive(bool value) { _lastWaterPumpActive = value; -#if defined(BOAT_CONTROL_PANEL) - BoolStateUpdate update = { _lastWaterPumpActive }; +#if defined(NEXTION_DISPLAY_DEVICE) + BoolStateUpdate update = { _lastWaterPumpActive }; notifyCurrentPage(static_cast(PageUpdateType::WaterPumpActive), &update); #endif } @@ -183,8 +184,8 @@ void SensorCommandHandler::setWaterPumpActive(bool value) void SensorCommandHandler::setDaytime(bool isDaytime) { _isDaytime = isDaytime; -#if defined(BOAT_CONTROL_PANEL) - BoolStateUpdate update = { _isDaytime }; +#if defined(NEXTION_DISPLAY_DEVICE) + BoolStateUpdate update = { _isDaytime }; notifyCurrentPage(static_cast(PageUpdateType::Daytime), &update); #endif } @@ -193,8 +194,8 @@ void SensorCommandHandler::setGpsLocation(double lat, double lon) { _gpsLatitude = lat; _gpsLongitude = lon; -#if defined(BOAT_CONTROL_PANEL) - FloatStateUpdate updateLat = { static_cast(_gpsLatitude) }; +#if defined(NEXTION_DISPLAY_DEVICE) + FloatStateUpdate updateLat = { static_cast(_gpsLatitude) }; notifyCurrentPage(static_cast(PageUpdateType::GpsLatitude), &updateLat); FloatStateUpdate updateLon = { static_cast(_gpsLongitude) }; @@ -205,8 +206,8 @@ void SensorCommandHandler::setGpsLocation(double lat, double lon) void SensorCommandHandler::setGpsAltitude(double alt) { _altitude = alt; -#if defined(BOAT_CONTROL_PANEL) - FloatStateUpdate update = { static_cast(_altitude) }; +#if defined(NEXTION_DISPLAY_DEVICE) + FloatStateUpdate update = { static_cast(_altitude) }; notifyCurrentPage(static_cast(PageUpdateType::GpsAltitude), &update); #endif } @@ -214,8 +215,8 @@ void SensorCommandHandler::setGpsAltitude(double alt) void SensorCommandHandler::setGpsCourse(double course) { _gpsCourse = course; -#if defined(BOAT_CONTROL_PANEL) - FloatStateUpdate update = { static_cast(_gpsCourse) }; +#if defined(NEXTION_DISPLAY_DEVICE) + FloatStateUpdate update = { static_cast(_gpsCourse) }; notifyCurrentPage(static_cast(PageUpdateType::Bearing), &update); #endif } @@ -223,8 +224,8 @@ void SensorCommandHandler::setGpsCourse(double course) void SensorCommandHandler::setGpsSatellites(uint32_t sats) { _gpsSatellites = sats; -#if defined(BOAT_CONTROL_PANEL) - UInt16Update update = { static_cast(_gpsSatellites) }; +#if defined(NEXTION_DISPLAY_DEVICE) + UInt16Update update = { static_cast(_gpsSatellites) }; notifyCurrentPage(static_cast(PageUpdateType::GpsSatellites), &update); #endif } @@ -232,8 +233,8 @@ void SensorCommandHandler::setGpsSatellites(uint32_t sats) void SensorCommandHandler::setGpsDirection(const char* dir) { _gpsDirection = dir; -#if defined(BOAT_CONTROL_PANEL) - CharStateUpdate update = {}; +#if defined(NEXTION_DISPLAY_DEVICE) + CharStateUpdate update = {}; update.length = min(SystemFunctions::calculateLength(dir), static_cast((CharStateUpdate::MaxLength - 1))); snprintf_P(update.value, CharStateUpdate::MaxLength, PSTR("%s"), dir); notifyCurrentPage(static_cast(PageUpdateType::Direction), &update); @@ -243,7 +244,7 @@ void SensorCommandHandler::setGpsDirection(const char* dir) void SensorCommandHandler::setGpsDistance(double distance) { _gpsDistance = distance; -#if defined(BOAT_CONTROL_PANEL) +#if defined(NEXTION_DISPLAY_DEVICE) DoubleStateUpdate update = {}; update.value = getGpsDistance(); notifyCurrentPage(static_cast(PageUpdateType::GpsDistance), &update); @@ -253,23 +254,20 @@ void SensorCommandHandler::setGpsDistance(double distance) void SensorCommandHandler::setHornActive(bool value) { _lastHornActive = value; -#if defined(BOAT_CONTROL_PANEL) +#if defined(NEXTION_DISPLAY_DEVICE) BoolStateUpdate update = { _lastHornActive }; notifyCurrentPage(static_cast(PageUpdateType::HornActive), &update); #endif } -#if defined(FUSE_BOX_CONTROLLER) void SensorCommandHandler::setup(RemoteSensor* remoteSensors[], size_t remoteSensorCount) { _remoteSensors = remoteSensors; _remoteSensorCount = remoteSensorCount; } -#endif bool SensorCommandHandler::handleCommand(SerialCommandManager* sender, const char* command, const StringKeyValue params[], uint8_t paramCount) { -#if defined(FUSE_BOX_CONTROLLER) if (_remoteSensors != nullptr && _remoteSensorCount > 0) { for (size_t i = 0; i < _remoteSensorCount; i++) @@ -280,8 +278,6 @@ bool SensorCommandHandler::handleCommand(SerialCommandManager* sender, const cha } } } -#endif - // Handle query requests (no parameters) - return current sensor values if (paramCount == 0) diff --git a/SmartFuseBox/SensorCommandHandler.h b/SmartFuseBox/SensorCommandHandler.h index 7ccc8b7..c1a9363 100644 --- a/SmartFuseBox/SensorCommandHandler.h +++ b/SmartFuseBox/SensorCommandHandler.h @@ -21,11 +21,15 @@ #include "SerialCommandManager.h" #include "Local.h" -#include "SharedBaseCommandHandler.h" +#if defined(NEXTION_DISPLAY_DEVICE) +#include +#endif + +#include "BaseNextionCommandHandler.h" class RemoteSensor; -class SensorCommandHandler : public SharedBaseCommandHandler +class SensorCommandHandler : public BaseNextionCommandHandler { private: float _lastTemperature = NAN; @@ -48,7 +52,11 @@ class SensorCommandHandler : public SharedBaseCommandHandler size_t _remoteSensorCount; public: - explicit SensorCommandHandler(BroadcastManager* broadcastManager, WarningManager* warningManager); + explicit SensorCommandHandler(BroadcastManager* broadcastManager, +#if defined(NEXTION_DISPLAY_DEVICE) + NextionControl* nextionControl, +#endif + WarningManager* warningManager); bool handleCommand(SerialCommandManager* sender, const char* command, const StringKeyValue params[], uint8_t paramCount) override; const char* const* supportedCommands(size_t& count) const override; diff --git a/SmartFuseBox/SensorConfigCommandHandler.h b/SmartFuseBox/SensorConfigCommandHandler.h index 8ce4e08..4335e6e 100644 --- a/SmartFuseBox/SensorConfigCommandHandler.h +++ b/SmartFuseBox/SensorConfigCommandHandler.h @@ -50,7 +50,6 @@ class SensorConfigCommandHandler : public virtual BaseCommandHandler, public Bas { private: SerialCommandManager* _commandMgrComputer; - SerialCommandManager* _commandMgrLink; void broadcastSensorConfig(SerialCommandManager* sender, uint8_t index, const SensorEntry& entry) { @@ -68,16 +67,11 @@ class SensorConfigCommandHandler : public virtual BaseCommandHandler, public Bas if (_commandMgrComputer) _commandMgrComputer->sendCommand(SensorConfigGetAll, buf); - - if (_commandMgrLink && _commandMgrLink != _commandMgrComputer) - _commandMgrLink->sendCommand(SensorConfigGetAll, buf); } public: - SensorConfigCommandHandler(SerialCommandManager* commandMgrComputer, - SerialCommandManager* commandMgrLink) - : _commandMgrComputer(commandMgrComputer), - _commandMgrLink(commandMgrLink) + explicit SensorConfigCommandHandler(SerialCommandManager* commandMgrComputer) + : _commandMgrComputer(commandMgrComputer) { } diff --git a/SmartFuseBox/SensorFactory.h b/SmartFuseBox/SensorFactory.h index c4b8ffc..f960963 100644 --- a/SmartFuseBox/SensorFactory.h +++ b/SmartFuseBox/SensorFactory.h @@ -33,6 +33,7 @@ #include "WaterSensorHandler.h" #include "Dht11SensorHandler.h" +#include "GpsSensorHandler.h" #include "LightSensorHandler.h" #include "SystemSensorHandler.h" #include "BinaryPresenceSensor.h" @@ -52,6 +53,7 @@ struct SensorFactoryContext RelayController* relayController; IWifiController* wifiController; IBluetoothRadio* bluetoothRadio; + Stream* gpsSerial; #if defined(SD_CARD_SUPPORT) SdCardLogger* sdCardLogger; @@ -234,6 +236,22 @@ class SensorFactory pulseDurationSec); } + case SensorIdList::GpsSensor: + { + if (ctx.gpsSerial == nullptr) + return nullptr; + + return new GpsSensorHandler( + ctx.gpsSerial, +#if defined(MESSAGE_BUS) + ctx.messageBus, +#endif + ctx.broadcastManager, + ctx.sensorCommandHandler, + ctx.warningManager, + entry.name); + } + default: return nullptr; } diff --git a/SmartFuseBox/SmartFuseBox.ino b/SmartFuseBox/SmartFuseBox.ino index 11825ab..038ed0a 100644 --- a/SmartFuseBox/SmartFuseBox.ino +++ b/SmartFuseBox/SmartFuseBox.ino @@ -26,69 +26,42 @@ #include "Dht11SensorHandler.h" #include "LightSensorHandler.h" #include "SystemSensorHandler.h" +#include "GpsSensorHandler.h" -#include "RemoteSensor.h" - - // BaseSensor.h is required here because the localSensors[] array below is typed - // as BaseSensorHandler*. When MQTT_SUPPORT is active, RemoteSensor inherits from - // a different base that is not pulled in by the sensor-specific headers above. -#if defined(MQTT_SUPPORT) -#include "BaseSensor.h" -#endif - -// Hardware UART selection. Change these two defines if your board routes its -// serial ports differently (e.g. swap Serial1 for Serial2 on a Mega-class board). + // Hardware UART selection. Change this define if your board routes its serial +// port differently. // COMPUTER_SERIAL – connected to the host PC / debug monitor. -// LINK_SERIAL – connected to a secondary controller or companion board. +// GPS_SERIAL – connected to the GPS module. #define COMPUTER_SERIAL Serial -#define LINK_SERIAL Serial1 +#define GPS_SERIAL Serial2 // forward declares void onComputerCommandReceived(SerialCommandManager* mgr); -void onLinkCommandReceived(SerialCommandManager* mgr); // Consumer note: -// - `commandMgrComputer` and `commandMgrLink` are local to your .ino so you can -// select the hardware Serial instances (Serial, Serial1, etc.) and baud rates -// appropriate for your board. Keep the callbacks `onComputerCommandReceived` and -// `onLinkCommandReceived` in this file so they can access these serial managers. -// - Construct `SmartFuseBoxApp` with pointers to these serial managers and your -// relay pin mapping. Then construct any board-specific sensors using the -// accessors from `app` (e.g. `app.messageBus()`, `app.broadcastManager()`, -// `app.sensorCommandHandler()`). Finally call `app.setup(...)` with your -// sensor arrays from `setup()` and call `app.loop()` from `loop()`. +// - `commandMgrComputer` is local to your .ino so you can select the hardware +// Serial instance and baud rate appropriate for your board. Keep the callback +// `onComputerCommandReceived` in this file so it can access the serial manager. +// - Construct `SmartFuseBoxApp` with a pointer to the serial manager. Then +// configure board-specific resources (e.g. GPS serial) via app accessors before +// calling `app.setup()`. Call `app.loop()` from `loop()`. SerialCommandManager commandMgrComputer(&COMPUTER_SERIAL, onComputerCommandReceived, '\n', ':', ';', '=', 500, 64); -SerialCommandManager commandMgrLink(&LINK_SERIAL, onLinkCommandReceived, '\n', ':', ';', '=', 500, 64); - -SmartFuseBoxApp app(&commandMgrComputer, &commandMgrLink); - -// Project specific remote sensors -#if defined(MQTT_SUPPORT) -MqttSensorChannel gpsMqttChannels[] = { - { "Latitude", "latitude", "latitude", nullptr, "°", false}, - { "Longitude", "longitude", "longitude", nullptr, "°", false} -}; -RemoteSensor gpsLatLonSensor(SensorIdList::GpsSensor, "Gps", SensorGpsLatLong, "Gps", gpsMqttChannels, 2); -#else -RemoteSensor gpsLatLonSensor(SensorIdList::GpsSensor, "Gps", SensorGpsLatLong, 2); -#endif -RemoteSensor* remoteSensors[] = { - &gpsLatLonSensor -}; -constexpr uint8_t remoteSensorCount = sizeof(remoteSensors) / sizeof(remoteSensors[0]); +SmartFuseBoxApp app(&commandMgrComputer); void setup() { // Serial initialization is performed first to ensure that any logging or error messages // from DateTimeManager or ConfigManager during initialization are properly output. SystemFunctions::initializeSerial(COMPUTER_SERIAL, 115200, true); - SystemFunctions::initializeSerial(LINK_SERIAL, 19200, true); + SystemFunctions::initializeSerial(GPS_SERIAL, GpsBaudRate, false); + + app.setGpsSerial(&GPS_SERIAL); // configure app - app.setup(remoteSensors, remoteSensorCount); + app.setup(nullptr, 0); } void loop() @@ -105,12 +78,3 @@ void onComputerCommandReceived(SerialCommandManager* mgr) SystemFunctions::resetSerial(COMPUTER_SERIAL); } -// Called when a command arrives on LINK_SERIAL that no registered handler -// claimed. Forwards the error notification to the computer-side monitor -// (not the link serial) so the host can log it, then resets the link buffer. -void onLinkCommandReceived(SerialCommandManager* mgr) -{ - commandMgrComputer.sendError(mgr->getRawMessage(), F("STATLNK")); - SystemFunctions::resetSerial(LINK_SERIAL); -} - diff --git a/SmartFuseBox/SmartFuseBox.vcxproj b/SmartFuseBox/SmartFuseBox.vcxproj index c026d8a..a16f58b 100644 --- a/SmartFuseBox/SmartFuseBox.vcxproj +++ b/SmartFuseBox/SmartFuseBox.vcxproj @@ -74,9 +74,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -92,6 +119,7 @@ + @@ -133,14 +161,21 @@ CppCode true + + + + + + + @@ -150,13 +185,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -169,6 +234,7 @@ + @@ -235,9 +301,9 @@ VisualMicroDebugger - $(ProjectDir)..\..\..\Users\Simon\Documents\Arduino\libraries\SerialCommandManager\src;$(ProjectDir)..\..\..\Users\Simon\Documents\Arduino\libraries\SensorManager\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\WiFi\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Network\src;$(ProjectDir)..\..\..\Users\Simon\Documents\Arduino\libraries\dht11;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\EEPROM\src;$(ProjectDir)..\..\..\program files\microsoft visual studio\18\insiders\common7\ide\extensions\0qz5sbjj.e2i\Micro Platforms\default;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\dio_qspi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\cores\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\variants\nodemcu-32s;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\newlib\platform_include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\config\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\config\include\freertos;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\config\xtensa\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\FreeRTOS-Kernel\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\FreeRTOS-Kernel\portable\xtensa\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\FreeRTOS-Kernel\portable\xtensa\include\freertos;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\esp_additions\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\include\soc;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\include\soc\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\dma\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\ldo\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\debug_probe\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\tuning_scheme_impl\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\power_supply\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\port\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\port\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\heap\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\heap\tlsf;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\log\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\soc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\soc\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\soc\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\soc\esp32\register;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\hal\platform_port\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\hal\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\hal\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_rom\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_rom\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_rom\esp32\include\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_rom\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_system\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_system\port\include\private;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\xtensa\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\xtensa\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\xtensa\deprecated_include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_timer\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\include\apps;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\include\apps\sntp;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\lwip\src\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\freertos\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\esp32xx\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\esp32xx\include\arch;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\esp32xx\include\sys;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\gemmlowp;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\flatbuffers\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\ruy;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\kissfft;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp32-camera\driver\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp32-camera\conversions\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\deprecated;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\i2c\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\touch_sensor\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\twai\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\touch_sensor\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_pm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_ringbuf\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_gpio\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_pcnt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_gptimer\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_spi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_mcpwm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_ana_cmpr\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_i2s\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_sdmmc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\sdmmc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_sdspi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_sdio\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_dac\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_rmt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_tsens\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_sdm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_i2c\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_uart\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\vfs\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_ledc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_parlio\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_usb_serial_jtag\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_twai\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_jpeg\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\app\ember_coupling;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\lib;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\lib\dnssd;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\OpenThread;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\jsoncpp\repo\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlfaultinjection\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlassert\repo\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlio\repo\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\zzz_generated\app-common;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp-idf\espressif__esp_matter;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter\zap_common;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter\utils;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter_bridge;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter_console;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32\nimble;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32\route_hook;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_eth\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_event\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\include\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\osi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\api\include\api;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\btc\profile\esp\blufi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\btc\profile\esp\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\hci_log\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\ble_log\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\host\bluedroid\api\include\api;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\core;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\core\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\core\storage;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\btc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\models\common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\models\client\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\models\server\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\api\core\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\api\models\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\api;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\lib\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\api\core\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\api\models\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\btc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\dfu;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\mbt;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\tinycrypt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\tinycrypt\port;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_wifi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_wifi\include\local;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_wifi\wifi_apps\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_wifi\wifi_apps\nan_app\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_phy\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_phy\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_netif\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\port\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\library;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\esp_crt_bundle\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\everest\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\p256-m;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\p256-m\p256-m;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\fatfs\diskio;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\fatfs\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\fatfs\vfs;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wear_levelling\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_partition\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\app_update\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bootloader_support\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bootloader_support\bootloader_flash\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_app_format\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_bootloader_format\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\console;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_vfs_console\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\openthread\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\openthread\openthread\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\nvs_flash\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\spi_flash\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_secure_cert_mgr\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__json_parser\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__jsmn\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\spiffs\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_http_client\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__json_generator\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\json\cJSON;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__mdns\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_delta_ota\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_encrypted_img\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_insights\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_diagnostics\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\efuse\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\efuse\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_mm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_security\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\pthread\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_psram\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\app_trace\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wpa_supplicant\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wpa_supplicant\port\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wpa_supplicant\esp_supplicant\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_coex\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_gdbstub\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\unity\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\unity\unity\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\cmock\CMock\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_bitscrambler\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\http_parser;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp-tls;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp-tls\esp-tls-crypto;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_adc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_adc\interface;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_adc\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_adc\deprecated\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_isp\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_cam\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_cam\interface;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_jpeg\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_ppa\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_touch_sens\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_touch_sens\hw_ver1\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hal_ieee802154\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hid\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\tcp_transport\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_http_server\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_https_ota\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_https_server\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_lcd\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_lcd\interface;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protobuf-c\protobuf-c;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\include\common;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\include\security;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\include\transports;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\include\crypto\srp6a;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\proto-c;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_local_ctrl\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espcoredump\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espcoredump\include\port\xtensa;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\idf_test\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\idf_test\include\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\ieee802154\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mqtt\esp-mqtt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\nvs_sec_provider\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\perfmon\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\rt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\ulp\ulp_common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\ulp\ulp_fsm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\ulp\ulp_fsm\include\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wifi_provisioning\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-nn\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-nn\src\common;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__rmaker_common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__cbor\tinycbor\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_diag_data_store\src\rtc_store;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_diag_data_store\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-serial-flasher\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-serial-flasher\port;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_rcp_update\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\chmorgan__esp-libhelix-mp3\libhelix-mp3\pub;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-modbus\freemodbus\common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-zboss-lib\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-zigbee-lib\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-zigbee-lib\include\radio_spinel;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__libsodium\libsodium\src\libsodium\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__libsodium\port_include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\dotprod\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\support\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\support\mem\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\hann\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman_harris\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman_nuttall\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\nuttall\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\flat_top\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\iir\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\fir\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\add\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\sub\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\mul\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\addc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\mulc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\sqrt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mul\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\add\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\addc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mulc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\sub\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\fft\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\dct\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\conv\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mul\test\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\kalman\ekf\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\kalman\ekf_imu13states\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_modem\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_modem\command\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_schedule\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__network_provisioning\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_rainmaker\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__lan86xx_common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__lan867x\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__qrcode\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\joltwallet__littlefs\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\fb_gfx\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0\xtensa-esp-elf\esp32\no-rtti;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0\backward;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\lib\gcc\xtensa-esp-elf\14.2.0\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\lib\gcc\xtensa-esp-elf\14.2.0\include-fixed;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include;$(ProjectDir)..\..\..\Users\Simon\Documents\Arduino\libraries\SerialCommandManager\src - $(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\bin\xtensa-esp32-elf-g++ - $(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\bin\xtensa-esp32-elf-g++ + $(ProjectDir)..\SmartFuseBox;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SerialCommandManager\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SensorManager\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\NextionControl\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SdFat\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\SPI\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\WiFi\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Network\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\TinyGPSPlus\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\HTTPClient\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\NetworkClientSecure\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Update\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\Ds1302\src;$(ProjectDir)..\..\..\..\program files\microsoft visual studio\18\insiders\common7\ide\extensions\0qz5sbjj.e2i\Micro Platforms\default;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\dio_qspi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\cores\esp32;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\variants\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\newlib\platform_include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\config\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\config\include\freertos;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\config\xtensa\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\FreeRTOS-Kernel\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\FreeRTOS-Kernel\portable\xtensa\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\FreeRTOS-Kernel\portable\xtensa\include\freertos;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\esp_additions\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\include\soc;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\include\soc\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\dma\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\ldo\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\debug_probe\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\tuning_scheme_impl\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\power_supply\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\port\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\port\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\port\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\heap\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\heap\tlsf;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\log\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\soc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\soc\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\soc\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\soc\esp32s3\register;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\hal\platform_port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\hal\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\hal\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_rom\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_rom\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_rom\esp32s3\include\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_rom\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_system\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_system\port\include\private;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\xtensa\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\xtensa\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\xtensa\deprecated_include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_timer\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\include\apps;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\include\apps\sntp;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\lwip\src\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\freertos\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\esp32xx\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\esp32xx\include\arch;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\esp32xx\include\sys;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\gemmlowp;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\flatbuffers\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\ruy;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\kissfft;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp32-camera\driver\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp32-camera\conversions\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\deprecated;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\i2c\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\touch_sensor\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\twai\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\touch_sensor\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_pm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_ringbuf\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_gpio\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_pcnt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_gptimer\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_spi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_mcpwm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_ana_cmpr\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_i2s\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_sdmmc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\sdmmc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_sdspi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_sdio\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_dac\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_rmt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_tsens\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_sdm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_i2c\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_uart\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\vfs\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_ledc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_parlio\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_usb_serial_jtag\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_twai\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_jpeg\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\app\ember_coupling;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\lib;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\lib\dnssd;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\OpenThread;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\jsoncpp\repo\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlfaultinjection\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlassert\repo\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlio\repo\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\zzz_generated\app-common;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp-idf\espressif__esp_matter;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter\zap_common;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter\utils;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter_bridge;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter_console;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32\nimble;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32\route_hook;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_eth\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_event\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\include\esp32c3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\osi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\api\include\api;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\btc\profile\esp\blufi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\btc\profile\esp\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\hci_log\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\ble_log\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\core;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\core\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\core\storage;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\btc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\models\common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\models\client\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\models\server\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\api\core\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\api\models\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\api;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\lib\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\api\core\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\api\models\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\btc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\dfu;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\mbt;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\tinycrypt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\tinycrypt\port;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\ans\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\bas\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\dis\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\gap\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\gatt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\hr\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\htp\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\ias\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\ipss\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\lls\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\prox\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\cts\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\tps\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\hid\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\sps\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\cte\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\util\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\store\ram\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\store\config\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\ras\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\porting\nimble\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\transport\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\porting\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\porting\npl\freertos\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\esp-hci\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_wifi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_wifi\include\local;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_wifi\wifi_apps\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_wifi\wifi_apps\nan_app\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_phy\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_phy\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_netif\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\library;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\esp_crt_bundle\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\everest\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\p256-m;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\p256-m\p256-m;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\fatfs\diskio;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\fatfs\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\fatfs\vfs;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wear_levelling\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_partition\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\app_update\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bootloader_support\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bootloader_support\bootloader_flash\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_app_format\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_bootloader_format\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\console;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_vfs_console\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\openthread\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\openthread\openthread\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\nvs_flash\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\spi_flash\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_secure_cert_mgr\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__json_parser\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__jsmn\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\spiffs\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_http_client\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__json_generator\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\json\cJSON;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__mdns\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_delta_ota\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_encrypted_img\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_insights\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_diagnostics\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-sr\esp-tts\esp_tts_chinese\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-sr\include\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-sr\src\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\efuse\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\efuse\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_mm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_security\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\pthread\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_psram\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_psram\xip_impl\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\app_trace\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wpa_supplicant\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wpa_supplicant\port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wpa_supplicant\esp_supplicant\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_coex\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_gdbstub\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\unity\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\unity\unity\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\cmock\CMock\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_bitscrambler\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\http_parser;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp-tls;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp-tls\esp-tls-crypto;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_adc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_adc\interface;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_adc\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_adc\deprecated\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_isp\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_cam\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_cam\interface;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_cam\dvp\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_jpeg\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_ppa\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_touch_sens\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_touch_sens\hw_ver2\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hal_ieee802154\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hid\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\tcp_transport\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_http_server\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_https_ota\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_https_server\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_lcd\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_lcd\interface;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_lcd\rgb\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protobuf-c\protobuf-c;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\include\common;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\include\security;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\include\transports;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\include\crypto\srp6a;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\proto-c;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_local_ctrl\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espcoredump\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espcoredump\include\port\xtensa;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\idf_test\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\idf_test\include\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\ieee802154\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mqtt\esp-mqtt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\nvs_sec_provider\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\perfmon\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\rt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\touch_element\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\ulp\ulp_common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\ulp\ulp_fsm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\ulp\ulp_fsm\include\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\usb\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wifi_provisioning\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-nn\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-nn\src\common;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__rmaker_common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__cbor\tinycbor\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_diag_data_store\src\rtc_store;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_diag_data_store\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-serial-flasher\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-serial-flasher\port;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_rcp_update\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__dl_fft;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__dl_fft\base;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__dl_fft\base\isa;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\dotprod\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\support\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\support\mem\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\hann\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman_harris\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman_nuttall\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\nuttall\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\flat_top\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\iir\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\fir\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\add\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\sub\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\mul\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\addc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\mulc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\sqrt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mul\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\add\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\addc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mulc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\sub\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\fft\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\dct\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\conv\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mul\test\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\kalman\ekf\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\kalman\ekf_imu13states\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\chmorgan__esp-libhelix-mp3\libhelix-mp3\pub;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-modbus\freemodbus\common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-zboss-lib\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-zigbee-lib\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-zigbee-lib\include\radio_spinel;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__libsodium\libsodium\src\libsodium\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__libsodium\port_include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_modem\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_modem\command\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_schedule\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__network_provisioning\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_rainmaker\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__qrcode\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\joltwallet__littlefs\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\FreeRTOS-Kernel\include\freertos;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\arduino_tinyusb\tinyusb\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\arduino_tinyusb\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\fb_gfx\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0\xtensa-esp-elf\esp32s3\no-rtti;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0\backward;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\lib\gcc\xtensa-esp-elf\14.2.0\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\lib\gcc\xtensa-esp-elf\14.2.0\include-fixed;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SerialCommandManager\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SensorManager\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\NextionControl\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SdFat\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\SPI\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\WiFi\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Network\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\TinyGPSPlus\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\HTTPClient\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\NetworkClientSecure\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Update\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\Ds1302\src + $(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\bin\xtensa-esp32s3-elf-g++ + $(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\bin\xtensa-esp32s3-elf-g++ false @@ -262,16 +328,16 @@ - $(ProjectDir)..\..\..\Users\Simon\Documents\Arduino\libraries\SerialCommandManager\src;$(ProjectDir)..\..\..\Users\Simon\Documents\Arduino\libraries\SensorManager\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\WiFi\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Network\src;$(ProjectDir)..\..\..\Users\Simon\Documents\Arduino\libraries\dht11;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\EEPROM\src;$(ProjectDir)..\..\..\program files\microsoft visual studio\18\insiders\common7\ide\extensions\0qz5sbjj.e2i\Micro Platforms\default;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\dio_qspi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\cores\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\variants\nodemcu-32s;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\newlib\platform_include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\config\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\config\include\freertos;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\config\xtensa\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\FreeRTOS-Kernel\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\FreeRTOS-Kernel\portable\xtensa\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\FreeRTOS-Kernel\portable\xtensa\include\freertos;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\freertos\esp_additions\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\include\soc;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\include\soc\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\dma\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\ldo\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\debug_probe\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\tuning_scheme_impl\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\power_supply\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\port\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hw_support\port\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\heap\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\heap\tlsf;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\log\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\soc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\soc\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\soc\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\soc\esp32\register;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\hal\platform_port\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\hal\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\hal\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_rom\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_rom\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_rom\esp32\include\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_rom\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_system\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_system\port\include\private;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\xtensa\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\xtensa\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\xtensa\deprecated_include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_timer\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\include\apps;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\include\apps\sntp;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\lwip\src\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\freertos\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\esp32xx\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\esp32xx\include\arch;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\lwip\port\esp32xx\include\sys;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\gemmlowp;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\flatbuffers\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\ruy;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\kissfft;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp32-camera\driver\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp32-camera\conversions\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\deprecated;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\i2c\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\touch_sensor\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\twai\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\driver\touch_sensor\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_pm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_ringbuf\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_gpio\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_pcnt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_gptimer\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_spi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_mcpwm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_ana_cmpr\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_i2s\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_sdmmc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\sdmmc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_sdspi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_sdio\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_dac\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_rmt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_tsens\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_sdm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_i2c\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_uart\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\vfs\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_ledc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_parlio\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_usb_serial_jtag\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_twai\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_jpeg\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\app\ember_coupling;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\lib;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\lib\dnssd;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\OpenThread;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\jsoncpp\repo\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlfaultinjection\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlassert\repo\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlio\repo\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\zzz_generated\app-common;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp-idf\espressif__esp_matter;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter\zap_common;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter\utils;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter_bridge;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_matter_console;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32\nimble;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32\route_hook;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_eth\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_event\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\include\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\osi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\api\include\api;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\btc\profile\esp\blufi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\btc\profile\esp\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\hci_log\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\ble_log\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\host\bluedroid\api\include\api;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\core;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\core\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\core\storage;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\btc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\models\common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\models\client\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\models\server\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\api\core\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\api\models\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\api;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\lib\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\api\core\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\api\models\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\btc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\dfu;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\mbt;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\tinycrypt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bt\common\tinycrypt\port;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_wifi\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_wifi\include\local;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_wifi\wifi_apps\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_wifi\wifi_apps\nan_app\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_phy\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_phy\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_netif\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\port\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\library;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\esp_crt_bundle\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\everest\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\p256-m;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\p256-m\p256-m;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\fatfs\diskio;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\fatfs\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\fatfs\vfs;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wear_levelling\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_partition\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\app_update\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bootloader_support\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\bootloader_support\bootloader_flash\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_app_format\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_bootloader_format\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\console;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_vfs_console\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\openthread\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\openthread\openthread\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\nvs_flash\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\spi_flash\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_secure_cert_mgr\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__json_parser\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__jsmn\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\spiffs\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_http_client\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__json_generator\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\json\cJSON;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__mdns\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_delta_ota\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_encrypted_img\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_insights\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_diagnostics\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\efuse\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\efuse\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_mm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_security\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\pthread\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_psram\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\app_trace\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wpa_supplicant\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wpa_supplicant\port\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wpa_supplicant\esp_supplicant\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_coex\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_gdbstub\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\unity\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\unity\unity\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\cmock\CMock\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_bitscrambler\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\http_parser;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp-tls;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp-tls\esp-tls-crypto;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_adc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_adc\interface;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_adc\esp32\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_adc\deprecated\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_isp\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_cam\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_cam\interface;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_jpeg\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_ppa\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_touch_sens\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_driver_touch_sens\hw_ver1\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hal_ieee802154\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_hid\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\tcp_transport\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_http_server\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_https_ota\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_https_server\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_lcd\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_lcd\interface;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protobuf-c\protobuf-c;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\include\common;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\include\security;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\include\transports;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\include\crypto\srp6a;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\protocomm\proto-c;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\esp_local_ctrl\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espcoredump\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espcoredump\include\port\xtensa;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\idf_test\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\idf_test\include\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\ieee802154\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\mqtt\esp-mqtt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\nvs_sec_provider\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\perfmon\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\rt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\ulp\ulp_common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\ulp\ulp_fsm\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\ulp\ulp_fsm\include\esp32;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\wifi_provisioning\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-nn\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-nn\src\common;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__rmaker_common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__cbor\tinycbor\src;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_diag_data_store\src\rtc_store;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_diag_data_store\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-serial-flasher\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-serial-flasher\port;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_rcp_update\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\chmorgan__esp-libhelix-mp3\libhelix-mp3\pub;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-modbus\freemodbus\common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-zboss-lib\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-zigbee-lib\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-zigbee-lib\include\radio_spinel;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__libsodium\libsodium\src\libsodium\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__libsodium\port_include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\dotprod\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\support\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\support\mem\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\hann\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman_harris\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman_nuttall\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\nuttall\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\windows\flat_top\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\iir\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\fir\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\add\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\sub\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\mul\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\addc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\mulc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\math\sqrt\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mul\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\add\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\addc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mulc\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\sub\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\fft\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\dct\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\conv\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mul\test\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\kalman\ekf\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp-dsp\modules\kalman\ekf_imu13states\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_modem\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_modem\command\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_schedule\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__network_provisioning\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__esp_rainmaker\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__lan86xx_common\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__lan867x\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\espressif__qrcode\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\joltwallet__littlefs\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32-libs\3.3.7\include\fb_gfx\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0\xtensa-esp-elf\esp32\no-rtti;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0\backward;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\lib\gcc\xtensa-esp-elf\14.2.0\include;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\lib\gcc\xtensa-esp-elf\14.2.0\include-fixed;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include;$(ProjectDir)..\..\..\Users\Simon\Documents\Arduino\libraries\SerialCommandManager\src;%(AdditionalIncludeDirectories) - $(ProjectDir)..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\bin\xtensa-esp32-elf-g++ + $(ProjectDir)..\SmartFuseBox;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SerialCommandManager\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SensorManager\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\NextionControl\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SdFat\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\SPI\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\WiFi\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Network\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\TinyGPSPlus\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\HTTPClient\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\NetworkClientSecure\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Update\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\Ds1302\src;$(ProjectDir)..\..\..\..\program files\microsoft visual studio\18\insiders\common7\ide\extensions\0qz5sbjj.e2i\Micro Platforms\default;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\dio_qspi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\cores\esp32;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\variants\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\newlib\platform_include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\config\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\config\include\freertos;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\config\xtensa\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\FreeRTOS-Kernel\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\FreeRTOS-Kernel\portable\xtensa\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\FreeRTOS-Kernel\portable\xtensa\include\freertos;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\esp_additions\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\include\soc;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\include\soc\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\dma\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\ldo\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\debug_probe\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\tuning_scheme_impl\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\power_supply\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\port\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\port\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hw_support\mspi_timing_tuning\port\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\heap\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\heap\tlsf;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\log\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\soc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\soc\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\soc\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\soc\esp32s3\register;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\hal\platform_port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\hal\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\hal\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_rom\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_rom\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_rom\esp32s3\include\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_rom\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_system\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_system\port\include\private;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\xtensa\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\xtensa\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\xtensa\deprecated_include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_timer\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\include\apps;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\include\apps\sntp;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\lwip\src\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\freertos\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\esp32xx\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\esp32xx\include\arch;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\lwip\port\esp32xx\include\sys;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\gemmlowp;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\flatbuffers\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\ruy;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-tflite-micro\third_party\kissfft;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp32-camera\driver\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp32-camera\conversions\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\deprecated;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\i2c\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\touch_sensor\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\twai\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\driver\touch_sensor\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_pm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_ringbuf\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_gpio\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_pcnt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_gptimer\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_spi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_mcpwm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_ana_cmpr\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_i2s\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_sdmmc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\sdmmc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_sdspi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_sdio\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_dac\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_rmt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_tsens\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_sdm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_i2c\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_uart\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\vfs\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_ledc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_parlio\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_usb_serial_jtag\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_twai\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_jpeg\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\app\ember_coupling;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\lib;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\lib\dnssd;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\OpenThread;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\jsoncpp\repo\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlfaultinjection\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlassert\repo\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\third_party\nlio\repo\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\zzz_generated\app-common;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp-idf\espressif__esp_matter;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter\zap_common;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter\utils;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter_bridge;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_matter_console;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32\nimble;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_matter\connectedhomeip\connectedhomeip\src\platform\ESP32\route_hook;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_eth\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_event\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\include\esp32c3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\osi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\api\include\api;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\btc\profile\esp\blufi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\btc\profile\esp\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\hci_log\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\ble_log\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\core;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\core\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\core\storage;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\btc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\models\common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\models\client\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\models\server\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\api\core\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\api\models\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\api;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\lib\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\api\core\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\api\models\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\btc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\dfu;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\esp_ble_mesh\v1.1\mbt;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\tinycrypt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\common\tinycrypt\port;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\ans\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\bas\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\dis\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\gap\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\gatt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\hr\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\htp\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\ias\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\ipss\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\lls\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\prox\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\cts\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\tps\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\hid\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\sps\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\cte\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\util\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\store\ram\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\store\config\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\host\services\ras\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\porting\nimble\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\nimble\transport\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\porting\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\nimble\porting\npl\freertos\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bt\host\nimble\esp-hci\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_wifi\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_wifi\include\local;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_wifi\wifi_apps\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_wifi\wifi_apps\nan_app\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_phy\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_phy\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_netif\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\library;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\esp_crt_bundle\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\everest\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\p256-m;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mbedtls\mbedtls\3rdparty\p256-m\p256-m;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\fatfs\diskio;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\fatfs\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\fatfs\vfs;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wear_levelling\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_partition\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\app_update\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bootloader_support\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\bootloader_support\bootloader_flash\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_app_format\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_bootloader_format\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\console;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_vfs_console\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\openthread\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\openthread\openthread\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\nvs_flash\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\spi_flash\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_secure_cert_mgr\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__json_parser\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__jsmn\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\spiffs\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_http_client\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__json_generator\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\json\cJSON;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__mdns\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_delta_ota\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_encrypted_img\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_insights\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_diagnostics\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-sr\esp-tts\esp_tts_chinese\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-sr\include\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-sr\src\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\efuse\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\efuse\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_mm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_security\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\pthread\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_psram\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_psram\xip_impl\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\app_trace\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wpa_supplicant\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wpa_supplicant\port\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wpa_supplicant\esp_supplicant\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_coex\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_gdbstub\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\unity\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\unity\unity\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\cmock\CMock\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_bitscrambler\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\http_parser;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp-tls;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp-tls\esp-tls-crypto;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_adc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_adc\interface;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_adc\esp32s3\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_adc\deprecated\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_isp\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_cam\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_cam\interface;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_cam\dvp\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_jpeg\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_ppa\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_touch_sens\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_driver_touch_sens\hw_ver2\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hal_ieee802154\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_hid\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\tcp_transport\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_http_server\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_https_ota\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_https_server\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_lcd\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_lcd\interface;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_lcd\rgb\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protobuf-c\protobuf-c;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\include\common;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\include\security;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\include\transports;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\include\crypto\srp6a;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\protocomm\proto-c;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\esp_local_ctrl\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espcoredump\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espcoredump\include\port\xtensa;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\idf_test\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\idf_test\include\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\ieee802154\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\mqtt\esp-mqtt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\nvs_sec_provider\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\perfmon\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\rt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\touch_element\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\ulp\ulp_common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\ulp\ulp_fsm\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\ulp\ulp_fsm\include\esp32s3;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\usb\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\wifi_provisioning\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-nn\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-nn\src\common;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__rmaker_common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__cbor\tinycbor\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_diag_data_store\src\rtc_store;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_diag_data_store\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-serial-flasher\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-serial-flasher\port;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_rcp_update\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__dl_fft;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__dl_fft\base;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__dl_fft\base\isa;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\dotprod\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\support\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\support\mem\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\hann\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman_harris\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\blackman_nuttall\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\nuttall\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\windows\flat_top\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\iir\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\fir\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\add\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\sub\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\mul\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\addc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\mulc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\math\sqrt\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mul\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\add\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\addc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mulc\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\sub\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\fft\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\dct\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\conv\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\matrix\mul\test\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\kalman\ekf\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-dsp\modules\kalman\ekf_imu13states\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\chmorgan__esp-libhelix-mp3\libhelix-mp3\pub;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-modbus\freemodbus\common\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-zboss-lib\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-zigbee-lib\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp-zigbee-lib\include\radio_spinel;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__libsodium\libsodium\src\libsodium\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__libsodium\port_include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_modem\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_modem\command\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_schedule\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__network_provisioning\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__esp_rainmaker\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\espressif__qrcode\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\joltwallet__littlefs\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\freertos\FreeRTOS-Kernel\include\freertos;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\arduino_tinyusb\tinyusb\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\arduino_tinyusb\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp32s3-libs\3.3.7\include\fb_gfx\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0\xtensa-esp-elf\esp32s3\no-rtti;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include\c++\14.2.0\backward;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\lib\gcc\xtensa-esp-elf\14.2.0\include;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\lib\gcc\xtensa-esp-elf\14.2.0\include-fixed;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\xtensa-esp-elf\include;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SerialCommandManager\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SensorManager\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\NextionControl\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\SdFat\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\SPI\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\WiFi\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Network\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\TinyGPSPlus\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\EEPROM\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\HTTPClient\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\NetworkClientSecure\src;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\hardware\esp32\3.3.7\libraries\Update\src;$(ProjectDir)..\..\..\..\Users\Simon\Documents\Arduino\libraries\Ds1302\src;%(AdditionalIncludeDirectories) + $(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\arduino15\packages\esp32\tools\esp-x32\2511\bin\xtensa-esp32s3-elf-g++ c++2a gnu11 - $(ProjectDir)..\..\..\program files\microsoft visual studio\18\insiders\common7\ide\extensions\0qz5sbjj.e2i\Micro Platforms\default\vm-intelli-pre.h;$(ProjectDir)..\..\..\Users\Simon\AppData\Local\Temp\VMBuilds\SmartFuseBox\esp32_nodemcu-32s\Debug\.vmintelli\65341a464675da498b7f6fc6e48f1d1b\vm-intelli-gcc-defines.h;$(ProjectDir)..\..\..\program files\microsoft visual studio\18\insiders\common7\ide\extensions\0qz5sbjj.e2i\Micro Platforms\default\vm-intelli-post.h;$(ProjectDir)__vm\.SmartFuseBox.vsarduino.h;%(ForcedIncludeFiles) + $(ProjectDir)..\..\..\..\program files\microsoft visual studio\18\insiders\common7\ide\extensions\0qz5sbjj.e2i\Micro Platforms\default\vm-intelli-pre.h;$(ProjectDir)..\..\..\..\Users\Simon\AppData\Local\Temp\VMBuilds\SmartFuseBox\esp32_esp32s3\Debug\.vmintelli\43a5584cc1e3cbf43014a5b588f12f8e\vm-intelli-gcc-defines.h;$(ProjectDir)..\..\..\..\program files\microsoft visual studio\18\insiders\common7\ide\extensions\0qz5sbjj.e2i\Micro Platforms\default\vm-intelli-post.h;$(ProjectDir)__vm\.SmartFuseBox.vsarduino.h;%(ForcedIncludeFiles) true true - _VMICRO_INTELLISENSE;__2511_esp32__;__2511_ESP32__;_VMDEBUG=1;F_CPU=240000000L;ARDUINO=108010;ARDUINO_NODEMCU_32S;ARDUINO_ARCH_ESP32;ARDUINO_BOARD=NODEMCU_32S;ARDUINO_VARIANT=nodemcu-32s;ARDUINO_PARTITION_default;ARDUINO_HOST_OS=windows;ARDUINO_FQBN={build.fqbn};ESP32=ESP32;CORE_DEBUG_LEVEL=0;ARDUINO_USB_CDC_ON_BOOT=0;%(PreprocessorDefinitions) + _VMICRO_INTELLISENSE;__2511_esp32s3__;__2511_ESP32S3__;_VMDEBUG=1;F_CPU=240000000L;ARDUINO=108010;ARDUINO_ESP32S3_DEV;ARDUINO_ARCH_ESP32;ARDUINO_BOARD=ESP32S3_DEV;ARDUINO_VARIANT=esp32s3;ARDUINO_PARTITION_default;ARDUINO_HOST_OS=windows;ARDUINO_FQBN={build.fqbn};ESP32=ESP32;CORE_DEBUG_LEVEL=0;ARDUINO_RUNNING_CORE=1;ARDUINO_EVENT_RUNNING_CORE=1;ARDUINO_USB_MODE=1;ARDUINO_USB_CDC_ON_BOOT=0;ARDUINO_USB_MSC_ON_BOOT=0;ARDUINO_USB_DFU_ON_BOOT=0;%(PreprocessorDefinitions) diff --git a/SmartFuseBox/SmartFuseBox.vcxproj.filters b/SmartFuseBox/SmartFuseBox.vcxproj.filters index c05afa7..47c5310 100644 --- a/SmartFuseBox/SmartFuseBox.vcxproj.filters +++ b/SmartFuseBox/SmartFuseBox.vcxproj.filters @@ -13,9 +13,6 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - {b2560382-112e-4051-8e71-7cbe1f03a4f2} - {5acd69d0-acce-44d6-a78e-47664e8e5636} @@ -61,6 +58,24 @@ {f48d29f5-d769-4b98-95b3-644826ed0f98} + + {b3847c11-0bbc-4a90-8aa9-fd4507fcbf5c} + + + {dafb435f-7360-4352-b4aa-45bb9b2c98cf} + + + {7694abdc-b60b-4f29-a0db-de19e00cc1cb} + + + {3e32da0a-967e-46f5-89d1-241953e77613} + + + {b2560382-112e-4051-8e71-7cbe1f03a4f2} + + + {c01f5b86-3ca9-4b0f-854a-2b57b09e207d} + @@ -182,7 +197,7 @@ Source Files\Scheduler - Header Files\Sensors + Header Files\Components Source Files\SerialCommandHandlers @@ -220,8 +235,92 @@ Source Files + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Nextion + + + Source Files\Other + + + Source Files\Other + + + Source Files + + + Source Files + + + Source Files + + + Source Files + - Header Files + Header Files\Components @@ -232,7 +331,7 @@ Header Files\SerialCommandHandlers - Header Files\Sensors + Header Files\Components Header Files @@ -291,14 +390,11 @@ Header Files\BusinessLogic - - Header Files - Header Files - Header Files\Sensors + Header Files\Components Header Files @@ -394,7 +490,7 @@ Header Files\Bluetooth - Header Files\Sensors + Header Files\Components Header Files\SDCard @@ -403,10 +499,10 @@ Header Files\SDCard - Header Files\Sensors + Header Files\Components - Header Files\Sensors + Header Files\Components Header Files\SerialCommandHandlers @@ -462,9 +558,6 @@ Header Files - - Header Files - Header Files @@ -477,9 +570,129 @@ Header Files - + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Nextion + + + Header Files\Components + + + Header Files\Other + + + Header Files\Other + + + Header Files\Other + + + Header Files\Other + + + Header Files\Components + + + Header Files\Components + + + Source Files\Nextion + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files + + Header Files\SerialCommandHandlers + + + Header Files\Other + + + Header Files\Wifi + + + Header Files\Components + diff --git a/SmartFuseBox/SmartFuseBoxApp.cpp b/SmartFuseBox/SmartFuseBoxApp.cpp index 32eaf53..59f3b61 100644 --- a/SmartFuseBox/SmartFuseBoxApp.cpp +++ b/SmartFuseBox/SmartFuseBoxApp.cpp @@ -22,6 +22,11 @@ #include "ConfigManager.h" #include "SensorFactory.h" +#if defined(NEXTION_DISPLAY_DEVICE) +#include "NextionFactory.h" +#include +#endif + #if defined(CARD_CONFIG_LOADER) #include "SDCardConfigLoader.h" @@ -34,22 +39,32 @@ constexpr uint8_t DefaultDelay = 5; // Actual pins are loaded from config in setup() via syncPinsFromConfig(). static uint8_t _disabledRelayPins[ConfigRelayCount]; -SmartFuseBoxApp::SmartFuseBoxApp(SerialCommandManager* commandMgrComputer, - SerialCommandManager* commandMgrLink) +SmartFuseBoxApp::SmartFuseBoxApp(SerialCommandManager* commandMgrComputer) : _commandMgrComputer(commandMgrComputer), - _commandMgrLink(commandMgrLink), _messageBus(), _relayController(&_messageBus, (memset(_disabledRelayPins, DefaultValue, sizeof(_disabledRelayPins)), _disabledRelayPins), ConfigRelayCount), _soundController(), - _broadcastManager(commandMgrComputer, commandMgrLink), - _warningManager(commandMgrLink, HeartbeatIntervalMs, HeartbeatTimeoutMs), - _relayHandler(commandMgrComputer, commandMgrLink, &_relayController), - _soundHandler(commandMgrComputer, commandMgrLink, &_soundController), + _broadcastManager(commandMgrComputer), + _warningManager(), +#if defined(NEXTION_DISPLAY_DEVICE) + _nextionControl(NextionFactory::Create(&_warningManager, commandMgrComputer, &_soundController, &_relayController, + ConfigManager::getConfigPtr() ? ConfigManager::getConfigPtr()->location.locationType : LocationType::Other)), +#endif + _relayHandler(commandMgrComputer, &_relayController), + _soundHandler(commandMgrComputer, &_soundController), _interceptDebugHandler(&_broadcastManager), - _sensorCommandHandler(&_broadcastManager, &_warningManager), - _sensorConfigHandler(commandMgrComputer, commandMgrLink), + _sensorCommandHandler(&_broadcastManager, +#if defined(NEXTION_DISPLAY_DEVICE) + _nextionControl, +#endif + &_warningManager), + _sensorConfigHandler(commandMgrComputer), _warningCommandHandler(&_broadcastManager, &_warningManager), - _ackHandler(&_broadcastManager, &_warningManager), + _ackHandler(&_broadcastManager, +#if defined(NEXTION_DISPLAY_DEVICE) + _nextionControl, +#endif + &_warningManager), _systemCommandHandler(&_broadcastManager, &_warningManager), _bluetoothController(&_systemCommandHandler, &_sensorCommandHandler, &_relayController, &_warningManager, commandMgrComputer), _wifiController(&_messageBus, commandMgrComputer, &_warningManager), @@ -57,6 +72,8 @@ SmartFuseBoxApp::SmartFuseBoxApp(SerialCommandManager* commandMgrComputer, &_bluetoothController, &_wifiController), _configHandler(&_wifiController, &_configController), + _nextionConfigHandler(&_configController), + _externalSensorConfigHandler(commandMgrComputer), _configNetworkHandler(&_configController, &_wifiController, &_relayController), _relayNetworkHandler(&_relayController), _soundNetworkHandler(&_soundController), @@ -72,6 +89,9 @@ SmartFuseBoxApp::SmartFuseBoxApp(SerialCommandManager* commandMgrComputer, , _sensorController(nullptr) , _factorySensors(nullptr) , _factorySensorCount(0) + , _gpsSerial(nullptr) + , _remoteSensors(nullptr) + , _remoteSensorCount(0) #if defined(MQTT_SUPPORT) , _mqttController(&_messageBus, ConfigManager::getConfigPtr(), _wifiController.getRadio(), commandMgrComputer) @@ -95,7 +115,7 @@ SmartFuseBoxApp::SmartFuseBoxApp(SerialCommandManager* commandMgrComputer, #endif #if defined(CARD_CONFIG_LOADER) - , _sdCardConfigLoader(commandMgrComputer, commandMgrLink, &_configController, &_relayController) + , _sdCardConfigLoader(commandMgrComputer, &_configController, &_relayController) #endif { #if defined(CARD_CONFIG_LOADER) @@ -105,8 +125,6 @@ SmartFuseBoxApp::SmartFuseBoxApp(SerialCommandManager* commandMgrComputer, void SmartFuseBoxApp::setup(RemoteSensor** remoteSensors, uint8_t remoteSensorCount) { - DateTimeManager::setDateTime(); - // retrieve config settings ConfigManager::begin(); @@ -121,14 +139,63 @@ void SmartFuseBoxApp::setup(RemoteSensor** remoteSensors, uint8_t remoteSensorCo _relayController.setSoundController(&_soundController); if (remoteSensorCount > 0 && remoteSensors != nullptr) - { - _sensorCommandHandler.setup(remoteSensors, remoteSensorCount); - } + { + _sensorCommandHandler.setup(remoteSensors, remoteSensorCount); + } + + Config* config = ConfigManager::getConfigPtr(); + + DateTimeManager::begin(config->rtc); + + // Build remote sensors from RemoteSensorsConfig (populated by ConfigManager::load()). + // Sensors are created dynamically here; ownership stays with SmartFuseBoxApp (_remoteSensors). + // Any sensors passed in by the caller are ignored when config has entries. + if (config->remoteSensors.count > 0) + { + _remoteSensorCount = config->remoteSensors.count; + _remoteSensors = new RemoteSensor*[_remoteSensorCount]; + +#if defined(MQTT_SUPPORT) + // Allocate one MqttSensorChannel per sensor; stored in a flat array. + MqttSensorChannel* channels = new MqttSensorChannel[_remoteSensorCount]; +#endif + + for (uint8_t i = 0; i < _remoteSensorCount; i++) + { + const RemoteSensorConfig& entry = config->remoteSensors.sensors[i]; + +#if defined(MQTT_SUPPORT) + channels[i].name = entry.mqttName; + channels[i].slug = entry.mqttSlug; + channels[i].typeSlug = entry.mqttTypeSlug; + channels[i].deviceClass = entry.mqttDeviceClass[0] != '\0' ? entry.mqttDeviceClass : nullptr; + channels[i].unit = entry.mqttUnit[0] != '\0' ? entry.mqttUnit : nullptr; + channels[i].isBinary = entry.mqttIsBinary; + + _remoteSensors[i] = new RemoteSensor( + entry.sensorId, + entry.name, + entry.name, // commandId: external devices push updates using the sensor name + entry.name, // mqttTopic + &channels[i], + 1); +#else + _remoteSensors[i] = new RemoteSensor( + entry.sensorId, + entry.name, + entry.name, // commandId + 1); +#endif + } + + remoteSensors = _remoteSensors; + remoteSensorCount = _remoteSensorCount; + _sensorCommandHandler.setup(_remoteSensors, _remoteSensorCount); + } + // Build local sensors from SensorsConfig (populated by ConfigManager::load() above). // SensorFactory allocates each enabled entry once; ownership stays with SmartFuseBoxApp. - Config* config = ConfigManager::getConfigPtr(); - { SensorFactoryContext ctx; ctx.messageBus = &_messageBus; @@ -138,6 +205,7 @@ void SmartFuseBoxApp::setup(RemoteSensor** remoteSensors, uint8_t remoteSensorCo ctx.relayController = &_relayController; ctx.wifiController = &_wifiController; ctx.bluetoothRadio = &_bluetoothController; + ctx.gpsSerial = _gpsSerial; #if defined(SD_CARD_SUPPORT) ctx.sdCardLogger = &_sdCardLogger; #endif @@ -180,6 +248,8 @@ void SmartFuseBoxApp::setup(RemoteSensor** remoteSensors, uint8_t remoteSensorCo _serialHandlers[_serialHandlerCount++] = &_relayHandler; _serialHandlers[_serialHandlerCount++] = &_soundHandler; _serialHandlers[_serialHandlerCount++] = &_configHandler; + _serialHandlers[_serialHandlerCount++] = &_nextionConfigHandler; + _serialHandlers[_serialHandlerCount++] = &_externalSensorConfigHandler; _serialHandlers[_serialHandlerCount++] = &_ackHandler; _serialHandlers[_serialHandlerCount++] = &_systemCommandHandler; _serialHandlers[_serialHandlerCount++] = &_warningCommandHandler; @@ -187,7 +257,6 @@ void SmartFuseBoxApp::setup(RemoteSensor** remoteSensors, uint8_t remoteSensorCo _serialHandlers[_serialHandlerCount++] = &_sensorConfigHandler; _serialHandlers[_serialHandlerCount++] = &_schedulerCommandHandler; - _commandMgrLink->registerHandlers(_serialHandlers, _serialHandlerCount); _commandMgrComputer->registerHandlers(_serialHandlers, _serialHandlerCount); // Give the WiFi command bridge @@ -285,7 +354,6 @@ void SmartFuseBoxApp::loop() SystemCpuMonitor::startTask(); _commandMgrComputer->readCommands(); - _commandMgrLink->readCommands(); SystemCpuMonitor::endTask(); #if defined(LED_MANAGER) @@ -366,6 +434,7 @@ void SmartFuseBoxApp::configureWifiSupport(Config* config) // network command handlers INetworkCommandHandler* networkHandlers[] = { &_relayNetworkHandler, &_soundNetworkHandler, &_warningNetworkHandler, &_systemNetworkHandler, _sensorNetworkHandler, &_configNetworkHandler, &_schedulerNetworkHandler, + &_externalSensorNetworkHandler, &_wifiCommandBridge }; size_t networkHandlerCount = sizeof(networkHandlers) / sizeof(networkHandlers[0]); @@ -380,6 +449,7 @@ void SmartFuseBoxApp::configureWifiSupport(Config* config) &_warningNetworkHandler, _sensorNetworkHandler, &_schedulerNetworkHandler, + &_externalSensorNetworkHandler, }; uint8_t jsonVisitorCount = sizeof(jsonVisitors) / sizeof(jsonVisitors[0]); _wifiController.registerJsonVisitors(jsonVisitors, jsonVisitorCount); diff --git a/SmartFuseBox/SmartFuseBoxApp.h b/SmartFuseBox/SmartFuseBoxApp.h index ec3eece..f9fdc86 100644 --- a/SmartFuseBox/SmartFuseBoxApp.h +++ b/SmartFuseBox/SmartFuseBoxApp.h @@ -19,6 +19,8 @@ #include "Local.h" +#include + #include #include @@ -37,6 +39,9 @@ #include "AckCommandHandler.h" #include "SystemCommandHandler.h" #include "ConfigCommandHandler.h" +#include "NextionConfigCommandHandler.h" +#include "ExternalSensorConfigCommandHandler.h" +#include "ExternalSensorNetworkHandler.h" #include "BluetoothRadioBridge.h" #include "IBluetoothRadio.h" #include "WifiController.h" @@ -77,6 +82,10 @@ #include "OtaManager.h" #endif +#if defined(NEXTION_DISPLAY_DEVICE) +#include +#endif + #include "BaseSensor.h" #include "RemoteSensor.h" #include "SensorFactory.h" @@ -85,7 +94,6 @@ class SmartFuseBoxApp { private: SerialCommandManager* _commandMgrComputer; - SerialCommandManager* _commandMgrLink; MessageBus _messageBus; RelayController _relayController; @@ -93,6 +101,10 @@ class SmartFuseBoxApp BroadcastManager _broadcastManager; WarningManager _warningManager; +#if defined(NEXTION_DISPLAY_DEVICE) + NextionControl* _nextionControl; +#endif + RelayCommandHandler _relayHandler; SoundCommandHandler _soundHandler; InterceptDebugHandler _interceptDebugHandler; @@ -108,6 +120,8 @@ class SmartFuseBoxApp ConfigController _configController; ConfigCommandHandler _configHandler; + NextionConfigCommandHandler _nextionConfigHandler; + ExternalSensorConfigCommandHandler _externalSensorConfigHandler; ConfigNetworkHandler _configNetworkHandler; RelayNetworkHandler _relayNetworkHandler; @@ -124,6 +138,9 @@ class SmartFuseBoxApp SensorController* _sensorController; BaseSensor** _factorySensors; uint8_t _factorySensorCount; + Stream* _gpsSerial; + RemoteSensor** _remoteSensors; + uint8_t _remoteSensorCount; #if defined(MQTT_SUPPORT) MQTTController _mqttController; @@ -136,11 +153,12 @@ class SmartFuseBoxApp SchedulerCommandHandler _schedulerCommandHandler; SchedulerNetworkHandler _schedulerNetworkHandler; + ExternalSensorNetworkHandler _externalSensorNetworkHandler; WifiCommandBridge _wifiCommandBridge; // Persistent storage for the serial handler array so WifiCommandBridge // can hold a pointer to it safely beyond setup(). - static constexpr uint8_t MaxSerialHandlerCount = 10; + static constexpr uint8_t MaxSerialHandlerCount = 12; ISerialCommandHandler* _serialHandlers[MaxSerialHandlerCount]; uint8_t _serialHandlerCount; ScheduleController _scheduleController; @@ -163,12 +181,13 @@ class SmartFuseBoxApp void configureBluetoothSupport(Config* config); public: - SmartFuseBoxApp(SerialCommandManager* commandMgrComputer, - SerialCommandManager* commandMgrLink); + explicit SmartFuseBoxApp(SerialCommandManager* commandMgrComputer); void setup(RemoteSensor** remoteSensors, uint8_t remoteSensorCount); void loop(); + void setGpsSerial(Stream* gpsSerial) { _gpsSerial = gpsSerial; } + MessageBus* messageBus() { return &_messageBus; } BroadcastManager* broadcastManager() { return &_broadcastManager; } WarningManager* warningManager() { return &_warningManager; } diff --git a/SmartFuseBox/SoundCommandHandler.cpp b/SmartFuseBox/SoundCommandHandler.cpp index a4bb272..a520ce0 100644 --- a/SmartFuseBox/SoundCommandHandler.cpp +++ b/SmartFuseBox/SoundCommandHandler.cpp @@ -24,9 +24,9 @@ -SoundCommandHandler::SoundCommandHandler(SerialCommandManager* commandMgrComputer, SerialCommandManager* commandMgrLink, - SoundController* soundController) - : _commandMgrComputer(commandMgrComputer), _commandMgrLink(commandMgrLink), _soundController(soundController) +SoundCommandHandler::SoundCommandHandler(SerialCommandManager* commandMgrComputer, + SoundController* soundController) + : _commandMgrComputer(commandMgrComputer), _soundController(soundController) { } @@ -135,11 +135,6 @@ bool SoundCommandHandler::handleCommand(SerialCommandManager* sender, const char void SoundCommandHandler::broadcast(const char* cmd, const StringKeyValue* param) { - if (_commandMgrLink != nullptr) - { - sendAckOk(_commandMgrLink, cmd, param); - } - if (_commandMgrComputer != nullptr) { sendAckOk(_commandMgrComputer, cmd, param); diff --git a/SmartFuseBox/SoundCommandHandler.h b/SmartFuseBox/SoundCommandHandler.h index 7a84450..3a3c2c1 100644 --- a/SmartFuseBox/SoundCommandHandler.h +++ b/SmartFuseBox/SoundCommandHandler.h @@ -27,11 +27,10 @@ class SoundCommandHandler : public BaseCommandHandler { private: SerialCommandManager* _commandMgrComputer; - SerialCommandManager* _commandMgrLink; SoundController* _soundController; public: - SoundCommandHandler(SerialCommandManager* commandMgrComputer, SerialCommandManager* commandMgrLink, SoundController* soundController); + SoundCommandHandler(SerialCommandManager* commandMgrComputer, SoundController* soundController); bool handleCommand(SerialCommandManager* sender, const char* command, const StringKeyValue params[], uint8_t paramCount) override; const char* const* supportedCommands(size_t& count) const override; diff --git a/SmartFuseBox/SunCalculator.cpp b/SmartFuseBox/SunCalculator.cpp new file mode 100644 index 0000000..391007f --- /dev/null +++ b/SmartFuseBox/SunCalculator.cpp @@ -0,0 +1,128 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "SunCalculator.h" +#include "ConfigManager.h" +#include "DateTimeManager.h" + +SunTimes SunCalculator::calculateSunTimes(GpsSensorHandler* gpsHandler) +{ + Config* config = ConfigManager::getConfigPtr(); + + if (!config || !gpsHandler) + { + return { -1, -1, false }; + } + + return calculateSunTimes( + gpsHandler->getLatitude(), + gpsHandler->getLongitude(), + DateTimeManager::getYear(), + DateTimeManager::getMonth(), + DateTimeManager::getDay(), + config->system.timezoneOffset + ); +} + +SunTimes SunCalculator::calculateSunTimes(float lat, float lon, uint16_t year, uint8_t month, uint8_t day, int8_t tzOffset) +{ + // Calculate day of year + int32_t N = day + 31 * (month - 1); + + if (month > 2) + N -= (4 * month + 23) / 10; + + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) + N += (month > 2) ? 1 : 0; + + // Convert longitude to hour value + float lngHour = lon / 15.0; + + // Approximate sunrise time + float t_rise = N + ((6 - lngHour) / 24); + float t_set = N + ((18 - lngHour) / 24); + + // Sun's mean anomaly + float M_rise = (0.9856 * t_rise) - 3.289; + float M_set = (0.9856 * t_set) - 3.289; + + // Sun's true longitude + float L_rise = fmod(M_rise + (1.916 * sin(M_rise * PI / 180)) + + (0.020 * sin(2 * M_rise * PI / 180)) + 282.634, 360); + float L_set = fmod(M_set + (1.916 * sin(M_set * PI / 180)) + + (0.020 * sin(2 * M_set * PI / 180)) + 282.634, 360); + + // Sun's right ascension + float RA_rise = fmod(atan(0.91764 * tan(L_rise * PI / 180)) * 180 / PI, 360); + float RA_set = fmod(atan(0.91764 * tan(L_set * PI / 180)) * 180 / PI, 360); + + // Right ascension value needs to be in the same quadrant as L + float Lquadrant_rise = floor(L_rise / 90) * 90; + float RAquadrant_rise = floor(RA_rise / 90) * 90; + RA_rise = RA_rise + (Lquadrant_rise - RAquadrant_rise); + + float Lquadrant_set = floor(L_set / 90) * 90; + float RAquadrant_set = floor(RA_set / 90) * 90; + RA_set = RA_set + (Lquadrant_set - RAquadrant_set); + + // Convert to hours + RA_rise /= 15; + RA_set /= 15; + + // Calculate sun's declination + float sinDec_rise = 0.39782 * sin(L_rise * PI / 180); + float cosDec_rise = cos(asin(sinDec_rise)); + + float sinDec_set = 0.39782 * sin(L_set * PI / 180); + float cosDec_set = cos(asin(sinDec_set)); + + // Calculate sun's local hour angle + float cosH_rise = (cos(90.833 * PI / 180) - (sinDec_rise * sin(lat * PI / 180))) / + (cosDec_rise * cos(lat * PI / 180)); + float cosH_set = (cos(90.833 * PI / 180) - (sinDec_set * sin(lat * PI / 180))) / + (cosDec_set * cos(lat * PI / 180)); + + // Check if sun is always up or always down + if (cosH_rise > 1 || cosH_set > 1) + { + // Sun never rises (polar night) + return { -1, -1, false }; + } + + if (cosH_rise < -1 || cosH_set < -1) + { + // Sun never sets (midnight sun) + return { 0, 24, false }; + } + + // Finish calculating H and convert into hours + float H_rise = 360 - acos(cosH_rise) * 180 / PI; + float H_set = acos(cosH_set) * 180 / PI; + + H_rise /= 15; + H_set /= 15; + + // Calculate local mean time of rising/setting + float T_rise = H_rise + RA_rise - (0.06571 * t_rise) - 6.622; + float T_set = H_set + RA_set - (0.06571 * t_set) - 6.622; + + // Adjust back to UTC and then to local time + float UT_rise = fmod(T_rise - lngHour + 24, 24); + float UT_set = fmod(T_set - lngHour + 24, 24); + + return { UT_rise + tzOffset, UT_set + tzOffset, true }; +} \ No newline at end of file diff --git a/SmartFuseBox/SunCalculator.h b/SmartFuseBox/SunCalculator.h new file mode 100644 index 0000000..da24e71 --- /dev/null +++ b/SmartFuseBox/SunCalculator.h @@ -0,0 +1,35 @@ +/* + * SmartFuseBox + * Copyright (C) 2025 Simon Carter (s1cart3r@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "GpsSensorHandler.h" + +struct SunTimes { + float sunrise; // hours since midnight + float sunset; + bool isValid; // false if polar day/night +}; + +class SunCalculator +{ +private: + GpsSensorHandler* _gpsHandler; +public: + static SunTimes calculateSunTimes(GpsSensorHandler* gpsHandler); + static SunTimes calculateSunTimes(float lat, float lon, uint16_t year, uint8_t month, uint8_t day, int8_t tzOffset); +}; diff --git a/SmartFuseBox/SystemCommandHandler.cpp b/SmartFuseBox/SystemCommandHandler.cpp index 2490bba..ab81e6c 100644 --- a/SmartFuseBox/SystemCommandHandler.cpp +++ b/SmartFuseBox/SystemCommandHandler.cpp @@ -217,7 +217,6 @@ bool SystemCommandHandler::handleCommand(SerialCommandManager* sender, const cha } else if (SystemFunctions::commandMatches(command, SystemRtcDiagnostic)) { -#if defined(BOAT_CONTROL_PANEL) char diagnosticMsg[64]; bool success = DateTimeManager::rtcDiagnostic(diagnosticMsg, sizeof(diagnosticMsg)); @@ -233,7 +232,6 @@ bool SystemCommandHandler::handleCommand(SerialCommandManager* sender, const cha { sendAckErr(sender, command, param.value); } -#endif } else if (SystemFunctions::commandMatches(command, SystemUptime)) { diff --git a/SmartFuseBox/SystemDefinitions.h b/SmartFuseBox/SystemDefinitions.h index 654f5ed..b3c7a55 100644 --- a/SmartFuseBox/SystemDefinitions.h +++ b/SmartFuseBox/SystemDefinitions.h @@ -77,9 +77,9 @@ constexpr char ConfigResetSettings[] = "C2"; constexpr char ConfigRename[] = "C3"; constexpr char ConfigSpiPins[] = "C4"; constexpr char ConfigMapHomeButton[] = "C5"; -constexpr char ConfigSetButtonColor[] = "C6"; // Retired — use RelaySetButtonColor (R7) +constexpr char ConfigXpdzTonePin[] = "C6"; constexpr char ConfigBoatType[] = "C7"; -constexpr char ConfigSoundRelayId[] = "C8"; // Retired — use RelaySetActionType (R10) +constexpr char ConfigHw479RgbPins[] = "C8"; constexpr char ConfigSoundStartDelay[] = "C9"; constexpr char ConfigBluetoothEnable[] = "C10"; @@ -92,8 +92,8 @@ constexpr char ConfigWifiPort[] = "C15"; constexpr char ConfigWifiState[] = "C16"; constexpr char ConfigWifiApIpAddress[] = "C17"; -constexpr char ConfigDefaultRelayState[] = "C18"; // Retired — use RelaySetDefaultState (R8) -constexpr char ConfigLinkRelays[] = "C19"; // Retired — use RelayLink (R9) +constexpr char ConfigRtcPins[] = "C18"; +constexpr char ConfigNextion[] = "C19"; constexpr char ConfigTimeZoneOffset[] = "C20"; constexpr char ConfigMmsi[] = "C21"; constexpr char ConfigCallSign[] = "C22"; @@ -158,15 +158,24 @@ constexpr char SensorGpsSatellites[] = "S20"; constexpr char SensorGpsDistance[] = "S21"; constexpr char SensorBinaryPresence[] = "S22"; +constexpr char NextionGetConfig[] = "N0"; +constexpr char NextionEnabled[] = "N1"; +constexpr char NextionHardwareSerial[] = "N2"; +constexpr char NextionRxPin[] = "N3"; +constexpr char NextionTxPin[] = "N4"; +constexpr char NextionBaudRate[] = "N5"; +constexpr char NextionUartNum[] = "N6"; + +constexpr char ExternalSensorGetAll[] = "E0"; +constexpr char ExternalSensorSetCore[] = "E1"; +constexpr char ExternalSensorSetMqtt[] = "E2"; +constexpr char ExternalSensorRemove[] = "E3"; +constexpr char ExternalSensorRename[] = "E4"; constexpr char AckSuccess[] = "ok"; constexpr char ValueParamName[] = "v"; -constexpr uint64_t HeartbeatIntervalMs = 1000; -constexpr uint64_t HeartbeatTimeoutMs = 3000; - - constexpr uint8_t ConfigMaxLinkedRelays = 2; @@ -224,6 +233,8 @@ enum class SensorIdList : uint8_t GpsSensor = 0x3, SystemSensor = 0x4, BinaryPresenceSensor = 0x5, + + None = 0xFF }; struct CommandResult { diff --git a/SmartFuseBox/WarningManager.cpp b/SmartFuseBox/WarningManager.cpp index 81a460c..60cc7b9 100644 --- a/SmartFuseBox/WarningManager.cpp +++ b/SmartFuseBox/WarningManager.cpp @@ -18,59 +18,43 @@ #include "WarningManager.h" #include "SystemFunctions.h" -#if defined(BOAT_CONTROL_PANEL) #include "ToneManager.h" #include "DateTimeManager.h" -#endif // Define sensor warning mask (bits 20-31) constexpr uint32_t SENSOR_WARNING_MASK = 0xFFF00000U; // Bits 20-31 set -WarningManager::WarningManager(SerialCommandManager* commandMgr, uint64_t heartbeatInterval, - uint64_t heartbeatTimeout -#if defined(BOAT_CONTROL_PANEL) - , RgbLedFade* warningStatus, - ToneManager* toneManager -#endif - ) - - : _commandMgr(commandMgr), -#if defined(BOAT_CONTROL_PANEL) - _warningStatus(warningStatus), - _toneManager(toneManager), -#endif +WarningManager::WarningManager( + ) + : _warningStatus(nullptr), + _toneManager(nullptr), _localWarnings(0), - _remoteWarnings(0), -#if defined(BOAT_CONTROL_PANEL) _previousWarnings(0), - _lastTonePlayed(0), -#endif - _heartbeatInterval(heartbeatInterval), - _heartbeatTimeout(heartbeatTimeout), - _lastHeartbeatSent(0), - _lastHeartbeatReceived(0), - _heartbeatEnabled(heartbeatInterval > 0) + _lastTonePlayed(0) { -#if defined(BOAT_CONTROL_PANEL) updateLedStatus(); -#endif } -void WarningManager::update(uint64_t now) +void WarningManager::setWarningStatus(RgbLedFade* warningStatus) { - if (_heartbeatEnabled && _commandMgr) - { - updateConnection(now); - } + _warningStatus = warningStatus; + updateLedStatus(); +} -#if defined(BOAT_CONTROL_PANEL) +void WarningManager::setToneManager(ToneManager* toneManager) +{ + _toneManager = toneManager; +} + +void WarningManager::update(uint64_t now) +{ // Update LED status every loop iteration updateLedStatus(); // Handle warning tone alerts if (_toneManager) { - uint32_t currentWarnings = _localWarnings | _remoteWarnings; + uint32_t currentWarnings = _localWarnings; // Check if warnings are active if (currentWarnings != 0) @@ -111,15 +95,6 @@ void WarningManager::update(uint64_t now) _previousWarnings = currentWarnings; } -#endif -} - -void WarningManager::notifyHeartbeatAck() -{ - _lastHeartbeatReceived = SystemFunctions::millis64(); - - // Clear the connection lost warning (connection is now established) - clearWarning(WarningType::ConnectionLost); } void WarningManager::raiseWarning(WarningType type) @@ -128,32 +103,15 @@ void WarningManager::raiseWarning(WarningType type) return; uint32_t warningBit = static_cast(type); - bool wasActive = (_localWarnings & warningBit) != 0; _localWarnings |= warningBit; - // Broadcast warning change if it's new - if (!wasActive) - { - broadcastWarningChange(type, true); - } - // Auto-raise SensorFailure for any sensor-related warning (bit 20+) if (warningBit & SENSOR_WARNING_MASK) { - uint32_t sensorFailureBit = static_cast(WarningType::SensorFailure); - bool sensorFailureWasActive = (_localWarnings & sensorFailureBit) != 0; - _localWarnings |= sensorFailureBit; - - // Broadcast SensorFailure if it wasn't already active - if (!sensorFailureWasActive) - { - broadcastWarningChange(WarningType::SensorFailure, true); - } + _localWarnings |= static_cast(WarningType::SensorFailure); } -#if defined(BOAT_CONTROL_PANEL) updateLedStatus(); -#endif } void WarningManager::clearWarning(WarningType type) @@ -162,180 +120,52 @@ void WarningManager::clearWarning(WarningType type) return; uint32_t warningBit = static_cast(type); - bool wasActive = (_localWarnings & warningBit) != 0; _localWarnings &= ~warningBit; - // Broadcast warning change if it was active - if (wasActive) - { - broadcastWarningChange(type, false); - } - // Auto-clear SensorFailure only if no sensor warnings remain (check bits 20+) if (warningBit & SENSOR_WARNING_MASK) { - // Check if any other sensor warnings are still active (excluding SensorFailure itself) uint32_t otherSensorWarnings = _localWarnings & SENSOR_WARNING_MASK & ~static_cast(WarningType::SensorFailure); - + if (otherSensorWarnings == 0) { - uint32_t sensorFailureBit = static_cast(WarningType::SensorFailure); - bool sensorFailureWasActive = (_localWarnings & sensorFailureBit) != 0; - _localWarnings &= ~sensorFailureBit; - - // Broadcast SensorFailure clear if it was active - if (sensorFailureWasActive) - { - broadcastWarningChange(WarningType::SensorFailure, false); - } + _localWarnings &= ~static_cast(WarningType::SensorFailure); } } -#if defined(BOAT_CONTROL_PANEL) updateLedStatus(); -#endif } void WarningManager::clearAllWarnings() { _localWarnings = 0; - _remoteWarnings = 0; -#if defined(BOAT_CONTROL_PANEL) updateLedStatus(); -#endif } bool WarningManager::hasWarnings() const { - return (_localWarnings | _remoteWarnings) != 0; + return _localWarnings != 0; } bool WarningManager::isWarningActive(WarningType type) const { if (type == WarningType::None) return false; - - uint32_t warningBit = static_cast(type); - // Warning is active if it's in EITHER local or remote - return ((_localWarnings | _remoteWarnings) & warningBit) != 0; -} - -void WarningManager::sendHeartbeat() -{ - if (_commandMgr) - { - // Include local warnings in heartbeat - char params[64]; - -#if defined(BOAT_CONTROL_PANEL) - snprintf_P(params, sizeof(params), PSTR("w=%s%lx;t=%lu"), - HexPrefix, _localWarnings, DateTimeManager::getCurrentTime()); -#else - snprintf_P(params, sizeof(params), PSTR("w=%s%lx"), - HexPrefix, _localWarnings); -#endif - _commandMgr->sendCommand(SystemHeartbeatCommand, params); - } -} -void WarningManager::updateConnection(uint64_t now) -{ - // Send heartbeat if interval elapsed - if (now - _lastHeartbeatSent >= _heartbeatInterval) - { - sendHeartbeat(); - _lastHeartbeatSent = now; - } - - // Check for timeout (only after we've sent at least one heartbeat) - if (_lastHeartbeatSent > 0 || now >= _heartbeatTimeout) - { - // Protect against race condition: if lastHeartbeatReceived is in the future - // (due to notifyHeartbeatAck calling SystemFunctions::millis64() after we captured 'now'), - // treat it as just received (delta = 0) - uint64_t delta = (_lastHeartbeatReceived > now) ? 0 : (now - _lastHeartbeatReceived); - - bool connected = (_lastHeartbeatReceived > 0) && (delta < _heartbeatTimeout); - - bool wasConnected = !isWarningActive(WarningType::ConnectionLost); - - // Only update warning state if connection status changed - if (connected && !wasConnected) - { - clearWarning(WarningType::ConnectionLost); - -#if defined(BOAT_CONTROL_PANEL) - // Send time sync when connection is established - if (_commandMgr && DateTimeManager::isTimeSet()) - { - char buffer[20]; - snprintf_P(buffer, sizeof(buffer), PSTR("v=%lu"), DateTimeManager::getCurrentTime()); - _commandMgr->sendCommand(SystemSetDateTime, buffer); - } -#endif - } - else if (!connected && wasConnected) - { - raiseWarning(WarningType::ConnectionLost); - _remoteWarnings = 0; // Clear remote warnings on connection loss - } - } + uint32_t warningBit = static_cast(type); + return (_localWarnings & warningBit) != 0; } uint32_t WarningManager::getActiveWarningsMask() const -{ - // Return combined view of local and remote warnings - return _localWarnings | _remoteWarnings; -} - -uint32_t WarningManager::getLocalWarningsMask() const { return _localWarnings; } -uint32_t WarningManager::getRemoteWarningsMask() const -{ - return _remoteWarnings; -} - -void WarningManager::updateRemoteWarnings(uint32_t remoteWarningMask) -{ - _remoteWarnings = remoteWarningMask; - -#if defined(BOAT_CONTROL_PANEL) - updateLedStatus(); -#endif -} - -void WarningManager::broadcastWarningChange(WarningType type, bool isActive) -{ - if (!_commandMgr || type == WarningType::None) - return; - - // Format warning type as hex string (e.g., "0x800") - char warningHex[12]; - uint32_t warningValue = static_cast(type); - snprintf_P(warningHex, sizeof(warningHex), PSTR("%s%lx"), HexPrefix, warningValue); - - // Format as W2:=<0|1> - char params[32]; - snprintf_P(params, sizeof(params), PSTR("%s=%d"), warningHex, isActive ? 1 : 0); - - // Send W2 command via LINK - _commandMgr->sendCommand(WarningStatus, params); -} - -#if defined(BOAT_CONTROL_PANEL) - void WarningManager::updateLedStatus() { if (!_warningStatus) return; - uint32_t allWarnings = _localWarnings | _remoteWarnings; - - // Set warning state based on active warnings - _warningStatus->setWarning(allWarnings != 0); + _warningStatus->setWarning(_localWarnings != 0); } -#endif \ No newline at end of file diff --git a/SmartFuseBox/WarningManager.h b/SmartFuseBox/WarningManager.h index 92e41b0..7a85a75 100644 --- a/SmartFuseBox/WarningManager.h +++ b/SmartFuseBox/WarningManager.h @@ -18,41 +18,30 @@ #pragma once #include -#include #include #include "Local.h" #include "SystemDefinitions.h" #include "WarningType.h" -#if defined(BOAT_CONTROL_PANEL) #include "RgbLedFade.h" class ToneManager; -#endif /** * @class WarningManager - * @brief Simple, centralized warning management with built-in heartbeat monitoring. + * @brief Simple, centralized warning management. * - * This class manages all system warnings including connection heartbeat monitoring. - * Warnings can be raised/cleared from anywhere in the code. The heartbeat functionality - * is built-in and automatically manages the ConnectionLost warning. + * This class manages all system warnings. + * Warnings can be raised/cleared from anywhere in the code. * * Features: * - Bitmap-based warning tracking using bit flags (supports up to 32 warnings) - * - Built-in heartbeat monitoring with automatic F0 command transmission * - Extensible WarningType enum for adding new warnings * - Query methods to check active warnings * * Usage: * @code - * WarningManager warningMgr(&commandMgrLink); - * - * // In loop or refresh: - * warningMgr.update(SystemFunctions::millis64()); - * - * // When ACK:F0=ok received: - * warningMgr.notifyHeartbeatAck(); + * WarningManager warningMgr; * * // Raise warnings from anywhere: * warningMgr.raiseWarning(WarningType::HighCompassTemperature); @@ -67,77 +56,32 @@ class ToneManager; class WarningManager { private: - SerialCommandManager* _commandMgr; // For sending heartbeat commands -#if defined(BOAT_CONTROL_PANEL) RgbLedFade* _warningStatus; // LED indicator for warnings ToneManager* _toneManager; // Sound alert manager -#endif uint32_t _localWarnings; // Bitmap of LOCAL warnings (raised by this device) - uint32_t _remoteWarnings; // Bitmap of REMOTE warnings (from connected device) -#if defined(BOAT_CONTROL_PANEL) uint32_t _previousWarnings; // Previous warning state for change detection - uint64_t _lastTonePlayed; // Last time bad tone was played -#endif - - // Heartbeat state - uint64_t _heartbeatInterval; // How often to send heartbeat (ms) - uint64_t _heartbeatTimeout; // Timeout before connection lost (ms) - uint64_t _lastHeartbeatSent; // When last heartbeat was sent - uint64_t _lastHeartbeatReceived; // When last ack was received - bool _heartbeatEnabled; // Is heartbeat active - - /** - * @brief Send a heartbeat command. - */ - void sendHeartbeat(); - - /** - * @brief Update connection state based on heartbeat. - * @param now Current time in milliseconds - */ - void updateConnection(uint64_t now); + uint64_t _lastTonePlayed; // Last time bad tone was played - /** - * @brief Broadcast warning change to connected device via LINK. - * Sends W2 command with warning status. - * @param type The warning type that changed - * @param isActive True if warning is now active, false if cleared - */ - void broadcastWarningChange(WarningType type, bool isActive); - -#if defined(BOAT_CONTROL_PANEL) - void updateLedStatus(); -#endif + void updateLedStatus(); public: - /** - * @brief Constructor. - * @param ledManager Pointer to LedMatrixManager for updating warning indicators - * @param commandMgr Pointer to SerialCommandManager for heartbeat commands (optional) - * @param heartbeatInterval How often to send heartbeat in milliseconds - * @param heartbeatTimeout Timeout before connection considered lost in milliseconds + /** + * @brief Constructor. * @param warningStatus Pointer to RgbLedFade for warning status LED - */ - explicit WarningManager(SerialCommandManager* commandMgr, uint64_t heartbeatInterval, - uint64_t heartbeatTimeout -#if defined(BOAT_CONTROL_PANEL) - , RgbLedFade* warningStatus, ToneManager* toneManager = nullptr); -#else - ); -#endif + */ + explicit WarningManager(); + + // Set runtime-owned LED indicator (can be attached after construction) + void setWarningStatus(RgbLedFade* warningStatus); + + // Set runtime-owned ToneManager (can be attached after construction) + void setToneManager(ToneManager* toneManager); /** - * @brief Update heartbeat and check for timeouts. - * Call this periodically (e.g., from refresh()). + * @brief Update warning state. * @param now Current time in milliseconds (SystemFunctions::millis64()) */ void update(uint64_t now); - /** - * @brief Notify that a heartbeat acknowledgement was received. - * Call this when ACK:F0=ok is received. - */ - void notifyHeartbeatAck(); - /** * @brief Raise (activate) a warning. * @param type The warning type to activate @@ -169,27 +113,8 @@ class WarningManager bool isWarningActive(WarningType type) const; /** - * @brief Get the complete bitmask of all active warnings (local + remote). + * @brief Get the bitmask of all active warnings. * @return uint32_t containing all active warning flags */ uint32_t getActiveWarningsMask() const; - - /** - * @brief Update remote warning bitmask from connected device. - * This replaces (not merges) the remote warnings with the new state. - * @param remoteWarningMask Bitmask of warnings from remote device - */ - void updateRemoteWarnings(uint32_t remoteWarningMask); - - /** - * @brief Get only the local warning bitmask. - * @return uint32_t containing only locally-raised warning flags - */ - uint32_t getLocalWarningsMask() const; - - /** - * @brief Get only the remote warning bitmask. - * @return uint32_t containing only remotely-raised warning flags - */ - uint32_t getRemoteWarningsMask() const; }; diff --git a/SmartFuseBox/WarningType.h b/SmartFuseBox/WarningType.h index f72e8c5..44e314c 100644 --- a/SmartFuseBox/WarningType.h +++ b/SmartFuseBox/WarningType.h @@ -38,7 +38,7 @@ enum class WarningType : uint32_t // System warnings (bits 0-19) DefaultConfigurationFuseBox = 1UL << 0, // 0x00000001 - Using default config DefaultConfigurationControlPanel = 1UL << 1, // 0x00000002 - Using default config - ConnectionLost = 1UL << 2, // 0x00000004 - Link heartbeat lost + ConnectionLost = 1UL << 2, // 0x00000004 - Connection lost warning HighCompassTemperature = 1UL << 3, // 0x00000008 - Compass temperature threshold exceeded LowBattery = 1UL << 4, // 0x00000010 - Battery voltage low BluetoothInitFailed = 1UL << 5, // 0x00000020 - Bluetooth initialization failed @@ -50,6 +50,7 @@ enum class WarningType : uint32_t SdCardError = 1UL << 11, // 0x00000800 - SD card read/write error SdCardMissing = 1UL << 12, // 0x00001000 - SD card not detected SdCardLowSpace = 1UL << 13, // 0x00002000 - SD card free space below 10% + NextionInvalidConfig = 1UL << 14, // 0x00004000 - Nextion display configuration invalid // Sensor warnings (bits 20+) SensorFailure = 1UL << 20, // 0x00100000 - Sensor communication failure @@ -74,7 +75,7 @@ static const char WT_10[] PROGMEM = "SPI Pin Config Error"; static const char WT_11[] PROGMEM = "SD Card Error"; static const char WT_12[] PROGMEM = "SD Card Not Found"; static const char WT_13[] PROGMEM = "SD Card Low Space"; -static const char WT_14[] PROGMEM = ""; +static const char WT_14[] PROGMEM = "Nextion Invalid Config"; static const char WT_15[] PROGMEM = ""; static const char WT_16[] PROGMEM = ""; static const char WT_17[] PROGMEM = ""; diff --git a/TestData/test-commands-wifi.txt b/TestData/test-commands-wifi.txt index e99fc43..93e17ec 100644 --- a/TestData/test-commands-wifi.txt +++ b/TestData/test-commands-wifi.txt @@ -308,3 +308,122 @@ POST /api/config/C26 v=0 -> {"success":true}|{*} POST /api/config/C27 g=1;w=1;s=1 -> {"success":true}|{*} POST /api/config/C27 g=0;w=0;s=0 -> {"success":true}|{*} + +# ============================================================ +# XPDZ TONE PIN CONFIGURATION (C6) +# ============================================================ + +# Set XpdzTone pin to a valid GPIO +POST /api/config/C6 v=25 -> {"success":true}|{*} +POST /api/config/C6 v=26 -> {"success":true}|{*} + +# Set XpdzTone pin to PinDisabled (255 = not fitted) +POST /api/config/C6 v=255 -> {"success":true}|{*} + +# Missing parameter +POST /api/config/C6 -> {"error":*} + +# ============================================================ +# HW479 RGB LED PIN CONFIGURATION (C8) +# ============================================================ + +# Set all three RGB pins +POST /api/config/C8 r=25;g=26;b=27 -> {"success":true}|{*} +POST /api/config/C8 r=16;g=17;b=18 -> {"success":true}|{*} + +# Disable all pins (PinDisabled = 255) +POST /api/config/C8 r=255;g=255;b=255 -> {"success":true}|{*} + +# Mixed: some disabled +POST /api/config/C8 r=25;g=255;b=255 -> {"success":true}|{*} + +# Missing parameter(s) +POST /api/config/C8 r=25;g=26 -> {"error":*} + +# ============================================================ +# RTC DS1302 PIN CONFIGURATION (C18) +# ============================================================ + +# Set all three DS1302 pins +POST /api/config/C18 dat=4;clk=5;rst=6 -> {"success":true}|{*} +POST /api/config/C18 dat=14;clk=15;rst=16 -> {"success":true}|{*} + +# Disable all pins (PinDisabled = 255) +POST /api/config/C18 dat=255;clk=255;rst=255 -> {"success":true}|{*} + +# Missing parameter(s) +POST /api/config/C18 dat=4;clk=5 -> {"error":*} + +# ============================================================ +# NEXTION DISPLAY CONFIGURATION (N0-N6) via /api/config +# ============================================================ + +# N0: Get all Nextion config as JSON object +GET /api/config/N0 -> {*} + +# N1: Enable/disable Nextion display +POST /api/config/N1 v=1 -> {"success":true}|{*} +POST /api/config/N1 v=0 -> {"success":true}|{*} +POST /api/config/N1 v=true -> {"success":true}|{*} +POST /api/config/N1 v=false -> {"success":true}|{*} + +# N2: Hardware serial vs software serial +POST /api/config/N2 v=1 -> {"success":true}|{*} +POST /api/config/N2 v=0 -> {"success":true}|{*} + +# N3: RX pin +POST /api/config/N3 v=16 -> {"success":true}|{*} +POST /api/config/N3 v=255 -> {"success":true}|{*} + +# N4: TX pin +POST /api/config/N4 v=17 -> {"success":true}|{*} +POST /api/config/N4 v=255 -> {"success":true}|{*} + +# N5: Baud rate +POST /api/config/N5 v=9600 -> {"success":true}|{*} +POST /api/config/N5 v=115200 -> {"success":true}|{*} +POST /api/config/N5 v=921600 -> {"success":true}|{*} + +# N6: UART number (1 or 2 only) +POST /api/config/N6 v=1 -> {"success":true}|{*} +POST /api/config/N6 v=2 -> {"success":true}|{*} + +# Invalid UART number +POST /api/config/N6 v=0 -> {"error":*} +POST /api/config/N6 v=3 -> {"error":*} + +# ============================================================ +# EXTERNAL / REMOTE SENSOR CONFIGURATION (E0-E4) via /api/externalsensor +# ============================================================ + +# E0: Get all external sensor config (GET returns JSON array) +GET /api/externalsensor -> {"success":true,"count":*,"sensors":*} +GET /api/externalsensor/E0 -> {"success":true,"count":*,"sensors":*} + +# E1: Set core fields for a new external sensor +POST /api/externalsensor/E1 i=0;id=7;n=TempSensor;mn=temperature;ms=temp -> {"success":true}|{*} +POST /api/externalsensor/E1 i=1;id=8;n=HumSensor;mn=humidity;ms=hum -> {"success":true}|{*} + +# E2: Set MQTT detail fields (sensor must exist via E1 first) +POST /api/externalsensor/E2 i=0;mt=sensor;md=temperature;mu=C;bin=0 -> {"success":true}|{*} +POST /api/externalsensor/E2 i=1;mt=sensor;md=humidity;mu=%;bin=0 -> {"success":true}|{*} + +# E2: Binary sensor +POST /api/externalsensor/E2 i=0;mt=binary_sensor;md=motion;mu=;bin=1 -> {"success":true}|{*} + +# E4: Rename an existing sensor +POST /api/externalsensor/E4 0=Outside Temp -> {"success":true}|{*} + +# E3: Remove sensors +POST /api/externalsensor/E3 1 -> {"success":true}|{*} +POST /api/externalsensor/E3 0 -> {"success":true}|{*} + +# Error cases - invalid sensor index +POST /api/externalsensor/E1 i=8;id=7;n=TooMany;mn=x;ms=x -> {"error":*} +POST /api/externalsensor/E2 i=8;mt=sensor;md=temperature;mu=C;bin=0 -> {"error":*} +POST /api/externalsensor/E3 8 -> {"error":*} +POST /api/externalsensor/E4 8=NewName -> {"error":*} + +# Error cases - missing parameters +POST /api/externalsensor/E1 i=0 -> {"error":*} +POST /api/externalsensor/E2 -> {"error":*} diff --git a/TestData/test-commands.txt b/TestData/test-commands.txt index 71740ae..6c351c8 100644 --- a/TestData/test-commands.txt +++ b/TestData/test-commands.txt @@ -685,3 +685,151 @@ R7:6=255 -> ACK:R7=ok:* # Invalid color R7:7=6 -> ACK:R7=Invalid color:* + +# ============================================================ +# XPDZ TONE PIN CONFIGURATION (C6) +# ============================================================ + +# Set XpdzTone pin to a valid GPIO +C6:v=25 -> ACK:C6=ok:* +C6:v=26 -> ACK:C6=ok:* + +# Set XpdzTone pin to PinDisabled (255 = not fitted) +C6:v=255 -> ACK:C6=ok:* + +# Missing parameter +C6 -> ACK:C6=Invalid parameter:* + +# Verify C1 includes C6 +C1 -> C* + +# ============================================================ +# HW479 RGB LED PIN CONFIGURATION (C8) +# ============================================================ + +# Set all three RGB pins +C8:r=25;g=26;b=27 -> ACK:C8=ok:* +C8:r=16;g=17;b=18 -> ACK:C8=ok:* + +# Disable all pins (PinDisabled = 255) +C8:r=255;g=255;b=255 -> ACK:C8=ok:* + +# Mixed: some disabled, some active +C8:r=25;g=255;b=255 -> ACK:C8=ok:* + +# Missing parameter(s) +C8:r=25;g=26 -> ACK:C8=Invalid parameter:* +C8 -> ACK:C8=Invalid parameter:* + +# ============================================================ +# RTC DS1302 PIN CONFIGURATION (C18) +# ============================================================ + +# Set all three DS1302 pins +C18:dat=4;clk=5;rst=6 -> ACK:C18=ok:* +C18:dat=14;clk=15;rst=16 -> ACK:C18=ok:* + +# Disable all pins (PinDisabled = 255) +C18:dat=255;clk=255;rst=255 -> ACK:C18=ok:* + +# Mixed: some disabled +C18:dat=4;clk=255;rst=255 -> ACK:C18=ok:* + +# Missing parameter(s) +C18:dat=4;clk=5 -> ACK:C18=Invalid parameter:* +C18 -> ACK:C18=Invalid parameter:* + +# ============================================================ +# NEXTION DISPLAY CONFIGURATION (N0-N6) +# ============================================================ + +# Get all Nextion config (returns N1-N6 values then ACK) +N0 -> ACK:N0=ok + +# N1: Enable/disable Nextion display +N1:v=1 -> ACK:N1=ok:* +N1:v=0 -> ACK:N1=ok:* +N1:v=true -> ACK:N1=ok:* +N1:v=false -> ACK:N1=ok:* + +# Missing parameter +N1 -> ACK:N1=Invalid parameter:* + +# N2: Hardware serial vs software serial +N2:v=1 -> ACK:N2=ok:* +N2:v=0 -> ACK:N2=ok:* + +# Missing parameter +N2 -> ACK:N2=Invalid parameter:* + +# N3: RX pin (255 = PinDisabled) +N3:v=16 -> ACK:N3=ok:* +N3:v=255 -> ACK:N3=ok:* + +# Missing parameter +N3 -> ACK:N3=Invalid parameter:* + +# N4: TX pin (255 = PinDisabled) +N4:v=17 -> ACK:N4=ok:* +N4:v=255 -> ACK:N4=ok:* + +# Missing parameter +N4 -> ACK:N4=Invalid parameter:* + +# N5: Baud rate (common values) +N5:v=9600 -> ACK:N5=ok:* +N5:v=115200 -> ACK:N5=ok:* +N5:v=921600 -> ACK:N5=ok:* + +# Missing parameter +N5 -> ACK:N5=Invalid parameter:* + +# N6: UART number (only 1 or 2 are valid; UART0 reserved) +N6:v=1 -> ACK:N6=ok:* +N6:v=2 -> ACK:N6=ok:* + +# Invalid UART number +N6:v=0 -> ACK:N6=Invalid parameter:* +N6:v=3 -> ACK:N6=Invalid parameter:* + +# Missing parameter +N6 -> ACK:N6=Invalid parameter:* + +# ============================================================ +# EXTERNAL / REMOTE SENSOR CONFIGURATION (E0-E4) +# ============================================================ + +# E0: Get all external sensor config entries +E0 -> ACK:E0=ok:* + +# E1: Set core fields for a new external sensor +E1:i=0;id=7;n=TempSensor;mn=temperature;ms=temp -> ACK:E1=ok:reboot=1 + +# E1: Add a second sensor +E1:i=1;id=8;n=HumSensor;mn=humidity;ms=hum -> ACK:E1=ok:reboot=1 + +# E2: Set MQTT detail fields (sensor must exist first via E1) +E2:i=0;mt=sensor;md=temperature;mu=C;bin=0 -> ACK:E2=ok:reboot=1 +E2:i=1;mt=sensor;md=humidity;mu=%;bin=0 -> ACK:E2=ok:reboot=1 + +# E2: Binary sensor +E2:i=0;mt=binary_sensor;md=motion;mu=;bin=1 -> ACK:E2=ok:reboot=1 + +# E4: Rename an existing sensor +E4:0=Outside Temp -> ACK:E4=ok:reboot=1 + +# E3: Remove sensor at index 1 +E3:1 -> ACK:E3=ok:reboot=1 + +# E3: Remove sensor at index 0 +E3:0 -> ACK:E3=ok:reboot=1 + +# Error cases - invalid sensor index +E1:i=8;id=7;n=TooMany;mn=x;ms=x -> ACK:E1=Invalid sensor index:*|ACK:E1=Invalid parameter:* +E2:i=8;mt=sensor;md=temperature;mu=C;bin=0 -> ACK:E2=Invalid sensor index:*|ACK:E2=Invalid parameter:* +E3:8 -> ACK:E3=Invalid sensor index:* +E4:8=NewName -> ACK:E4=Invalid sensor index:* + +# Error cases - missing parameters +E1:i=0 -> ACK:E1=Invalid or missing parameters:*|ACK:E1=Invalid parameter:* +E2 -> ACK:E2=Invalid or missing parameters:*|ACK:E2=Invalid parameter:*