Piano baseado em Arduino com gravação e repetição

Tempo de leitura: 11 minutes

O Arduino tem sido uma bênção para as pessoas que não são da área de eletrônica criarem coisas facilmente. Tem sido uma ótima ferramenta de prototipagem ou para tentar algo legal, neste projeto vamos construir um pequeno e divertido Piano usando o Arduino. Este piano é bastante simples com apenas 8 botões e campainha. Ele usa a função tone() do Arduino para criar vários tipos de notas de piano no alto-falante. Para apimentar um pouco, adicionamos o recurso de gravação no projeto, o que nos permite tocar uma música, gravá-la e reproduzi-la repetidamente quando necessário. Parece interessante certo !! Então, vamos começar a construir …

Materiais requisitados:

  • Arduino Uno
  • Visor LCD 16*2
  • Campainha
  • Trimmer 10k
  • Interruptor SPDT
  • Botão de pressão (x8)
  • Resistores (10k, 560R, 1,5k, 2,6k, 3,9, 5,6k, 6,8k, 8,2k, 10k)
  • ProtoBoard
  • Fios de conexão

Diagrama de circuito:

O projeto Arduino Piano completo pode ser construído em cima de uma placa de ensaio com alguns fios de conexão. O diagrama de circuito feito usando fritzing que mostra a visão do protoboad do projeto é mostrado abaixo

Basta seguir o diagrama de circuito e conectar os fios de acordo, os botões e campainha usados com um módulo PCB, mas no hardware real usamos apenas o interruptor e a campainha, não deve confundi-lo muito porque eles têm o mesmo tipo de pino . Você também pode consultar a imagem abaixo do hardware para fazer suas conexões.

O valor dos resistores da esquerda está na seguinte ordem: 10k, 560R, 1,5k, 2,6k, 3,9, 5,6k, 6,8k, 8,2k e 10k. Se você não tiver a mesma chave DPST, pode usar a chave seletora normal como a mostrada no diagrama de circuito acima. Agora, vamos dar uma olhada no esquema do projeto para entender por que fizemos as seguintes conexões.

Esquemas e explicação:

O esquema do diagrama de circuito mostrado acima é dado abaixo, ele também foi feito usando Fritzing.

Uma conexão principal que precisamos entender é como conectamos os 8 botões ao Arduino por meio do pino A0 analógico. Basicamente, precisamos de 8 pinos de entrada que podem ser conectados aos 8 botões de entrada, mas para projetos como este não podemos usar 8 pinos do microcontrolador apenas para botões, pois podemos precisar deles para uso posterior. Em nosso caso, temos o display LCD para fazer a interface.

Portanto, usamos o pino analógico do Arduino e formamos um divisor de potencial com vários valores de resistor para completar o circuito. Desta forma, quando cada botão é pressionado, uma tensão analógica diferente será fornecida ao pino analógico. Um circuito de amostra com apenas dois resistores e dois botões de pressão é mostrado abaixo.

Neste caso, o pino ADC receberá + 5V quando os botões não forem pressionados, se o primeiro botão for pressionado, o divisor de potencial será concluído através do resistor 560R e se o segundo botão for pressionado, o divisor de potencial será competido usando o 1.5 resistor k. Desta forma, a tensão recebida pelo pino ADC irá variar com base nas fórmulas do divisor de potencial.

Fora isso, todas as conexões são diretas, o LCD é conectado aos pinos 8, 9, 10, 11 e 12. O buzzer é conectado ao pino 7 e a chave SPDT é conectada ao pino 6 do Arduino. O projeto completo é alimentado pela porta USB do laptop. Você também pode conectar o Arduino a uma fonte de 9 V ou 12 V através do conector DC e o projeto continuará funcionando da mesma forma.

 

Compreendendo a função Tone() do Arduino:

O Arduino tem uma função tone() útil que pode ser usada para gerar sinais de frequência variados que podem ser usados ​​para produzir sons diferentes usando uma campainha. Então, vamos entender como a função funciona e como pode ser usada com o Arduino.

