Zephyr RTOS 下 bt_le_scan_start 函数详解
目录概述1 函数功能介绍1.1 函数原型和参数和主要参数1.2 主要功能1.3 扫描参数选择指南2 基本使用方法2.1 简单扫描示例2.2 完整的使用流程3 高级用法3.1 被动扫描与主动扫描对比3.2 使用白名单过滤3.3 扫描参数计算和优化3.4 解析特定广播数据如iBeacon4 重要注意事项4.1 功耗管理4.2 回调函数注意事项4.3 内存管理4.4 错误处理5 常见问题解决概述bt_le_scan_start是 Zephyr Bluetooth API 的核心函数之一用于启动蓝牙低功耗BLE扫描。它允许设备作为中心设备Central或观察者Observer发现周围的蓝牙外围设备。1 函数功能介绍1.1 函数原型和参数和主要参数1 函数原型int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb);2 主要参数- param- 扫描参数结构体struct bt_le_scan_param { uint8_t type; // 扫描类型被动或主动 uint8_t options; // 扫描选项位掩码 uint16_t interval; // 扫描间隔单位0.625ms uint16_t window; // 扫描窗口单位0.625ms uint8_t timeout; // 扫描超时单位秒0持续扫描 };-1扫描类型type#define BT_LE_SCAN_TYPE_PASSIVE 0x00 // 被动扫描 #define BT_LE_SCAN_TYPE_ACTIVE 0x01 // 主动扫描-2 扫描选项options#define BT_LE_SCAN_OPT_NONE 0x00 // 无选项 #define BT_LE_SCAN_OPT_FILTER_DUPLICATE 0x01 // 过滤重复广播 #define BT_LE_SCAN_OPT_FILTER_WHITELIST 0x02 // 只扫描白名单设备 #define BT_LE_SCAN_OPT_CODED 0x04 // 使用编码PHYBLE 5.03cb- 扫描回调函数typedef void bt_le_scan_cb_t(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf);回调参数addr发现的设备地址rssi信号强度指示器dBmadv_type广播类型buf广播数据缓冲区广播类型adv_type#define BT_GAP_ADV_TYPE_ADV_IND 0x00 // 可连接的非定向广播 #define BT_GAP_ADV_TYPE_ADV_DIRECT_IND 0x01 // 可连接的定向广播 #define BT_GAP_ADV_TYPE_ADV_SCAN_IND 0x02 // 可扫描的非定向广播 #define BT_GAP_ADV_TYPE_ADV_NONCONN_IND 0x03 // 不可连接的非定向广播 #define BT_GAP_ADV_TYPE_SCAN_RSP 0x04 // 扫描响应1.2 主要功能bt_le_scan_start是 Zephyr Bluetooth API 的核心函数之一用于启动蓝牙低功耗BLE扫描。它允许设备作为中心设备Central或观察者Observer发现周围的蓝牙外围设备。功能总结如下设备发现扫描并发现周围的 BLE 广播设备广播数据收集获取设备的广播数据如设备名称、服务 UUID、制造商数据等连接准备为后续的连接操作发现目标设备信标监控监控 iBeacon、Eddystone 等蓝牙信标1.3 扫描参数选择指南2 基本使用方法2.1 简单扫描示例#include zephyr/bluetooth/bluetooth.h #include zephyr/bluetooth/conn.h /* 扫描回调函数 */ static void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { char addr_str[BT_ADDR_LE_STR_LEN]; // 转换地址为字符串 bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); // 打印设备信息 printk(发现设备: %s, RSSI: %d, 广播类型: 0x%02x\n, addr_str, rssi, adv_type); // 解析广播数据 parse_advertising_data(buf); } /* 解析广播数据 */ static void parse_advertising_data(struct net_buf_simple *buf) { while (buf-len 1) { uint8_t len net_buf_simple_pull_u8(buf); uint8_t type net_buf_simple_pull_u8(buf); switch (type) { case BT_DATA_NAME_COMPLETE: case BT_DATA_NAME_SHORTENED: { char name[31]; size_t name_len MIN(len - 1, sizeof(name) - 1); memcpy(name, buf-data, name_len); name[name_len] \0; printk(设备名称: %s\n, name); break; } case BT_DATA_UUID16_SOME: case BT_DATA_UUID16_ALL: { printk(16位UUID服务: ); for (int i 0; i (len - 1) / 2; i) { uint16_t uuid net_buf_simple_pull_le16(buf); printk(%04x , uuid); } printk(\n); break; } case BT_DATA_MANUFACTURER_DATA: { uint16_t company_id net_buf_simple_pull_le16(buf); printk(制造商数据公司ID: 0x%04x\n, company_id); break; } } // 跳过剩余数据 net_buf_simple_pull(buf, len - 1); } } /* 启动扫描 */ int start_ble_scan(void) { struct bt_le_scan_param scan_param { .type BT_LE_SCAN_TYPE_ACTIVE, // 主动扫描 .options BT_LE_SCAN_OPT_FILTER_DUPLICATE, // 过滤重复 .interval BT_GAP_SCAN_FAST_INTERVAL, // 快速扫描间隔 .window BT_GAP_SCAN_FAST_WINDOW, // 快速扫描窗口 .timeout 30, // 扫描30秒后停止 }; int err bt_le_scan_start(scan_param, scan_cb); if (err) { printk(启动扫描失败: %d\n, err); return err; } printk(BLE扫描已启动\n); return 0; } /* 停止扫描 */ void stop_ble_scan(void) { int err bt_le_scan_stop(); if (err) { printk(停止扫描失败: %d\n, err); } else { printk(BLE扫描已停止\n); } }2.2 完整的使用流程#include zephyr/kernel.h #include zephyr/bluetooth/bluetooth.h #include zephyr/bluetooth/conn.h #define SCAN_DURATION_SECONDS 30 #define TARGET_DEVICE_NAME MyDevice static struct k_timer scan_timer; static bool found_target false; /* 扫描回调 */ static void scan_callback(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { char addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); // 检查是否为扫描响应 if (adv_type BT_GAP_ADV_TYPE_SCAN_RSP) { printk(收到扫描响应: %s, RSSI: %d\n, addr_str, rssi); } // 解析广播数据查找目标设备 check_for_target_device(addr, rssi, buf); } /* 检查目标设备 */ static void check_for_target_device(const bt_addr_le_t *addr, int8_t rssi, struct net_buf_simple *buf) { char addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); // 解析广播数据 while (buf-len 1) { uint8_t len net_buf_simple_pull_u8(buf); uint8_t type net_buf_simple_pull_u8(buf); if (type BT_DATA_NAME_COMPLETE) { char name[31]; size_t name_len MIN(len - 1, sizeof(name) - 1); memcpy(name, buf-data, name_len); name[name_len] \0; if (strcmp(name, TARGET_DEVICE_NAME) 0) { printk(找到目标设备: %s, 地址: %s, RSSI: %d\n, name, addr_str, rssi); found_target true; // 停止扫描 bt_le_scan_stop(); } } net_buf_simple_pull(buf, len - 1); } } /* 定时器回调扫描超时 */ static void scan_timeout(struct k_timer *timer) { if (!found_target) { printk(扫描超时未找到目标设备\n); bt_le_scan_stop(); } } /* 初始化蓝牙 */ int init_bluetooth(void) { int err; err bt_enable(NULL); if (err) { printk(蓝牙初始化失败: %d\n, err); return err; } printk(蓝牙已初始化\n); return 0; } /* 主函数 */ int main(void) { int err; // 初始化蓝牙 err init_bluetooth(); if (err) { return 0; } // 初始化定时器 k_timer_init(scan_timer, scan_timeout, NULL); // 设置扫描参数 struct bt_le_scan_param scan_param { .type BT_LE_SCAN_TYPE_ACTIVE, .options BT_LE_SCAN_OPT_FILTER_DUPLICATE, .interval BT_GAP_SCAN_FAST_INTERVAL, .window BT_GAP_SCAN_FAST_WINDOW, .timeout 0, // 手动控制超时 }; // 启动扫描 printk(开始扫描目标设备...\n); err bt_le_scan_start(scan_param, scan_callback); if (err) { printk(启动扫描失败: %d\n, err); return 0; } // 启动超时定时器 k_timer_start(scan_timer, K_SECONDS(SCAN_DURATION_SECONDS), K_NO_WAIT); // 等待扫描完成 while (!found_target) { k_sleep(K_MSEC(100)); } printk(扫描完成\n); return 0; }3 高级用法3.1 被动扫描与主动扫描对比/* 被动扫描配置低功耗 */ void start_passive_scan(void) { struct bt_le_scan_param scan_param { .type BT_LE_SCAN_TYPE_PASSIVE, // 被动扫描 .options BT_LE_SCAN_OPT_FILTER_DUPLICATE, .interval BT_GAP_SCAN_SLOW_INTERVAL_1, // 慢速扫描 .window BT_GAP_SCAN_SLOW_WINDOW_1, .timeout 0, // 持续扫描 }; bt_le_scan_start(scan_param, scan_cb); printk(被动扫描已启动低功耗模式\n); } /* 主动扫描配置快速发现 */ void start_active_scan(void) { struct bt_le_scan_param scan_param { .type BT_LE_SCAN_TYPE_ACTIVE, // 主动扫描 .options BT_LE_SCAN_OPT_FILTER_DUPLICATE, .interval BT_GAP_SCAN_FAST_INTERVAL, // 快速扫描 .window BT_GAP_SCAN_FAST_WINDOW, .timeout 10, // 10秒后自动停止 }; bt_le_scan_start(scan_param, scan_cb); printk(主动扫描已启动快速发现模式\n); }3.2 使用白名单过滤/* 设置白名单并扫描 */ void start_whitelist_scan(void) { int err; // 定义白名单设备地址 bt_addr_le_t whitelist[] { {BT_ADDR_LE_RANDOM, {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}}, {BT_ADDR_LE_PUBLIC, {{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}}}, }; // 清除现有白名单 bt_le_whitelist_clear(); // 添加设备到白名单 for (int i 0; i ARRAY_SIZE(whitelist); i) { err bt_le_whitelist_add(whitelist[i]); if (err) { printk(添加白名单失败 [%d]: %d\n, i, err); } } // 设置白名单扫描参数 struct bt_le_scan_param scan_param { .type BT_LE_SCAN_TYPE_ACTIVE, .options BT_LE_SCAN_OPT_FILTER_WHITELIST | // 只扫描白名单 BT_LE_SCAN_OPT_FILTER_DUPLICATE, .interval BT_GAP_SCAN_FAST_INTERVAL, .window BT_GAP_SCAN_FAST_WINDOW, .timeout 0, }; err bt_le_scan_start(scan_param, scan_cb); if (err) { printk(白名单扫描启动失败: %d\n, err); } else { printk(白名单扫描已启动\n); } }3.3 扫描参数计算和优化/* 计算扫描占空比和功耗 */ void calculate_scan_parameters(void) { // 计算实际时间单位毫秒 // 时间 参数值 × 0.625ms uint16_t interval_ms BT_GAP_SCAN_FAST_INTERVAL * 625 / 1000; // 60ms uint16_t window_ms BT_GAP_SCAN_FAST_WINDOW * 625 / 1000; // 30ms float duty_cycle (float)window_ms / interval_ms * 100; // 50% printk(扫描参数:\n); printk( 间隔: %dms\n, interval_ms); printk( 窗口: %dms\n, window_ms); printk( 占空比: %.1f%%\n, duty_cycle); // 估计功耗示例值实际值取决于硬件 float current_active 10.0f; // 扫描时电流mA float current_idle 0.1f; // 空闲时电流mA float avg_current (duty_cycle / 100) * current_active ((100 - duty_cycle) / 100) * current_idle; printk( 估计平均电流: %.2fmA\n, avg_current); } /* 根据应用需求选择扫描参数 */ struct bt_le_scan_param get_optimal_scan_params(enum scan_requirement req) { struct bt_le_scan_param param {0}; switch (req) { case REQUIREMENT_FAST_DISCOVERY: // 快速发现高占空比 param.type BT_LE_SCAN_TYPE_ACTIVE; param.interval 0x0060; // 60ms param.window 0x0060; // 60ms (100%占空比) param.timeout 10; break; case REQUIREMENT_LOW_POWER: // 低功耗低占空比 param.type BT_LE_SCAN_TYPE_PASSIVE; param.interval 0x1000; // 2560ms param.window 0x0012; // 11.25ms (~0.44%占空比) param.timeout 0; break; case REQUIREMENT_BACKGROUND: // 后台扫描平衡模式 param.type BT_LE_SCAN_TYPE_PASSIVE; param.interval 0x0800; // 1280ms param.window 0x0018; // 15ms (~1.17%占空比) param.timeout 0; param.options BT_LE_SCAN_OPT_FILTER_DUPLICATE; break; } return param; }3.4 解析特定广播数据如iBeacon/* iBeacon数据结构 */ struct ibeacon_data { uint8_t flags[3]; uint8_t length; uint8_t type; uint16_t company_id; uint16_t beacon_type; uint8_t proximity_uuid[16]; uint16_t major; uint16_t minor; int8_t tx_power; }; /* 解析iBeacon数据 */ static void parse_ibeacon(const bt_addr_le_t *addr, int8_t rssi, struct net_buf_simple *buf) { struct ibeacon_data *ibeacon; while (buf-len 1) { uint8_t len net_buf_simple_pull_u8(buf); uint8_t type net_buf_simple_pull_u8(buf); if (type BT_DATA_MANUFACTURER_DATA len 25) { // 检查是否是iBeaconApple公司ID uint16_t company_id net_buf_simple_pull_le16(buf); if (company_id 0x004C) { // Apple公司ID uint16_t beacon_type net_buf_simple_pull_le16(buf); if (beacon_type 0x0215) { // iBeacon类型 char addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); // 提取UUID uint8_t uuid[16]; for (int i 0; i 16; i) { uuid[i] net_buf_simple_pull_u8(buf); } // 提取Major和Minor uint16_t major net_buf_simple_pull_le16(buf); uint16_t minor net_buf_simple_pull_le16(buf); // 提取发射功率 int8_t tx_power net_buf_simple_pull_u8(buf); // 计算距离简化模型 float distance calculate_distance(rssi, tx_power); printk(iBeacon发现:\n); printk( 地址: %s\n, addr_str); printk( Major: %u, Minor: %u\n, major, minor); printk( RSSI: %d, TX Power: %d\n, rssi, tx_power); printk( 估计距离: %.2f米\n, distance); } } } net_buf_simple_pull(buf, len - 1); } }4 重要注意事项4.1功耗管理/* 错误的做法持续高占空比扫描 */ void bad_power_management(void) { struct bt_le_scan_param bad_param { .interval 0x0060, // 60ms .window 0x0060, // 60ms (100%占空比) .timeout 0, // 持续扫描 }; // 这会快速耗尽电池 } /* 正确的做法间歇扫描 */ void good_power_management(void) { struct k_work_delayable scan_work; // 工作函数执行短时间扫描 static void scan_work_handler(struct k_work *work) { struct bt_le_scan_param param { .type BT_LE_SCAN_TYPE_PASSIVE, .interval 0x0060, .window 0x0030, // 30ms (50%占空比) .timeout 3, // 只扫描3秒 }; bt_le_scan_start(param, scan_cb); // 安排下一次扫描例如30秒后 k_work_schedule(scan_work, K_SECONDS(30)); } }4.2回调函数注意事项/* 错误的做法在回调中进行耗时操作 */ void bad_scan_callback(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { // 错误可能阻塞系统工作队列 k_sleep(K_SECONDS(1)); // 错误执行复杂计算 perform_complex_calculation(); // 错误进行I/O操作 write_to_filesystem(); } /* 正确的做法快速处理延迟复杂操作 */ static struct k_workqueue *app_workq; static struct k_work deferred_work; void good_scan_callback(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { // 快速复制数据 struct device_data { bt_addr_le_t addr; int8_t rssi; uint8_t data[32]; size_t data_len; } *data k_malloc(sizeof(*data)); if (!data) return; memcpy(data-addr, addr, sizeof(bt_addr_le_t)); >4.3内存管理/* 正确处理广播数据缓冲区 */ void handle_adv_data_safely(struct net_buf_simple *buf) { // 错误保存缓冲区指针缓冲区会被重用 // static struct net_buf_simple *saved_buf; // 不要这样做 // 正确复制需要的数据 static uint8_t saved_data[32]; size_t copy_len MIN(buf-len, sizeof(saved_data)); memcpy(saved_data, buf-data, copy_len); // 如果需要处理原始数据使用pull函数 while (buf-len 1) { uint8_t len net_buf_simple_pull_u8(buf); uint8_t type net_buf_simple_pull_u8(buf); // 处理数据... net_buf_simple_pull(buf, len - 1); } }4.4并发和状态管理static atomic_t scan_state ATOMIC_INIT(0); #define SCAN_STATE_IDLE 0 #define SCAN_STATE_SCANNING 1 #define SCAN_STATE_STOPPING 2 /* 安全的扫描控制 */ int safe_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb) { int expected SCAN_STATE_IDLE; // 使用原子操作检查状态 if (!atomic_cas(scan_state, expected, SCAN_STATE_SCANNING)) { printk(扫描已在进行中\n); return -EBUSY; } int err bt_le_scan_start(param, cb); if (err) { atomic_set(scan_state, SCAN_STATE_IDLE); } return err; } void safe_scan_stop(void) { atomic_set(scan_state, SCAN_STATE_STOPPING); int err bt_le_scan_stop(); if (err 0) { atomic_set(scan_state, SCAN_STATE_IDLE); } else { // 处理错误 } }4.4错误处理/* 全面的错误处理 */ int robust_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb) { int err; // 检查蓝牙状态 if (!bt_is_ready()) { printk(蓝牙未就绪\n); return -EAGAIN; } // 检查参数有效性 if (param-window param-interval) { printk(错误扫描窗口不能大于间隔\n); return -EINVAL; } // 尝试启动扫描 err bt_le_scan_start(param, cb); switch (err) { case 0: printk(扫描启动成功\n); break; case -EALREADY: printk(扫描已在运行中\n); // 可以选择先停止再重启 bt_le_scan_stop(); k_sleep(K_MSEC(100)); err bt_le_scan_start(param, cb); break; case -EINVAL: printk(无效的参数\n); break; case -ENOMEM: printk(内存不足\n); // 可以尝试释放内存后重试 cleanup_memory(); err bt_le_scan_start(param, cb); break; default: printk(未知错误: %d\n, err); break; } return err; }5 常见问题解决1扫描不到设备void troubleshoot_scan_issues(void) { printk(扫描问题排查:\n); // 1. 检查蓝牙状态 if (!bt_is_ready()) { printk( - 蓝牙未初始化或未就绪\n); return; } // 2. 检查扫描参数 printk( - 扫描类型: %s\n, bt_dev.scan_param.type ? 主动 : 被动); // 3. 检查硬件限制 if (CONFIG_BT_MAX_CONN 0) { printk( - 最大连接数配置为0\n); } // 4. 建议操作 printk(建议:\n); printk( 1. 确认目标设备正在广播\n); printk( 2. 尝试不同的扫描间隔/窗口\n); printk( 3. 禁用重复过滤选项\n); printk( 4. 检查信号强度可能距离太远\n); }2扫描性能优化/* 自适应扫描策略 */ struct adaptive_scanner { uint8_t scan_phase; uint32_t devices_found; struct k_work_delayable phase_work; }; static void adaptive_scan_phase(struct k_work *work) { struct adaptive_scanner *scanner CONTAINER_OF(work, struct adaptive_scanner, phase_work); switch (scanner-scan_phase) { case 0: // 阶段1快速主动扫描 start_fast_active_scan(); scanner-scan_phase 1; k_work_schedule(scanner-phase_work, K_SECONDS(5)); break; case 1: // 阶段2慢速被动扫描 if (scanner-devices_found 0) { // 发现设备保持较慢的扫描 start_slow_passive_scan(); } else { // 未发现设备返回快速扫描 scanner-scan_phase 0; } k_work_schedule(scanner-phase_work, K_SECONDS(30)); break; } }3代码组织建议/* 推荐的代码结构 */ struct ble_scanner { struct bt_le_scan_param params; bt_le_scan_cb_t *callback; atomic_t state; struct k_mutex lock; struct k_workqueue *workq; struct k_work_delayable scan_work; struct k_work_delayable timeout_work; }; // 初始化扫描器 int ble_scanner_init(struct ble_scanner *scanner); // 启动扫描带错误处理和重试 int ble_scanner_start(struct ble_scanner *scanner); // 停止扫描安全地 int ble_scanner_stop(struct ble_scanner *scanner); // 处理扫描结果 void ble_scanner_process_result(struct ble_scanner *scanner, const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf);