sexta-feira, maio 02, 2014

Comunicação Bluetooth entre Arduino e Android - Software do Android, parte 3

Nesta parte final da minha apresentação rápida do software no lado do Android vamos, finalmente, ver como enviar e receber dados.

Em princípio o que é preciso fazer é simples:
  • Obter um BluetoothSocket
  • Conectar o BluetoothSocket
  • Obter os streams de entrada e saída associados ao socket
  • Usar as funções normais de InputStream e OutputStream para receber e enviar dados
A comunicação não deve ser feita quando existe uma procura de dispositivos em andamento, portanto aguarde ela terminar ou cancele com cancelDiscovery().

Vejamos os detalhes sórdidos.

Obtendo um BluetoothSocket

O socket é obtido através do método createRfcommSocketToServiceRecord do objeto BluetoothDevice que obtivemos da procura de dispositivos (ou construímos a partir de um endereço MAC). O parâmetro para este método é a identificação (UUID) do canal que será utilizado (podemos grosseiramente comparar isto com o nome do serviço do TCP/IP). O módulo que estamos usando com o Arduino usa a identificação padrão para SPP (Serial Port Profile):

UUID myUUID = UUID.fromString ("00001101-0000-1000-8000-00805f9b34fb");

Atenção que createRfcommSocketToServiceRecord pode gerar uma IOException.

Conectando o BluetoothSocket

O método conect do BluetoothSocket realiza a conexão. Isto pode demorar algum tempo, portanto deve ser feito em uma thread separada da UI. Caso algo dê errado será gerada uma IOException.

A primeira vez que você conecta é feito um procedimento de pareamento, no qual o Android solicita a digitação da senha. O Android armazena os pareamentos, não sendo necessario fornecer a senha nas conexões futuras (a não ser que você apaga o pareamento).

Obtendo os streams de entrada e saída

Esta parte é simples, basta usar os métodos getInputStream e getOutputStream do BluetoothSocket. Mais uma vez, IOException indica que algo deu errado.

Recebendo e enviando dados

A principal função de leitura de um InputStream é read(byte[] buffer, int byteOffset, int byteCount). Para usá-la você precisa criar antes um buffer (vetor de bytes). byteOffset define a posição inicial onde os bytes recebidos serão colocados e byteCount o númeto máximo de bytes a receber; para o caso mais comum (iniciar na posição 0 e receber no máximo buffer.length) você pode usar a versão simplificada read(byte[] buffer). O método retorna o número de bytes efetivamente recebido, ou -1 se chegou ao fim do stream. Este método bloqueia o processamento até que pelo menso um byte seja recebido, portanto não deve ser chamado na thread de UI.

Analogamente, a principal função de escrita em um OutputStream é write(byte[] buffer, int byteOffset, int byteCount), que pode ser simplificado para write(byte[] buffer). O processamento pode ficar preso por algum tempo nesta chamada.

O que enviar, inclusive formatos, respostas e dinâmica da conversação, fica por sua conta. Pessoalmente gosto de enviar os dados em pacotes com um formato rígido, com um tipo de pacote e tamanho no início. No lado da dinâmica, você pode pensar em um esquema em que toda comunicação é iniciada pelo Android, com o Arduino enviando somente em resposta a uma solicitação do Android, ou em um esquema totalmente assíncrono em que qualquer lado pode fazer uma transmissão a qualquer momento. A segunda opção é mais complicada de implementar.

Tratamento de erros

Uma questão importante é o que acontece quando a conexão é interrompida (por exemplo, você desligou o módulo ou ele ficou fora de alcance). Infelizmente, nenhum aviso é dado para a sua aplicação; uma recepção em curso permanece indefinidamente pendente. Você pode evitar isto testando a presença de dados antes de ler e implementando manualmente um timeout (exemplo). Se você tentar fazer um envio e a conexão estiver interrompida, uma exceção é gerada.

De um modo geral é bastante complexo fazer um programa que trate de forma "graciosa" todos os erros possíveis.

Um outro "detalhe" é que o stack Bluetooth do Android possui algumas instabilidades. Ele foi refeito na versão 4.2 e acredita-se estar bem mais confiável e resistente. Nas minhas experiências, ocasionalmente foi necessário desligar e ligar o Bluetooth para conseguir reconectar após a interrupção anormal de conexões.


Fechando a série, veremos no próximo post um exemplo completo de comunicação entre o Android e o Arduino.

Nenhum comentário: