开发你的第一个BLE应用程序—Blinky


本文将基于Nordic nRF5 SDK开发我们的第一个BLE应用程序——Blinky(类似跑马灯小程序),哪怕你之前没有任何BLE开发经验,也不用担心,只要跟着文中所述步骤,你就可以一步步搭建自己的第一个BLE应用程序。通过这个Blinky程序的搭建,你将体会到BLE的一些基本概念,对BLE将会有一个非常直观的认识,为后续自己的BLE应用程序开发打下一个坚实的基础。

如果你已经有一些BLE应用开发经验,只是对Nordic产品开发不熟,那么建议你可以直接去阅读下一篇文章:

下文所述的代码工程我已经将其上传到百度云盘中,有需要的同学可以到如下链接下载:

  • 链接: https://pan.baidu.com/s/1FKTfY3Q_zBVvviO7KC7Gyg#list/path=%2Fblog   密码: y8fb

1. 开发准备

1)      nRF52或者nRF51开发板1块。请参考“Nordic nRF51/nRF52开发流程说明”,购买相应开发板(DK)。

2)      开发环境搭建。简述如下(详细说明请参考“Nordic nRF51/nRF52开发环境搭建”):

  1. 安装Keil5 MDK
  2. 安装SDK。SDK下载链接:https://www.nordicsemi.com/Software-and-Tools/Software/nRF5-SDK/Download#infotabs。如果你开发的是nRF52系列,请选择最新版SDK(当前是SDK15.3.0);如果你开发的是nRF51系列,请下载nRF5 SDK12.3.0(nRF51最高SDK版本只能到12.3.0,后续SDK就不再支持nRF51
  3. 安装ARM CMSIS4.5.0,下载链接:https://github.com/ARM-software/CMSIS/releases/download/v4.5.0/ARM.CMSIS.4.5.0.pack。
  4. 安装Keil5 Device Family Pack,下载链接:https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-MDK/Download#infotabs,请选择“Pack,3-clause BSD license”
  5. 安装nRF5 Command Line Tools,下载链接(Windows版):https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF5-Command-Line-Tools/Download#infotabs。
  6. 安装安卓版或者iOS版nRF connect。iOS版nRF connect请到苹果app store下载,搜索“nRF”即可以找到。安卓版nRF connect可以到Nordic Github官网上下载,下载链接为:https://github.com/NordicSemiconductor/Android-nRF-Connect/releases
  7. 安装PC版nRF connect或者nRFgo studio。PC版nRF connect下载链接(Windows版):https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Connect-for-desktop/Download#infotabs。

注:如果你使用的是Linux系统/Mac系统,或者你使用的不是Keil5-MDK,请参考“Nordic nRF51/nRF52开发环境搭建”来搭建你的开发环境。

2. 运行Blinky程序

请按照如下步骤运行SDK自带的Blinky程序

1)      确认自己的芯片型号或者开发板。如果采用Nordic官方开发板的话,芯片型号和开发板编号对应关系如下:

  • nRF52832和nRF52810对应开发板编号为PCA10040。虽然52832和52810共用同一块开发板,但是他们在SDK中的项目编号是不一样的,52832对应PCA10040目录,52810对应PCA10040e目录,由于52810和52832 PIN to PIN兼容,软件也是完全兼容的,因此SDK很多项目只有PCA10040的目录,而没有PCA10040e目录,此时需要你自己来建立PCA10040e对应的目录和工程,具体说明可参考:https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.3.0%2Fnrf52810_user_guide.html。
  • nRF52840和nRF52811对应开发板编号为PCA10056。虽然52840和52811共用同一块开发板,但是他们在SDK中的项目编号是不一样的,52840对应PCA10056目录,52811对应PCA10056e目录,由于52811和52840 PIN to PIN兼容,软件也是完全兼容的,因此SDK很多项目只有PCA10056的目录,而没有PCA10056e目录,此时需要你自己来建立PCA10056e对应的目录和工程,具体说明可参考:https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.3.0%2Fnrf52811_user_guide.html&cp=5_1_5_1。
  • nRF52840 dongle编号为PCA10059
  • nRF51系列对应开发板编号为PCA10028

这里我会以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程序。

  • 蓝牙协议栈下载(整个开发周期只下载一次)。在Keil ‘select target’下拉列表中,默认选择的是Keil工程对应的Target,即‘nrf52832_xxaa’。但我们还可以选择另一个target ‘flash_s132_nrf52_6.1.1_softdevice’,即softdevice对应的target,然后点击“下载download”(不需要编译哦!),此时会把softdevice下载到开发板中。

 

  • 应用下载。重新选择Target:‘nrf52832_xxaa’,点击“下载Download”,此时会把Blinky程序下载到开发板中。此时开发板的LED1常亮,表示程序运行正常。

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又将熄灭。

 

3. 修改Blinky程序

3.1 修改广播名称

如前所述,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的搜索功能非常好用,很多问题都可以通过它来解决

3.2 修改广播间隔

如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了:

 

3.3 修改Button characteristic行为 (设备发数据给手机)

现在的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(&params, 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, &params);

}

我们可以仿照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(&params, 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, &params);      

}

重新编译下载blinky程序,连接设备,使能cccd,按下Button2,你会看到Button characteristic的值变为5:

 

3.4 修改LED characteristic行为(手机发数据给设备)

由于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操作设备的过程和原理。

上述代码工程我已经将其上传到百度云盘中,有需要的同学可以到如下链接下载:

  • 链接: https://pan.baidu.com/s/1FKTfY3Q_zBVvviO7KC7Gyg#list/path=%2Fblog   密码: y8fb

下载“tutorial_ble_app_blinky_SDK15_3_0.rar”,然后解压缩到SDK15.3.0如下目录下:nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\

即可成功编译。

4. 开发手机端Blinky程序

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操作界面如下所示:

 

  • nRF Blinky Android版源代码及开发说明请参考:https://github.com/NordicSemiconductor/Android-nRF-Blinky
  • nRF Blinky iOS版源代码及开发说明请参考:https://github.com/NordicSemiconductor/iOS-nRF-Blinky

这篇文章主要让大家对BLE有个大概的认识,后面我会专门写一篇文章来介绍上面提到的BLE service,characteristic,write,notify以及CCCD等,以帮助大家深刻理解这些概念,感兴趣的读者请参考:

手把手教你开发BLE数据透传应用程序

相关