From 1e47fc3de7e84b80b28bc9b5c81e3bf756860a63 Mon Sep 17 00:00:00 2001 From: eddy Date: Sat, 1 Aug 2026 00:26:50 +0200 Subject: [PATCH] fix: button toggle logic for relay1 --- src/main.cpp | 180 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 132 insertions(+), 48 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 8062e2e..3a12d11 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -9,12 +10,17 @@ #include #include -#define RELAY1_PIN 1 -#define RELAY2_PIN 2 +#define RELAY1_PIN 39 +#define RELAY2_PIN 38 #define TOUCH1_PIN 8 #define TOUCH2_PIN 6 #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 CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361f26d8" @@ -29,7 +35,7 @@ #define OTA_STATUS_ACK 0x00 #define OTA_STATUS_NAK 0x01 #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 ESP_UUID_LEN 16 #define NVS_NAMESPACE "hmac" @@ -38,12 +44,16 @@ #define FIRMWARE_VERSION "6" NimBLECharacteristic *pCharacteristic; -bool deviceConnected = false; -bool bleRelay1 = false; -bool bleRelay2 = false; bool relay1State = 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; esp_ota_handle_t otaHandle = 0; @@ -60,31 +70,75 @@ bool keyProvisioned = false; bool uuidProvisioned = false; 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 { void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override { - deviceConnected = true; - lastSeq = 0; - Serial.println("BLE: device connected"); + uint16_t handle = connInfo.getConnHandle(); + int slot = -1; + 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 { - deviceConnected = false; - bleRelay1 = false; - bleRelay2 = false; - lastSeq = 0; - Serial.println("BLE: device disconnected"); - NimBLEDevice::startAdvertising(); + uint16_t handle = connInfo.getConnHandle(); + for (int i = 0; i < MAX_CLIENTS; i++) { + if (connActive[i] && connHandles[i] == handle) { + connActive[i] = false; + bleRelay1[i] = false; + 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]; uint32_t seq = ((uint32_t)packet[1] << 24) | ((uint32_t)packet[2] << 16) | ((uint32_t)packet[3] << 8) | (uint32_t)packet[4]; - if (seq <= lastSeq) { - Serial.printf("BLE: replay rejected (seq=%lu, last=%lu)\n", seq, lastSeq); + if (seq <= lastSeq[idx]) { + Serial.printf("BLE: replay rejected (seq=%lu, last=%lu, slot=%d)\n", seq, lastSeq[idx], idx); return false; } @@ -104,7 +158,7 @@ static bool verifyHmac(const uint8_t *packet) { return false; } - lastSeq = seq; + lastSeq[idx] = seq; return true; } @@ -123,7 +177,7 @@ static void parseHexKey(const String &hex, uint8_t *out) { } static void ledOtaBusy() { - digitalWrite(LED_PIN, (millis() / 100) % 2 ? HIGH : LOW); + leds[0] = (millis() / 100) % 2 ? CRGB::Green : CRGB::Black; } static void otaReset() { @@ -193,8 +247,10 @@ class OtaCharacteristicCallbacks : public NimBLECharacteristicCallbacks { digitalWrite(RELAY2_PIN, LOW); relay1State = false; relay2State = false; - bleRelay1 = false; - bleRelay2 = false; + for (int i = 0; i < MAX_CLIENTS; i++) { + bleRelay1[i] = false; + bleRelay2[i] = false; + } Serial.printf("OTA: started, expecting %lu bytes\n", otaExpectedSize); return; } @@ -225,7 +281,8 @@ class OtaCharacteristicCallbacks : public NimBLECharacteristicCallbacks { sendOtaNotify(pChar, OTA_STATUS_ERROR, 0); return; } - digitalWrite(LED_PIN, HIGH); + leds[0] = CRGB::Green; + FastLED.show(); sendOtaNotify(pChar, OTA_STATUS_COMPLETE, 0); Serial.println("OTA: SHA256 OK, rebooting..."); delay(500); @@ -256,6 +313,13 @@ class OtaCharacteristicCallbacks : public NimBLECharacteristicCallbacks { class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic *pChar, NimBLEConnInfo &connInfo) override { 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(); if (value.length() != HMAC_PKT_SIZE) { 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(); - if (!verifyHmac(pkt)) { + if (!verifyHmac(pkt, idx)) { return; } uint8_t cmd = pkt[0]; - uint32_t seq = lastSeq; + uint32_t seq = lastSeq[idx]; if (cmd == 0x01) { - bleRelay1 = true; - Serial.printf("BLE: relay1 ON (seq=%lu)\n", seq); + bleRelay1[idx] = true; + Serial.printf("BLE: relay1 ON (seq=%lu, slot=%d)\n", seq, idx); } else if (cmd == 0x00) { - bleRelay1 = false; - Serial.printf("BLE: relay1 OFF (seq=%lu)\n", seq); + bleRelay1[idx] = false; + Serial.printf("BLE: relay1 OFF (seq=%lu, slot=%d)\n", seq, idx); } else if (cmd == 0x02) { - bleRelay2 = true; - Serial.printf("BLE: relay2 ON (seq=%lu)\n", seq); + bleRelay2[idx] = true; + Serial.printf("BLE: relay2 ON (seq=%lu, slot=%d)\n", seq, idx); } else if (cmd == 0x03) { - bleRelay2 = false; - Serial.printf("BLE: relay2 OFF (seq=%lu)\n", seq); + bleRelay2[idx] = false; + Serial.printf("BLE: relay2 OFF (seq=%lu, slot=%d)\n", seq, idx); } } }; @@ -291,7 +355,9 @@ class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks { void setup() { Serial.begin(115200); delay(2000); - Serial.println("=== Atom VoiceS3R BLE Relay ==="); + Serial.println("=== AtomS3 Lite BLE Relay ==="); + + FastLED.addLeds(leds, NUM_LEDS); preferences.begin(NVS_NAMESPACE, true); size_t len = preferences.getBytes(NVS_KEY_NAME, hmacKey, HMAC_KEY_LEN); @@ -328,12 +394,13 @@ void setup() { pinMode(BUTTON_PIN, INPUT); pinMode(RELAY1_PIN, OUTPUT); pinMode(RELAY2_PIN, OUTPUT); - pinMode(LED_PIN, OUTPUT); for (int i = 0; i < 3; i++) { - digitalWrite(LED_PIN, HIGH); + leds[0] = CRGB::Green; + FastLED.show(); delay(200); - digitalWrite(LED_PIN, LOW); + leds[0] = CRGB::Black; + FastLED.show(); delay(200); } Serial.println("LED blink done, starting BLE..."); @@ -343,6 +410,7 @@ void setup() { NimBLEServer *pServer = NimBLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); + pServer->advertiseOnDisconnect(true); NimBLEService *pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( @@ -472,9 +540,20 @@ void loop() { bool t2Active = (millis() - t2Change > DEBOUNCE_MS) && t2Stable; 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) { - relay1State = t1Active || btnActive || (deviceConnected && bleRelay1); - relay2State = t2Active || (deviceConnected && bleRelay2); + relay1State = t1Active || (buttonToggle && digitalRead(BUTTON_PIN) != LOW) || + (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 { relay1State = false; relay2State = false; @@ -484,35 +563,40 @@ void loop() { digitalWrite(RELAY1_PIN, relay1State ? HIGH : LOW); digitalWrite(RELAY2_PIN, relay2State ? HIGH : LOW); - digitalWrite(LED_PIN, (relay1State || relay2State) ? HIGH : LOW); + FastLED.show(); static bool lastRelay1 = false; static bool lastRelay2 = false; 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", relay1State ? "ON" : "OFF", relay2State ? "ON" : "OFF", t1Active ? "YES" : "no", t2Active ? "YES" : "no", btnActive ? "YES" : "no", - bleRelay1 ? "ON" : "OFF", - bleRelay2 ? "ON" : "OFF"); + anyBle1 ? "ON" : "OFF", + anyBle2 ? "ON" : "OFF"); lastRelay1 = relay1State; lastRelay2 = relay2State; } static uint32_t heartbeat = 0; 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", relay2State ? "ON" : "OFF", t1Active ? "YES" : "no", t2Active ? "YES" : "no", btnActive ? "YES" : "no", - bleRelay1 ? "ON" : "OFF", - bleRelay2 ? "ON" : "OFF"); + anyBle1 ? "ON" : "OFF", + anyBle2 ? "ON" : "OFF", + connectedCount); heartbeat = millis(); } delay(50); -} \ No newline at end of file +}