diff --git a/docs/API.md b/docs/API.md index 52d9a12..9600357 100644 --- a/docs/API.md +++ b/docs/API.md @@ -233,3 +233,308 @@ text 0.3.x Future firmware versions may add fields while maintaining backward compatibility. Consumers should ignore unknown fields. + +--- + +## Embedded Dashboard + +The ESP32 is planned to serve a lightweight mobile dashboard at: + + GET / + +When connected directly to the ESP32 AP: + + http://192.168.4.1/ + +The page should: + +- Poll /status +- Display battery state +- Display temperature sensors +- Display relay states +- Show alarms +- Allow basic relay control + +Design limits: + +- Small inline HTML/CSS/JS +- No external libraries +- No large assets +- No heavy JavaScript framework + +The embedded dashboard is intended as a convenient phone view, not a replacement for the physical Pico dashboard. + +--- + +## Future WiFi API + +Future configuration may include WiFi management endpoints. + +Possible future endpoints: + + GET /config/wifi + POST /config/wifi + POST /config/wifi/add + POST /config/wifi/remove + POST /config/wifi/reorder + +Potential config model: + + ap_enabled + sta_enabled + hostname + saved_networks + +Saved networks should support priority order. + +Example: + + Starlink + Home WiFi + Shop WiFi + +The AP should remain enabled even when connected to STA WiFi. + +--- + +# HTTP / UART Parity Endpoints + +These endpoints mirror UART setup/control messages. + +## Save Config + + POST /config/save + +Equivalent UART message: + + {"type":"save_config"} + +## BMS Setup Mode + +Enter BMS setup mode: + + POST /bms/setup/enter + +Equivalent UART message: + + {"type":"enter_bms_setup"} + +Exit BMS setup mode: + + POST /bms/setup/exit + +Equivalent UART message: + + {"type":"exit_bms_setup"} + +## BLE Scan + +Scan for BLE devices: + + POST /bms/scan + +Equivalent UART message: + + {"type":"scan_ble"} + +Response: + + { + "type": "ble_scan_response", + "devices": [ + { + "index": 1, + "name": "House Battery", + "address": "aa:bb:cc:dd:ee:ff", + "rssi": -65 + } + ] + } + +## Select BMS + +Select a BMS from the most recent scan: + + POST /bms/select + +Request body: + + { + "index": 1 + } + +Equivalent UART message: + + {"type":"select_bms","index":1} + +--- + +# Preferred Relay Command Endpoint + +The preferred relay control endpoint is: + + POST /relay/set + +Request body: + + { + "id": "relay_1", + "state": true + } + +Example: + + curl -X POST http://192.168.88.108/relay/set \ + -H "Content-Type: application/json" \ + -d '{"id":"relay_1","state":true}' + +Equivalent UART message: + + {"type":"set_relay","relay":"relay_1","enabled":true} + +Legacy GET relay routes may remain for compatibility: + + GET /relay/relay_1/on + GET /relay/relay_1/off + GET /relay/relay_2/on + GET /relay/relay_2/off + +--- + +# WiFi Configuration API + +## Get WiFi Config + + GET /config/wifi + +## Set WiFi Config + + POST /config/wifi + +Request body: + + { + "ssid": "Starlink", + "password": "password_here" + } + +## Connect STA WiFi + + POST /wifi/connect + +## Clear WiFi Config + + POST /wifi/clear + +The ESP32 AP remains available even when STA WiFi is configured. + +Equivalent UART messages: + + {"type":"wifi_request"} + + {"type":"config_wifi","ssid":"Starlink","password":"password_here"} + + {"type":"wifi_connect"} + + {"type":"wifi_clear"} + +--- + +# WiFi Priority and Runtime Failover + +Saved WiFi networks support priority. + +Lower priority numbers are attempted first. + +Example: + + { + "networks": [ + { + "ssid": "Starlink", + "password": "starlink_password", + "priority": 1 + }, + { + "ssid": "Home WiFi", + "password": "home_password", + "priority": 2 + } + ] + } + +The ESP32 behavior is: + + AP always stays enabled + STA tries saved networks by priority + If disconnected at runtime, STA retries saved networks every 30 seconds + If all STA networks fail, AP remains available at 192.168.4.1 + + +--- + +# API Parity Notes + +The HTTP API is intended to mirror the UART JSON protocol where practical. + +## Preferred Relay Control + +Preferred endpoint: + + POST /relay/set + +Request: + + { + "id": "relay_1", + "state": true + } + +Response: + + { + "type": "relay_response", + "ok": true, + "id": "relay_1", + "state": true + } + +Legacy relay routes may remain available for compatibility: + + GET /relay/relay_1/on + GET /relay/relay_1/off + GET /relay/relay_2/on + GET /relay/relay_2/off + +## WiFi Parity + +WiFi configuration supports multiple saved STA networks. + +Request: + + POST /config/wifi + +Body: + + { + "networks": [ + { + "ssid": "Starlink", + "password": "starlink_password", + "priority": 1 + }, + { + "ssid": "Home WiFi", + "password": "home_password", + "priority": 2 + } + ] + } + +Lower priority numbers are attempted first. + +Runtime behavior: + + AP always remains enabled. + STA tries saved networks by priority. + If STA disconnects, saved networks are retried every 30 seconds. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 59f7fa9..4d96b63 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -233,3 +233,99 @@ Possible future integrations: - OTA updates All future integrations should consume the same generic status/config model. + +--- + +## Networking Architecture + +### Current Network Mode + +The ESP32 currently runs in Access Point mode. + +Default access: + + http://192.168.4.1 + +The AP is the recovery/setup network. + +### Planned Network Mode + +The planned long-term network mode is AP + STA. + +AP mode: + + Always available + Used for recovery and direct setup + +STA mode: + + Connects to a configured WiFi network + Used for camp/home access from phone or laptop + +Example networks: + + Starlink + Home WiFi + Shop WiFi + +### Multiple WiFi Profiles + +The ESP32 should eventually store multiple WiFi profiles and try them in priority order. + +Example: + + 1. Starlink + 2. Home WiFi + 3. Shop WiFi + +Expected behavior: + + Start AP first + Try configured STA networks + Connect to the first available network + Keep AP available even after STA connects + +Reason: + +- Starlink may be off +- Home WiFi may only be available in the driveway +- AP must remain available for recovery +- User should not lose access because of a bad WiFi password + +### Phone Dashboard Access + +When connected to the ESP32 AP: + + http://192.168.4.1 + +When connected through Starlink/home WiFi: + + http://controller-ip + +Future mDNS target: + + http://overland-controller.local + +### Dashboard Roles + +ESP32: + + Source of truth + Lightweight phone dashboard + HTTP API + Recovery AP + +Pico 2 W: + + Instant physical dashboard + UART client + Touchscreen UI + +Optional future Pi Zero: + + Advanced web dashboard + Logging + Charts + Integrations + +The Pi Zero should be optional, not required for core operation. diff --git a/docs/PARITY_MATRIX.md b/docs/PARITY_MATRIX.md new file mode 100644 index 0000000..37f3a14 --- /dev/null +++ b/docs/PARITY_MATRIX.md @@ -0,0 +1,81 @@ +# Command Parity Matrix + +This document tracks feature parity across the three control surfaces. + +Control surfaces: + +- USB serial console +- UART JSON protocol +- HTTP API + +## Current Parity + +| Feature | USB Serial | UART JSON | HTTP API | Notes | +|---|---:|---:|---:|---| +| Status | Yes | Yes | Yes | `/status` equals `status_request` | +| Config view | Yes | Yes | Yes | `/config` equals `config_request` | +| Relay control | Yes | Yes | Yes | Preferred HTTP endpoint is `POST /relay/set` | +| Device name config | Yes | Yes | Yes | `devicename`, `config_device`, `/config/device` | +| Relay config | Yes | Yes | Yes | `relayname`, `config_relay`, `/config/relay` | +| Temperature config | Yes | Yes | Yes | `tempname`, `config_temp`, `/config/temp` | +| BMS config | Yes | Yes | Yes | `bmsname`/`bmsaddr`, `config_bms`, `/config/bms` | +| Save config | Yes | Yes | Yes | `save`, `save_config`, `/config/save` | +| Factory reset | Yes | Yes | Yes | AP remains recovery path | +| BMS setup enter | Yes | Yes | Yes | Setup pauses normal BMS reconnect behavior | +| BMS setup exit | Yes | Yes | Yes | Returns to normal mode | +| BLE scan | Yes | Yes | Yes | Some BMS devices require repeated scans | +| Select BMS | Yes | Yes | Yes | Uses most recent scan result index | +| WiFi config | Yes | Yes | Yes | Supports multiple saved STA networks | +| WiFi priority | Yes | Yes | Yes | Lower priority number is tried first | +| WiFi connect | Yes | Yes | Yes | AP remains available | +| WiFi clear | Yes | Yes | Yes | Clears saved STA networks | + +## Preferred Command Model + +Use generic IDs everywhere. + +Relays: + + relay_1 + relay_2 + +Temperature sensors: + + temp_1 through temp_8 + +Preferred relay command shape: + + { + "type": "set_relay", + "id": "relay_1", + "state": true + } + +Legacy UART relay fields may still be accepted: + + relay + enabled + +Preferred HTTP relay endpoint: + + POST /relay/set + +Legacy HTTP relay routes may remain for compatibility: + + GET /relay/relay_1/on + GET /relay/relay_1/off + GET /relay/relay_2/on + GET /relay/relay_2/off + +## Response Shape Standard + +Success responses should include: + + ok: true + +Error responses should include: + + ok: false + error: "error_code" + +Status/config responses should preserve the generic data model used by `/status`. diff --git a/docs/README.md b/docs/README.md index d6b1cc4..17a4bad 100644 --- a/docs/README.md +++ b/docs/README.md @@ -27,3 +27,8 @@ This folder contains project documentation for Overland Controller. Older or duplicate documents have been moved to `archive/`. Archived documents are kept for history, but the primary documents above should be treated as the current source of truth. + + +## Parity + +- `PARITY_MATRIX.md` - Command parity across USB serial, UART JSON, and HTTP API diff --git a/docs/SERIAL_COMMANDS.md b/docs/SERIAL_COMMANDS.md index 8bcad41..f038891 100644 --- a/docs/SERIAL_COMMANDS.md +++ b/docs/SERIAL_COMMANDS.md @@ -364,3 +364,53 @@ Returns complete runtime status including: - Alarms - Network state - System information + + +--- + +# WiFi Serial Commands + +## wifi status + +Print current WiFi status. + +## wifi list + +Print saved WiFi networks, priorities, and whether passwords are set. + +## wifi clear + +Clear saved STA WiFi networks. + +## wifi ssid + +Set primary WiFi SSID. + +## wifi pass + +Set primary WiFi password. + +## wifi add || + +Add a saved WiFi network. + +Example: + + wifi add Starlink|starlink_password|1 + wifi add WardAP|Ward5213|2 + wifi save + wifi connect + +Lower priority numbers are tried first. + +## wifi save + +Save WiFi configuration. + +## wifi connect + +Attempt STA WiFi connection using saved networks by priority. + +AP remains available at: + + 192.168.4.1 diff --git a/docs/UART_PROTOCOL.md b/docs/UART_PROTOCOL.md index df30a1f..03b02c4 100644 --- a/docs/UART_PROTOCOL.md +++ b/docs/UART_PROTOCOL.md @@ -205,3 +205,30 @@ Runtime behavior: If STA disconnects, the ESP32 retries saved networks by priority every 30 seconds. AP mode remains available as a recovery path. + + +--- + +# UART Parity Notes + +UART should use the same generic command model as HTTP. + +## Preferred Relay Command + +Preferred: + + {"type":"set_relay","id":"relay_1","state":true} + +Legacy accepted shape: + + {"type":"set_relay","relay":"relay_1","enabled":true} + +Preferred response: + + {"type":"relay_response","ok":true,"id":"relay_1","state":true} + +## WiFi Priority Config + + {"type":"config_wifi","networks":[{"ssid":"Starlink","password":"password","priority":1},{"ssid":"Home WiFi","password":"password","priority":2}]} + +Lower priority numbers are tried first. diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index dee7099..682bbc4 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -591,8 +591,8 @@ bool setRelayById(const String& relayId, bool enabled) { void sendRelayResponse(Stream& output, const String& relayId, bool enabled) { DynamicJsonDocument doc(256); doc["type"] = MSG_RELAY_RESPONSE; - doc["relay"] = relayId; - doc["enabled"] = enabled; + doc["id"] = relayId; + doc["state"] = enabled; doc["ok"] = true; serializeJson(doc, output); output.println(); @@ -712,8 +712,15 @@ void handleUartMessage(const String& line) { } if (strcmp(type, MSG_SET_RELAY) == 0 || strcmp(type, "set_relay") == 0) { - String relayId = doc["relay"] | ""; - bool enabled = doc["enabled"] | false; + String relayId = doc["id"] | ""; + if (relayId.length() == 0) { + relayId = doc["relay"] | ""; + } + + bool enabled = doc["state"] | false; + if (doc["enabled"].is()) { + enabled = doc["enabled"].as(); + } if (!setRelayById(relayId, enabled)) { sendError(DashboardSerial, "unknown_relay"); @@ -1631,9 +1638,8 @@ void handleSetRelayPost() { DynamicJsonDocument response(512); response["type"] = "relay_response"; response["ok"] = true; - response["relay"] = relayId; + response["id"] = relayId; response["state"] = state; - response["enabled"] = state; String output; serializeJson(response, output);