fix: button toggle logic for relay1
This commit is contained in:
parent
a371e29058
commit
1e47fc3de7
1 changed files with 132 additions and 48 deletions
178
src/main.cpp
178
src/main.cpp
|
|
@ -1,6 +1,7 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <NimBLEDevice.h>
|
#include <NimBLEDevice.h>
|
||||||
#include <NimBLEAdvertising.h>
|
#include <NimBLEAdvertising.h>
|
||||||
|
#include <FastLED.h>
|
||||||
|
|
||||||
#include <mbedtls/md.h>
|
#include <mbedtls/md.h>
|
||||||
#include <mbedtls/sha256.h>
|
#include <mbedtls/sha256.h>
|
||||||
|
|
@ -9,12 +10,17 @@
|
||||||
#include <esp_partition.h>
|
#include <esp_partition.h>
|
||||||
#include <nvs_flash.h>
|
#include <nvs_flash.h>
|
||||||
|
|
||||||
#define RELAY1_PIN 1
|
#define RELAY1_PIN 39
|
||||||
#define RELAY2_PIN 2
|
#define RELAY2_PIN 38
|
||||||
#define TOUCH1_PIN 8
|
#define TOUCH1_PIN 8
|
||||||
#define TOUCH2_PIN 6
|
#define TOUCH2_PIN 6
|
||||||
#define BUTTON_PIN 41
|
#define BUTTON_PIN 41
|
||||||
#define LED_PIN 46
|
#define LED_PIN 35
|
||||||
|
#define NUM_LEDS 1
|
||||||
|
|
||||||
|
CRGB leds[NUM_LEDS];
|
||||||
|
|
||||||
|
#define MAX_CLIENTS 2
|
||||||
|
|
||||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361f26d8"
|
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361f26d8"
|
||||||
|
|
@ -29,7 +35,7 @@
|
||||||
#define OTA_STATUS_ACK 0x00
|
#define OTA_STATUS_ACK 0x00
|
||||||
#define OTA_STATUS_NAK 0x01
|
#define OTA_STATUS_NAK 0x01
|
||||||
#define OTA_STATUS_COMPLETE 0x10
|
#define OTA_STATUS_COMPLETE 0x10
|
||||||
#define OTA_STATUS_ERROR 0x11 // 1 cmd + 4 seq + 32 hmac
|
#define OTA_STATUS_ERROR 0x11
|
||||||
#define HMAC_KEY_LEN 32
|
#define HMAC_KEY_LEN 32
|
||||||
#define ESP_UUID_LEN 16
|
#define ESP_UUID_LEN 16
|
||||||
#define NVS_NAMESPACE "hmac"
|
#define NVS_NAMESPACE "hmac"
|
||||||
|
|
@ -38,12 +44,16 @@
|
||||||
#define FIRMWARE_VERSION "6"
|
#define FIRMWARE_VERSION "6"
|
||||||
|
|
||||||
NimBLECharacteristic *pCharacteristic;
|
NimBLECharacteristic *pCharacteristic;
|
||||||
bool deviceConnected = false;
|
|
||||||
bool bleRelay1 = false;
|
|
||||||
bool bleRelay2 = false;
|
|
||||||
bool relay1State = false;
|
bool relay1State = false;
|
||||||
bool relay2State = false;
|
bool relay2State = false;
|
||||||
uint32_t lastSeq = 0;
|
bool buttonToggle = false;
|
||||||
|
|
||||||
|
bool connActive[MAX_CLIENTS] = {false, false};
|
||||||
|
bool bleRelay1[MAX_CLIENTS] = {false, false};
|
||||||
|
bool bleRelay2[MAX_CLIENTS] = {false, false};
|
||||||
|
uint32_t lastSeq[MAX_CLIENTS] = {0, 0};
|
||||||
|
uint16_t connHandles[MAX_CLIENTS] = {0xFFFF, 0xFFFF};
|
||||||
|
int connectedCount = 0;
|
||||||
|
|
||||||
bool otaInProgress = false;
|
bool otaInProgress = false;
|
||||||
esp_ota_handle_t otaHandle = 0;
|
esp_ota_handle_t otaHandle = 0;
|
||||||
|
|
@ -60,31 +70,75 @@ bool keyProvisioned = false;
|
||||||
bool uuidProvisioned = false;
|
bool uuidProvisioned = false;
|
||||||
Preferences preferences;
|
Preferences preferences;
|
||||||
|
|
||||||
|
static int findClientIdx(uint16_t connHandle) {
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (connActive[i] && connHandles[i] == connHandle) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
class MyServerCallbacks : public NimBLEServerCallbacks {
|
class MyServerCallbacks : public NimBLEServerCallbacks {
|
||||||
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override {
|
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override {
|
||||||
deviceConnected = true;
|
uint16_t handle = connInfo.getConnHandle();
|
||||||
lastSeq = 0;
|
int slot = -1;
|
||||||
Serial.println("BLE: device connected");
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (!connActive[i]) {
|
||||||
|
slot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (slot >= 0) {
|
||||||
|
connHandles[slot] = handle;
|
||||||
|
connActive[slot] = true;
|
||||||
|
bleRelay1[slot] = false;
|
||||||
|
bleRelay2[slot] = false;
|
||||||
|
lastSeq[slot] = 0;
|
||||||
|
connectedCount++;
|
||||||
|
Serial.printf("BLE: device connected (conn=%d, slot=%d, clients=%d/%d)\n",
|
||||||
|
handle, slot, connectedCount, MAX_CLIENTS);
|
||||||
|
} else {
|
||||||
|
Serial.printf("BLE: device connected but no free slot (conn=%d, clients=%d/%d)\n",
|
||||||
|
handle, connectedCount, MAX_CLIENTS);
|
||||||
|
}
|
||||||
|
if (connectedCount < MAX_CLIENTS) {
|
||||||
|
NimBLEDevice::startAdvertising();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override {
|
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override {
|
||||||
deviceConnected = false;
|
uint16_t handle = connInfo.getConnHandle();
|
||||||
bleRelay1 = false;
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
bleRelay2 = false;
|
if (connActive[i] && connHandles[i] == handle) {
|
||||||
lastSeq = 0;
|
connActive[i] = false;
|
||||||
Serial.println("BLE: device disconnected");
|
bleRelay1[i] = false;
|
||||||
NimBLEDevice::startAdvertising();
|
bleRelay2[i] = false;
|
||||||
|
lastSeq[i] = 0;
|
||||||
|
connHandles[i] = 0xFFFF;
|
||||||
|
connectedCount--;
|
||||||
|
Serial.printf("BLE: device disconnected (conn=%d, slot=%d, clients=%d/%d)\n",
|
||||||
|
handle, i, connectedCount, MAX_CLIENTS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (connectedCount < MAX_CLIENTS) {
|
||||||
|
NimBLEDevice::startAdvertising();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool verifyHmac(const uint8_t *packet) {
|
static bool verifyHmac(const uint8_t *packet, int idx) {
|
||||||
|
if (idx < 0 || idx >= MAX_CLIENTS || !connActive[idx]) {
|
||||||
|
Serial.printf("BLE: HMAC verify called for invalid idx=%d\n", idx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t cmd = packet[0];
|
uint8_t cmd = packet[0];
|
||||||
uint32_t seq = ((uint32_t)packet[1] << 24) |
|
uint32_t seq = ((uint32_t)packet[1] << 24) |
|
||||||
((uint32_t)packet[2] << 16) |
|
((uint32_t)packet[2] << 16) |
|
||||||
((uint32_t)packet[3] << 8) |
|
((uint32_t)packet[3] << 8) |
|
||||||
(uint32_t)packet[4];
|
(uint32_t)packet[4];
|
||||||
|
|
||||||
if (seq <= lastSeq) {
|
if (seq <= lastSeq[idx]) {
|
||||||
Serial.printf("BLE: replay rejected (seq=%lu, last=%lu)\n", seq, lastSeq);
|
Serial.printf("BLE: replay rejected (seq=%lu, last=%lu, slot=%d)\n", seq, lastSeq[idx], idx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +158,7 @@ static bool verifyHmac(const uint8_t *packet) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSeq = seq;
|
lastSeq[idx] = seq;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,7 +177,7 @@ static void parseHexKey(const String &hex, uint8_t *out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ledOtaBusy() {
|
static void ledOtaBusy() {
|
||||||
digitalWrite(LED_PIN, (millis() / 100) % 2 ? HIGH : LOW);
|
leds[0] = (millis() / 100) % 2 ? CRGB::Green : CRGB::Black;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void otaReset() {
|
static void otaReset() {
|
||||||
|
|
@ -193,8 +247,10 @@ class OtaCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
||||||
digitalWrite(RELAY2_PIN, LOW);
|
digitalWrite(RELAY2_PIN, LOW);
|
||||||
relay1State = false;
|
relay1State = false;
|
||||||
relay2State = false;
|
relay2State = false;
|
||||||
bleRelay1 = false;
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
bleRelay2 = false;
|
bleRelay1[i] = false;
|
||||||
|
bleRelay2[i] = false;
|
||||||
|
}
|
||||||
Serial.printf("OTA: started, expecting %lu bytes\n", otaExpectedSize);
|
Serial.printf("OTA: started, expecting %lu bytes\n", otaExpectedSize);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +281,8 @@ class OtaCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
||||||
sendOtaNotify(pChar, OTA_STATUS_ERROR, 0);
|
sendOtaNotify(pChar, OTA_STATUS_ERROR, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
digitalWrite(LED_PIN, HIGH);
|
leds[0] = CRGB::Green;
|
||||||
|
FastLED.show();
|
||||||
sendOtaNotify(pChar, OTA_STATUS_COMPLETE, 0);
|
sendOtaNotify(pChar, OTA_STATUS_COMPLETE, 0);
|
||||||
Serial.println("OTA: SHA256 OK, rebooting...");
|
Serial.println("OTA: SHA256 OK, rebooting...");
|
||||||
delay(500);
|
delay(500);
|
||||||
|
|
@ -256,6 +313,13 @@ class OtaCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
||||||
class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
||||||
void onWrite(NimBLECharacteristic *pChar, NimBLEConnInfo &connInfo) override {
|
void onWrite(NimBLECharacteristic *pChar, NimBLEConnInfo &connInfo) override {
|
||||||
if (otaInProgress) return;
|
if (otaInProgress) return;
|
||||||
|
|
||||||
|
int idx = findClientIdx(connInfo.getConnHandle());
|
||||||
|
if (idx < 0) {
|
||||||
|
Serial.printf("BLE: write from unknown conn handle %d\n", connInfo.getConnHandle());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string value = pChar->getValue();
|
std::string value = pChar->getValue();
|
||||||
if (value.length() != HMAC_PKT_SIZE) {
|
if (value.length() != HMAC_PKT_SIZE) {
|
||||||
Serial.printf("BLE: bad packet length %d (expected %d)\n",
|
Serial.printf("BLE: bad packet length %d (expected %d)\n",
|
||||||
|
|
@ -265,25 +329,25 @@ class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
||||||
|
|
||||||
const uint8_t *pkt = (const uint8_t *)value.data();
|
const uint8_t *pkt = (const uint8_t *)value.data();
|
||||||
|
|
||||||
if (!verifyHmac(pkt)) {
|
if (!verifyHmac(pkt, idx)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t cmd = pkt[0];
|
uint8_t cmd = pkt[0];
|
||||||
uint32_t seq = lastSeq;
|
uint32_t seq = lastSeq[idx];
|
||||||
|
|
||||||
if (cmd == 0x01) {
|
if (cmd == 0x01) {
|
||||||
bleRelay1 = true;
|
bleRelay1[idx] = true;
|
||||||
Serial.printf("BLE: relay1 ON (seq=%lu)\n", seq);
|
Serial.printf("BLE: relay1 ON (seq=%lu, slot=%d)\n", seq, idx);
|
||||||
} else if (cmd == 0x00) {
|
} else if (cmd == 0x00) {
|
||||||
bleRelay1 = false;
|
bleRelay1[idx] = false;
|
||||||
Serial.printf("BLE: relay1 OFF (seq=%lu)\n", seq);
|
Serial.printf("BLE: relay1 OFF (seq=%lu, slot=%d)\n", seq, idx);
|
||||||
} else if (cmd == 0x02) {
|
} else if (cmd == 0x02) {
|
||||||
bleRelay2 = true;
|
bleRelay2[idx] = true;
|
||||||
Serial.printf("BLE: relay2 ON (seq=%lu)\n", seq);
|
Serial.printf("BLE: relay2 ON (seq=%lu, slot=%d)\n", seq, idx);
|
||||||
} else if (cmd == 0x03) {
|
} else if (cmd == 0x03) {
|
||||||
bleRelay2 = false;
|
bleRelay2[idx] = false;
|
||||||
Serial.printf("BLE: relay2 OFF (seq=%lu)\n", seq);
|
Serial.printf("BLE: relay2 OFF (seq=%lu, slot=%d)\n", seq, idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -291,7 +355,9 @@ class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(2000);
|
delay(2000);
|
||||||
Serial.println("=== Atom VoiceS3R BLE Relay ===");
|
Serial.println("=== AtomS3 Lite BLE Relay ===");
|
||||||
|
|
||||||
|
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
|
||||||
|
|
||||||
preferences.begin(NVS_NAMESPACE, true);
|
preferences.begin(NVS_NAMESPACE, true);
|
||||||
size_t len = preferences.getBytes(NVS_KEY_NAME, hmacKey, HMAC_KEY_LEN);
|
size_t len = preferences.getBytes(NVS_KEY_NAME, hmacKey, HMAC_KEY_LEN);
|
||||||
|
|
@ -328,12 +394,13 @@ void setup() {
|
||||||
pinMode(BUTTON_PIN, INPUT);
|
pinMode(BUTTON_PIN, INPUT);
|
||||||
pinMode(RELAY1_PIN, OUTPUT);
|
pinMode(RELAY1_PIN, OUTPUT);
|
||||||
pinMode(RELAY2_PIN, OUTPUT);
|
pinMode(RELAY2_PIN, OUTPUT);
|
||||||
pinMode(LED_PIN, OUTPUT);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
digitalWrite(LED_PIN, HIGH);
|
leds[0] = CRGB::Green;
|
||||||
|
FastLED.show();
|
||||||
delay(200);
|
delay(200);
|
||||||
digitalWrite(LED_PIN, LOW);
|
leds[0] = CRGB::Black;
|
||||||
|
FastLED.show();
|
||||||
delay(200);
|
delay(200);
|
||||||
}
|
}
|
||||||
Serial.println("LED blink done, starting BLE...");
|
Serial.println("LED blink done, starting BLE...");
|
||||||
|
|
@ -343,6 +410,7 @@ void setup() {
|
||||||
|
|
||||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
NimBLEServer *pServer = NimBLEDevice::createServer();
|
||||||
pServer->setCallbacks(new MyServerCallbacks());
|
pServer->setCallbacks(new MyServerCallbacks());
|
||||||
|
pServer->advertiseOnDisconnect(true);
|
||||||
|
|
||||||
NimBLEService *pService = pServer->createService(SERVICE_UUID);
|
NimBLEService *pService = pServer->createService(SERVICE_UUID);
|
||||||
pCharacteristic = pService->createCharacteristic(
|
pCharacteristic = pService->createCharacteristic(
|
||||||
|
|
@ -472,9 +540,20 @@ void loop() {
|
||||||
bool t2Active = (millis() - t2Change > DEBOUNCE_MS) && t2Stable;
|
bool t2Active = (millis() - t2Change > DEBOUNCE_MS) && t2Stable;
|
||||||
bool btnActive = (millis() - btnChange > DEBOUNCE_MS) && btnStable;
|
bool btnActive = (millis() - btnChange > DEBOUNCE_MS) && btnStable;
|
||||||
|
|
||||||
|
static bool prevBtnActive = false;
|
||||||
|
if (btnActive && !prevBtnActive) {
|
||||||
|
buttonToggle = !buttonToggle;
|
||||||
|
Serial.printf("Button: toggle -> %s\n", buttonToggle ? "ON" : "OFF");
|
||||||
|
}
|
||||||
|
prevBtnActive = btnActive;
|
||||||
|
|
||||||
if (!otaInProgress) {
|
if (!otaInProgress) {
|
||||||
relay1State = t1Active || btnActive || (deviceConnected && bleRelay1);
|
relay1State = t1Active || (buttonToggle && digitalRead(BUTTON_PIN) != LOW) ||
|
||||||
relay2State = t2Active || (deviceConnected && bleRelay2);
|
(connActive[0] && bleRelay1[0]) || (connActive[1] && bleRelay1[1]);
|
||||||
|
relay2State = t2Active ||
|
||||||
|
(connActive[0] && bleRelay2[0]) || (connActive[1] && bleRelay2[1]);
|
||||||
|
|
||||||
|
leds[0] = (connectedCount > 0) ? CRGB::Green : CRGB::Black;
|
||||||
} else {
|
} else {
|
||||||
relay1State = false;
|
relay1State = false;
|
||||||
relay2State = false;
|
relay2State = false;
|
||||||
|
|
@ -484,33 +563,38 @@ void loop() {
|
||||||
digitalWrite(RELAY1_PIN, relay1State ? HIGH : LOW);
|
digitalWrite(RELAY1_PIN, relay1State ? HIGH : LOW);
|
||||||
digitalWrite(RELAY2_PIN, relay2State ? HIGH : LOW);
|
digitalWrite(RELAY2_PIN, relay2State ? HIGH : LOW);
|
||||||
|
|
||||||
digitalWrite(LED_PIN, (relay1State || relay2State) ? HIGH : LOW);
|
FastLED.show();
|
||||||
|
|
||||||
static bool lastRelay1 = false;
|
static bool lastRelay1 = false;
|
||||||
static bool lastRelay2 = false;
|
static bool lastRelay2 = false;
|
||||||
if (relay1State != lastRelay1 || relay2State != lastRelay2) {
|
if (relay1State != lastRelay1 || relay2State != lastRelay2) {
|
||||||
|
bool anyBle1 = (connActive[0] && bleRelay1[0]) || (connActive[1] && bleRelay1[1]);
|
||||||
|
bool anyBle2 = (connActive[0] && bleRelay2[0]) || (connActive[1] && bleRelay2[1]);
|
||||||
Serial.printf("Relays: R1=%s R2=%s (t1=%s t2=%s btn=%s ble1=%s ble2=%s)\n",
|
Serial.printf("Relays: R1=%s R2=%s (t1=%s t2=%s btn=%s ble1=%s ble2=%s)\n",
|
||||||
relay1State ? "ON" : "OFF",
|
relay1State ? "ON" : "OFF",
|
||||||
relay2State ? "ON" : "OFF",
|
relay2State ? "ON" : "OFF",
|
||||||
t1Active ? "YES" : "no",
|
t1Active ? "YES" : "no",
|
||||||
t2Active ? "YES" : "no",
|
t2Active ? "YES" : "no",
|
||||||
btnActive ? "YES" : "no",
|
btnActive ? "YES" : "no",
|
||||||
bleRelay1 ? "ON" : "OFF",
|
anyBle1 ? "ON" : "OFF",
|
||||||
bleRelay2 ? "ON" : "OFF");
|
anyBle2 ? "ON" : "OFF");
|
||||||
lastRelay1 = relay1State;
|
lastRelay1 = relay1State;
|
||||||
lastRelay2 = relay2State;
|
lastRelay2 = relay2State;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t heartbeat = 0;
|
static uint32_t heartbeat = 0;
|
||||||
if (!otaInProgress && millis() - heartbeat > 5000) {
|
if (!otaInProgress && millis() - heartbeat > 5000) {
|
||||||
Serial.printf("[heartbeat] R1=%s R2=%s t1=%s t2=%s btn=%s ble1=%s ble2=%s\n",
|
bool anyBle1 = (connActive[0] && bleRelay1[0]) || (connActive[1] && bleRelay1[1]);
|
||||||
|
bool anyBle2 = (connActive[0] && bleRelay2[0]) || (connActive[1] && bleRelay2[1]);
|
||||||
|
Serial.printf("[heartbeat] R1=%s R2=%s t1=%s t2=%s btn=%s ble1=%s ble2=%s clients=%d\n",
|
||||||
relay1State ? "ON" : "OFF",
|
relay1State ? "ON" : "OFF",
|
||||||
relay2State ? "ON" : "OFF",
|
relay2State ? "ON" : "OFF",
|
||||||
t1Active ? "YES" : "no",
|
t1Active ? "YES" : "no",
|
||||||
t2Active ? "YES" : "no",
|
t2Active ? "YES" : "no",
|
||||||
btnActive ? "YES" : "no",
|
btnActive ? "YES" : "no",
|
||||||
bleRelay1 ? "ON" : "OFF",
|
anyBle1 ? "ON" : "OFF",
|
||||||
bleRelay2 ? "ON" : "OFF");
|
anyBle2 ? "ON" : "OFF",
|
||||||
|
connectedCount);
|
||||||
heartbeat = millis();
|
heartbeat = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue