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