聚丰项目 > 基于AB32VG1的蓝牙通信小程序

基于AB32VG1的蓝牙通信小程序

蓝讯骄龙 AB32VG1 是中科蓝讯在 2020 RT-Thread 开发者大会上首度面向通用市场发布的其自主 RISC-V 内核 32 位 MCU 芯片,具有丰富的软硬件资源和低成本优势,微信小程序作为微信公众号和app不足之处的补充,能够提供更好的用户体验,多元性的曝光,在餐饮行业,服务行业,电子商务行业等领域有显著的用户流量。本设计意在为AB32开发板提供更多的应用方向。

寻游记 寻游记

分享
0 喜欢这个项目
团队介绍

寻游记 寻游记

团队成员

寻游记 嵌入式软件

分享
项目简介
蓝讯骄龙 AB32VG1 是中科蓝讯在 2020 RT-Thread 开发者大会上首度面向通用市场发布的其自主 RISC-V 内核 32 位 MCU 芯片,具有丰富的软硬件资源和低成本优势,微信小程序作为微信公众号和app不足之处的补充,能够提供更好的用户体验,多元性的曝光,在餐饮行业,服务行业,电子商务行业等领域有显著的用户流量。本设计意在为AB32开发板提供更多的应用方向。
硬件说明

RT-Thread使用情况概述:

整个方案涉及的技术有:rtt模块化任务代码设计,BLE-GATT,微信小程序软件开发(wxml+wxss+js);

内核部分:使用了线程、信号量、定时器 、PWM

设备驱动:

GPIO/PWM/BLE 等


PC端将程序烧录到MCU,通过downloader调用ble命令开始开发板蓝牙广播,使其处于可被发现状态;小程序端打开蓝牙搜索,找到开发板,获取Notify/read/write信息;


这里着重说一下BLE-GATT。

现在低功耗蓝牙(BLE)连接都是建立在 GATT (Generic Attribute Profile) 协议之上。GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。

详细介绍 GATT 之前,需要了解 GAP(Generic Access Profile),它在用来控制设备连接和广播。GAP 使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。例如 Beacon 设备就只是向外广播,不支持连接,小米手环就等设备就可以与中心设备连接。

GAP 给设备定义了若干角色,其中主要的两个是:外围设备(Peripheral)和中心设备(Central)。

外围设备:这一般是简单的低功耗设备,用来提供数据,并连接到一个更加相对强大的中心设备。这里指我们的开发板。
中心设备:中心设备相对比较强大,用来连接其他外围设备。这里指手机端。

GAP 的广播工作流程如下图所示cen-per.png

    

GATT通信的双方是C/S关系。外设作为GATT服务端(Server),它维持了ATT的查找表以及service和characteristic的定义。中心设备是GATT客户端(Client),他向Server发起请求。需要注意的是,所有的通信事件,都是由客户端(也叫主设备,Master)发起,并且接收服务端(也叫从设备,Slava)的响应。而GATT事务是建立在嵌套的Profiles,Services和Characteristics之上的,如下如所示:

GATT结构.png


Profile :

Profile并不是实际存在于BLE外设上的,它只是一个被Bluetooth SIG或者外设设计者预先定义的Service的集合。例如我们的例程心率Profile(Heart Rate Profile)就是结合了Heart Rate Service和Device Information Sercvice。所有官方通过GATT Profile的列表可以从这里找到。

这里给出官网的列表Assigned Numbers


Service:

Service是把数据分成一个个的独立逻辑项,它包含一个或者多个Characteristic。每个Service有一个UUID唯一标识。UUID有16bit的,或者128bit的。16bit的UUID是官方通过认证的,需要花钱购买,128bit是自定义的,这个就可以自己随便设置。
官方通过了一些标准Service,完整列表在这里。以Heart Rate Service为例,可以看到它的官方通过16bitUUID是0x180D,包含3个Characteristic:Heart Rate Measurement,Body Sensor Location和Heart Control Point,并且定义了只有一个第一个必须的,它是可选实现的。

Characteristic :

