目录

概述

1 Advertising功能介绍

1.1 实现原理

 1.2 广播类型

 1.3 广播数据格式

1.4 优化建议

 1.5 常见问题和解决方法

2 Nordic 蓝牙广播(Advertising)功能实现

2.1 环境准备与SDK基础

2.2 广播功能实现

2.3 广播优化与最佳实践

3 实际应用案例

3.1 iBeacon实现

3.2 环境传感器广播

3.3 功耗测量优化

3.4 广播性能优化技巧

4 常见问题解决

4.1 广播不可见

4.2 数据更新失败

4.3 高功耗问题


概述

本文主要介绍Zephyr RTOS蓝牙广播(Advertising)功能实现,Zephyr RTOS为Nordic芯片提供了强大的蓝牙支持,使开发者能够高效实现BLE功能。下面我将详细介绍在Zephyr OS上实现蓝牙广播的完整流程。

1 Advertising功能介绍

蓝牙广播是低功耗蓝牙(BLE)中设备向外发送信息的主要方式,适用于信标、传感器数据广播、设备发现等场景。下面我将详细介绍蓝牙广播的实现原理和具体方法。

1.1 实现原理

蓝牙广播的核心机制是:

  1. 设备周期性地在三个广播信道(37, 38, 39)上发送广播包

  2. 广播包包含设备信息、服务标识和自定义数据

  3. 附近设备通过扫描接收这些广播信息

  4. 设备可以设置广播参数(间隔、类型、数据等)

 1.2 广播类型

广播类型描述适用场景
ADV_IND0可连接的非定向广播通用设备发现
ADV_DIRECT_IND1可连接的定向广播快速重连
ADV_SCAN_IND2可扫描的非定向广播广播数据
ADV_NONCONN_IND3不可连接的非定向广播信标设备
ADV_DIRECT_IND_LOW4低占空比的定向广播节能设备

 1.3 广播数据格式

1)合理组织AD结构

// 推荐的广播数据结构
[Flags][Service UUID][Device Name][Tx Power][Manufacturer Data]

2)控制数据长度

广播数据:最大31字节扫描响应数据:最大31字节优先放置重要信息在广播数据中

1.4 优化建议

1)功耗优化

  • 适当增加广播间隔(100ms-1s)

  • 使用不可连接广播类型(ADV_NONCONN_IND)

  • 在不需要时停止广播

2) 数据压缩

  • 使用紧凑的数据格式

  • 避免重复信息

  • 使用自定义二进制格式替代文本

 3)安全考虑

  • 避免广播敏感信息

  • 使用随机设备地址

  • 实现广播数据加密(如Eddystone-EID)

 1.5 常见问题和解决方法

1)广播数据被截断

  • 检查数据长度是否超过31字节

  • 优先放置重要数据在开头

  • 使用扫描响应数据补充信息

2) 设备无法被发现

  • 确认使用了正确的广播类型

  • 检查广播信道是否被干扰

  • 验证广播间隔设置是否合理

3) 功耗过高

  • 增加广播间隔

  • 减少广播数据量

  • 使用更节能的广播类型

 4)兼容性问题

  • 避免使用BLE 5.0特有功能与旧设备通信

  • 包含基本的BLE 4.0兼容数据

  • 提供多种广播数据格式

2 Nordic 蓝牙广播(Advertising)功能实现

2.1 环境准备与SDK基础

1) 开发环境

  • 硬件:nRF52系列开发板(如nRF52832/nRF52840)

  • 软件

    • nRF Connect SDK(最新版本)

    • Segger Embedded Studio 或 VSCode + nRF Connect插件

    • nRF Command Line Tools

2) SDK关键组件

  • SoftDevice:蓝牙协议栈

  • BLE Advertising Module:广播功能核心模块

  • BLE Services:GAP和GATT服务

2.2 广播功能实现

1)基础广播实现

#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gap.h>/* 广播参数配置 */
static const struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME, // 可连接且包含设备名800,   // 最小广播间隔: 800*0.625ms=500ms1600,  // 最大广播间隔: 1600*0.625ms=1000msNULL); // 对等设备地址(定向广播使用)/* 广播数据结构 */
static const struct bt_data ad[] = {BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};/* 扫描响应数据 */
static const struct bt_data sd[] = {BT_DATA(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)),
};void main(void)
{int err;/* 初始化蓝牙协议栈 */err = bt_enable(NULL);if (err) {printk("Bluetooth init failed (err %d)\n", err);return;}printk("Bluetooth initialized\n");/* 启动广播 */err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));if (err) {printk("Advertising failed to start (err %d)\n", err);return;}printk("Advertising successfully started\n");/* 主循环 */while (1) {k_sleep(K_SECONDS(10));// 在此添加广播更新逻辑}
}

