|
|
@@ -2,6 +2,7 @@
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include "app_config.h"
|
|
|
|
#include "app_config.h"
|
|
|
|
#include "application.h"
|
|
|
|
#include "application.h"
|
|
|
@@ -40,6 +41,8 @@ static portMUX_TYPE s_diagnostics_lock = portMUX_INITIALIZER_UNLOCKED;
|
|
|
|
static wifi_state_t s_wifi_state = {0};
|
|
|
|
static wifi_state_t s_wifi_state = {0};
|
|
|
|
static bool s_littlefs_mounted;
|
|
|
|
static bool s_littlefs_mounted;
|
|
|
|
static httpd_handle_t s_server;
|
|
|
|
static httpd_handle_t s_server;
|
|
|
|
|
|
|
|
static esp_netif_t *s_station_netif;
|
|
|
|
|
|
|
|
static esp_netif_t *s_access_point_netif;
|
|
|
|
static application_t s_application;
|
|
|
|
static application_t s_application;
|
|
|
|
static http_api_t s_api;
|
|
|
|
static http_api_t s_api;
|
|
|
|
static sync_service_t s_sync;
|
|
|
|
static sync_service_t s_sync;
|
|
|
@@ -52,6 +55,7 @@ static network_configuration_t s_configuration;
|
|
|
|
static bool s_scan_pending;
|
|
|
|
static bool s_scan_pending;
|
|
|
|
static bool s_scan_ready;
|
|
|
|
static bool s_scan_ready;
|
|
|
|
static bool s_dns_running;
|
|
|
|
static bool s_dns_running;
|
|
|
|
|
|
|
|
static bool s_validation_waiting_for_disconnect;
|
|
|
|
|
|
|
|
|
|
|
|
enum { kNetworkRequestBytes = 160U, kNetworkResponseBytes = 768U };
|
|
|
|
enum { kNetworkRequestBytes = 160U, kNetworkResponseBytes = 768U };
|
|
|
|
|
|
|
|
|
|
|
@@ -266,14 +270,20 @@ static bool fallback_active(void) {
|
|
|
|
return state.fallback_active;
|
|
|
|
return state.fallback_active;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void log_netif_ip(const char *name, esp_netif_t *netif) {
|
|
|
|
|
|
|
|
esp_netif_ip_info_t info = {0};
|
|
|
|
|
|
|
|
if (netif == NULL || esp_netif_get_ip_info(netif, &info) != ESP_OK) { ESP_LOGW(kLogTag, "%s IP unavailable", name); return; }
|
|
|
|
|
|
|
|
ESP_LOGI(kLogTag, "%s IP " IPSTR, name, IP2STR(&info.ip));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void captive_dns_task(void *unused) {
|
|
|
|
static void captive_dns_task(void *unused) {
|
|
|
|
(void)unused;
|
|
|
|
(void)unused;
|
|
|
|
const int socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
|
|
const int socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
|
|
if (socket_fd < 0) { ESP_LOGW(kLogTag, "captive DNS unavailable"); s_dns_running = false; vTaskDelete(NULL); return; }
|
|
|
|
if (socket_fd < 0) { ESP_LOGE(kLogTag, "captive DNS socket failed: errno=%d", errno); s_dns_running = false; vTaskDelete(NULL); return; }
|
|
|
|
struct sockaddr_in local = {.sin_family = AF_INET, .sin_port = htons(kDnsPort), .sin_addr.s_addr = htonl(INADDR_ANY)};
|
|
|
|
struct sockaddr_in local = {.sin_family = AF_INET, .sin_port = htons(kDnsPort), .sin_addr.s_addr = htonl(INADDR_ANY)};
|
|
|
|
if (bind(socket_fd, (struct sockaddr *)&local, sizeof(local)) != 0) { close(socket_fd); s_dns_running = false; vTaskDelete(NULL); return; }
|
|
|
|
if (bind(socket_fd, (struct sockaddr *)&local, sizeof(local)) != 0) { ESP_LOGE(kLogTag, "captive DNS bind failed: errno=%d", errno); close(socket_fd); s_dns_running = false; vTaskDelete(NULL); return; }
|
|
|
|
const struct timeval timeout = {.tv_sec = 1, .tv_usec = 0};
|
|
|
|
const struct timeval timeout = {.tv_sec = 1, .tv_usec = 0};
|
|
|
|
setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
|
|
|
if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) ESP_LOGW(kLogTag, "captive DNS timeout setup failed: errno=%d", errno);
|
|
|
|
const uint8_t address[] = {192U, 168U, 4U, 1U};
|
|
|
|
const uint8_t address[] = {192U, 168U, 4U, 1U};
|
|
|
|
while (fallback_active()) {
|
|
|
|
while (fallback_active()) {
|
|
|
|
uint8_t query[256]; uint8_t response[272]; struct sockaddr_in client = {0}; socklen_t client_length = sizeof(client);
|
|
|
|
uint8_t query[256]; uint8_t response[272]; struct sockaddr_in client = {0}; socklen_t client_length = sizeof(client);
|
|
|
@@ -290,14 +300,22 @@ static void start_captive_dns(void) {
|
|
|
|
if (xTaskCreate(captive_dns_task, "captive_dns", 3072U, NULL, 3U, NULL) != pdPASS) { s_dns_running = false; ESP_LOGW(kLogTag, "captive DNS task unavailable"); }
|
|
|
|
if (xTaskCreate(captive_dns_task, "captive_dns", 3072U, NULL, 3U, NULL) != pdPASS) { s_dns_running = false; ESP_LOGW(kLogTag, "captive DNS task unavailable"); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void enable_fallback_ap(void) {
|
|
|
|
static bool enable_fallback_ap(void) {
|
|
|
|
wifi_config_t access_point = {0};
|
|
|
|
wifi_config_t access_point = {0};
|
|
|
|
snprintf((char *)access_point.ap.ssid, sizeof(access_point.ap.ssid), "%s", "Battleship-open");
|
|
|
|
snprintf((char *)access_point.ap.ssid, sizeof(access_point.ap.ssid), "%s", "Battleship-open");
|
|
|
|
access_point.ap.ssid_len = strlen((const char *)access_point.ap.ssid);
|
|
|
|
access_point.ap.ssid_len = strlen((const char *)access_point.ap.ssid);
|
|
|
|
access_point.ap.channel = 1U; access_point.ap.max_connection = kFallbackMaxConnections; access_point.ap.authmode = WIFI_AUTH_OPEN;
|
|
|
|
access_point.ap.channel = 1U; access_point.ap.max_connection = kFallbackMaxConnections; access_point.ap.authmode = WIFI_AUTH_OPEN;
|
|
|
|
if (esp_wifi_set_config(WIFI_IF_AP, &access_point) != ESP_OK || esp_wifi_set_mode(WIFI_MODE_APSTA) != ESP_OK) { ESP_LOGW(kLogTag, "fallback access point unavailable"); return; }
|
|
|
|
const esp_err_t config_result = esp_wifi_set_config(WIFI_IF_AP, &access_point);
|
|
|
|
|
|
|
|
const esp_err_t mode_result = esp_wifi_set_mode(WIFI_MODE_APSTA);
|
|
|
|
|
|
|
|
if (config_result != ESP_OK || mode_result != ESP_OK) {
|
|
|
|
|
|
|
|
ESP_LOGE(kLogTag, "fallback AP attempt failed: config=%s mode=%s", esp_err_to_name(config_result), esp_err_to_name(mode_result));
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.fallback_active = true; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.fallback_active = true; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
start_captive_dns();
|
|
|
|
start_captive_dns();
|
|
|
|
|
|
|
|
log_netif_ip("AP", s_access_point_netif);
|
|
|
|
|
|
|
|
ESP_LOGI(kLogTag, "fallback AP active");
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void disable_fallback_ap(void) {
|
|
|
|
static void disable_fallback_ap(void) {
|
|
|
@@ -340,8 +358,12 @@ static const char *configuration_message(void) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void apply_network_action(network_action_t action) {
|
|
|
|
static void apply_network_action(network_action_t action) {
|
|
|
|
if (action == NETWORK_ACTION_FALLBACK) enable_fallback_ap();
|
|
|
|
if (action == NETWORK_ACTION_FALLBACK) { ESP_LOGW(kLogTag, "network transition: fallback"); enable_fallback_ap(); }
|
|
|
|
else if (action == NETWORK_ACTION_CONNECT && esp_wifi_connect() != ESP_OK) ESP_LOGW(kLogTag, "saved network connection request failed");
|
|
|
|
else if (action == NETWORK_ACTION_CONNECT) {
|
|
|
|
|
|
|
|
const esp_err_t result = esp_wifi_connect();
|
|
|
|
|
|
|
|
if (result != ESP_OK) ESP_LOGW(kLogTag, "saved network connection request failed: %s", esp_err_to_name(result));
|
|
|
|
|
|
|
|
else ESP_LOGI(kLogTag, "network transition: connection requested");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static network_action_t network_event_action(bool connected) {
|
|
|
|
static network_action_t network_event_action(bool connected) {
|
|
|
@@ -357,7 +379,11 @@ static void network_timer_callback(void *unused) {
|
|
|
|
const uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000U);
|
|
|
|
const uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000U);
|
|
|
|
network_action_t action;
|
|
|
|
network_action_t action;
|
|
|
|
portENTER_CRITICAL(&s_network_lock); action = network_manager_tick(&s_network_manager, now_ms); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
portENTER_CRITICAL(&s_network_lock); action = network_manager_tick(&s_network_manager, now_ms); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
|
|
|
|
if (action == NETWORK_ACTION_FALLBACK) ESP_LOGW(kLogTag, "saved network connection timed out after 30 seconds");
|
|
|
|
apply_network_action(action);
|
|
|
|
apply_network_action(action);
|
|
|
|
|
|
|
|
bool must_retry_fallback = false;
|
|
|
|
|
|
|
|
portENTER_CRITICAL(&s_network_lock); must_retry_fallback = s_network_manager.state == NETWORK_STATE_FALLBACK; portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
|
|
|
|
if (must_retry_fallback && !fallback_active()) { ESP_LOGW(kLogTag, "retrying fallback AP startup"); enable_fallback_ap(); }
|
|
|
|
bool validation_timed_out = false; bool success_notice_expired = false; network_profile_t previous = {0}; bool had_previous = false;
|
|
|
|
bool validation_timed_out = false; bool success_notice_expired = false; network_profile_t previous = {0}; bool had_previous = false;
|
|
|
|
portENTER_CRITICAL(&s_configuration_lock);
|
|
|
|
portENTER_CRITICAL(&s_configuration_lock);
|
|
|
|
validation_timed_out = network_configuration_validation_timed_out(&s_configuration, now_ms);
|
|
|
|
validation_timed_out = network_configuration_validation_timed_out(&s_configuration, now_ms);
|
|
|
@@ -396,31 +422,43 @@ static void validation_connected_work(void *unused) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void wifi_event_handler(void *argument, esp_event_base_t event_base, int32_t event_id, void *event_data) {
|
|
|
|
static void wifi_event_handler(void *argument, esp_event_base_t event_base, int32_t event_id, void *event_data) {
|
|
|
|
(void)argument; (void)event_data;
|
|
|
|
(void)argument;
|
|
|
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) apply_network_action(network_event_action(false));
|
|
|
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { ESP_LOGI(kLogTag, "Wi-Fi event: STA started"); apply_network_action(network_event_action(false)); }
|
|
|
|
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
|
|
|
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
|
|
|
|
|
|
|
const wifi_event_sta_disconnected_t *disconnected = event_data;
|
|
|
|
|
|
|
|
ESP_LOGW(kLogTag, "Wi-Fi event: STA disconnected, reason=%u", disconnected == NULL ? 0U : (unsigned int)disconnected->reason);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.connected = false; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.connected = false; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
if (!configuration_is_validating()) apply_network_action(network_event_action(false));
|
|
|
|
if (configuration_is_validating()) {
|
|
|
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { s_scan_pending = false; s_scan_ready = true; }
|
|
|
|
portENTER_CRITICAL(&s_configuration_lock); s_validation_waiting_for_disconnect = false; portEXIT_CRITICAL(&s_configuration_lock);
|
|
|
|
|
|
|
|
} else apply_network_action(network_event_action(false));
|
|
|
|
|
|
|
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { ESP_LOGI(kLogTag, "Wi-Fi event: scan complete"); s_scan_pending = false; s_scan_ready = true; }
|
|
|
|
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
|
|
|
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
|
|
|
bool validating = configuration_is_validating();
|
|
|
|
bool validating = configuration_is_validating();
|
|
|
|
if (validating) {
|
|
|
|
if (validating) {
|
|
|
|
if (s_server != NULL) httpd_queue_work(s_server, validation_connected_work, NULL);
|
|
|
|
bool accepts_ip;
|
|
|
|
|
|
|
|
portENTER_CRITICAL(&s_configuration_lock); accepts_ip = !s_validation_waiting_for_disconnect; portEXIT_CRITICAL(&s_configuration_lock);
|
|
|
|
|
|
|
|
if (accepts_ip && s_server != NULL) httpd_queue_work(s_server, validation_connected_work, NULL);
|
|
|
|
} else network_event_action(true);
|
|
|
|
} else network_event_action(true);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.connected = true; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.connected = true; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
|
|
|
|
log_netif_ip("STA", s_station_netif);
|
|
|
|
if (!validating && fallback_active()) disable_fallback_ap();
|
|
|
|
if (!validating && fallback_active()) disable_fallback_ap();
|
|
|
|
}
|
|
|
|
} else if (event_base == WIFI_EVENT) ESP_LOGW(kLogTag, "Wi-Fi event ignored: id=%ld", (long)event_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static esp_err_t start_wifi(void) {
|
|
|
|
static esp_err_t start_wifi(void) {
|
|
|
|
network_profile_t profile = {0};
|
|
|
|
network_profile_t profile = {0};
|
|
|
|
const bool has_profile = network_credential_store_load(&profile);
|
|
|
|
const network_credential_load_result_t load_result = network_credential_store_load(&profile);
|
|
|
|
|
|
|
|
const bool has_profile = load_result == NETWORK_CREDENTIAL_LOAD_VALID;
|
|
|
|
wifi_init_config_t init_config = WIFI_INIT_CONFIG_DEFAULT();
|
|
|
|
wifi_init_config_t init_config = WIFI_INIT_CONFIG_DEFAULT();
|
|
|
|
wifi_config_t station_config = {0};
|
|
|
|
wifi_config_t station_config = {0};
|
|
|
|
const uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000U);
|
|
|
|
const uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000U);
|
|
|
|
portENTER_CRITICAL(&s_network_lock); const network_action_t initial_action = network_manager_init(&s_network_manager, has_profile ? &profile : NULL, now_ms); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
portENTER_CRITICAL(&s_network_lock); network_action_t initial_action = network_manager_init(&s_network_manager, has_profile ? &profile : NULL, now_ms); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.configured = has_profile; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.configured = has_profile; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
esp_netif_create_default_wifi_sta(); esp_netif_create_default_wifi_ap();
|
|
|
|
if (load_result == NETWORK_CREDENTIAL_LOAD_VALID) ESP_LOGI(kLogTag, "saved network profile accepted");
|
|
|
|
|
|
|
|
else if (load_result == NETWORK_CREDENTIAL_LOAD_ABSENT) ESP_LOGW(kLogTag, "saved network profile absent; starting fallback");
|
|
|
|
|
|
|
|
else ESP_LOGE(kLogTag, "saved network profile invalid or unsupported; starting fallback");
|
|
|
|
|
|
|
|
s_station_netif = esp_netif_create_default_wifi_sta(); s_access_point_netif = esp_netif_create_default_wifi_ap();
|
|
|
|
|
|
|
|
if (s_station_netif == NULL || s_access_point_netif == NULL) return ESP_ERR_NO_MEM;
|
|
|
|
ESP_RETURN_ON_ERROR(esp_wifi_init(&init_config), kLogTag, "wifi initialization failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_wifi_init(&init_config), kLogTag, "wifi initialization failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, NULL), kLogTag, "wifi event registration failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, NULL), kLogTag, "wifi event registration failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, NULL), kLogTag, "ip event registration failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, NULL), kLogTag, "ip event registration failed");
|
|
|
@@ -429,9 +467,20 @@ static esp_err_t start_wifi(void) {
|
|
|
|
if (has_profile) {
|
|
|
|
if (has_profile) {
|
|
|
|
memcpy(station_config.sta.ssid, profile.ssid, kNetworkSsidBytes);
|
|
|
|
memcpy(station_config.sta.ssid, profile.ssid, kNetworkSsidBytes);
|
|
|
|
memcpy(station_config.sta.password, profile.password, kNetworkPasswordBytes);
|
|
|
|
memcpy(station_config.sta.password, profile.password, kNetworkPasswordBytes);
|
|
|
|
ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_STA, &station_config), kLogTag, "saved network configuration failed");
|
|
|
|
const esp_err_t station_result = esp_wifi_set_config(WIFI_IF_STA, &station_config);
|
|
|
|
|
|
|
|
if (station_result != ESP_OK) {
|
|
|
|
|
|
|
|
ESP_LOGE(kLogTag, "saved network configuration rejected: %s; falling back", esp_err_to_name(station_result));
|
|
|
|
|
|
|
|
portENTER_CRITICAL(&s_network_lock); initial_action = network_manager_init(&s_network_manager, NULL, now_ms); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.configured = false; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ESP_RETURN_ON_ERROR(esp_wifi_set_mode(initial_action == NETWORK_ACTION_FALLBACK ? WIFI_MODE_AP : WIFI_MODE_STA), kLogTag, "wifi mode setup failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t mode_result = esp_wifi_set_mode(initial_action == NETWORK_ACTION_FALLBACK ? WIFI_MODE_AP : WIFI_MODE_STA);
|
|
|
|
|
|
|
|
if (mode_result != ESP_OK && initial_action != NETWORK_ACTION_FALLBACK) {
|
|
|
|
|
|
|
|
ESP_LOGE(kLogTag, "STA mode setup failed: %s; attempting fallback", esp_err_to_name(mode_result));
|
|
|
|
|
|
|
|
portENTER_CRITICAL(&s_network_lock); initial_action = network_manager_init(&s_network_manager, NULL, now_ms); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
|
|
|
|
mode_result = esp_wifi_set_mode(WIFI_MODE_AP);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ESP_RETURN_ON_ERROR(mode_result, kLogTag, "wifi mode setup failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_wifi_start(), kLogTag, "wifi start failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_wifi_start(), kLogTag, "wifi start failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_timer_start_periodic(s_network_timer, 1000000U), kLogTag, "network timer start failed");
|
|
|
|
ESP_RETURN_ON_ERROR(esp_timer_start_periodic(s_network_timer, 1000000U), kLogTag, "network timer start failed");
|
|
|
|
if (initial_action == NETWORK_ACTION_FALLBACK) enable_fallback_ap();
|
|
|
|
if (initial_action == NETWORK_ACTION_FALLBACK) enable_fallback_ap();
|
|
|
@@ -573,7 +622,10 @@ static esp_err_t network_validate_handler(httpd_req_t *request) {
|
|
|
|
started = network_configuration_begin(&s_configuration, &s_network_manager, &candidate, now_ms);
|
|
|
|
started = network_configuration_begin(&s_configuration, &s_network_manager, &candidate, now_ms);
|
|
|
|
portEXIT_CRITICAL(&s_configuration_lock); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
portEXIT_CRITICAL(&s_configuration_lock); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
if (!started) { network_response(request, 409U, "{\"ok\":false,\"code\":\"NETWORK_BUSY\",\"message\":\"Другая операция уже выполняется.\"}"); return ESP_OK; }
|
|
|
|
if (!started) { network_response(request, 409U, "{\"ok\":false,\"code\":\"NETWORK_BUSY\",\"message\":\"Другая операция уже выполняется.\"}"); return ESP_OK; }
|
|
|
|
enable_fallback_ap(); esp_wifi_disconnect();
|
|
|
|
enable_fallback_ap();
|
|
|
|
|
|
|
|
portENTER_CRITICAL(&s_configuration_lock); s_validation_waiting_for_disconnect = true; portEXIT_CRITICAL(&s_configuration_lock);
|
|
|
|
|
|
|
|
const esp_err_t disconnect_result = esp_wifi_disconnect();
|
|
|
|
|
|
|
|
if (disconnect_result == ESP_ERR_WIFI_NOT_CONNECT) { portENTER_CRITICAL(&s_configuration_lock); s_validation_waiting_for_disconnect = false; portEXIT_CRITICAL(&s_configuration_lock); }
|
|
|
|
if (configure_station_profile(&candidate) != ESP_OK || esp_wifi_connect() != ESP_OK) {
|
|
|
|
if (configure_station_profile(&candidate) != ESP_OK || esp_wifi_connect() != ESP_OK) {
|
|
|
|
portENTER_CRITICAL(&s_network_lock); portENTER_CRITICAL(&s_configuration_lock);
|
|
|
|
portENTER_CRITICAL(&s_network_lock); portENTER_CRITICAL(&s_configuration_lock);
|
|
|
|
network_configuration_finish(&s_configuration, &s_network_manager, false, now_ms);
|
|
|
|
network_configuration_finish(&s_configuration, &s_network_manager, false, now_ms);
|
|
|
@@ -588,7 +640,7 @@ static esp_err_t network_delete_handler(httpd_req_t *request) {
|
|
|
|
if (!network_credential_store_delete()) { network_response(request, 503U, "{\"ok\":false,\"code\":\"STORAGE_UNAVAILABLE\",\"message\":\"Не удалось удалить сохранённую сеть.\"}"); return ESP_OK; }
|
|
|
|
if (!network_credential_store_delete()) { network_response(request, 503U, "{\"ok\":false,\"code\":\"STORAGE_UNAVAILABLE\",\"message\":\"Не удалось удалить сохранённую сеть.\"}"); return ESP_OK; }
|
|
|
|
const uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000U);
|
|
|
|
const uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000U);
|
|
|
|
portENTER_CRITICAL(&s_network_lock); network_manager_init(&s_network_manager, NULL, now_ms); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
portENTER_CRITICAL(&s_network_lock); network_manager_init(&s_network_manager, NULL, now_ms); portEXIT_CRITICAL(&s_network_lock);
|
|
|
|
portENTER_CRITICAL(&s_configuration_lock); network_configuration_init(&s_configuration); portEXIT_CRITICAL(&s_configuration_lock);
|
|
|
|
portENTER_CRITICAL(&s_configuration_lock); network_configuration_init(&s_configuration); s_validation_waiting_for_disconnect = false; portEXIT_CRITICAL(&s_configuration_lock);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.configured = false; s_wifi_state.connected = false; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
portENTER_CRITICAL(&s_wifi_lock); s_wifi_state.configured = false; s_wifi_state.connected = false; portEXIT_CRITICAL(&s_wifi_lock);
|
|
|
|
esp_wifi_disconnect(); configure_station_profile(NULL); enable_fallback_ap();
|
|
|
|
esp_wifi_disconnect(); configure_station_profile(NULL); enable_fallback_ap();
|
|
|
|
network_response(request, 200U, "{\"ok\":true,\"state\":\"fallback\",\"message\":\"Сохранённая сеть удалена. Battleship-open остаётся доступной.\"}"); return ESP_OK;
|
|
|
|
network_response(request, 200U, "{\"ok\":true,\"state\":\"fallback\",\"message\":\"Сохранённая сеть удалена. Battleship-open остаётся доступной.\"}"); return ESP_OK;
|
|
|
@@ -617,7 +669,8 @@ static esp_err_t static_file_handler(httpd_req_t *request) {
|
|
|
|
static esp_err_t start_http_server(void) {
|
|
|
|
static esp_err_t start_http_server(void) {
|
|
|
|
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
|
|
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
|
|
config.max_uri_handlers = 25U; config.max_open_sockets = kHttpMaxOpenSockets; config.uri_match_fn = httpd_uri_match_wildcard; config.lru_purge_enable = true;
|
|
|
|
config.max_uri_handlers = 25U; config.max_open_sockets = kHttpMaxOpenSockets; config.uri_match_fn = httpd_uri_match_wildcard; config.lru_purge_enable = true;
|
|
|
|
ESP_RETURN_ON_ERROR(httpd_start(&s_server, &config), kLogTag, "http server start failed");
|
|
|
|
const esp_err_t start_result = httpd_start(&s_server, &config);
|
|
|
|
|
|
|
|
if (start_result != ESP_OK) { ESP_LOGE(kLogTag, "HTTP server startup failed: %s", esp_err_to_name(start_result)); return start_result; }
|
|
|
|
const httpd_uri_t routes[] = {
|
|
|
|
const httpd_uri_t routes[] = {
|
|
|
|
{.uri = "/", .method = HTTP_GET, .handler = root_handler},
|
|
|
|
{.uri = "/", .method = HTTP_GET, .handler = root_handler},
|
|
|
|
{.uri = "/setup", .method = HTTP_GET, .handler = setup_handler},
|
|
|
|
{.uri = "/setup", .method = HTTP_GET, .handler = setup_handler},
|
|
|
@@ -643,7 +696,11 @@ static esp_err_t start_http_server(void) {
|
|
|
|
{.uri = "/ws", .method = HTTP_GET, .handler = websocket_handler, .is_websocket = true},
|
|
|
|
{.uri = "/ws", .method = HTTP_GET, .handler = websocket_handler, .is_websocket = true},
|
|
|
|
{.uri = "/*", .method = HTTP_GET, .handler = static_file_handler},
|
|
|
|
{.uri = "/*", .method = HTTP_GET, .handler = static_file_handler},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
for (size_t index = 0; index < sizeof(routes) / sizeof(routes[0]); ++index) ESP_RETURN_ON_ERROR(httpd_register_uri_handler(s_server, &routes[index]), kLogTag, "route registration failed");
|
|
|
|
for (size_t index = 0; index < sizeof(routes) / sizeof(routes[0]); ++index) {
|
|
|
|
|
|
|
|
const esp_err_t register_result = httpd_register_uri_handler(s_server, &routes[index]);
|
|
|
|
|
|
|
|
if (register_result != ESP_OK) { ESP_LOGE(kLogTag, "HTTP route registration failed: %s", esp_err_to_name(register_result)); return register_result; }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ESP_LOGI(kLogTag, "HTTP server active");
|
|
|
|
return ESP_OK;
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -655,8 +712,8 @@ static void mount_littlefs(void) {
|
|
|
|
|
|
|
|
|
|
|
|
void app_main(void) {
|
|
|
|
void app_main(void) {
|
|
|
|
esp_err_t result = nvs_flash_init();
|
|
|
|
esp_err_t result = nvs_flash_init();
|
|
|
|
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); result = nvs_flash_init(); }
|
|
|
|
if (result != ESP_OK) ESP_LOGE(kLogTag, "NVS unavailable (%s); preserving storage and starting fallback", esp_err_to_name(result));
|
|
|
|
ESP_ERROR_CHECK(result); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
|
|
ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
|
|
application_init(&s_application, (random_source_t){.next_u32 = platform_random, .context = NULL});
|
|
|
|
application_init(&s_application, (random_source_t){.next_u32 = platform_random, .context = NULL});
|
|
|
|
const esp_timer_create_args_t bot_timer_args = {.callback = bot_timer_callback, .name = "bot_turn"};
|
|
|
|
const esp_timer_create_args_t bot_timer_args = {.callback = bot_timer_callback, .name = "bot_turn"};
|
|
|
|
ESP_ERROR_CHECK(esp_timer_create(&bot_timer_args, &s_bot_timer));
|
|
|
|
ESP_ERROR_CHECK(esp_timer_create(&bot_timer_args, &s_bot_timer));
|
|
|
|