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

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

[复制链接]
ziziran 发布时间:2020-5-23 16:48
0 前言
4 s3 I/ a% i+ W/ V自己的主要目的是学习BLE相关的知识,以及其具体的应用。因此不会像前一小节那样,组成那么长链路的系统。为最佳的学习BLE,就需要组成最小的闭环回路。' q+ x) ^" E2 h( A# u" Z

- q$ D! |' [/ C1 |& K1 最小闭环回路& k$ A. c+ P, t+ y% T
最小闭环系统,就是STM32WB的开发板和笔记本组成的系统。现在基本的笔记本自带蓝牙。
* c4 o3 E3 X- _) ~0 d, ^

1

1
9 K8 r/ n. }* I1 \  J* S
; l; N( `  h  I( e
) p9 e0 {+ P$ N4 [
; Y( u: A5 N2 U" w9 r& v( K
# g: V$ d1 X" R) q
* `% [1 G2 J; E+ b
2 系统软件环境
, [" w' c6 F$ g在开发板中的软件使用的是:P-NUCLEO-WB55.Nucleo\Applications\BLE\BLE_HeartRate项目工程。2 l2 |6 `% Q: D9 s2 F. H
笔记本使用上位机是自己编写的python代码。
+ `$ H1 h, U4 v- e- ^9 m* X; ^

2

2
$ u& u" w7 k5 C

  K! b  @9 [1 T/ q+ _( y我们将开发板当作外围设备。将笔记本作为中心设备。5 ~* _2 o+ y+ v
: v* [7 q# E$ x) b1 M! w# F& T( L
3 连接实验
6 ~& p! R. w; T5 V) M/ K3.1 发现设备
) h, ?, a( d7 I/ d3 ?下载好单片机程序,给单片机上电。& p; r, _" Q! t& m; R7 b& @+ }
在笔记本设置中打开蓝牙,并点击添加蓝牙。
9 h5 \, Q) D' e

3

3
& M9 g* h4 N& V- g

( p+ O% Y) o( k6 [
/ a/ i7 w% k  z. ^% N2 w2 j可以看到我们的设备HRSTM
3 _/ Q/ _8 }  r$ X2 y* Y; _3 m  @* s接下来使用我编写好代码:0 q8 W2 ]- L5 f+ I7 O
import bleak.backends.dotnet.discovery as Discovery; e$ b) B; y  q
import asyncio! s& o' w) K& v
async def run():
0 a, o+ n; j& o2 x    devices = await Discovery.discover()6 m4 [* U/ r8 E8 j8 f9 X
    for d in devices:
6 \9 _5 [8 x# Y' x# U% F+ ^+ r        print(d)2 i" w% Q! y- e. R/ b6 [1 R5 L8 F( \
loop = asyncio.get_event_loop()5 e4 y, r/ D! _7 b
loop.run_until_complete(run())
& ?. E6 T* c5 @! T
" N% s2 j7 d4 E; C4 r* e' m在输出窗口可以看到可以被发现了的蓝牙:
- S% X. B. X' b) mB8:7C:6F:47:81:BF: MiKettle  G" a8 V" V" ]8 r
6B:96:A5:AF:06:A7: Apple, Inc. (b'\x10\x05Q\x1c\x82.\xec')$ f$ h0 l5 W# o$ O% Z! }/ p
80:E1:26:00:68:7C: HRSTM
+ c! N+ Q$ E; @- H" O' D8 H7 p, y' W  J# h5 N
我们也可以看到我们的蓝牙应用HRSTM。其中,一个很重要的东西就是设备的ID,这个是我们连接的一个关键,就和电话号码一样,而HRSTM就是持有人的别名。
  \1 u' S# w% O3 T8 B+ e3 t1 B% c- c7 y2 i

* j) {7 y& k6 I" b! W3.2 连接和发现服务% z0 ?2 K. Q: y
接下来,我们应用上面的地址,来连接和发现一下HRSTM有哪些服务:
: }" U3 Y6 F  l/ ]0 g7 ^1 nasync with BleakClient(address, loop=loop) as client:1 @1 p  B7 F+ V. q4 B3 U
    x = await client.is_connected()# t, w+ O/ f3 Q8 {1 o. z
    log.info("Connected: {0}".format(x))( R4 c! u! |8 t3 t" N
        log.info("[Service] {0}: {1}".format(service.uuid, service.description))
, t& j, p. I2 i+ l        for char in service.characteristics:
$ M2 \: N& Y4 m  \" T            if "read" in char.properties:4 R! e. t. L! A4 M, m5 d
                try:& R$ ~# f! A/ `
                    value = bytes(await client.read_gatt_char(char.uuid))  S+ M5 L3 s) f
                except Exception as e:
+ M* ?/ Z2 N5 V& H! E                    value = str(e).encode()8 L' e8 e$ Z& t- A, k  F  Q0 O- s4 p5 T
            else:
; H: a. M' e! i                value = None, ~; K' D. g+ ]  e+ \7 S
log.info(
, E3 M; x2 r1 u8 M1 f$ L# B0 m                "\t[Characteristic] {0}: ({1}) | Name: {2}, Value: {3} ".format(! L0 n' u- K7 P* O3 r$ ]
                    char.uuid, ",".join(char.properties), char.description, value
$ D+ g+ o$ y- k8 W) N( {3 r                )4 p* b! |2 F# ~9 W3 u
            )# v+ m- L9 d7 ~: {
            for descriptor in char.descriptors:
! E% Z) r( b9 |2 n& }; r! F                value = await client.read_gatt_descriptor(descriptor.handle). \* M# P' B9 B( i, H
                log.info(
' E3 O- g2 Z4 z8 h+ ]2 F; q$ y                    "\t\t[Descriptor] {0}: (Handle: {1}) | Value: {2} ".format(. M/ A! k5 X2 a2 L2 l: l6 |6 z
                        descriptor.uuid, descriptor.handle, bytes(value)6 ]% Q% k% N- h/ [9 d; @
                    )
# O: C& `' f# Q  M4 {                )
4 N6 m0 c9 y5 d; e% q( `) Q' a$ I" A8 A. M; U' d
输出的结果:
, e' f+ G/ y# Q7 l, M/ h( XConnected: True7 i1 g  b4 |. e+ t# ?8 l
[Service] 00001801-0000-1000-8000-00805f9b34fb: Generic Attribute Profile: _" z7 g6 }5 ?$ W
         [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb: (indicate) | Name: , Value: None8 B& a% N5 v7 h: g$ |: r  M1 R1 e
                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 4) | Value: b'\x02\x00') F: b* @  X2 h: w3 w7 J# U
[Service] 00001800-0000-1000-8000-00805f9b34fb: Generic Access Profile
( C/ e  V1 b  P. n% V+ A6 l         [Characteristic] 00002a00-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'STM32WB'  W- B! J$ {0 Y
         [Characteristic] 00002a01-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'@\x03'+ D- _% Z. {* x& H* J4 @
         [Characteristic] 00002a04-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\xff\xff\xff\xff\x00\x00\xff\xff'
- p8 Y/ T8 d+ i* l" B5 ?[Service] 0000180a-0000-1000-8000-00805f9b34fb: Device Information" b! r- |* T' [. A+ n9 d' m
         [Characteristic] 00002a29-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'STM\x00'
; l7 b5 E6 y. K  }5 }' C' T[Service] 0000180d-0000-1000-8000-00805f9b34fb: Heart Rate" K6 ^0 E( f6 ^0 Y
         [Characteristic] 00002a37-0000-1000-8000-00805f9b34fb: (notify) | Name: , Value: None5 F3 v: G. D' ~( M8 _
                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 18) | Value: b'\x00\x00'" n' k7 X5 Z# t+ l' U
         [Characteristic] 00002a38-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\x04'; x& z( `! M$ o0 R( M" E0 m8 E
         [Characteristic] 00002a39-0000-1000-8000-00805f9b34fb: (write) | Name: , Value: None# ~5 f0 m: r! n! m+ J  g& A
* Y+ M+ q) A/ H. m5 K9 z
观察,返回的信息,我们大致可以通过,服务、特性、描述等知道其性能和功能。其中,发现UUID都是单片机中定义好的,在UUID.h 文件目录下:7 ~- Q7 h1 C% f- B
/* UUIDs for Heart Rate Service */
7 W$ x6 X) D& d9 p2 N* R4 r, R+ y#define HEART_RATE_SERVICE_UUID                                        (0x180D)
' Y8 O/ S; W' q. l! Y/ b9 s#define CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID                   (0x2902)% z( K' I; m5 ]
#define HEART_RATE_MEASURMENT_UUID                                   (0x2A37)
! q0 S4 ?  }# x8 f! a' J* ]#define SENSOR_LOCATION_UUID                                           (0x2A38)
- D0 z( g6 O9 _" B) R+ G9 g$ B#define CONTROL_POINT_UUID                                             (0x2A39)1 E# n% K1 o3 l9 t( ^/ s

' l5 r' F* c* A6 n8 m5 }5 C0 d3.3 通知和获取数据; S: Q8 N( Z3 W2 |
通过STM的蓝牙软件,发现软件是有数据传递的。
  N  D: t2 H; ~

4

4
# ?$ w# J7 [* Q: O' }7 K5 f7 G: G
3 U% K6 x) V" f) {4 z8 t; U

