基于nRF5的多主一从蓝牙实验(数据传输)

nRF52832的多主一从蓝牙透传

多主一从简介

nRF52832蓝牙通信的方式是NUS主从通信,NUS具体实现方式即申请一个NUS服务,这里不多作阐述。所谓多主一从,就是多个蓝牙主机(手机),与一个从机间的通信,解决了传统蓝牙间只能一对一数据传输的弊端,官方例程中有实现了多主一从的点灯程序,以下介绍的是多主一从的数据传输,废话少说,下面是实现方式和注意要点。

工程修改

1.定义最大连接数

sdk_config.h中找到这三个宏定义,显然,这里就是定义蓝牙最大连接数目的地方,第一个是最大主机连接数,第二个是最大从机连接数,第三个是总最大连接数,这次我们最大可连接四个主机,那么只需要在这里修改第一个和第三个即可。

//  NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - Maximum number of peripheral links. 
#ifndef NRF_SDH_BLE_PERIPHERAL_LINK_COUNT
#define NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 4
#endif

//  NRF_SDH_BLE_CENTRAL_LINK_COUNT - Maximum number of central links. 
#ifndef NRF_SDH_BLE_CENTRAL_LINK_COUNT
#define NRF_SDH_BLE_CENTRAL_LINK_COUNT 0
#endif

//  NRF_SDH_BLE_TOTAL_LINK_COUNT - Total link count. 
//  Maximum number of total concurrent connections using the default configuration.

#ifndef NRF_SDH_BLE_TOTAL_LINK_COUNT
#define NRF_SDH_BLE_TOTAL_LINK_COUNT 4
#endif

2.main.c修改

先来看看代码

int main(void)
{
    log_init();             // LOG初始化
    timers_init();          // 定时器初始化
	leds_init();			// led初始化
    power_management_init();// 电源管理初始化
    ble_stack_init();       // BLE堆栈初始化
    gap_params_init();      // 初始化GAP
    gatt_init();            // 初始化GATT
    services_init();        // BLE服务初始化
    advertising_init();     // 广播初始化
    conn_params_init();     // 连接参数初始化
        
    UART_Init(APP_UartEvtHandle);   //串口初始化
    UART_Write("3.5_ble_peripheral_multi\n", sizeof("3.5_ble_peripheral_multi")); 
NRF_LOG_INFO("Multiperipheral example started.");
    advertising_start();    // 开始广播

    for (;;)
    {
        idle_state_handle();  
    }
}

这是main函数的代码,我这边只对与基本初始化不同的地方进行阐述。

BLE堆栈初始化

先看到ble_stack_init()

static void ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}

这里没什么不同,主要是其中定义的回调函数ble_evt_handler

static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
	ret_code_t err_code;
    switch (p_ble_evt->header.evt_id)
    {
		case BLE_GAP_EVT_CONNECTED:
			on_connected(&p_ble_evt->evt.gap_evt);
			break;

		case BLE_GAP_EVT_DISCONNECTED:
			on_disconnected(&p_ble_evt->evt.gap_evt);
			break;

		case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
				// Pairing not supported
				err_code = sd_ble_gap_sec_params_reply(p_ble_evt->evt.gap_evt.conn_handle,
													   BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
													   NULL,
													   NULL);
				APP_ERROR_CHECK(err_code);
				break;

		case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
			{
				NRF_LOG_DEBUG("PHY update request.");
				ble_gap_phys_t const phys =
				{
					.rx_phys = BLE_GAP_PHY_AUTO,
					.tx_phys = BLE_GAP_PHY_AUTO,
				};
				err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
				APP_ERROR_CHECK(err_code);
			} break;

		case BLE_GATTS_EVT_SYS_ATTR_MISSING:
		// No system attributes have been stored.
			err_code = sd_ble_gatts_sys_attr_set(p_ble_evt->evt.gap_evt.conn_handle, NULL, 0, 0);
			APP_ERROR_CHECK(err_code);
			break;

		case BLE_GATTC_EVT_TIMEOUT:
			// Disconnect on GATT Client timeout event.
			NRF_LOG_DEBUG("GATT Client Timeout.");
			err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
											 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
			APP_ERROR_CHECK(err_code);
			break;

		case BLE_GATTS_EVT_TIMEOUT:
			// Disconnect on GATT Server timeout event.
			NRF_LOG_DEBUG("GATT Server Timeout.");
			err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
											 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
			APP_ERROR_CHECK(err_code);
			break;

			default:
			// No implementation needed.
			break;
	}
}

此回调函数中处理了几个事件,其中连接事件和断开事件需要修改,可以看到这两个事件分别调用了 on_connected()on_disconnected() 这两个函数。

static void on_connected(const ble_gap_evt_t * const p_gap_evt)
{
    ret_code_t  err_code;
    uint32_t    periph_link_cnt = ble_conn_state_peripheral_conn_count(); // Number of peripheral links.

	bsp_board_led_on(CONNECTED_LED);
    UART_Printf("Connection with link 0x%x established.\n", p_gap_evt->conn_handle);

    // Assign connection handle to available instance of QWR module.
    for (uint32_t i = 0; i < NRF_SDH_BLE_PERIPHERAL_LINK_COUNT; i++)
    {
        if (m_qwr[i].conn_handle == BLE_CONN_HANDLE_INVALID)
        {
            err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr[i], p_gap_evt->conn_handle);
            APP_ERROR_CHECK(err_code);
            break;
        }
    }

    if (periph_link_cnt == NRF_SDH_BLE_PERIPHERAL_LINK_COUNT)
    {
			bsp_board_led_off(ADVERTISING_LED);
    }
    else
    {
        // Continue advertising. More connections can be established because the maximum link count has not been reached.
        advertising_start();
    }
	nrf_delay_ms(500);
	bsp_board_led_off(CONNECTED_LED);
}

