Nel tutorial di oggi, finalmente vedremo come effettuare un collegamento con un dispositivo Bluetooth e inviare dei dati…
Profili e UUID
Lo standard Bluetooth definisce una serie di profili, ovvero di funzionalità che i dispositivi Bluetooth offrono.
Per semplificare, i profili Bluetooth corrispondono a “cosa” un dispositivo può fare; ad esempio, un auricolare Bluetooth implementerà sicuramente il profilo HSP (Headset Service Profile) che specifica come ricevere/trasmettere audio e come inviare semplici comandi (“rispondi”, “alza il volume…”) al telefono associato. Alcuni auricolari più avanzati implementano anche il profilo A2DP (Advanced Audio Distribution Profile), consentendo all’utente anche di ascoltare musica trasmessa dal telefono.
Nello stabilire una connessione tra due dispositivi, il dispositivo che inizia la connessione può utilizzare il protocollo SDP (Service Discovery Protocol) per scoprire quali servizi sono offerti dall’altro, ovvero quali profili questo implementa.
Ogni servizio è definito da un identificativo numerico (UUID) da 128 bit. Normalmente si utilizza una versione corta (short form) di tale identificativo:
- si parte da un UUID base di 128 bit, 0x00000000-0000-1000-8000-00805F9B34FB
- si inserisce la versione corta dell’UUID al posto dei primi 8 zeri
- si ottiene quindi l’UUID completo del servizio
Ad esempio il servizio HSP ha UUID breve 0x1108, quindi il suo UUID completo sarà
0x00001108-0000-1000-8000-00805F9B34FB
SPP
Il profilo più semplice e più utilizzato per comunicare con dispositivi embedded è sicuramente il Serial Port Profile (SPP), che ha UUID breve 0x1101.
Questo profilo emula tra i due dispositivi una connessione seriale:
Android
Negli scorsi tutorial abbiamo visto come ottenere l’elenco dei dispositivi associati al nostro smartphone.
Ogni dispositivo è una istanza dell’oggetto BluetoothDevice; tale oggetto mette a disposizione due metodi per aprire un canale di comunicazione:
[checklist]
- createRfcommSocketToServiceRecord(UUID)
- createInsecureRfcommSocketToServiceRecord(UUID)
[/checklist]
Entrambi i metodi accettano l’UUID del profilo da utilizzare e si distinguono solo perché il primo cerca di effettuare una connessione cifrata. I più comuni moduli Bluetooth che si usano nei sistemi embedded non supportano tale modalità, quindi sarà necessario usare il secondo.
Se la connessione va a buon fine, i metodi restituiscono un oggetto di tipo BluetoothSocket che rappresenta il canale di comunicazione tra lo smartphone e il dispositivo associato; in caso di errore invece viene sollevata una eccezione.
Vediamo come inviare dati ad un dispositivo che implementa il profilo SSP.
Per prima cosa definiamo l’UUID del profilo:
UUID SPP_UUID = java.util.UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); |
Quindi otteniamo il canale di comunicazione con il dispositivo:
BluetoothSocket btSocket = null; try { btSocket = targetDevice.createInsecureRfcommSocketToServiceRecord(SPP_UUID); } catch (IOException e) { Toast.makeText(this, "Unable to open a serial socket with the device", Toast.LENGTH_SHORT).show(); } |
Il canale non è ancora aperto, dobbiamo utilizzare il metodo connect() per collegarci al dispositivo:
try { btSocket.connect(); } catch (IOException e) { Toast.makeText(this, "Unable to connect to the device", Toast.LENGTH_SHORT).show(); } |
Una volta connessi, l’oggetto BluetoothSocket mette a disposizione due Stream, uno per inviare (OutputStream) e uno per ricevere (InputStream) dati. Per comodità, possiamo utilizzare un oggetto OutputStreamWriter che rende più facile inviare sequenze di caratteri attraverso uno stream:
try { OutputStreamWriter writer = new OutputStreamWriter(btSocket.getOutputStream()); writer.write("Hello World!\r\n"); writer.flush(); } catch (IOException e) { Toast.makeText(this, "Unable to send message to the device", Toast.LENGTH_SHORT).show(); } |
Il nostro writer mantiene un buffer locale: per essere sicuri che i dati vengano effettivamente inviati, ricordatevi sempre di effettuare il flush().
Al termine della connessione, il socket deve essere chiuso:
try { btSocket.close(); } catch (IOException e) { Toast.makeText(this, "Unable to close the connection to the device", Toast.LENGTH_SHORT).show(); } |
Nella prossima pagina, vi mostrerò una app che implementa quando spiegato sopra…