bluetooth — Bluetooth a basso livello

Questo modulo fornisce un’interfaccia al controller Bluetooth integrato. Supporta Bluetooth Low Energy (BLE) nei ruoli Central, Peripheral, Broadcaster e Observer, oltre a GATT Server e Client e ai canali L2CAP orientati alla connessione. Un dispositivo può operare contemporaneamente in più ruoli. Sono supportati anche il pairing e il bonding.

Questa API è pensata per corrispondere al protocollo Bluetooth a basso livello e per fornire i blocchi costitutivi di astrazioni di più alto livello, come tipi di dispositivo specifici.

Suggerimento

Per la maggior parte delle applicazioni, è preferibile la libreria di più alto livello aioble, che fornisce un wrapper basato su asyncio attorno a questo modulo. Vedi aioble — BLE asincrono.

class BLE

class bluetooth.BLE

Restituisce l’oggetto singleton BLE.

Configurazione

active(active: bool | None = None, /) bool

Modifica opzionalmente lo stato attivo della radio BLE e restituisce lo stato corrente.

La radio deve essere resa attiva prima di usare qualsiasi altro metodo di questa classe.

config(param: str, /) Any
config(*, **kwargs: Any) None

Ottiene o imposta i valori di configurazione dell’interfaccia BLE. Per ottenere un valore, il nome del parametro deve essere indicato come stringa tra virgolette e si può interrogare un solo parametro alla volta. Per impostare i valori usare la sintassi a parole chiave; è possibile impostare uno o più parametri alla volta.

I valori attualmente supportati sono:

  • 'mac': L’indirizzo attualmente in uso, a seconda della modalità di indirizzo corrente. Restituisce una tupla (addr_type, addr).

    Vedi gap_scan per dettagli sul tipo di indirizzo.

    Questo può essere interrogato solo mentre l’interfaccia è attiva.

  • 'addr_mode': Imposta la modalità di indirizzo. I valori sono:

    Valore

    Nome

    Comportamento

    0x00

    PUBLIC

    Usa l’indirizzo pubblico del controller.

    0x01

    RANDOM

    Usa un indirizzo statico generato.

    0x02

    RPA

    Usa indirizzi privati risolvibili.

    0x03

    NRPA

    Usa indirizzi privati non risolvibili.

    Per impostazione predefinita l’interfaccia userà un indirizzo PUBLIC se disponibile, altrimenti userà un indirizzo RANDOM.

  • 'gap_name': Ottiene/imposta il nome del dispositivo GAP usato dal servizio Generic Access (UUID 0x1800), caratteristica Device Name (UUID 0x2a00). Può essere impostato in qualsiasi momento e modificato più volte.

  • 'rxbuf': Ottiene/imposta la dimensione in byte del buffer interno usato per memorizzare gli eventi in arrivo. Questo buffer è globale per l’intero driver BLE e quindi gestisce i dati in arrivo per tutti gli eventi, incluse tutte le caratteristiche. Aumentarlo consente una migliore gestione dei dati in arrivo a raffica (per esempio i risultati di scansione) e la possibilità di ricevere valori di caratteristiche più grandi.

  • 'mtu': Ottiene/imposta l’MTU che verrà usato durante uno scambio ATT MTU. L’MTU risultante sarà il minimo tra questo e l’MTU del dispositivo remoto. Lo scambio ATT MTU non avverrà automaticamente (a meno che non sia il dispositivo remoto a iniziarlo) e deve essere avviato manualmente con gattc_exchange_mtu. Usa l’evento _IRQ_MTU_EXCHANGED per scoprire l’MTU di una determinata connessione.

  • 'bond': Imposta se il bonding sarà abilitato durante il pairing. Quando è abilitato, le richieste di pairing imposteranno il flag «bond» e le chiavi saranno memorizzate da entrambi i dispositivi.

  • 'mitm': Imposta se la protezione MITM è richiesta per il pairing.

  • 'io': Imposta le capacità di I/O di questo dispositivo.

    Le opzioni disponibili sono:

    Costante

    Valore

    Capacità

    _IO_CAPABILITY_DISPLAY_ONLY

    0

    Solo display

    _IO_CAPABILITY_DISPLAY_YESNO

    1

    Display con input sì/no

    _IO_CAPABILITY_KEYBOARD_ONLY

    2

    Solo tastiera

    _IO_CAPABILITY_NO_INPUT_OUTPUT

    3

    Nessun input o output

    _IO_CAPABILITY_KEYBOARD_DISPLAY

    4

    Tastiera e display

  • 'le_secure': Imposta se è richiesto il pairing «LE Secure». Il valore predefinito è false (cioè consente il «Legacy Pairing»).

Gestione degli eventi

irq(handler: Callable[[int, Tuple], Any | None], /) None

Registra una callback per gli eventi dello stack BLE. L”handler accetta due argomenti, event (che sarà uno dei codici sotto) e data (che è una tupla di valori specifica per l’evento).

Nota: Come ottimizzazione per evitare allocazioni inutili, le voci addr, adv_data, char_data, notify_data e uuid nelle tuple sono istanze memoryview di sola lettura che puntano al ringbuffer interno di bluetooth e sono valide solo durante l’invocazione della funzione handler dell’IRQ. Se il programma deve salvare uno di questi valori per accedervi dopo che l’handler dell’IRQ è ritornato (per esempio salvandolo in un’istanza di classe o in una variabile globale), deve farne una copia, usando bytes() oppure bluetooth.UUID(), in questo modo:

connected_addr = bytes(addr)  # equivalently: adv_data, char_data, or notify_data
matched_uuid = bluetooth.UUID(uuid)

Per esempio, l’handler dell’IRQ per un risultato di scansione potrebbe ispezionare adv_data per decidere se è il dispositivo corretto e solo allora copiare i dati dell’indirizzo da usare altrove nel programma. E per stampare i dati dall’interno dell’handler dell’IRQ sarà necessario print(bytes(addr)).

Un handler tipicamente effettua il dispatch in base al codice dell’evento e spacchetta la tupla del payload specifico dell’evento:

def bt_irq(event, data):
    if event == _IRQ_CENTRAL_CONNECT:
        conn_handle, addr_type, addr = data
        ...
    elif event == _IRQ_SCAN_RESULT:
        addr_type, addr, adv_type, rssi, adv_data = data
        ...

Ogni codice di evento, il payload che fornisce e una breve descrizione sono elencati di seguito. Per gli eventi il cui campo status è menzionato, status è 0 in caso di successo e un valore diverso da zero specifico dell’implementazione in caso di errore.

Costante

Valore

Evento

Tupla del payload

_IRQ_CENTRAL_CONNECT

1

Un central si è connesso a questo peripheral.

(conn_handle, addr_type, addr)

_IRQ_CENTRAL_DISCONNECT

2

Un central si è disconnesso da questo peripheral.

(conn_handle, addr_type, addr)

_IRQ_GATTS_WRITE

3

Un client connesso ha scritto su una caratteristica o un descrittore locale. Usa gatts_read per recuperare il nuovo valore.

(conn_handle, attr_handle)

_IRQ_GATTS_READ_REQUEST

4

Un client connesso ha emesso una lettura. Restituisci un codice di errore diverso da zero dalla tabella sottostante per negare la lettura, oppure 0 / None per accettarla.

(conn_handle, attr_handle)

_IRQ_SCAN_RESULT

5

È stato ricevuto un singolo pacchetto di advertising durante una scansione attiva.

(addr_type, addr, adv_type, rssi, adv_data)

_IRQ_SCAN_DONE

6

La scansione corrente è terminata, perché è trascorsa la durata configurata oppure perché è stato chiamato gap_scan(None).

()

_IRQ_PERIPHERAL_CONNECT

7

Una gap_connect emessa in precedenza è andata a buon fine.

(conn_handle, addr_type, addr)

_IRQ_PERIPHERAL_DISCONNECT

8

Un peripheral connesso si è disconnesso.

(conn_handle, addr_type, addr)

_IRQ_GATTC_SERVICE_RESULT

9

È stato trovato un servizio da gattc_discover_services.

(conn_handle, start_handle, end_handle, uuid)

_IRQ_GATTC_SERVICE_DONE

10

La scoperta dei servizi è terminata.

(conn_handle, status)

_IRQ_GATTC_CHARACTERISTIC_RESULT

11

È stata trovata una caratteristica da gattc_discover_characteristics.

(conn_handle, end_handle, value_handle, properties, uuid)

_IRQ_GATTC_CHARACTERISTIC_DONE

12

La scoperta delle caratteristiche è terminata.

(conn_handle, status)

_IRQ_GATTC_DESCRIPTOR_RESULT

13

È stato trovato un descrittore da gattc_discover_descriptors.

(conn_handle, dsc_handle, uuid)

_IRQ_GATTC_DESCRIPTOR_DONE

14

La scoperta dei descrittori è terminata.

(conn_handle, status)

_IRQ_GATTC_READ_RESULT

15

Una gattc_read emessa in precedenza ha restituito dati.

(conn_handle, value_handle, char_data)

_IRQ_GATTC_READ_DONE

16

Una gattc_read emessa in precedenza è terminata.

(conn_handle, value_handle, status)

_IRQ_GATTC_WRITE_DONE

17

Una gattc_write emessa in precedenza è stata confermata.

(conn_handle, value_handle, status)

_IRQ_GATTC_NOTIFY

18

Un server remoto ha inviato una notifica (non confermata).

(conn_handle, value_handle, notify_data)

_IRQ_GATTC_INDICATE

19

Un server remoto ha inviato un’indicazione (confermata).

(conn_handle, value_handle, notify_data)

_IRQ_GATTS_INDICATE_DONE

20

Un’indicazione inviata in precedenza è stata confermata dal client (o è andata in timeout).

(conn_handle, value_handle, status)

_IRQ_MTU_EXCHANGED

21

Uno scambio ATT MTU è stato completato (avviato da entrambi i lati).

(conn_handle, mtu)

_IRQ_L2CAP_ACCEPT

22

Un dispositivo remoto ha richiesto una connessione L2CAP su un PSM su cui questo dispositivo è in ascolto. Restituisci un intero diverso da zero per rifiutare, oppure 0 / None per accettare.

(conn_handle, cid, psm, our_mtu, peer_mtu)

_IRQ_L2CAP_CONNECT

23

Un canale L2CAP è ora stabilito, sia accettando una richiesta in arrivo sia completando una l2cap_connect in uscita.

(conn_handle, cid, psm, our_mtu, peer_mtu)

_IRQ_L2CAP_DISCONNECT

24

Un canale L2CAP è stato disconnesso. status è 0 per una disconnessione pulita, oppure diverso da zero se un tentativo di connessione in uscita è fallito.

(conn_handle, cid, psm, status)

_IRQ_L2CAP_RECV

25

Sono arrivati dati su un canale L2CAP. Chiama l2cap_recvinto per leggerli.

(conn_handle, cid)

_IRQ_L2CAP_SEND_READY

26

Una precedente l2cap_send che aveva restituito False si è svuotata e il canale è di nuovo pronto. Uno status diverso da zero significa che il buffer di trasmissione è andato in overflow e l’applicazione deve reinviare i dati.

(conn_handle, cid, status)

_IRQ_CONNECTION_UPDATE

27

Il dispositivo remoto ha aggiornato i parametri di connessione (interval, latency, supervision timeout).

(conn_handle, conn_interval, conn_latency, supervision_timeout, status)

_IRQ_ENCRYPTION_UPDATE

28

Lo stato di crittografia di una connessione è cambiato, tipicamente dopo il completamento del pairing o del bonding.

(conn_handle, encrypted, authenticated, bonded, key_size)

_IRQ_GET_SECRET

29

Lo stack sta richiedendo un segreto di bonding memorizzato. Se key è None, restituisci il valore memorizzato di posizione index di tipo sec_type; altrimenti restituisci il valore associato alla coppia (sec_type, key) indicata. Restituisci None se non c’è nulla di memorizzato.

(sec_type, index, key)

_IRQ_SET_SECRET

30

Lo stack chiede all’applicazione di rendere persistente un segreto di bonding. Restituisci True una volta memorizzato.

(sec_type, key, value)

_IRQ_PASSKEY_ACTION

31

È richiesta un’azione di passkey come parte del pairing. Rispondi usando gap_passkey; vedi la tabella delle azioni di passkey sottostante per le azioni possibili.

(conn_handle, action, passkey)

Per l’evento _IRQ_GATTS_READ_REQUEST, i codici di ritorno disponibili sono:

Costante

Valore

Significato

_GATTS_NO_ERROR

0x00

Accetta la lettura.

_GATTS_ERROR_READ_NOT_PERMITTED

0x02

Lettura non consentita.

_GATTS_ERROR_WRITE_NOT_PERMITTED

0x03

Scrittura non consentita.

_GATTS_ERROR_INSUFFICIENT_AUTHENTICATION

0x05

Il client non è autenticato.

_GATTS_ERROR_INSUFFICIENT_AUTHORIZATION

0x08

Il client non è autorizzato.

_GATTS_ERROR_INSUFFICIENT_ENCRYPTION

0x0f

Il link non è crittografato.

Per l’evento _IRQ_PASSKEY_ACTION, le azioni disponibili sono:

Costante

Valore

Significato

_PASSKEY_ACTION_NONE

0

Nessuna azione richiesta.

_PASSKEY_ACTION_INPUT

2

Chiedi all’utente di inserire la passkey mostrata sul dispositivo remoto.

_PASSKEY_ACTION_DISPLAY

3

Mostra una passkey di 6 cifre che il dispositivo remoto dovrà inserire.

_PASSKEY_ACTION_NUMERIC_COMPARISON

4

Conferma che la passkey corrisponde a quella mostrata sul dispositivo remoto.

Per risparmiare spazio nel firmware, queste costanti non sono incluse nel modulo bluetooth. Aggiungi al tuo programma quelle che ti servono dagli elenchi sopra.

Ruolo Broadcaster (Advertiser)

gap_advertise(interval_us: int | None, adv_data: bytes | None = None, *, resp_data: bytes | None = None, connectable: bool = True) None

Avvia l’advertising all’intervallo specificato (in microsecondi). Questo intervallo verrà arrotondato per difetto al multiplo di 625us più vicino. Per interrompere l’advertising, imposta interval_us a None.

adv_data e resp_data possono essere di qualsiasi tipo che implementi il buffer protocol (per esempio bytes, bytearray, str). adv_data è incluso in tutti i broadcast, mentre resp_data viene inviato in risposta a una scansione attiva.

Nota: se adv_data (o resp_data) è None, allora verranno riutilizzati i dati passati alla precedente chiamata a gap_advertise. Questo consente a un broadcaster di riprendere l’advertising con il solo gap_advertise(interval_us). Per cancellare il payload di advertising passa un bytes vuoto, cioè b''.

Ruolo Observer (Scanner)

gap_scan(duration_ms: int | None, interval_us: int = 1280000, window_us: int = 11250, active: bool = False, /) None

Esegue un’operazione di scansione che dura per la durata specificata (in millisecondi).

Per eseguire la scansione a tempo indefinito, imposta duration_ms a 0.

Per interrompere la scansione, imposta duration_ms a None.

Usa interval_us e window_us per configurare opzionalmente il duty cycle. Lo scanner verrà eseguito per window_us microsecondi ogni interval_us microsecondi per un totale di duration_ms millisecondi. L’intervallo e la finestra predefiniti sono rispettivamente 1,28 secondi e 11,25 millisecondi (scansione in background).

Per ogni risultato di scansione verrà generato l’evento _IRQ_SCAN_RESULT, con dati dell’evento (addr_type, addr, adv_type, rssi, adv_data).

I valori di addr_type indicano indirizzi pubblici o casuali:

Valore

Nome

Significato

0x00

PUBLIC

Indirizzo di dispositivo pubblico.

0x01

RANDOM

Indirizzo casuale (statico, RPA o NRPA; il tipo è codificato nell’indirizzo stesso).

I valori di adv_type corrispondono alla Bluetooth Specification:

Valore

Nome

Significato

0x00

ADV_IND

Advertising non diretto, connettibile e scansionabile.

0x01

ADV_DIRECT_IND

Advertising diretto e connettibile.

0x02

ADV_SCAN_IND

Advertising non diretto e scansionabile.

0x03

ADV_NONCONN_IND

Advertising non diretto e non connettibile.

0x04

SCAN_RSP

Risposta alla scansione.

active può essere impostato a True se vuoi ricevere le risposte di scansione nei risultati.

Quando la scansione viene interrotta (per il termine della durata o quando viene esplicitamente fermata), verrà generato l’evento _IRQ_SCAN_DONE.

Ruolo Central

Un dispositivo central può connettersi a peripheral che ha scoperto usando il ruolo observer (vedi gap_scan) oppure con un indirizzo noto.

gap_connect(addr_type: int | None, addr: bytes | None = None, scan_duration_ms: int = 2000, min_conn_interval_us: int | None = None, max_conn_interval_us: int | None = None, /) None

Si connette a un peripheral.

Vedi gap_scan per dettagli sui tipi di indirizzo.

Per annullare anticipatamente un tentativo di connessione in sospeso, chiama gap_connect(None).

In caso di successo, verrà generato l’evento _IRQ_PERIPHERAL_CONNECT. Se si annulla un tentativo di connessione, verrà generato l’evento _IRQ_PERIPHERAL_DISCONNECT.

Il dispositivo attenderà fino a scan_duration_ms per ricevere un payload di advertising dal dispositivo.

L’intervallo di connessione può essere configurato in microsecondi usando min_conn_interval_us e/o max_conn_interval_us. Altrimenti verrà scelto un intervallo predefinito, tipicamente compreso tra 30000 e 50000 microsecondi. Un intervallo più breve aumenterà il throughput, a scapito del consumo energetico.

Ruolo Peripheral

Ci si aspetta che un dispositivo peripheral invii advertising connettibili (vedi gap_advertise). Di solito agirà come GATT server, avendo prima registrato servizi e caratteristiche usando gatts_register_services.

Quando un central si connette, verrà generato l’evento _IRQ_CENTRAL_CONNECT.

Ruoli Central e Peripheral

gap_disconnect(conn_handle: int, /) bool

Disconnette l’handle di connessione specificato. Può trattarsi di un central che si è connesso a questo dispositivo (se agisce come peripheral) oppure di un peripheral a cui questo dispositivo si era connesso in precedenza (se agisce come central).

In caso di successo, verrà generato l’evento _IRQ_PERIPHERAL_DISCONNECT o _IRQ_CENTRAL_DISCONNECT.

Restituisce False se l’handle di connessione non era connesso, altrimenti True.

GATT Server

Un GATT server ha un insieme di servizi registrati. Ogni servizio può contenere caratteristiche, ognuna delle quali ha un valore. Le caratteristiche possono anche contenere descrittori, che a loro volta hanno valori.

Questi valori sono memorizzati localmente e vi si accede tramite il loro «value handle» generato durante la registrazione del servizio. Possono anche essere letti o scritti da un dispositivo client remoto. Inoltre, un server può «notificare» una caratteristica a un client connesso tramite un handle di connessione.

Un dispositivo nel ruolo central o peripheral può funzionare come GATT server, tuttavia nella maggior parte dei casi sarà più comune che sia un dispositivo peripheral ad agire come server.

Le caratteristiche e i descrittori hanno una dimensione massima predefinita di 20 byte (l’ATT MTU predefinito di 23 byte meno un header ATT di 3 byte; un MTU negoziato più grande non aumenta di per sé questo limite). Tutto ciò che vi viene scritto da un client sarà troncato a questa lunghezza. Tuttavia, qualsiasi scrittura locale aumenterà la dimensione massima, quindi se vuoi consentire scritture più grandi da un client verso una determinata caratteristica, usa gatts_write dopo la registrazione, per esempio gatts_write(char_handle, bytes(100)).

gatts_register_services(services_definition: Sequence[Sequence], /) Sequence[Sequence[int]]

Configura il server con i servizi specificati, sostituendo eventuali servizi esistenti.

services_definition è una lista di services, dove ogni service è una tupla di due elementi contenente un UUID e una lista di characteristics.

Ogni characteristic è una tupla di due o tre elementi contenente un UUID, un valore flags e, opzionalmente, una lista di descriptors.

Ogni descriptor è una tupla di due elementi contenente un UUID e un valore flags.

I flags sono una combinazione OR bit a bit dei flag definiti sotto. Impostano sia il comportamento della caratteristica (o del descrittore) sia i requisiti di sicurezza e privacy.

Il valore restituito è una lista (un elemento per servizio) di tuple (ogni elemento è un value handle). Gli handle delle caratteristiche e dei descrittori sono appiattiti nella stessa tupla, nell’ordine in cui sono definiti.

L’esempio seguente registra due servizi (Heart Rate e Nordic UART):

bt = bluetooth.BLE()
bt.active(True)

# Heart Rate service: one Heart Rate Measurement characteristic.
HR_SERVICE = (
    bluetooth.UUID(0x180D),
    (
        (bluetooth.UUID(0x2A37),
         bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY),
    ),
)

# Nordic UART service: a TX characteristic the client subscribes
# to for notifications, and an RX characteristic it writes to.
UART_SERVICE = (
    bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E'),
    (
        (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'),
         bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY),
        (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'),
         bluetooth.FLAG_WRITE),
    ),
)

((hr,), (tx, rx)) = bt.gatts_register_services(
    (HR_SERVICE, UART_SERVICE),
)

I tre value handle (hr, tx, rx) possono essere usati con gatts_read, gatts_write, gatts_notify e gatts_indicate.

Nota: L’advertising deve essere interrotto prima di registrare i servizi.

