【低功耗蓝牙】③ 蓝牙服务和特性的创建

摘要

本文章主要基于ESP32的MicroPython平台,讲解了蓝牙服务和特性的建立,以及基于特性的数据交互,实现手机于蓝牙模块相互通信。主要涉及的概念有 UUIDGATT服务特性

UUID

UUIDUniversity Unique Identifie 的缩写,翻译成中文为 通用唯一标识符。是蓝牙组织联盟定义的用于区分蓝牙服务和特性的的标识符,总长度为128 Bit。
例如:

03B80E5A-EDE8-4B33-A751-6CE34EC4C700

7772E5DB-3868-4112-A1A9-F2669D106BF3

128Bit的UUID占用16个字节,在编程和传输的时候都很不方便,所以蓝牙联盟定义了一个UUID的基地址,允许在此基础上使用16Bit的UUID。

UUID基地址: 0x0000xxxx-0000-1000-8000-00805F9B34FB

比如16Bit的UUID : 0x2A37 转换成128Bit的UUID为:

0x00002A37-0000-1000-8000-00805F9B34FB

服务和特性

低功耗蓝牙设备之间通信,都是基于服务和特性。

一个蓝牙设备中可以包含若干个服务
一个服务中可以包含若干个特性
每一个服务或者特性都要有一个UUID

蓝牙的数据交互都是基于一个个特性进行的,数据交互的方式有五种,分别是ReadWriteWrite WithOutResponsNotifyIndication
蓝牙服务特性结构图

服务和特性特创建

蓝牙设备要在进入广播态之前创建服务和特性,在MicroPython中大概分为四步:
① 创建要使用的UUID;
② 使用UUID创建特性并设置特性的读写权限;
③ 将创建好的特性添加到服务集合中;
④ 将服务集合注册到协议栈中。

比如我们要创建一个UUID为9011的服务,该服务里面包含两个特性;
这两个特性的UUID分别是9012和9013;
9012的特性拥有Read和Write的权限,9013的特性拥有Read和Notify的权限。

在这里插入图片描述

可以使用如下代码实现:

from machine import Pin
from time import sleep_ms
import ubluetooth        #导入BLE功能模块

ble = ubluetooth.BLE()   #创建BLE设备
ble.active(True)         #打开BLE

#创建要使用的UUID
SERVER_1_UUID = ubluetooth.UUID(0x9011)
CHAR_A_UUID = ubluetooth.UUID(0x9012)
CHAR_B_UUID = ubluetooth.UUID(0x9013)

#创建特性并设置特性的读写权限
CHAR_A = (CHAR_A_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE, )
CHAR_B = (CHAR_B_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, )

SERVER_1 = (SERVER_1_UUID, (CHAR_A , CHAR_B, ) , ) #把特性A和特性B放入服务1
SERVICES = (SERVER_1, ) #把服务1放入服务集和中
((char_a, char_b), ) = ble.gatts_register_services(SERVICES) #注册服务到gatts

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

运行代码,通过微信小程序谷雨蓝牙连接设备后,可以参看到蓝牙设备上的服务可特性列表如下:

谷雨蓝牙

当然,一个蓝牙设备里面也可以创建多个服务,下面给出了实现两个服务的参考代码:

from machine import Pin
from time import sleep_ms
import ubluetooth         #导入BLE功能模块

ble = ubluetooth.BLE()    #创建BLE设备
ble.active(True)          #打开BLE

#创建要使用的UUID
SERVER_1_UUID = ubluetooth.UUID(0x9011)
CHAR_A_UUID = ubluetooth.UUID(0x9012)
CHAR_B_UUID = ubluetooth.UUID(0x9013)

SERVER_2_UUID = ubluetooth.UUID(0x9021)
CHAR_C_UUID = ubluetooth.UUID(0x9022)
CHAR_D_UUID = ubluetooth.UUID(0x9023)

