13#include "esp_system.h"
14#include "freertos/FreeRTOS.h"
15#include "freertos/event_groups.h"
16#include "lwip/ip4_addr.h"
20static const char*
TAG =
"WiFi-Ctrl";
23#define WIFI_CONNECTED_BIT BIT0
24#define WIFI_FAIL_BIT BIT1
25#define WIFI_SCAN_DONE_BIT BIT2
26#define WIFI_GOT_IP_BIT BIT3
39 bool start()
override;
42 const char*
getName()
const override {
return "wifi"; }
47 bool isEnabled()
const override {
return enabled_; }
51 bool connect(
const char* ssid,
const char* password,
52 uint32_t timeoutMs = 10000)
override;
58 int8_t
getRssi()
const override;
64 bool startAp(
const char* ssid,
const char* password =
nullptr,
65 uint8_t channel = 1)
override;
70 void onIpEvent(int32_t eventId,
void* eventData);
77 bool enabled_ =
false;
82 esp_netif_t* staNetif_ =
nullptr;
83 esp_netif_t* apNetif_ =
nullptr;
86 EventGroupHandle_t eventGroup_ =
nullptr;
87 esp_event_handler_instance_t wifiEventHandler_ =
nullptr;
88 esp_event_handler_instance_t ipEventHandler_ =
nullptr;
91 char currentSsid_[33] = {0};
92 esp_ip4_addr_t currentIp_ = {0};
93 uint8_t retryCount_ = 0;
94 static constexpr uint8_t MAX_RETRY = 5;
97 bool scanInProgress_ =
false;
98 bool scanComplete_ =
false;
116 int32_t eventId,
void* eventData) {
132 int32_t eventId,
void* eventData) {
152 eventGroup_ = xEventGroupCreate();
154 LOG_E(
TAG,
"Failed to create event group");
160 LOG_I(
TAG,
"WiFi controller initialized");
193bool WifiController::initNetif() {
195 static bool tcpipInitialized =
false;
196 if (!tcpipInitialized) {
197 esp_err_t ret = esp_netif_init();
199 LOG_E(
TAG,
"esp_netif_init failed: %s", esp_err_to_name(ret));
203 ret = esp_event_loop_create_default();
204 if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
206 LOG_E(
TAG,
"esp_event_loop_create_default failed: %s", esp_err_to_name(ret));
210 tcpipInitialized =
true;
219void WifiController::deinitNetif() {
221 esp_netif_destroy(staNetif_);
225 esp_netif_destroy(apNetif_);
241 if (enabled_ && currentMode_ == mode) {
246 LOG_E(
TAG,
"Cannot enable - service not started");
261 staNetif_ = esp_netif_create_default_wifi_sta();
264 apNetif_ = esp_netif_create_default_wifi_ap();
272 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
273 esp_err_t ret = esp_wifi_init(&cfg);
275 LOG_E(
TAG,
"esp_wifi_init failed: %s", esp_err_to_name(ret));
281 ret = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
285 LOG_E(
TAG,
"Failed to register WiFi event handler: %s", esp_err_to_name(ret));
291 ret = esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID,
295 LOG_E(
TAG,
"Failed to register IP event handler: %s", esp_err_to_name(ret));
296 esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifiEventHandler_);
297 wifiEventHandler_ =
nullptr;
304 wifi_mode_t wifiMode = WIFI_MODE_NULL;
312 ret = esp_wifi_set_mode(wifiMode);
314 LOG_E(
TAG,
"esp_wifi_set_mode failed: %s", esp_err_to_name(ret));
319 LOG_I(
TAG,
"Calling esp_wifi_start...");
320 LOG_I(
TAG,
"Task: %s, stack free: %lu", pcTaskGetName(
nullptr),
321 (
unsigned long)uxTaskGetStackHighWaterMark(
nullptr));
323 vTaskDelay(pdMS_TO_TICKS(100));
324 ret = esp_wifi_start();
325 LOG_I(
TAG,
"esp_wifi_start returned: %s", esp_err_to_name(ret));
328 LOG_E(
TAG,
"esp_wifi_start failed");
333 LOG_I(
TAG,
"Setting enabled state...");
339 LOG_I(
TAG,
"WiFi enabled (mode=%d)",
static_cast<int>(mode));
357 if (wifiEventHandler_) {
358 esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID,
360 wifiEventHandler_ =
nullptr;
362 if (ipEventHandler_) {
363 esp_event_handler_instance_unregister(IP_EVENT, ESP_EVENT_ANY_ID,
365 ipEventHandler_ =
nullptr;
373 currentSsid_[0] =
'\0';
387 uint32_t timeoutMs) {
390 LOG_E(
TAG,
"Cannot connect - WiFi not in STA mode");
395 wifi_config_t wifiConfig = {};
396 strncpy((
char*)wifiConfig.sta.ssid, ssid,
sizeof(wifiConfig.sta.ssid) - 1);
398 strncpy((
char*)wifiConfig.sta.password, password,
399 sizeof(wifiConfig.sta.password) - 1);
401 wifiConfig.sta.threshold.authmode = password ? WIFI_AUTH_WPA2_PSK : WIFI_AUTH_OPEN;
403 esp_err_t ret = esp_wifi_set_config(WIFI_IF_STA, &wifiConfig);
405 LOG_E(
TAG,
"esp_wifi_set_config failed: %s", esp_err_to_name(ret));
410 strncpy(currentSsid_, ssid,
sizeof(currentSsid_) - 1);
411 currentSsid_[
sizeof(currentSsid_) - 1] =
'\0';
419 ret = esp_wifi_connect();
421 LOG_E(
TAG,
"esp_wifi_connect failed: %s", esp_err_to_name(ret));
427 EventBits_t bits = xEventGroupWaitBits(eventGroup_,
430 pdMS_TO_TICKS(timeoutMs));
433 LOG_I(
TAG,
"Connected to %s", ssid);
434 vTaskDelay(pdMS_TO_TICKS(500));
437 LOG_W(
TAG,
"Connection to %s failed", ssid);
448 esp_wifi_disconnect();
451 currentSsid_[0] =
'\0';
462 if (!ip || len < 16 || currentIp_.addr == 0) {
466 snprintf(ip, len, IPSTR, IP2STR(¤tIp_));
476 if (!mac)
return false;
479 esp_wifi_get_mode(&mode);
481 wifi_interface_t iface = (mode == WIFI_MODE_AP) ? WIFI_IF_AP : WIFI_IF_STA;
482 return esp_wifi_get_mac(iface, mac) == ESP_OK;
494 wifi_ap_record_t apInfo;
495 if (esp_wifi_sta_get_ap_info(&apInfo) == ESP_OK) {
506 LOG_I(
TAG,
"startScan() called, enabled=%d, mode=%d", enabled_,
static_cast<int>(currentMode_));
510 LOG_W(
TAG,
"startScan() - not in STA mode");
514 scanInProgress_ =
true;
515 scanComplete_ =
false;
518 wifi_scan_config_t scanConfig = {};
519 scanConfig.show_hidden =
true;
521 LOG_I(
TAG,
"Starting WiFi scan...");
522 esp_err_t ret = esp_wifi_scan_start(&scanConfig,
false);
524 LOG_E(
TAG,
"esp_wifi_scan_start failed: %s", esp_err_to_name(ret));
525 scanInProgress_ =
false;
529 LOG_I(
TAG,
"Scan started successfully");
538 return scanComplete_;
548 if (!results || maxResults == 0 || !scanComplete_) {
553 esp_wifi_scan_get_ap_num(&numAps);
559 uint16_t toGet = (numAps < maxResults) ? numAps : maxResults;
560 wifi_ap_record_t* apRecords =
new (std::nothrow) wifi_ap_record_t[toGet];
562 LOG_E(
TAG,
"OOM allocating scan results buffer");
566 if (esp_wifi_scan_get_ap_records(&toGet, apRecords) != ESP_OK) {
571 for (uint16_t i = 0; i < toGet; i++) {
572 strncpy(results[i].ssid, (
char*)apRecords[i].ssid, 32);
573 results[i].
ssid[32] =
'\0';
574 memcpy(results[i].bssid, apRecords[i].bssid, 6);
575 results[i].
rssi = apRecords[i].rssi;
576 results[i].
channel = apRecords[i].primary;
579 switch (apRecords[i].authmode) {
591 return static_cast<uint8_t
>(toGet);
605 LOG_E(
TAG,
"Cannot start AP - WiFi not in AP mode");
609 wifi_config_t wifiConfig = {};
610 strncpy((
char*)wifiConfig.ap.ssid, ssid,
sizeof(wifiConfig.ap.ssid) - 1);
611 wifiConfig.ap.ssid_len = strlen(ssid);
612 wifiConfig.ap.channel = channel;
613 wifiConfig.ap.max_connection = 4;
615 if (password && strlen(password) >= 8) {
616 strncpy((
char*)wifiConfig.ap.password, password,
617 sizeof(wifiConfig.ap.password) - 1);
618 wifiConfig.ap.authmode = WIFI_AUTH_WPA2_PSK;
620 wifiConfig.ap.authmode = WIFI_AUTH_OPEN;
623 esp_err_t ret = esp_wifi_set_config(WIFI_IF_AP, &wifiConfig);
625 LOG_E(
TAG,
"esp_wifi_set_config (AP) failed: %s", esp_err_to_name(ret));
629 LOG_I(
TAG,
"AP started: %s (channel %d)", ssid, channel);
642 wifi_sta_list_t staList;
643 if (esp_wifi_ap_get_sta_list(&staList) == ESP_OK) {
656 case WIFI_EVENT_STA_START:
659 LOG_I(
TAG,
"Event handler returning...");
663 case WIFI_EVENT_STA_CONNECTED:
669 case WIFI_EVENT_STA_DISCONNECTED: {
671 if (retryCount_ < MAX_RETRY) {
673 LOG_I(
TAG,
"Reconnecting (attempt %d)", retryCount_);
682 case WIFI_EVENT_SCAN_DONE:
683 scanInProgress_ =
false;
684 scanComplete_ =
true;
689 case WIFI_EVENT_AP_STACONNECTED: {
690 auto*
event = (wifi_event_ap_staconnected_t*)eventData;
691 LOG_I(
TAG,
"Station " MACSTR
" connected", MAC2STR(event->mac));
695 case WIFI_EVENT_AP_STADISCONNECTED: {
696 auto*
event = (wifi_event_ap_stadisconnected_t*)eventData;
697 LOG_I(
TAG,
"Station " MACSTR
" disconnected", MAC2STR(event->mac));
712 if (eventId == IP_EVENT_STA_GOT_IP) {
713 auto*
event = (ip_event_got_ip_t*)eventData;
714 currentIp_ =
event->ip_info.ip;
718 LOG_I(
TAG,
"Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
719 }
else if (eventId == IP_EVENT_STA_LOST_IP) {
#define WIFI_SCAN_DONE_BIT
#define WIFI_CONNECTED_BIT
Event-group bit definitions for Wi-Fi connection state.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define LOG_I(tag, fmt,...)
void console_flush(void)
Flushes buffered console output transports.
#define LOG_E(tag, fmt,...)
bool start() override
Starts Wi-Fi controller service state.
WifiState getWifiState() const override
bool isConnected() const override
bool getMacAddress(uint8_t *mac) const override
Reads MAC address of active Wi-Fi interface.
void onWifiEvent(int32_t eventId, void *eventData)
Handles Wi-Fi events from ESP-IDF event loop.
const char * getName() const override
bool init() override
Initializes controller resources and event group.
core::ServiceState getState() const override
uint8_t getConnectedStations() const override
Returns number of stations connected to soft-AP.
bool startAp(const char *ssid, const char *password=nullptr, uint8_t channel=1) override
Configures and starts soft-AP parameters.
bool connect(const char *ssid, const char *password, uint32_t timeoutMs=10000) override
Connects STA interface to an access point.
uint8_t getScanResults(WifiScanResult *results, uint8_t maxResults) override
Copies scan results into caller buffer.
bool isEnabled() const override
void stop() override
Stops Wi-Fi controller and disables active Wi-Fi stack.
bool enable(WifiMode mode=WifiMode::STA) override
Enables Wi-Fi in requested mode and starts driver.
bool startScan() override
Starts asynchronous AP scan.
void onIpEvent(int32_t eventId, void *eventData)
Handles IP-related events from ESP-IDF event loop.
void disable() override
Disables Wi-Fi driver and resets runtime state.
int8_t getRssi() const override
Returns RSSI of current STA connection.
static WifiController * instance_
bool isScanComplete() const override
Returns whether the last scan has completed.
bool getIpAddress(char *ip, size_t len) const override
Returns current STA IPv4 address as text.
void disconnect() override
Disconnects from current access point and clears connection state.
WifiMode getMode() const override
const char * getCurrentSsid() const override
IWifiController * getWifiControllerInstance()
Returns the singleton Wi-Fi controller service instance.
static void ipEventHandler(void *arg, esp_event_base_t eventBase, int32_t eventId, void *eventData)
IP event callback bridge.
static void wifiEventHandler(void *arg, esp_event_base_t eventBase, int32_t eventId, void *eventData)
Wi-Fi event callback bridge.
static WifiController g_wifiController
Singleton Wi-Fi controller instance.