
0 前言 自己的主要目的是学习BLE相关的知识,以及其具体的应用。因此不会像前一小节那样,组成那么长链路的系统。为最佳的学习BLE,就需要组成最小的闭环回路。# E* q6 O, ?" S* w4 V 1 最小闭环回路 最小闭环系统,就是STM32WB的开发板和笔记本组成的系统。现在基本的笔记本自带蓝牙。3 W! d5 ^1 p2 y: A, D: }: j" R 1 ![]() , G' G! _* o3 J: p" J! Y- o m, x# i4 s. g. ~1 l }- B! G: D : I9 A4 B5 a1 d% ]- V0 A! | H: P8 t 2 系统软件环境 在开发板中的软件使用的是:P-NUCLEO-WB55.Nucleo\Applications\BLE\BLE_HeartRate项目工程。 笔记本使用上位机是自己编写的python代码。 2 ![]() 我们将开发板当作外围设备。将笔记本作为中心设备。 3 连接实验; e; ?& G8 a2 Y. @ 3.1 发现设备 下载好单片机程序,给单片机上电。 在笔记本设置中打开蓝牙,并点击添加蓝牙。 3 ![]() & a$ E) S! R Z" n; |8 |! J+ X 可以看到我们的设备HRSTM。9 q) r+ K% Y: `1 A. g 接下来使用我编写好代码: import bleak.backends.dotnet.discovery as Discovery2 Q. z( M% ^, {6 u3 } import asyncio7 @1 C. n W8 A async def run():+ N* X& m6 a1 s+ D% v devices = await Discovery.discover()2 o( ?( B/ u$ g! G+ N9 s2 h! d for d in devices:# {% `0 H, B6 m4 _4 d2 W, J3 F print(d)$ Z! P5 U% Z- w3 k; O loop = asyncio.get_event_loop()0 W* R7 ?- \, u1 f } loop.run_until_complete(run())1 e# R. C1 ~. W q( ] 在输出窗口可以看到可以被发现了的蓝牙:4 l8 C+ [! m/ f1 | g; i B8:7C:6F:47:81:BF: MiKettle 6B:96:A5:AF:06:A7: Apple, Inc. (b'\x10\x05Q\x1c\x82.\xec')5 r* g% s; Z8 i 80:E1:26:00:68:7C: HRSTM $ [# L v; l6 |0 ^/ U; N+ | 我们也可以看到我们的蓝牙应用HRSTM。其中,一个很重要的东西就是设备的ID,这个是我们连接的一个关键,就和电话号码一样,而HRSTM就是持有人的别名。 3 l( ~+ b) @" V; W& e* F3 M. ~ ! ^- a4 a6 _- [( E3 \% ~ 3.2 连接和发现服务 接下来,我们应用上面的地址,来连接和发现一下HRSTM有哪些服务: async with BleakClient(address, loop=loop) as client: x = await client.is_connected() log.info("Connected: {0}".format(x)) log.info("[Service] {0}: {1}".format(service.uuid, service.description))! S6 d$ g) n' E& V( y% W: H for char in service.characteristics:* J. |, N. ]/ S4 B u. p) v if "read" in char.properties: try: value = bytes(await client.read_gatt_char(char.uuid)) except Exception as e: value = str(e).encode() else: H: B# C6 L' H2 v value = None log.info( "\t[Characteristic] {0}: ({1}) | Name: {2}, Value: {3} ".format($ U2 H6 d. o; K' d) u1 M) F char.uuid, ",".join(char.properties), char.description, value ) )' N$ R) [, K: X8 L/ R2 d! F for descriptor in char.descriptors: value = await client.read_gatt_descriptor(descriptor.handle)+ C* D/ W, @0 ^ log.info( "\t\t[Descriptor] {0}: (Handle: {1}) | Value: {2} ".format( descriptor.uuid, descriptor.handle, bytes(value) )$ X& K6 h! ]( | ) # A' H0 w) C. D# V: x2 } 输出的结果: Connected: True- k. J |6 v/ g' Y [Service] 00001801-0000-1000-8000-00805f9b34fb: Generic Attribute Profile [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb: (indicate) | Name: , Value: None0 f8 B4 H: B+ C5 o% o+ W6 Q$ ^ [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 4) | Value: b'\x02\x00' [Service] 00001800-0000-1000-8000-00805f9b34fb: Generic Access Profile6 \( J. i% D/ T9 P/ F0 ? [Characteristic] 00002a00-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'STM32WB'- j/ `, t% n9 Z; n2 J$ ~4 J [Characteristic] 00002a01-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'@\x03'5 L p+ d% E: I! c9 D, j7 Y i [Characteristic] 00002a04-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\xff\xff\xff\xff\x00\x00\xff\xff'! s& s, R+ W) S+ ? [Service] 0000180a-0000-1000-8000-00805f9b34fb: Device Information [Characteristic] 00002a29-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'STM\x00' [Service] 0000180d-0000-1000-8000-00805f9b34fb: Heart Rate1 L3 A. s9 }# C! ~$ k; X" v# \8 W' J [Characteristic] 00002a37-0000-1000-8000-00805f9b34fb: (notify) | Name: , Value: None( _4 [# I% J7 P$ `& U [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 18) | Value: b'\x00\x00' [Characteristic] 00002a38-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\x04' [Characteristic] 00002a39-0000-1000-8000-00805f9b34fb: (write) | Name: , Value: None 观察,返回的信息,我们大致可以通过,服务、特性、描述等知道其性能和功能。其中,发现UUID都是单片机中定义好的,在UUID.h 文件目录下: /* UUIDs for Heart Rate Service */ #define HEART_RATE_SERVICE_UUID (0x180D) #define CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID (0x2902), r- H$ R" @0 U- Q #define HEART_RATE_MEASURMENT_UUID (0x2A37)7 ]+ t$ y* [: q #define SENSOR_LOCATION_UUID (0x2A38) #define CONTROL_POINT_UUID (0x2A39)9 X: g$ y! Q) ?: g: d9 ]" X 3.3 通知和获取数据 通过STM的蓝牙软件,发现软件是有数据传递的。 4 ![]() ( ~ ~$ M) {5 Q/ E 其采用的方式是通知的方式,我是用了一下程序: - q% u* _+ F! G9 t% } def notification_handler(sender, data):; Q2 b& N6 j+ {! Z; W # strs=data.hex print(f"{sender}: {data}")( r* {% e7 l T byarray = bytearray(data)5 C' E% i4 d& P* k print(byarray.hex())) |, n) B! _) w' t5 n8 W ; r, ]4 r0 R: j, S! s1 b2 }/ y async def run(address, loop, debug=False): async with BleakClient(address, loop=loop) as client:0 Z1 Y, R3 J/ |# W x = await client.is_connected()1 u! l' ?8 z; o( B, ]/ p" y await client.start_notify(CHARACTERISTIC_UUID, notification_handler)! ?! ^+ V; t: z) L await asyncio.sleep(5.0, loop=loop) await client.stop_notify(CHARACTERISTIC_UUID)- q, v2 B/ {' K" t8 L8 x if __name__ == "__main__": if __name__ == "__main__": address = ('80:E1:26:00:68:7C') #你的地址( g8 y; J" s5 G1 `: ]. n# b loop = asyncio.get_event_loop() loop.run_until_complete(run(address, loop, True)), J9 O/ r; V: |, g) y$ |& f5 Z 返回了一下数据: 00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f7\x00\x14\x00\x00\x04')' g4 U; [7 ?0 {" Z1 \* x0 G 1f370014000004 00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f6\x00\x1e\x00\x00\x04') 1f36001e0000040 _. Z. |* v' S" I3 a/ o, F 00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f4\x00(\x00\x00\x04')8 e! s, m7 X, M: r0 ` 1f340028000004 + N5 k' F k* w 其中00002a37是HEART_RATE_MEASURMENT_UUID ,数据是十六进制的1f36001e000004,其中0x36是心跳,1e是能量消耗,我将能量变化修改为,每次+10,从每次读回的值就可以看到。 HRSAPP_Context.MeasurementvalueChar.EnergyExpended += 10; ! V% v, s+ g" b& x 4 小结: I7 h i) _! q 以上步骤,从发现蓝牙,连接蓝牙,获取蓝牙服务,以及获取数据。其实,实验中还有许多的小问题,比如指示灯的闪烁,为啥STM官方的APP可以直接连接,自己的却不行?, [* B6 G* F1 Z$ T) e! {3 V, r 我相信,在后面一步一步的代码解析,就会解释这些现象。3 Z2 v* `6 P, Y5 i- F |
Nucleo-STM32WB09KE测评-蓝牙调试并使用OLED显示数据
【NUCLEO-WB09KE评测】创建BLE工程和开启调试信息
【NUCLEO-WB09KE评测】+2.串口打印
【NUCLEO-WB09KE评测】+5.实现标准电量服务
【NUCLEO-WB09KE评测】BLE例程HeartRate及工具探索
【NUCLEO-WB09KE评测】+3.蓝牙广播
STM32WB系列MCU低功耗测试
Nucleo-STM32WB09KE测评--IIC传感器读取
【NUCLEO-WB09KE评测】+4.蓝牙控制LED
【NUCLEO-WB09KE评测】+1.点灯