From daced162ba09456c576890c13bfa6341ba75a724 Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sat, 3 Jan 2026 22:59:36 -0600 Subject: [PATCH 1/3] taskDriven by claude - must be checked --- include/BLE.cpp | 129 ++------------------------------- include/BLE.hpp | 2 +- include/defines.h | 27 +++++++ include/encoder.cpp | 31 +++++--- include/encoder.hpp | 6 ++ include/servo.cpp | 73 ++++++++++++++++++- include/servo.hpp | 17 +++++ include/setup.cpp | 168 ++++++++++++++++++++++++++++++++++++++----- include/socketIO.cpp | 18 +++-- include/socketIO.hpp | 1 - src/main.cpp | 104 +++++++++++++++++---------- 11 files changed, 378 insertions(+), 198 deletions(-) diff --git a/include/BLE.cpp b/include/BLE.cpp index 713cf0e..3b9c106 100644 --- a/include/BLE.cpp +++ b/include/BLE.cpp @@ -7,9 +7,6 @@ #include #include "bmHTTP.hpp" -std::atomic flag_scan_requested{false}; -std::atomic credsGiven{false}; -std::atomic tokenGiven{false}; std::atomic isBLEClientConnected{false}; std::atomic scanBlock{false}; std::atomic finalAuth{false}; @@ -121,125 +118,12 @@ void notifyAuthStatus(bool success) { tmpConfChar->setValue(""); // Clear value after notify } -bool BLEtick(NimBLEAdvertising* pAdvertising) { - printf("BleTick\n"); - if(flag_scan_requested) { - flag_scan_requested = false; - if (!scanBlock) { - scanBlock = true; - printf("Scanning WiFi...\n"); - bmWiFi.scanAndUpdateSSIDList(); - } - else printf("Duplicate scan request\n"); - } - else if (credsGiven) { - std::string tmpSSID; - std::string tmpUNAME; - std::string tmpPASS; - wifi_auth_mode_t tmpAUTH; - { - std::lock_guard lock(dataMutex); - tmpSSID = SSID; - tmpUNAME = UNAME; - tmpPASS = PASS; - tmpAUTH = auth; - credsGiven = false; - } - - bool wifiConnect; - if (tmpAUTH == WIFI_AUTH_WPA2_ENTERPRISE || tmpAUTH == WIFI_AUTH_WPA3_ENTERPRISE) - wifiConnect = bmWiFi.attemptConnect(tmpSSID.c_str(), tmpUNAME.c_str(), tmpPASS.c_str(), tmpAUTH); - else wifiConnect = bmWiFi.attemptConnect(tmpSSID.c_str(), tmpPASS.c_str(), tmpAUTH); - if (!wifiConnect) { - // notify errored - notifyConnectionStatus(false); - return false; - } - - nvs_handle_t WiFiHandle; - esp_err_t err = nvs_open(nvsWiFi, NVS_READWRITE, &WiFiHandle); - if (err != ESP_OK) { - printf("ERROR Saving Credentials\n"); - // notify errored - notifyConnectionStatus(false); - return false; - } - else { - err = nvs_set_str(WiFiHandle, ssidTag, tmpSSID.c_str()); - if (err == ESP_OK) err = nvs_set_str(WiFiHandle, passTag, tmpPASS.c_str()); - if (err == ESP_OK) err = nvs_set_str(WiFiHandle, unameTag, tmpUNAME.c_str()); - if (err == ESP_OK) err = nvs_set_u8(WiFiHandle, authTag, (uint8_t)tmpAUTH); - if (err == ESP_OK) nvs_commit(WiFiHandle); - nvs_close(WiFiHandle); - } - if (err == ESP_OK) { - // notify connected - notifyConnectionStatus(true); - } - else { - // notify connected - notifyConnectionStatus(false); - } - } - else if (tokenGiven) { - tokenGiven = false; - if (!bmWiFi.isConnected()) { - printf("ERROR: token given without WiFi connection\n"); - notifyAuthStatus(false); - return false; - } - - // HTTP request to verify device with token - std::string tmpTOKEN; - { - std::lock_guard lock(dataMutex); - tmpTOKEN = TOKEN; - } - - cJSON *responseRoot; - bool success = httpGET("verify_device", tmpTOKEN, responseRoot); - if (!success) return false; - success = false; - - if (responseRoot != NULL) { - cJSON *tokenItem = cJSON_GetObjectItem(responseRoot, "token"); - if (cJSON_IsString(tokenItem) && tokenItem->valuestring != NULL) { - printf("New token received: %s\n", tokenItem->valuestring); - - // Save token to NVS - nvs_handle_t AuthHandle; - esp_err_t nvs_err = nvs_open(nvsAuth, NVS_READWRITE, &AuthHandle); - if (nvs_err == ESP_OK) { - nvs_err = nvs_set_str(AuthHandle, tokenTag, tokenItem->valuestring); - if (nvs_err == ESP_OK) { - nvs_commit(AuthHandle); - success = true; - webToken = tokenItem->valuestring; - } - else printf("ERROR: could not save webToken to NVS\n"); - nvs_close(AuthHandle); - } - else printf("ERROR: Couldn't open NVS for auth token\n"); - } - cJSON_Delete(responseRoot); - } - else printf("Failed to parse JSON response\n"); - - finalAuth = true; - notifyAuthStatus(success); - if (success) NimBLEDevice::deinit(true); // deinitialize BLE - return success; - } - return false; -} +// BLEtick() removed - replaced by bleSetupTask() in setup.cpp void reset() { esp_wifi_scan_stop(); if (!finalAuth) esp_wifi_disconnect(); scanBlock = false; - flag_scan_requested = false; - credsGiven = false; - tokenGiven = false; } void MyServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { @@ -295,7 +179,6 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI else error = true; if (error) { printf("ERROR: Invalid Auth mode passed in with JSON.\n"); - credsGiven = false; cJSON_Delete(root); return; } @@ -314,13 +197,13 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI SSID = ssid->valuestring; PASS = passPresent ? password->valuestring : ""; UNAME = unamePresent ? uname->valuestring : ""; - credsGiven = tempCredsGiven; // update the global flag. + // Signal via event group instead of flag + xEventGroupSetBits(g_system_events, EVENT_BLE_CREDS_RECEIVED); } else printf("ERROR: Did not receive necessary credentials.\n"); cJSON_Delete(root); } else { printf("Failed to parse JSON\n"); - credsGiven = false; } } } @@ -329,14 +212,16 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI printf("Received Token: %s\n", val.c_str()); std::lock_guard lock(dataMutex); TOKEN = val; - tokenGiven = true; + // Signal via event group instead of flag + xEventGroupSetBits(g_system_events, EVENT_BLE_TOKEN_RECEIVED); } } else if (pChar == currentRefreshChar) { if (val == "Start") { // Refresh characteristic printf("Refresh Requested\n"); - flag_scan_requested = true; + // Signal via event group instead of flag + xEventGroupSetBits(g_system_events, EVENT_BLE_SCAN_REQUEST); } else if (val == "Done") { printf("Data read complete\n"); diff --git a/include/BLE.hpp b/include/BLE.hpp index 8a3d34f..116d96f 100644 --- a/include/BLE.hpp +++ b/include/BLE.hpp @@ -23,6 +23,6 @@ class MyCharCallbacks : public NimBLECharacteristicCallbacks { }; NimBLEAdvertising* initBLE(); -bool BLEtick(NimBLEAdvertising* pAdvertising); +// BLEtick removed - now using event-driven bleSetupTask #endif \ No newline at end of file diff --git a/include/defines.h b/include/defines.h index e41d3da..07c60f4 100644 --- a/include/defines.h +++ b/include/defines.h @@ -2,6 +2,33 @@ #define DEFINES_H #include "driver/gpio.h" #include "driver/ledc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" + +// Task priorities +#define SERVO_TASK_PRIORITY (tskIDLE_PRIORITY + 4) // Highest - real-time control +#define ENCODER_TASK_PRIORITY (tskIDLE_PRIORITY + 3) // High - encoder processing +#define SOCKETIO_TASK_PRIORITY (tskIDLE_PRIORITY + 2) // Medium +#define BLE_TASK_PRIORITY (tskIDLE_PRIORITY + 1) // Low +#define MAIN_TASK_PRIORITY (tskIDLE_PRIORITY + 1) // Low + +// Event bits for system events +#define EVENT_WIFI_CONNECTED BIT0 +#define EVENT_SOCKETIO_CONNECTED BIT1 +#define EVENT_SOCKETIO_DISCONNECTED BIT2 +#define EVENT_CLEAR_CALIB BIT3 +#define EVENT_SAVE_POSITION BIT4 +#define EVENT_BLE_SCAN_REQUEST BIT5 +#define EVENT_BLE_CREDS_RECEIVED BIT6 +#define EVENT_BLE_TOKEN_RECEIVED BIT7 + +// Global synchronization primitives +extern EventGroupHandle_t g_system_events; +extern QueueHandle_t g_servo_command_queue; +extern QueueHandle_t g_encoder_event_queue; +extern SemaphoreHandle_t g_calibration_mutex; #define ccwSpeed 6500 #define cwSpeed 3300 diff --git a/include/encoder.cpp b/include/encoder.cpp index 1687c4c..cf3c4a2 100644 --- a/include/encoder.cpp +++ b/include/encoder.cpp @@ -3,6 +3,7 @@ #include "esp_log.h" #include "soc/gpio_struct.h" #include "servo.hpp" +#include "defines.h" static const char *TAG = "ENCODER"; @@ -47,18 +48,32 @@ void IRAM_ATTR Encoder::isr_handler(void* arg) if (encoder->last_count_base > 3) { encoder->count += 1; encoder->last_count_base -= 4; - if (calibListen) servoCalibListen(); - if (encoder->feedWDog) esp_timer_restart(encoder->watchdog_handle, 500000); - if (encoder->wandListen) servoWandListen(); - if (encoder->serverListen) servoServerListen(); + + // DEFER to task via queue instead of direct function calls + encoder_event_t event = { + .count = encoder->count.load(), + .is_top_encoder = (encoder == topEnc) + }; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + if (g_encoder_event_queue != NULL) { + xQueueSendFromISR(g_encoder_event_queue, &event, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } } else if (encoder->last_count_base < 0) { encoder->count -= 1; encoder->last_count_base += 4; - if (calibListen) servoCalibListen(); - if (encoder->feedWDog) esp_timer_restart(encoder->watchdog_handle, 500000); - if (encoder->wandListen) servoWandListen(); - if (encoder->serverListen) servoServerListen(); + + // DEFER to task via queue instead of direct function calls + encoder_event_t event = { + .count = encoder->count.load(), + .is_top_encoder = (encoder == topEnc) + }; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + if (g_encoder_event_queue != NULL) { + xQueueSendFromISR(g_encoder_event_queue, &event, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } } encoder->last_state_a = current_a; diff --git a/include/encoder.hpp b/include/encoder.hpp index a4015b2..d1e7d2a 100644 --- a/include/encoder.hpp +++ b/include/encoder.hpp @@ -4,6 +4,12 @@ #include #include "esp_timer.h" +// Encoder event structure for queue +typedef struct { + int32_t count; + bool is_top_encoder; +} encoder_event_t; + class Encoder { public: // Shared between ISR and main code diff --git a/include/servo.cpp b/include/servo.cpp index edbd87b..db3ee30 100644 --- a/include/servo.cpp +++ b/include/servo.cpp @@ -115,9 +115,11 @@ void initMainLoop() { } void IRAM_ATTR watchdogCallback(void* arg) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + if (runningManual || runningServer) { // if we're trying to move and our timer ran out, we need to recalibrate - clearCalibFlag = true; + xEventGroupSetBitsFromISR(g_system_events, EVENT_CLEAR_CALIB, &xHigherPriorityTaskWoken); topEnc->pauseWatchdog(); // get ready for recalibration by clearing all these listeners @@ -129,11 +131,13 @@ void IRAM_ATTR watchdogCallback(void* arg) { else { // if no movement is running, we're fine // save current servo-encoder position for reinitialization - savePosFlag = true; + xEventGroupSetBitsFromISR(g_system_events, EVENT_SAVE_POSITION, &xHigherPriorityTaskWoken); } // clear running flags runningManual = false; runningServer = false; + + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } void servoSavePos() { @@ -240,4 +244,69 @@ void runToAppPos(uint8_t appPos) { if (startLess) servoOn(CCW, server); // begin servo movement else servoOn(CW, server); topEnc->serverListen = true; // start listening for shutoff point +} + +// Servo control task - processes encoder events and servo commands +void servoControlTask(void* arg) { + encoder_event_t enc_event; + servo_cmd_msg_t cmd; + + printf("Servo control task started\n"); + + while (1) { + // Block waiting for encoder events (higher priority) + if (xQueueReceive(g_encoder_event_queue, &enc_event, pdMS_TO_TICKS(10)) == pdTRUE) { + // Process encoder event (work that was done in ISR before) + + // Handle calibration listening + if (calibListen) { + int32_t effDiff = (bottomEnc->getCount() - topEnc->getCount()) - baseDiff; + if (effDiff > 1) { + servoOn(CCW, manual); + } + else if (effDiff < -1) { + servoOn(CW, manual); + } + else { + servoOff(); + } + } + + // Only process top encoder events for watchdog and listeners + if (enc_event.is_top_encoder) { + // Feed watchdog in task context (not ISR) + if (topEnc->feedWDog) { + esp_timer_restart(topEnc->watchdog_handle, 500000); + } + + // Check wand listener - now safe in task context + if (topEnc->wandListen) { + servoWandListen(); + } + + // Check server listener - now safe in task context + if (topEnc->serverListen) { + servoServerListen(); + } + } + } + + // Check for direct servo commands (lower priority) + if (xQueueReceive(g_servo_command_queue, &cmd, 0) == pdTRUE) { + switch (cmd.command) { + case SERVO_CMD_STOP: + servoOff(); + break; + case SERVO_CMD_MOVE_CCW: + servoOn(CCW, cmd.is_manual ? manual : server); + break; + case SERVO_CMD_MOVE_CW: + servoOn(CW, cmd.is_manual ? manual : server); + break; + case SERVO_CMD_MOVE_TO_POSITION: + runToAppPos(cmd.target_position); + break; + } + } + } } \ No newline at end of file diff --git a/include/servo.hpp b/include/servo.hpp index a738004..a4fcfdd 100644 --- a/include/servo.hpp +++ b/include/servo.hpp @@ -9,6 +9,20 @@ #define server 1 #define manual 0 +// Command structures for servo control +typedef enum { + SERVO_CMD_STOP, + SERVO_CMD_MOVE_CCW, + SERVO_CMD_MOVE_CW, + SERVO_CMD_MOVE_TO_POSITION +} servo_command_t; + +typedef struct { + servo_command_t command; + uint8_t target_position; // For MOVE_TO_POSITION + bool is_manual; // vs server-initiated +} servo_cmd_msg_t; + extern std::atomic calibListen; extern std::atomic clearCalibFlag; extern std::atomic savePosFlag; @@ -35,4 +49,7 @@ void servoWandListen(); void servoServerListen(); void runToAppPos(uint8_t appPos); +// Servo control task +void servoControlTask(void* arg); + #endif \ No newline at end of file diff --git a/include/setup.cpp b/include/setup.cpp index 74dba76..35ee13c 100644 --- a/include/setup.cpp +++ b/include/setup.cpp @@ -5,14 +5,147 @@ #include "defines.h" #include "bmHTTP.hpp" #include "socketIO.hpp" +#include + +// External declarations from BLE.cpp +extern std::mutex dataMutex; +extern wifi_auth_mode_t auth; +extern std::string SSID; +extern std::string PASS; +extern std::string UNAME; +extern std::string TOKEN; +extern std::atomic scanBlock; +extern std::atomic finalAuth; + +// External functions from BLE.cpp +extern void notifyConnectionStatus(bool success); +extern void notifyAuthStatus(bool success); + +// BLE setup task - event-driven instead of polling +void bleSetupTask(void* arg) { + NimBLEAdvertising* pAdvertising = initBLE(); + EventBits_t bits; + + printf("BLE setup task started\n"); + + while (1) { + // Wait for BLE events instead of polling + bits = xEventGroupWaitBits( + g_system_events, + EVENT_BLE_SCAN_REQUEST | EVENT_BLE_CREDS_RECEIVED | EVENT_BLE_TOKEN_RECEIVED, + pdTRUE, // Clear bits on exit + pdFALSE, // Wait for ANY bit (not all) + portMAX_DELAY // Block forever until event + ); + + if (bits & EVENT_BLE_SCAN_REQUEST) { + if (!scanBlock) { + scanBlock = true; + printf("Scanning WiFi...\n"); + bmWiFi.scanAndUpdateSSIDList(); + } + else printf("Duplicate scan request\n"); + } + + if (bits & EVENT_BLE_CREDS_RECEIVED) { + std::string tmpSSID; + std::string tmpUNAME; + std::string tmpPASS; + wifi_auth_mode_t tmpAUTH; + { + std::lock_guard lock(dataMutex); + tmpSSID = SSID; + tmpUNAME = UNAME; + tmpPASS = PASS; + tmpAUTH = auth; + } + + bool wifiConnect; + if (tmpAUTH == WIFI_AUTH_WPA2_ENTERPRISE || tmpAUTH == WIFI_AUTH_WPA3_ENTERPRISE) + wifiConnect = bmWiFi.attemptConnect(tmpSSID, tmpUNAME, tmpPASS, tmpAUTH); + else wifiConnect = bmWiFi.attemptConnect(tmpSSID, tmpPASS, tmpAUTH); + + if (!wifiConnect) { + notifyConnectionStatus(false); + printf("Connection failed\n"); + continue; + } + + nvs_handle_t WiFiHandle; + esp_err_t err = nvs_open(nvsWiFi, NVS_READWRITE, &WiFiHandle); + if (err == ESP_OK) { + esp_err_t saveErr = ESP_OK; + saveErr |= nvs_set_str(WiFiHandle, ssidTag, tmpSSID.c_str()); + saveErr |= nvs_set_str(WiFiHandle, passTag, tmpPASS.c_str()); + saveErr |= nvs_set_u8(WiFiHandle, authTag, (uint8_t)tmpAUTH); + + if (tmpUNAME.length() > 0) { + saveErr |= nvs_set_str(WiFiHandle, unameTag, tmpUNAME.c_str()); + } + + if (saveErr == ESP_OK) { + nvs_commit(WiFiHandle); + printf("WiFi credentials saved to NVS\n"); + notifyConnectionStatus(true); + } else { + printf("Failed to save WiFi credentials\n"); + notifyConnectionStatus(false); + } + nvs_close(WiFiHandle); + } + } + + if (bits & EVENT_BLE_TOKEN_RECEIVED) { + std::string tmpTOKEN; + { + std::lock_guard lock(dataMutex); + tmpTOKEN = TOKEN; + } + + cJSON* JSONresponse = NULL; + if (httpGET("api/devices/auth", tmpTOKEN, JSONresponse)) { + cJSON *success = cJSON_GetObjectItem(JSONresponse, "success"); + bool authSuccess = cJSON_IsTrue(success); + + if (authSuccess) { + nvs_handle_t authHandle; + if (nvs_open(nvsAuth, NVS_READWRITE, &authHandle) == ESP_OK) { + if (nvs_set_str(authHandle, tokenTag, tmpTOKEN.c_str()) == ESP_OK) { + nvs_commit(authHandle); + printf("Token saved to NVS\n"); + notifyAuthStatus(true); + finalAuth = true; + + // Task complete - delete itself + vTaskDelete(NULL); + } + nvs_close(authHandle); + } + } else { + printf("Token authentication failed\n"); + notifyAuthStatus(false); + } + cJSON_Delete(JSONresponse); + } else { + printf("HTTP request failed\n"); + notifyAuthStatus(false); + } + } + } +} void initialSetup() { printf("Entered Setup\n"); - NimBLEAdvertising* pAdv = initBLE(); - - while (!BLEtick(pAdv)) { + + // Create BLE setup task instead of polling loop + xTaskCreate(bleSetupTask, "ble_setup", 8192, NULL, BLE_TASK_PRIORITY, NULL); + + // Wait for BLE setup to complete (finalAuth = true) + while (!finalAuth) { vTaskDelay(pdMS_TO_TICKS(100)); } + + printf("BLE setup complete\n"); } void setupLoop() { @@ -56,29 +189,28 @@ void setupLoop() { // The server will verify the token during connection handshake webToken = std::string(token); printf("Connecting to Socket.IO server with saved token...\n"); - statusResolved = false; initSocketIO(); - // Wait for device_init message from server with timeout - int timeout_count = 0; - const int MAX_TIMEOUT = 60; // 10 seconds (20 * 500ms) - while (!statusResolved && timeout_count < MAX_TIMEOUT) { - printf("Waiting for device_init message... (%d/%d)\n", timeout_count, MAX_TIMEOUT); - vTaskDelay(pdMS_TO_TICKS(500)); - timeout_count++; - } + // Wait for connection event with timeout + EventBits_t bits = xEventGroupWaitBits( + g_system_events, + EVENT_SOCKETIO_CONNECTED | EVENT_SOCKETIO_DISCONNECTED, + pdTRUE, // Clear on exit + pdFALSE, // Wait for ANY bit + pdMS_TO_TICKS(10000) // 10 second timeout + ); - if (timeout_count >= MAX_TIMEOUT) { - printf("Timeout waiting for device_init - connection failed\n"); - stopSocketIO(); - initSuccess = false; - initialSetup(); - } else { + if (bits & EVENT_SOCKETIO_CONNECTED) { initSuccess = connected; if (!initSuccess) { printf("Device authentication failed - entering setup\n"); initialSetup(); } + } else { + printf("Timeout waiting for connection\n"); + stopSocketIO(); + initSuccess = false; + initialSetup(); } } else { diff --git a/include/socketIO.cpp b/include/socketIO.cpp index 496a234..d8d2ac0 100644 --- a/include/socketIO.cpp +++ b/include/socketIO.cpp @@ -6,11 +6,11 @@ #include "cJSON.h" #include "calibration.hpp" #include "servo.hpp" +#include "defines.h" static esp_socketio_client_handle_t io_client; static esp_socketio_packet_handle_t tx_packet = NULL; -std::atomic statusResolved{true}; std::atomic connected{false}; // Event handler for Socket.IO events @@ -64,7 +64,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, // Mark connection as failed connected = false; - statusResolved = true; + xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); } // Handle device_init event else if (strcmp(eventName->valuestring, "device_init") == 0) { @@ -101,13 +101,13 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, // Now mark as connected connected = true; - statusResolved = true; + xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_CONNECTED); } else { printf("Device authentication failed\n"); calib.clearCalibrated(); deleteWiFiAndTokenDetails(); connected = false; - statusResolved = true; + xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); } } } @@ -124,7 +124,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, calib.clearCalibrated(); deleteWiFiAndTokenDetails(); connected = false; - statusResolved = true; + xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); } // Handle calib_start event @@ -236,9 +236,9 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, } } - // Set flags to indicate connection failure + // Signal disconnection via event group connected = false; - statusResolved = true; + xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); break; } } @@ -247,7 +247,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, if (data->websocket_event_id == WEBSOCKET_EVENT_DISCONNECTED) { printf("WebSocket disconnected\n"); connected = false; - statusResolved = true; + xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); } } @@ -255,7 +255,6 @@ void initSocketIO() { // Prepare the Authorization Header (Bearer format) std::string authHeader = "Authorization: Bearer " + webToken + "\r\n"; - statusResolved = false; connected = false; esp_socketio_client_config_t config = {}; @@ -277,7 +276,6 @@ void stopSocketIO() { io_client = NULL; tx_packet = NULL; connected = false; - statusResolved = false; } } diff --git a/include/socketIO.hpp b/include/socketIO.hpp index 9144a4c..4d3c7f2 100644 --- a/include/socketIO.hpp +++ b/include/socketIO.hpp @@ -2,7 +2,6 @@ #define SOCKETIO_HPP #include -extern std::atomic statusResolved; extern std::atomic connected; // Initialize Socket.IO client and connect to server diff --git a/src/main.cpp b/src/main.cpp index a1aafe4..b357bdf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,59 +9,54 @@ #include "encoder.hpp" #include "calibration.hpp" +// Global synchronization primitives +EventGroupHandle_t g_system_events = NULL; +QueueHandle_t g_servo_command_queue = NULL; +QueueHandle_t g_encoder_event_queue = NULL; +SemaphoreHandle_t g_calibration_mutex = NULL; + // Global encoder instances Encoder* topEnc = new Encoder(ENCODER_PIN_A, ENCODER_PIN_B); Encoder* bottomEnc = new Encoder(InputEnc_PIN_A, InputEnc_PIN_B); -// Global encoder pointers (used by servo.cpp) - // Global calibration instance Calibration calib; -void mainApp() { - 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(ret); - - bmWiFi.init(); - calib.init(); +void mainTask(void* arg) { + EventBits_t bits; - // Initialize encoders - topEnc->init(); - bottomEnc->init(); - servoInit(); - - setupLoop(); + printf("Main task started - event-driven mode\n"); - statusResolved = false; - - int32_t prevCount = topEnc->getCount(); - - // Main loop while (1) { - // websocket disconnect/reconnect handling - if (statusResolved) { + // Block waiting for ANY event (no polling!) + bits = xEventGroupWaitBits( + g_system_events, + EVENT_SOCKETIO_DISCONNECTED | EVENT_CLEAR_CALIB | EVENT_SAVE_POSITION, + pdTRUE, // Clear bits on exit + pdFALSE, // Wait for ANY bit (not all) + portMAX_DELAY // Block forever - no polling! + ); + + if (bits & EVENT_SOCKETIO_DISCONNECTED) { if (!connected) { printf("Disconnected! Beginning setup loop.\n"); stopSocketIO(); setupLoop(); } - else printf("Reconnected!\n"); - statusResolved = false; + else { + printf("Reconnected!\n"); + } } - - if (clearCalibFlag) { + + if (bits & EVENT_CLEAR_CALIB) { + xSemaphoreTake(g_calibration_mutex, portMAX_DELAY); calib.clearCalibrated(); + xSemaphoreGive(g_calibration_mutex); emitCalibStatus(false); - clearCalibFlag = false; } - if (savePosFlag) { + + if (bits & EVENT_SAVE_POSITION) { servoSavePos(); - savePosFlag = false; // Send position update to server uint8_t currentAppPos = calib.convertToAppPos(topEnc->getCount()); @@ -69,7 +64,6 @@ void mainApp() { printf("Sent pos_hit: position %d\n", currentAppPos); } - vTaskDelay(pdMS_TO_TICKS(100)); } } @@ -91,6 +85,44 @@ void encoderTest() { } extern "C" void app_main() { - mainApp(); - // encoderTest(); + // Initialize NVS first + esp_err_t ret = nvs_flash_init(); + 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(ret); + + // Create synchronization primitives + g_system_events = xEventGroupCreate(); + g_servo_command_queue = xQueueCreate(10, sizeof(servo_cmd_msg_t)); + g_encoder_event_queue = xQueueCreate(50, sizeof(encoder_event_t)); + g_calibration_mutex = xSemaphoreCreateMutex(); + + if (g_system_events == NULL || g_servo_command_queue == NULL || + g_encoder_event_queue == NULL || g_calibration_mutex == NULL) { + printf("ERROR: Failed to create synchronization primitives\n"); + return; + } + + // Initialize hardware + bmWiFi.init(); + calib.init(); + + // Initialize encoders + topEnc->init(); + bottomEnc->init(); + servoInit(); + + // Create servo control task (highest priority - real-time) + xTaskCreate(servoControlTask, "servo_ctrl", 4096, NULL, SERVO_TASK_PRIORITY, NULL); + + // Create main task (lower priority) + xTaskCreate(mainTask, "main", 4096, NULL, MAIN_TASK_PRIORITY, NULL); + + // Run setup loop (this will handle WiFi/SocketIO connection) + setupLoop(); + + // app_main returns, but tasks continue running + printf("app_main complete - tasks running\n"); } \ No newline at end of file From 636aaf64f806bb1e92ccdcbb3f4b384d562d08c3 Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sun, 4 Jan 2026 11:43:23 -0600 Subject: [PATCH 2/3] Static classes, enterprise networks properly handled --- include/WiFi.cpp | 12 ++++++++++-- include/WiFi.hpp | 6 ++++-- include/calibration.cpp | 5 +++++ include/calibration.hpp | 22 ++++++++++------------ include/servo.cpp | 16 ++++++++-------- include/setup.cpp | 16 ++++++++-------- include/socketIO.cpp | 6 +++--- src/main.cpp | 11 ++++------- 8 files changed, 52 insertions(+), 42 deletions(-) diff --git a/include/WiFi.cpp b/include/WiFi.cpp index 80ca08c..3d55bbe 100644 --- a/include/WiFi.cpp +++ b/include/WiFi.cpp @@ -12,8 +12,6 @@ esp_event_handler_instance_t WiFi::instance_got_ip = NULL; #define WIFI_CONNECTED_BIT BIT0 #define WIFI_STARTED_BIT BIT1 -WiFi bmWiFi; - // 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) { @@ -119,6 +117,16 @@ bool WiFi::isConnected() { return (bits & WIFI_CONNECTED_BIT); } +// Helper to check if auth mode requires enterprise credentials +bool WiFi::isEnterpriseMode(wifi_auth_mode_t authMode) { + return (authMode == WIFI_AUTH_WPA2_ENTERPRISE || + authMode == WIFI_AUTH_WPA3_ENTERPRISE || + authMode == WIFI_AUTH_WPA2_WPA3_ENTERPRISE || + authMode == WIFI_AUTH_WPA_ENTERPRISE || + authMode == WIFI_AUTH_WPA3_ENT_192 || + authMode == WIFI_AUTH_ENTERPRISE); // Deprecated alias for WPA2 +} + // --- GET IP AS STRING --- std::string WiFi::getIP() { esp_netif_ip_info_t ip_info; diff --git a/include/WiFi.hpp b/include/WiFi.hpp index 77bde73..fb58bff 100644 --- a/include/WiFi.hpp +++ b/include/WiFi.hpp @@ -14,6 +14,10 @@ class WiFi { const std::string password, const wifi_auth_mode_t authMode); static bool isConnected(); static void scanAndUpdateSSIDList(); + + // Helper to check if auth mode requires enterprise credentials + static bool isEnterpriseMode(wifi_auth_mode_t authMode); + private: static void processScanResults(); static std::atomic authFailed; @@ -27,6 +31,4 @@ class WiFi { static std::string getIP(); }; -extern WiFi bmWiFi; - #endif \ No newline at end of file diff --git a/include/calibration.cpp b/include/calibration.cpp index e472015..6a27d5d 100644 --- a/include/calibration.cpp +++ b/include/calibration.cpp @@ -2,6 +2,11 @@ #include "defines.h" #include "nvs_flash.h" +// Define static members +std::atomic Calibration::DownTicks{0}; +std::atomic Calibration::UpTicks{0}; +std::atomic Calibration::calibrated{false}; + void Calibration::init() { nvs_handle_t calibHandle; if (nvs_open(nvsCalib, NVS_READONLY, &calibHandle) == ESP_OK) { diff --git a/include/calibration.hpp b/include/calibration.hpp index e862e7e..ff3b0dc 100644 --- a/include/calibration.hpp +++ b/include/calibration.hpp @@ -5,20 +5,18 @@ class Calibration { public: - void init(); - bool beginDownwardCalib(Encoder& topEnc); - bool completeCalib(Encoder& topEnc); - int32_t convertToTicks(uint8_t appPos); - uint8_t convertToAppPos(int32_t ticks); - bool getCalibrated() {return calibrated;} - bool clearCalibrated(); - std::atomic DownTicks; - std::atomic UpTicks; + static void init(); + static bool beginDownwardCalib(Encoder& topEnc); + static bool completeCalib(Encoder& topEnc); + static int32_t convertToTicks(uint8_t appPos); + static uint8_t convertToAppPos(int32_t ticks); + static bool getCalibrated() {return calibrated;} + static bool clearCalibrated(); + static std::atomic DownTicks; + static std::atomic UpTicks; private: - std::atomic calibrated; + static std::atomic calibrated; }; -extern Calibration calib; - #endif \ No newline at end of file diff --git a/include/servo.cpp b/include/servo.cpp index db3ee30..ed8ac20 100644 --- a/include/servo.cpp +++ b/include/servo.cpp @@ -42,7 +42,7 @@ void servoInit() { gpio_set_level(servoSwitch, 0); // Start with servo power off topEnc->count = servoReadPos(); - if (calib.getCalibrated()) initMainLoop(); + if (Calibration::getCalibrated()) initMainLoop(); } void servoOn(uint8_t dir, uint8_t manOrServer) { @@ -72,7 +72,7 @@ bool servoInitCalib() { bottomEnc->wandListen = false; topEnc->wandListen = false; topEnc->serverListen = false; - if (!calib.clearCalibrated()) return false; + if (!Calibration::clearCalibrated()) return false; if (topEnc == nullptr || bottomEnc == nullptr) { printf("ERROR: CALIBRATION STARTED BEFORE SERVO INITIALIZATION\n"); return false; @@ -93,7 +93,7 @@ bool servoBeginDownwardCalib() { calibListen = false; servoOff(); vTaskDelay(pdMS_TO_TICKS(1000)); - if (!calib.beginDownwardCalib(*topEnc)) return false; + if (!Calibration::beginDownwardCalib(*topEnc)) return false; baseDiff = bottomEnc->getCount() - topEnc->getCount(); calibListen = true; return true; @@ -103,7 +103,7 @@ bool servoCompleteCalib() { calibListen = false; servoOff(); vTaskDelay(pdMS_TO_TICKS(1000)); - if (!calib.completeCalib(*topEnc)) return false; + if (!Calibration::completeCalib(*topEnc)) return false; initMainLoop(); return true; } @@ -183,8 +183,8 @@ void servoWandListen() { stopServerRun(); // freeze atomic values - int32_t upBound = calib.UpTicks; - int32_t downBound = calib.DownTicks; + int32_t upBound = Calibration::UpTicks; + int32_t downBound = Calibration::DownTicks; int32_t bottomCount = bottomEnc->getCount(); int32_t topCount = topEnc->getCount(); @@ -231,13 +231,13 @@ void servoServerListen() { void runToAppPos(uint8_t appPos) { // manual control takes precedence over remote control, always. // also do not begin operation if not calibrated; - if (runningManual || !calib.getCalibrated()) return; + if (runningManual || !Calibration::getCalibrated()) return; servoOff(); // allow servo position to settle vTaskDelay(pdMS_TO_TICKS(500)); int32_t topCount = topEnc->getCount(); - target = calib.convertToTicks(appPos); // calculate target encoder position + target = Calibration::convertToTicks(appPos); // calculate target encoder position if (abs(topCount - target) <= 1) return; startLess = topCount < target; if (runningManual) return; // check again before starting remote control diff --git a/include/setup.cpp b/include/setup.cpp index 35ee13c..4792d2e 100644 --- a/include/setup.cpp +++ b/include/setup.cpp @@ -42,7 +42,7 @@ void bleSetupTask(void* arg) { if (!scanBlock) { scanBlock = true; printf("Scanning WiFi...\n"); - bmWiFi.scanAndUpdateSSIDList(); + WiFi::scanAndUpdateSSIDList(); } else printf("Duplicate scan request\n"); } @@ -61,9 +61,9 @@ void bleSetupTask(void* arg) { } bool wifiConnect; - if (tmpAUTH == WIFI_AUTH_WPA2_ENTERPRISE || tmpAUTH == WIFI_AUTH_WPA3_ENTERPRISE) - wifiConnect = bmWiFi.attemptConnect(tmpSSID, tmpUNAME, tmpPASS, tmpAUTH); - else wifiConnect = bmWiFi.attemptConnect(tmpSSID, tmpPASS, tmpAUTH); + if (WiFi::isEnterpriseMode(tmpAUTH)) + wifiConnect = WiFi::attemptConnect(tmpSSID, tmpUNAME, tmpPASS, tmpAUTH); + else wifiConnect = WiFi::attemptConnect(tmpSSID, tmpPASS, tmpAUTH); if (!wifiConnect) { notifyConnectionStatus(false); @@ -76,12 +76,12 @@ void bleSetupTask(void* arg) { if (err == ESP_OK) { esp_err_t saveErr = ESP_OK; saveErr |= nvs_set_str(WiFiHandle, ssidTag, tmpSSID.c_str()); - saveErr |= nvs_set_str(WiFiHandle, passTag, tmpPASS.c_str()); saveErr |= nvs_set_u8(WiFiHandle, authTag, (uint8_t)tmpAUTH); - if (tmpUNAME.length() > 0) { + if (tmpUNAME.length() > 0) saveErr |= nvs_set_str(WiFiHandle, unameTag, tmpUNAME.c_str()); - } + if (tmpPASS.length() > 0) + saveErr |= nvs_set_str(WiFiHandle, passTag, tmpPASS.c_str()); if (saveErr == ESP_OK) { nvs_commit(WiFiHandle); @@ -170,7 +170,7 @@ void setupLoop() { char pw[pwSize]; nvs_get_str(WiFiHandle, passTag, pw, &pwSize); nvs_close(WiFiHandle); - if (!bmWiFi.attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) { + if (!WiFi::attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) { // Make RGB LED certain color (Blue?) printf("Found credentials, failed to connect.\n"); initialSetup(); diff --git a/include/socketIO.cpp b/include/socketIO.cpp index d8d2ac0..0c79e22 100644 --- a/include/socketIO.cpp +++ b/include/socketIO.cpp @@ -91,7 +91,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, if (port != 1) printf("ERROR: NON-1 PORT RECEIVED\n"); // Report back actual calibration status from device else { - bool deviceCalibrated = calib.getCalibrated(); + bool deviceCalibrated = Calibration::getCalibrated(); emitCalibStatus(deviceCalibrated); printf(" Reported calibrated=%d for port %d\n", deviceCalibrated, port); runToAppPos(lastPos); @@ -104,7 +104,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_CONNECTED); } else { printf("Device authentication failed\n"); - calib.clearCalibrated(); + Calibration::clearCalibrated(); deleteWiFiAndTokenDetails(); connected = false; xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); @@ -121,7 +121,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, printf("Server message: %s\n", message->valuestring); } } - calib.clearCalibrated(); + Calibration::clearCalibrated(); deleteWiFiAndTokenDetails(); connected = false; xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); diff --git a/src/main.cpp b/src/main.cpp index b357bdf..4a17883 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,9 +19,6 @@ SemaphoreHandle_t g_calibration_mutex = NULL; Encoder* topEnc = new Encoder(ENCODER_PIN_A, ENCODER_PIN_B); Encoder* bottomEnc = new Encoder(InputEnc_PIN_A, InputEnc_PIN_B); -// Global calibration instance -Calibration calib; - void mainTask(void* arg) { EventBits_t bits; @@ -50,7 +47,7 @@ void mainTask(void* arg) { if (bits & EVENT_CLEAR_CALIB) { xSemaphoreTake(g_calibration_mutex, portMAX_DELAY); - calib.clearCalibrated(); + Calibration::clearCalibrated(); xSemaphoreGive(g_calibration_mutex); emitCalibStatus(false); } @@ -59,7 +56,7 @@ void mainTask(void* arg) { servoSavePos(); // Send position update to server - uint8_t currentAppPos = calib.convertToAppPos(topEnc->getCount()); + uint8_t currentAppPos = Calibration::convertToAppPos(topEnc->getCount()); emitPosHit(currentAppPos); printf("Sent pos_hit: position %d\n", currentAppPos); @@ -106,8 +103,8 @@ extern "C" void app_main() { } // Initialize hardware - bmWiFi.init(); - calib.init(); + WiFi::init(); + Calibration::init(); // Initialize encoders topEnc->init(); From c56e6ab5f3f481078d1bddb773f5ed25ee3fa30e Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sun, 4 Jan 2026 12:22:54 -0600 Subject: [PATCH 3/3] made creds non-static since BLETick functionality no longer in BLE file --- include/BLE.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/BLE.cpp b/include/BLE.cpp index 3b9c106..ca24e63 100644 --- a/include/BLE.cpp +++ b/include/BLE.cpp @@ -13,10 +13,10 @@ std::atomic finalAuth{false}; std::mutex dataMutex; wifi_auth_mode_t auth; -static std::string SSID = ""; -static std::string TOKEN = ""; -static std::string PASS = ""; -static std::string UNAME = ""; +std::string SSID = ""; +std::string TOKEN = ""; +std::string PASS = ""; +std::string UNAME = ""; // Global pointers to characteristics for notification support std::atomic ssidListChar = nullptr;