文章目录

  • 1.蓝牙BLE GATT协议(转载)
  • 1.1 通用属性协议(GATT)
  • 1.1.1 Profile(规范)
  • 1.1.2 Service(服务)
  • 1.1.3 Characteristic(特征)
  • 1.1.4 UUID(通用唯一识别码)
  • 2.W801蓝牙例程中的ble_gatt_svc_def结构体
  • 2.1微信小程序API函数 wx.onBluetoothDeviceFound
  • 2.2微信小程序API函数wx.getBLEDeviceServices
  • 2.3微信小程序API函数wx.getBLEDeviceCharacteristics
  • 2.4 微信小程序API函数 wx.notifyBLECharacteristicValueChange
  • 2.5 微信小程序API函数 wx.writeBLECharacteristicValue
  • 3.W801接收手机指令控制LED
  • 4.W801发送数据到微信小程序


  1. W801蓝牙通信源代码来源:作者: Mr.赵,CSND博客:W801/W800蓝牙收发数据与控制设计(一)-INDICATE ;源码:代码仓库
  2. 微信小程序源代码来源:作者:cwlgoodman,码云仓库: 微信小程序源代码,这个仓库有4个工程,我们用的是第一个:

1.bluetooth_demo
2.bluetooth_lock
3.glucometer
4.wechat_api

  1. 这篇文章要配合另外一篇一起读:《联盛德W801系列5-微信小程序与W801蓝牙通信例程(阅读笔记)

1.蓝牙BLE GATT协议(转载)

这里摘录一段《ESP32学习笔记(30)——BLE GATT服务端自定义服务和特征》的内容:

1.1 通用属性协议(GATT)

GATT是用Attribute Protocal(属性协议)定义的一个service(服务)框架。这个框架定义了Services以及它们的Characteristics的格式和规程。规程就是定义了包括发现、读、写、通知、指示以及配置广播的characteristics。

为实现配置文件(Profile)的设备定义了两种角色:Client(客户端)、Server(服务器)。esp32(W801)的ble一般就处于Server模式。

一旦两个设备建立了连接,GATT就开始发挥效用,同时意味着GAP协议管理的广播过程结束了。

1.1.1 Profile(规范)

profile 可以理解为一种规范,建立的蓝牙应用任务,蓝牙任务实际上分为两类:标准蓝牙任务规范 profile(公有任务),非标准蓝牙任务规范 profile(私有任务)。

标准蓝牙任务规范 profile:指的是从蓝牙特别兴趣小组 SIG 的官网上已经发布的 GATT 规范列表,包括警告通知(alert notification),血压测量(blood pressure),心率(heart rate),电池(battery)等等。它们都是针对具体的低功耗蓝牙的应用实例来设计的。目前蓝牙技术联盟还在不断的制定新的规范,并且发布。

非标准蓝牙任务规范 profile:指的是供应商自定义的任务,在蓝牙 SIG 小组内未定义的任务规范。

1.1.2 Service(服务)

service 可以理解为一个服务,在 BLE 从机中有多个服务,例如:电量信息服务、系统信息服务等;
每个 service 中又包含多个 characteristic 特征值;
每个具体的 characteristic 特征值才是 BLE 通信的主题,比如当前的电量是 80%,电量的 characteristic 特征值存在从机的 profile 里,这样主机就可以通过这个 characteristic 来读取 80% 这个数据。
GATT 服务一般包含几个具有相关的功能,比如特定传感器的读取和设置,人机接口的输入输出。组织具有相关的特性到服务中既实用又有效,因为它使得逻辑上和用户数据上的边界变得更加清晰,同时它也有助于不同应用程序间代码的重用。

1.1.3 Characteristic(特征)

characteristic 特征,BLE 主从机的通信均是通过 characteristic 来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。

1.1.4 UUID(通用唯一识别码)

uuid 通用唯一识别码,我们刚才提到的 service 和 characteristic 都需要一个唯一的 uuid 来标识;
每个从机都会有一个 profile,不管是自定义的 simpleprofile,还是标准的防丢器 profile,他们都是由一些 service 组成,每个 service 又包含了多个 characteristic,主机和从机之间的通信,均是通过characteristic来实现。

=================================抄录结束

GATT结构由嵌套的Profile、Service、Characteristics组成,如下图:

微信开发者 手机蓝牙未打开_微信开发者 手机蓝牙未打开

2.W801蓝牙例程中的ble_gatt_svc_def结构体

《\src\app\bleapp\wm_ble_server_api_demo.c》 中的结构体:

