本帖最后由 dql2016 于 2021-6-28 12:18 编辑
之前一直在研究RSL10如何跟微信小程序通信,将采集的数据发送给微信小程序,通过阅读安森美的文档和一些关于蓝牙的资料得知可以通过广播或者通知、读特征值的方式实现。其中广播或者通知是蓝牙设备主动将数据发送给微信小程序,而读特征值是需要微信小程序主动发送指令给蓝牙设备请求数据。为了简单起见,先使用广播方式发送数据,受到w494143467大神的帖子【环境专家之智能手表】Part6:BLE广播温度、湿度和气压数据https://bbs.eeworld.com.cn/thread-1168955-1-1.html启发,我详细学习了一下蓝牙的广播的知识,并成功的修改了RSL10的例程,将自定义的数据通过广播发送到手机蓝牙调试app上。在此对w494143467的无私分享表示感谢!
蓝牙规范可以在官方网站https://www.bluetooth.com/specifications/specs/搜索"core"关键字即可下载,最新的规范是5.2。蓝牙网络有三种连接方式,如图1所示,可以点对点,一对多,星型组网。每一种连接方式在连接建立之前都需要一种机制让两个蓝牙设备连接起来,广播就是这种机制中十分重要的一环。
图1 蓝牙三种网络连接方式
低功耗蓝牙设备通过广播信道发现其它设备,一个设备进行广播,而另一个设备进行扫描,这样进行扫描的设备就可以发现广播设备然后发起连接了。本次大赛提供的RSL10支持蓝牙5.0,蓝牙5.0是2016年提出的蓝牙技术标准,它把广播信道抽象为两类,一种叫主广播信道(primary advertisement channels),工作在37,38,39三个信道中,频率分别是2402 MHz、2426 MHz、2480 MHz。在学习蓝牙广播的过程中需要知道的两个角色:
广播者:发送广播数据
观察者:扫描广播者
图2 蓝牙广播
一个广播设备发送的广播可以被多个观察者设备接收到,如图2所示,广播是蓝牙通信中唯一的一种设备可以同时的向多个设备发送数据的通信方式。标准广播包的数据载荷是31字节,用于描述广播者和它具备的特性。 但也可以包含你想发送给其它设备的自定义数据。如果标准31字节载荷不够用,BLE也支持扩展的广播载荷(称之为扫描响应), 扫描响应载荷也是31字节,因此通过广播,蓝牙设备可以一次发送62字节的数据给周围的设备。下载附件
保存到相册
2021-6-28 10:03 上传
图3 蓝牙广播和响应数据包结构
总接一下,BLE蓝牙广播有两个基本特点,分别是:
1.广播包数据最大长度为31字节
2.数据格式为长度(1字节)类型(1字节)数据(n字节)......
其中长度=n+1
结合例程ble_peripheral_server_hrp进行测试,将工程复制到工作区。
这个例程没有开启SEGGER RTT打印调试,不是很方便,这里就手动添加一下。首先在工程属性中增加宏定义RSL10_DEBUG=DBG_RTT
然后双击ble_peripheral_server_hrp.rteconfig,勾上SEGGER RTT功能。
编译一下,可以看到相关文件自动添加进来了,没有错误。
在app.c的main函数中调用了函数
APP_SetAdvScanData();
该函数进行广播数据和扫描响应数据内容的设置,在app_config.c中实现
/* ----------------------------------------------------------------------------
* Function : void APP_SetAdvScanData(void)
* ----------------------------------------------------------------------------
* Description : Initializes the advertisement data structure
* Inputs : None
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
/**
* @brief Initializes the advertisement data structure
*
* Assumptions : None
*/
void APP_SetAdvScanData(void)
{
uint8_t companyID[] = APP_COMPANY_ID;
uint8_t devName[] = APP_DEVICE_NAME_DEFAULT;
uint8_t hrp_uuid[] = HRPS_SERVICE_UUID;
/* Set advertising data as device name */
app_adv_info.host.adv_data_len = 0;
GAPM_AddAdvData(GAP_AD_TYPE_COMPLETE_NAME, devName,
sizeof(APP_DEVICE_NAME_DEFAULT)-1, app_adv_info.host.adv_data,
&app_adv_info.host.adv_data_len);
/* Set scan response data as heart rate UUID and company ID */
app_adv_info.host.scan_rsp_data_len = 0;
GAPM_AddAdvData(GAP_AD_TYPE_SERVICE_16_BIT_DATA, hrp_uuid,
sizeof(hrp_uuid), app_adv_info.host.scan_rsp_data,
&app_adv_info.host.scan_rsp_data_len);
GAPM_AddAdvData(GAP_AD_TYPE_MANU_SPECIFIC_DATA, companyID,
APP_COMPANY_ID_LEN, app_adv_info.host.scan_rsp_data,
&app_adv_info.host.scan_rsp_data_len);
}
用到的宏在app.h中定义(我将APP_DEVICE_NAME_DEFAULT修改了以方便观察)
/* Advertising data is composed by device name and company id */
#define APP_DEVICE_NAME_DEFAULT "a0"
/* Maximum length for the device name.
* This is limited by GAP_MAX_NAME_SIZE during the stack build. */
#define APP_DEVICE_NAME_MAXLEN 0x20
/* Manufacturer info (ON SEMICONDUCTOR Company ID) */
#define APP_COMPANY_ID {0x62, 0x3}
#define APP_COMPANY_ID_LEN 2
在ble_hrps.h中定义了
/** Heart rate UUID */
#define HRPS_SERVICE_UUID {0x0D, 0x18}
具体实现在ble_gap.c中
/* ----------------------------------------------------------------------------
* Function : bool GAPM_AddAdvData(enum gap_ad_type newDataFlag,
* uint8_t *newData, uint8_t newDataLen,
* uint8_t *resultAdvData, uint8_t* resultAdvDataLen)
* ----------------------------------------------------------------------------
* Description : Utility function to add advertising data into the result
* buffer.
* function
* Inputs : newDataFlag - GAP Advertising flag (see
* enum gap_ad_type)
* newData - Data to be added to resultAdvData
* newDataLen - Length of the data being added (excluding
* flag)
* Outputs : resultAdvData - Resulting advertising data
* resultAdvDataLen - Full advertising data length
* (including flags and field length bytes)
* Assumptions : resultAdvDataLen must be initialized to 0 before adding data
* ------------------------------------------------------------------------- */
bool GAPM_AddAdvData(enum gap_ad_type newDataFlag, uint8_t *newData,
uint8_t newDataLen, uint8_t *resultAdvData,
uint8_t* resultAdvDataLen)
{
/* If new data element cannot be added to current accumulated data */
if ((ADV_DATA_LEN - *resultAdvDataLen) < (newDataLen + 2))
{
return false;
}
/* Else, add new data element into current accumulated data */
resultAdvData[*resultAdvDataLen] = newDataLen + 1;
resultAdvData[*resultAdvDataLen + 1] = newDataFlag;
memcpy(resultAdvData + *resultAdvDataLen + 2, newData, newDataLen);
*resultAdvDataLen += newDataLen + 2;
return true;
}
其中ADV_DATA_LEN定义在文件co_bt_defines.h中
#define ADV_DATA_LEN 0x1F
0x1F也就是十进制的31。通过源码可以知道,GAPM_AddAdvData函数的作用就是将newData数据填充到resultAdvData,并按照格式长度:类型:数据填充
广播类型定义在gap.h中
/*
* Enumerations
****************************************************************************************
*/
/// GAP Advertising Flags
enum gap_ad_type
{
/// Flag
GAP_AD_TYPE_FLAGS = 0x01,
/// Use of more than 16 bits UUID
GAP_AD_TYPE_MORE_16_BIT_UUID = 0x02,
/// Complete list of 16 bit UUID
GAP_AD_TYPE_COMPLETE_LIST_16_BIT_UUID = 0x03,
/// Use of more than 32 bit UUD
GAP_AD_TYPE_MORE_32_BIT_UUID = 0x04,
/// Complete list of 32 bit UUID
GAP_AD_TYPE_COMPLETE_LIST_32_BIT_UUID = 0x05,
/// Use of more than 128 bit UUID
GAP_AD_TYPE_MORE_128_BIT_UUID = 0x06,
/// Complete list of 128 bit UUID
GAP_AD_TYPE_COMPLETE_LIST_128_BIT_UUID = 0x07,
/// Shortened device name
GAP_AD_TYPE_SHORTENED_NAME = 0x08,
/// Complete device name
GAP_AD_TYPE_COMPLETE_NAME = 0x09,
/// Transmit power
GAP_AD_TYPE_TRANSMIT_POWER = 0x0A,
/// Class of device
GAP_AD_TYPE_CLASS_OF_DEVICE = 0x0D,
/// Simple Pairing Hash C
GAP_AD_TYPE_SP_HASH_C = 0x0E,
/// Simple Pairing Randomizer
GAP_AD_TYPE_SP_RANDOMIZER_R = 0x0F,
/// Temporary key value
GAP_AD_TYPE_TK_VALUE = 0x10,
/// Out of Band Flag
GAP_AD_TYPE_OOB_FLAGS = 0x11,
/// Slave connection interval range
GAP_AD_TYPE_SLAVE_CONN_INT_RANGE = 0x12,
/// Require 16 bit service UUID
GAP_AD_TYPE_RQRD_16_BIT_SVC_UUID = 0x14,
/// Require 32 bit service UUID
GAP_AD_TYPE_RQRD_32_BIT_SVC_UUID = 0x1F,
/// Require 128 bit service UUID
GAP_AD_TYPE_RQRD_128_BIT_SVC_UUID = 0x15,
/// Service data 16-bit UUID
GAP_AD_TYPE_SERVICE_16_BIT_DATA = 0x16,
/// Service data 32-bit UUID
GAP_AD_TYPE_SERVICE_32_BIT_DATA = 0x20,
/// Service data 128-bit UUID
GAP_AD_TYPE_SERVICE_128_BIT_DATA = 0x21,
/// Public Target Address
GAP_AD_TYPE_PUB_TGT_ADDR = 0x17,
/// Random Target Address
GAP_AD_TYPE_RAND_TGT_ADDR = 0x18,
/// Appearance
GAP_AD_TYPE_APPEARANCE = 0x19,
/// Advertising Interval
GAP_AD_TYPE_ADV_INTV = 0x1A,
/// LE Bluetooth Device Address
GAP_AD_TYPE_LE_BT_ADDR = 0x1B,
/// LE Role
GAP_AD_TYPE_LE_ROLE = 0x1C,
/// Simple Pairing Hash C-256
GAP_AD_TYPE_SPAIR_HASH = 0x1D,
/// Simple Pairing Randomizer R-256
GAP_AD_TYPE_SPAIR_RAND = 0x1E,
/// 3D Information Data
GAP_AD_TYPE_3D_INFO = 0x3D,
/// Manufacturer specific data
GAP_AD_TYPE_MANU_SPECIFIC_DATA = 0xFF,
};
用户自定义的数据需要指定类型为GAP_AD_TYPE_MANU_SPECIFIC_DATA。输入参数是在app_config.c中定义的
/* ADV_DATA and SCAN_RSP data are set in APP_BLE_Initialize() */
union gapm_adv_info app_adv_info;
跳转到gapm_task.h得到
/// Set advertising mode Command
struct gapm_start_advertise_cmd
{
/// GAPM requested operation:
/// - GAPM_ADV_NON_CONN: Start non connectable advertising
/// - GAPM_ADV_UNDIRECT: Start undirected connectable advertising
/// - GAPM_ADV_DIRECT: Start directed connectable advertising
/// - GAPM_ADV_DIRECT_LDC: Start directed connectable advertising using Low Duty Cycle
struct gapm_air_operation op;
/// Minimum interval for advertising
uint16_t intv_min;
/// Maximum interval for advertising
uint16_t intv_max;
///Advertising channel map
uint8_t channel_map;
/// Advertising information
union gapm_adv_info
{
/// Host information advertising data (GAPM_ADV_NON_CONN and GAPM_ADV_UNDIRECT)
struct gapm_adv_host host;
/// Direct address information (GAPM_ADV_DIRECT)
/// (used only if reconnection address isn't set or privacy disabled)
struct gap_bdaddr direct;
} info;
};
其中结构体在该文件gapm_task.h中定义为
/// Advertising data that contains information set by host.
struct gapm_adv_host
{
/// Advertising mode :
/// - GAP_NON_DISCOVERABLE: Non discoverable mode
/// - GAP_GEN_DISCOVERABLE: General discoverable mode
/// - GAP_LIM_DISCOVERABLE: Limited discoverable mode
/// - GAP_BROADCASTER_MODE: Broadcaster mode
uint8_t mode;
/// Advertising filter policy:
/// - ADV_ALLOW_SCAN_ANY_CON_ANY: Allow both scan and connection requests from anyone
/// - ADV_ALLOW_SCAN_WLST_CON_ANY: Allow both scan req from White List devices only and
/// connection req from anyone
/// - ADV_ALLOW_SCAN_ANY_CON_WLST: Allow both scan req from anyone and connection req
/// from White List devices only
/// - ADV_ALLOW_SCAN_WLST_CON_WLST: Allow scan and connection requests from White List
/// devices only
uint8_t adv_filt_policy;
/// Advertising data length - maximum 28 bytes, 3 bytes are reserved to set
/// Advertising AD type flags, shall not be set in advertising data
uint8_t adv_data_len;
/// Advertising data
//To resolve issue CEVA-212
//uint8_t adv_data[GAP_ADV_DATA_LEN-3];
uint8_t adv_data[GAP_ADV_DATA_LEN];
/// Scan response data length- maximum 31 bytes
uint8_t scan_rsp_data_len;
/// Scan response data
uint8_t scan_rsp_data[GAP_SCAN_RSP_DATA_LEN];
/// Peer address
struct gap_bdaddr peer_addr;
};
其中宏在gap.h中定义为
/// ADV Data and Scan Response length
#define GAP_ADV_DATA_LEN (0x1F)
#define GAP_SCAN_RSP_DATA_LEN (0x1F)
也就是广播数据包载荷这里规定了最大长度为28字节,预留3字节。
void APP_SetAdvScanData(void)
{
uint8_t companyID[] = APP_COMPANY_ID;
uint8_t devName[] = APP_DEVICE_NAME_DEFAULT;
uint8_t hrp_uuid[] = HRPS_SERVICE_UUID;
/* Set advertising data as device name */
app_adv_info.host.adv_data_len = 0;
GAPM_AddAdvData(GAP_AD_TYPE_COMPLETE_NAME, devName,sizeof(APP_DEVICE_NAME_DEFAULT)-1,
app_adv_info.host.adv_data,&app_adv_info.host.adv_data_len);
APP_FollowSetAdvScanData();
/* Set scan response data as heart rate UUID and company ID */
app_adv_info.host.scan_rsp_data_len = 0;
GAPM_AddAdvData(GAP_AD_TYPE_SERVICE_16_BIT_DATA, hrp_uuid,sizeof(hrp_uuid), app_adv_info.host.scan_rsp_data,
&app_adv_info.host.scan_rsp_data_len);
GAPM_AddAdvData(GAP_AD_TYPE_MANU_SPECIFIC_DATA, companyID,
APP_COMPANY_ID_LEN, app_adv_info.host.scan_rsp_data,
&app_adv_info.host.scan_rsp_data_len);
}
这里我就直接拿来w494143467大神分享的代码,我将数据增加到广播数据内容增加到22字节,这里需要注意的是app_adv_info.host.adv_data_len这个参数,它与前面填充的字节长度有关。
#define BROADCASTER_DATA_LEN 22
uint8_t Broadcaster_Data[BROADCASTER_DATA_LEN] = {0x00,0x01, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x1a,0x2a,0x3a,0x4a,0x5a,0x6a};
void APP_FollowSetAdvScanData(void)
{
app_adv_info.host.adv_data_len = 4;
GAPM_AddAdvData(GAP_AD_TYPE_MANU_SPECIFIC_DATA, Broadcaster_Data,
BROADCASTER_DATA_LEN, app_adv_info.host.adv_data,
&app_adv_info.host.adv_data_len);
}
烧录进去后,打开安卓蓝牙调试助手,可以扫描到设备了
广播包详细信息栏里的Len=Type(1字节)+Data长度
|