11.5. Connessioni

Una volta che un dispositivo centrale seleziona un dispositivo periferico dal flusso di advertising e gli invia una richiesta di connessione, entrambe le parti escono dalla modalità di advertising / scanning ed entrano in una connessione. La radio pianifica ora la propria attività sui canali dati del link layer, saltando in modo pseudo-casuale tra di essi secondo la sequenza concordata al momento della connessione. Tutto ciò che sta al di sopra del link layer – GATT, sicurezza, L2CAP – viene eseguito sulla connessione stabilita qui.

11.5.1. L’evento di connessione

Due dispositivi in una connessione BLE non trasmettono in modo continuo. Concordano un intervallo di connessione e, a ogni intervallo, entrambe le parti attivano la radio, scambiano i pacchetti eventualmente in coda, confermano ciò che hanno ricevuto e tornano in stato di sleep. Ciascuno di questi scambi è chiamato evento di connessione.

Una timeline con il dispositivo centrale sulla traccia in alto e il dispositivo periferico su quella in basso. A ogni confine di intervallo di connessione entrambe le tracce mostrano un breve impulso etichettato "TX/RX". Lo spazio tra gli impulsi è etichettato "connection interval"; la durata di ciascun impulso è "connection event". Il dispositivo periferico ha alcuni eventi saltati etichettati "peripheral latency: peripheral may skip if idle".

La radio di ciascun lato è attiva solo durante i brevi eventi di connessione, mentre tutto il resto è in sleep. Il dispositivo periferico può saltare eventi grazie alla peripheral latency.

I valori che governano tutto questo vengono negoziati al momento della connessione e il link layer ne impone il rispetto. Sono visibili all’applicazione sia come parametri richiesti sia come valori risultanti riportati indietro.

  • Intervallo di connessione. Da 7,5 ms a 4 s, in passi di 1,25 ms. Il dispositivo centrale adotta il valore richiesto dal dispositivo periferico, a meno che la richiesta non sia irragionevole. Intervalli più brevi consegnano i dati con minore latenza al costo di una maggiore attività radio; quelli più lunghi risparmiano energia ma rendono più lento ogni round trip.

  • Peripheral latency. Un intero N non negativo. Il dispositivo periferico può saltare fino a N eventi di connessione quando non ha nulla da inviare, tornando in sleep invece di attivare la radio per uno scambio vuoto. Utile per sensori che si attivano per inviare un dato una volta al secondo ma desiderano un intervallo di connessione basso e reattivo per il raro messaggio immediato.

  • Supervision timeout. Da 100 ms a 32 s. Se una delle due parti non riceve nulla dall’altra per questo periodo di tempo, il collegamento viene dichiarato perso ed entrambe le parti tornano in advertising / scanning. Il timeout deve essere più lungo di connection_interval * (1 + peripheral_latency) – il link layer rifiuta i valori che violano questa condizione.

aioble.Device.connect() accetta min_conn_interval_us e max_conn_interval_us in modo che il dispositivo centrale possa richiedere un determinato intervallo; il valore effettivo su cui la radio si è stabilizzata può essere riletto attraverso la configurazione del link layer dopo che la connessione è attiva.

11.5.2. Cosa significano «centrale» e «periferico» all’interno di una connessione

I ruoli stabiliti al momento dell’advertising rimangono validi dopo che la connessione è attiva:

  • Il dispositivo centrale governa la temporizzazione – possiede il lato master della sequenza di hopping e degli eventi di connessione.

  • Il dispositivo periferico è reattivo. Può richiedere una modifica dei parametri di connessione (ad esempio un intervallo più lento per risparmiare energia) ma è il dispositivo centrale a decidere se accettarla.

I ruoli sono indipendenti da chi ospita il database GATT, che costituisce un asse separato. Per convenzione il dispositivo periferico è anche il server GATT e il dispositivo centrale è il client GATT, ma BLE consente a entrambe le parti di ospitare servizi GATT. Le camere seguono quasi sempre la convenzione: periferico + server per le applicazioni in cui «la camera pubblica dati», centrale + client per le applicazioni in cui «la camera legge da un sensore».

11.5.3. La maximum transmission unit (MTU)

Per impostazione predefinita il link layer trasporta pacchetti brevi – 27 byte di payload, di cui solo 23 disponibili per GATT. È sufficiente per una piccola lettura o un comando breve, ma è poco rispetto a qualsiasi dato multi-byte. Entrambe le parti possono negoziare un valore più alto, fino al limite supportato dal firmware della radio (tipicamente alcune centinaia di byte sui controller moderni).

L’API aioble gestisce la negoziazione attraverso aioble.DeviceConnection.exchange_mtu() e il risultato diventa disponibile nell’attributo mtu. MTU più grandi comportano meno round trip per qualsiasi valore superiore a ~20 byte, a un piccolo costo in termini di memoria buffer.

11.5.4. Durata di vita

Una connessione dura fino a una di queste condizioni:

  • una delle due parti chiama disconnect(),

  • scatta il supervision timeout (fuori portata, radio spenta, peer in crash), oppure

  • si verifica un esplicito errore a livello di link layer (mancata corrispondenza della cifratura, rifiuto del pairing).

Quando la connessione cade, ogni operazione GATT in coda o in transito su di essa solleva aioble.DeviceDisconnectedError e qualsiasi blocco async with connection in cui si trova l’applicazione esce in modo pulito. Un dispositivo periferico tipicamente risponde tornando a aioble.advertise() e attendendo il prossimo dispositivo centrale; un dispositivo centrale risponde riprendendo lo scanning oppure portando la disconnessione all’attenzione dell’applicazione.

La durata di vita è una delle ragioni per cui aioble è utile. Un’API BLE sincrona dovrebbe esporre callback di disconnessione e maschere di eventi; la versione asyncio trasforma le disconnessioni in eccezioni all’interno della coroutine che era in attesa dell’operazione, che è esattamente lo scopo per cui è pensata la pulizia con async with.