revert to non-task-driven, no tangible difference in performance. May change back later.

This commit is contained in:
2026-01-08 18:14:36 -06:00
parent e437c32c28
commit 03ca64080c
15 changed files with 240 additions and 435 deletions

View File

@@ -7,16 +7,19 @@
#include <mutex> #include <mutex>
#include "bmHTTP.hpp" #include "bmHTTP.hpp"
std::atomic<bool> flag_scan_requested{false};
std::atomic<bool> credsGiven{false};
std::atomic<bool> tokenGiven{false};
std::atomic<bool> isBLEClientConnected{false}; std::atomic<bool> isBLEClientConnected{false};
std::atomic<bool> scanBlock{false}; std::atomic<bool> scanBlock{false};
std::atomic<bool> finalAuth{false}; std::atomic<bool> finalAuth{false};
std::mutex dataMutex; std::mutex dataMutex;
wifi_auth_mode_t auth; wifi_auth_mode_t auth;
std::string SSID = ""; static std::string SSID = "";
std::string TOKEN = ""; static std::string TOKEN = "";
std::string PASS = ""; static std::string PASS = "";
std::string UNAME = ""; static std::string UNAME = "";
// Global pointers to characteristics for notification support // Global pointers to characteristics for notification support
std::atomic<NimBLECharacteristic*> ssidListChar = nullptr; std::atomic<NimBLECharacteristic*> ssidListChar = nullptr;
@@ -118,12 +121,125 @@ void notifyAuthStatus(bool success) {
tmpConfChar->setValue(""); // Clear value after notify tmpConfChar->setValue(""); // Clear value after notify
} }
// BLEtick() removed - replaced by bleSetupTask() in setup.cpp 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<std::mutex> 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<std::mutex> 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;
}
void reset() { void reset() {
esp_wifi_scan_stop(); esp_wifi_scan_stop();
if (!finalAuth) esp_wifi_disconnect(); if (!finalAuth) esp_wifi_disconnect();
scanBlock = false; scanBlock = false;
flag_scan_requested = false;
credsGiven = false;
tokenGiven = false;
} }
void MyServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { void MyServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
@@ -179,6 +295,7 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
else error = true; else error = true;
if (error) { if (error) {
printf("ERROR: Invalid Auth mode passed in with JSON.\n"); printf("ERROR: Invalid Auth mode passed in with JSON.\n");
credsGiven = false;
cJSON_Delete(root); cJSON_Delete(root);
return; return;
} }
@@ -197,13 +314,13 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
SSID = ssid->valuestring; SSID = ssid->valuestring;
PASS = passPresent ? password->valuestring : ""; PASS = passPresent ? password->valuestring : "";
UNAME = unamePresent ? uname->valuestring : ""; UNAME = unamePresent ? uname->valuestring : "";
// Signal via event group instead of flag credsGiven = tempCredsGiven; // update the global flag.
xEventGroupSetBits(g_system_events, EVENT_BLE_CREDS_RECEIVED);
} }
else printf("ERROR: Did not receive necessary credentials.\n"); else printf("ERROR: Did not receive necessary credentials.\n");
cJSON_Delete(root); cJSON_Delete(root);
} else { } else {
printf("Failed to parse JSON\n"); printf("Failed to parse JSON\n");
credsGiven = false;
} }
} }
} }
@@ -212,16 +329,14 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
printf("Received Token: %s\n", val.c_str()); printf("Received Token: %s\n", val.c_str());
std::lock_guard<std::mutex> lock(dataMutex); std::lock_guard<std::mutex> lock(dataMutex);
TOKEN = val; TOKEN = val;
// Signal via event group instead of flag tokenGiven = true;
xEventGroupSetBits(g_system_events, EVENT_BLE_TOKEN_RECEIVED);
} }
} }
else if (pChar == currentRefreshChar) { else if (pChar == currentRefreshChar) {
if (val == "Start") { if (val == "Start") {
// Refresh characteristic // Refresh characteristic
printf("Refresh Requested\n"); printf("Refresh Requested\n");
// Signal via event group instead of flag flag_scan_requested = true;
xEventGroupSetBits(g_system_events, EVENT_BLE_SCAN_REQUEST);
} }
else if (val == "Done") { else if (val == "Done") {
printf("Data read complete\n"); printf("Data read complete\n");

View File

@@ -23,6 +23,6 @@ class MyCharCallbacks : public NimBLECharacteristicCallbacks {
}; };
NimBLEAdvertising* initBLE(); NimBLEAdvertising* initBLE();
// BLEtick removed - now using event-driven bleSetupTask bool BLEtick(NimBLEAdvertising* pAdvertising);
#endif #endif

View File

@@ -12,6 +12,8 @@ esp_event_handler_instance_t WiFi::instance_got_ip = NULL;
#define WIFI_CONNECTED_BIT BIT0 #define WIFI_CONNECTED_BIT BIT0
#define WIFI_STARTED_BIT BIT1 #define WIFI_STARTED_BIT BIT1
WiFi bmWiFi;
// The Event Handler (The engine room) // The Event Handler (The engine room)
void WiFi::event_handler(void* arg, esp_event_base_t event_base, void WiFi::event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) { int32_t event_id, void* event_data) {
@@ -117,16 +119,6 @@ bool WiFi::isConnected() {
return (bits & WIFI_CONNECTED_BIT); 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 --- // --- GET IP AS STRING ---
std::string WiFi::getIP() { std::string WiFi::getIP() {
esp_netif_ip_info_t ip_info; esp_netif_ip_info_t ip_info;

View File

@@ -14,10 +14,6 @@ class WiFi {
const std::string password, const wifi_auth_mode_t authMode); const std::string password, const wifi_auth_mode_t authMode);
static bool isConnected(); static bool isConnected();
static void scanAndUpdateSSIDList(); static void scanAndUpdateSSIDList();
// Helper to check if auth mode requires enterprise credentials
static bool isEnterpriseMode(wifi_auth_mode_t authMode);
private: private:
static void processScanResults(); static void processScanResults();
static std::atomic<bool> authFailed; static std::atomic<bool> authFailed;
@@ -31,4 +27,6 @@ class WiFi {
static std::string getIP(); static std::string getIP();
}; };
extern WiFi bmWiFi;
#endif #endif

View File

@@ -2,11 +2,6 @@
#include "defines.h" #include "defines.h"
#include "nvs_flash.h" #include "nvs_flash.h"
// Define static members
std::atomic<int32_t> Calibration::DownTicks{0};
std::atomic<int32_t> Calibration::UpTicks{0};
std::atomic<bool> Calibration::calibrated{false};
void Calibration::init() { void Calibration::init() {
nvs_handle_t calibHandle; nvs_handle_t calibHandle;
if (nvs_open(nvsCalib, NVS_READONLY, &calibHandle) == ESP_OK) { if (nvs_open(nvsCalib, NVS_READONLY, &calibHandle) == ESP_OK) {

View File

@@ -5,18 +5,20 @@
class Calibration { class Calibration {
public: public:
static void init(); void init();
static bool beginDownwardCalib(Encoder& topEnc); bool beginDownwardCalib(Encoder& topEnc);
static bool completeCalib(Encoder& topEnc); bool completeCalib(Encoder& topEnc);
static int32_t convertToTicks(uint8_t appPos); int32_t convertToTicks(uint8_t appPos);
static uint8_t convertToAppPos(int32_t ticks); uint8_t convertToAppPos(int32_t ticks);
static bool getCalibrated() {return calibrated;} bool getCalibrated() {return calibrated;}
static bool clearCalibrated(); bool clearCalibrated();
static std::atomic<int32_t> DownTicks; std::atomic<int32_t> DownTicks;
static std::atomic<int32_t> UpTicks; std::atomic<int32_t> UpTicks;
private: private:
static std::atomic<bool> calibrated; std::atomic<bool> calibrated;
}; };
extern Calibration calib;
#endif #endif

View File

@@ -2,33 +2,6 @@
#define DEFINES_H #define DEFINES_H
#include "driver/gpio.h" #include "driver/gpio.h"
#include "driver/ledc.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 ccwSpeed 6500
#define cwSpeed 3300 #define cwSpeed 3300

View File

@@ -3,7 +3,6 @@
#include "esp_log.h" #include "esp_log.h"
#include "soc/gpio_struct.h" #include "soc/gpio_struct.h"
#include "servo.hpp" #include "servo.hpp"
#include "defines.h"
static const char *TAG = "ENCODER"; static const char *TAG = "ENCODER";
@@ -49,32 +48,26 @@ void IRAM_ATTR Encoder::isr_handler(void* arg)
if (encoder->last_count_base > 3) { if (encoder->last_count_base > 3) {
encoder->count += 1; encoder->count += 1;
encoder->last_count_base -= 4; encoder->last_count_base -= 4;
if (calibListen) servoCalibListen();
// DEFER to task via queue instead of direct function calls if (encoder->feedWDog) {
encoder_event_t event = { esp_timer_stop(encoder->watchdog_handle);
.count = encoder->count.load(), esp_timer_start_once(encoder->watchdog_handle, 500000);
.is_top_encoder = (encoder == topEnc) debugLEDTgl();
};
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (g_encoder_event_queue != NULL) {
xQueueSendFromISR(g_encoder_event_queue, &event, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} }
if (encoder->wandListen) servoWandListen();
if (encoder->serverListen) servoServerListen();
} }
else if (encoder->last_count_base < 0) { else if (encoder->last_count_base < 0) {
encoder->count -= 1; encoder->count -= 1;
encoder->last_count_base += 4; encoder->last_count_base += 4;
if (calibListen) servoCalibListen();
// DEFER to task via queue instead of direct function calls if (encoder->feedWDog) {
encoder_event_t event = { esp_timer_stop(encoder->watchdog_handle);
.count = encoder->count.load(), esp_timer_start_once(encoder->watchdog_handle, 500000);
.is_top_encoder = (encoder == topEnc) debugLEDTgl();
};
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (g_encoder_event_queue != NULL) {
xQueueSendFromISR(g_encoder_event_queue, &event, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} }
if (encoder->wandListen) servoWandListen();
if (encoder->serverListen) servoServerListen();
} }
encoder->last_state_a = current_a; encoder->last_state_a = current_a;

View File

@@ -4,12 +4,6 @@
#include <atomic> #include <atomic>
#include "esp_timer.h" #include "esp_timer.h"
// Encoder event structure for queue
typedef struct {
int32_t count;
bool is_top_encoder;
} encoder_event_t;
class Encoder { class Encoder {
public: public:
// Shared between ISR and main code // Shared between ISR and main code

View File

@@ -47,7 +47,7 @@ void servoInit() {
gpio_set_level(debugLED, 0); // Start with LED off gpio_set_level(debugLED, 0); // Start with LED off
topEnc->count = servoReadPos(); topEnc->count = servoReadPos();
if (Calibration::getCalibrated()) initMainLoop(); if (calib.getCalibrated()) initMainLoop();
debugLEDSwitch(1); debugLEDSwitch(1);
} }
@@ -87,7 +87,7 @@ bool servoInitCalib() {
bottomEnc->wandListen.store(false, std::memory_order_release); bottomEnc->wandListen.store(false, std::memory_order_release);
topEnc->wandListen.store(false, std::memory_order_release); topEnc->wandListen.store(false, std::memory_order_release);
topEnc->serverListen.store(false, std::memory_order_release); topEnc->serverListen.store(false, std::memory_order_release);
if (!Calibration::clearCalibrated()) return false; if (!calib.clearCalibrated()) return false;
if (topEnc == nullptr || bottomEnc == nullptr) { if (topEnc == nullptr || bottomEnc == nullptr) {
printf("ERROR: CALIBRATION STARTED BEFORE SERVO INITIALIZATION\n"); printf("ERROR: CALIBRATION STARTED BEFORE SERVO INITIALIZATION\n");
return false; return false;
@@ -117,7 +117,7 @@ bool servoBeginDownwardCalib() {
calibListen = false; calibListen = false;
servoOff(); servoOff();
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
if (!Calibration::beginDownwardCalib(*topEnc)) return false; if (!calib.beginDownwardCalib(*topEnc)) return false;
baseDiff = bottomEnc->getCount() - topEnc->getCount(); baseDiff = bottomEnc->getCount() - topEnc->getCount();
calibListen = true; calibListen = true;
return true; return true;
@@ -127,7 +127,7 @@ bool servoCompleteCalib() {
calibListen = false; calibListen = false;
servoOff(); servoOff();
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
if (!Calibration::completeCalib(*topEnc)) return false; if (!calib.completeCalib(*topEnc)) return false;
initMainLoop(); initMainLoop();
return true; return true;
} }
@@ -139,11 +139,9 @@ void initMainLoop() {
} }
void IRAM_ATTR watchdogCallback(void* arg) { void IRAM_ATTR watchdogCallback(void* arg) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (runningManual || runningServer) { if (runningManual || runningServer) {
// if we're trying to move and our timer ran out, we need to recalibrate // if we're trying to move and our timer ran out, we need to recalibrate
xEventGroupSetBitsFromISR(g_system_events, EVENT_CLEAR_CALIB, &xHigherPriorityTaskWoken); clearCalibFlag = true;
topEnc->pauseWatchdog(); topEnc->pauseWatchdog();
// get ready for recalibration by clearing all these listeners // get ready for recalibration by clearing all these listeners
@@ -155,13 +153,11 @@ void IRAM_ATTR watchdogCallback(void* arg) {
else { else {
// if no movement is running, we're fine // if no movement is running, we're fine
// save current servo-encoder position for reinitialization // save current servo-encoder position for reinitialization
xEventGroupSetBitsFromISR(g_system_events, EVENT_SAVE_POSITION, &xHigherPriorityTaskWoken); savePosFlag = true;
} }
// clear running flags // clear running flags
runningManual = false; runningManual = false;
runningServer = false; runningServer = false;
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} }
void servoSavePos() { void servoSavePos() {
@@ -207,8 +203,8 @@ void servoWandListen() {
stopServerRun(); stopServerRun();
// freeze atomic values // freeze atomic values
int32_t upBound = Calibration::UpTicks; int32_t upBound = calib.UpTicks;
int32_t downBound = Calibration::DownTicks; int32_t downBound = calib.DownTicks;
int32_t bottomCount = bottomEnc->getCount(); int32_t bottomCount = bottomEnc->getCount();
int32_t topCount = topEnc->getCount(); int32_t topCount = topEnc->getCount();
@@ -261,10 +257,10 @@ void servoServerListen() {
void runToAppPos(uint8_t appPos) { void runToAppPos(uint8_t appPos) {
// manual control takes precedence over remote control, always. // manual control takes precedence over remote control, always.
// also do not begin operation if not calibrated; // also do not begin operation if not calibrated;
if (runningManual || !Calibration::getCalibrated()) return; if (runningManual || !calib.getCalibrated()) return;
servoOff(); servoOff();
target = Calibration::convertToTicks(appPos); // calculate target encoder position target = calib.convertToTicks(appPos); // calculate target encoder position
printf("runToAppPos Called, running to %d from %d", target.load(), topEnc->getCount()); printf("runToAppPos Called, running to %d from %d", target.load(), topEnc->getCount());
// allow servo position to settle // allow servo position to settle
@@ -276,82 +272,4 @@ void runToAppPos(uint8_t appPos) {
if (startLess) servoOn(CCW, server); // begin servo movement if (startLess) servoOn(CCW, server); // begin servo movement
else servoOn(CW, server); else servoOn(CW, server);
topEnc->serverListen.store(true, std::memory_order_release); // start listening for shutoff point topEnc->serverListen.store(true, std::memory_order_release); // 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) {
servoCalibListen();
}
// 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_stop(topEnc->watchdog_handle);
esp_timer_start_once(topEnc->watchdog_handle, 500000);
debugLEDTgl();
}
// Check wand listener - now safe in task context
if (topEnc->wandListen) {
servoWandListen();
}
// Check server listener - now safe in task context
if (topEnc->serverListen) {
servoServerListen();
}
}
// Only process top encoder events for watchdog and listeners
else {
// Feed watchdog in task context (not ISR)
if (bottomEnc->feedWDog) {
esp_timer_stop(bottomEnc->watchdog_handle);
esp_timer_start_once(bottomEnc->watchdog_handle, 500000);
debugLEDTgl();
}
// Check wand listener - now safe in task context
if (bottomEnc->wandListen) {
servoWandListen();
}
// Check server listener - now safe in task context
if (bottomEnc->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;
}
}
}
} }

View File

@@ -9,20 +9,6 @@
#define server 1 #define server 1
#define manual 0 #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<bool> calibListen; extern std::atomic<bool> calibListen;
extern std::atomic<bool> clearCalibFlag; extern std::atomic<bool> clearCalibFlag;
extern std::atomic<bool> savePosFlag; extern std::atomic<bool> savePosFlag;
@@ -52,7 +38,4 @@ void servoWandListen();
void servoServerListen(); void servoServerListen();
void runToAppPos(uint8_t appPos); void runToAppPos(uint8_t appPos);
// Servo control task
void servoControlTask(void* arg);
#endif #endif

View File

@@ -5,147 +5,14 @@
#include "defines.h" #include "defines.h"
#include "bmHTTP.hpp" #include "bmHTTP.hpp"
#include "socketIO.hpp" #include "socketIO.hpp"
#include <mutex>
// 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<bool> scanBlock;
extern std::atomic<bool> 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");
WiFi::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<std::mutex> lock(dataMutex);
tmpSSID = SSID;
tmpUNAME = UNAME;
tmpPASS = PASS;
tmpAUTH = auth;
}
bool wifiConnect;
if (WiFi::isEnterpriseMode(tmpAUTH))
wifiConnect = WiFi::attemptConnect(tmpSSID, tmpUNAME, tmpPASS, tmpAUTH);
else wifiConnect = WiFi::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_u8(WiFiHandle, authTag, (uint8_t)tmpAUTH);
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);
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<std::mutex> 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() { void initialSetup() {
printf("Entered Setup\n"); printf("Entered Setup\n");
NimBLEAdvertising* pAdv = initBLE();
// Create BLE setup task instead of polling loop
xTaskCreate(bleSetupTask, "ble_setup", 8192, NULL, BLE_TASK_PRIORITY, NULL); while (!BLEtick(pAdv)) {
// Wait for BLE setup to complete (finalAuth = true)
while (!finalAuth) {
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
} }
printf("BLE setup complete\n");
} }
void setupLoop() { void setupLoop() {
@@ -170,7 +37,7 @@ void setupLoop() {
char pw[pwSize]; char pw[pwSize];
nvs_get_str(WiFiHandle, passTag, pw, &pwSize); nvs_get_str(WiFiHandle, passTag, pw, &pwSize);
nvs_close(WiFiHandle); nvs_close(WiFiHandle);
if (!WiFi::attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) { if (!bmWiFi.attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) {
// Make RGB LED certain color (Blue?) // Make RGB LED certain color (Blue?)
printf("Found credentials, failed to connect.\n"); printf("Found credentials, failed to connect.\n");
initialSetup(); initialSetup();
@@ -189,28 +56,29 @@ void setupLoop() {
// The server will verify the token during connection handshake // The server will verify the token during connection handshake
webToken = std::string(token); webToken = std::string(token);
printf("Connecting to Socket.IO server with saved token...\n"); printf("Connecting to Socket.IO server with saved token...\n");
statusResolved = false;
initSocketIO(); initSocketIO();
// Wait for connection event with timeout // Wait for device_init message from server with timeout
EventBits_t bits = xEventGroupWaitBits( int timeout_count = 0;
g_system_events, const int MAX_TIMEOUT = 60; // 10 seconds (20 * 500ms)
EVENT_SOCKETIO_CONNECTED | EVENT_SOCKETIO_DISCONNECTED, while (!statusResolved && timeout_count < MAX_TIMEOUT) {
pdTRUE, // Clear on exit printf("Waiting for device_init message... (%d/%d)\n", timeout_count, MAX_TIMEOUT);
pdFALSE, // Wait for ANY bit vTaskDelay(pdMS_TO_TICKS(500));
pdMS_TO_TICKS(10000) // 10 second timeout timeout_count++;
); }
if (bits & EVENT_SOCKETIO_CONNECTED) { if (timeout_count >= MAX_TIMEOUT) {
printf("Timeout waiting for device_init - connection failed\n");
stopSocketIO();
initSuccess = false;
initialSetup();
} else {
initSuccess = connected; initSuccess = connected;
if (!initSuccess) { if (!initSuccess) {
printf("Device authentication failed - entering setup\n"); printf("Device authentication failed - entering setup\n");
initialSetup(); initialSetup();
} }
} else {
printf("Timeout waiting for connection\n");
stopSocketIO();
initSuccess = false;
initialSetup();
} }
} }
else { else {

View File

@@ -12,6 +12,7 @@
static esp_socketio_client_handle_t io_client; static esp_socketio_client_handle_t io_client;
static esp_socketio_packet_handle_t tx_packet = NULL; static esp_socketio_packet_handle_t tx_packet = NULL;
std::atomic<bool> statusResolved{true};
std::atomic<bool> connected{false}; std::atomic<bool> connected{false};
// Event handler for Socket.IO events // Event handler for Socket.IO events
@@ -65,7 +66,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
// Mark connection as failed // Mark connection as failed
connected = false; connected = false;
xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); statusResolved = true;
} }
// Handle device_init event // Handle device_init event
else if (strcmp(eventName->valuestring, "device_init") == 0) { else if (strcmp(eventName->valuestring, "device_init") == 0) {
@@ -92,7 +93,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
if (port != 1) printf("ERROR: NON-1 PORT RECEIVED\n"); if (port != 1) printf("ERROR: NON-1 PORT RECEIVED\n");
// Report back actual calibration status from device // Report back actual calibration status from device
else { else {
bool deviceCalibrated = Calibration::getCalibrated(); bool deviceCalibrated = calib.getCalibrated();
emitCalibStatus(deviceCalibrated); emitCalibStatus(deviceCalibrated);
printf(" Reported calibrated=%d for port %d\n", deviceCalibrated, port); printf(" Reported calibrated=%d for port %d\n", deviceCalibrated, port);
runToAppPos(lastPos); runToAppPos(lastPos);
@@ -102,13 +103,13 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
// Now mark as connected // Now mark as connected
connected = true; connected = true;
xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_CONNECTED); statusResolved = true;
} else { } else {
printf("Device authentication failed\n"); printf("Device authentication failed\n");
Calibration::clearCalibrated(); calib.clearCalibrated();
deleteWiFiAndTokenDetails(); deleteWiFiAndTokenDetails();
connected = false; connected = false;
xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); statusResolved = true;
} }
} }
} }
@@ -122,10 +123,10 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
printf("Server message: %s\n", message->valuestring); printf("Server message: %s\n", message->valuestring);
} }
} }
Calibration::clearCalibrated(); calib.clearCalibrated();
deleteWiFiAndTokenDetails(); deleteWiFiAndTokenDetails();
connected = false; connected = false;
xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); statusResolved = true;
} }
// Handle calib_start event // Handle calib_start event
@@ -292,9 +293,8 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
} }
} }
// Signal disconnection via event group
connected = false; connected = false;
xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); statusResolved = true;
break; break;
} }
} }
@@ -303,7 +303,7 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
if (data->websocket_event_id == WEBSOCKET_EVENT_DISCONNECTED) { if (data->websocket_event_id == WEBSOCKET_EVENT_DISCONNECTED) {
printf("WebSocket disconnected\n"); printf("WebSocket disconnected\n");
connected = false; connected = false;
xEventGroupSetBits(g_system_events, EVENT_SOCKETIO_DISCONNECTED); statusResolved = true;
} }
} }
@@ -312,6 +312,7 @@ void initSocketIO() {
// Prepare the Authorization Header (Bearer format) // Prepare the Authorization Header (Bearer format)
std::string authHeader = "Authorization: Bearer " + webToken + "\r\n"; std::string authHeader = "Authorization: Bearer " + webToken + "\r\n";
statusResolved = false;
connected = false; connected = false;
esp_socketio_client_config_t config = {}; esp_socketio_client_config_t config = {};
@@ -338,6 +339,7 @@ void stopSocketIO() {
io_client = NULL; io_client = NULL;
tx_packet = NULL; tx_packet = NULL;
connected = false; connected = false;
statusResolved = false;
} }
} }

