Bluetooth: pilotiamo led RGB su Arduino con Android [parte 2]

Prima di iniziare per chi non avesse letto la prima parte di questo progetto “Bluetooth: pilotare led RGB su Arduino con Android ” ecco il link [parte 1].
Dopo aver realizzato il codice da caricare su Arduino ora vediamo quello per creare la nostra applicazione Android, in particolare vedremo come:

  1. Ricercare i dispositivi bluetooth nelle vicinanze
  2. Connetterci al dispositivo remoto
  3. Inviare dati al dispositivo remoto

Ricercare i dispositivi bluetooth nelle vicinanze

La prima cosa che si deve fare dopo aver creato il progetto, sia che si utilizzi Eclipse o Android Studio, è quello di dichiarare i seguenti permessi all’interno del file AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

Il primo android.permission.BLUETOOTH consente all’app di utilizzare la connettività bluetooth del telefono, mentre la seconda android.permission.BLUETOOTH_ADMIN permette di utilizzare la funzionalità di discovery di nuovi dispositivi bluetooth.

Per poter interfacciarsi con il bluetooth del telefono si deve utilizzare l’oggetto BluetoothAdapter, sul quale attraverso il metodo statico getDefaultAdapter() si ottiene l’istanza di default.

btAdapter = BluetoothAdapter.getDefaultAdapter();

Nel caso lo smartphone non avesse il modulo bluetooth tale metodo restituisce null, questo comporta la stampa a video di un messaggio di errore (Toast) che ci avverte dell’impossibilità  dell’applicazione di funzionare e di conseguenza con il metodo finish() viene terminata immediatamente l’app.

if (btAdapter == null) {
    Toast.makeText(this, "This app requires a bluetooth capable phone", Toast.LENGTH_SHORT).show();
    finish();
}

Successivamente prima di far partire la ricerca dei dispositivi si deve controllare se il bluetooth è attivo e nel caso sia disattivato ne si richiede l’abilitazione attraverso un Intent al quale si associa un ID (REQUEST_ENABLE_BT) necessario per identificare la richiesta.

if(!btAdapter.isEnabled()) {
    Intent turnOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(turnOn,REQUEST_ENABLE_BT);
} else startDiscoveryDevice();

L’avvio dell’Intent fa aprire un Alert Dialog che ci chiede di attivare o meno il modulo bluetooth, dopo aver cliccato sul tasto SI (o NO) viene invocato il metodo onActivityResult() nel quale andremo a verificare il tasto premuto ed a eseguire o meno la scansione dei dispositivi.

@Override
// Manages intent results
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
     super.onActivityResult(requestCode, resultCode, data);
     if (requestCode==REQUEST_ENABLE_BT && resultCode==RESULT_OK)
         startDiscoveryDevice();
}

Il metodo startDiscoveryDevice() è quello che si occupa di far avviare la ricerca dei dispositivi attraverso l’invocazione del metodo startDiscovery() sull’oggetto btAdapter di tipo BluetoothAdapter, questo metodo demanda il compito della scansione al S.O Android il quale ogni volta che rileva un nuovo dispositivo invia dei messaggi broadcast, è necessario quindi utilizzare un BroadcastReceiver.

// Start discovery devices
public void startDiscoveryDevice() {
    adapter.clear();
    btAdapter.startDiscovery();
}

Per poter utilizzare un BroadcastReceiver come prima cosa si deve istanziare l’oggetto, indicare per quali eventi il BroadcastReceiver deve essere informato, attraverso l’uso dell’Intent Filter, ed infine registrarlo con il metodo registerReceiver().

// Define BroadcastReceiver object to handle events bluetooth
private MyBroadcastReceiver btReceiver = new MyBroadcastReceiver();

// Register the MyBroadcastReceiver for two notifications
IntentFilter deviceFoundFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
IntentFilter discoveryFinishedfilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(btReceiver, deviceFoundFilter);
registerReceiver(btReceiver, discoveryFinishedfilter);

Gli eventi utilizzati per il filtro sono:

  • BluetoothDevice.ACTION_FOUND: quando un nuovo dispositivo è rilevato
  • BluetoothAdapter.ACTION_DISCOVERY_FINISHED: quando il processo di discovery termina

L’ultima cosa che ci rimane da fare per quanto riguarda il BroadcastReceiver è l’implementazione del metodo onReceive() che viene chiamato da Android per notificare l’evento, attraverso la stringa action si ricava il tipo di evento e in base a quello si agisce di conseguenza.

@Override
public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    // when a new device is discovered, add it to the ListView
    if (BluetoothDevice.ACTION_FOUND.equals(action)) {
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        MainActivity.adapter.add(device.getName());
    }
    
    // if the scanning process has finished, enable the button
    if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
        Toast.makeText(context,"Scan finish!",Toast.LENGTH_SHORT).show();
    }
}

NOTA: Ricordarsi anche di eliminare la registrazione del BroadcastReceiver al termine del suo utilizzo, in questa app è stato fatto all’interno del metodo onPause() invocato da Android quando l’attuale activity viene messa in pausa e un’activity precedente viene ripristinata.

public void onPause() {
    super.onPause();
    // Unregister the MyBroadcastReceiver
    unregisterReceiver(btReceiver);
}

Connetterci al dispositivo remoto

Dopo aver visto come cercare i dispositivi non ci resta che connetterci, ma prima di passare al codice bisogna chiarire due aspetti importanti:

  1. Profilo Bluetooth
  2. UUID

Il primo rappresenta la funzionalità che il dispositivo bluetooth offre, tra cui troviamo:

  • HSP (Handset Profile): fornisce le funzionalità di base necessarie per la comunicazione tra il telefono e l’auricolare.
  • A2DP (Advanced Audio Distribution Profile): consente una trasmissione di segnali audio stereo con qualità superiore rispetto alla codifica mono utilizzata per i profili HSP e HFP.
  • SPP (Serial Port Profile): utilizzato per comunicare con dispositivi embedded emulando una connessione seriale

L’UUID (universally unique identifierè un identificativo numerico da 128 bit, di solito viene usata una versione corta (short form) di questo identificativo:

  1. Si parte da un UUID base di 128 bit, 0x00000000-0000-1000-8000-00805F9B34FB
  2. Si inserisce la versione corta dell’UUID al posto dei primi 8 zeri (0x1101 nel caso SPP)
  3. Si ottiene quindi l’UUID completo del servizio

Ora che abbiamo fatto un po’ di chiarezza su questi due punti vediamo il codice che ci permette di collegarci al dispositivo bluetooth appena trovato.
Come prima cosa si deve definire l’UUID del profilo:

UUID SPP_UUID = java.util.UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

Ottenere il canale di comunicazione con il dispositivo, attraverso il metodo createInsecureRfcommSocketToServiceRecord(SPP_UUID) il quale restituisce un oggetto di tipo BluetoothSocket:

// Create a connection to the device with the SPP UUID
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();
    return;
}

ed infine aprire la connessione:

try {
    btSocket.connect();
} catch (IOException e) {
    Toast.makeText(this, "Unable to connect to the device", 
    Toast.LENGTH_SHORT).show();
}

Inviare dati al dispositivo remoto

Una volta connessi, l’oggetto BluetoothSocket mette a disposizione due Stream, uno per inviare (OutputStream) e uno per ricevere (InputStream) dati, nel nostro caso visto che dobbiamo solamente inviare dati verso Arduino utilizzeremo il metodo getOutputStream(), attraverso il quale scriveremo la tripletta dei colori RGB definita con le seekBar.
Per essere sicuri che i dati vengano inviati bisogna ricordarsi sempre di effettuare il flush().

// Send data RGB to Arduino
public void send(byte[] color) {
    try {
        writer = btSocket.getOutputStream();
        writer.write(color);
        writer.flush();
     } catch (IOException e) {
         Toast.makeText(this, "Unable to send message to the device", Toast.LENGTH_SHORT).show();
     }
}

NOTA: E’ buona norma ricordarsi di chiudere la connessione bluetooth dopo che si è concluso il lavoro, in questa app la chiusura è stata effettuata nel metodo onDestroy()  e associata anche al pulsante DISCONNECT.

Il risultato finale dell’app è visibile in Fig.1, dove si possono distinguere i seguenti oggetti:

  • Button RESET LED:  utilizzato per portare tutte le seekBar a 0 e inviare il dato ad Arduino
  • Button DISCONNECT: utilizzato per chiudere la connessione bluetooth con Arduino
  • Button FIND DEVICES: utilizzato per far partire la ricerca dei dispositivi bluetooth
  • ListView: un’area dove inserire i vari dispositivi trovati
  • seekBar: i vari slider utilizzati per variare il colore dei led RGB su Arduino
Layout ArduinoRGB
Fig.1 Layout “ArduinoRGB”

Per scaricare il progetto  completo clicca sull’icona qui sotto.

Download
Download