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

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

[复制链接]
ziziran 发布时间:2020-5-23 16:48
0 前言
$ L% e* r, A3 @8 |2 F自己的主要目的是学习BLE相关的知识,以及其具体的应用。因此不会像前一小节那样,组成那么长链路的系统。为最佳的学习BLE,就需要组成最小的闭环回路。/ R7 K( U/ c0 W( V  C/ {/ b
9 D3 `- ?( N; X& o1 V
1 最小闭环回路% J( l) \4 s4 b2 p& x
最小闭环系统,就是STM32WB的开发板和笔记本组成的系统。现在基本的笔记本自带蓝牙。
: f' e6 z1 r7 g2 e) W

1

1

& ]4 G+ m3 @  t$ P% v( Z1 v- z- x6 d- _# q- f0 b
; ~9 R- v6 H0 E+ F
  x) T/ m; a( A$ z6 a4 j7 |
7 ~, a0 j! ~: f

1 U; r9 G! O" L' b9 u$ p2 系统软件环境
" V3 z5 }1 T8 V$ m  ]. C& R7 r在开发板中的软件使用的是:P-NUCLEO-WB55.Nucleo\Applications\BLE\BLE_HeartRate项目工程。
7 w' H6 S# i1 u  m: e笔记本使用上位机是自己编写的python代码。
  ~2 \9 o- ~+ ^9 A* _

2

2

( X8 |5 S' t" U* Q2 }: N. w/ X3 H0 D" f( S* I% |0 b; P+ x
我们将开发板当作外围设备。将笔记本作为中心设备。
8 t; W6 O8 n7 B/ [) o  g! |, d3 y
3 连接实验9 [2 M4 @' ]! Z- L/ {" x: f4 R; y; U
3.1 发现设备
( I. j  d, u& N9 E" L7 t* s- S3 h2 R下载好单片机程序,给单片机上电。$ y! \7 n# s! F0 b7 l3 L
在笔记本设置中打开蓝牙,并点击添加蓝牙。
3 w' f- ~; @5 g

3

3
2 q. H4 I- Q3 D( K
% @/ C2 Y/ ~) t" q- q

$ P  g  ]0 s; O# r$ [可以看到我们的设备HRSTM0 U2 t9 S# B; k7 A- r
接下来使用我编写好代码:& d1 f& p4 G( n% G  h
import bleak.backends.dotnet.discovery as Discovery
* i' J& s5 c7 R. w* `+ Iimport asyncio
1 s$ L7 h9 ]# `/ Z) ^async def run():* J% f% {* s7 L% }6 f. _8 g
    devices = await Discovery.discover()
0 H  \7 y+ A2 p    for d in devices:; u; }# M( y! I
        print(d)
! T0 |9 g, e6 ^3 Dloop = asyncio.get_event_loop()
+ d# x0 i3 ^' Bloop.run_until_complete(run())% [$ ~# _1 B0 T5 v3 G/ ^0 h3 F& s

" Q! E* |" A* m" f( H, O在输出窗口可以看到可以被发现了的蓝牙:
6 |% F3 K6 Q5 N) g" y6 Z' aB8:7C:6F:47:81:BF: MiKettle
+ H3 O( N/ r- m6B:96:A5:AF:06:A7: Apple, Inc. (b'\x10\x05Q\x1c\x82.\xec')! p1 i$ J; L3 Z% K5 C4 G
80:E1:26:00:68:7C: HRSTM
7 I+ N. k7 E; T, n1 E, U# e, d) ]: U/ ?9 r
我们也可以看到我们的蓝牙应用HRSTM。其中,一个很重要的东西就是设备的ID,这个是我们连接的一个关键,就和电话号码一样,而HRSTM就是持有人的别名。
; v( B) K+ a, B4 J+ N& J# L9 y* R- \+ L# |/ y8 Q
: r7 U$ ~/ N8 R6 h4 `
3.2 连接和发现服务
7 Q* h# @! p, b! w接下来,我们应用上面的地址,来连接和发现一下HRSTM有哪些服务:; b  D' A5 x7 k* T( o5 h" v% }- y
async with BleakClient(address, loop=loop) as client:
+ b. q2 H6 V% O9 ?" I2 H" B    x = await client.is_connected()& j2 l* N9 q# D9 ^8 y, K
    log.info("Connected: {0}".format(x))
3 {$ n5 [. p# R5 P5 I7 W( h" I# o( u        log.info("[Service] {0}: {1}".format(service.uuid, service.description))
' i, \7 @) r/ G/ r$ O) \% J        for char in service.characteristics:4 G7 }/ x; @1 S4 X0 ]+ d0 ~
            if "read" in char.properties:2 F4 q3 l9 Q7 K
                try:
  \2 e, C* o" x/ C7 a                    value = bytes(await client.read_gatt_char(char.uuid))
6 E  C) C0 }' x7 ~! v9 ~) `                except Exception as e:$ ]6 L6 Z/ t8 A. C
                    value = str(e).encode()2 @. M1 D; c& Y0 n- p9 G$ d
            else:
) O$ g, f* F+ I1 {                value = None
  R3 X) a8 e" h! z" Plog.info(5 b  {0 ~1 E( ?& o
                "\t[Characteristic] {0}: ({1}) | Name: {2}, Value: {3} ".format(
$ O7 t5 o1 r/ O$ J" O+ e0 R                    char.uuid, ",".join(char.properties), char.description, value5 b! P, l) q% w1 `6 Z* w
                )- ^: Y- y8 h/ Y( q& S4 e4 ]
            )
6 F( I3 o. W: H            for descriptor in char.descriptors:
6 B4 g8 t: c- w. q" ]) b( v                value = await client.read_gatt_descriptor(descriptor.handle)1 P3 g) @8 E- H7 ~+ e$ R9 K
                log.info(
+ u2 _" K8 j; S) \5 C                    "\t\t[Descriptor] {0}: (Handle: {1}) | Value: {2} ".format(
3 g% p, y$ ^4 |                        descriptor.uuid, descriptor.handle, bytes(value)
7 w8 i; x* M5 c( ~                    )
+ a8 ?2 ]: D& t. @3 t) }                )4 m  ?% J: m; ?3 b
' c8 g8 \9 _5 |% S
输出的结果:
5 T6 `6 l; g7 v4 e) [: p/ o# L0 oConnected: True
+ Q, r6 ^" D+ Y! K9 E. B: z[Service] 00001801-0000-1000-8000-00805f9b34fb: Generic Attribute Profile( `& e/ c. y6 O- ]& j0 Q2 K
         [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb: (indicate) | Name: , Value: None
0 y) C! S% H8 e  }# `                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 4) | Value: b'\x02\x00'
* O* v; ?0 ~+ S* {% V) H[Service] 00001800-0000-1000-8000-00805f9b34fb: Generic Access Profile, R5 B& C2 s; b6 v) ?0 g
         [Characteristic] 00002a00-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'STM32WB'
