Arduino - Hardware
Usei um Arduino Nano para facilitar a montagem na protoboard. Além do módulo Bluetooth, coloquei dois LEDs, um sensor de temperatura LM35 e um servomotor. Os LEDs estão ligados a saídas digitais comuns, o servomotor a uma saída digital com PWM e o sensor de temperatura a uma entrada analógica.
O módulo Bluetooth está ligado à serial normal do Arduino. Isto permite testar o software retirando o módulo, ligando o Arduino ao PC via USB e enviando os comandos através de um programa de comunicação normal (como o monitor serial da IDE do Arduino). Por outro lado, é preciso desconectar o módulo para fazer a carga do software e desconectar do micro para a operação com o módulo.
Arduino - Software
Nada de muito especial por aqui. Os comandos consistem em um único dígito, de 0 a 6. A resposta é a leitura do sensor de temperatura, em décimos de grau, sempre com três dígitos (seguidos de CR e LF). A leitura do sensor está descrita no meu post sobre o LM35. Os LEDs são controlados por digitalWrite e o servo pela biblioteca que vem com a IDE do Arduino.
// Exemplo de comunicação por Bluetooth
#include <Servo.h>
// Alguns dispositivos
static const int pin_ledr = 2;
static const int pin_ledg = 3;
static const int pin_servo = 5;
static const int pin_lm35 = A0;
static Servo servo;
// Iniciação
void setup ()
{
// Módulo BT está ligado à serial
Serial.begin (9600);
// Portas digitais
pinMode (pin_ledr, OUTPUT);
pinMode (pin_ledg, OUTPUT);
servo.attach(pin_servo);
servo.write(0);
// Sensor de temperatura LM35
analogReference (INTERNAL);
}
// Loop Principal
void loop()
{
int cmd;
if (Serial.available())
{
// recebeu um comando
cmd = Serial.read();
if ((cmd < '0') | (cmd > '6'))
return; // ignora comando inválido
// Trata o comando
switch (cmd)
{
case '0': // nop
break;
case '1': // acende led vermelho
digitalWrite (pin_ledr, HIGH);
break;
case '2': // apaga led vermelho
digitalWrite (pin_ledr, LOW);
break;
case '3': // acende led verde
digitalWrite (pin_ledg, HIGH);
break;
case '4': // apaga led verde
digitalWrite (pin_ledg, LOW);
break;
case '5': // Coloca o servo na posição 0
servo.write(0);
break;
case '6': // Coloca o servo na posição 180
servo.write(180);
break;
}
// Le o sensor de temperatura
int vSensor = analogRead(pin_lm35);
long temp = (vSensor*1100L)/1024L;
// Monta e envia a resposta
char resp[6];
resp[0] = (char) (((temp / 100) % 10) + '0');
resp[1] = (char) (((temp / 10) % 10) + '0');
resp[2] = (char) ((temp % 10) + '0');
resp[3] = '\r';
resp[4] = '\n';
resp[5] = '\0';
Serial.print (resp);
}
}
Android - UI
Para simplificar, a interface se resume a uma única tela. Nada de muito elaborado (ou bonito), apenas o essencial para testarmos o funcionamento.
Android - Software
O ponto de partida foi o meu exemplo de procura de dispositivos. O principal acréscimo é a thread que cuida da conexão e comunicação. Para fazer a interface entre esta thread e os eventos da UI, usei uma BlockingQueue. O código possui uma detecção de erros bem básica.
private BlockingQueue<byte[]> filaCmd;
// Trata botão Acende Led Vermelho
private final OnClickListener acendeLedR = new OnClickListener() {
@Override
public void onClick(View v) {
try {
filaCmd.put (new byte[] { '1' });
} catch (InterruptedException e) {
}
}
};
// Trata a conexão e comunicação com o Arduino
private class ConexaoThread extends Thread {
private final BluetoothSocket mmSocket;
private InputStream mmInStream;
private OutputStream mmOutStream;
public ConexaoThread(BluetoothDevice device) {
BluetoothSocket tmp = null;
// Obtem o socket
try {
tmp = device.createRfcommSocketToServiceRecord(serialUUID);
} catch (IOException e) {
// tmp ficará com null
}
mmSocket = tmp;
}
public void run() {
// Confirma que obteve o socket
if (mmSocket == null) {
return;
}
// Garante que não tem um discovery sendo executado
mBtAdapter.cancelDiscovery();
// Tenta conectar
try {
mmSocket.connect();
} catch (IOException connectException) {
return; // não conseguiu
}
// Obtem os streams de entrada e saída
try {
mmInStream = mmSocket.getInputStream();
mmOutStream = mmSocket.getOutputStream();
} catch (IOException e) {
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
bBtConectado = true;
acertaTela();
// Trata a comunicação
while (true) {
byte[] cmd;
try {
cmd = filaCmd.take(); // Aguarda um comando
if ((cmd == null) || (cmd[0] == '*')) {
break;
}
// Envia o comando
mmOutStream.write(cmd);
// Le a resposta (timeout de 3 segundos)
int available = 0;
long limite = System.currentTimeMillis() + 3000;
while(((available = mmInStream.available()) < 5) &&
(System.currentTimeMillis() < limite)) {
Thread.sleep(250);
}
if (available >= 5) {
byte[] resp = new byte[available];
mmInStream.read(resp);
mostraTemp (resp);
}
} catch (InterruptedException e) {
break;
} catch (IOException e) {
break;
}
}
// Encerra a comunicação
try {
mmInStream.close();
mmOutStream.close();
mmSocket.close();
} catch (IOException closeException) { }
bBtConectado = false;
acertaTela();
}
}
Funcionamento
O vídeo abaixo mostra o sistema em funcionamento.
Fontes
Os fontes dos softwares estão nos Arquivos do Blog (veja o link lá no alto à direita), no arquivo DemoBT.zip.



5 comentários:
Amigo obrigado pelo esquema, parabéns.
Quero saber o porque dos resistores no rx e tx. É pra garantir nivel logico 0?
E no pwm tambem é necessario?
Leonardo, como descrito em um post anterior o módulo opera a 3.3V, daí o divisor resistivo no pino Rx. O resistor no pino Tx é um pull-up que garante o nível 1. O PWM está ligado direto a um servo e não precisa de nenhum resistor.
Muito legal, obrigado.
Foi voce quem fez o programa?
Eduardo: sim.
Postar um comentário