nRF52832主机从机数据交互

本文详细介绍了如何在C语言环境中实现蓝牙Nordic UART服务,包括从机数据接收的回调函数、主机发送数据的函数以及错误调试过程。此外,还展示了如何通过按键控制定时器来定时发送数据,并提供了Wireshark抓包结果作为验证。
摘要由CSDN通过智能技术生成

代码编写

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关闭定时器之后,停止发送;

抓包器结果:

 

 

 

 

 

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值