Antes disso, devemos saber como funciona uma campainha Piezo. Podemos ter aprendido sobre cristais Piezo em nossa escola, nada mais é que um cristal que converte vibrações mecânicas em eletricidade ou vice-versa. Aqui aplicamos uma corrente variável (frequência) para a qual o cristal vibra, produzindo som. Portanto, para fazer com que a campainha Piezo produza algum ruído, temos que fazer o cristal elétrico Piezo vibrar. A altura e o tom do ruído dependem de quão rápido o cristal vibra. Conseqüentemente, o tom e a altura podem ser controlados variando a frequência da corrente.

Ok, então como obtemos uma frequência variável do Arduino? É aqui que entra a função tone(). O tone() pode gerar uma freqüência particular em um pino específico. A duração do tempo também pode ser mencionada, se necessário. A sintaxe para tom() é

Sintaxe
tom(pin, frequência)
tom(pin, frequência, duração)

Parâmetros
pin: o pino sobre o qual gerar o tom
frequência: a frequência do tom em hertz - unsigned int
duração: a duração do tom em milissegundos (opcional1) - sem sinal longo

Os valores de pino podem ser qualquer um de seu pino digital. Usei o pino número 8 aqui. A frequência que pode ser gerada depende do tamanho do temporizador em sua placa Arduino. Para UNO e a maioria das outras placas comuns, a frequência mínima que pode ser produzida é 31 Hz e a frequência máxima que pode ser produzida é 65535 Hz. No entanto, nós, humanos, podemos ouvir apenas frequências entre 2.000 Hz e 5.000 Hz.

 

Tocando tons de piano no Arduino:

Ok, antes mesmo de começar neste tópico, deixe-me deixar claro que sou um novato com notas musicais ou piano, então, por favor, me perdoe se alguma coisa mencionada neste título for um jargão.

Agora sabemos que podemos usar a função de tons no Arduino para produzir alguns sons, mas como podemos tocar tons de uma nota específica usando o mesmo. Para nossa sorte, existe uma biblioteca chamada “pitches.h”, escrita por Brett Hagman. Esta biblioteca contém todas as informações sobre qual frequência é equivalente a qual nota em um piano. Fiquei surpreso com o quão bem esta biblioteca poderia realmente funcionar e tocar quase todas as notas em um piano, eu usei a mesma para tocar as notas de piano de Pirates of Caribbean, Crazy Frog, Mario e até titanic e elas soaram incríveis. Ops! Estamos saindo um pouco do assunto aqui, então, se você estiver interessado nisso, dê uma olhada em tocar melodias usando o projeto Arduino. Você também encontrará mais explicações sobre a biblioteca pitches.h nesse projeto.

Nosso projeto tem apenas 8 botões, então cada botão pode tocar apenas uma nota musical particular e, portanto, no total, podemos tocar apenas 8 notas. Selecionei as notas mais usadas em um piano, mas você pode selecionar qualquer 8 ou até mesmo expandir o projeto com mais botões e adicionar mais notas.

As notas selecionadas neste projeto são as notas C4, R4, E4, F4, G4, A4, B4 e C5 que podem ser tocadas usando os botões 1 a 8 respectivamente.

 

Programando o Arduino:

Chega de teoria nos deixe chegar à parte divertida de programar o Arduino. O Programa Arduino completo é fornecido no final desta página. Você pode pular para baixo se estiver ansioso ou ler mais para entender como o código funciona.

Em nosso programa Arduino, temos que ler a tensão analógica do pino A0, então prever qual botão foi pressionado e tocar o respectivo tom para aquele botão. Ao fazer isso, devemos também registrar qual botão o usuário pressionou e por quanto tempo ele/ela pressionou, para que possamos recriar o tom que foi reproduzido pelo usuário mais tarde.

Antes de ir para a parte lógica, temos que declarar quais 8 notas tocaremos. A respectiva frequência para as notas é então obtida da biblioteca pitches.h e, em seguida, uma matriz é formada conforme mostrado abaixo. Aqui, a frequência para tocar a nota C4 é 262 e assim por diante.

