你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

[STM32WB 蓝牙探学] 2 BLE的连接与操作

[复制链接]
ziziran 发布时间:2020-5-23 16:48
0 前言6 N/ C) B% N' i( Q) E  @+ W
自己的主要目的是学习BLE相关的知识,以及其具体的应用。因此不会像前一小节那样,组成那么长链路的系统。为最佳的学习BLE,就需要组成最小的闭环回路。9 [3 L% }$ j7 v/ |4 O9 C

* ]+ q' r6 t: W; G8 z& S1 最小闭环回路& i; b" N6 `$ C
最小闭环系统,就是STM32WB的开发板和笔记本组成的系统。现在基本的笔记本自带蓝牙。
# v$ c4 T0 K& N! A* k

1

1

9 r  s8 g/ r6 g& L, c) t8 ]: i: o- ^
! n3 h1 B* S2 X, R8 \& v" a

. D+ q& O1 a9 h( ~4 z; I- M* Q* l0 Q

) H  @1 I- u2 S9 N. Q* w2 系统软件环境& j" P- [" t: h8 I- E8 ]
在开发板中的软件使用的是:P-NUCLEO-WB55.Nucleo\Applications\BLE\BLE_HeartRate项目工程。3 r! \$ h9 Y0 \1 I6 a) I
笔记本使用上位机是自己编写的python代码。5 F9 g0 x' s* I$ N7 e: q

2

2
- `8 `- m! J* L  J

) S: n4 }8 T  d我们将开发板当作外围设备。将笔记本作为中心设备。% W; i- l* b5 c4 z
2 n( p. m, y1 S1 B; b
3 连接实验$ B5 {+ i: O& G0 h' j
3.1 发现设备  |) }' t/ G" Y% G# Q; N% p
下载好单片机程序,给单片机上电。
- Q4 |7 k! y  E在笔记本设置中打开蓝牙,并点击添加蓝牙。) g2 I" S, q4 \, b! i* ~7 J8 p6 e1 v2 Q

3

3

* W; D( @7 a5 l) r
6 Y& b0 K3 W$ H  r5 q' v2 j* O8 Q# L5 R! G! ]# S6 A6 {
可以看到我们的设备HRSTM# U; M+ P/ d" L
接下来使用我编写好代码:7 O/ D0 x: H% y, {; e% f) a
import bleak.backends.dotnet.discovery as Discovery
+ c; n+ ?& d1 K& v7 {import asyncio
% k5 U. q+ k1 i! n' Uasync def run():) H# y* b+ \6 s  _
    devices = await Discovery.discover()' G4 f0 h0 A6 u' k4 ~8 T
    for d in devices:
2 j. v& K; t: q) e6 ]( u& ?        print(d)
' @- \5 m6 |! _! ~9 |loop = asyncio.get_event_loop()) R! n' g& F! t, f9 ~# b
loop.run_until_complete(run())2 w  P& b. S$ F  y& `# [
) ~' P; O$ N8 Y: K% L+ l
在输出窗口可以看到可以被发现了的蓝牙:
# t% W; u- `# f% m4 IB8:7C:6F:47:81:BF: MiKettle& l- v/ D# A- Y/ Q9 h
6B:96:A5:AF:06:A7: Apple, Inc. (b'\x10\x05Q\x1c\x82.\xec')6 [( Y3 ~2 l% F9 \
80:E1:26:00:68:7C: HRSTM
- ~6 q$ R# F6 j
8 c/ r* p& v  r- @, V我们也可以看到我们的蓝牙应用HRSTM。其中,一个很重要的东西就是设备的ID,这个是我们连接的一个关键,就和电话号码一样,而HRSTM就是持有人的别名。
2 j8 }" ~' G9 I0 Q
7 b: c4 G( z$ B( n4 p& ]  V& m' P% l: u4 _3 D
3.2 连接和发现服务2 p. [' U" J  c+ e
接下来,我们应用上面的地址,来连接和发现一下HRSTM有哪些服务:
/ S6 L9 y* ]3 x" _7 iasync with BleakClient(address, loop=loop) as client:( `& C; H& d' l# u4 w
    x = await client.is_connected()  A5 B, D1 J" S! r7 g" Z
    log.info("Connected: {0}".format(x))" m+ t9 T; e: j# s# `& S( w
        log.info("[Service] {0}: {1}".format(service.uuid, service.description))
3 m; p7 G3 C4 U  _        for char in service.characteristics:
: f: R4 }1 S. y( X& O9 M- S2 D/ F            if "read" in char.properties:
* c# B* I2 C) `1 o/ f                try:
8 c7 C- O8 [! D2 C, \                    value = bytes(await client.read_gatt_char(char.uuid))5 }+ \& i9 p* z" ]
                except Exception as e:
( b! m2 x( o! l: h. [                    value = str(e).encode()
1 ]% h* c$ F* h, u7 I            else:) F8 H( V, O# X% ]! w
                value = None- e7 U( @" r+ a" W
log.info(/ o0 v5 [" n* r: I( l1 q
                "\t[Characteristic] {0}: ({1}) | Name: {2}, Value: {3} ".format(+ f9 e7 z3 l+ @; N8 y# J
                    char.uuid, ",".join(char.properties), char.description, value  M0 D; R) @' ^- B
                )7 t, R1 a  f% Z& H
            )# B4 p6 m8 c7 E8 G! G
            for descriptor in char.descriptors:2 c9 B9 \8 y7 n* f8 X2 P
                value = await client.read_gatt_descriptor(descriptor.handle)8 [" x! w8 V; F; c( a9 i& b- t
                log.info(* c: ~) p" }* y3 c6 P2 ~
                    "\t\t[Descriptor] {0}: (Handle: {1}) | Value: {2} ".format(
3 q+ S  @: W1 Y) J                        descriptor.uuid, descriptor.handle, bytes(value)
+ p0 U- ]* o& J" i6 F                    )* J# o$ N: v/ S2 J5 }, J0 T! \
                )
/ E7 P7 t5 n% c( @, q3 R5 b5 n- [. A0 S' e3 @) ^
输出的结果:
8 N  U& x7 B1 F) RConnected: True
' t% }/ @) _% _[Service] 00001801-0000-1000-8000-00805f9b34fb: Generic Attribute Profile% z0 H+ S+ m) V! x& k# H
         [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb: (indicate) | Name: , Value: None
$ {2 t8 f( H3 f* S" O$ W; A; }                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 4) | Value: b'\x02\x00'' S$ ]" m( q/ q+ ?9 m
[Service] 00001800-0000-1000-8000-00805f9b34fb: Generic Access Profile
- }1 F9 Q8 `* C8 ]- J' x         [Characteristic] 00002a00-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'STM32WB'8 b  T' ]5 ~1 c# m
         [Characteristic] 00002a01-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'@\x03'
. u/ N  \7 X; _6 N         [Characteristic] 00002a04-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\xff\xff\xff\xff\x00\x00\xff\xff'" H! y/ U3 @5 {: }) X4 i0 r
[Service] 0000180a-0000-1000-8000-00805f9b34fb: Device Information# ]* j  }. t8 T
         [Characteristic] 00002a29-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'STM\x00'
- \' P/ p2 {9 ~0 l" W3 s[Service] 0000180d-0000-1000-8000-00805f9b34fb: Heart Rate7 f+ `$ X& s* o7 J
         [Characteristic] 00002a37-0000-1000-8000-00805f9b34fb: (notify) | Name: , Value: None
5 ?- J, V4 W+ y6 }$ a+ S                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 18) | Value: b'\x00\x00'
! ]! ~) q& [$ ^) d$ {) s4 y+ V5 D         [Characteristic] 00002a38-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\x04'7 v  E! l& K/ b5 z. q5 h
         [Characteristic] 00002a39-0000-1000-8000-00805f9b34fb: (write) | Name: , Value: None  M7 Y" A) N( H
0 V, b& o( b% u+ o+ }, t( D
观察,返回的信息,我们大致可以通过,服务、特性、描述等知道其性能和功能。其中,发现UUID都是单片机中定义好的,在UUID.h 文件目录下:
6 q1 X9 h/ G6 j% {) Z* e, e1 \0 j/* UUIDs for Heart Rate Service */
+ Q) |: U; K* i% f#define HEART_RATE_SERVICE_UUID                                        (0x180D)/ ^- }: N2 X6 Q0 ?3 U7 I: q5 J
#define CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID                   (0x2902)
7 T6 P# Y2 t4 M. F#define HEART_RATE_MEASURMENT_UUID                                   (0x2A37)1 c( `5 g" v  y2 E$ W" @
#define SENSOR_LOCATION_UUID                                           (0x2A38)
7 D5 ?9 k: Q7 |+ ~#define CONTROL_POINT_UUID                                             (0x2A39)$ m% a: a. h4 ?. j$ s

7 M; r* f- H! u7 D6 m3.3 通知和获取数据: f, d$ A1 H7 w" y% S
通过STM的蓝牙软件,发现软件是有数据传递的。
* ]; c( |2 T! L- D, x# f

4

4
. f: h* ~& x/ U: R8 w# R3 n3 Q

# y, u  F* A$ t5 ^' s6 ]8 C% F3 ?- S9 h
其采用的方式是通知的方式,我是用了一下程序:
4 h+ Y% O+ W1 B# F7 M, W' J
8 [# {7 F8 r' m" ydef notification_handler(sender, data):
3 j1 r8 h' h2 n, s2 P    # strs=data.hex$ }3 d  v# D. c6 i" a9 i* w
    print(f"{sender}: {data}")8 _% s& ~: e% X9 x  c7 U
    byarray = bytearray(data)" a/ W+ |" F9 C, @: z
    print(byarray.hex())4 d# b+ G4 U3 N# M: s+ Q3 Y
# j/ w* F8 Y2 ?
async def run(address, loop, debug=False):# I7 I/ F5 g1 U& f& ^
    async with BleakClient(address, loop=loop) as client:" ^8 k/ w# G( p( e% O( z; `: s
        x = await client.is_connected()8 L3 P% b- P8 b& S7 N
        await client.start_notify(CHARACTERISTIC_UUID, notification_handler)
1 B! ?. J- _" v7 y! R! c* o' E4 b        await asyncio.sleep(5.0, loop=loop)
1 Z( C) R. f% \0 N1 |$ j( N        await client.stop_notify(CHARACTERISTIC_UUID)$ b; f. I- M7 S2 X2 u/ U0 P: {0 ]
if __name__ == "__main__":5 N3 a7 C3 @: D: b5 _8 r6 t1 r: C
if __name__ == "__main__":  A9 `# M2 L# ^( F: l0 `# A3 a/ k* D
    address = ('80:E1:26:00:68:7C')  #你的地址+ R' b6 A# A6 o" K( z8 O. r
    loop = asyncio.get_event_loop()* f. D1 _2 |, W/ f" `' Q; d; ?
    loop.run_until_complete(run(address, loop, True))0 M$ v' P3 a# M

. @" G$ A$ ~% B! E4 ]! z7 _3 t返回了一下数据:4 Z- B# ]/ g/ x2 W; h0 K
00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f7\x00\x14\x00\x00\x04')) c3 U( n8 L* x8 Y# D6 R+ S8 _
1f370014000004
. f: l$ U; k0 H! C00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f6\x00\x1e\x00\x00\x04')
  h- x& {8 {/ }" Y! G; v3 l1f36001e000004
9 [7 a9 |( R  f00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f4\x00(\x00\x00\x04')
5 E% C9 b! `. s. H1f340028000004
/ q& ?) U' \/ C8 l  N7 ^
6 @# V6 B; o, {1 t其中00002a37HEART_RATE_MEASURMENT_UUID ,数据是十六进制的1f36001e000004,其中0x36是心跳,1e是能量消耗,我将能量变化修改为,每次+10,从每次读回的值就可以看到。: g/ R# @! f# ?: u  e) K
HRSAPP_Context.MeasurementvalueChar.EnergyExpended += 10;
4 E6 c' J' J+ i; T' W
) W2 ]  G3 W3 Q' P/ Q( @" @; Q 4 小结* ^7 W# m! m5 x9 f6 R0 K3 P8 q
以上步骤,从发现蓝牙,连接蓝牙,获取蓝牙服务,以及获取数据。其实,实验中还有许多的小问题,比如指示灯的闪烁,为啥STM官方的APP可以直接连接,自己的却不行?0 R4 Q& {  ?# p! c) x
我相信,在后面一步一步的代码解析,就会解释这些现象。2 S1 |% m) ~7 b0 f( C6 ~4 c
4 Y' H8 A2 z! K+ c. x& R1 s  l

  p5 p: ]! u5 o( N2 q" m$ C/ L5 G' d# B2 ]$ ~1 Y
  \7 n2 j# F6 C0 ?8 D% w

" V" {+ V1 T* n/ s1 U
收藏 评论0 发布时间:2020-5-23 16:48

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版