feat(gateway): add ESP-Touch smartconfig provisioning support and enhance network management
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -12,10 +12,10 @@ This folder hosts the native ESP-IDF C++ rewrite of the Lua DALI gateway.
|
|||||||
- `dali_domain/`: native DALI domain facade over `dali_cpp` and raw frame sinks.
|
- `dali_domain/`: native DALI domain facade over `dali_cpp` and raw frame sinks.
|
||||||
- `gateway_ble/`: NimBLE GATT bridge for BLE transport parity on `FFF1`/`FFF2`/`FFF3`, including raw DALI notifications.
|
- `gateway_ble/`: NimBLE GATT bridge for BLE transport parity on `FFF1`/`FFF2`/`FFF3`, including raw DALI notifications.
|
||||||
- `gateway_controller/`: Lua-compatible gateway command dispatcher, internal scene/group state, and notification fan-out.
|
- `gateway_controller/`: Lua-compatible gateway command dispatcher, internal scene/group state, and notification fan-out.
|
||||||
- `gateway_network/`: HTTP `/info`, `/dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP port `2020` command/notify routing, Wi-Fi STA lifecycle, setup AP mode, ESP-NOW setup ingress, and BOOT-button Wi-Fi reset for the native gateway.
|
- `gateway_network/`: HTTP `/info`, `/dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP port `2020` command/notify routing, Wi-Fi STA lifecycle, ESP-Touch smartconfig, setup AP mode, ESP-NOW setup ingress, and BOOT-button Wi-Fi reset for the native gateway.
|
||||||
- `gateway_runtime/`: persistent runtime state, command queueing, and device info services.
|
- `gateway_runtime/`: persistent runtime state, command queueing, and device info services.
|
||||||
- `gateway_usb_setup/`: optional USB Serial/JTAG setup bridge; disabled by default so USB remains available for debug at boot.
|
- `gateway_usb_setup/`: optional USB Serial/JTAG setup bridge; disabled by default so USB remains available for debug at boot.
|
||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
The native rewrite now wires a shared `gateway_core` bootstrap component, a multi-channel `dali_domain` wrapper over `dali_cpp`, a local vendored `dali` hardware backend from the LuatOS ESP-IDF port with raw receive fan-out, an initial `gateway_runtime` service that provides persistent settings, device info, Lua-compatible command framing helpers, and Lua-style query command deduplication, plus a `gateway_controller` service that starts the gateway command task, dispatches core Lua gateway opcodes, and owns internal scene/group state. The gateway app also includes a `gateway_ble` NimBLE bridge that advertises a Lua-compatible GATT service and forwards `FFF3` framed notifications, incoming `FFF1`/`FFF2`/`FFF3` writes, and native raw DALI frame notifications into the matching raw channel, and a `gateway_network` service that provides the native HTTP `/info`, `GET`/`POST /dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP control-plane router on port `2020`, Wi-Fi STA lifecycle, the Lua-style `LAMMIN_Gateway` setup AP on `192.168.3.1`, ESP-NOW setup ingress for Lua-compatible `connReq`/`connAck`/`echo`/`cmd`/`data`/`uart` packets, native raw DALI frame forwarding back to connected setup peers, and BOOT-button Wi-Fi credential clearing. Startup behavior is configured in `main/Kconfig.projbuild`: BLE is enabled by default, Wi-Fi STA and ESP-NOW setup mode are disabled by default, and the built-in USB Serial/JTAG interface stays in debug mode unless the optional USB setup bridge mode is selected. The gateway app exposes per-channel PHY selection through `main/Kconfig.projbuild`; each channel can be disabled, bound to the native DALI GPIO HAL, or bound to a UART1/UART2 serial PHY. The checked-in `sdkconfig` is aligned with the app's custom 16 MB partition table so the Wi-Fi/BLE/network-enabled image fits the OTA app slots.
|
The native rewrite now wires a shared `gateway_core` bootstrap component, a multi-channel `dali_domain` wrapper over `dali_cpp`, a local vendored `dali` hardware backend from the LuatOS ESP-IDF port with raw receive fan-out, an initial `gateway_runtime` service that provides persistent settings, device info, Lua-compatible command framing helpers, and Lua-style query command deduplication, plus a `gateway_controller` service that starts the gateway command task, dispatches core Lua gateway opcodes, and owns internal scene/group state. The gateway app also includes a `gateway_ble` NimBLE bridge that advertises a Lua-compatible GATT service and forwards `FFF3` framed notifications, incoming `FFF1`/`FFF2`/`FFF3` writes, and native raw DALI frame notifications into the matching raw channel, and a `gateway_network` service that provides the native HTTP `/info`, `GET`/`POST /dali/cmd`, `/led/1`, `/led/0`, `/jq.js`, UDP control-plane router on port `2020`, Wi-Fi STA lifecycle, ESP-Touch smartconfig credential provisioning, the Lua-style `LAMMIN_Gateway` setup AP on `192.168.3.1`, ESP-NOW setup ingress for Lua-compatible `connReq`/`connAck`/`echo`/`cmd`/`data`/`uart` packets, native raw DALI frame forwarding back to connected setup peers, and BOOT-button Wi-Fi credential clearing. Startup behavior is configured in `main/Kconfig.projbuild`: BLE is enabled by default, Wi-Fi STA, smartconfig, and ESP-NOW setup mode are disabled by default, and the built-in USB Serial/JTAG interface stays in debug mode unless the optional USB setup bridge mode is selected. Runtime settings and internal scene/group data are cached in RAM after load, skip unchanged flash writes, and batch Wi-Fi credential commits to reduce flash stalls on ESP32-S3 boards where flash and PSRAM share the SPI bus. The gateway app exposes per-channel PHY selection through `main/Kconfig.projbuild`; each channel can be disabled, bound to the native DALI GPIO HAL, or bound to a UART1/UART2 serial PHY. The checked-in `sdkconfig` is aligned with the app's custom 16 MB partition table so the Wi-Fi/BLE/network-enabled image fits the OTA app slots.
|
||||||
@@ -293,6 +293,13 @@ config GATEWAY_ESPNOW_SETUP_SUPPORTED
|
|||||||
help
|
help
|
||||||
Enables ESP-NOW setup ingress when setup AP mode is entered.
|
Enables ESP-NOW setup ingress when setup AP mode is entered.
|
||||||
|
|
||||||
|
config GATEWAY_SMARTCONFIG_SUPPORTED
|
||||||
|
bool "ESP-Touch smartconfig provisioning is supported"
|
||||||
|
depends on GATEWAY_WIFI_SUPPORTED
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enables Lua-compatible ESP-Touch smartconfig provisioning for Wi-Fi credentials.
|
||||||
|
|
||||||
config GATEWAY_START_ESPNOW_SETUP_ENABLED
|
config GATEWAY_START_ESPNOW_SETUP_ENABLED
|
||||||
bool "Enter ESP-NOW setup mode at startup"
|
bool "Enter ESP-NOW setup mode at startup"
|
||||||
depends on GATEWAY_ESPNOW_SETUP_SUPPORTED
|
depends on GATEWAY_ESPNOW_SETUP_SUPPORTED
|
||||||
@@ -300,6 +307,21 @@ config GATEWAY_START_ESPNOW_SETUP_ENABLED
|
|||||||
help
|
help
|
||||||
Starts the setup AP and ESP-NOW setup ingress immediately at boot. Disabled by default.
|
Starts the setup AP and ESP-NOW setup ingress immediately at boot. Disabled by default.
|
||||||
|
|
||||||
|
config GATEWAY_START_SMARTCONFIG_ENABLED
|
||||||
|
bool "Start ESP-Touch smartconfig at startup"
|
||||||
|
depends on GATEWAY_SMARTCONFIG_SUPPORTED
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Starts ESP-Touch provisioning at boot. Disabled by default so Wi-Fi stays off unless configured.
|
||||||
|
|
||||||
|
config GATEWAY_SMARTCONFIG_TIMEOUT_SEC
|
||||||
|
int "ESP-Touch smartconfig timeout seconds"
|
||||||
|
depends on GATEWAY_SMARTCONFIG_SUPPORTED
|
||||||
|
range 15 255
|
||||||
|
default 60
|
||||||
|
help
|
||||||
|
Timeout passed to ESP-IDF smartconfig before provisioning restarts internally.
|
||||||
|
|
||||||
choice GATEWAY_USB_STARTUP_MODE
|
choice GATEWAY_USB_STARTUP_MODE
|
||||||
prompt "USB Serial/JTAG startup mode"
|
prompt "USB Serial/JTAG startup mode"
|
||||||
default GATEWAY_USB_STARTUP_DEBUG_JTAG
|
default GATEWAY_USB_STARTUP_DEBUG_JTAG
|
||||||
|
|||||||
@@ -53,6 +53,10 @@
|
|||||||
#define CONFIG_GATEWAY_USB_SETUP_READ_TIMEOUT_MS 20
|
#define CONFIG_GATEWAY_USB_SETUP_READ_TIMEOUT_MS 20
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC
|
||||||
|
#define CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC 60
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr const char* kProjectName = "DALI_485_Gateway";
|
constexpr const char* kProjectName = "DALI_485_Gateway";
|
||||||
constexpr const char* kProjectVersion = "0.1.0";
|
constexpr const char* kProjectVersion = "0.1.0";
|
||||||
@@ -94,6 +98,18 @@ constexpr bool kEspnowSetupStartupEnabled = true;
|
|||||||
constexpr bool kEspnowSetupStartupEnabled = false;
|
constexpr bool kEspnowSetupStartupEnabled = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_GATEWAY_SMARTCONFIG_SUPPORTED
|
||||||
|
constexpr bool kSmartconfigSupported = true;
|
||||||
|
#else
|
||||||
|
constexpr bool kSmartconfigSupported = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_GATEWAY_START_SMARTCONFIG_ENABLED
|
||||||
|
constexpr bool kSmartconfigStartupEnabled = true;
|
||||||
|
#else
|
||||||
|
constexpr bool kSmartconfigStartupEnabled = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_GATEWAY_USB_STARTUP_SETUP_SERIAL
|
#ifdef CONFIG_GATEWAY_USB_STARTUP_SETUP_SERIAL
|
||||||
constexpr bool kUsbSetupStartupEnabled = true;
|
constexpr bool kUsbSetupStartupEnabled = true;
|
||||||
#else
|
#else
|
||||||
@@ -351,6 +367,10 @@ extern "C" void app_main(void) {
|
|||||||
network_config.espnow_setup_enabled = profile.enable_espnow;
|
network_config.espnow_setup_enabled = profile.enable_espnow;
|
||||||
network_config.espnow_setup_startup_enabled =
|
network_config.espnow_setup_startup_enabled =
|
||||||
profile.enable_espnow && kEspnowSetupStartupEnabled;
|
profile.enable_espnow && kEspnowSetupStartupEnabled;
|
||||||
|
network_config.smartconfig_enabled = profile.enable_wifi && kSmartconfigSupported;
|
||||||
|
network_config.smartconfig_startup_enabled =
|
||||||
|
profile.enable_wifi && kSmartconfigSupported && kSmartconfigStartupEnabled;
|
||||||
|
network_config.smartconfig_timeout_sec = CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC;
|
||||||
#ifdef CONFIG_GATEWAY_NETWORK_HTTP_ENABLED
|
#ifdef CONFIG_GATEWAY_NETWORK_HTTP_ENABLED
|
||||||
network_config.http_enabled = true;
|
network_config.http_enabled = true;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -628,7 +628,10 @@ CONFIG_GATEWAY_START_BLE_ENABLED=y
|
|||||||
CONFIG_GATEWAY_WIFI_SUPPORTED=y
|
CONFIG_GATEWAY_WIFI_SUPPORTED=y
|
||||||
# CONFIG_GATEWAY_START_WIFI_STA_ENABLED is not set
|
# CONFIG_GATEWAY_START_WIFI_STA_ENABLED is not set
|
||||||
CONFIG_GATEWAY_ESPNOW_SETUP_SUPPORTED=y
|
CONFIG_GATEWAY_ESPNOW_SETUP_SUPPORTED=y
|
||||||
|
CONFIG_GATEWAY_SMARTCONFIG_SUPPORTED=y
|
||||||
# CONFIG_GATEWAY_START_ESPNOW_SETUP_ENABLED is not set
|
# CONFIG_GATEWAY_START_ESPNOW_SETUP_ENABLED is not set
|
||||||
|
# CONFIG_GATEWAY_START_SMARTCONFIG_ENABLED is not set
|
||||||
|
CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC=60
|
||||||
CONFIG_GATEWAY_USB_STARTUP_DEBUG_JTAG=y
|
CONFIG_GATEWAY_USB_STARTUP_DEBUG_JTAG=y
|
||||||
# CONFIG_GATEWAY_USB_STARTUP_SETUP_SERIAL is not set
|
# CONFIG_GATEWAY_USB_STARTUP_SETUP_SERIAL is not set
|
||||||
# end of Gateway Startup Services
|
# end of Gateway Startup Services
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ struct GatewayChannelSnapshot {
|
|||||||
|
|
||||||
struct GatewayControllerSnapshot {
|
struct GatewayControllerSnapshot {
|
||||||
bool setup_mode{false};
|
bool setup_mode{false};
|
||||||
|
bool wireless_setup_mode{false};
|
||||||
bool ble_enabled{false};
|
bool ble_enabled{false};
|
||||||
bool wifi_enabled{false};
|
bool wifi_enabled{false};
|
||||||
bool ip_router_enabled{false};
|
bool ip_router_enabled{false};
|
||||||
@@ -73,6 +74,8 @@ class GatewayController {
|
|||||||
void addGatewayNameSink(GatewayNameSink sink);
|
void addGatewayNameSink(GatewayNameSink sink);
|
||||||
|
|
||||||
bool setupMode() const;
|
bool setupMode() const;
|
||||||
|
bool wirelessSetupMode() const;
|
||||||
|
void setWirelessSetupMode(bool enabled);
|
||||||
bool bleEnabled() const;
|
bool bleEnabled() const;
|
||||||
bool wifiEnabled() const;
|
bool wifiEnabled() const;
|
||||||
bool ipRouterEnabled() const;
|
bool ipRouterEnabled() const;
|
||||||
@@ -174,6 +177,7 @@ class GatewayController {
|
|||||||
std::map<uint8_t, SceneStore> scenes_;
|
std::map<uint8_t, SceneStore> scenes_;
|
||||||
std::map<uint8_t, GroupStore> groups_;
|
std::map<uint8_t, GroupStore> groups_;
|
||||||
bool setup_mode_{false};
|
bool setup_mode_{false};
|
||||||
|
bool wireless_setup_mode_{false};
|
||||||
bool ble_enabled_{false};
|
bool ble_enabled_{false};
|
||||||
bool wifi_enabled_{false};
|
bool wifi_enabled_{false};
|
||||||
bool ip_router_enabled_{true};
|
bool ip_router_enabled_{true};
|
||||||
|
|||||||
@@ -185,6 +185,14 @@ bool GatewayController::setupMode() const {
|
|||||||
return setup_mode_;
|
return setup_mode_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GatewayController::wirelessSetupMode() const {
|
||||||
|
return wireless_setup_mode_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GatewayController::setWirelessSetupMode(bool enabled) {
|
||||||
|
wireless_setup_mode_ = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
bool GatewayController::bleEnabled() const {
|
bool GatewayController::bleEnabled() const {
|
||||||
return ble_enabled_;
|
return ble_enabled_;
|
||||||
}
|
}
|
||||||
@@ -200,6 +208,7 @@ bool GatewayController::ipRouterEnabled() const {
|
|||||||
GatewayControllerSnapshot GatewayController::snapshot() {
|
GatewayControllerSnapshot GatewayController::snapshot() {
|
||||||
GatewayControllerSnapshot out;
|
GatewayControllerSnapshot out;
|
||||||
out.setup_mode = setup_mode_;
|
out.setup_mode = setup_mode_;
|
||||||
|
out.wireless_setup_mode = wireless_setup_mode_;
|
||||||
out.ble_enabled = ble_enabled_;
|
out.ble_enabled = ble_enabled_;
|
||||||
out.wifi_enabled = wifi_enabled_;
|
out.wifi_enabled = wifi_enabled_;
|
||||||
out.ip_router_enabled = ip_router_enabled_;
|
out.ip_router_enabled = ip_router_enabled_;
|
||||||
@@ -302,8 +311,13 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
|||||||
case 0x04:
|
case 0x04:
|
||||||
if (addr == 0) {
|
if (addr == 0) {
|
||||||
wifi_enabled_ = false;
|
wifi_enabled_ = false;
|
||||||
} else if (addr == 1 || addr == 101) {
|
wireless_setup_mode_ = false;
|
||||||
|
} else if (addr == 1) {
|
||||||
wifi_enabled_ = true;
|
wifi_enabled_ = true;
|
||||||
|
wireless_setup_mode_ = false;
|
||||||
|
} else if (addr == 100 || addr == 101) {
|
||||||
|
wifi_enabled_ = true;
|
||||||
|
wireless_setup_mode_ = true;
|
||||||
}
|
}
|
||||||
for (const auto& sink : wifi_state_sinks_) {
|
for (const auto& sink : wifi_state_sinks_) {
|
||||||
sink(addr);
|
sink(addr);
|
||||||
@@ -317,12 +331,9 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
|
|||||||
if (setup_mode_ && config_.setup_supported) {
|
if (setup_mode_ && config_.setup_supported) {
|
||||||
feature |= 0x01;
|
feature |= 0x01;
|
||||||
}
|
}
|
||||||
if (config_.ble_supported) {
|
if (wireless_setup_mode_ && config_.wifi_supported) {
|
||||||
feature |= 0x02;
|
feature |= 0x02;
|
||||||
}
|
}
|
||||||
if (config_.wifi_supported) {
|
|
||||||
feature |= 0x04;
|
|
||||||
}
|
|
||||||
if (config_.ip_router_supported && ip_router_enabled_) {
|
if (config_.ip_router_supported && ip_router_enabled_) {
|
||||||
feature |= 0x08;
|
feature |= 0x08;
|
||||||
}
|
}
|
||||||
@@ -593,6 +604,9 @@ bool GatewayController::setSceneEnabled(uint8_t gateway_id, uint8_t scene_id, bo
|
|||||||
if (scene_data == nullptr) {
|
if (scene_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (scene_data->enabled == enabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
scene_data->enabled = enabled;
|
scene_data->enabled = enabled;
|
||||||
return saveScene(gateway_id, scene_id);
|
return saveScene(gateway_id, scene_id);
|
||||||
}
|
}
|
||||||
@@ -604,21 +618,29 @@ bool GatewayController::setSceneDetail(uint8_t gateway_id, uint8_t scene_id, uin
|
|||||||
if (scene_data == nullptr) {
|
if (scene_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
scene_data->brightness = std::min<uint8_t>(brightness, 254);
|
const uint8_t next_brightness = std::min<uint8_t>(brightness, 254);
|
||||||
scene_data->color_mode = std::min<uint8_t>(color_mode, 2);
|
const uint8_t next_color_mode = std::min<uint8_t>(color_mode, 2);
|
||||||
if (scene_data->color_mode == 0) {
|
uint8_t next_data1 = 0;
|
||||||
scene_data->data1 = data1;
|
uint8_t next_data2 = 0;
|
||||||
scene_data->data2 = data2;
|
uint8_t next_data3 = 0;
|
||||||
scene_data->data3 = 0;
|
if (next_color_mode == 0) {
|
||||||
} else if (scene_data->color_mode == 1) {
|
next_data1 = data1;
|
||||||
scene_data->data1 = data1;
|
next_data2 = data2;
|
||||||
scene_data->data2 = data2;
|
} else if (next_color_mode == 1) {
|
||||||
scene_data->data3 = data3;
|
next_data1 = data1;
|
||||||
} else {
|
next_data2 = data2;
|
||||||
scene_data->data1 = 0;
|
next_data3 = data3;
|
||||||
scene_data->data2 = 0;
|
|
||||||
scene_data->data3 = 0;
|
|
||||||
}
|
}
|
||||||
|
if (scene_data->brightness == next_brightness && scene_data->color_mode == next_color_mode &&
|
||||||
|
scene_data->data1 == next_data1 && scene_data->data2 == next_data2 &&
|
||||||
|
scene_data->data3 == next_data3) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
scene_data->brightness = next_brightness;
|
||||||
|
scene_data->color_mode = next_color_mode;
|
||||||
|
scene_data->data1 = next_data1;
|
||||||
|
scene_data->data2 = next_data2;
|
||||||
|
scene_data->data3 = next_data3;
|
||||||
return saveScene(gateway_id, scene_id);
|
return saveScene(gateway_id, scene_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,7 +649,11 @@ bool GatewayController::setSceneName(uint8_t gateway_id, uint8_t scene_id, std::
|
|||||||
if (scene_data == nullptr) {
|
if (scene_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
scene_data->name = NormalizeName(name);
|
const auto normalized = NormalizeName(name);
|
||||||
|
if (scene_data->name == normalized) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
scene_data->name = normalized;
|
||||||
return saveSceneName(gateway_id, scene_id, scene_data->name);
|
return saveSceneName(gateway_id, scene_id, scene_data->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,6 +662,13 @@ bool GatewayController::deleteScene(uint8_t gateway_id, uint8_t scene_id) {
|
|||||||
if (scene_data == nullptr) {
|
if (scene_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const bool already_default = !scene_data->enabled && scene_data->brightness == 254 &&
|
||||||
|
scene_data->color_mode == 2 && scene_data->data1 == 0 &&
|
||||||
|
scene_data->data2 == 0 && scene_data->data3 == 0 &&
|
||||||
|
scene_data->name.empty();
|
||||||
|
if (already_default) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
*scene_data = InternalScene{};
|
*scene_data = InternalScene{};
|
||||||
deleteSceneStorage(gateway_id, scene_id);
|
deleteSceneStorage(gateway_id, scene_id);
|
||||||
deleteSceneNameStorage(gateway_id, scene_id);
|
deleteSceneNameStorage(gateway_id, scene_id);
|
||||||
@@ -688,6 +721,9 @@ bool GatewayController::setGroupEnabled(uint8_t gateway_id, uint8_t group_id, bo
|
|||||||
if (group_data == nullptr) {
|
if (group_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (group_data->enabled == enabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
group_data->enabled = enabled;
|
group_data->enabled = enabled;
|
||||||
return saveGroup(gateway_id, group_id);
|
return saveGroup(gateway_id, group_id);
|
||||||
}
|
}
|
||||||
@@ -698,8 +734,13 @@ bool GatewayController::setGroupDetail(uint8_t gateway_id, uint8_t group_id, uin
|
|||||||
if (group_data == nullptr) {
|
if (group_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
group_data->target_type = normalizeGroupTargetType(target_type);
|
const uint8_t next_target_type = normalizeGroupTargetType(target_type);
|
||||||
group_data->target_value = normalizeGroupTargetValue(group_data->target_type, target_value);
|
const uint8_t next_target_value = normalizeGroupTargetValue(next_target_type, target_value);
|
||||||
|
if (group_data->target_type == next_target_type && group_data->target_value == next_target_value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
group_data->target_type = next_target_type;
|
||||||
|
group_data->target_value = next_target_value;
|
||||||
return saveGroup(gateway_id, group_id);
|
return saveGroup(gateway_id, group_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,7 +749,11 @@ bool GatewayController::setGroupName(uint8_t gateway_id, uint8_t group_id, std::
|
|||||||
if (group_data == nullptr) {
|
if (group_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
group_data->name = NormalizeName(name);
|
const auto normalized = NormalizeName(name);
|
||||||
|
if (group_data->name == normalized) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
group_data->name = normalized;
|
||||||
return saveGroupName(gateway_id, group_id, group_data->name);
|
return saveGroupName(gateway_id, group_id, group_data->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,6 +762,11 @@ bool GatewayController::deleteGroup(uint8_t gateway_id, uint8_t group_id) {
|
|||||||
if (group_data == nullptr) {
|
if (group_data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const bool already_default = !group_data->enabled && group_data->target_type == 2 &&
|
||||||
|
group_data->target_value == 0 && group_data->name.empty();
|
||||||
|
if (already_default) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
*group_data = InternalGroup{};
|
*group_data = InternalGroup{};
|
||||||
deleteGroupStorage(gateway_id, group_id);
|
deleteGroupStorage(gateway_id, group_id);
|
||||||
deleteGroupNameStorage(gateway_id, group_id);
|
deleteGroupNameStorage(gateway_id, group_id);
|
||||||
@@ -1125,7 +1175,10 @@ bool GatewayController::eraseKey(std::string_view key) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const esp_err_t err = nvs_erase_key(storage_, std::string(key).c_str());
|
const esp_err_t err = nvs_erase_key(storage_, std::string(key).c_str());
|
||||||
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
|
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (err != ESP_OK) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return nvs_commit(storage_) == ESP_OK;
|
return nvs_commit(storage_) == ESP_OK;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
#include "gateway_runtime.hpp"
|
||||||
#include "lwip/sockets.h"
|
#include "lwip/sockets.h"
|
||||||
|
|
||||||
namespace gateway {
|
namespace gateway {
|
||||||
@@ -20,12 +22,14 @@ namespace gateway {
|
|||||||
class GatewayController;
|
class GatewayController;
|
||||||
class DaliDomainService;
|
class DaliDomainService;
|
||||||
struct DaliRawFrame;
|
struct DaliRawFrame;
|
||||||
class GatewayRuntime;
|
|
||||||
|
|
||||||
struct GatewayNetworkServiceConfig {
|
struct GatewayNetworkServiceConfig {
|
||||||
bool wifi_enabled{true};
|
bool wifi_enabled{true};
|
||||||
bool espnow_setup_enabled{true};
|
bool espnow_setup_enabled{true};
|
||||||
bool espnow_setup_startup_enabled{false};
|
bool espnow_setup_startup_enabled{false};
|
||||||
|
bool smartconfig_enabled{true};
|
||||||
|
bool smartconfig_startup_enabled{false};
|
||||||
|
uint8_t smartconfig_timeout_sec{60};
|
||||||
bool http_enabled{true};
|
bool http_enabled{true};
|
||||||
bool udp_enabled{true};
|
bool udp_enabled{true};
|
||||||
uint16_t http_port{80};
|
uint16_t http_port{80};
|
||||||
@@ -65,6 +69,8 @@ class GatewayNetworkService {
|
|||||||
esp_err_t ensureNetworkStack();
|
esp_err_t ensureNetworkStack();
|
||||||
esp_err_t startWifi();
|
esp_err_t startWifi();
|
||||||
esp_err_t startSetupAp();
|
esp_err_t startSetupAp();
|
||||||
|
esp_err_t startSmartconfig();
|
||||||
|
void stopSmartconfig();
|
||||||
esp_err_t startEspNow();
|
esp_err_t startEspNow();
|
||||||
void stopEspNow();
|
void stopEspNow();
|
||||||
esp_err_t addEspNowPeer(const uint8_t* mac, bool broadcast = false);
|
esp_err_t addEspNowPeer(const uint8_t* mac, bool broadcast = false);
|
||||||
@@ -96,8 +102,12 @@ class GatewayNetworkService {
|
|||||||
esp_netif_t* wifi_sta_netif_{nullptr};
|
esp_netif_t* wifi_sta_netif_{nullptr};
|
||||||
esp_netif_t* wifi_ap_netif_{nullptr};
|
esp_netif_t* wifi_ap_netif_{nullptr};
|
||||||
bool wifi_started_{false};
|
bool wifi_started_{false};
|
||||||
|
bool wifi_event_handlers_registered_{false};
|
||||||
bool setup_ap_started_{false};
|
bool setup_ap_started_{false};
|
||||||
bool espnow_started_{false};
|
bool espnow_started_{false};
|
||||||
|
bool smartconfig_started_{false};
|
||||||
|
bool smartconfig_event_handler_registered_{false};
|
||||||
|
std::optional<WirelessInfo> smartconfig_pending_wireless_;
|
||||||
bool espnow_connected_{false};
|
bool espnow_connected_{false};
|
||||||
std::array<uint8_t, 6> espnow_peer_{};
|
std::array<uint8_t, 6> espnow_peer_{};
|
||||||
TaskHandle_t boot_button_task_handle_{nullptr};
|
TaskHandle_t boot_button_task_handle_{nullptr};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_netif.h"
|
#include "esp_netif.h"
|
||||||
#include "esp_netif_ip_addr.h"
|
#include "esp_netif_ip_addr.h"
|
||||||
|
#include "esp_smartconfig.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_wifi.h"
|
#include "esp_wifi.h"
|
||||||
#include "lwip/inet.h"
|
#include "lwip/inet.h"
|
||||||
@@ -91,6 +92,17 @@ std::string MacToHex(const uint8_t mac[6]) {
|
|||||||
return std::string(out);
|
return std::string(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BoundedString(const uint8_t* data, size_t len) {
|
||||||
|
if (data == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
size_t actual_len = 0;
|
||||||
|
while (actual_len < len && data[actual_len] != 0) {
|
||||||
|
++actual_len;
|
||||||
|
}
|
||||||
|
return std::string(reinterpret_cast<const char*>(data), actual_len);
|
||||||
|
}
|
||||||
|
|
||||||
std::string LocalMacHex(wifi_interface_t interface) {
|
std::string LocalMacHex(wifi_interface_t interface) {
|
||||||
uint8_t mac[6] = {};
|
uint8_t mac[6] = {};
|
||||||
if (esp_wifi_get_mac(interface, mac) != ESP_OK) {
|
if (esp_wifi_get_mac(interface, mac) != ESP_OK) {
|
||||||
@@ -194,6 +206,11 @@ esp_err_t GatewayNetworkService::start() {
|
|||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
} else if (config_.smartconfig_startup_enabled) {
|
||||||
|
err = startSmartconfig();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
} else if (config_.wifi_enabled) {
|
} else if (config_.wifi_enabled) {
|
||||||
err = startWifi();
|
err = startWifi();
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
@@ -261,6 +278,7 @@ esp_err_t GatewayNetworkService::startWifi() {
|
|||||||
if (wifi_started_) {
|
if (wifi_started_) {
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
stopSmartconfig();
|
||||||
stopEspNow();
|
stopEspNow();
|
||||||
setup_ap_started_ = false;
|
setup_ap_started_ = false;
|
||||||
|
|
||||||
@@ -279,18 +297,21 @@ esp_err_t GatewayNetworkService::startWifi() {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
if (!wifi_event_handlers_registered_) {
|
||||||
&GatewayNetworkService::HandleWifiEvent, this);
|
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
&GatewayNetworkService::HandleWifiEvent, this);
|
||||||
ESP_LOGE(kTag, "failed to register Wi-Fi event handler: %s", esp_err_to_name(err));
|
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||||
return err;
|
ESP_LOGE(kTag, "failed to register Wi-Fi event handler: %s", esp_err_to_name(err));
|
||||||
}
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
||||||
&GatewayNetworkService::HandleWifiEvent, this);
|
&GatewayNetworkService::HandleWifiEvent, this);
|
||||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||||
ESP_LOGE(kTag, "failed to register IP event handler: %s", esp_err_to_name(err));
|
ESP_LOGE(kTag, "failed to register IP event handler: %s", esp_err_to_name(err));
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
|
wifi_event_handlers_registered_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
||||||
@@ -329,12 +350,14 @@ esp_err_t GatewayNetworkService::startWifi() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wifi_started_ = true;
|
wifi_started_ = true;
|
||||||
|
controller_.setWirelessSetupMode(false);
|
||||||
ESP_LOGI(kTag, "Wi-Fi STA started has_credentials=%d",
|
ESP_LOGI(kTag, "Wi-Fi STA started has_credentials=%d",
|
||||||
device_info.wlan.has_value() && !device_info.wlan->ssid.empty());
|
device_info.wlan.has_value() && !device_info.wlan->ssid.empty());
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t GatewayNetworkService::startSetupAp() {
|
esp_err_t GatewayNetworkService::startSetupAp() {
|
||||||
|
stopSmartconfig();
|
||||||
if (wifi_ap_netif_ == nullptr) {
|
if (wifi_ap_netif_ == nullptr) {
|
||||||
wifi_ap_netif_ = esp_netif_create_default_wifi_ap();
|
wifi_ap_netif_ = esp_netif_create_default_wifi_ap();
|
||||||
if (wifi_ap_netif_ == nullptr) {
|
if (wifi_ap_netif_ == nullptr) {
|
||||||
@@ -397,10 +420,73 @@ esp_err_t GatewayNetworkService::startSetupAp() {
|
|||||||
|
|
||||||
wifi_started_ = true;
|
wifi_started_ = true;
|
||||||
setup_ap_started_ = true;
|
setup_ap_started_ = true;
|
||||||
|
controller_.setWirelessSetupMode(true);
|
||||||
ESP_LOGI(kTag, "setup AP started ssid=%s ip=192.168.3.1", kSetupApSsid);
|
ESP_LOGI(kTag, "setup AP started ssid=%s ip=192.168.3.1", kSetupApSsid);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t GatewayNetworkService::startSmartconfig() {
|
||||||
|
if (!config_.smartconfig_enabled) {
|
||||||
|
ESP_LOGW(kTag, "smartconfig requested but not supported");
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
if (smartconfig_started_) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_.wifi_enabled = true;
|
||||||
|
if (setup_ap_started_) {
|
||||||
|
stopEspNow();
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
||||||
|
wifi_started_ = false;
|
||||||
|
setup_ap_started_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = startWifi();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!smartconfig_event_handler_registered_) {
|
||||||
|
err = esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID,
|
||||||
|
&GatewayNetworkService::HandleWifiEvent, this);
|
||||||
|
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||||
|
ESP_LOGE(kTag, "failed to register smartconfig event handler: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
smartconfig_event_handler_registered_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = esp_smartconfig_set_type(SC_TYPE_ESPTOUCH);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(kTag, "failed to set smartconfig type: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_esptouch_set_timeout(config_.smartconfig_timeout_sec));
|
||||||
|
|
||||||
|
smartconfig_start_config_t smartconfig = SMARTCONFIG_START_CONFIG_DEFAULT();
|
||||||
|
err = esp_smartconfig_start(&smartconfig);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(kTag, "failed to start smartconfig: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
smartconfig_pending_wireless_.reset();
|
||||||
|
smartconfig_started_ = true;
|
||||||
|
controller_.setWirelessSetupMode(true);
|
||||||
|
ESP_LOGI(kTag, "ESP-Touch smartconfig started timeout=%us", config_.smartconfig_timeout_sec);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GatewayNetworkService::stopSmartconfig() {
|
||||||
|
if (!smartconfig_started_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_smartconfig_stop());
|
||||||
|
smartconfig_started_ = false;
|
||||||
|
smartconfig_pending_wireless_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t GatewayNetworkService::startEspNow() {
|
esp_err_t GatewayNetworkService::startEspNow() {
|
||||||
if (espnow_started_) {
|
if (espnow_started_) {
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -629,6 +715,42 @@ void GatewayNetworkService::handleWifiEvent(esp_event_base_t event_base, int32_t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event_base == SC_EVENT) {
|
||||||
|
if (event_id == SC_EVENT_SCAN_DONE) {
|
||||||
|
ESP_LOGI(kTag, "smartconfig scan done");
|
||||||
|
} else if (event_id == SC_EVENT_FOUND_CHANNEL) {
|
||||||
|
ESP_LOGI(kTag, "smartconfig found channel");
|
||||||
|
} else if (event_id == SC_EVENT_GOT_SSID_PSWD && event_data != nullptr) {
|
||||||
|
auto* event = static_cast<smartconfig_event_got_ssid_pswd_t*>(event_data);
|
||||||
|
wifi_config_t wifi_config = {};
|
||||||
|
std::memcpy(wifi_config.sta.ssid, event->ssid, sizeof(wifi_config.sta.ssid));
|
||||||
|
std::memcpy(wifi_config.sta.password, event->password, sizeof(wifi_config.sta.password));
|
||||||
|
wifi_config.sta.bssid_set = event->bssid_set;
|
||||||
|
if (event->bssid_set) {
|
||||||
|
std::memcpy(wifi_config.sta.bssid, event->bssid, sizeof(wifi_config.sta.bssid));
|
||||||
|
}
|
||||||
|
|
||||||
|
WirelessInfo wireless;
|
||||||
|
wireless.ssid = BoundedString(event->ssid, sizeof(event->ssid));
|
||||||
|
wireless.password = BoundedString(event->password, sizeof(event->password));
|
||||||
|
uint8_t mac[6] = {};
|
||||||
|
if (esp_wifi_get_mac(WIFI_IF_STA, mac) == ESP_OK) {
|
||||||
|
wireless.mac = MacToHex(mac);
|
||||||
|
}
|
||||||
|
smartconfig_pending_wireless_ = wireless;
|
||||||
|
|
||||||
|
ESP_LOGI(kTag, "smartconfig got credentials ssid=%s", wireless.ssid.c_str());
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
|
||||||
|
} else if (event_id == SC_EVENT_SEND_ACK_DONE) {
|
||||||
|
stopSmartconfig();
|
||||||
|
controller_.setWirelessSetupMode(false);
|
||||||
|
ESP_LOGI(kTag, "smartconfig ACK sent");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||||
const auto info = runtime_.deviceInfo();
|
const auto info = runtime_.deviceInfo();
|
||||||
if (info.wlan.has_value() && !info.wlan->ssid.empty()) {
|
if (info.wlan.has_value() && !info.wlan->ssid.empty()) {
|
||||||
@@ -643,7 +765,7 @@ void GatewayNetworkService::handleWifiEvent(esp_event_base_t event_base, int32_t
|
|||||||
const bool has_credentials = !info.wlan->ssid.empty();
|
const bool has_credentials = !info.wlan->ssid.empty();
|
||||||
info.wlan->ip.clear();
|
info.wlan->ip.clear();
|
||||||
runtime_.setWirelessInfo(std::move(*info.wlan));
|
runtime_.setWirelessInfo(std::move(*info.wlan));
|
||||||
if (has_credentials) {
|
if (has_credentials && !smartconfig_started_) {
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -654,8 +776,13 @@ void GatewayNetworkService::handleWifiEvent(esp_event_base_t event_base, int32_t
|
|||||||
auto* event = static_cast<ip_event_got_ip_t*>(event_data);
|
auto* event = static_cast<ip_event_got_ip_t*>(event_data);
|
||||||
char ip[16] = {0};
|
char ip[16] = {0};
|
||||||
esp_ip4addr_ntoa(&event->ip_info.ip, ip, sizeof(ip));
|
esp_ip4addr_ntoa(&event->ip_info.ip, ip, sizeof(ip));
|
||||||
WirelessInfo wireless = runtime_.deviceInfo().wlan.value_or(WirelessInfo{});
|
WirelessInfo wireless = smartconfig_pending_wireless_.value_or(
|
||||||
|
runtime_.deviceInfo().wlan.value_or(WirelessInfo{}));
|
||||||
wireless.ip = ip;
|
wireless.ip = ip;
|
||||||
|
uint8_t mac[6] = {};
|
||||||
|
if (esp_wifi_get_mac(WIFI_IF_STA, mac) == ESP_OK) {
|
||||||
|
wireless.mac = MacToHex(mac);
|
||||||
|
}
|
||||||
runtime_.setWirelessInfo(std::move(wireless));
|
runtime_.setWirelessInfo(std::move(wireless));
|
||||||
ESP_LOGI(kTag, "Wi-Fi got IP %s", ip);
|
ESP_LOGI(kTag, "Wi-Fi got IP %s", ip);
|
||||||
}
|
}
|
||||||
@@ -903,7 +1030,9 @@ void GatewayNetworkService::handleGatewayNotification(const std::vector<uint8_t>
|
|||||||
void GatewayNetworkService::handleWifiControl(uint8_t mode) {
|
void GatewayNetworkService::handleWifiControl(uint8_t mode) {
|
||||||
if (mode == 0) {
|
if (mode == 0) {
|
||||||
config_.wifi_enabled = false;
|
config_.wifi_enabled = false;
|
||||||
|
stopSmartconfig();
|
||||||
stopEspNow();
|
stopEspNow();
|
||||||
|
controller_.setWirelessSetupMode(false);
|
||||||
if (wifi_started_) {
|
if (wifi_started_) {
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
||||||
@@ -923,8 +1052,13 @@ void GatewayNetworkService::handleWifiControl(uint8_t mode) {
|
|||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(startSetupAp());
|
ESP_ERROR_CHECK_WITHOUT_ABORT(startSetupAp());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (mode == 100) {
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(startSmartconfig());
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (mode == 1) {
|
if (mode == 1) {
|
||||||
config_.wifi_enabled = true;
|
config_.wifi_enabled = true;
|
||||||
|
stopSmartconfig();
|
||||||
if (setup_ap_started_) {
|
if (setup_ap_started_) {
|
||||||
stopEspNow();
|
stopEspNow();
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
||||||
@@ -994,6 +1128,7 @@ std::string GatewayNetworkService::gatewaySnapshotJson() {
|
|||||||
cJSON* gw_info = cJSON_CreateObject();
|
cJSON* gw_info = cJSON_CreateObject();
|
||||||
if (gw_info != nullptr) {
|
if (gw_info != nullptr) {
|
||||||
cJSON_AddBoolToObject(gw_info, "setupMode", snapshot.setup_mode);
|
cJSON_AddBoolToObject(gw_info, "setupMode", snapshot.setup_mode);
|
||||||
|
cJSON_AddBoolToObject(gw_info, "wlSetupMode", snapshot.wireless_setup_mode);
|
||||||
cJSON_AddBoolToObject(gw_info, "bleEnabled", snapshot.ble_enabled);
|
cJSON_AddBoolToObject(gw_info, "bleEnabled", snapshot.ble_enabled);
|
||||||
cJSON_AddBoolToObject(gw_info, "wifiEnabled", snapshot.wifi_enabled);
|
cJSON_AddBoolToObject(gw_info, "wifiEnabled", snapshot.wifi_enabled);
|
||||||
cJSON_AddBoolToObject(gw_info, "IPRouter", snapshot.ip_router_enabled);
|
cJSON_AddBoolToObject(gw_info, "IPRouter", snapshot.ip_router_enabled);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -127,6 +128,7 @@ class GatewayRuntime {
|
|||||||
GatewaySettingsStore settings_;
|
GatewaySettingsStore settings_;
|
||||||
std::optional<std::vector<uint8_t>> current_command_;
|
std::optional<std::vector<uint8_t>> current_command_;
|
||||||
std::deque<std::vector<uint8_t>> pending_commands_;
|
std::deque<std::vector<uint8_t>> pending_commands_;
|
||||||
|
mutable std::map<uint8_t, std::string> gateway_names_;
|
||||||
size_t gateway_count_{0};
|
size_t gateway_count_{0};
|
||||||
bool ble_enabled_{false};
|
bool ble_enabled_{false};
|
||||||
CommandDropReason last_enqueue_drop_reason_{CommandDropReason::kNone};
|
CommandDropReason last_enqueue_drop_reason_{CommandDropReason::kNone};
|
||||||
|
|||||||
@@ -119,7 +119,14 @@ std::optional<std::string> GatewaySettingsStore::getWifiPassword() const {
|
|||||||
|
|
||||||
bool GatewaySettingsStore::setWifiCredentials(std::string_view ssid,
|
bool GatewaySettingsStore::setWifiCredentials(std::string_view ssid,
|
||||||
std::string_view password) {
|
std::string_view password) {
|
||||||
return writeString(kWifiSsidKey, ssid) && writeString(kWifiPasswordKey, password);
|
if (handle_ == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const esp_err_t ssid_err = nvs_set_str(handle_, kWifiSsidKey, std::string(ssid).c_str());
|
||||||
|
const esp_err_t password_err =
|
||||||
|
nvs_set_str(handle_, kWifiPasswordKey, std::string(password).c_str());
|
||||||
|
return ssid_err == ESP_OK && password_err == ESP_OK && nvs_commit(handle_) == ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GatewaySettingsStore::clearWifiCredentials() {
|
bool GatewaySettingsStore::clearWifiCredentials() {
|
||||||
@@ -129,12 +136,17 @@ bool GatewaySettingsStore::clearWifiCredentials() {
|
|||||||
|
|
||||||
esp_err_t ssid_err = nvs_erase_key(handle_, kWifiSsidKey);
|
esp_err_t ssid_err = nvs_erase_key(handle_, kWifiSsidKey);
|
||||||
esp_err_t password_err = nvs_erase_key(handle_, kWifiPasswordKey);
|
esp_err_t password_err = nvs_erase_key(handle_, kWifiPasswordKey);
|
||||||
|
const bool ssid_missing = ssid_err == ESP_ERR_NVS_NOT_FOUND;
|
||||||
|
const bool password_missing = password_err == ESP_ERR_NVS_NOT_FOUND;
|
||||||
if (ssid_err == ESP_ERR_NVS_NOT_FOUND) {
|
if (ssid_err == ESP_ERR_NVS_NOT_FOUND) {
|
||||||
ssid_err = ESP_OK;
|
ssid_err = ESP_OK;
|
||||||
}
|
}
|
||||||
if (password_err == ESP_ERR_NVS_NOT_FOUND) {
|
if (password_err == ESP_ERR_NVS_NOT_FOUND) {
|
||||||
password_err = ESP_OK;
|
password_err = ESP_OK;
|
||||||
}
|
}
|
||||||
|
if (ssid_missing && password_missing) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return ssid_err == ESP_OK && password_err == ESP_OK && nvs_commit(handle_) == ESP_OK;
|
return ssid_err == ESP_OK && password_err == ESP_OK && nvs_commit(handle_) == ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,10 +360,16 @@ void GatewayRuntime::setWirelessInfo(WirelessInfo info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GatewayRuntime::clearWirelessInfo() {
|
bool GatewayRuntime::clearWirelessInfo() {
|
||||||
|
bool had_credentials = false;
|
||||||
{
|
{
|
||||||
LockGuard guard(command_lock_);
|
LockGuard guard(command_lock_);
|
||||||
|
had_credentials = wireless_info_.has_value() &&
|
||||||
|
(!wireless_info_->ssid.empty() || !wireless_info_->password.empty());
|
||||||
wireless_info_.reset();
|
wireless_info_.reset();
|
||||||
}
|
}
|
||||||
|
if (!had_credentials) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return settings_.clearWifiCredentials();
|
return settings_.clearWifiCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,19 +397,37 @@ GatewayDeviceInfo GatewayRuntime::deviceInfo() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GatewayRuntime::bleEnabled() const {
|
bool GatewayRuntime::bleEnabled() const {
|
||||||
|
LockGuard guard(command_lock_);
|
||||||
return ble_enabled_;
|
return ble_enabled_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GatewayRuntime::setBleEnabled(bool enabled) {
|
bool GatewayRuntime::setBleEnabled(bool enabled) {
|
||||||
|
{
|
||||||
|
LockGuard guard(command_lock_);
|
||||||
|
if (ble_enabled_ == enabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!settings_.setBleEnabled(enabled)) {
|
if (!settings_.setBleEnabled(enabled)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
LockGuard guard(command_lock_);
|
||||||
ble_enabled_ = enabled;
|
ble_enabled_ = enabled;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GatewayRuntime::gatewayName(uint8_t gateway_id) const {
|
std::string GatewayRuntime::gatewayName(uint8_t gateway_id) const {
|
||||||
return settings_.getGatewayName(gateway_id, defaultGatewayName(gateway_id));
|
LockGuard guard(command_lock_);
|
||||||
|
const auto cached = gateway_names_.find(gateway_id);
|
||||||
|
if (cached != gateway_names_.end()) {
|
||||||
|
return cached->second;
|
||||||
|
}
|
||||||
|
auto name = settings_.getGatewayName(gateway_id, defaultGatewayName(gateway_id));
|
||||||
|
if (name.size() > kMaxGatewayNameBytes) {
|
||||||
|
name.resize(kMaxGatewayNameBytes);
|
||||||
|
}
|
||||||
|
gateway_names_[gateway_id] = name;
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GatewayRuntime::setGatewayName(uint8_t gateway_id, std::string_view name) {
|
bool GatewayRuntime::setGatewayName(uint8_t gateway_id, std::string_view name) {
|
||||||
@@ -403,7 +439,17 @@ bool GatewayRuntime::setGatewayName(uint8_t gateway_id, std::string_view name) {
|
|||||||
normalized = defaultGatewayName(gateway_id);
|
normalized = defaultGatewayName(gateway_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings_.setGatewayName(gateway_id, normalized);
|
if (gatewayName(gateway_id) == normalized) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings_.setGatewayName(gateway_id, normalized)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LockGuard guard(command_lock_);
|
||||||
|
gateway_names_[gateway_id] = normalized;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GatewayRuntime::gatewaySerialHex(uint8_t gateway_id) const {
|
std::string GatewayRuntime::gatewaySerialHex(uint8_t gateway_id) const {
|
||||||
|
|||||||
Reference in New Issue
Block a user