Characteristic 在GATT事务中的最低界别的是Characteristic,Characteristic是最小的逻辑数据单元,当然它可能包含一个组关联的数据,例如加速度计的X/Y/Z三轴值。与Service类似,每个Characteristic用16bit或者128bit的UUID唯一标识。你可以免费使用Bluetooth SIG官方定义的标准Characteristic,使用官方定义的,可以确保BLE的软件和硬件能相互理解。当然,你可以自定义Characteristic,这样的话,就只有你自己的软件和外设能够相互理解。
举个例子,Heart Rate Measurement Characteristic,这是上面提到的Heart Rate Service必需实现的Characteristic,它的UUID是0x2A37。它的数据结构是,开始8bit定义心率数据格式(是UINT8还是UINT16?),接下来就是对应格式的实际心率数据。
实际上,和BLE外设打交道,主要是通过Characteristic。你可以从Characteristic读取数据,也可以往Characteristic写数据。这样就实现了双向的通信。所以你可以自己实现一个类似串口(UART)的service,这个Service中包含两个Characteristic,一个被配置只读的通道(RX),另一个配置为只写的通道(TX)。


软件说明

软件部分设计逻辑还是十分明确的;

开发板:打开ble,开始广播等待被发现。建立通信后,按照主机端的请求发送相应数据;

小程序:打开蓝牙适配器,搜索周围蓝牙,获取搜索过程中所搜索到的设备信息,连接想要连接的设备,获取服务、特征值,进行数据交互;


贴上代码:

在进行完基本初始化之后,进行对BLE的设置:

    //设置回调函数

    ble_hs_cfg.sync_cb = blehr_on_sync;

    //初始化定时器

    ble_npl_callout_init(&blehr_tx_timer, nimble_port_get_dflt_eventq(),

                    blehr_tx_hrate, NULL);

    ble_npl_callout_init(&my_notify_tx_timer,nimble_port_get_dflt_eventq(),

                    my_notify_tx,NULL);

    //初始化gatt服务

    rc = gatt_svr_init();

    assert(rc == 0);

    //设备默认名字

    //这里可以更改设备名字

    rc = ble_svc_gap_device_name_set(device_name);


我在DEVICE_INFO Service里添加了String(0x2A3D)0X5A5D 的自定义characteristic;

属性分别为READ和NOTIFY

{

            /*Characteristic:Model number string */

            .uuid=BLE_UUID16_DECLARE(GATT_STRING_UUID),

            .access_cb=gatt_svr_chr_access_device_info,

            .flags=BLE_GATT_CHR_F_READ,

        },

 

        {

            /*Characteristic:Model number string */

            .uuid=BLE_UUID16_DECLARE(GATT_RIGHT_UUID),

            .access_cb=gatt_svr_chr_access_device_info,

            .val_handle = &my_notify_handle,

            .flags=BLE_GATT_CHR_F_NOTIFY,

        },

给2A3D的value为:"ab32vg1";

在blehr中写了(0x5A5D)notify的实现;

代码如下:


//my notify--

static void my_notify_stop(void)

{

    ble_npl_callout_stop(&my_notify_tx_timer);

}


//重置

static void my_notify_reset(void)

{

    int rc;


    rc = ble_npl_callout_reset(&my_notify_tx_timer, RT_TICK_PER_SECOND);

    assert(rc == 0);

}



static void my_notify_tx(struct ble_npl_event* ev2)

{

    static char my_notify_hrm[2];

    int rc;

    struct os_mbuf *om;


    if (!notify_state) {

        my_notify_stop();

        XXX = 15;

        return;

    }


    my_notify_hrm[0] = 0x02;   /* contact of a sensor *///传感器的联系

    my_notify_hrm[1] = XXX; /* storing dummy data *///储存虚拟数据

    /* Simulation of heart beats *///模拟心跳

        XXX++;

    if (XXX == 50) {

        XXX = 90;

    }



    om = ble_hs_mbuf_from_flat(my_notify_hrm, sizeof(my_notify_hrm));


    rc = ble_gattc_notify_custom(my_notify_conn_handle, my_notify_handle, om);


    assert(rc == 0);

    my_notify_reset();

}


在blehr的gap事件函数中设置notify事件:

    case BLE_GAP_EVENT_SUBSCRIBE:

        //对等方订阅状态的状态更改1?2:3

        MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; "

                          ,"val_handle2=%d\n",

                    event->subscribe.cur_notify, my_notify_handle);

        if (event->subscribe.attr_handle !=0) {

            if (event->subscribe.attr_handle==my_notify_handle)

            {

                notify_state = event->subscribe.cur_notify;

                my_notify_conn_handle = event->subscribe.conn_handle;

                my_notify_reset();

            }

                       

            } else if (event->subscribe.attr_handle != (&my_notify_handle)) {

                notify_state = event->subscribe.cur_notify;

                notify_conn_handle = 0;

                my_notify_conn_handle=0;

                my_notify_stop();

        }

        break;


最后进行定时器的初始化:

        ble_npl_callout_init(&my_notify_tx_timer,nimble_port_get_dflt_eventq(),my_notify_tx,NULL);


现在进行测试:

img_20211212175540.jpg


img_20211212174808.jpg

search.jpg

img_20211212174808.jpg




开发板程序运行正常!



现在展示小程序的代码:

wxml:


<view class="content">

  <text>\ntext>

  <view class="p2bindtap="getBluetoothDevices">

    <text style="margin-left:36px">可用设备text>

  view>

  <block wx:if="{{show_available_devices_switch === 0}}">

    <block wx:for="{{devices}}">

      <view>

        <text style="margin-left:36px"  class="p3id="{{item.deviceId}}" data-device-name="{{item.name}}" bindtap="connectTO">{{item.name}}text>

      view>

    block>

  block>

  <block wx:if="{{connected_device_switch === 0}}">

    <view>

      <text class="p3style="margin-left:36px">{{device_name}} 已连接text>

    view>

  block>

  <text>\n\n\ntext>



  <view>

    <text class="p2style="margin-left:36px">接收字符串 text>

  view>

  <view>

    <text class="p3style="margin-left:36px">{{cur_string}}《-text>

  view>

  <text>\ntext>

  

  <view>

    <text class="p2style="margin-left:36px">接收订阅text>

  view>

  <view>

    <text class="p3style="margin-left:36px">{{cur_notify}}《-text>

  view>

  <text>\ntext>


wxss:

page {

  color: #333;

}

.devices_summary {

  margin-top: 30px;

  padding: 10px;

  font-size: 16px;

}

.device_list {

  height: 300px;

  margin: 50px 5px;

  margin-top: 0;

  border: 1px solid #EEE;

  border-radius: 5px;

  width: auto;

}

.device_item {

  border-bottom: 1px solid #EEE;

  padding: 10px;

  color: #666;

}

.device_item_hover {

  background-color: rgba(0, 0, 0, .1);

}

.connected_info {

  position: fixed;

  bottom: 0;

  width: 100%;

  background-color: #F0F0F0;

  padding: 10px;

  padding-bottom: 20px;

  margin-bottom: env(safe-area-inset-bottom);

  font-size: 14px;

  min-height: 100px;

  box-shadow: 0px 0px 3px 0px;

}

.connected_info .operation {

  position: absolute;

  display: inline-block;

  right: 30px;

}

.p1 {

  position: relative;

  left: 36px;

}

.p2 {

  font-size: 1.3em;

  font-weight: 500;

  font-family: 'Times New Roman', Times, serif;

}

.p3 {

  font-weight: 400;

  color: rgb(171, 171, 171);

}

.p4 {

  position: relative;

  right: 36px;

}

.p5 {

  font-family: SimSun;

}


js后端:

   // 初始化蓝牙适配器

      this.initBluetoothAdapter();

      // 本机蓝牙适配器状态

      this.getBluetoothAdapterState();

      // 开始搜索外围设备

      this.startBluetoothDevicesDiscovery();


   /* 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。*/

   getBluetoothDevices: function() {

      var that = this;

      wx.getBluetoothDevices({

         success: function(res) {

            console.log('搜到的蓝牙设备数目:' + res.devices.length);

            console.log(res.devices);

            that.setData({

               devices: res.devices,

               /* 显示可用设备开关 */

               show_available_devices_switch: 0,

            });

         },

}

...

具体代码在我的评论区


演示效果

微信小程序端展示

img_20211212180921.jpg

img_20211212181006.jpg

img_20211212181005.jpg

img_20211212181007.jpg




代码地址:https://gitee.com/YYYYYao/ab32-vg1-ble.git


演示视频:


附件

(6.94 MB)下载

评论区(1 )
  • 寻游记: 这里是代码:https://gitee.com/YYYYYao/ab32-vg1-ble.git

    回复