Skip to content

Commit cba29ea

Browse files
committed
queue throttling + slave latency and minor refactor
1 parent d67f311 commit cba29ea

File tree

2 files changed

+64
-28
lines changed

2 files changed

+64
-28
lines changed

src/helpers/nrf52/SerialBLEInterface.cpp

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,23 @@
44
#include "ble_gap.h"
55
#include "ble_hci.h"
66

7+
// Magic numbers came from actual testing
78
#define BLE_HEALTH_CHECK_INTERVAL 10000 // Advertising watchdog check every 10 seconds
9+
#define BLE_RETRY_THROTTLE_MS 250 // Throttle retries to 250ms when queue buildup detected
10+
11+
// Connection parameters (units: interval=1.25ms, timeout=10ms)
12+
#define BLE_MIN_CONN_INTERVAL 12 // 15ms
13+
#define BLE_MAX_CONN_INTERVAL 24 // 30ms
14+
#define BLE_SLAVE_LATENCY 4
15+
#define BLE_CONN_SUP_TIMEOUT 200 // 2000ms
16+
17+
// Advertising parameters
18+
#define BLE_ADV_INTERVAL_MIN 32 // 20ms (units: 0.625ms)
19+
#define BLE_ADV_INTERVAL_MAX 244 // 152.5ms (units: 0.625ms)
20+
#define BLE_ADV_FAST_TIMEOUT 30 // seconds
21+
22+
// RX drain buffer size for overflow protection
23+
#define BLE_RX_DRAIN_BUF_SIZE 32
824

925
static SerialBLEInterface* instance = nullptr;
1026

