Framework Arduino Básico Aula 10: POO II

 Imagem meramente Ilustrativa

Recapitulando

    Na aula passada retomamos o assunto POO visto no Minicurso básico de C++ dando um exemplo de como criar uma classe Led para instanciar vários objetos leds em um projeto. Foi um exemplo simples com um construtor, um setter para ajustar o pino encapsulado e dois métodos para alterar o comportamento do led, isto é, acender e apagar.

    Essa classe pode te servir de exemplo para criar outras classes que trabalham com saídas digitais, mas e se fosse uma entrada digital? O que muda? Para tentar responder vamos dar um exemplo criando um classe para botões.

    Mas antes disso, vamos revisitar o uso de botões ainda na programação estruturada, dando um exemplo curioso de uso de Buzzer no Wokwi. É um projeto como o da imagem inicial da postagem, mas ao invés de você perder tempo adicionando componentes, eu já vou te dar um link para carregar o projeto pronto e salvar uma cópia com outro nome. Por exemplo: Framework Arduino Básico Aula 10: Piano PE.

    Link: https://wokwi.com/projects/412486913521116161

Um pequeno piano no Wokwi

    Nesse projeto pegamos sete botões, configuramos com resistor em pull down e criamos uma conexão para cada um com os pinos digitais de 13 a 6. Depois adicionamos um Buzzer e o conectamos ao pino 2 e a um barramento GND. Tudo isso já vimos como fazer em aulas anteriores.

    A novidade aqui é que se você clicar em um botão no modo de desenho do projeto, isto é, não durante a simulação, verá que nas opções de push-button eu tenho um item que me permite configurar no ambiente uma tecla de atalho. 

Tela mostrando o ajuste de tecla de atalho em um botão
Tela mostrando o ajuste de tecla de atalho em um botão

    Assim, quando você pressionar ALT+Z, será a mesma coisa que clicar no botão Vermelho da esquerda e se pressionar ALT+X será mesma coisa que clicar no botão verde, e assim por diante. Isso quer dizer que você poderá acionar os botões no seu computador com uma combinação de ALTE + a sequencia de teclas de Z até M. O "A" equivale ao botão vermelho da direita. 

    Como você já deve ter imaginado, são sete botões para tocar sete notas nesse piano improvisado, mas lembre-se de, ao iniciar a simulação, dar um clique em um botão para levar o foco do programa para essa tela, caso contrário as teclas não funcionarão.

    As notas musicais são sons que possuem uma frequência específica e você aprendeu na aula de buzzer que a função tone() me permite especificar qual a frequência que o buzzer vai tocar. Uma rápida pesquisa pela internet permite conhecer qual a frequência das  notas DO, RE, MI, FA, SOL, LÁ e SI  em uma escala musical.

    Se você conhece um pouco de música, sabe que as notas podem ter diferentes oitavas, isto é, podem estar em escalas mais graves ou agudas. Para subir uma oitava basta DOBRAR a frequência de uma nota e para descer, basta dividir a frequência por dois.

    Nosso pianinho vai ser bem fraquinho, pois o Arduino não tem muitos pinos para trabalharmos as notas e um outro detalhe é que a frequência das notas são números decimais, mas a função tone() só trabalha com números inteiros, por isso o pianinho será um pouco desafinado.

Vendo o Sketch do piano em programação estruturada

    Antes de complicar com a POO vamos ver como fica o Sketch desse piano em Programação Estruturada. É bem simples. Para facilitar a sua vida eu usei a diretiva #define para criar constantes com os nomes de notas musicais em uma escala e suas respectivas frequências:

  1. #define  Do  261
  2. #define  Re  293
  3. #define  Mi  329
  4. #define  Fa  349
  5. #define  Sol  391
  6. #define  La  440
  7. #define  Si  493
  8. #define  Do2  523

  9. void setup() {
  10.   pinMode(2, OUTPUT);
  11.   pinMode(6, INPUT);
  12.   pinMode(7, INPUT);
  13.   pinMode(8, INPUT);
  14.   pinMode(9, INPUT);
  15.   pinMode(10, INPUT);
  16.   pinMode(11, INPUT);
  17.   pinMode(12, INPUT);
  18.   pinMode(13, INPUT);
  19. }

  20. void loop() {

  21.   if (digitalRead(13)==HIGH){
  22.     tone(2,Do,100);  
  23.   }
  24.   if (digitalRead(12)==HIGH){
  25.     tone(2,Re,100);    
  26.   }
  27.   if (digitalRead(11)==HIGH){
  28.     tone(2,Mi,100);    
  29.   }
  30.   if (digitalRead(10)==HIGH){
  31.     tone(2,Fa,100);    
  32.   }
  33.   if (digitalRead(9)==HIGH){
  34.     tone(2,Sol,100);    
  35.   }
  36.   if (digitalRead(8)==HIGH){
  37.     tone(2,La,100);    
  38.   }
  39.   if (digitalRead(7)==HIGH){
  40.     tone(2,Si,100);    
  41.   }
  42.   if (digitalRead(6)==HIGH){
  43.     tone(2,Do2,100);    
  44.   }  
  45. delay(100);
  46. noTone(2);
  47. }

    Depois das diretivas eu tenho um setup() configurando os pinos dos botões como entradas e o pino do buzzer como saída. No loop() eu apenas coloquei testes com if para testar se o botão foi pressionado, isto é, se o digitalRead() do botão ficou HIGH

    Se for pressionado cada bloco de if executa um tone() com a tona específica, usando a constante no lugar do número da frequência. Ao final temos um delay() para o debounce permitindo a leitura do click e fechamos com o noTone() para liberar o buzzer para tocar outra frequência.

    Com relação ao delay() final, alguns autores falam que ele deve dar um pausa suficiente para o click sem bloquear totalmente o programa, por isso encontramos delays com valores menores, como 30 milissegundos. Eu sugiro que você teste diferentes intervalos e use o que melhor funciona em diferentes máquinas.

     Pensando no Botão como uma Classe

        Assim como o led, para construir um botão eu preciso estabelecer um pino para ele. Esse pino pode ser digital ou analógico. Para facilitar, eu vou criar uma classe só para botões que usem pinos digitais.

        Pelo que vimos da aula anterior, o led tinha o pino como atributo. Sendo assim, minha classe Botão também terá pino como atributo e para ajustar o atributo pino encapsulado eu também utilizarei um setter para ajustar o pino. Então até o momento nossa classe seria assim:

    class Botao {
          //atributos
        private:
          int pino;
        public:
          //construtor 
          Botao(int pino) {
            setPino(pino);
          }
          //setter
          void setPino(int pino){
            this->pino = pino;
            pinMode(pino, INPUT);
          }
    };

        No entanto, diferentemente do led que eu programador mandava acender ou apagar, no botão é o usuário que vai apertá-lo e vou precisar saber se o botão foi apertado para fazer alguma coisa. Sendo assim eu vou precisar do estado do botão. 

        Isso quer dizer que eu tenho outro atributo que é o estado do botão. Ai temos um ponto onde pode haver alguns divergências quando comparamos o material disponível na Web. Esse atributo estado poderia ser declarado como um booleano ou como um inteiro. As duas estratégias tem vantagens e desvantagens. Aqui vou adotá-lo como inteiro. 

        Como vou precisar pegar o estado do botão, precisarei de um getter para pegar o atributo estado que está encapsulado. Sendo assim nosso código poderia ficar desse jeito:

    class Botao {
          //atributos
        private:
          int pino;
          int estado;
        public:
          //construtor 
          Botao(int pino) {
            setPino(pino);
          }
          //setter
          void setPino(int pino){
            this->pino = pino;
            pinMode(pino, INPUT);
          }
           //getter
          int getPino(int pino){
            this->estado=estado;
            estado=digitalRead(pino);
            return estado;
          }
    };


        Repare que diferentemente do setter que pode iniciar com void, o getter vai devolver ao código o atributo estado que no nosso caso é inteiro, logo ele começa com um int. Veja que esse getter não funcionaria se o pino fosse analógico. Eu precisaria ter um analogRead(), mas veremos isso em outra aula.

    O Sketch em POO

        Assim nosso código ficou desse jeito. Você pode copiar e colar no seu projeto ou abrir diretamente o nosso link e salvar a cópia com outro nome:

    1. #define  Do  261
    2. #define  Re  293
    3. #define  Mi  329
    4. #define  Fa  349
    5. #define  Sol  391
    6. #define  La  440
    7. #define  Si  493
    8. #define  Do2  523

    9.  class Botao {
    10.     private:
    11.       int pino;
    12.       int estado;
    13.     public:
    14.       Botao(int pino) {
    15.         setPino(pino);
    16.       }
    17.       void setPino(int pino){
    18.         this->pino = pino;
    19.         pinMode(pino, INPUT);
    20.       }
    21.       int getPino() {
    22.         this->estado=estado;
    23.         estado=digitalRead(pino);
    24.         return estado;
    25.       }
    26. };

    27. Botao Botao6(6);
    28. Botao Botao7(7);
    29. Botao Botao8(8);
    30. Botao Botao9(9);
    31. Botao Botao10(10);
    32. Botao Botao11(11);
    33. Botao Botao12(12);
    34. Botao Botao13(13);

    35. void setup() {
    36.   pinMode(2, OUTPUT);
    37. }

    38. void loop() {

    39.   if (Botao13.getPino()==1){
    40.     tone(2,Do,100);  
    41.   }   
    42.   if (Botao12.getPino()==1){
    43.     tone(2,Re,100);    
    44.   }
    45.   if (Botao11.getPino()==1){
    46.     tone(2,Mi,100);    
    47.   }
    48.   if (Botao10.getPino()==1){
    49.     tone(2,Fa,100);    
    50.   }
    51.   if (Botao9.getPino()==1){
    52.     tone(2,Sol,100);    
    53.   }
    54.   if (Botao8.getPino()==1){
    55.     tone(2,La,100);    
    56.   }
    57.   if (Botao7.getPino()==1){
    58.     tone(2,Si,100);    
    59.   }
    60.   if (Botao6.getPino()==1){
    61.     tone(2,Do2,100);    
    62.   }  
    63. delay(100);
    64. noTone(2);
    65. }

        Link: https://wokwi.com/projects/412488183630919681

         Vou comentar apenas as diferenças em relação ao Sketch anterior da Programação Estruturada. Eu tenho a criação da classe das linhas 10 a 27, conforme comentamos anteriormente. Note que eu poderia ter colocado a classe em outro arquivo, mas vou deixar no mesmo Sketch assim como o exemplo da aula anterior para facilitar o entendimento.

        Depois das linhas 29 a 36 eu tenho o instanciamento dos objetos botões sendo que para facilitar usei no nome a indicação do pino. Isso precisa ser feito antes da função de setup() como expliquei na aula anterior

        Só que agora o meu setup() não está vazio, pois precisei configurar o pino do buzzer como saída apesar de que alguns desenvolvedores optariam por criar uma classe também para o buzzer. Criar uma classe para usar um único componente em um arquivo de exemplo, não seria muito produtivo.

        Depois na função loop() foi quase igual ao raciocínio da Programação Estruturada, só que ao invés de usar o digitalRead() eu usei o método getPino()para cada botão. Aula que vem continuaremos com mais conteúdo.


    Próxima Aula                                                                                                                        Aula Anterior

    Referências

    ARDUINO DOCS. Arduino Documentation. Disponível em: <https://docs.arduino.cc/> Acesso em 05 ago. 2024.

    OLIVEIRA, Cláudio Luis Vieira et al. Aprenda Arduino: Uma abordagem prática. Duque de Caixas: Katzen Editora, 2018. 181p. Disponível em: <https://www.fatecjd.edu.br/fatecino/material/ebook-aprenda-arduino.pdf> Acesso em 05 ago. 2024.

    OLIVEIRA, Cláudio Luis Vieira. Arduino Descomplicado - Como Elaborar Projetos de Eletrônica. São Paulo: Editora Érica, 2015. 287p. 

    TINKERCAD. Centro de Aprendizagem. Disponível em: <https://www.tinkercad.com/learn> Acesso em 05 ago. 2024.

    WOKWI. Referência do wokwi-arduino-uno. Disponível em: <https://docs.arduino.cc/> Acesso em 05 ago. 2024.