Add immediate parameter to BLE connection commands. (#1137)

* Add immediate parameter to BLE connection commands.

When sending a command to read/write a characteristic on a BLE server this adds a new parameter to cause the action to be
performed immediately. This will stop the scan in progress to perform the actions queued.

Example use:
```
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,
  "immediate":true }'
```

Stop BLE processing when immeditate command sent.

* Use a shorter connection timeout.

* Update docs.

* Remove MQTT spam for connection retrys

* Prevent crashing when the normal connection process occurs at the same time as an immediate action.

* Use a semaphore to protect BLE device vector.
This commit is contained in:
h2zero authored and GitHub committed 2022-01-23 19:47:17 -06:00
1 parent 8d793a8b50
commit 5dac1a7035
4 files changed
+121 -53

No files matched your search

+102 -41
View File
@@ -35,6 +35,7 @@ Thanks to wolass https://github.com/wolass for suggesting me HM 10 and dinosd ht
# ifdef ESP32
# include "FreeRTOS.h"
SemaphoreHandle_t semaphoreCreateOrUpdateDevice;
SemaphoreHandle_t semaphoreBLEOperation;
QueueHandle_t BLEQueue;
// Headers used for deep sleep functions
# include <NimBLEAdvertisedDevice.h>
@@ -255,7 +256,7 @@ void createOrUpdateDevice(const char* mac, uint8_t flags, std::string model) {
device->connect = true;
}
if (model.compare("") == 0) device->sensorModel_id = model;
if (!model.empty()) device->sensorModel_id = model;
if (flags & device_flags_isWhiteL || flags & device_flags_isBlackL) {
device->isWhtL = flags & device_flags_isWhiteL;
@@ -458,43 +459,56 @@ void BLEscan() {
* Connect to BLE devices and initiate the callbacks with a service/characteristic request
*/
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->connect) {
Log.trace(F("Model to connect found: %s" CR), p->macAdr);
NimBLEAddress addr(std::string(p->macAdr));
if (p->sensorModel_id.compare("LYWSD03MMC") == 0 || p->sensorModel_id.compare("MHO-C401") == 0) {
LYWSD03MMC_connect BLEclient(addr);
BLEclient.processActions(BLEactions);
BLEclient.publishData();
}
if (p->sensorModel_id.compare("DT24-BLE") == 0) {
DT24_connect BLEclient(addr);
BLEclient.processActions(BLEactions);
BLEclient.publishData();
}
if (p->sensorModel_id.compare("GENERIC") == 0) {
GENERIC_connect BLEclient(addr);
BLEclient.processActions(BLEactions);
}
if (p->sensorModel_id.compare("HHCCJCY01HHCC") == 0) {
HHCCJCY01HHCC_connect BLEclient(addr);
BLEclient.processActions(BLEactions);
BLEclient.publishData();
}
if (BLEactions.size() > 0) {
std::vector<BLEAction> swap;
for (auto& it : BLEactions) {
if (!it.complete && --it.ttl) {
swap.push_back(it);
if (!ProcessLock) {
Log.notice(F("BLE Connect begin" CR));
do {
for (vector<BLEdevice*>::iterator it = devices.begin(); it != devices.end(); ++it) {
BLEdevice* p = *it;
if (p->connect) {
Log.trace(F("Model to connect found: %s" CR), p->macAdr);
NimBLEAddress addr(std::string(p->macAdr));
if (p->sensorModel_id.compare("LYWSD03MMC") == 0 || p->sensorModel_id.compare("MHO-C401") == 0) {
LYWSD03MMC_connect BLEclient(addr);
BLEclient.processActions(BLEactions);
BLEclient.publishData();
} else if (p->sensorModel_id.compare("DT24-BLE") == 0) {
DT24_connect BLEclient(addr);
BLEclient.processActions(BLEactions);
BLEclient.publishData();
} else if (p->sensorModel_id.compare("HHCCJCY01HHCC") == 0) {
HHCCJCY01HHCC_connect BLEclient(addr);
BLEclient.processActions(BLEactions);
BLEclient.publishData();
} else {
GENERIC_connect BLEclient(addr);
if (BLEclient.processActions(BLEactions)) {
// If we don't regularly connect to this, disable connections so advertisements
// won't be filtered if BLE_FILTER_CONNECTABLE is set.
p->connect = false;
}
}
if (BLEactions.size() > 0) {
std::vector<BLEAction> swap;
for (auto& it : BLEactions) {
if (!it.complete && --it.ttl) {
swap.push_back(it);
} else if (memcmp(it.addr, p->macAdr, sizeof(it.addr)) == 0) {
if (p->sensorModel_id != "DT24-BLE" &&
p->sensorModel_id != "HHCCJCY01HHCC" &&
p->sensorModel_id != "LYWSD03MMC" &&
p->sensorModel_id != "MHO-C401") {
// if irregulary connected to and connection failed clear the connect flag.
p->connect = false;
}
}
}
std::swap(BLEactions, swap);
}
}
std::swap(BLEactions, swap);
}
}
} while (BLEactions.size() > 0);
Log.notice(F("BLE Connect end" CR));
}
Log.notice(F("BLE Connect end" CR));
}
void stopProcessing() {
@@ -520,11 +534,16 @@ void coreTask(void* pvParameters) {
if (client.state() != 0) {
Log.warning(F("MQTT client disconnected no BLE scan" CR));
} else if (!ProcessLock) {
BLEscan();
// Launching a connect every BLEscanBeforeConnect
if ((!(scanCount % BLEscanBeforeConnect) || scanCount == 1) && bleConnect)
BLEconnect();
dumpDevices();
if (xSemaphoreTake(semaphoreBLEOperation, pdMS_TO_TICKS(30000)) == pdTRUE) {
BLEscan();
// Launching a connect every BLEscanBeforeConnect
if ((!(scanCount % BLEscanBeforeConnect) || scanCount == 1) && bleConnect)
BLEconnect();
dumpDevices();
xSemaphoreGive(semaphoreBLEOperation);
} else {
Log.error(F("Failed to start scan - BLE busy" CR));
}
}
if (lowpowermode) {
lowPowerESP32();
@@ -604,6 +623,9 @@ void setupBT() {
semaphoreCreateOrUpdateDevice = xSemaphoreCreateBinary();
xSemaphoreGive(semaphoreCreateOrUpdateDevice);
semaphoreBLEOperation = xSemaphoreCreateBinary();
xSemaphoreGive(semaphoreBLEOperation);
BLEQueue = xQueueCreate(32, sizeof(NimBLEAdvertisedDevice*));
BLEDevice::setScanDuplicateCacheSize(BLEScanDuplicateCacheSize);
@@ -968,8 +990,47 @@ void MQTTtoBTAction(JsonObject& BTdata) {
} else {
return;
}
createOrUpdateDevice(action.addr, device_flags_connect, "GENERIC");
BLEactions.push_back(action);
createOrUpdateDevice(action.addr, device_flags_connect, "");
if (BTdata.containsKey("immediate") && BTdata["immediate"].as<bool>()) {
// Immediate action; we need to prevent the normal connection action and stop scanning
ProcessLock = true;
NimBLEScan* pScan = NimBLEDevice::getScan();
if (pScan->isScanning()) {
pScan->stop();
}
if (xSemaphoreTake(semaphoreBLEOperation, pdMS_TO_TICKS(5000)) == pdTRUE) {
// swap the vectors so only this device is processed
std::vector<BLEdevice*> dev_swap;
dev_swap.push_back(getDeviceByMac(action.addr));
std::swap(devices, dev_swap);
std::vector<BLEAction> act_swap;
act_swap.push_back(action);
std::swap(BLEactions, act_swap);
// Unlock here to allow the action to be performed
ProcessLock = false;
BLEconnect();
// back to normal
std::swap(devices, dev_swap);
std::swap(BLEactions, act_swap);
// If we stopped the scheduled connect for this action, do the scheduled now
if ((!(scanCount % BLEscanBeforeConnect) || scanCount == 1) && bleConnect)
BLEconnect();
xSemaphoreGive(semaphoreBLEOperation);
} else {
Log.error(F("BLE busy - command not sent" CR));
JsonObject result = getBTJsonObject();
result["id"] = action.addr;
result["success"] = false;
pubBT(result);
}
} else {
BLEactions.push_back(action);
}
# endif
}