feat(runtime): add integration hardening and diagnostics

This commit is contained in:
2026-09-05 21:56:09 +03:00
parent ffdaa299bc
commit c730cb818d
19 changed files with 956 additions and 160 deletions
+82 -80
View File
@@ -13,42 +13,61 @@
#define MAZE32_STATION_RETRY_LIMIT 3U
#define MAZE32_STATION_TIMEOUT_MS 12000U
static const char *TAG = "maze32_network";
static maze32_network_t *active_network;
static const char* TAG = "maze32_network";
static maze32_network_t* active_network;
static void set_state(maze32_network_t *network, maze32_network_state_t state)
{
static void set_state(maze32_network_t* network, maze32_network_state_t state) {
network->state = state;
ESP_LOGI(TAG, "Network state: %s", maze32_network_state_name(state));
}
static void network_event(void *argument,
esp_event_base_t event_base,
int32_t event_id,
void *event_data)
{
static void network_event(void* argument, esp_event_base_t event_base, int32_t event_id,
void* event_data) {
(void)event_data;
maze32_network_t *network = argument;
maze32_network_t* network = argument;
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START &&
network->state == MAZE32_NETWORK_STATION_CONNECTING) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED &&
network->state == MAZE32_NETWORK_STATION_CONNECTING) {
(network->state == MAZE32_NETWORK_STATION_CONNECTING ||
network->state == MAZE32_NETWORK_STATION_CONNECTED)) {
if (network->state == MAZE32_NETWORK_STATION_CONNECTED) {
set_state(network, MAZE32_NETWORK_STATION_CONNECTING);
network->retry_count = 0U;
}
if (network->retry_count < MAZE32_STATION_RETRY_LIMIT) {
++network->retry_count;
ESP_LOGW(TAG, "Station connection failed; retry %u/%u",
network->retry_count, MAZE32_STATION_RETRY_LIMIT);
ESP_LOGW(TAG, "Station connection failed; retry %u/%u", network->retry_count,
MAZE32_STATION_RETRY_LIMIT);
esp_wifi_connect();
} else {
} else if (network->station_wait_active) {
xEventGroupSetBits(network->events, MAZE32_FAILED_BIT);
} else if (network->server != NULL) {
set_state(network, MAZE32_NETWORK_PROVISIONING);
esp_err_t result =
httpd_queue_work(network->server, maze32_network_return_to_provisioning, network);
if (result != ESP_OK) {
ESP_LOGE(TAG, "Could not schedule runtime fallback: %s", esp_err_to_name(result));
set_state(network, MAZE32_NETWORK_ERROR);
}
} else {
set_state(network, MAZE32_NETWORK_ERROR);
}
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(network->events, MAZE32_CONNECTED_BIT);
if (network->station_wait_active) {
xEventGroupSetBits(network->events, MAZE32_CONNECTED_BIT);
} else if (network->state == MAZE32_NETWORK_STATION_CONNECTING) {
network->retry_count = 0U;
set_state(network, MAZE32_NETWORK_STATION_CONNECTED);
char address[16];
if (maze32_network_ip_address(network, address, sizeof(address)) == ESP_OK) {
ESP_LOGI(TAG, "Runtime station reconnect succeeded: IP=%s", address);
}
}
}
}
static esp_err_t stop_wifi(void)
{
static esp_err_t stop_wifi(void) {
esp_err_t result = esp_wifi_stop();
if (result == ESP_ERR_WIFI_NOT_STARTED || result == ESP_ERR_WIFI_NOT_INIT) {
return ESP_OK;
@@ -56,8 +75,7 @@ static esp_err_t stop_wifi(void)
return result;
}
static esp_err_t start_provisioning(maze32_network_t *network)
{
static esp_err_t start_provisioning(maze32_network_t* network) {
set_state(network, MAZE32_NETWORK_PROVISIONING);
esp_err_t result = stop_wifi();
if (result != ESP_OK) {
@@ -89,9 +107,8 @@ static esp_err_t start_provisioning(maze32_network_t *network)
return result;
}
static esp_err_t connect_station(maze32_network_t *network,
const maze32_wifi_credentials_t *credentials)
{
static esp_err_t connect_station(maze32_network_t* network,
const maze32_wifi_credentials_t* credentials) {
esp_err_t result = stop_wifi();
if (result != ESP_OK) {
return result;
@@ -113,19 +130,19 @@ static esp_err_t connect_station(maze32_network_t *network,
result = esp_wifi_set_config(WIFI_IF_STA, &station);
}
if (result == ESP_OK) {
network->station_wait_active = 1;
result = esp_wifi_start();
}
if (result != ESP_OK) {
network->station_wait_active = 0;
ESP_LOGE(TAG, "Station startup failed: %s", esp_err_to_name(result));
return start_provisioning(network);
}
EventBits_t bits = xEventGroupWaitBits(
network->events,
MAZE32_CONNECTED_BIT | MAZE32_FAILED_BIT,
pdTRUE,
pdFALSE,
pdMS_TO_TICKS(MAZE32_STATION_TIMEOUT_MS));
EventBits_t bits =
xEventGroupWaitBits(network->events, MAZE32_CONNECTED_BIT | MAZE32_FAILED_BIT, pdTRUE,
pdFALSE, pdMS_TO_TICKS(MAZE32_STATION_TIMEOUT_MS));
network->station_wait_active = 0;
if ((bits & MAZE32_CONNECTED_BIT) != 0U) {
set_state(network, MAZE32_NETWORK_STATION_CONNECTED);
char address[16];
@@ -139,22 +156,18 @@ static esp_err_t connect_station(maze32_network_t *network,
return start_provisioning(network);
}
static int save_credentials(const maze32_wifi_credentials_t *credentials, void *context)
{
static int save_credentials(const maze32_wifi_credentials_t* credentials, void* context) {
(void)context;
return maze32_config_store_save(credentials) == ESP_OK ? 0 : -1;
}
static int clear_credentials(void *context)
{
static int clear_credentials(void* context) {
(void)context;
return maze32_config_store_clear() == ESP_OK ? 0 : -1;
}
esp_err_t maze32_network_init(maze32_network_t *network,
maze32_reconfiguration_guard_fn guard,
void *guard_context)
{
esp_err_t maze32_network_init(maze32_network_t* network, maze32_reconfiguration_guard_fn guard,
void* guard_context) {
if (network == NULL || guard == NULL || active_network != NULL) {
return ESP_ERR_INVALID_ARG;
}
@@ -173,8 +186,8 @@ esp_err_t maze32_network_init(maze32_network_t *network,
if (result != ESP_OK) {
return result;
}
snprintf(network->access_point_ssid, sizeof(network->access_point_ssid),
"maze32-%02X%02X", mac[4], mac[5]);
snprintf(network->access_point_ssid, sizeof(network->access_point_ssid), "maze32-%02X%02X",
mac[4], mac[5]);
wifi_init_config_t wifi_configuration = WIFI_INIT_CONFIG_DEFAULT();
result = esp_wifi_init(&wifi_configuration);
@@ -196,8 +209,7 @@ esp_err_t maze32_network_init(maze32_network_t *network,
return result;
}
esp_err_t maze32_network_start(maze32_network_t *network)
{
esp_err_t maze32_network_start(maze32_network_t* network) {
if (network == NULL || xSemaphoreTake(network->operation_mutex, portMAX_DELAY) != pdTRUE) {
return ESP_ERR_INVALID_ARG;
}
@@ -214,16 +226,13 @@ esp_err_t maze32_network_start(maze32_network_t *network)
return result;
}
esp_err_t maze32_network_apply_credentials(maze32_network_t *network,
const maze32_wifi_credentials_t *credentials)
{
esp_err_t maze32_network_apply_credentials(maze32_network_t* network,
const maze32_wifi_credentials_t* credentials) {
if (network == NULL) {
return ESP_ERR_INVALID_ARG;
}
int result = maze32_wifi_config_apply(credentials,
network->reconfiguration_guard,
save_credentials,
network->guard_context);
int result = maze32_wifi_config_apply(credentials, network->reconfiguration_guard,
save_credentials, network->guard_context);
if (result == -1) {
return ESP_ERR_INVALID_ARG;
}
@@ -233,13 +242,11 @@ esp_err_t maze32_network_apply_credentials(maze32_network_t *network,
return result == 0 ? ESP_OK : ESP_FAIL;
}
esp_err_t maze32_network_reset_credentials(maze32_network_t *network)
{
esp_err_t maze32_network_reset_credentials(maze32_network_t* network) {
if (network == NULL) {
return ESP_ERR_INVALID_ARG;
}
int result = maze32_wifi_config_reset(network->reconfiguration_guard,
clear_credentials,
int result = maze32_wifi_config_reset(network->reconfiguration_guard, clear_credentials,
network->guard_context);
if (result == -2) {
return ESP_ERR_INVALID_STATE;
@@ -247,9 +254,8 @@ esp_err_t maze32_network_reset_credentials(maze32_network_t *network)
return result == 0 ? ESP_OK : ESP_FAIL;
}
void maze32_network_retry_saved_credentials(void *context)
{
maze32_network_t *network = context;
void maze32_network_retry_saved_credentials(void* context) {
maze32_network_t* network = context;
if (network == NULL || xSemaphoreTake(network->operation_mutex, portMAX_DELAY) != pdTRUE) {
return;
}
@@ -265,9 +271,8 @@ void maze32_network_retry_saved_credentials(void *context)
xSemaphoreGive(network->operation_mutex);
}
void maze32_network_return_to_provisioning(void *context)
{
maze32_network_t *network = context;
void maze32_network_return_to_provisioning(void* context) {
maze32_network_t* network = context;
if (network == NULL || xSemaphoreTake(network->operation_mutex, portMAX_DELAY) != pdTRUE) {
return;
}
@@ -278,11 +283,8 @@ void maze32_network_return_to_provisioning(void *context)
xSemaphoreGive(network->operation_mutex);
}
esp_err_t maze32_network_scan(maze32_network_t *network,
maze32_wifi_scan_result_t *results,
size_t capacity,
size_t *count)
{
esp_err_t maze32_network_scan(maze32_network_t* network, maze32_wifi_scan_result_t* results,
size_t capacity, size_t* count) {
if (network == NULL || results == NULL || count == NULL || capacity == 0U ||
capacity > MAZE32_WIFI_SCAN_LIMIT ||
xSemaphoreTake(network->operation_mutex, pdMS_TO_TICKS(1000U)) != pdTRUE) {
@@ -313,34 +315,34 @@ esp_err_t maze32_network_scan(maze32_network_t *network,
return result;
}
maze32_network_state_t maze32_network_state(const maze32_network_t *network)
{
maze32_network_state_t maze32_network_state(const maze32_network_t* network) {
return network == NULL ? MAZE32_NETWORK_ERROR : network->state;
}
const char *maze32_network_state_name(maze32_network_state_t state)
{
const char* maze32_network_state_name(maze32_network_state_t state) {
switch (state) {
case MAZE32_NETWORK_BOOTING: return "booting";
case MAZE32_NETWORK_STATION_CONNECTING: return "station_connecting";
case MAZE32_NETWORK_STATION_CONNECTED: return "station_connected";
case MAZE32_NETWORK_PROVISIONING: return "provisioning";
case MAZE32_NETWORK_ERROR: return "error";
default: return "unknown";
case MAZE32_NETWORK_BOOTING:
return "booting";
case MAZE32_NETWORK_STATION_CONNECTING:
return "station_connecting";
case MAZE32_NETWORK_STATION_CONNECTED:
return "station_connected";
case MAZE32_NETWORK_PROVISIONING:
return "provisioning";
case MAZE32_NETWORK_ERROR:
return "error";
default:
return "unknown";
}
}
const char *maze32_network_access_point_ssid(const maze32_network_t *network)
{
const char* maze32_network_access_point_ssid(const maze32_network_t* network) {
return network == NULL ? "" : network->access_point_ssid;
}
esp_err_t maze32_network_ip_address(const maze32_network_t *network,
char *output,
size_t output_size)
{
if (network == NULL || output == NULL || output_size < 16U ||
network->station_netif == NULL) {
esp_err_t maze32_network_ip_address(const maze32_network_t* network, char* output,
size_t output_size) {
if (network == NULL || output == NULL || output_size < 16U || network->station_netif == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_netif_ip_info_t information;