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);