From c1cc621fb79d8d5ebc4c40a1dcab824984a1d77c Mon Sep 17 00:00:00 2001
From: Darrell
Date: Mon, 26 Dec 2022 19:03:31 -0500
Subject: [PATCH] Have nodes broadcast an ibeacon advertisement (#763)
---
lib/BleFingerprint/util.h | 1 +
src/Enrollment.cpp | 537 ++++++++++++++++++--------------------
src/mqtt.cpp | 3 +-
src/mqtt.h | 2 +-
4 files changed, 265 insertions(+), 278 deletions(-)
diff --git a/lib/BleFingerprint/util.h b/lib/BleFingerprint/util.h
index 9b524e9..6a772c1 100644
--- a/lib/BleFingerprint/util.h
+++ b/lib/BleFingerprint/util.h
@@ -14,6 +14,7 @@ const BLEUUID miThermUUID(uint16_t(0x181A));
const BLEUUID trackrUUID((uint16_t)0x0F3E);
const BLEUUID vanmoofUUID(0x6acc5540, 0xe631, 0x4069, 0x944db8ca7598ad50);
const BLEUUID tractiveUUID(0x20130001, 0x0719, 0x4b6e, 0xbe5d158ab92fa5a4);
+const BLEUUID espresenseUUID(0xe5ca1ade, 0xf007, 0xba11, 0x0000000000000000);
const BLEUUID nutUUID((uint16_t)0x1803);
diff --git a/src/Enrollment.cpp b/src/Enrollment.cpp
index 60c34da..0be4d34 100644
--- a/src/Enrollment.cpp
+++ b/src/Enrollment.cpp
@@ -1,292 +1,277 @@
#include "Enrollment.h"
-#include "globals.h"
-#include "mqtt.h"
-#include "HttpWebServer.h"
-#include
#include
-#include
#include
#include
+#include
+#include
-namespace Enrollment
-{
- static const char hex_digits[] = "0123456789abcdef";
- static bool lastEnrolling = false;
- static NimBLEServer *pServer;
- static String id;
- static unsigned long lastLoop = 0;
- static int connectionToEnroll = -1;
+#include "HttpWebServer.h"
+#include "globals.h"
+#include "mqtt.h"
+#include "util.h"
- class ServerCallbacks : public NimBLEServerCallbacks
- {
- void onConnect(NimBLEServer *pServer)
- {
- Serial.println("Client connected");
- if (enrolling)
- {
- Serial.println("Multi-connect support: start advertising");
- NimBLEDevice::startAdvertising();
- }
- };
+namespace Enrollment {
+static const char hex_digits[] = "0123456789abcdef";
+static bool lastEnrolling = true;
+static String name;
+static unsigned long lastLoop = 0;
+static int connectionToEnroll = -1;
+static uint16_t major, minor;
+static NimBLEServer *pServer;
+static NimBLEAdvertisementData *oAdvertisementData;
+static NimBLEService *heartRate;
+static NimBLEService *deviceInfo;
- void onConnect(NimBLEServer *pServer, ble_gap_conn_desc *desc)
- {
- Serial.print("Client address: ");
- Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str());
- if (enrolling) {
- NimBLEDevice::startSecurity(desc->conn_handle);
- connectionToEnroll = desc->conn_handle;
- }
- };
-
- void onDisconnect(NimBLEServer *pServer)
- {
- if (enrolling) {
- Serial.println("Client disconnected - start advertising");
- NimBLEDevice::startAdvertising();
- }
- };
-
- void onMTUChange(uint16_t MTU, ble_gap_conn_desc *desc)
- {
- Serial.printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle);
- };
-
- void onAuthenticationComplete(ble_gap_conn_desc *desc)
- {
- if (!desc->sec_state.encrypted) Serial.printf("Encrypt connection failed conn: %d!\n", desc->conn_handle);
- else Serial.printf("Encrypt connection success conn: %d!\n", desc->conn_handle);
- };
- };
-
- class CharacteristicCallbacks : public NimBLECharacteristicCallbacks
- {
- void onRead(NimBLECharacteristic *pCharacteristic)
- {
- Serial.print(pCharacteristic->getUUID().toString().c_str());
- Serial.print(": onRead(), value: ");
- Serial.println(pCharacteristic->getValue().c_str());
- };
-
- void onWrite(NimBLECharacteristic *pCharacteristic)
- {
- Serial.print(pCharacteristic->getUUID().toString().c_str());
- Serial.print(": onWrite(), value: ");
- Serial.println(pCharacteristic->getValue().c_str());
- };
-
- void onNotify(NimBLECharacteristic *pCharacteristic)
- {
- //Serial.println("Sending notification to clients");
- };
-
- void onStatus(NimBLECharacteristic *pCharacteristic, Status status, int code)
- {
-/* String str = ("Notification/Indication status code: ");
- str += status;
- str += ", return code: ";
- str += code;
- str += ", ";
- str += NimBLEUtils::returnCodeToString(code);
- Serial.println(str); */
- };
-
- void onSubscribe(NimBLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue)
- {
- String str = "Client ID: ";
- str += desc->conn_handle;
- str += " Address: ";
- str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str();
- if (subValue == 0)
- {
- str += " Unsubscribed to ";
- }
- else if (subValue == 1)
- {
- str += " Subscribed to notfications for ";
- }
- else if (subValue == 2)
- {
- str += " Subscribed to indications for ";
- }
- else if (subValue == 3)
- {
- str += " Subscribed to notifications and indications for ";
- }
- str += std::string(pCharacteristic->getUUID()).c_str();
- Serial.println(str);
- };
- };
-
- class DescriptorCallbacks : public NimBLEDescriptorCallbacks
- {
- void onWrite(NimBLEDescriptor *pDescriptor)
- {
- std::string dscVal = pDescriptor->getValue();
- Serial.print("Descriptor witten value:");
- Serial.println(dscVal.c_str());
- };
-
- void onRead(NimBLEDescriptor *pDescriptor)
- {
- Serial.print(pDescriptor->getUUID().toString().c_str());
- Serial.println(" Descriptor read");
- };
- };
-
-
- static DescriptorCallbacks dscCallbacks;
- static CharacteristicCallbacks chrCallbacks;
-
- static int ble_sm_read_bond(uint16_t conn_handle, struct ble_store_value_sec *out_bond)
- {
- struct ble_store_key_sec key_sec;
- struct ble_gap_conn_desc desc;
- int rc;
-
- rc = ble_gap_conn_find(conn_handle, &desc);
- if (rc != 0)
- {
- return rc;
+class ServerCallbacks : public NimBLEServerCallbacks {
+ void onConnect(NimBLEServer *pServer) {
+ Serial.println("Client connected");
+ if (enrolling) {
+ Serial.println("Multi-connect support: start advertising");
+ NimBLEDevice::startAdvertising();
}
+ };
- memset(&key_sec, 0, sizeof key_sec);
- key_sec.peer_addr = desc.peer_id_addr;
+ void onConnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) {
+ Serial.print("Client address: ");
+ Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str());
+ if (enrolling) {
+ NimBLEDevice::startSecurity(desc->conn_handle);
+ connectionToEnroll = desc->conn_handle;
+ }
+ };
- rc = ble_store_read_peer_sec(&key_sec, out_bond);
+ void onDisconnect(NimBLEServer *pServer) {
+ if (enrolling) {
+ Serial.println("Client disconnected - start advertising");
+ NimBLEDevice::startAdvertising();
+ }
+ };
+
+ void onMTUChange(uint16_t MTU, ble_gap_conn_desc *desc) {
+ Serial.printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle);
+ };
+
+ void onAuthenticationComplete(ble_gap_conn_desc *desc) {
+ if (!desc->sec_state.encrypted)
+ Serial.printf("Encrypt connection failed conn: %d!\n", desc->conn_handle);
+ else
+ Serial.printf("Encrypt connection success conn: %d!\n", desc->conn_handle);
+ };
+};
+
+class CharacteristicCallbacks : public NimBLECharacteristicCallbacks {
+ void onRead(NimBLECharacteristic *pCharacteristic) {
+ Serial.print(pCharacteristic->getUUID().toString().c_str());
+ Serial.print(": onRead(), value: ");
+ Serial.println(pCharacteristic->getValue().c_str());
+ };
+
+ void onWrite(NimBLECharacteristic *pCharacteristic) {
+ Serial.print(pCharacteristic->getUUID().toString().c_str());
+ Serial.print(": onWrite(), value: ");
+ Serial.println(pCharacteristic->getValue().c_str());
+ };
+
+ void onNotify(NimBLECharacteristic *pCharacteristic){
+ // Serial.println("Sending notification to clients");
+ };
+
+ void onStatus(NimBLECharacteristic *pCharacteristic, Status status, int code){
+ /* String str = ("Notification/Indication status code: ");
+ str += status;
+ str += ", return code: ";
+ str += code;
+ str += ", ";
+ str += NimBLEUtils::returnCodeToString(code);
+ Serial.println(str); */
+ };
+
+ void onSubscribe(NimBLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue) {
+ String str = "Client ID: ";
+ str += desc->conn_handle;
+ str += " Address: ";
+ str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str();
+ if (subValue == 0) {
+ str += " Unsubscribed to ";
+ } else if (subValue == 1) {
+ str += " Subscribed to notfications for ";
+ } else if (subValue == 2) {
+ str += " Subscribed to indications for ";
+ } else if (subValue == 3) {
+ str += " Subscribed to notifications and indications for ";
+ }
+ str += std::string(pCharacteristic->getUUID()).c_str();
+ Serial.println(str);
+ };
+};
+
+class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
+ void onWrite(NimBLEDescriptor *pDescriptor) {
+ std::string dscVal = pDescriptor->getValue();
+ Serial.print("Descriptor witten value:");
+ Serial.println(dscVal.c_str());
+ };
+
+ void onRead(NimBLEDescriptor *pDescriptor) {
+ Serial.print(pDescriptor->getUUID().toString().c_str());
+ Serial.println(" Descriptor read");
+ };
+};
+
+static DescriptorCallbacks dscCallbacks;
+static CharacteristicCallbacks chrCallbacks;
+
+static int ble_sm_read_bond(uint16_t conn_handle, struct ble_store_value_sec *out_bond) {
+ struct ble_store_key_sec key_sec;
+ struct ble_gap_conn_desc desc;
+ int rc;
+
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ if (rc != 0) {
return rc;
}
- bool tryGetIrkFromConnection(uint16_t conn_handle, std::string &irk)
- {
- struct ble_store_value_sec bond;
- int rc;
+ memset(&key_sec, 0, sizeof key_sec);
+ key_sec.peer_addr = desc.peer_id_addr;
- rc = ble_sm_read_bond(conn_handle, &bond);
- if (rc != 0)
- return false;
-
- if (!bond.irk_present)
- return false;
-
- std::string output;
- output.reserve(32);
- for (int i = 0; i < 16; i++)
- {
- auto c = bond.irk[15 - i];
- output.push_back(hex_digits[c >> 4]);
- output.push_back(hex_digits[c & 15]);
- }
- irk = output;
- return true;
- }
-
- void Setup() {
- NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT);
- NimBLEDevice::setSecurityAuth(true, true, true);
-
- pServer = NimBLEDevice::createServer();
- pServer->setCallbacks(new ServerCallbacks());
-
- NimBLEService *heartRate = pServer->createService("180D");
- NimBLECharacteristic *bpm = heartRate->createCharacteristic("2A37", NIMBLE_PROPERTY::NOTIFY, 2);
- bpm->setCallbacks(&chrCallbacks);
-
- NimBLEService *deviceInfo = pServer->createService("180A");
- NimBLECharacteristic *manufName = deviceInfo->createCharacteristic("2A29", NIMBLE_PROPERTY::READ);
- manufName->setValue("ESPresense");
- manufName->setCallbacks(&chrCallbacks);
- NimBLECharacteristic *appearance = deviceInfo->createCharacteristic("2A01", NIMBLE_PROPERTY::READ, 2);
- appearance->setValue((int16_t) 0x4142);
- appearance->setCallbacks(&chrCallbacks);
- NimBLECharacteristic *modelNum = deviceInfo->createCharacteristic("2A24", NIMBLE_PROPERTY::READ);
- modelNum->setValue(std::string(room.c_str()));
- modelNum->setCallbacks(&chrCallbacks);
-
- heartRate->start();
- deviceInfo->start();
-
- NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
- pAdvertising->addServiceUUID(heartRate->getUUID());
- pAdvertising->setScanResponse(true);
-
- pServer->start();
- }
-
- bool Loop()
- {
- if (enrolling != lastEnrolling)
- {
- HttpWebServer::SendState();
- auto pAdvertising = NimBLEDevice::getAdvertising();
- if (enrolling)
- {
- pAdvertising->start();
- Serial.println("Advertising started");
- }
- else
- {
- pAdvertising->stop();
- Serial.println("Advertising stopped");
- }
- lastEnrolling = enrolling;
- if (enrolling) enrollingEndMillis = millis() + 120000;
- }
-
- if (enrolling && enrollingEndMillis < millis())
- {
- enrolling = false;
- }
-
- if (millis() - lastLoop > 1000)
- {
- lastLoop = millis();
-
- if (enrolling) HttpWebServer::SendState();
- if (pServer->getConnectedCount())
- {
- NimBLEService *pSvc = pServer->getServiceByUUID("180D");
- if (pSvc)
- {
- NimBLECharacteristic *pChr = pSvc->getCharacteristic("2A37");
- if (pChr)
- {
- pChr->setValue((short) (micros() && 0x00FF));
- pChr->notify(true);
- }
- }
-
- if (enrolling && connectionToEnroll > -1)
- {
- std::string irk;
- if (tryGetIrkFromConnection(connectionToEnroll, irk))
- {
- alias(String("irk:") + irk.c_str(), id.isEmpty() ? (String("irk:") + irk.c_str()) : id);
- enrolling = false;
- pServer->disconnect(connectionToEnroll);
- connectionToEnroll = -1;
- }
- }
- }
- }
-
- return true;
- }
-
- bool Command(String& command, String& pay)
- {
- if (command == "enroll")
- {
- id = pay.equals("PRESS") ? "" : pay;
- enrolling = true;
- return true;
- }
- return false;
- }
-
- bool SendDiscovery()
- {
- return sendButtonDiscovery("Enroll", EC_CONFIG);
- }
+ rc = ble_store_read_peer_sec(&key_sec, out_bond);
+ return rc;
}
+
+bool tryGetIrkFromConnection(uint16_t conn_handle, std::string &irk) {
+ struct ble_store_value_sec bond;
+ int rc;
+
+ rc = ble_sm_read_bond(conn_handle, &bond);
+ if (rc != 0)
+ return false;
+
+ if (!bond.irk_present)
+ return false;
+
+ std::string output;
+ output.reserve(32);
+ for (int i = 0; i < 16; i++) {
+ auto c = bond.irk[15 - i];
+ output.push_back(hex_digits[c >> 4]);
+ output.push_back(hex_digits[c & 15]);
+ }
+ irk = output;
+ return true;
+}
+
+void Setup() {
+ NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT);
+ NimBLEDevice::setSecurityAuth(true, true, true);
+
+ pServer = NimBLEDevice::createServer();
+ pServer->setCallbacks(new ServerCallbacks());
+
+ heartRate = pServer->createService("180D");
+ NimBLECharacteristic *bpm = heartRate->createCharacteristic("2A37", NIMBLE_PROPERTY::NOTIFY, 2);
+ bpm->setCallbacks(&chrCallbacks);
+
+ deviceInfo = pServer->createService("180A");
+ NimBLECharacteristic *manufName = deviceInfo->createCharacteristic("2A29", NIMBLE_PROPERTY::READ);
+ manufName->setValue("ESPresense");
+ manufName->setCallbacks(&chrCallbacks);
+ NimBLECharacteristic *appearance = deviceInfo->createCharacteristic("2A01", NIMBLE_PROPERTY::READ, 2);
+ appearance->setValue((int16_t)0x4142);
+ appearance->setCallbacks(&chrCallbacks);
+ NimBLECharacteristic *modelNum = deviceInfo->createCharacteristic("2A24", NIMBLE_PROPERTY::READ);
+ modelNum->setValue(std::string(room.c_str()));
+ modelNum->setCallbacks(&chrCallbacks);
+
+ heartRate->start();
+ deviceInfo->start();
+
+ uint32_t nodeId = (uint32_t)(ESP.getEfuseMac() >> 24);
+ major = (nodeId & 0xFFFF0000) >> 16;
+ minor = nodeId & 0xFFFF;
+
+ BLEBeacon oBeacon = BLEBeacon();
+ oBeacon.setManufacturerId(0x4C00);
+ oBeacon.setProximityUUID(espresenseUUID);
+ oBeacon.setMajor(major);
+ oBeacon.setMinor(minor);
+ oBeacon.setSignalPower(-59);
+ oAdvertisementData = new NimBLEAdvertisementData();
+ oAdvertisementData->setFlags(BLE_HS_ADV_F_BREDR_UNSUP);
+ oAdvertisementData->setManufacturerData(oBeacon.getData());
+
+ pServer->start();
+}
+
+bool Loop() {
+ if (enrolling != lastEnrolling) {
+ HttpWebServer::SendState();
+ auto pAdvertising = NimBLEDevice::getAdvertising();
+ if (enrolling) {
+ pAdvertising->reset();
+ pAdvertising->setScanResponse(true);
+ pAdvertising->setAdvertisementType(BLE_GAP_CONN_MODE_UND);
+ pAdvertising->addServiceUUID(heartRate->getUUID());
+ pAdvertising->start();
+ Serial.println("Advertising HRM");
+ } else {
+ alias(Sprintf("iBeacon:e5ca1ade-f007-ba11-0000-000000000000-%hu-%hu", major, minor), "node:" + id, room);
+
+ pAdvertising->reset();
+ pAdvertising->setScanResponse(false);
+ pAdvertising->setAdvertisementType(BLE_GAP_CONN_MODE_NON);
+ pAdvertising->setAdvertisementData(*oAdvertisementData);
+ pAdvertising->start();
+ Serial.println("Advertising iBeacon");
+ }
+ lastEnrolling = enrolling;
+ if (enrolling) enrollingEndMillis = millis() + 120000;
+ }
+
+ if (enrolling && enrollingEndMillis < millis()) {
+ enrolling = false;
+ }
+
+ if (millis() - lastLoop > 1000) {
+ lastLoop = millis();
+
+ if (enrolling) HttpWebServer::SendState();
+ if (pServer->getConnectedCount()) {
+ NimBLEService *pSvc = pServer->getServiceByUUID("180D");
+ if (pSvc) {
+ NimBLECharacteristic *pChr = pSvc->getCharacteristic("2A37");
+ if (pChr) {
+ pChr->setValue((short)(micros() && 0x00FF));
+ pChr->notify(true);
+ }
+ }
+
+ if (enrolling && connectionToEnroll > -1) {
+ std::string irk;
+ if (tryGetIrkFromConnection(connectionToEnroll, irk)) {
+ auto id = name.isEmpty() ? "" : slugify(name);
+ alias(String("irk:") + irk.c_str(), id.isEmpty() ? (String("irk:") + irk.c_str()) : id, name);
+ enrolling = false;
+ pServer->disconnect(connectionToEnroll);
+ connectionToEnroll = -1;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool Command(String &command, String &pay) {
+ if (command == "enroll") {
+ name = pay.equals("PRESS") ? "" : pay;
+ enrolling = true;
+ return true;
+ }
+ return false;
+}
+
+bool SendDiscovery() {
+ return sendButtonDiscovery("Enroll", EC_CONFIG);
+}
+} // namespace Enrollment
diff --git a/src/mqtt.cpp b/src/mqtt.cpp
index 1b3b648..5eeb9ff 100644
--- a/src/mqtt.cpp
+++ b/src/mqtt.cpp
@@ -212,11 +212,12 @@ bool sendDeleteDiscovery(const String &domain, const String &name)
return pub(discoveryTopic.c_str(), 0, false, "");
}
-bool alias(const String &alias, const String &id)
+bool alias(const String &alias, const String &id, const String &name = "")
{
Serial.printf("Setting %s->%s\n", alias.c_str(), id.c_str());
doc.clear();
doc["id"] = id;
+ doc["name"] = name;
serializeJson(doc, buffer);
String settingsTopic = CHANNEL + String("/settings/") + alias + "/config";
return pub(settingsTopic.c_str(), 0, true, buffer);
diff --git a/src/mqtt.h b/src/mqtt.h
index 7c4e768..8fcbdd9 100644
--- a/src/mqtt.h
+++ b/src/mqtt.h
@@ -25,4 +25,4 @@ bool sendLightDiscovery(const String &name, const String &entityCategory, bool r
bool sendDeleteDiscovery(const String &domain, const String &name);
-bool alias(const String &alias, const String &id);
+bool alias(const String &alias, const String &id, const String &name);