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

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

[复制链接]
ziziran 发布时间:2020-5-23 16:48
0 前言( ~2 y! A( c  S! e
自己的主要目的是学习BLE相关的知识,以及其具体的应用。因此不会像前一小节那样,组成那么长链路的系统。为最佳的学习BLE,就需要组成最小的闭环回路。9 P, `  D% Q) y
. a: C$ w: ]# I5 {5 o' z, Y3 F
1 最小闭环回路
$ q! g9 e( O' b# D最小闭环系统,就是STM32WB的开发板和笔记本组成的系统。现在基本的笔记本自带蓝牙。! A/ Z" Y( o5 ~7 P

1

1

* h8 n( G# A6 @6 \1 u/ x' X  z
5 S  x5 q, t  d0 o

6 P2 n7 B* J( G6 q# A, B4 D9 I, E5 D" ^. \  H8 N- Z

: o) a; l1 r2 u3 n" m) v4 R# j
( T1 ~2 K9 w# p7 T* k, u2 系统软件环境
5 V1 a" v$ r1 W6 s! v' _: S, U: ?在开发板中的软件使用的是:P-NUCLEO-WB55.Nucleo\Applications\BLE\BLE_HeartRate项目工程。+ H1 C' w" r2 r9 a4 L
笔记本使用上位机是自己编写的python代码。
( X) H5 g2 @1 {1 Z  C$ X: f

2

2
6 U8 ~' h0 o0 t& O

3 r$ o  C/ r# M我们将开发板当作外围设备。将笔记本作为中心设备。
- S! M0 y5 u6 c; i9 U7 U* ~) |; [1 u" k
3 连接实验
% t* Z4 h; W( a2 z6 }3.1 发现设备( [6 H8 S) o" q' B1 {
下载好单片机程序,给单片机上电。  W' F( D: G5 h. {, `
在笔记本设置中打开蓝牙,并点击添加蓝牙。
; z- c: ?- S: m) H: t: o' Y: |) e

3

3

. T  Q5 Z/ g1 t8 F4 t2 ~0 H/ g; e' \9 R" ]+ n) E/ [

' v7 b0 X( ~- S- }2 |可以看到我们的设备HRSTM1 p! J( b- l" c! x+ n
接下来使用我编写好代码:/ O5 @' o  N+ V) }
import bleak.backends.dotnet.discovery as Discovery
* w5 y: e2 U. a& ~. Y" B8 S: {( n3 ?3 Limport asyncio4 T7 ?9 N* G/ U+ B
async def run():  V( C0 K) g; [+ L7 d
    devices = await Discovery.discover()- H& H6 n( I# Y" d6 H2 `6 h
    for d in devices:
8 L+ A4 ~- i' M# a! ~8 m3 K2 t        print(d)
  q' E, j5 Y6 O( v5 ]" q; t8 Qloop = asyncio.get_event_loop()
% B2 n' S1 V% Y. N- b4 H( Hloop.run_until_complete(run())
* l7 B# x2 w' c1 W% T2 F
+ N  J" x5 r( V- m/ w/ T4 V在输出窗口可以看到可以被发现了的蓝牙:
# N1 B* `9 r; N4 |$ a3 O2 {9 P+ {6 EB8:7C:6F:47:81:BF: MiKettle
* G1 G9 k; E8 }6B:96:A5:AF:06:A7: Apple, Inc. (b'\x10\x05Q\x1c\x82.\xec')+ v" L8 h5 G: M: N( {0 _3 U
80:E1:26:00:68:7C: HRSTM
* H7 x5 V+ [1 P* Y* \) Z; a4 `2 l8 E/ p
我们也可以看到我们的蓝牙应用HRSTM。其中,一个很重要的东西就是设备的ID,这个是我们连接的一个关键,就和电话号码一样,而HRSTM就是持有人的别名。
- \2 [# w# J  O+ |2 @$ N
8 k0 G1 d. |; [' y
! {# x# L% G) X) [/ M8 O3.2 连接和发现服务
! z6 a% Y1 }, p+ ^' R& L) A接下来,我们应用上面的地址,来连接和发现一下HRSTM有哪些服务:) }! M3 q' {3 v. M4 ^5 z
async with BleakClient(address, loop=loop) as client:5 O% g# n1 U7 @# L! L
    x = await client.is_connected()* }7 F% X4 p+ \. i6 f1 u; g
    log.info("Connected: {0}".format(x))
. g% {; Q6 i* Z        log.info("[Service] {0}: {1}".format(service.uuid, service.description))
3 a5 ], F' `( g; {1 R0 \8 i1 [        for char in service.characteristics:
& V+ c; Q1 C, `% r/ Y% ~7 |            if "read" in char.properties:! G. g& O2 E* f; B2 z% r
                try:
# c, W1 J5 A' D                    value = bytes(await client.read_gatt_char(char.uuid))
2 U" `. [7 T1 v6 O* }, Q" N                except Exception as e:" x) Y8 V& Y/ c& I0 G5 q8 a
                    value = str(e).encode()
; i+ m# W9 g3 V$ a4 C3 P7 s6 z            else:
" w+ ?+ c+ y0 ]: l2 g! ?! O                value = None) B+ V/ T$ J7 c( H/ ^
log.info($ j. q8 x8 m. p. n
                "\t[Characteristic] {0}: ({1}) | Name: {2}, Value: {3} ".format(
/ p2 ?5 _5 e$ i4 L& m* d                    char.uuid, ",".join(char.properties), char.description, value
' A3 N8 [) I. b" j7 q/ V                )% U- q8 E- y5 J, m' E- p
            )/ ^0 Y% b  u" m& w# D( R" A
            for descriptor in char.descriptors:
3 B" x- H0 D6 _% M2 r                value = await client.read_gatt_descriptor(descriptor.handle)  z$ o, J: h( _8 A& R+ H; W. x
                log.info(; S5 p& D; v3 m7 \& i
                    "\t\t[Descriptor] {0}: (Handle: {1}) | Value: {2} ".format(
, u- j) X$ C5 B7 _# F/ l                        descriptor.uuid, descriptor.handle, bytes(value)
4 _0 D7 }" ~0 c                    ); L: a; g: v* f1 @( o3 K  a
                )5 s) {, z; M- U4 J8 X

: [& r/ h9 f1 E输出的结果:  Z( [. \. Q% y  V/ j/ o! p( e
Connected: True5 J% x" v% z$ C
[Service] 00001801-0000-1000-8000-00805f9b34fb: Generic Attribute Profile1 Z( Y$ Z6 l) _% |" [: p& s
         [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb: (indicate) | Name: , Value: None
2 k' J% o! c$ g  j6 A                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 4) | Value: b'\x02\x00'
1 w: m: j7 X8 t' \: I2 T/ p[Service] 00001800-0000-1000-8000-00805f9b34fb: Generic Access Profile4 N  J6 a$ M6 O* k
         [Characteristic] 00002a00-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'STM32WB'8 B% }; a3 i/ Y2 n
         [Characteristic] 00002a01-0000-1000-8000-00805f9b34fb: (read,write-without-response,write,authenticated-signed-writes) | Name: , Value: b'@\x03'( u- r$ f) n8 b; b1 Z+ L3 r
         [Characteristic] 00002a04-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\xff\xff\xff\xff\x00\x00\xff\xff'' N1 I0 b+ Y$ m  I
[Service] 0000180a-0000-1000-8000-00805f9b34fb: Device Information# P; n' \0 ~1 ?' [9 Q) S
         [Characteristic] 00002a29-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'STM\x00'
/ p" J( F9 p+ [$ W- n# B[Service] 0000180d-0000-1000-8000-00805f9b34fb: Heart Rate. ^* i4 S- I7 u7 s
         [Characteristic] 00002a37-0000-1000-8000-00805f9b34fb: (notify) | Name: , Value: None
% ~5 s. a# C. r/ N2 y                   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb: (Handle: 18) | Value: b'\x00\x00'
" @0 K) z+ k5 p6 N  k         [Characteristic] 00002a38-0000-1000-8000-00805f9b34fb: (read) | Name: , Value: b'\x04'
  c( e6 ~  f+ a         [Characteristic] 00002a39-0000-1000-8000-00805f9b34fb: (write) | Name: , Value: None$ D& k4 t: l1 E5 j7 Z

# _4 p0 ~' @( S  t0 y2 b* A观察,返回的信息,我们大致可以通过,服务、特性、描述等知道其性能和功能。其中,发现UUID都是单片机中定义好的,在UUID.h 文件目录下:
/ @) A  W- J/ R; C/* UUIDs for Heart Rate Service */
" I8 z/ O% Z( w( \" N#define HEART_RATE_SERVICE_UUID                                        (0x180D)
, C4 u: h7 z' s#define CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID                   (0x2902)
: s' q# p, Q: Q1 ~" e#define HEART_RATE_MEASURMENT_UUID                                   (0x2A37)
3 I( m/ L2 O$ w2 i& h- s#define SENSOR_LOCATION_UUID                                           (0x2A38)
% P- ]8 O9 m; S( J#define CONTROL_POINT_UUID                                             (0x2A39): I+ W: X( z. b. |3 E

5 [  o) d* {# F( Q3 _; D! j1 y* |# w3.3 通知和获取数据, F  p5 [( V' F8 n. l* i% m$ A
通过STM的蓝牙软件,发现软件是有数据传递的。
. \/ l+ U& f5 U- G

4

4

) H: T  C& i- ]
4 h3 b! S. r+ Q. l5 j5 P  S  y' B1 J$ C; I* z
其采用的方式是通知的方式,我是用了一下程序:0 h" K1 u( ?4 ^, _+ ]) A

) c: t) V) u. p6 V- Rdef notification_handler(sender, data):
( b+ L. g+ X, s: v2 C    # strs=data.hex
+ y: z! G5 I  |  o- [7 K    print(f"{sender}: {data}")
# T: j' Y% D$ @. M* d7 n+ b% T    byarray = bytearray(data)
* b7 J7 a2 ?( v3 z- d1 N- x2 F    print(byarray.hex())
- h# p) `6 D" K& x/ c+ v/ p9 i" ]; j. [" |, t9 T% q
async def run(address, loop, debug=False):( h( u  g7 s( Y% o8 h6 ?
    async with BleakClient(address, loop=loop) as client:
9 ^8 V9 L' B/ m- M: L( \6 l1 y        x = await client.is_connected()
5 H; J* W  Z6 X, _7 A7 C        await client.start_notify(CHARACTERISTIC_UUID, notification_handler)( H$ O' z/ w9 w* q: J
        await asyncio.sleep(5.0, loop=loop)
0 @0 @! _! ^+ z$ n        await client.stop_notify(CHARACTERISTIC_UUID)3 x& c1 F0 \5 Q. j
if __name__ == "__main__":
. x  k3 @* K0 k: \8 }if __name__ == "__main__":7 i4 e: z7 `& z" ?. v
    address = ('80:E1:26:00:68:7C')  #你的地址
' o: q; b! r2 }4 z! I    loop = asyncio.get_event_loop()
' Z8 t9 U; s* i5 u    loop.run_until_complete(run(address, loop, True))+ L  T  i! n$ E4 l
: _" ^5 U" ?- Z8 _- w6 b. }
返回了一下数据:* b. i! b; \' P) H
00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f7\x00\x14\x00\x00\x04')
* u0 y; G; H0 L1f370014000004
* J$ U$ {8 R" D, c9 _; W00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f6\x00\x1e\x00\x00\x04')
, w7 }( E' }- T; G1f36001e000004
- D9 P# I6 C* R00002a37-0000-1000-8000-00805f9b34fb: bytearray(b'\x1f4\x00(\x00\x00\x04'); G5 z) @7 G( T
1f340028000004
# {; q+ m+ W1 ~; A; r" y) o# S' I6 \# t" [; d( n; n. x% Z6 ?) R* V
其中00002a37HEART_RATE_MEASURMENT_UUID ,数据是十六进制的1f36001e000004,其中0x36是心跳,1e是能量消耗,我将能量变化修改为,每次+10,从每次读回的值就可以看到。
. A; z/ `4 M; X/ G( qHRSAPP_Context.MeasurementvalueChar.EnergyExpended += 10;; v' ^5 r8 u3 \: [5 y0 Q# E# P
$ Y0 L9 h: P4 ~9 L* \, y
4 小结& C. j6 n* p% w/ ~
以上步骤,从发现蓝牙,连接蓝牙,获取蓝牙服务,以及获取数据。其实,实验中还有许多的小问题,比如指示灯的闪烁,为啥STM官方的APP可以直接连接,自己的却不行?
; Q/ |; |2 q! O9 X6 O- j我相信,在后面一步一步的代码解析,就会解释这些现象。
2 R( x6 d/ a# _; ^& F8 a# h  T. o
$ o/ C$ i7 M( J

1 R1 `8 k/ t' Z& Y' a' U+ S4 Z6 S4 F) S5 V

3 N1 ^; d3 G( r  H/ W& n% ~! s  M+ J# H: C, A9 i7 O
收藏 评论0 发布时间:2020-5-23 16:48

举报

0个回答

所属标签

相似分享

官网相关资源

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