Buzzer e tom: sair do pisca-pisca — Arduino e IoT — semana 7 do 1o trimestre
Semana 7 de 13· 1o trimestre · 01/02 a 30/04
Buzzer e tom: sair do pisca-pisca
Sinal sonoro: o buzzer passivo e a função tone.
Aula 1 — Buzzer passivo: tom fixo
Objetivos
- Montar um buzzer passivo no
GPIO25e fazer sair um tom audivel. - Explicar a diferença entre buzzer ativo e buzzer passivo lendo a plaquinha do componente.
- Fazer um tom soar por tempo determinado e cortar o som com
noTone. - Dizer por que
toneexiste no core do ESP32 e o que ela usa por baixo. - Reconhecer no monitor serial que o programa esta rodando mesmo sem o som.
Material
- 1 ESP32 DevKit V1 por aluno, com o cabo USB
- 1 buzzer passivo por aluno (o corpo e o quadrado preto com dois pinos)
- 1 protoboard de 830 pontos e 2 jumpers macho-macho por aluno
- 1 módulo MB102 por dupla
- 1 LED de 3,3 V e 1 resistor de 330 por aluno, para a montagem final da atividade
Conceitos
Buzzer ativo e buzzer passivo
Os dois buzzers do kit produzem som, mas são pecas diferentes.
| Caracteristica | Buzzer ativo | Buzzer passivo |
|---|---|---|
| O que tem dentro | um oscilador proprio | nada, so a membrana |
| Como se usa | digitalWrite em HIGH e LOW | tone com uma frequência |
| Altura do som | uma so, fixa | qualquer uma que você pedir |
| Custa mais caro | não | sim |
| Cabecalho na plaquinha | + e - | +, - e as letras S e O |
O teste pratico que o professor faz na abertura vale mais que a tabela: ligar os dois em HIGH e ouvir. O ativo bipa. O passivo fica mudo, porque sem tone não existe sinal para ele transformar em som.
O "+ e -" e para alimentacao. O "S e O" e para o sinal — S de signal, O de output. Esta aula usa o S.
tone e noTone
tone(PINO_BUZZER, 440); // comeca a tocar 440 Hz delay(400); // deixa soar noTone(PINO_BUZZER); // corta o som
tone recebe o pino e a frequência em hertz. Ela pode ser chamada de novo com outra frequência enquanto o som esta tocando, e o tom muda na hora, sem parar.
noTone e a unica forma de cortar o som. Sem ela, o buzzer continua soando até a placa perder a energia. Esse e o primeiro erro da aula e ele não gera erro de compilacao: o programa funciona, e o som nunca para.
Frequência e nota musical
Toda frequência e uma nota. O 440 do telefone e a nota LA na oitava 4. 262 e o DO na oitava 3, meio tom abaixo. O aluno não precisa saber a tabela de Sharp para fazer a aula — precisa saber que subir o número sobe o som.
A faixa audivel vai de aproximadamente 20 Hz a 20.000 Hz, mas um buzzer pequeno e de qualidade ruim. Entre 200 Hz e 2000 Hz ele responde bem. Abaixo de 100 Hz o aluno acha que o buzzer quebrou, e ele esta certo: o componente não vibra o bastante.
O que tone faz por baixo
O professor abre o arquivo do core e mostra o que tone faz. No ESP32, tone não fica parada girando: ela coloca um pedido numa fila e uma tarefa separada gera o sinal. Duas consequencias que o aluno pode ler no proprio programa:
- Uma so nota por vez. O código do core recusa um segundo
toneem outro pino enquanto o primeiro ainda toca. Tocar dois buzzers ao mesmo tempo comtonenão funciona. - O
delaye o que segura a nota. Como o som e gerado em outra tarefa, o unico lugar onde o tempo da nota mora e nodelaydo seu programa. Remova odelaye a nota muda de frequência sem parar de soar — o professor mostra isso em 10 segundos.
Atividade
Montagem:
- O pino
Sdo buzzer passivo noGPIO25. - O pino
+do buzzer passivo na trilha+(5 V) da protoboard. - Um jumper ligando o
GPIO25ao pinoS.
Nota: 10 pontos. Critério de fim: o monitor serial mostra a contagem de notas subindo enquanto o tom de 440 Hz soa e para a cada ciclo.
- Ligue o circuito do enunciado e grave o sketch da resolucao.
- Troque
FREQUENCIAde 440 para 880, grave e anote qual nota o professor toca no monitor do PC. Descreva em uma frase a relacao entre o número e a altura do som. - Troque de novo, agora para 100. Grave e espere ouvir. Explique em uma frase por que não se ouve nada nessa frequência.
- Comente a linha do
noTone, grave e espere 10 segundos. Descreva o que aconteceu e diga, em uma frase, quem e que manda parar o som em um programa correto. - Troque
DURACAOde 400 para 150 e depois para 900. Anote qual das três você prefere e por que. - No monitor serial, calcule quantos milissegundos de som efetivo existem em 10 notas. O código conta o tempo com a pausa ou sem ela?
Resolucao
#include <Arduino.h> const int PINO_BUZZER = 25; const int FREQUENCIA = 440; const int DURACAO = 400; const int PAUSA = 150; int notasTocadas = 0; void setup() { pinMode(PINO_BUZZER, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("Buzzer passivo — tom fixo de 440 Hz (nota LA4)"); Serial.println("Frequencia | notas tocadas | tempo total"); } void loop() { notasTocadas++; tone(PINO_BUZZER, FREQUENCIA); delay(DURACAO); noTone(PINO_BUZZER); delay(PAUSA); Serial.print(FREQUENCIA); Serial.print(" Hz | "); Serial.print(notasTocadas); Serial.print(" | "); Serial.print(notasTocadas * (DURACAO + PAUSA)); Serial.println(" ms"); }
Por que assim e não de outro jeito. A ordem e tone, delay, noTone, delay. As duas ultimas linhas não são redundantes: a primeira para o som, a segunda segura o silencio. O aluno que escreve tone e delay e chega direto em tone de novo não tem pausa nenhuma — e a melodia sai chapada, sem espaco entre as notas.
O pinMode vem no setup pelo mesmo motivo do dia 2: sem ele o pino fica como entrada e não sai som. Aqui o erro e menos visivel, porque o programa "parece funcionar" — o serial imprime e o professor ve número subindo. O som e que não vem, e o aluno sugere trocar o buzzer.
O tempo impresso no serial e notasTocadas * (DURACAO + PAUSA), e não notasTocadas * DURACAO. Isso responde o item 6 da atividade: o código conta o tempo com a pausa, porque e o tempo que passou de verdade. O aluno que multiplica so por DURACAO esta contando o som e esquecendo o silencio.
Criterios de correcao
| Critério | Pontos |
|---|---|
Circuito montado com o pino S no GPIO25 e o + na trilha positiva | 2 pontos |
pinMode declarado no setup antes do primeiro tone | 2 pontos |
Par tone e noTone presentes, e o som realmente para entre as notas | 3 pontos |
| Item 3 respondido: a frequência de 100 Hz esta explicada em termos do buzzer | 1 ponto |
| Item 6 respondido: o tempo calculado inclui a pausa | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
Esquecer o noTone | "O programa funciona, mas o som fica ligando e não desliga" | "noTone e a unica coisa que corta o som. Sem ela o tom soa até a placa perder energia." |
tone no buzzer ativo | Nada acontece, ou so um clique no início | "O ativo tem oscilador proprio: use digitalWrite. tone so funciona no passivo, porque so o passivo tem membrana para vibrar." |
Usar tone e delay sem noTone entre as notas | Melodia sem espaco, tudo emendado | "Depois do delay da nota, vem noTone e mais um delay. O segundo delay e o silencio entre as notas." |
Fio no O do buzzer | Sem som, e o aluno trocou de pino | "O O e o outro sinal, para entrada. Para saída do ESP32 use o S." |
| Colocar resistor no serie | "O professor pediu resistor" | "No buzzer passivo não vai. O componente já limita a corrente interna. Resistor aqui so reduz o volume e rouba a tensão do sinal." |
| Gravar sem escolher a porta de novo | "Não gravou" depois da troca de kit | "Escolha a porta em Ferramentas > Porta antes de gravar. A porta muda quando você troca o cabo." |
Desafio extra
Faça o mesmo programa com dois buzzers passivos, em pinos diferentes. Escreva em quatro linhas o que aconteceu quando você chamou tone nos dois pinos ao mesmo tempo, e leia o código do core de tone procurando a linha que impede. Qual teria de ser a solução real para dois sons simultâneos em um ESP32?
A resolucao, compilada
// Aula 1 do dia 7: o buzzer passivo e a funcao tone. // // Esta e a primeira saida que NAO e um LED. O aluno descobre que o mesmo pino // digital que acende uma lampada consegue tambem produzir um som, porque o que // importa nao e o que esta ligado no pino: e o sinal que sai dele. #include <Arduino.h> // Pino do buzzer. O 25 e um GPIO comum, sem funcao especial, e o que sobra // quando a placa so tem pinos de uso geral livres na bancada. const int PINO_BUZZER = 25; // Frequencia do tom em Hz. 440 Hz e a nota LA4, a mesma que o telefone da // sua mae usa. Declarar a frequencia como constante e o que permite trocar a // nota sem mexer na logica do programa. const int FREQUENCIA = 440; // Tempo de cada nota, em milissegundos. O nome e duracao e nao intervalo // porque aqui o intervalo e o silencio, que e outra coisa. const int DURACAO = 400; // Espaco entre uma nota e a seguinte, em milissegundos. const int PAUSA = 150; // Contador de notas ja tocadas. Serve para o professor provar que o programa // esta vivo mesmo quando o ouvinte nao distingue um tom de outro. int notasTocadas = 0; void setup() { // OUTPUT tem que vir ANTES do primeiro tone. Esquecido, o pino fica como // entrada e nao sai som nenhum — sem erro de compilacao, como no dia 2. pinMode(PINO_BUZZER, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("Buzzer passivo — tom fixo de 440 Hz (nota LA4)"); Serial.println("Frequencia | notas tocadas | tempo total"); Serial.println("O buzzer ATIVO so liga e desliga: ele nao aceita tone."); Serial.println(); } void loop() { notasTocadas++; // Liga o som. O terceiro argumento, se fosse escrito, seria a duracao em // milissegundos — mas deixamos de fora de proposito: quem corta o som e o // noTone, e nao a funcao. Isso mostra o par completo de tone e noTone. tone(PINO_BUZZER, FREQUENCIA); delay(DURACAO); // Corta o som. Sem esta linha, o tom continua soando ate o fim do dia: e o // erro numero um desta aula, porque o programa "funciona" e nao para. noTone(PINO_BUZZER); delay(PAUSA); Serial.print(FREQUENCIA); Serial.print(" Hz | "); Serial.print(notasTocadas); Serial.print(" | "); Serial.print(notasTocadas * (DURACAO + PAUSA)); Serial.println(" ms"); }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia07 aula1.
Aula 2 — Sequência de tons: tabela de notas e melodia
Objetivos
- Transformar uma sequência de notas em um par de tabelas de dados, e não em um código repetido.
- Percorrer as duas tabelas com um
fore usar o índice para casar frequência com duracao. - Representar uma pausa dentro da tabela, em vez de com um
ifno meio doloop. - Explicar em uma frase por que a tabela e melhor que a repeticao de
tone. - Ler no monitor serial uma saída que ensina a ordem das notas sem depender do ouvido.
Material
- 1 ESP32 DevKit V1 por aluno, com o cabo USB
- 1 buzzer passivo por aluno (o corpo e o quadrado preto com dois pinos)
- 1 protoboard de 830 pontos e 2 jumpers macho-macho por aluno
- 1 módulo MB102 por dupla
- Folha de caderno do aluno, para anotar a ordem das notas
Conceitos
De código repetido para dado
A melodia da aula 1 escrita do jeito obvio:
tone(PINO_BUZZER, 523); delay(300); tone(PINO_BUZZER, 587); delay(300); tone(PINO_BUZZER, 659); delay(300); tone(PINO_BUZZER, 587); delay(300); // ... e mais quatorze linhas igual a esta
O problema não e o tamanho. E que para trocar duas notas de lugar, o aluno tem que reescrever o código inteiro, e qualquer erro de digito vira uma nota errada em vez de um erro visivel de compilacao.
Quando a nota vira dado — um número dentro de um conjunto — trocar duas notas e trocar dois números no mesmo lugar do texto. E a diferença entre um programa que se edita e um programa que se reescreve.
As duas tabelas e o índice
A melodia e descrita por dois conjuntos do mesmo tamanho:
const int FREQUENCIAS[] = {523, 587, 659, 587, 440, ...}; const int DURACOES[] = {300, 300, 300, 300, 300, ...};
O índice i e o que amarra os dois. FREQUENCIAS[i] e a nota; DURACOES[i] e quanto tempo ela dura. Como o for sobe de zero até o total, as duas tabelas andam sempre juntas.
O for inteiro, e a parte que o aluno precisa entender de verdade:
for (int i = 0; i < TOTAL_NOTAS; i++) { int hz = FREQUENCIAS[i]; int duracao = DURACOES[i]; // toca hz durante duracao }
O TOTAL_NOTAS tem que ser o número de posicoes da tabela. Este e o erro que mais acontece na aula: acrescentar uma nota no fim da tabela e esquecer de atualizar o total. O for para antes da ultima e a musica fica sem a nota final, sem erro nenhum.
A pausa como valor
Uma pausa e uma nota sem frequência. Como 0 não e nenhuma frequência audivel, escrever 0 na tabela significa silencio com duracao:
if (hz == SEM_SOM) { noTone(PINO_BUZZER); } else { tone(PINO_BUZZER, hz); }
A alternativa seria quebrar a tabela em duas e meter um delay grande no meio, mas ai a pausa fica fora da melodia e o for deixa de descrever a musica inteira. Com a pausa dentro da tabela, o for continua sendo a partitura completa.
Ordem e repeticao
A tabela não e um dicionario de consulta. Ela e a frase executada de cima para baixo, e a ordem importa tanto quanto o valor. Trocar 523 e 659 de lugar muda a musica.
Esta distincao entre tabela que consulta e tabela que executa reaparece no dia 11, quando o display recebe quatro valores em quatro posicoes fixas.
Atividade
Montagem:
- O pino
Sdo buzzer passivo noGPIO25. - O pino
+do buzzer passivo na trilha+(5 V) da protoboard. - Um jumper ligando o
GPIO25ao pinoS.
Nota: 10 pontos. Critério de fim: o monitor serial mostra a sequência de notas, com uma linha para cada posicao da tabela, e o som repete a mesma ordem.
- Ligue o circuito do enunciado e grave o sketch da resolucao.
- Escreva no caderno, antes de ouvir, a ordem das 16 notas da tabela, incluindo a pausa. Depois ouve e confira. Quantas posicoes existem na tabela de frequencias e quantas na de duracoes?
- Troque os números
523e587da primeira posicao e ouve de novo. Em uma frase: o que mudou na musica, e por que isso e uma vantagem de guardar a nota como dado e não como código? - Troque a primeira nota de 523 para 440 e conte quantas vezes a primeira nota mudou ao longo do programa. Explique por que o
forcomeca sempre em zero. - Acrescente uma nota nova ao final da tabela de frequencias, mas não mude o
TOTAL_NOTAS. Grave e ouve. Descreva o que aconteceu com a sua nota. - Acerte o
TOTAL_NOTASe diga, em uma frase, qual erro da atividade 5 teria sido detectado pelo compilador e qual não foi.
Resolucao
#include <Arduino.h> const int PINO_BUZZER = 25; const int SEM_SOM = 0; const int FREQUENCIAS[] = { 523, 587, 659, 587, 440, 523, 587, 523, 0, 659, 523, 784, 659, 523, 587, 659, 392, }; const int DURACOES[] = { 300, 300, 300, 300, 300, 300, 300, 300, 400, 300, 300, 400, 300, 300, 300, 400, 700, }; const int TOTAL_NOTAS = 16; int volta = 0; void setup() { pinMode(PINO_BUZZER, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("Melodia com tabela de notas — dia 7, aula 2"); Serial.println("nota | Hz | duracao"); } void loop() { volta++; for (int i = 0; i < TOTAL_NOTAS; i++) { int hz = FREQUENCIAS[i]; int duracao = DURACOES[i]; if (hz == SEM_SOM) { Serial.print("pausa | 0 | "); Serial.print(duracao); Serial.println(" ms"); noTone(PINO_BUZZER); } else { tone(PINO_BUZZER, hz); Serial.print(" "); if (hz < 1000) { Serial.print(" "); } Serial.print(hz); Serial.print(" | "); Serial.print(duracao); Serial.println(" ms"); } delay(duracao); } noTone(PINO_BUZZER); Serial.println(); Serial.print("volta "); Serial.print(volta); Serial.println(" — recomecando"); delay(600); }
Por que assim e não de outro jeito. O delay(duracao) fica depois do if, e não dentro de cada ramo. Se ele estivesse dentro dos dois ramos, a pausa e a nota teriam cada uma a sua copia do delay, e qualquer mudanca no tempo da nota exigiria mudar dois lugares. Com um delay so, o for continua sendo a partitura inteira: uma linha de tempo por volta, exatamente como na partitura de musica.
O noTone fora do for e o que garante que a ultima nota — que dura 700 ms, a mais longa da tabela — não fique soando durante os 600 ms de espera entre uma volta e outra. Sem ele a melodia "encavalga": a ultima nota ainda esta no ar quando a primeira recomeça.
O if (hz < 1000) dentro do Serial.print existe so para o número sair alinhado no monitor. O professor pede para o aluno ler em voz alta a coluna: e mais facil achar um erro de digito na tabela quando as colunas batem. Isso e instrumentacao, não lógica.
O if da pausa testa hz == SEM_SOM e não hz == 0 porque o 0 e um valor sem significado acustico e merece um nome. Compare com TOTAL_NOTAS, que também e um número solto: ali o nome ajuda o leitor a lembrar que e um total e não um índice.
Criterios de correcao
| Critério | Pontos |
|---|---|
As duas tabelas declaradas com const, e com o mesmo número de posicoes | 2 pontos |
TOTAL_NOTAS bate com o número de posicoes da tabela | 2 pontos |
O for usa o mesmo índice nas duas tabelas, sem índice escrito a mao | 3 pontos |
A pausa esta dentro da tabela e tratada por condição, não por delay solto | 1 ponto |
| Item 6 respondido: a distorcao entre a distorcao de código e de dado esta explicada | 2 pontos |
Erros comuns
| Erro | Como aparece | Correcao |
|---|---|---|
TOTAL_NOTAS menor que a tabela | "A ultima nota não toca" | "Conte as posicoes da tabela e ajuste o total. O for para no total, então a nota que ficou fora nunca e lida." |
TOTAL_NOTAS maior que a tabela | "Deu número aleatorio e travou o som" | "O for leu posicao que não existe. A tabela tem 16; o total tem que ser 16." |
| Duas tabelas de tamanhos diferentes | "As notas saem com o tempo da nota anterior" | "DURACOES[i] casa com FREQUENCIAS[i]. Se uma tabela tem 15 e a outra 16, a posicao 15 usa lixo de memória." |
Pausa como delay grande no meio do for | "A pausa some quando eu mudo a ordem das notas" | "Pause com 0 na tabela. Assim o for continua sendo a partitura inteira e a pausa viaja com a nota." |
delay(duracao) repetido dentro dos dois ramos do if | "Mudar a duracao so muda a nota, não a pausa" | "Ponha um delay(duracao) so, depois do if. Vale para os dois casos." |
Recomecar o loop sem noTone no fim | "A ultima nota fica presa no fim da musica" | "Corte o som depois do for, antes do delay entre voltas." |
Número de nota escrito direto no Serial.print | "O monitor mostra sempre a mesma nota, mas a musica muda" | "Imprima hz, a variável do laco. Número solto no print e número falso no monitor." |
Desafio extra
Escreva a tabela de uma musica que a turma conheca, com as frequencias e as duracoes, e faca o programa tocar. Depois escreva duas tabelas de dois instrumentos diferentes para a mesma melodia, com timbres distintos. Em quatro linhas: como uma unica estrutura de dados poderia guardar as duas vozes ao mesmo tempo, e qual campo a mais cada tabela precisaria ter?
>A resolucao, compilada
// Aula 2 do dia 7: a tabela de notas vira melodia. // // O aluno ate aqui escrevia a nota repetida no codigo. Hoje a nota vira DADO: // um conjunto de frequencias e um conjunto de duracoes. O parouco de codigo que // toca a melodia e o mesmo que toca uma nota so — muda o que esta na tabela. #include <Arduino.h> // Pino do buzzer, igual a aula 1. const int PINO_BUZZER = 25; // Silencio no fim da melodia. Escrever 0 na frequencia e o jeito de falar // "esta posicao e uma pausa", e por isso que a tabela tem frequencia E duracao. const int SEM_SOM = 0; // Frequencia de cada nota, em Hz. A ordem e a ordem da melodia: e uma frase // que se lê de cima para baixo. Uma tabela de notas nao e um dicionario de // consulta, e a propria musica. const int FREQUENCIAS[] = { 523, 587, 659, 587, // dó ré mi ré 440, 523, 587, 523, // lá dó ré dó 0, // pausa de um tempo 659, 523, 784, 659, // mi dó sol mi 523, 587, 659, // dó ré mi 392, // sol grave, para fechar }; // Duracao de cada nota, em milissegundos. Mesma quantidade de posicoes que a // tabela de frequencias: o indice e o que amarra as duas. const int DURACOES[] = { 300, 300, 300, 300, 300, 300, 300, 300, 400, 300, 300, 400, 300, 300, 300, 400, 700, }; // Quantas notas tem a melodia. Declarar o total evita o erro classico de // contar a posicao errada quando alguem acrescenta uma nota no fim. const int TOTAL_NOTAS = 16; int volta = 0; void setup() { pinMode(PINO_BUZZER, OUTPUT); Serial.begin(115200); Serial.println(); Serial.println("Melodia com tabela de notas — dia 7, aula 2"); Serial.println("nota | Hz | duracao"); Serial.println(); } void loop() { volta++; for (int i = 0; i < TOTAL_NOTAS; i++) { int hz = FREQUENCIAS[i]; int duracao = DURACOES[i]; if (hz == SEM_SOM) { // Pausa: o silencio e uma escolha, entao o monitor precisa mostrar que // houve uma. Sem este else, o aluno nao sabe por que o som "pulou". Serial.print("pausa | 0 | "); Serial.print(duracao); Serial.println(" ms"); noTone(PINO_BUZZER); } else { tone(PINO_BUZZER, hz); Serial.print(" "); if (hz < 1000) { Serial.print(" "); } Serial.print(hz); Serial.print(" | "); Serial.print(duracao); Serial.println(" ms"); } // O sono acontece DEPOIS do print e nao antes: se a placa dormir, o // professor nao ve a nota que o aluno tocou. delay(duracao); } // Fim da melodia: corta o som. O noTone fora do laco e o que garante que a // ultima nota, longa, nao fique soando ate a proxima volta. noTone(PINO_BUZZER); Serial.println(); Serial.print("volta "); Serial.print(volta); Serial.println(" — recomecando"); Serial.println(); delay(600); }
Sem saída de compilação gravada. Rode python3 validar.py -t 1 dia07 aula2.