on_connected() 这个函数中,用 nrf_ble_qwr_conn_handle_assign() 函数对每一个主机设备分配一个任务句柄,相当于是连接后要做的事,由于我们这里是第三个参数NULL,所以并不做任何处理,但这个步骤不可省略。

static void on_disconnected(ble_gap_evt_t const * const p_gap_evt)
{
    ret_code_t  err_code;
    uint32_t    periph_link_cnt = ble_conn_state_peripheral_conn_count(); // Number of peripheral links.

    UART_Printf("Connection 0x%x has been disconnected. Reason: 0x%X\n",
                 p_gap_evt->conn_handle,
                 p_gap_evt->params.disconnected.reason);

    if (periph_link_cnt == 0)
    {
        APP_ERROR_CHECK(err_code);
    }

    if (periph_link_cnt == (NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - 1))
    {
        // Advertising is not running when all connections are taken, and must therefore be started.
        advertising_start();
    }
}

接下来继续看到 on_disconnected() 这个函数,值得注意的是,我们在这里需要对当前的主机连接数进行判断,因为当当前连接数到达最大连接数时,广播讲停止,而当前连接数不是再是最大连接数时,需要我们重新开启广播。

服务的注册

当我们蓝牙建立了连接后,我们需要什么样的服务,就要通过服务初始化函数services_init() 去申请注册,作为多主一从,则需要初始化每个连接的排队写服务,在官方例程中还另外初始化了点灯服务,而我们这里则初始化的是NUS服务,即串口透传。
另外别忘了在前面定义NUS的最大实例数。

BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT); //NUS实例化
static void services_init(void)
{
    ret_code_t         err_code;
    ble_nus_init_t     nus_init;
    nrf_ble_qwr_init_t qwr_init = {0};

    // Initialize Queued Write Module instances.
    qwr_init.error_handler = nrf_qwr_error_handler;

    for (uint32_t i = 0; i < LINK_TOTAL; i++)
    {
        err_code = nrf_ble_qwr_init(&m_qwr[i], &qwr_init);
        APP_ERROR_CHECK(err_code);
    }

    // Initialize NUS.
    memset(&nus_init, 0, sizeof(nus_init));

    nus_init.data_handler = nus_data_handler;

    err_code = ble_nus_init(&m_nus, &nus_init);
    APP_ERROR_CHECK(err_code);

	
//	    // Initialize LBS.
//    init.led_write_handler = led_write_handler;

//    err_code = ble_lbs_init(&m_lbs, &init);
//    APP_ERROR_CHECK(err_code);

    ble_conn_state_init();
}

那么在这个NUS的回调函数nus_data_handler 中,我们实现了对数据的接收和串口打印。

static void nus_data_handler(ble_nus_evt_t * p_evt)
{
    if (p_evt->type == BLE_NUS_EVT_RX_DATA)
    {
        UART_Printf("Recive on connection handle 0x%04x.\n", p_evt->conn_handle);
        //UART_Write((uint8_t*)p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
		ble_nus_chars_received_uart_print((uint8_t*)p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);		
        
        NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
        NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
    }
    else if (p_evt->type == BLE_NUS_EVT_TX_RDY)
    {
        NRF_LOG_DEBUG(" Service is ready to accept new data to be transmitted..");
    }
    else if(p_evt->type == BLE_NUS_EVT_COMM_STARTED)
    {
       NRF_LOG_DEBUG("NUS Notification Enable.");
    }
    else if (p_evt->type == BLE_NUS_EVT_COMM_STOPPED)
    {
        NRF_LOG_DEBUG("NUS Notification Disable.");
    }
}

串口打印

最后是串口打印的初始化和回调,这里不多解释

void UART_Init(uart_user_callback pHandle)
{
  m_uart_callback = pHandle;
  
  uart_fifo_init();
  uart_timer_init();
  //baudrate = 115200
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;
  uartConfig.pseltxd = TX_PIN_NUMBER;
  uartConfig.pselrxd = RX_PIN_NUMBER;
  //uartConfig.pselcts = CTS_PIN_NUMBER;
  //uartConfig.pselrts = RTS_PIN_NUMBER;

  nrf_drv_uart_init(&m_Uart,&uartConfig,uart_event_handler);

  nrf_drv_uart_rx(&m_Uart, rxBuf,1);
}
void APP_UartEvtHandle(uart_evt_t* pEvt)
{
    uint16_t len = 0;
    ble_conn_state_conn_handle_list_t conn_handles = ble_conn_state_periph_handles();
    
    switch(pEvt->evt_type)
    {
      case UART_EVT_RX_TIMEOUT:
      case UART_EVT_RX_OVERFLOW:
        len = UART_Read(Buf,pEvt->status);
        if(len > BLE_NUS_MAX_DATA_LEN) break;
        for (uint8_t i = 0; i < conn_handles.len; i++)
        {
  //        UART_Printf("Sent on connection handle 0x%04x.\n", conn_handles.conn_handles[i]);
          ble_nus_data_send(&m_nus, Buf, &len, conn_handles.conn_handles[i]); 
        }
      break;
    }
}

结语

北欧半导体为我们提供了一套简便快捷的蓝牙BLE主从多机通信方案,让蓝牙的应用和开发变得容易。

参考资料

谷雨物联网在线文档
Nordic官方文档
官方多主一从例程

ps:初学蓝牙,如有不妥,欢迎指正,感谢阅读

你可能感兴趣的:(基于nRF5的多主一从蓝牙实验(数据传输))