( \$ d' M1 y4 [+ w1 \         [Characteristic] 00002a01-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'@\x03'
! ^/ j$ @. \2 X8 h; z         [Characteristic] 00002a04-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\xff\xff\xff\xff\x00\x00\xff\xff'  G7 U9 H' e: V6 D; k
[Service] 0000180a-0000-1000-8000-00805f9b34fb: Device Information
1 K0 i5 |# Y  c! a         [Characteristic] 00002a29-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'STM\x00', p" F0 A$ z4 n& _
[Service] 0000180d-0000-1000-8000-00805f9b34fb: Heart Rate9 u5 N, O  a: T) B/ \1 N! M  _
         [Characteristic] 00002a37-0000-1000-8000-00805f9b34fb: (notify) | Name: , Value: None
; D+ i% C8 l" Y2 x0 g; X  s( g% R, t* p. ^                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 18) | Value: b'\x00\x00': A  r3 @% q, e1 _# h! M
         [Characteristic] 00002a38-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\x04'9 C1 h% g! b! E3 f
         [Characteristic] 00002a39-0000-1000-8000-00805f9b34fb: (write) | Name: , Value: None
9 \6 ]* R  w4 M
- @+ h4 H" f6 O. t$ N, T. U0 a观察,返回的信息,我们大致可以通过,服务、特性、描述等知道其性能和功能。其中,发现UUID都是单片机中定义好的,在UUID.h 文件目录下:
9 F$ [( b- U" g4 h5 n1 @. g- T/* UUIDs for Heart Rate Service */
0 M1 }, N6 S9 k  n$ G# h9 h#define HEART_RATE_SERVICE_UUID                                        (0x180D)6 c& L5 H7 T) \* s, `* L
#define CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID                   (0x2902)
7 L" ]) ]& L3 [" p1 X" \, W/ O2 \#define HEART_RATE_MEASURMENT_UUID                                   (0x2A37)
" [) \' ~* U* `& F( {#define SENSOR_LOCATION_UUID                                           (0x2A38)
. f% G3 j( G8 k7 M/ \( l) m/ P#define CONTROL_POINT_UUID                                             (0x2A39)& M' c$ [1 G( W* i
9 a, l5 X, }+ `5 p, r7 E
3.3 通知和获取数据! C7 S4 p% w- \3 b
通过STM的蓝牙软件,发现软件是有数据传递的。0 q; a9 K6 \8 D3 P' x+ `% w% e

4

4

" k% {7 s1 J) U: G+ l
3 P- b. \, o5 j1 K: h# W( Z$ J: i0 H+ B
其采用的方式是通知的方式,我是用了一下程序:
/ b+ Y0 K' s4 {0 d
# Q) _8 M, Y7 B- G& kdef notification_handler(sender, data):
: Q2 q% ]% L0 S; d! t9 A    # strs=data.hex! x0 ?9 I) Y  l( J4 P
    print(f"{sender}: {data}")3 l0 i0 h! N# ^- k7 F, \- o
    byarray = bytearray(data)
% G6 c* @& `/ C3 _7 o    print(byarray.hex())
) u! r7 p. O, ^' o5 l6 W1 U
- ~) p, e7 p! }( n9 c3 ]" j0 ^4 iasync def run(address, loop, debug=False):- E* Q" a' g/ t9 M1 O( L/ I
    async with BleakClient(address, loop=loop) as client:: E# Y+ c, E" V9 j/ ^. n
        x = await client.is_connected()9 |. l* C; g4 L; K" K. x5 B
        await client.start_notify(CHARACTERISTIC_UUID, notification_handler)# \* K% Q6 X  _1 a; t4 S
        await asyncio.sleep(5.0, loop=loop)
  l% Y" X" s3 o3 Q* N+ N        await client.stop_notify(CHARACTERISTIC_UUID)9 s" v' W# j! F( R- f7 @- q/ \
if __name__ == "__main__":8 Z% K0 ]; l  k6 Z3 V
if __name__ == "__main__":
6 V0 o3 b' L3 C: Y* C4 f    address = ('80:E1:26:00:68:7C')  #你的地址
* {, ~1 D- A1 @' n* T$ ]    loop = asyncio.get_event_loop()
( ~( `9 L" U, Q. @4 d4 I6 ^    loop.run_until_complete(run(address, loop, True))( e% N* W( ]$ X/ O; e6 e

2 n0 n; p& F- \8 q返回了一下数据:
* B7 Y1 H$ d3 ^+ {( v  o! @00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f7\x00\x14\x00\x00\x04')
+ L4 k$ R& B1 u1f370014000004  U7 |! m% D/ {
00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f6\x00\x1e\x00\x00\x04')- Z; ?; n( b7 |# K- G+ Y: a. S
1f36001e000004' v2 O! i6 X0 C" s+ T9 Q
00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f4\x00(\x00\x00\x04')# t0 D6 r* q9 I2 d4 Y
1f340028000004
. p- W- A# _( B# w1 ~6 b: m2 ~1 t4 u+ B* y
其中00002a37HEART_RATE_MEASURMENT_UUID ,数据是十六进制的1f36001e000004,其中0x36是心跳,1e是能量消耗,我将能量变化修改为,每次+10,从每次读回的值就可以看到。
$ j8 a4 M$ z9 Q3 w6 m$ a3 RHRSAPP_Context.MeasurementvalueChar.EnergyExpended += 10;8 f& ^" z( B2 x, |) R

3 c( B; A* e: y! o! b) u5 Z8 K: G 4 小结
( V- G  Q# O  ^0 Q1 `! U4 m7 S以上步骤,从发现蓝牙,连接蓝牙,获取蓝牙服务,以及获取数据。其实,实验中还有许多的小问题,比如指示灯的闪烁,为啥STM官方的APP可以直接连接,自己的却不行?: |5 N& Q' ?# \9 w
我相信,在后面一步一步的代码解析,就会解释这些现象。7 m- }3 m5 s: W
' q2 i( v4 Z9 c! ^/ X5 s

# [  R/ R* ~6 N9 m
7 m9 v& @0 X0 j: H
5 ^- R! }; [$ @7 p

& i3 s  d; J4 p* _: t* {' p
收藏 评论0 发布时间:2020-5-23 16:48

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版