Depois de quase um século, estamos aqui voltando a falar sobre comunicação Bluetooth. Agora, entre Arduino e Android. A realização desse post foi fortemente influenciada pelos comentários no post sobre programação Bluetooth entre dois dispositivos Android. Prontos? Vamos!
Para mostrar como se faz a conexão e comunicação entre um aparelho Android e um Arduino, vamos desenvolver um contador. Algo bem simples. O app Android terá apenas uma tela, nesta tela um TextView para exibir o valor do contador e outro para exibir o status da conexão. O valor do contador exibido será calculado pelo Arduino e transmitido ao Android, que é apenas responsável por exibir o número (assim demonstramos transmissões Arduino -> Android). Se o usuário clicar na tela, o Android envia um comando para que o contador seja reiniciado (assim demonstramos transmissões Android -> Arduino). Que acham? Bem simples e faz sentido, não?
Módulo Bluetooth
A maioria dos Arduinos não possui um módulo Bluetooth embutido (com exceção de alguns como ArduinoBT). Logo, é necessário um módulo de comunicação Bluetooth externo. Alguns exemplos são os módulos HC-05, HC-06, HC-07 e HC-08. Não vamos explorar muito as diferenças entre esses módulos. Mas algumas informações são úteis. Uma das principais diferenças entre o HC-05 e o HC-06 é que o HC-05 pode atuar como master ou slave, enquanto o HC-06 atua apenas como slave. Isso implica que o HC-06 não é capaz de iniciar uma conexão com outro dispositivo, ele apenas pode aceitar ou negar quando outro dispositivo tenta iniciar uma conexão. Mas após a conexão ser estabelecida, a comunicação de dados ocorre em duas vias, tanto do módulo HC-06 para o outro aparelho quanto do aparelho para o HC-06. Neste tutorial, vamos usar o módulo HC-06 para realizar a comunicação.
O módulo HC-06 possui quatro pinos: VCC e GND para alimentação do módulo. O pino VCC pode ser conectado diretamente à saída de 5V do Arduino, já que suporta tensões entre 3.3V e 6V. Os outros dois, TXD e RXD são os pinos de escrita e leitura do módulo. Estes serão utilizados para comunicação serial entre o HC-06 e o Arduino.
Você deve estar se perguntando como o módulo Bluetooth se integra ao Arduino e como os dados vão sair do Arduino e chegar no Android e vice-versa. É o que veremos.
Arduino
A integração entre o Arduino e o HC-06 é bem simples. Todas as informações que o Arduino precisar transmitir ao Android deverão passar antes pelo módulo Bluetooth HC-06 e vice versa. A comunicação entre Arduino e HC-06 ocorre via serial. Então, tecnicamente, bastaria conectar os pinos seriais (RX, TX) do Arduino aos pinos seriais (TXD, RXD) do HC-06 e usar as funções da biblioteca Serial para ler e escrever caracteres. Os caracteres que chegassem à porta serial RX do Arduino correspondem aos dados que o Android está enviando. Os caracteres escritos através da porta serial TX do Arduino seriam enviados ao Android pelo módulo Bluetooth. Mas encontrei problemas ao tentar utilizar os pinos RX, TX padrões do Arduino. Apesar de obter sucesso realizando a conexão, as informações simplesmente não fluíam entre os lados da conexão.
Para resolver esse problema, usei a biblioteca SoftwareSerial. O que essa biblioteca faz é simular as portas seriais RX, TX utilizando os pinos digitais. Isso é maravilhoso! Então, em vez de conectar (RXD,TXD) do módulo HC-06 a (TX,RX) do Arduino, podemos escolher dois pinos digitais para simular as porta seriais e fazer a mesma conexão. Aqui, escolhi de forma arbitrária os pinos 8 e 9 para RX e TX, respectivamente. Um detalhe que pode vir a ser bem importante: o módulo HC-06 trabalha com níveis lógicos de tensão de 3.3V. Isso significa que os pinos RXD do HC-06 só estão preparados para trabalhar em torno de 3.3V, enquanto o Arduino estará oferecendo 5V na saída do pino 9 (que estamos usando como TX). Para diminuir a chance de danificar o módulo Bluetooth, é bem recomendado que você diminua a tensão de 5V para 3.3V. Façamos isso com um divisor de tensão, que tal? Dessa maneira, a saída de 5V do pino digital do Arduino chega ao pino RXD do módulo Bluetooth como 3.3V e não corremos risco de fritar o senhor HC-06. Mostramos esse divisor de tensão no esquemático lá embaixo, relaxa.
Feitas essas conexões, o resto é alimentar o módulo Bluetooth com 5V e pronto. Veja o esquemático, é tranquilo. Leia o código e os comentários, é favorável.
#include <SoftwareSerial.h> /* Definição de um objeto SoftwareSerial. * Usaremos os pinos 8 e 9, como RX e TX, respectivamente. */ SoftwareSerial serial(8, 9); /* A String data será utilizada para armazenar dados vindos * do Android. O inteiro counter será incrementado a cada * execução do loop principal e transmitido ao Android. * O led conectado ao pino 2 é mais para debug. É útil. */ String data = ""; int counter = 0; int led = 2; /* Nosso setup inclui a inicialização do objeto SoftwareSerial, * com uma baud rate de 9600 bps. A definição do pino do led * como saída e um delay de 2 segundos, só para garantir que * o módulo HC-06 iniciou direitinho. */ void setup() { serial.begin(9600); pinMode(led, OUTPUT); delay(2000); } /* Vamos pelo loop passo a passo. */ void loop() { /* No início de cada loop, verificamos se há algo no buffer * da nossa serial. Se houver bytes disponíveis, significa * que o Android enviou algo, então faremos a leitura do * novo caractere e adicionamos ao final da string data. */ while(serial.available() > 0) { data += char(serial.read()); } /* Se o Arduino receber a string "restart" seguida de uma * quebra de linha, reinicializamos o contador e ligamos * o led por um segundo. Esse comando indicará que a * comunicação no sentido Android -> Arduino está sendo * realizada corretamente. */ if(data == "restart\n") { digitalWrite(led, HIGH); counter = 0; delay(1000); digitalWrite(led, LOW); } /* Ao fim de cada loop, o Arduino transmite uma string * representando o valor armazenado no contador, seguido * de uma quebra de linha. Essa string será enviada para o * módulo HC-06 e daí para o Android. */ serial.print(String(counter)); serial.print('\n'); /* Finalmente, incrementamos o contador e limpamos data. */ counter = counter + 1; data = ""; /* Um pequeno delay para evitar bugs estranhos. */ delay(10); }
Android
Permissões
Precisamos obter permissão para utilizar o dispositivo Bluetooth do Android. Fazemos isso adicionando essas linhas ao Manifest.xml, dentro da tag <manifest>. Faça isso.
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
Design
Já comentamos como seria nossa interface gráfica no começo do post, lembra? Uma tela, um TextView para o contador, um TextView para mensagem de status da conexão. Abaixo estão o arquivo XML que usei e um print de como deve ficar a tela do app. Modifique o visual como preferir. Sei lá, coloque no plano de fundo uma selfie sua.
<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" android:paddingLeft="4dp" android:paddingRight="4dp" android:paddingTop="4dp" android:paddingBottom="4dp" tools:context=".MainActivity" android:background="#ff6eb6ff" android:onClick="restartCounter"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="DRAGÃO" android:id="@+id/counterMessage" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:textSize="@dimen/abc_text_size_display_3_material" android:textColor="#ffffffff"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello there!" android:id="@+id/statusMessage" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:textColor="#ffffffff"/> </RelativeLayout>
Aproveite, procure o arquivo styles.xml, que provavelmente estará no caminho app/src/main/res/values/styles.xml e deixe-o assim:
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> </style> </resources>
Isso serve para remover aquela barra superior com o nome do aplicativo. Faz com que o design fique mais limpo, mais bonito.
Programação
Classe ConnectionThread
Se você leu Programação Bluetooth no Android, sabe que criamos uma classe, subclasse de Thread, para gerenciar a conexão Bluetooth. Faremos o mesmo aqui. Não precisamos reescrever essa classe, pois já fizemos isso. É claro, mesmo que já tenhamos a classe ConnectionThread, isso não nos impede de fazer alguns aprimoramentos. Então, o código abaixo é uma versão levemente aprimorada da ConnectionThread que fizemos no primeiro post sobre Bluetooth.
package br.com.dragaosemchama.supercounter; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.os.Bundle; import android.os.Message; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.UUID; public class ConnectionThread extends Thread{ BluetoothSocket btSocket = null; BluetoothServerSocket btServerSocket = null; InputStream input = null; OutputStream output = null; String btDevAddress = null; String myUUID = "00001101-0000-1000-8000-00805F9B34FB"; boolean server; boolean running = false; boolean isConnected = false; /* Este construtor prepara o dispositivo para atuar como servidor. */ public ConnectionThread() { this.server = true; } /* Este construtor prepara o dispositivo para atuar como cliente. Tem como argumento uma string contendo o endereço MAC do dispositivo Bluetooth para o qual deve ser solicitada uma conexão. */ public ConnectionThread(String btDevAddress) { this.server = false; this.btDevAddress = btDevAddress; } /* O método run() contem as instruções que serão efetivamente realizadas em uma nova thread. */ public void run() { /* Anuncia que a thread está sendo executada. Pega uma referência para o adaptador Bluetooth padrão. */ this.running = true; BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); /* Determina que ações executar dependendo se a thread está configurada para atuar como servidor ou cliente. */ if(this.server) { /* Servidor. */ try { /* Cria um socket de servidor Bluetooth. O socket servidor será usado apenas para iniciar a conexão. Permanece em estado de espera até que algum cliente estabeleça uma conexão. */ btServerSocket = btAdapter.listenUsingRfcommWithServiceRecord("Super Counter", UUID.fromString(myUUID)); btSocket = btServerSocket.accept(); /* Se a conexão foi estabelecida corretamente, o socket servidor pode ser liberado. */ if(btSocket != null) { btServerSocket.close(); } } catch (IOException e) { /* Caso ocorra alguma exceção, exibe o stack trace para debug. Envia um código para a Activity principal, informando que a conexão falhou. */ e.printStackTrace(); toMainActivity("---N".getBytes()); } } else { /* Cliente. */ try { /* Obtem uma representação do dispositivo Bluetooth com endereço btDevAddress. Cria um socket Bluetooth. */ BluetoothDevice btDevice = btAdapter.getRemoteDevice(btDevAddress); btSocket = btDevice.createRfcommSocketToServiceRecord(UUID.fromString(myUUID)); /* Envia ao sistema um comando para cancelar qualquer processo de descoberta em execução. */ btAdapter.cancelDiscovery(); /* Solicita uma conexão ao dispositivo cujo endereço é btDevAddress. Permanece em estado de espera até que a conexão seja estabelecida. */ if (btSocket != null) { btSocket.connect(); } } catch (IOException e) { /* Caso ocorra alguma exceção, exibe o stack trace para debug. Envia um código para a Activity principal, informando que a conexão falhou. */ e.printStackTrace(); toMainActivity("---N".getBytes()); } } /* Pronto, estamos conectados! Agora, só precisamos gerenciar a conexão. ... */ if(btSocket != null) { /* Envia um código para a Activity principal informando que a a conexão ocorreu com sucesso. */ this.isConnected = true; toMainActivity("---S".getBytes()); try { /* Obtem referências para os fluxos de entrada e saída do socket Bluetooth. */ input = btSocket.getInputStream(); output = btSocket.getOutputStream(); /* Permanece em estado de espera até que uma mensagem seja recebida. Armazena a mensagem recebida no buffer. Envia a mensagem recebida para a Activity principal, do primeiro ao último byte lido. Esta thread permanecerá em estado de escuta até que a variável running assuma o valor false. */ while(running) { /* Cria um byte array para armazenar temporariamente uma mensagem recebida. O inteiro bytes representará o número de bytes lidos na última transmissão recebida. O inteiro bytesRead representa o número total de bytes lidos antes de uma quebra de linha. A quebra de linha representa o fim da mensagem. */ byte[] buffer = new byte[1024]; int bytes; int bytesRead = -1; /* Lê os bytes recebidos e os armazena no buffer até que uma quebra de linha seja identificada. Nesse ponto, assumimos que a mensagem foi transmitida por completo. */ do { bytes = input.read(buffer, bytesRead+1, 1); bytesRead+=bytes; } while(buffer[bytesRead] != '\n'); /* A mensagem recebida é enviada para a Activity principal. */ toMainActivity(Arrays.copyOfRange(buffer, 0, bytesRead-1)); } } catch (IOException e) { /* Caso ocorra alguma exceção, exibe o stack trace para debug. Envia um código para a Activity principal, informando que a conexão falhou. */ e.printStackTrace(); toMainActivity("---N".getBytes()); this.isConnected = false; } } } /* Utiliza um handler para enviar um byte array à Activity principal. O byte array é encapsulado em um Bundle e posteriormente em uma Message antes de ser enviado. */ private void toMainActivity(byte[] data) { Message message = new Message(); Bundle bundle = new Bundle(); bundle.putByteArray("data", data); message.setData(bundle); MainActivity.handler.sendMessage(message); } /* Método utilizado pela Activity principal para transmitir uma mensagem ao outro lado da conexão. A mensagem deve ser representada por um byte array. */ public void write(byte[] data) { if(output != null) { try { /* Transmite a mensagem. */ output.write(data); } catch (IOException e) { e.printStackTrace(); } } else { /* Envia à Activity principal um código de erro durante a conexão. */ toMainActivity("---N".getBytes()); } } /* Método utilizado pela Activity principal para encerrar a conexão */ public void cancel() { try { running = false; this.isConnected = false; btServerSocket.close(); btSocket.close(); } catch (IOException e) { e.printStackTrace(); } running = false; this.isConnected = false; } public boolean isConnected() { return this.isConnected; } }
As alterações feitas são (1) adição da variável booleana isConnected, que indica exatamente o que o nome diz: a thread Bluetooth está conectada ou não? e (2) para garantir que as mensagens transmitidas do Arduino para o Android não cheguem quebradas, leremos byte a byte o que for recebido no Android e armazenaremos em um buffer. Quando, enfim, recebermos um caractere ‘\n’, uma quebra de linha, então assumimos que a mensagem completa foi recebida e trabalhamos com ela. Você pode ver essa alteração no loop while localizado entre as linhas 159 e 186 da classe ConnectionThread.
Ótimo. Adicione essa classe ao projeto e siga em frente.
Activity Principal
Já que nosso único objetivo é conectar o Android a um módulo Bluetooth que provavelmente já conhecemos o endereço, não haverá aquela complicação toda de realizar busca por dispositivos, etc. Já sabemos o endereço do nosso módulo HC-06. Você já sabe? É bom saber, pois vamos precisar dele.
Então, de forma sucinta, o que faremos na Activity principal é ativar o hardware Bluetooth, iniciar uma conexão com um dispositivo pré-definido e atualizar os TextView relativos ao contador e ao status da conexão.
Veja abaixo o código que utilizei para a Activity Principal. Eu poderia explicá-lo novamente, mas como adicionei comentários em praticamente todos os trechos do código, acredito que será tranquilo acompanhá-lo e entendê-lo. Você consegue! Que a força esteja com você.
package br.com.dragaosemchama.supercounter; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; public class MainActivity extends Activity { /* Definição dos objetos que serão usados na Activity Principal statusMessage mostrará mensagens de status sobre a conexão counterMessage mostrará o valor do contador como recebido do Arduino connect é a thread de gerenciamento da conexão Bluetooth */ static TextView statusMessage; static TextView counterMessage; ConnectionThread connect; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /* Link entre os elementos da interface gráfica e suas representações em Java. */ statusMessage = (TextView) findViewById(R.id.statusMessage); counterMessage = (TextView) findViewById(R.id.counterMessage); /* Teste rápido. O hardware Bluetooth do dispositivo Android está funcionando ou está bugado de forma misteriosa? Será que existe, pelo menos? Provavelmente existe. */ BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); if (btAdapter == null) { statusMessage.setText("Que pena! Hardware Bluetooth não está funcionando :("); } else { statusMessage.setText("Ótimo! Hardware Bluetooth está funcionando :)"); } /* A chamada do seguinte método liga o Bluetooth no dispositivo Android sem pedido de autorização do usuário. É altamente não recomendado no Android Developers, mas, para simplificar este app, que é um demo, faremos isso. Na prática, em um app que vai ser usado por outras pessoas, não faça isso. */ btAdapter.enable(); /* Definição da thread de conexão como cliente. Aqui, você deve incluir o endereço MAC do seu módulo Bluetooth. O app iniciará e vai automaticamente buscar por esse endereço. Caso não encontre, dirá que houve um erro de conexão. */ connect = new ConnectionThread("00:14:03:18:43:45"); connect.start(); /* Um descanso rápido, para evitar bugs esquisitos. */ try { Thread.sleep(1000); } catch (Exception E) { E.printStackTrace(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } public static Handler handler = new Handler() { @Override public void handleMessage(Message msg) { /* Esse método é invocado na Activity principal sempre que a thread de conexão Bluetooth recebe uma mensagem. */ Bundle bundle = msg.getData(); byte[] data = bundle.getByteArray("data"); String dataString= new String(data); /* Aqui ocorre a decisão de ação, baseada na string recebida. Caso a string corresponda à uma das mensagens de status de conexão (iniciadas com --), atualizamos o status da conexão conforme o código. */ if(dataString.equals("---N")) statusMessage.setText("Ocorreu um erro durante a conexão D:"); else if(dataString.equals("---S")) statusMessage.setText("Conectado :D"); else { /* Se a mensagem não for um código de status, então ela deve ser tratada pelo aplicativo como uma mensagem vinda diretamente do outro lado da conexão. Nesse caso, simplesmente atualizamos o valor contido no TextView do contador. */ counterMessage.setText(dataString); } } }; /* Esse método é invocado sempre que o usuário clicar na TextView que contem o contador. O app Android transmite a string "restart", seguido de uma quebra de linha, que é o indicador de fim de mensagem. */ public void restartCounter(View view) { connect.write("restart\n".getBytes()); } }
Pronto, depois de modificar o código de sua Activity principal e deixá-lo aproximadamente como mencionado acima, você está pronto para testar o app. Acredito fortemente nisso. Se faltar algo, me avisem, obrigado!
Notas Breves
Provavelmente, na primeira conexão entre o seu dispositivo Android e o módulo Bluetooth, você deverá entrar com uma senha para realizar o pareamento. Para o meu dispositivo, um módulo HC-06, a senha correta era 1234. Caso essa não funcione, tente 0000. Se essa não funcionar, então não sei. É uma boa ideia pesquisar na Internet sobre o assunto, pois seu módulo Bluetooth é estranho.
Excelente. Obrigado por continuar a ajudar não só a mim como aos demais.
Queria sua opinião em um certo caso. (Se não for pedir muito rsrs.) Os dados enviado ao Android é recebido na Activity principal (A). Certo. Na aplicação que estou desenvolvendo, ao ser detectado uma presença no sensor, é enviado uma string para o Android. Possuo uma outra Tela, uma segunda Activity (B), e preciso receber esse dado ATUALIZADO nessa Activity B. Essa Activity B é uma simulação de um estacionamento. Onde tenho o layout e os carros. Os carros ficam invisíveis. Ao receber o dado enviado do Arduino, na detecção do sensor, O carro aparece, indicando que a vaga está ocupada. Porém, preciso fazer esse sincronismo nessa Activity B. Detectou presença? O carro aparece. Não detectou presença? O carro desaparece.
Bom, é bem isso a aplicação que estou fazendo. No momento. eu consigo enviar o dado da Activity A para a B. Mas se o dado alterar de valor, na Activity B ele permanece com o valor antigo, isso porque o dado não foi atualizado.
Queria saber de você a melhor forma de fazer isso. Pensei em colocar o handler na Activity secundária. Resolveria a situação?
Novamente, muito obrigado pela ajuda que está me dando. Peço desculpa por comentar tanto seus post trazendo problemas haha.
Saudações de novo, Jayme, hahahah,
Sim. Acredito fortemente que colocar o handler na Activity B resolverá o problema. Afinal, você só precisa dos dados que vêm pelo Bluetooth nessa Activity B, correto?
Agora, se você inicia a conexão na Activity A e espera os dados na Activity B, isso pode se tornar mais complicado para desenvolver e ainda não fiz testes para saber se a classe ConnectionThread funciona direitinho nessa situação.
Acredito que você deveria testar fazer da seguinte maneira: se você precisa dos dados Bluetooth apenas na Activity B, leve todas as operações Bluetooth para essa Activity. Se você sair dela, feche a conexão direitinho, quando você voltar para ela, reconecte. Tente fazer algo nesse estilo, para diminuir a complexidade do app.
E não se preocupe em perguntar. Você está certo! A gente também aprende muito quando tem que pensar em como resolver os problemas. Sucesso desenvolvendo seu app! Qualquer coisa, estamos aqui.
Olá amigo, excelente trabalho. Ainda sou novo no Android e Arduino, segui o passo a passo de seu código, mas aparece uma correção q ñ sei onde consertar:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
O programa pede para criar campo ou class “menu” em Jonas.Viana.R
Agradeço sua ajuda se pd dizer o q devo fazer ou onde devo, ñ consigo fazer com o programa compile.
Grato por qq ajuda.
Olá, Jonas,
Isso parece muito um erro de referência dentro do Android Studio. Suponho que você esteja usando Android Studio. Mas vamos ver algumas possibilidades.
Você consegue encontrar o arquivo R.java no seu projeto? Se não, tente Build -> Clean Project. Isso vai reconstruir o projeto e o arquivo R também.
Se o arquivo R.java já existe, você vê problemas em outras partes do código relacionadas ao arquivo R?
Na pior das hipóteses, você ainda pode iniciar um novo projeto e copiar/colar os arquivos de código que você já fez. É possível quando o projeto é pequeno. Muitos problemas que tive com este R.java só foram resolvidos assim.
Olá amigo. Encontro o arquivo R.java e complemento com a classe e campo menu sugerido pelo programa(Netbeans), corrige o erro aparente, mas sempre q compilo, o msm erro aparece e o código q alterei no R.java volta como era antes, apagando o q escrevi.
Arquivo R.java antes do complemento pedido pelo programa::
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package Jonas.AlvesVV;
public final class R {
static int menu_main;
public static final class attr {
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
public static final class id {
public static final int counterMessage=0x7f050000;
public static final int statusMessage=0x7f050001;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040000;
}
}
Arquivo R.Java dps q complemento e a “sugestão” desaparece::
package Jonas.AlvesVV;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
public static final class id {
public static final int counterMessage=0x7f050000;
public static final int statusMessage=0x7f050001;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040000;
}
static class menu {
static int menu_main;
public menu() {
}
}
}
Tentei alterar o arquivo e salvar, da certo, mas ao compilar volta td como era antes.
Grato por sua atenção.
Olá amigo, novamente aqui. Consegui resolver o problema criando uma nova pasta com um novo arquivo xml dentro, o qual o programa pedia p implementar(menu/menu_main.xml). Agora o programa funciona perfeitamente. Peço sua ajuda quanto a algum conteúdo especifico sobre comunicação Bluetooth, a questão do array e seus dados, essa parte ñ compreendi muito bem a lógica. Quero alterar o programa para q envie um numero de serie a cada clique do botão(Ex: 12 45 78 89). Mas essa parte de comunicação ainda estou perdido. Pd ser um site, artigo ou coisa assim. Grato por qq ajuda.
Saudações, Jonas,
Que bom que você conseguiu resolver o problema! Problemas com o R são, eu devo admitir, muito irritantes.
Sobre o array, o método de envio de dados via Bluetooth aceita apenas dados formatados como vetores de bytes (tipo byte[]). Então quando você precisa enviar uma String, é necessário converter essa String para byte[] antes.
Veja as linhas 134 a 136 do último código do post, referente ao método restartCounter(). Esse é o método executado quando você clica na tela. Ele é executado porque colocamos o atributo android:onClick=”restartCounter” na descrição de atributos da tela no arquivo main_activity.xml, na seção Design. Se criarmos um botão e adicionarmos o atributo android:onClick, indicando qual método deve ser executado, pronto, você tem o botão que executa uma determinada função. Veja a linha 135 de MainActivity.java:
connect.write(“restart\n”.getBytes());
Se você substituir a palavra “restart” por “12 45 78 79” vai estar transmitindo essas informações sempre que clicar na tela (ou futuramente no botão que você vai criar).
Muito obrigado pela atenção amigo, ajuda demais, porém me expressei errado.
Estou tentando fazer com que o Android exiba um numero sequencial(12 45 78), que serve como um ID p/ cada mercadoria, mandado pelo Arduino, quando alguém aciona o botão de entrada do arduino.
No caso tento alterar os comando a partir da linha 159 [[[ while(running) ]]] da ConnectionThread e das linhas 94 a 128 [[[ Handler handler ]]] da MainActivity, para receber esse numero sequencial e exibir.
Minha dificuldade está em alterar esses comandos p receber/exibir esse numero sequencial(12 45 78).
Novamente, muito obrigado.
Entendo. Vamos pensar um pouco. O sistema é o seguinte: você clica em um botão conectado ao Arduino, o Arduino interpreta o clique e envia uma String “12 45 78” usando a função serial.print(), assim como nas linhas 58 e 59 do código do Arduino que utilizamos no post.
A classe ConnectionThread não precisa ser alterada. Ela lida com o recebimento de dados e a transmissão deles de forma transparente só pra você usá-la no seu projeto. A não ser que você queira incrementar as capacidades dela, melhor deixá-la quietinha.
Pronto, usando o código do Android da forma que ele está, você já receberá a String no app e o método handleMessage() da MainActivity será chamado quando a String for recebida. A linha 124 é responsável por exibir essa String no elemento TextView que denominamos counterMessage. Até aí tudo bem? Imagino que o que você quer fazer já está exemplificado aqui. Você só precisaria criar e posicionar um elemento TextView (que é basicamente um elemento de exibição de texto) dentro da sua interface gráfica, criar um objeto referente a ele dentro da sua Activity e usar o método setText() para atualizar o valor sempre que a String recebida for referente ao código sequencial que você mencionou. Da mesma forma que fizemos aqui para counterMessage.
Era essa sua dúvida? Espero ter esclarecido.
Boa noite, estou querendo desenvolver um app android para apenas receber informações do arduino, essa programação é feita no software do arduino?
Jessica,
A programação deve ser feita nos dois dispositivos, tanto no Android quanto no Arduino. Afinal tanto o aplicativo quanto o firmware (o programa que está executando no Arduino) precisam estar conectados e operar de forma sincronizada para que as informações sejam enviadas e processadas corretamente. Nesse post descrevemos todos os passos para realizar a conexão e como enviar informações do Arduino para o Android.
Is this text italic?
Yes!
Olá Amigo.
Excelente tutorial.
Tentei testar o aplicativo e no meu caso esta dando um erro na linha 188 no método copyOfRange, como não reconhecido pela API.
Vc sabe como eu posso resolver esse erro?
Abraços!!!.
Olá, Rafael,
Acho bem improvável que esteja usando uma API abaixo da 9, mas verifique isso. Porque o método copyOfRange foi introduzido na API do Android nível 9, então é necessário ter no mínimo essa API instalada antes de usar o método Arrays.copyOfRange(). Se não for nada relacionado a isso e o erro persistir, poderia me mandar a mensagem de erro exata? Espero que dê certo.
Beleza David!!!
Era exatamente, isso que vc comentou, usei uma outra API e o eclipse parou de identificar o erro, porém, agora ao executar o aplicativo em um tablet, o mesmo executa, conecta com o módulo bluetooth e em seguida fecha sozinho.
Eu percebi que no Arduino quando programo para enviar pela serial de modo contínuo caracteres ou números sem o “/n” o aplicativo conecta e não fecha,mas os dados não são mostrados no TextView e quando eu programo no Arduino para enviar dados com o “/n”, ele apenas conecta e fecha logo em seguida.
Rafael,
Que bom que funcionou! Esse seria um erro bem bizarro. Mas eu tenho uma suspeita, o símbolo de quebra de linha (que estamos usando como término de mensagem) é “\n”, com a barra invertida. No seu comentário, você menciona que no código está “/n”.
Espero que seja isso. Caso não seja, estamos aqui.
Então David, realmente no código esta correto o “\n”.
Eu estive olhando o código no eclipse e percebi que no arquivo AndroidManifest não há qualquer declaração da classe ConnectionThread(), apenas da Activity MainActivity, no caso, seria necessário a declaração da classe ConnectionThread() no AndroidManifest, mesmo ela não sendo uma Activity?
Se sim, como eu a declararia?
Abraços!!!
Rafael,
Não é necessário declarar as classes, exceto Activities, no AndroidManifest. Você pode testar o código usando o debugger do Android Studio e me mostrar qual a mensagem de erro dada pelo debugger quando o aplicativo trava? Seria muito útil. Além disso, os códigos que você tá usando de ConnectionThread, MainBluetoothActivity e o código do Arduino que fazem o sistema travar seriam úteis. Se você puder compartilhá-los comigo, use o site https://codeshare.io/. É possível criar um arquivo de código e compartilhar usando a URL que eles criaram.
Sei que não é sobre o tópico, mas envolve Android Studio.
Como consigo colocar um Gráfico no android?
Agradeço desde já.
Gabriel,
Nunca fiz um aplicativo que necessitasse de gráficos, mas imagino que a melhor maneira de adicionar gráficos seja recorrer a pacotes especializados para essa função. Numa pesquisa rápida encontrei dois pacotes bem promissores. Não tive tempo de testá-los, mas você pode ver o que consegue com eles. Têm demonstrações e exemplos nos sites de cada pacote:
https://github.com/PhilJay/MPAndroidChart
http://www.android-graphview.org/
É uma ideia para um post no futuro. Obrigado.
Ótimo post amigo. Queria somente tirar uma dúvida, se possível.
Eu fiz o aplicativo do jeito que está aí e funcionou. Porém, para o contador zerar preciso tocar um número aleatório de vezes na tela. As vezes com um toque já funciona, as vezes com 2, 3 ou 4.
Não sei se deu pra entender direito, mas não era pra zerar a cada toque na tela? É como se a comunicação estivesse lenta e ele não capturasse a mensagem de restart.
Grato.
Olá, Leandro,
Perdão pela demora absurda. Infelizmente não sei por que esse problema está ocorrendo na sua montagem.
Já tive alguns problemas assim e foram problemas na interface gráfica, em que um elemento X da tela estava sobreposto a outro elemento Y; e o toque que supostamente era no elemento Y às vezes acabava sendo redirecionado ao callback do elemento X, erradamente. Também é possível que seja alguma falha de comunicação entre os dispositivos. Uma ideia, se não atrapalhar seu projeto, é adicionar redundância, ou seja, a cada toque na tela, transmitir 3 ou 4 vezes o sinal para o Arduino reiniciar a contagem. Assim, a probabilidade do Arduino não receber a mensagem é menor.
Espero que dê certo
Olá
Amigo uma duvida… fiz uma classe com thread em loop que fica monitorando a conexao bluetooth (bluetooth.isConnect())… e enviando uma mensagem para a MainActivy através do handler porem a resposta fica oscilando entre true e false mesmo o led do bluetooth do arduino estando ligado e comunicando normalmente…
Alguma ideia do que pode ser?
Desde ja agradeço pelo tutorial serviu de grande ajuda para mim
Saudações, Mateus,
Não faço ideia de que problema pode estar ocorrendo. Mas tenho uma ideia pra você testar, se ainda estiver no projeto, desculpa pela demora.
Não sei exatamente a maneira que você implementou a thread. Mas você pode tentar fazer com que ela verifique a conexão Bluetooth um certo número de vezes por segundo. Por exemplo, 4 vezes por segundo, a intervalos de 250 milissegundos. No entanto, você só atualiza a Activity principal após um segundo, ou seja, após os 4 testes. Isso vai fazer com que a oscilação dos valores true e false diminua. Verifique como a oscilação varia conforme você muda o número de testes por segundo e escolha os melhores valores. Você poderia também atualizar só a cada dois segundos. Depende da sua aplicação.
Estou com sofrendo com bugs safados no meu projeto rsrs.
Vou tentar explicar como é o meu projeto e falar como é esse bug, pra ver se vocês conseguem visualizar o projeto e tentar identificar o que pode tá causando esse maldito bug kk.
O objetivo do meu projeto é controlar um ventilador pelo celular via bluetooth e pelo botão físico (chave seletora de velocidade) já existente no ventilador. No ventilador, existe uma chave seletora que controla a velocidade do mesmo. É uma chave que possui 1 comum(que vem da rede elétrica) e 3 saídas para o ventilador. A velocidade é de acordo com cada saída. No caso é um ventilador de 3 velocidades obviamente. O que eu fiz foi seguinte: substituí a chave seletora por 3 relés. o comum da rede que vai pra chave seletora, eu desconectei da chave seletora e conectei no comum de cada relé e os fios das saídas da chave seletora que vão para o ventilador eu desconectei da chave seletora e conectei nos NA de cada relé. E no comum da chave seletora, eu conectei o GND, e as saídas da chave seletora, eu conectei em 3 portas digitais do meu Arduíno Nano. Ou seja, a chave seletora virou um periférico de entrada de dados. obs: Sabemos que são dois fios que vem da rede elétrica pro ventilador, um fio vai direto para o motor do ventilador e ou outro vai pra chave seletora e as saídas da chave seletora que vão pra o ventilador. As modificações que eu fiz foi no fio que vai para a chave seletora, o outro fio que vai direto para o motor do ventilador eu não modifiquei nada.
Usando o módulo bluetooth , eu conectei meu celular, usando um app. Assim posso realizar o controle do ventilador (regulagem de velocidade e ligar/desligar) tanto pelo celular, quanto pela própria chave seletora do mesmo.
O projeto funciona basicamente assim: Quando enviar o comando do celular ou da chave seletora pra escolher determinada velocidade ou ligar/desligar o ventilador, o arduíno vai energizar o relé responsável pela tal velocidade ou ligar/desligar o ventilador, dependendo do comando enviado do celular ou da chave seletora. Ou seja, o arduíno vai controlar os relés que são responsáveis pela regulagem de velocidade e por ligar/desligar o ventilador. Ex: se eu enviar o comando do celular ou da chave seletora pro ventilador girar na velocidade 3, o arduíno vai energizar o relé responsável por essa velocidade, conectando o fio responsável por essa velocidade a rede elétrica. E quando enviar o comando pra desligar o ventilador, o arduíno vai desenergizar todos os relés e consequentemente desligará o ventilador.
Realizei os testes do meu projeto sem carga, e funcionou perfeitamente. Mas com carga (ventilador), fica dando “bug” no meu projeto.
Esse bug é o seguinte: Quando mando algum comando do celular pra determinada velocidade, os 3 relés só piscam (eles ligam e desligam). Aí tenho que dar o comando 5 ou 6 vezes, pro relé da velocidade desejada, ligar. Volto a repetir, esse bug só acontece quando o ventilador tá conectado aos relés. Sem o ventilador, os relés funcionam perfeitamente. Obs: os parâmetros (tensão e corrente) da minha carga(ventilador) estão dentro dos limites de parâmetros dos relés e esse erro só acontece quando envio os comandos pelo celular. Na chave seletora, funciona perfeitamente.
Olá Relclenson!
Seu projeto foi entendido. Mas o motivo do bug, isso definitivamente não sei te dizer. Pelo que vi, quando você não conecta o ventilador ao sistema, o envio de comandos pelo celular funciona bem. Mas se você adicionar o ventilador ao sistema, o envio de comandos pelo celular fica intermitente, precisa enviar o comando várias vezes.
Outro leitor já comentou que teve esse problema de intermitência. Um forma de contornar o problema é usar redundância: a cada clique na tela do Android, enviar o comando ao Arduino múltiplas vezes, visando a diminuir a probabilidade de o Arduino não receber a mensagem. Sugiro que você tente aplicar essa ideia ao seu projeto e veja se dá certo.
Espero que funcione!
Postando aqui só pra agradecer o excelente tutorial, David! Me ajudou mto em um projeto, usei o HC-05 e deixei a base do programa q postou, funcionou perfeitamente a comunicação entre o arduino e o android.
Abraços!
:)
Amigão, você poderia passar essa código para mim, por favor?
Olá, Jhonathan,
Rapaz, o código já está todo no post, basta copiar e colar, hahah.
Só mais uma pergunta, estou testando esse código com um módulo HC-06, porém não estou obtendo êxito. Eu tenho que configurar algo no celular ou ajustar no código para que essa aplicação funcione?
Sim, você precisa usar o endereço MAC do seu dispositivo HC-06 na linha 59 do código da Activity Principal. Além disso, pra que a conexão Bluetooth seja iniciada, você precisa parear o seu aparelho Android com o HC-06, como você normalmente faz pra parear dois dispositivos Bluetooth.
Cara !! Muito Obrigado.
voce acabou de salvar o meu TG !!! hahaha, vou acompanhar seu site
Isso é ótimo, Edimar! Obrigado pelo apoio o/
Podes me ajudar? Quando eu abro o aplicativo, ele conecta e logo fecha sozinho. Estou enviando dados continuamente com um HC-06. Obrigado e parabéns pelo trabalho!
Esqueci de falar antes: estou usando um PIC para a parte microcontrolada. No pic, envio os dados como *char.
na verdade consegui resolver simplismente mandando o pic enviar o \n no fim haha. obrigado e desculpe o encômodo!
Que ótimo, Eduardo! Bom saber que as instruções do tutorial podem ser modeladas pra funcionar com PIC também.
Buenas, estou com um problema que não sei resolver, provavelmente porque não sei quase nada de java, só programo em C, mas enfim, gostaria de saber se podes me ajudar.
O pic envia vários dados ao celular, sendo separados por letras para que eu saiba o que é cada coisa. Um exemplo de como ele envia: “E0012I4T003F”
E é para energia, I para índice, T para tempo e o F para sinalizar que terminou.
eu preciso separar isso no aplicativo, mas não tenho ideia como. em C fiz com alguns If’s, sem problemas. Mas quando tento comparar a variável data no main (não modifiquei os nomes do teu código) utilizando “if(data==’E’)” recebo uma mensagem de erro dizendo que não posso utilizar == com variáveis do tipo byte. Não sei o que fazer. Obrigado desde ja!
Eduardo,
Quando você usa if(data==’E’), está comparando um array de bytes com um char. Essa comparação pode até fazer sentido se você usar um índice i, por exemplo, if(data[i]==’E’).
Mas enfim, minha sugestão, usando Java no Android, é utilizar a variável do código que nomeei como dataString para extrair os valores que você quer. Você poderia definir um separador padrão, por exemplo um espaço, para separar os valores relevantes. Em vez de enviar para o app “E0012I4T003F”, você enviaria “0012 4 003″, em que o primeiro elemento é energia, depois do espaço é índice e depois no outro espaço é tempo. Então você usaria o método split() para separar a String usando ” ” como delimitador e depois assinalaria um valor inteiro a cada variável baseado na posição, por exemplo:
String[] valores = dataString.split(” “);
int energia = Integer.parseInt(valores[0]);
int indice = Integer.parseInt(valores[1]);
int tempo = Integer.parseInt(valores[2]);
Espero que ajude :)
Ola
estou com um problema com o método write que não consigo resolver, quando eu acesso o método usando como onclick
public void restartCounter(View view) {
bt_connect.write(“restart\n”.getBytes());
}
funciona 100%, mas quando tento usar-lo dentro de outro método
public void MsgToArduino(String text) {
if(text.equalsIgnoreCase(“ligar luz”)){
bt_connect.write(“aa\n”.getBytes());
}
}
Ele me retorna NullPointer .
NullPointer Exception siginifca que o objeto bt_connect não está sendo inicializado no código. A variável está definida, mas não está apontando para um objeto. Você precisa, antes de usar o método write, inicializar bt_connect. Veja as linhas 39-68 da Activity principal do código do Android.
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter == null) {
statusMessage.setText("Que pena! Hardware Bluetooth não está funcionando :(");
} else {
statusMessage.setText("Ótimo! Hardware Bluetooth está funcionando :)");
}
/* A chamada do seguinte método liga o Bluetooth no dispositivo Android
sem pedido de autorização do usuário. É altamente não recomendado no
Android Developers, mas, para simplificar este app, que é um demo,
faremos isso. Na prática, em um app que vai ser usado por outras
pessoas, não faça isso.
*/
btAdapter.enable();
/* Definição da thread de conexão como cliente.
Aqui, você deve incluir o endereço MAC do seu módulo Bluetooth.
O app iniciará e vai automaticamente buscar por esse endereço.
Caso não encontre, dirá que houve um erro de conexão.
*/
connect = new ConnectionThread("00:14:03:18:43:45");
connect.start();
/* Um descanso rápido, para evitar bugs esquisitos.
*/
try {
Thread.sleep(1000);
} catch (Exception E) {
E.printStackTrace();
}
Olá, estou utilizando seu código como modelo para a comunicação entre um sensor ultrassônico e o android, porém, tive alguns problemas. O textview utilizado para exibir o valor vindo do arduino só exibe a casa da dezena e da centena do número. Além disso o textview fica piscando devido a velocidade de atualização mas minha intenção é que o valor enviado fique lá por pelo menos 1 segundo. Tentei colocar um delay no código do arduino e um Thread.sleep no android depois que ele atribui o valor ao textview mas nada tem dado certo. Você tem alguma ideia de como resolver esses problemas?
Olá, Vinícius,
O TextView não tem limitação de caracteres. Então imagino que o valor de distância esteja sendo truncado em alguma parte do código. Observe de novo atentamente o código e veja todo o fluxo do valor, imprimindo, se necessário, o valor da distância naquela etapa.
Sobre os delays, tanto no Arduino, quanto no Android, essa é uma maneira correta de tratar o problema da velocidade de atualização. Não entendo porque não funcionou. Outra alternativa, por exemplo, é ter um contador de quantas transmissões chegaram no Android. Então você só atualiza o TextView quando um determinado número de transmissões forem atingidas. Isso considerando que o Arduino fica o tempo inteiro transmitindo. Ainda outra opção é salvar o tempo em segundos da última modificação do TextView e somente fazer nova alteração quando a diferença entre o tempo atual e o tempo da última modificação chegar a um determinado valor, no seu caso, um segundo.
Como eu faço para chamar mais de um dado do arduino? (Preciso chamar 15 diferentes)
Olá, Raul,
Nesse código você pode enviar strings de dados. Caso queira enviar 15 diferentes valores, você pode colocá-los todos em uma string, em uma sequência definida, e depois decodificá-los. Exemplo, imagine que você precisa enviar 15 dados numéricos. Crie uma string com todos esses valores separados por espaço e envie. Depois, no Android, você pode separar a string usando split() e transformar os valores individuais em int ou float.
Caso essa não seja a solução para o problema e o projeto tenha mais restricões, pode chamar por aqui e nos dar mais detalhes :D
Muito bem explicado… mas estou com um probleminha
to usando HC-06,… já fiz o pareamento antes de abrir o app… peguei o MAC do dispositivo…
Por favor, me ajude
Desde já agradeço
Weldis Alves
Perdão, Weldis.
Já que você obteve o endereço MAC, acredito que basta substituir a string da linha 59 da Activity Principal pelo endereço MAC do seu dispositivo. Não deu certo? Alguma mensagem de erro?
Oi! Excelente tutorial.
Estou tentando fazer algo parecido, mas estou com um problema já no início.
Ligo o HC-06, ele fica com a luz piscando, esperando uma conexão, porém o dispositivo não aparece na lista para parear. Precisa fazer alguma pré configuração no HC-06?
Obrigado.
Oi, Vinicius.
Quando fiz o post, não precisei usar nenhuma configuração extra. Será que o seu módulo não está entrando no modo descoberta ou pareamento? Nunca tentei, mas você pode tentar configurá-lo usando comandos AT. Seguem alguns links que talvez sejam úteis. Espero que dê certo.
https://www.instructables.com/id/AT-command-mode-of-HC-05-Bluetooth-module/
http://www.galaxysofts.com/new/bluetooth-hc-05-hc-06-command-master-mode-commands/
Olá, estou com um probleminha com meu projeto…
eu estou tentando controlar dois motores por meio de um aplicativo de celular, usando o módulo HC-06. Mas quando eu aperto um botão do software, existe um delay de aproximadamente 6 segundos para a função do botão ser executada. O que você acha ? Seria algum problema com o meu módulo ? Obrigado.
Oi, Gianluca. Difícil dizer com certeza. Mas qual é a distância entre os dispositivos? Dependendo da distância, pode haver falhas de comunicação que atrasam o recebimento de dados com Bluetooth, pois os dados precisam ser reenviados em caso de falha. Isso ocorre mesmo com os dispositivos bem próximos? Mesmo assim, 6 segundos seria muito tempo. Talvez esteja acontecendo interferência com outros dispositivos, a faixa de frequência que o Bluetooth usa para comunicação pode estar lotada. Pode ser também algum problema no próprio módulo, como você citou. Ou um pequeno bug no código. Realmente difícil dizer, pois muitas possibilidades.
Amigo como posso fazer o seguinte, ao receber uma ligacao, pegar o numero que esta ligando, verificar se ele est’a nos meus contatos e enviar para o bluetooth do arduino. Caso ninguem me ligar eu envio para o arduino as horas do relogio.
Eu to perdido com o android
Oi, Pedro. Você vai precisar preparar seu app para interceptar as chamadas. Um exemplo que encontrei está aqui: https://stackoverflow.com/questions/15563921/how-to-detect-incoming-calls-in-an-android-device.
Depois disso, terá que integrar o código desse post no seu app e fazer com que a mensagem Bluetooth seja enviada ao Arduino dentro do método onIncomingCallReceived() (que está no link que enviei).
Acredito que esse é o caminho.
Ola, boa tarde estou com uma duvida no meu caso são varios HC-06 então preciso que o Android procure o seus MAC para parear eu consigo fazer com outros celulares mas no HC-06 ele n da nada .
linha a baixo
private void list() {
pd = BA.getBondedDevices();
ArrayList list = new ArrayList();
for (BluetoothDevice bt : pd) {
list.add(bt.getName());
}
Toast.makeText(this, “disposito on”, Toast.LENGTH_SHORT).show();
ArrayAdapter ad = new ArrayAdapter(this, android.R.layout.simple_list_item_1, list);
lis.setAdapter(ad);
}
Oi, Luiz. Estranho que só no HC-06 o código não funcione. Mas infelizmente não tenho mais o HC-06 para testar o seu código aqui :(