Browse Source

KBF-32 create new wifi.h and tests

Bence Balint 2 years ago
parent
commit
4bd1be9f6b

+ 3 - 0
CMakeLists.txt

@@ -19,6 +19,9 @@ idf_component_register(
         "src/task.cpp"
         "src/uart.cpp"
         "src/web_service.cpp"
+        "src/wifi/ap.cpp"
+        "src/wifi/sta.cpp"
+        "src/wifi/wifi.cpp"
         "src/wifi_legacy/ap.cpp"
         "src/wifi_legacy/sta.cpp"
         "src/wifi_legacy/driver.cpp"

+ 85 - 0
include/kbf/wifi.h

@@ -0,0 +1,85 @@
+#ifndef KBF_WIFI_H
+#define KBF_WIFI_H
+
+#include <string>
+#include <vector>
+
+#include "net.h"
+
+namespace kbf::wifi {
+    void stop();
+
+    enum class Mode {
+        OFF,
+        STA,
+        AP,
+        DUAL
+    };
+
+    Mode mode();
+
+    class STA;
+
+    namespace ap {
+        void start(
+                std::string ssid,
+                std::string password,
+                net::IP ip = net::IP("192.168.4.1"),
+                net::IP netmask = net::IP("255.255.255.0")
+        );
+
+        void stop();
+
+        [[nodiscard]] const std::string &ssid();
+
+        [[nodiscard]] const std::string &password();
+
+        extern void (*onConnect)(STA &);
+
+        extern void (*onDisconnect)(STA &);
+    }
+
+    struct APInfo {
+        std::string ssid;
+        uint8_t rssi;
+    };
+
+    namespace sta {
+        void start();
+
+        void stop();
+
+        bool connect(std::string ssid, std::string password, bool async = false, int maxRetry = 3);
+
+        void disconnect();
+
+        [[nodiscard]] bool connected();
+
+        [[nodiscard]] const std::string &ssid();
+
+        [[nodiscard]] const std::string &password();
+
+        [[nodiscard]] const net::IP &ip();
+
+        extern void (*onConnect)();
+
+        extern void (*onIp)();
+
+        extern void (*onDisconnect)();
+
+        void startScan(void *data);
+
+        extern void (*onScanDone)(std::vector<APInfo> &apList, void *data);
+    }
+
+    namespace dual {
+        void start(
+                std::string ssid,
+                std::string password,
+                net::IP ip = net::IP("192.168.4.1"),
+                net::IP netmask = net::IP("255.255.255.0")
+        );
+    }
+}
+
+#endif //KBF_WIFI_H

+ 1 - 0
src/wifi/ap.cpp

@@ -0,0 +1 @@
+#include "kbf/wifi.h"

+ 1 - 0
src/wifi/sta.cpp

@@ -0,0 +1 @@
+#include "kbf/wifi.h"

+ 1 - 0
src/wifi/wifi.cpp

@@ -0,0 +1 @@
+#include "kbf/wifi.h"

+ 1 - 0
test/CMakeLists.txt

@@ -5,6 +5,7 @@ idf_component_register(
         "driver"
         "esp-idf-lib"
         "rtos"
+        "wifi"
 
         INCLUDE_DIRS
         "."

+ 205 - 0
test/wifi/test_wifi.cpp

@@ -0,0 +1,205 @@
+#include <atomic>
+#include <vector>
+
+#include "kbf.h"
+#include "kbf/wifi.h"
+#include "kbf/rtos.h"
+
+#include <unity.h>
+
+#define TEST_SSID   "kbf_test_wifi_ap"
+#define MASTER_SSID "kbf_test_wifi_master"
+#define SLAVE_SSID  "kbf_test_wifi_slave"
+#define WIFI_PASS   "Pas$w0Rd1337"
+
+using namespace kbf;
+using std::vector;
+
+static std::atomic<int> state{0};
+static rtos::EventGroup event; // NOLINT(cert-err58-cpp)
+
+static void singleMaster() {
+    state = 0;
+    event.clear();
+
+    wifi::ap::onConnect    = {[](wifi::STA &) { TEST_ASSERT_EQUAL(0, state++); }};
+    wifi::ap::onDisconnect = {[](wifi::STA &) {
+        TEST_ASSERT_EQUAL(1, state++);
+        event.setBit(0);
+    }};
+    wifi::ap::start(TEST_SSID, WIFI_PASS);
+
+    event.waitForBit(0);
+    TEST_ASSERT_EQUAL(2, state);
+
+    kbf::sleep(100); // wait for slave tests to finish
+    wifi::stop();
+}
+
+static void singleSlave() {
+    state = 0;
+    event.clear();
+
+    wifi::sta::onConnect    = {[]() { TEST_ASSERT_EQUAL(0, state++); }};
+    wifi::sta::onIp         = {[]() { TEST_ASSERT_EQUAL(1, state++); }};
+    wifi::sta::onDisconnect = {[]() { event.setBit(0); }};
+    wifi::sta::start();
+
+    wifi::sta::connect(TEST_SSID, WIFI_PASS);
+    TEST_ASSERT_EQUAL(2, state);
+
+    event.waitForBit(0);
+    TEST_ASSERT_EQUAL(2, state);
+    wifi::stop();
+}
+
+TEST_CASE_MULTIPLE_DEVICES("WiFi AP <--> STA", "[kbf_wifi]", singleMaster, singleSlave);
+
+static void asyncMaster() {
+    state = 0;
+    event.clear();
+
+    wifi::ap::onConnect    = {[](wifi::STA &) { TEST_ASSERT_EQUAL(0, state++); }};
+    wifi::ap::onDisconnect = {[](wifi::STA &) {
+        TEST_ASSERT_EQUAL(1, state++);
+        event.setBit(0);
+    }};
+    wifi::ap::start(TEST_SSID, WIFI_PASS);
+
+    event.waitForBit(0);
+    TEST_ASSERT_EQUAL(2, state);
+
+    kbf::sleep(100); // wait for slave tests to finish
+    wifi::stop();
+}
+
+static void asyncSlave() {
+    state = 0;
+    event.clear();
+
+    wifi::sta::onConnect    = {[]() { TEST_ASSERT_EQUAL(0, state++); }};
+    wifi::sta::onIp         = {[]() { TEST_ASSERT_EQUAL(1, state++); }};
+    wifi::sta::onDisconnect = {[]() { event.setBit(0); }};
+    wifi::sta::start();
+
+    wifi::sta::connect(TEST_SSID, WIFI_PASS, true);
+    TEST_ASSERT_EQUAL(0, state);
+
+    event.waitForBit(0);
+    TEST_ASSERT_EQUAL(2, state);
+    wifi::stop();
+}
+
+TEST_CASE_MULTIPLE_DEVICES("WiFi AP <--> STA async", "[kbf_wifi]", asyncMaster, asyncSlave);
+
+static void dualMaster() {
+    state = 0;
+    event.clear();
+
+    wifi::ap::onConnect     = {[](wifi::STA &) {
+        TEST_ASSERT_EQUAL(0, state++);
+        wifi::sta::connect(SLAVE_SSID, WIFI_PASS);
+    }};
+    wifi::sta::onConnect    = {[]() {
+        TEST_ASSERT_EQUAL(1, state++);
+        wifi::sta::disconnect();
+    }};
+    wifi::sta::onDisconnect = {[]() {
+        TEST_ASSERT_EQUAL(2, state++);
+    }};
+    wifi::ap::onDisconnect  = {[](wifi::STA &) {
+        TEST_ASSERT_EQUAL(3, state++);
+        event.setBit(0);
+    }};
+    wifi::dual::start(MASTER_SSID, WIFI_PASS);
+
+    event.waitForBit(0);
+    TEST_ASSERT_EQUAL(4, state);
+    wifi::stop();
+}
+
+static void dualSlave() {
+    state = 0;
+    event.clear();
+
+    wifi::sta::onConnect    = {[]() { TEST_ASSERT_EQUAL(0, state++); }};
+    wifi::ap::onConnect     = {[](wifi::STA &) { TEST_ASSERT_EQUAL(1, state++); }};
+    wifi::ap::onDisconnect  = {[](wifi::STA &) {
+        TEST_ASSERT_EQUAL(2, state++);
+        wifi::sta::disconnect();
+    }};
+    wifi::sta::onDisconnect = {[]() {
+        TEST_ASSERT_EQUAL(3, state++);
+        event.setBit(0);
+    }};
+    wifi::dual::start(SLAVE_SSID, WIFI_PASS);
+    wifi::sta::connect(MASTER_SSID, WIFI_PASS);
+
+    event.waitForBit(0);
+    TEST_ASSERT_EQUAL(4, state);
+    wifi::stop();
+}
+
+TEST_CASE_MULTIPLE_DEVICES("WiFi dual <--> dual", "[kbf_wifi]", dualMaster, dualSlave);
+
+static void scanMaster() {
+    event.clear();
+
+    wifi::ap::start(TEST_SSID, WIFI_PASS);
+    wifi::ap::onDisconnect = {[](wifi::STA &){ event.setBit(0); }};
+
+    event.waitForBit(0);
+    wifi::stop();
+}
+
+static void scanSlave() {
+    state = 0;
+    event.clear();
+
+    wifi::sta::onScanDone = {[](vector<wifi::APInfo> &apList, void* data) {
+        TEST_ASSERT_EQUAL(0, state++);
+        TEST_ASSERT_EQUAL_STRING("test", static_cast<char *>(data));
+
+        bool found = false;
+        for (const auto &ap : apList) {
+            if (ap.ssid == TEST_SSID) {
+                found = true;
+            }
+        }
+        TEST_ASSERT_TRUE(found);
+
+        wifi::sta::connect(TEST_SSID, WIFI_PASS);
+    }};
+    wifi::sta::onConnect = {[]() { event.setBit(0); }};
+
+    wifi::sta::start();
+    wifi::sta::startScan((void *) "test");
+
+    event.waitForBit(0);
+    TEST_ASSERT_EQUAL(1, state);
+    wifi::stop();
+}
+
+TEST_CASE_MULTIPLE_DEVICES("WiFi scan", "[kbf_wifi]", scanMaster, scanSlave);
+
+static void ipMaster() {
+    event.clear();
+
+    wifi::ap::start(TEST_SSID, WIFI_PASS, net::IP("192.168.100.1"));
+    wifi::ap::onDisconnect = {[](wifi::STA &){ event.setBit(0); }};
+
+    event.waitForBit(0);
+    wifi::stop();
+}
+
+static void ipSlave() {
+    wifi::sta::start();
+    wifi::sta::connect(TEST_SSID, WIFI_PASS);
+
+    auto ip = wifi::sta::ip().str();
+    TEST_ASSERT_EQUAL_STRING("192.168.100.2", ip.c_str());
+
+    wifi::stop();
+}
+
+TEST_CASE_MULTIPLE_DEVICES("WiFi custom IP address", "[kbf_wifi]", ipMaster, ipSlave);

+ 0 - 0
test/test_wifi_legacy.cpp → test/wifi/test_wifi_legacy.cpp


+ 182 - 0
test/wifi/test_wifi_modeswitch.cpp

@@ -0,0 +1,182 @@
+#include <vector>
+
+#include <esp_log.h>
+
+#include "kbf.h"
+#include "kbf/wifi.h"
+#include "kbf/rtos.h"
+#include "kbf/web_service.h"
+#include "kbf/http/client.h"
+#include "kbf/http/exception.h"
+
+#include <unity.h>
+
+#define MASTER_SSID   "kbf_test_wifi_master"
+#define SLAVE_SSID    "kbf_test_wifi_slave"
+#define WIFI_PASS     "Pas$w0Rd1337"
+#define MASTER_AP     "http://192.168.1.1/ping"
+#define MASTER_STA    "http://192.168.2.2/ping"
+#define SLAVE_AP      "http://192.168.2.1/ping"
+#define SLAVE_STA     "http://192.168.1.2/ping"
+#define PING_RESPONSE "P0NG"
+
+#define PING 0
+
+using namespace kbf;
+using std::vector;
+
+static rtos::EventGroup event; // NOLINT(cert-err58-cpp)
+static string           lastPingID;
+
+class PingController : public WebService::Controller {
+public:
+    PingController() : Controller("/ping") {}
+
+protected:
+    http::Response get(const http::Request &request) override {
+        lastPingID = request.query.at("id");
+        ESP_LOGI("PingController", "%s", lastPingID.c_str());
+        event.setBit(PING);
+        return http::Response(PING_RESPONSE);
+    }
+};
+
+static auto client = new http::Client(); // NOLINT(cert-err58-cpp)
+
+static void pingOK(const string &url, int id) {
+    const auto response = client->get(url + "?id=" + std::to_string(id));
+    TEST_ASSERT_EQUAL(200, response->status);
+    TEST_ASSERT_EQUAL_STRING(PING_RESPONSE, response->body.c_str());
+}
+
+static void pingFail(const string &url) {
+    try {
+        client->get(url);
+    } catch (http::exception::ConnectionError &) {
+        return;
+    }
+    TEST_FAIL();
+}
+
+static void waitForPing(int id) {
+    ESP_LOGI(__func__, "%d", id);
+    event.waitForBit(PING);
+    event.clear();
+    TEST_ASSERT_EQUAL_STRING(std::to_string(id).c_str(), lastPingID.c_str());
+}
+
+static void fail() {
+    TEST_FAIL();
+}
+
+static void fail(wifi::STA &) {
+    TEST_FAIL();
+}
+
+void master() {
+    constexpr const char *const TAG = __func__;
+    auto pingService = WebService();
+    pingService.controller<PingController>();
+
+    ESP_LOGI(TAG, "0 - starting AP");
+    wifi::ap::onDisconnect  = fail;
+    wifi::sta::onDisconnect = fail;
+    wifi::ap::start(MASTER_SSID, WIFI_PASS);
+    TEST_ASSERT_EQUAL(wifi::Mode::AP, wifi::mode());
+    pingService.start();
+
+    ESP_LOGI(TAG, "1 - master is AP, slave is STA");
+    waitForPing(0);
+    pingOK(SLAVE_STA, 1);
+    pingFail(SLAVE_AP);
+
+    ESP_LOGI(TAG, "2 - master is AP, slave switches to DUAL");
+    waitForPing(2);
+    pingOK(SLAVE_STA, 3);
+    pingFail(SLAVE_AP);
+
+    ESP_LOGI(TAG, "3 - master switches to DUAL, slave is DUAL");
+    wifi::sta::start();
+    TEST_ASSERT_EQUAL(wifi::Mode::DUAL, wifi::mode());
+    wifi::sta::connect(SLAVE_SSID, WIFI_PASS);
+    pingOK(SLAVE_STA, 4);
+    pingOK(SLAVE_AP, 5);
+    waitForPing(6);
+    waitForPing(7);
+
+    ESP_LOGI(TAG, "4 - master switches to STA, slave is DUAL");
+    kbf::sleep(100); // wait for slave to disable onDisconnect
+    wifi::ap::stop();
+    TEST_ASSERT_EQUAL(wifi::Mode::STA, wifi::mode());
+    pingOK(SLAVE_AP, 8);
+    waitForPing(9);
+    pingFail(SLAVE_STA);
+
+    ESP_LOGI(TAG, "5 - master is STA, slave switches to AP");
+    waitForPing(10);
+    pingOK(SLAVE_AP,  11);
+    pingFail(SLAVE_STA);
+
+    ESP_LOGI(TAG, "finished");
+    wifi::sta::onDisconnect = nullptr;
+    kbf::sleep(100); // wait for slave to disable onDisconnect
+
+    pingService.stop();
+    wifi::stop();
+    TEST_ASSERT_EQUAL(wifi::Mode::OFF, wifi::mode());
+}
+
+void slave() {
+    constexpr const char *const TAG = __func__;
+    auto pingService = WebService();
+    pingService.controller<PingController>();
+
+    ESP_LOGI(TAG, "0 - starting STA");
+    wifi::ap::onDisconnect  = fail;
+    wifi::sta::onDisconnect = fail;
+    wifi::sta::start();
+    TEST_ASSERT_EQUAL(wifi::Mode::STA, wifi::mode());
+    pingService.start();
+
+    ESP_LOGI(TAG, "1 - master: AP, slave is STA");
+    wifi::sta::connect(MASTER_SSID, WIFI_PASS);
+    pingOK(MASTER_AP, 0);
+    waitForPing(1);
+    pingFail(MASTER_STA);
+
+    ESP_LOGI(TAG, "2 - master is AP, slave switches to DUAL");
+    wifi::ap::start(SLAVE_SSID, WIFI_PASS);
+    TEST_ASSERT_EQUAL(wifi::Mode::DUAL, wifi::mode());
+    pingOK(MASTER_AP, 2);
+    waitForPing(3);
+    pingFail(MASTER_STA);
+
+    ESP_LOGI(TAG, "3 - master switches to DUAL, slave is DUAL");
+    waitForPing(4);
+    waitForPing(5);
+    pingOK(MASTER_STA, 6);
+    pingOK(MASTER_AP, 7);
+
+    ESP_LOGI(TAG, "4 - master switches to STA, slave is DUAL");
+    wifi::sta::onDisconnect = nullptr;
+    waitForPing(8);
+    pingOK(MASTER_STA, 9);
+    pingFail(MASTER_AP);
+
+    ESP_LOGI(TAG, "5 - master is STA, slave switches to AP");
+    wifi::sta::stop();
+    TEST_ASSERT_EQUAL(wifi::Mode::AP, wifi::mode());
+    pingOK(MASTER_STA, 10);
+    waitForPing(11);
+    pingFail(MASTER_AP);
+
+    ESP_LOGI(TAG, "finished");
+    wifi::ap::onDisconnect = nullptr;
+    kbf::sleep(100); // wait for master to disable onDisconnect
+
+    pingService.stop();
+    wifi::stop();
+    TEST_ASSERT_EQUAL(wifi::Mode::OFF, wifi::mode());
+}
+
+TEST_CASE_MULTIPLE_DEVICES("WiFi mode STA to DUAL", "[kbf_wifi]", master, slave);