硬件: Central: nRF52840, Peripheral: nRF52832
SDK: Ver 15.0.0
目标: Peripheral 从机定时启动蓝牙广播,以及与主机联结,待交换完数据后,主动断开与主机的联结
参考: https://devzone.nordicsemi.com/f/nordic-q-a/29365/advertisment-not-stopping
说明:
Nordic SoftDevice 蓝牙栈内部自动处理广播事务, 只有使用 协议栈 API 请求才能改变其处理流程;
即是仅在用户程序中设置蓝牙广播的 BLE_ADV_MODE_IDLE 模式,或调用 sd_ble_gap_adv_stop(),并不能确保蓝牙广播停止后不再启动;
通过设置全局变量, 于 ble_advertising.c 中修改 ble_advertising_start() 函数中, 修改 sd_ble_gap_adv_start() 的启动条件, 便可实现真正的广播停止;
主要步骤:
1. 定义两个定时器 TIMER1, TIMER2, 以及广播停止标识;
TIMER1 用于从机蓝牙初始化及广播定时器; TIMER2 作为从机与主机断开的定时器;
APP_TIMER_DEF( bt_start_period_timer_id );
APP_TIMER_DEF( bt_cancel_period_timer_id );
#define BT_START_PERIOD_TIMER_INTERVAL APP_TIMER_TICKS( 10000 ) // 10 秒
#define BT_CANCEL_PERIOD_TIMER_INTERVAL APP_TIMER_TICKS( 100 ) // 100 毫秒
uint8_t mark_of_adv_stop = 0;
并于 main.c 函数中初始化 timer, 创建定时器及触发回调函数,并启动 TIMER1;
int main ( void )
{
//...
timers_init();
ret_code_t err_code;
err_code = app_timer_create(&bt_start_period_timer_id, APP_TIMER_MODE_REPEATED, bt_start_timeout_handler);
APP_ERROR_CHECK(err_code);
err_code = app_timer_create(&bt_cancel_period_timer_id, APP_TIMER_MODE_REPEATED, bt_cancel_timeout_handler);
APP_ERROR_CHECK(err_code);
err_code = app_timer_start( bt_start_period_timer_id, BT_START_PERIOD_TIMER_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
//...
}
2. 定时器 TIMER1 回调函数, 开始蓝牙初始化和开始广播,并在函数中启动 TIMER2;
static void bt_start_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
ret_code_t err_code;
err_code = app_timer_start( bt_cancel_period_timer_id, BT_CANCEL_PERIOD_TIMER_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
//printf( "adv current before adv-start: %d \r\n",(&m_advertising)->adv_mode_current);
if ( m_conn_handle == BLE_CONN_HANDLE_INVALID )
{
mark_of_adv_stop = 0;
advertising_init();
advertising_start_with_mode( BLE_ADV_MODE_FAST );
}
//printf( "adv current after adv-start: %d \r\n",(&m_advertising)->adv_mode_current);
}
3. 定时器 TIMER2 回调函数
static void bt_cancel_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
ret_code_t err_code;
//printf( "adv current before gap-disc: %d \r\n",(&m_advertising)->adv_mode_current );
if ( m_conn_handle != BLE_CONN_HANDLE_INVALID )
{
err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
APP_ERROR_CHECK(err_code);
}
mark_of_adv_stop = 1;
advertising_start_with_mode( BLE_ADV_MODE_IDLE );
// printf( "adv current after gap-disc: %d \r\n",(&m_advertising)->adv_mode_current );
// 注:曾用 sd_ble_gap_adv_stop() 测试,主机未扫描时,程序正常,但主机扫描联结后,程序出错
// 但未进一步测试,故在此备注说明
// err_code = sd_ble_gap_adv_stop( m_advertising.adv_handle );
// APP_ERROR_CHECK(err_code);
app_timer_stop( bt_cancel_period_timer_id );
}
4. 广播初始化函数
BLE_ADVERTISING_DEF( m_advertising );
/**@brief Function for initializing the Advertising functionality.*/
static void advertising_init(void)
{
uint32_t err_code;
ble_advertising_init_t init;
ble_advdata_manuf_data_t adv_manuf_data;
uint8_array_t adv_manuf_data_array;
uint8_t adv_manuf_data_data[1];
adv_manuf_data_data[0] = 0x88;
memset(&init, 0, sizeof(init));
init.advdata.name_type = BLE_ADVDATA_FULL_NAME;
init.advdata.include_appearance = false;
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
init.srdata.uuids_complete.p_uuids = m_adv_uuids;
adv_manuf_data_array.p_data = adv_manuf_data_data;
adv_manuf_data_array.size = sizeof(adv_manuf_data_data);
adv_manuf_data.company_identifier = 0x9876;
adv_manuf_data.data = adv_manuf_data_array;
init.advdata.p_manuf_specific_data = &adv_manuf_data;
init.config.ble_adv_whitelist_enabled = false;
init.config.ble_adv_directed_high_duty_enabled = false;
init.config.ble_adv_directed_enabled = false;
init.config.ble_adv_fast_enabled = true;
init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
init.config.ble_adv_fast_timeout = APP_ADV_DURATION;
init.evt_handler = on_adv_evt;
err_code = ble_advertising_init(&m_advertising, &init);
APP_ERROR_CHECK(err_code);
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}
5. 带广播模式的蓝牙广播启动函数
static void advertising_start_with_mode( ble_adv_mode_t mode )
{
uint32_t err_code = ble_advertising_start(&m_advertising, mode);
APP_ERROR_CHECK(err_code);
bsp_board_led_on(ADVERTISING_LED);
}
6. 添加sd_ble_gap_adv_start() 启动控制条件的 ble_advertising.c
extern uint8_t mark_of_adv_stop;
uint32_t ble_advertising_start(ble_advertising_t * const p_advertising,
ble_adv_mode_t advertising_mode)
{
// ...
if ( mark_of_adv_stop == 0 )
{
ret = sd_ble_gap_adv_start(p_advertising->adv_handle, p_advertising->conn_cfg_tag);
if (ret != NRF_SUCCESS)
{
return ret;
}
}
// ...
}