11.5. Conexiones

Una vez que un dispositivo central elige un periférico del flujo de anuncios y le envía una solicitud de conexión, ambos lados abandonan el modo de anuncio / escaneo y entran en una conexión. Ahora la radio programa su actividad en los canales de datos de la capa de enlace, saltando de forma pseudoaleatoria entre ellos según la secuencia acordada en el momento de la conexión. Todo lo que está por encima de la capa de enlace – GATT, seguridad, L2CAP – se ejecuta sobre la conexión que se establece aquí.

11.5.1. El evento de conexión

Dos dispositivos en una conexión BLE no transmiten de forma continua. Acuerdan un intervalo de conexión, y en cada intervalo ambos lados despiertan la radio, intercambian los paquetes que tengan en cola, confirman lo que han recibido y vuelven a dormir. Cada uno de esos intercambios se denomina evento de conexión.

Una línea de tiempo con el dispositivo central en la pista superior y el periférico en la pista inferior. En cada límite de intervalo de conexión ambas pistas muestran un pulso breve etiquetado como "TX/RX". El hueco entre pulsos está etiquetado como "intervalo de conexión"; la duración de cada pulso es "evento de conexión". El periférico tiene algunos eventos omitidos etiquetados como "latencia del periférico: el periférico puede omitir si está inactivo".

La radio de cada lado está despierta solo durante los breves eventos de conexión, mientras que todo lo demás permanece dormido. El periférico puede omitir eventos bajo la latencia del periférico.

Los números que rigen esto se negocian en el momento de la conexión y la capa de enlace los hace cumplir. Son visibles para la aplicación tanto como parámetros del lado de la solicitud como en forma de los valores resultantes que se informan de vuelta.

  • Intervalo de conexión. De 7,5 ms a 4 s, en pasos de 1,25 ms. El central elige el valor que solicita el periférico a menos que la solicitud no sea razonable. Los intervalos más cortos entregan datos con menor latencia a costa de más actividad de radio; los más largos ahorran energía pero hacen que cada ida y vuelta sea más lenta.

  • Latencia del periférico. Un entero no negativo N. Al periférico se le permite omitir hasta N eventos de conexión cuando no tiene nada que enviar, volviendo a dormir en lugar de despertar la radio para un intercambio vacío. Útil para sensores que despiertan para informar una vez por segundo pero quieren un intervalo de conexión bajo y receptivo para el raro mensaje inmediato.

  • Tiempo de espera de supervisión. De 100 ms a 32 s. Si cualquiera de los lados no recibe nada del otro durante este tiempo, el enlace se declara perdido y ambos lados vuelven al anuncio / escaneo. El tiempo de espera debe ser mayor que connection_interval * (1 + peripheral_latency) – la capa de enlace rechaza los valores que violen esa condición.

aioble.Device.connect() acepta min_conn_interval_us y max_conn_interval_us para que el central pueda solicitar un rango concreto; el valor real en el que se asentó la radio se puede leer de vuelta a través de la configuración de la capa de enlace una vez que la conexión está activa.

11.5.2. Qué significan «central» y «periférico» dentro de una conexión

Los roles establecidos en el momento del anuncio se mantienen tras establecerse la conexión:

  • El central dirige la temporización – es propietario del lado maestro de la secuencia de saltos y de los eventos de conexión.

  • El periférico es reactivo. Puede solicitar un cambio en los parámetros de conexión (un intervalo más lento para ahorrar energía, por ejemplo), pero el central decide si lo acepta.

Los roles son independientes de quién aloja la base de datos GATT, que es un eje aparte. Por convención, el periférico es también el servidor GATT y el central es el cliente GATT, pero BLE permite que cualquiera de los lados aloje servicios GATT. Las cámaras casi siempre siguen la convención: periférico + servidor para aplicaciones del tipo «la cámara publica datos», central + cliente para aplicaciones del tipo «la cámara lee de un sensor».

11.5.3. La unidad máxima de transmisión (MTU)

La capa de enlace transporta paquetes que son cortos por defecto – 27 bytes de carga útil, de los cuales solo 23 están disponibles para GATT. Eso es suficiente para una pequeña lectura o un comando corto, pero diminuto al lado de cualquier cosa de varios bytes. Ambos lados pueden negociar este valor al alza, hasta el límite que admita el firmware de la radio (normalmente unos pocos cientos de bytes en los controladores modernos).

La API de aioble dirige la negociación mediante aioble.DeviceConnection.exchange_mtu() y el resultado queda disponible en el atributo mtu. Las MTU más grandes implican menos idas y vueltas para cualquier valor mayor de ~20 bytes, a un pequeño coste en memoria de búfer.

11.5.4. Tiempo de vida

Una conexión dura hasta que ocurre una de estas cosas:

  • cualquiera de los lados llama a disconnect(),

  • se dispara el tiempo de espera de supervisión (fuera de alcance, radio apagada, el par se bloqueó), o

  • un fallo explícito de la capa de enlace (discrepancia de cifrado, rechazo del emparejamiento).

Cuando la conexión se cae, toda operación GATT en cola o en curso sobre ella lanza aioble.DeviceDisconnectedError, y cualquier bloque async with connection en el que esté la aplicación se cierra limpiamente. Un periférico normalmente responde volviendo a aioble.advertise() y esperando al siguiente central; un central responde escaneando de nuevo o haciendo aflorar la desconexión a la aplicación.

El tiempo de vida es una de las razones por las que aioble resulta útil. Una API BLE síncrona tendría que exponer funciones de retorno (callback) de desconexión y máscaras de eventos; la versión asyncio convierte las desconexiones en excepciones dentro de la corrutina que esperaba la operación, que es para lo que está hecha la limpieza de async with.