From d6dfa5d6815d40deb62d045aac6182716880c456 Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sat, 29 Nov 2025 16:24:56 -0600 Subject: [PATCH] Compilable version with wifi support - need to test --- .gitignore | 2 + .vscode/settings.json | 68 +++++++++++++++- include/BLE.cpp | 4 +- include/BLE.hpp | 8 +- include/WiFi.cpp | 178 +++++++++++++++++++++++++++++++++++++++--- include/WiFi.hpp | 23 +++++- include/setup.cpp | 12 +++ include/setup.hpp | 6 ++ src/main.cpp | 47 +++++++++-- 9 files changed, 320 insertions(+), 28 deletions(-) create mode 100644 include/setup.cpp create mode 100644 include/setup.hpp diff --git a/.gitignore b/.gitignore index 89cc49c..e01547b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .vscode/c_cpp_properties.json .vscode/launch.json .vscode/ipch + +.DS_Store \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 9139fdd..d7874eb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,71 @@ { "files.associations": { - "nimbledevice.h": "c" + "nimbledevice.h": "c", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "semaphore": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "text_encoding": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "variant": "cpp" } } \ No newline at end of file diff --git a/include/BLE.cpp b/include/BLE.cpp index b1e9ee9..08e5374 100644 --- a/include/BLE.cpp +++ b/include/BLE.cpp @@ -4,8 +4,8 @@ bool flag_scan_requested = false; -std::string SSID = ""; -std::string password = ""; +std::string tempSSID = ""; +std::string tempPassword = ""; bool SSIDGiven = false; bool passGiven = false; diff --git a/include/BLE.hpp b/include/BLE.hpp index ecf25ba..23a91fa 100644 --- a/include/BLE.hpp +++ b/include/BLE.hpp @@ -5,8 +5,8 @@ extern bool flag_scan_requested; -extern std::string SSID; -extern std::string password; +extern std::string tempSSID; +extern std::string tempPassword; extern bool SSIDGiven; extern bool passGiven; @@ -40,7 +40,7 @@ class MyCharCallbacks : public NimBLECharacteristicCallbacks { // SSID characteristic if (val.length() > 0) { printf("Received SSID: %s\n", val.c_str()); - SSID = val; + tempSSID = val; SSIDGiven = true; } } @@ -49,7 +49,7 @@ class MyCharCallbacks : public NimBLECharacteristicCallbacks { if (val.length() > 0) { printf("Received Password: %s\n", val.c_str()); passGiven = true; - password = val; + tempPassword = val; } } else if (uuidStr.find("0003") != std::string::npos) { diff --git a/include/WiFi.cpp b/include/WiFi.cpp index 0c4dd19..162b5c5 100644 --- a/include/WiFi.cpp +++ b/include/WiFi.cpp @@ -1,24 +1,176 @@ #include "WiFi.hpp" -#include "esp_wifi.h" -#include "esp_event.h" +#include "esp_eap_client.h" #include "cJSON.h" // Native replacement for ArduinoJson #include "BLE.hpp" -void init_wifi() { - // 1. Init Network Interface - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - esp_netif_create_default_wifi_sta(); +bool WiFi::authFailed = false; +EventGroupHandle_t WiFi::s_wifi_event_group = NULL; +esp_netif_t* WiFi::netif = NULL; +esp_event_handler_instance_t WiFi::instance_any_id = NULL; +esp_event_handler_instance_t WiFi::instance_got_ip = NULL; - // 2. Init WiFi Driver - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); +#define WIFI_CONNECTED_BIT BIT0 - // 3. Set Mode to Station (Client) - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK(esp_wifi_start()); +// The Event Handler (The engine room) +void WiFi::event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) { + + // We got disconnected -> Retry automatically + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + // 1. Cast the data to the correct struct + wifi_event_sta_disconnected_t* event = (wifi_event_sta_disconnected_t*) event_data; + + printf("WiFi Disconnected. Reason Code: %d\n", event->reason); + + // 2. Check specific Reason Codes + switch (event->reason) { + case WIFI_REASON_AUTH_EXPIRE: // Reason 2 + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: // Reason 15 (Most Common for Wrong Pass) + case WIFI_REASON_BEACON_TIMEOUT: // Reason 200 + case WIFI_REASON_AUTH_FAIL: // Reason 203 + case WIFI_REASON_ASSOC_FAIL: // Reason 204 + case WIFI_REASON_HANDSHAKE_TIMEOUT: // Reason 205 + printf("ERROR: Likely Wrong Password!\n"); + // OPTIONAL: Don't retry immediately if password is wrong + authFailed = true; + break; + + case WIFI_REASON_NO_AP_FOUND: // Reason 201 + printf("ERROR: SSID Not Found\n"); + authFailed = true; + break; + + default: + printf("Retrying...\n"); + esp_wifi_connect(); // Retry for other reasons (interference, etc) + break; + } + xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } + // 3. We got an IP Address -> Success! + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + printf("Got IP: " IPSTR "\n", IP2STR(&event->ip_info.ip)); + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } } +void WiFi::init() { + // 1. Init Network Interface + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_create_default_wifi_sta(); + + // 2. Init WiFi Driver + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + // 3. Set Mode to Station (Client) + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + + ESP_ERROR_CHECK(esp_event_handler_instance_register( + WIFI_EVENT, // The "Topic" (Base) + ESP_EVENT_ANY_ID, // The specific "Subject" (Any ID) + &event_handler, // The Function to call + NULL, // Argument to pass to the function (optional) + &instance_any_id // Where to store the registration handle + )); + + ESP_ERROR_CHECK(esp_event_handler_instance_register( + IP_EVENT, + IP_EVENT_STA_GOT_IP, + &event_handler, + NULL, + &instance_got_ip + )); + + ESP_ERROR_CHECK(esp_wifi_start()); +} + +// --- CHECK STATUS --- +bool WiFi::isConnected() { + if (s_wifi_event_group == NULL) return false; + EventBits_t bits = xEventGroupGetBits(s_wifi_event_group); + return (bits & WIFI_CONNECTED_BIT); +} + +// --- GET IP AS STRING --- +std::string WiFi::getIP() { + esp_netif_ip_info_t ip_info; + esp_netif_get_ip_info(netif, &ip_info); + char buf[20]; + sprintf(buf, IPSTR, IP2STR(&ip_info.ip)); + return std::string(buf); +} + +bool WiFi::attemptConnect(char *SSID, char *PW, wifi_auth_mode_t authMode) { + wifi_config_t wifi_config = {}; + strncpy((char*)wifi_config.sta.ssid, SSID, sizeof(wifi_config.sta.ssid)); + strncpy((char*)wifi_config.sta.password, PW, sizeof(wifi_config.sta.password)); + + wifi_config.sta.threshold.authmode = authMode; + wifi_config.sta.pmf_cfg.capable = true; + wifi_config.sta.pmf_cfg.required = false; + + esp_wifi_set_config(WIFI_IF_STA, &wifi_config); + + return awaitConnected(); +} + +bool WiFi::attemptConnectEnterprise(char *SSID, char *username, char *PW, wifi_auth_mode_t authMode) { + std::string ssid = SSID; + std::string uname = username; + std::string password = PW; + // 1. Auto-generate the Identity + std::string identity = "anonymous"; + + // Check if the username is an email (contains '@') + size_t atPos = uname.find('@'); + if (atPos != std::string::npos) { + // Append the domain from the real username + // Example: "user@gmail.com" -> "anonymous@gmail.com" + identity += uname.substr(atPos); + } + + printf("Real User: %s\n", uname.c_str()); + printf("Outer ID : %s (Privacy Safe)\n", identity.c_str()); + + // 2. Clear & Config + esp_wifi_disconnect(); + + wifi_config_t wifi_config = {}; + strncpy((char*)wifi_config.sta.ssid, ssid.c_str(), sizeof(wifi_config.sta.ssid)); + wifi_config.sta.threshold.authmode = authMode; + esp_wifi_set_config(WIFI_IF_STA, &wifi_config); + + // 3. Set the calculated identity (using new ESP-IDF v5.x API) + esp_eap_client_set_identity((uint8_t *)identity.c_str(), identity.length()); + esp_eap_client_set_username((uint8_t *)uname.c_str(), uname.length()); + esp_eap_client_set_password((uint8_t *)password.c_str(), password.length()); + + esp_wifi_sta_enterprise_enable(); + + return awaitConnected(); +} + +bool WiFi::awaitConnected() { + authFailed = false; + if (esp_wifi_connect() != ESP_OK) return false; + + uint8_t attempts = 0; + while (!isConnected() && attempts < 20) { + if (authFailed) { + printf("SSID/Password was wrong! Aborting connection attempt.\n"); + return false; + } + vTaskDelay(500); + attempts++; + } + if (isConnected()) return true; + return false; +} + +// ------------- non-class -------------- void scanAndUpdateSSIDList() { printf("Starting WiFi Scan...\n"); diff --git a/include/WiFi.hpp b/include/WiFi.hpp index b71734b..8b4ab53 100644 --- a/include/WiFi.hpp +++ b/include/WiFi.hpp @@ -1,7 +1,28 @@ #ifndef WIFI_H #define WIFI_H -void init_wifi(); +#include "esp_wifi.h" +#include + +class WiFi { + public: + static void init(); + static bool attemptConnect(char *SSID, char *PW, wifi_auth_mode_t authMode); + static bool attemptConnectEnterprise(char *SSID, char *username, + char *PW, wifi_auth_mode_t authMode); + private: + static bool authFailed; + static bool awaitConnected(); + static esp_event_handler_instance_t instance_any_id; + static esp_event_handler_instance_t instance_got_ip; + static EventGroupHandle_t s_wifi_event_group; + static esp_netif_t* netif; + static void event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data); + static bool isConnected(); + static std::string getIP(); +}; + void scanAndUpdateSSIDList(); #endif \ No newline at end of file diff --git a/include/setup.cpp b/include/setup.cpp new file mode 100644 index 0000000..f414525 --- /dev/null +++ b/include/setup.cpp @@ -0,0 +1,12 @@ +#include "setup.hpp" +#include "BLE.hpp" +#include "WiFi.hpp" + +void initialSetup() { + NimBLEAdvertising* pAdv = initBLE(); + + while (1) { // try to connect to wifi too. + BLEtick(pAdv); + vTaskDelay(pdMS_TO_TICKS(10)); + } +} \ No newline at end of file diff --git a/include/setup.hpp b/include/setup.hpp new file mode 100644 index 0000000..847a91d --- /dev/null +++ b/include/setup.hpp @@ -0,0 +1,6 @@ +#ifndef SETUP_H +#define SETUP_H + +void initialSetup(); + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b3f6c99..7974aef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,21 +5,54 @@ #include "NimBLEDevice.h" #include "BLE.hpp" #include "WiFi.hpp" +#include "setup.hpp" extern "C" void app_main() { - esp_err_t ret = nvs_flash_init(); + esp_err_t ret = nvs_flash_init(); // change to secure init logic soon!! // 2. If NVS is full or corrupt (common after flashing new code), erase and retry if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); - init_wifi(); - NimBLEAdvertising* pAdv = initBLE(); + WiFi bmWiFi; + bmWiFi.init(); + + nvs_handle_t WiFiHandle; + nvs_open("WiFiCreds", NVS_READWRITE, &WiFiHandle); + size_t ssidSize; + esp_err_t WiFiPrefsError = nvs_get_str(WiFiHandle, "SSID", NULL, &ssidSize); + size_t pwSize; + WiFiPrefsError |= nvs_get_str(WiFiHandle, "PW", NULL, &pwSize); + uint8_t authMode; + WiFiPrefsError |= nvs_get_u8(WiFiHandle, "AuthMode", &authMode); + if (WiFiPrefsError == ESP_ERR_NVS_NOT_FOUND) { + // Make the RGB LED a certain color (Blue?) + nvs_close(WiFiHandle); + initialSetup(); + } else if (WiFiPrefsError == ESP_OK) { + char ssid[ssidSize]; + nvs_get_str(WiFiHandle, "SSID", ssid, &ssidSize); + char pw[pwSize]; + nvs_get_str(WiFiHandle, "PW", pw, &pwSize); + nvs_close(WiFiHandle); + // TODO: add enterprise support + if (!bmWiFi.attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) { + // Make RGB LED certain color (Blue?) + initialSetup(); + } + } else { + // Make RGB LED certain color (Blue?) + nvs_close(WiFiHandle); + printf("Program error in Wifi Connection\n"); + initialSetup(); + } + + // Main loop while (1) { - BLEtick(pAdv); - vTaskDelay(pdMS_TO_TICKS(10)); + // Your main application logic here + vTaskDelay(pdMS_TO_TICKS(100)); } } \ No newline at end of file