#define WM_GATT_SVC_UUID      	0xFFF0
#define WM_GATT_INDICATE_UUID 	0xFFF1
#define WM_GATT_WRITE_UUID    	0xFFF2
static const struct ble_gatt_svc_def gatt_demo_svr_svcs[] = {
    {
        /* Service: uart */
        .type = BLE_GATT_SVC_TYPE_PRIMARY,
        .uuid = BLE_UUID16_DECLARE(WM_GATT_SVC_UUID),
        .characteristics = (struct ble_gatt_chr_def[]) { {
                .uuid = BLE_UUID16_DECLARE(WM_GATT_WRITE_UUID),
                .val_handle = &g_ble_demo_attr_write_handle,
                .access_cb = gatt_svr_chr_demo_access_func,
                .flags = BLE_GATT_CHR_F_WRITE,
            },{
                .uuid = BLE_UUID16_DECLARE(WM_GATT_INDICATE_UUID),
                .val_handle = &g_ble_demo_attr_indicate_handle,
                .access_cb = gatt_svr_chr_demo_access_func,
                .flags = BLE_GATT_CHR_F_INDICATE,
            },{
              0, /* No more characteristics in this service */
            } 
         },
    },

    {
        0, /* No more services */
    },
};

微信开发者 手机蓝牙未打开_API_02

2.1微信小程序API函数 wx.onBluetoothDeviceFound

wx.onBluetoothDeviceFound,查找设备API,可以获得以下信息:

{"devices":
	[
		{
			"deviceId":"28:6D:CD:D1:5C:30",
			"name":"WM-D1:5C:30",
			"RSSI":-52,
			"connectable":true,
			"advertisData":{},
			"advertisServiceUUIDs":["0000FFF0-0000-1000-8000-00805F9B34FB"],
			"localName":"WM-D1:5C:30",
			"serviceData":{}
		}
	]
}

其中 deviceId为蓝牙MAC地址,name为下面代码生成《\src\app\bleapp\wm_bt_app.c》第22行:

int tls_ble_gap_init(void)
{
    char default_device_name[MYNEWT_VAL(BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH)];
    uint8_t bt_mac[6];
    int ret_len = 0;
    
    g_scan_state = WM_BLE_SCAN_STOP;
    memset(&adv_params_dft, 0, sizeof(adv_params_dft)); 
    adv_params_dft.conn_mode = BLE_GAP_CONN_MODE_UND;  //default conn  mode;
    adv_params_dft.disc_mode = BLE_GAP_DISC_MODE_GEN;  //default disc  mode;
    memset(&disc_params_dft, 0, sizeof(disc_params_dft));
    memset(&direct_adv_addr, 0, sizeof(direct_adv_addr));
    dl_list_init(&report_evt_list.list);
    ble_npl_mutex_init(&report_evt_list.list_mutex);
    
    if(btif_config_get_str("Local", "Adapter", "Name", default_device_name, &ret_len))
    {
        ble_svc_gap_device_name_set(default_device_name);
    }else
    {
        tls_get_bt_mac_addr(bt_mac);
        sprintf(default_device_name, "WM-%02X:%02X:%02X", bt_mac[3], bt_mac[4], bt_mac[5]);
        ble_svc_gap_device_name_set(default_device_name);
    }
    return 0;
}

2.2微信小程序API函数wx.getBLEDeviceServices

wx.getBLEDeviceServices获取服务信息API,获取到以下内容:

{
	"services":
	[
		{
			"uuid":"0000FFF0-0000-1000-8000-00805F9B34FB",
			"isPrimary":true
		}
	],
	"errCode":0,
	"errno":0,
	"errMsg":"getBLEDeviceServices:ok"
}

对应下面的内容:

微信开发者 手机蓝牙未打开_API_03

2.3微信小程序API函数wx.getBLEDeviceCharacteristics

wx.getBLEDeviceCharacteristics获取特征值API,获取以下内容:

{"characteristics":
	[
		{"uuid":"0000FFF2-0000-1000-8000-00805F9B34FB",
		"handle":3,
		"properties":
			{
				"read":false,
				"write":true,
				"notify":false,
				"indicate":false,
				"writeNoResponse":false,
				"writeDefault":true
			}
		},
		{"uuid":"0000FFF1-0000-1000-8000-00805F9B34FB",
		"handle":5,
		"properties":
			{
				"read":false,
				"write":false,
				"notify":false,
				"indicate":true,
				"writeNoResponse":false,
				"writeDefault":false
			}
		}
	],
	"errCode":0,
	"errno":0,
	"errMsg":"getBLEDeviceCharacteristics:ok"
}

获取的特征值由下面的源码决定了:

微信开发者 手机蓝牙未打开_微信开发者 手机蓝牙未打开_04

