micropython / micropython/micropython-lib

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

Abierto
#950 9 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Lenguaje dominante
Python
Estrellas
2.9k
Forks
1.1k
Merge medio
7 d 6 h
PR fusionados (30 d)
3

Descripción

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)

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza con aioble/central.py:_connect y revisa la gestión de estado relacionada en aioble/device.py y aioble/client.py. Reproduce el problema en la Raspberry Pi Pico W usando el script proporcionado, con el periférico apagado, y luego vuelve a intentar la conexión. Se considera terminado cuando un timeout no deja un estado de conexión falso y el reintento no continúa como conectado ni falla durante el descubrimiento de servicios.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
embedded-iot, networking
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
45/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.