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