柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据

## 前言

一直在关注 RT-Thread 的柿饼派和 RW007 模块,听说最近新版的 RW007 模块支持 BLE 功能了,于是便向客服咨询在柿饼派上的  RW007 模块是否也支持 BLE 功能,客服回复说目前最新的柿饼派是使用新版本的 RW007 模块,也是可以通过更新 RW007 的固件和更新RW007 的驱动,来使用 RW007 的 BLE 功能的。前面已经分享过部分关于 RW007 模块如何更新的带 BLE 功能的方法,这里也再次分享一下在柿饼派上怎样更新 RW007 固件和更新 RW007 的驱动,并且测试 RW007 的 BLE 功能,仅以此贴来记录该过程。


 

### 步骤一:柿饼派更新 RW007 的固件

  在柿饼派上更新 RW007 的固件,可以参考官方提供的 RW007 通过 UDP_OTA 工具升级 RW007 固件的文档,进行操作。

#### 1. RW007 模块需要和 PC 电脑连在同一个局域网

#### 2. 打开升级工具配置升级

 

(1)双击运行 `udp_ota.exe`软件

柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据_第1张图片

 

#### 3. 升级完成

 

等待进度条完成,然后重启板子。

柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据_第2张图片

 

### 步骤二:柿饼派修改 SDK 更新 RW007 的驱动

  由于需要使用新版本的 RW007 模块的 BLE 功能,所以需要对应更新主机端柿饼派的 RW007 驱动,主要是替换`SDK`中`project\firmware\packages`目录下的`rw007-latest`目录先删除,然后拷贝提供的`rw007-v2.0.1`文件夹,即可完成替换更新RW007 的驱动。

柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据_第3张图片

 

 替换驱动文件后,通过 `env` 编译柿饼派的固件,然后通过`persimmon mod`工具更新柿饼派的固件,即可。

柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据_第4张图片

 

 

柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据_第5张图片

 

### 步骤三:柿饼派测试 RW007 的 BLE 功能

  当按照前面的步骤进行操作后,可以通过调试串口的 `msh`命令行进行调试。因为在`RW007`驱动包里面默认开启了相关的功能测试示例,可以在`MSH`命令行中通过`rw007_ble`查询。下面的内容来自于官方提供的 `RW007` BLE 功能使用说明操作。

 

```

msh />rw007_ble

[rw007_ble command]

 

rw007_ble help

rw007_ble init central/peripheral       Note: init ble mode

rw007_ble get_addr                      Note: get ble address

rw007_ble update_params                 Note: update connect parameters(no support)

rw007_ble scan                          Note: scan ble slave

rw007_ble stop_scan                     Note: stop scan

rw007_ble connect xx:xx:xx:xx:xx:xx     Note: use slave addr to connect

rw007_ble disconnect [conn_handle]      Note: disconnect slave

rw007_ble get_server                    Note: discover all server(no support)

rw007_ble get_char                      Note: discover all description(no support)

rw007_ble get_disc                      Note: discover all characteristic(no support)

rw007_ble mtu_exch                      Note: ble mtu exchange(no support)

rw007_ble notify                        Note: enable ble notify(no support)

rw007_ble write                         Note: ble write data(no support)

rw007_ble read                          Note: ble read data(no support)

rw007_ble notify_change [conn_handle] [char_value] [UUID]   Note: ble notify configure by uuid

rw007_ble write_uuid [conn_handle] [UUID] [data] Note: ble write data by uuid

rw007_ble read_uuid                              Note: ble read data by uuid(no support)

 

```

####  BLE 功能初始化

 

`BLE `功能初始化函数,使用 `BLE`功能必须调用。

 

- 1.`RW007`BLE 设备初始化为主机设备。

 

```

rw007_ble init central

```

 

测试示例

 

```

msh />rw007_ble init central

122 - ble_cmd_init

start ble central

msh />

 

```

 

- 2. `RW007`BLE 设备初始化为从机设备

     注意:目前不支持使用从机模式。

 

```

rw007_ble init peripheral

```

 

#### BLE 功能获取本机的BLE设备地址

 

由于BLE设备的地址分为`public`公共地址和`random`随机地址。

 

```

rw007_ble get_addr

```

 

测试示例

 

```

msh />rw007_ble get_addr

273 - ble_cmd_get_addr

msh />resp_type: 0, len: 12

RW007_BLE_RSP_TYPE_ADDR_GET

public_id_addr = 48:00:42:8c:47:c9

random_id_addr = 00:04:5c:43:00:00

 

```

 

#### BLE 功能扫描设备

 

目前`RW007`BLE 功能支持通过 `scan`命令,扫描周围的`BLE`设备。

 

- 1.执行扫描周围`BLE`设备

 

```

rw007_ble scan

```

 

测试示例:

 

```

msh />rw007_ble scan

176 - ble_cmd_scan

msh />resp_type: 1, len: 65

RW007_BLE_NTF_TYPE_DISCOVER

received advertisement; event_type=0 rssi=-60 addr_type=0 addr=4c:ed:fb:00:04:b1

resp_type: 1, len: 56


 

```

 

这里的 `addr`地址内容可以为后面连接设备使用,由于目前没有直接显示对应 BLE 设备的名称,需要先人工确定 BLE 设备的地址。

 

- 2.停止扫描周围`BLE`设备

  该指令用于 `RW007`模块正在执行`scan`扫描操作过程中,马上停止扫描的动作,可以执行此命令。

 

```

rw007_ble stop_scan

```

 

#### BLE 功能设备连接

 

目前 `RW007` BLE 功能设备支持通过 `BLE`设备的类`mac`地址来连接设备,该命令仅支持主机模式去连接从机设备的类`mac`地址,连接成功后会有对应的`conn_handle`值,这个`conn_handle`值在其他功能沿用。

 

```

rw007_ble connect xx:xx:xx:xx:xx:xx

```

 

测试示例

 

```

msh />rw007_ble connect 4c:ed:fb:00:04:b1

198 - ble_cmd_connect

str_addr: 4c:ed:fb:00:04:b1

mac addr: b1: 4: 0:fb:ed:4c

msh />resp_type: 1, len: 52

RW007_BLE_NTF_TYPE_CONNECT

type: 0, status: 0, conn_handle: 1

 

resp_type: 1, len: 44

RW007_BLE_NTF_TYPE_CONNECT_DESC

our_id_addr: c9:47:8c:42:00:48

peer_id_addr: b1:04:00:fb:ed:4c

 

```

 

#### BLE 功能设备断开连接

 

目前`RW007`BLE 功能设备支持主动断开从设备的连接。

 

```

rw007_ble disconnect [conn_handle]

```

 

其中 `conn_handle`与 `connect`连接时的需要一致。

 

```

msh />rw007_ble disconnect 1

341 - ble_cmd_disconnect

msh />resp_type: 1, len: 52

RW007_BLE_NTF_TYPE_DISCONN

 

```

 

#### BLE 功能写数据

 

目前`RW007`BLE 功能写数据,支持通过指定`UUID`来写对应的数据。

 

```

rw007_ble write_uuid [conn_handle] [UUID] [data]

```

 

指令说明:`[conn_handle]`为 `RW007`连接设备时生成的对应值,`[UUID]`为对应 `BLE`设备的特征参数,`[data]`为需要发送的数据。

 

测试示例:

 

```

msh />rw007_ble write_uuid 1 ffe1 Hello,RT-Thread....

402 - ble_cmd_gattc_write_by_uuid

write conn_hanle:1 uuid:ffe1 data:Hello,RT-Thread....

write rc:0

msh />

 

```

 

注意:目前已经支持通过 128 位的 UUID 进行写数据操作

 

测试示例:

 

```

msh />rw007_ble write_uuid 1 ebe0ccbe7a0a4b0c8a1a6ff2997da3a6 00

data: 00

write conn_hanle:1 uuid:ebe0ccbe7a0a4b0c8a1a6ff2997da3a6

write rc:0

```

 

说明:测试示例中的 `[conn_handle]`为 1,`[UUID]`为`ebe0ccbe7a0a4b0c8a1a6ff2997da3a6`,`[data]`为 `0x00`(这里是使用 hex 值)

 

#### BLE 功能更新notify 参数

 

目前 `RW007` BLE 功能支持修改接收 `notify`参数。

 

```

rw007_ble notify_change [conn_handle] [char_value] [UUID]

```

 

参数说明:

 

`[conn_handle]`为 `RW007`连接设备时生成的对应值

 

`[char_value]` 为配置参数,具体如下

 

```

0:disable indication¬ification

1:enable notification,disable indication

2:enable indication, disable notification

3:enable indication ¬ification

```

 

`[UUID]`为对应 `BLE`设备的特征参数值。

 

#### BLE 功能关于广播包类型的说明

 

目前 `RW007`  BLE 功能中可以关于广播包 `event`的类型可以分为下面几种。

 

```

/* Advertising report */

#define BLE_HCI_ADV_RPT_EVTYPE_ADV_IND      (0)

#define BLE_HCI_ADV_RPT_EVTYPE_DIR_IND      (1)

#define BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND     (2)

#define BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND  (3)

#define BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP     (4)

```

 

广播包结构如下:

 

```

广播包结构描述:

struct ble_gap_disc_desc {

    uint8_t event_type;

    uint8_t length_data;

    ble_addr_t addr;

    int8_t rssi;

    uint8_t *data;

 

    /***

     * LE direct advertising report fields; direct_addr is BLE_ADDR_ANY if

     * direct address fields are not present.

     */

    ble_addr_t direct_addr;

};

 

通过event_type区分出不同的广播包类型,所有类型分为如下几种:

/* Advertising report */

0 : 普通广播数据包

1 : 直接广播包 

2 : 扫描请求包 

3 : 不可连接广播指示

4 : 扫描响应数据包

```

 

因此,在设备的`scan`扫描回应中可以通过对应的`event_type`来判定是哪种广播包的类型。

 

在 `RW007`的驱动代码中,可以通过下面的`event_type`来判断是哪种类型的广播包,可以让用户自行实现对应功能。

 

```

case RW007_BLE_NTF_TYPE_DISC:

        {

            rt_kprintf("RW007_BLE_NTF_TYPE_DISCOVER\n");

            ....

            switch(event_type)

            {

                case BLE_HCI_ADV_RPT_EVTYPE_ADV_IND:

 

                    break;

                case BLE_HCI_ADV_RPT_EVTYPE_DIR_IND:

 

                    break;

                case BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND:

 

                    break;

                case BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND:

 

                    break;

                case BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP:

 

                    break;

            }

            ....

        }

```

 

测试示例:

 

- 1.初始化 `RW007`BLE 功能为主机模式。

 

```

msh /> rw007_ble init central

263 - ble_cmd_init

start ble central

msh />

 

```

 

- 2.执行`BLE`扫描指令

 

```

msh />rw007_ble scan 

318 - ble_cmd_scan

msh />resp_type: 1, len: 83

RW007_BLE_NTF_TYPE_DISCOVER

received advertisement; event_type=3 rssi=-56 addr_type=1 addr=41:ef:77:50:2b:29

mfg_data = 1e ff 06 00 01 09 20 02 5b 5d cd 33 a4 8c c7 c5 36 ac de 12 ab 17 87 89 e1 84 e9 19 ec c4 ad 

 

resp_type: 1, len: 83

RW007_BLE_NTF_TYPE_DISCOVER

received advertisement; event_type=3 rssi=-65 addr_type=1 addr=3c:2e:33:ea:8d:aa

mfg_data = 1e ff 06 00 01 09 20 02 28 4c cf 9b a7 f2 7d f6 c7 7e d9 54 ac dd 91 a6 ee 0d 22 43 e1 8f e3 

 

resp_type: 1, len: 83

RW007_BLE_NTF_TYPE_DISCOVER

received advertisement; event_type=3 rssi=-59 addr_type=1 addr=42:18:ab:80:5c:11

mfg_data = 1e ff 06 00 01 09 20 02 eb bf cf 15 97 aa 50 7f a4 57 25 d9 21 35 71 eb 6f aa a1 56 5c d5 e2 

 

```

 

- 3.观察`event_type=`可以确定是哪种广播包类型。

 

```

RW007_BLE_NTF_TYPE_DISCOVER

received advertisement; event_type=0 rssi=-64 addr_type=1 addr=61:63:46:c3:49:99

mfg_data = 02 01 1a 02 0a 0c 0b ff 4c 00 10 06 00 19 1d 6d 63 18 

 

resp_type: 1, len: 52

RW007_BLE_NTF_TYPE_DISCOVER

received advertisement; event_type=4 rssi=-64 addr_type=1 addr=61:63:46:c3:49:99

mfg_data = 

 

resp_type: 1, len: 73

RW007_BLE_NTF_TYPE_DISCOVER

received advertisement; event_type=0 rssi=-73 addr_type=0 addr=91:21:52:00:4e:b8

mfg_data = 14 ff 4c 00 07 0f 00 02 20 91 21 52 00 4e b8 f5 58 58 3c 39 00 

 

```

 

#### BLE 功能读数据

 

目前 `RW007`支持通过 `UUID` 读取数据的功能

 

```

rw007_ble read_uuid [conn_handle] [UUID]

```

 

参数说明:

 

`[conn_handle]`为 `RW007`连接设备时生成的对应值

 

`[UUID]`为对应 `BLE`设备的特征参数。

 

测试示例:

 

```

msh />rw007_ble read_uuid 1 2a24

read conn_hanle:1 uuid:2a24

read rc:0

msh />ble data input packet resp_type: 0, len: 18

RW007_BLE_RSP_TYPE_READ

connect:1 attr_handle:14 uuid: 24 2a  read data:

00000000: 4C 59 57 53 44 30 33 4D 4D 43 00                 LYWSD03MMC.

```

 

说明:这里的 `[conn_handle]`为 1,`[UUID]` 为 `2a24 `,读取到的数据为 BLE 设备的名称。

 

目前 RW007 模块已经支持读取 128 位的 UUID 的功能。

 

测试示例:

 

```

msh />rw007_ble read_uuid 1 ebe0ccbe7a0a4b0c8a1a6ff2997da3a6

read conn_hanle:1 uuid:ebe0ccbe7a0a4b0c8a1a6ff2997da3a6

read rc:0

msh />ble data input packet resp_type: 0, len: 22

RW007_BLE_RSP_TYPE_READ

connect:1 attr_handle:51 uuid: a6 a3 7d 99 f2 6f 1a 8a 0c 4b 0a 7a be cc e0 eb  read data:

00000000: 00                     

```

 

说明:这里的 `[conn_handle]`为 1,`[UUID]` 为128位的 `ebe0ccbe7a0a4b0c8a1a6ff2997da3a6 `,读取到的数据值为 `00`

 

### 步骤四:柿饼派读取米家蓝牙温湿度计数据显示在界面上

感谢能坚持看到这里的每一个你,下面将会通过添加编写部分功能代码和创建 UI 工程,实现柿饼派读取米家温湿度计数据显示在界面上的功能。接下来会贴一部分实现代码和代码说明,可能会有点乏味,但是尽可能把实现步骤描述得具体些,避免采坑。

 

 * 1. 添加测试文件,参考代码

 * 2. 编译固件,验证功能

 * 3. 创建 UI 工程,简单布局

 * 4. c-js 之间互通说明

 * 5. UI 工程修改,验证功能

 

#### 1.添加测试代码

在官方提供的 `SDK`中的`project\firmware\applications`目录下添加`ble_example.c`文件,然后参考`project\firmware\packages\rw007-v2.0.1\src\ble_cmd_rw007.c`文件,主要拷贝几个对应的函数。通过前面的可以了解到,需要(1)RW007 初始化为 BLE 主机模式 (2)RW007 连接米家蓝牙温湿度计2代设备 (3)RW007 通过 UUID 读取米家蓝牙温湿度计2代设备的数据 ,所以需要参考 BLE功能初始化、连接、通过 UUID 读取数据的功能实现函数。

 

(1)在`ble_example.c`中添加 RW007 初始化为BLE主机模式的函数

 

```c

static int ble_example_init(void)

{

    rt_kprintf("ble_example_init\n");

    rt_uint8_t roles = 0;

    roles = RW007_BLE_INIT_ROLE_CENTRAL;

 

    rw007_ble_init(roles);

 

    rw007_ble_resp_handle_cb_reg(rw007_ble_resp_handle);

    rw007_ble_ntf_handle_cb_reg(rw007_ble_ntf_handle);

    return 0;

 

}

```

 

由于在初始化函数中,需要注册两个回调函数`rw007_ble_resp_handle` 和 `rw007_ble_ntf_handle`,所以也要从`project\firmware\packages\rw007-v2.0.1\src\ble_cmd_rw007.c`文件中对应拷贝过来。

 

```c

static void rw007_ble_resp_handle(rt_uint16_t resp_type, void *data, rt_uint16_t size)

{

    switch(resp_type)

    {

        case RW007_BLE_RSP_TYPE_INIT:

            rt_kprintf("RW007_BLE_RSP_TYPE_INIT\n");

            break;

        case RW007_BLE_RSP_TYPE_ADDR_GET:

            rt_kprintf("RW007_BLE_RSP_TYPE_ADDR_GET\n");

            ble_get_addr *get_addr = (ble_get_addr *)data;

            rt_kprintf("public_id_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", get_addr->public_id_addr[5], get_addr->public_id_addr[4],

                                                                           get_addr->public_id_addr[3], get_addr->public_id_addr[2],

                                                                           get_addr->public_id_addr[1], get_addr->public_id_addr[0]);

            rt_kprintf("random_id_addr = %02x:%02x:%02x:%02x:%02x:%02x\n", get_addr->random_id_addr[5], get_addr->random_id_addr[4],

                                                                           get_addr->random_id_addr[3], get_addr->random_id_addr[2],

                                                                           get_addr->random_id_addr[1], get_addr->random_id_addr[0]);

            break;

        case RW007_BLE_RSP_TYPE_CONN_INFO_GET:

            rt_kprintf("RW007_BLE_RSP_TYPE_CONN_INFO_GET\n");

            break;

        case RW007_BLE_RSP_TYPE_CONN_UPD_PARAMS:

            rt_kprintf("RW007_BLE_RSP_TYPE_CONN_UPD_PARAMS\n");

            break;

        case RW007_BLE_RSP_TYPE_SCAN:

            rt_kprintf("RW007_BLE_RSP_TYPE_SCAN\n");

            break;

        case RW007_BLE_RSP_TYPE_CONNECT:

            rt_kprintf("RW007_BLE_RSP_TYPE_CONNECT\n");

            break;

        case RW007_BLE_RSP_TYPE_DISCONN:

            rt_kprintf("RW007_BLE_RSP_TYPE_DISCONN\n");

            break;

        case RW007_BLE_RSP_TYPE_GATT_DIS_FULL:

            rt_kprintf("RW007_BLE_RSP_TYPE_GATT_DIS_FULL\n");

            break;

        case RW007_BLE_RSP_TYPE_GATTC_SRV:

            rt_kprintf("RW007_BLE_RSP_TYPE_GATTC_SRV\n");

            break;

        case RW007_BLE_RSP_TYPE_GATTC_CHR:

            rt_kprintf("RW007_BLE_RSP_TYPE_GATTC_CHR\n");

            break;

        case RW007_BLE_RSP_TYPE_GATTC_DSC:

            rt_kprintf("RW007_BLE_RSP_TYPE_GATTC_DSC\n");

            break;

        case RW007_BLE_RSP_TYPE_MTU_EXCHANGE:

            rt_kprintf("RW007_BLE_RSP_TYPE_MTU_EXCHANGE\n");

            break;

        case RW007_BLE_RSP_TYPE_NOTIFY:

            rt_kprintf("RW007_BLE_RSP_TYPE_NOTIFY\n");

            break;

        case RW007_BLE_RSP_TYPE_WRITE:

            rt_kprintf("RW007_BLE_RSP_TYPE_WRITE\n");

            break;

        case RW007_BLE_RSP_TYPE_READ:

            rt_kprintf("RW007_BLE_RSP_TYPE_READ\n");

            break;

        default:

            rt_kprintf("error response\n");

            break;

    }

}

// recv slave notity handle

static void rw007_ble_ntf_handle(rt_uint16_t ntf_type, void *data, rt_uint16_t size)

{

    switch(ntf_type)

    {

        case RW007_BLE_NTF_TYPE_CONN_UPD:

            rt_kprintf("RW007_BLE_NTF_TYPE_CONN_UPD\n");

            break;

        case RW007_BLE_NTF_TYPE_CONN_UPD_PARAMS:

            rt_kprintf("RW007_BLE_NTF_TYPE_CONN_UPD_PARAMS\n");

            break;

        case RW007_BLE_NTF_TYPE_DISC:

        {

            struct rw007_ble_gap_event_discov *disc_desc = RT_NULL;

            char *dicover_data = RT_NULL;

 

            rt_kprintf("RW007_BLE_NTF_TYPE_DISCOVER\n");

 

            disc_desc = (struct rw007_ble_gap_event_discov *)data;

            dicover_data = (char*)(disc_desc + 1);

 

            switch(disc_desc->discov_type)

            {

                case BLE_HCI_ADV_RPT_EVTYPE_ADV_IND:

 

                    break;

                case BLE_HCI_ADV_RPT_EVTYPE_DIR_IND:

 

                    break;

                case BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND:

 

                    break;

                case BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND:

 

                    break;

                case BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP:

 

                    break;

            }

 

             rt_kprintf("received advertisement; event_type=%d rssi=%d addr_type=%d addr=",

                                                                    disc_desc->discov_type,

                                                                    disc_desc->rssi,

                                                                    disc_desc->addr_type);

            rt_kprintf("%02x:%02x:%02x:%02x:%02x:%02x\n", disc_desc->addr[5], disc_desc->addr[4],

                                                          disc_desc->addr[3], disc_desc->addr[2],

                                                          disc_desc->addr[1], disc_desc->addr[0]);

            rt_kprintf("mfg_data = ");

            for (int i = 0; i < disc_desc->length_data; i++)

            {

                rt_kprintf("%02x ", dicover_data[i]);

            }

            rt_kprintf("\n\n");

            break;

        }

        case RW007_BLE_NTF_TYPE_CONNECT:

        {

            struct rw007_ble_gap_event_connect *connect_event;

            struct rw007_ble_conn_desc *conn_desc;

 

            rt_kprintf("RW007_BLE_NTF_TYPE_CONNECT\n");

 

            connect_event = (struct rw007_ble_gap_event_connect *)data;

            conn_desc = (struct rw007_ble_conn_desc*)(connect_event + 1);

 

            rt_kprintf("connect event status: %d, conn_handle: %d\n", connect_event->status, connect_event->conn_handle);

            rt_kprintf("our_id_type:%d our_id_addr: ", conn_desc->our_id_type);

            _print_addr(conn_desc->our_id_addr);

            rt_kprintf("\npeer_id_type:%d peer_id_addr: ", conn_desc->peer_id_type);

            _print_addr(conn_desc->peer_id_addr);

            rt_kprintf("\nconn_itv:%d, conn_latency:%d, conn_suptout:%d, role:%d\n", conn_desc->conn_itvl,

                                conn_desc->conn_latency, conn_desc->supervision_timeout, conn_desc->role);

            break;

        }

        case RW007_BLE_NTF_TYPE_DISCONN:

            rt_kprintf("RW007_BLE_NTF_TYPE_DISCONN\n");

            break;

        case RW007_BLE_NTF_TYPE_MTU_EXCHANGE:

            rt_kprintf("RW007_BLE_NTF_TYPE_MTU_EXCHANGE\n");

            break;

        case RW007_BLE_NTF_TYPE_DISC_COMPLETE:

            rt_kprintf("RW007_BLE_NTF_TYPE_DISC_COMPLETE\n");

            break;

        case RW007_BLE_NTF_TYPE_ADV_COMPLETE:

            rt_kprintf("RW007_BLE_NTF_TYPE_ADV_COMPLETE\n");

            break;

        case RW007_BLE_NTF_TYPE_SUBSCRIBE:

            rt_kprintf("RW007_BLE_NTF_TYPE_SUBSCRIBE\n");

            break;

        case RW007_BLE_NTF_TYPE_NOTIFY_RX:

            {

             struct rw007_ble_gap_event_notify_rx *notify_rx;

 

             rt_kprintf("RW007_BLE_NTF_TYPE_NOTIFY_RX\n");

 

             notify_rx = (struct rw007_ble_gap_event_notify_rx *)data;

             rt_kprintf("conn_handle:%d, attr_handle:%d, rcv (%s) length:%d, data:\n",

                        notify_rx->conn_handle, notify_rx->attr_handle,

                        notify_rx->indication ? "indication" : "notification", notify_rx->length_data);

             hex_dump((const rt_uint8_t *)(notify_rx + 1), notify_rx->length_data);

            break;

        }

        default:

            rt_kprintf("error notify\n");

            break;

    }

}

```

 

