Skip to content

Commit 2be60ae

Browse files
committed
fix(ble): Fix notification timing
1 parent a3786a2 commit 2be60ae

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

libraries/BLE/src/BLECharacteristic.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,8 +954,18 @@ int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr
954954
}
955955
rc = ble_gap_conn_find(conn_handle, &desc);
956956
assert(rc == 0);
957+
958+
// Set write context flag to defer notifications
959+
pCharacteristic->m_inWriteContext = true;
957960
pCharacteristic->setValue(buf, len);
958961
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc);
962+
pCharacteristic->m_inWriteContext = false;
963+
964+
// Execute any deferred notifications after write context ends
965+
if (pCharacteristic->m_deferNotifications) {
966+
pCharacteristic->m_deferNotifications = false;
967+
pCharacteristic->notifyDeferred();
968+
}
959969

960970
return 0;
961971
}
@@ -1025,6 +1035,14 @@ void BLECharacteristic::notify(bool is_notification) {
10251035
assert(getService() != nullptr);
10261036
assert(getService()->getServer() != nullptr);
10271037

1038+
// If we're in a write context, defer the notification
1039+
if (m_inWriteContext) {
1040+
log_v("Deferring notification - in write context");
1041+
m_deferNotifications = true;
1042+
m_pCallbacks->onNotify(this); // Still invoke the callback
1043+
return;
1044+
}
1045+
10281046
int rc = 0;
10291047
m_pCallbacks->onNotify(this); // Invoke the notify callback.
10301048

@@ -1153,6 +1171,134 @@ void BLECharacteristic::notify(bool is_notification) {
11531171
log_v("<< notify");
11541172
} // Notify
11551173

1174+
/**
1175+
* @brief Execute deferred notifications.
1176+
* This function is called after a write context ends to send any notifications
1177+
* that were deferred during the write operation.
1178+
* @return N/A.
1179+
*/
1180+
void BLECharacteristic::notifyDeferred() {
1181+
log_v(">> notifyDeferred: executing deferred notifications");
1182+
1183+
assert(getService() != nullptr);
1184+
assert(getService()->getServer() != nullptr);
1185+
1186+
int rc = 0;
1187+
m_pCallbacks->onNotify(this); // Invoke the notify callback.
1188+
1189+
// GeneralUtils::hexDump() doesn't output anything if the log level is not
1190+
// "VERBOSE". Additionally, it is very CPU intensive, even when it doesn't
1191+
// output anything! So it is much better to *not* call it at all if not needed.
1192+
// In a simple program which calls BLECharacteristic::notify() every 50 ms,
1193+
// the performance gain of this little optimization is 37% in release mode
1194+
// (-O3) and 57% in debug mode.
1195+
// Of course, the "#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE" guard
1196+
// could also be put inside the GeneralUtils::hexDump() function itself. But
1197+
// it's better to put it here also, as it is clearer (indicating a verbose log
1198+
// thing) and it allows to remove the "m_value.getValue().c_str()" call, which
1199+
// is, in itself, quite CPU intensive.
1200+
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
1201+
GeneralUtils::hexDump((uint8_t *)m_value.getValue().c_str(), m_value.getValue().length());
1202+
#endif
1203+
1204+
if (getService()->getServer()->getConnectedCount() == 0) {
1205+
log_v("<< notifyDeferred: No connected clients.");
1206+
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NO_CLIENT, 0);
1207+
return;
1208+
}
1209+
1210+
if (m_subscribedVec.size() == 0) {
1211+
log_v("<< notifyDeferred: No clients subscribed.");
1212+
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NO_SUBSCRIBER, 0);
1213+
return;
1214+
}
1215+
1216+
bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) || (m_properties & BLE_GATT_CHR_F_READ_AUTHOR) || (m_properties & BLE_GATT_CHR_F_READ_ENC);
1217+
1218+
for (auto &myPair : m_subscribedVec) {
1219+
uint16_t _mtu = getService()->getServer()->getPeerMTU(myPair.first);
1220+
1221+
// check if connected and subscribed
1222+
if (_mtu == 0 || myPair.second == 0) {
1223+
continue;
1224+
}
1225+
1226+
if (reqSec) {
1227+
struct ble_gap_conn_desc desc;
1228+
rc = ble_gap_conn_find(myPair.first, &desc);
1229+
if (rc != 0 || !desc.sec_state.encrypted) {
1230+
continue;
1231+
}
1232+
}
1233+
1234+
String value = getValue();
1235+
size_t length = value.length();
1236+
1237+
if (length > _mtu - 3) {
1238+
log_w("- Truncating to %d bytes (maximum notify size)", _mtu - 3);
1239+
}
1240+
1241+
// For deferred notifications, default to notification (not indication)
1242+
bool is_notification = true;
1243+
if (is_notification && (!(myPair.second & NIMBLE_SUB_NOTIFY))) {
1244+
log_w("Sending notification to client subscribed to indications, sending indication instead");
1245+
is_notification = false;
1246+
}
1247+
1248+
if (!is_notification && (!(myPair.second & NIMBLE_SUB_INDICATE))) {
1249+
log_w("Sending indication to client subscribed to notification, sending notification instead");
1250+
is_notification = true;
1251+
}
1252+
1253+
if (!is_notification) { // is indication
1254+
m_semaphoreConfEvt.take("indicate");
1255+
}
1256+
1257+
// don't create the m_buf until we are sure to send the data or else
1258+
// we could be allocating a buffer that doesn't get released.
1259+
// We also must create it in each loop iteration because it is consumed with each host call.
1260+
os_mbuf *om = ble_hs_mbuf_from_flat((uint8_t *)value.c_str(), length);
1261+
1262+
if (!is_notification && (m_properties & BLECharacteristic::PROPERTY_INDICATE)) {
1263+
if (!BLEDevice::getServer()->setIndicateWait(myPair.first)) {
1264+
log_e("prior Indication in progress");
1265+
os_mbuf_free_chain(om);
1266+
return;
1267+
}
1268+
1269+
rc = ble_gatts_indicate_custom(myPair.first, m_handle, om);
1270+
if (rc != 0) {
1271+
BLEDevice::getServer()->clearIndicateWait(myPair.first);
1272+
}
1273+
} else {
1274+
rc = ble_gatts_notify_custom(myPair.first, m_handle, om);
1275+
}
1276+
1277+
if (rc != 0) {
1278+
log_e("<< ble_gatts_%s_custom: rc=%d %s", is_notification ? "notify" : "indicate", rc, GeneralUtils::errorToString(rc));
1279+
m_semaphoreConfEvt.give();
1280+
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_GATT, rc); // Invoke the notify callback.
1281+
return;
1282+
}
1283+
1284+
if (!is_notification) { // is indication
1285+
if (!m_semaphoreConfEvt.timedWait("indicate", indicationTimeout)) {
1286+
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, 0); // Invoke the notify callback.
1287+
} else {
1288+
auto code = m_semaphoreConfEvt.value();
1289+
if (code == ESP_OK) {
1290+
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::SUCCESS_INDICATE, code); // Invoke the notify callback.
1291+
} else {
1292+
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, code);
1293+
}
1294+
}
1295+
} else {
1296+
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0); // Invoke the notify callback.
1297+
}
1298+
}
1299+
log_v("<< notifyDeferred");
1300+
} // notifyDeferred
1301+
11561302
void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc) {
11571303
onRead(pCharacteristic);
11581304
} // onRead

libraries/BLE/src/BLECharacteristic.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ class BLECharacteristic {
246246
portMUX_TYPE m_readMux;
247247
uint8_t m_removed;
248248
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
249+
bool m_inWriteContext = false;
250+
bool m_deferNotifications = false;
249251
#endif
250252

251253
/***************************************************************************
@@ -271,6 +273,7 @@ class BLECharacteristic {
271273
#if defined(CONFIG_NIMBLE_ENABLED)
272274
void setSubscribe(struct ble_gap_event *event);
273275
static int handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
276+
void notifyDeferred();
274277
#endif
275278
}; // BLECharacteristic
276279

0 commit comments

Comments
 (0)