I flag disponibili per caratteristiche e descrittori sono:

Costante

Valore

Significato

_FLAG_BROADCAST

0x0001

La caratteristica può essere trasmessa in broadcast.

_FLAG_READ

0x0002

Il client può leggere il valore.

_FLAG_WRITE_NO_RESPONSE

0x0004

Il client può scrivere senza attendere una risposta.

_FLAG_WRITE

0x0008

Il client può scrivere con una risposta confermata.

_FLAG_NOTIFY

0x0010

Il server può inviare notifiche (non confermate).

_FLAG_INDICATE

0x0020

Il server può inviare indicazioni (confermate).

_FLAG_AUTHENTICATED_SIGNED_WRITE

0x0040

Il client può emettere scritture firmate.

_FLAG_AUX_WRITE

0x0100

Proprietà estese: sono consentite scritture in coda/affidabili.

_FLAG_READ_ENCRYPTED

0x0200

La lettura richiede un link crittografato.

_FLAG_READ_AUTHENTICATED

0x0400

La lettura richiede un link autenticato (protetto da MITM).

_FLAG_READ_AUTHORIZED

0x0800

La lettura richiede un’autorizzazione a livello applicativo.

_FLAG_WRITE_ENCRYPTED

0x1000

La scrittura richiede un link crittografato.

_FLAG_WRITE_AUTHENTICATED

0x2000

La scrittura richiede un link autenticato (protetto da MITM).

_FLAG_WRITE_AUTHORIZED

0x4000

La scrittura richiede un’autorizzazione a livello applicativo.

Come per le costanti di evento sopra, questi flag non sono forniti dal modulo bluetooth; copia nel tuo programma quelli che ti servono.

gatts_read(value_handle: int, /) bytes

Legge il valore locale per questo handle (che è stato scritto da gatts_write oppure da un client remoto).

gatts_write(value_handle: int, data: bytes, send_update: bool = False, /) None

Scrive il valore locale per questo handle, che può essere letto da un client.

Se send_update è True, allora qualsiasi client iscritto verrà notificato (o indicato, a seconda di ciò a cui è iscritto e di quali operazioni la caratteristica supporta) riguardo a questa scrittura.

gatts_notify(conn_handle: int, value_handle: int, data: bytes | None = None, /) None

Invia una richiesta di notifica a un client connesso.

Se data è None (il valore predefinito), allora verrà inviato il valore locale corrente (come impostato con gatts_write).

Altrimenti, se data non è None, quel valore viene inviato al client come parte della notifica. Il valore locale non verrà modificato.

Nota: La notifica verrà inviata indipendentemente dallo stato di iscrizione del client a questa caratteristica.

gatts_indicate(conn_handle: int, value_handle: int, data: bytes | None = None, /) None

Invia una richiesta di indicazione a un client connesso.

Se data è None (il valore predefinito), allora verrà inviato il valore locale corrente (come impostato con gatts_write).

Altrimenti, se data non è None, quel valore viene inviato al client come parte dell’indicazione. Il valore locale non verrà modificato.

Alla conferma (o in caso di fallimento, per esempio timeout), verrà generato l’evento _IRQ_GATTS_INDICATE_DONE.

Nota: L’indicazione verrà inviata indipendentemente dallo stato di iscrizione del client a questa caratteristica.

gatts_set_buffer(value_handle: int, len: int, append: bool = False, /) None

Imposta la dimensione del buffer interno per un valore, in byte. Questo limiterà la scrittura più grande che può essere ricevuta. Il valore predefinito è 20 byte (ATT MTU predefinito di 23 meno l’header ATT di 3 byte).

Impostare append a True farà sì che tutte le scritture remote vengano accodate al valore corrente, anziché sostituirlo. In questo modo possono essere bufferizzati al massimo len byte. Quando usi gatts_read, il valore verrà cancellato dopo la lettura. Questa funzionalità è utile quando si implementa qualcosa come il Nordic UART Service.

GATT Client

Un GATT client può scoprire e leggere/scrivere caratteristiche su un GATT server remoto.

È più comune che un dispositivo nel ruolo central agisca come GATT client, tuttavia è anche possibile che un peripheral agisca come client per scoprire informazioni sul central che si è connesso ad esso (per esempio per leggere il nome del dispositivo dal device information service).

gattc_discover_services(conn_handle: int, uuid: UUID | None = None, /) None

Interroga un server connesso per i suoi servizi.