int notes[] = {262, 294, 330, 349, 392, 440, 494, 523}; // Defina a frequência para C4, D4, E4, F4, G4, A4, B4,

A seguir, temos que mencionar a quais pinos o display LCD está conectado. Se você estiver seguindo exatamente os mesmos esquemas fornecidos acima, não é necessário alterar nada aqui.

const int rs = 8, en = 9, d4 = 10, d5 = 11, d6 = 12, d7 = 13; // Pinos aos quais o LCD está conectado
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

Em seguida, dentro de nossa função de configuração, apenas inicializamos o módulo LCD e o monitor serial para depuração. Também exibimos uma mensagem de introdução apenas para ter certeza de que as coisas estão funcionando conforme o planejado. A seguir, dentro da função de loop principal, temos dois loops while.

Um loop while será executado enquanto a chave SPDT estiver posicionada para gravar mais. No modo de gravação o usuário pode pagar os tons necessários e ao mesmo tempo o tom que está sendo reproduzido também será salvo. Então, o loop while se parece com este abaixo

while (digitalRead(6) == 0) //Se a chave seletora estiver definida no modo de gravação
{
  lcd.setCursor(0, 0); lcd.print("Recording..");
  lcd.setCursor(0, 1);
  Detect_button();
  Play_tone();
}

Como você deve ter notado, temos duas funções dentro do loop while. A primeira função Detect_button() é usada para encontrar qual botão o usuário pressionou e a segunda função Play_tone() é usada para tocar o respectivo tom. Além desta função, a função Detect_button() também registra qual botão está sendo pressionado e a função Play_tone() registra por quanto tempo o botão foi pressionado.

Dentro da função Detect_button() lemos a tensão analógica do pino A0 e comparamos com alguns valores predefinidos para descobrir qual botão foi pressionado. O valor pode ser determinado usando a calculadora do divisor de tensão acima ou usando o monitor serial para verificar qual valor analógico é lido para cada botão.

void Detect_button()
{
  analogVal = analogRead(A0); //leia a tensão analógica no pino A0
  pev_button = button; //lembre-se do botão anterior pressionado pelo usuário

  if (analogVal < 550)
    button = 8;

  if (analogVal < 500)
    button = 7;

  if (analogVal < 450)
    button = 6;

  if (analogVal < 400)
    button = 5;

  if (analogVal < 300)
    button = 4;

  if (analogVal < 250)
    button = 3;

  if (analogVal < 150)
    button = 2;

  if (analogVal < 100)
    button = 1;

  if (analogVal > 1000)
    button = 0;
   
/****Grave os botões pressionados em uma matriz***/
  if (button != pev_button && pev_button != 0)
  {
    recorded_button[button_index] = pev_button;
    button_index++;
    recorded_button[button_index] = 0;
    button_index++;
  }
/**Fim do programa de gravação**/
}

Como dito, dentro desta função também gravamos a sequência em que os botões são pressionados. Os valores registrados são armazenados em uma matriz chamada record_button[]. Primeiro verificamos se há um novo botão pressionado, se pressionado, também verificamos se não é o botão 0. Onde o botão 0 não é nada, mas nenhum botão foi pressionado. Dentro do loop if, armazenamos o valor no local do índice fornecido pela variável button_index e, em seguida, também aumentamos esse valor do índice para não sobrescrever no mesmo local.

/**** Grave os botões pressionados em uma matriz ***/
  if (button != pev_button && pev_button != 0)
  {
    recorded_button[button_index] = pev_button;
    button_index++;
    recorded_button[button_index] = 0;
    button_index++;
  }
/**Fim do programa de gravação**/

Dentro da função Play_tone(), tocaremos o respectivo tom para o botão pressionado usando várias condições if. Além disso, usaremos um array chamado record_time[], dentro do qual salvaremos a duração de tempo em que o botão foi pressionado. A operação é semelhante à sequência do botão de gravação, pois usamos a função millis() para determinar por quanto tempo cada botão foi pressionado, também para reduzir o tamanho da variável dividimos o valor por 10. Para o botão 0, o que significa que o usuário não pressionando qualquer coisa, não tocamos nenhum tom pela mesma duração. O código completo dentro da função é mostrado abaixo.

void Play_tone()
{

 /****Registre o intervalo de tempo entre cada pressionamento de botão em uma matriz***/

  if (button != pev_button)
  {
    lcd.clear(); //Então limpe
    note_time = (millis() - start_time) / 10;
    recorded_time[time_index] = note_time;
    time_index++;
    start_time = millis();
  }
  /**Fim do programa de gravação**/

  if (button == 0)
  {
    noTone(7);
    lcd.print("0 -> Pause..");
  }

  if (button == 1)
  {
    tone(7, notes[0]);
    lcd.print("1 -> NOTE_C4");
  }

  if (button == 2)
  {
    tone(7, notes[1]);
    lcd.print("2 -> NOTE_D4");
  }

  if (button == 3)
  {
    tone(7, notes[2]);
    lcd.print("3 -> NOTE_E4");
  }

  if (button == 4)
  {
    tone(7, notes[3]);
    lcd.print("4 -> NOTE_F4");
  }

  if (button == 5)
  {
    tone(7, notes[4]);
    lcd.print("5 -> NOTE_G4");
  }

  if (button == 6)
  {
    tone(7, notes[5]);
    lcd.print("6 -> NOTE_A4");
  }

  if (button == 7)
  {
    tone(7, notes[6]);
    lcd.print("7 -> NOTE_B4");
  }

  if (button == 8)
  {
    tone(7, notes[7]);
    lcd.print("8 -> NOTE_C5");
  }
}

Finalmente, após a gravação, o usuário deve alternar o DPST para a outra direção para reproduzir o tom gravado. Quando isso é feito, o programa interrompe o loop while anterior e entra no segundo loop while onde tocamos as notas na sequência dos botões pressionados por uma duração que foi previamente gravada. O código para fazer o mesmo é mostrado abaixo.

while (digitalRead(6) == 1) //Se a chave seletora estiver no modo de reprodução
{
  lcd.clear();
  lcd.setCursor(0, 0);  lcd.print("Now Playing..");

  for (int i = 0; i < sizeof(recorded_button) / 2; i++)
  {
    delay((recorded_time[i]) * 10); //Espere antes de pagar a próxima música

    if (recorded_button[i] == 0)
      noTone(7); //user dint touch any button
    else
      tone(7, notes[(recorded_button[i] - 1)]); //tocar o som correspondente ao botão tocado pelo usuário
  }
  
}

Tocar, gravar, repetir e repetir! :

Faça o hardware de acordo com o diagrama de circuito mostrado e carregue o código para a placa Arduino e seu tempo mostrado. Posicione o SPDT no modo de gravação e comece a tocar os tons de sua escolha, pressionando cada botão produzirá um tom diferente. Durante este modo, o LCD exibirá “Recording…” e na segunda linha você verá o nome da nota que está sendo pressionada, conforme mostrado abaixo

Depois de reproduzir seu tom, alterne a chave SPDT para o outro lado e o LCD deve exibir “Now Playing..” e, em seguida, comece a reproduzir o tom que acabou de tocar. O mesmo tom será reproduzido repetidamente, desde que a chave seletora seja mantida na posição mostrada na imagem abaixo.

O funcionamento completo do projeto pode ser encontrado no vídeo abaixo. Espero que você tenha entendido o projeto e gostado de construí-lo. Se você tiver qualquer problema em construir este post-los na seção de comentários ou use os fóruns para ajuda técnica em seu projeto.

Código

/*
   Piano baseado em Arduino e opção de gravação e reprodução
*/

#include <LiquidCrystal.h>

int notes[] = {262, 294, 330, 349, 392, 440, 494, 523}; //Set frequency for C4, D4, E4, F4, G4, A4, B4, C5

const int rs = 8, en = 9, d4 = 10, d5 = 11, d6 = 12, d7 = 13; //Pinos aos quais o LCD está conectado
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

char button = 0;
int analogVal;
char REC = 0;

int recorded_button[200];
int pev_button;

int recorded_time [200];
char time_index;

char button_index = 0;

unsigned long start_time;
int note_time;

void setup() {

  Serial.begin(9600);
  pinMode (6, INPUT);

  lcd.begin(16, 2); // Estamos usando um display LCD 16 * 2
  lcd.print("Arduino Piano"); // Exibir uma mensagem de introdução
  lcd.setCursor(0, 1);   // coloque o cursor na coluna 0, linha 1
  lcd.print("-CapSistema"); //Exibir uma mensagem de introdução

  delay(2000); //Aguarde o display mostrar informações
  lcd.clear(); //Então limpe

}

void loop() 
{

  while (digitalRead(6) == 0) //Se a chave seletora estiver definida no modo de gravação
  {
    lcd.setCursor(0, 0); lcd.print("Recording..");
    lcd.setCursor(0, 1);

    Detect_button();
    Play_tone();
  }

 
  while (digitalRead(6) == 1) //Se a chave seletora estiver no modo de reprodução
  {
      lcd.clear();
      lcd.setCursor(0, 0);  lcd.print("Now Playing..");

      for (int i = 0; i < sizeof(recorded_button) / 2; i++)
      {
        delay((recorded_time[i]) * 10); //Espere antes de pagar a próxima música

        if (recorded_button[i] == 0)
          noTone(7); //o usuário não toque em nenhum botão
        else
          tone(7, notes[(recorded_button[i] - 1)]); //tocar o som correspondente ao botão tocado pelo usuário
      }
  }
}

void Detect_button()
{
  analogVal = analogRead(A0); //leia a tensão analógica no pino A0

  pev_button = button; //lembre-se do botão anterior pressionado pelo usuário

  if (analogVal < 550)
    button = 8;

  if (analogVal < 500)
    button = 7;

  if (analogVal < 450)
    button = 6;

  if (analogVal < 400)
    button = 5;

  if (analogVal < 300)
    button = 4;

  if (analogVal < 250)
    button = 3;

  if (analogVal < 150)
    button = 2;

  if (analogVal < 100)
    button = 1;

  if (analogVal > 1000)
    button = 0;

    
/****Grave os botões pressionados em uma matriz***/
  if (button != pev_button && pev_button != 0)
  {
    recorded_button[button_index] = pev_button; 
    button_index++;
    recorded_button[button_index] = 0;
    button_index++;
  }
/**Fim do programa de gravação**/
}

void Play_tone()
{

 /****Registre o intervalo de tempo entre cada pressionamento de botão em uma matriz***/
  if (button != pev_button)
  {
    lcd.clear(); //Então limpe
    note_time = (millis() - start_time) / 10;

    recorded_time[time_index] = note_time;
    time_index++;

    start_time = millis();
  }
  /**Fim do programa de gravação**/

  if (button == 0)
  {
    noTone(7);
    lcd.print("0 -> Pause..");
  }

  if (button == 1)
  {
    tone(7, notes[0]);
    lcd.print("1 -> NOTE_C4");
  }

  if (button == 2)
  {
    tone(7, notes[1]);
    lcd.print("2 -> NOTE_D4");
  }

  if (button == 3)
  {
    tone(7, notes[2]);
    lcd.print("3 -> NOTE_E4");
  }

  if (button == 4)
  {
    tone(7, notes[3]);
    lcd.print("4 -> NOTE_F4");
  }

  if (button == 5)
  {
    tone(7, notes[4]);
    lcd.print("5 -> NOTE_G4");
  }

  if (button == 6)
  {
    tone(7, notes[5]);
    lcd.print("6 -> NOTE_A4");
  }

  if (button == 7)
  {
    tone(7, notes[6]);
    lcd.print("7 -> NOTE_B4");
  }

  if (button == 8)
  {
    tone(7, notes[7]);
    lcd.print("8 -> NOTE_C5");
  }
}