代码编写
main.c添加
1.ble_nus_c_evt_handler()函数(蓝牙接收事件回调函数)
//蓝牙接收事件回调
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, ble_nus_c_evt_t const * p_ble_nus_evt)
{
ret_code_t err_code;
switch (p_ble_nus_evt->evt_type)
{ //一旦服务发现完成,就开始分配连接句柄,同时使能通知
case BLE_NUS_C_EVT_DISCOVERY_COMPLETE:
NRF_LOG_INFO("Discovery complete.");
err_code = ble_nus_c_handles_assign(p_ble_nus_c, p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles);
APP_ERROR_CHECK(err_code);
//使能cccd通知使能
err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c);
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("Connected to device with Nordic UART Service.");
break;
//一旦有接收到蓝牙事件,则开始通过串口调试助手在电脑上进行打印
case BLE_NUS_C_EVT_NUS_TX_EVT://收到从机数据
//ble_nus_chars_received_uart_print(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);
break;
case BLE_NUS_C_EVT_DISCONNECTED://断开连接
NRF_LOG_INFO("Disconnected.");
scan_start();//开始
break;
}
}
2.ble_nus_chars_received_uart_print()函数(蓝牙接收数据打印函数)
//蓝牙主机收到从机数据,log打印(原串口打印)
static void ble_nus_chars_received_uart_print(uint8_t * p_data, uint16_t data_len)
{
NRF_LOG_INFO("从机发送到主机");
NRF_LOG_HEXDUMP_DEBUG(p_data, data_len);//打印数组
}
3.修改buttons_leds_init() 函数(led,按键硬件初始化函数)和按键回调函数bsp_event_handler()
void bsp_event_handler(bsp_event_t event)
{
uint8_t data_array[1];
uint32_t ret_val;
NRF_LOG_INFO("event = %d", event);
switch (event)
{
case BSP_EVENT_SLEEP:
break;
case BSP_EVENT_KEY_0:
NRF_LOG_INFO("BSP_EVENT_KEY_0");
data_array[0] = 0x02;
//向从机发送数据
ret_val = ble_nus_c_string_send(&m_ble_nus_c, data_array, 1);
break;
case BSP_EVENT_KEY_1:
NRF_LOG_INFO("BSP_EVENT_KEY_1");
data_array[0] = 0x04;
ret_val = ble_nus_c_string_send(&m_ble_nus_c, data_array, 1);
break;
case BSP_EVENT_KEY_2:
NRF_LOG_INFO("BSP_EVENT_KEY_2");
data_array[0] = 0x05;
//向从机发送数据
ret_val = ble_nus_c_string_send(&m_ble_nus_c, data_array, 1);
break;
// case BSP_EVENT_KEY_3:
// if(timer_state == false)
// {
// //在定时器内定时向从机发送指令
// timer_state = true;
// application_timers_start();//启动定时器
// }
// else
// {
// timer_state = false;
// application_timers_stop();//关闭定时器
// }
break;
default:
break;
}
}
/**@brief 初始化板级设备*/
static void buttons_leds_init(void)
{
ret_code_t err_code;
bsp_event_t startup_event;
//修改成加上按键初始化
err_code = bsp_init(BSP_INIT_LEDS|BSP_INIT_BUTTONS, bsp_event_handler);
APP_ERROR_CHECK(err_code);
err_code = bsp_btn_ble_init(NULL, &startup_event);
APP_ERROR_CHECK(err_code);
}
ble_nus_c.c添加
目录:...\SDK\components\ble\ble_services\ble_nus_c\ble_nus_c.c
1.ble_nus_c_init(&m_ble_nus_c, &init);函数
//主机需要连接的服务构造
uint32_t ble_nus_c_init(ble_nus_c_t * p_ble_nus_c, ble_nus_c_init_t * p_ble_nus_c_init)
{
uint32_t err_code;
ble_uuid_t uart_uuid;
ble_uuid128_t nus_base_uuid = NUS_BASE_UUID;//基础UUID
VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
VERIFY_PARAM_NOT_NULL(p_ble_nus_c_init);
err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_ble_nus_c->uuid_type);//添加基础UUID
VERIFY_SUCCESS(err_code);
uart_uuid.type = p_ble_nus_c->uuid_type;//服务的UUID类型
uart_uuid.uuid = BLE_UUID_NUS_SERVICE;//主服务UUID
p_ble_nus_c->conn_handle = BLE_CONN_HANDLE_INVALID;//清空连接句柄
p_ble_nus_c->evt_handler = p_ble_nus_c_init->evt_handler;//分配事件
p_ble_nus_c->handles.nus_tx_handle = BLE_GATT_HANDLE_INVALID;//清空TX句柄
p_ble_nus_c->handles.nus_rx_handle = BLE_GATT_HANDLE_INVALID;//清空RX句柄
return ble_db_discovery_evt_register(&uart_uuid);//用于注册DB发现模块的函数
}
2.ble_nus_c_tx_notif_enable()函数(从机通知使能函数)
//从机通知使能
uint32_t ble_nus_c_tx_notif_enable(ble_nus_c_t * p_ble_nus_c)
{
VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
if ( (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
||(p_ble_nus_c->handles.nus_tx_cccd_handle == BLE_GATT_HANDLE_INVALID)
)
{
return NRF_ERROR_INVALID_STATE;
}
return cccd_configure(p_ble_nus_c->conn_handle,p_ble_nus_c->handles.nus_tx_cccd_handle, true);
}
3.cccd_configure()函数(CCCD进行使能函数)
/*从机通知之前需要先CCCD进行使能 */
static uint32_t cccd_configure(uint16_t conn_handle, uint16_t cccd_handle, bool enable)
{
uint8_t buf[BLE_CCCD_VALUE_LEN];
buf[0] = enable ? BLE_GATT_HVX_NOTIFICATION : 0;
buf[1] = 0;
ble_gattc_write_params_t const write_params =
{
.write_op = BLE_GATT_OP_WRITE_REQ,
.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE,
.handle = cccd_handle,
.offset = 0,
.len = sizeof(buf),
.p_value = buf
};
return sd_ble_gattc_write(conn_handle, &write_params);
}
4.ble_nus_c_on_ble_evt函数(用于服务事件处理,包括连接,断开,空中属性等处理)
//用于服务事件处理,包括连接,断开,空中属性等处理
void ble_nus_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
//=======================主从机数据交互添加
ble_nus_c_t * p_ble_nus_c = (ble_nus_c_t *)p_context;
if ((p_ble_nus_c == NULL) || (p_ble_evt == NULL))
{
return;
}
if ( (p_ble_nus_c->conn_handle != BLE_CONN_HANDLE_INVALID)
&&(p_ble_nus_c->conn_handle != p_ble_evt->evt.gap_evt.conn_handle)
)
{
return;
}
switch (p_ble_evt->header.evt_id)
{ //发生客户端通知事件
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_nus_c, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
if (p_ble_evt->evt.gap_evt.conn_handle == p_ble_nus_c->conn_handle
&& p_ble_nus_c->evt_handler != NULL)
{
ble_nus_c_evt_t nus_c_evt;
nus_c_evt.evt_type = BLE_NUS_C_EVT_DISCONNECTED;
p_ble_nus_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
}
break;
default:
// No implementation needed.
break;
}
//=======================主从机数据交互添加==end
}
5.on_hvx函数(处理从协议栈接收的句柄值通知的功能,也就是接收从机的数据)
/**@brief 处理从协议栈接收的句柄值通知的功能,也就是接收从机的数据
*
*
* @param[in] p_ble_nus_c 指向NUS客户机结构的指针
* @param[in] p_ble_evt 指向接收到的BLE事件的指针.
*/
static void on_hvx(ble_nus_c_t * p_ble_nus_c, ble_evt_t const * p_ble_evt)
{
// HVX 只能在客户端发送时发生
if ( (p_ble_nus_c->handles.nus_tx_handle != BLE_GATT_HANDLE_INVALID)
&& (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_nus_c->handles.nus_tx_handle)
&& (p_ble_nus_c->evt_handler != NULL))
{
ble_nus_c_evt_t ble_nus_c_evt;
ble_nus_c_evt.evt_type = BLE_NUS_C_EVT_NUS_TX_EVT;//触发TX操作,接收从机上传数据
ble_nus_c_evt.p_data = (uint8_t *)p_ble_evt->evt.gattc_evt.params.hvx.data;//数据
ble_nus_c_evt.data_len = p_ble_evt->evt.gattc_evt.params.hvx.len;//数据长度
//回调函数在ble_nus_c_init里面分配,
//分配语句:p_ble_nus_c->evt_handler = p_ble_nus_c_init->evt_handler;//分配事件
//函数实现:ble_nus_c_evt_handler
p_ble_nus_c->evt_handler(p_ble_nus_c, &ble_nus_c_evt);//调用回调函数
NRF_LOG_DEBUG("Client sending data.");
}
}
6.ble_nus_c_string_send()函数(向从机发送数据)
//主机发送数据给从机
uint32_t ble_nus_c_string_send(ble_nus_c_t * p_ble_nus_c, uint8_t * p_string, uint16_t length)
{
VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
if (length > BLE_NUS_MAX_DATA_LEN)
{
NRF_LOG_WARNING("Content too long.");
return NRF_ERROR_INVALID_PARAM;
}
if (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
NRF_LOG_WARNING("Connection handle invalid.");
return NRF_ERROR_INVALID_STATE;
}
ble_gattc_write_params_t const write_params =
{
.write_op = BLE_GATT_OP_WRITE_CMD,
.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE,
.handle = p_ble_nus_c->handles.nus_rx_handle,
.offset = 0,
.len = length,
.p_value = p_string
};
//主机写的功能。
return sd_ble_gattc_write(p_ble_nus_c->conn_handle, &write_params);
}
错误调试
错误1:ERROR 8 [NRF_ERROR_INVALID_STATE]
现象
以下代码段
//蓝牙接收事件回调
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, ble_nus_c_evt_t const * p_ble_nus_evt)
{
ret_code_t err_code;
switch (p_ble_nus_evt->evt_type)
{ //一旦服务发现完成,就开始分配连接句柄,同时使能通知
case BLE_NUS_C_EVT_DISCOVERY_COMPLETE:
NRF_LOG_INFO("Discovery complete.");
err_code = ble_nus_c_handles_assign(p_ble_nus_c, p_ble_nus_evt->conn_handle, &p_ble_nus_evt->handles);
APP_ERROR_CHECK(err_code);
//使能cccd通知使能
err_code = ble_nus_c_tx_notif_enable(p_ble_nus_c);
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("Connected to device with Nordic UART Service.");
break;
//一旦有接收到蓝牙事件,则开始通过串口调试助手在电脑上进行打印
case BLE_NUS_C_EVT_NUS_TX_EVT://收到从机数据
//ble_nus_chars_received_uart_print(p_ble_nus_evt->p_data, p_ble_nus_evt->data_len);
break;
case BLE_NUS_C_EVT_DISCONNECTED://断开连接
NRF_LOG_INFO("Disconnected.");
scan_start();//开始
break;
}
}
出现错误ERROR 8 [NRF_ERROR_INVALID_STATE],如图
原因
主机设置的特性UUID和特性的UUID不一样,如下图
解决:
修改主机设定的特性UUID和从机相同即可
如下图:
修改之后运行结果:
运行结果
Wireshark软件设置:设置值显示从机接收到的数据
按键1按下:向从机发送0X02
按键2按下:向从机发送0X04
按键3按下:向从机发送-0x05,从机返回当前已拍摄的照片张数
功能拓展
在收发数据成功的基础上按键开关定时器,并定时发送数据给从机:
main.c加入定时器相关代码:
修改定时器初始化函数timer_init
#define BATTERY_LEVEL_MEAS_INTERVAL APP_TIMER_TICKS(500) /*定时器中断时间,每隔500ms中断一次 */
APP_TIMER_DEF(m_battery_timer_id); //定时器句柄(定义定时器实例)
/**@brief初始化软件定时器*/
static void timer_init(void)
{
ret_code_t err_code = app_timer_init();
APP_ERROR_CHECK(err_code);
//加入定时器句柄
err_code = app_timer_create(&m_battery_timer_id,
APP_TIMER_MODE_REPEATED,
send_data_timeout_handler);
APP_ERROR_CHECK(err_code);
}
加入定时器开启/关闭代码
static void application_timers_start(void)//开启定时器
{
ret_code_t err_code;
// Start application timers.
err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
}
static void application_timers_stop(void)//关闭定时器
{
app_timer_stop(m_battery_timer_id);
}
加入定时器回调函数
//定时器回调函数
static void send_data_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
disconnect_time++;
if(disconnect_time%4 == 0)//定时向从机发送数据
{
// NRF_LOG_INFO("disconnect_time = %d", disconnect_time);
uint8_t data_array[1];
data_array[0] = 0x02;
ble_nus_c_string_send(&m_ble_nus_c, data_array, 1);
}
}
修改按键事件回调函数
在按键回调函数里面加入按键开光定时器的代码:
case BSP_EVENT_KEY_3:
if(timer_state == false)
{
//在定时器内定时向从机发送指令
timer_state = true;
application_timers_start();//启动定时器
}
else
{
timer_state = false;
application_timers_stop();//关闭定时器
}
break;
修改之后运行结果
按下按键4按键开启定时器之后,每隔两秒向从机发送一次0X02,按下按键4关闭定时器之后,停止发送;
抓包器结果: