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

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

[复制链接]
ziziran 发布时间:2020-5-23 16:48
0 前言6 G4 i5 e9 k5 T) }6 t# v3 k
自己的主要目的是学习BLE相关的知识,以及其具体的应用。因此不会像前一小节那样,组成那么长链路的系统。为最佳的学习BLE,就需要组成最小的闭环回路。# e% V  @/ `/ g+ V7 W
" N3 b; O; A/ C
1 最小闭环回路
5 g/ v, o* D, }最小闭环系统,就是STM32WB的开发板和笔记本组成的系统。现在基本的笔记本自带蓝牙。
! [8 ?2 [* c7 \2 d, ~

1

1
7 b7 J4 c0 M1 p- G/ r

* C/ }- \3 N7 u& y
$ s& u) J9 i5 B7 t9 s# V  P

( S2 M8 }( y5 q/ P1 s, Y' H8 A0 b* [

# W* w$ l$ w& U2 系统软件环境( a0 K: x5 q; P% U. [( ?6 k# o
在开发板中的软件使用的是:P-NUCLEO-WB55.Nucleo\Applications\BLE\BLE_HeartRate项目工程。
2 o. M) a, x  D3 D7 K5 _1 @笔记本使用上位机是自己编写的python代码。
" |- O" ~5 A9 |& B2 Y+ n/ c' I* ^" K

2

2
, r9 b. ]$ l, w) H8 }( x
, R4 |, R. w/ e2 D% @5 m" A# U; V8 C
我们将开发板当作外围设备。将笔记本作为中心设备。( ]! y3 y2 X. ?4 r* W) N' a/ e) W4 j
7 T1 Z* T- R* b4 T
3 连接实验
+ v5 x# O  W: X* D3.1 发现设备
. _& W. i+ s0 `1 J下载好单片机程序,给单片机上电。/ ]% ]2 ~3 W/ j6 c
在笔记本设置中打开蓝牙,并点击添加蓝牙。: n+ y9 l. o0 E( |

3

3

$ n8 U( g5 [( N" w+ r
! b" v& a3 d/ P4 g4 o2 l. \5 j# [; Y8 ]- {$ q  h7 X# l7 a# j% Q( k
可以看到我们的设备HRSTM
  G0 r1 {- K* [' `- j5 N接下来使用我编写好代码:
- N8 T6 S$ r: Q* I. ^0 V. |import bleak.backends.dotnet.discovery as Discovery( G* ~* c$ X/ O- G; s& q
import asyncio  O5 V4 Z5 L: V7 P$ _
async def run():0 [) y3 @, t! `+ s/ `5 M8 v) ~* q
    devices = await Discovery.discover()! @. T: ^- m$ Q0 @+ {( `6 F/ |# p
    for d in devices:/ W) _0 A; F8 H! i9 j$ {* a# F
        print(d)' R; x( A! i7 E
loop = asyncio.get_event_loop(), b/ D' I3 e8 }$ C5 B  N6 [
loop.run_until_complete(run())
( ~0 x3 n5 g4 ]# K
- L$ v/ p; O2 Y在输出窗口可以看到可以被发现了的蓝牙:) s- m  ~( ^$ e  M$ t& |
B8:7C:6F:47:81:BF: MiKettle
) Y( h. I$ X3 g6B:96:A5:AF:06:A7: Apple, Inc. (b'\x10\x05Q\x1c\x82.\xec')
, v; A7 J9 j% y80:E1:26:00:68:7C: HRSTM
3 Y, y, S( }" I7 X# D' w; Z
- i  ]6 Q/ U; L我们也可以看到我们的蓝牙应用HRSTM。其中,一个很重要的东西就是设备的ID,这个是我们连接的一个关键,就和电话号码一样,而HRSTM就是持有人的别名。. i9 Y3 N# G% A0 M' X4 {
8 x6 ~9 {/ v1 Y
& z, U% ^( Q) t8 F
3.2 连接和发现服务
" c- W# q2 @5 \) t- M& i4 N接下来,我们应用上面的地址,来连接和发现一下HRSTM有哪些服务:; S/ a* x- v2 _7 M' R
async with BleakClient(address, loop=loop) as client:& f, K& U+ k- e$ L4 v4 ?# t4 f
    x = await client.is_connected()
% S) m- [& z: o! _' o* p+ M! s8 W" @    log.info("Connected: {0}".format(x))
/ x, f' n8 S  J, M* u( e' I        log.info("[Service] {0}: {1}".format(service.uuid, service.description))
  @: J8 B- k' v; _( T8 ~/ A        for char in service.characteristics:% h; t% ]0 X% F8 Q! ]0 |
            if "read" in char.properties:8 [' T. o' w8 B: s# y$ x
                try:. D3 Y8 v+ f# e# b1 T
                    value = bytes(await client.read_gatt_char(char.uuid))% T! {1 U& c4 T3 R
                except Exception as e:
' w; Z" f" l8 ?) F                    value = str(e).encode()
3 o! r3 p# C: c5 a3 ?6 q+ q            else:; v& q# P+ I: x! u5 H9 ]. {
                value = None
) U4 e) c+ I6 _4 U$ A' O8 i% S6 Zlog.info(! ]9 z  d) C4 p/ H( Z
                "\t[Characteristic] {0}: ({1}) | Name: {2}, Value: {3} ".format(: e4 B) W3 i: s4 @
                    char.uuid, ",".join(char.properties), char.description, value
2 @5 h9 k9 h* R                )4 ]. \9 O7 K! E' m; P& e- s3 T( h
            ), }# w- {$ P8 s) W
            for descriptor in char.descriptors:
: Z1 v& @2 X8 V1 p$ x" V- b3 E                value = await client.read_gatt_descriptor(descriptor.handle)  c# R) y3 ^, q9 ~6 c9 ~8 w4 Q1 @8 f
                log.info(+ _* F: r# M- z% f( i/ h
                    "\t\t[Descriptor] {0}: (Handle: {1}) | Value: {2} ".format(
' H, `; ~$ C/ E& v                        descriptor.uuid, descriptor.handle, bytes(value)
7 \/ e, n- V% p2 x! ~                    )4 H# v. p2 s/ F  |, F! G1 h
                )4 N: R* L' ~9 L% ]' L2 O7 J1 g# J$ t( m9 n
