feat: add device UUID pairing — unique identity per ESP-phone pair
- Firmware: auto-generate 16-byte UUID on first boot (NVS), new READ characteristic - BLE name: AtomS3R-Relay → SylDa_Server - KEY? now returns DEVICE_ID + HMAC_KEY, SET_ID command for UUID provisioning - App: UUID+HMAC loaded from prefs, scan by service UUID, verify UUID after connect - UUID mismatch → disconnect + skip address + rescan - Key conflict dialog: keep ESP keys, overwrite ESP (with checkbox confirm), or cancel - NO_KEY with existing app keys: send existing pair instead of generating new - Added design spec doc
This commit is contained in:
parent
73b3c0eaa0
commit
348c55d830
9 changed files with 527 additions and 106 deletions
156
src/main.cpp
156
src/main.cpp
|
|
@ -7,26 +7,36 @@
|
|||
#include <mbedtls/md.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
#define SENSOR_PIN 38
|
||||
#define RELAY_PIN 1
|
||||
#define LED_PIN 46
|
||||
#define RELAY1_PIN 1
|
||||
#define RELAY2_PIN 2
|
||||
#define TOUCH1_PIN 8
|
||||
#define TOUCH2_PIN 6
|
||||
#define BUTTON_PIN 41
|
||||
#define LED_PIN 46
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361f26d8"
|
||||
#define UUID_CHAR_UUID "beb5483e-36e1-4688-b7f5-ea07361f26d9"
|
||||
|
||||
#define HMAC_PKT_SIZE 37 // 1 cmd + 4 seq + 32 hmac
|
||||
#define HMAC_KEY_LEN 32
|
||||
#define ESP_UUID_LEN 16
|
||||
#define NVS_NAMESPACE "hmac"
|
||||
#define NVS_KEY_NAME "key"
|
||||
#define NVS_UUID_NAME "uuid"
|
||||
|
||||
BLECharacteristic *pCharacteristic;
|
||||
bool deviceConnected = false;
|
||||
bool bleCommand = false;
|
||||
bool relayState = false;
|
||||
bool bleRelay1 = false;
|
||||
bool bleRelay2 = false;
|
||||
bool relay1State = false;
|
||||
bool relay2State = false;
|
||||
uint32_t lastSeq = 0;
|
||||
|
||||
uint8_t hmacKey[HMAC_KEY_LEN];
|
||||
uint8_t deviceUuid[ESP_UUID_LEN];
|
||||
bool keyProvisioned = false;
|
||||
bool uuidProvisioned = false;
|
||||
Preferences preferences;
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks {
|
||||
|
|
@ -37,7 +47,8 @@ class MyServerCallbacks : public BLEServerCallbacks {
|
|||
}
|
||||
void onDisconnect(BLEServer *pServer) override {
|
||||
deviceConnected = false;
|
||||
bleCommand = false;
|
||||
bleRelay1 = false;
|
||||
bleRelay2 = false;
|
||||
lastSeq = 0;
|
||||
Serial.println("BLE: device disconnected");
|
||||
BLEDevice::startAdvertising();
|
||||
|
|
@ -109,23 +120,29 @@ class MyCharacteristicCallbacks : public BLECharacteristicCallbacks {
|
|||
uint32_t seq = lastSeq;
|
||||
|
||||
if (cmd == 0x01) {
|
||||
bleCommand = true;
|
||||
Serial.printf("BLE: relay ON (seq=%lu)\n", seq);
|
||||
} else {
|
||||
bleCommand = false;
|
||||
Serial.printf("BLE: relay OFF (seq=%lu)\n", seq);
|
||||
bleRelay1 = true;
|
||||
Serial.printf("BLE: relay1 ON (seq=%lu)\n", seq);
|
||||
} else if (cmd == 0x00) {
|
||||
bleRelay1 = false;
|
||||
Serial.printf("BLE: relay1 OFF (seq=%lu)\n", seq);
|
||||
} else if (cmd == 0x02) {
|
||||
bleRelay2 = true;
|
||||
Serial.printf("BLE: relay2 ON (seq=%lu)\n", seq);
|
||||
} else if (cmd == 0x03) {
|
||||
bleRelay2 = false;
|
||||
Serial.printf("BLE: relay2 OFF (seq=%lu)\n", seq);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
delay(2000);
|
||||
Serial.begin(115200);
|
||||
delay(500);
|
||||
delay(2000);
|
||||
Serial.println("=== Atom VoiceS3R BLE Relay ===");
|
||||
|
||||
preferences.begin(NVS_NAMESPACE, true);
|
||||
size_t len = preferences.getBytes(NVS_KEY_NAME, hmacKey, HMAC_KEY_LEN);
|
||||
size_t uuidLen = preferences.getBytes(NVS_UUID_NAME, deviceUuid, ESP_UUID_LEN);
|
||||
preferences.end();
|
||||
|
||||
if (len == HMAC_KEY_LEN) {
|
||||
|
|
@ -136,8 +153,28 @@ void setup() {
|
|||
Serial.println("No HMAC key provisioned — connect USB and run SET_KEY");
|
||||
}
|
||||
|
||||
pinMode(SENSOR_PIN, INPUT_PULLUP);
|
||||
pinMode(RELAY_PIN, OUTPUT);
|
||||
if (uuidLen == ESP_UUID_LEN) {
|
||||
uuidProvisioned = true;
|
||||
Serial.print("UUID loaded from NVS: ");
|
||||
for (int i = 0; i < ESP_UUID_LEN; i++) Serial.printf("%02X", deviceUuid[i]);
|
||||
Serial.println();
|
||||
} else {
|
||||
uuidProvisioned = false;
|
||||
for (int i = 0; i < ESP_UUID_LEN; i++) deviceUuid[i] = (uint8_t)(esp_random() & 0xFF);
|
||||
preferences.begin(NVS_NAMESPACE, false);
|
||||
preferences.putBytes(NVS_UUID_NAME, deviceUuid, ESP_UUID_LEN);
|
||||
preferences.end();
|
||||
uuidProvisioned = true;
|
||||
Serial.print("Generated new UUID: ");
|
||||
for (int i = 0; i < ESP_UUID_LEN; i++) Serial.printf("%02X", deviceUuid[i]);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
pinMode(TOUCH1_PIN, INPUT_PULLUP);
|
||||
pinMode(TOUCH2_PIN, INPUT_PULLUP);
|
||||
pinMode(BUTTON_PIN, INPUT);
|
||||
pinMode(RELAY1_PIN, OUTPUT);
|
||||
pinMode(RELAY2_PIN, OUTPUT);
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
|
@ -148,7 +185,7 @@ void setup() {
|
|||
}
|
||||
Serial.println("LED blink done, starting BLE...");
|
||||
|
||||
BLEDevice::init("AtomS3R-Relay");
|
||||
BLEDevice::init("SylDa_Server");
|
||||
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
|
@ -164,6 +201,12 @@ void setup() {
|
|||
pCharacteristic->setValue("0");
|
||||
pService->start();
|
||||
|
||||
BLECharacteristic *pUuidChar = pService->createCharacteristic(
|
||||
UUID_CHAR_UUID,
|
||||
BLECharacteristic::PROPERTY_READ
|
||||
);
|
||||
pUuidChar->setValue(deviceUuid, ESP_UUID_LEN);
|
||||
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
|
|
@ -171,7 +214,7 @@ void setup() {
|
|||
pAdvertising->setMinPreferred(0x12);
|
||||
BLEDevice::startAdvertising();
|
||||
|
||||
Serial.println("BLE: advertising started as 'AtomS3R-Relay'");
|
||||
Serial.println("BLE: advertising started as 'SylDa_Server'");
|
||||
|
||||
if (keyProvisioned) {
|
||||
Serial.print("HMAC Key: ");
|
||||
|
|
@ -194,7 +237,10 @@ void loop() {
|
|||
if (c == '\n') {
|
||||
serialBuf.trim();
|
||||
if (serialBuf == "KEY?") {
|
||||
if (keyProvisioned) {
|
||||
if (keyProvisioned && uuidProvisioned) {
|
||||
Serial.print("DEVICE_ID:");
|
||||
for (int i = 0; i < ESP_UUID_LEN; i++) Serial.printf("%02X", deviceUuid[i]);
|
||||
Serial.println();
|
||||
Serial.print("HMAC_KEY:");
|
||||
for (int i = 0; i < HMAC_KEY_LEN; i++) {
|
||||
Serial.printf("%02X", hmacKey[i]);
|
||||
|
|
@ -217,35 +263,77 @@ void loop() {
|
|||
} else {
|
||||
Serial.println("ERROR: invalid key format");
|
||||
}
|
||||
} else if (serialBuf.startsWith("SET_ID:")) {
|
||||
String hex = serialBuf.substring(7);
|
||||
hex.toUpperCase();
|
||||
if (hex.length() == 32 && isHexKey(hex)) {
|
||||
for (int i = 0; i < ESP_UUID_LEN; i++) {
|
||||
deviceUuid[i] = (uint8_t)strtoul(hex.substring(i * 2, i * 2 + 2).c_str(), nullptr, 16);
|
||||
}
|
||||
preferences.begin(NVS_NAMESPACE, false);
|
||||
preferences.putBytes(NVS_UUID_NAME, deviceUuid, ESP_UUID_LEN);
|
||||
preferences.end();
|
||||
uuidProvisioned = true;
|
||||
Serial.println("OK");
|
||||
Serial.println("UUID stored in NVS");
|
||||
} else {
|
||||
Serial.println("ERROR: invalid UUID format");
|
||||
}
|
||||
}
|
||||
serialBuf = "";
|
||||
}
|
||||
}
|
||||
|
||||
int sensorState = digitalRead(SENSOR_PIN);
|
||||
static bool t1Stable = false;
|
||||
static bool t2Stable = false;
|
||||
static bool btnStable = false;
|
||||
static uint32_t t1Change = 0;
|
||||
static uint32_t t2Change = 0;
|
||||
static uint32_t btnChange = 0;
|
||||
#define DEBOUNCE_MS 50
|
||||
bool t1Raw = (digitalRead(TOUCH1_PIN) == LOW);
|
||||
bool t2Raw = (digitalRead(TOUCH2_PIN) == LOW);
|
||||
bool btnRaw = (digitalRead(BUTTON_PIN) == LOW);
|
||||
if (t1Raw != t1Stable) { t1Change = millis(); t1Stable = t1Raw; }
|
||||
if (t2Raw != t2Stable) { t2Change = millis(); t2Stable = t2Raw; }
|
||||
if (btnRaw != btnStable) { btnChange = millis(); btnStable = btnRaw; }
|
||||
bool t1Active = (millis() - t1Change > DEBOUNCE_MS) && t1Stable;
|
||||
bool t2Active = (millis() - t2Change > DEBOUNCE_MS) && t2Stable;
|
||||
bool btnActive = (millis() - btnChange > DEBOUNCE_MS) && btnStable;
|
||||
|
||||
bool touchActive = (sensorState == LOW);
|
||||
bool bleActive = (deviceConnected && bleCommand);
|
||||
relay1State = t1Active || btnActive || (deviceConnected && bleRelay1);
|
||||
relay2State = t2Active || (deviceConnected && bleRelay2);
|
||||
|
||||
relayState = touchActive || bleActive;
|
||||
digitalWrite(RELAY1_PIN, relay1State ? HIGH : LOW);
|
||||
digitalWrite(RELAY2_PIN, relay2State ? HIGH : LOW);
|
||||
|
||||
digitalWrite(RELAY_PIN, relayState ? HIGH : LOW);
|
||||
digitalWrite(LED_PIN, (relay1State || relay2State) ? HIGH : LOW);
|
||||
|
||||
static bool lastRelay = false;
|
||||
if (relayState != lastRelay) {
|
||||
Serial.printf("Relay %s (touch=%s, ble=%s)\n",
|
||||
relayState ? "ON" : "OFF",
|
||||
sensorState == LOW ? "YES" : "no",
|
||||
deviceConnected ? "connected" : "disconnected");
|
||||
lastRelay = relayState;
|
||||
static bool lastRelay1 = false;
|
||||
static bool lastRelay2 = false;
|
||||
if (relay1State != lastRelay1 || relay2State != lastRelay2) {
|
||||
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");
|
||||
lastRelay1 = relay1State;
|
||||
lastRelay2 = relay2State;
|
||||
}
|
||||
|
||||
static uint32_t heartbeat = 0;
|
||||
if (millis() - heartbeat > 5000) {
|
||||
Serial.printf("[heartbeat] relay=%s touch=%s ble=%s\n",
|
||||
relayState ? "ON" : "OFF",
|
||||
sensorState == LOW ? "YES" : "no",
|
||||
deviceConnected ? "connected" : "disconnected");
|
||||
Serial.printf("[heartbeat] 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");
|
||||
heartbeat = millis();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue