[aioble] After timeout, reconnection is successful. (Peripherals are always powered off)
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 45/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Stale
- Tech stack
- python
- Domain
- embedded-iot, networking
Research direction
Start with aioble/central.py:_connect and review the related state handling in aioble/device.py and aioble/client.py. Reproduce on the Raspberry Pi Pico W using the provided script with the peripheral powered off, then retry the connection. Done means a timeout leaves no false connected state and the retry does not proceed as connected or fail during service discovery.
Written by the indexing model from the issue text.
Description
The other device is powered off. However, after a connection timeout, it appears to be connected when retried. In fact, it is failing.
I'm not a native speaker, so sorry if my writing is wrong.
-
Environment
- Raspberry Pi Pico W with RP2040
- MicroPython v1.24.0 on 2024-10-25
- IDE:Thonny
-
Steps to reproduce
- Power off the peripheral device.
- Timeout occurs when trying to connect.
- Retry and the connection succeeds. (It actually fails.)
-
Code used for testing
from micropython import const
import asyncio
import aioble
# import test_aioble as aioble
import sys
_TARGET_DEVICE_ADDR = const("d7:de:4c:f4:56:e5")
async def gather(ai):
ret=[]
async for x in ai: ret.append(x)
return ret
async def device_details(connection):
if connection is None:
return
try:
services = await gather(connection.services())
except asyncio.TimeoutError as e:
print("Timeout. (discovering services)")
sys.print_exception(e)
except Exception as e:
print("Error (discovering services): {}".format(e))
sys.print_exception(e)
else:
if services is None or len(services) <= 0:
print("\n\"Service\" not found.")
return
for s in services:
print("\t", s)
async def print_details(device):
connection = None
# connecting to device
try:
print("\nConnecting to {} ... ".format(device), end="")
connection = await device.connect(timeout_ms=2000)
except asyncio.TimeoutError as e:
print("Timeout.")
sys.print_exception(e)
except Exception as e:
print("Error: {}".format(e))
sys.print_exception(e)
else:
print("Connected.")
await device_details(connection)
await connection.disconnect()
async def main():
device = aioble.Device(aioble.ADDR_RANDOM, _TARGET_DEVICE_ADDR)
while True:
await print_details(device)
r = input("\nenter return (to retry) / q(uit). > ").strip()
if r.upper() == "Q":
sys.exit(0)
asyncio.run(main())
- Output
MPY: soft reboot
MicroPython v1.24.0 on 2024-10-25; Raspberry Pi Pico W with RP2040
Type "help()" for more information.
>>>
>>> %Run -c $EDITOR_CONTENT
MPY: soft reboot
Connecting to Device(ADDR_RANDOM, d7:de:4c:f4:56:e5) ... Timeout.
Traceback (most recent call last):
File "<stdin>", line 47, in print_details
File "aioble/device.py", line 149, in connect
File "aioble/central.py", line 140, in _connect
File "aioble/device.py", line 94, in __exit__
TimeoutError:
enter return (to retry) / q(uit). >
Connecting to Device(ADDR_RANDOM, d7:de:4c:f4:56:e5, CONNECTED) ... Connected.
Error (discovering services): can't convert NoneType to int
Traceback (most recent call last):
File "<stdin>", line 24, in device_details
File "<stdin>", line 14, in gather
File "aioble/client.py", line 128, in __anext__
File "aioble/client.py", line 120, in _start
File "aioble/client.py", line 193, in _start_discovery
TypeError: can't convert NoneType to int
enter return (to retry) / q(uit). >
- How to fix it
I added exception handling because of the following problem. (It is not a good idea to fix it in this place.)- When the timeout occurs, the variable "_connection" in aioble is not in the correct state.
- When the timeout occurs, the internal state of the module "bluetooth" seems to remain connected.
# https://github.com/micropython/micropython-lib/blob/master/micropython/bluetooth/aioble/aioble/central.py#L107
async def _connect(
connection, timeout_ms, scan_duration_ms, min_conn_interval_us, max_conn_interval_us
):
<< Omitted >>
try:
with DeviceTimeout(None, timeout_ms):
ble.gap_connect(
device.addr_type,
device.addr,
scan_duration_ms,
min_conn_interval_us,
max_conn_interval_us,
)
# Wait for the connected IRQ.
await connection._event.wait()
assert connection._conn_handle is not None
# Register connection handle -> device.
DeviceConnection._connected[connection._conn_handle] = connection
except:
device._connection = None
ble.gap_connect(None)
raise
finally:
# After timeout, don't hold a reference and ignore future events.
_connecting.remove(device)
- Dominant language
- Python
- Stars
- 2.9k
- Forks
- 1.1k
- Avg merge
- 7d 6h
- Merged PRs (30d)
- 3
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from micropython/micropython-lib
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
micropython/micropython-lib#1074 ·
-
needs-info
Difficulty 1/5 Under an hour Newbie friendliness 72/100
micropython/micropython-lib#943 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
micropython/micropython-lib#931 · 1 comment ·
-
Difficulty 1/5 Under an hour Newbie friendliness 62/100
micropython/micropython-lib#795 · 2 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
micropython/micropython-lib#762 ·
All issues in micropython/micropython-lib
Similar issues
-
documentation help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
simonw/sqlite-utils#872 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100