2)高级广播功能

- 2.1) 制造商特定数据广播

/* 制造商特定数据结构 */
#define MANUFACTURER_ID 0x0059 // Nordic的厂商IDstatic uint8_t mfg_data[5] = {0x01, 0x02, 0x03, 0x04, 0x05}; // 自定义数据static struct bt_data custom_ad[] = {BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data)),
};/* 更新广播数据 */
void update_advertising_data(void)
{// 动态更新制造商数据mfg_data[0] = (k_uptime_get_32() >> 24) & 0xFF;mfg_data[1] = (k_uptime_get_32() >> 16) & 0xFF;// 停止当前广播bt_le_adv_stop();// 使用新数据重新启动广播bt_le_adv_start(adv_param, custom_ad, ARRAY_SIZE(custom_ad), NULL, 0);
}

-2.2) 扩展广播 (BLE 5.0+)

#if defined(CONFIG_BT_EXT_ADV)
/* 扩展广播参数 */
static const struct bt_le_adv_param ext_adv_param = {.id = 0,.sid = 0,.secondary_max_skip = 0,.options = BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_USE_NAME |BT_LE_ADV_OPT_CONNECTABLE,.interval_min = BT_GAP_ADV_FAST_INT_MIN_2, // 30ms.interval_max = BT_GAP_ADV_FAST_INT_MAX_2, // 50ms.peer = NULL,
};/* 配置扩展广播 */
void start_extended_advertising(void)
{struct bt_le_ext_adv *ext_adv;int err;/* 创建扩展广播实例 */err = bt_le_ext_adv_create(&ext_adv_param, NULL, &ext_adv);if (err) {printk("Failed to create extended advertising set (err %d)\n", err);return;}/* 设置广播数据 */err = bt_le_ext_adv_set_data(ext_adv, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));if (err) {printk("Failed to set advertising data (err %d)\n", err);return;}/* 启动扩展广播 */err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);if (err) {printk("Failed to start extended advertising (err %d)\n", err);return;}printk("Extended advertising started\n");
}
#endif /* CONFIG_BT_EXT_ADV */

3. 动态广播间隔调整

/* 动态调整广播间隔 */
void adjust_advertising_interval(uint16_t min_interval, uint16_t max_interval)
{// 创建新的广播参数const struct bt_le_adv_param *new_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME,min_interval,max_interval,NULL);// 停止当前广播bt_le_adv_stop();// 使用新参数启动广播bt_le_adv_start(new_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));printk("Advertising interval adjusted to %d-%d ms\n",min_interval * 5 / 8, max_interval * 5 / 8);
}/* 在低功耗模式下延长广播间隔 */
void enter_low_power_mode(void)
{// 设置长间隔:2000-4000 msadjust_advertising_interval(3200, 6400);
}/* 在活跃模式下缩短广播间隔 */
void enter_active_mode(void)
{// 设置短间隔:100-200 msadjust_advertising_interval(160, 320);
}

2.3 广播优化与最佳实践

1) 广播数据压缩技巧

/* 紧凑广播数据结构 */
static const struct bt_data compressed_ad[] = {// 标志位 (1字节)BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),// 短名称 (4字节)BT_DATA(BT_DATA_NAME_SHORTENED, "Zeph", 4),// 服务UUID (2字节)BT_DATA_BYTES(BT_DATA_UUID16_SOME, BT_UUID_16_ENCODE(BT_UUID_HRS_VAL)),// 制造商数据 (5字节: 厂商ID + 3字节自定义)BT_DATA(BT_DATA_MANUFACTURER_DATA, (uint8_t[]){0x59, 0x00, 0x01, 0x02, 0x03}, 5),// 发射功率 (2字节)BT_DATA_BYTES(BT_DATA_TX_POWER, 0xF4) // -12 dBm
};

2) 广播功耗优化

/* 优化广播参数 */
static const struct bt_le_adv_param low_power_adv_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_LOW_POWER,1600,   // 1000ms3200,   // 2000msNULL);/* 降低发射功率 */
void reduce_tx_power(void)
{int err;// 设置-20dBm的发射功率err = bt_le_set_tx_power(BT_LE_TX_POWER_N4); // -20dBmif (err) {printk("Failed to set TX power (err %d)\n", err);} else {printk("TX power reduced to -20dBm\n");}
}/* 配置广播睡眠模式 */
void configure_advertising_scheduler(void)
{// 仅在白天活动 (示例)k_work_schedule(&adv_work, K_HOURS(6));  // 6AM开始广播k_work_schedule(&sleep_work, K_HOURS(18)); // 6PM停止广播
}static void start_adv_work(struct k_work *work)
{bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));printk("Advertising started\n");
}static void stop_adv_work(struct k_work *work)
{bt_le_adv_stop();printk("Advertising stopped\n");
}K_WORK_DELAYABLE_DEFINE(adv_work, start_adv_work);
K_WORK_DELAYABLE_DEFINE(sleep_work, stop_adv_work);

3 实际应用案例

3.1 iBeacon实现

#include <zephyr/sys/byteorder.h>/* iBeacon数据格式 */
void configure_ibeacon(void)
{const struct bt_data ibeacon_ad[] = {BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),BT_DATA_BYTES(BT_DATA_MANUFACTURER_DATA,0x4c, 0x00, // Apple ID0x02, 0x15, // iBeacon类型// UUID0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2,0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0,// Major0x00, 0x01,// Minor0x00, 0x02,// Tx Power0xC5)};// 使用不可连接广播const struct bt_le_adv_param *ibeacon_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_NONE, 160,   // 100ms320,   // 200msNULL);// 启动iBeacon广播bt_le_adv_start(ibeacon_param, ibeacon_ad, ARRAY_SIZE(ibeacon_ad), NULL, 0);printk("iBeacon advertising started\n");
}

3.2 环境传感器广播

/* 传感器数据结构 */
struct sensor_data {int16_t temperature; // 温度 (0.01°C)uint16_t humidity;   // 湿度 (0.01%)uint8_t battery;     // 电池电量 (0-100%)
};/* 更新传感器广播数据 */
void update_sensor_advertising(struct sensor_data *data)
{uint8_t mfg_data[7];// 厂商ID (Nordic)mfg_data[0] = 0x59;mfg_data[1] = 0x00;// 温度 (小端序)sys_put_le16(data->temperature, &mfg_data[2]);// 湿度 (小端序)sys_put_le16(data->humidity, &mfg_data[4]);// 电池电量mfg_data[6] = data->battery;// 更新广播数据const struct bt_data sensor_ad[] = {BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data))};// 停止并重新启动广播以更新数据bt_le_adv_stop();bt_le_adv_start(adv_param, sensor_ad, ARRAY_SIZE(sensor_ad), NULL, 0);printk("Sensor data updated: T=%.2fC, H=%.2f%%, Bat=%d%%\n",data->temperature / 100.0, data->humidity / 100.0, data->battery);
}

3.3 功耗测量优化

/* 功耗测量代码片段 */
void measure_power_consumption(void)
{uint32_t start_time = k_uptime_get_32();uint32_t start_cycles = k_cycle_get_32();// 执行广播操作bt_le_adv_start(/* 参数 */);k_sleep(K_MSEC(5000));bt_le_adv_stop();uint32_t duration_ms = k_uptime_get_32() - start_time;uint64_t total_cycles = k_cycle_get_32() - start_cycles;// 计算平均电流(需要硬件测量校准)float avg_current = (total_cycles * 1000.0) / (duration_ms * sys_clock_hw_cycles_per_sec());printk("Advertising power: %.2f mA\n", avg_current);
}

3.4 广播性能优化技巧

  1. 数据压缩:使用自定义二进制格式而非文本

  2. 广播间隔:根据应用场景动态调整间隔

    • 发现阶段:短间隔(100-200ms)

    • 稳定状态:长间隔(1-2秒)

  3. 广播信道:优先使用信道37(最少干扰)

  4. 数据分片:对大型数据使用扫描响应

  5. 广播时间窗口:仅在需要时广播

4 常见问题解决

4.1 广播不可见

// 在代码中添加验证
if (!bt_le_adv_is_enabled()) {printk("Advertising not enabled!\n");
}// 检查广播参数
if (adv_param->options & BT_LE_ADV_OPT_SCANNABLE) {printk("Broadcast is scannable\n");
}

4.2 数据更新失败

void safe_update_advertising(void)
{static struct k_mutex adv_mutex;k_mutex_lock(&adv_mutex, K_FOREVER);bt_le_adv_stop();// 更新数据bt_le_adv_start(/* 新参数 */);k_mutex_unlock(&adv_mutex);
}

4.3 高功耗问题

  • 动态调整广播间隔

  • 使用-20dBm到-12dBm的发射功率

  • 实现广播睡眠计划

// 1. 降低发射功率
bt_le_set_tx_power(BT_LE_TX_POWER_N8); // -8dBm// 2. 增加广播间隔
adjust_advertising_interval(3200, 6400); // 2-4秒// 3. 使用不可连接广播
const struct bt_le_adv_param *non_conn_param = BT_LE_ADV_PARAM(BT_LE_ADV_OPT_NONE, 1600, 3200, NULL);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/88668.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/88668.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/88668.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器不支持PUT,DELETE 的解决方案

nginx 的更改&#xff1a; set $method $request_method; if ($http_X_HTTP_Method_Override ~* PUT|DELETE) { set $method $http_X_HTTP_Method_Override; } proxy_method $method; axios 的更改&#xff1a; const method config.me…

从0开始学习计算机视觉--Day04--线性分类

从宏观来看&#xff0c;卷积网络可以看做是由一个个不同的神经网络组件组合而成&#xff0c;就像积木一样通过不同类型的组件搭建形成&#xff0c;其中线性分类器是一个很重要的组件&#xff0c;在很多卷积网络中都有用到&#xff0c;所以了解清楚它的工作原理对我们后续的学习…

基于ComfyUI与Wan2.1模型的本地化视频生成环境搭建指南

文章目录 前言1.软件准备1.1 ComfyUI1.2 文本编码器1.3 VAE1.4 视频生成模型2.整合配置3. 本地运行测试4. 公网使用Wan2.1模型生成视频4.1 创建远程连接公网地址5. 固定远程访问公网地址总结前言 各位小伙伴们,今天我们将为您展示一套创新的人工智能应用方案!本次教程将指导…

Vue 2 项目中内嵌 md 文件

推荐方案&#xff1a;raw-loader marked 解析 Markdown 第一步&#xff1a;安装依赖 npm install marked --save npm install raw-loader --save-dev第二步&#xff1a;配置 webpack 支持 .md 文件 打开 vue.config.js 或 webpack.config.js&#xff0c;添加以下配置&#…

Spring AI初识及简单使用,快速上手。

Spring AI简介 在当今这样一个快速发展的技术时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为各行各业的一种标配。而作为一款主流的Java应用开发框架Spring&#xff0c;肯定会紧跟时代的潮流&#xff0c;所以&#xff0c;推出了Spring AI框架。 官网描述&#…

Flask中的render_template与make_response:生动解析与深度对比

文章目录 Flask中的render_template与make_response&#xff1a;生动解析与深度对比一、&#x1f31f; 核心概念速览二、&#xfffd; render_template - 网页内容的主厨特点与内部机制适用场景高级用法示例 三、&#x1f381; make_response - 响应的包装专家核心功能解析适用…

WordPress目录说明

在WordPress建站过程中&#xff0c;理解服务器目录结构是非常重要的。以下是一个基础的WordPress服务器目录指南&#xff1a; /wp-admin/ &#xff1a;这个目录包含了WordPress网站的所有管理功能&#xff0c;包括用于处理网站后台的所有PHP文件。 /wp-includes/ &#xff1a;…

HTTP面试题——缓存技术

目录 HTTP缓存技术有哪些&#xff1f; 什么是强制缓存&#xff1f; 什么是协商缓存&#xff1f; HTTP缓存技术有哪些&#xff1f; 对于一些具有重复性的HTTP请求&#xff0c;比如每次请求得到的数据都是一样的&#xff0c;我们可以把这对 请求-响应的数据都缓存在本地&#x…

virtual box 不能分配 USB设备 IFX DAS JDS TriBoard TC2X5 V2.0 [0700] 到虚拟电脑 win10

VirtualBox: Failed to attach the USB device to the virtual machine – Bytefreaks.net ISSUE&#xff1a; virtual box 不能分配 USB设备 IFX DAS JDS TriBoard TC2X5 V2.0 [0700] 到虚拟电脑 win10. USB device IFX DAS JDS TriBoard TC2X5 V2.0 with UUID {91680aeb-e1…

Deepoc大模型重构核工业智能基座:混合增强架构与安全增强决策技术​

面向复杂系统的高可靠AI赋能体系构建 Deepoc大模型通过多维度技术突破&#xff0c;显著提升核工业知识处理与决策可靠性。经核能行业验证&#xff0c;其生成内容可验证性提升68%&#xff0c;关键参数失真率<0.3%&#xff0c;形成覆盖核能全链条的定制化方案&#xff0c;使企…

第12章:冰箱里的CT扫描仪——计算机视觉如何洞穿食材的“生命密码“

第11章:冰箱里的CT扫描仪——计算机视觉如何成为食材健康的"超级诊断官" “糟了!冰箱里草莓长出了白色绒毛,鸡胸肉渗出了可疑的粉红色液体!” 这揭示了厨房生存的更基本挑战:如何像经验丰富的主厨一样,一眼洞穿食材的健康密码? 本章将揭示计算机视觉技术如何赋…

虚幻基础:窗口——重定向

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 重定向&#xff1a;给骨架添加兼容骨架。使得不同模型间复用动画资源 重定向&#xff1a;给骨架添加兼容骨架。使得不同模型间复用动画资源

CSS 逐帧动画

CSS 逐帧动画实现指南 逐帧动画(frame-by-frame animation)是一种通过快速连续显示一系列静态图像来创造运动效果的技术。以下是使用CSS实现逐帧动画的几种方法。 1. 使用 steps() 计时函数 这是实现逐帧动画最常用的方法&#xff0c;通过animation-timing-function的steps(…

高版本IDEA如何开发低版本jdk项目

问题描述 我这个人比较喜欢新的东西&#xff0c;比如使用idea的时候&#xff0c;我就喜欢最新版本。 但是有个问题&#xff0c;最新版本的idea好像不支持jdk1.6&#xff0c;导致我无法去用新版本idea开发项目。 直到有一天&#xff0c;idea给了我一个提示如下&#xff0c;之…

Java设计模式->责任链模式的介绍

目录 1、责任链模式概念 1.1、定义介绍 1.2、流程图 1.3、优缺点 2、实现 3、应用场景 3.1、Springmvc流程 3.2、mybatis的执行流程 3.3、Spring的过滤器和拦截器 3.4、sentinel限流熔断 3.5、aop的加载和使用 4、举例 前言 是一种 行为型设计模式&#xff0c;它通…

【Bluedroid】蓝牙启动之 btm_acl_device_down 流程源码解析

本文详细分析Android蓝牙协议栈在设备故障时的处理流程。当蓝牙设备发生硬件故障或系统异常时,协议栈通过btm_acl_device_down触发多层次的资源清理和状态重置,包括ACL连接终止、L2CAP通道释放、SCO连接清理、BLE拓扑更新、设备数据库重置等关键操作,确保系统安全恢复。 一、…

随记:WebMvcConfigurationSupport 和WebMvcConfigurer 的区别

WebMvcConfigurationSupport &#xff08;抽象类&#xff09; 他是一个完整的 MVC 配置基类&#xff0c;他会禁用所有自动配置。默认静态资源映射也没有了。默认消息转换器&#xff08;json、xml&#xff09;也没有了。错误处理页默认的error也没有了。 WebMvcConfigurer &am…

npm run dev报错

1. 引言 1.1 什么是npm run dev npm run dev 是一个在 Node.js 项目中常用的命令&#xff0c;它允许开发者运行一个预定义的脚本&#xff0c;通常用于启动开发服务器或者执行开发环境的构建任务。这个命令是通过 package.json 文件中的 scripts 部分定义的&#xff0c;例如&…

Kotlin环境搭建与基础语法入门

目标&#xff1a;完成开发环境配置&#xff0c;编写第一个Kotlin程序&#xff0c;理解变量、数据类型和基本输出。 1. 环境搭建 步骤1&#xff1a;安装JDK 下载并安装 JDK 17&#xff08;Kotlin兼容性最佳版本&#xff09;。 配置环境变量 JAVA_HOME&#xff0c;并在终端验证…

CLion开发Qt桌面程序_git的简单使用_小团体

OS&#xff1a;Windows Qt&#xff1a;6.8.1&#xff08;6.x&#xff09; Eg&#xff1a;学生信息管理系统 前言 Qt Creator编写代码不是太方便&#xff0c;使用CLion编写代码或许是个不错的主意&#xff0c;CLion在此处主要是用于后端和测试的开发&#xff0c;界面方面还是…