6 F* r/ z- S' Q5 E) H其采用的方式是通知的方式,我是用了一下程序:9 u& b2 n+ C/ R' V$ [8 c9 j

+ u/ c* o1 r1 [2 ~3 `% h* pdef notification_handler(sender, data):, p, r+ m7 a. Q; \5 N- a
    # strs=data.hex' V: G" X" q4 L! d: T2 M  q
    print(f"{sender}: {data}"): q8 c- a/ o* ^( e8 x! x+ L
    byarray = bytearray(data)2 B- Q& U" x2 u5 f: K
    print(byarray.hex())
' T: J7 y1 i4 v! S
; \+ y0 ~1 @! wasync def run(address, loop, debug=False):
. j1 @  q) y( N4 l: Q) `# E    async with BleakClient(address, loop=loop) as client:
, t: s' Y( G1 m) c& t' q$ B        x = await client.is_connected()
; q' N$ T7 `( x( q! b1 m        await client.start_notify(CHARACTERISTIC_UUID, notification_handler)4 C$ \; {  ?" S  _3 m3 Y
        await asyncio.sleep(5.0, loop=loop)# E0 [7 x; q: f% Q$ i6 d4 l  v
        await client.stop_notify(CHARACTERISTIC_UUID)
& P1 k% M  y' w% D# Qif __name__ == "__main__":* s/ q: ^2 ~7 t; F# F
if __name__ == "__main__":. M: X2 o% [5 \! t7 N/ F
    address = ('80:E1:26:00:68:7C')  #你的地址
7 O# o  {4 Z! ?- q: J, [    loop = asyncio.get_event_loop()+ x" v& C. U6 T
    loop.run_until_complete(run(address, loop, True))
* Z" t: u  k$ S8 o( m: |( [) a1 N5 D! }* ?, e5 M4 f) A& r3 L
返回了一下数据:# I9 X  ]; X/ y, h! b% S5 O
00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f7\x00\x14\x00\x00\x04')
0 [  B& G2 m% R; t5 g1f370014000004
3 S% S0 F+ {& S' o00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f6\x00\x1e\x00\x00\x04'), a6 T0 T0 z6 ~3 w' D9 y& g
1f36001e000004
0 v  D. y  I, T# x1 L00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f4\x00(\x00\x00\x04')' F5 t1 V+ D" {! A& s6 \- O! D
1f340028000004
% z* D# m8 S, [3 S
' k* _* w- a& f6 u其中00002a37HEART_RATE_MEASURMENT_UUID ,数据是十六进制的1f36001e000004,其中0x36是心跳,1e是能量消耗,我将能量变化修改为,每次+10,从每次读回的值就可以看到。0 @: Y& N8 Q8 r2 R/ t( ^8 f
HRSAPP_Context.MeasurementvalueChar.EnergyExpended += 10;
* B& c& Y+ m( X/ e( q1 C" G; i' t( f, _0 _6 c+ h9 A( v
4 小结# g- j7 J, h, s5 l
以上步骤,从发现蓝牙,连接蓝牙,获取蓝牙服务,以及获取数据。其实,实验中还有许多的小问题,比如指示灯的闪烁,为啥STM官方的APP可以直接连接,自己的却不行?, P/ q+ s/ K  o4 m
我相信,在后面一步一步的代码解析,就会解释这些现象。4 _2 l9 \4 o% S9 t

  g0 `( r- C5 n) o2 Z+ g" y
( `. h; D" w6 i% ~
& [/ Q& @9 F# K/ u6 F
9 A( J+ }2 G& E$ e

3 A6 o  [% f! Y4 l, {
收藏 评论0 发布时间:2020-5-23 16:48

举报

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