@@ -38,14 +54,18 @@ void SerialBLEInterface::onSecured(uint16_t connection_handle) {
3854
// Apple: "The product will not read or use the parameters in the Peripheral Preferred Connection Parameters characteristic."
3955
// So we explicitly set it here to make Android & Apple match
4056
ble_gap_conn_params_t conn_params;
41-
conn_params.min_conn_interval = 12; // 15ms
42-
conn_params.max_conn_interval = 24; // 30ms
43-
conn_params.slave_latency = 0;
44-
conn_params.conn_sup_timeout = 200; // 2000ms
57+
conn_params.min_conn_interval = BLE_MIN_CONN_INTERVAL;
58+
conn_params.max_conn_interval = BLE_MAX_CONN_INTERVAL;
59+
conn_params.slave_latency = BLE_SLAVE_LATENCY;
60+
conn_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT;
4561

4662
uint32_t err_code = sd_ble_gap_conn_param_update(connection_handle, &conn_params);
4763
if (err_code == NRF_SUCCESS) {
48-
BLE_DEBUG_PRINTLN("Connection parameter update requested: 15-30ms interval, 2s timeout");
64+
BLE_DEBUG_PRINTLN("Connection parameter update requested: %u-%ums interval, latency=%u, %ums timeout",
65+
conn_params.min_conn_interval * 5 / 4, // convert to ms (1.25ms units)
66+
conn_params.max_conn_interval * 5 / 4,
67+
conn_params.slave_latency,
68+
conn_params.conn_sup_timeout * 10); // convert to ms (10ms units)
4969
} else {
5070
BLE_DEBUG_PRINTLN("Failed to request connection parameter update: %lu", err_code);
5171
}
@@ -116,14 +136,18 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) {
116136

117137
// Connection interval units: 1.25ms, supervision timeout units: 10ms
118138
ble_gap_conn_params_t ppcp_params;
119-
ppcp_params.min_conn_interval = 12; // 15ms
120-
ppcp_params.max_conn_interval = 24; // 30ms
121-
ppcp_params.slave_latency = 0;
122-
ppcp_params.conn_sup_timeout = 200; // 2000ms
139+
ppcp_params.min_conn_interval = BLE_MIN_CONN_INTERVAL;
140+
ppcp_params.max_conn_interval = BLE_MAX_CONN_INTERVAL;
141+
ppcp_params.slave_latency = BLE_SLAVE_LATENCY;
142+
ppcp_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT;
123143

124144
uint32_t err_code = sd_ble_gap_ppcp_set(&ppcp_params);
125145
if (err_code == NRF_SUCCESS) {
126-
BLE_DEBUG_PRINTLN("PPCP set: 15-30ms interval, 2s timeout");
146+
BLE_DEBUG_PRINTLN("PPCP set: %u-%ums interval, latency=%u, %ums timeout",
147+
ppcp_params.min_conn_interval * 5 / 4, // convert to ms (1.25ms units)
148+
ppcp_params.max_conn_interval * 5 / 4,
149+
ppcp_params.slave_latency,
150+
ppcp_params.conn_sup_timeout * 10); // convert to ms (10ms units)
127151
} else {
128152
BLE_DEBUG_PRINTLN("Failed to set PPCP: %lu", err_code);
129153
}
@@ -153,8 +177,8 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) {
153177

154178
Bluefruit.ScanResponse.addName();
155179

156-
Bluefruit.Advertising.setInterval(32, 244);
157-
Bluefruit.Advertising.setFastTimeout(30);
180+
Bluefruit.Advertising.setInterval(BLE_ADV_INTERVAL_MIN, BLE_ADV_INTERVAL_MAX);
181+
Bluefruit.Advertising.setFastTimeout(BLE_ADV_FAST_TIMEOUT);
158182

159183
Bluefruit.Advertising.restartOnDisconnect(true);
160184

@@ -163,6 +187,7 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) {
163187
void SerialBLEInterface::clearBuffers() {
164188
send_queue_len = 0;
165189
recv_queue_len = 0;
190+
_last_retry_attempt = 0;
166191
bleuart.flush();
167192
}
168193

@@ -257,21 +282,30 @@ size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) {
257282
BLE_DEBUG_PRINTLN("writeBytes: connection invalid, clearing send queue");
258283
send_queue_len = 0;
259284
} else {
260-
Frame frame_to_send = send_queue[0];
261-
262-
size_t written = bleuart.write(frame_to_send.buf, frame_to_send.len);
263-
if (written == frame_to_send.len) {
264-
BLE_DEBUG_PRINTLN("writeBytes: sz=%u, hdr=%u", (unsigned)frame_to_send.len, (unsigned)frame_to_send.buf[0]);
265-
shiftSendQueueLeft();
266-
} else if (written > 0) {
267-
BLE_DEBUG_PRINTLN("writeBytes: partial write, sent=%u of %u, dropping corrupted frame", (unsigned)written, (unsigned)frame_to_send.len);
268-
shiftSendQueueLeft();
269-
} else {
270-
if (!isConnected()) {
271-
BLE_DEBUG_PRINTLN("writeBytes failed: connection lost, dropping frame");
285+
unsigned long now = millis();
286+
bool throttle_active = (_last_retry_attempt > 0 && (now - _last_retry_attempt) < BLE_RETRY_THROTTLE_MS);
287+
288+
if (!throttle_active) {
289+
Frame frame_to_send = send_queue[0];
290+
291+
size_t written = bleuart.write(frame_to_send.buf, frame_to_send.len);
292+
if (written == frame_to_send.len) {
293+
BLE_DEBUG_PRINTLN("writeBytes: sz=%u, hdr=%u", (unsigned)frame_to_send.len, (unsigned)frame_to_send.buf[0]);
294+
_last_retry_attempt = 0;
295+
shiftSendQueueLeft();
296+
} else if (written > 0) {
297+
BLE_DEBUG_PRINTLN("writeBytes: partial write, sent=%u of %u, dropping corrupted frame", (unsigned)written, (unsigned)frame_to_send.len);
298+
_last_retry_attempt = 0;
272299
shiftSendQueueLeft();
273300
} else {
274-
BLE_DEBUG_PRINTLN("writeBytes failed (buffer full), keeping frame for retry");
301+
if (!isConnected()) {
302+
BLE_DEBUG_PRINTLN("writeBytes failed: connection lost, dropping frame");
303+
_last_retry_attempt = 0;
304+
shiftSendQueueLeft();
305+
} else {
306+
BLE_DEBUG_PRINTLN("writeBytes failed (buffer full), keeping frame for retry");
307+
_last_retry_attempt = now;
308+
}
275309
}
276310
}
277311
}
@@ -329,9 +363,9 @@ void SerialBLEInterface::onBleUartRX(uint16_t conn_handle) {
329363

330364
if (avail > MAX_FRAME_SIZE) {
331365
BLE_DEBUG_PRINTLN("onBleUartRX: WARN: BLE RX overflow, avail=%d, draining all", avail);
332-
uint8_t drain_buf[32];
366+
uint8_t drain_buf[BLE_RX_DRAIN_BUF_SIZE];
333367
while (instance->bleuart.available() > 0) {
334-
int chunk = instance->bleuart.available() > 32 ? 32 : instance->bleuart.available();
368+
int chunk = instance->bleuart.available() > BLE_RX_DRAIN_BUF_SIZE ? BLE_RX_DRAIN_BUF_SIZE : instance->bleuart.available();
335369
instance->bleuart.readBytes(drain_buf, chunk);
336370
}
337371
continue;
@@ -349,5 +383,5 @@ bool SerialBLEInterface::isConnected() const {
349383
}
350384

351385
bool SerialBLEInterface::isWriteBusy() const {
352-
return send_queue_len >= (FRAME_QUEUE_SIZE - 1);
386+
return send_queue_len >= (FRAME_QUEUE_SIZE * 2 / 3);
353387
}

src/helpers/nrf52/SerialBLEInterface.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class SerialBLEInterface : public BaseSerialInterface {
1313
bool _isDeviceConnected;
1414
uint16_t _conn_handle;
1515
unsigned long _last_health_check;
16+
unsigned long _last_retry_attempt;
1617

1718
struct Frame {
1819
uint8_t len;
@@ -46,6 +47,7 @@ class SerialBLEInterface : public BaseSerialInterface {
4647
_isDeviceConnected = false;
4748
_conn_handle = BLE_CONN_HANDLE_INVALID;
4849
_last_health_check = 0;
50+
_last_retry_attempt = 0;
4951
send_queue_len = 0;
5052
recv_queue_len = 0;
5153
}

0 commit comments

Comments
 (0)