Files
blinds_esp32/blinds_esp32.ino
2025-07-12 17:49:38 -05:00

946 lines
30 KiB
C++

#include <WiFi.h>
#include <Streaming.h>
#include <HTTPClient.h>
#include <Preferences.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
#include <driver/gptimer.h>
#define ccwSpeed 6500
#define cwSpeed 3300
#define offSpeed 4900
#define ccwMax 10
#define cwMax 0
#define getMovingCW(port) ((movingCW & (1 << port)) >> port)
#define setMovingCW(port) (movingCW |= (1 << port))
#define clearMovingCW(port) (movingCW &= ~(1 << port))
#define getMovingCCW(port) ((movingCCW & (1 << port)) >> port)
#define setMovingCCW(port) (movingCCW |= (1 << port))
#define clearMovingCCW(port) (movingCCW &= ~(1 << port))
#define getCalibCW(port) ((calibCW & (1 << port)) >> port)
#define setCalibCW(port) (calibCW |= (1 << port))
#define clearCalibCW(port) (calibCW &= ~(1 << port))
#define getCalibCCW(port) ((calibCCW & (1 << port)) >> port)
#define setCalibCCW(port) (calibCCW |= (1 << port))
#define clearCalibCCW(port) (calibCCW &= ~(1 << port))
#define getCalibDone(port) ((calibDone & (1 << port)) >> port)
#define setCalibDone(port) (calibDone |= (1 << port))
#define clearCalibDone(port) (calibDone &= ~(1 << port))
#define getBlocked(port) ((blocked & (1 << port)) >> port)
#define setBlocked(port) (blocked |= (1 << port))
#define clearBlocked(port) (blocked &= ~(1 << port))
#define getPos10(port) ((movingCCW & (1 << (port + 4))) >> (port + 4))
#define setPos10(port) (movingCCW |= (1 << (port + 4)))
#define clearPos10(port) (movingCCW &= ~(1 << (port + 4)))
#define getPos0(port) ((movingCW & (1 << (port + 4))) >> (port + 4))
#define setPos0(port) (movingCW |= (1 << (port + 4)))
#define clearPos0(port) (movingCW &= ~(1 << (port + 4)))
#define prefNameCalibs "periph_info_"
const uint8_t portToServo[4] = {15, 12, 13, 26};
const uint8_t portTo0Button[4] = {2, 36, 34, 14};
const uint8_t portTo1Button[4] = {4, 39, 35, 25};
volatile uint8_t calibCCW = 0;
volatile uint8_t calibCW = 0;
uint8_t targetPos[4] = {0, 0, 0, 0};
volatile uint8_t movingCCW = 0;
volatile uint8_t movingCW = 0;
volatile uint32_t cwTimes[4] = {0,0,0,0};
volatile uint32_t ccwTimes[4] = {0,0,0,0};
volatile uint32_t totalTimes[4] = {0,0,0,0};
volatile uint8_t calibDone = 0;
uint8_t blocked = 0;
gptimer_handle_t portTimers[4] = {NULL, NULL, NULL, NULL};
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 10000, // 10KHz, 1 tick = 1ms
};
portMUX_TYPE sharedData = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR portStop(uint8_t portNum) {
gptimer_stop(portTimers[portNum]);
portENTER_CRITICAL_ISR(&sharedData);
ledcWrite(portToServo[portNum], offSpeed);
clearMovingCW(portNum);
clearMovingCCW(portNum);
portEXIT_CRITICAL_ISR(&sharedData);
}
void IRAM_ATTR portStopButton(uint8_t portNum) {
portENTER_CRITICAL_ISR(&sharedData);
ledcWrite(portToServo[portNum], offSpeed);
clearMovingCW(portNum);
clearMovingCCW(portNum);
portEXIT_CRITICAL_ISR(&sharedData);
}
static IRAM_ATTR bool port0Stop(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
BaseType_t high_task_awoken = pdFALSE;
portStop(0);
return high_task_awoken == pdTRUE;
}
static IRAM_ATTR bool port1Stop(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
BaseType_t high_task_awoken = pdFALSE;
portStop(1);
return high_task_awoken == pdTRUE;
}
static IRAM_ATTR bool port2Stop(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
BaseType_t high_task_awoken = pdFALSE;
portStop(2);
return high_task_awoken == pdTRUE;
}
static IRAM_ATTR bool port3Stop(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
BaseType_t high_task_awoken = pdFALSE;
portStop(3);
return high_task_awoken == pdTRUE;
}
void IRAM_ATTR port0StopButton() {portStopButton(0);}
void IRAM_ATTR port1StopButton() {portStopButton(1);}
void IRAM_ATTR port2StopButton() {portStopButton(2);}
void IRAM_ATTR port3StopButton() {portStopButton(3);}
typedef bool (* GptimerEventCallback_t)(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data);
GptimerEventCallback_t portStops[] = {
&port0Stop,
&port1Stop,
&port2Stop,
&port3Stop
};
void (*portStopsButton[])(void) = {
&port0StopButton, // Take the address of the function
&port1StopButton,
&port2StopButton,
&port3StopButton
};
void IRAM_ATTR portSwitchCalib(uint8_t portNum) {
portENTER_CRITICAL_ISR(&sharedData);
if (!getCalibCW(portNum)) {
ledcWrite(portToServo[portNum], cwSpeed);
ESP_ERROR_CHECK(gptimer_stop(portTimers[portNum]));
uint64_t count;
ESP_ERROR_CHECK(gptimer_get_raw_count(portTimers[portNum], &count));
ccwTimes[portNum] = count;
setCalibCW(portNum);
}
portEXIT_CRITICAL_ISR(&sharedData);
}
void IRAM_ATTR port0SwitchCalib() {portSwitchCalib(0);}
void IRAM_ATTR port1SwitchCalib() {portSwitchCalib(1);}
void IRAM_ATTR port2SwitchCalib() {portSwitchCalib(2);}
void IRAM_ATTR port3SwitchCalib() {portSwitchCalib(3);}
void (*portSwitchCalibs[])(void) = {
&port0SwitchCalib, // Take the address of the function
&port1SwitchCalib,
&port2SwitchCalib,
&port3SwitchCalib
};
void IRAM_ATTR portEndCalib(uint8_t portNum) {
portENTER_CRITICAL_ISR(&sharedData);
if (!getCalibDone(portNum)) {
ledcWrite(portToServo[portNum], offSpeed);
ESP_ERROR_CHECK(gptimer_stop(portTimers[portNum]));
uint64_t count;
ESP_ERROR_CHECK(gptimer_get_raw_count(portTimers[portNum], &count));
cwTimes[portNum] = count;
setCalibDone(portNum);
}
portEXIT_CRITICAL_ISR(&sharedData);
}
void IRAM_ATTR port0EndCalib() {portEndCalib(0);}
void IRAM_ATTR port1EndCalib() {portEndCalib(1);}
void IRAM_ATTR port2EndCalib() {portEndCalib(2);}
void IRAM_ATTR port3EndCalib() {portEndCalib(3);}
void (*portEndCalibs[])(void) = {
&port0EndCalib, // Take the address of the function
&port1EndCalib,
&port2EndCalib,
&port3EndCalib
};
void IRAM_ATTR hitPos0(uint8_t portNum) {
portENTER_CRITICAL_ISR(&sharedData);
if (!getPos0(portNum)) {
ledcWrite(portToServo[portNum], offSpeed);
setPos0(portNum);
}
portEXIT_CRITICAL_ISR(&sharedData);
}
void IRAM_ATTR port0hit0() {hitPos0(0);}
void IRAM_ATTR port1hit0() {hitPos0(1);}
void IRAM_ATTR port2hit0() {hitPos0(2);}
void IRAM_ATTR port3hit0() {hitPos0(3);}
void (*portsHit0[])(void) = {
&port0hit0, // Take the address of the function
&port1hit0,
&port2hit0,
&port3hit0
};
void IRAM_ATTR hitPos10(uint8_t portNum) {
portENTER_CRITICAL_ISR(&sharedData);
if (!getPos10(portNum)) {
ledcWrite(portToServo[portNum], offSpeed);
setPos10(portNum);
}
portEXIT_CRITICAL_ISR(&sharedData);
}
void IRAM_ATTR port0hit10() {hitPos10(0);}
void IRAM_ATTR port1hit10() {hitPos10(1);}
void IRAM_ATTR port2hit10() {hitPos10(2);}
void IRAM_ATTR port3hit10() {hitPos10(3);}
void (*portsHit10[])(void) = {
&port0hit10, // Take the address of the function
&port1hit10,
&port2hit10,
&port3hit10
};
Preferences prefs;
SocketIOclient socketIO;
String ssid = "";
String password = "";
String token = "";
const String socketUrl = "/socket.io/?EIO=4";
const char* ntpServer = "pool.ntp.org";
bool ssidgiven = false;
bool passgiven = false;
bool tokengiven = false;
bool connecting = false;
bool socketIoHandshakeDone = false;
BLECharacteristic *ssidChar;
BLECharacteristic *passChar;
BLECharacteristic *ssidListChar;
BLECharacteristic *tokenChar;
BLECharacteristic *ssidRefreshChar;
BLECharacteristic *connectConfirmChar;
HTTPClient http;
bool restart = false;
const String address = "192.168.1.190";
const uint16_t port = 3000;
const String path = "/socket.io/?EIO=4&transport=websocket";
const String httpSrv = "http://192.168.1.190:3000/";
void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length);
String getEncryption(wifi_auth_mode_t type) {
switch (type) {
case WIFI_AUTH_OPEN:
return "OPEN";
case WIFI_AUTH_WEP:
case WIFI_AUTH_WPA_PSK:
case WIFI_AUTH_WPA2_PSK:
case WIFI_AUTH_WPA_WPA2_PSK:
case WIFI_AUTH_WPA3_PSK:
return "SECURED";
default:
// These include WPA2/WPA3-Enterprise and others requiring user/password or certs
return "UNSUPPORTED";
}
}
void scanAndUpdateSSIDList() {
int n = WiFi.scanNetworks();
String ssidList = "";
if (n == 0) ssidList = ";";
else for (int i = 0; i < n; ++i) {
ssidList += WiFi.SSID(i) + "," + getEncryption(WiFi.encryptionType(i));
if (i < n - 1) ssidList += ";"; // comma-delimited
}
ssidListChar->setValue(ssidList.c_str());
Serial.println("Updated SSID list: " + ssidList);
}
class MyCallbacks;
class MyServerCallbacks;
void saveWiFiCreds(const String& ssid, const String& password) {
prefs.begin("wifi"); // false = read/write
prefs.putString("ssid", ssid);
if (password != "") prefs.putString("password", password);
prefs.end();
}
void saveToken(const String& token) {
prefs.begin("auth", false);
prefs.putString("token", token);
prefs.end();
}
void saveCalibTimes(const uint8_t& portNum) {
String prefName = prefNameCalibs + String(portNum);
prefs.begin(prefName.c_str());
portENTER_CRITICAL(&sharedData);
uint32_t cwTime = cwTimes[portNum];
uint32_t ccwTime = ccwTimes[portNum];
portEXIT_CRITICAL(&sharedData);
Serial.printf("CwTime: %d", cwTime);
Serial.printf("CcwTime: %d", ccwTime);
prefs.putULong("cwTime", cwTime);
prefs.putULong("ccwTime", ccwTime);
if (!prefs.getBool("calibrated")) prefs.putBool("calibrated", true);
if (prefs.getUChar("pos") != 0) prefs.putUChar("pos", 0);
prefs.end();
detachInterrupt(portTo1Button[portNum]);
portENTER_CRITICAL(&sharedData);
clearCalibDone(portNum);
portEXIT_CRITICAL(&sharedData);
}
void beginCalib(const uint8_t& portNum) {
prefs.begin((prefNameCalibs + String(portNum)).c_str());
if (prefs.getBool("calibrated")) prefs.putBool("calibrated", false);
prefs.end();
Serial.printf("beginCalib%d\n", portNum);
ledcWrite(portToServo[portNum], ccwSpeed);
ESP_ERROR_CHECK(gptimer_disable(portTimers[portNum]));
ESP_ERROR_CHECK(gptimer_del_timer(portTimers[portNum]));
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &portTimers[portNum]));
gptimer_event_callbacks_t cbs = {
.on_alarm = portStops[portNum],
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(portTimers[portNum], &cbs, NULL));
ESP_ERROR_CHECK(gptimer_enable(portTimers[portNum]));
ESP_ERROR_CHECK(gptimer_set_raw_count(portTimers[portNum], 0));
ESP_ERROR_CHECK(gptimer_start(portTimers[portNum]));
attachInterrupt(portTo0Button[portNum], portSwitchCalibs[portNum], RISING);
portENTER_CRITICAL(&sharedData);
clearCalibCCW(portNum);
portEXIT_CRITICAL(&sharedData);
}
void switchCalib(const uint8_t& portNum) {
detachInterrupt(portTo0Button[portNum]);
ledcWrite(portToServo[portNum], cwSpeed);
ESP_ERROR_CHECK(gptimer_set_raw_count(portTimers[portNum], 0));
ESP_ERROR_CHECK(gptimer_start(portTimers[portNum]));
attachInterrupt(portTo1Button[portNum], portEndCalibs[portNum], RISING);
portENTER_CRITICAL(&sharedData);
clearCalibCW(portNum);
portEXIT_CRITICAL(&sharedData);
}
void cancelCalib(const uint8_t& portNum) {
detachInterrupt(portTo0Button[portNum]);
detachInterrupt(portTo1Button[portNum]);
ledcWrite(portToServo[portNum], offSpeed);
portENTER_CRITICAL(&sharedData);
clearCalibCCW(portNum);
clearCalibCW(portNum);
clearCalibDone(portNum);
cwTimes[portNum] = 0;
ccwTimes[portNum] = 0;
portEXIT_CRITICAL(&sharedData);
ESP_ERROR_CHECK(gptimer_stop(portTimers[portNum]));
}
bool getCalibStatus(const uint8_t& portNum) {
prefs.begin((prefNameCalibs + String(portNum)).c_str());
bool retVal = prefs.getBool("calibrated");
prefs.end();
return retVal;
}
uint32_t getCWTime(const uint8_t& portNum) {
prefs.begin((prefNameCalibs + String(portNum)).c_str());
uint32_t retVal = prefs.getULong("cwTime");
prefs.end();
return retVal;
}
uint32_t getCCWTime(const uint8_t& portNum) {
prefs.begin((prefNameCalibs + String(portNum)).c_str());
uint32_t retVal = prefs.getULong("ccwTime");
prefs.end();
return retVal;
}
void clearNamespace(const char* nameSpace) {
prefs.begin(nameSpace);
prefs.clear();
prefs.end();
}
void clearStorage() {
clearNamespace("wifi");
clearNamespace("auth");
for (uint8_t i = 0; i < 4; i++) {
String prefName = prefNameCalibs + String(i);
clearNamespace(prefName.c_str());
}
}
uint8_t getPos(const uint8_t& portNum) {
prefs.begin((prefNameCalibs + String(portNum)).c_str());
uint8_t prevPos = prefs.getUChar("pos");
prefs.end();
return prevPos;
}
void putPos(const uint8_t& portNum, const uint8_t& pos) {
prefs.begin((prefNameCalibs + String(portNum)).c_str());
prefs.putUChar("pos", pos);
prefs.end();
}
uint32_t posUpdateAndGetTurnTime(const uint8_t& portNum, const uint8_t& newPos) {
prefs.begin((prefNameCalibs + String(portNum)).c_str());
uint8_t prevPos = prefs.getUChar("pos");
if (newPos != prevPos) prefs.putUChar("pos", newPos);
prefs.end();
uint32_t timeToTurn = 0;
uint32_t remainingTime;
uint64_t readMillis;
gptimer_get_raw_count(portTimers[portNum], &readMillis);
portENTER_CRITICAL(&sharedData);
remainingTime = (totalTimes[portNum] > readMillis) ? totalTimes[portNum] - (uint32_t)readMillis : 0;
portEXIT_CRITICAL(&sharedData);
uint32_t cwTime = getCWTime(portNum);
uint32_t ccwTime = getCCWTime(portNum);
if (newPos < prevPos) {
timeToTurn = ((prevPos - newPos) * cwTime) / 10;
Serial.printf("TimeToTurn: %d\n", timeToTurn);
portENTER_CRITICAL(&sharedData);
if (getMovingCW(portNum)) {
portEXIT_CRITICAL(&sharedData);
gptimer_stop(portTimers[portNum]);
timeToTurn += remainingTime;
} else if (getMovingCCW(portNum)) {
portEXIT_CRITICAL(&sharedData);
gptimer_stop(portTimers[portNum]);
double result = ((double)cwTime / (double) ccwTime) * (double) remainingTime;
uint32_t intRes = result;
if (intRes > timeToTurn) {
result = ((double)ccwTime / (double)cwTime) * (double) timeToTurn;
timeToTurn = remainingTime - (uint32_t)result;
} else {
timeToTurn -= intRes;
portENTER_CRITICAL(&sharedData);
clearMovingCCW(portNum);
setMovingCW(portNum);
portEXIT_CRITICAL(&sharedData);
}
} else {
setMovingCW(portNum);
portEXIT_CRITICAL(&sharedData);
}
}
else if (newPos > prevPos) {
timeToTurn = ((newPos - prevPos) * ccwTime) / 10;
portENTER_CRITICAL(&sharedData);
if (getMovingCCW(portNum)) {
portEXIT_CRITICAL(&sharedData);
gptimer_stop(portTimers[portNum]);
timeToTurn += remainingTime;
} else if (getMovingCW(portNum)) {
portEXIT_CRITICAL(&sharedData);
gptimer_stop(portTimers[portNum]);
double result = ((double)ccwTime / (double)cwTime) * (double)remainingTime;
uint32_t intRes = result;
if (intRes > timeToTurn) {
result = ((double)cwTime / (double)ccwTime) * (double)timeToTurn;
timeToTurn = remainingTime - (uint32_t)result;
} else {
timeToTurn -= intRes;
portENTER_CRITICAL(&sharedData);
clearMovingCW(portNum);
setMovingCCW(portNum);
portEXIT_CRITICAL(&sharedData);
}
} else {
setMovingCCW(portNum);
portEXIT_CRITICAL(&sharedData);
}
}
else timeToTurn = 0;
return timeToTurn;
}
void notifyServerCalibrated(const uint8_t& port) {
DynamicJsonDocument doc(256);
JsonArray array = doc.to<JsonArray>();
array.add("calib_done");
JsonObject param1 = array.createNestedObject();
param1["port"] = port + 1;
String toSend;
serializeJson(doc, toSend);
socketIO.sendEVENT(toSend);
}
void notifyPos(const uint8_t& port, const uint8_t& pos) {
DynamicJsonDocument doc(256);
JsonArray array = doc.to<JsonArray>();
array.add("pos_hit");
JsonObject param1 = array.createNestedObject();
param1["port"] = port + 1;
param1["pos"] = pos;
String toSend;
serializeJson(doc, toSend);
socketIO.sendEVENT(toSend);
}
void initSetup();
void connect();
void initializePeriphs();
void setup() {
Serial.begin(9600);
// while(!Serial);
pinMode(15, OUTPUT);
digitalWrite(15, HIGH);
connect();
configTime(0, 0, ntpServer);
Serial.println("Waiting for NTP time synchronization...");
struct tm timeinfo;
uint8_t retryCount = 0;
// Wait for time to be set (it might take a few seconds)
while (!getLocalTime(&timeinfo) && retryCount < 10) {
Serial.print(".");
delay(1000);
retryCount++;
}
if (retryCount >= 10) {
Serial.println("\nFailed to obtain time from NTP server after multiple retries!");
} else {
Serial.println("\nTime synchronized successfully!");
}
for (uint8_t i = 0; i < 4; i++) {
ledcAttach(portToServo[i], 50, 16);
ledcWrite(portToServo[i], offSpeed);
}
for (uint8_t i = 0; i < 4; i++) pinMode(portTo0Button[i], INPUT);
for (uint8_t i = 0; i < 4; i++) pinMode(portTo1Button[i], INPUT);
for (uint8_t i = 0; i < 4; i++) {
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &portTimers[i]));
gptimer_event_callbacks_t cbs = {
.on_alarm = portStops[i],
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(portTimers[i], &cbs, NULL));
ESP_ERROR_CHECK(gptimer_enable(portTimers[i]));
}
String auth = String("Bearer ") + token;
// server address, port and URL
socketIO.begin(address, port, socketUrl, true, auth);
// event handler
socketIO.onEvent(socketIOEvent);
initializePeriphs();
}
void loop() {
socketIO.loop();
for (uint8_t i = 0; i < 4; i++) {
portENTER_CRITICAL(&sharedData);
if (getCalibCCW(i)) {
portEXIT_CRITICAL(&sharedData);
beginCalib(i);
} else if (getCalibCW(i)) {
portEXIT_CRITICAL(&sharedData);
switchCalib(i);
} else if (getCalibDone(i)) {
portEXIT_CRITICAL(&sharedData);
saveCalibTimes(i);
notifyServerCalibrated(i);
} else if (getPos0(i)) {
clearPos0(i);
clearMovingCW(i);
portEXIT_CRITICAL(&sharedData);
gptimer_stop(portTimers[i]);
if (!(getPos(i) == 0)) putPos(i, 0);
notifyPos(i, 0);
detachInterrupt(portTo1Button[i]);
} else if (getPos10(i)) {
clearPos10(i);
clearMovingCCW(i);
portEXIT_CRITICAL(&sharedData);
gptimer_stop(portTimers[i]);
if (!(getPos(i) == 10)) putPos(i, 10);
notifyPos(i, 10);
detachInterrupt(portTo0Button[i]);
}
else {
portEXIT_CRITICAL(&sharedData);
if (getCalibStatus(i)) {
portENTER_CRITICAL(&sharedData);
if (getBlocked(i) && !getMovingCW(i) && !getMovingCCW(i)) clearBlocked(i);
portEXIT_CRITICAL(&sharedData);
if (targetPos[i] != 0 && targetPos[i] != 10) {
if (!getBlocked(i)) {
uint32_t timeToTurn = posUpdateAndGetTurnTime(i, targetPos[i]);
if (timeToTurn != 0) {
portENTER_CRITICAL(&sharedData);
totalTimes[i] = timeToTurn;
bool movingcw = getMovingCW(i);
bool movingccw = getMovingCCW(i);
portEXIT_CRITICAL(&sharedData);
if (movingcw) {
ledcWrite(portToServo[i], cwSpeed);
attachInterrupt(portTo1Button[i], portsHit0[i], RISING);
}
else if (movingccw) {
ledcWrite(portToServo[i], ccwSpeed);
attachInterrupt(portTo0Button[i], portsHit10[i], RISING);
}
gptimer_set_raw_count(portTimers[i], 0);
gptimer_alarm_config_t alarm_config = {
.alarm_count = timeToTurn,
.flags = {
.auto_reload_on_alarm = false,
}
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(portTimers[i], &alarm_config));
ESP_ERROR_CHECK(gptimer_start(portTimers[i]));
}
}
} else if (!(targetPos[i] == getPos(i))){
setBlocked(i);
putPos(i, targetPos[i]);
if (targetPos[i] == 0) {
ledcWrite(portToServo[i], cwSpeed);
gptimer_stop(portTimers[i]);
portENTER_CRITICAL(&sharedData);
setMovingCW(i);
clearMovingCCW(i);
portEXIT_CRITICAL(&sharedData);
attachInterrupt(portTo1Button[i], portStopsButton[i], RISING);
}
else {
ledcWrite(portToServo[i], ccwSpeed);
gptimer_stop(portTimers[i]);
portENTER_CRITICAL(&sharedData);
setMovingCCW(i);
clearMovingCW(i);
portEXIT_CRITICAL(&sharedData);
attachInterrupt(portTo0Button[i], portStopsButton[i], RISING);
}
}
}
}
}
}
void initializePeriphs() {
if (http.begin(httpSrv + "position")) {
http.addHeader("Authorization", "Bearer " + token);
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.println("Error getting details");
}
JsonArray docArray = doc.as<JsonArray>();
for (JsonObject periph : docArray) {
uint8_t periphNum = periph["peripheral_number"].as<uint8_t>() - 1;
if (periph["await_calib"].as<bool>()) {
portENTER_CRITICAL(&sharedData);
setCalibCCW(periphNum);
clearCalibCW(periphNum);
clearCalibDone(periphNum);
portEXIT_CRITICAL(&sharedData);
}
targetPos[periphNum] = periph["last_pos"].as<uint8_t>();
}
}
}
http.end();
}
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
// Code to execute when a client connects
Serial.println("Client connected");
}
void onDisconnect(BLEServer* pServer) {
// Code to execute when a client disconnects
Serial.println("Client disconnected");
restart = true;
// For example, restart advertising
if (connecting) pServer->startAdvertising();
}
};
class MyCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pChar) {
if (pChar == ssidChar) {
ssid = pChar->getValue().c_str();
Serial.println("SSID received: " + ssid);
ssidgiven = true;
} else if (pChar == passChar) {
password = pChar->getValue().c_str();
Serial.println("Password received: " + password);
passgiven = true;
}
else if (pChar == tokenChar) {
token = pChar->getValue().c_str();
Serial.println("Token received: " + token);
tokengiven = true;
}
else if (pChar == ssidRefreshChar) {
Serial.println("Wifi Refresh");
restart = true;
scanAndUpdateSSIDList();
ssidListChar->notify();
}
}
};
void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
switch(type) {
case sIOtype_DISCONNECT:
Serial.printf("[IOc] Disconnected!\n");
break;
case sIOtype_CONNECT:
Serial.printf("[IOc] Connected to url: %s\n", payload);
// join default namespace (no auto join in Socket.IO V3)
socketIO.send(sIOtype_CONNECT, "/");
break;
case sIOtype_EVENT:
{
char * sptr = NULL;
int id = strtol((char *)payload, &sptr, 10);
Serial.printf("[IOc] get event: %s id: %d\n", payload, id);
if(id) {
payload = (uint8_t *)sptr;
}
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, payload, length);
if(error) {
Serial.print(F("deserializeJson() failed: "));
Serial.printf(error.c_str());
return;
}
String eventName = doc[0];
Serial.printf("[IOc] event name: %s\n", eventName.c_str());
if (eventName == "posUpdates") {
JsonArray updatesList = doc[1].as<JsonArray>();
for (JsonVariant update : updatesList) {
if (update.is<JsonObject>()) {
JsonObject updateJSON = update.as<JsonObject>();
uint8_t periphNum = updateJSON["periphNum"].as<uint8_t>() - 1;
uint8_t pos = updateJSON["pos"].as<uint8_t>();
Serial.printf("Peripheral: %d, Position: %d\n", periphNum, pos);
targetPos[periphNum] = pos;
}
}
} else if (eventName == "calib") {
JsonObject toCalib = doc[1].as<JsonObject>();
uint8_t periphNum = toCalib["periphNum"].as<uint8_t>() - 1;
portENTER_CRITICAL(&sharedData);
if (!(getCalibCW(periphNum) || getCalibDone(periphNum))) setCalibCCW(periphNum);
portEXIT_CRITICAL(&sharedData);
} else if (eventName == "cancel_calib") {
JsonObject toCancel = doc[1].as<JsonObject>();
cancelCalib(toCancel["periphNum"].as<uint8_t>() - 1);
}
}
break;
case sIOtype_ACK:
Serial.printf("[IOc] get ack: %u\n", length);
break;
case sIOtype_ERROR:
Serial.printf("[IOc] get error: %u\n", length);
break;
case sIOtype_BINARY_EVENT:
Serial.printf("[IOc] get binary: %u\n", length);
break;
case sIOtype_BINARY_ACK:
Serial.printf("[IOc] get binary ack: %u\n", length);
break;
}
}
void initSetup() {
BLEDevice::init("BlindMaster Device");
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks);
BLEService *pService = pServer->createService(BLEUUID((uint16_t)0x181C)); // custom service
BLESecurity *pSecurity = new BLESecurity();
pSecurity->setCapability(ESP_IO_CAP_NONE);
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); // or ESP_LE_AUTH_BOND
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
ssidListChar = pService->createCharacteristic("0000", BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
ssidListChar->addDescriptor(new BLE2902());
ssidRefreshChar = pService->createCharacteristic("0004", BLECharacteristic::PROPERTY_WRITE);
ssidChar = pService->createCharacteristic("0001", BLECharacteristic::PROPERTY_WRITE);
passChar = pService->createCharacteristic("0002", BLECharacteristic::PROPERTY_WRITE);
connectConfirmChar = pService->createCharacteristic("0005", BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
connectConfirmChar->addDescriptor(new BLE2902());
tokenChar = pService->createCharacteristic("0003", BLECharacteristic::PROPERTY_WRITE);
ssidChar->setCallbacks(new MyCallbacks());
passChar->setCallbacks(new MyCallbacks());
tokenChar->setCallbacks(new MyCallbacks());
ssidRefreshChar->setCallbacks(new MyCallbacks());
scanAndUpdateSSIDList();
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID("181C");
pAdvertising->start();
Serial.println("BLE service started.");
while (1) {
while (!(ssidgiven && passgiven)) delay(100);
if (password == "") WiFi.begin(ssid.c_str());
else WiFi.begin(ssid.c_str(), password.c_str());
uint32_t startAttemptTime = millis();
const uint32_t timeout = 10000;
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < timeout) {
Serial.print(".");
delay(500);
}
if (WiFi.status() == WL_CONNECTED) {
Serial<<endl<<"Connected at "<<WiFi.localIP()<<endl;
connectConfirmChar->setValue("Connected");
connectConfirmChar->notify();
connectConfirmChar->setValue(""); // Clear value after notify
restart = false;
saveWiFiCreds(ssid, password);
while (!tokengiven) {
delay(100);
if (restart) {
ssidgiven = false;
passgiven = false;
break;
}
}
if (restart) continue;
if (http.begin(httpSrv + "verify_device")) {
http.addHeader("Authorization", "Bearer " + token);
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.f_str());
ssidgiven = false;
passgiven = false;
tokengiven = false;
}
token = doc["token"].as<String>();
Serial.println(token);
saveToken(token);
break;
}
http.end();
}
} else {
Serial<<endl<<"Timed out"<<endl;
ssidgiven = false;
passgiven = false;
WiFi.disconnect(true);
}
WiFi.disconnect(true);
connectConfirmChar->setValue("Error");
connectConfirmChar->notify();
connectConfirmChar->setValue(""); // Clear value after notify
}
pAdvertising->stop();
}
void connect() {
connecting = true;
prefs.begin("wifi", true); // true = read only
String storedSSID = prefs.getString("ssid", "");
String storedPass = prefs.getString("password", "");
prefs.end();
if (storedSSID.length() == 0 && storedPass.length() == 0) {
initSetup();
} else if (storedSSID.length() != 0 && storedPass.length() == 0) {
WiFi.begin(storedSSID.c_str());
uint32_t startAttemptTime = millis();
const uint32_t timeout = 10000;
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < timeout) {
Serial.print(".");
delay(500);
}
if (WiFi.status() != WL_CONNECTED) {
initSetup();
connect();
}
} else {
WiFi.begin(storedSSID.c_str(), storedPass.c_str());
uint32_t startAttemptTime = millis();
const uint32_t timeout = 10000;
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < timeout) {
Serial.print(".");
delay(500);
}
Serial.println(WiFi.status());
if (WiFi.status() != WL_CONNECTED) {
initSetup();
connect();
}
}
connecting = false;
prefs.begin("auth", true);
token = prefs.getString("token", "");
prefs.end();
}