Ver código fonte

KBF-32 implement wifi::ap

Bence Balint 3 anos atrás
pai
commit
5896674e28

+ 10 - 0
include/kbf/exception.h

@@ -41,6 +41,16 @@ namespace kbf::exception {
 
     class OutOfMemoryError : public std::exception {
     };
+
+    class InvalidArgument : public std::exception {
+        const char *msg;
+    public:
+        explicit InvalidArgument(const char *msg) : msg(msg) {}
+
+        [[nodiscard]] const char *what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_USE_NOEXCEPT override {
+            return msg;
+        }
+    };
 }
 
 #endif //KBF_EXCEPTION_H

+ 14 - 0
include/kbf/internal/wifi.h

@@ -0,0 +1,14 @@
+#ifdef KBF_INTERNAL
+#ifndef KBF_INTERNAL_WIFI_H
+#define KBF_INTERNAL_WIFI_H
+
+namespace kbf::wifi::internal {
+    void init();
+
+    extern bool initialized;
+
+    extern Mode mode;
+}
+
+#endif //KBF_INTERNAL_WIFI_H
+#endif //KBF_INTERNAL

+ 14 - 1
include/kbf/wifi.h

@@ -18,7 +18,15 @@ namespace kbf::wifi {
 
     Mode mode();
 
-    class STA;
+    class STA {
+    public:
+        STA() : aid(0), mac{} {};
+
+        explicit STA(wifi_event_ap_staconnected_t &data) : aid(data.aid), mac(data.mac) {};
+
+        const int           aid;
+        const kbf::net::MAC mac;
+    };
 
     namespace ap {
         void start(
@@ -80,6 +88,11 @@ namespace kbf::wifi {
                 net::IP netmask = net::IP("255.255.255.0")
         );
     }
+
+    namespace exception {
+        class InvalidMode : public std::exception {
+        };
+    }
 }
 
 #endif //KBF_WIFI_H

+ 153 - 1
src/wifi/ap.cpp

@@ -1 +1,153 @@
-#include "kbf/wifi.h"
+#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/exception.h"
+
+using namespace std;
+
+namespace {
+    constexpr const char *const TAG = "kbf::wifi::ap";
+
+    string ssid;
+    string password;
+
+    bool dhcpInit = false;
+
+    esp_netif_obj    *interface = nullptr;
+    wifi_ap_config_t apConfig   = {};
+
+    esp_event_handler_instance_t connectHandler    = {};
+    esp_event_handler_instance_t disconnectHandler = {};
+
+    void handleConnect(void *arg, esp_event_base_t baseType, int32_t eventId, void *);
+
+    void handleDisconnect(void *arg, esp_event_base_t baseType, int32_t eventId, void *);
+
+    void initDhcp(const kbf::net::IP &ip, const kbf::net::IP &netmask);
+
+    void registerEventHandlers();
+
+    void unregisterEventHandlers();
+}
+
+using namespace kbf;
+using namespace kbf::wifi;
+
+const std::string &ap::ssid() { return ::ssid; }
+
+const std::string &ap::password() { return ::password; }
+
+void ap::start(std::string ssid, std::string password, net::IP ip, net::IP netmask) {
+    ESP_LOGI(TAG, "%s(%s, <omitted>, %s, %s)", __func__, ssid.c_str(), ip.str().c_str(), netmask.str().c_str());
+
+    if (internal::mode == Mode::AP || internal::mode == Mode::DUAL) throw exception::InvalidMode();
+    if (ssid.empty()) throw kbf::exception::InvalidArgument("no SSID");
+    if (password.length() < 8) throw kbf::exception::InvalidArgument("password.length() < 8");
+
+    internal::init();
+    interface = esp_netif_create_default_wifi_ap();
+    initDhcp(ip, netmask);
+    registerEventHandlers();
+
+    std::move(ssid.begin(), ssid.end(), ::ssid);
+    std::move(password.begin(), password.end(), ::password);
+
+    apConfig.ssid_len       = ::ssid.length();
+    apConfig.max_connection = 5; // TODO use Kconfig
+    apConfig.authmode       = WIFI_AUTH_WPA2_PSK; // TODO use AUTH_OPEN if no password is provided
+
+    wifi_config_t config = {.ap = apConfig};
+    CHECK(esp_wifi_set_config(WIFI_IF_AP, &config));
+
+    if (internal::mode == Mode::OFF) {
+        CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
+        CHECK(esp_wifi_start());
+        internal::mode = Mode::AP;
+    } else {
+        CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
+        internal::mode = Mode::DUAL;
+    }
+}
+
+void ap::stop() {
+    ESP_LOGI(TAG, "%s()", __func__);
+
+    if (internal::mode != Mode::AP && internal::mode != Mode::DUAL) {
+        throw wifi::exception::InvalidMode();
+    }
+
+    esp_netif_destroy(interface);
+    unregisterEventHandlers();
+
+    if (internal::mode == Mode::DUAL) {
+        CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
+        internal::mode = Mode::STA;
+    } else {
+        CHECK(esp_wifi_stop());
+        internal::mode = Mode::OFF;
+    }
+}
+
+void initDhcp(const net::IP &ip, const net::IP &netmask) {
+    ESP_LOGI(TAG, "%s()", __func__);
+
+    if (dhcpInit) {
+        ESP_LOGI(TAG, "DHCP already initialized");
+        return;
+    }
+
+    esp_netif_ip_info_t ipInfo;
+    IP4_ADDR(&ipInfo.ip, ip[0], ip[1], ip[2], ip[3]);
+    IP4_ADDR(&ipInfo.gw, ip[0], ip[1], ip[2], ip[3]);
+    IP4_ADDR(&ipInfo.netmask, netmask[0], netmask[1], netmask[2], netmask[3]);
+
+    CHECK(esp_netif_dhcps_stop(interface));
+    CHECK(esp_netif_set_ip_info(interface, &ipInfo));
+    CHECK(esp_netif_dhcps_start(interface));
+
+    dhcpInit = true;
+}
+
+void registerEventHandlers() {
+    ESP_LOGI(TAG, "%s()", __func__);
+
+    CHECK(esp_event_handler_instance_register(
+            WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, &handleConnect, nullptr, &connectHandler
+    ));
+    CHECK(esp_event_handler_instance_register(
+            WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, &handleDisconnect, nullptr, &disconnectHandler
+    ));
+}
+
+void unregisterEventHandlers() {
+    ESP_LOGI(TAG, "%s()", __func__);
+
+    CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, &connectHandler));
+    CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, &disconnectHandler));
+    CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, &handleConnect));
+    CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, &handleDisconnect));
+}
+
+void handleConnect(void *, esp_event_base_t, int32_t, void *data) {
+    ESP_LOGI(TAG, "%s()", __func__);
+
+    if (ap::onConnect) {
+        auto sta = STA(*static_cast<wifi_event_ap_staconnected_t *>(data));
+        ap::onConnect(sta);
+    }
+}
+
+void handleDisconnect(void *, esp_event_base_t, int32_t, void *data) {
+    ESP_LOGI(TAG, "%s()", __func__);
+
+    if (ap::onDisconnect) {
+        auto sta = STA(*static_cast<wifi_event_ap_staconnected_t *>(data));
+        ap::onDisconnect(sta);
+    }
+}

