Buzzer e tom: sair do pisca-pisca — Arduino e IoT — semana 7 do 1o trimestre

Informatica · Conteudo · publicado em 02/10/2026
Semana 7 · Buzzer e tom: sair do pisca-pisca — Material de Apoio Arduino

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 GPIO25 e 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 tone existe 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.

CaracteristicaBuzzer ativoBuzzer passivo
O que tem dentroum oscilador proprionada, so a membrana
Como se usadigitalWrite em HIGH e LOWtone com uma frequência
Altura do somuma so, fixaqualquer uma que você pedir
Custa mais caronãosim
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 tone em outro pino enquanto o primeiro ainda toca. Tocar dois buzzers ao mesmo tempo com tone não funciona.
  • O delay e o que segura a nota. Como o som e gerado em outra tarefa, o unico lugar onde o tempo da nota mora e no delay do seu programa. Remova o delay e a nota muda de frequência sem parar de soar — o professor mostra isso em 10 segundos.

Atividade

Montagem:

  • O pino S do buzzer passivo no GPIO25.
  • O pino + do buzzer passivo na trilha + (5 V) da protoboard.
  • Um jumper ligando o GPIO25 ao pino S.

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.

  1. Ligue o circuito do enunciado e grave o sketch da resolucao.
  2. Troque FREQUENCIA de 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.
  3. Troque de novo, agora para 100. Grave e espere ouvir. Explique em uma frase por que não se ouve nada nessa frequência.
  4. 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.
  5. Troque DURACAO de 400 para 150 e depois para 900. Anote qual das três você prefere e por que.
  6. 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érioPontos
Circuito montado com o pino S no GPIO25 e o + na trilha positiva2 pontos
pinMode declarado no setup antes do primeiro tone2 pontos
Par tone e noTone presentes, e o som realmente para entre as notas3 pontos
Item 3 respondido: a frequência de 100 Hz esta explicada em termos do buzzer1 ponto
Item 6 respondido: o tempo calculado inclui a pausa2 pontos

Erros comuns

ErroComo apareceCorrecao
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 ativoNada 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 notasMelodia 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 buzzerSem 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 for e usar o índice para casar frequência com duracao.
  • Representar uma pausa dentro da tabela, em vez de com um if no meio do loop.
  • 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 S do buzzer passivo no GPIO25.
  • O pino + do buzzer passivo na trilha + (5 V) da protoboard.
  • Um jumper ligando o GPIO25 ao pino S.

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.

  1. Ligue o circuito do enunciado e grave o sketch da resolucao.
  2. 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?
  3. Troque os números 523 e 587 da 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?
  4. Troque a primeira nota de 523 para 440 e conte quantas vezes a primeira nota mudou ao longo do programa. Explique por que o for comeca sempre em zero.
  5. 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.
  6. Acerte o TOTAL_NOTAS e 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érioPontos
As duas tabelas declaradas com const, e com o mesmo número de posicoes2 pontos
TOTAL_NOTAS bate com o número de posicoes da tabela2 pontos
O for usa o mesmo índice nas duas tabelas, sem índice escrito a mao3 pontos
A pausa esta dentro da tabela e tratada por condição, não por delay solto1 ponto
Item 6 respondido: a distorcao entre a distorcao de código e de dado esta explicada2 pontos

Erros comuns

ErroComo apareceCorrecao
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.