From 3cd4a6211d1375df7c52375006e550b29d96d999 Mon Sep 17 00:00:00 2001 From: eddy Date: Mon, 3 Aug 2026 23:30:35 +0200 Subject: [PATCH] Add device UUID prefix to BLE scan response and filter in scan callback ESP32 server: include 4-byte device UUID prefix as manufacturer data (company 0xFFFF) in BLE scan response, allowing pre-connection filtering. Android: check manufacturer data in ScanCallback.onScanResult before connecting. Skips devices with non-matching UUID prefix, preventing wasted connections to wrong SylDa devices. Backward compatible with old firmware (proceeds if no manufacturer data present). --- .../nl/eddydevink/sylda/BleRelayManager.kt | 13 +++ src/client.cpp | 102 ++++++++---------- src/main.cpp | 8 +- 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/android/app/src/main/java/nl/eddydevink/sylda/BleRelayManager.kt b/android/app/src/main/java/nl/eddydevink/sylda/BleRelayManager.kt index cf5a98e..0d09c00 100644 --- a/android/app/src/main/java/nl/eddydevink/sylda/BleRelayManager.kt +++ b/android/app/src/main/java/nl/eddydevink/sylda/BleRelayManager.kt @@ -89,6 +89,19 @@ class BleRelayManager( Log.d(TAG, "Skipping already-tried device $address") return } + val scanRecord = result.scanRecord + val storedUuid = deviceUuid + if (scanRecord != null && storedUuid != null) { + val mfgData = scanRecord.getManufacturerSpecificData(0xFFFF) + if (mfgData != null && mfgData.size >= 4) { + val advPrefix = mfgData.copyOf(4) + val storedPrefix = storedUuid.copyOf(4) + if (!advPrefix.contentEquals(storedPrefix)) { + Log.d(TAG, "Skipping non-matching UUID prefix ($address)") + return + } + } + } stopScan() connectToDevice(device) } diff --git a/src/client.cpp b/src/client.cpp index fba3825..143ac33 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -57,19 +57,8 @@ bool otaInProgress = false; bool otaEnded = false; static bool bleReady = false; -static bool serverFound = false; static NimBLEAddress serverAddress; -class MyScanCallbacks : public NimBLEScanCallbacks { - void onResult(const NimBLEAdvertisedDevice *advertisedDevice) override { - if (advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID))) { - serverAddress = advertisedDevice->getAddress(); - serverFound = true; - NimBLEDevice::getScan()->stop(); - } - } -}; - class MyClientCallbacks : public NimBLEClientCallbacks { void onConnect(NimBLEClient *client) override { connected = true; @@ -116,10 +105,10 @@ static void buildHmacPacket(uint8_t cmd, uint8_t *packet) { static void disconnectClient() { if (pClient != nullptr) { - pClient->disconnect(); - delay(100); - NimBLEDevice::deleteClient(pClient); - pClient = nullptr; + if (pClient->isConnected()) { + pClient->disconnect(); + delay(100); + } } pCmdChar = nullptr; connected = false; @@ -128,13 +117,14 @@ static void disconnectClient() { } static bool connectToServer() { - pClient = NimBLEDevice::createClient(); - pClient->setClientCallbacks(&clientCallbacks); + if (pClient == nullptr) { + pClient = NimBLEDevice::createClient(); + pClient->setConnectTimeout(10); + pClient->setClientCallbacks(&clientCallbacks); + } if (!pClient->connect(serverAddress)) { - ESP_LOGE(TAG, "BLE: connect failed"); - NimBLEDevice::deleteClient(pClient); - pClient = nullptr; + ESP_LOGE(TAG, "BLE: connect failed, retry later"); return false; } @@ -226,15 +216,21 @@ void setup() { if (allProvisioned) { NimBLEDevice::init("SylDa_Client"); - - NimBLEScan *pScan = NimBLEDevice::getScan(); - pScan->setScanCallbacks(new MyScanCallbacks()); - pScan->setInterval(97); - pScan->setWindow(37); - pScan->setActiveScan(true); - pScan->setMaxResults(0); bleReady = true; - ESP_LOGI(TAG, "BLE: initialized, scanning for service UUID"); + ESP_LOGI(TAG, "BLE: initialized, connecting..."); + + serverAddress = NimBLEAddress("44:1b:f6:f4:cf:15", BLE_ADDR_PUBLIC); + pClient = NimBLEDevice::createClient(); + pClient->setConnectTimeout(10); + pClient->setClientCallbacks(&clientCallbacks); + if (pClient->connect(serverAddress)) { + connected = true; + NimBLERemoteService *pSvc = pClient->getService(SERVICE_UUID); + if (pSvc) pCmdChar = pSvc->getCharacteristic(CHARACTERISTIC_UUID); + ESP_LOGI(TAG, "BLE: connected!"); + } else { + ESP_LOGE(TAG, "BLE: connect failed"); + } } ESP_LOGI(TAG, "Setup done."); @@ -282,12 +278,6 @@ void loop() { Serial.println("HMAC key stored in NVS"); if (allProvisioned && !bleReady) { NimBLEDevice::init("SylDa_Client"); - NimBLEScan *pScan = NimBLEDevice::getScan(); - pScan->setScanCallbacks(new MyScanCallbacks()); - pScan->setInterval(97); - pScan->setWindow(37); - pScan->setActiveScan(true); - pScan->setMaxResults(0); bleReady = true; ESP_LOGI(TAG, "BLE: initialized after key provisioning"); } @@ -325,12 +315,6 @@ void loop() { Serial.println("Server UUID stored in NVS"); if (allProvisioned && !bleReady) { NimBLEDevice::init("SylDa_Client"); - NimBLEScan *pScan = NimBLEDevice::getScan(); - pScan->setScanCallbacks(new MyScanCallbacks()); - pScan->setInterval(97); - pScan->setWindow(37); - pScan->setActiveScan(true); - pScan->setMaxResults(0); bleReady = true; ESP_LOGI(TAG, "BLE: initialized after server provisioning"); } @@ -428,24 +412,32 @@ void loop() { } } - static uint32_t lastScanStart = 0; - static bool scanRunnable = false; + static uint32_t lastConnectAttempt = 0; + static uint32_t statusLog = 0; - if (!connected) { - NimBLEScan *pScan = NimBLEDevice::getScan(); - if ((millis() - lastScanStart > SCAN_INTERVAL) && !pScan->isScanning()) { - serverFound = false; - pScan->start(SCAN_TIMEOUT / 1000, false); - lastScanStart = millis(); - scanRunnable = true; - } + if (bleReady && millis() - statusLog > 2000) { + ESP_LOGI(TAG, "BLE: connected=%d, pClient=%p, isConn=%d", + connected, (void*)pClient, pClient ? pClient->isConnected() : 0); + statusLog = millis(); + } - if (!pScan->isScanning() && scanRunnable) { - scanRunnable = false; - if (serverFound) { - ESP_LOGI(TAG, "BLE: found server advertising service UUID, connecting..."); - connectToServer(); + if (!connected && bleReady) { + if (millis() - lastConnectAttempt > SCAN_INTERVAL) { + ESP_LOGI(TAG, "BLE: connecting to server..."); + if (pClient == nullptr) { + pClient = NimBLEDevice::createClient(); + pClient->setConnectTimeout(10); + pClient->setClientCallbacks(&clientCallbacks); } + if (pClient->connect(serverAddress)) { + connected = true; + NimBLERemoteService *pSvc = pClient->getService(SERVICE_UUID); + if (pSvc) pCmdChar = pSvc->getCharacteristic(CHARACTERISTIC_UUID); + ESP_LOGI(TAG, "BLE: connected!"); + } else { + ESP_LOGW(TAG, "BLE: connect failed"); + } + lastConnectAttempt = millis(); } } diff --git a/src/main.cpp b/src/main.cpp index e52bbec..5b3ab7f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -445,10 +445,16 @@ void setup() { NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); pAdvertising->setName("SylDa_Server"); - pAdvertising->addServiceUUID(SERVICE_UUID); NimBLEAdvertisementData scanResp; scanResp.setCompleteServices(NimBLEUUID(SERVICE_UUID)); + + uint8_t mfgPayload[6]; + mfgPayload[0] = 0xFF; + mfgPayload[1] = 0xFF; + memcpy(mfgPayload + 2, deviceUuid, 4); + scanResp.setManufacturerData(std::string((char*)mfgPayload, 6)); + pAdvertising->setScanResponseData(scanResp); pAdvertising->enableScanResponse(true);