Manual

do

Maker

.

com

Adams Family digital song

Adams Family digital song

Bonito o título em inglês, hum? É realmente um "Adams Family digital song", mas "bem" digital.

Usei um módulo relé de 16 canais da Fulltronic, um buzzer, um PCF8574 e um ESP32 na placa de desenvolvimento da Curto Circuito. Bastante inspiração também. Depois troquei o buzzer por um falante pequeno, achando que era problema do buzzer, mas era problema do programador, escrevendo código ruim no melhor estilo "Batalha Makers Brasil".

Até os jumpers resolvi dar uma mudada. A MASUGUX agora vende jumpers e conectores largos para ter uma barra de jumpers única. É fácil trocar, basta levantar a travinha do lado que prende o conector e depois encaixar o jumper "pelado" na barra.

adamsFamily-jumper-300x169.webp

Há mais de uma vantagem em utilizar esse conector múltiplo de jumpers; por exemplo, garantir que eles não fiquem dando mal contato. A outra vantagem é que garante a ordem das conexões. Eu gostei e adotei, se quiser experimentar também, esse é o link para compra dos headers. Se quiser jumpers com headers prontos pra usar (caso tenha preguiça extrema), o link para compra é esse.

Vou aproveitar e deixar um link para compra do ESP32-CAM a um preço maluco, que é um tipo de "subsídio" para o vendedor ganhar pontos no MercadoLivre; inclui cabo de alimentação, FTDI, jumpers e headers. Tudo isso por  bem menos do que estão vendendo apenas o ESP32-CAM. Não se preocupe, o vendedor é novo mas é confiável, recomendado pelo nosso parceiro MASUGUX. Essa promoção não é para sempre, então aproveite o quanto antes clicando aqui.

O projeto deu um trabalhão por conta de um erro de lógica, foi difícil achar uma partitura gratuita da música da família Adams e tive que arrumar uma fonte para alimentar esse monstrão de 16 canais, apesar de só ter usado 8 (porque estou sem outro PCF8574).

No final das contas, a fonte que alimentei o módulo relé de 16 canais não foi suficiente, mexi demais nos tempos e nomes dos tempos porque achei que eu estava fazendo errado, mas no final das contas a partitura não era do jeito que eu imaginei o som da música na minha cabeça. Acabei aproveitando só as notas das partituras e o resto ficou bagunçado, mas o resultado agrada.

Melodia, harmonia e ritmo

A primeiríssima aula do bona é a definição do que é música. Não vou explicar a teoria da música, não se preocupe. Bem, não vou explicar ela toda, pelo menos.

Acontece que a música é divida em tempos de notas e compasso. No caso da música "The Adams Family" o compasso é um dos mais fáceis, a divisão é 4/4. Os tempos das notas dependem do gosto. Na verdade, uo tempo é marcado em batidas por minuto. Por exemplo, se marcado =120 significa que uma semínima equivale a 120 batidas por minuto , mas o BPM pode ser modificado para atender a própria vontade, não precisamos nos apegar a isso em um projeto maker.

Os tempos das notas são representados pelos seus desenhos:

tempos_das_notas-300x193.webp

A bolinha branca equivale a 4 tempos, seguido por 2 tempos, 1 tempo, meio tempo, 1/4 de tempo, 1/8 de tempo e 1/16 de tempo.

Partitura da música The Adams Family

Pesquisei um bocado e consegui encontrar uma partitura gratuita. Poderia tirar de ouvido, mas na segunda sequencia percebi que ia errar, ao ver a partitura.

Escala

As notas da escala maior todos conhecem; dó, ré, mi, fá, sol, lá, si. As notas continuam para cima e para baixo, fazendo sons mais graves (para baixo) e mais agudos (para cima). Quando sai da escala inicial é chamado de "oitava". O segundo dó é a primeira oitava e assim por diante. Além disso, tem o meio tom, que pode ser acima ou abaixo, dependendo de algumas características, mas basicamente, o meio tom de uma nota acima é sustenido, enquanto meio tom abaixo é bemol. Isso significa que Dó# (sustenido) é Ré bemol.

Por que entrei em tantos detalhes técnicos? Apenas para poder explicar o código que vem mais adiante.

Partitura The Adams Family

