feat(gateway): add ESP-Touch smartconfig provisioning support and enhance network management
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -13,6 +14,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "gateway_runtime.hpp"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
namespace gateway {
|
||||
@@ -20,12 +22,14 @@ namespace gateway {
|
||||
class GatewayController;
|
||||
class DaliDomainService;
|
||||
struct DaliRawFrame;
|
||||
class GatewayRuntime;
|
||||
|
||||
struct GatewayNetworkServiceConfig {
|
||||
bool wifi_enabled{true};
|
||||
bool espnow_setup_enabled{true};
|
||||
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 udp_enabled{true};
|
||||
uint16_t http_port{80};
|
||||
@@ -65,6 +69,8 @@ class GatewayNetworkService {
|
||||
esp_err_t ensureNetworkStack();
|
||||
esp_err_t startWifi();
|
||||
esp_err_t startSetupAp();
|
||||
esp_err_t startSmartconfig();
|
||||
void stopSmartconfig();
|
||||
esp_err_t startEspNow();
|
||||
void stopEspNow();
|
||||
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_ap_netif_{nullptr};
|
||||
bool wifi_started_{false};
|
||||
bool wifi_event_handlers_registered_{false};
|
||||
bool setup_ap_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};
|
||||
std::array<uint8_t, 6> espnow_peer_{};
|
||||
TaskHandle_t boot_button_task_handle_{nullptr};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ip_addr.h"
|
||||
#include "esp_smartconfig.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "lwip/inet.h"
|
||||
@@ -91,6 +92,17 @@ std::string MacToHex(const uint8_t mac[6]) {
|
||||
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) {
|
||||
uint8_t mac[6] = {};
|
||||
if (esp_wifi_get_mac(interface, mac) != ESP_OK) {
|
||||
@@ -194,6 +206,11 @@ esp_err_t GatewayNetworkService::start() {
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
} else if (config_.smartconfig_startup_enabled) {
|
||||
err = startSmartconfig();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
} else if (config_.wifi_enabled) {
|
||||
err = startWifi();
|
||||
if (err != ESP_OK) {
|
||||
@@ -261,6 +278,7 @@ esp_err_t GatewayNetworkService::startWifi() {
|
||||
if (wifi_started_) {
|
||||
return ESP_OK;
|
||||
}
|
||||
stopSmartconfig();
|
||||
stopEspNow();
|
||||
setup_ap_started_ = false;
|
||||
|
||||
@@ -279,18 +297,21 @@ esp_err_t GatewayNetworkService::startWifi() {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||
&GatewayNetworkService::HandleWifiEvent, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to register Wi-Fi event handler: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
if (!wifi_event_handlers_registered_) {
|
||||
err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||
&GatewayNetworkService::HandleWifiEvent, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
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,
|
||||
&GatewayNetworkService::HandleWifiEvent, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to register IP event handler: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
||||
&GatewayNetworkService::HandleWifiEvent, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to register IP event handler: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
wifi_event_handlers_registered_ = true;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
||||
@@ -329,12 +350,14 @@ esp_err_t GatewayNetworkService::startWifi() {
|
||||
}
|
||||
|
||||
wifi_started_ = true;
|
||||
controller_.setWirelessSetupMode(false);
|
||||
ESP_LOGI(kTag, "Wi-Fi STA started has_credentials=%d",
|
||||
device_info.wlan.has_value() && !device_info.wlan->ssid.empty());
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::startSetupAp() {
|
||||
stopSmartconfig();
|
||||
if (wifi_ap_netif_ == nullptr) {
|
||||
wifi_ap_netif_ = esp_netif_create_default_wifi_ap();
|
||||
if (wifi_ap_netif_ == nullptr) {
|
||||
@@ -397,10 +420,73 @@ esp_err_t GatewayNetworkService::startSetupAp() {
|
||||
|
||||
wifi_started_ = true;
|
||||
setup_ap_started_ = true;
|
||||
controller_.setWirelessSetupMode(true);
|
||||
ESP_LOGI(kTag, "setup AP started ssid=%s ip=192.168.3.1", kSetupApSsid);
|
||||
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() {
|
||||
if (espnow_started_) {
|
||||
return ESP_OK;
|
||||
@@ -629,6 +715,42 @@ void GatewayNetworkService::handleWifiEvent(esp_event_base_t event_base, int32_t
|
||||
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) {
|
||||
const auto info = runtime_.deviceInfo();
|
||||
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();
|
||||
info.wlan->ip.clear();
|
||||
runtime_.setWirelessInfo(std::move(*info.wlan));
|
||||
if (has_credentials) {
|
||||
if (has_credentials && !smartconfig_started_) {
|
||||
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);
|
||||
char ip[16] = {0};
|
||||
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;
|
||||
uint8_t mac[6] = {};
|
||||
if (esp_wifi_get_mac(WIFI_IF_STA, mac) == ESP_OK) {
|
||||
wireless.mac = MacToHex(mac);
|
||||
}
|
||||
runtime_.setWirelessInfo(std::move(wireless));
|
||||
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) {
|
||||
if (mode == 0) {
|
||||
config_.wifi_enabled = false;
|
||||
stopSmartconfig();
|
||||
stopEspNow();
|
||||
controller_.setWirelessSetupMode(false);
|
||||
if (wifi_started_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
||||
@@ -923,8 +1052,13 @@ void GatewayNetworkService::handleWifiControl(uint8_t mode) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(startSetupAp());
|
||||
return;
|
||||
}
|
||||
if (mode == 100) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(startSmartconfig());
|
||||
return;
|
||||
}
|
||||
if (mode == 1) {
|
||||
config_.wifi_enabled = true;
|
||||
stopSmartconfig();
|
||||
if (setup_ap_started_) {
|
||||
stopEspNow();
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
|
||||
@@ -994,6 +1128,7 @@ std::string GatewayNetworkService::gatewaySnapshotJson() {
|
||||
cJSON* gw_info = cJSON_CreateObject();
|
||||
if (gw_info != nullptr) {
|
||||
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, "wifiEnabled", snapshot.wifi_enabled);
|
||||
cJSON_AddBoolToObject(gw_info, "IPRouter", snapshot.ip_router_enabled);
|
||||
|
||||
Reference in New Issue
Block a user