micropython / micropython/micropython-lib

[aioble] After timeout, reconnection is successful. (Peripherals are always powered off)

Ouverte
#950 9 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Langage dominant
Python
Étoiles
2.9k
Forks
1.1k
Merge moyen
7 j 6 h
PR mergées (30 j)
3

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

    1. Power off the peripheral device.
    2. Timeout occurs when trying to connect.
    3. 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)

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par aioble/central.py:_connect et examinez la gestion d’état associée dans aioble/device.py et aioble/client.py. Reproduisez le problème sur le Raspberry Pi Pico W à l’aide du script fourni, avec le périphérique éteint, puis réessayez la connexion. Le travail est terminé lorsqu’un timeout ne laisse pas d’état connecté erroné et que la nouvelle tentative ne se poursuit pas comme si la connexion était établie ou n’échoue pas pendant la découverte des services.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
embedded-iot, networking
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
Plutôt claire
Accessibilité débutants
45/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.