A partitura é essa a seguir. Gostaria de citar também que não basta fazer uma nota tocar no tempo certo, as notas são separadas, então existe um intervalo mínimo entre o tocar de uma e outra, exceto contenham semi-ligaduras ou ligaduras, representadas na partitura por um arco sobre ou sob elas.

Por que falei desses detalhes técnicos? Apenas para poder explicar também no código mais adiante.

adamsFamilyPartitureFull-220x300.webp

Afinação

No modo convencional, os instrumentos musicais são afinados em Lá. A nota Lá tem uma frequência de 440Hz e, por curiosidade, algumas pessoas afinavam o violão em telefones públicos antigamente, usando o som de linha como referência, porque nos países da américa o tom varia entre 350 e 440Hz. No Brasil, o tom adotado é de 425Hz, quase o Lá perfeito.

Enfim, começando a falar tecnicamente do que nos interessa, tive que "afinar" as notas, pois as referência que eu tinham estavam bem fora. Alguma coisa fez com que as notas de referência que eu tinha estivessem bastante desafinadas, então peguei o afinador da CifraClub que utilizo no meu smartphone e fui tocando uma a uma no ESP32 e aferindo, até ter todas elas afinadas para o meu projeto. Tenho quase certeza que você também precisará fazer um ajuste, dependendo das circunstâncias, então, lembre-se da dica acima (do afinador, não do telefone público).

Código para tocar a música Adams Family no ESP32

ESP32 não é Arduino. As coisas são um pouco diferentes, ainda que tendo uma API para programá-lo na IDE do Arduino. Para tocar o buzzer, usamos ledcSetup, ledcWriteTone e ledcAttach, explicado melhor no código.

Para as notas, criei um array de int contendo todos os valores e para identificá-las, utilizei um enumerador contendo seus nomes, só que invés de usar o posicional do enumerador, atribui os valores para cada item.

Para tocar, criei uma função chamada play que recebe como parâmetros a nota, o intervalo entre as notas e o tempo de cada nota. Para tocar o Sol da terceira escala com descolamento de 10ms e tempo de semínima, utilizei:

play(sol_3,10,notesTime.seminima);

Dá um pouco de trabalho escrever assim, mas desse modo é lido como se fosse uma partitura, o que facilita escrever a música lendo a partitura, como fiz. Não estou querendo insinuar que ficou perfeito, mas ficou bastante bom. Outra coisa; pela partitura temos sí bemol e mí bemol, sendo que na segunda e terceira sequência tem um bequadro, mas preferi ignorar o bemol porque o som não estava exatamente adequado. Só estou dando essa explicação porque certamente músicos também verão o vídeo e não quero passar a impressão de que foi "sem querer". Outra coisa; em relação aos intervalos, não estão precisos, mas preferi me ater ao funcionamento.

Outra coisa; eu esperava tempos de notas diferentes do que estão na partitura. Além disso, eu mexi muito nos tempos das notas, nos nomes e acabei bagunçando um bocado. Sinta-se à vontade para corrigir o código:

#include <Arduino.h>
#include <Wire.h>

#define CHANELL    0
#define FREQUENCE  20000
#define RESOLUTION 10
#define BUZZER_PIN GPIO_NUM_14
#define PCF_ADDR 0x22

enum notes{
    do_3 = 25, do_sustenido_3 = 26, re_3 = 27, re_sustenido_3 = 28, mi_3 = 29, fa_3 = 30, fa_sustenido_3 = 31, sol_3 = 32, sol_sustenido_3 = 33, la_3 = 34, la_sustenido_3 = 35, si_3 = 36,
    do_4 = 37, do_sustenido_4 = 38, re_4 = 39, re_sustenido_4 = 40, mi_4 = 41, fa_4 = 42, fa_sustenido_4 = 43, sol_4 = 44, sol_sustenido_4 = 45, la_4 = 46, la_sustenido_4 = 47, si_4 = 48,
    do_5 = 49, do_sustenido_5 = 50, re_5 = 51, re_sustenido_5 = 52, mi_5 = 53, fa_5 = 54, fa_sustenido_5 = 55, sol_5 = 56, sol_sustenido_5 = 57, la_5 = 58, la_sustenido_5 = 59, si_5 = 60,
    do_6 = 61, do_sustenido_6 = 62, re_6 = 63, re_sustenido_6 = 64, mi_6 = 65, fa_6 = 66, fa_sustenido_6 = 67, sol_6 = 68, sol_sustenido_6 = 69, la_6 = 70, la_sustenido_6 = 71, si_6 = 72,
    do_7 = 73, do_sustenido_7 = 74, re_7 = 75, re_sustenido_7 = 76, mi_7 = 77, fa_7 = 78, fa_sustenido_7 = 79, sol_7 = 80, sol_sustenido_7 = 81, la_7 = 82, la_sustenido_7 = 83, si_7 = 84,
    do_8 = 85
};

struct NotesTime{
    //bpm reduzido (batidas por minuto)
    float semibreve    = 1000.0;    // 4 temp
    float minima       = 500.0;    // 2 tempo
    float seminima     = 250.0;    // 1 tempo
    float colcheia     = 125.5;     // 1/2 tempo
    float semicolcheia = 62.5;    // 1/4 tempo
    //Não implementado fusa e semifusa
} notesTime;

int note_value[89] = {31, 33, 35, 37, 39, 41, 44, 46, 49, 52,
                      55, 58, 62, 65, 69, 73, 78, 82, 87, 93,
                      98, 104, 110, 117, 123, 131, 139, 147, 156, 165,
                      175, 185, 196, 208, 220, 233, 247, 262, 277, 294,
                      311, 330, 349, 370, 392, 415, 440, 466, 494, 523,
                      554, 587, 622, 659, 698, 740, 784, 831, 880, 932,
                      988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661,
                      1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960,
                      3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978};

void play(uint8_t note, float interval, float noteTime);
void clap(uint8_t claps, int interval);

void vTaskAdamsMusic(void *pvParameters){
  play(sol_3,10,notesTime.seminima);
  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);

  clap(1,notesTime.minima);
  
  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);
  play(re_4,10,notesTime.seminima);

  clap(1,notesTime.minima);

  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);
  play(re_4,10,notesTime.seminima);

  vTaskDelay(pdMS_TO_TICKS(notesTime.seminima));
  vTaskDelay(pdMS_TO_TICKS(notesTime.seminima));

  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);
  play(re_4,10,notesTime.seminima);

  vTaskDelay(pdMS_TO_TICKS(notesTime.seminima));
  vTaskDelay(pdMS_TO_TICKS(notesTime.seminima));

  play(sol_3,10,notesTime.seminima);
  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);

  clap(1,notesTime.minima);

  //eu prefiro assim.
  play(fa_4,10,notesTime.seminima);
  play(la_sustenido_4,10,notesTime.minima);
  play(re_5,10,notesTime.seminima);
  play(la_sustenido_4,10,notesTime.minima);
  play(sol_4,10,notesTime.seminima);
  play(mi_4,10,notesTime.minima);
  play(do_5,10,notesTime.semibreve);

  vTaskDelay(pdMS_TO_TICKS(notesTime.semicolcheia));

  //sol_sus la do la fa re si_
  play(sol_sustenido_4,10,notesTime.seminima);
  play(la_4,10,notesTime.minima);
  play(do_5,10,notesTime.seminima);
  play(la_4,10,notesTime.minima);
  play(fa_4,10,notesTime.seminima);
  play(re_4,10,notesTime.minima);
  play(la_sustenido_4,10,notesTime.semibreve);

  play(fa_4,10,notesTime.seminima);
  play(la_sustenido_4,10,notesTime.minima);
  play(re_5,10,notesTime.seminima);
  play(la_sustenido_4,10,notesTime.minima);
  play(sol_4,10,notesTime.seminima);
  play(mi_4,10,notesTime.minima);
  play(do_5,10,notesTime.semibreve);

  vTaskDelay(pdMS_TO_TICKS(notesTime.semicolcheia));

  //si la fa sol la si 
  play(la_sustenido_4,10,notesTime.seminima);
  play(la_4,10,notesTime.minima);
  play(fa_4,10,notesTime.seminima);
  play(sol_4,10,notesTime.minima);
  play(la_4,10,notesTime.seminima);
  play(la_sustenido_4,10,notesTime.semibreve);

  //INTRODUCAO NOVAMENTE
  play(sol_3,10,notesTime.seminima);
  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);

  clap(1,notesTime.minima);

  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);
  play(re_4,10,notesTime.seminima);

  clap(1,notesTime.minima);

  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);
  play(re_4,10,notesTime.seminima);

  vTaskDelay(pdMS_TO_TICKS(notesTime.seminima));
  vTaskDelay(pdMS_TO_TICKS(notesTime.seminima));

  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);
  play(re_4,10,notesTime.seminima);

  vTaskDelay(pdMS_TO_TICKS(notesTime.seminima));
  vTaskDelay(pdMS_TO_TICKS(notesTime.seminima));

  play(sol_3,10,notesTime.seminima);
  play(la_3,10,notesTime.seminima);
  play(si_3,10,notesTime.seminima);
  play(do_4,10,notesTime.seminima);

  clap(1,notesTime.minima); 

  vTaskDelete(NULL);
}

void vTaskTun(void *pvParameters){
  for (int i=25;i<86;i++){
      play(i,10,notesTime.semibreve);
  }
  vTaskDelete(NULL);
}

void play(uint8_t note, float interval, float noteTime){
    ledcWriteTone(CHANELL,note_value[note]);
    vTaskDelay(pdMS_TO_TICKS(noteTime-interval));
    ledcWriteTone(CHANELL,0);
    vTaskDelay(pdMS_TO_TICKS(interval));
}

void clap(uint8_t claps, int interval){
    vTaskDelay(pdMS_TO_TICKS(interval));
    Wire.beginTransmission(0x22);
    Wire.write(0xFF);
    digitalWrite(26,LOW);
    digitalWrite(33,LOW);
    Wire.endTransmission();
    vTaskDelay(pdMS_TO_TICKS(interval+(interval/2)));
    Wire.beginTransmission(0x22);
    Wire.write(0x00);
    digitalWrite(26,HIGH);
    digitalWrite(33,HIGH);
    Wire.endTransmission();
    vTaskDelay(pdMS_TO_TICKS(interval+(interval/2)));
}

void setup() {
  Wire.begin();
  pinMode(33,OUTPUT);
  pinMode(26,OUTPUT);
  digitalWrite(26,HIGH);
  digitalWrite(33,HIGH);
  gpio_set_direction(BUZZER_PIN,GPIO_MODE_INPUT);
  ledcSetup(CHANELL, FREQUENCE, RESOLUTION);
  ledcAttachPin(BUZZER_PIN, CHANELL);
  xTaskCreatePinnedToCore(vTaskAdamsMusic, "vTaskAdamsMusic",10000,NULL,0,NULL,0);
  //xTaskCreatePinnedToCore(vTaskTun, "vTaskTun",10000,NULL,0,NULL,0);
}

void loop() {
  // put your main code here, to run repeatedly:
}

Como pode ser visto, criei uma task para testar (vTaskTun) e uma task que é para tocar a música. Repare que estou utilizando o delete no final da task. Todos os recursos do RTOS utilizados aqui estão descritos nos artigos de ESP32, que você encontra aqui no site, clicando alí em cima no menu ESP32. Clicando aqui do lado esquerdo também dá certo.

No site tem diversos artigos sobre PCF8574, um ótimo expansor de IO. Sugiro esse.

Vídeo

Vou editar o vídeo "Adams Family" agora, logo mais estará no canal DobitAoByteBrasil. Se não é inscrito, entre no nosso canal, se inscreva, deixe seu like e clique no sininho para receber notificações!

Projetos para sua empresa

Manual do Maker é mais do que um blog. Somos uma empresa de serviços, cuja principal atividade é desenvolvimento e implementação de projetos embarcados e IoT. Trabalhamos dentro da legalidade, emitindo nota fiscal de serviço, elaboração de requisitos de sistema e documentação do código. Não importa onde você esteja, montamos a prova de conceito e gravamos vídeo do projeto antes de entregar o código e/ou hardware configurado.

Em parceria com a AFEletrônica, projetamos e produzimos hardware industrial (também com nota), para lhe entregar a melhor experiência para seu projeto. Acesse no link do carrossel, ao início do artigo.

Será um prazer tê-lo como nosso cliente!

Inscreva-se no nosso canal Manual do Maker no YouTube.

Também estamos no Instagram.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.