Create BLE connect class and add derivatives (#979)

This commit is contained in:
h2zero authored and GitHub committed 2021-06-17 16:52:03 -05:00
1 parent d35cc03b49
commit 792807e6e5
4 files changed
+250 -157

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
#ifndef zBLEConnect_h
#define zBLEConnect_h
#ifdef ESP32
# include "ArduinoJson.h"
# include "NimBLEDevice.h"
extern void pubBT(JsonObject& data);
class zBLEConnect {
public:
NimBLEClient* m_pClient;
TaskHandle_t m_taskHandle = nullptr;
zBLEConnect(NimBLEAddress& addr) { m_pClient = NimBLEDevice::createClient(addr); }
virtual ~zBLEConnect() { NimBLEDevice::deleteClient(m_pClient); }
virtual void publishData() {}
virtual NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID& service, const NimBLEUUID& characteristic);
};
class LYWSD03MMC_connect : public zBLEConnect {
void notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify);
public:
LYWSD03MMC_connect(NimBLEAddress& addr) : zBLEConnect(addr) {}
void publishData() override;
};
class DT24_connect : public zBLEConnect {
std::vector<uint8_t> m_data;
void notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify);
public:
DT24_connect(NimBLEAddress& addr) : zBLEConnect(addr) {}
void publishData() override;
};
#endif //ESP32
#endif //zBLEConnect_h
+162
View File
@@ -0,0 +1,162 @@
#ifdef ESP32
# include "User_config.h"
# ifdef ZgatewayBT
# include "ArduinoJson.h"
# include "ArduinoLog.h"
# include "ZgatewayBLEConnect.h"
# include "config_BT.h"
# define convertTemp_CtoF(c) ((c * 1.8) + 32)
extern bool ProcessLock;
extern std::vector<BLEdevice*> devices;
NimBLERemoteCharacteristic* zBLEConnect::getCharacteristic(const NimBLEUUID& service,
const NimBLEUUID& characteristic) {
BLERemoteCharacteristic* pRemoteCharacteristic = nullptr;
if (!m_pClient) {
Log.error(F("No BLE client" CR));
} else if (!m_pClient->isConnected() && !m_pClient->connect()) {
Log.error(F("Connect to: %s failed" CR), m_pClient->getPeerAddress().toString().c_str());
} else {
BLERemoteService* pRemoteService = m_pClient->getService(service);
if (!pRemoteService) {
Log.notice(F("Failed to find service UUID: %s" CR), service.toString().c_str());
} else {
Log.trace(F("Found service: %s" CR), service.toString().c_str());
Log.trace(F("Client isConnected, freeHeap: %d" CR), ESP.getFreeHeap());
pRemoteCharacteristic = pRemoteService->getCharacteristic(characteristic);
if (!pRemoteCharacteristic) {
Log.notice(F("Failed to find characteristic UUID: %s" CR), characteristic.toString().c_str());
}
}
}
return pRemoteCharacteristic;
}
/*-----------------------LYWSD03MMC && MHO_C401 HANDLING-----------------------*/
void LYWSD03MMC_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
if (m_taskHandle == nullptr) {
return; // unexpected notification
}
if (!ProcessLock) {
Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str());
if (length == 5) {
Log.trace(F("Device identified creating BLE buffer" CR));
JsonObject& BLEdata = getBTJsonObject();
String mac_address = m_pClient->getPeerAddress().toString().c_str();
mac_address.toUpperCase();
for (std::vector<BLEdevice*>::iterator it = devices.begin(); it != devices.end(); ++it) {
BLEdevice* p = *it;
if ((strcmp(p->macAdr, (char*)mac_address.c_str()) == 0)) {
if (p->sensorModel == LYWSD03MMC)
BLEdata.set("model", "LYWSD03MMC");
else if (p->sensorModel == MHO_C401)
BLEdata.set("model", "MHO_C401");
}
}
BLEdata.set("id", (char*)mac_address.c_str());
Log.trace(F("Device identified in CB: %s" CR), (char*)mac_address.c_str());
BLEdata.set("tempc", (float)((pData[0] | (pData[1] << 8)) * 0.01));
BLEdata.set("tempf", (float)(convertTemp_CtoF((pData[0] | (pData[1] << 8)) * 0.01)));
BLEdata.set("hum", (float)(pData[2]));
BLEdata.set("volt", (float)(((pData[4] * 256) + pData[3]) / 1000.0));
BLEdata.set("batt", (float)(((((pData[4] * 256) + pData[3]) / 1000.0) - 2.1) * 100));
pubBT(BLEdata);
} else {
Log.notice(F("Invalid notification data" CR));
return;
}
} else {
Log.trace(F("Callback process canceled by processLock" CR));
}
xTaskNotifyGive(m_taskHandle);
}
void LYWSD03MMC_connect::publishData() {
NimBLEUUID serviceUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6");
NimBLEUUID charUUID("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6");
NimBLERemoteCharacteristic* pChar = getCharacteristic(serviceUUID, charUUID);
if (pChar && pChar->canNotify()) {
Log.trace(F("Registering notification" CR));
if (pChar->subscribe(true, std::bind(&LYWSD03MMC_connect::notifyCB, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4))) {
m_taskHandle = xTaskGetCurrentTaskHandle();
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT)) == pdFALSE) {
m_taskHandle = nullptr;
}
} else {
Log.notice(F("Failed registering notification" CR));
}
}
}
/*-----------------------DT24 HANDLING-----------------------*/
void DT24_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
if (m_taskHandle == nullptr) {
return; // unexpected notification
}
if (!ProcessLock) {
Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str());
if (length == 20) {
m_data.assign(pData, pData + length);
return;
} else if (m_data.size() == 20 && length == 16) {
m_data.insert(m_data.end(), pData, pData + length);
// DT24-BLE data format
// https://github.com/NiceLabs/atorch-console/blob/master/docs/protocol-design.md#dc-meter-report
// Data comes as two packets ( 20 and 16 ), and am only processing first
Log.trace(F("Device identified creating BLE buffer" CR));
JsonObject& BLEdata = getBTJsonObject();
String mac_address = m_pClient->getPeerAddress().toString().c_str();
mac_address.toUpperCase();
BLEdata.set("model", "DT24");
BLEdata.set("id", (char*)mac_address.c_str());
Log.trace(F("Device identified in CB: %s" CR), (char*)mac_address.c_str());
BLEdata.set("volt", (float)(((m_data[4] * 256 * 256) + (m_data[5] * 256) + m_data[6]) / 10.0));
BLEdata.set("current", (float)(((m_data[7] * 256 * 256) + (m_data[8] * 256) + m_data[9]) / 1000.0));
BLEdata.set("power", (float)(((m_data[10] * 256 * 256) + (m_data[11] * 256) + m_data[12]) / 10.0));
BLEdata.set("energy", (float)(((m_data[13] * 256 * 256 * 256) + (m_data[14] * 256 * 256) + (m_data[15] * 256) + m_data[16]) / 100.0));
BLEdata.set("price", (float)(((m_data[17] * 256 * 256) + (m_data[18] * 256) + m_data[19]) / 100.0));
pubBT(BLEdata);
} else {
Log.notice(F("Invalid notification data" CR));
return;
}
} else {
Log.trace(F("Callback process canceled by processLock" CR));
}
xTaskNotifyGive(m_taskHandle);
}
void DT24_connect::publishData() {
NimBLEUUID serviceUUID("ffe0");
NimBLEUUID charUUID("ffe1");
NimBLERemoteCharacteristic* pChar = getCharacteristic(serviceUUID, charUUID);
if (pChar && pChar->canNotify()) {
Log.trace(F("Registering notification" CR));
if (pChar->subscribe(true, std::bind(&DT24_connect::notifyCB, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4))) {
m_taskHandle = xTaskGetCurrentTaskHandle();
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT)) == pdFALSE) {
m_taskHandle = nullptr;
}
} else {
Log.notice(F("Failed registering notification" CR));
}
}
}
# endif //ZgatewayBT
#endif //ESP32
+32 -157
View File
@@ -46,14 +46,10 @@ FreeRTOS::Semaphore semaphoreCreateOrUpdateDevice = FreeRTOS::Semaphore("createO
# include <esp_wifi.h>
# include <stdatomic.h>
# include "ZgatewayBLEConnect.h"
# include "soc/timer_group_reg.h"
# include "soc/timer_group_struct.h"
void notifyCB(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify);
# endif
# if !defined(ESP32) && !defined(ESP8266)
@@ -63,18 +59,11 @@ void notifyCB(
# include <vector>
using namespace std;
struct BLEdevice {
char macAdr[18];
bool isDisc;
bool isWhtL;
bool isBlkL;
ble_sensor_model sensorModel;
};
# define device_flags_init 0 << 0
# define device_flags_isDisc 1 << 0
# define device_flags_isWhiteL 1 << 1
# define device_flags_isBlackL 1 << 2
# define device_flags_connect 1 << 3
struct decompose {
int start;
@@ -85,7 +74,7 @@ struct decompose {
vector<BLEdevice*> devices;
int newDevices = 0;
static BLEdevice NO_DEVICE_FOUND = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false, false, false, UNKNOWN_MODEL};
static BLEdevice NO_DEVICE_FOUND = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false, false, false, false, UNKNOWN_MODEL};
static bool oneWhite = false;
int minRssi = abs(MinimumRSSI); //minimum rssi value
@@ -134,7 +123,7 @@ int btQueueBlocked = 0;
int btQueueLengthSum = 0;
int btQueueLengthCount = 0;
JsonObject& getBTJsonObject(const char* json = NULL, bool haPresenceEnabled = true) {
JsonObject& getBTJsonObject(const char* json, bool haPresenceEnabled) {
int next, last;
for (bool blocked = false;;) {
next = atomic_load_explicit(&jsonBTBufferQueueNext, ::memory_order_seq_cst); // use namespace std -> ambiguous error...
@@ -178,7 +167,7 @@ void emptyBTQueue() {
JsonBundle jsonBTBuffer;
JsonObject& getBTJsonObject(const char* json = NULL, bool haPresenceEnabled = true) {
JsonObject& getBTJsonObject(const char* json, bool haPresenceEnabled) {
return jsonBTBuffer.createObject();
}
@@ -235,6 +224,7 @@ void createOrUpdateDevice(const char* mac, uint8_t flags, ble_sensor_model model
device->isDisc = flags & device_flags_isDisc;
device->isWhtL = flags & device_flags_isWhiteL;
device->isBlkL = flags & device_flags_isBlackL;
device->connect = flags & device_flags_connect;
if (model != UNKNOWN_MODEL) device->sensorModel = model;
devices.push_back(device);
newDevices++;
@@ -634,6 +624,13 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
Log.notice(F("Device detected: %s" CR), (char*)mac_adress.c_str());
BLEdevice* device = getDeviceByMac(BLEdata["id"].as<const char*>());
# if BLE_FILTER_CONNECTABLE
if (device->connect) {
Log.notice(F("Filtered connectable device" CR));
return;
}
# endif
if ((!oneWhite || isWhite(device)) && !isBlack(device)) { //if not black listed mac we go AND if we have no white mac or this mac is white we go out
if (advertisedDevice->haveName())
BLEdata.set("name", (char*)advertisedDevice->getName().c_str());
@@ -716,82 +713,10 @@ void BLEscan() {
pBLEScan->setWindow(BLEScanWindow);
BLEScanResults foundDevices = pBLEScan->start(Scan_duration / 1000, false);
scanCount++;
Log.notice(F("Found %d devices, scan number %d end deinit controller" CR), foundDevices.getCount(), scanCount);
Log.notice(F("Found %d devices, scan number %d end" CR), foundDevices.getCount(), scanCount);
enableCore0WDT();
}
/**
* Callback method to retrieve data from devices characteristics
*/
void notifyCB(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
if (!ProcessLock) {
Log.trace(F("Callback from %s characteristic" CR), pBLERemoteCharacteristic->getUUID().toString().c_str());
// This logic needs refactoring and should be changed to a device/model approach based on the sensor macAdr lookup. Growing this beyond 2 devices will get too cumbersome
if (length == 5) {
Log.trace(F("Device identified creating BLE buffer" CR));
JsonObject& BLEdata = getBTJsonObject();
String mac_adress = pBLERemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString().c_str();
mac_adress.toUpperCase();
for (vector<BLEdevice*>::iterator it = devices.begin(); it != devices.end(); ++it) {
BLEdevice* p = *it;
if ((strcmp(p->macAdr, (char*)mac_adress.c_str()) == 0)) {
if (p->sensorModel == LYWSD03MMC)
BLEdata.set("model", "LYWSD03MMC");
else if (p->sensorModel == MHO_C401)
BLEdata.set("model", "MHO_C401");
}
}
BLEdata.set("id", (char*)mac_adress.c_str());
Log.trace(F("Device identified in CB: %s" CR), (char*)mac_adress.c_str());
BLEdata.set("tempc", (float)((pData[0] | (pData[1] << 8)) * 0.01));
BLEdata.set("tempf", (float)(convertTemp_CtoF((pData[0] | (pData[1] << 8)) * 0.01)));
BLEdata.set("hum", (float)(pData[2]));
BLEdata.set("volt", (float)(((pData[4] * 256) + pData[3]) / 1000.0));
BLEdata.set("batt", (float)(((((pData[4] * 256) + pData[3]) / 1000.0) - 2.1) * 100));
pubBT(BLEdata);
} else if (length == 20) {
// DT24-BLE data format
// https://github.com/NiceLabs/atorch-console/blob/master/docs/protocol-design.md#dc-meter-report
// Data comes as two packets ( 20 and 16 ), and am only processing first
Log.trace(F("Device identified creating BLE buffer" CR));
JsonObject& BLEdata = getBTJsonObject();
String mac_adress = pBLERemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString().c_str();
mac_adress.toUpperCase();
for (vector<BLEdevice*>::iterator it = devices.begin(); it != devices.end(); ++it) {
BLEdevice* p = *it;
if ((strcmp(p->macAdr, (char*)mac_adress.c_str()) == 0)) {
if (p->sensorModel == LYWSD03MMC)
BLEdata.set("model", "LYWSD03MMC");
else if (p->sensorModel == MHO_C401)
BLEdata.set("model", "MHO_C401");
else if (p->sensorModel == DT24)
BLEdata.set("model", "DT24");
}
}
BLEdata.set("id", (char*)mac_adress.c_str());
Log.trace(F("Device identified in CB: %s" CR), (char*)mac_adress.c_str());
BLEdata.set("volt", (float)(((pData[4] * 256 * 256) + (pData[5] * 256) + pData[6]) / 10.0));
BLEdata.set("current", (float)(((pData[7] * 256 * 256) + (pData[8] * 256) + pData[9]) / 1000.0));
BLEdata.set("power", (float)(((pData[10] * 256 * 256) + (pData[11] * 256) + pData[12]) / 10.0));
BLEdata.set("energy", (float)(((pData[13] * 256 * 256 * 256) + (pData[14] * 256 * 256) + (pData[15] * 256) + pData[16]) / 100.0));
BLEdata.set("price", (float)(((pData[17] * 256 * 256) + (pData[18] * 256) + pData[19]) / 100.0));
pubBT(BLEdata);
} else {
Log.notice(F("Device not identified" CR));
}
} else {
Log.trace(F("Callback process canceled by processLock" CR));
}
}
/**
* Connect to BLE devices and initiate the callbacks with a service/characteristic request
*/
@@ -799,74 +724,24 @@ void BLEconnect() {
Log.notice(F("BLE Connect begin" CR));
for (vector<BLEdevice*>::iterator it = devices.begin(); it != devices.end(); ++it) {
BLEdevice* p = *it;
if (p->sensorModel == LYWSD03MMC || p->sensorModel == MHO_C401) {
Log.trace(F("Model to connect found" CR));
NimBLEClient* pClient;
pClient = BLEDevice::createClient();
BLEUUID serviceUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6");
BLEUUID charUUID("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6");
BLEAddress sensorAddress(p->macAdr);
if (!pClient->connect(sensorAddress)) {
Log.notice(F("Failed to find client: %s" CR), p->macAdr);
} else {
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (!pRemoteService) {
Log.notice(F("Failed to find service UUID: %s" CR), serviceUUID.toString().c_str());
} else {
Log.trace(F("Found service: %s" CR), serviceUUID.toString().c_str());
// Obtain a reference to the characteristic in the service of the remote BLE server.
if (pClient->isConnected()) {
Log.trace(F("Client isConnected, freeHeap: %d" CR), ESP.getFreeHeap());
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (!pRemoteCharacteristic) {
Log.notice(F("Failed to find characteristic UUID: %s" CR), charUUID.toString().c_str());
} else {
if (pRemoteCharacteristic->canNotify()) {
Log.trace(F("Registering notification" CR));
pRemoteCharacteristic->subscribe(true, notifyCB);
delay(BLE_CNCT_TIMEOUT);
} else {
Log.notice(F("Failed registering notification" CR));
}
}
}
}
}
NimBLEDevice::deleteClient(pClient);
} else if (p->sensorModel == DT24) {
if (p->connect) {
Log.trace(F("Model to connect found: %s" CR), p->macAdr);
NimBLEClient* pClient;
pClient = BLEDevice::createClient();
BLEUUID serviceUUID("ffe0");
BLEUUID charUUID("ffe1");
BLEAddress sensorAddress(p->macAdr);
if (!pClient->connect(sensorAddress)) {
Log.notice(F("Failed to find client: %s" CR), p->macAdr);
} else {
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (!pRemoteService) {
Log.notice(F("Failed to find service UUID: %s" CR), serviceUUID.toString().c_str());
} else {
Log.trace(F("Found service: %s" CR), serviceUUID.toString().c_str());
// Obtain a reference to the characteristic in the service of the remote BLE server.
if (pClient->isConnected()) {
Log.trace(F("Client isConnected, freeHeap: %d" CR), ESP.getFreeHeap());
BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (!pRemoteCharacteristic) {
Log.notice(F("Failed to find characteristic UUID: %s" CR), charUUID.toString().c_str());
} else {
if (pRemoteCharacteristic->canNotify()) {
Log.trace(F("Registering notification" CR));
pRemoteCharacteristic->subscribe(true, notifyCB);
delay(BLE_CNCT_TIMEOUT);
} else {
Log.notice(F("Failed registering notification" CR));
}
}
}
NimBLEAddress addr(std::string(p->macAdr));
switch (p->sensorModel) {
case LYWSD03MMC:
case MHO_C401: {
LYWSD03MMC_connect BLEclient(addr);
BLEclient.publishData();
break;
}
case DT24: {
DT24_connect BLEclient(addr);
BLEclient.publishData();
break;
}
default:
break;
}
NimBLEDevice::deleteClient(pClient);
}
}
Log.notice(F("BLE Connect end" CR));
@@ -1314,13 +1189,13 @@ JsonObject& process_bledata(JsonObject& BLEdata) {
if (strstr(service_data, "588703") != NULL) {
Log.trace(F("MHO_C401 add to list for future connect" CR));
if (device->sensorModel == -1)
createOrUpdateDevice(mac, device_flags_init, MHO_C401);
createOrUpdateDevice(mac, device_flags_connect, MHO_C401);
}
Log.trace(F("Is it a LYWSD03MMC?" CR));
if (strstr(service_data, "585b05") != NULL) {
Log.trace(F("LYWSD03MMC add to list for future connect" CR));
if (device->sensorModel == -1)
createOrUpdateDevice(mac, device_flags_init, LYWSD03MMC);
createOrUpdateDevice(mac, device_flags_connect, LYWSD03MMC);
}
Log.trace(F("Is it a custom (pvvx) LYWSD03MMC?" CR));
if (service_len >= 30 && strncmp(service_data + 6, "38c1a4", 6) == 0) {
@@ -1421,7 +1296,7 @@ JsonObject& process_bledata(JsonObject& BLEdata) {
Log.trace(F("DT24 data reading data reading" CR));
BLEdata.set("model", "DT24");
if (device->sensorModel == -1)
createOrUpdateDevice(mac, device_flags_init, DT24);
createOrUpdateDevice(mac, device_flags_connect, DT24);
return BLEdata;
}
}
+18
View File
@@ -40,6 +40,12 @@ extern int btQueueLengthCount;
# define AttemptBLECOnnect true //do we by default attempt a BLE connection to sensors with ESP32
# endif
bool bleConnect = AttemptBLECOnnect;
// Sets whether to filter publishing of scanned devices that require a connection.
// Default (1) prevents overwriting the publication of the device connection data with the advertised data.
# ifndef BLE_FILTER_CONNECTABLE
# define BLE_FILTER_CONNECTABLE 1
# endif
#endif
/*----------------------BT topics & parameters-------------------------*/
@@ -161,4 +167,16 @@ enum ble_sensor_model {
# endif
#endif
/*---------------INTERNAL USE: DO NOT MODIFY--------------*/
struct BLEdevice {
char macAdr[18];
bool isDisc;
bool isWhtL;
bool isBlkL;
bool connect;
ble_sensor_model sensorModel;
};
JsonObject& getBTJsonObject(const char* json = NULL, bool haPresenceEnabled = true);
#endif