1 从机广播,主机扫描
从机(外围设备)要被主机连接,那么它就必须先被主机发现。这个时候,从机设备把自身信息以广播形式发射出去。
扫描是一个在一定范围内用来寻址其他低功耗蓝牙设备广播的过程。从机不断发送广播信号给主机 (Observer ),如果手机不开启扫描窗口,主机是收不到设备 的广播的。
observer 为主机观察者,advertiser 就是从机广播。
广播包有两种: 广播包 (Advertising Data)和 响应包 (Scan Response),其中
广播包是每个设备必须广播的,而响应包是可选的。广播响应包(Scan Response)的设置是为了给广播一个额外的 31 字节数据,用于主机为 主动扫描情况下,反馈数据使用
2 蓝牙广播包
nordic的一个广播数据实际上最多可以携带 31 字节的数据。
下面分析一下nordic52832工程样例中的广播初始化:
static void advertising_init(void)
{
ret_code_t err_code;
memset(&g_advertisingInit, 0, sizeof(g_advertisingInit));
// 初始化广播数据包和扫描响应包内容
InitAdvertisingData(&g_advertisingInit);
InitScanResponseData(&g_advertisingInit);
g_advertisingInit.config.ble_adv_fast_enabled = true; // 广播类型,快速广播
g_advertisingInit.config.ble_adv_fast_interval = APP_ADV_INTERVAL; // 广播间隔
g_advertisingInit.config.ble_adv_fast_timeout = APP_ADV_DURATION; // 广播超时时间,值0则保持一种广播模式不变
g_advertisingInit.evt_handler = on_adv_evt;
err_code = ble_advertising_init(&m_advertising, &g_advertisingInit); // 初始化广播,导入参数
APP_ERROR_CHECK(err_code);
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG); // 设置广播识别号
}
void InitAdvertisingData(ble_advertising_init_t *pInit)
{
pInit->advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; // 蓝牙设备模式,LE普通发现模式和不支持BR/EDR模式
// TIPS:由于Nordic平台差异company_identifier占用了数据内容两字节
g_advertisingData.company_identifier = 0; // 数据包类型
g_advertisingData.company_identifier = g_advertisingData.company_identifier | (0 << 8); // 同步时间标识
g_advertisingData.data.p_data = g_advertisingDataEventsAndParamsData;
g_advertisingData.data.size = 24;
pInit->advdata.p_manuf_specific_data = &g_advertisingData;
}
主要是对 &ble_advdata_t;和&config进行配置;
typedef struct
{
ble_advdata_name_type_t name_type; /**< Type of device name. */
uint8_t short_name_len; /**< Length of short device name (if short type is specified). */
bool include_appearance; /**< Determines if Appearance shall be included. */
uint8_t flags; /**< Advertising data Flags field. */
int8_t * p_tx_power_level; /**< TX Power Level field. */
ble_advdata_uuid_list_t uuids_more_available; /**< List of UUIDs in the 'More Available' list. */
ble_advdata_uuid_list_t uuids_complete; /**< List of UUIDs in the 'Complete' list. */
ble_advdata_uuid_list_t uuids_solicited; /**< List of solicited UUIDs. */
ble_advdata_conn_int_t * p_slave_conn_int; /**< Slave Connection Interval Range. */
ble_advdata_manuf_data_t * p_manuf_specific_data; /**< Manufacturer specific data. */
ble_advdata_service_data_t * p_service_data_array; /**< Array of Service data structures. */
uint8_t service_data_count; /**< Number of Service data structures. */
bool include_ble_device_addr; /**< Determines if LE Bluetooth Device Address shall be included. */
ble_advdata_le_role_t le_role; /**< LE Role field. Included when different from @ref BLE_ADVDATA_ROLE_NOT_PRESENT. @warning This field can be used only for NFC. For BLE advertising, set it to NULL. */
ble_advdata_tk_value_t * p_tk_value; /**< Security Manager TK value field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
uint8_t * p_sec_mgr_oob_flags; /**< Security Manager Out Of Band Flags field. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
ble_gap_lesc_oob_data_t * p_lesc_data; /**< LE Secure Connections OOB data. Included when different from NULL. @warning This field can be used only for NFC. For BLE advertising, set it to NULL.*/
} ble_advdata_t;
&advdata 广播数据定义了广播的基本参数,比如名称类型,最短名称长度,
设备模式,发射功率等等,需要什么改什么。
typedef struct
{
bool ble_adv_on_disconnect_disabled; /**< Enable or disable automatic return to advertising upon disconnecting.*/
bool ble_adv_whitelist_enabled; /**< Enable or disable use of the whitelist. */
bool ble_adv_directed_high_duty_enabled; /**< Enable or disable high duty direct advertising mode. Can not be used together with extended advertising. */
bool ble_adv_directed_enabled; /**< Enable or disable direct advertising mode. */
bool ble_adv_fast_enabled; /**< Enable or disable fast advertising mode. */
bool ble_adv_slow_enabled; /**< Enable or disable slow advertising mode. */
uint32_t ble_adv_directed_interval; /**< Advertising interval for directed advertising. */
uint32_t ble_adv_directed_timeout; /**< Time-out (number of tries) for direct advertising. */
uint32_t ble_adv_fast_interval; /**< Advertising interval for fast advertising. */
uint32_t ble_adv_fast_timeout; /**< Time-out (in units of 10ms) for fast advertising. */
uint32_t ble_adv_slow_interval; /**< Advertising interval for slow advertising. */
uint32_t ble_adv_slow_timeout; /**< Time-out (in units of 10ms) for slow advertising. */
bool ble_adv_extended_enabled; /**< Enable or disable extended advertising. */
uint32_t ble_adv_secondary_phy; /**< PHY for the secondary (extended) advertising @ref BLE_GAP_PHYS (BLE_GAP_PHY_1MBPS, BLE_GAP_PHY_2MBPS or BLE_GAP_PHY_CODED). */
uint32_t ble_adv_primary_phy; /**< PHY for the primary advertising. @ref BLE_GAP_PHYS (BLE_GAP_PHY_1MBPS, BLE_GAP_PHY_2MBPS or BLE_GAP_PHY_CODED). */
} ble_adv_modes_config_t;
&config结构体设置了广播的模式配置参数,比如比较多,包括快速广播,慢速广播,定向广播等。
广播数据是放在 ble_advdata_t 下的ble_advdata_manuf_data_t结构体里面
typedef struct
{
uint16_t company_identifier; /**< Company identifier code. */
uint8_array_t data; /**< Additional manufacturer specific data. */
} ble_advdata_manuf_data_t;
typedef struct
{
uint16_t size; /**< Number of array entries. */
uint8_t * p_data; /**< Pointer to array entries. */
} uint8_array_t;
ok,那我们别的配置都先不管,直接写数据拿来用。写一个数组替换掉g_advertisingDataEventsAndParamsData,然后发射出去
void ad_test(void){
NRF_LOG_INFO("ad_test");
uint8 test_data[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x68,0x96,0x88,0x69,0x86,0x00,0x00,0x66,0x51,0x15,0x99,0x19,0x96,0x10,0x29};
g_advertisingData.company_identifier=0x0302;
g_advertisingData.data.size = 24;
//g_advertisingData.data.p_data =test_data;
memcpy(g_advertisingDataEventsAndParamsData,test_data,sizeof(test_data));
//EnableAdvertising();
}
找一个地方调用test函数,利用UpdataAdvData();更新发送
static void adv_scan_start(void)
{
ret_code_t err_code;
//check if there are no flash operations in progress
if(!nrf_fstorage_is_busy(NULL))
{
// Start scanning for peripherals and initiate connection to devices which
// advertise Heart Rate or Running speed and cadence UUIDs.
// scan_start();
//scan_test();
// Start advertising.
ad_test();
UpdataAdvData();
// advertising_advdata_update();
// err_code = ble_advertising_init(&m_advertising, &g_advertisingInit); // 初始化广播,导入参数
// APP_ERROR_CHECK(err_code);
//
// ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
//
// err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
// APP_ERROR_CHECK(err_code);
//
// NRF_LOG_INFO("ok");
}
}
@brief 更新广播内容
@param 无
@return 无
*/
void UpdataAdvData(void)
{
bool eraseBonds = false;
if(GetBleConnectStatus() == false)
{
advertising_stop();
advertising_advdata_update();
advertising_start(eraseBonds);
}
}
void advertising_advdata_update(void)
{
ret_code_t err_code;
err_code = ble_advertising_init(&m_advertising, &g_advertisingInit); // 初始化广播,导入参数
APP_ERROR_CHECK(err_code);
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG); // 设置广播识别号
}
void advertising_start(bool erase_bonds)
{
if(erase_bonds == true)
{
delete_bonds();
// Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED event
}
else
{
ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
}
}
UpdataAdvData();等效于注释掉那一堆
// err_code = ble_advertising_init(&m_advertising, &g_advertisingInit); // 初始化广播,导入参数
// APP_ERROR_CHECK(err_code);
//
// ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
//
// err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
// APP_ERROR_CHECK(err_code);
来看下广播
3 扫描响应包
当主机接收到广播包后,它可能发送请求更多数据包的请求,称为扫描回应,如果
它被设置成主动扫描,从机设备将会发送一个扫描回应做为对主机请求的回应,扫描回
应最多也可以携带31字节的数据。广播扫描回应包的数据结构类型可以和广播包一致。
跟广播类似
void scan_test(void){
NRF_LOG_INFO("scan_test");
uint8 test_data[]={0x00,0x01,0x02,0x02,0x01,0x00,0x00,0x19,0x96,0x10,0x29,0x00,0x00,0x00,0x00,0x00,0x00,0x96,0x069,};
g_scanResponseData.company_identifier =0x0504;
g_scanResponseData.data .size=12;
memcpy(g_scanResponseStatusAndParamsData,test_data,sizeof(test_data));
}
放入在广播相同的adv_scan_start(void)函数内。
这里注意
初始化扫描应答包里面放了广播的名字
pInit->srdata.name_type = BLE_ADVDATA_FULL_NAME;
#define DEVICE_NAME “Mr.Lv’device”
所以剩余的长度没有24个那么多,否则名字不能完全显示出来。
上面是扫描响应数据,下面是名字对应的ASCLL码的值。