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
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:
Postar um comentário