蓝牙】③ 蓝牙服务和特性的创建"/>
【低功耗蓝牙】③ 蓝牙服务和特性的创建
摘要
本文章主要基于ESP32的MicroPython平台,讲解了蓝牙服务和特性的建立,以及基于特性的数据交互,实现手机于蓝牙模块相互通信。主要涉及的概念有 UUID
,GATT
,服务
,特性
UUID
UUID
是 University Unique Identifie 的缩写,翻译成中文为 通用唯一标识符。是蓝牙组织联盟定义的用于区分蓝牙服务和特性的的标识符,总长度为128 Bit。
例如:
03B80E5A-EDE8-4B33-A751-6CE34EC4C7007772E5DB-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
蓝牙的数据交互都是基于一个个特性进行的,数据交互的方式有五种,分别是Read
,Write
,Write WithOutRespons
,Notify
,Indication
。
服务和特性特创建
蓝牙设备要在进入广播态之前创建服务和特性,在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放入服务1CHAR_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') #回复Helloble.irq(ble_irq)
在上述代码中,我们创建了一个UUID为9011
的服务,并在该服务中创建了一个UUID为9012
的特性,该特性的操作权限是 Read
,Write
,Notify
。在手机端打开谷雨蓝牙小程序,连接设备,选择UUID为9012的特性,进入常规试图
,点击监听
,随便写入一下数据,可以看到,设备回复给手机Hello。
低功耗蓝牙设备之间通信常用的方式是Read
,Write
和 Notify
,必须在创建特性时赋予对应的权限,才能在通信中使用。如果某个特性在创建的时候,没有开启 Write 权限,则手机将无法通过该特性发送数据到设备。
蓝牙联盟已定义的16Bit UUID
对于一些常用的功能,蓝牙组织联盟已经为其定义好了UUID,我们在开发产品的时候直接使用即可。
16 Bit UUID定义文档下载地址:.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) #注册服务到gattsble.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) #注册服务到gattsble.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协议
作者:我是鹏老师
更多推荐
【低功耗蓝牙】③ 蓝牙服务和特性的创建
发布评论