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();
}
}
通过client工具,可以观察到数据如下,主机可以周期的接收到 0x55 的数据 ,正式从机发送的数据。。所以。。。。
ok。。。。。。。。。。。。。。。。。
总结:
1.添加对应的控制服务
2扫描要连接的蓝牙的特征
3.连接蓝牙,并保存handle
4.找服务和特征值。
5.抛出发现完成事件
6.断开初始化handle