(2) 添加 `RW007`连接米家蓝牙温湿度计2代设备的实现函数

 

```c

tatic int ble_example_connect(void)

{

    rt_kprintf("ble_example_connect\n");

    ble_addr_t addr;

    addr.type = RW007_BLE_ADDR_PUBLIC;

 

    rt_kprintf("str_addr: %s\n", "A4:C1:38:35:52:94");

 

    _hexstrtoaddr("A4:C1:38:35:52:94", addr.val);

 

    rt_kprintf("mac addr: %2x:%2x:%2x:%2x:%2x:%2x\n", addr.val[0], addr.val[1],

                                                          addr.val[2], addr.val[3],

                                                          addr.val[4], addr.val[5]);

 

    rw007_ble_connect(&addr);

    return 0;

}

```

说明:前面已经通过 BLE 测试和手机的 BLE 调试助手可以知道米家蓝牙温湿度计2代设备的 mac 地址为`A4:C1:38:35:52:94`,所以这里直接固定设备的`mac`地址用于连接。

 

(3)RW007 通过 `UUID` 读取米家蓝牙温湿度计2代设备的数据

  由于前面通过 BLE 测试连接上米家蓝牙温湿度计2代设备后,会主动收到来自米家蓝牙温湿度计2代设备的温湿度数据的`notify`,并在`rw007_ble_ntf_handle`函数中打印输出,现在需要解析接收到的温湿度数据,在`rw007_ble_ntf_handle`的函数中添加解析数据的功能。

 

  ```c

case RW007_BLE_NTF_TYPE_NOTIFY_RX:

            {

             struct rw007_ble_gap_event_notify_rx *notify_rx;

             char cRes[50] = {0};

 

             rt_kprintf("RW007_BLE_NTF_TYPE_NOTIFY_RX\n");

 

             notify_rx = (struct rw007_ble_gap_event_notify_rx *)data;

             rt_kprintf("conn_handle:%d, attr_handle:%d, rcv (%s) length:%d, data:\n",

                        notify_rx->conn_handle, notify_rx->attr_handle,

                        notify_rx->indication ? "indication" : "notification", notify_rx->length_data);

             hex_dump((const rt_uint8_t *)(notify_rx + 1), notify_rx->length_data);

 

             int32_t temp = 0xffff;

             int hum = 0xff;

             int32_t v_bat = 0xffff;

 

             rt_uint8_t *ptr = (const rt_uint8_t *)(notify_rx + 1);

 

             //Here to get mi tempture data

             temp = 0xffff & ptr[1];

             temp = temp << 8;

             temp = temp | ptr[0];

             hum = ptr[2];

             v_bat = 0xffff & ptr[4];

             v_bat = v_bat << 8;

             v_bat = v_bat | ptr[3];

             rt_kprintf("temp=%d mC,hum=%d%,v_bat=%dmV\n", temp, hum, v_bat);

             memset(cRes,0,sizeof(cRes));

             sprintf(cRes,"temp=%dmC,hum=%d%%,v_bat=%dmV", temp, hum, v_bat);

             rt_kprintf("cres=%s \n",cRes);

 

            break;

            }

  ```

 

(4) 添加通过命令启动函数,测试功能

```c

static int ble_example_start(void)

{

    ble_example_init();

    rt_thread_delay(1000);

    ble_example_connect();

    rt_thread_delay(5000);

    return 0;

 

}

MSH_CMD_EXPORT(ble_example_start,ble_example_start)

```

说明:这里加了一些延时是考虑到连接设备需要一些时间。

(5)编译固件,烧录验证功能

编译固件,给柿饼派升级固件后,通过在调试串口 `msh`中输入`ble_example_start`命令进行启动,启动后便会连接米家蓝牙温湿度计2代设备,并把获取到温湿度数据进行解析,打印出来。

```

msh />ble_example_start

ble_example_init

ble_example_connect

str_addr: A4:C1:38:35:52:94

mac addr: 94:52:35:38:c1:a4

ble data input packet resp_type: 1, len: 46

RW007_BLE_NTF_TYPE_CONNECT

connect event status: 0, conn_handle: 1

our_id_type:0 our_id_addr: 48:00:42:8c:47:c9

peer_id_type:0 peer_id_addr: a4:c1:38:35:52:94

conn_itv:80, conn_latency:0, conn_suptout:256, role:0

ble data input packet resp_type: 1, len: 13

RW007_BLE_NTF_TYPE_NOTIFY_RX

conn_handle:1, attr_handle:54, rcv (notification) length:5, data:

00000000: 47 0B 3B B9 0A                                   G.;..

temp=2887 mC,hum=59%,v_bat=2745mV

cres=temp=2887mC,hum=59%,v_bat=2745mV 

 

```

 

#### 添加把数据传递到界面的实现函数(C-To-JS)

通过前面的步骤,已经成功解析到了米家蓝牙温湿度计2代设备的温湿度数据,现在需要把这些数据显示到 LCD 屏上,需要在 SDK 中参考示例代码中的`docs\src\PersimM3_JS_GUI_C_TransData\js_message_test.c`进行数据的封装传到GUI 中。

 

(1)创建 `module`并初始化

 

需要创建添加`module`初始化便于 JS 中导入该模块。

```c

static js_object_t js_message_obj = JS_ECMA_VALUE_UNDEFINED;

static void js_message_info_free(void *native)

{

    js_message_obj = JS_ECMA_VALUE_UNDEFINED;

    rt_kprintf("==> js_message_obj = JS_ECMA_VALUE_UNDEFINED;\n");

}

 

static const js_object_native_info_t js_message_info =

{

    .free_cb = js_message_info_free

};


 

js_object_t example_module_init(void)

{

    js_object_t obj = js_create_object();

 

    if (js_resolve_error(obj))

        return js_create_null();

    rt_kprintf("==> module_init\n");

 

    js_message_obj = obj;

    js_emitter(js_message_obj, js_create_undefined());

    js_set_property_native_pointer(js_message_obj, "_free_cb", NULL, &js_message_info);

 

    return obj;

}

JS_MODULE(example_module,example_module_init)

```

注意:这里的`example_module`在 js 中

 

(2)封装数据传输函数

在 C代码层到JS脚本层主要是通过事件监听机制异步上报数据或触发JS逻辑主动到C层取数据;当前异步机制是通过GUI的消息队列实现,先往GUI的消息队列发送消息,然后GUI收到对应消息后触发监听回调,所以需要`js_message_send_data`和`js_callback_message`函数,可以直接从示例代码中拷贝过来使用。

 

```c

static rt_bool_t js_message_send_data(const char *name, js_object_t data)

{

    rt_bool_t ret = RT_FALSE;

 

    if (js_context_lock() != RT_EOK)

        return ret;

    rt_kprintf("==> js_message_send_data start\n");

     if (js_object_is_object(js_message_obj))

     {

        js_object_t msg = js_create_object();

        if (!js_resolve_error(msg))

        {

            js_set_string_property_value(msg, "name", name);

            js_set_property_value(msg, "data", data);

 

            ret = js_send_callback_func(js_callback_message, msg);

            js_release_object(msg);

        }

    }

    rt_kprintf("==> js_message_send_data end\n");

    js_context_unlock();

 

    return ret;

}

```

 

```c

static rt_bool_t js_callback_message(js_object_t args)

{

    if (js_context_lock() != RT_EOK)

        return RT_FALSE;

 

    rt_kprintf("==> js_callback_message start\n");

    if (js_object_is_object(js_message_obj))

    {

        js_object_t msg_name = js_get_property_value(args, "name");

        js_object_t msg_data = js_get_property_value(args, "data");

        char str_buf[JS_STRING_BUFFER_SIZE];

        char *name = js_object_to_string(msg_name, str_buf, JS_STRING_BUFFER_SIZE);

        if (name)

        {

            js_event_emit(js_message_obj, name, &msg_data, 1);

            if (name != str_buf)

                JS_FREE(name);

        }

        js_release_object(msg_data);

        js_release_object(msg_name);

    }

    js_release_object(args);

    rt_kprintf("==> js_callback_message end\n");

    js_context_unlock();

 

    return RT_TRUE;

}

 

```

(3)在解析数据后把数据传到界面显示

在`rw007_ble_ntf_handle`的函数中添加把解析数据后把数据传到界面显示的功能,只需要使用`js_message_send_data`函数进行传输数据即可。

 

```c

        case RW007_BLE_NTF_TYPE_NOTIFY_RX:

            {

             struct rw007_ble_gap_event_notify_rx *notify_rx;

             char cRes[50] = {0};

 

             rt_kprintf("RW007_BLE_NTF_TYPE_NOTIFY_RX\n");

 

             notify_rx = (struct rw007_ble_gap_event_notify_rx *)data;

             rt_kprintf("conn_handle:%d, attr_handle:%d, rcv (%s) length:%d, data:\n",

                        notify_rx->conn_handle, notify_rx->attr_handle,

                        notify_rx->indication ? "indication" : "notification", notify_rx->length_data);

             hex_dump((const rt_uint8_t *)(notify_rx + 1), notify_rx->length_data);

 

             int32_t temp = 0xffff;

             int hum = 0xff;

             int32_t v_bat = 0xffff;

 

             rt_uint8_t *ptr = (const rt_uint8_t *)(notify_rx + 1);

 

             //Here to get mi tempture data

             temp = 0xffff & ptr[1];

             temp = temp << 8;

             temp = temp | ptr[0];

             hum = ptr[2];

             v_bat = 0xffff & ptr[4];

             v_bat = v_bat << 8;

             v_bat = v_bat | ptr[3];

             rt_kprintf("temp=%d mC,hum=%d%,v_bat=%dmV\n", temp, hum, v_bat);

             memset(cRes,0,sizeof(cRes));

             sprintf(cRes,"temp=%dmC,hum=%d%%,v_bat=%dmV", temp, hum, v_bat);

             rt_kprintf("cres=%s \n",cRes);

 

            if (js_context_lock() == RT_EOK)

            {

                js_object_t value =string_to_js_object(cRes);

                js_message_send_data("mi_data", value);

                rt_kprintf("value:%s\n", value);

                js_release_object(value);

                js_context_unlock();

            }

 

            break;

        }

```

说明:需要注意` js_message_send_data("mi_data", value);`这里的`mi_data`,后面UI界面中将通过这个属性来接收数据。

 

(4)编译固件,烧录程序。

根据前面的步骤操作,这里柿饼派的固件已经准备好了,下面需要创建 UI 工程进行界面显示米家蓝牙温湿度计2代设备的温湿度数据。

 

#### 创建 UI 工程显示米家蓝牙温湿度计2代设备的温湿度数据

 

关于`UI`界面工程的创建,这里不做具体说明,可以参考 SDK 目录下`docs\2-PersimM3_UI_Quick_Start.pdf`文档的操作,这里只说明核心部分的操作。

 

(1) UI 工程界面控件布局

由于这里只做数据显示,所以仅需要放置几个 `Label`控件,为了美观些这里也特意放置了一些图标和设置背景图片。

 

柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据_第6张图片

 

柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据_第7张图片

 

(2) JS 代码的编写

主要通过`require`导入`example_module`,这里的`example_module`是前面`C`代码中初始化过的,然后通过`this.example_module.on("mi_data",function(data){});`进行数据解析,并把数据更新到UI界面中。这里的`mi_data`也是需要与`C`代码中配对使用的。 

```javascript

var page = {

 

    /* 此方法在第一次显示窗体前发生 */

    onLoad: function (event) {

        this.example_module = require("example_module");

        console.dir(this.example_module);

        var that = this;

        function insertStr(soure, start, newStr) {

            return soure.slice(0, start) + newStr + soure.slice(start);

        }

 

        this.example_module.on("mi_data",function(data){

 

            //temp=3205mC,hum=54%,v_bat=2766mV

            //data_length:32

            s_l1 = data.indexOf(',')

            temp_data =data.substring(0,s_l1)

            temp_value_l = temp_data.indexOf('=')

            temp_value1 = temp_data.substring(temp_value_l+1,temp_data.length -2 )

            temp_value=insertStr(temp_value1,2,".");

            s_l2 = data.lastIndexOf(',')

            hum_value = data.substring(s_l1+5,s_l2)

            vbat_data =  data.substring(s_l2+7,data.length -2)

            vbat_value=insertStr(vbat_data,1,".");

            that.setData({temp_value : temp_value+'℃'});

            that.setData({hum_value : hum_value});

            that.setData({bat_value : vbat_value+'V'});

        });

    },

 

    /* 此方法展示窗体后发生 */

    onResume: function (event) {

 

    },

 

    /* 当前页状态变化为显示时触发 */

    onShow: function (event) {

 

    },

 

    /* 当前页状态变化为隐藏时触发 */

    onHide: function (event) {

 

    },

 

    /* 此方法关闭窗体前发生 */

    onExit: function (event) {

 

    },

};

 

Page(page);

 

page = 0;

 

```

(3) 下载 UI 工程到柿饼派,体验效果

UI 工程下载完成后,在 调试串口的`msh`命令行中输入`ble_example_start`,然后观察UI界面的效果。

```

msh />ble_example_start

ble_example_init

ble_example_connect

str_addr: A4:C1:38:35:52:94

mac addr: 94:52:35:38:c1:a4

ble data input packet resp_type: 1, len: 46

RW007_BLE_NTF_TYPE_CONNECT

connect event status: 0, conn_handle: 1

our_id_type:0 our_id_addr: 48:00:42:8c:47:c9

peer_id_type:0 peer_id_addr: a4:c1:38:35:52:94

conn_itv:80, conn_latency:0, conn_suptout:256, role:0

msh />ble data input packet resp_type: 1, len: 13

RW007_BLE_NTF_TYPE_NOTIFY_RX

conn_handle:1, attr_handle:54, rcv (notification) length:5, data:

00000000: 28 0B 42 A9 0A                                   (.B..

temp=2856 mC,hum=66%,v_bat=2729mV

cres=temp=2856mC,hum=66%,v_bat=2729mV 

==> js_message_send_data start

==> js_message_send_data end

value:

==> js_callback_message start

==> js_callback_message end

 

```

此时,柿饼派的界面就显示米家蓝牙温湿度计2代设备的温湿度和电量的数据。

柿饼派通过RW007的BLE 功能读取米家蓝牙温湿度计数据_第8张图片

 

## 总结与展望

经过这次使用柿饼派上的 RW007 WIFI 模块学习 BLE 功能的使用和读取米家温湿度计数据显示在界面上的过程中,学会 RW007 上如何使用 BLE 功能和巩固了柿饼派JS与C 之间的数据交互的操作,在这次仅是使用了BLE 的初始化和连接功能,希望在后面使用更多的功能制作更多有趣的作品与大家分享。

你可能感兴趣的:(RW007,高速WIFI,学习)