diff --git a/docs/api-and-serial-commands.md b/docs/api-and-serial-commands.md index 1bbfd45..09dcfc0 100644 --- a/docs/api-and-serial-commands.md +++ b/docs/api-and-serial-commands.md @@ -2,7 +2,7 @@ ## HTTP API -Base URL while connected to ESP32 AP: +Base URL while connected to the ESP32 access point: http://192.168.4.1 @@ -14,10 +14,6 @@ Base URL while connected to ESP32 AP: Returns full controller status. -Example: - - http://192.168.4.1/status - Includes: - Battery/BMS telemetry @@ -31,34 +27,56 @@ Includes: - System info - Current configuration +Relay states use generic relay IDs: + + relay_1 + relay_2 + +Temperature sensors use generic sensor IDs: + + temp_1 + temp_2 + temp_3 + temp_4 + temp_5 + temp_6 + temp_7 + temp_8 + --- -## Relay Control +## Relay Control API -### GET /relay/starlink/on +### GET /relay/relay_1/on Turns relay 1 on. -### GET /relay/starlink/off +### GET /relay/relay_1/off Turns relay 1 off. -### GET /relay/fridge/on +### GET /relay/relay_2/on Turns relay 2 on. -### GET /relay/fridge/off +### GET /relay/relay_2/off Turns relay 2 off. -Current legacy names: +Relay display names are configurable. The firmware should not assume what a relay controls. -- starlink = relay 1 / GPIO16 -- fridge = relay 2 / GPIO17 +--- -Future goal: +## Deprecated Relay Aliases -- Replace legacy names with generic relay IDs. +These legacy routes may still exist for compatibility during development, but should not be used by new clients: + + /relay/starlink/on + /relay/starlink/off + /relay/fridge/on + /relay/fridge/off + +Future work should remove these aliases after the Pico/dashboard clients use generic relay IDs. --- @@ -68,10 +86,6 @@ Future goal: Returns saved configuration. -Example: - - http://192.168.4.1/config - --- ### POST /config/relay @@ -82,7 +96,7 @@ Example body: { "id": "relay_1", - "name": "Starlink", + "name": "Aux Power", "enabled": true } @@ -101,8 +115,8 @@ Example body: { "enabled": true, - "name": "Greta", - "address": "a5:c2:37:2c:05:dc", + "name": "House Battery", + "address": "aa:bb:cc:dd:ee:ff", "address_type": "public" } @@ -116,7 +130,7 @@ Example body: { "id": "temp_1", - "name": "Fridge Zone 1", + "name": "Cabin", "address": "", "enabled": true } @@ -136,13 +150,13 @@ Valid IDs: ### POST /config/factory-reset -Clears saved config and restores defaults. +Clears saved config and restores generic defaults. --- -## Serial Monitor Commands +## Serial Monitor -Serial baud: +Baud: 115200 @@ -152,7 +166,7 @@ Line ending: --- -## Status and Config +## Status and Config Commands ### status @@ -172,7 +186,7 @@ Clears saved config and restores defaults. --- -## Log Level +## Log Level Commands ### log quiet @@ -184,37 +198,30 @@ Normal serial output. ### log debug -Enables recurring debug output such as BMS and sensor updates. +Enables recurring debug output. --- -## Relay Control +## Relay Control Commands -### relay starlink on +Legacy relay control commands may still exist during development: -Turns legacy Starlink relay on. + relay starlink on + relay starlink off + relay fridge on + relay fridge off -### relay starlink off - -Turns legacy Starlink relay off. - -### relay fridge on - -Turns legacy Fridge relay on. - -### relay fridge off - -Turns legacy Fridge relay off. +Future work should replace these with generic serial relay commands. --- -## Relay Configuration +## Relay Configuration Commands -### relayname 1 Starlink +### relayname 1 Aux Power Sets relay 1 display name. -### relayname 2 Fridge +### relayname 2 Accessory Power Sets relay 2 display name. @@ -222,35 +229,27 @@ Run `save` after changing names. --- -## Temperature Sensor Configuration +## Temperature Sensor Configuration Commands -### tempname 1 Fridge Zone 1 +### tempname 1 Cabin Sets temp sensor 1 display name. -### tempname 2 Fridge Zone 2 +### tempname 2 Outside Air Sets temp sensor 2 display name. -### tempname 3 Rear Seat Area - -Sets temp sensor 3 display name. - -### tempname 4 Outside Air - -Sets temp sensor 4 display name. - Run `save` after changing names. --- -## BMS Configuration +## BMS Configuration Commands -### bmsname Greta +### bmsname House Battery Sets BMS display name. -### bmsaddr a5:c2:37:2c:05:dc +### bmsaddr aa:bb:cc:dd:ee:ff Sets BMS BLE address. @@ -258,7 +257,7 @@ Run `save` after changing BMS config manually. --- -## BMS Setup Mode +## BMS Setup Mode Commands ### enter setup @@ -276,9 +275,8 @@ Scans for BLE devices. Notes: -- JBD/Xiaoxiang BMS advertising can be intermittent. +- Some BMS modules advertise intermittently. - Multiple scans may be required. -- Greta may not appear every scan. ### select bms 1 @@ -302,14 +300,11 @@ Leaves BMS setup mode and allows BMS reconnect. enter setup scan ble scan ble - scan ble select bms 1 - relayname 1 Starlink - relayname 2 Fridge - tempname 1 Fridge Zone 1 - tempname 2 Fridge Zone 2 - tempname 3 Rear Seat Area - tempname 4 Outside Air + relayname 1 Aux Power + relayname 2 Accessory Power + tempname 1 Cabin + tempname 2 Outside Air save config @@ -317,8 +312,7 @@ Leaves BMS setup mode and allows BMS reconnect. ## Known Notes -- BMS telemetry works reliably once the address is configured. -- BLE scan works, but JBD/Xiaoxiang advertising is intermittent. +- BMS telemetry is reliable once a BMS address is configured. +- BLE discovery depends on how often the BMS advertises. - DS18B20 sensors require a 4.7kΩ pull-up resistor on the 1-Wire data bus. -- Relay API still uses legacy names internally. -- Future work should move relay runtime commands to generic relay IDs. +- Some legacy relay names still exist internally and should be removed in a future cleanup. diff --git a/firmware/esp32/xterra-controller/app_config.cpp b/firmware/esp32/xterra-controller/app_config.cpp index 5d40c91..1f5ba79 100644 --- a/firmware/esp32/xterra-controller/app_config.cpp +++ b/firmware/esp32/xterra-controller/app_config.cpp @@ -35,9 +35,9 @@ void loadDefaultConfig() { }; } - appConfig.bms.enabled = true; - appConfig.bms.name = "BMS"; - appConfig.bms.address = "a5:c2:37:2c:05:dc"; + appConfig.bms.enabled = false; + appConfig.bms.name = ""; + appConfig.bms.address = ""; appConfig.bms.addressType = "public"; } diff --git a/firmware/esp32/xterra-controller/bms.cpp b/firmware/esp32/xterra-controller/bms.cpp index 8baaa33..9817a0a 100644 --- a/firmware/esp32/xterra-controller/bms.cpp +++ b/firmware/esp32/xterra-controller/bms.cpp @@ -3,8 +3,9 @@ #include #include #include "logger.h" +#include "app_config.h" -static const char* BMS_ADDRESS = "a5:c2:37:2c:05:dc"; +// BMS address is loaded from appConfig.bms.address. static const char* BMS_SERVICE_UUID = "ff00"; static const char* BMS_NOTIFY_UUID = "ff01"; static const char* BMS_WRITE_UUID = "ff02"; @@ -300,7 +301,15 @@ static bool connectBms() { Serial.println("BMS: Connecting..."); - NimBLEAddress address(std::string(BMS_ADDRESS), BLE_ADDR_PUBLIC); + if (!appConfig.bms.enabled || appConfig.bms.address.length() == 0) { + bmsData.connected = false; + return false; + } + + NimBLEAddress address( + std::string(appConfig.bms.address.c_str()), + appConfig.bms.addressType == "random" ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC + ); client = NimBLEDevice::createClient(); diff --git a/firmware/esp32/xterra-controller/xterra-controller.ino b/firmware/esp32/xterra-controller/xterra-controller.ino index d0b08b3..cd73eeb 100644 --- a/firmware/esp32/xterra-controller/xterra-controller.ino +++ b/firmware/esp32/xterra-controller/xterra-controller.ino @@ -587,6 +587,65 @@ void handleFactoryResetConfig() { sendConfigJson(); } + +void handleGenericRelayRoute() { + String uri = server.uri(); + + // Expected format: + // /relay/relay_1/on + // /relay/relay_1/off + // /relay/relay_2/on + // /relay/relay_2/off + + String prefix = "/relay/"; + if (!uri.startsWith(prefix)) { + server.send(404, "application/json", "{\"ok\":false,\"error\":\"invalid_relay_route\"}"); + return; + } + + String rest = uri.substring(prefix.length()); + int slash = rest.indexOf('/'); + + if (slash < 0) { + server.send(400, "application/json", "{\"ok\":false,\"error\":\"missing_relay_action\"}"); + return; + } + + String relayId = rest.substring(0, slash); + String action = rest.substring(slash + 1); + + bool enabled; + + if (action == "on") { + enabled = true; + } else if (action == "off") { + enabled = false; + } else { + server.send(400, "application/json", "{\"ok\":false,\"error\":\"invalid_relay_action\"}"); + return; + } + + if (relayId == "relay_1") { + relays.starlink = enabled; + } else if (relayId == "relay_2") { + relays.fridge = enabled; + } else { + server.send(404, "application/json", "{\"ok\":false,\"error\":\"unknown_relay\"}"); + return; + } + + updateRelayOutputs(); + + DynamicJsonDocument doc(512); + doc["ok"] = true; + doc["id"] = relayId; + doc["state"] = enabled; + + String output; + serializeJson(doc, output); + server.send(200, "application/json", output); +} + void handleStatus() { DynamicJsonDocument doc(2048); buildStatusDocument(doc); @@ -669,6 +728,11 @@ void setup() { } server.on("/status", handleStatus); + server.on("/relay/relay_1/on", HTTP_GET, handleGenericRelayRoute); + server.on("/relay/relay_1/off", HTTP_GET, handleGenericRelayRoute); + server.on("/relay/relay_2/on", HTTP_GET, handleGenericRelayRoute); + server.on("/relay/relay_2/off", HTTP_GET, handleGenericRelayRoute); + server.on("/config", HTTP_GET, handleGetConfig); server.on("/config/relay", HTTP_POST, handleUpdateRelayConfig);