ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于PIC-BLE开发板的低功耗蓝牙资产追踪器设计与实战

2026/8/19 15:07:54 拓冰建站 浏览量
基于PIC-BLE开发板的低功耗蓝牙资产追踪器设计与实战 1. 项目概述从概念到现实的BLE资产追踪器做硬件开发的朋友尤其是玩过单片机物联网项目的应该都对“资产追踪”这个概念不陌生。简单来说就是给你关心的物品——比如工具箱、宠物项圈、仓库里的贵重设备——装上一个能周期性报告自己位置的小玩意儿。市面上成熟的方案很多但成本、功耗和灵活性往往难以兼得。这也是为什么当我拿到Microchip的PIC-BLE开发板时第一个想到的就是用它来从头搭建一个属于自己的、高度可定制的BLE资产追踪器。这个系列文章已经进行到了第三部分。如果说前两部分我们解决了硬件选型、环境搭建和基础BLE通信那么这一部分我们要啃的就是最核心也最实用的“硬骨头”如何让我们的追踪器变得更智能、更省电并且能与手机App稳定可靠地“对话”。具体来说我们会深入BLE的广播与扫描机制实现基于信号强度RSSI的粗略测距设计一套低功耗的周期性广播策略并最终完成与手机端的配对、绑定以及数据交换。整个过程我会把在调试中踩过的坑、实测有效的参数配置以及那些数据手册里不会写的“玄学”问题都毫无保留地分享出来。2. 核心架构与低功耗设计思路拆解在动手写代码之前我们必须把整个系统的运行逻辑想清楚。一个资产追踪器它绝大部分时间应该处于“沉睡”状态只有需要报告自身状态或被查找时才短暂醒来工作。我们的PIC-BLE开发板基于低功耗蓝牙5.0这为我们的设计提供了坚实的基础。2.1 系统状态机设计我设计的核心是一个简单的三状态机深度睡眠状态CPU和大部分外设关闭仅保留RTC实时时钟和BLE射频的极低功耗监听模式。此时电流可以降到几个微安级别。广播状态设备定时醒来开启BLE射频向外广播特定的数据包。广播持续一个很短的时间窗口例如100ms然后立即判断是否需要进入连接状态否则返回睡眠。连接状态当手机App主动发起连接后设备进入此状态。此时可以双向高速传输数据例如上报电池电量、设置报警阈值、更新设备名称等。通信完成后设备应尽快断开连接回到睡眠状态。这个状态切换的核心驱动力是定时器中断和BLE事件回调。定时器负责周期性地唤醒设备进入广播状态而BLE协议栈的事件如连接建立、断开、数据接收则驱动状态间的其他转换。2.2 广播数据包设计承载信息的“名片”BLE设备在未被连接时通过广播包来宣告自己的存在。这个包里的数据就是我们与扫描者手机沟通的全部内容。对于资产追踪器广播包需要精心设计。我通常将其分为两部分广播数据和扫描响应数据。广播数据是设备必须发送的应尽量精简。我一般会放入Flags标明设备支持BLE通用发现模式。完整的设备名称或者一个缩写的名称。厂商自定义数据这是我们的“黄金地段”。在这里我会塞入设备类型标识例如0x01代表资产追踪器。电池电压用1个字节表示百分比。一个可选的报警标志位如设备被移动。扫描响应数据是在扫描者主动请求时才回复的可以放一些补充信息比如更详细的设备型号或序列号。固件版本号。自定义的配置信息。在PIC-BLE的SDK中配置广播数据是通过一个结构体数组来完成的。你需要非常清楚每个字节的含义一个错误的长度字段都会导致广播失败。// 示例定义广播数据 static uint8_t advData[] { 0x02, // Length of this data block GAP_ADTYPE_FLAGS, // Type: Flags GAP_ADTYPE_FLAGS_GENERAL | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED, // Value 0x0A, // Length (设备名长度 1) GAP_ADTYPE_LOCAL_NAME_COMPLETE, // Type: Complete local name A, s, s, e, t, -, T, 0, 1, // Device Name 0x05, // Length of Manufacturer Data block GAP_ADTYPE_MANUFACTURER_SPECIFIC, // Type 0x00, 0x00, // 假设的厂商ID (LSB, MSB) 0x01, // 自定义数据: 设备类型资产追踪器 0x64 // 自定义数据: 电池电量100% };注意广播数据的总长度有限制通常31字节。务必计算好每个字段的长度长度值包含字段类型和值本身的字节数。一个快速核对的方法是每个数据块第一个字节是长度N那么紧随其后的N-1个字节就是类型和内容。2.3 功耗预算与睡眠周期计算功耗是资产追踪器的生命线。假设我们使用一块500mAh的纽扣电池目标是续航一年。目标平均电流500mAh / (365天 * 24小时) ≈ 57μA。深度睡眠电流查阅PIC-BLE数据手册在待机模式下约为1.5μA这部分是固定开销。广播事件功耗每次广播射频和CPU工作瞬时电流可能达到10mA。关键是每次广播的持续时间。计算周期如果我们设定每2秒广播一次每次广播处理耗时10ms。平均电流 睡眠电流 (工作电流 * 占空比)。占空比 工作时间 / 总周期 10ms / 2000ms 0.005。工作平均电流贡献 10mA * 0.005 50μA。总平均电流 ≈ 1.5μA 50μA 51.5μA。51.5μA 57μA理论上是可行的。这给了我们一个基准广播间隔不能远短于2秒单次广播时间要严格控制。在实际调试中你需要用电流表或功耗分析仪实际测量因为软件处理逻辑、外设如ADC测电池电压的开启都会增加额外功耗。3. BLE广播、扫描与RSSI测距实战有了理论设计接下来就是具体的代码实现。我们使用Microchip的BLE协议栈它通过一系列的事件回调函数来工作。3.1 配置与启动广播首先我们需要初始化GAP通用访问配置文件层并设置广播参数。void APP_BleConfigAdv(void) { tBTM_BLE_ADV_PARAMS advParams; memset(advParams, 0, sizeof(advParams)); advParams.adv_int_min 160; // 最小广播间隔单位0.625ms即100ms advParams.adv_int_max 240; // 最大广播间隔150ms advParams.adv_type BTM_BLE_ADV_IND; // 可连接的非定向广播 advParams.own_addr_type BLE_ADDR_PUBLIC; // 使用公共地址 advParams.adv_chnl_map BTM_BLE_ADV_CHNL_ALL; // 在所有3个广播信道发送 advParams.adv_filter_policy BTM_BLE_ADV_ALLOW_SCAN_ANY_CON_ANY; // 允许任何设备扫描和连接 // 设置广播数据 BTM_BleSetAdvData(BTM_BLE_ADV_DATA_TYPE_DATA, sizeof(advData), advData); // 设置扫描响应数据 BTM_BleSetAdvData(BTM_BLE_ADV_DATA_TYPE_SCAN_RSP, sizeof(scanRspData), scanRspData); // 应用广播参数 BTM_BleSetAdvParams(advParams); }初始化完成后在应用状态机中当需要开始广播时调用BTM_BleStartAdv()。当广播超时或连接建立时协议栈会通过BTM_BLE_ADV_START_CFM和BTM_BLE_ADV_STOP_CFM等事件通知应用层。3.2 获取与处理RSSI值RSSI是接收信号强度指示单位dBm。它是一个负值绝对值越小信号越强。在资产追踪场景中我们可以用它来粗略判断设备的远近。在PIC-BLE作为外设Peripheral时它本身无法直接获取到扫描它的中心设备比如手机的RSSI。但是在连接建立之后双方都可以读取到链路层Link Layer的RSSI。更常见的做法是由手机App在扫描到广播包时记录下该广播包的RSSI然后通过连接后的自定义特性Characteristic发送给追踪器或者由App自己处理用于在UI上显示信号强弱。不过我们的追踪器固件可以做一些优化。例如我们可以在广播数据中携带一个“参考发射功率”字段例如0xED代表 -19dBm。手机App在收到广播时获取到RSSI例如 -65dBm并结合参考发射功率利用简化的路径损耗模型进行非常粗略的距离估算距离 ≈ 10^((发射功率 - RSSI) / (10 * n))其中n是环境因子通常取2~4。这个计算最好在手机端完成因为追踪器为了省电应尽量避免复杂的浮点运算。3.3 实现低功耗周期性广播单纯的启动广播并不能实现低功耗因为广播会一直进行。我们需要结合MCU的低功耗模式。以PIC-BLE使用的PIC单片机为例可以使用其SLEEP模式或IDLE模式并通过看门狗定时器或外部RTC定时唤醒。一个典型的流程如下设备上电初始化后进入SLEEP模式。看门狗定时器配置为2秒溢出产生中断唤醒MCU。中断服务例程中设置一个软件标志flag_adv_wakeup true。主循环检测到该标志调用BTM_BleStartAdv()开始广播。同时启动一个硬件定时器设置为100ms后超时。在广播期间如果收到连接请求协议栈事件会处理连接建立并停止广播定时器。如果100ms内未建立连接定时器超时中断触发调用BTM_BleStopAdv()停止广播。清除flag_adv_wakeup再次进入SLEEP模式等待下一个2秒唤醒周期。这个流程的关键在于广播的持续时间由我们自己的硬件定时器严格控制而不是依赖BLE协议栈的广播超时参数。这确保了射频活动时间被精确限制从而控制了功耗。实操心得在调试低功耗时务必断开调试器因为调试接口本身会消耗电流并可能阻止MCU进入深度睡眠。使用串联在电源回路中的精密万用表或者专用的功耗分析工具来测量电流曲线。你会看到一个清晰的“钉床”图案长时间的低电流基线睡眠伴随着周期性的短脉冲广播。4. 连接、配对绑定与数据通信设备被手机发现后下一步就是建立稳定连接并进行安全的数据交换。4.1 连接参数协商连接建立后中心设备手机会提议一组连接参数包括连接间隔两个数据包之间的时间。范围是7.5ms到4s。对于资产追踪器在连接状态下为了快速传输数据可以设为20-50ms传输完成后应立即请求更新为一个更长的间隔如500ms以上以节省功耗或者直接断开。从机延迟允许从机我们的追踪器跳过多少个连接事件而不监听。这是省电的关键。设为非零值可以让设备在连接状态下也大部分时间睡眠。监督超时连接失败的超时判断时间通常是连接间隔的10倍。在PIC-BLE的协议栈事件BTM_BLE_CONN_UPDATE_IND中我们可以收到手机提议的参数。如果参数不合适例如间隔太短耗电我们可以调用BTM_BleConnParamUpdateResponse来拒绝或提议新参数。4.2 配对与绑定流程实现为了实现安全通信和避免每次连接都手动配对我们需要实现绑定功能。绑定过程包括配对、交换密钥、在双方存储绑定信息。I/O能力设置我们的追踪器没有显示屏和键盘所以通常将I/O能力设置为BTM_IO_CAP_NONE并选择“Just Works”配对方式。对于资产追踪这种安全要求不极端高的场景这是最常用的方式。BTM_SecSetIoCap(BTM_IO_CAP_NONE);使能绑定在初始化GAP时设置绑定模式。gapSettings.bondingMode BTM_BONDING_MODE_ENABLED; gapSettings.autoAcceptPairing true; // 自动接受配对请求处理配对事件协议栈会通过BTM_PAIRING_COMPLETE_EVT事件通知配对结果。成功后绑定信息会自动存储到Flash的保留区域。后续连接当已绑定的手机再次连接时双方会使用存储的长期密钥进行加密连接无需再次配对实现“无感”重连。4.3 自定义GATT服务与特性设计数据通信通过GATT通用属性协议进行。我们需要创建一个自定义服务包含多个特性。例如我们定义一个Asset Tracker Service(UUID: 0xFFF0)特性1位置状态(UUID: 0xFFF1)属性READ, NOTIFY值一个字节0x00静止0x01移动0x02报警。手机可以读取并且当追踪器检测到移动时可以通过NOTIFY主动推送状态给手机。特性2电池电量(UUID: 0xFFF2)属性READ值一个字节0-100表示百分比。特性3报警阈值(UUID: 0xFFF3)属性READ, WRITE值两个字节表示触发移动报警的RSSI阈值单位dBm绝对值。手机可以写入新的阈值来配置追踪器。在PIC-BLE SDK中你需要使用ATT_SERVER_AddService等API来逐层构建这个数据库。每个特性都需要定义其UUID、属性、权限和值句柄。这是一个细致活必须对照蓝牙SIG的规范来确保格式正确。5. 手机端调试与联调实战技巧固件写好了怎么验证你需要一个强大的手机端调试工具。我强烈推荐nRF Connect由Nordic Semiconductor开发。它免费、功能全面是调试BLE的瑞士军刀。5.1 使用nRF Connect进行基础调试扫描打开App开始扫描。你应该能看到你的设备“Asset-T01”出现在列表中并显示其RSSI和广播数据。点开广播数据可以解析出我们自定义的厂商数据核对电池电量等信息是否正确。连接点击设备名称进行连接。连接成功后App会自动发现该设备的所有GATT服务并以树状结构展示出来。你应该能找到我们自定义的FFF0服务及其下的三个特性。读写测试点击“电池电量”特性执行READ操作看返回的值是否与你在固件中设置的一致。点击“报警阈值”特性在WRITE输入框中输入新的十六进制值如FFEC代表-20dBm点击发送。然后在你的追踪器固件中应该能收到ATT_WRITE_REQ事件并解析出写入的值。要让追踪器发送NOTIFY你需要在手机端先向“位置状态”特性的CCCD客户端特性配置描述符写入0x0001来启用通知。然后当你的追踪器固件调用ATT_SERVER_SendNotification时手机端就会自动收到数据更新。5.2 常见联调问题与排查实录即使理论清晰第一次联调也几乎一定会遇到问题。下面是我踩过的一些坑和解决方法问题1手机扫描不到设备。可能原因A广播数据格式错误。排查检查广播数据数组的长度字段。这是最高发的错误。用nRF Connect的“广播数据解析”功能看看如果解析乱码或失败基本就是这里错了。解决逐字节核对确保每个数据块的第一个字节长度等于后面“类型内容”的字节数。可能原因B广播间隔太短或太长。排查手机BLE扫描有默认的扫描窗口和间隔。如果你的设备广播间隔非常不规律或极长可能会错过。解决将adv_int_min和adv_int_max设置为一个合理的、相同的值比如160100ms。确保广播已经成功启动检查BTM_BLE_ADV_START_CFM事件是否返回成功。问题2连接建立后立即断开。可能原因A连接参数不可接受。排查在nRF Connect的连接事件日志里查看连接参数更新过程。手机可能会提议一个非常短的连接间隔而你的固件可能无法处理或拒绝了。解决在固件的连接参数更新事件中打印出手机提议的参数。如果间隔小于你设定的最小值比如30ms可以拒绝并提议一个更大的值如100ms。可能原因BMTU最大传输单元协商失败。排查虽然不常见但有些协议栈实现对MTU交换敏感。解决在连接建立后主动发起MTU交换请求BTM_BleMtuExchange并处理交换完成事件。问题3写入特性失败返回错误码0x80应用错误。可能原因特性权限或句柄错误。排查确认你写入的特性属性包含WRITE。在nRF Connect中长按特性查看其属性。解决检查固件中创建该特性时是否正确设置了ATT_PROPERTY_WRITE。同时确认手机端写入的句柄与你固件中注册的特性值句柄完全一致。问题4NOTIFY通知手机收不到。可能原因CCCD未正确启用。排查这是99%的原因。在nRF Connect中找到“位置状态”特性看其下方是否有一个名为“Client Characteristic Configuration”的描述符其值是否为0x0001启用通知或0x0002启用指示。解决手机端必须先向这个CCCD写入01:00小端序即0x0001来启用通知。你的固件需要在ATT_WRITE_REQ事件中检查写入的句柄是否为CCCD的句柄并保存其使能状态。只有在此状态下调用ATT_SERVER_SendNotification才会真正发送。调试是一个需要耐心和逻辑分析的过程。养成好习惯在固件中所有关键事件处添加日志输出通过串口打印同时结合手机端调试工具的日志两相对照问题往往就无处遁形了。当你看到手机App上成功显示出追踪器的电量并能远程修改其报警阈值时那种成就感就是驱动我们这些硬件开发者不断折腾的最大乐趣。