346 lines
11 KiB
C
346 lines
11 KiB
C
/*
|
|
* wifi_controller.c
|
|
*
|
|
* Copyright (c) 2025 Umber Networks & Robert McMahon
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include "wifi_controller.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_log.h"
|
|
#include "esp_wifi.h"
|
|
#include "esp_event.h"
|
|
#include "esp_netif.h"
|
|
#include "inttypes.h"
|
|
#include "wifi_cfg.h"
|
|
|
|
// Dependencies
|
|
#include "iperf.h"
|
|
#include "status_led.h"
|
|
#include "wifi_monitor.h"
|
|
#include "gps_sync.h"
|
|
|
|
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
|
|
#include "csi_manager.h"
|
|
#endif
|
|
|
|
static const char *TAG = "WIFI_CTL";
|
|
|
|
static wifi_ctl_mode_t s_current_mode = WIFI_CTL_MODE_STA;
|
|
static uint8_t s_monitor_channel_active = 6;
|
|
static uint8_t s_monitor_channel_staging = 6;
|
|
|
|
static bool s_monitor_enabled = false;
|
|
static uint32_t s_monitor_frame_count = 0;
|
|
static TaskHandle_t s_monitor_stats_task_handle = NULL;
|
|
|
|
// --- Event Handler ---
|
|
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
|
|
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
|
ESP_LOGI(TAG, "Got IP -> LED Connected");
|
|
status_led_set_state(LED_STATE_CONNECTED);
|
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
|
if (s_current_mode == WIFI_CTL_MODE_STA) {
|
|
status_led_set_state(LED_STATE_NO_CONFIG);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ... [Log Collapse / Monitor Callback Logic] ...
|
|
static void log_collapse_event(uint32_t nav_duration_us, int rssi, int retry) {
|
|
gps_timestamp_t ts = gps_get_timestamp();
|
|
int64_t now_ms = ts.gps_us / 1000;
|
|
ESP_LOGI(TAG, "COLLAPSE: Time=%" PRId64 "ms, Sync=%d, Dur=%lu us, RSSI=%d, Retry=%d",
|
|
now_ms, ts.synced ? 1 : 0, nav_duration_us, rssi, retry);
|
|
}
|
|
|
|
static void monitor_frame_callback(const wifi_frame_info_t *frame, const uint8_t *payload, uint16_t len) {
|
|
s_monitor_frame_count++;
|
|
if (frame->retry && frame->duration_id > 5000) {
|
|
log_collapse_event((float)frame->duration_id, frame->rssi, frame->retry);
|
|
}
|
|
}
|
|
|
|
static void monitor_stats_task(void *arg) {
|
|
while (1) {
|
|
vTaskDelay(pdMS_TO_TICKS(10000));
|
|
wifi_collapse_stats_t stats;
|
|
if (wifi_monitor_get_stats(&stats) == ESP_OK) {
|
|
ESP_LOGI("MONITOR", "--- Stats: %lu frames, Retry: %.2f%%, Avg NAV: %u us ---",
|
|
(unsigned long)stats.total_frames, stats.retry_rate, stats.avg_nav);
|
|
if (wifi_monitor_is_collapsed()) ESP_LOGW("MONITOR", "⚠️ COLLAPSE DETECTED! ⚠️");
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Helper to apply IP settings ---
|
|
static void apply_ip_settings(void) {
|
|
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
|
if (!netif) return;
|
|
|
|
if (wifi_cfg_get_dhcp()) {
|
|
esp_netif_dhcpc_start(netif);
|
|
} else {
|
|
esp_netif_dhcpc_stop(netif);
|
|
|
|
char ip[16], mask[16], gw[16];
|
|
if (wifi_cfg_get_ipv4(ip, mask, gw)) {
|
|
esp_netif_ip_info_t info = {0};
|
|
// API Fix: esp_ip4addr_aton returns uint32_t
|
|
info.ip.addr = esp_ip4addr_aton(ip);
|
|
info.netmask.addr = esp_ip4addr_aton(mask);
|
|
info.gw.addr = esp_ip4addr_aton(gw);
|
|
|
|
esp_netif_set_ip_info(netif, &info);
|
|
ESP_LOGI(TAG, "Static IP applied: %s", ip);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// PUBLIC API IMPLEMENTATION
|
|
// ============================================================================
|
|
|
|
void wifi_ctl_init(void) {
|
|
s_current_mode = WIFI_CTL_MODE_STA;
|
|
s_monitor_enabled = false;
|
|
s_monitor_frame_count = 0;
|
|
|
|
// 1. Initialize Network Interface
|
|
esp_netif_create_default_wifi_sta();
|
|
|
|
// 2. Apply IP Settings (Static vs DHCP)
|
|
apply_ip_settings();
|
|
|
|
// 3. Initialize Wi-Fi Driver
|
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
|
|
// 4. Register Events
|
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
|
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL, NULL));
|
|
|
|
// 5. Configure Storage & Mode
|
|
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
ESP_ERROR_CHECK(esp_wifi_start());
|
|
|
|
// 6. Apply Saved Config
|
|
if (!wifi_cfg_apply_from_nvs()) {
|
|
ESP_LOGW(TAG, "No saved WiFi config found, driver initialized in defaults.");
|
|
status_led_set_state(LED_STATE_NO_CONFIG);
|
|
} else {
|
|
ESP_LOGI(TAG, "WiFi driver initialized from NVS.");
|
|
status_led_set_state(LED_STATE_WAITING);
|
|
esp_wifi_connect();
|
|
}
|
|
|
|
// Load Staging Params
|
|
char mode_ignored[16];
|
|
wifi_cfg_get_mode(mode_ignored, &s_monitor_channel_staging);
|
|
if (s_monitor_channel_staging == 0) s_monitor_channel_staging = 6;
|
|
}
|
|
|
|
// --- Mode Control (Core) ---
|
|
|
|
esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel, wifi_bandwidth_t bw) {
|
|
if (channel == 0) channel = s_monitor_channel_staging;
|
|
|
|
if (s_current_mode == WIFI_CTL_MODE_MONITOR && s_monitor_channel_active == channel) {
|
|
ESP_LOGW(TAG, "Already in monitor mode (Ch %d)", channel);
|
|
return ESP_OK;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Switching to MONITOR MODE (Ch %d)", channel);
|
|
|
|
iperf_stop();
|
|
vTaskDelay(pdMS_TO_TICKS(500));
|
|
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
|
|
csi_mgr_disable();
|
|
#endif
|
|
|
|
esp_wifi_disconnect();
|
|
esp_wifi_stop();
|
|
vTaskDelay(pdMS_TO_TICKS(500));
|
|
|
|
esp_wifi_set_mode(WIFI_MODE_NULL);
|
|
if (wifi_monitor_init(channel, monitor_frame_callback) != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to init monitor mode");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
esp_wifi_set_bandwidth(WIFI_IF_STA, bw);
|
|
|
|
if (wifi_monitor_start() != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to start monitor mode");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
s_monitor_enabled = true;
|
|
s_current_mode = WIFI_CTL_MODE_MONITOR;
|
|
s_monitor_channel_active = channel;
|
|
status_led_set_state(LED_STATE_MONITORING);
|
|
|
|
if (s_monitor_stats_task_handle == NULL) {
|
|
xTaskCreate(monitor_stats_task, "monitor_stats", 4096, NULL, 5, &s_monitor_stats_task_handle);
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t wifi_ctl_switch_to_sta(void) {
|
|
if (s_current_mode == WIFI_CTL_MODE_STA) {
|
|
ESP_LOGI(TAG, "Already in STA mode");
|
|
return ESP_OK;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Switching to STA MODE");
|
|
|
|
if (s_monitor_stats_task_handle != NULL) {
|
|
vTaskDelete(s_monitor_stats_task_handle);
|
|
s_monitor_stats_task_handle = NULL;
|
|
}
|
|
|
|
if (s_monitor_enabled) {
|
|
wifi_monitor_stop();
|
|
s_monitor_enabled = false;
|
|
vTaskDelay(pdMS_TO_TICKS(500));
|
|
}
|
|
|
|
esp_wifi_set_mode(WIFI_MODE_STA);
|
|
vTaskDelay(pdMS_TO_TICKS(500));
|
|
|
|
esp_wifi_start();
|
|
esp_wifi_connect();
|
|
|
|
s_current_mode = WIFI_CTL_MODE_STA;
|
|
status_led_set_state(LED_STATE_WAITING);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
// --- Wrappers for cmd_monitor.c ---
|
|
|
|
void wifi_ctl_monitor_start(int channel) {
|
|
wifi_ctl_switch_to_monitor((uint8_t)channel, WIFI_BW_HT20);
|
|
}
|
|
|
|
void wifi_ctl_stop(void) {
|
|
wifi_ctl_switch_to_sta();
|
|
}
|
|
|
|
void wifi_ctl_start_station(void) {
|
|
wifi_ctl_switch_to_sta();
|
|
}
|
|
|
|
void wifi_ctl_start_ap(void) {
|
|
ESP_LOGW(TAG, "AP Mode not fully implemented, using STA");
|
|
wifi_ctl_switch_to_sta();
|
|
}
|
|
|
|
// --- Settings ---
|
|
|
|
void wifi_ctl_set_channel(int channel) {
|
|
if (channel < 1 || channel > 14) {
|
|
ESP_LOGE(TAG, "Invalid channel %d", channel);
|
|
return;
|
|
}
|
|
s_monitor_channel_staging = (uint8_t)channel;
|
|
|
|
if (s_current_mode == WIFI_CTL_MODE_MONITOR) {
|
|
ESP_LOGI(TAG, "Switching live channel to %d", channel);
|
|
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
|
|
s_monitor_channel_active = (uint8_t)channel;
|
|
}
|
|
}
|
|
|
|
void wifi_ctl_status(void) {
|
|
const char *mode_str = (s_current_mode == WIFI_CTL_MODE_MONITOR) ? "MONITOR" :
|
|
(s_current_mode == WIFI_CTL_MODE_AP) ? "AP" : "STATION";
|
|
|
|
printf("WiFi Status:\n");
|
|
printf(" Mode: %s\n", mode_str);
|
|
if (s_current_mode == WIFI_CTL_MODE_MONITOR) {
|
|
printf(" Channel: %d\n", s_monitor_channel_active);
|
|
printf(" Frames: %lu\n", (unsigned long)s_monitor_frame_count);
|
|
}
|
|
printf(" Staging Ch: %d\n", s_monitor_channel_staging);
|
|
}
|
|
|
|
// --- Params (NVS) ---
|
|
|
|
bool wifi_ctl_param_is_unsaved(void) {
|
|
return wifi_cfg_monitor_channel_is_unsaved(s_monitor_channel_staging);
|
|
}
|
|
|
|
void wifi_ctl_param_save(const char *dummy) {
|
|
(void)dummy;
|
|
if (wifi_cfg_set_monitor_channel(s_monitor_channel_staging)) {
|
|
ESP_LOGI(TAG, "Monitor channel (%d) saved to NVS", s_monitor_channel_staging);
|
|
} else {
|
|
ESP_LOGI(TAG, "No changes to save.");
|
|
}
|
|
}
|
|
|
|
void wifi_ctl_param_init(void) {
|
|
char mode_ignored[16];
|
|
uint8_t ch = 0;
|
|
wifi_cfg_get_mode(mode_ignored, &ch);
|
|
if (ch > 0) s_monitor_channel_staging = ch;
|
|
ESP_LOGI(TAG, "Reloaded monitor channel: %d", s_monitor_channel_staging);
|
|
}
|
|
|
|
void wifi_ctl_param_clear(void) {
|
|
wifi_cfg_clear_monitor_channel();
|
|
s_monitor_channel_staging = 6;
|
|
ESP_LOGI(TAG, "Monitor config cleared (Defaulting to Ch 6).");
|
|
}
|
|
|
|
// --- Getters ---
|
|
|
|
wifi_ctl_mode_t wifi_ctl_get_mode(void) { return s_current_mode; }
|
|
int wifi_ctl_get_channel(void) { return s_monitor_channel_active; }
|
|
|
|
// --- Deprecated ---
|
|
static void auto_monitor_task_func(void *arg) {
|
|
uint8_t channel = (uint8_t)(uintptr_t)arg;
|
|
ESP_LOGI(TAG, "Waiting for WiFi connection before switching to monitor mode...");
|
|
while (status_led_get_state() != LED_STATE_CONNECTED) {
|
|
vTaskDelay(pdMS_TO_TICKS(500));
|
|
}
|
|
ESP_LOGI(TAG, "WiFi connected, waiting for GPS sync (2s)...");
|
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
|
wifi_ctl_switch_to_monitor(channel, WIFI_BW_HT20);
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
void wifi_ctl_auto_monitor_start(uint8_t channel) {
|
|
xTaskCreate(auto_monitor_task_func, "auto_monitor", 4096, (void*)(uintptr_t)channel, 5, NULL);
|
|
}
|