Pular para o conteúdo principal

Criando aplicação de Código Morse com Arduino + ADK + Android

Bom dia.

Há alguns dias, publiquei o post "Como conectar uma placa ADK e um aplicativo Android de forma indolor", onde falei sobre os passos básicos para fazer a conexão entre smartphone e arduino mega com suporte a ADK. Fiquei de escrever novos conteúdos. Sendo assim, como prometido, desta vez vou falar sobre a troca de dados entre os dois elementos.

* OBS: Caso não saiba o que é Arduino, ADK ou Android, leia o post que mencionei anteriormente.

Outro ponto importante. Não vou colocar todo o código fonte produzido aqui, porque prejudicaria a leitura e ficaria um post muito grande. Mas, não se preocupem, joguei tudo no GitHub. O link para download de todo fonte está aqui.

Vamos começar pela parte Arduino. Perceba que na descrição do AndroidAccessory colocamos dados mais reais desta vez. Veja como ficou:

AndroidAccessory acc("RicardoOgliari", "MorseTHT", "Código Morse como você nunca viu", "1.0.0", "URI", "Serial");

A seguir podem ser vistas três funções: ponto, traco e espera. Todas mudam a cor do LED RGB e, sem seguida, chamam a função delay passando um parâmetro em milissegundos. Desta forma, a troca de cores e a mensagem Morse será perceptível a olho nú. Caso contrário, o processo seria tão rápido que daria a impressão de apenas uma cor existente no LED.

Dentro do loop, setamos a cor para preto inicialmente. Lembrando que o segundo, terceiro e quatro parâmetros da função setColorRGB são RED, GREEN e BLUE, respectivamente. O isConnected já foi falado no outro artigo. Ele retorna true se já existe um equipamento conversando com a placa ADK. Sendo assim, se entrar neste if a brincadeira começa.

O segredo começa a ser desvendado na criação da variável len, que recebe o retorno da função read de acc, objeto de AndroidAccessory. Esta função recebe por parâmetro um vetor de bytes, seu tamanho e por fim, um valor inicial caso a transmissão não seja reconhecida. E logo abaixo, temos mais um teste lógico. Se a variável len for maior que zero, então isso nos diz que teve a recepção de dados no ADK.


void loop() {
  leds.setColorRGB(0, 0, 0, 0); 
  if (acc.isConnected()) {
      int len = acc.read(rcvmsg, sizeof(rcvmsg), 1);
      if (len > 0) {
        if (rcvmsg[0] == COMANDO_MORSE) {
          byte qtdLetras = rcvmsg[1];
          ... 


Se o último if retornou true, isso também significa que o vetor de bytes foi valorizado. Existem alguns padrões de uso deste vetor de bytes. No exemplo mostrado aqui, no primeiro byte enviado pelo aplicativo informamos qual a ação que queremos efetuar. Logicamente só temos uma ação e isso é supérfluo. Mas, decidi deixar assim para o leitor ver como pode tratar diferentes tarefas oriundas do smartphone. Sendo assim, caso o segundo if for verdadeiro, o ADK não só recebeu dados, como o primeiro byte recebido indica que a ação desejada é um comando morse.

Na sequência recuperamos o segundo byte recebido, armazenado o mesmo na variável qtdLetras. Isso serve para sabermos quantos caracteres que foram passados na caixa de texto da aplicação Android. A variável indiceCor, presente na sequência, trata a mudança de cor utiliza no led de uma letra para outra. Assim fica mais fácil identificar o código morse.


OBS: Para saber tudo sobre o Código Morse, visite a Wikipedia.


Depois encontramos um laço for que percorrerá todas as letras recebidas, começando pelo índdice dois do vetor de bytes. Isso porque já lemos e utilizamos os índices 0 e 1. Dentro do for é feito um switch-case para saber qual o valor do byte recebido. Sabendo o código ASCII e, que o mesmo representa os números decimais de 97 até 122 para a sequência de letras de 'a' até 'z', utilizamo-nos nos respectivos cases. Veja:


          for (int i = 2; i < qtdLetras+2; i++){
             switch (rcvmsg[i]){
               case 97:
                 ponto();
                 espera();
                 traco();
                 espera();
                 break;
               case 98:
                 traco();
                 espera();
                 ponto();
                 espera();
                 ponto();
                 espera();
                 ponto();
                 espera();
                 break;

Outro ponto importante na comunicação Android - ADK e, essencial neste projeto, é avisar o aplicativo de que o código morse já foi apresentado e o arduino está pronto para receber um novo texto e refazer a tradução. Para isso vamos usar a função write de AndroidAccessory. Os parâmetros enviados são um vetor de bytes e seu tamanho. Veja:

byte sntmsg[1] = {0xe}; //está no início do código, na criação das variáveis
acc.write(sntmsg, 1);//na parte final do código

E no Android?
Bem, no lado Android o processo não é tão simples assim.

OBS: Antes de começarmos a ver os códigos, indico que pegue os mesmos no GitHub, ficará mais fácil de acompanhar.

No método onCreate da única Activity presente no projeto é criado um BroadcastReceiver para interceptar o momento em que a união entre smartphone e ADK for feita.

mUsbManager = UsbManager.getInstance(this);
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(mUsbReceiver, filter);


Perceba que ao registrar o receiver o primeiro parâmetro é um mUsbReceiver. Este é um objeto criado na própria classe. Perceba que ao sobrescrever o onReceive é tratado os dois possíveis eventos: o acessório foi plugado ou desplugado. E, também, preste atenção e veja que temos dois métodos auxiliares: openAccessory e closeAccessory.


private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
   @Override
   public void onReceive(Context context, Intent intent) {
     String action = intent.getAction();
     if (ACTION_USB_PERMISSION.equals(action)) {
       synchronized (this) {
         UsbAccessory accessory = UsbManager.getAccessory(intent);
         if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
             openAccessory(accessory);
         } else {}
         mPermissionRequestPending = false;
       }
     } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
       UsbAccessory accessory = UsbManager.getAccessory(intent);
       if (accessory != null && accessory.equals(mAccessory)) {
         closeAccessory();
       }
     }
   }
};


Vamos estudar primeiro o openAccessory. Neste método a linha mais importante é justamente a primeira. Onde o método openAccessory de UsbManager é chamado. O retorno é uma instância da classe FileDescriptor que, por sua vez, serve de parâmetro para criarmos instância de FileInputStream e FileOutputStream. Estas classes stream serão o caminho de ida e volta dos dados. A estrada da informação entre arduino e smartphone.

private void openAccessory(UsbAccessory accessory) {
        mFileDescriptor = mUsbManager.openAccessory(accessory);
        if (mFileDescriptor != null) {
            mAccessory = accessory;
            FileDescriptor fd = mFileDescriptor.getFileDescriptor();
            mInputStream = new FileInputStream(fd);
            mOutputStream = new FileOutputStream(fd);
            Thread thread = new Thread(null, commRunnable, TAG);
            thread.start();
        } else {}
    }


No método descrito anteriormente é possível perceber a criação de uma Thread, sendo que um dos parâmetros passados é uma instância de Runnable criada na própria classe, chamada de commRunnable. Esta classe tem um papel importante. Ela fica chegando se tem algo novo oriundo do input stream. Caso afirmativo, significa que o ADK mandou dados para a aplicação. Isso só acontece quando a placa já mostrou o código morse e está aguardando um novo texto. Sendo assim, o campo de texto e o botão voltam a ter seu estado enabled como true.

Runnable commRunnable = new Runnable() {
        @Override
        public void run() {
            int ret = 0;
            byte[] buffer = new byte[255];
            while (ret >= 0) {
                try {
                    ret = mInputStream.read(buffer);
                } catch (IOException e) {
                    break;
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvEdt.setEnabled(true);
                        btn.setEnabled(true);
                    }
                });
            }
        }
    }; 

No método closeAccessory só acontece a limpeza das variáveis, para não ocuparmos recursos da memória desnecessariamente.

E o envio dos dados? Isso acontece no método showMorse. Ele foi passado para a propriedade onClick do Button. Neste trecho de código é construído o vetor de bytes, com a tarefa desejada no primeiro índice, que, deve ser o mesmo byte configurado no teste if no código presente na placa, feito em C++. O vetor de bytes é passado como parâmetro no método write de output stream. Desta forma os dados chegaram no ADK. E, tanto o campo de texto como o botão tem seu estado enabled configurado para false.


public void showMorse(View v) {
        String texto = tvEdt.getText().toString();
        Toast.makeText(this, texto, 8000).show();
        byte[] buffer = new byte[2 + texto.length()];
        buffer[0] = 0xf;
        buffer[1] = (byte)texto.length();
        for (int pos = 2, i = 0; i < texto.length(); i++, pos++){
            buffer[pos] = (byte)texto.charAt(i);
        
        }
        try {
            mOutputStream.write(buffer);
            tvEdt.setEnabled(false);
            btn.setEnabled(false);
        } catch (IOException e) {}
    }


O código do aplicativo também a parte de controle de pausa da aplicação e de sua retomada. No onResume é chamado o método getAccessoryList da classe UsbManager. Caso exista um acessório plugado, o método openAccessory é chamado novamente.

Pronto, isso é tudo.

Veja a solução em funcionamento:

Conclusão

O segredo é o input stream e output stream. Ambos são usados extensamente em vários tipos de conexão, como HTTP por exemplo. O vetor de bytes e sua correta separação é a chave para uma boa conversa entre as duas estrelas deste artigo, o aplicativo Android e o ADK.

Agora é colocar a cabeça para funcionar e criar nossos próprios projetos. Afinal, a onda agora é MAKE YOURSELF.

Comentários

Postagens mais visitadas deste blog

PrimeFaces com JSF2 no ScrumToys

O JSF traz um grupo de componentes visuais prontos, a partir da versão 2.0 com suporte a Ajax nativo! Melhor que isso é a possibilidade de customizar ou utilizar uma suíte de componentes prontos para "turbinar" o projeto com o minimo de esforço. PrimeFaces é uma suite de componentes JSF customizados, a maioria com suporte a ajax, conta com um Kit para desenvolvimento mobile, é open source, a documentação é razoável, facíl de instalar e usar. Nesse exemplo utilizo o PrimeFaces 2.0.0, versão com suporte a JSF 2, no ScrumToys. Conheçe o ScrumToys ? ScrumToys é um Projeto Exemplo para JavaServer Faces 2.0 dentro do NetBeans 6.8, criado e mantido pelo time da Globalcode. É interessante que você tenha o NetBeans 6.8 para fazer esse exemplo, senão tiver baixe aqui . Baixe também, aqui , o PrimeFaces e o configure no NetBeans como biblioteca para o ScrumToys. Inicialmente o PrimeFaces requer a runtime JSF, mas isso pode variar dependendo de quais componentes você utilize, mai

Seja um Palestrante do TDC Connections 2022

A Equipe TDC está muito feliz e orgulhosa em começar mais uma jornada de eventos em 2022 e criar muitas oportunidades! Agora, chegou o momento para você que sempre quis ser palestrante mas não teve oportunidade, e também de quem já é palestrante experiente, e está ansioso para compartilhar aquela nova prática ou caso de sucesso fresquinho, submeter sua palestra para o TDC Connections e co-criar conosco o maior evento de desenvolvedores do Brasil.  Link de inscrição: https://cfp-conn.thedevconf.com.br/?_ga=2.103579840.1386204218.1643036190-1988609034.1631800014 Confira aqui o calendário do TDC 2022. Nesta edição teremos muitas novidades e você poderá palestrar de forma Online ou Presencial! Entenda como vai funcionar o TDC CONNECTIONS: Nesta edição, além de todo o conteúdo dividido em Trilhas transmitidas ao vivo (13 trilhas simultâneas) também teremos dois eventos híbridos em Recife e Belo Horizonte, que terão uma trilha por dia, ao vivo, para um público limitado, seguindo protocolo

Entendendo como funciona a programação de computadores: linguagens de programação, lógica, banco de dados

Nesse post, diferente dos últimos que foram mais enfáticos nas experiências com tecnologias, vou focar um pouco mais nos profissionais que estão começando, ou pretendem ingressar na área de desenvolvimento de software, falando sobre conceitos fundamentais relacionados a programação em geral . Mercado de trabalho para programação Conforme já sabemos, o mercado de desenvolvimento de software, especialmente no Brasil, continua em franca expansão, sendo que cada vez mais as empresas buscam desenvolver seus próprios sistemas usando as mais diferentes e novas tecnologias. Algumas matérias interessantes: As seis profissões mais valorizadas em 2010 no IDG Now! Muitas vagas e sensação de reaquecimento da economia Por isso, a área de desenvolvimento de software tem despertado interesse em muitos profissionais de outras áreas que desejam mudar de profissão, já que as oportunidades de trabalho tendem a ser maiores. Esse é um perfil presente em muitos dos clientes da Globalcode que acabou m

JSON fácil em Java com GSon !

Ola pessoal ! O formato JSON ( J ava S cript O bject N otation) vem se consagrando cada vez mais na comunicação de dados, principalmente nos dispositivos móveis devido a esse formato ser mais leve que o XML e também mais legível. Uma prova disso são as inúmeras bibliotecas que existem para manipular esse formato, e no caso do Android, o suporte ao JSON é nativo. Mas apesar de ter esse suporte nativo, algumas operações devem ser feitas manualmente e o código acaba ficando um pouco verboso e repetitivo, já que para cada objeto que se deseja transmitir é necessário fazer um método que lê as propriedades do JSON e faz as devidas atribuições no seu objeto Java. Vamos supor o seguinte objeto sendo transmitido em JSON: {   user: {     id: 123456,     name: "Neto Marin",     username: "netomarin",     email: "netomarin@globalcode.com.br"   } } Se você fosse tratar um Webservice que envia esse JSON para o seu aplicativo Android, além de criar a o

Top 4 desafios do gestor de TI e como superá-los

  O setor de TI se apresenta como uma área essencial ao desenvolvimento e expansão da empresa, em especial, aquelas que têm a criação de soluções tecnológicas como core business. O mercado dinâmico da atualidade, com mudanças constantes e concorrência elevada, exige aperfeiçoamento do gestor de TI para se destacar em suas atividades. Pensando em te ajudar nesse processo, separamos 4 desafios do gestor de TI e como superá-los. Acompanhe! Transformação digital na atualidade A transformação digital é constante e define os caminhos futuros da nossa sociedade. Por isso, o setor de TI vem se tornando mais estratégico em empresas de todos os segmentos. O home office é um bom exemplo de como a revolução digital está se processando. Vamos falar sobre ele e outros pontos nos tópicos a seguir. Confira! Desafios do gestor de TI Para se manter atualizado sobre o tema e permanecer competitivo no mercado, é  necessário acompanhar as tendências de TI . Confira, abaixo, alguns dos principais desafios d

Segurança da informação no alvo: o que esperar do futuro?

A segurança da informação não se trata apenas de proteger os dados contra acesso não autorizado. Na prática, refere-se aos recursos que impedem: uso; registro; inspeção; divulgação; interrupção; modificação; destruição de dados. O uso de dados pode abranger desde um perfil nas redes sociais a detalhes financeiros, biometrias ou novos projetos. Por isso, a preocupação com a proteção dos dados é crescente, tanto para empresas quanto clientes. Para muitas pessoas, a coleta de dados é considerada invasão de privacidade, criando desconfiança do titular dos dados, pois o uso dos dados pode ser facilmente corrompido, utilizado para fins não declarados. Apesar de o uso dos dados terem impulsionado os avanços tecnológicos na última década, as organizações lidam com o desafio de distinguir dados de informações pessoais de modo a proteger a privacidade e as preferências dos clientes. Neste artigo, abordaremos o impacto da segurança da informação e o que esperar do futuro. Confira casos conhecido