1.目的
在SDK10.0\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay\pca10028\s130\arm4的基础上面添加一个控制串口服务,让其可以同时连接3个设备
2.分析
学习nrf51822主机和从机通信
3.平台:
协议栈版本:SDK10.0.0
编译软件:keil 5.12
硬件平台:nrf51822最小系统
例子:SDK 10.0.0\examples\ble_peripheral\ble_app_uart\pca10028\s110\arm4 从机例子
SDK10.0\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay\pca10028\s130\arm4 做主机
4步骤.
1.把ble_nus_c.c文件假如到工程。
添加对应的路径以及头文件
在main函数添加
/**@snippet [Handling events from the ble_nus_c module] */ static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, const ble_nus_c_evt_t * p_ble_nus_evt) { uint32_t err_code; switch (p_ble_nus_evt->evt_type) { case BLE_NUS_C_EVT_FOUND_NUS_TX_CHARACTERISTIC: APPL_LOG("The device has the device TX characteristic\r\n"); break; case BLE_NUS_C_EVT_FOUND_NUS_RX_CHARACTERISTIC: err_code = ble_nus_c_rx_notif_enable(p_ble_nus_c); APP_ERROR_CHECK(err_code); APPL_LOG("The device has the device RX characteristic\r\n"); break; case BLE_NUS_C_EVT_NUS_RX_EVT: for (uint32_t i = 0; i < p_ble_nus_evt->data_len; i++) { SEGGER_RTT_printf(0,"p_ble_nus_evt->p_data[%d]=%x\r\n", i,p_ble_nus_evt->p_data[i]); //通过j-link的rtt发送接收到的数据 //while(app_uart_put( p_ble_nus_evt->p_data[i]) != NRF_SUCCESS); } break; case BLE_NUS_C_EVT_DISCONNECTED: APPL_LOG("NUS device disconnected\r\n"); scan_start(); break; } }
/**@brief Function for initializing the NUS Client. */ static void nus_c_init(ble_nus_c_t *ble_nus_c) { uint32_t err_code; ble_nus_c_init_t nus_c_init_t; nus_c_init_t.evt_handler = ble_nus_c_evt_handler; err_code = ble_nus_c_init(ble_nus_c, &nus_c_init_t); APP_ERROR_CHECK(err_code); }
on_ble_central_evt()中函数改为如下
static void on_ble_central_evt(const ble_evt_t * const p_ble_evt) { // The addresses of peers we attempted to connect to. static ble_gap_addr_t periph_addr_hrs; static ble_gap_addr_t periph_addr_rsc; static ble_gap_addr_t periph_addr_nus;//定义连接外部具有串口服务的蓝牙地址 // For readability. const ble_gap_evt_t * const p_gap_evt = &p_ble_evt->evt.gap_evt; switch (p_ble_evt->header.evt_id) { /** Upon connection, check which peripheral has connected (HR or RSC), initiate DB * discovery, update LEDs status and resume scanning if necessary. */ case BLE_GAP_EVT_CONNECTED: { uint8_t i = 0; uint32_t err_code; // For readability. const ble_gap_addr_t * const peer_addr = &p_gap_evt->params.connected.peer_addr; /** Check which peer has connected, save the connection handle and initiate DB discovery. * DB discovery will invoke a callback (hrs_c_evt_handler and rscs_c_evt_handler) * upon completion, which is used to enable notifications from the peer. */ // if(memcmp(&periph_addr_hrs, peer_addr, sizeof(ble_gap_addr_t)) == 0)//对比地址 { NRF_LOG_PRINTF("HRS central connected\r\n"); // Reset the peer address we had saved. memset(&periph_addr_hrs, 0, sizeof(ble_gap_addr_t)); if(m_conn_handle_nus_c == BLE_CONN_HANDLE_INVALID) { m_conn_handle_nus_c = p_gap_evt->conn_handle; m_ble_nus_c.conn_handle = p_ble_evt->evt.gap_evt.conn_handle; //保存handle err_code = ble_db_discovery_start(&m_ble_db_discovery_nus, p_gap_evt->conn_handle); //开始找服务 APP_ERROR_CHECK(err_code); }else if(m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID) { m_conn_handle_hrs_c = p_gap_evt->conn_handle; m_ble_hrs_c.conn_handle = p_ble_evt->evt.gap_evt.conn_handle; err_code = ble_db_discovery_start(&m_ble_db_discovery_hrs, p_gap_evt->conn_handle); APP_ERROR_CHECK(err_code); } else if(m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID) { m_conn_handle_rscs_c = p_gap_evt->conn_handle; m_ble_rsc_c.conn_handle = p_ble_evt->evt.gap_evt.conn_handle; err_code = ble_db_discovery_start(&m_ble_db_discovery_rsc, p_gap_evt->conn_handle); APP_ERROR_CHECK(err_code); } // m_nus.conn_handle = p_gap_evt->conn_handle; NRF_LOG_PRINTF("Starting DB discovery for Nordic_UART\r\n"); } LEDS_ON(CENTRAL_CONNECTED_LED); if (ble_conn_state_n_centrals() == MAX_CONNECTED_CENTRALS)//判断是否超过了规定的最大连接个数 { LEDS_OFF(CENTRAL_SCANNING_LED); } else { // Resume scanning. LEDS_ON(CENTRAL_SCANNING_LED); scan_start(); } } break; // BLE_GAP_EVT_CONNECTED /** Upon disconnection, reset the connection handle of the peer which disconnected, update * the LEDs status and start scanning again. */ case BLE_GAP_EVT_DISCONNECTED: //蓝牙断开 { uint8_t n_centrals ; uint8_t i = 0 ; if (p_gap_evt->conn_handle == m_conn_handle_nus_c)//判断断开的是是不是串口服务哪个蓝牙 { NRF_LOG_PRINTF(" central disconnected (reason: %d)\r\n", p_gap_evt->params.disconnected.reason); m_conn_handle_nus_c = BLE_CONN_HANDLE_INVALID; //把handle初始化 } else if (p_gap_evt->conn_handle == m_conn_handle_hrs_c)//判断断开的外设是不是心率那个 { NRF_LOG_PRINTF("HRS central disconnected (reason: %d)\r\n", p_gap_evt->params.disconnected.reason); m_conn_handle_hrs_c = BLE_CONN_HANDLE_INVALID; //初始化 } else if(p_gap_evt->conn_handle == m_conn_handle_rscs_c) { NRF_LOG_PRINTF("RSC central disconnected (reason: %d)\r\n", p_gap_evt->params.disconnected.reason); m_conn_handle_rscs_c = BLE_CONN_HANDLE_INVALID; } // Start scanning // scan_start(); // Update LEDs status. LEDS_ON(CENTRAL_SCANNING_LED); n_centrals = ble_conn_state_n_centrals(); if (n_centrals == 0) { LEDS_OFF(CENTRAL_CONNECTED_LED); } } break; // BLE_GAP_EVT_DISCONNECTED case BLE_GAP_EVT_ADV_REPORT: { uint32_t err_code; data_t adv_data; data_t type_data; bool do_connect = false; // For readibility. const ble_gap_addr_t * const peer_addr = &p_gap_evt->params.adv_report.peer_addr; // Initialize advertisement report for parsing. adv_data.p_data = (uint8_t *)p_gap_evt->params.adv_report.data; adv_data.data_len = p_gap_evt->params.adv_report.dlen; err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, &adv_data, &type_data); //寻找名字字段 // NRF_LOG_PRINTF("type_data.p_data[0]: %s)\r\n",type_data.p_data);//???? // NRF_LOG_PRINTF("lenght: %d\r\n",strlen("Nordic_UART")); //strlen("HRS")??????????? if( memcmp(type_data.p_data,"Nordic_UART",strlen("Nordic_UART"))==0)//对比名字,是不是"Nordic_UART" { memcpy(&periph_addr_hrs, peer_addr, sizeof(ble_gap_addr_t));//复制蓝牙mac do_connect = true; //赋值 } else { err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // Look for the services in 'complete' if it was not found in 'more available'. err_code = adv_report_parse(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // If we can't parse the data, then exit. break; } } // Verify if any UUID match the Heart rate or Running speed and cadence services. for (uint32_t u_index = 0; u_index < (type_data.data_len / UUID16_SIZE); u_index++) { uint16_t extracted_uuid; UUID16_EXTRACT(&extracted_uuid, &type_data.p_data[u_index * UUID16_SIZE]); /** We do not want to connect to two peripherals offering the same service, so when * a UUID is matched, we check that we are not already connected to a peer which * offers the same service. We then save the peer address, so that upon connection * we can tell which peer has connected and update its respective connection * handle. */ if ((extracted_uuid == BLE_UUID_HEART_RATE_SERVICE) && (m_conn_handle_hrs_c == BLE_CONN_HANDLE_INVALID)) { do_connect = true; memcpy(&periph_addr_hrs, peer_addr, sizeof(ble_gap_addr_t)); } else if ((extracted_uuid == BLE_UUID_RUNNING_SPEED_AND_CADENCE) && (m_conn_handle_rscs_c == BLE_CONN_HANDLE_INVALID)) { do_connect = true; memcpy(&periph_addr_rsc, peer_addr, sizeof(ble_gap_addr_t)); } } } if (do_connect) { // Initiate connection. err_code = sd_ble_gap_connect(peer_addr, &m_scan_param, &m_connection_param);//通过地址mac连接蓝牙 if (err_code != NRF_SUCCESS) { APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n", err_code); } } } break; // BLE_GAP_ADV_REPORT case BLE_GAP_EVT_TIMEOUT: { // We have not specified a timeout for scanning, so only connection attemps can timeout. if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { APPL_LOG("[APPL]: Connection Request timed out.\r\n"); } } break; // BLE_GAP_EVT_TIMEOUT case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { // Accept parameters requested by peer. ret_code_t err_code; err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle, &p_gap_evt->params.conn_param_update_request.conn_params); APP_ERROR_CHECK(err_code); } break; // BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST default: // No implementation needed. break; } }
蓝牙协议栈事件回调
/**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler. * * @details This function is called from the scheduler in the main loop after a BLE stack event has * been received. * * @param[in] p_ble_evt Bluetooth stack event. */ static void ble_evt_dispatch(ble_evt_t * p_ble_evt) { uint16_t conn_handle; uint16_t role; /** The Connection state module has to be fed BLE events in order to function correctly * Remember to call ble_conn_state_on_ble_evt before calling any ble_conns_state_* functions. */ ble_conn_state_on_ble_evt(p_ble_evt); pm_ble_evt_handler(p_ble_evt); // The connection handle should really be retrievable for any event type. conn_handle = p_ble_evt->evt.gap_evt.conn_handle; role = ble_conn_state_role(conn_handle); // Based on the role this device plays in the connection, dispatch to the right applications. if (role == BLE_GAP_ROLE_PERIPH) //判断角色。是不是外设事件 { // Manages peripheral LEDs. on_ble_peripheral_evt(p_ble_evt); ble_advertising_on_ble_evt(p_ble_evt); ble_conn_params_on_ble_evt(p_ble_evt); // Dispatch to peripheral applications. //ble_nus_on_ble_evt (&m_nus, p_ble_evt); ble_hrs_on_ble_evt (&m_hrs, p_ble_evt); ble_rscs_on_ble_evt(&m_rscs, p_ble_evt); } else if ((role == BLE_GAP_ROLE_CENTRAL) || (p_ble_evt->header.evt_id == BLE_GAP_EVT_ADV_REPORT))//判断是不是中心事件 { /** on_ble_central_evt will update the connection handles, so we want to execute it * after dispatching to the central applications upon disconnection. */ if (p_ble_evt->header.evt_id != BLE_GAP_EVT_DISCONNECTED) { on_ble_central_evt(p_ble_evt); } if (conn_handle == m_conn_handle_hrs_c) { /** on_ble_central_evt will update the connection handles, so we want to execute it * after dispatching to the central applications upon disconnection. */ if (p_ble_evt->header.evt_id != BLE_GAP_EVT_DISCONNECTED) { on_ble_central_evt(p_ble_evt); } if (conn_handle == m_conn_handle_hrs_c) //判断是不是hrs handle { ble_hrs_c_on_ble_evt(&m_ble_hrs_c, p_ble_evt); ble_db_discovery_on_ble_evt(&m_ble_db_discovery_hrs, p_ble_evt); //产生对应的回调 } else if (conn_handle == m_conn_handle_rscs_c) //判断是不是<span style="font-family: Arial;"> rscs handle {</span><span style="font-family: Arial;"> ble_rscs_c_on_ble_evt(&m_ble_rsc_c, p_ble_evt);</span>
ble_db_discovery_on_ble_evt(&m_ble_db_discovery_rsc, p_ble_evt); //产生对应回调 } else if (conn_handle == m_conn_handle_nus_c)//判断是不是串口 handle { ble_nus_c_on_ble_evt(&m_ble_nus_c, p_ble_evt); ble_db_discovery_on_ble_evt(&m_ble_db_discovery_nus, p_ble_evt); } // If the peer disconnected, we update the connection handles last. if (p_ble_evt->header.evt_id == BLE_GAP_EVT_DISCONNECTED) { on_ble_central_evt(p_ble_evt); } } }
主函数如下
int main(void) { ret_code_t err_code; bool erase_bonds; err_code = NRF_LOG_INIT(); APP_ERROR_CHECK(err_code); SEGGER_RTT_Init(); NRF_LOG_PRINTF("Relay Example\r\n"); APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, NULL); buttons_leds_init(&erase_bonds); if (erase_bonds == true) { NRF_LOG("Bonds erased!\r\n"); } ble_stack_init(); peer_manager_init(erase_bonds); db_discovery_init(); hrs_c_init(); rscs_c_init(); nus_c_init(&m_ble_nus_c);//这是添加的控制串口服务 gap_params_init(); conn_params_init(); services_init(); advertising_init(); /** Start scanning for peripherals and initiate connection to devices which * advertise Heart Rate or Running speed and cadence UUIDs. */ scan_start(); // Turn on the LED to signal scanning. LEDS_ON(CENTRAL_SCANNING_LED); // Start advertising. err_code = ble_advertising_start(BLE_ADV_MODE_FAST); APP_ERROR_CHECK(err_code); SEGGER_RTT_printf(0,"hello"); for (;;) { // Wait for BLE events. power_manage(); } }
在SDK 10.0.0\examples\ble_peripheral\ble_app_uart\pca10028\s110\arm4 从机例子中,
在main.c中添加如下代码
定时事件ID和时间
APP_TIMER_DEF(m_send_data_timer_id); #define SEND_DATA_TIMER_INTERVAL APP_TIMER_TICKS(1000, APP_TIMER_PRESCALER) /**< Heart rate measurement interval (ticks). */
void send_data_timeout_handler(void * p_context) { uint8_t data = 0x55; if(m_nus.is_notification_enabled == true) //假如主机使能了从机通知 { ble_nus_string_send(&m_nus,&data,1);//发送数据 } } /**@brief Function for the Timer initialization. * * @details Initializes the timer module. This creates and starts application timers. */ static void timers_init(void) { uint32_t err_code; //定一个周期1s的事件, err_code = app_timer_create(&m_send_data_timer_id, APP_TIMER_MODE_REPEATED, send_data_timeout_handler); }
主函数如下
/**@brief Application main function. */ int main(void) { uint32_t err_code; bool erase_bonds; uint8_t start_string[] = START_STRING; // Initialize. APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false); uart_init(); timers_init();<span style="white-space:pre"> </span>//定义一个1s的事件 buttons_leds_init(&erase_bonds); ble_stack_init(); gap_params_init(); services_init(); advertising_init(); conn_params_init(); printf("%s",start_string); err_code = app_timer_start(m_send_data_timer_id, SEND_DATA_TIMER_INTERVAL, NULL);//开始这个事件 APP_ERROR_CHECK(err_code); err_code = ble_advertising_start(BLE_ADV_MODE_FAST); APP_ERROR_CHECK(err_code); // Enter main loop. for (;;) { power_manage(); } }
最后,把从机和主机程序分别下载到芯片里面。主机用j-link仿真并打开RTT功能(具体看:http://blog.csdn.net/a369000753/article/details/51192707 RTT的使用)
通过client工具,可以观察到数据如下,主机可以周期的接收到 0x55 的数据 ,正式从机发送的数据。。所以。。。。
ok。。。。。。。。。。。。。。。。。
总结:
1.添加对应的控制服务
2扫描要连接的蓝牙的特征
3.连接蓝牙,并保存handle
4.找服务和特征值。
5.抛出发现完成事件
6.断开初始化handle