+ 36 - 0
src/wifi/wifi.cpp

@@ -1 +1,37 @@
 #include "kbf/wifi.h"
+
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_wifi.h>
+
+#define KBF_INTERNAL
+
+#include "kbf/internal/wifi.h"
+#include "kbf/nvs.h"
+#include "kbf/macros.h"
+
+
+using namespace kbf;
+using namespace kbf::wifi;
+
+bool internal::initialized = false;
+
+Mode internal::mode = Mode::OFF;
+
+void internal::init() {
+    if (initialized) return;
+
+    CHECK(esp_netif_init());
+    CHECK(esp_event_loop_create_default());
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    auto err = esp_wifi_init(&cfg);
+    if (err == ESP_ERR_NVS_NOT_INITIALIZED) {
+        kbf::nvs::init();
+        CHECK(esp_wifi_init(&cfg));
+    } else {
+        CHECK(err);
+    }
+
+    initialized = true;
+}

+ 3 - 0
test/wifi/test_wifi.cpp

@@ -29,6 +29,9 @@ static void singleMaster() {
     }};
     wifi::ap::start(TEST_SSID, WIFI_PASS);
 
+    TEST_ASSERT_EQUAL_STRING(TEST_SSID, wifi::ap::ssid().c_str());
+    TEST_ASSERT_EQUAL_STRING(WIFI_PASS, wifi::ap::password().c_str());
+
     event.waitForBit(0);
     TEST_ASSERT_EQUAL(2, state);