Have nodes broadcast an ibeacon advertisement (#763)

This commit is contained in:
Darrell 2022-12-26 19:03:31 -05:00 committed by GitHub
parent 77373d83cb
commit c1cc621fb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 265 additions and 278 deletions

View File

@ -14,6 +14,7 @@ const BLEUUID miThermUUID(uint16_t(0x181A));
const BLEUUID trackrUUID((uint16_t)0x0F3E); const BLEUUID trackrUUID((uint16_t)0x0F3E);
const BLEUUID vanmoofUUID(0x6acc5540, 0xe631, 0x4069, 0x944db8ca7598ad50); const BLEUUID vanmoofUUID(0x6acc5540, 0xe631, 0x4069, 0x944db8ca7598ad50);
const BLEUUID tractiveUUID(0x20130001, 0x0719, 0x4b6e, 0xbe5d158ab92fa5a4); const BLEUUID tractiveUUID(0x20130001, 0x0719, 0x4b6e, 0xbe5d158ab92fa5a4);
const BLEUUID espresenseUUID(0xe5ca1ade, 0xf007, 0xba11, 0x0000000000000000);
const BLEUUID nutUUID((uint16_t)0x1803); const BLEUUID nutUUID((uint16_t)0x1803);

View File

@ -1,37 +1,38 @@
#include "Enrollment.h" #include "Enrollment.h"
#include "globals.h"
#include "mqtt.h"
#include "HttpWebServer.h"
#include <NimBLEDevice.h>
#include <NimBLEAdvertisedDevice.h> #include <NimBLEAdvertisedDevice.h>
#include <NimBLEService.h>
#include <NimBLECharacteristic.h> #include <NimBLECharacteristic.h>
#include <NimBLEDescriptor.h> #include <NimBLEDescriptor.h>
#include <NimBLEDevice.h>
#include <NimBLEService.h>
namespace Enrollment #include "HttpWebServer.h"
{ #include "globals.h"
static const char hex_digits[] = "0123456789abcdef"; #include "mqtt.h"
static bool lastEnrolling = false; #include "util.h"
static NimBLEServer *pServer;
static String id;
static unsigned long lastLoop = 0;
static int connectionToEnroll = -1;
class ServerCallbacks : public NimBLEServerCallbacks namespace Enrollment {
{ static const char hex_digits[] = "0123456789abcdef";
void onConnect(NimBLEServer *pServer) 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;
class ServerCallbacks : public NimBLEServerCallbacks {
void onConnect(NimBLEServer *pServer) {
Serial.println("Client connected"); Serial.println("Client connected");
if (enrolling) if (enrolling) {
{
Serial.println("Multi-connect support: start advertising"); Serial.println("Multi-connect support: start advertising");
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
} }
}; };
void onConnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) void onConnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) {
{
Serial.print("Client address: "); Serial.print("Client address: ");
Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str()); Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str());
if (enrolling) { if (enrolling) {
@ -40,50 +41,44 @@ namespace Enrollment
} }
}; };
void onDisconnect(NimBLEServer *pServer) void onDisconnect(NimBLEServer *pServer) {
{
if (enrolling) { if (enrolling) {
Serial.println("Client disconnected - start advertising"); Serial.println("Client disconnected - start advertising");
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
} }
}; };
void onMTUChange(uint16_t MTU, ble_gap_conn_desc *desc) void onMTUChange(uint16_t MTU, ble_gap_conn_desc *desc) {
{
Serial.printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle); Serial.printf("MTU updated: %u for connection ID: %u\n", MTU, desc->conn_handle);
}; };
void onAuthenticationComplete(ble_gap_conn_desc *desc) void onAuthenticationComplete(ble_gap_conn_desc *desc) {
{ if (!desc->sec_state.encrypted)
if (!desc->sec_state.encrypted) Serial.printf("Encrypt connection failed conn: %d!\n", desc->conn_handle); Serial.printf("Encrypt connection failed conn: %d!\n", desc->conn_handle);
else Serial.printf("Encrypt connection success conn: %d!\n", desc->conn_handle); else
}; Serial.printf("Encrypt connection success conn: %d!\n", desc->conn_handle);
}; };
};
class CharacteristicCallbacks : public NimBLECharacteristicCallbacks class CharacteristicCallbacks : public NimBLECharacteristicCallbacks {
{ void onRead(NimBLECharacteristic *pCharacteristic) {
void onRead(NimBLECharacteristic *pCharacteristic)
{
Serial.print(pCharacteristic->getUUID().toString().c_str()); Serial.print(pCharacteristic->getUUID().toString().c_str());
Serial.print(": onRead(), value: "); Serial.print(": onRead(), value: ");
Serial.println(pCharacteristic->getValue().c_str()); Serial.println(pCharacteristic->getValue().c_str());
}; };
void onWrite(NimBLECharacteristic *pCharacteristic) void onWrite(NimBLECharacteristic *pCharacteristic) {
{
Serial.print(pCharacteristic->getUUID().toString().c_str()); Serial.print(pCharacteristic->getUUID().toString().c_str());
Serial.print(": onWrite(), value: "); Serial.print(": onWrite(), value: ");
Serial.println(pCharacteristic->getValue().c_str()); Serial.println(pCharacteristic->getValue().c_str());
}; };
void onNotify(NimBLECharacteristic *pCharacteristic) void onNotify(NimBLECharacteristic *pCharacteristic){
{ // Serial.println("Sending notification to clients");
//Serial.println("Sending notification to clients");
}; };
void onStatus(NimBLECharacteristic *pCharacteristic, Status status, int code) void onStatus(NimBLECharacteristic *pCharacteristic, Status status, int code){
{ /* String str = ("Notification/Indication status code: ");
/* String str = ("Notification/Indication status code: ");
str += status; str += status;
str += ", return code: "; str += ", return code: ";
str += code; str += code;
@ -92,62 +87,48 @@ namespace Enrollment
Serial.println(str); */ Serial.println(str); */
}; };
void onSubscribe(NimBLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue) void onSubscribe(NimBLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue) {
{
String str = "Client ID: "; String str = "Client ID: ";
str += desc->conn_handle; str += desc->conn_handle;
str += " Address: "; str += " Address: ";
str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str(); str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str();
if (subValue == 0) if (subValue == 0) {
{
str += " Unsubscribed to "; str += " Unsubscribed to ";
} } else if (subValue == 1) {
else if (subValue == 1)
{
str += " Subscribed to notfications for "; str += " Subscribed to notfications for ";
} } else if (subValue == 2) {
else if (subValue == 2)
{
str += " Subscribed to indications for "; str += " Subscribed to indications for ";
} } else if (subValue == 3) {
else if (subValue == 3)
{
str += " Subscribed to notifications and indications for "; str += " Subscribed to notifications and indications for ";
} }
str += std::string(pCharacteristic->getUUID()).c_str(); str += std::string(pCharacteristic->getUUID()).c_str();
Serial.println(str); Serial.println(str);
}; };
}; };
class DescriptorCallbacks : public NimBLEDescriptorCallbacks class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
{ void onWrite(NimBLEDescriptor *pDescriptor) {
void onWrite(NimBLEDescriptor *pDescriptor)
{
std::string dscVal = pDescriptor->getValue(); std::string dscVal = pDescriptor->getValue();
Serial.print("Descriptor witten value:"); Serial.print("Descriptor witten value:");
Serial.println(dscVal.c_str()); Serial.println(dscVal.c_str());
}; };
void onRead(NimBLEDescriptor *pDescriptor) void onRead(NimBLEDescriptor *pDescriptor) {
{
Serial.print(pDescriptor->getUUID().toString().c_str()); Serial.print(pDescriptor->getUUID().toString().c_str());
Serial.println(" Descriptor read"); Serial.println(" Descriptor read");
}; };
}; };
static DescriptorCallbacks dscCallbacks;
static CharacteristicCallbacks chrCallbacks;
static DescriptorCallbacks dscCallbacks; static int ble_sm_read_bond(uint16_t conn_handle, struct ble_store_value_sec *out_bond) {
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_store_key_sec key_sec;
struct ble_gap_conn_desc desc; struct ble_gap_conn_desc desc;
int rc; int rc;
rc = ble_gap_conn_find(conn_handle, &desc); rc = ble_gap_conn_find(conn_handle, &desc);
if (rc != 0) if (rc != 0) {
{
return rc; return rc;
} }
@ -156,10 +137,9 @@ namespace Enrollment
rc = ble_store_read_peer_sec(&key_sec, out_bond); rc = ble_store_read_peer_sec(&key_sec, out_bond);
return rc; return rc;
} }
bool tryGetIrkFromConnection(uint16_t conn_handle, std::string &irk) bool tryGetIrkFromConnection(uint16_t conn_handle, std::string &irk) {
{
struct ble_store_value_sec bond; struct ble_store_value_sec bond;
int rc; int rc;
@ -172,33 +152,32 @@ namespace Enrollment
std::string output; std::string output;
output.reserve(32); output.reserve(32);
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++) {
{
auto c = bond.irk[15 - i]; auto c = bond.irk[15 - i];
output.push_back(hex_digits[c >> 4]); output.push_back(hex_digits[c >> 4]);
output.push_back(hex_digits[c & 15]); output.push_back(hex_digits[c & 15]);
} }
irk = output; irk = output;
return true; return true;
} }
void Setup() { void Setup() {
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT); NimBLEDevice::setSecurityIOCap(BLE_HS_IO_NO_INPUT_OUTPUT);
NimBLEDevice::setSecurityAuth(true, true, true); NimBLEDevice::setSecurityAuth(true, true, true);
pServer = NimBLEDevice::createServer(); pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks()); pServer->setCallbacks(new ServerCallbacks());
NimBLEService *heartRate = pServer->createService("180D"); heartRate = pServer->createService("180D");
NimBLECharacteristic *bpm = heartRate->createCharacteristic("2A37", NIMBLE_PROPERTY::NOTIFY, 2); NimBLECharacteristic *bpm = heartRate->createCharacteristic("2A37", NIMBLE_PROPERTY::NOTIFY, 2);
bpm->setCallbacks(&chrCallbacks); bpm->setCallbacks(&chrCallbacks);
NimBLEService *deviceInfo = pServer->createService("180A"); deviceInfo = pServer->createService("180A");
NimBLECharacteristic *manufName = deviceInfo->createCharacteristic("2A29", NIMBLE_PROPERTY::READ); NimBLECharacteristic *manufName = deviceInfo->createCharacteristic("2A29", NIMBLE_PROPERTY::READ);
manufName->setValue("ESPresense"); manufName->setValue("ESPresense");
manufName->setCallbacks(&chrCallbacks); manufName->setCallbacks(&chrCallbacks);
NimBLECharacteristic *appearance = deviceInfo->createCharacteristic("2A01", NIMBLE_PROPERTY::READ, 2); NimBLECharacteristic *appearance = deviceInfo->createCharacteristic("2A01", NIMBLE_PROPERTY::READ, 2);
appearance->setValue((int16_t) 0x4142); appearance->setValue((int16_t)0x4142);
appearance->setCallbacks(&chrCallbacks); appearance->setCallbacks(&chrCallbacks);
NimBLECharacteristic *modelNum = deviceInfo->createCharacteristic("2A24", NIMBLE_PROPERTY::READ); NimBLECharacteristic *modelNum = deviceInfo->createCharacteristic("2A24", NIMBLE_PROPERTY::READ);
modelNum->setValue(std::string(room.c_str())); modelNum->setValue(std::string(room.c_str()));
@ -207,62 +186,71 @@ namespace Enrollment
heartRate->start(); heartRate->start();
deviceInfo->start(); deviceInfo->start();
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); uint32_t nodeId = (uint32_t)(ESP.getEfuseMac() >> 24);
pAdvertising->addServiceUUID(heartRate->getUUID()); major = (nodeId & 0xFFFF0000) >> 16;
pAdvertising->setScanResponse(true); 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(); pServer->start();
} }
bool Loop() bool Loop() {
{ if (enrolling != lastEnrolling) {
if (enrolling != lastEnrolling)
{
HttpWebServer::SendState(); HttpWebServer::SendState();
auto pAdvertising = NimBLEDevice::getAdvertising(); auto pAdvertising = NimBLEDevice::getAdvertising();
if (enrolling) if (enrolling) {
{ pAdvertising->reset();
pAdvertising->setScanResponse(true);
pAdvertising->setAdvertisementType(BLE_GAP_CONN_MODE_UND);
pAdvertising->addServiceUUID(heartRate->getUUID());
pAdvertising->start(); pAdvertising->start();
Serial.println("Advertising started"); Serial.println("Advertising HRM");
} } else {
else alias(Sprintf("iBeacon:e5ca1ade-f007-ba11-0000-000000000000-%hu-%hu", major, minor), "node:" + id, room);
{
pAdvertising->stop(); pAdvertising->reset();
Serial.println("Advertising stopped"); pAdvertising->setScanResponse(false);
pAdvertising->setAdvertisementType(BLE_GAP_CONN_MODE_NON);
pAdvertising->setAdvertisementData(*oAdvertisementData);
pAdvertising->start();
Serial.println("Advertising iBeacon");
} }
lastEnrolling = enrolling; lastEnrolling = enrolling;
if (enrolling) enrollingEndMillis = millis() + 120000; if (enrolling) enrollingEndMillis = millis() + 120000;
} }
if (enrolling && enrollingEndMillis < millis()) if (enrolling && enrollingEndMillis < millis()) {
{
enrolling = false; enrolling = false;
} }
if (millis() - lastLoop > 1000) if (millis() - lastLoop > 1000) {
{
lastLoop = millis(); lastLoop = millis();
if (enrolling) HttpWebServer::SendState(); if (enrolling) HttpWebServer::SendState();
if (pServer->getConnectedCount()) if (pServer->getConnectedCount()) {
{
NimBLEService *pSvc = pServer->getServiceByUUID("180D"); NimBLEService *pSvc = pServer->getServiceByUUID("180D");
if (pSvc) if (pSvc) {
{
NimBLECharacteristic *pChr = pSvc->getCharacteristic("2A37"); NimBLECharacteristic *pChr = pSvc->getCharacteristic("2A37");
if (pChr) if (pChr) {
{ pChr->setValue((short)(micros() && 0x00FF));
pChr->setValue((short) (micros() && 0x00FF));
pChr->notify(true); pChr->notify(true);
} }
} }
if (enrolling && connectionToEnroll > -1) if (enrolling && connectionToEnroll > -1) {
{
std::string irk; std::string irk;
if (tryGetIrkFromConnection(connectionToEnroll, 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); alias(String("irk:") + irk.c_str(), id.isEmpty() ? (String("irk:") + irk.c_str()) : id, name);
enrolling = false; enrolling = false;
pServer->disconnect(connectionToEnroll); pServer->disconnect(connectionToEnroll);
connectionToEnroll = -1; connectionToEnroll = -1;
@ -272,21 +260,18 @@ namespace Enrollment
} }
return true; return true;
} }
bool Command(String& command, String& pay) bool Command(String &command, String &pay) {
{ if (command == "enroll") {
if (command == "enroll") name = pay.equals("PRESS") ? "" : pay;
{
id = pay.equals("PRESS") ? "" : pay;
enrolling = true; enrolling = true;
return true; return true;
} }
return false; return false;
}
bool SendDiscovery()
{
return sendButtonDiscovery("Enroll", EC_CONFIG);
}
} }
bool SendDiscovery() {
return sendButtonDiscovery("Enroll", EC_CONFIG);
}
} // namespace Enrollment

View File

@ -212,11 +212,12 @@ bool sendDeleteDiscovery(const String &domain, const String &name)
return pub(discoveryTopic.c_str(), 0, false, ""); 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()); Serial.printf("Setting %s->%s\n", alias.c_str(), id.c_str());
doc.clear(); doc.clear();
doc["id"] = id; doc["id"] = id;
doc["name"] = name;
serializeJson(doc, buffer); serializeJson(doc, buffer);
String settingsTopic = CHANNEL + String("/settings/") + alias + "/config"; String settingsTopic = CHANNEL + String("/settings/") + alias + "/config";
return pub(settingsTopic.c_str(), 0, true, buffer); return pub(settingsTopic.c_str(), 0, true, buffer);

View File

@ -25,4 +25,4 @@ bool sendLightDiscovery(const String &name, const String &entityCategory, bool r
bool sendDeleteDiscovery(const String &domain, const String &name); 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);