Para quem ainda não conhece este pequeno carro fica aqui um vídeo.
O i-Racer responde a um conjunto limitado de comandos em hexadecimal cujo significado pode ser encontrado aqui.
O objetivo é demonstrar o funcionamento do envio dos comandos através do Bluetooth, para isso vamos implementar quatro comandos: andar para a frente, andar para trás, virar rodas para direita e virar rodas para a esquerda. Os restantes comandos são igualmente fáceis de implementar basta alterar os códigos enviados ao i-Racer.
Para começar a interface.
O design:
Em execução:
Resumindo:
- oito botões;
- duas listviews, uma onde estão os botões a outra para apresentar os dispositivos Bluetooth emparelhados e detetados.
O xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="false"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:layout_marginTop="4dp"
android:orientation="horizontal" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onLigarClick"
android:text="@string/ligar" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onProcurarClick"
android:text="@string/procurar" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onAbrirClick"
android:text="@string/abrir" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="false"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:layout_marginTop="58dp"
android:orientation="horizontal" >
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onFrenteClick"
android:text="@string/frente" />
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onPararClick"
android:text="@string/parar" />
<Button
android:id="@+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onEsquerdaClick"
android:text="@string/esquerda" />
<Button
android:id="@+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onDireitaClick"
android:text="@string/direita" />
<Button
android:id="@+id/button8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onSairClick"
android:text="@string/sair" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/linearLayout1"
android:layout_marginLeft="0dp"
android:layout_marginTop="82dp"
android:orientation="vertical" >
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
</RelativeLayout>
Agora uma classe para controlar as comunicações. Os tutoriais da google sobre o Bluetooth do Android são muito completos mas também algo complexos, ainda que isso seja normal quando estamos a criar programas de comunicação entre dispositivos.
Para este pequeno dispositivo nós só temos de enviar os comandos não temos de receber, assim a classe criada permite abrir um canal de comunicação, estabelecer uma ligação, enviar dados, devolver o estado da ligação e terminar a ligação.
O código é algo complexo mas está comentado e envia mensagens para o Log de modo que a que seja mais fácil fazer o debug da aplicação.
package edu.pjcferreira.btremote;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
public class ConnectThread extends Thread{
private BluetoothAdapter mBluetoothAdapter;//dispositivo local
private BluetoothSocket mmSocket;
private BluetoothDevice mmDevice; //dispositivo remoto
private static final UUID MY_UUID =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //para ligar a uma board serial tem que ser com este UUID
private int estado;
private OutputStream mmOutStream=null;
//recebe o dispositivo remoto ao qual ligar
public ConnectThread(BluetoothDevice device){
BluetoothSocket tmp=null;
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mmDevice=device;
estado=-1;
try{
Log.d("BT","Abre o canal de ligação");
tmp=mmDevice.createRfcommSocketToServiceRecord(MY_UUID);
mmSocket=tmp;
estado=0;
Log.d("BT","Canal aberto!");
}catch(IOException e){
estado=-1;
}
}
//método para enviar os comandos
public void enviar(byte x){
if (mmOutStream==null){
try{
mmOutStream=mmSocket.getOutputStream();
}catch(IOException e){
return;
}
}
try{
mmOutStream.write(x);
}catch(IOException ee){
return;
}
Log.d("BT","ENVIADO COM SUCESSO!");
}
//função que devolve o estado da ligação
public String getEstado(){
return Integer.toString(estado);
}
//estabelece a ligação
public void run(){
mBluetoothAdapter.cancelDiscovery();
try{
mmSocket.connect();
Log.d("BT","LIGADO");
estado=1;
}catch(IOException connectException){
try{
mmSocket.close();
Log.d("BT","ERRO A LIGAR");
estado=-1;
}catch(IOException closeException){}
}
return;
}
//termina a ligação
public void cancel(){
try{
mmSocket.close();
estado=-1;
}catch(IOException e){}
}
}
Agora com esta classe podemos iniciar a comunicação mas primeiro precisamos de verificar se o dispositivo tem Bluetooth, isso é feito na função onCreate que se apresenta a seguir.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//verifica se existe bt neste dispositivo
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter==null){
showMessage("Não tem suporte para bluetooth!");
}else{
showMessage("Tem suporte para bluetooth!");
}
//lista de dispositivos
mArrayAdapter = new ArrayAdapter<String>(this,R.layout.simplerow,0);
//registar a função que vai receber os dispositivos encontrados
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
//tambem recebe a mensagem que terminou a pesquisa
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, filter);
//lista de dispositivos
listView = (ListView) findViewById(R.id.listView1);
}
Esta função também regista uma função callback para que quando os dispositivos Bluetooth são encontrados poderem ser adicionados à listview. O código dessa função é este:
private final BroadcastReceiver mReceiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent){
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
showMessage(device.getName());
}else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
// showMessage("Terminado! Encontrados " + mArrayAdapter.getCount() + " dispositivos!");
listView.setAdapter(mArrayAdapter);
listView.setClickable(true);
//função a chamar quando clicar num item da lista
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
Object o = listView.getItemAtPosition(position);
showMessage("Selecionou " + o.toString());
//tenta ligar ao dispositivo
dispositivo=position;
String[] endereco=o.toString().split("\n");
/* showMessage(endereco[0]);
showMessage(endereco[1]);*/
remoteBluetoothDevice= mBluetoothAdapter.getRemoteDevice(endereco[1]);
}
});
}
}
};
De seguida precisamos de uma função para pesquisar dispositivos Bluetooth, quer os que estão emparelhados quer outros:
public void onProcurarClick(View botton) {
//primeiro vamos procurar dispositivos emparelhados
pairedDevices=mBluetoothAdapter.getBondedDevices();
//se existem
if(pairedDevices.size()>0){
for(BluetoothDevice device : pairedDevices){
mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
showMessage(device.getName());
}
}
//procurar novos dispositivos
mBluetoothAdapter.startDiscovery();
}
Posto isto podemos abrir uma ligação com o dispositivo selecionado:
public void onAbrirClick(View botton) {
if(remoteBluetoothDevice==null){
showMessage("Primeiro deve selecionar o dispositivo!");
return;
}
ligacao=new ConnectThread(remoteBluetoothDevice);
ligacao.run();
showMessage(ligacao.getEstado());
}
Agora podemos enviar os comandos, para isso temos uma função para cada comando:
public void onFrenteClick(View botton) {
byte x;
x=0x1B;
if(ligacao==null){
showMessage("Primeiro tem de abrir uma ligação.");
return;
}
ligacao.enviar(x);
}
public void onEsquerdaClick(View botton) {
byte x;
x=0x36;
if(ligacao==null){
showMessage("Primeiro tem de abrir uma ligação.");
return;
}
ligacao.enviar(x);
}
public void onDireitaClick(View botton) {
byte x;
x=0x46;
if(ligacao==null){
showMessage("Primeiro tem de abrir uma ligação.");
return;
}
ligacao.enviar(x);
}
public void onPararClick(View botton){
byte x;
x=0x00;
if(ligacao==null){
showMessage("Primeiro tem de abrir uma ligação.");
return;
}
ligacao.enviar(x);
}
Antes de terminarmos precisamos de limpar a casa, o botão sair e ainda a função onDestroy:
public void onSairClick(View botton) {
byte x;
x=0x00;
if(ligacao!=null){
ligacao.enviar(x);
ligacao.cancel();
}
finish();
}
protected void onDestroy() {
byte x;
x=0x00;
if(ligacao!=null){
ligacao.enviar(x);
ligacao.cancel();
}
this.unregisterReceiver(mReceiver);
super.onDestroy();
}
Quase me esquecia as permissões, neste projeto precisamos de duas:
- Permissão para utilizar Bluetooth:
<uses-permission android:name="android.permission.BLUETOOTH"/>
- Permissão para pesquisar dispositivos com o Bluetooth:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
Sem comentários:
Enviar um comentário