feat: Add v0.5 BLE scanning — NimBLE passive scan, BLE_DATA UDP stream
- NimBLE stack init with passive BLE scanning - BLE ON/OFF command with NVS persistence - BLE_DATA,<mac>,<rssi>,<pub|rnd>,<name> UDP packets - 30s periodic scan restart to refresh duplicate filter - ble= field in STATUS reply - sdkconfig: enable BT+NimBLE, BLE-only mode, disable Bluedroid
This commit is contained in:
@@ -42,6 +42,10 @@
|
|||||||
#include "lwip/sockets.h"
|
#include "lwip/sockets.h"
|
||||||
#include "ping/ping_sock.h"
|
#include "ping/ping_sock.h"
|
||||||
|
|
||||||
|
#include "nimble/nimble_port.h"
|
||||||
|
#include "nimble/nimble_port_freertos.h"
|
||||||
|
#include "host/ble_hs.h"
|
||||||
|
#include "host/util/util.h"
|
||||||
#include "protocol_examples_common.h"
|
#include "protocol_examples_common.h"
|
||||||
#include "esp_csi_gain_ctrl.h"
|
#include "esp_csi_gain_ctrl.h"
|
||||||
|
|
||||||
@@ -95,6 +99,11 @@ static volatile int64_t s_last_motion_time = 0;
|
|||||||
static uint32_t s_energy_buf[WANDER_WINDOW];
|
static uint32_t s_energy_buf[WANDER_WINDOW];
|
||||||
static uint32_t s_energy_idx = 0;
|
static uint32_t s_energy_idx = 0;
|
||||||
|
|
||||||
|
/* BLE scanning */
|
||||||
|
#define BLE_SCAN_RESTART_US 30000000LL /* restart scan every 30s to refresh duplicate filter */
|
||||||
|
static bool s_ble_enabled = false;
|
||||||
|
static uint8_t s_ble_own_addr_type;
|
||||||
|
|
||||||
/* UDP socket for CSI data transmission */
|
/* UDP socket for CSI data transmission */
|
||||||
static int s_udp_socket = -1;
|
static int s_udp_socket = -1;
|
||||||
static struct sockaddr_in s_dest_addr;
|
static struct sockaddr_in s_dest_addr;
|
||||||
@@ -122,9 +131,13 @@ static void config_load_nvs(void)
|
|||||||
if (nvs_get_i32(h, "threshold", &thresh) == ESP_OK && thresh > 0) {
|
if (nvs_get_i32(h, "threshold", &thresh) == ESP_OK && thresh > 0) {
|
||||||
s_motion_threshold = (float)thresh / 1000000.0f;
|
s_motion_threshold = (float)thresh / 1000000.0f;
|
||||||
}
|
}
|
||||||
|
int8_t ble;
|
||||||
|
if (nvs_get_i8(h, "ble_scan", &ble) == ESP_OK) {
|
||||||
|
s_ble_enabled = (ble != 0);
|
||||||
|
}
|
||||||
nvs_close(h);
|
nvs_close(h);
|
||||||
ESP_LOGI(TAG, "NVS loaded: rate=%d tx_power=%d adaptive=%d threshold=%.6f",
|
ESP_LOGI(TAG, "NVS loaded: rate=%d tx_power=%d adaptive=%d threshold=%.6f ble=%d",
|
||||||
s_send_frequency, s_tx_power_dbm, s_adaptive, s_motion_threshold);
|
s_send_frequency, s_tx_power_dbm, s_adaptive, s_motion_threshold, s_ble_enabled);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "NVS: no saved config, using defaults");
|
ESP_LOGI(TAG, "NVS: no saved config, using defaults");
|
||||||
}
|
}
|
||||||
@@ -426,6 +439,115 @@ static esp_err_t wifi_ping_router_start(void)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- BLE scanning --- */
|
||||||
|
|
||||||
|
static int ble_gap_event_cb(struct ble_gap_event *event, void *arg);
|
||||||
|
|
||||||
|
static void ble_scan_start(void)
|
||||||
|
{
|
||||||
|
struct ble_gap_disc_params disc_params = {0};
|
||||||
|
disc_params.passive = 1;
|
||||||
|
disc_params.filter_duplicates = 1;
|
||||||
|
disc_params.itvl = 0;
|
||||||
|
disc_params.window = 0;
|
||||||
|
disc_params.filter_policy = 0;
|
||||||
|
disc_params.limited = 0;
|
||||||
|
|
||||||
|
int rc = ble_gap_disc(s_ble_own_addr_type, BLE_HS_FOREVER, &disc_params,
|
||||||
|
ble_gap_event_cb, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "BLE: scan start failed rc=%d", rc);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "BLE: scan started ok");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ble_gap_event_cb(struct ble_gap_event *event, void *arg)
|
||||||
|
{
|
||||||
|
switch (event->type) {
|
||||||
|
case BLE_GAP_EVENT_DISC: {
|
||||||
|
struct ble_gap_disc_desc *disc = &event->disc;
|
||||||
|
ESP_LOGI(TAG, "BLE: disc event rssi=%d addr=%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
disc->rssi, disc->addr.val[5], disc->addr.val[4], disc->addr.val[3],
|
||||||
|
disc->addr.val[2], disc->addr.val[1], disc->addr.val[0]);
|
||||||
|
|
||||||
|
/* Parse advertisement for device name */
|
||||||
|
struct ble_hs_adv_fields fields;
|
||||||
|
int rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
|
||||||
|
|
||||||
|
char name[32] = "";
|
||||||
|
if (rc == 0 && fields.name != NULL && fields.name_len > 0) {
|
||||||
|
int nlen = fields.name_len < (int)sizeof(name) - 1 ? fields.name_len : (int)sizeof(name) - 1;
|
||||||
|
memcpy(name, fields.name, nlen);
|
||||||
|
name[nlen] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send BLE_DATA via UDP */
|
||||||
|
char buf[128];
|
||||||
|
int len = snprintf(buf, sizeof(buf),
|
||||||
|
"BLE_DATA,%02x:%02x:%02x:%02x:%02x:%02x,%d,%s,%s\n",
|
||||||
|
disc->addr.val[5], disc->addr.val[4], disc->addr.val[3],
|
||||||
|
disc->addr.val[2], disc->addr.val[1], disc->addr.val[0],
|
||||||
|
disc->rssi,
|
||||||
|
disc->addr.type == BLE_ADDR_PUBLIC ? "pub" : "rnd",
|
||||||
|
name);
|
||||||
|
|
||||||
|
if (s_udp_socket >= 0) {
|
||||||
|
sendto(s_udp_socket, buf, len, 0,
|
||||||
|
(struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BLE_GAP_EVENT_DISC_COMPLETE:
|
||||||
|
if (s_ble_enabled) {
|
||||||
|
ble_scan_start();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_scan_restart_timer_cb(void *arg)
|
||||||
|
{
|
||||||
|
if (s_ble_enabled) {
|
||||||
|
ble_gap_disc_cancel();
|
||||||
|
ble_scan_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_on_sync(void)
|
||||||
|
{
|
||||||
|
int rc = ble_hs_util_ensure_addr(0);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "BLE: ensure addr failed rc=%d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rc = ble_hs_id_infer_auto(0, &s_ble_own_addr_type);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(TAG, "BLE: infer addr type failed rc=%d", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "BLE: stack synced, addr_type=%d", s_ble_own_addr_type);
|
||||||
|
|
||||||
|
if (s_ble_enabled) {
|
||||||
|
ble_scan_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_on_reset(int reason)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "BLE: stack reset reason=%d", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_host_task(void *param)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "BLE host task started");
|
||||||
|
nimble_port_run();
|
||||||
|
nimble_port_freertos_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Adaptive sampling --- */
|
/* --- Adaptive sampling --- */
|
||||||
|
|
||||||
static void adaptive_task(void *arg)
|
static void adaptive_task(void *arg)
|
||||||
@@ -577,10 +699,11 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
snprintf(reply, reply_size,
|
snprintf(reply, reply_size,
|
||||||
"OK STATUS uptime=%s heap=%lu rssi=%d tx_power=%d rate=%d hostname=%s version=%s adaptive=%s motion=%d",
|
"OK STATUS uptime=%s heap=%lu rssi=%d tx_power=%d rate=%d hostname=%s version=%s adaptive=%s motion=%d ble=%s",
|
||||||
uptime_str, (unsigned long)heap, rssi, (int)s_tx_power_dbm,
|
uptime_str, (unsigned long)heap, rssi, (int)s_tx_power_dbm,
|
||||||
s_send_frequency, CONFIG_CSI_HOSTNAME, app_desc->version,
|
s_send_frequency, CONFIG_CSI_HOSTNAME, app_desc->version,
|
||||||
s_adaptive ? "on" : "off", s_motion_detected ? 1 : 0);
|
s_adaptive ? "on" : "off", s_motion_detected ? 1 : 0,
|
||||||
|
s_ble_enabled ? "on" : "off");
|
||||||
return strlen(reply);
|
return strlen(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,6 +740,25 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
return strlen(reply);
|
return strlen(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* BLE ON/OFF */
|
||||||
|
if (strncmp(cmd, "BLE ", 4) == 0) {
|
||||||
|
const char *arg = cmd + 4;
|
||||||
|
if (strncmp(arg, "ON", 2) == 0) {
|
||||||
|
s_ble_enabled = true;
|
||||||
|
config_save_i8("ble_scan", 1);
|
||||||
|
ble_scan_start();
|
||||||
|
snprintf(reply, reply_size, "OK BLE scanning on");
|
||||||
|
} else if (strncmp(arg, "OFF", 3) == 0) {
|
||||||
|
s_ble_enabled = false;
|
||||||
|
config_save_i8("ble_scan", 0);
|
||||||
|
ble_gap_disc_cancel();
|
||||||
|
snprintf(reply, reply_size, "OK BLE scanning off");
|
||||||
|
} else {
|
||||||
|
snprintf(reply, reply_size, "ERR BLE ON or OFF");
|
||||||
|
}
|
||||||
|
return strlen(reply);
|
||||||
|
}
|
||||||
|
|
||||||
/* ADAPTIVE ON/OFF */
|
/* ADAPTIVE ON/OFF */
|
||||||
if (strncmp(cmd, "ADAPTIVE ", 9) == 0) {
|
if (strncmp(cmd, "ADAPTIVE ", 9) == 0) {
|
||||||
const char *arg = cmd + 9;
|
const char *arg = cmd + 9;
|
||||||
@@ -769,6 +911,23 @@ void app_main()
|
|||||||
ESP_ERROR_CHECK(esp_task_wdt_reconfigure(&wdt_cfg));
|
ESP_ERROR_CHECK(esp_task_wdt_reconfigure(&wdt_cfg));
|
||||||
ESP_LOGI(TAG, "Watchdog configured: 30s timeout");
|
ESP_LOGI(TAG, "Watchdog configured: 30s timeout");
|
||||||
|
|
||||||
|
/* BLE: Initialize NimBLE stack */
|
||||||
|
ESP_ERROR_CHECK(nimble_port_init());
|
||||||
|
ble_hs_cfg.reset_cb = ble_on_reset;
|
||||||
|
ble_hs_cfg.sync_cb = ble_on_sync;
|
||||||
|
nimble_port_freertos_init(ble_host_task);
|
||||||
|
|
||||||
|
/* BLE: periodic scan restart to refresh duplicate filter */
|
||||||
|
const esp_timer_create_args_t ble_timer_args = {
|
||||||
|
.callback = ble_scan_restart_timer_cb,
|
||||||
|
.name = "ble_scan",
|
||||||
|
};
|
||||||
|
esp_timer_handle_t ble_timer;
|
||||||
|
esp_timer_create(&ble_timer_args, &ble_timer);
|
||||||
|
esp_timer_start_periodic(ble_timer, BLE_SCAN_RESTART_US);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "BLE: NimBLE initialized, scan=%s", s_ble_enabled ? "on" : "off");
|
||||||
|
|
||||||
s_led_mode = LED_SLOW_BLINK;
|
s_led_mode = LED_SLOW_BLINK;
|
||||||
|
|
||||||
udp_socket_init();
|
udp_socket_init();
|
||||||
|
|||||||
@@ -62,3 +62,14 @@ CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
|||||||
#
|
#
|
||||||
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
||||||
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
|
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# BLE (NimBLE, scan-only, WiFi coexistence)
|
||||||
|
#
|
||||||
|
CONFIG_BT_ENABLED=y
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
|
||||||
|
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||||
|
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||||
|
CONFIG_BT_NIMBLE_ENABLED=y
|
||||||
|
CONFIG_ESP_WIFI_IRAM_OPT=n
|
||||||
|
|||||||
Reference in New Issue
Block a user