: D/ E- b9 j3 ]& L7 t
输出的结果:
1 N$ j7 K+ z+ D5 A" m# T1 c) zConnected: True
, T3 [+ n9 d" Y9 ?4 V[Service] 00001801-0000-1000-8000-00805f9b34fb: Generic Attribute Profile
: v" C/ y$ ]( F) ]. m: O8 Z* W/ c         [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb: (indicate) | Name: , Value: None
, x# z( D" c4 Z                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 4) | Value: b'\x02\x00'1 z8 K: _, s- V3 D: ]8 J
[Service] 00001800-0000-1000-8000-00805f9b34fb: Generic Access Profile8 a- ~7 z! \0 u! {& M8 |
         [Characteristic] 00002a00-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'STM32WB'- b; h. U- G; `
         [Characteristic] 00002a01-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'@\x03'5 }4 [+ Y4 S4 G2 U% y" D5 x$ W+ D
         [Characteristic] 00002a04-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\xff\xff\xff\xff\x00\x00\xff\xff'; ?8 d) O) L& s/ Z2 g
[Service] 0000180a-0000-1000-8000-00805f9b34fb: Device Information4 f3 o$ I2 J7 ?& e+ L9 J
         [Characteristic] 00002a29-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'STM\x00'
+ A8 ~  g, s- `5 ^3 c7 Q/ A( ][Service] 0000180d-0000-1000-8000-00805f9b34fb: Heart Rate; ?/ C! ?. B6 p7 Z% [3 C9 r
         [Characteristic] 00002a37-0000-1000-8000-00805f9b34fb: (notify) | Name: , Value: None( [0 @/ W/ t* t) u
                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 18) | Value: b'\x00\x00'
. T, e# b/ b' ]' m7 k1 W         [Characteristic] 00002a38-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\x04'2 |, N- Y1 o3 J' ~" o) F# V
         [Characteristic] 00002a39-0000-1000-8000-00805f9b34fb: (write) | Name: , Value: None' ]0 ?1 O; J6 \

+ w8 C. e/ S  R  |  l  I& G观察,返回的信息,我们大致可以通过,服务、特性、描述等知道其性能和功能。其中,发现UUID都是单片机中定义好的,在UUID.h 文件目录下:
2 C# E- }5 l+ Y/* UUIDs for Heart Rate Service */, s9 A6 z6 L9 J( H
#define HEART_RATE_SERVICE_UUID                                        (0x180D)& v6 n) r2 v& I- h2 `% l2 B3 I5 j
#define CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID                   (0x2902)' c* i- b1 @7 S  R$ x
#define HEART_RATE_MEASURMENT_UUID                                   (0x2A37)' f0 j+ F( O& Q( D0 C
#define SENSOR_LOCATION_UUID                                           (0x2A38), p! Y) @& S: E+ v% c8 n5 h
#define CONTROL_POINT_UUID                                             (0x2A39)
( f1 S8 E2 `/ Q$ N+ ]$ _9 @  Q! ?, M" j
3.3 通知和获取数据7 x8 u+ @/ j6 X8 n3 J( A
通过STM的蓝牙软件,发现软件是有数据传递的。
, P- F2 ~7 P: z. L

4

4
& b7 N( u9 s+ v* `3 F- N

6 y/ m4 D" W( k  U0 u: ^! B# i* `3 @, U# A1 h& V
其采用的方式是通知的方式,我是用了一下程序:
/ {. P6 t7 D" u' a  A5 I& L
, C) R  o, j5 ]8 Ddef notification_handler(sender, data):
! ?6 n2 H+ m4 @; P/ \    # strs=data.hex; H4 t. P. c2 @9 b
    print(f"{sender}: {data}")
4 [- A4 D& d- R- A    byarray = bytearray(data)
+ L$ B4 E4 J  x$ D7 v, a$ g, j2 Q    print(byarray.hex())
- R' A5 `" i6 I/ v7 c% w0 i6 [1 T: I- ]7 j0 `7 F/ x: u( z
async def run(address, loop, debug=False):
! f9 B! \# y: _, h; w# `2 `% a    async with BleakClient(address, loop=loop) as client:$ R- L3 l6 r/ \0 e0 P( y
        x = await client.is_connected()/ Y8 }. c- D, E; Q
        await client.start_notify(CHARACTERISTIC_UUID, notification_handler)) p2 e# p' [2 J. g
        await asyncio.sleep(5.0, loop=loop)
  q  B3 R- }+ O5 u        await client.stop_notify(CHARACTERISTIC_UUID)
# {3 N- W/ `7 t) g8 y4 e  |( hif __name__ == "__main__":# P! O$ q, E1 ^, U$ C. j' k
if __name__ == "__main__":
* }& n5 I9 j! ?- ?/ v    address = ('80:E1:26:00:68:7C')  #你的地址, S  ~9 R/ y, y# x; I: Q
    loop = asyncio.get_event_loop()
+ ^# `. i: K) X+ Z& X" q6 g6 f8 K& P    loop.run_until_complete(run(address, loop, True))' m2 e% ^9 C, g  `
9 o! o/ _3 g5 V7 j. J5 `' H7 p5 @
返回了一下数据:
* p' e, u3 B  A( O+ R: ^00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f7\x00\x14\x00\x00\x04')' f1 v  G& y3 M1 m" U
1f370014000004
8 ?( U  ?1 \6 o' d00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f6\x00\x1e\x00\x00\x04')2 g. g) \" q& s0 c
1f36001e000004
1 U/ \  c" |# Y% g" D: ^3 i0 w; |00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f4\x00(\x00\x00\x04')2 f% T/ d- g& X4 r3 X2 L
1f340028000004: `: v8 z( {! k; K

4 K' K+ v' M# U6 `其中00002a37HEART_RATE_MEASURMENT_UUID ,数据是十六进制的1f36001e000004,其中0x36是心跳,1e是能量消耗,我将能量变化修改为,每次+10,从每次读回的值就可以看到。5 x" V( i: E1 Z6 n0 o% K9 ~. }
HRSAPP_Context.MeasurementvalueChar.EnergyExpended += 10;% A/ G# p# u$ [; Y( ^6 @1 S; ^; e
6 y1 c( y  q, U0 g, \  C' C: I
4 小结6 l2 C* X! F5 K  ?1 Q
以上步骤,从发现蓝牙,连接蓝牙,获取蓝牙服务,以及获取数据。其实,实验中还有许多的小问题,比如指示灯的闪烁,为啥STM官方的APP可以直接连接,自己的却不行?6 x$ d0 s& G: D: |" H2 j
我相信,在后面一步一步的代码解析,就会解释这些现象。
( v, i3 M: y! q, J* e4 s4 u% Y, u- M5 b0 M3 ]6 j
0 e. x9 ~9 u. Q+ p% r, U! ?7 |0 C# o
: x$ x1 l' R% O* T

  ~4 w# d- ^3 p( [+ s9 O. Z% C$ E( b+ }# |9 D: S3 I9 N% {9 k
收藏 评论0 发布时间:2020-5-23 16:48

举报

0个回答

所属标签

相似分享

官网相关资源

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