Comunicação Socket entre um App Android e uma NodeMCU

O Android Things é uma ótima opção para quem desenvolve com o Android Studio e deseja entrar no mundo da Internet das Coisas (IoT) e construir protótipos de dispositivos embarcados.

Porém, muitos desenvolvedores (meu caso por exemplo) entram neste mundo utilizando outras tecnologias, como o Arduíno e eventualmente placas da família ESP8266 como a NodeMCU. Pois elas fornecem conexão WI-FI nativamente e eu considero um meio-termo em termos de abstração de hardware.

Este artigo procura mostrar os desafios e soluções que encontrei ao realizar uma conexão Socket entre um aplicativo Android e uma placa NodeMCU, durante o desenvolvimento de um projeto pessoal.

NodeMCU — Robusta, simples de usar e de baixo custo — Fonte: Github

Caso você tenha vontade utilizar uma NodeMCU, a página oficial possui diversos tutoriais e neste artigo eu explico um pouco da preparação de ambiente para fazê-la funcionar. E caso queira reproduzir o comportamento que irei mostrar neste artigo, o código em LUA para transformar a MCU num servidor TCP é o seguinte:

Contexto

Em meu projeto, eu gostaria de capturar bytes de um arquivo de áudio do meu Smartphone e transmiti-los para uma NodeMCU que estará conectada num auto-falante.

Uma vez que parte deste projeto será Headless (sem interface gráfica), sem requisitos de segurança e com atualização em tempo real, optei pelo protocolo Socket para comunicação.

Abordagens para se lidar com sockets

Durante minhas pesquisas, encontrei 3 abordagens para lidar com sockets. E vou relatar o porque de cada uma ter sido aproveitada ou não em meu projeto.

Abordagem 1 — Criar um socket a cada requisição do Android

Em todos os exemplos que encontrei de implementações Cliente/Servidor em Java, há três características que se repetem em todos eles:

  1. Utilizam loops infinitos do estilo while(true) no servidor. Algo que particularmente me causa um pequeno incômodo numa aplicação server Side, mas acredito que seja inevitável. Uma vez que está presente até no site Oficial da Oracle. Como o meu servidor irá ficar na MCU, não irei passar por este incômodo.
  2. Desconectam o Socket a cada mensagem enviada. No loop infinito a cada Thread criada, um socket é criado e desconectado. No exemplo KKMultiServerThread disponível no Site da Oracle, é possível percebemos isso. Situação que também vi se repetir em outras referências como esta.
  3. Utilizam a função ReadLine(). Ao realizar o Debug, percebi que nesta instrução acontece um problema. Talvez esta função funcione normalmente em implementações Java não-Android. Mas como é possível ver no vídeo abaixo, no Android o Debug simplesmente pára de funcionar.

Caso queira reproduzir este comportamento é só clonar este repositório do meu Github e retirar os comentários do método sendSocket() da classe MainActivity. A classe que contém esta primeira implementação é a WithoutBridgeConnectType.

Abordagem 2 — Utilizar uma implementação de Sockets Assíncronos feita sob medida para o Android.

Pesquisando no GitHub encontrei o AndroidAsync, que é uma Library Android para manipular Sockets e diversos outros protocolos como WebSocket, HTTP Client e Socket.IO.

Uma diferença desta library é a presença de Sockets Assíncronos que segundo a descrição do repositório, são orientados por callbacks, com apenas uma Thread. Comportamento que ganhou bastante destaque na tecnologia Node.js.

Por isso, me empolguei bastante em integrá-la ao meu projeto, uma vez que tive muitos bons resultados com Node.js trabalhando com callbacks de forma assíncrona. A integração da library com o projeto é bem simples, basta você adicionar a referência no Gradle:

E basicamente, o método abaixo irá enviar e receber os dados de forma assíncrona.

Assim como a primeira abordagem, acredito que esta implementação funcione bem em outros cenários. Porém, como pode se perceber no vídeo abaixo, após alguns envios a conexão com a NodeMCU é perdida.

O vídeo ficou virado, mas observe que na quarta vez que eu envio o comando o led não acende mais.

Abordagem 3— Realizar uma conexão permanente do Android e só desconectar quando o aplicativo for fechado

Finalmente, alterei de abordagem e consegui um resultado melhor. Apesar da mudança ser sutil, foi de muita importância. Basicamente as mudanças de minha implementação foram:

Mudança 1 — Substituir a função ReadLine() por Read(). Desta forma, ao invés de ler uma string eu leio um Array de bytes e transformo numa String.

Mudança 2 — Não criar um Socket a cada requisição, mantendo a conexão permanente. Como é possível ver na classe BridgeConnectType, eu não desconecto o Socket em momento algum.

Em meu código-fonte eu modelei esta implementação como uma “ponte”. Pois é como se existisse uma ponte lógica entre a MCU e o aplicativo Android. Como é possível ver no vídeo abaixo, a alteração gerou bons resultados.

Ignore os trincados na tela do celular.

O tempo de resposta é muito bom e para o tipo de objetivo que eu estou buscando (transmissão de áudio) acredito que irá ser o ideal.

Conclusão

Nem sempre é necessário se comunicar com a MCU com sockets. Porém a grande vantagem do uso desta tecnologia é a comunicação bidirecional, conforme explicado aqui por Douglas C Schmidt. Principalmente em casos onde existe a necessidade de envio de dados e atualização dos resultados em tempo real.

Se o objetivo é iniciar na área de embarcados e posteriormente se aprofundar na Internet das Coisas, eu acredito que a NodeMCU uma opção bastante interessante. A curva de aprendizado, em comparação ao desenvolvimento com C++ e Ladder pode ser considerada média, exigindo apenas um pouco de paciência para pesquisa.

Outro ponto positivo para esta placa é que eu considero que os modelos atualmente suportados pelo Android Things ainda tem um preço um pouco salgado se comprados no Brasil. O Intel Edison por exemplo, oscila entre R$ 450,00 e o Raspberry PI é encontrado por cerca de R$ 250,00.

Já uma NodeMCU pode ser encontrada até por R$ 25,00 e mesmo não tendo tanto poder de processamento quanto os citados e não possuir funções como HDMI, Bluetooth e áudio nativas, pode proporcionar de forma fácil, conectividade aos seus primeiros protótipos.