View File

@@ -2,6 +2,7 @@
#define SOCKETIO_HPP #define SOCKETIO_HPP
#include <atomic> #include <atomic>
extern std::atomic<bool> statusResolved;
extern std::atomic<bool> connected; extern std::atomic<bool> connected;
// Initialize Socket.IO client and connect to server // Initialize Socket.IO client and connect to server

View File

@@ -9,58 +9,67 @@
#include "encoder.hpp" #include "encoder.hpp"
#include "calibration.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 // Global encoder instances
Encoder* topEnc = new Encoder(ENCODER_PIN_A, ENCODER_PIN_B); Encoder* topEnc = new Encoder(ENCODER_PIN_A, ENCODER_PIN_B);
Encoder* bottomEnc = new Encoder(InputEnc_PIN_A, InputEnc_PIN_B); Encoder* bottomEnc = new Encoder(InputEnc_PIN_A, InputEnc_PIN_B);
void mainTask(void* arg) { // Global encoder pointers (used by servo.cpp)
EventBits_t bits;
// 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();
printf("Main task started - event-driven mode\n"); // Initialize encoders
topEnc->init();
bottomEnc->init();
servoInit();
setupLoop();
statusResolved = false;
int32_t prevCount = topEnc->getCount();
// Main loop
while (1) { while (1) {
// Block waiting for ANY event (no polling!) // websocket disconnect/reconnect handling
bits = xEventGroupWaitBits( if (statusResolved) {
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) { if (!connected) {
printf("Disconnected! Beginning setup loop.\n"); printf("Disconnected! Beginning setup loop.\n");
stopSocketIO(); stopSocketIO();
setupLoop(); setupLoop();
} }
else { else printf("Reconnected!\n");
printf("Reconnected!\n"); statusResolved = false;
}
} }
if (bits & EVENT_CLEAR_CALIB) { if (clearCalibFlag) {
xSemaphoreTake(g_calibration_mutex, portMAX_DELAY); calib.clearCalibrated();
Calibration::clearCalibrated();
xSemaphoreGive(g_calibration_mutex);
emitCalibStatus(false); emitCalibStatus(false);
clearCalibFlag = false;
} }
if (savePosFlag) {
if (bits & EVENT_SAVE_POSITION) {
servoSavePos(); servoSavePos();
savePosFlag = false;
// Send position update to server // Send position update to server
uint8_t currentAppPos = Calibration::convertToAppPos(topEnc->getCount()); uint8_t currentAppPos = calib.convertToAppPos(topEnc->getCount());
emitPosHit(currentAppPos); emitPosHit(currentAppPos);
printf("Sent pos_hit: position %d\n", currentAppPos); printf("Sent pos_hit: position %d\n", currentAppPos);
} }
vTaskDelay(pdMS_TO_TICKS(100));
} }
} }
@@ -82,44 +91,6 @@ void encoderTest() {
} }
extern "C" void app_main() { extern "C" void app_main() {
// Initialize NVS first mainApp();
esp_err_t ret = nvs_flash_init(); // encoderTest();
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
WiFi::init();
Calibration::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");
} }