Specifica opzionalmente un uuid di servizio per interrogare solo quel servizio.

Per ogni servizio scoperto, verrà generato l’evento _IRQ_GATTC_SERVICE_RESULT, seguito da _IRQ_GATTC_SERVICE_DONE al completamento.

gattc_discover_characteristics(conn_handle: int, start_handle: int, end_handle: int, uuid: UUID | None = None, /) None

Interroga un server connesso per le caratteristiche nell’intervallo specificato.

Specifica opzionalmente un uuid di caratteristica per interrogare solo quella caratteristica.

Passando start_handle=1 ed end_handle=0xffff si copre l’intero intervallo di attribute handle del GATT, quindi questa combinazione di fatto cerca in ogni servizio del dispositivo remoto.

Per ogni caratteristica scoperta, verrà generato l’evento _IRQ_GATTC_CHARACTERISTIC_RESULT, seguito da _IRQ_GATTC_CHARACTERISTIC_DONE al completamento.

gattc_discover_descriptors(conn_handle: int, start_handle: int, end_handle: int, /) None

Interroga un server connesso per i descrittori nell’intervallo specificato.

Per ogni descrittore scoperto, verrà generato l’evento _IRQ_GATTC_DESCRIPTOR_RESULT, seguito da _IRQ_GATTC_DESCRIPTOR_DONE al completamento.

gattc_read(conn_handle: int, value_handle: int, /) None

Emette una lettura remota verso un server connesso per l’handle di caratteristica o descrittore specificato.

Quando un valore è disponibile, verrà generato l’evento _IRQ_GATTC_READ_RESULT, seguito da _IRQ_GATTC_READ_DONE al completamento.

gattc_write(conn_handle: int, value_handle: int, data: bytes, mode: int = 0, /) None

Emette una scrittura remota verso un server connesso per l’handle di caratteristica o descrittore specificato.

L’argomento mode specifica il comportamento della scrittura; i valori attualmente supportati sono:

  • mode=0 (predefinito) è una scrittura senza risposta: la scrittura verrà inviata al server remoto ma non verrà restituita alcuna conferma e non verrà generato alcun evento.

  • mode=1 è una scrittura con risposta: al server remoto viene richiesto di inviare una risposta/conferma di aver ricevuto i dati.

Se viene ricevuta una risposta dal server remoto, verrà generato l’evento _IRQ_GATTC_WRITE_DONE.

gattc_exchange_mtu(conn_handle: int, /) None

Avvia lo scambio MTU con un server connesso, usando l’MTU preferito impostato con BLE.config(mtu=value).

L’evento _IRQ_MTU_EXCHANGED verrà generato al completamento dello scambio MTU.

Lo scambio MTU è tipicamente avviato dal central; NimBLE supporta entrambi i ruoli.

Canali L2CAP orientati alla connessione

Questa funzionalità consente uno scambio di dati simile ai socket tra due dispositivi BLE. Una volta che i dispositivi sono connessi tramite GAP, ciascun dispositivo può mettersi in ascolto affinché l’altro si connetta su un PSM numerico (Protocol/Service Multiplexer).

Può essere attivo un solo canale L2CAP alla volta (cioè non è possibile connettersi mentre si è in ascolto).

I canali L2CAP attivi sono identificati dall’handle di connessione su cui sono stati stabiliti e da un CID (channel ID).

I canali orientati alla connessione hanno un controllo di flusso integrato basato su crediti. A differenza di ATT, dove i dispositivi negoziano un MTU condiviso, sia il dispositivo in ascolto sia quello che si connette impostano ciascuno un MTU indipendente che limita la quantità massima di dati in sospeso che il dispositivo remoto può inviare prima che siano completamente consumati in l2cap_recvinto.

l2cap_listen(psm: int, mtu: int, /) None

Inizia a mettersi in ascolto delle richieste di canale L2CAP in arrivo sul psm specificato, con l’MTU locale impostato a mtu.

Quando un dispositivo remoto avvia una connessione, verrà generato l’evento _IRQ_L2CAP_ACCEPT, che dà al server in ascolto la possibilità di rifiutare la connessione in arrivo (restituendo un intero diverso da zero).

Una volta accettata la connessione, verrà generato l’evento _IRQ_L2CAP_CONNECT, consentendo al server di ottenere il channel ID (CID) e l’MTU locale e remoto.

Nota: Attualmente non è possibile interrompere l’ascolto.

l2cap_connect(conn_handle: int, psm: int, mtu: int, /) None

Si connette a un peer in ascolto sul psm specificato, con l’MTU locale impostato a mtu.

In caso di connessione riuscita, verrà generato l’evento _IRQ_L2CAP_CONNECT, consentendo al client di ottenere il CID e l’MTU locale e remoto (peer).

Una connessione non riuscita genererà l’evento _IRQ_L2CAP_DISCONNECT con uno status diverso da zero.

l2cap_disconnect(conn_handle: int, cid: int, /) None

Disconnette un canale L2CAP attivo con i conn_handle e cid specificati.

l2cap_send(conn_handle: int, cid: int, buf: bytes, /) bool

Invia il buf specificato (che deve supportare il buffer protocol) sul canale L2CAP identificato da conn_handle e cid.

Il buffer deve soddisfare entrambi i limiti: non deve superare l’MTU remoto (peer) e non deve superare il doppio dell’MTU locale.

Questo restituirà False se il canale è ora «in stallo», il che significa che l2cap_send non deve essere chiamato di nuovo finché non viene ricevuto l’evento _IRQ_L2CAP_SEND_READY (che avverrà quando il dispositivo remoto concederà più crediti, tipicamente dopo aver ricevuto ed elaborato i dati).

l2cap_recvinto(conn_handle: int, cid: int, buf: Any | None, /) int

Riceve dati dai conn_handle e cid specificati nel buf fornito (che deve supportare il buffer protocol, per esempio bytearray o memoryview).

Restituisce il numero di byte letti dal canale.

Se buf è None, restituisce il numero di byte disponibili.

Nota: Dopo aver ricevuto l’evento _IRQ_L2CAP_RECV, l’applicazione dovrebbe continuare a chiamare l2cap_recvinto finché non ci sono più byte disponibili nel buffer di ricezione (tipicamente fino alla dimensione dell’MTU remoto (peer)).

Finché il buffer di ricezione non è vuoto, al dispositivo remoto non verranno concessi più crediti di canale e non potrà inviare altri dati.

Pairing e Bonding

Il pairing consente di crittografare e autenticare una connessione tramite lo scambio di segreti (con protezione MITM opzionale tramite autenticazione passkey).

Il bonding è il processo di memorizzazione di quei segreti in una memoria non volatile. Quando è effettuato il bonding, un dispositivo è in grado di risolvere un indirizzo privato risolvibile (RPA) di un altro dispositivo in base alla identity resolving key (IRK) memorizzata. Per supportare il bonding, un’applicazione deve implementare gli eventi _IRQ_GET_SECRET e _IRQ_SET_SECRET.

gap_pair(conn_handle: int, /) None

Avvia il pairing con il dispositivo remoto.

Prima di chiamare questo metodo, assicurati che le opzioni di configurazione io, mitm, le_secure e bond siano impostate (tramite config).

In caso di pairing riuscito, verrà generato l’evento _IRQ_ENCRYPTION_UPDATE.

gap_passkey(conn_handle: int, action: int, passkey: int, /) None

Risponde a un evento _IRQ_PASSKEY_ACTION per i conn_handle e action specificati. Il significato di passkey dipende da action (che a sua volta dipende dalla capacità di I/O configurata):

Azione

Risposta passkey richiesta

_PASSKEY_ACTION_INPUT

La passkey che l’utente legge dal dispositivo remoto.

_PASSKEY_ACTION_DISPLAY

Una passkey casuale di 6 cifre generata localmente e mostrata all’utente.

_PASSKEY_ACTION_NUMERIC_COMPARISON

1 per accettare la passkey mostrata nell’evento _IRQ_PASSKEY_ACTION, oppure 0 per annullare il pairing.

class UUID

class bluetooth.UUID(value: int | bytes | str, /)

Crea un’istanza UUID con il value specificato. Bluetooth usa tre larghezze di UUID; UUID accetta tutte:

Larghezza UUID

Tipi di value accettati

Esempio

16 bit

int o un buffer di 2 byte (little-endian)

UUID(0x2908) oppure UUID(b'\x08\x29')

32 bit

buffer di 4 byte (little-endian)

UUID(b'\x08\x29\x00\x00')

128 bit

buffer di 16 byte o una stringa con trattini

UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')

Gli UUID a 16 e 32 bit sono tipicamente identificatori allocati dal SIG (vedi i numeri assegnati Bluetooth); gli UUID a 128 bit sono normalmente definiti dal vendor.