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

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

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

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, , 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, );

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

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

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

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

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

开发你的第一个BLE应用程序—Blinky的更多相关文章

  1. 基于Unity的AR开发初探:第一个AR应用程序

    记得2014年曾经写过一个Unity3D的游戏开发初探系列,收获了很多好评和鼓励,不过自那之后再也没有用过Unity,因为没有相关的需求让我能用到.目前公司有一个App开发的需求,想要融合一下AR到A ...

  2. android 串口开发第一篇:搭建ndk开发环境以及第一个jni调用程序

    一:ndk环境搭建 1:开发环境 我使用的是android studio 2.3.3版本,搭建ndk开发环境比较简单,打开File----Settings----Appearance&Beha ...

  3. .Net开发之旅(一个年少轻狂的程序员的感慨)

    高端大气上档次.这次当时一个身为懵懂初中生的我对程序员这一职位的描述.那时虽不是随处都能看到黑客大军的波及,但至少是知道所谓的黑客爸爸的厉害,一言不合说被黑就被黑.对于懵懂的我那是一种向往.自己也曾想 ...

  4. Web程序员开发App系列 - 开发我的第一个App,源码下载

    Web程序员开发App系列 Web程序员开发App系列 - 认识HBuilder Web程序员开发App系列 - 申请苹果开发者账号 Web程序员开发App系列 - 调试Android和iOS手机代码 ...

  5. 李洪强iOS开发之【零基础学习iOS开发】【02-C语言】02-第一个C语言程序

    前言 前面已经唠叨了这么多理论知识,从这讲开始,就要通过接触代码来学习C语言的语法.学习任何一门语言,首先要掌握的肯定是语法.学习C语言语法的目的:就是能够利用C语言编写程序,然后运行程序跟硬件(计算 ...

  6. C语言入门(2)——安装VS2013开发环境并编写第一个C语言程序

    在C语言入门系列中,我们使用Visual studio 2013 Professional作为开发工具.本篇详细介绍如何安装Visualstudio 2013 Professional并写出我们第一个 ...

  7. Android For JNI(一)——JNI的概念以及C语言开发工具dev-c++,编写你的第一个C语言程序,使用C启动JAVA程序

    Android For JNI(一)--JNI的概念以及C语言开发工具dev-c++,编写你的第一个C语言程序 当你的Android之旅一步步的深入的时候,你其实会发现,很多东西都必须去和framew ...

  8. 如何快速地开发一个微信小程序

    如何快速地开发一个微信小程序呢?我觉得作为初学者,最好能有一个模板,然后改这个模板. 同样作为初学者,刚开始的时候我有下面的几个问题,后来通过问同学,我弄清楚了. 微信小程序可以连接MySQL或者Sq ...

  9. 如何用原生js开发一个Chrome扩展程序

    原文地址:How to Build a Simple Chrome Extension in Vanilla JavaScript 开发一个Chrome扩展程序非常简单,只需要使用原生的js就可以完成 ...

随机推荐

  1. 深入了解Map

    联系 Java中的Map类似于OC的Dictionary,都是一个个键值对组成,一键对应一值.我在之前的文章中讲解过Set,其实在JAVA底层Set依赖的也是Map,那我们都知道,Set是单列的(只有 ...

  2. Spring的事务 之 9.1 数据库事务概述 ——跟我学spring3

    9.1  数据库事务概述 事务首先是一系列操作组成的工作单元,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务. 事务必需满足ACID(原子性.一致性.隔离性和持久性 ...

  3. HTML DOM - 导航

    HTML DOM 节点列表长度 length 属性定义节点列表中节点的数量. 您可以使用 length 属性来循环节点列表: x=document.getElementsByTagName(" ...

  4. Spring3 MVC使用@ResponseBody的乱码问题及解决办法

    近日用Spring3的MVC写东西,深感其之于Webwork/Struts2的便利,但是在通过@ResponseBody这个annotation输出一个json字符串的时候,发现页面上获得的json字 ...

  5. The note of Developing Innovative Ideas for New Companies Course

    This course is free on the Coursera Site,But it only has English version Threee pieces of the course ...

  6. Android Data Binding使用笔记

    说在前面:先来三个文档,官网文档:https://developer.Android.com/topic/libraries/data-binding/index.html 官网文档的汉化版:http ...

  7. 如何将代码提交到git上

    http://blog.csdn.net/laozitianxia/article/details/50682100 这个博客介绍的很详细.

  8. BOM,DOM常见操作和DHML

    BOM (Browser Object Model)浏览器对象模型,控制浏览器的一些行为 window对象 代表一个HTML文档 属性 页面导航的5个属性 self, parent, top, ope ...

  9. js基础进阶--关于Array.prototype.slice.call(arguments) 的思考

    欢迎访问我的个人博客:http://www.xiaolongwu.cn Array.prototype.slice.call(arguments)的作用为:强制转化arguments为数组格式,一般出 ...

  10. MySQL中查询时"Lost connection to MySQL server during query"报错的解决方案

    一.问题描述: mysql数据库查询时,遇到下面的报错信息: 二.原因分析: dw_user 表数据量比较大,直接查询速度慢,容易"卡死",导致数据库自动连接超时.... 三.解决 ...