本文涉及到ble从机的:
- 广播相关
- 连接相关
- 服务/特征相关
- 数据收发相关
- 其他
1.[广播相关]
广播内容:
对于BLE4.x或者兼容模式,广义上的广播分为:广播数据,扫描回复数据,这两个内容的总长度都是31个字节;
对于广播的内容格式, 蓝牙有定义 按照 |1byte长度|1byte类型|n字节内容|循环放,如:
// GAP - SCAN RSP data (max size = 31 bytes) static uint8_t scanRspData[] = { 9, // length of this data //length GAP_ADTYPE_LOCAL_NAME_COMPLETE, //complete name type 'b','l','e','_','n','a','m','e', //name 5, //length 0xff, //manufacturer type 0x00,0x01,0x02,0x03 //manufacturer data };
设置更新广播内容
//设置/修改扫描回复以及广播数据,这种修改需要重启广播 GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, sizeof ( scanRspData ), scanRspData ); GAPRole_SetParameter( GAPROLE_ADVERT_DATA, sizeof( advertData ), advertData ); //或者 通过下面方式进行修改,实时生效 bStatus_t GAP_UpdateAdvertisingData( uint8_t taskID,uint8_t adType,uint8_t dataLen,uint8_t *pAdvertData );
广播间隔超时和类型:
//获取当前的广播间隔 //这里的值都是1=0.625ms uint16_t advInt; advInt = GAP_GetParamValue( TGAP_DISC_ADV_INT_MIN ); PRINT("min adv %d\r\n",advInt); advInt = GAP_GetParamValue( TGAP_DISC_ADV_INT_MAX ); PRINT("max adv %d\r\n",advInt); //修改广播间隔 /*注意,广播的参数修改,需要重启广播,才能生效 *可以修改后,然后在广播 GAPROLE_WAITING(广播停止)状态产生后,然后立刻开启广播. 不能关闭广播后立刻开启广播. */ //1=0.625ms, 160*0.625ms = 100ms uint16_t advInt = 160; GAP_SetParamValue( TGAP_DISC_ADV_INT_MIN, advInt ); GAP_SetParamValue( TGAP_DISC_ADV_INT_MAX, advInt ); //获取通用广播超时 //1=0.625ms uint16_t adv_timeout; adv_timeout = GAP_GetParamValue(TGAP_GEN_DISC_ADV_MIN); PRINT("get adv timer TGAP_GEN_DISC_ADV_MIN=%d\r\n",adv_timeout); //设置通用广播的超时 //1=0.625ms adv_timeout = 1600; GAP_SetParamValue(TGAP_GEN_DISC_ADV_MIN,adv_timeout); PRINT("set adv timer TGAP_GEN_DISC_ADV_MIN=%d\r\n",adv_timeout); //修改广播类型 // GAP_ADVERTISEMENT_TYPE_DEFINES GAP Advertising Event Types #define GAP_ADTYPE_ADV_IND 0x00 //!< Connectable undirected advertisement #define GAP_ADTYPE_ADV_HDC_DIRECT_IND 0x01 //!< Connectable high duty cycle directed advertisement #define GAP_ADTYPE_ADV_SCAN_IND 0x02 //!< Scannable undirected advertisement #define GAP_ADTYPE_ADV_NONCONN_IND 0x03 //!< Non-Connectable undirected advertisement #define GAP_ADTYPE_ADV_LDC_DIRECT_IND 0x04 //!< Connectable low duty cycle directed advertisement //对于只广播的类型,如一些 beacon,可以用 GAP_ADTYPE_ADV_NONCONN_IND 类型,这样不可以连接,会比较省电,但是数据只能放到 GAPROLE_ADVERT_DATA 中不能放到 GAPROLE_SCAN_RSP_DATA 中 uint8 adv_type = GAP_ADTYPE_ADV_IND; GAPRole_SetParameter( GAPROLE_ADV_EVENT_TYPE,sizeof( uint8_t ),&adv_type);
广播开关:
//获取当前广播状态 //0为非广播,非0为广播 uint8_t adv_status; GAPRole_GetParameter(GAPROLE_ADVERT_ENABLED, &adv_status ); //开启广播 //对应状态上报:GAPROLE_ADVERTISING uint8_t advertising_enable = true; GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8_t ), &advertising_enable ); //关闭广播 // 对应的状态上报: // :GAPROLE_WAITING // :pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT advertising_enable = false; GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8_t ), &advertising_enable ); //重启广播 //要先关闭广播,然后在停止广播的状态产生后才能再去使能广播,不能刚执行完关闭广播,就去开启广播 //否则会导致立刻开启失败,如当前在广播状态,执行关闭广播,然后立刻执行开始广播,这时候开始广播是不会调用成功的 //因为这里只是设置协议栈状态机标识位,实际上广播并没有立刻停止,要等协议栈轮询处理后,才会真正关闭,如果现在去启动广播,协议栈会认为当前已经在广播状态
2.[连接相关]
2.1 状态和参数
//连接参数 //设置最大最小连接间隔,最小为6,这里通常是在初始化的时候设置 //这里的最大最小范围要尽量的大,比如6,1000因为主机端连的时候,可能会不断的修改连接间隔,从而实现更快速的服务相关的查询 //后面的连接间隔,通过GAPRole_PeripheralConnParamUpdateReq 函数去更新即可 GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), &desired_min_interval ); GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), &desired_max_interval ); //在从机启动的api里面传入了几个回调函数: GAPRole_PeripheralStartDevice( Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs ); //其中Peripheral_PeripheralCBs的定义类似下面: static gapRolesCBs_t Peripheral_PeripheralCBs = { peripheralStateNotificationCB, // Profile State Change Callbacks,广播状态改变,连接断开连上都会调用这个回调 peripheralRssiCB, // When a valid RSSI is read from controller (not used by application),当读RSSI后,这里会产生 RSSI的回调,把rssi 值传进来. peripheralParamUpdateCB // 连接参数更新,比如连接 间隔发生改变之类,都会传入到这个函数里 }; //连接参数更新回掉函数 static void peripheralParamUpdateCB( uint16 connHandle, uint16 connInterval, uint16 connSlaveLatency, uint16 connTimeout ) { if( connHandle == peripheralConnList.connHandle ) { peripheralConnList.connInterval = connInterval; peripheralConnList.connSlaveLatency = connSlaveLatency; peripheralConnList.connTimeout = connTimeout; PRINT("param update: connection:%d,interval:%d,latency:%d,timeout:%d\n",connHandle,connInterval,connSlaveLatency,connTimeout); } else { PRINT("peripheralParamUpdateCB err..\n"); } }
2.2 连接控制相关API
//断开连接 //这里的connHandle 就是在连接的时候传进来的时候的代表一个连接 //在多连接场景,每个链接对应的这个值不同 extern bStatus_t GAPRole_TerminateLink( uint16 connHandle ); //连接参数更新 //这个是从机端,去向主机端请求连接更新 //这里的gapConnHandle 就是在连接的时候传进来的时候的代表一个连接,多连接场景,这个值不同. //注意下面几个参数值,这几个值要按照ble的规范去写,如果是对接ios,ios的这几个值更为严格. GAPRole_PeripheralConnParamUpdateReq( gapConnHandle, DEFAULT_DESIRED_MIN_CONN_INTERVAL, DEFAULT_DESIRED_MAX_CONN_INTERVAL, DEFAULT_DESIRED_SLAVE_LATENCY, DEFAULT_DESIRED_CONN_TIMEOUT, heartRate_TaskID );
2.3 链接断开原因
0x13:
1.对方主动断开
reason:0x08
在链接参数 timeout到来之前,蓝牙主从一直没有进行通信,可能的原因:
- 一方异常断电了,没有通知对方
- 代码有有问题,比如代码中有长时间delay 严重影响到协议栈的主循环运行
- 定时不准, 蓝牙的定时来自RTC,RTC的定时决定通信的收发窗口
- 收发器信号品质问题,导致丢包率大
- 距离远信号太差
- 信号倍频时钟误差大,导致丢包率太大
- 电路板layout的不好,比如GND没处理,或者相关的电容位置不合理,导致信号质量不好,导致丢包率大
### 3.[服务/特征相关] ### 4.[数据传输相关] ### 5.[其他] #### 修改发射功率 #####初始化时候修改 在协议栈出事时候的config.h文件中修改以下定义: ```c #ifndef BLE_TX_POWER #define BLE_TX_POWER LL_TX_POWEER_0_DBM #endif
初始化以后修改
这里的传参,和BLE_TX_POWER 中宏的取值范围一致
extern bStatus_t LL_SetTxPowerLevel( u8 power );
BLE从机的名称
BLE里面有两个地方涉及到名字:
- 广播/扫描回复包中
- GAP中
在wch的例程里,分别对应下面两个地方
(规范并没有规定,两个地方的名字必须一致,但是这里建议大家用一致.)
如果程序中修改,上电时候改这两个数组就好了
// GAP - SCAN RSP data (max size = 31 bytes) static uint8 scanRspData[ ] = { // complete name 0x12, // length of this data GAP_ADTYPE_LOCAL_NAME_COMPLETE, 'S', 'i', 'm', 'p', 'l', 'e', ' ', 'P', 'e', 'r', 'i', 'p', 'h', 'e', 'r', 'a', 'l', // connection interval range 0x05, // length of this data GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE, LO_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ), // 100ms HI_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ), LO_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ), // 1s HI_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ), // Tx power level 0x02, // length of this data GAP_ADTYPE_POWER_LEVEL, 0 // 0dBm }; // GAP GATT Attributes static uint8 attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple Peripheral"; // Set the GAP Characteristics GGS_SetParameter( GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName );
IOS中BLE设备的名称问题:
https://developer.apple.com/forums/thread/19381
对于ble设备有两个名称,一个是在广播/扫描回复 的字段中放着,一个是叫ATT device name,需要连接后去通过GATT读取。 并没有明确的要求二者必须一致,但是对于诸如N家,S家的SDK来看,二者是一致的,在IOS 获取BLE设备的名字时, 如果这个设备之前没有连接过,那默认是广播中的名字;当连接后,IOS会通过去读从机的ATT DEVICE NAME,然后私自建立MAC->ATT DEVICE NAME 映射表,当下次再扫描时候,直接根据mac地址匹配自己缓存的BLE设备名字,所以IOS开发者时不时抱怨,为什么广播的名字改了,扫描出来的名字却没变。IOS开发者需要每次都从广播/扫描回复数据中 拿BLE设备广播出来的名称即可。
也可以在开发ble固件时候,强制让广播名字与ATT device name 一致,这样IOS端的代码就不用动.
IOS中的BLE MAC地址问题:
IOS 不提供BLE的MAC地址获取接口,如需要mac地址,通常做法是把mac地址放到广播信息中,比如放到名字中,或者放到自定义信息中
微信小程序中交换mtu问题
微信的小程序的api中提供了 一个设置mtu的api: wx.setBLEMTU(Object object)
具体官方链接:https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-ble/wx.setBLEMTU.html
这个api 有多个返回:
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
deviceId | string | 是 | 用于区分设备的 id | |
mtu | number | 是 | 最大传输单元(22,512) 区间内,单位 bytes | |
success | function | 否 | 接口调用成功的回调函数 | |
fail | function | 否 | 接口调用失败的回调函数 | |
complete | function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
存在问题:当设备的mtu 不支持传入的值时候,会直接返回fail,而不会把从机支持的最大值给报上来。
从微信8.0.9 开始,当mtu交换后,会将最终交互的mtu传出来
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!