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

2º Bate Papo do SP-GTUG - Divulgação

Pessoal, Para aqueles que já estiveram presentes nas atividades do SP-GTUG (encontros ou bate papos na Globalcode), e para os que ainda não puderam estar presentes, teremos um novo bate papo acerca de mais duas tecnologias do Google: AdSense e Python no GAE. Este será o nosso segundo bate papo do SP-GTUG, nos mesmos moldes dos minicursos da Globalcode . Local e data O bate papo acontecerá no dia 27/03/10, das 10:00 às 13:00 na Globalcode. Para fazer sua inscrição e participar acesse a home da Globalcode e vá até a parte de "Minicursos Gratuitos da Semana ". Conteúdo das apresentações Abaixo seguem as descrições passadas pelos palestrantes: Título: "Algoritmos em Python" Resumo: Python é uma linguagem considerada frugal, simples, sem ser simplória! Entenda como construir algoritmos ninjas com essa linguagem, adotada pela Google e pelo MIT. Veremos muito, muito código, desde algoritmos clássicos até tutoriais do

10 reasons why we love JSF

1. One-slide technology: it's so simple that I can explain basic JSF with one slide. 2. Easy to extend: components, listeners, render kit, Events, Controller, etc. 3. Real-world adoption: JBoss, Exadel, Oracle, IBM, ... 4. Architecture model: you can choose between more than 100 different architecture. 5. Open-mind community: using JSF you are going to meet very interesting people. 6. We are using JSF the last 5 years and we found very good market for JSF in Brazil 7. Progress: look to JSf 1.1 to JSF 1.2, JSF 1.2 to JSF 2.0. People are working really hard! 8. Many professionals now available 9. It's a standard. It's JCP. Before complain, report and help! 10. Ed Burns, spec leader, is an old Globalcode community friend! EXTRA: My wife is specialist in JSF. She's my F1 for JSF :) Nice job JSF community! -Vinicius Senger

JSF 2 - Composite Components, você não precisa mais ser um ninja

Estamos em uma nova era da computação, os dados não estão mais localizados em um banco dentro de sua empresa, vivemos a explosão de redes sociais, informações são geradas a todo instante, e se torna essencial que sua aplicação conheça os serviços disponíveis na web e consumam suas APIs geralmente disponíveis por serviços REST. Legal, mas como ficam meus aplicativos Java EE neste novo cenário? Para quem vem acompanhando a evolução da plataforma, é notório que todo esforço vem sendo utilizado para aumentar a produtividade e a integração com novos serviços. Basicamente duas especificações surgem com muita força para atender este cenário, a JSR - 314 (JSF-2) e JSR - 311 (JAX-RS), neste post exploraremos a JSR-314 (JSF2) e sua nova forma de criar Composite Components. Uma das grandes queixas dos desenvolvedores JSF era a complexidade em criar composite components, era necessário um vasto conhecimento sobre o ciclo de vida de uma aplicação JSF. Agora, você não precisa ser mais um “ninja” em

Gosta de informática e sonha ser programador Java um dia ?

A Globalcode está procurando jovens que queiram ser programadores no futuro, para fazer parte de sua equipe de vendas e operacional na unidade Paraíso em São Paulo somente. O grande diferencial é o planejamento para carreira de programador. O planejamento de cursos do portifólio da Globalcode: Academia do Programador, Academia Java, Academia Web, Robótica, Eletrônica, etc, será realizado periodicamente de acordo com o desempenho e interesse individual. Atenção : Este não é um estágio para trabalhar com Java, são duas vagas CLT administrativas para pessoas que queiram ser programadores no futuro. Perfil : Conhecimentos de informática básica, saber lidar com pessoas e trabalhar em equipe. Deve ter concluído o ensino médio, ter boa escrita, ser organizado, responsável, comprometido e dinâmico. Interesse por computação, programação, internet, robótica, eletrônica,... Temos duas vagas para jovem-aprendiz abertas: 1) Setor operacional No dia a dia Serviços externos Controle de estoqu

JavaMail: Enviando mensagem HTML com anexos

Introdução Depois do post "JavaMail: Enviando e-mail com Java" , que apresentava como enviar um e-mail com Java, resolvi complementar a assunto apresentando como enviar uma mensagem formatada, em HTML , e também como realizar o envio de anexos. Bibliotecas Além da biblioteca JavaMail, veja mais no post anterior , é necessário incluir o JavaBeans Activation Framework (JAF), apenas se a versão utilizada for anterior ao JSE 6.0 , que já tem o JAF incluso. O JAF está disponível em http://www.oracle.com/technetwork/java/javase/downloads/index-135046.html , e neste download encontramos, alguns exemplos na pasta demo , documentação, incluindo javadocs, na pasta docs e a biblioteca activation.jar , que deve ser acrescentada no classpath da aplicação para versões anteriores ao JSE 6.0. Exemplo Primeiramente devemos realizar a configuração da javax.mail.Session e da javax.mail.internet.MimeMessage , estes passos podem ser vistos no post anterior . Agora vamos montar um

TDC INNOVATION lança University Pass

Modalidade de ingresso tem como objetivo ajudar na capacitação dos universitários Uma pesquisa realizada em 2020 pela Associação Brasileira das Empresas de Tecnologia da Informação e Comunicação (Brasscom) diz que até o ano de 2024 o Brasil precisará de cerca de 420 mil profissionais na área de Tecnologia da Informação. Porém, por ano, a mesma pesquisa diz que o país forma apenas 46 mil profissionais capacitados no nicho. Pensando nisso, para ajudar na formação e capacitação desses jovens profissionais, o TDC INNOVATION, segunda edição do ano do The Developer's Conference, lança o University Pass, modalidade de ingresso que possibilita aceso digital gratuito a todas as palestras do evento, ou com 50% de desconto para quem preferir ir pessoalmente. Com o tema central “Desafios para a criação do futuro Digital”, o TDC INNOVATION ocorrerá entre 1 e 3 de junho, de forma híbrida: presencialmente no Centro de Convenções CentroSul, em Florianópolis, e com transmissão simultaneamente pela