Refactor GatewayModbus and GatewayNetwork components

- Updated GatewayModbusConfig to allow uart_port and pin values to be -1, indicating an unconfigured state.
- Enhanced GatewayNetworkService to support an additional setup AP button with configurable GPIO and active low settings.
- Refactored boot button configuration logic to reduce redundancy and improve clarity.
- Introduced a new method for handling GPIO input configuration.
- Improved boot button task loop to handle both boot and setup AP buttons more effectively.
- Added programming mode functionality to EtsDeviceRuntime, allowing toggling and querying of the programming state.
- Implemented memory checks to avoid unnecessary reads in EtsDeviceRuntime.
- Enhanced security storage to derive factory FDSK from the device's serial number and store it in NVS.
- Updated factory FDSK loading logic to ensure proper key generation and storage.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-13 12:36:16 +08:00
parent df1dd472cc
commit b74367e5a0
18 changed files with 2244 additions and 609 deletions
@@ -909,22 +909,37 @@ esp_err_t GatewayNetworkService::configureStatusLed() {
}
esp_err_t GatewayNetworkService::configureBootButton() {
if (config_.boot_button_gpio < 0) {
if (config_.boot_button_gpio < 0 && config_.setup_ap_button_gpio < 0) {
return ESP_OK;
}
gpio_config_t io_config = {};
io_config.pin_bit_mask = 1ULL << static_cast<uint32_t>(config_.boot_button_gpio);
io_config.mode = GPIO_MODE_INPUT;
io_config.pull_up_en = config_.boot_button_active_low ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
io_config.pull_down_en = config_.boot_button_active_low ? GPIO_PULLDOWN_DISABLE : GPIO_PULLDOWN_ENABLE;
io_config.intr_type = GPIO_INTR_DISABLE;
const esp_err_t err = gpio_config(&io_config);
const auto configure_input = [](int gpio, bool active_low, const char* name) -> esp_err_t {
if (gpio < 0) {
return ESP_OK;
}
gpio_config_t io_config = {};
io_config.pin_bit_mask = 1ULL << static_cast<uint32_t>(gpio);
io_config.mode = GPIO_MODE_INPUT;
io_config.pull_up_en = active_low ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
io_config.pull_down_en = active_low ? GPIO_PULLDOWN_DISABLE : GPIO_PULLDOWN_ENABLE;
io_config.intr_type = GPIO_INTR_DISABLE;
const esp_err_t err = gpio_config(&io_config);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to configure %s GPIO%d: %s", name, gpio, esp_err_to_name(err));
}
return err;
};
esp_err_t err = configure_input(config_.boot_button_gpio, config_.boot_button_active_low,
"Wi-Fi reset button");
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to configure boot button GPIO%d: %s", config_.boot_button_gpio,
esp_err_to_name(err));
return err;
}
return err;
if (config_.setup_ap_button_gpio == config_.boot_button_gpio) {
return ESP_OK;
}
return configure_input(config_.setup_ap_button_gpio, config_.setup_ap_button_active_low,
"setup AP button");
}
esp_err_t GatewayNetworkService::startHttpServer() {
@@ -985,7 +1000,8 @@ esp_err_t GatewayNetworkService::startUdpTask() {
}
esp_err_t GatewayNetworkService::startBootButtonTask() {
if (config_.boot_button_gpio < 0 || boot_button_task_handle_ != nullptr) {
if ((config_.boot_button_gpio < 0 && config_.setup_ap_button_gpio < 0) ||
boot_button_task_handle_ != nullptr) {
return ESP_OK;
}
@@ -1349,39 +1365,88 @@ void GatewayNetworkService::bootButtonTaskLoop() {
const TickType_t poll_ticks = pdMS_TO_TICKS(100);
const uint32_t long_press_ms = std::max<uint32_t>(config_.boot_button_long_press_ms, 100);
auto is_pressed = [this]() {
const int level = gpio_get_level(static_cast<gpio_num_t>(config_.boot_button_gpio));
return config_.boot_button_active_low ? level == 0 : level != 0;
auto is_pressed = [](int gpio, bool active_low) {
if (gpio < 0) {
return false;
}
const int level = gpio_get_level(static_cast<gpio_num_t>(gpio));
return active_low ? level == 0 : level != 0;
};
while (true) {
if (!is_pressed()) {
vTaskDelay(poll_ticks);
continue;
}
auto wait_release = [&](int gpio, bool active_low) {
uint32_t pressed_ms = 0;
while (is_pressed()) {
while (is_pressed(gpio, active_low)) {
vTaskDelay(poll_ticks);
pressed_ms += 100;
}
return pressed_ms;
};
if (pressed_ms >= long_press_ms) {
ESP_LOGW(kTag, "BOOT long press clears Wi-Fi credentials and restarts");
runtime_.clearWirelessInfo();
stopEspNow();
if (wifi_started_) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
auto enter_setup_ap = [this]() {
ESP_LOGI(kTag, "setup AP button enters setup AP mode");
const uint32_t stack_size = std::max<uint32_t>(config_.boot_button_task_stack_size, 8192);
const BaseType_t created = xTaskCreate(
[](void* arg) {
auto* service = static_cast<GatewayNetworkService*>(arg);
service->handleWifiControl(101);
vTaskDelete(nullptr);
},
"gateway_setup_ap", stack_size, this, config_.boot_button_task_priority, nullptr);
if (created != pdPASS) {
ESP_LOGE(kTag, "failed to create setup AP task");
}
};
auto clear_wifi_and_restart = [this]() {
ESP_LOGW(kTag, "Wi-Fi reset button clears Wi-Fi credentials and restarts");
runtime_.clearWirelessInfo();
stopEspNow();
if (wifi_started_) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_disconnect());
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop());
}
vTaskDelay(pdMS_TO_TICKS(300));
esp_restart();
};
while (true) {
const bool same_button = config_.boot_button_gpio >= 0 &&
config_.boot_button_gpio == config_.setup_ap_button_gpio;
if (same_button && is_pressed(config_.boot_button_gpio, config_.boot_button_active_low)) {
const uint32_t pressed_ms = wait_release(config_.boot_button_gpio,
config_.boot_button_active_low);
if (pressed_ms >= long_press_ms) {
clear_wifi_and_restart();
} else {
enter_setup_ap();
}
vTaskDelay(pdMS_TO_TICKS(300));
esp_restart();
} else {
ESP_LOGI(kTag, "BOOT short press enters setup AP mode");
handleWifiControl(101);
continue;
}
vTaskDelay(pdMS_TO_TICKS(300));
if (is_pressed(config_.setup_ap_button_gpio, config_.setup_ap_button_active_low)) {
wait_release(config_.setup_ap_button_gpio, config_.setup_ap_button_active_low);
enter_setup_ap();
vTaskDelay(pdMS_TO_TICKS(300));
continue;
}
if (is_pressed(config_.boot_button_gpio, config_.boot_button_active_low)) {
const uint32_t pressed_ms = wait_release(config_.boot_button_gpio,
config_.boot_button_active_low);
if (pressed_ms >= long_press_ms) {
clear_wifi_and_restart();
}
vTaskDelay(pdMS_TO_TICKS(300));
continue;
}
if (config_.setup_ap_button_gpio < 0 && config_.boot_button_gpio < 0) {
vTaskDelete(nullptr);
return;
}
vTaskDelay(poll_ticks);
}
}