add resetGPIO
This commit is contained in:
370
include/BLE.cpp
370
include/BLE.cpp
@@ -1,370 +0,0 @@
|
||||
#include "BLE.hpp"
|
||||
#include "NimBLEDevice.h"
|
||||
#include "WiFi.hpp"
|
||||
#include "nvs_flash.h"
|
||||
#include "socketIO.hpp"
|
||||
#include "defines.h"
|
||||
#include <mutex>
|
||||
#include "bmHTTP.hpp"
|
||||
#include "esp_mac.h"
|
||||
|
||||
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> scanBlock{false};
|
||||
std::atomic<bool> finalAuth{false};
|
||||
std::mutex dataMutex;
|
||||
|
||||
wifi_auth_mode_t auth;
|
||||
static std::string SSID = "";
|
||||
static std::string TOKEN = "";
|
||||
static std::string PASS = "";
|
||||
static std::string UNAME = "";
|
||||
|
||||
// Global pointers to characteristics for notification support
|
||||
std::atomic<NimBLECharacteristic*> ssidListChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> connectConfirmChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> authConfirmChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> credsChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> tokenChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> ssidRefreshChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> deviceInfoChar = nullptr;
|
||||
|
||||
NimBLEAdvertising* initBLE() {
|
||||
finalAuth = false;
|
||||
|
||||
NimBLEDevice::init("BlindMaster-C6");
|
||||
|
||||
// Optional: Boost power for better range (ESP32-C6 supports up to +20dBm)
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
// Set security
|
||||
NimBLEDevice::setSecurityAuth(false, false, true); // bonding=false, mitm=false, sc=true (Secure Connections)
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT); // No input/output capability
|
||||
|
||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
pServer->advertiseOnDisconnect(true); // Automatically restart advertising on disconnect
|
||||
|
||||
NimBLEService *pService = pServer->createService("181C");
|
||||
|
||||
// Create all characteristics with callbacks
|
||||
MyCharCallbacks* charCallbacks = new MyCharCallbacks();
|
||||
|
||||
// 0x0000 - SSID List (READ)
|
||||
ssidListChar = pService->createCharacteristic(
|
||||
"0000",
|
||||
NIMBLE_PROPERTY::READ
|
||||
);
|
||||
ssidListChar.load()->createDescriptor("2902"); // Add BLE2902 descriptor for notifications
|
||||
|
||||
// 0x0001 - Credentials JSON (WRITE) - Replaces separate SSID/Password/Uname
|
||||
// Expected JSON format: {"ssid":"network","password":"pass"}
|
||||
credsChar = pService->createCharacteristic(
|
||||
"0001",
|
||||
NIMBLE_PROPERTY::WRITE
|
||||
);
|
||||
credsChar.load()->setCallbacks(charCallbacks);
|
||||
|
||||
// 0x0002 - Token (WRITE)
|
||||
tokenChar = pService->createCharacteristic(
|
||||
"0002",
|
||||
NIMBLE_PROPERTY::WRITE
|
||||
);
|
||||
tokenChar.load()->setCallbacks(charCallbacks);
|
||||
|
||||
// 0x0003 - Auth Confirmation (READ + NOTIFY)
|
||||
authConfirmChar = pService->createCharacteristic(
|
||||
"0003",
|
||||
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY
|
||||
);
|
||||
authConfirmChar.load()->createDescriptor("2902"); // Add BLE2902 descriptor for notifications
|
||||
|
||||
// 0x0004 - SSID Refresh
|
||||
ssidRefreshChar = pService->createCharacteristic(
|
||||
"0004",
|
||||
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY
|
||||
);
|
||||
ssidRefreshChar.load()->setCallbacks(charCallbacks);
|
||||
|
||||
// 0x0005 - Connect Confirmation (READ + NOTIFY)
|
||||
connectConfirmChar = pService->createCharacteristic(
|
||||
"0005",
|
||||
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY
|
||||
);
|
||||
connectConfirmChar.load()->createDescriptor("2902"); // Add BLE2902 descriptor for notifications
|
||||
|
||||
// 0x0006 - Device Info (READ) - MAC address and other device details
|
||||
deviceInfoChar = pService->createCharacteristic(
|
||||
"0006",
|
||||
NIMBLE_PROPERTY::READ
|
||||
);
|
||||
// Build device info JSON with MAC address
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
char macStr[18];
|
||||
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
cJSON *infoRoot = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(infoRoot, "mac", macStr);
|
||||
cJSON_AddStringToObject(infoRoot, "firmware", "1.0.0");
|
||||
cJSON_AddStringToObject(infoRoot, "model", "BlindMaster-C6");
|
||||
char *infoJson = cJSON_PrintUnformatted(infoRoot);
|
||||
deviceInfoChar.load()->setValue(std::string(infoJson));
|
||||
cJSON_Delete(infoRoot);
|
||||
free(infoJson);
|
||||
|
||||
// Start
|
||||
pService->start();
|
||||
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID("181C");
|
||||
pAdvertising->setName("BlindMaster-C6");
|
||||
pAdvertising->enableScanResponse(true);
|
||||
pAdvertising->setPreferredParams(0x06, 0x12); // Connection interval preferences
|
||||
pAdvertising->start();
|
||||
|
||||
printf("BLE Started. Waiting...\n");
|
||||
|
||||
return pAdvertising;
|
||||
}
|
||||
|
||||
void notifyConnectionStatus(bool success) {
|
||||
NimBLECharacteristic* tmpConfChar = connectConfirmChar.load();
|
||||
tmpConfChar->setValue(success ? "Connected" : "Error");
|
||||
tmpConfChar->notify();
|
||||
tmpConfChar->setValue(""); // Clear value after notify
|
||||
}
|
||||
void notifyAuthStatus(bool success) {
|
||||
NimBLECharacteristic* tmpConfChar = authConfirmChar.load();
|
||||
tmpConfChar->setValue(success ? "Authenticated" : "Error");
|
||||
tmpConfChar->notify();
|
||||
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<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() {
|
||||
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) {
|
||||
isBLEClientConnected = true;
|
||||
printf("Client connected\n");
|
||||
reset();
|
||||
};
|
||||
|
||||
void MyServerCallbacks::onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
|
||||
isBLEClientConnected = false;
|
||||
printf("Client disconnected - reason: %d\n", reason);
|
||||
reset();
|
||||
}
|
||||
|
||||
void MyCharCallbacks::onRead(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) {
|
||||
printf("Characteristic Read\n");
|
||||
}
|
||||
|
||||
void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) {
|
||||
std::string val = pChar->getValue();
|
||||
std::string uuidStr = pChar->getUUID().toString();
|
||||
|
||||
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
|
||||
if (pChar == currentCredsChar) {
|
||||
// Credentials JSON characteristic
|
||||
if (val.length() > 0) {
|
||||
printf("Received JSON: %s\n", val.c_str());
|
||||
|
||||
// Parse JSON using cJSON
|
||||
cJSON *root = cJSON_Parse(val.c_str());
|
||||
if (root != NULL) {
|
||||
cJSON *ssid = cJSON_GetObjectItem(root, "ssid");
|
||||
cJSON *password = cJSON_GetObjectItem(root, "password");
|
||||
cJSON *authType = cJSON_GetObjectItem(root, "auth");
|
||||
cJSON *uname = cJSON_GetObjectItem(root, "uname");
|
||||
|
||||
bool enterprise = false;
|
||||
bool open = false;
|
||||
bool error = false;
|
||||
if (cJSON_IsNumber(authType)) {
|
||||
enterprise = authType->valueint == WIFI_AUTH_WPA2_ENTERPRISE ||
|
||||
authType->valueint == WIFI_AUTH_WPA3_ENTERPRISE;
|
||||
open = authType->valueint == WIFI_AUTH_OPEN;
|
||||
error = authType->valueint < 0 || authType->valueint >= WIFI_AUTH_MAX;
|
||||
}
|
||||
else error = true;
|
||||
if (error) {
|
||||
printf("ERROR: Invalid Auth mode passed in with JSON.\n");
|
||||
credsGiven = false;
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
|
||||
bool ssidPresent = cJSON_IsString(ssid) && ssid->valuestring != NULL;
|
||||
bool passPresent = cJSON_IsString(password) && password->valuestring != NULL;
|
||||
bool unamePresent = cJSON_IsString(uname) && uname->valuestring != NULL;
|
||||
bool tempCredsGiven = ssidPresent && (passPresent || open) &&
|
||||
(unamePresent || !enterprise);
|
||||
|
||||
if (tempCredsGiven) {
|
||||
printf("Received credentials, will attempt connection\n");
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
|
||||
auth = (wifi_auth_mode_t)(authType->valueint);
|
||||
SSID = ssid->valuestring;
|
||||
PASS = passPresent ? password->valuestring : "";
|
||||
UNAME = unamePresent ? uname->valuestring : "";
|
||||
credsGiven = tempCredsGiven; // update the global flag.
|
||||
}
|
||||
else printf("ERROR: Did not receive necessary credentials.\n");
|
||||
cJSON_Delete(root);
|
||||
} else {
|
||||
printf("Failed to parse JSON\n");
|
||||
credsGiven = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pChar == currentTokenChar) {
|
||||
if (val.length() > 0) {
|
||||
printf("Received Token: %s\n", val.c_str());
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
TOKEN = val;
|
||||
tokenGiven = true;
|
||||
}
|
||||
}
|
||||
else if (pChar == currentRefreshChar) {
|
||||
if (val == "Start") {
|
||||
// Refresh characteristic
|
||||
printf("Refresh Requested\n");
|
||||
flag_scan_requested = true;
|
||||
}
|
||||
else if (val == "Done") {
|
||||
printf("Data read complete\n");
|
||||
scanBlock = false;
|
||||
}
|
||||
}
|
||||
else printf("Unknown UUID: %s\n", uuidStr.c_str());
|
||||
}
|
||||
259
include/WiFi.cpp
259
include/WiFi.cpp
@@ -1,259 +0,0 @@
|
||||
#include "WiFi.hpp"
|
||||
#include "esp_eap_client.h"
|
||||
#include "cJSON.h" // Native replacement for ArduinoJson
|
||||
#include "BLE.hpp"
|
||||
|
||||
std::atomic<bool> WiFi::authFailed{false};
|
||||
EventGroupHandle_t WiFi::s_wifi_event_group = NULL;
|
||||
esp_netif_t* WiFi::netif = NULL;
|
||||
esp_event_handler_instance_t WiFi::instance_any_id = NULL;
|
||||
esp_event_handler_instance_t WiFi::instance_got_ip = NULL;
|
||||
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_STARTED_BIT BIT1
|
||||
|
||||
WiFi bmWiFi;
|
||||
|
||||
// The Event Handler (The engine room)
|
||||
void WiFi::event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data) {
|
||||
|
||||
// WiFi driver has finished initialization
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
printf("WiFi initialized and ready\n");
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_STARTED_BIT);
|
||||
}
|
||||
// We got disconnected -> Retry automatically
|
||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
// 1. Cast the data to the correct struct
|
||||
wifi_event_sta_disconnected_t* event = (wifi_event_sta_disconnected_t*) event_data;
|
||||
|
||||
printf("WiFi Disconnected. Reason Code: %d\n", event->reason);
|
||||
|
||||
// 2. Check specific Reason Codes
|
||||
switch (event->reason) {
|
||||
case WIFI_REASON_AUTH_EXPIRE: // Reason 2
|
||||
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: // Reason 15 (Most Common for Wrong Pass)
|
||||
case WIFI_REASON_BEACON_TIMEOUT: // Reason 200
|
||||
case WIFI_REASON_AUTH_FAIL: // Reason 202
|
||||
case WIFI_REASON_HANDSHAKE_TIMEOUT: // Reason 204
|
||||
printf("ERROR: Likely Wrong Password!\n");
|
||||
authFailed = true;
|
||||
break;
|
||||
|
||||
case WIFI_REASON_NO_AP_FOUND: // Reason 201
|
||||
printf("ERROR: SSID Not Found\n");
|
||||
authFailed = true;
|
||||
break;
|
||||
|
||||
case WIFI_REASON_ASSOC_LEAVE: // Reason 8 - Manual disconnect
|
||||
printf("Manual disconnect, not retrying\n");
|
||||
break;
|
||||
|
||||
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_wifi_connect();
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Retrying...\n");
|
||||
esp_wifi_connect();
|
||||
break;
|
||||
}
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) {
|
||||
// This is triggered when the scan finishes!
|
||||
printf("Scan complete, processing results...\n");
|
||||
|
||||
// Call a function to process results and notify BLE
|
||||
processScanResults();
|
||||
}
|
||||
// 3. We got an IP Address -> Success!
|
||||
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));
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void WiFi::init() {
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
// 1. Init Network Interface
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
netif = esp_netif_create_default_wifi_sta();
|
||||
|
||||
// 2. Init WiFi Driver
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
// 3. Set Mode to Station (Client)
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||||
WIFI_EVENT, // The "Topic" (Base)
|
||||
ESP_EVENT_ANY_ID, // The specific "Subject" (Any ID)
|
||||
&event_handler, // The Function to call
|
||||
NULL, // Argument to pass to the function (optional)
|
||||
&instance_any_id // Where to store the registration handle
|
||||
));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||||
IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_got_ip
|
||||
));
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
xEventGroupWaitBits(s_wifi_event_group, WIFI_STARTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
}
|
||||
|
||||
// --- CHECK STATUS ---
|
||||
bool WiFi::isConnected() {
|
||||
if (s_wifi_event_group == NULL) return false;
|
||||
EventBits_t bits = xEventGroupGetBits(s_wifi_event_group);
|
||||
return (bits & WIFI_CONNECTED_BIT);
|
||||
}
|
||||
|
||||
// --- GET IP AS STRING ---
|
||||
std::string WiFi::getIP() {
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_get_ip_info(netif, &ip_info);
|
||||
char buf[20];
|
||||
sprintf(buf, IPSTR, IP2STR(&ip_info.ip));
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
bool WiFi::attemptConnect(const std::string ssid, const std::string password,
|
||||
const wifi_auth_mode_t authMode) {
|
||||
esp_wifi_sta_enterprise_disable();
|
||||
esp_wifi_disconnect();
|
||||
|
||||
wifi_config_t wifi_config = {};
|
||||
snprintf((char*)wifi_config.sta.ssid, sizeof(wifi_config.sta.ssid), "%s", ssid.c_str());
|
||||
snprintf((char*)wifi_config.sta.password, sizeof(wifi_config.sta.password), "%s", password.c_str());
|
||||
|
||||
wifi_config.sta.threshold.authmode = authMode;
|
||||
wifi_config.sta.pmf_cfg.capable = true;
|
||||
wifi_config.sta.pmf_cfg.required = false;
|
||||
|
||||
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
|
||||
return awaitConnected();
|
||||
}
|
||||
|
||||
bool WiFi::attemptConnect(const std::string ssid, const std::string uname,
|
||||
const std::string password, const wifi_auth_mode_t authMode) {
|
||||
esp_wifi_disconnect();
|
||||
|
||||
// 1. Auto-generate the Identity
|
||||
std::string identity = "anonymous";
|
||||
size_t atPos = uname.find('@');
|
||||
if (atPos != std::string::npos) identity += uname.substr(atPos);
|
||||
|
||||
printf("Real User: %s\n", uname.c_str());
|
||||
printf("Outer ID : %s (Privacy Safe)\n", identity.c_str());
|
||||
|
||||
wifi_config_t wifi_config = {};
|
||||
snprintf((char*)wifi_config.sta.ssid, sizeof(wifi_config.sta.ssid), "%s", ssid.c_str());
|
||||
wifi_config.sta.threshold.authmode = authMode;
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
|
||||
// 3. Set the calculated identity (using new ESP-IDF v5.x API)
|
||||
esp_wifi_sta_enterprise_enable();
|
||||
esp_eap_client_set_identity((uint8_t *)identity.c_str(), identity.length());
|
||||
esp_eap_client_set_username((uint8_t *)uname.c_str(), uname.length());
|
||||
esp_eap_client_set_password((uint8_t *)password.c_str(), password.length());
|
||||
|
||||
return awaitConnected();
|
||||
}
|
||||
|
||||
bool WiFi::awaitConnected() {
|
||||
authFailed = false;
|
||||
if (esp_wifi_connect() != ESP_OK) return false;
|
||||
|
||||
uint8_t attempts = 0;
|
||||
while (!isConnected() && attempts < 20) {
|
||||
if (authFailed) {
|
||||
printf("SSID/Password was wrong! Aborting connection attempt.\n");
|
||||
return false;
|
||||
}
|
||||
vTaskDelay(500);
|
||||
attempts++;
|
||||
}
|
||||
if (isConnected()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------- non-class --------------
|
||||
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();
|
||||
|
||||
wifi_scan_config_t scan_config = {
|
||||
.ssid = NULL,
|
||||
.bssid = NULL,
|
||||
.channel = 0,
|
||||
.show_hidden = false
|
||||
};
|
||||
esp_err_t err = esp_wifi_scan_start(&scan_config, false);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
printf("Scan failed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void WiFi::processScanResults() {
|
||||
// 2. Get the results
|
||||
uint16_t ap_count = 0;
|
||||
esp_wifi_scan_get_ap_num(&ap_count);
|
||||
|
||||
// Limit to 10 networks to save RAM/BLE MTU space
|
||||
if (ap_count > 10) ap_count = 10;
|
||||
|
||||
wifi_ap_record_t *ap_list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * ap_count);
|
||||
if (ap_list == NULL) {
|
||||
printf("Heap allocation error in processScanResults\n");
|
||||
return;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_list));
|
||||
|
||||
// 3. Build JSON using cJSON
|
||||
cJSON *root = cJSON_CreateArray();
|
||||
|
||||
for (int i = 0; i < ap_count; i++) {
|
||||
cJSON *item = cJSON_CreateObject();
|
||||
// ESP-IDF stores SSID as uint8_t, cast to char*
|
||||
cJSON_AddStringToObject(item, "ssid", (char *)ap_list[i].ssid);
|
||||
cJSON_AddNumberToObject(item, "rssi", ap_list[i].rssi);
|
||||
// Add encryption type if you want (optional)
|
||||
cJSON_AddNumberToObject(item, "auth", ap_list[i].authmode);
|
||||
|
||||
cJSON_AddItemToArray(root, item);
|
||||
}
|
||||
|
||||
// 4. Convert to String
|
||||
char *json_string = cJSON_PrintUnformatted(root); // Compact JSON
|
||||
printf("JSON: %s\n", json_string);
|
||||
|
||||
// 5. Update BLE
|
||||
if (ssidListChar != nullptr) {
|
||||
ssidListChar.load()->setValue(std::string(json_string));
|
||||
NimBLECharacteristic *tmpRefreshChar = ssidRefreshChar.load();
|
||||
tmpRefreshChar->setValue("Ready");
|
||||
tmpRefreshChar->notify();
|
||||
}
|
||||
|
||||
// 6. Cleanup Memory
|
||||
free(ap_list);
|
||||
cJSON_Delete(root); // This deletes all children (items) too
|
||||
free(json_string); // cJSON_Print allocates memory, you must free it
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "bmHTTP.hpp"
|
||||
#include "esp_http_client.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "defines.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
std::string webToken;
|
||||
const std::string urlBase = std::string("http") + (secureSrv ? "s" : "") + "://" + srvAddr + "/";
|
||||
|
||||
esp_err_t _http_event_handler(esp_http_client_event_t *evt) {
|
||||
switch(evt->event_id) {
|
||||
case HTTP_EVENT_ON_DATA: {
|
||||
// Append received data to buffer (handles both chunked and non-chunked)
|
||||
if (evt->data_len > 0 && evt->user_data != NULL) {
|
||||
std::string* rxBuffer = (std::string*)evt->user_data;
|
||||
rxBuffer->append((char*)evt->data, evt->data_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool httpGET(std::string endpoint, std::string token, cJSON* &JSONresponse) {
|
||||
std::string url = urlBase + endpoint;
|
||||
std::string responseBuffer = "";
|
||||
|
||||
esp_http_client_config_t config = {};
|
||||
config.url = url.c_str();
|
||||
config.event_handler = _http_event_handler; // Attach the bucket
|
||||
config.user_data = &responseBuffer; // Pass pointer to our string so the handler can write to it
|
||||
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);
|
||||
|
||||
// Add authorization header
|
||||
std::string authHeader = "Bearer " + token;
|
||||
esp_http_client_set_header(client, "Authorization", authHeader.c_str());
|
||||
|
||||
// Open connection and fetch headers
|
||||
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) {
|
||||
printf("Response: %s\n", responseBuffer.c_str());
|
||||
JSONresponse = cJSON_Parse(responseBuffer.c_str());
|
||||
if (JSONresponse) success = true;
|
||||
}
|
||||
} else {
|
||||
printf("HTTP GET failed: %s\n", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
esp_http_client_cleanup(client);
|
||||
return success;
|
||||
}
|
||||
|
||||
void deleteWiFiAndTokenDetails() {
|
||||
nvs_handle_t wifiHandle;
|
||||
if (nvs_open(nvsWiFi, NVS_READWRITE, &wifiHandle) == ESP_OK) {
|
||||
if (nvs_erase_all(wifiHandle) == ESP_OK) {
|
||||
printf("Successfully erased WiFi details\n");
|
||||
nvs_commit(wifiHandle);
|
||||
}
|
||||
else printf("ERROR: Erase wifi failed\n");
|
||||
nvs_close(wifiHandle);
|
||||
}
|
||||
else printf("ERROR: Failed to open WiFi section for deletion\n");
|
||||
|
||||
nvs_handle_t authHandle;
|
||||
if (nvs_open(nvsAuth, NVS_READWRITE, &authHandle) == ESP_OK) {
|
||||
if (nvs_erase_all(authHandle) == ESP_OK) {
|
||||
printf("Successfully erased Auth details\n");
|
||||
nvs_commit(authHandle);
|
||||
}
|
||||
else printf("ERROR: Erase auth failed\n");
|
||||
nvs_close(authHandle);
|
||||
}
|
||||
else printf("ERROR: Failed to open Auth section for deletion\n");
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
#include "calibration.hpp"
|
||||
#include "defines.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
void Calibration::init() {
|
||||
nvs_handle_t calibHandle;
|
||||
if (nvs_open(nvsCalib, NVS_READONLY, &calibHandle) == ESP_OK) {
|
||||
int32_t tempUpTicks;
|
||||
int32_t tempDownTicks;
|
||||
uint8_t tempCalib;
|
||||
esp_err_t err = ESP_OK;
|
||||
err |= nvs_get_i32(calibHandle, UpTicksTag, &tempUpTicks);
|
||||
err |= nvs_get_i32(calibHandle, DownTicksTag, &tempDownTicks);
|
||||
err |= nvs_get_u8(calibHandle, statusTag, &tempCalib);
|
||||
if (err == ESP_OK) {
|
||||
UpTicks = tempUpTicks;
|
||||
DownTicks = tempDownTicks;
|
||||
calibrated = tempCalib;
|
||||
printf("Range: %d - %d\n", tempUpTicks, tempDownTicks);
|
||||
}
|
||||
else {
|
||||
printf("Data missing from NVS\n");
|
||||
calibrated = false;
|
||||
}
|
||||
nvs_close(calibHandle);
|
||||
}
|
||||
else {
|
||||
printf("CALIBINIT: failed to open NVS - not created?\n");
|
||||
calibrated = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Calibration::clearCalibrated() {
|
||||
if (!calibrated) return true;
|
||||
// clear variable and NVS
|
||||
calibrated = false;
|
||||
nvs_handle_t calibHandle;
|
||||
if (nvs_open(nvsCalib, NVS_READWRITE, &calibHandle) == ESP_OK) {
|
||||
if (nvs_set_u8(calibHandle, statusTag, false) != ESP_OK) {
|
||||
printf("Error saving calibration status as false.\n");
|
||||
return false;
|
||||
}
|
||||
nvs_commit(calibHandle);
|
||||
nvs_close(calibHandle);
|
||||
}
|
||||
else {
|
||||
printf("Error opening calibration NVS segment.\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Calibration::beginDownwardCalib(Encoder& topEnc) {
|
||||
int32_t tempUpTicks = topEnc.getCount();
|
||||
nvs_handle_t calibHandle;
|
||||
if (nvs_open(nvsCalib, NVS_READWRITE, &calibHandle) == ESP_OK) {
|
||||
if (nvs_set_i32(calibHandle, UpTicksTag, tempUpTicks) == ESP_OK) {
|
||||
printf("Saved UpTicks to NVS\n");
|
||||
UpTicks = tempUpTicks;
|
||||
nvs_commit(calibHandle);
|
||||
}
|
||||
else {
|
||||
printf("Error saving UpTicks.\n");
|
||||
return false;
|
||||
}
|
||||
nvs_close(calibHandle);
|
||||
}
|
||||
else {
|
||||
printf("Error opening NVS to save UpTicks\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Calibration::completeCalib(Encoder& topEnc) {
|
||||
int32_t tempDownTicks = topEnc.getCount();
|
||||
if (tempDownTicks == UpTicks) {
|
||||
printf("ERROR: NO RANGE\n");
|
||||
return false;
|
||||
}
|
||||
nvs_handle_t calibHandle;
|
||||
if (nvs_open(nvsCalib, NVS_READWRITE, &calibHandle) == ESP_OK) {
|
||||
esp_err_t err = ESP_OK;
|
||||
err |= nvs_set_i32(calibHandle, DownTicksTag, tempDownTicks);
|
||||
err |= nvs_set_u8(calibHandle, statusTag, true);
|
||||
if (err != ESP_OK) {
|
||||
printf("Error saving calibration data.\n");
|
||||
return false;
|
||||
}
|
||||
DownTicks = tempDownTicks;
|
||||
calibrated = true;
|
||||
printf("Range: %d - %d\n", UpTicks.load(), tempDownTicks);
|
||||
nvs_commit(calibHandle);
|
||||
nvs_close(calibHandle);
|
||||
}
|
||||
else {
|
||||
printf("Error opening calibration NVS segment.\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t Calibration::convertToTicks(uint8_t appPos) {
|
||||
// appPos between 0 and 10, convert to target encoder ticks.
|
||||
return (((int32_t)appPos * (UpTicks - DownTicks)) / 10) + DownTicks;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
#include "encoder.hpp"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/gpio_struct.h"
|
||||
#include "servo.hpp"
|
||||
|
||||
static const char *TAG = "ENCODER";
|
||||
|
||||
// Constructor
|
||||
Encoder::Encoder(gpio_num_t pinA, gpio_num_t pinB)
|
||||
: pin_a(pinA), pin_b(pinB), count(0),
|
||||
last_state_a(0), last_state_b(0), last_count_base(0),
|
||||
watchdog_handle(nullptr) {}
|
||||
|
||||
// Static ISR - receives Encoder instance via arg
|
||||
void IRAM_ATTR Encoder::isr_handler(void* arg)
|
||||
{
|
||||
Encoder* encoder = static_cast<Encoder*>(arg);
|
||||
|
||||
// Read GPIO levels directly from hardware
|
||||
uint32_t gpio_levels = GPIO.in.val;
|
||||
uint8_t current_a = (gpio_levels >> encoder->pin_a) & 0x1;
|
||||
uint8_t current_b = (gpio_levels >> encoder->pin_b) & 0x1;
|
||||
|
||||
// Quadrature decoding logic
|
||||
if (current_a != encoder->last_state_a) {
|
||||
if (!current_a) {
|
||||
if (current_b) encoder->last_count_base++;
|
||||
else encoder->last_count_base--;
|
||||
}
|
||||
else {
|
||||
if (current_b) encoder->last_count_base--;
|
||||
else encoder->last_count_base++;
|
||||
}
|
||||
}
|
||||
else if (current_b != encoder->last_state_b) {
|
||||
if (!current_b) {
|
||||
if (current_a) encoder->last_count_base--;
|
||||
else encoder->last_count_base++;
|
||||
}
|
||||
else {
|
||||
if (current_a) encoder->last_count_base++;
|
||||
else encoder->last_count_base--;
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate to full detent count
|
||||
if (encoder->last_count_base > 3) {
|
||||
encoder->count += 1;
|
||||
encoder->last_count_base -= 4;
|
||||
if (calibListen) servoCalibListen();
|
||||
if (encoder->feedWDog) {
|
||||
esp_timer_stop(encoder->watchdog_handle);
|
||||
esp_timer_start_once(encoder->watchdog_handle, 500000);
|
||||
debugLEDTgl();
|
||||
}
|
||||
if (encoder->wandListen) servoWandListen();
|
||||
if (encoder->serverListen) servoServerListen();
|
||||
}
|
||||
else if (encoder->last_count_base < 0) {
|
||||
encoder->count -= 1;
|
||||
encoder->last_count_base += 4;
|
||||
if (calibListen) servoCalibListen();
|
||||
if (encoder->feedWDog) {
|
||||
esp_timer_stop(encoder->watchdog_handle);
|
||||
esp_timer_start_once(encoder->watchdog_handle, 500000);
|
||||
debugLEDTgl();
|
||||
}
|
||||
if (encoder->wandListen) servoWandListen();
|
||||
if (encoder->serverListen) servoServerListen();
|
||||
}
|
||||
|
||||
encoder->last_state_a = current_a;
|
||||
encoder->last_state_b = current_b;
|
||||
}
|
||||
|
||||
void Encoder::init()
|
||||
{
|
||||
gpio_config_t io_conf = {};
|
||||
io_conf.intr_type = GPIO_INTR_ANYEDGE;
|
||||
io_conf.pin_bit_mask = (1ULL << pin_a) | (1ULL << pin_b);
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
|
||||
// Install ISR service if not already installed
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1);
|
||||
|
||||
// Attach ISR with THIS instance as argument
|
||||
gpio_isr_handler_add(pin_a, Encoder::isr_handler, this);
|
||||
gpio_isr_handler_add(pin_b, Encoder::isr_handler, this);
|
||||
|
||||
ESP_LOGI(TAG, "Encoder initialized on pins %d and %d", pin_a, pin_b);
|
||||
}
|
||||
|
||||
void Encoder::deinit()
|
||||
{
|
||||
gpio_isr_handler_remove(pin_a);
|
||||
gpio_isr_handler_remove(pin_b);
|
||||
ESP_LOGI(TAG, "Encoder deinitialized");
|
||||
}
|
||||
|
||||
void Encoder::setupWatchdog() {
|
||||
if (watchdog_handle == NULL) {
|
||||
const esp_timer_create_args_t enc_watchdog_args = {
|
||||
.callback = &watchdogCallback,
|
||||
.dispatch_method = ESP_TIMER_ISR,
|
||||
.name = "encoder_wdt",
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_create(&enc_watchdog_args, &watchdog_handle));
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(watchdog_handle, 500000));
|
||||
feedWDog = true;
|
||||
}
|
||||
|
||||
void IRAM_ATTR Encoder::pauseWatchdog() {
|
||||
if (watchdog_handle != nullptr) esp_timer_stop(watchdog_handle);
|
||||
feedWDog = false;
|
||||
}
|
||||
|
||||
Encoder::~Encoder() {
|
||||
if (watchdog_handle != NULL) {
|
||||
esp_timer_stop(watchdog_handle);
|
||||
esp_timer_delete(watchdog_handle);
|
||||
watchdog_handle = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
#include "servo.hpp"
|
||||
#include "driver/ledc.h"
|
||||
#include "defines.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include "esp_log.h"
|
||||
#include "socketIO.hpp"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
std::atomic<bool> calibListen{false};
|
||||
std::atomic<int32_t> baseDiff{0};
|
||||
std::atomic<int32_t> target{0};
|
||||
|
||||
std::atomic<bool> runningManual{false};
|
||||
std::atomic<bool> runningServer{false};
|
||||
std::atomic<bool> clearCalibFlag{false};
|
||||
std::atomic<bool> savePosFlag{false};
|
||||
std::atomic<bool> startLess{false};
|
||||
|
||||
void servoInit() {
|
||||
// LEDC timer configuration (C++ aggregate initialization)
|
||||
ledc_timer_config_t ledc_timer = {};
|
||||
ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE;
|
||||
ledc_timer.timer_num = LEDC_TIMER_0;
|
||||
ledc_timer.duty_resolution = LEDC_TIMER_16_BIT;
|
||||
ledc_timer.freq_hz = 50;
|
||||
ledc_timer.clk_cfg = LEDC_AUTO_CLK;
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
// LEDC channel configuration
|
||||
ledc_channel_config_t ledc_channel = {};
|
||||
ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE;
|
||||
ledc_channel.channel = servoLEDCChannel;
|
||||
ledc_channel.timer_sel = LEDC_TIMER_0;
|
||||
ledc_channel.intr_type = LEDC_INTR_DISABLE;
|
||||
ledc_channel.gpio_num = servoPin;
|
||||
ledc_channel.duty = offSpeed; // Start off
|
||||
ledc_channel.hpoint = 0;
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
|
||||
// Configure servo power switch pin as output
|
||||
gpio_set_direction(servoSwitch, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(servoSwitch, 0); // Start with servo power off
|
||||
|
||||
// Configure debug LED pin as output
|
||||
gpio_reset_pin(GPIO_NUM_22);
|
||||
gpio_set_direction(debugLED, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(debugLED, 0); // Start with LED off
|
||||
|
||||
topEnc->count = servoReadPos();
|
||||
if (calib.getCalibrated()) initMainLoop();
|
||||
debugLEDSwitch(1);
|
||||
}
|
||||
|
||||
void servoOn(uint8_t dir, uint8_t manOrServer) {
|
||||
servoMainSwitch(1);
|
||||
ledc_set_duty(LEDC_LOW_SPEED_MODE, servoLEDCChannel, (dir ? ccwSpeed : cwSpeed));
|
||||
ledc_update_duty(LEDC_LOW_SPEED_MODE, servoLEDCChannel);
|
||||
runningManual = !manOrServer;
|
||||
runningServer = manOrServer;
|
||||
}
|
||||
|
||||
void servoOff() {
|
||||
ledc_set_duty(LEDC_LOW_SPEED_MODE, servoLEDCChannel, offSpeed);
|
||||
ledc_update_duty(LEDC_LOW_SPEED_MODE, servoLEDCChannel);
|
||||
runningManual = false;
|
||||
runningServer = false;
|
||||
servoMainSwitch(0);
|
||||
}
|
||||
|
||||
void servoMainSwitch(uint8_t onOff) {
|
||||
gpio_set_level(servoSwitch, onOff ? 1 : 0);
|
||||
}
|
||||
|
||||
void debugLEDSwitch(uint8_t onOff) {
|
||||
gpio_set_level(debugLED, onOff ? 1 : 0);
|
||||
}
|
||||
|
||||
void debugLEDTgl() {
|
||||
static bool onOff = false;
|
||||
gpio_set_level(debugLED, onOff);
|
||||
onOff = !onOff;
|
||||
}
|
||||
|
||||
bool servoInitCalib() {
|
||||
topEnc->pauseWatchdog();
|
||||
// get ready for calibration by clearing all these listeners
|
||||
bottomEnc->wandListen.store(false, std::memory_order_release);
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
topEnc->serverListen.store(false, std::memory_order_release);
|
||||
if (!calib.clearCalibrated()) return false;
|
||||
if (topEnc == nullptr || bottomEnc == nullptr) {
|
||||
printf("ERROR: CALIBRATION STARTED BEFORE SERVO INITIALIZATION\n");
|
||||
return false;
|
||||
}
|
||||
baseDiff = bottomEnc->getCount() - topEnc->getCount();
|
||||
calibListen = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void servoCancelCalib() {
|
||||
calibListen = false;
|
||||
servoOff();
|
||||
}
|
||||
|
||||
void servoCalibListen() {
|
||||
int32_t effDiff = (bottomEnc->getCount() - topEnc->getCount()) - baseDiff;
|
||||
if (effDiff > 1) servoOn(CCW, manual);
|
||||
else if (effDiff < -1) {
|
||||
servoOn(CW, manual);
|
||||
}
|
||||
else {
|
||||
servoOff();
|
||||
}
|
||||
}
|
||||
|
||||
bool servoBeginDownwardCalib() {
|
||||
calibListen = false;
|
||||
servoOff();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
if (!calib.beginDownwardCalib(*topEnc)) return false;
|
||||
baseDiff = bottomEnc->getCount() - topEnc->getCount();
|
||||
calibListen = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool servoCompleteCalib() {
|
||||
calibListen = false;
|
||||
servoOff();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
if (!calib.completeCalib(*topEnc)) return false;
|
||||
initMainLoop();
|
||||
return true;
|
||||
}
|
||||
|
||||
void initMainLoop() {
|
||||
topEnc->setupWatchdog();
|
||||
servoSavePos();
|
||||
bottomEnc->wandListen.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
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;
|
||||
topEnc->pauseWatchdog();
|
||||
|
||||
// get ready for recalibration by clearing all these listeners
|
||||
bottomEnc->wandListen.store(false, std::memory_order_release);
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
topEnc->serverListen.store(false, std::memory_order_release);
|
||||
servoOff();
|
||||
}
|
||||
else {
|
||||
// if no movement is running, we're fine
|
||||
// save current servo-encoder position for reinitialization
|
||||
savePosFlag = true;
|
||||
}
|
||||
// clear running flags
|
||||
runningManual = false;
|
||||
runningServer = false;
|
||||
}
|
||||
|
||||
void servoSavePos() {
|
||||
// save current servo-encoder position for use on reinitialization
|
||||
nvs_handle_t servoHandle;
|
||||
if (nvs_open(nvsServo, NVS_READWRITE, &servoHandle) == ESP_OK) {
|
||||
int32_t topCount = topEnc->getCount();
|
||||
if (nvs_set_i32(servoHandle, posTag, topCount) != ESP_OK)
|
||||
printf("Error saving current position\n");
|
||||
else printf("Success - Current position saved as: %d\n", topCount);
|
||||
nvs_commit(servoHandle);
|
||||
nvs_close(servoHandle);
|
||||
}
|
||||
else {
|
||||
printf("Error opening servoPos NVS segment.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int32_t servoReadPos() {
|
||||
// save current servo-encoder position for use on reinitialization
|
||||
int32_t val = 0;
|
||||
nvs_handle_t servoHandle;
|
||||
if (nvs_open(nvsServo, NVS_READONLY, &servoHandle) == ESP_OK) {
|
||||
if (nvs_get_i32(servoHandle, posTag, &val) != ESP_OK)
|
||||
printf("Error reading current position\n");
|
||||
else printf("Success - Current position read as: %d\n", val);
|
||||
nvs_close(servoHandle);
|
||||
}
|
||||
else {
|
||||
printf("Error opening servoPos NVS segment.\n");
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void stopServerRun() {
|
||||
// stop listener and stop running if serverRun is still active.
|
||||
topEnc->serverListen.store(false, std::memory_order_release);
|
||||
if (runningServer) servoOff();
|
||||
}
|
||||
|
||||
void servoWandListen() {
|
||||
// stop any remote-initiated movement
|
||||
stopServerRun();
|
||||
|
||||
// freeze atomic values
|
||||
int32_t upBound = calib.UpTicks;
|
||||
int32_t downBound = calib.DownTicks;
|
||||
int32_t bottomCount = bottomEnc->getCount();
|
||||
int32_t topCount = topEnc->getCount();
|
||||
|
||||
// ensure the baseDiff doesn't wait on wand to turn all the way back to original range.
|
||||
if ((upBound > downBound && bottomCount - baseDiff > upBound)
|
||||
|| (upBound < downBound && bottomCount - baseDiff < upBound))
|
||||
baseDiff = bottomCount - upBound;
|
||||
else if ((upBound > downBound && bottomCount - baseDiff < downBound)
|
||||
|| (upBound < downBound && bottomCount - baseDiff > downBound))
|
||||
baseDiff = bottomCount - downBound;
|
||||
|
||||
// calculate the difference between wand and top servo
|
||||
int32_t effDiff = (bottomCount - topCount) - baseDiff;
|
||||
|
||||
// if we are at either bound, stop servo and servo-listener
|
||||
// if effective difference is 0, stop servo and servo-listener
|
||||
// otherwise, run servo in whichever direction necessary and
|
||||
// ensure servo-listener is active.
|
||||
if (topCount >= (MAX(upBound, downBound) - 1)
|
||||
&& effDiff > 1) { // TODO: see whether these margins need to be removed.
|
||||
servoOff();
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
}
|
||||
else if (topCount <= (MIN(upBound, downBound) + 1)
|
||||
&& effDiff < -1) {
|
||||
servoOff();
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
}
|
||||
else if (effDiff > 1) {
|
||||
topEnc->wandListen.store(true, std::memory_order_release);
|
||||
servoOn(CCW, manual);
|
||||
}
|
||||
else if (effDiff < -1) {
|
||||
topEnc->wandListen.store(true, std::memory_order_release);
|
||||
servoOn(CW, manual);
|
||||
}
|
||||
else {
|
||||
servoOff();
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
void servoServerListen() {
|
||||
// If we have reached or passed our goal, stop running and stop listener.
|
||||
if (topEnc->getCount() >= target && startLess) stopServerRun();
|
||||
else if (topEnc->getCount() <= target && !startLess) stopServerRun();
|
||||
baseDiff = bottomEnc->getCount() - topEnc->getCount();
|
||||
}
|
||||
|
||||
void runToAppPos(uint8_t appPos) {
|
||||
// manual control takes precedence over remote control, always.
|
||||
// also do not begin operation if not calibrated;
|
||||
if (runningManual || !calib.getCalibrated()) return;
|
||||
servoOff();
|
||||
|
||||
target = calib.convertToTicks(appPos); // calculate target encoder position
|
||||
printf("runToAppPos Called, running to %d from %d", target.load(), topEnc->getCount());
|
||||
|
||||
// allow servo position to settle
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
int32_t topCount = topEnc->getCount();
|
||||
if (abs(topCount - target) <= 1) return;
|
||||
startLess = topCount < target;
|
||||
if (runningManual) return; // check again before starting remote control
|
||||
if (startLess) servoOn(CCW, server); // begin servo movement
|
||||
else servoOn(CW, server);
|
||||
topEnc->serverListen.store(true, std::memory_order_release); // start listening for shutoff point
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
#include "setup.hpp"
|
||||
#include "BLE.hpp"
|
||||
#include "WiFi.hpp"
|
||||
#include "nvs_flash.h"
|
||||
#include "defines.h"
|
||||
#include "bmHTTP.hpp"
|
||||
#include "socketIO.hpp"
|
||||
|
||||
void initialSetup() {
|
||||
printf("Entered Setup\n");
|
||||
NimBLEAdvertising* pAdv = initBLE();
|
||||
|
||||
while (!BLEtick(pAdv)) {
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,408 +0,0 @@
|
||||
#include "socketIO.hpp"
|
||||
#include "esp_socketio_client.h"
|
||||
#include "bmHTTP.hpp" // To access webToken
|
||||
#include "WiFi.hpp"
|
||||
#include "setup.hpp"
|
||||
#include "cJSON.h"
|
||||
#include "calibration.hpp"
|
||||
#include "servo.hpp"
|
||||
#include "defines.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
static esp_socketio_client_handle_t io_client;
|
||||
static esp_socketio_packet_handle_t tx_packet = NULL;
|
||||
|
||||
std::atomic<bool> statusResolved{true};
|
||||
std::atomic<bool> connected{false};
|
||||
|
||||
// Event handler for Socket.IO events
|
||||
static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
int32_t event_id, void *event_data) {
|
||||
esp_socketio_event_data_t *data = (esp_socketio_event_data_t *)event_data;
|
||||
esp_socketio_packet_handle_t packet = data->socketio_packet;
|
||||
|
||||
switch (event_id) {
|
||||
case SOCKETIO_EVENT_OPENED:
|
||||
printf("Socket.IO Received OPEN packet\n");
|
||||
// Connect to default namespace "/"
|
||||
esp_socketio_client_connect_nsp(data->client, NULL, NULL);
|
||||
break;
|
||||
|
||||
case SOCKETIO_EVENT_NS_CONNECTED: {
|
||||
printf("Socket.IO Connected to namespace!\n");
|
||||
// Check if connected to default namespace
|
||||
char *nsp = esp_socketio_packet_get_nsp(packet);
|
||||
if (strcmp(nsp, "/") == 0) {
|
||||
printf("Connected to default namespace - waiting for device_init...\n");
|
||||
}
|
||||
// Don't set connected yet - wait for device_init message from server
|
||||
break;
|
||||
}
|
||||
|
||||
case SOCKETIO_EVENT_DATA: {
|
||||
printf("Received Socket.IO data\n");
|
||||
// Parse the received packet
|
||||
cJSON *json = esp_socketio_packet_get_json(packet);
|
||||
if (json) {
|
||||
char *json_str = cJSON_Print(json);
|
||||
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;
|
||||
// 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 = calib.getCalibrated();
|
||||
emitCalibStatus(deviceCalibrated);
|
||||
printf(" Reported calibrated=%d for port %d\n", deviceCalibrated, port);
|
||||
runToAppPos(lastPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now mark as connected
|
||||
connected = true;
|
||||
statusResolved = true;
|
||||
} else {
|
||||
printf("Device authentication failed\n");
|
||||
calib.clearCalibrated();
|
||||
deleteWiFiAndTokenDetails();
|
||||
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);
|
||||
}
|
||||
}
|
||||
calib.clearCalibrated();
|
||||
deleteWiFiAndTokenDetails();
|
||||
connected = false;
|
||||
statusResolved = true;
|
||||
}
|
||||
|
||||
// Handle calib_start event
|
||||
else if (strcmp(eventName->valuestring, "calib_start") == 0) {
|
||||
printf("Device calibration begun, setting up...\n");
|
||||
cJSON *data = cJSON_GetArrayItem(json, 1);
|
||||
if (data) {
|
||||
cJSON *port = cJSON_GetObjectItem(data, "port");
|
||||
if (port && cJSON_IsNumber(port)) {
|
||||
if (port->valueint != 1) {
|
||||
printf("Error, non-1 port received for calibration\n");
|
||||
emitCalibError("Non-1 Port");
|
||||
}
|
||||
else {
|
||||
printf("Running initCalib...\n");
|
||||
if (!servoInitCalib()) {
|
||||
printf("initCalib returned False\n");
|
||||
emitCalibError("Initialization failed");
|
||||
}
|
||||
else {
|
||||
printf("Ready to calibrate\n");
|
||||
emitCalibStage1Ready();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle user_stage1_complete event
|
||||
else if (strcmp(eventName->valuestring, "user_stage1_complete") == 0) {
|
||||
printf("User completed stage 1 (tilt up), switching direction...\n");
|
||||
cJSON *data = cJSON_GetArrayItem(json, 1);
|
||||
if (data) {
|
||||
cJSON *port = cJSON_GetObjectItem(data, "port");
|
||||
if (port && cJSON_IsNumber(port)) {
|
||||
if (port->valueint != 1) {
|
||||
printf("Error, non-1 port received for calibration\n");
|
||||
emitCalibError("Non-1 Port");
|
||||
}
|
||||
else {
|
||||
if (!servoBeginDownwardCalib())
|
||||
emitCalibError("Direction Switch Failed");
|
||||
else emitCalibStage2Ready();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle user_stage2_complete event
|
||||
else if (strcmp(eventName->valuestring, "user_stage2_complete") == 0) {
|
||||
printf("User completed stage 2 (tilt down), finalizing calibration...\n");
|
||||
cJSON *data = cJSON_GetArrayItem(json, 1);
|
||||
if (data) {
|
||||
cJSON *port = cJSON_GetObjectItem(data, "port");
|
||||
if (port && cJSON_IsNumber(port)) {
|
||||
if (port->valueint != 1) {
|
||||
printf("Error, non-1 port received for calibration\n");
|
||||
emitCalibError("Non-1 port");
|
||||
}
|
||||
else {
|
||||
if (!servoCompleteCalib()) emitCalibError("Completion failed");
|
||||
else emitCalibDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle user_stage1_complete event
|
||||
else if (strcmp(eventName->valuestring, "cancel_calib") == 0) {
|
||||
printf("Canceling calibration process...\n");
|
||||
cJSON *data = cJSON_GetArrayItem(json, 1);
|
||||
if (data) {
|
||||
cJSON *port = cJSON_GetObjectItem(data, "port");
|
||||
if (port && cJSON_IsNumber(port)) {
|
||||
if (port->valueint != 1) {
|
||||
printf("Error, non-1 port received for calibration\n");
|
||||
emitCalibError("Non-1 Port");
|
||||
}
|
||||
else {
|
||||
servoCancelCalib();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(json_str);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SOCKETIO_EVENT_ERROR: {
|
||||
printf("Socket.IO Error!\n");
|
||||
servoCancelCalib();
|
||||
esp_websocket_event_data_t *ws_event = data->websocket_event;
|
||||
|
||||
if (ws_event) {
|
||||
// 1. Check for TLS/SSL specific errors (Certificate issues)
|
||||
if (ws_event->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
|
||||
// This prints the "MbedTLS" error code (The low-level crypto library)
|
||||
// Common codes: -0x2700 (CRT verify failed), -0x7200 (SSL handshake failed)
|
||||
if (ws_event->error_handle.esp_tls_stack_err != 0) {
|
||||
printf("TLS/SSL Stack Error: -0x%x\n", -ws_event->error_handle.esp_tls_stack_err);
|
||||
}
|
||||
|
||||
// 2. Check the Certificate Verification Flags
|
||||
// If this is non-zero, the certificate was rejected.
|
||||
if (ws_event->error_handle.esp_tls_cert_verify_flags != 0) {
|
||||
uint32_t flags = ws_event->error_handle.esp_tls_cert_verify_flags;
|
||||
printf("Certificate Verification FAILED. Flags: 0x%lx\n", flags);
|
||||
|
||||
// Simple decoder for common flags:
|
||||
if (flags & (1 << 0)) printf(" - CRT_NOT_TRUSTED (Root CA not found in bundle)\n");
|
||||
if (flags & (1 << 1)) printf(" - CRT_BAD_KEY_USAGE\n");
|
||||
if (flags & (1 << 2)) printf(" - CRT_EXPIRED (Check your ESP32 system time!)\n");
|
||||
if (flags & (1 << 3)) printf(" - CRT_CN_MISMATCH (Domain name doesn't match cert)\n");
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Check for HTTP Handshake errors (401/403/404)
|
||||
// This happens if SSL worked, but the server rejected your path or token
|
||||
else if (ws_event->error_handle.error_type == WEBSOCKET_ERROR_TYPE_HANDSHAKE) {
|
||||
int status = ws_event->error_handle.esp_ws_handshake_status_code;
|
||||
printf("HTTP Handshake Error: %d\n", status);
|
||||
|
||||
if (status == 401 || status == 403) {
|
||||
printf("Authentication failed - invalid token\n");
|
||||
} else if (status == 404) {
|
||||
printf("404 Not Found - Check your URI/Path\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connected = false;
|
||||
statusResolved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle WebSocket-level disconnections
|
||||
if (data->websocket_event_id == WEBSOCKET_EVENT_DISCONNECTED) {
|
||||
printf("WebSocket disconnected\n");
|
||||
connected = false;
|
||||
statusResolved = true;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string uriString = std::string("ws") + (secureSrv ? "s" : "") + "://" + srvAddr + "/socket.io/?EIO=4&transport=websocket";
|
||||
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 = {};
|
||||
config.websocket_config.uri = uriString.c_str();
|
||||
config.websocket_config.headers = authHeader.c_str();
|
||||
|
||||
if (secureSrv) {
|
||||
config.websocket_config.transport = WEBSOCKET_TRANSPORT_OVER_SSL;
|
||||
config.websocket_config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
}
|
||||
|
||||
io_client = esp_socketio_client_init(&config);
|
||||
tx_packet = esp_socketio_client_get_tx_packet(io_client);
|
||||
|
||||
esp_socketio_register_events(io_client, SOCKETIO_EVENT_ANY, socketio_event_handler, NULL);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to emit Socket.IO event with data
|
||||
static void emitSocketEvent(const char* eventName, cJSON* data) {
|
||||
if (esp_socketio_packet_set_header(tx_packet, EIO_PACKET_TYPE_MESSAGE,
|
||||
SIO_PACKET_TYPE_EVENT, NULL, -1) == ESP_OK) {
|
||||
cJSON *array = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(array, cJSON_CreateString(eventName));
|
||||
cJSON_AddItemToArray(array, data);
|
||||
|
||||
esp_socketio_packet_set_json(tx_packet, array);
|
||||
esp_socketio_client_send_data(io_client, tx_packet);
|
||||
esp_socketio_packet_reset(tx_packet);
|
||||
|
||||
cJSON_Delete(array);
|
||||
} else {
|
||||
// If packet header setup failed, clean up the data object
|
||||
cJSON_Delete(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to emit 'calib_done' as expected by your server
|
||||
void emitCalibDone(int port) {
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(data, "port", port);
|
||||
emitSocketEvent("calib_done", data);
|
||||
}
|
||||
|
||||
// Function to emit 'calib_stage1_ready' to notify server device is ready for tilt up
|
||||
void emitCalibStage1Ready(int port) {
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(data, "port", port);
|
||||
emitSocketEvent("calib_stage1_ready", data);
|
||||
}
|
||||
|
||||
// Function to emit 'calib_stage2_ready' to notify server device is ready for tilt down
|
||||
void emitCalibStage2Ready(int port) {
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(data, "port", port);
|
||||
emitSocketEvent("calib_stage2_ready", data);
|
||||
}
|
||||
|
||||
// Function to emit 'report_calib_status' to tell server device's actual calibration state
|
||||
void emitCalibStatus(bool calibrated, int port) {
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(data, "port", port);
|
||||
cJSON_AddBoolToObject(data, "calibrated", calibrated);
|
||||
emitSocketEvent("report_calib_status", data);
|
||||
}
|
||||
|
||||
// Function to emit 'device_calib_error' to notify server of calibration failure
|
||||
void emitCalibError(const char* errorMessage, int port) {
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(data, "port", port);
|
||||
cJSON_AddStringToObject(data, "message", errorMessage);
|
||||
emitSocketEvent("device_calib_error", data);
|
||||
}
|
||||
|
||||
// Function to emit 'pos_hit' to notify server of position change
|
||||
void emitPosHit(int pos, int port) {
|
||||
cJSON *data = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(data, "port", port);
|
||||
cJSON_AddNumberToObject(data, "pos", pos);
|
||||
emitSocketEvent("pos_hit", data);
|
||||
}
|
||||
Reference in New Issue
Block a user