8000 tests/multi_bluetooth: Add basic performance tests. · larsks/micropython@1342deb · GitHub
[go: up one dir, main page]

Skip to content

Commit 1342deb

Browse files
committed
tests/multi_bluetooth: Add basic performance tests.
1. Exchange GATT notifications. 2. Transmit a stream of data over L2CAP. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
1 parent a76604a commit 1342deb

File tree

4 files changed

+281
-0
lines changed

4 files changed

+281
-0
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Ping-pong GATT notifications between two devices.
2+
3+
from micropython import const
4+
import time, machine, bluetooth
5+
6+
TIMEOUT_MS = 2000
7+
8+
_IRQ_CENTRAL_CONNECT = const(1)
9+
_IRQ_CENTRAL_DISCONNECT = const(2)
10+
_IRQ_PERIPHERAL_CONNECT = const(7)
11+
_IRQ_PERIPHERAL_DISCONNECT = const(8)
12+
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
13+
_IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
14+
_IRQ_GATTC_NOTIFY = const(18)
15+
16+
# How long to run the test for.
17+
_NUM_NOTIFICATIONS = const(50)
18+
19+
SERVICE_UUID = bluetooth.UUID("A5A5A5A5-FFFF-9999-1111-5A5A5A5A5A5A")
20+
CHAR_UUID = bluetooth.UUID("00000000-1111-2222-3333-444444444444")
21+
CHAR = (
22+
CHAR_UUID,
23+
bluetooth.FLAG_NOTIFY,
24+
)
25+
SERVICE = (
26+
SERVICE_UUID,
27+
(CHAR,),
28+
)
29+
SERVICES = (SERVICE,)
30+
31+
is_central = False
32+
33+
waiting_events = {}
34+
35+
36+
def irq(event, data):
37+
if event == _IRQ_CENTRAL_CONNECT:
38+
waiting_events[event] = data[0]
39+
elif event == _IRQ_PERIPHERAL_CONNECT:
40+
waiting_events[event] = data[0]
41+
elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
42+
# conn_handle, def_handle, value_handle, properties, uuid = data
43+
if data[-1] == CHAR_UUID:
44+
waiting_events[event] = data[2]
45+
else:
46+
return
47+
elif event == _IRQ_GATTC_NOTIFY:
48+
if is_central:
49+
conn_handle, value_handle, notify_data = data
50+
ble.gatts_notify(conn_handle, value_handle, b"central" + notify_data)
51+
52+
if event not in waiting_events:
53+
waiting_events[event] = None
54+
55+
56+
def wait_for_event(event, timeout_ms):
57+
t0 = time.ticks_ms()
58+
while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms:
59+
if event in waiting_events:
60+
return waiting_events.pop(event)
61+
machine.idle()
62+
raise ValueError("Timeout waiting for {}".format(event))
63+
64+
65+
# Acting in peripheral role.
66+
def instance0():
67+
multitest.globals(BDADDR=ble.config("mac"))
68+
((char_handle,),) = ble.gatts_register_services(SERVICES)
69+
print("gap_advertise")
70+
ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY")
71+
multitest.next()
72+
try:
73+
# Wait for central to connect to us.
74+
conn_handle = wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS)
75+
76+
# Discover characteristics.
77+
ble.gattc_discover_characteristics(conn_handle, 1, 65535)
78+
value_handle = wait_for_event(_IRQ_GATTC_CHARACTERISTIC_RESULT, TIMEOUT_MS)
79+
wait_for_event(_IRQ_GATTC_CHARACTERISTIC_DONE, TIMEOUT_MS)
80+
81+
# Give the central enough time to discover chars.
82+
time.sleep_ms(500)
83+
84+
ticks_start = time.ticks_ms()
85+
86+
for i in range(_NUM_NOTIFICATIONS):
87+
# Send a notification and wait for a response.
88+
ble.gatts_notify(conn_handle, value_handle, "peripheral" + str(i))
89+
wait_for_event(_IRQ_GATTC_NOTIFY, TIMEOUT_MS)
90+
91+
ticks_end = time.ticks_ms()
92+
ticks_total = time.ticks_diff(ticks_end, ticks_start)
93+
print(
94+
"Acknowledged {} notifications in {} ms. {} ms/notification.".format(
95+
_NUM_NOTIFICATIONS, ticks_total, ticks_total // _NUM_NOTIFICATIONS
96+
)
97+
)
98+
99+
# Disconnect the central.
100+
print("gap_disconnect:", ble.gap_disconnect(conn_handle))
101+
wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS)
102+
finally:
103+
ble.active(0)
104+
105+
106+
# Acting in central role.
107+
def instance1():
108+
global is_central
109+
is_central = True
110+
((char_handle,),) = ble.gatts_register_services(SERVICES)
111+
multitest.next()
112+
try:
113+
# Connect to peripheral and then disconnect.
114+
print("gap_connect")
115+
ble.gap_connect(*BDADDR)
116+
conn_handle = wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS)
117+
118+
# Discover characteristics.
119+
ble.gattc_discover_characteristics(conn_handle, 1, 65535)
120+
value_handle = wait_for_event(_IRQ_GATTC_CHARACTERISTIC_RESULT, TIMEOUT_MS)
121+
wait_for_event(_IRQ_GATTC_CHARACTERISTIC_DONE, TIMEOUT_MS)
122+
123+
# The IRQ handler will respond to each notification.
124+
125+
# Wait for the peripheral to disconnect us.
126+
wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, 20000)
127+
finally:
128+
ble.active(0)
129+
130+
131+
ble = bluetooth.BLE()
132+
ble.active(1)
133+
ble.irq(irq)

tests/multi_bluetooth/perf_gatt_notify.py.exp

Whitespace-only changes.

tests/multi_bluetooth/perf_l2cap.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Send L2CAP data as fast as possible and time it.
2+
3+
from micropython import const
4+
import time, machine, bluetooth, random
5+
6+
TIMEOUT_MS = 1000
7+
8+
_IRQ_CENTRAL_CONNECT = const(1)
9+
_IRQ_CENTRAL_DISCONNECT = const(2)
10+
_IRQ_PERIPHERAL_CONNECT = const(7)
11+
_IRQ_PERIPHERAL_DISCONNECT = const(8)
12+
_IRQ_L2CAP_ACCEPT = const(22)
13+
_IRQ_L2CAP_CONNECT = const(23)
14+
_IRQ_L2CAP_DISCONNECT = const(24)
15+
_IRQ_L2CAP_RECV = const(25)
16+
_IRQ_L2CAP_SEND_READY = const(26)
17+
18+
_L2CAP_PSM = const(22)
19+
_L2CAP_MTU = const(512)
20+
21+
_PAYLOAD_LEN = const(_L2CAP_MTU)
22+
_NUM_PAYLOADS = const(20)
23+
24+
_RANDOM_SEED = 22
25+
26+
27+
waiting_events = {}
28+
29+
30+
def irq(event, data):
31+
if event == _IRQ_CENTRAL_CONNECT:
32+
conn_handle, addr_type, addr = data
33+
waiting_events[event] = conn_handle
34+
elif event == _IRQ_PERIPHERAL_CONNECT:
35+
conn_handle, addr_type, addr = data
36+
waiting_events[event] = conn_handle
37+
elif event == _IRQ_L2CAP_ACCEPT:
38+
conn_handle, cid, psm, our_mtu, peer_mtu = data
39+
waiting_events[event] = (conn_handle, cid, psm)
40+
elif event == _IRQ_L2CAP_CONNECT:
41+
conn_handle, cid, psm, our_mtu, peer_mtu = data
42+
waiting_events[event] = (conn_handle, cid, psm, our_mtu, peer_mtu)
43+
44+
if event not in waiting_events:
45+
waiting_events[event] = None
46+
47+
48+
def wait_for_event(event, timeout_ms):
49+
t0 = time.ticks_ms()
50+
while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms:
51+
if event in waiting_events:
52+
return waiting_events.pop(event)
53+
machine.idle()
54+
raise ValueError("Timeout waiting for {}".format(event))
55+
56+
57+
def send_data(ble, conn_handle, cid):
58+
buf = bytearray(_PAYLOAD_LEN)
59+
for i in range(_NUM_PAYLOADS):
60+
for j in range(_PAYLOAD_LEN):
61+
buf[j] = random.randint(0, 255)
62+
if not ble.l2cap_send(conn_handle, cid, buf):
63+
wait_for_event(_IRQ_L2CAP_SEND_READY, TIMEOUT_MS)
64+
65+
66+
def recv_data(ble, conn_handle, cid):
67+
buf = bytearray(_PAYLOAD_LEN)
68+
recv_bytes = 0
69+
recv_correct = 0
70+
expected_bytes = _PAYLOAD_LEN * _NUM_PAYLOADS
71+
ticks_first_byte = 0
72+
while recv_bytes < expected_bytes:
73+
wait_for_event(_IRQ_L2CAP_RECV, TIMEOUT_MS)
74+
if not ticks_first_byte:
75+
ticks_first_byte = time.ticks_ms()
76+
while True:
77+
n = ble.l2cap_recvinto(conn_handle, cid, buf)
78+
if n == 0:
79+
break
80+
recv_bytes += n
81+
for i in range(n):
82+
if buf[i] == random.randint(0, 255):
83+
recv_correct += 1
84+
ticks_end = time.ticks_ms()
85+
return recv_bytes, recv_correct, time.ticks_diff(ticks_end, ticks_first_byte)
86+
87+
88+
# Acting in peripheral role.
89+
def instance0():
90+
multitest.globals(BDADDR=ble.config("mac"))
91+
ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY")
92+
multitest.next()
93+
try:
94+
# Wait for central to connect to us.
95+
conn_handle = wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS)
96+
97+
ble.l2cap_listen(_L2CAP_PSM, _L2CAP_MTU)
98+
99+
conn_handle, cid, psm = wait_for_event(_IRQ_L2CAP_ACCEPT, TIMEOUT_MS)
100+
conn_handle, cid, psm, our_mtu, peer_mtu = wait_for_event(_IRQ_L2CAP_CONNECT, TIMEOUT_MS)
101+
102+
random.seed(_RANDOM_SEED)
103+
104+
send_data(ble, conn_handle, cid)
105+
106+
wait_for_event(_IRQ_L2CAP_DISCONNECT, TIMEOUT_MS)
107+
108+
# Wait for the central to disconnect.
109+
wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS)
110+
finally:
111+
ble.active(0)
112+
113+
114+
# Acting in central role.
115+
def instance1():
116+
multitest.next()
117+
try:
118+
# Connect to peripheral and then disconnect.
119+
ble.gap_connect(*BDADDR)
120+
conn_handle = wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS)
121+
122+
ble.l2cap_connect(conn_handle, _L2CAP_PSM, _L2CAP_MTU)
123+
conn_handle, cid, psm, our_mtu, peer_mtu = wait_for_event(_IRQ_L2CAP_CONNECT, TIMEOUT_MS)
124+
125+
random.seed(_RANDOM_SEED)
126+
127+
recv_bytes, recv_correct, total_ticks = recv_data(ble, conn_handle, cid)
128+
129+
# Disconnect channel.
130+
ble.l2cap_disconnect(conn_handle, cid)
131+
wait_for_event(_IRQ_L2CAP_DISCONNECT, TIMEOUT_MS)
132+
133+
print(
134+
"Received {}/{} bytes in {} ms. {} B/s".format(
135+
recv_bytes, recv_correct, total_ticks, recv_bytes * 1000 // total_ticks
136+
)
137+
)
138+
139+
# Disconnect from peripheral.
140+
ble.gap_disconnect(conn_handle)
141+
wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS)
142+
finally:
143+
ble.active(0)
144+
145+
146+
ble = bluetooth.BLE()
147+
ble.active(1)
148+
ble.irq(irq)

tests/multi_bluetooth/perf_l2cap.py.exp

Whitespace-only changes.

0 commit comments

Comments
 (0)
0