#创建特性并设置特性的读写权限
CHAR_A = (CHAR_A_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE, )
CHAR_B = (CHAR_B_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, )
SERVER_1 = (SERVER_1_UUID, (CHAR_A , CHAR_B, ) , ) #把特性A和特性B放入服务1

CHAR_C = (CHAR_C_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE, )
CHAR_D = (CHAR_D_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, )
SERVER_2 = (SERVER_2_UUID, (CHAR_C , CHAR_D, ) , ) #把特性A和特性B放入服务

SERVICES = (SERVER_1, SERVER_2 , ) #把服务1和服务2放入服务集和中
((char_a, char_b), (char_c, char_d), ) = ble.gatts_register_services(SERVICES) #注册服务到gatts

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

读者可直接运行上述代码,使用谷雨蓝牙小程序观察一下服务和特性的结构与期望是否一致。

数据交互

低功耗蓝牙之间的数据交互都是基于特性,以手机连接蓝牙模块为例,手机读取蓝牙模块的数据,使用的是Read方法,手机发送数据给蓝牙模块使用的是Write方法,蓝牙模块发送数据到手机,一般使用的是Notify方法,而且手机端还要打开Notify监听

通信示例代码如下:


from machine import Pin
from time import sleep_ms
import ubluetooth        #导入BLE功能模块

ble = ubluetooth.BLE()   #创建BLE设备
ble.active(True)         #打开BLE

#创建要使用的UUID
SERVER_1_UUID = ubluetooth.UUID(0x9011)
CHAR_A_UUID = ubluetooth.UUID(0x9012)
CHAR_B_UUID = ubluetooth.UUID(0x9013)

#创建特性并设置特性的读写权限
CHAR_A = (CHAR_A_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE | ubluetooth.FLAG_NOTIFY, )
CHAR_B = (CHAR_B_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY, )

SERVER_1 = (SERVER_1_UUID, (CHAR_A , CHAR_B, ) , ) #把特性A和特性B放入服务1
SERVICES = (SERVER_1, ) #把服务1放入服务集和中
((char_a, char_b), ) = ble.gatts_register_services(SERVICES) #注册服务到gatts

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

def ble_irq(event, data): # 蓝牙中断函数
    if event == 1: #蓝牙已连接
      print("BLE 连接成功")

    elif event == 2: #蓝牙断开连接
      print("BLE 断开连接")
      ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

    elif event == 3: #收到数据
      onn_handle, char_handle = data #判断是来自那个特性的消息
      buffer = ble.gatts_read(char_handle) #读取接收到的消息
      print(char_handle, buffer) #打印消息内容
      ble.gatts_notify(0, char_handle, 'Hello') #回复Hello

ble.irq(ble_irq)

在上述代码中,我们创建了一个UUID为9011的服务,并在该服务中创建了一个UUID为9012的特性,该特性的操作权限是 ReadWriteNotify。在手机端打开谷雨蓝牙小程序,连接设备,选择UUID为9012的特性,进入常规试图,点击监听,随便写入一下数据,可以看到,设备回复给手机Hello。
在这里插入图片描述

低功耗蓝牙设备之间通信常用的方式是ReadWrite Notify ,必须在创建特性时赋予对应的权限,才能在通信中使用。如果某个特性在创建的时候,没有开启 Write 权限,则手机将无法通过该特性发送数据到设备。

蓝牙联盟已定义的16Bit UUID

对于一些常用的功能,蓝牙组织联盟已经为其定义好了UUID,我们在开发产品的时候直接使用即可。

16 Bit UUID定义文档下载地址:https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf

电池电量指示

在上述文档(16BitUUID)中定义了电池电量服务的UUID是0x180F,电池电量特性的UUID是0x2A19,我们可以使用这两个UUID实现电池电量指示的功能,代码如下:

from machine import Pin
from time import sleep_ms
import ubluetooth        #导入BLE功能模块

ble = ubluetooth.BLE()   #创建BLE设备
ble.active(True)         #打开BLE

#创建电池服务和特性的UUID
BATTERY_SERVER_UUID = ubluetooth.UUID(0x180F)
BATTERY_CHAR_UUID = ubluetooth.UUID(0x2A19)

