#include #include "esp_event.h" #include "esp_log.h" #include "esp_wifi.h" #include "esp_netif.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "frame_client.h" static const char *TAG = "frame_client"; #define STA_CONNECTED_BIT BIT0 #define STA_FAILED_BIT BIT1 static EventGroupHandle_t s_sta_event_group; /* wifi_sta_config_t's ssid/password fields are fixed-size byte arrays, not * necessarily null-terminated (a full 32-char SSID fills the field exactly). * snprintf() flags that as a possible truncation at -Werror, so copy by * hand instead. */ static void copy_wifi_field(uint8_t *dst, size_t dst_size, const char *src) { size_t len = strnlen(src, dst_size); memcpy(dst, src, len); if (len < dst_size) { dst[len] = '\0'; } } static void sta_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { ESP_LOGW(TAG, "Disconnected from home WiFi"); xEventGroupSetBits(s_sta_event_group, STA_FAILED_BIT); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); xEventGroupSetBits(s_sta_event_group, STA_CONNECTED_BIT); } } esp_err_t frame_wifi_connect_sta(const frame_config_t *cfg) { s_sta_event_group = xEventGroupCreate(); esp_netif_create_default_wifi_sta(); wifi_init_config_t init_cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&init_cfg)); esp_event_handler_instance_t wifi_handler; esp_event_handler_instance_t ip_handler; ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &sta_event_handler, NULL, &wifi_handler)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &sta_event_handler, NULL, &ip_handler)); wifi_config_t wifi_config = {0}; copy_wifi_field(wifi_config.sta.ssid, sizeof(wifi_config.sta.ssid), cfg->sta_ssid); copy_wifi_field(wifi_config.sta.password, sizeof(wifi_config.sta.password), cfg->sta_password); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); esp_err_t result = ESP_FAIL; for (int attempt = 1; attempt <= CONFIG_FRAME_STA_CONNECT_MAX_RETRIES; attempt++) { ESP_LOGI(TAG, "Connecting to '%s' (attempt %d/%d)", cfg->sta_ssid, attempt, CONFIG_FRAME_STA_CONNECT_MAX_RETRIES); xEventGroupClearBits(s_sta_event_group, STA_CONNECTED_BIT | STA_FAILED_BIT); esp_wifi_connect(); EventBits_t bits = xEventGroupWaitBits(s_sta_event_group, STA_CONNECTED_BIT | STA_FAILED_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(CONFIG_FRAME_STA_CONNECT_TIMEOUT_MS)); if (bits & STA_CONNECTED_BIT) { result = ESP_OK; break; } ESP_LOGW(TAG, "Attempt %d/%d failed", attempt, CONFIG_FRAME_STA_CONNECT_MAX_RETRIES); } esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_handler); esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, ip_handler); vEventGroupDelete(s_sta_event_group); s_sta_event_group = NULL; if (result != ESP_OK) { esp_wifi_stop(); } return result; } void frame_client_run(const frame_config_t *cfg) { /* TODO(step 6): HTTP fetch from cfg->toolsserver -> epd_write_stream -> * epd_refresh -> esp_deep_sleep. Idling here for now so the device * stays observable over serial while STA connectivity is verified. */ ESP_LOGI(TAG, "Connected to home WiFi. toolsserver='%s' (fetch/display cycle not yet implemented)", cfg->toolsserver); while (1) { ESP_LOGI(TAG, "heartbeat: still connected"); vTaskDelay(pdMS_TO_TICKS(60000)); } }