perf: tune BLE scanning performance, switch over to NimBLE
This commit is contained in:
+1
-1
@@ -3,4 +3,4 @@ cmake_minimum_required(VERSION 3.22)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(esp-anchor VERSION 0.1.0)
|
||||
project(esp-anchor VERSION 0.2.0)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "src/ble_scanner.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES bt esp_event
|
||||
REQUIRES esp_event bt
|
||||
)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
menu "BLE Scanner"
|
||||
|
||||
config BLE_SCAN_INTERVAL_MS
|
||||
int "Scan interval (ms)"
|
||||
default 100
|
||||
range 3 10240
|
||||
help
|
||||
Time between the start of consecutive scan windows (0.625 ms units
|
||||
internally). Larger values reduce duty cycle and leave more airtime
|
||||
for WiFi.
|
||||
|
||||
config BLE_SCAN_WINDOW_MS
|
||||
int "Scan window (ms)"
|
||||
default 80
|
||||
range 3 10240
|
||||
help
|
||||
Duration of each active scan window. Must be <= scan interval.
|
||||
Default 80 ms gives 80% duty cycle (up from 50% with previous
|
||||
defaults) without starving WiFi TX.
|
||||
|
||||
endmenu
|
||||
@@ -23,8 +23,8 @@ typedef struct {
|
||||
typedef void (*ble_scanner_cb_t)(const ble_beacon_t *beacon);
|
||||
|
||||
/**
|
||||
* Initialise the Bluedroid BLE stack and register the scan callback.
|
||||
* Must be called once after esp_bt_controller_init / esp_bluedroid_init.
|
||||
* Initialise the NimBLE stack and register the scan callback. Must be called
|
||||
* once before ble_scanner_start(). Starts the NimBLE host task internally.
|
||||
*/
|
||||
void ble_scanner_init(ble_scanner_cb_t cb);
|
||||
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
#include "ble_scanner.h"
|
||||
#include "esp_bt.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "esp_log.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||
|
||||
#define TAG "ble_scanner"
|
||||
|
||||
static ble_scanner_cb_t s_cb = NULL;
|
||||
/* Units: 1 tick = 0.625 ms */
|
||||
#define MS_TO_BLE_TICKS(ms) ((ms) * 1000 / 625)
|
||||
|
||||
/* Passive scan: 100ms interval, 50ms window (50% duty cycle) */
|
||||
static esp_ble_scan_params_t s_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_PASSIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0xA0, /* 100ms: 0xA0 * 0.625ms */
|
||||
.scan_window = 0x50, /* 50ms: 0x50 * 0.625ms */
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE,
|
||||
};
|
||||
static ble_scanner_cb_t s_cb = NULL;
|
||||
static bool s_scan_active = false;
|
||||
static uint8_t s_own_addr_type;
|
||||
|
||||
/* Eddystone URL expansion codes 0x00-0x0D per spec */
|
||||
static const char *const s_url_expansions[] = {
|
||||
@@ -150,69 +147,72 @@ static bool parse_beacon(const uint8_t *data, uint8_t len, ble_beacon_t *out)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gap_event_handler(esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t *param)
|
||||
static void do_start_scan(void)
|
||||
{
|
||||
switch (event) {
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||
if (param->scan_param_cmpl.status == ESP_BT_STATUS_SUCCESS) {
|
||||
esp_ble_gap_start_scanning(0); /* 0 = scan indefinitely */
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Scan param set failed: %d", param->scan_param_cmpl.status);
|
||||
}
|
||||
break;
|
||||
struct ble_gap_disc_params p = {
|
||||
.itvl = MS_TO_BLE_TICKS(CONFIG_BLE_SCAN_INTERVAL_MS),
|
||||
.window = MS_TO_BLE_TICKS(CONFIG_BLE_SCAN_WINDOW_MS),
|
||||
.filter_policy = 0, /* accept all, no whitelist */
|
||||
.limited = 0,
|
||||
.passive = 1,
|
||||
.filter_duplicates = 0, /* report every advertisement for live RSSI */
|
||||
};
|
||||
int rc = ble_gap_disc(s_own_addr_type, BLE_HS_FOREVER, &p, gap_event_cb, NULL);
|
||||
if (rc != 0)
|
||||
ESP_LOGE(TAG, "ble_gap_disc failed: %d", rc);
|
||||
}
|
||||
|
||||
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||
if (param->scan_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Scan start failed: %d", param->scan_start_cmpl.status);
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
|
||||
esp_ble_gap_cb_param_t *p = param;
|
||||
if (p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT && s_cb) {
|
||||
static int gap_event_cb(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
if (event->type == BLE_GAP_EVENT_DISC && s_cb) {
|
||||
ble_beacon_t beacon = {0};
|
||||
beacon.rssi = (int8_t)p->scan_rst.rssi;
|
||||
if (parse_beacon(p->scan_rst.ble_adv, p->scan_rst.adv_data_len, &beacon)) {
|
||||
beacon.rssi = event->disc.rssi;
|
||||
if (parse_beacon(event->disc.data, event->disc.length_data, &beacon))
|
||||
s_cb(&beacon);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||
ESP_LOGI(TAG, "Scan stopped");
|
||||
break;
|
||||
static void on_nimble_sync(void)
|
||||
{
|
||||
ble_hs_id_infer_auto(0, &s_own_addr_type);
|
||||
if (s_scan_active)
|
||||
do_start_scan();
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
static void on_nimble_reset(int reason)
|
||||
{
|
||||
ESP_LOGW(TAG, "NimBLE reset: %d", reason);
|
||||
}
|
||||
|
||||
static void nimble_host_task(void *arg)
|
||||
{
|
||||
nimble_port_run();
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
void ble_scanner_init(ble_scanner_cb_t cb)
|
||||
{
|
||||
s_cb = cb;
|
||||
|
||||
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
|
||||
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
|
||||
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
|
||||
ESP_ERROR_CHECK(esp_bluedroid_init());
|
||||
ESP_ERROR_CHECK(esp_bluedroid_enable());
|
||||
|
||||
ESP_ERROR_CHECK(esp_ble_gap_register_callback(gap_event_handler));
|
||||
ESP_LOGI(TAG, "BLE scanner initialised");
|
||||
nimble_port_init();
|
||||
ble_hs_cfg.sync_cb = on_nimble_sync;
|
||||
ble_hs_cfg.reset_cb = on_nimble_reset;
|
||||
nimble_port_freertos_init(nimble_host_task);
|
||||
ESP_LOGI(TAG, "BLE scanner initialised (NimBLE)");
|
||||
}
|
||||
|
||||
void ble_scanner_start(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Starting BLE scan");
|
||||
ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&s_scan_params));
|
||||
/* Scanning begins in the param-set callback once confirmed */
|
||||
ESP_LOGI(TAG, "Starting BLE scan (interval=%dms window=%dms)",
|
||||
CONFIG_BLE_SCAN_INTERVAL_MS, CONFIG_BLE_SCAN_WINDOW_MS);
|
||||
s_scan_active = true;
|
||||
if (ble_hs_synced())
|
||||
do_start_scan();
|
||||
/* else: on_nimble_sync() will start scanning once host syncs with controller */
|
||||
}
|
||||
|
||||
void ble_scanner_stop(void)
|
||||
{
|
||||
esp_ble_gap_stop_scanning();
|
||||
s_scan_active = false;
|
||||
ble_gap_disc_cancel();
|
||||
}
|
||||
|
||||
@@ -16,4 +16,5 @@ idf_component_register(
|
||||
app_update
|
||||
driver
|
||||
esp_driver_gpio
|
||||
esp_coex
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "led_indicator.h"
|
||||
#include "ota_manager.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_coexist.h"
|
||||
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
@@ -236,6 +237,8 @@ void app_main(void)
|
||||
ESP_LOGI(TAG, "OTA rollback cancelled - firmware validated");
|
||||
}
|
||||
|
||||
esp_coex_preference_set(ESP_COEX_PREFER_BALANCE);
|
||||
|
||||
// init BLE scanner
|
||||
ble_scanner_init(on_ble_scan_result);
|
||||
ble_scanner_start();
|
||||
|
||||
+13
-2
@@ -1,7 +1,18 @@
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
# NimBLE host task pinned to core 0 alongside the BT controller (WiFi stays core 1)
|
||||
CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y
|
||||
# Only provisioning needs 1 connection; scanning needs 0
|
||||
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1
|
||||
# mbuf pool sized for high-throughput advertisement reports
|
||||
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=16
|
||||
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_SIZE=320
|
||||
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=6
|
||||
CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=4096
|
||||
# Advertisement report flow control
|
||||
CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y
|
||||
CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM=100
|
||||
|
||||
# WiFi
|
||||
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
|
||||
|
||||
Reference in New Issue
Block a user