mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
queue throttling + slave latency and minor refactor
This commit is contained in:
parent
d67f311c3d
commit
cba29ea50c
2 changed files with 64 additions and 28 deletions
|
|
@ -4,7 +4,23 @@
|
||||||
#include "ble_gap.h"
|
#include "ble_gap.h"
|
||||||
#include "ble_hci.h"
|
#include "ble_hci.h"
|
||||||
|
|
||||||
|
// Magic numbers came from actual testing
|
||||||
#define BLE_HEALTH_CHECK_INTERVAL 10000 // Advertising watchdog check every 10 seconds
|
#define BLE_HEALTH_CHECK_INTERVAL 10000 // Advertising watchdog check every 10 seconds
|
||||||
|
#define BLE_RETRY_THROTTLE_MS 250 // Throttle retries to 250ms when queue buildup detected
|
||||||
|
|
||||||
|
// Connection parameters (units: interval=1.25ms, timeout=10ms)
|
||||||
|
#define BLE_MIN_CONN_INTERVAL 12 // 15ms
|
||||||
|
#define BLE_MAX_CONN_INTERVAL 24 // 30ms
|
||||||
|
#define BLE_SLAVE_LATENCY 4
|
||||||
|
#define BLE_CONN_SUP_TIMEOUT 200 // 2000ms
|
||||||
|
|
||||||
|
// Advertising parameters
|
||||||
|
#define BLE_ADV_INTERVAL_MIN 32 // 20ms (units: 0.625ms)
|
||||||
|
#define BLE_ADV_INTERVAL_MAX 244 // 152.5ms (units: 0.625ms)
|
||||||
|
#define BLE_ADV_FAST_TIMEOUT 30 // seconds
|
||||||
|
|
||||||
|
// RX drain buffer size for overflow protection
|
||||||
|
#define BLE_RX_DRAIN_BUF_SIZE 32
|
||||||
|
|
||||||
static SerialBLEInterface* instance = nullptr;
|
static SerialBLEInterface* instance = nullptr;
|
||||||
|
|
||||||
|
|
@ -38,14 +54,18 @@ void SerialBLEInterface::onSecured(uint16_t connection_handle) {
|
||||||
// Apple: "The product will not read or use the parameters in the Peripheral Preferred Connection Parameters characteristic."
|
// Apple: "The product will not read or use the parameters in the Peripheral Preferred Connection Parameters characteristic."
|
||||||
// So we explicitly set it here to make Android & Apple match
|
// So we explicitly set it here to make Android & Apple match
|
||||||
ble_gap_conn_params_t conn_params;
|
ble_gap_conn_params_t conn_params;
|
||||||
conn_params.min_conn_interval = 12; // 15ms
|
conn_params.min_conn_interval = BLE_MIN_CONN_INTERVAL;
|
||||||
conn_params.max_conn_interval = 24; // 30ms
|
conn_params.max_conn_interval = BLE_MAX_CONN_INTERVAL;
|
||||||
conn_params.slave_latency = 0;
|
conn_params.slave_latency = BLE_SLAVE_LATENCY;
|
||||||
conn_params.conn_sup_timeout = 200; // 2000ms
|
conn_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT;
|
||||||
|
|
||||||
uint32_t err_code = sd_ble_gap_conn_param_update(connection_handle, &conn_params);
|
uint32_t err_code = sd_ble_gap_conn_param_update(connection_handle, &conn_params);
|
||||||
if (err_code == NRF_SUCCESS) {
|
if (err_code == NRF_SUCCESS) {
|
||||||
BLE_DEBUG_PRINTLN("Connection parameter update requested: 15-30ms interval, 2s timeout");
|
BLE_DEBUG_PRINTLN("Connection parameter update requested: %u-%ums interval, latency=%u, %ums timeout",
|
||||||
|
conn_params.min_conn_interval * 5 / 4, // convert to ms (1.25ms units)
|
||||||
|
conn_params.max_conn_interval * 5 / 4,
|
||||||
|
conn_params.slave_latency,
|
||||||
|
conn_params.conn_sup_timeout * 10); // convert to ms (10ms units)
|
||||||
} else {
|
} else {
|
||||||
BLE_DEBUG_PRINTLN("Failed to request connection parameter update: %lu", err_code);
|
BLE_DEBUG_PRINTLN("Failed to request connection parameter update: %lu", err_code);
|
||||||
}
|
}
|
||||||
|
|
@ -116,14 +136,18 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) {
|
||||||
|
|
||||||
// Connection interval units: 1.25ms, supervision timeout units: 10ms
|
// Connection interval units: 1.25ms, supervision timeout units: 10ms
|
||||||
ble_gap_conn_params_t ppcp_params;
|
ble_gap_conn_params_t ppcp_params;
|
||||||
ppcp_params.min_conn_interval = 12; // 15ms
|
ppcp_params.min_conn_interval = BLE_MIN_CONN_INTERVAL;
|
||||||
ppcp_params.max_conn_interval = 24; // 30ms
|
ppcp_params.max_conn_interval = BLE_MAX_CONN_INTERVAL;
|
||||||
ppcp_params.slave_latency = 0;
|
ppcp_params.slave_latency = BLE_SLAVE_LATENCY;
|
||||||
ppcp_params.conn_sup_timeout = 200; // 2000ms
|
ppcp_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT;
|
||||||
|
|
||||||
uint32_t err_code = sd_ble_gap_ppcp_set(&ppcp_params);
|
uint32_t err_code = sd_ble_gap_ppcp_set(&ppcp_params);
|
||||||
if (err_code == NRF_SUCCESS) {
|
if (err_code == NRF_SUCCESS) {
|
||||||
BLE_DEBUG_PRINTLN("PPCP set: 15-30ms interval, 2s timeout");
|
BLE_DEBUG_PRINTLN("PPCP set: %u-%ums interval, latency=%u, %ums timeout",
|
||||||
|
ppcp_params.min_conn_interval * 5 / 4, // convert to ms (1.25ms units)
|
||||||
|
ppcp_params.max_conn_interval * 5 / 4,
|
||||||
|
ppcp_params.slave_latency,
|
||||||
|
ppcp_params.conn_sup_timeout * 10); // convert to ms (10ms units)
|
||||||
} else {
|
} else {
|
||||||
BLE_DEBUG_PRINTLN("Failed to set PPCP: %lu", err_code);
|
BLE_DEBUG_PRINTLN("Failed to set PPCP: %lu", err_code);
|
||||||
}
|
}
|
||||||
|
|
@ -153,8 +177,8 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) {
|
||||||
|
|
||||||
Bluefruit.ScanResponse.addName();
|
Bluefruit.ScanResponse.addName();
|
||||||
|
|
||||||
Bluefruit.Advertising.setInterval(32, 244);
|
Bluefruit.Advertising.setInterval(BLE_ADV_INTERVAL_MIN, BLE_ADV_INTERVAL_MAX);
|
||||||
Bluefruit.Advertising.setFastTimeout(30);
|
Bluefruit.Advertising.setFastTimeout(BLE_ADV_FAST_TIMEOUT);
|
||||||
|
|
||||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||||
|
|
||||||
|
|
@ -163,6 +187,7 @@ void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) {
|
||||||
void SerialBLEInterface::clearBuffers() {
|
void SerialBLEInterface::clearBuffers() {
|
||||||
send_queue_len = 0;
|
send_queue_len = 0;
|
||||||
recv_queue_len = 0;
|
recv_queue_len = 0;
|
||||||
|
_last_retry_attempt = 0;
|
||||||
bleuart.flush();
|
bleuart.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,21 +282,30 @@ size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) {
|
||||||
BLE_DEBUG_PRINTLN("writeBytes: connection invalid, clearing send queue");
|
BLE_DEBUG_PRINTLN("writeBytes: connection invalid, clearing send queue");
|
||||||
send_queue_len = 0;
|
send_queue_len = 0;
|
||||||
} else {
|
} else {
|
||||||
Frame frame_to_send = send_queue[0];
|
unsigned long now = millis();
|
||||||
|
bool throttle_active = (_last_retry_attempt > 0 && (now - _last_retry_attempt) < BLE_RETRY_THROTTLE_MS);
|
||||||
|
|
||||||
size_t written = bleuart.write(frame_to_send.buf, frame_to_send.len);
|
if (!throttle_active) {
|
||||||
if (written == frame_to_send.len) {
|
Frame frame_to_send = send_queue[0];
|
||||||
BLE_DEBUG_PRINTLN("writeBytes: sz=%u, hdr=%u", (unsigned)frame_to_send.len, (unsigned)frame_to_send.buf[0]);
|
|
||||||
shiftSendQueueLeft();
|
size_t written = bleuart.write(frame_to_send.buf, frame_to_send.len);
|
||||||
} else if (written > 0) {
|
if (written == frame_to_send.len) {
|
||||||
BLE_DEBUG_PRINTLN("writeBytes: partial write, sent=%u of %u, dropping corrupted frame", (unsigned)written, (unsigned)frame_to_send.len);
|
BLE_DEBUG_PRINTLN("writeBytes: sz=%u, hdr=%u", (unsigned)frame_to_send.len, (unsigned)frame_to_send.buf[0]);
|
||||||
shiftSendQueueLeft();
|
_last_retry_attempt = 0;
|
||||||
} else {
|
shiftSendQueueLeft();
|
||||||
if (!isConnected()) {
|
} else if (written > 0) {
|
||||||
BLE_DEBUG_PRINTLN("writeBytes failed: connection lost, dropping frame");
|
BLE_DEBUG_PRINTLN("writeBytes: partial write, sent=%u of %u, dropping corrupted frame", (unsigned)written, (unsigned)frame_to_send.len);
|
||||||
|
_last_retry_attempt = 0;
|
||||||
shiftSendQueueLeft();
|
shiftSendQueueLeft();
|
||||||
} else {
|
} else {
|
||||||
BLE_DEBUG_PRINTLN("writeBytes failed (buffer full), keeping frame for retry");
|
if (!isConnected()) {
|
||||||
|
BLE_DEBUG_PRINTLN("writeBytes failed: connection lost, dropping frame");
|
||||||
|
_last_retry_attempt = 0;
|
||||||
|
shiftSendQueueLeft();
|
||||||
|
} else {
|
||||||
|
BLE_DEBUG_PRINTLN("writeBytes failed (buffer full), keeping frame for retry");
|
||||||
|
_last_retry_attempt = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -329,9 +363,9 @@ void SerialBLEInterface::onBleUartRX(uint16_t conn_handle) {
|
||||||
|
|
||||||
if (avail > MAX_FRAME_SIZE) {
|
if (avail > MAX_FRAME_SIZE) {
|
||||||
BLE_DEBUG_PRINTLN("onBleUartRX: WARN: BLE RX overflow, avail=%d, draining all", avail);
|
BLE_DEBUG_PRINTLN("onBleUartRX: WARN: BLE RX overflow, avail=%d, draining all", avail);
|
||||||
uint8_t drain_buf[32];
|
uint8_t drain_buf[BLE_RX_DRAIN_BUF_SIZE];
|
||||||
while (instance->bleuart.available() > 0) {
|
while (instance->bleuart.available() > 0) {
|
||||||
int chunk = instance->bleuart.available() > 32 ? 32 : instance->bleuart.available();
|
int chunk = instance->bleuart.available() > BLE_RX_DRAIN_BUF_SIZE ? BLE_RX_DRAIN_BUF_SIZE : instance->bleuart.available();
|
||||||
instance->bleuart.readBytes(drain_buf, chunk);
|
instance->bleuart.readBytes(drain_buf, chunk);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -349,5 +383,5 @@ bool SerialBLEInterface::isConnected() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SerialBLEInterface::isWriteBusy() const {
|
bool SerialBLEInterface::isWriteBusy() const {
|
||||||
return send_queue_len >= (FRAME_QUEUE_SIZE - 1);
|
return send_queue_len >= (FRAME_QUEUE_SIZE * 2 / 3);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ class SerialBLEInterface : public BaseSerialInterface {
|
||||||
bool _isDeviceConnected;
|
bool _isDeviceConnected;
|
||||||
uint16_t _conn_handle;
|
uint16_t _conn_handle;
|
||||||
unsigned long _last_health_check;
|
unsigned long _last_health_check;
|
||||||
|
unsigned long _last_retry_attempt;
|
||||||
|
|
||||||
struct Frame {
|
struct Frame {
|
||||||
uint8_t len;
|
uint8_t len;
|
||||||
|
|
@ -46,6 +47,7 @@ public:
|
||||||
_isDeviceConnected = false;
|
_isDeviceConnected = false;
|
||||||
_conn_handle = BLE_CONN_HANDLE_INVALID;
|
_conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||||
_last_health_check = 0;
|
_last_health_check = 0;
|
||||||
|
_last_retry_attempt = 0;
|
||||||
send_queue_len = 0;
|
send_queue_len = 0;
|
||||||
recv_queue_len = 0;
|
recv_queue_len = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue