一、简介
由于蓝牙主机和从机组网,如果不使用 Mesh 的话,只能组微微网。蓝牙5.0的微微网最大可以连接20个从机。
1.1 连接句柄
在主机与从机发生连接的时候会进行连接句柄的分配。连接句柄的作用是在蓝牙数据进行分组的时候进行设备区分的。连接句柄相当于一个“令牌”,从设备一旦和主设备发生连接,主设备就给从设备分配一个“令牌”。主设备通过这个“令牌”来识别与区分从设备。因此对于连接句柄的分配将是实现一主多从连接,并且进行通信的关键。
二、ble_app_multilink_central
打开工程 不同SDK\examples\ble_central\ble_app_multilink_central
2.1 main
2.2 buttons_init
在按键操作里,有一主多从发送数据的操作,可先不看这里。
2.2.1 button_event_handler
2.2.2 led_status_send_to_all
遍历所有从机,给从机发送 LED 灯亮灭命令
2.3 ble_stack_init
ble_stack_init() 协议栈初始化函数基本结构没有变化,设备变化的地方有下面几个地方:
2.3.1 nrf_sdh_ble_default_cfg_set
定义了可以连接的从机最大数量为8
2.3.2 ble_evt_handler
ble协议栈的回调函数,看下其中对于连接和断开等GAP状态的处理。
BLE_GAP_EVT_CONNECTED 连接状态,我们首先调用 ble_nus_c_handles_assign
函数去分配句柄,但实际上由于第三个参数为NULL,这里我们等于是给nus服务句柄置0。接下来我们调用 ble_db_discovery_start
函数去发现服务(由于有多个连接,所以我们需要根据当前连接的设备句柄参数去发现服务),成功发现NUS服务之后,会给我们在 ble_nus_c_evt_handler
回调函数中返回 BLE_NUS_C_EVT_DISCOVERY_COMPLETE 事件。
BLE_GAP_EVT_DISCONNECTED 断开状态,先判断了是否有设备连接,并且在最后重新开启扫描。
2.4 scan_start
在主函数中初始化完成后就会调用 scan_start() 开始监听从机的 BLE 广播。每当监听到 BLE 广播时,协议栈就会给上层应用一个广播事件,该事件由 NRF_SDH_BLE_OBSERVER 观察者递交给 ble_evt_handler 函数。
在 ble_evt_handler()
函数中添加 BLE_GAP_EVT_ADV_REPORT 事件,如果发现了从机广播,则产生 BLE_GAP_EVT_ADV_REPORT 事件,也就是广播报告,那么久调用 on_adv_report()
函数解析广播。
static void on_adv_report(ble_gap_evt_adv_report_t const *p_adv_report)
{
ret_code_t err_code;
if (ble_advdata_name_find(p_adv_report->data.p_data,
p_adv_report->data.len,
m_target_periph_ame))// 发现指定名称的设备
{
// 对指定参数进行连接
err_code = sd_ble_gap_connect(&p_adv_report->peer_addr,
&m_scan_params,
&m_connection_param,
APP_BLE_CONN_CFG_TAG);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("Connection Request Failed, reason %d", err_code);
}
}
else
{
err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer);// 如果没发现继续扫描
}
}
通过 ble_advdata_name_find
函数发现指定广播名称的广播数据,如果是的话,则通过 sd_ble_gap_connect
函数对目标发起 GAP 连接。
这样主机设备就可以判断哪个设备是我们需要连接的从机了,所以主机程序和从机程序中,广播名称都要设置为:Nordic_Blinky
三、主机设备通知使能
3.1 lbs_c_init
不同于从机设备的多路连接(因为从机自己提供服务,服务是唯一的),主机连接的多个从机有可能存在服务的不同,所以我们需要初始化多个 m_lbs_c 的实例。 同样的在 lbs_c_init 的初始化函数中,我们需要对这些 m_lbs_c 的实例都进行初始化操作。
BLE_LBS_C_ARRAY_DEF(m_lbs_c, NRF_SDH_BLE_CENTRAL_LINK_COUNT);
3.2 ble_lbs_c_init
通过 ble_lbs_c_init 函数实现主机初始化,这个函数是一个数据结构体类型,通过 i 的不同值,来分别代表不同的从机链路。这个函数被称为客户端处理事件初始化函数。首先设置一个基础 UUID
,这个基础 UUID 通过协议栈函数 sd_ble_uuid_vs_add
赋值给主设备。然后通过调用注册函数 ble_db_discovery_evt_register
通过 UUID 查出对应的服务。如果基础 UUID 相同,则会启动里面的回调函数 db_discovery_evt_handler
进行操作。
3.3 lbs_c_evt_handler
当发现主服务完成后 BLE_LBS_C_EVT_DISCOVERY_COMPLETE,启动主机使能从机的通知。
3.3.1 ble_lbs_c_button_notif_enable
进入按键通知使能服务 ble_lbs_c_button_notif_enable 函数内部,使能通知实际上就是 CCCD 的写操作,实际上就是通过协议栈函数 sd_ble_gattc_write
来实现 CCCD 的写入
3.3.2 cccd_configure
3.3.3 tx_buffer_process
四、主机设备接收从机数据
4.1 ble_lbs_c_on_ble_evt
当通知使能后,主机就可以接收从机发来的通知信息,一旦从机使用 hvx 通知函数上传数据给主机,主机就会收到数据,此时就会触发 BLE_GATTC_EVT_HVX 事件
4.2 on_hvx
on_hvx 函数最终处理接收到的从机通知数据,触发数据类型为按键通知 BLE_LBS_C_EVT_BUTTON_NOTIFICATION ,操作句柄为 p_ble_lbs_c->conn_handle,数据存 ble_lbs_c_evt.params.button.button_state 中
4.3 lbs_c_evt_handler
一旦触发了蓝牙事件 BLE_LBS_C_EVT_BUTTON_NOTIFICATION,在蓝牙事件回调函数 lbs_c_evt_handler 中,根据不同的 p_lbs_c_evt->conn_handle 决定不同的从机链路,来点亮哪个LED灯。
五、主机设备数据写从机
5.1 ble_lbs_led_status_send
把主机按键状态作为数据写入到从机,通过 i 的值控制不同的从机。tx_buffer_process
这个函数就是前面通知使能中使用主机 cccd 写函数,这里同样可以作为写数据使用。
• 由 Leung 写于 2020 年 9 月 29 日
• 参考:青风电子社区