diff --git a/firmware/esp32/overland-controller/overland-controller.ino b/firmware/esp32/overland-controller/overland-controller.ino index 525c517..91d1c96 100644 --- a/firmware/esp32/overland-controller/overland-controller.ino +++ b/firmware/esp32/overland-controller/overland-controller.ino @@ -189,8 +189,100 @@ void sendRelayResponse(Stream& output, const String& relayId, bool enabled) { output.println(); } + +void sendConfigResponse(Stream& output, bool ok = true) { + DynamicJsonDocument doc(4096); + + doc["type"] = "config_response"; + doc["ok"] = ok; + + JsonObject config = doc.createNestedObject("config"); + config["device_name"] = appConfig.deviceName; + + JsonArray relaysArray = config.createNestedArray("relays"); + for (int i = 0; i < MAX_RELAYS; i++) { + JsonObject relay = relaysArray.createNestedObject(); + relay["id"] = appConfig.relays[i].id; + relay["name"] = appConfig.relays[i].name; + relay["pin"] = appConfig.relays[i].pin; + relay["enabled"] = appConfig.relays[i].enabled; + } + + JsonObject bms = config.createNestedObject("bms"); + bms["enabled"] = appConfig.bms.enabled; + bms["name"] = appConfig.bms.name; + bms["address"] = appConfig.bms.address; + bms["address_type"] = appConfig.bms.addressType; + + JsonArray temps = config.createNestedArray("temperature_sensors"); + for (int i = 0; i < MAX_TEMP_SENSORS; i++) { + JsonObject temp = temps.createNestedObject(); + temp["id"] = appConfig.tempSensors[i].id; + temp["name"] = appConfig.tempSensors[i].name; + temp["address"] = appConfig.tempSensors[i].address; + temp["enabled"] = appConfig.tempSensors[i].enabled; + } + + serializeJson(doc, output); + output.println(); +} + +void sendSimpleOk(Stream& output, const char* type) { + DynamicJsonDocument doc(256); + doc["type"] = type; + doc["ok"] = true; + serializeJson(doc, output); + output.println(); +} + +void sendBleScanResponse(Stream& output) { + DynamicJsonDocument doc(2048); + + doc["type"] = "ble_scan_response"; + + JsonArray devices = doc.createNestedArray("devices"); + + int count = getBleScanResultCount(); + for (int i = 0; i < count; i++) { + const BleScanResult* result = getBleScanResult(i); + + if (!result) { + continue; + } + + JsonObject device = devices.createNestedObject(); + device["index"] = i + 1; + device["name"] = result->name; + device["address"] = result->address; + device["rssi"] = result->rssi; + } + + serializeJson(doc, output); + output.println(); +} + +int findRelayConfigIndexForUart(const String& id) { + for (int i = 0; i < MAX_RELAYS; i++) { + if (appConfig.relays[i].id == id) { + return i; + } + } + + return -1; +} + +int findTempConfigIndexForUart(const String& id) { + for (int i = 0; i < MAX_TEMP_SENSORS; i++) { + if (appConfig.tempSensors[i].id == id) { + return i; + } + } + + return -1; +} + void handleUartMessage(const String& line) { - DynamicJsonDocument doc(512); + DynamicJsonDocument doc(1024); DeserializationError error = deserializeJson(doc, line); if (error) { @@ -200,12 +292,17 @@ void handleUartMessage(const String& line) { const char* type = doc["type"] | ""; - if (strcmp(type, MSG_STATUS_REQUEST) == 0) { + if (strcmp(type, MSG_STATUS_REQUEST) == 0 || strcmp(type, "status_request") == 0) { sendStatus(DashboardSerial); return; } - if (strcmp(type, MSG_SET_RELAY) == 0) { + if (strcmp(type, "config_request") == 0) { + sendConfigResponse(DashboardSerial); + return; + } + + if (strcmp(type, MSG_SET_RELAY) == 0 || strcmp(type, "set_relay") == 0) { String relayId = doc["relay"] | ""; bool enabled = doc["enabled"] | false; @@ -218,6 +315,153 @@ void handleUartMessage(const String& line) { return; } + if (strcmp(type, "config_device") == 0) { + if (!doc["device_name"].is()) { + sendError(DashboardSerial, "missing_device_name"); + return; + } + + appConfig.deviceName = doc["device_name"].as(); + sendConfigResponse(DashboardSerial); + return; + } + + if (strcmp(type, "config_relay") == 0) { + String id = doc["id"] | ""; + int index = findRelayConfigIndexForUart(id); + + if (index < 0) { + sendError(DashboardSerial, "unknown_relay"); + return; + } + + if (doc["name"].is()) { + appConfig.relays[index].name = doc["name"].as(); + } + + if (doc["enabled"].is()) { + appConfig.relays[index].enabled = doc["enabled"].as(); + } + + sendConfigResponse(DashboardSerial); + return; + } + + if (strcmp(type, "config_temp") == 0) { + String id = doc["id"] | ""; + int index = findTempConfigIndexForUart(id); + + if (index < 0) { + sendError(DashboardSerial, "unknown_temp_sensor"); + return; + } + + if (doc["name"].is()) { + appConfig.tempSensors[index].name = doc["name"].as(); + } + + if (doc["address"].is()) { + appConfig.tempSensors[index].address = doc["address"].as(); + } + + if (doc["enabled"].is()) { + appConfig.tempSensors[index].enabled = doc["enabled"].as(); + } + + sendConfigResponse(DashboardSerial); + return; + } + + if (strcmp(type, "config_bms") == 0) { + if (doc["enabled"].is()) { + appConfig.bms.enabled = doc["enabled"].as(); + } + + if (doc["name"].is()) { + appConfig.bms.name = doc["name"].as(); + } + + if (doc["address"].is()) { + appConfig.bms.address = doc["address"].as(); + } + + if (doc["address_type"].is()) { + appConfig.bms.addressType = doc["address_type"].as(); + } + + sendConfigResponse(DashboardSerial); + return; + } + + if (strcmp(type, "save_config") == 0) { + saveConfig(); + sendConfigResponse(DashboardSerial); + return; + } + + if (strcmp(type, "factory_reset") == 0) { + factoryResetConfig(); + sendConfigResponse(DashboardSerial); + return; + } + + if (strcmp(type, "enter_bms_setup") == 0) { + enterBmsSetupMode(); + sendSimpleOk(DashboardSerial, "bms_setup_response"); + return; + } + + if (strcmp(type, "exit_bms_setup") == 0) { + exitBmsSetupMode(); + sendSimpleOk(DashboardSerial, "bms_setup_response"); + return; + } + + if (strcmp(type, "scan_ble") == 0) { + scanBleDevices(20); + sendBleScanResponse(DashboardSerial); + return; + } + + if (strcmp(type, "select_bms") == 0) { + int selected = doc["index"] | 0; + + if (selected < 1 || selected > getBleScanResultCount()) { + sendError(DashboardSerial, "invalid_bms_selection"); + return; + } + + const BleScanResult* result = getBleScanResult(selected - 1); + + if (!result) { + sendError(DashboardSerial, "invalid_bms_selection"); + return; + } + + appConfig.bms.enabled = true; + appConfig.bms.address = result->address; + appConfig.bms.addressType = "public"; + + if (result->name.length() > 0) { + appConfig.bms.name = result->name; + } else { + appConfig.bms.name = "BMS"; + } + + saveConfig(); + exitBmsSetupMode(); + + DynamicJsonDocument response(512); + response["type"] = "bms_setup_response"; + response["ok"] = true; + response["name"] = appConfig.bms.name; + response["address"] = appConfig.bms.address; + + serializeJson(response, DashboardSerial); + DashboardSerial.println(); + return; + } + sendError(DashboardSerial, "unknown_message_type"); }