蓝牙设备的五种状态
- 就绪态
- 广播态
- 连接态
- 发起态
- 扫描态
设备上电后,蓝牙芯片进入就绪态,发起广播就会进入广播态,如果被别的设备连接,就会进入连接态;断开连接就会重新进入就绪态。
蓝牙主机设备可以在就绪态发起扫描,进入扫描态;如果发现需要连接的设备发起连接,进入发起连接态,如果对方接受连接,则双方都进入连接态
蓝牙的基本操作
- 创建蓝牙对象
- 打开蓝牙
- 开始广播,进入广播态,只有广播态的蓝牙设备才能被搜索到。
- 打开手机连接该设备,则进入连接态
在蓝牙的各种状态转换时,需要使用中断函数处理蓝牙的状态变化,并进行相应的操作
from bluetooth import BLE
from machine import Pin
ble = BLE()
ble.active(True)
led = Pin(2, Pin.OUT)
led.on()
// 开始广播
ble.gap_advertise(
100,
adv_data = b'\x02\x01\x05\x05\x09x42\x69\x62\x69',
resp_data = b'\x06\xFF\x41\x42\x43\x44\x45')
def ble_ieq(event, data): # 蓝牙中断函数
if event == 1: # 蓝牙已连接,设备进入连接态
led.off()
elif event == 2: # 蓝牙断开连接,进入就绪态
// 重新发起广播
ble.gap_advertise(
100,
adv_data = b'\x02\x01\x05\x05\x09x42\x69\x62\x69',
resp_data = b'\x06\xFF\x41\x42\x43\x44\x45')
led.off()
ble.irq(ble_irq)
蓝牙服务的特性
- 应用层:作为蓝牙的应用开发者,我们只需要关注这一层即可
- 协议栈:由芯片原厂实现
- 物理层:由芯片原厂实现
蓝牙设备在应用层是通过服务和特性来实现,一个蓝牙设备可以包含若干个服务,没个服务可以包含若干个特性,每个特性可以包含读写通知。每个服务和特性都要有一个UUID。
-
UUID: 蓝牙组织定义的,用于区分每个服务和特性的标识符,从长度128Bit(16字节)
这种UUID太长,蓝牙组织定义了一个UUID基地址,允许使用16Bit的UUID与该地址拼接形成128Bit UUID
我们如何构建一个应用程序呢?
这里描述的是构建一个应用程序,包含一个服务,服务包含两个特性,特性A包含可读可通知,特性B包含可读可写
from bluetooth import BLE
ble = BLE()
ble.active(True)
SERVER_1_UUID = bluetooth.UUID(0x9011)
CHAR_A_UUID = bluetooth.UUID(0x9012)
CHAR_B_UUID = bluetooth.UUID(0x9013)
# 设置特性的读写权限
CHAR_A = (CHAR_A_UUID, bluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY,)
CHAR_A = (CHAR_A_UUID, bluetooth.FLAG_READ | ubluetooth.FLAG_WRITE,)
# 将特性A和特性B放入服务1中
SERVER_1 = (SERVER_1_UUID, (CHAR_A, CHAR_B,),)
# 把服务1放入服务集合中
SERVICES = (SERVER_1, )
# 注册服务到gatts
((char_a, char_b),) = ble.gatts_register_services(SERVICES)
# 开启广播
ble.gap_advertise(
100,
adv_data = b'\x02\x01\x05\x05\x09x42\x69\x62\x69')
def ble_ieq(event, data): # 蓝牙中断函数
if event == 1: # 蓝牙已连接,设备进入连接态
led.off()
elif event == 2: # 蓝牙断开连接,进入就绪态
// 重新发起广播
ble.gap_advertise(
100,
adv_data = b'\x02\x01\x05\x05\x09x42\x69\x62\x69')
led.off()
ble.irq(ble_irq)
练习
蓝牙数据的收发
蓝牙的通讯是基于一个个特性实现的,每一个特性都可以被看作一个数据点。数据的收发都需要依托于这些数据点,对数据点的操作方法有如下五种类型。
- Read
- Write
- Write With No Response
- Notify
- Indicate
以蓝牙和手机通信作为讲解,设备作为从机,蓝牙作为主机,讲解特性
读操作:就是手机读取设备中某个特性的值 写操作:手机修改设备中某个特性的值,需要设备回应
Write With No Response与写操作类似,就是写完后不需要设备回应
Notify:设备中的某个值变化,同志手机读取某个特性的数据,需要在手机端,订阅相应的通知才有效。
Indicate: 与Notify操作类似,不同之处在于Indicate需要手机回应,Notify则不需要。
# 在中断函数中加入事件来处理这些
def ble_ieq(event, data): # 蓝牙中断函数
if event == 1: # 蓝牙已连接,设备进入连接态
pass
elif event == 2: # 蓝牙断开连接,进入就绪态
// 重新发起广播
ble.gap_advertise(
100,
adv_data = b'\x02\x01\x05\x05\x09x42\x69\x62\x69')
elif event == 3: # 收到Write请求时,触发该事件
conn_handle, char_handle = data
buffer = ble.gatts_read(char_handle) # 读取手机输入的数据
print(buffer) # 打印输入的数据
if buffer == b'A': # 如果受到字母A
ble.gatts_notify(0, char_handle, b'b') # 返回字母B, 手机端打开订阅功能才能受到字母B
总结
- 低功耗蓝牙通讯是基于特性完成的
- 常用的通讯方式有Read, Write, Notify
- 手机端订阅才能受到 Notify
作业
手机蓝牙控制 LED 亮灭 Write 字母A: LED亮 Write 字母B: LED灭
蓝牙组织联盟定义好的UUID
联盟已经定义好了常用的蓝牙设备UUID比如手表,心率,温度,湿度等。
想要知道这两个特性的值如何设置,需要查阅另一份文档GATT Specification Supplement
比如湿度 是16位无符号类型数据,单位为 0.01%,范围是 0.00-100.00
温度 是16位有符号类型数据, 单位为 0.01,范围是 -273.15-327.67
from ublutooth import BLE
ble = BLE()
ble.active(True)
SERVER_1_UUID = ubluetooth.UUID(0x081A)
CHAR_A_UUID = ubluetooth.UUID(0x2A6E)
CHAR_B_UUID = ubluetooth.UUID(0x2A6F)
# 设置蓝牙特性
CHAR_A = (CHAR_A_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE | ubluetooth.FLAG_NOTIFY,)
CHAR_B = (CHAR_B_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE,)
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.gatts_write(char_b,b'\x34\x12') # 小端模式 湿度数据 1234
ble.gatts_write(char_b,b'\x24\x10') # 小端模式 温度数据 1024
如何读取蓝牙电池电量信息呢??
SPP协议(蓝牙实现串口数据透传)
市面上的HC-05蓝牙透传模块,采用的就是经典蓝牙的SPP协议,低功耗蓝牙并未定义SPP协议标准
我们可以定义一个服务和特性,来实现类似于SPP的功能
import ubluetooth
from ubluetooth import BLE
from machine import UART, Pin
# 使用串口1
uart1 = UART(1, baudrate=115200, tx= 23, rx=22)
ble = BLE() # 创建蓝牙对象
ble.active(True) # 打开蓝牙
SPP_SERVER_UUID = ubluetooth.UUID('035892AB-EDCB-5678-4321-A1B2C3D4E5F6') # 服务UUID
SPP_CHAR_UUID = ubluetooth.UUID('035892AB-EDCB-5678-4321-A1B2C3D4E5F7') # 特性UUID
SPP_CHAR = (SPP_CHAR_UUID, ubluetooth.FLAG_WRITE | ubluetooth.FLAG_NOTIFY,) # 创建特性
SPP_SERVER = (SPP_SERVER_UUID, (SPP_CHAR,),) # 把特性A和特性B放入服务1
SERVICES = (SPP_SERVER,)
((spp_char,),) = ble.gatts_register_services(SERVICES) # 注册服务到gatts
def ble_irq(event,data):
if event ==1 :
elif event == 2:
elif event ==3: # Write事件
conn_handle, char_handle = data
buffer = ble.gatts_read(char_handle) # 读取数据
uart1.write(buffer) # 通过串口输出
while True:
if uart1.any !=0:
r = uart1.read() # 读取串口数据
ble.gatts_notify(0, spp_char, r) # 通过蓝牙Notify发送给手机,实现数据发送