CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
WifiController.cpp
Go to the documentation of this file.
1
5
7#include "cdc_hal/hw_config.h"
8#include "cdc_log.h"
9#include "esp_wifi.h"
10#include "esp_event.h"
11#include "esp_netif.h"
12#include "esp_mac.h"
13#include "esp_system.h"
14#include "freertos/FreeRTOS.h"
15#include "freertos/event_groups.h"
16#include "lwip/ip4_addr.h"
17#include <cstring>
18#include <new>
19
20static const char* TAG = "WiFi-Ctrl";
21
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
27
28namespace cdc::hal {
29
34public:
35 WifiController() = default;
36
37 // IService implementation
38 bool init() override;
39 bool start() override;
40 void stop() override;
41 core::ServiceState getState() const override { return state_; }
42 const char* getName() const override { return "wifi"; }
43
44 // IWifiController implementation
45 bool enable(WifiMode mode = WifiMode::STA) override;
46 void disable() override;
47 bool isEnabled() const override { return enabled_; }
48 WifiMode getMode() const override { return currentMode_; }
49 WifiState getWifiState() const override { return wifiState_; }
50
51 bool connect(const char* ssid, const char* password,
52 uint32_t timeoutMs = 10000) override;
53 void disconnect() override;
54 bool isConnected() const override { return wifiState_ == WifiState::GOT_IP; }
55 const char* getCurrentSsid() const override { return currentSsid_; }
56 bool getIpAddress(char* ip, size_t len) const override;
57 bool getMacAddress(uint8_t* mac) const override;
58 int8_t getRssi() const override;
59
60 bool startScan() override;
61 bool isScanComplete() const override;
62 uint8_t getScanResults(WifiScanResult* results, uint8_t maxResults) override;
63
64 bool startAp(const char* ssid, const char* password = nullptr,
65 uint8_t channel = 1) override;
66 uint8_t getConnectedStations() const override;
67
68 // Event handlers (called from WiFi event callbacks)
69 void onWifiEvent(int32_t eventId, void* eventData);
70 void onIpEvent(int32_t eventId, void* eventData);
71
72private:
73 bool initNetif();
74 void deinitNetif();
75
77 bool enabled_ = false;
78 WifiMode currentMode_ = WifiMode::OFF;
80
81 // Network interfaces
82 esp_netif_t* staNetif_ = nullptr;
83 esp_netif_t* apNetif_ = nullptr;
84
85 // Event handling
86 EventGroupHandle_t eventGroup_ = nullptr;
87 esp_event_handler_instance_t wifiEventHandler_ = nullptr;
88 esp_event_handler_instance_t ipEventHandler_ = nullptr;
89
90 // Connection info
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;
95
96 // Scan state
97 bool scanInProgress_ = false;
98 bool scanComplete_ = false;
99
100 // Singleton for event callbacks (public for static event handlers)
101public:
103};
104
106
115static void wifiEventHandler(void* arg, esp_event_base_t eventBase,
116 int32_t eventId, void* eventData) {
117 (void)arg;
119 WifiController::instance_->onWifiEvent(eventId, eventData);
120 }
121}
122
131static void ipEventHandler(void* arg, esp_event_base_t eventBase,
132 int32_t eventId, void* eventData) {
133 (void)arg;
135 WifiController::instance_->onIpEvent(eventId, eventData);
136 }
137}
138
144 if (state_ != core::ServiceState::UNINITIALIZED) {
145 return state_ == core::ServiceState::INITIALIZED ||
147 }
148
149 instance_ = this;
150
151 // Create event group
152 eventGroup_ = xEventGroupCreate();
153 if (!eventGroup_) {
154 LOG_E(TAG, "Failed to create event group");
156 return false;
157 }
158
160 LOG_I(TAG, "WiFi controller initialized");
161 return true;
162}
163
169 if (state_ == core::ServiceState::INITIALIZED ||
170 state_ == core::ServiceState::STOPPED) {
172 return true;
173 }
174 return state_ == core::ServiceState::STARTED;
175}
176
181 if (state_ == core::ServiceState::STARTED) {
182 if (enabled_) {
183 disable();
184 }
186 }
187}
188
193bool WifiController::initNetif() {
194 // Initialize TCP/IP stack (once)
195 static bool tcpipInitialized = false;
196 if (!tcpipInitialized) {
197 esp_err_t ret = esp_netif_init();
198 if (ret != ESP_OK) {
199 LOG_E(TAG, "esp_netif_init failed: %s", esp_err_to_name(ret));
200 return false;
201 }
202
203 ret = esp_event_loop_create_default();
204 if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
205 // ESP_ERR_INVALID_STATE means already created, which is fine
206 LOG_E(TAG, "esp_event_loop_create_default failed: %s", esp_err_to_name(ret));
207 return false;
208 }
209
210 tcpipInitialized = true;
211 }
212
213 return true;
214}
215
219void WifiController::deinitNetif() {
220 if (staNetif_) {
221 esp_netif_destroy(staNetif_);
222 staNetif_ = nullptr;
223 }
224 if (apNetif_) {
225 esp_netif_destroy(apNetif_);
226 apNetif_ = nullptr;
227 }
228}
229
236 if (mode == WifiMode::OFF) {
237 disable();
238 return true;
239 }
240
241 if (enabled_ && currentMode_ == mode) {
242 return true;
243 }
244
245 if (state_ != core::ServiceState::STARTED) {
246 LOG_E(TAG, "Cannot enable - service not started");
247 return false;
248 }
249
250 // Disable first if changing mode
251 if (enabled_) {
252 disable();
253 }
254
255 if (!initNetif()) {
256 return false;
257 }
258
259 // Create network interfaces based on mode
260 if (mode == WifiMode::STA || mode == WifiMode::STA_AP) {
261 staNetif_ = esp_netif_create_default_wifi_sta();
262 }
263 if (mode == WifiMode::AP || mode == WifiMode::STA_AP) {
264 apNetif_ = esp_netif_create_default_wifi_ap();
265 }
266
267 // Note: CONFIG_ESP_PHY_ENABLE_USB=y in sdkconfig.defaults
268 // causes ESP-IDF to automatically call phy_bbpll_en_usb(true)
269 // during PHY initialization, so no manual workaround needed.
270
271 // Initialize WiFi
272 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
273 esp_err_t ret = esp_wifi_init(&cfg);
274 if (ret != ESP_OK) {
275 LOG_E(TAG, "esp_wifi_init failed: %s", esp_err_to_name(ret));
276 deinitNetif();
277 return false;
278 }
279
280 // Register event handlers
281 ret = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
282 &wifiEventHandler, nullptr,
283 &wifiEventHandler_);
284 if (ret != ESP_OK) {
285 LOG_E(TAG, "Failed to register WiFi event handler: %s", esp_err_to_name(ret));
286 esp_wifi_deinit();
287 deinitNetif();
288 return false;
289 }
290
291 ret = esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID,
292 &ipEventHandler, nullptr,
293 &ipEventHandler_);
294 if (ret != ESP_OK) {
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;
298 esp_wifi_deinit();
299 deinitNetif();
300 return false;
301 }
302
303 // Set mode
304 wifi_mode_t wifiMode = WIFI_MODE_NULL;
305 switch (mode) {
306 case WifiMode::STA: wifiMode = WIFI_MODE_STA; break;
307 case WifiMode::AP: wifiMode = WIFI_MODE_AP; break;
308 case WifiMode::STA_AP: wifiMode = WIFI_MODE_APSTA; break;
309 default: break;
310 }
311
312 ret = esp_wifi_set_mode(wifiMode);
313 if (ret != ESP_OK) {
314 LOG_E(TAG, "esp_wifi_set_mode failed: %s", esp_err_to_name(ret));
315 disable();
316 return false;
317 }
318
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)); // Give time for USB to transmit
324 ret = esp_wifi_start();
325 LOG_I(TAG, "esp_wifi_start returned: %s", esp_err_to_name(ret));
327 if (ret != ESP_OK) {
328 LOG_E(TAG, "esp_wifi_start failed");
329 disable();
330 return false;
331 }
332
333 LOG_I(TAG, "Setting enabled state...");
335 enabled_ = true;
336 currentMode_ = mode;
337 wifiState_ = WifiState::DISCONNECTED;
338
339 LOG_I(TAG, "WiFi enabled (mode=%d)", static_cast<int>(mode));
341 return true;
342}
343
348 if (!enabled_) {
349 return;
350 }
351
352 // Stop WiFi
353 esp_wifi_stop();
354 esp_wifi_deinit();
355
356 // Unregister event handlers
357 if (wifiEventHandler_) {
358 esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID,
359 wifiEventHandler_);
360 wifiEventHandler_ = nullptr;
361 }
362 if (ipEventHandler_) {
363 esp_event_handler_instance_unregister(IP_EVENT, ESP_EVENT_ANY_ID,
364 ipEventHandler_);
365 ipEventHandler_ = nullptr;
366 }
367
368 deinitNetif();
369
370 enabled_ = false;
371 currentMode_ = WifiMode::OFF;
372 wifiState_ = WifiState::DISCONNECTED;
373 currentSsid_[0] = '\0';
374 currentIp_.addr = 0;
375
376 LOG_I(TAG, "WiFi disabled");
377}
378
386bool WifiController::connect(const char* ssid, const char* password,
387 uint32_t timeoutMs) {
388 if (!enabled_ || (currentMode_ != WifiMode::STA &&
389 currentMode_ != WifiMode::STA_AP)) {
390 LOG_E(TAG, "Cannot connect - WiFi not in STA mode");
391 return false;
392 }
393
394 // Configure station
395 wifi_config_t wifiConfig = {};
396 strncpy((char*)wifiConfig.sta.ssid, ssid, sizeof(wifiConfig.sta.ssid) - 1);
397 if (password) {
398 strncpy((char*)wifiConfig.sta.password, password,
399 sizeof(wifiConfig.sta.password) - 1);
400 }
401 wifiConfig.sta.threshold.authmode = password ? WIFI_AUTH_WPA2_PSK : WIFI_AUTH_OPEN;
402
403 esp_err_t ret = esp_wifi_set_config(WIFI_IF_STA, &wifiConfig);
404 if (ret != ESP_OK) {
405 LOG_E(TAG, "esp_wifi_set_config failed: %s", esp_err_to_name(ret));
406 return false;
407 }
408
409 // Save SSID
410 strncpy(currentSsid_, ssid, sizeof(currentSsid_) - 1);
411 currentSsid_[sizeof(currentSsid_) - 1] = '\0';
412
413 // Reset state
414 retryCount_ = 0;
415 wifiState_ = WifiState::CONNECTING;
416 xEventGroupClearBits(eventGroup_, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT | WIFI_GOT_IP_BIT);
417
418 // Start connection
419 ret = esp_wifi_connect();
420 if (ret != ESP_OK) {
421 LOG_E(TAG, "esp_wifi_connect failed: %s", esp_err_to_name(ret));
422 wifiState_ = WifiState::CONNECTION_FAILED;
423 return false;
424 }
425
426 // Wait for connection result
427 EventBits_t bits = xEventGroupWaitBits(eventGroup_,
429 pdFALSE, pdFALSE,
430 pdMS_TO_TICKS(timeoutMs));
431
432 if (bits & WIFI_GOT_IP_BIT) {
433 LOG_I(TAG, "Connected to %s", ssid);
434 vTaskDelay(pdMS_TO_TICKS(500));
435 return true;
436 } else {
437 LOG_W(TAG, "Connection to %s failed", ssid);
438 wifiState_ = WifiState::CONNECTION_FAILED;
439 return false;
440 }
441}
442
447 if (wifiState_ == WifiState::CONNECTED || wifiState_ == WifiState::GOT_IP) {
448 esp_wifi_disconnect();
449 }
450 wifiState_ = WifiState::DISCONNECTED;
451 currentSsid_[0] = '\0';
452 currentIp_.addr = 0;
453}
454
461bool WifiController::getIpAddress(char* ip, size_t len) const {
462 if (!ip || len < 16 || currentIp_.addr == 0) {
463 return false;
464 }
465
466 snprintf(ip, len, IPSTR, IP2STR(&currentIp_));
467 return true;
468}
469
475bool WifiController::getMacAddress(uint8_t* mac) const {
476 if (!mac) return false;
477
478 wifi_mode_t mode;
479 esp_wifi_get_mode(&mode);
480
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;
483}
484
490 if (wifiState_ != WifiState::GOT_IP) {
491 return 0;
492 }
493
494 wifi_ap_record_t apInfo;
495 if (esp_wifi_sta_get_ap_info(&apInfo) == ESP_OK) {
496 return apInfo.rssi;
497 }
498 return 0;
499}
500
506 LOG_I(TAG, "startScan() called, enabled=%d, mode=%d", enabled_, static_cast<int>(currentMode_));
507
508 if (!enabled_ || (currentMode_ != WifiMode::STA &&
509 currentMode_ != WifiMode::STA_AP)) {
510 LOG_W(TAG, "startScan() - not in STA mode");
511 return false;
512 }
513
514 scanInProgress_ = true;
515 scanComplete_ = false;
516 xEventGroupClearBits(eventGroup_, WIFI_SCAN_DONE_BIT);
517
518 wifi_scan_config_t scanConfig = {};
519 scanConfig.show_hidden = true;
520
521 LOG_I(TAG, "Starting WiFi scan...");
522 esp_err_t ret = esp_wifi_scan_start(&scanConfig, false);
523 if (ret != ESP_OK) {
524 LOG_E(TAG, "esp_wifi_scan_start failed: %s", esp_err_to_name(ret));
525 scanInProgress_ = false;
526 return false;
527 }
528
529 LOG_I(TAG, "Scan started successfully");
530 return true;
531}
532
538 return scanComplete_;
539}
540
547uint8_t WifiController::getScanResults(WifiScanResult* results, uint8_t maxResults) {
548 if (!results || maxResults == 0 || !scanComplete_) {
549 return 0;
550 }
551
552 uint16_t numAps = 0;
553 esp_wifi_scan_get_ap_num(&numAps);
554
555 if (numAps == 0) {
556 return 0;
557 }
558
559 uint16_t toGet = (numAps < maxResults) ? numAps : maxResults;
560 wifi_ap_record_t* apRecords = new (std::nothrow) wifi_ap_record_t[toGet];
561 if (!apRecords) {
562 LOG_E(TAG, "OOM allocating scan results buffer");
563 return 0;
564 }
565
566 if (esp_wifi_scan_get_ap_records(&toGet, apRecords) != ESP_OK) {
567 delete[] apRecords;
568 return 0;
569 }
570
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;
577
578 // Map auth mode to security type
579 switch (apRecords[i].authmode) {
580 case WIFI_AUTH_OPEN: results[i].security = WifiSecurity::OPEN; break;
581 case WIFI_AUTH_WEP: results[i].security = WifiSecurity::WEP; break;
582 case WIFI_AUTH_WPA_PSK: results[i].security = WifiSecurity::WPA_PSK; break;
583 case WIFI_AUTH_WPA2_PSK: results[i].security = WifiSecurity::WPA2_PSK; break;
584 case WIFI_AUTH_WPA3_PSK: results[i].security = WifiSecurity::WPA3_PSK; break;
585 case WIFI_AUTH_WPA2_ENTERPRISE: results[i].security = WifiSecurity::WPA2_ENTERPRISE; break;
586 default: results[i].security = WifiSecurity::WPA2_PSK; break;
587 }
588 }
589
590 delete[] apRecords;
591 return static_cast<uint8_t>(toGet);
592}
593
601bool WifiController::startAp(const char* ssid, const char* password,
602 uint8_t channel) {
603 if (!enabled_ || (currentMode_ != WifiMode::AP &&
604 currentMode_ != WifiMode::STA_AP)) {
605 LOG_E(TAG, "Cannot start AP - WiFi not in AP mode");
606 return false;
607 }
608
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;
614
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;
619 } else {
620 wifiConfig.ap.authmode = WIFI_AUTH_OPEN;
621 }
622
623 esp_err_t ret = esp_wifi_set_config(WIFI_IF_AP, &wifiConfig);
624 if (ret != ESP_OK) {
625 LOG_E(TAG, "esp_wifi_set_config (AP) failed: %s", esp_err_to_name(ret));
626 return false;
627 }
628
629 LOG_I(TAG, "AP started: %s (channel %d)", ssid, channel);
630 return true;
631}
632
638 if (currentMode_ != WifiMode::AP && currentMode_ != WifiMode::STA_AP) {
639 return 0;
640 }
641
642 wifi_sta_list_t staList;
643 if (esp_wifi_ap_get_sta_list(&staList) == ESP_OK) {
644 return staList.num;
645 }
646 return 0;
647}
648
654void WifiController::onWifiEvent(int32_t eventId, void* eventData) {
655 switch (eventId) {
656 case WIFI_EVENT_STA_START:
657 LOG_I(TAG, "Station started");
659 LOG_I(TAG, "Event handler returning...");
661 break;
662
663 case WIFI_EVENT_STA_CONNECTED:
664 wifiState_ = WifiState::CONNECTED;
665 xEventGroupSetBits(eventGroup_, WIFI_CONNECTED_BIT);
666 LOG_I(TAG, "Connected to AP");
667 break;
668
669 case WIFI_EVENT_STA_DISCONNECTED: {
670 wifiState_ = WifiState::DISCONNECTED;
671 if (retryCount_ < MAX_RETRY) {
672 retryCount_++;
673 LOG_I(TAG, "Reconnecting (attempt %d)", retryCount_);
674 esp_wifi_connect();
675 } else {
676 xEventGroupSetBits(eventGroup_, WIFI_FAIL_BIT);
677 wifiState_ = WifiState::CONNECTION_FAILED;
678 }
679 break;
680 }
681
682 case WIFI_EVENT_SCAN_DONE:
683 scanInProgress_ = false;
684 scanComplete_ = true;
685 xEventGroupSetBits(eventGroup_, WIFI_SCAN_DONE_BIT);
686 LOG_I(TAG, "Scan complete");
687 break;
688
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));
692 break;
693 }
694
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));
698 break;
699 }
700
701 default:
702 break;
703 }
704}
705
711void WifiController::onIpEvent(int32_t eventId, void* eventData) {
712 if (eventId == IP_EVENT_STA_GOT_IP) {
713 auto* event = (ip_event_got_ip_t*)eventData;
714 currentIp_ = event->ip_info.ip;
715 wifiState_ = WifiState::GOT_IP;
716 retryCount_ = 0;
717 xEventGroupSetBits(eventGroup_, WIFI_GOT_IP_BIT);
718 LOG_I(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
719 } else if (eventId == IP_EVENT_STA_LOST_IP) {
720 currentIp_.addr = 0;
721 if (wifiState_ == WifiState::GOT_IP) {
722 wifiState_ = WifiState::CONNECTED;
723 }
724 LOG_W(TAG, "Lost IP address");
725 }
726}
727
730
738
739} // namespace cdc::hal
static const char * TAG
#define WIFI_FAIL_BIT
#define WIFI_SCAN_DONE_BIT
#define WIFI_GOT_IP_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,...)
Definition cdc_log.h:146
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
void console_flush(void)
Flushes buffered console output transports.
Definition cdc_log.cpp:393
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
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.