本文涉及到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 链接断开原因
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传出来