From 45fa356d668ca9313f278297f348842c0c980834 Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sun, 11 Jan 2026 19:02:14 -0600 Subject: [PATCH] Task-Driven, powersaving trial written, must test. --- include/BLE.cpp | 16 +++- include/WiFi.cpp | 91 ++++++++++++++++---- include/WiFi.hpp | 3 +- include/bmEvents.cpp | 44 ---------- include/bmEvents.hpp | 25 ------ include/bmHTTP.cpp | 53 ++++++++++++ include/bmHTTP.hpp | 1 + include/calibration.cpp | 52 ++++++++++++ include/calibration.hpp | 5 ++ include/mainEventLoop.cpp | 172 ++++++++++++++++++++++++++++++++++++++ include/mainEventLoop.hpp | 21 +++++ include/servo.cpp | 17 ++-- include/servo.hpp | 2 - include/setup.cpp | 130 +++++++++++++++++++++------- include/setup.hpp | 10 ++- include/socketIO.cpp | 89 +++++++------------- include/socketIO.hpp | 3 - src/main.cpp | 14 ++-- 18 files changed, 550 insertions(+), 198 deletions(-) delete mode 100644 include/bmEvents.cpp delete mode 100644 include/bmEvents.hpp create mode 100644 include/mainEventLoop.cpp create mode 100644 include/mainEventLoop.hpp diff --git a/include/BLE.cpp b/include/BLE.cpp index 7680222..5a2908f 100644 --- a/include/BLE.cpp +++ b/include/BLE.cpp @@ -1,3 +1,6 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" #include "BLE.hpp" #include "NimBLEDevice.h" #include "WiFi.hpp" @@ -6,7 +9,6 @@ #include "defines.h" #include #include "bmHTTP.hpp" -#include #include "setup.hpp" #include "esp_mac.h" @@ -26,6 +28,10 @@ static std::string UNAME = ""; // Global pointers to characteristics for notification support std::atomic ssidListChar = nullptr; std::atomic connectConfirmChar = nullptr; + +// Forward declarations +bool attemptUseWiFiCreds(); +bool tokenCheck(); std::atomic authConfirmChar = nullptr; std::atomic credsChar = nullptr; std::atomic tokenChar = nullptr; @@ -382,7 +388,7 @@ bool attemptUseWiFiCreds() { if (!wifiConnect) { // notify errored notifyConnectionStatus(false); - return; + return false; } nvs_handle_t WiFiHandle; @@ -391,7 +397,7 @@ bool attemptUseWiFiCreds() { printf("ERROR Saving Credentials\n"); // notify errored notifyConnectionStatus(false); - return; + return false; } else { err = nvs_set_str(WiFiHandle, ssidTag, tmpSSID.c_str()); @@ -404,9 +410,11 @@ bool attemptUseWiFiCreds() { if (err == ESP_OK) { // notify connected notifyConnectionStatus(true); + return true; } else { - // notify connected + // notify errored notifyConnectionStatus(false); + return false; } } \ No newline at end of file diff --git a/include/WiFi.cpp b/include/WiFi.cpp index 3cd133a..f7e186a 100644 --- a/include/WiFi.cpp +++ b/include/WiFi.cpp @@ -2,8 +2,9 @@ #include "esp_eap_client.h" #include "cJSON.h" // Native replacement for ArduinoJson #include "BLE.hpp" +#include "esp_wifi_he.h" -std::atomic WiFi::authFailed{false}; +TaskHandle_t WiFi::awaitConnectHandle = NULL; EventGroupHandle_t WiFi::s_wifi_event_group = NULL; esp_netif_t* WiFi::netif = NULL; esp_event_handler_instance_t WiFi::instance_any_id = NULL; @@ -36,12 +37,16 @@ void WiFi::event_handler(void* arg, esp_event_base_t event_base, case WIFI_REASON_AUTH_FAIL: // Reason 202 case WIFI_REASON_HANDSHAKE_TIMEOUT: // Reason 204 printf("ERROR: Likely Wrong Password!\n"); - authFailed = true; + if (awaitConnectHandle != NULL) { + xTaskNotify(awaitConnectHandle, false, eSetValueWithOverwrite); + } break; case WIFI_REASON_NO_AP_FOUND: // Reason 201 printf("ERROR: SSID Not Found\n"); - authFailed = true; + if (awaitConnectHandle != NULL) { + xTaskNotify(awaitConnectHandle, false, eSetValueWithOverwrite); + } break; case WIFI_REASON_ASSOC_LEAVE: // Reason 8 - Manual disconnect @@ -51,11 +56,13 @@ void WiFi::event_handler(void* arg, esp_event_base_t event_base, case WIFI_REASON_ASSOC_FAIL: // Reason 203 (Can be AP busy/rate limiting) printf("Association failed, will retry...\n"); vTaskDelay(pdMS_TO_TICKS(1000)); // Wait 1 second before retry to avoid rate limiting + esp_netif_dhcpc_start(netif); esp_wifi_connect(); break; default: printf("Retrying...\n"); + esp_netif_dhcpc_start(netif); esp_wifi_connect(); break; } @@ -72,7 +79,11 @@ void WiFi::event_handler(void* arg, esp_event_base_t event_base, 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)); + esp_netif_dhcpc_stop(netif); xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + if (awaitConnectHandle != NULL) { + xTaskNotify(awaitConnectHandle, true, eSetValueWithOverwrite); + } } } @@ -108,6 +119,7 @@ void WiFi::init() { ESP_ERROR_CHECK(esp_wifi_start()); xEventGroupWaitBits(s_wifi_event_group, WIFI_STARTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY); + awaitConnectHandle = NULL; } // --- CHECK STATUS --- @@ -170,19 +182,46 @@ bool WiFi::attemptConnect(const std::string ssid, const std::string uname, } bool WiFi::awaitConnected() { - authFailed = false; - if (esp_wifi_connect() != ESP_OK) return false; + awaitConnectHandle = xTaskGetCurrentTaskHandle(); + if (esp_wifi_connect() != ESP_OK) { + awaitConnectHandle = NULL; + return false; + } - uint8_t attempts = 0; - while (!isConnected() && attempts < 20) { - if (authFailed) { + uint32_t status; + uint8_t MAX_TIMEOUT = 10; //seconds + if (xTaskNotifyWait(0, ULONG_MAX, &status, pdMS_TO_TICKS(MAX_TIMEOUT * 1000)) == pdTRUE) { + awaitConnectHandle = NULL; + if (!status) { printf("SSID/Password was wrong! Aborting connection attempt.\n"); return false; } - vTaskDelay(500); - attempts++; + } else { + // Timeout - check if connected anyway + awaitConnectHandle = NULL; + } + + if (isConnected()) { + esp_wifi_set_ps(WIFI_PS_MIN_MODEM); + uint8_t protocol_bitmap = 0; + esp_err_t err = esp_wifi_get_protocol(WIFI_IF_STA, &protocol_bitmap); + + if (err == ESP_OK && (protocol_bitmap & WIFI_PROTOCOL_11AX)) { + // WiFi 6 (802.11ax) - Use Target Wake Time (TWT) for power saving + wifi_twt_setup_config_t twt_config = { + .setup_cmd = TWT_REQUEST, + .trigger = true, + .flow_type = 0, // Announced + .flow_id = 0, + .wake_invl_expn = 12, // Exponent for interval + .min_wake_dura = 255, // ~65ms (unit is 256 microseconds) + .wake_invl_mant = 14648, // Mantissa (mant * 2^exp = 60,000,000 us = 60s) + .timeout_time_ms = 5000, + }; + esp_wifi_sta_itwt_setup(&twt_config); + } + return true; } - if (isConnected()) return true; return false; } @@ -190,8 +229,6 @@ bool WiFi::awaitConnected() { void WiFi::scanAndUpdateSSIDList() { printf("Starting WiFi Scan...\n"); - // 1. Start Scan (Blocking Mode = true) - // In blocking mode, this function waits here until scan is done (~2 seconds) esp_wifi_sta_enterprise_disable(); esp_wifi_disconnect(); @@ -222,7 +259,13 @@ void WiFi::processScanResults() { printf("Heap allocation error in processScanResults\n"); return; } - ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_list)); + + esp_err_t err = esp_wifi_scan_get_ap_records(&ap_count, ap_list); + if (err != ESP_OK) { + printf("Failed to get scan records\n"); + free(ap_list); + return; + } // 3. Build JSON using cJSON cJSON *root = cJSON_CreateArray(); @@ -254,4 +297,24 @@ void WiFi::processScanResults() { free(ap_list); cJSON_Delete(root); // This deletes all children (items) too free(json_string); // cJSON_Print allocates memory, you must free it +} + +bool WiFi::attemptDHCPrenewal() { + xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + esp_netif_dhcpc_start(netif); + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, + WIFI_CONNECTED_BIT, + pdFALSE, + pdFALSE, + pdMS_TO_TICKS(4000)); + + if (bits & WIFI_CONNECTED_BIT) { + printf("renewal success"); + // Stop the client again to save power. + esp_netif_dhcpc_stop(netif); + return true; + } + printf("DHCP Renewal failed. Reconnecting Wi-Fi...\n"); + esp_wifi_disconnect(); + return awaitConnected(); } \ No newline at end of file diff --git a/include/WiFi.hpp b/include/WiFi.hpp index 335b3ed..4d76780 100644 --- a/include/WiFi.hpp +++ b/include/WiFi.hpp @@ -14,9 +14,10 @@ class WiFi { const std::string password, const wifi_auth_mode_t authMode); static bool isConnected(); static void scanAndUpdateSSIDList(); + static bool attemptDHCPrenewal(); private: + static TaskHandle_t awaitConnectHandle; static void processScanResults(); - static std::atomic authFailed; static bool awaitConnected(); static esp_event_handler_instance_t instance_any_id; static esp_event_handler_instance_t instance_got_ip; diff --git a/include/bmEvents.cpp b/include/bmEvents.cpp deleted file mode 100644 index 1269004..0000000 --- a/include/bmEvents.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "bmEvents.hpp" -#include -#include - -// blueprint for sending events... maybe I never use this. We'll see. -// bool send_app_event(app_event_t *event, QueueHandle_t event_queue) { -// if (event_queue == NULL) return false; - -// // A. Are we in an Interrupt Service Routine (Hardware context)? -// if (xPortInIsrContext()) { -// BaseType_t xHigherPriorityTaskWoken = pdFALSE; - -// // Use the "FromISR" version -// BaseType_t result = xQueueSendFromISR(event_queue, event, &xHigherPriorityTaskWoken); - -// // This is crucial for "Instant Wakeup" -// if (result == pdPASS) portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -// return (result == pdPASS); -// } - -// // B. Are we in a Standard Task (WiFi/BLE Callback context)? -// else { -// // Use the standard version -// // 10 ticks wait time is arbitrary; allows a small buffer if queue is full -// return (xQueueSend(event_queue, event, pdMS_TO_TICKS(10)) == pdPASS); -// } -// } - - -// blueprint for deleting more complex event queues -// void deinit_BLE_event_queue(QueueHandle_t& event_queue) { -// if (event_queue != NULL) { -// app_event_t temp_event; -// while (xQueueReceive(event_queue, &temp_event, 0) == pdTRUE) { -// if (temp_event.data != NULL) free(temp_event.data); -// } - -// vQueueDelete(event_queue); - -// event_queue = NULL; - -// printf("Event queue deleted.\n"); -// } -// } \ No newline at end of file diff --git a/include/bmEvents.hpp b/include/bmEvents.hpp deleted file mode 100644 index 9a3ae9a..0000000 --- a/include/bmEvents.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef BM_EVENTS_H -#define BM_EVENTS_H -#include - -// bool send_app_event(app_event_t *event); - -// void deinit_event_queue(QueueHandle_t& event_queue); - -// // 1. Event Types: The "What" -// typedef enum { -// EVENT_BUTTON_PRESSED, -// EVENT_WIFI_CONNECTED, -// EVENT_WIFI_DISCONNECTED, -// EVENT_BLE_DATA_RECEIVED, -// EVENT_TIMER_TICK -// } event_type_t; - -// // 2. The Message Structure: The "Payload" -// typedef struct { -// event_type_t type; -// void *data; // Optional: Pointer to data (buffer, string, etc.) -// int data_len; // Optional: Length of data -// } app_event_t; - -#endif \ No newline at end of file diff --git a/include/bmHTTP.cpp b/include/bmHTTP.cpp index 84d47b4..2533f35 100644 --- a/include/bmHTTP.cpp +++ b/include/bmHTTP.cpp @@ -63,6 +63,59 @@ bool httpGET(std::string endpoint, std::string token, cJSON* &JSONresponse) { return success; } +bool httpPOST(std::string endpoint, std::string token, cJSON* postData, cJSON* &JSONresponse) { + std::string url = urlBase + endpoint; + std::string responseBuffer = ""; + + // Convert JSON object to string + char* postString = cJSON_PrintUnformatted(postData); + if (postString == NULL) { + printf("Failed to serialize JSON for POST\n"); + return false; + } + + esp_http_client_config_t config = {}; + config.url = url.c_str(); + config.method = HTTP_METHOD_POST; + config.event_handler = _http_event_handler; + config.user_data = &responseBuffer; + if (secureSrv) { + config.transport_type = HTTP_TRANSPORT_OVER_SSL; + config.crt_bundle_attach = esp_crt_bundle_attach; + } + + esp_http_client_handle_t client = esp_http_client_init(&config); + + // Set headers + std::string authHeader = "Bearer " + token; + esp_http_client_set_header(client, "Authorization", authHeader.c_str()); + esp_http_client_set_header(client, "Content-Type", "application/json"); + + // Set POST data + esp_http_client_set_post_field(client, postString, strlen(postString)); + + // Perform request + esp_err_t err = esp_http_client_perform(client); + bool success = false; + + if (err == ESP_OK) { + int status_code = esp_http_client_get_status_code(client); + printf("Status = %d, Content Length = %d\n", status_code, esp_http_client_get_content_length(client)); + + if (status_code == 200 || status_code == 201) { + printf("Response: %s\n", responseBuffer.c_str()); + JSONresponse = cJSON_Parse(responseBuffer.c_str()); + if (JSONresponse) success = true; + } + } else { + printf("HTTP POST failed: %s\n", esp_err_to_name(err)); + } + + free(postString); + esp_http_client_cleanup(client); + return success; +} + void deleteWiFiAndTokenDetails() { nvs_handle_t wifiHandle; if (nvs_open(nvsWiFi, NVS_READWRITE, &wifiHandle) == ESP_OK) { diff --git a/include/bmHTTP.hpp b/include/bmHTTP.hpp index 332344c..fb45a5d 100644 --- a/include/bmHTTP.hpp +++ b/include/bmHTTP.hpp @@ -6,6 +6,7 @@ extern std::string webToken; bool httpGET(std::string endpoint, std::string token, cJSON* &JSONresponse); +bool httpPOST(std::string endpoint, std::string token, cJSON* postData, cJSON* &JSONresponse); void deleteWiFiAndTokenDetails(); diff --git a/include/calibration.cpp b/include/calibration.cpp index e472015..d9b2586 100644 --- a/include/calibration.cpp +++ b/include/calibration.cpp @@ -1,6 +1,16 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "calibration.hpp" #include "defines.h" #include "nvs_flash.h" +#include "socketIO.hpp" +#include + +// Static member definitions +std::atomic Calibration::calibrated{false}; +std::atomic Calibration::UpTicks{0}; +std::atomic Calibration::DownTicks{0}; +TaskHandle_t calibTaskHandle = NULL; void Calibration::init() { nvs_handle_t calibHandle; @@ -109,4 +119,46 @@ uint8_t Calibration::convertToAppPos(int32_t ticks) { // appPos between 0 and 10, convert to target encoder ticks. int8_t retVal = (ticks - DownTicks) * 10 / (UpTicks - DownTicks); return (retVal < 0) ? 0 : ((retVal > 10) ? 10 : retVal); +} + +bool calibrate() { + calibTaskHandle = xTaskGetCurrentTaskHandle(); + printf("Connecting to Socket.IO server for calibration...\n"); + initSocketIO(); + + // Wait for device_init message from server with timeout + int timeout_count = 0; + const int MAX_TIMEOUT = 60; // seconds + + uint32_t status; + // Wait for notification with timeout + if (xTaskNotifyWait(0, ULONG_MAX, &status, pdMS_TO_TICKS(MAX_TIMEOUT * 1000)) == pdTRUE) { + // Notification received within timeout + if (status) { + printf("Connected successfully, awaiting destroy command\n"); + xTaskNotifyWait(0, ULONG_MAX, &status, portMAX_DELAY); + calibTaskHandle = NULL; + if (status == 2) { // calibration complete + printf("Calibration process complete\n"); + stopSocketIO(); + return true; + } + else { // unexpected disconnect + printf("Disconnected unexpectedly!\n"); + stopSocketIO(); + return false; + } + } else { + calibTaskHandle = NULL; + printf("Connection failed! Returning to setup.\n"); + stopSocketIO(); + return false; + } + } else { + // Timeout reached + calibTaskHandle = NULL; + printf("Timeout waiting for device_init - connection failed\n"); + stopSocketIO(); + return false; + } } \ No newline at end of file diff --git a/include/calibration.hpp b/include/calibration.hpp index ff3b0dc..4558e59 100644 --- a/include/calibration.hpp +++ b/include/calibration.hpp @@ -1,5 +1,7 @@ #ifndef CALIBRATION_H #define CALIBRATION_H +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include #include "encoder.hpp" @@ -19,4 +21,7 @@ class Calibration { static std::atomic calibrated; }; +extern TaskHandle_t calibTaskHandle; +bool calibrate(); + #endif \ No newline at end of file diff --git a/include/mainEventLoop.cpp b/include/mainEventLoop.cpp new file mode 100644 index 0000000..ecbc808 --- /dev/null +++ b/include/mainEventLoop.cpp @@ -0,0 +1,172 @@ +#include "mainEventLoop.hpp" +#include "calibration.hpp" +#include "setup.hpp" +#include "servo.hpp" +#include "bmHTTP.hpp" +#include "cJSON.h" +#include "encoder.hpp" +#include "WiFi.hpp" + +TaskHandle_t wakeTaskHandle = NULL; + +void wakeTimer(void* pvParameters) { + while (1) { + vTaskDelay(pdMS_TO_TICKS(60000)); + main_event_type_t evt = EVENT_REQUEST_POS; + xQueueSend(main_event_queue, &evt, portMAX_DELAY); + } +} + +bool postServoPos(uint8_t currentAppPos) { + // Create POST data + cJSON* posData = cJSON_CreateObject(); + cJSON_AddNumberToObject(posData, "port", 1); + cJSON_AddNumberToObject(posData, "pos", currentAppPos); + + // Send position update + cJSON* response = nullptr; + bool success = httpPOST("position", webToken, posData, response); + cJSON_Delete(posData); + + if (success && response != nullptr) { + // Parse await_calib from response + cJSON* awaitCalibItem = cJSON_GetObjectItem(response, "await_calib"); + bool awaitCalib = false; + if (cJSON_IsBool(awaitCalibItem)) { + awaitCalib = awaitCalibItem->valueint != 0; + } + + printf("Position update sent: %d, await_calib=%d\n", currentAppPos, awaitCalib); + cJSON_Delete(response); + + if (awaitCalib) { + Calibration::clearCalibrated(); + if (!calibrate()) { + if (!WiFi::attemptDHCPrenewal()) + setupAndCalibrate(); + else { + if (!calibrate()) { + printf("ERROR OCCURED: EVEN AFTER SETUP, SOCKET OPENING FAIL\n"); + setupAndCalibrate(); + } + } + } + } + } + return success; +} + +bool getServoPos() { + cJSON* response = nullptr; + bool success = httpGET("position", webToken, response); + + if (success && response != NULL) { + // Check if response is an array + if (cJSON_IsArray(response)) { + int arraySize = cJSON_GetArraySize(response); + + // Condition 1: More than one object in array + if (arraySize > 1) { + printf("Multiple peripherals detected, entering setup.\n"); + cJSON_Delete(response); + return false; + } + // Condition 2: Check peripheral_number in first object + else if (arraySize > 0) { + cJSON *firstObject = cJSON_GetArrayItem(response, 0); + if (firstObject != NULL) { + cJSON *peripheralNum = cJSON_GetObjectItem(firstObject, "peripheral_number"); + if (cJSON_IsNumber(peripheralNum) && peripheralNum->valueint != 1) { + printf("Peripheral number is not 1, entering setup.\n"); + cJSON_Delete(response); + return false; + } + printf("Verified new token!\n"); + + cJSON *awaitCalib = cJSON_GetObjectItem(firstObject, "await_calib"); + if (cJSON_IsBool(awaitCalib)) { + if (awaitCalib->valueint) { + Calibration::clearCalibrated(); + if (!calibrate()) { + if (!WiFi::attemptDHCPrenewal()) + setupAndCalibrate(); + else { + if (!calibrate()) { + printf("ERROR OCCURED: EVEN AFTER SETUP, SOCKET OPENING FAIL\n"); + setupAndCalibrate(); + } + } + } + } + else { + cJSON* pos = cJSON_GetObjectItem(firstObject, "last_pos"); + runToAppPos(pos->valueint); + } + } + cJSON_Delete(response); + } + } + } + } + return success; +} + +QueueHandle_t main_event_queue = NULL; + +void mainEventLoop() { + main_event_queue = xQueueCreate(10, sizeof(main_event_type_t)); + main_event_type_t received_event_type; + + while (true) { + if (xQueueReceive(main_event_queue, &received_event_type, portMAX_DELAY)) { + if (received_event_type == EVENT_CLEAR_CALIB) { + Calibration::clearCalibrated(); + if (!calibrate()) { + if (!WiFi::attemptDHCPrenewal()) + setupAndCalibrate(); + else { + if (!calibrate()) { + printf("ERROR OCCURED: EVEN AFTER SETUP, SOCKET OPENING FAIL\n"); + setupAndCalibrate(); + } + } + } + } + else if (received_event_type == EVENT_SAVE_POS) { + servoSavePos(); + + uint8_t currentAppPos = Calibration::convertToAppPos(topEnc->getCount()); + + if (!postServoPos(currentAppPos)) { + printf("Failed to send position update\n"); + if (!WiFi::attemptDHCPrenewal()) { + setupAndCalibrate(); + postServoPos(currentAppPos); + } + else { + if (!postServoPos(currentAppPos)) { + printf("renewed dhcp successfully, but still failed to post\n"); + setupAndCalibrate(); + } + } + } + } + else if (received_event_type == EVENT_REQUEST_POS) { + if (!getServoPos()) { + printf("Failed to send position update\n"); + if (!WiFi::attemptDHCPrenewal()) { + setupAndCalibrate(); + getServoPos(); + } + else { + if (!getServoPos()) { + printf("renewed dhcp successfully, but still failed to post\n"); + setupAndCalibrate(); + } + } + } + } + } + } + vTaskDelete(NULL); +} \ No newline at end of file diff --git a/include/mainEventLoop.hpp b/include/mainEventLoop.hpp new file mode 100644 index 0000000..a9696bb --- /dev/null +++ b/include/mainEventLoop.hpp @@ -0,0 +1,21 @@ +#ifndef BM_EVENTS_H +#define BM_EVENTS_H +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +// Event Types +typedef enum { + EVENT_CLEAR_CALIB, + EVENT_SAVE_POS, + EVENT_REQUEST_POS +} main_event_type_t; + +void mainEventLoop(); + +extern QueueHandle_t main_event_queue; + +void wakeTimer(void* pvParameters); + +extern TaskHandle_t wakeTaskHandle; + +#endif \ No newline at end of file diff --git a/include/servo.cpp b/include/servo.cpp index 49d4916..4f37fe0 100644 --- a/include/servo.cpp +++ b/include/servo.cpp @@ -1,10 +1,12 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "servo.hpp" #include "driver/ledc.h" #include "defines.h" -#include #include "esp_log.h" #include "socketIO.hpp" #include "nvs_flash.h" +#include "mainEventLoop.hpp" std::atomic calibListen{false}; std::atomic baseDiff{0}; @@ -12,8 +14,6 @@ std::atomic target{0}; std::atomic runningManual{false}; std::atomic runningServer{false}; -std::atomic clearCalibFlag{false}; -std::atomic savePosFlag{false}; std::atomic startLess{false}; void servoInit() { @@ -142,8 +142,10 @@ void initMainLoop() { void IRAM_ATTR watchdogCallback(void* arg) { if (runningManual || runningServer) { - // if we're trying to move and our timer ran out, we need to recalibrate - clearCalibFlag = true; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + main_event_type_t evt = EVENT_CLEAR_CALIB; + BaseType_t result = xQueueSendFromISR(main_event_queue, &evt, &xHigherPriorityTaskWoken); + if (result == pdPASS) portYIELD_FROM_ISR(xHigherPriorityTaskWoken); topEnc->pauseWatchdog(); // get ready for recalibration by clearing all these listeners @@ -155,7 +157,10 @@ void IRAM_ATTR watchdogCallback(void* arg) { else { // if no movement is running, we're fine // save current servo-encoder position for reinitialization - savePosFlag = true; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + main_event_type_t evt = EVENT_SAVE_POS; + BaseType_t result = xQueueSendFromISR(main_event_queue, &evt, &xHigherPriorityTaskWoken); + if (result == pdPASS) portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } // clear running flags runningManual = false; diff --git a/include/servo.hpp b/include/servo.hpp index 54b81b2..7143904 100644 --- a/include/servo.hpp +++ b/include/servo.hpp @@ -10,8 +10,6 @@ #define manual 0 extern std::atomic calibListen; -extern std::atomic clearCalibFlag; -extern std::atomic savePosFlag; extern Encoder* topEnc; extern Encoder* bottomEnc; diff --git a/include/setup.cpp b/include/setup.cpp index bd7ad4f..7d7ad0b 100644 --- a/include/setup.cpp +++ b/include/setup.cpp @@ -1,3 +1,4 @@ +#include "freertos/FreeRTOS.h" #include "setup.hpp" #include "BLE.hpp" #include "WiFi.hpp" @@ -5,17 +6,31 @@ #include "defines.h" #include "bmHTTP.hpp" #include "socketIO.hpp" +#include "calibration.hpp" TaskHandle_t setupTaskHandle = NULL; +std::atomic awaitCalibration{false}; + void initialSetup() { printf("Entered Setup\n"); - NimBLEAdvertising* pAdv = initBLE(); + initBLE(); ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + setupTaskHandle = NULL; } -void setupLoop(void *pvParameters) { - TaskHandle_t parent_handle = (TaskHandle_t)pvParameters; +void setupAndCalibrate() { + while (1) { + setupLoop(); + if (awaitCalibration) { + if (calibrate()) break; + } + else break; + } +} + +void setupLoop() { + setupTaskHandle = xTaskGetCurrentTaskHandle(); bool initSuccess = false; while(!initSuccess) { nvs_handle_t WiFiHandle; @@ -31,7 +46,9 @@ void setupLoop(void *pvParameters) { // Make the RGB LED a certain color (Blue?) nvs_close(WiFiHandle); initialSetup(); - } else if (WiFiPrefsError == ESP_OK) { + continue; + } + else if (WiFiPrefsError == ESP_OK) { char ssid[ssidSize]; nvs_get_str(WiFiHandle, ssidTag, ssid, &ssidSize); char pw[pwSize]; @@ -41,6 +58,7 @@ void setupLoop(void *pvParameters) { // Make RGB LED certain color (Blue?) printf("Found credentials, failed to connect.\n"); initialSetup(); + continue; } else { printf("Connected to WiFi from NVS credentials\n"); @@ -55,31 +73,86 @@ void setupLoop(void *pvParameters) { // Use permanent device token to connect to Socket.IO // 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++; - } - - if (timeout_count >= MAX_TIMEOUT) { - printf("Timeout waiting for device_init - connection failed\n"); - stopSocketIO(); - initSuccess = false; + cJSON* response = nullptr; + initSuccess = httpGET("position", webToken, response); + if (!initSuccess) { initialSetup(); - } else { - initSuccess = connected; - if (!initSuccess) { - printf("Device authentication failed - entering setup\n"); - initialSetup(); - } + continue; } + initSuccess = false; + + if (response != NULL) { + // Check if response is an array + if (cJSON_IsArray(response)) { + int arraySize = cJSON_GetArraySize(response); + + // Condition 1: More than one object in array + if (arraySize > 1) { + printf("Multiple peripherals detected, entering setup.\n"); + cJSON_Delete(response); + initialSetup(); + continue; + } + // Condition 2: Check peripheral_number in first object + else if (arraySize > 0) { + cJSON *firstObject = cJSON_GetArrayItem(response, 0); + if (firstObject != NULL) { + cJSON *peripheralNum = cJSON_GetObjectItem(firstObject, "peripheral_number"); + if (cJSON_IsNumber(peripheralNum) && peripheralNum->valueint != 1) { + printf("Peripheral number is not 1, entering setup.\n"); + cJSON_Delete(response); + initialSetup(); + continue; + } + // Valid single peripheral with number 1, continue with normal flow + initSuccess = true; + printf("Verified new token!\n"); + + cJSON *awaitCalib = cJSON_GetObjectItem(firstObject, "await_calib"); + if (cJSON_IsBool(awaitCalib)) awaitCalibration = awaitCalib->valueint; + cJSON_Delete(response); + if (!awaitCalibration) { + // Create calibration status object + cJSON* calibPostObj = cJSON_CreateObject(); + cJSON_AddNumberToObject(calibPostObj, "port", 1); + cJSON_AddBoolToObject(calibPostObj, "calibrated", Calibration::getCalibrated()); + + // Send calibration status to server + cJSON* calibResponse = nullptr; + bool calibSuccess = httpPOST("report_calib_status", webToken, calibPostObj, calibResponse); + + if (calibSuccess && calibResponse != NULL) { + printf("Calibration status reported successfully\n"); + cJSON_Delete(calibResponse); + } else { + printf("Failed to report calibration status\n"); + } + cJSON_Delete(calibPostObj); + if (!Calibration::getCalibrated()) awaitCalibration = true; + } + } + else { + printf("null object\n"); + cJSON_Delete(response); + initialSetup(); + continue; + } + } + else { + printf("no items in array\n"); + cJSON_Delete(response); + initialSetup(); + continue; + } + } + else { + printf("Response not array\n"); + cJSON_Delete(response); + initialSetup(); + continue; + } + } + else printf("Failed to parse JSON response\n"); } else { printf("Token read unsuccessful, entering setup.\n"); @@ -103,6 +176,5 @@ void setupLoop(void *pvParameters) { initialSetup(); } } - xTaskNotifyGive(parent_handle); - vTaskDelete(NULL); + setupTaskHandle = NULL; // Clear handle on function exit (safety) } \ No newline at end of file diff --git a/include/setup.hpp b/include/setup.hpp index 9c81b57..786c420 100644 --- a/include/setup.hpp +++ b/include/setup.hpp @@ -1,10 +1,16 @@ #ifndef SETUP_H #define SETUP_H +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include + extern TaskHandle_t setupTaskHandle; -extern SemaphoreHandle_t Setup_Complete_Semaphore; +extern std::atomic awaitCalibration; void initialSetup(); -void setupLoop(void *pvParameters); +void setupLoop(); + +void setupAndCalibrate(); #endif \ No newline at end of file diff --git a/include/socketIO.cpp b/include/socketIO.cpp index 7829339..feda7f3 100644 --- a/include/socketIO.cpp +++ b/include/socketIO.cpp @@ -11,9 +11,7 @@ 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}; +static bool stopSocketFlag = false; // Event handler for Socket.IO events static void socketio_event_handler(void *handler_args, esp_event_base_t base, @@ -65,8 +63,8 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, } // Mark connection as failed - connected = false; - statusResolved = true; + if (calibTaskHandle != NULL) + xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite); } // Handle device_init event else if (strcmp(eventName->valuestring, "device_init") == 0) { @@ -91,25 +89,18 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, // TODO: UPDATE MOTOR/ENCODER STATES BASED ON THIS, as well as the successive websocket updates. printf(" Port %d: pos=%d\n", port, lastPos); if (port != 1) printf("ERROR: NON-1 PORT RECEIVED\n"); - // Report back actual calibration status from device - else { - bool deviceCalibrated = Calibration::getCalibrated(); - emitCalibStatus(deviceCalibrated); - printf(" Reported calibrated=%d for port %d\n", deviceCalibrated, port); - runToAppPos(lastPos); - } } } // Now mark as connected - connected = true; - statusResolved = true; + if (calibTaskHandle != NULL) + xTaskNotify(calibTaskHandle, true, eSetValueWithOverwrite); } else { printf("Device authentication failed\n"); Calibration::clearCalibrated(); deleteWiFiAndTokenDetails(); - connected = false; - statusResolved = true; + if (calibTaskHandle != NULL) + xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite); } } } @@ -125,8 +116,8 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, } Calibration::clearCalibrated(); deleteWiFiAndTokenDetails(); - connected = false; - statusResolved = true; + if (calibTaskHandle != NULL) + xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite); } // Handle calib_start event @@ -188,13 +179,21 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, } else { if (!servoCompleteCalib()) emitCalibError("Completion failed"); - else emitCalibDone(); + else { + emitCalibDone(); + } } } } } + + // Handle calib_done_ack event + else if (strcmp(eventName->valuestring, "calib_done_ack") == 0) { + printf("Server acknowledged calibration completion - safe to disconnect\n"); + stopSocketFlag = true; + } - // Handle user_stage1_complete event + // Handle cancel_calib event else if (strcmp(eventName->valuestring, "cancel_calib") == 0) { printf("Canceling calibration process...\n"); cJSON *data = cJSON_GetArrayItem(json, 1); @@ -211,37 +210,6 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, } } } - - // Handle server position change (manual or scheduled) - else if (strcmp(eventName->valuestring, "posUpdates") == 0) { - printf("Received position update from server\n"); - cJSON *updateList = cJSON_GetArrayItem(json, 1); - - if (cJSON_IsArray(updateList)) { - int updateCount = cJSON_GetArraySize(updateList); - printf("Processing %d position update(s)\n", updateCount); - - for (int i = 0; i < updateCount; i++) { - cJSON *update = cJSON_GetArrayItem(updateList, i); - cJSON *periphNum = cJSON_GetObjectItem(update, "periphNum"); - cJSON *pos = cJSON_GetObjectItem(update, "pos"); - - if (periphNum && cJSON_IsNumber(periphNum) && - pos && cJSON_IsNumber(pos)) { - int port = periphNum->valueint; - int position = pos->valueint; - - if (port != 1) - printf("ERROR: Received position update for non-1 port: %d\n", port); - else { - printf("Position update: position %d\n", position); - runToAppPos(position); - } - } - else printf("Invalid position update format\n"); - } - } - } } } @@ -293,8 +261,8 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, } } - connected = false; - statusResolved = true; + if (calibTaskHandle != NULL) + xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite); break; } } @@ -302,18 +270,21 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base, // Handle WebSocket-level disconnections if (data->websocket_event_id == WEBSOCKET_EVENT_DISCONNECTED) { printf("WebSocket disconnected\n"); - connected = false; - statusResolved = true; + if (calibTaskHandle != NULL) + xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite); + } + if (stopSocketFlag) { + if (calibTaskHandle != NULL) + xTaskNotify(calibTaskHandle, 2, eSetValueWithOverwrite); + stopSocketFlag = false; // Clear flag after notifying once } } const std::string uriString = std::string("ws") + (secureSrv ? "s" : "") + "://" + srvAddr + "/socket.io/?EIO=4&transport=websocket"; void initSocketIO() { + stopSocketFlag = false; // Reset flag for new connection // Prepare the Authorization Header (Bearer format) std::string authHeader = "Authorization: Bearer " + webToken + "\r\n"; - - statusResolved = false; - connected = false; esp_socketio_client_config_t config = {}; config.websocket_config.uri = uriString.c_str(); @@ -338,8 +309,6 @@ void stopSocketIO() { esp_socketio_client_destroy(io_client); io_client = NULL; tx_packet = NULL; - connected = false; - statusResolved = false; } } diff --git a/include/socketIO.hpp b/include/socketIO.hpp index 9144a4c..86c9d70 100644 --- a/include/socketIO.hpp +++ b/include/socketIO.hpp @@ -2,9 +2,6 @@ #define SOCKETIO_HPP #include -extern std::atomic statusResolved; -extern std::atomic connected; - // Initialize Socket.IO client and connect to server void initSocketIO(); diff --git a/src/main.cpp b/src/main.cpp index 4034111..a71cb32 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,17 +9,12 @@ #include "encoder.hpp" #include "calibration.hpp" #include "esp_pm.h" +#include "mainEventLoop.hpp" // 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) - -void MainTask(void *pvParameters) { - -} - 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 @@ -37,8 +32,11 @@ void mainApp() { bottomEnc->init(); servoInit(); - xTaskCreate(setupLoop, "Setup", 8192, xTaskGetCurrentTaskHandle(), 5, &setupTaskHandle); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + setupAndCalibrate(); + + xTaskCreate(wakeTimer, "wakeTimer", 1024, NULL, 5, &wakeTaskHandle); + + mainEventLoop(); // TOMORROW!!! // statusResolved = false;