Ble read/write characteristics over MQTT. (#988)
* EXPERIMENTAL: Ble read/write characteristics over MQTT. Uses #979 This allows reading and writing BLE characteristics from an MQTT message. Example format: mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoBT/config -m '{"ble_write_address":"AA:BB:CC:DD:EE:FF", "ble_write_service":"cba20d00-224d-11e6-9fb8-0002a5d5c51b", "ble_write_char":"cba20002-224d-11e6-9fb8-0002a5d5c51b", "ble_write_value":"TEST", "ttl":4 }' mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoBT/config -m '{"ble_read_address":"30:AE:A4:7C:3C:A6", "ble_read_service":"cba20d00-224d-11e6-9fb8-0002a5d5c51b", "ble_read_char":"cba20002-224d-11e6-9fb8-0002a5d5c51b", "ttl": 2 }' The ttl parameter is the number of calls to connect (defaults to 1), which occur after the BLE scan completes. A response is provided over MQTT in the format: write : {"id":"30:AE:A4:7C:3C:A6","service":"cba20d00-224d-11e6-9fb8-0002a5d5c51b","characteristic":"cba20002-224d-11e6-9fb8-0002a5d5c51b","write":"TEST","success":true} read : {"id":"30:AE:A4:7C:3C:A6","service":"cba20d00-224d-11e6-9fb8-0002a5d5c51b","characteristic":"cba20002-224d-11e6-9fb8-0002a5d5c51b","read":"TEST","success":true} * Add generic BLE connect class and read data as hex. * Add value_type parameter and document usage * Add note to docs
This commit is contained in:
1 parent
ac23235327
commit
7cfbd3d050
5 files changed
+251
-2
No files matched your search
+53
-1
@@ -103,7 +103,7 @@ In this case you should deactivate the BLE connection mechanism to avoid concurr
|
||||
For certain devices like LYWSD03MMC OpenMQTTGateway use a connection (due to the fact that the advertized data are encrypted), this connection mechanism is launched after every `ScanBeforeConnect` per default, you can modify it by following the procedure below.
|
||||
:::
|
||||
|
||||
## Setting the number of scans between before connect attempt
|
||||
## Setting the number of scans between connection attempts
|
||||
|
||||
If you want to change the number of BLE scans that are done before a BLE connect you can change it by MQTT, if you want the BLE connect to be every 30 scans:
|
||||
|
||||
@@ -152,6 +152,58 @@ you can also accept all the devices by the following command:
|
||||
|
||||
The default value is set into config_BT.h
|
||||
|
||||
## Read/write BLE characteristics over MQTT (ESP32 only)
|
||||
|
||||
The gateway can read and write BLE characteristics from devices and provide the results in an MQTT message.
|
||||
::: tip
|
||||
These actions will be taken on the next BLE connection, which occurs after scanning and after the scan count is reached, [see above to set this.](#setting-the-number-of-scans-between-connection-attempts)
|
||||
:::
|
||||
|
||||
### Example write command
|
||||
```
|
||||
mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoBT/config -m '{
|
||||
"ble_write_address":"AA:BB:CC:DD:EE:FF",
|
||||
"ble_write_service":"cba20d00-224d-11e6-9fb8-0002a5d5c51b",
|
||||
"ble_write_char":"cba20002-224d-11e6-9fb8-0002a5d5c51b",
|
||||
"ble_write_value":"TEST",
|
||||
"value_type":"STRING",
|
||||
"ttl":4 }'
|
||||
```
|
||||
Response:
|
||||
```
|
||||
{
|
||||
"id":"AA:BB:CC:DD:EE:FF",
|
||||
"service":"cba20d00-224d-11e6-9fb8-0002a5d5c51b",
|
||||
"characteristic":"cba20002-224d-11e6-9fb8-0002a5d5c51b",
|
||||
"write":"TEST",
|
||||
"success":true
|
||||
}
|
||||
```
|
||||
### Example read commnad
|
||||
```
|
||||
mosquitto_pub -t home/OpenMQTTGateway/commands/MQTTtoBT/config -m '{
|
||||
"ble_read_address":"AA:BB:CC:DD:EE:FF",
|
||||
"ble_read_service":"cba20d00-224d-11e6-9fb8-0002a5d5c51b",
|
||||
"ble_read_char":"cba20002-224d-11e6-9fb8-0002a5d5c51b",
|
||||
"value_type":"STRING",
|
||||
"ttl": 2 }'
|
||||
```
|
||||
Response:
|
||||
```
|
||||
{
|
||||
"id":"AA:BB:CC:DD:EE:FF",
|
||||
"service":"cba20d00-224d-11e6-9fb8-0002a5d5c51b",
|
||||
"characteristic":"cba20002-224d-11e6-9fb8-0002a5d5c51b",
|
||||
"read":"TEST",
|
||||
"success":true
|
||||
}
|
||||
```
|
||||
|
||||
::: tip
|
||||
The `ttl` parameter is the number of attempts to connect (defaults to 1), which occur after the BLE scan completes.
|
||||
`value_type` can be one of: STRING, HEX, INT, FLOAT. Default is STRING if omitted in the message.
|
||||
:::
|
||||
|
||||
## Other
|
||||
|
||||
To check your hm10 firmware version upload a serial sketch to the nodemcu (this will enable communication directly with the hm10) and launch the command:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#ifdef ESP32
|
||||
# include "ArduinoJson.h"
|
||||
# include "NimBLEDevice.h"
|
||||
# include "config_BT.h"
|
||||
|
||||
extern void pubBT(JsonObject& data);
|
||||
|
||||
@@ -13,6 +14,9 @@ public:
|
||||
TaskHandle_t m_taskHandle = nullptr;
|
||||
zBLEConnect(NimBLEAddress& addr) { m_pClient = NimBLEDevice::createClient(addr); }
|
||||
virtual ~zBLEConnect() { NimBLEDevice::deleteClient(m_pClient); }
|
||||
virtual bool writeData(BLEAction* action);
|
||||
virtual bool readData(BLEAction* action);
|
||||
virtual bool processActions(std::vector<BLEAction>& actions);
|
||||
virtual void publishData() {}
|
||||
virtual NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID& service, const NimBLEUUID& characteristic);
|
||||
};
|
||||
@@ -34,5 +38,12 @@ public:
|
||||
void publishData() override;
|
||||
};
|
||||
|
||||
class GENERIC_connect : public zBLEConnect {
|
||||
std::vector<uint8_t> m_data;
|
||||
|
||||
public:
|
||||
GENERIC_connect(NimBLEAddress& addr) : zBLEConnect(addr) {}
|
||||
};
|
||||
|
||||
#endif //ESP32
|
||||
#endif //zBLEConnect_h
|
||||
@@ -4,7 +4,6 @@
|
||||
# include "ArduinoJson.h"
|
||||
# include "ArduinoLog.h"
|
||||
# include "ZgatewayBLEConnect.h"
|
||||
# include "config_BT.h"
|
||||
|
||||
# define convertTemp_CtoF(c) ((c * 1.8) + 32)
|
||||
|
||||
@@ -35,6 +34,99 @@ NimBLERemoteCharacteristic* zBLEConnect::getCharacteristic(const NimBLEUUID& ser
|
||||
return pRemoteCharacteristic;
|
||||
}
|
||||
|
||||
bool zBLEConnect::writeData(BLEAction* action) {
|
||||
NimBLERemoteCharacteristic* pChar = getCharacteristic(action->service, action->characteristic);
|
||||
if (pChar && pChar->canWrite()) {
|
||||
switch (action->value_type) {
|
||||
case BLE_VAL_HEX: {
|
||||
int len = action->value.length();
|
||||
if (len % 2) {
|
||||
Log.error(F("Invalid HEX value length" CR));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> buf;
|
||||
for (auto i = 0; i < len; i += 2) {
|
||||
std::string temp = action->value.substr(i, 2);
|
||||
buf.push_back((uint8_t)strtoul(temp.c_str(), nullptr, 16));
|
||||
}
|
||||
return pChar->writeValue((const uint8_t*)&buf[0], buf.size());
|
||||
}
|
||||
case BLE_VAL_INT:
|
||||
return pChar->writeValue(strtol(action->value.c_str(), nullptr, 0));
|
||||
case BLE_VAL_FLOAT:
|
||||
return pChar->writeValue(strtod(action->value.c_str(), nullptr));
|
||||
default:
|
||||
return pChar->writeValue(action->value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool zBLEConnect::readData(BLEAction* action) {
|
||||
NimBLERemoteCharacteristic* pChar = getCharacteristic(action->service, action->characteristic);
|
||||
|
||||
if (pChar && pChar->canRead()) {
|
||||
action->value = pChar->readValue();
|
||||
if (action->value != "") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool zBLEConnect::processActions(std::vector<BLEAction>& actions) {
|
||||
bool result = false;
|
||||
if (actions.size() > 0) {
|
||||
for (auto& it : actions) {
|
||||
if (NimBLEAddress(it.addr) == m_pClient->getPeerAddress()) {
|
||||
JsonObject& BLEresult = getBTJsonObject();
|
||||
BLEresult.set("id", it.addr);
|
||||
BLEresult.set("service", (char*)it.service.toString().c_str());
|
||||
BLEresult.set("characteristic", (char*)it.characteristic.toString().c_str());
|
||||
|
||||
if (it.write) {
|
||||
Log.trace(F("processing BLE write" CR));
|
||||
BLEresult.set("write", it.value.c_str());
|
||||
result = writeData(&it);
|
||||
} else {
|
||||
Log.trace(F("processing BLE read" CR));
|
||||
result = readData(&it);
|
||||
if (result) {
|
||||
switch (it.value_type) {
|
||||
case BLE_VAL_HEX: {
|
||||
char* pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*)it.value.c_str(), it.value.length());
|
||||
BLEresult.set("read", pHex);
|
||||
free(pHex);
|
||||
break;
|
||||
}
|
||||
case BLE_VAL_INT: {
|
||||
int ival = *(int*)it.value.data();
|
||||
BLEresult.set<int>("read", ival);
|
||||
break;
|
||||
}
|
||||
case BLE_VAL_FLOAT: {
|
||||
float fval = *(double*)it.value.data();
|
||||
BLEresult.set<float>("read", fval);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLEresult.set("read", it.value.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it.complete = true;
|
||||
BLEresult.set("success", result);
|
||||
pubBT(BLEresult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*-----------------------LYWSD03MMC && MHO_C401 HANDLING-----------------------*/
|
||||
void LYWSD03MMC_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
|
||||
if (m_taskHandle == nullptr) {
|
||||
@@ -160,5 +252,6 @@ void DT24_connect::publishData() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# endif //ZgatewayBT
|
||||
#endif //ESP32
|
||||
@@ -71,6 +71,10 @@ struct decompose {
|
||||
bool reverse;
|
||||
};
|
||||
|
||||
# ifdef ESP32
|
||||
vector<BLEAction> BLEactions;
|
||||
# endif
|
||||
|
||||
vector<BLEdevice*> devices;
|
||||
int newDevices = 0;
|
||||
|
||||
@@ -728,21 +732,38 @@ void BLEconnect() {
|
||||
if (p->connect) {
|
||||
Log.trace(F("Model to connect found: %s" CR), p->macAdr);
|
||||
NimBLEAddress addr(std::string(p->macAdr));
|
||||
|
||||
switch (p->sensorModel) {
|
||||
case LYWSD03MMC:
|
||||
case MHO_C401: {
|
||||
LYWSD03MMC_connect BLEclient(addr);
|
||||
BLEclient.processActions(BLEactions);
|
||||
BLEclient.publishData();
|
||||
break;
|
||||
}
|
||||
case DT24: {
|
||||
DT24_connect BLEclient(addr);
|
||||
BLEclient.processActions(BLEactions);
|
||||
BLEclient.publishData();
|
||||
break;
|
||||
}
|
||||
case GENERIC: {
|
||||
GENERIC_connect BLEclient(addr);
|
||||
BLEclient.processActions(BLEactions);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (BLEactions.size() > 0) {
|
||||
std::vector<BLEAction> swap;
|
||||
for (auto& it : BLEactions) {
|
||||
if (!it.complete && --it.ttl) {
|
||||
swap.push_back(it);
|
||||
}
|
||||
}
|
||||
std::swap(BLEactions, swap);
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.notice(F("BLE Connect end" CR));
|
||||
@@ -1662,6 +1683,54 @@ void BTforceScan() {
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTtoBTAction(JsonObject& BTdata) {
|
||||
# ifdef ESP32
|
||||
BLEAction action;
|
||||
action.ttl = BTdata.containsKey("ttl") ? (uint8_t)BTdata["ttl"] : 1;
|
||||
action.value_type = BLE_VAL_STRING;
|
||||
if (BTdata.containsKey("value_type")) {
|
||||
String vt = BTdata["value_type"];
|
||||
vt.toUpperCase();
|
||||
if (vt == "HEX")
|
||||
action.value_type = BLE_VAL_HEX;
|
||||
else if (vt == "INT")
|
||||
action.value_type = BLE_VAL_INT;
|
||||
else if (vt == "FLOAT")
|
||||
action.value_type = BLE_VAL_FLOAT;
|
||||
else if (vt != "STRING") {
|
||||
Log.error(F("BLE value type invalid %s" CR), vt.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.trace(F("BLE ACTION TTL = %u" CR), action.ttl);
|
||||
action.complete = false;
|
||||
if (BTdata.containsKey("ble_write_address") &&
|
||||
BTdata.containsKey("ble_write_service") &&
|
||||
BTdata.containsKey("ble_write_char") &&
|
||||
BTdata.containsKey("ble_write_value")) {
|
||||
strcpy(action.addr, (const char*)BTdata["ble_write_address"]);
|
||||
action.service = NimBLEUUID((const char*)BTdata["ble_write_service"]);
|
||||
action.characteristic = NimBLEUUID((const char*)BTdata["ble_write_char"]);
|
||||
action.value = std::string((const char*)BTdata["ble_write_value"]);
|
||||
action.write = true;
|
||||
Log.trace(F("BLE ACTION Write" CR));
|
||||
} else if (BTdata.containsKey("ble_read_address") &&
|
||||
BTdata.containsKey("ble_read_service") &&
|
||||
BTdata.containsKey("ble_read_char")) {
|
||||
strcpy(action.addr, (const char*)BTdata["ble_read_address"]);
|
||||
action.service = NimBLEUUID((const char*)BTdata["ble_read_service"]);
|
||||
action.characteristic = NimBLEUUID((const char*)BTdata["ble_read_char"]);
|
||||
action.write = false;
|
||||
Log.trace(F("BLE ACTION Read" CR));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
createOrUpdateDevice(action.addr, device_flags_connect, GENERIC);
|
||||
BLEactions.push_back(action);
|
||||
# endif
|
||||
}
|
||||
|
||||
void MQTTtoBT(char* topicOri, JsonObject& BTdata) { // json object decoding
|
||||
if (cmpToMainTopic(topicOri, subjectMQTTtoBTset)) {
|
||||
Log.trace(F("MQTTtoBT json set" CR));
|
||||
@@ -1723,6 +1792,8 @@ void MQTTtoBT(char* topicOri, JsonObject& BTdata) { // json object decoding
|
||||
if (BTdata.containsKey("lowpowermode")) {
|
||||
changelowpowermode((int)BTdata["lowpowermode"]);
|
||||
}
|
||||
|
||||
MQTTtoBTAction(BTdata);
|
||||
# endif
|
||||
// MinRSSI set
|
||||
if (BTdata.containsKey("minrssi")) {
|
||||
|
||||
@@ -46,6 +46,7 @@ bool bleConnect = AttemptBLECOnnect;
|
||||
# ifndef BLE_FILTER_CONNECTABLE
|
||||
# define BLE_FILTER_CONNECTABLE 1
|
||||
# endif
|
||||
# include "NimBLEDevice.h"
|
||||
#endif
|
||||
|
||||
/*----------------------BT topics & parameters-------------------------*/
|
||||
@@ -151,6 +152,7 @@ enum ble_sensor_model {
|
||||
IBT4XS,
|
||||
DT24,
|
||||
EDDYSTONE_TLM,
|
||||
GENERIC,
|
||||
};
|
||||
|
||||
/*-------------------PIN DEFINITIONS----------------------*/
|
||||
@@ -168,6 +170,26 @@ enum ble_sensor_model {
|
||||
#endif
|
||||
|
||||
/*---------------INTERNAL USE: DO NOT MODIFY--------------*/
|
||||
#ifdef ESP32
|
||||
enum ble_val_type {
|
||||
BLE_VAL_STRING = 0,
|
||||
BLE_VAL_HEX,
|
||||
BLE_VAL_INT,
|
||||
BLE_VAL_FLOAT,
|
||||
};
|
||||
|
||||
struct BLEAction {
|
||||
std::string value;
|
||||
char addr[18];
|
||||
NimBLEUUID service;
|
||||
NimBLEUUID characteristic;
|
||||
bool write;
|
||||
bool complete;
|
||||
uint8_t ttl;
|
||||
ble_val_type value_type;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct BLEdevice {
|
||||
char macAdr[18];
|
||||
bool isDisc;
|
||||
|
||||
Reference in new issue
Block a user