nRF52832蓝牙主机实战用Nordic SDK实现按键控制从机与定时发送在物联网设备开发中蓝牙主机(Central)与从机(Peripheral)的交互是最常见的应用场景之一。nRF52832作为Nordic Semiconductor的明星产品凭借其低功耗特性和强大的蓝牙5.0支持成为许多嵌入式工程师的首选。本文将基于nRF5 SDK深入讲解如何构建一个功能完整的蓝牙主机系统实现按键触发数据发送、接收从机数据并打印以及通过定时器实现周期性通信。1. 项目架构与核心组件一个典型的蓝牙主机系统需要处理多个关键任务设备发现与连接扫描并识别支持NUS(Nordic UART Service)的从机设备数据收发通过GATT协议实现双向通信用户交互通过物理按键触发特定操作定时任务周期性执行特定功能nRF5 SDK为这些功能提供了完善的API支持主要涉及以下模块模块功能关键APIBLE Stack蓝牙协议栈基础sd_ble_*系列函数NUS ClientUART服务客户端ble_nus_c_*系列函数BSP板级支持包bsp_init,bsp_event_handlerApp Timer应用定时器app_timer_create,app_timer_start硬件初始化是项目的第一步需要正确配置GPIO、定时器和蓝牙协议栈static void buttons_leds_init(void) { ret_code_t err_code; bsp_event_t startup_event; // 初始化LED和按键注册事件回调 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); }2. NUS服务实现与数据交互Nordic UART Service(NUS)是模拟串口通信的蓝牙服务极大简化了数据传输实现。主机端需要完成服务发现、特性配置和数据收发三个关键步骤。2.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(发现服务完成); // 分配连接句柄 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); // 启用从机通知 err_code ble_nus_c_tx_notif_enable(p_ble_nus_c); APP_ERROR_CHECK(err_code); break; case BLE_NUS_C_EVT_NUS_TX_EVT: // 处理接收到的数据 break; case BLE_NUS_C_EVT_DISCONNECTED: NRF_LOG_INFO(连接断开); scan_start(); // 重新开始扫描 break; } }2.2 数据发送机制主机通过ble_nus_c_string_send函数向从机发送数据。按键触发是最常见的场景void bsp_event_handler(bsp_event_t event) { uint8_t data_array[1]; uint32_t ret_val; switch (event) { case 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: data_array[0] 0x04; ret_val ble_nus_c_string_send(m_ble_nus_c, data_array, 1); break; // 其他按键处理... } }3. 定时器集成与周期性通信许多应用需要定期发送心跳包或采集数据。nRF5 SDK的app_timer模块提供了轻量级的定时器解决方案。3.1 定时器初始化#define SEND_INTERVAL APP_TIMER_TICKS(2000) // 2秒间隔 APP_TIMER_DEF(m_send_timer_id); // 定时器实例 static void timer_init(void) { ret_code_t err_code app_timer_init(); APP_ERROR_CHECK(err_code); // 创建重复定时器 err_code app_timer_create(m_send_timer_id, APP_TIMER_MODE_REPEATED, timer_handler); APP_ERROR_CHECK(err_code); }3.2 定时器控制通过按键开启/关闭定时发送功能static bool timer_active false; void bsp_event_handler(bsp_event_t event) { // ...其他按键处理 case BSP_EVENT_KEY_3: if(!timer_active) { application_timers_start(); timer_active true; } else { application_timers_stop(); timer_active false; } break; } static void application_timers_start(void) { ret_code_t err_code; err_code app_timer_start(m_send_timer_id, SEND_INTERVAL, NULL); APP_ERROR_CHECK(err_code); }3.3 定时器回调实现static void timer_handler(void * p_context) { UNUSED_PARAMETER(p_context); static uint8_t counter 0; uint8_t data_array[1]; data_array[0] 0x02; // 定时发送的数据 ble_nus_c_string_send(m_ble_nus_c, data_array, 1); counter; if(counter 10) counter 0; // 防止溢出 }4. 常见问题与调试技巧在开发过程中经常会遇到各种错误和异常情况。以下是几个典型问题及其解决方案4.1 NRF_ERROR_INVALID_STATE错误这个错误通常发生在尝试操作尚未准备好的蓝牙连接时。常见原因包括服务发现未完成就尝试发送数据连接已断开但未正确更新状态CCCD(Client Characteristic Configuration Descriptor)未正确配置解决方案uint32_t ble_nus_c_tx_notif_enable(ble_nus_c_t * 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); }4.2 数据收发不稳定的处理增加重试机制对于关键数据实现简单的重发逻辑添加数据校验在应用层实现简单的校验和或CRC检查优化MTU大小通过sd_ble_gattc_exchange_mtu_request协商更大的MTU4.3 功耗优化建议在不需通信时降低广播间隔合理设置连接参数(conn_params)使用sd_app_evt_wait进入低功耗模式动态调整射频输出功率// 设置蓝牙发射功率 ret_code_t err_code sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 4); // 4dBm APP_ERROR_CHECK(err_code);5. 项目扩展与进阶功能基础功能实现后可以考虑添加更多实用功能提升系统价值5.1 多从机连接管理nRF52832支持同时连接多个从机设备。需要维护多个连接句柄和服务实例#define MAX_CONNECTIONS 3 typedef struct { ble_nus_c_t nus_instance; uint16_t conn_handle; bool connected; } ble_connection_t; ble_connection_t m_connections[MAX_CONNECTIONS]; // 在连接事件中分配实例 static void on_connect(ble_evt_t const * p_ble_evt) { for(int i0; iMAX_CONNECTIONS; i) { if(!m_connections[i].connected) { m_connections[i].conn_handle p_ble_evt-evt.gap_evt.conn_handle; m_connections[i].connected true; // 初始化NUS客户端实例 ble_nus_c_init_t nus_init {0}; nus_init.evt_handler nus_evt_handler; ble_nus_c_init(m_connections[i].nus_instance, nus_init); break; } } }5.2 数据加密与安全对于敏感数据应启用蓝牙配对和加密static void ble_stack_init(void) { ble_gap_conn_params_t gap_conn_params; ble_gap_conn_sec_mode_t sec_mode; BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(sec_mode); err_code sd_ble_gap_device_name_set(sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME)); APP_ERROR_CHECK(err_code); // 设置配对参数 ble_opt_t opt; opt.gap_opt.passkey.p_passkey 123456; // 静态配对码 err_code sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, opt); APP_ERROR_CHECK(err_code); }5.3 OTA固件更新通过蓝牙实现固件更新可以极大提升产品维护效率。nRF5 SDK提供了DFU(Device Firmware Update)功能实现Bootloader引导程序使用nrfutil工具生成DFU包通过NUS服务传输固件数据校验完成后跳转到新固件// DFU数据传输示例 static void handle_dfu_data(uint8_t * p_data, uint16_t length) { static uint32_t dfu_offset 0; // 写入Flash ret_code_t err_code nrf_dfu_flash_store(dfu_offset, p_data, length); if(err_code NRF_SUCCESS) { dfu_offset length; send_dfu_progress(dfu_offset); } else { send_dfu_error(err_code); } }在实际项目中蓝牙主机的开发远不止于代码实现。合理的状态机设计、完善的错误处理和用户反馈机制同样重要。通过按键LED指示当前状态、使用蜂鸣器提示重要事件、记录运行日志到Flash等辅助功能都能显著提升产品的用户体验和可靠性。
nRF52832蓝牙主机实战:用Nordic SDK实现按键控制从机与定时发送(附完整代码)
发布时间:2026/6/5 3:46:12
nRF52832蓝牙主机实战用Nordic SDK实现按键控制从机与定时发送在物联网设备开发中蓝牙主机(Central)与从机(Peripheral)的交互是最常见的应用场景之一。nRF52832作为Nordic Semiconductor的明星产品凭借其低功耗特性和强大的蓝牙5.0支持成为许多嵌入式工程师的首选。本文将基于nRF5 SDK深入讲解如何构建一个功能完整的蓝牙主机系统实现按键触发数据发送、接收从机数据并打印以及通过定时器实现周期性通信。1. 项目架构与核心组件一个典型的蓝牙主机系统需要处理多个关键任务设备发现与连接扫描并识别支持NUS(Nordic UART Service)的从机设备数据收发通过GATT协议实现双向通信用户交互通过物理按键触发特定操作定时任务周期性执行特定功能nRF5 SDK为这些功能提供了完善的API支持主要涉及以下模块模块功能关键APIBLE Stack蓝牙协议栈基础sd_ble_*系列函数NUS ClientUART服务客户端ble_nus_c_*系列函数BSP板级支持包bsp_init,bsp_event_handlerApp Timer应用定时器app_timer_create,app_timer_start硬件初始化是项目的第一步需要正确配置GPIO、定时器和蓝牙协议栈static void buttons_leds_init(void) { ret_code_t err_code; bsp_event_t startup_event; // 初始化LED和按键注册事件回调 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); }2. NUS服务实现与数据交互Nordic UART Service(NUS)是模拟串口通信的蓝牙服务极大简化了数据传输实现。主机端需要完成服务发现、特性配置和数据收发三个关键步骤。2.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(发现服务完成); // 分配连接句柄 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); // 启用从机通知 err_code ble_nus_c_tx_notif_enable(p_ble_nus_c); APP_ERROR_CHECK(err_code); break; case BLE_NUS_C_EVT_NUS_TX_EVT: // 处理接收到的数据 break; case BLE_NUS_C_EVT_DISCONNECTED: NRF_LOG_INFO(连接断开); scan_start(); // 重新开始扫描 break; } }2.2 数据发送机制主机通过ble_nus_c_string_send函数向从机发送数据。按键触发是最常见的场景void bsp_event_handler(bsp_event_t event) { uint8_t data_array[1]; uint32_t ret_val; switch (event) { case 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: data_array[0] 0x04; ret_val ble_nus_c_string_send(m_ble_nus_c, data_array, 1); break; // 其他按键处理... } }3. 定时器集成与周期性通信许多应用需要定期发送心跳包或采集数据。nRF5 SDK的app_timer模块提供了轻量级的定时器解决方案。3.1 定时器初始化#define SEND_INTERVAL APP_TIMER_TICKS(2000) // 2秒间隔 APP_TIMER_DEF(m_send_timer_id); // 定时器实例 static void timer_init(void) { ret_code_t err_code app_timer_init(); APP_ERROR_CHECK(err_code); // 创建重复定时器 err_code app_timer_create(m_send_timer_id, APP_TIMER_MODE_REPEATED, timer_handler); APP_ERROR_CHECK(err_code); }3.2 定时器控制通过按键开启/关闭定时发送功能static bool timer_active false; void bsp_event_handler(bsp_event_t event) { // ...其他按键处理 case BSP_EVENT_KEY_3: if(!timer_active) { application_timers_start(); timer_active true; } else { application_timers_stop(); timer_active false; } break; } static void application_timers_start(void) { ret_code_t err_code; err_code app_timer_start(m_send_timer_id, SEND_INTERVAL, NULL); APP_ERROR_CHECK(err_code); }3.3 定时器回调实现static void timer_handler(void * p_context) { UNUSED_PARAMETER(p_context); static uint8_t counter 0; uint8_t data_array[1]; data_array[0] 0x02; // 定时发送的数据 ble_nus_c_string_send(m_ble_nus_c, data_array, 1); counter; if(counter 10) counter 0; // 防止溢出 }4. 常见问题与调试技巧在开发过程中经常会遇到各种错误和异常情况。以下是几个典型问题及其解决方案4.1 NRF_ERROR_INVALID_STATE错误这个错误通常发生在尝试操作尚未准备好的蓝牙连接时。常见原因包括服务发现未完成就尝试发送数据连接已断开但未正确更新状态CCCD(Client Characteristic Configuration Descriptor)未正确配置解决方案uint32_t ble_nus_c_tx_notif_enable(ble_nus_c_t * 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); }4.2 数据收发不稳定的处理增加重试机制对于关键数据实现简单的重发逻辑添加数据校验在应用层实现简单的校验和或CRC检查优化MTU大小通过sd_ble_gattc_exchange_mtu_request协商更大的MTU4.3 功耗优化建议在不需通信时降低广播间隔合理设置连接参数(conn_params)使用sd_app_evt_wait进入低功耗模式动态调整射频输出功率// 设置蓝牙发射功率 ret_code_t err_code sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 4); // 4dBm APP_ERROR_CHECK(err_code);5. 项目扩展与进阶功能基础功能实现后可以考虑添加更多实用功能提升系统价值5.1 多从机连接管理nRF52832支持同时连接多个从机设备。需要维护多个连接句柄和服务实例#define MAX_CONNECTIONS 3 typedef struct { ble_nus_c_t nus_instance; uint16_t conn_handle; bool connected; } ble_connection_t; ble_connection_t m_connections[MAX_CONNECTIONS]; // 在连接事件中分配实例 static void on_connect(ble_evt_t const * p_ble_evt) { for(int i0; iMAX_CONNECTIONS; i) { if(!m_connections[i].connected) { m_connections[i].conn_handle p_ble_evt-evt.gap_evt.conn_handle; m_connections[i].connected true; // 初始化NUS客户端实例 ble_nus_c_init_t nus_init {0}; nus_init.evt_handler nus_evt_handler; ble_nus_c_init(m_connections[i].nus_instance, nus_init); break; } } }5.2 数据加密与安全对于敏感数据应启用蓝牙配对和加密static void ble_stack_init(void) { ble_gap_conn_params_t gap_conn_params; ble_gap_conn_sec_mode_t sec_mode; BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(sec_mode); err_code sd_ble_gap_device_name_set(sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME)); APP_ERROR_CHECK(err_code); // 设置配对参数 ble_opt_t opt; opt.gap_opt.passkey.p_passkey 123456; // 静态配对码 err_code sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, opt); APP_ERROR_CHECK(err_code); }5.3 OTA固件更新通过蓝牙实现固件更新可以极大提升产品维护效率。nRF5 SDK提供了DFU(Device Firmware Update)功能实现Bootloader引导程序使用nrfutil工具生成DFU包通过NUS服务传输固件数据校验完成后跳转到新固件// DFU数据传输示例 static void handle_dfu_data(uint8_t * p_data, uint16_t length) { static uint32_t dfu_offset 0; // 写入Flash ret_code_t err_code nrf_dfu_flash_store(dfu_offset, p_data, length); if(err_code NRF_SUCCESS) { dfu_offset length; send_dfu_progress(dfu_offset); } else { send_dfu_error(err_code); } }在实际项目中蓝牙主机的开发远不止于代码实现。合理的状态机设计、完善的错误处理和用户反馈机制同样重要。通过按键LED指示当前状态、使用蜂鸣器提示重要事件、记录运行日志到Flash等辅助功能都能显著提升产品的用户体验和可靠性。