2.4 微信小程序API函数 wx.notifyBLECharacteristicValueChange

wx.notifyBLECharacteristicValueChange 开启监听某个特征值的内容变化,如果有变化,会触发事件,调用 wx.onBLECharacteristicValueChange
我们判断到 properties.indicate == true,说明这个是指示属性,对这个uuid进行监听。

......
              if (item.properties.indicate) {
              //可读数据
                wx.notifyBLECharacteristicValueChange({
                  state: true,
                  deviceId: options.connectedDeviceId,
                  serviceId: that.data.services[0].uuid,
                  characteristicId: item.uuid,
                  success: function (res) {
                    console.log('启用notify成功')
                  }
                })
......

wx.onBLECharacteristicValueChange函数里面,把监听到的数据显示出来。

wx.onBLECharacteristicValueChange(function (res) {
      var receiveText = app.buf2string(res.value)
      console.log('接收到数据:' + receiveText)
      that.setData({
        receiveText: receiveText
      })
    })

2.5 微信小程序API函数 wx.writeBLECharacteristicValue

在本例中,"uuid"为【0000FFF2-0000-1000-8000-00805F9B34FB】的特征可以写入,当手机要对W801写入数据时使用wx.writeBLECharacteristicValue(注意第7行):

sendCmd:function(buff){
    var that = this
    if (that.data.connected) {
      wx.writeBLECharacteristicValue({
        deviceId: that.data.connectedDeviceId,
        serviceId: that.data.services[0].uuid,
        characteristicId: that.data.characteristics[0].uuid,
        value: buff,
        success: function (res) {
          console.log('发送成功')
        }
      })
    }
    else {
      wx.showModal({
        title: '提示',
        content: '蓝牙已断开',
        showCancel: false,
        success: function (res) {
          that.setData({
            searching: false
          })
        }
      })
    }

3.W801接收手机指令控制LED

在Mr.赵的例程中,使用3个字节分别控制3个灯(绿框),如图:

微信开发者 手机蓝牙未打开_数据_05


W801接收到数据后,控制LED的代码:

//接收手机发送的数据,注意是数据是按照字节进行的接收
		tls_os_queue_receive(ble_q,&msg, 0, 0);
		//打印ble收到数据的长度
		printf("ble revice len:%d\n",msg[0]);
		//依次打印收到的ble数据
		for(u8 i=0;i<msg[0];i++){
			printf("%x ",msg[i+1]);
			send_data[i] = msg[i+1];
		}printf("\n");
		
		//分别判断前三个字节的数据,对应开关灯:00 开  其他数据 关
		if(msg[1] != 0)
			tls_gpio_write(WM_IO_PB_05,1);
		else
			tls_gpio_write(WM_IO_PB_05,0);
		if(msg[2] != 0)
			tls_gpio_write(WM_IO_PB_25,1);
		else
			tls_gpio_write(WM_IO_PB_25,0);
		if(msg[3] != 0)
			tls_gpio_write(WM_IO_PB_26,1);
		else
			tls_gpio_write(WM_IO_PB_26,0);

微信小程序端点亮第1个LED(PB5)的代码:

onLed1on:function(){
    var that = this
    if (that.data.connected) {
      var buffer = new ArrayBuffer(3)  
      var dataView = new Uint8Array(buffer) 
      
      that.data.gbuff[0] = 0  // 全局变量,输出低电平--灯亮
      dataView[0]  = that.data.gbuff[0];
      dataView[1]= that.data.gbuff[1];
      dataView[2] = that.data.gbuff[2];
      console.log('发送内容'+dataView[0]+dataView[1]+dataView[2]);
      that.sendCmd(buffer);
    }    
  },

4.W801发送数据到微信小程序

如果W801需要上报状态给手机端,只要调用下面的函数,就能被手机端监听到:

int tls_ble_server_demo_api_send_msg(uint8_t *data, int data_len)
{
    int rc;
    struct os_mbuf *om;
    
    //TLS_BT_APPL_TRACE_DEBUG("### %s len=%d\r\n", __FUNCTION__, data_len);
    if(g_send_pending) return BLE_HS_EBUSY;

    if(data_len<=0 || data == NULL)
    {
        return BLE_HS_EINVAL;
    }
    
    om = ble_hs_mbuf_from_flat(data, data_len);
    if (!om) {
        return BLE_HS_ENOMEM;
    }
    rc = ble_gattc_indicate_custom(g_ble_demo_conn_handle,g_ble_demo_attr_indicate_handle, om); 
    if(rc == 0)
    {
        g_send_pending = 1;
    }
    return rc;
}