Delete/add_device cycle works smoothly now. All cases tested, next up is using position and calibration data to move motors and use encoders etc.

This commit is contained in:
2025-12-24 18:39:21 -06:00
parent 82da4930e5
commit 456e3dcfd3
7 changed files with 224 additions and 89 deletions

View File

@@ -12,6 +12,7 @@ std::atomic<bool> credsGiven{false};
std::atomic<bool> tokenGiven{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::mutex dataMutex; std::mutex dataMutex;
wifi_auth_mode_t auth; wifi_auth_mode_t auth;
@@ -29,6 +30,8 @@ std::atomic<NimBLECharacteristic*> tokenChar = nullptr;
std::atomic<NimBLECharacteristic*> ssidRefreshChar = nullptr; std::atomic<NimBLECharacteristic*> ssidRefreshChar = nullptr;
NimBLEAdvertising* initBLE() { NimBLEAdvertising* initBLE() {
finalAuth = false;
NimBLEDevice::init("BlindMaster-C6"); NimBLEDevice::init("BlindMaster-C6");
// Optional: Boost power for better range (ESP32-C6 supports up to +20dBm) // Optional: Boost power for better range (ESP32-C6 supports up to +20dBm)
@@ -101,7 +104,6 @@ NimBLEAdvertising* initBLE() {
pAdvertising->start(); pAdvertising->start();
printf("BLE Started. Waiting...\n"); printf("BLE Started. Waiting...\n");
flag_scan_requested = true;
return pAdvertising; return pAdvertising;
} }
@@ -223,7 +225,9 @@ bool BLEtick(NimBLEAdvertising* pAdvertising) {
} }
else printf("Failed to parse JSON response\n"); else printf("Failed to parse JSON response\n");
finalAuth = true;
notifyAuthStatus(success); notifyAuthStatus(success);
if (success) NimBLEDevice::deinit(true); // deinitialize BLE
return success; return success;
} }
return false; return false;
@@ -231,7 +235,7 @@ bool BLEtick(NimBLEAdvertising* pAdvertising) {
void reset() { void reset() {
esp_wifi_scan_stop(); esp_wifi_scan_stop();
if (!connected) esp_wifi_disconnect(); if (!finalAuth) esp_wifi_disconnect();
scanBlock = false; scanBlock = false;
flag_scan_requested = false; flag_scan_requested = false;
credsGiven = false; credsGiven = false;
@@ -260,8 +264,13 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
printf("onWrite called! UUID: %s, Value length: %d\n", uuidStr.c_str(), val.length()); printf("onWrite called! UUID: %s, Value length: %d\n", uuidStr.c_str(), val.length());
// Load atomic pointers for comparison
NimBLECharacteristic* currentCredsChar = credsChar.load();
NimBLECharacteristic* currentTokenChar = tokenChar.load();
NimBLECharacteristic* currentRefreshChar = ssidRefreshChar.load();
// Check which characteristic was written to // Check which characteristic was written to
if (pChar == credsChar) { if (pChar == currentCredsChar) {
// Credentials JSON characteristic // Credentials JSON characteristic
if (val.length() > 0) { if (val.length() > 0) {
printf("Received JSON: %s\n", val.c_str()); printf("Received JSON: %s\n", val.c_str());
@@ -315,7 +324,7 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
} }
} }
} }
else if (pChar == tokenChar) { else if (pChar == currentTokenChar) {
if (val.length() > 0) { if (val.length() > 0) {
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);
@@ -323,7 +332,7 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
tokenGiven = true; tokenGiven = true;
} }
} }
else if (pChar == ssidRefreshChar) { else if (pChar == currentRefreshChar) {
if (val == "Start") { if (val == "Start") {
// Refresh characteristic // Refresh characteristic
printf("Refresh Requested\n"); printf("Refresh Requested\n");

View File

@@ -29,6 +29,4 @@ class WiFi {
extern WiFi bmWiFi; extern WiFi bmWiFi;
void scanAndUpdateSSIDList();
#endif #endif

View File

@@ -1,6 +1,10 @@
#include "setup.hpp" #include "setup.hpp"
#include "BLE.hpp" #include "BLE.hpp"
#include "WiFi.hpp" #include "WiFi.hpp"
#include "nvs_flash.h"
#include "defines.h"
#include "bmHTTP.hpp"
#include "socketIO.hpp"
void initialSetup() { void initialSetup() {
printf("Entered Setup\n"); printf("Entered Setup\n");
@@ -9,4 +13,95 @@ void initialSetup() {
while (!BLEtick(pAdv)) { while (!BLEtick(pAdv)) {
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
} }
}
void setupLoop() {
bool initSuccess = false;
while(!initSuccess) {
nvs_handle_t WiFiHandle;
if (nvs_open(nvsWiFi, NVS_READONLY, &WiFiHandle) == ESP_OK) {
size_t ssidSize;
esp_err_t WiFiPrefsError = nvs_get_str(WiFiHandle, ssidTag, NULL, &ssidSize);
size_t pwSize;
WiFiPrefsError |= nvs_get_str(WiFiHandle, passTag, NULL, &pwSize);
uint8_t authMode;
WiFiPrefsError |= nvs_get_u8(WiFiHandle, authTag, &authMode);
printf("World\n");
if (WiFiPrefsError == ESP_ERR_NVS_NOT_FOUND) {
printf("Didn't find creds\n");
// Make the RGB LED a certain color (Blue?)
nvs_close(WiFiHandle);
initialSetup();
} else if (WiFiPrefsError == ESP_OK) {
char ssid[ssidSize];
nvs_get_str(WiFiHandle, ssidTag, ssid, &ssidSize);
char pw[pwSize];
nvs_get_str(WiFiHandle, passTag, pw, &pwSize);
nvs_close(WiFiHandle);
if (!bmWiFi.attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) {
// Make RGB LED certain color (Blue?)
printf("Found credentials, failed to connect.\n");
initialSetup();
}
else {
printf("Connected to WiFi from NVS credentials\n");
nvs_handle_t authHandle;
if (nvs_open(nvsAuth, NVS_READONLY, &authHandle) == ESP_OK) {
size_t tokenSize;
if (nvs_get_str(authHandle, tokenTag, NULL, &tokenSize) == ESP_OK) {
char token[tokenSize];
nvs_get_str(authHandle, tokenTag, token, &tokenSize);
nvs_close(authHandle);
// 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;
initialSetup();
} else {
initSuccess = connected;
if (!initSuccess) {
printf("Device authentication failed - entering setup\n");
initialSetup();
}
}
}
else {
printf("Token read unsuccessful, entering setup.\n");
initialSetup();
}
}
else {
printf("Auth NVS segment doesn't exist, entering setup.\n");
initialSetup();
}
}
} else {
// Make RGB LED certain color (Blue?)
nvs_close(WiFiHandle);
printf("Program error in Wifi Connection\n");
initialSetup();
}
}
else {
printf("WiFi NVS segment doesn't exist, entering setup.\n");
initialSetup();
}
}
} }

View File

@@ -2,5 +2,6 @@
#define SETUP_H #define SETUP_H
void initialSetup(); void initialSetup();
void setupLoop();
#endif #endif

View File

@@ -8,7 +8,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{false}; 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
@@ -29,10 +29,9 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
// Check if connected to default namespace // Check if connected to default namespace
char *nsp = esp_socketio_packet_get_nsp(packet); char *nsp = esp_socketio_packet_get_nsp(packet);
if (strcmp(nsp, "/") == 0) { if (strcmp(nsp, "/") == 0) {
printf("Connected to default namespace\n"); printf("Connected to default namespace - waiting for device_init...\n");
} }
connected = true; // Don't set connected yet - wait for device_init message from server
statusResolved = true;
break; break;
} }
@@ -43,6 +42,82 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
if (json) { if (json) {
char *json_str = cJSON_Print(json); char *json_str = cJSON_Print(json);
printf("Data: %s\n", json_str); printf("Data: %s\n", json_str);
// Check if this is an array event
if (cJSON_IsArray(json) && cJSON_GetArraySize(json) >= 2) {
cJSON *eventName = cJSON_GetArrayItem(json, 0);
if (cJSON_IsString(eventName)) {
// Handle error event
if (strcmp(eventName->valuestring, "error") == 0) {
printf("Received error message from server\n");
cJSON *data = cJSON_GetArrayItem(json, 1);
if (data) {
cJSON *message = cJSON_GetObjectItem(data, "message");
if (message && cJSON_IsString(message)) {
printf("Server error: %s\n", message->valuestring);
}
}
// Mark connection as failed
connected = false;
statusResolved = true;
}
// Handle device_init event
else if (strcmp(eventName->valuestring, "device_init") == 0) {
printf("Received device_init message\n");
cJSON *data = cJSON_GetArrayItem(json, 1);
if (data) {
cJSON *type = cJSON_GetObjectItem(data, "type");
if (type && strcmp(type->valuestring, "success") == 0) {
printf("Device authenticated successfully\n");
// Parse device state
cJSON *deviceState = cJSON_GetObjectItem(data, "deviceState");
if (cJSON_IsArray(deviceState)) {
int stateCount = cJSON_GetArraySize(deviceState);
printf("Device has %d peripheral(s):\n", stateCount);
for (int i = 0; i < stateCount; i++) {
cJSON *periph = cJSON_GetArrayItem(deviceState, i);
int port = cJSON_GetObjectItem(periph, "port")->valueint;
int lastPos = cJSON_GetObjectItem(periph, "lastPos")->valueint;
bool awaitCalib = cJSON_IsTrue(cJSON_GetObjectItem(periph, "awaitCalib"));
bool calibrated = cJSON_IsTrue(cJSON_GetObjectItem(periph, "calibrated"));
// TODO: UPDATE MOTOR/ENCODER STATES BASED ON THIS, as well as the successive websocket updates.
printf(" Port %d: pos=%d, calibrated=%d, awaitCalib=%d\n",
port, lastPos, calibrated, awaitCalib);
}
}
// Now mark as connected
connected = true;
statusResolved = true;
} else {
printf("Device authentication failed\n");
connected = false;
statusResolved = true;
}
}
}
// Handle device_deleted event
else if (strcmp(eventName->valuestring, "device_deleted") == 0) {
printf("Device has been deleted from account - disconnecting\n");
cJSON *data = cJSON_GetArrayItem(json, 1);
if (data) {
cJSON *message = cJSON_GetObjectItem(data, "message");
if (message && cJSON_IsString(message)) {
printf("Server message: %s\n", message->valuestring);
}
}
connected = false;
statusResolved = true;
}
}
}
free(json_str); free(json_str);
} }
break; break;
@@ -61,17 +136,19 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
} }
} }
// Disconnect and enter setup mode // Set flags to indicate connection failure
printf("Disconnecting and entering setup mode...\n");
esp_socketio_client_close(io_client, pdMS_TO_TICKS(1000));
esp_socketio_client_destroy(io_client);
initialSetup();
connected = false; connected = false;
statusResolved = true; statusResolved = true;
break; break;
} }
} }
// Handle WebSocket-level disconnections
if (data->websocket_event_id == WEBSOCKET_EVENT_DISCONNECTED) {
printf("WebSocket disconnected\n");
connected = false;
statusResolved = true;
}
} }
void initSocketIO() { void initSocketIO() {
@@ -92,6 +169,18 @@ void initSocketIO() {
esp_socketio_client_start(io_client); esp_socketio_client_start(io_client);
} }
void stopSocketIO() {
if (io_client != NULL) {
printf("Stopping Socket.IO client...\n");
esp_socketio_client_close(io_client, pdMS_TO_TICKS(1000));
esp_socketio_client_destroy(io_client);
io_client = NULL;
tx_packet = NULL;
connected = false;
statusResolved = false;
}
}
// Function to emit 'calib_done' as expected by your server // Function to emit 'calib_done' as expected by your server
void emitCalibDone(int port) { void emitCalibDone(int port) {
// Set packet header: EIO MESSAGE type, SIO EVENT type, default namespace "/" // Set packet header: EIO MESSAGE type, SIO EVENT type, default namespace "/"

View File

@@ -8,6 +8,9 @@ extern std::atomic<bool> connected;
// Initialize Socket.IO client and connect to server // Initialize Socket.IO client and connect to server
void initSocketIO(); void initSocketIO();
// Stop and destroy Socket.IO client
void stopSocketIO();
// Emit calibration done event to server // Emit calibration done event to server
void emitCalibDone(int port); void emitCalibDone(int port);

View File

@@ -3,12 +3,9 @@
#include "defines.h" #include "defines.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "BLE.hpp"
#include "WiFi.hpp" #include "WiFi.hpp"
#include "setup.hpp" #include "setup.hpp"
#include "bmHTTP.hpp"
#include "socketIO.hpp" #include "socketIO.hpp"
#include "cJSON.h"
extern "C" void app_main() { extern "C" void app_main() {
printf("Hello "); printf("Hello ");
@@ -22,80 +19,23 @@ extern "C" void app_main() {
bmWiFi.init(); bmWiFi.init();
bool initSuccess = false; setupLoop();
while(!initSuccess) {
nvs_handle_t WiFiHandle;
if (nvs_open(nvsWiFi, NVS_READONLY, &WiFiHandle) == ESP_OK) {
size_t ssidSize;
esp_err_t WiFiPrefsError = nvs_get_str(WiFiHandle, ssidTag, NULL, &ssidSize);
size_t pwSize;
WiFiPrefsError |= nvs_get_str(WiFiHandle, passTag, NULL, &pwSize);
uint8_t authMode;
WiFiPrefsError |= nvs_get_u8(WiFiHandle, authTag, &authMode);
printf("World\n");
if (WiFiPrefsError == ESP_ERR_NVS_NOT_FOUND) {
printf("Didn't find creds\n");
// Make the RGB LED a certain color (Blue?)
nvs_close(WiFiHandle);
initialSetup();
} else if (WiFiPrefsError == ESP_OK) {
char ssid[ssidSize];
nvs_get_str(WiFiHandle, ssidTag, ssid, &ssidSize);
char pw[pwSize];
nvs_get_str(WiFiHandle, passTag, pw, &pwSize);
nvs_close(WiFiHandle);
if (!bmWiFi.attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) {
// Make RGB LED certain color (Blue?)
printf("Found credentials, failed to connect.\n");
initialSetup();
}
else {
printf("Connected to WiFi from NVS credentials\n");
nvs_handle_t authHandle;
if (nvs_open(nvsAuth, NVS_READONLY, &authHandle) == ESP_OK) {
size_t tokenSize;
if (nvs_get_str(authHandle, tokenTag, NULL, &tokenSize) == ESP_OK) {
char token[tokenSize];
nvs_get_str(authHandle, tokenTag, token, &tokenSize);
nvs_close(authHandle);
// 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");
initSocketIO();
while (!statusResolved) vTaskDelay(pdMS_TO_TICKS(100));
initSuccess = connected;
}
else {
printf("Token read unsuccessful, entering setup.\n");
initialSetup();
}
}
else {
printf("Auth NVS segment doesn't exist, entering setup.\n");
initialSetup();
}
}
} else {
// Make RGB LED certain color (Blue?)
nvs_close(WiFiHandle);
printf("Program error in Wifi Connection\n");
initialSetup();
}
}
else {
printf("WiFi NVS segment doesn't exist, entering setup.\n");
initialSetup();
}
}
statusResolved = false;
// Main loop // Main loop
while (1) { while (1) {
if (statusResolved) {
if (!connected) {
printf("Disconnected! Beginning setup loop.\n");
stopSocketIO();
setupLoop();
}
else printf("Reconnected!\n");
statusResolved = false;
}
// Your main application logic here // Your main application logic here
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
printf("Main Loop\n"); printf("loop\n");
} }
} }