#创建特性并设置特性的读写权限
BATTERY_CHAR = (BATTERY_CHAR_UUID, ubluetooth.FLAG_READ , )

BATTERY_SERVER = (BATTERY_SERVER_UUID, (BATTERY_CHAR, ) , ) #把电量特性放入电池服务
SERVICES = (BATTERY_SERVER, ) #把电池服务服务放入服务集和中

((battery_char,), ) = ble.gatts_register_services(SERVICES) #注册服务到gatts

ble.gatts_write(battery_char, b'\x50') #设置电池电量为80%

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

def ble_irq(event, data): # 蓝牙中断函数
    if event == 1: #蓝牙已连接
      print("BLE 连接成功")

    elif event == 2: #蓝牙断开连接
      print("BLE 断开连接")
      ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

    elif event == 3: #收到数据
      print("收到新消息")

ble.irq(ble_irq)

电池电量用一个字节来表示,其单位是电量百分比,16进制数0x50转换成十进制为80,所以运行上述代码,连接该设备后,可以看到该设备的电量显示,如下图所示:

电池电量显示

通过手机微信小程序“谷雨蓝牙”连接该设备后,可以看到如下服务列表。

温湿度传感器

在蓝牙组织联盟发布的16Bit UUID 文档中,定义了环境传感器服务的UUID是 0x181A,温度特性的UUID是0x2A6E,湿度特性的UUID是0x2A6F。使用如下代码可实现简单温湿度传感器功能:

from machine import Pin
from time import sleep_ms
import ubluetooth        #导入BLE功能模块

ble = ubluetooth.BLE()   #创建BLE设备
ble.active(True)         #打开BLE

#创建环境传感器服务和特性的UUID
ENV_SERVER_UUID = ubluetooth.UUID(0x181A) #环境传感器服务
TEM_CHAR_UUID = ubluetooth.UUID(0x2A6E)   #温度特性
HUM_CHAR_UUID = ubluetooth.UUID(0x2A6F)   #湿度特性

#创建特性并设置特性的读写权限
TEM_CHAR = (TEM_CHAR_UUID, ubluetooth.FLAG_READ , )
HUM_CHAR = (HUM_CHAR_UUID, ubluetooth.FLAG_READ , )

ENV_SERVER = (ENV_SERVER_UUID, (TEM_CHAR, HUM_CHAR, ) , ) #把温湿度特性放入环境服务
SERVICES = (ENV_SERVER, ) #把环境服务放入服务集和中

((tem_char, hum_char, ), ) = ble.gatts_register_services(SERVICES) #注册服务到gatts

ble.gatts_write(tem_char, b'\x06\x08') #设置温度为20.54度(0x0806 = 2054)
ble.gatts_write(hum_char, b'\x09\x07') #设置湿度为18.01%(0x0709 = 1801)

#设置BLE广播数据并开始广播
ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

def ble_irq(event, data): # 蓝牙中断函数
    if event == 1: #蓝牙已连接
      print("BLE 连接成功")

    elif event == 2: #蓝牙断开连接
      print("BLE 断开连接")
      ble.gap_advertise(100, adv_data = b'\x02\x01\x06\x03\x09\x41\x42')

    elif event == 3: #收到数据
      print("收到新消息")

ble.irq(ble_irq)

运行上述代码,使用手机APP nRF Connect 连接设备后,可以读取到温湿度数据。温湿度数据使用两个字节来表示(低字节在前,高字节在后),温度的单位是0.01度,湿度的单位是0.01%

温湿度的获取

蓝牙组织联盟定义了很多常用的UUID,上面的示例只选取了其中的两个用作演示,有兴趣的同学可以自行尝试下别的UUID。

上一章节:【低功耗蓝牙】② 蓝牙状态切换和事件处理

下一章节:【低功耗蓝牙】④ 蓝牙MIDI协议

作者:我是鹏老师

  • 8
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值