|
@@ -1,167 +1,244 @@
|
|
|
#include "kbf/wifi.h"
|
|
|
|
|
|
+#include <esp_log.h>
|
|
|
+#include <esp_wifi.h>
|
|
|
+
|
|
|
+#define KBF_INTERNAL
|
|
|
+
|
|
|
+#include "kbf/internal/wifi.h"
|
|
|
#include "kbf/macros.h"
|
|
|
-#include "kbf/nvs.h"
|
|
|
+#include "kbf/exception.h"
|
|
|
+#include "kbf/rtos.h"
|
|
|
|
|
|
using namespace kbf;
|
|
|
+using namespace kbf::wifi;
|
|
|
+using std::string;
|
|
|
+using std::vector;
|
|
|
|
|
|
-wifi::STA::STA() {
|
|
|
- ESP_LOGI(TAG, "STA()");
|
|
|
- config.pmf_cfg = {true, false};
|
|
|
-}
|
|
|
+constexpr const char *const TAG = "kbf::wifi::sta";
|
|
|
|
|
|
-wifi::STA::~STA() {
|
|
|
- ESP_LOGI(TAG, "~STA()");
|
|
|
- unregisterEventHandlers();
|
|
|
-}
|
|
|
+namespace {
|
|
|
+ string ssid;
|
|
|
+ string password;
|
|
|
+
|
|
|
+ int retryNum = 0;
|
|
|
+ int retryMax = 0;
|
|
|
+
|
|
|
+ bool connected;
|
|
|
+ kbf::net::IP ip; // NOLINT(cert-err58-cpp)
|
|
|
+
|
|
|
+ kbf::rtos::EventGroup event; // NOLINT(cert-err58-cpp)
|
|
|
+ const int CONNECT_FINISHED = 0;
|
|
|
|
|
|
-shared_ptr<wifi::STA> wifi::STA::create() {
|
|
|
- return shared_ptr<STA>(new STA());
|
|
|
+ esp_netif_obj *interface = nullptr;
|
|
|
+ wifi_sta_config_t staConfig = {};
|
|
|
+
|
|
|
+ esp_event_handler_instance_t connectHandler = {};
|
|
|
+ esp_event_handler_instance_t ipHandler = {};
|
|
|
+ esp_event_handler_instance_t disconnectHandler = {};
|
|
|
+ esp_event_handler_instance_t scanDoneHandler = {};
|
|
|
+
|
|
|
+ void handleConnect(void *, esp_event_base_t, int32_t, void *);
|
|
|
+
|
|
|
+ void handleDisconnect(void *, esp_event_base_t, int32_t, void *);
|
|
|
+
|
|
|
+ void handleIp(void *, esp_event_base_t, int32_t, void *);
|
|
|
+
|
|
|
+ void handleScanDone(void *, esp_event_base_t, int32_t, void *);
|
|
|
+
|
|
|
+ void registerEventHandlers();
|
|
|
+
|
|
|
+ void unregisterEventHandlers();
|
|
|
}
|
|
|
|
|
|
-void wifi::STA::init() {
|
|
|
- netif = esp_netif_create_default_wifi_sta();
|
|
|
+bool sta::connected() { return ::connected; }
|
|
|
+
|
|
|
+const string &sta::ssid() { return ::ssid; }
|
|
|
+
|
|
|
+const string &sta::password() { return ::password; }
|
|
|
+
|
|
|
+const net::IP &sta::ip() { return ::ip; }
|
|
|
+
|
|
|
+void (*sta::onConnect)() = nullptr;
|
|
|
+
|
|
|
+void (*sta::onIp)() = nullptr;
|
|
|
+
|
|
|
+void (*sta::onDisconnect)() = nullptr;
|
|
|
+
|
|
|
+void (*sta::onScanDone)(vector<APInfo> &apList, void *data) = nullptr;
|
|
|
+
|
|
|
+void sta::start() {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
+
|
|
|
+ if (internal::mode == Mode::STA || internal::mode == Mode::DUAL) throw exception::InvalidMode();
|
|
|
+
|
|
|
+ internal::init();
|
|
|
+ interface = esp_netif_create_default_wifi_sta();
|
|
|
registerEventHandlers();
|
|
|
+
|
|
|
+ staConfig.pmf_cfg = {true, false};
|
|
|
+ wifi_config_t config = {.sta = staConfig};
|
|
|
+
|
|
|
+ if (internal::mode == Mode::OFF) {
|
|
|
+ CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
|
+ CHECK(esp_wifi_set_config(WIFI_IF_STA, &config));
|
|
|
+ CHECK(esp_wifi_start());
|
|
|
+ internal::setMode(Mode::STA);
|
|
|
+ } else {
|
|
|
+ CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
|
|
|
+ CHECK(esp_wifi_set_config(WIFI_IF_STA, &config));
|
|
|
+ internal::setMode(Mode::DUAL);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+void sta::stop() {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
|
|
|
-void wifi::STA::registerEventHandlers() {
|
|
|
- ESP_LOGD(TAG, "registerEventHandlers");
|
|
|
+ if (internal::mode != Mode::STA && internal::mode != Mode::DUAL) throw wifi::exception::InvalidMode();
|
|
|
|
|
|
- CHECK(esp_event_handler_instance_register(
|
|
|
- WIFI_EVENT, WIFI_EVENT_STA_START, &handleStart, this, &startHandler));
|
|
|
- CHECK(esp_event_handler_instance_register(
|
|
|
- WIFI_EVENT, WIFI_EVENT_STA_STOP, &handleStop, this, &stopHandler));
|
|
|
- CHECK(esp_event_handler_instance_register(
|
|
|
- WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &handleConnect, this, &connectHandler));
|
|
|
- CHECK(esp_event_handler_instance_register(
|
|
|
- WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &handleDisconnect, this, &disconnectHandler));
|
|
|
+ unregisterEventHandlers();
|
|
|
|
|
|
- CHECK(esp_event_handler_instance_register(
|
|
|
- IP_EVENT, IP_EVENT_STA_GOT_IP, &handleGotIp, this, &gotIpHandler));
|
|
|
-}
|
|
|
+ if (internal::mode == Mode::DUAL) {
|
|
|
+ CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
|
|
+ internal::setMode(Mode::AP);
|
|
|
+ } else {
|
|
|
+ CHECK(esp_wifi_stop());
|
|
|
+ internal::setMode(Mode::OFF);
|
|
|
+ }
|
|
|
|
|
|
-void wifi::STA::unregisterEventHandlers() {
|
|
|
- ESP_LOGD(TAG, "unregisterEventHandlers");
|
|
|
-
|
|
|
- CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_STA_START, &startHandler));
|
|
|
- CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_STA_STOP, &stopHandler));
|
|
|
- CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &connectHandler));
|
|
|
- CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnectHandler));
|
|
|
- CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnectHandler));
|
|
|
- CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &gotIpHandler));
|
|
|
-
|
|
|
- // TODO why are these necessary? shouldn't instance unregistration be enough?
|
|
|
- CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_START, &handleStart));
|
|
|
- CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_STOP, &handleStop));
|
|
|
- CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &handleConnect));
|
|
|
- CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &handleDisconnect));
|
|
|
- CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &handleGotIp));
|
|
|
+ ::connected = false;
|
|
|
+ esp_netif_destroy(interface);
|
|
|
}
|
|
|
|
|
|
-void wifi::STA::connect(const string &ssid, const string &password, int maxRetryAttempts) {
|
|
|
- ESP_LOGI(TAG, "connect()");
|
|
|
+bool sta::connect(string ssid, string password, bool async, int maxAttempts) {
|
|
|
+ ESP_LOGI(TAG, "%s(%s, <omitted>, %d, %d)", __func__, ssid.c_str(), async, maxAttempts);
|
|
|
+
|
|
|
+ if (internal::mode != Mode::STA && internal::mode != Mode::DUAL) throw wifi::exception::InvalidMode();
|
|
|
|
|
|
- wifi_config_t wifiConfig = {.sta = config};
|
|
|
- std::copy(ssid.begin(), ssid.end() + 1, std::begin(wifiConfig.sta.ssid));
|
|
|
- std::copy(password.begin(), password.end() + 1, std::begin(wifiConfig.sta.password));
|
|
|
retryNum = 0;
|
|
|
- retryMax = maxRetryAttempts;
|
|
|
+ retryMax = maxAttempts;
|
|
|
+
|
|
|
+ event.clear();
|
|
|
+
|
|
|
+ ::ssid = std::move(ssid);
|
|
|
+ ::password = std::move(password);
|
|
|
|
|
|
- ESP_LOGD(TAG, "SSID: \"%s\"; pass: \"%s\"; maxRetry = %d", wifiConfig.sta.ssid, wifiConfig.sta.password,
|
|
|
- maxRetryAttempts);
|
|
|
+ std::copy(::ssid.begin(), ::ssid.end() + 1, std::begin(staConfig.ssid));
|
|
|
+ std::copy(::password.begin(), ::password.end() + 1, std::begin(staConfig.password));
|
|
|
|
|
|
- // TODO check station_example_main.c; they use ESP_IF_WIFI_STA which doesn't compile in C++
|
|
|
- CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifiConfig));
|
|
|
+ wifi_config_t config = {.sta = staConfig};
|
|
|
+ CHECK(esp_wifi_set_config(WIFI_IF_STA, &config));
|
|
|
CHECK(esp_wifi_connect());
|
|
|
+
|
|
|
+ if (!async) {
|
|
|
+ // TODO set timeout for the case when we don't get an IP address
|
|
|
+ event.waitForBit(CONNECT_FINISHED);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
-void wifi::STA::disconnect() {
|
|
|
- ESP_LOGI(TAG, "disconnect()");
|
|
|
- retryMax = 0;
|
|
|
+void sta::disconnect() {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
+
|
|
|
+ retryMax = 0;
|
|
|
+ ::connected = false;
|
|
|
CHECK(esp_wifi_disconnect());
|
|
|
}
|
|
|
|
|
|
-void wifi::STA::handleStart(void *, esp_event_base_t, int32_t, void *) {
|
|
|
- ESP_LOGD(TAG, "handleStart()");
|
|
|
- auto instance = getSTA();
|
|
|
- if (instance->onStart) instance->onStart();
|
|
|
-}
|
|
|
+void sta::startScan(void *data) {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
|
|
|
-void wifi::STA::handleStop(void *, esp_event_base_t, int32_t, void *) {
|
|
|
- ESP_LOGD(TAG, "handleStop()");
|
|
|
- auto instance = getSTA();
|
|
|
- if (instance->onStop) instance->onStop();
|
|
|
-}
|
|
|
+ if (!onScanDone) { ESP_LOGW(TAG, "%s: onScanDone not set", __func__); }
|
|
|
|
|
|
-void wifi::STA::handleConnect(void *, esp_event_base_t, int32_t, void *) {
|
|
|
- ESP_LOGD(TAG, "handleConnect()");
|
|
|
- auto instance = getSTA();
|
|
|
- if (instance->onConnect) instance->onConnect();
|
|
|
+ CHECK(esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, &handleScanDone, data,
|
|
|
+ &scanDoneHandler));
|
|
|
+
|
|
|
+ CHECK(esp_wifi_scan_start(nullptr, false));
|
|
|
}
|
|
|
|
|
|
-void wifi::STA::handleDisconnect(void *, esp_event_base_t, int32_t, void *) {
|
|
|
- ESP_LOGD(TAG, "handleDisconnect()");
|
|
|
- auto instance = getSTA();
|
|
|
+namespace {
|
|
|
+ void registerEventHandlers() {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
|
|
|
- if (instance->retryNum++ < instance->retryMax) {
|
|
|
- if (instance->onReconnecting && !instance->onReconnecting(instance->retryNum, instance->retryMax)) {
|
|
|
- ESP_LOGI(TAG, "reconnect attempt cancelled");
|
|
|
- if (instance->onDisconnect) instance->onDisconnect();
|
|
|
- } else {
|
|
|
- ESP_LOGI(TAG, "reconnecting, attempt %d / %d", instance->retryNum, instance->retryMax);
|
|
|
- esp_wifi_connect();
|
|
|
- }
|
|
|
- } else {
|
|
|
- ESP_LOGD(TAG, "disconnected");
|
|
|
- if (instance->onDisconnect) instance->onDisconnect();
|
|
|
+ CHECK(esp_event_handler_instance_register(
|
|
|
+ WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &handleConnect, nullptr, &connectHandler));
|
|
|
+ CHECK(esp_event_handler_instance_register(
|
|
|
+ WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &handleDisconnect, nullptr, &disconnectHandler));
|
|
|
+ CHECK(esp_event_handler_instance_register(
|
|
|
+ IP_EVENT, IP_EVENT_STA_GOT_IP, &handleIp, nullptr, &ipHandler));
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-void wifi::STA::handleScanDone(void *data, esp_event_base_t, int32_t, void *) {
|
|
|
- ESP_LOGD(TAG, "handleScanDone()");
|
|
|
- auto instance = getSTA();
|
|
|
+ void unregisterEventHandlers() {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
|
|
|
- CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, &instance->scanDoneHandler));
|
|
|
- CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, &instance->handleScanDone));
|
|
|
+ CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &connectHandler));
|
|
|
+ CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnectHandler));
|
|
|
+ CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &ipHandler));
|
|
|
|
|
|
- if (!instance->onScanDone) {
|
|
|
- ESP_LOGW(TAG, "handleScanDone: onScanDone not set");
|
|
|
- return;
|
|
|
+ // TODO these are supposed to be deprecated and should not be necessary
|
|
|
+ // however, handleDisconnect will be called on wifi stop unless unregistered; IDF bug?
|
|
|
+ CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &handleConnect));
|
|
|
+ CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &handleDisconnect));
|
|
|
+ CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &handleIp));
|
|
|
}
|
|
|
|
|
|
- uint16_t size;
|
|
|
- esp_wifi_scan_get_ap_num(&size);
|
|
|
- wifi_ap_record_t apRecords[size];
|
|
|
- esp_wifi_scan_get_ap_records(&size, apRecords);
|
|
|
+ void handleConnect(void *, esp_event_base_t, int32_t, void *) {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
|
|
|
- vector<APInfo> result;
|
|
|
+ ::connected = true;
|
|
|
+ if (sta::onConnect) sta::onConnect();
|
|
|
+ }
|
|
|
+
|
|
|
+ void handleDisconnect(void *, esp_event_base_t, int32_t, void *) {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
|
|
|
- for (int i = 0; i < size; i++) {
|
|
|
- APInfo apInfo = {reinterpret_cast<char *>(apRecords[i].ssid), apRecords[i].rssi};
|
|
|
- result.push_back(apInfo);
|
|
|
+ if (retryNum++ < retryMax) {
|
|
|
+ ESP_LOGI(TAG, "reconnecting, attempt %d / %d", retryNum, retryMax);
|
|
|
+ CHECK(esp_wifi_connect());
|
|
|
+ } else {
|
|
|
+ ESP_LOGI(TAG, "disconnected");
|
|
|
+ ::connected = false;
|
|
|
+ if (sta::onDisconnect) sta::onDisconnect();
|
|
|
+ event.setBit(CONNECT_FINISHED);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- instance->onScanDone(result, data);
|
|
|
-}
|
|
|
+ void handleIp(void *, esp_event_base_t, int32_t, void *data) {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
|
|
|
-void wifi::STA::handleGotIp(void *, esp_event_base_t, int32_t, void *pEventData) {
|
|
|
- ESP_LOGD(TAG, "handleGotIp()");
|
|
|
- auto instance = getSTA();
|
|
|
- auto eventData = static_cast<ip_event_got_ip_t *>(pEventData);
|
|
|
+ auto eventData = static_cast<ip_event_got_ip_t *>(data);
|
|
|
+ ::ip = net::IP(eventData->ip_info.ip);
|
|
|
|
|
|
- instance->m_ip = net::IP(eventData->ip_info.ip);
|
|
|
- if (instance->onIp) instance->onIp();
|
|
|
- instance->retryNum = 0;
|
|
|
-}
|
|
|
+ if (sta::onIp) sta::onIp();
|
|
|
+ retryNum = 0;
|
|
|
|
|
|
-void wifi::STA::startScan(void *data) {
|
|
|
- ESP_LOGD(TAG, "startScan()");
|
|
|
+ event.setBit(CONNECT_FINISHED);
|
|
|
+ }
|
|
|
|
|
|
- if (!onScanDone) { ESP_LOGW(TAG, "startScan: onScanDone not set"); }
|
|
|
+ void handleScanDone(void *data, esp_event_base_t, int32_t, void *) {
|
|
|
+ ESP_LOGI(TAG, "%s()", __func__);
|
|
|
|
|
|
- CHECK(esp_event_handler_instance_register(
|
|
|
- WIFI_EVENT, WIFI_EVENT_SCAN_DONE, &handleScanDone, data, &scanDoneHandler));
|
|
|
+ CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, &scanDoneHandler));
|
|
|
+ CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, &::handleScanDone));
|
|
|
|
|
|
- CHECK(esp_wifi_scan_start(nullptr, false));
|
|
|
-}
|
|
|
+ if (!sta::onScanDone) {
|
|
|
+ ESP_LOGW(TAG, "%s: onScanDone not set", __func__);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint16_t size;
|
|
|
+ esp_wifi_scan_get_ap_num(&size);
|
|
|
+ wifi_ap_record_t apRecords[size];
|
|
|
+ esp_wifi_scan_get_ap_records(&size, apRecords);
|
|
|
+
|
|
|
+ vector<APInfo> result;
|
|
|
+
|
|
|
+ for (int i = 0; i < size; i++) {
|
|
|
+ APInfo apInfo = {reinterpret_cast<char *>(apRecords[i].ssid), apRecords[i].rssi};
|
|
|
+ result.push_back(apInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ sta::onScanDone(result, data);
|
|
|
+ }
|
|
|
+}
|