本文将基于Nordic nRF5 SDK开发咱们的第一个BLE应用程序——Blinky(相似跑马灯小程序),哪怕你以前没有任何BLE开发经验,也不用担忧,只要跟着文中所述步骤,你就能够一步步搭建本身的第一个BLE应用程序。经过这个Blinky程序的搭建,你将体会到BLE的一些基本概念,对BLE将会有一个很是直观的认识,为后续本身的BLE应用程序开发打下一个坚实的基础。html
若是你已经有一些BLE应用开发经验,只是对Nordic产品开发不熟,那么建议你能够直接去阅读下一篇文章:手把手教你开发BLE数据透传应用程序git
下文所述的代码工程我已经将其上传到百度云盘中,有须要的同窗能够到以下连接下载:github
1) nRF52或者nRF51开发板1块。请参考“Nordic nRF51/nRF52开发流程说明”,购买相应开发板(DK)。小程序
2) 开发环境搭建。简述以下(详细说明请参考“Nordic nRF51/nRF52开发环境搭建”):app
注:若是你使用的是Linux系统/Mac系统,或者你使用的不是Keil5-MDK,请参考“Nordic nRF51/nRF52开发环境搭建”来搭建你的开发环境。jsp
请按照以下步骤运行SDK自带的Blinky程序ide
1) 确认本身的芯片型号或者开发板。若是采用Nordic官方开发板的话,芯片型号和开发板编号对应关系以下:函数
这里我会以nRF52832开发板PCA10040为例阐述整个开发过程,其余开发板与之相似,你们本身能够触类旁通来开始本身的开发之旅。工具
2) 将开发板与PC机经过USB线相连,同时打开开发板电源(将左下角的拨位开关打到“ON”位置),打开桌面版nRF Connect,选择启动“Programmer”应用,因为驱动以前已经安装好了,设备能够当即识别成功。执行“Erase all”操做,以擦除芯片原始内容。测试
3) 打开SDK中的Blinky程序。对于blinky程序,若是是52832开发板,请打开:nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_blinky\pca10040\s132\arm5_no_packs;若是是51822开发板,请打开:nRF5_SDK_12.3.0_d7731ad\examples\ble_peripheral\experimental_ble_app_blinky\pca10028\s130\arm5_no_packs
后续将以52832开发板为例来阐述,51822与之相似就再也不阐述了。
注:Nordic SDK例程目录结构为:SDK版本/ examples /协议角色/例子名称/开发板型号/协议栈型号/工具链类型/具体工程,好比下面例子:
Nordic每个例子都支持5种工具链:Keil5/Keil4/IAR/GCC/SES,以下所示:
4) 编译上面的blinky程序。若是你已经按照以前的说明配置好了开发环境,那么这里编译是不会报任何错的。(若是你遇到了编译错误,请从新按照前面说明去搭建你的开发环境,不要怀疑SDK例子代码有问题哦)
5) 程序下载。程序下载包括2步:先下载softdevice,再下载应用。Softdevice是Nordic蓝牙协议栈的名称,整个开发过程当中只需下载一次。应用就是咱们这里的blinky程序。
6) 打开手机蓝牙和手机版nRF connect。在nRF connect中,你将看到一个广播设备:Nordic_Blinky,这个就是咱们的开发板。
7) 链接设备。点击“CONNECT”,手机将与设备创建链接,并开始服务发现过程,链接成功后,LED1熄灭,LED2点亮,最后将获得以下界面。
由上图可见,Blinky程序包含三个service:Generic Access(GAP),Generic Attribute(GATT),以及Nordic LED Button Service,GAP和GATT都是标准的蓝牙service,Nordic LED Button Service是Blinky程序自定义的service,它又具体包括两个characteristic:Button和LED。
8) 测试Blinky程序。点击右上角的“Enable CCCDs”以使能notification,以下:
按下开发板上的Button1按键,你会发现nRF connect中的Button characteristic Value会实时显示按键状态:pressed或者released。
点击nRF connect中的LED characteristic右边的向上箭头,选择“ON”并“SEND”,你会发现开发板的LED3将点亮;选择“OFF”并“SEND”,LED3又将熄灭。
如前所述,Blinky程序默认的广播名字是:Nordic_Blinky,咱们如今将其修改成“My_Blinky”,怎么作呢?咱们在Keil中全文搜索“Nordic_Blinky”,发现有以下宏定义:
#define DEVICE_NAME "Nordic_Blinky"
咱们只需将这里的Nordic_Blinky换成My_Blinky,即
#define DEVICE_NAME "My_Blinky"
就能够实现咱们的目标了。从新编译下载blinky程序,在nRF connect中,你将看到:
你再全文搜索一下“DEVICE_NAME”,你会发现广播名字实际上是经过以下API来完成修改的:
err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME));
注:Keil的搜索功能很是好用,不少问题均可以经过它来解决
如nRF connect界面所示,blinky程序的广播间隔大概为40ms左右,如今为了节省功耗,咱们将其改成200ms,经过搜索,咱们能够看到以下宏定义:
#define APP_ADV_INTERVAL 64 /**< The advertising interval (in units of 0.625 ms; this value corresponds to 40 ms). */
所以为了将广播间隔改成200ms,只需将APP_ADV_INTERVAL改为200/0.625 = 320,即
#define APP_ADV_INTERVAL 320
从新编译下载blinky程序,在nRF connect中,你将发现广播间隔已改成200ms了:
如今的Blinky程序,只有按下开发板的Button1时,nRF connect的Button characteristic 值才会更新。咱们如今新增一个功能:当按下开发板的Button 2时,让Button characteristic value更新为5(注: nRF connect把1当成按键按下,把0当成按键释放,为了更直观,咱们没有选择0或者1,而是随便选择一个值:5)。咱们先找到Button1按下的回调函数,以下所示:
case LEDBUTTON_BUTTON: NRF_LOG_INFO("Send button state change."); err_code = ble_lbs_on_button_change(m_conn_handle, &m_lbs, button_action); if (err_code != NRF_SUCCESS && err_code != BLE_ERROR_INVALID_CONN_HANDLE && err_code != NRF_ERROR_INVALID_STATE && err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) { APP_ERROR_CHECK(err_code); } break;
能够看出,咱们是经过ble_lbs_on_button_change来更新Button characteristic的值的,ble_lbs_on_button_change具体函数实现以下所示:
uint32_t ble_lbs_on_button_change(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t button_state) { ble_gatts_hvx_params_t params; uint16_t len = sizeof(button_state); memset(¶ms, 0, sizeof(params)); params.type = BLE_GATT_HVX_NOTIFICATION; params.handle = p_lbs->button_char_handles.value_handle; params.p_data = &button_state; params.p_len = &len; return sd_ble_gatts_hvx(conn_handle, ¶ms); }
咱们能够仿照Button1的作法,来添加Button2的代码。首先初始化app_button模块,让button2按下事件能够被button_event_handler捕获,以下:
static app_button_cfg_t buttons[] = { {LEDBUTTON_BUTTON, false, BUTTON_PULL, button_event_handler}, {BSP_BUTTON_1, false, BUTTON_PULL, button_event_handler} };
而后在button_event_handler调用ble_button2_send(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t val),以更新Button characteristic的值,代码以下所示:
case BSP_BUTTON_1: NRF_LOG_INFO("Button2 pressed."); ble_button2_send(m_conn_handle, &m_lbs, 5); break;
ble_button2_send(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t val)实现代码以下所示:
uint32_t ble_button2_send(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t val) { ble_gatts_hvx_params_t params; uint16_t len = sizeof(val); memset(¶ms, 0, sizeof(params)); params.type = BLE_GATT_HVX_NOTIFICATION; params.handle = p_lbs->button_char_handles.value_handle; //Button characteristic value handle params.p_data = &val; params.p_len = &len; return sd_ble_gatts_hvx(conn_handle, ¶ms); }
从新编译下载blinky程序,链接设备,使能cccd,按下Button2,你会看到Button characteristic的值变为5:
因为nRF connect把LED characteristic的值写死了,只能发送“ON”或者“OFF”,前面也测试过,发送“ON”以后,LED3将点亮,其实这里的“ON”,就是数值1。咱们如今增长一个新的功能:在收到“ON”命令后,把LED4 toggle一下。
这个实现起来比较简单,咱们只需在led_write_handler中添加bsp_board_led_invert(BSP_BOARD_LED_3),总体代码以下所示:
static void led_write_handler(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t led_state) { if (led_state) { bsp_board_led_on(LEDBUTTON_LED); NRF_LOG_INFO("Received LED ON!"); bsp_board_led_invert(BSP_BOARD_LED_3); } else { bsp_board_led_off(LEDBUTTON_LED); NRF_LOG_INFO("Received LED OFF!"); } }
从新编译下载blinky程序,链接设备,你会发现第一次发送“ON”,LED4点亮,第二次发送“ON”,LED4又将熄灭。
经过全文搜索“led_write_handler”,你会发现这个函数是被ble_lbs.c中的on_write调用,而on_write又被ble_lbs_on_ble_evt调用,而ble_lbs_on_ble_evt就是咱们的BLE事件回调函数,每当softdevice收到LED characteristic的写操做时,都会调用它,这就是经过nRF connect操做设备的过程和原理。
上述代码工程我已经将其上传到百度云盘中,有须要的同窗能够到以下连接下载:
下载“tutorial_ble_app_blinky_SDK15_3_0.rar”,而后解压缩到SDK15.3.0以下目录下:nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\
便可成功编译。
Nordic同时提供手机端的Blinky程序,该程序源代码彻底向用户开放,以帮助用户快速开发本身的第一个Android或者iOS BLE应用程序。用户能够先到app store下载iOS版nRF blinky,或者到Github下载Android版nRF blinky(下载连接:https://github.com/NordicSemiconductor/Android-nRF-Blinky/releases)。手机版的nRF Blinky跟前面介绍的Blinky固件是配合工做的,经过手机版nRF Blinky能够对前面的blinky固件进行操做。nRF Blinky app图标以下所示:
nRF Blinky操做界面以下所示:
这篇文章主要让你们对BLE有个大概的认识,后面我会专门写一篇文章来介绍上面提到的BLE service,characteristic,write,notify以及CCCD等,以帮助你们深入理解这些概念,感兴趣的读者请参考: