Tutoriais

Sistema Musical Modular para Arduino e ESP32

Eletrogate 13 de fevereiro de 2026

Introdução

A música pode ser compreendida como a organização de alturas e durações ao longo do tempo. Quando esses mesmos elementos são traduzidos para um ambiente computacional, surgem novas possibilidades de criação, experimentação e aprendizado.
Neste contexto, desenvolver melodias em código deixa de ser apenas uma forma de gerar sons e passa a ser um exercício de lógica musical aplicada. Ao estruturar notas, ritmos e silêncios de maneira organizada, é possível criar melodias elaboradas, personalizadas e reproduzíveis, utilizando apenas software e componentes simples.
Este artigo parte dessa relação entre música e programação para apresentar uma técnica de composição de melodias em microcontroladores, baseada na representação explícita da escala cromática, das figuras rítmicas e do andamento musical. A proposta é mostrar como conceitos tradicionais da escrita musical podem ser adaptados para o código, criando uma base clara e extensível para o desenvolvimento de sistemas sonoros embarcados.

Arquitetura Geral do Projeto

O sistema apresentado neste artigo foi estruturado a partir de uma arquitetura em camadas, visando separar claramente as responsabilidades do código e facilitar tanto o processo de composição das melodias quanto sua reutilização em diferentes projetos e hardwares.
De forma geral, o projeto é dividido em três versões complementares, que compartilham a mesma lógica musical, mas diferem no nível de abstração e na forma de geração do sinal sonoro:
Versão Engine (núcleo musical)
A versão engine representa o núcleo conceitual do projeto. Nela, são definidas as frequências das notas musicais, as figuras rítmicas baseadas no andamento (BPM) e as funções responsáveis por calcular a duração de cada evento sonoro.
Versão Clean e Modular (biblioteca reutilizável)
A partir da engine, o código é reorganizado em uma versão clean, estruturada como um módulo ou biblioteca. Nessa abordagem, os detalhes internos de cálculo de tempo e geração de som ficam encapsulados, enquanto o projeto principal passa a apenas “orquestrar” a execução das melodias.
Versão sem PWM por hardware (geração por software)
A terceira variação do projeto atende a cenários em que o microcontrolador não possui suporte adequado a PWM por hardware no pino desejado, ou quando o buzzer já está integrado à placa.
Nessa versão, a geração da frequência sonora é feita por meio de software, simulando uma onda quadrada com controle preciso de tempo.
Visão Geral da Arquitetura
De forma resumida, a arquitetura do projeto pode ser entendida como um fluxo progressivo:
Conceito musical → Engine → Módulo reutilizável → Aplicação específica de hardware
Essa separação permite que o leitor compreenda o sistema em diferentes níveis, desde a relação entre música e código até sua aplicação prática em microcontroladores distintos, sem perder clareza ou controle sobre o som gerado.

Linguagem Musical

Antes de entrar nos detalhes de implementação em hardware, é importante compreender como conceitos básicos da linguagem musical foram traduzidos para estruturas de código neste projeto. Essa etapa é fundamental para que o leitor consiga compor melodias próprias, e não apenas reproduzir exemplos prontos.
Notas musicais e frequências
No sistema proposto, cada nota musical é representada por sua frequência em Hertz (Hz), definida no código por meio de constantes #define. Esse método cria uma correspondência direta entre a notação musical tradicional e a escrita em código, facilitando tanto a leitura quanto a manutenção do programa.
No código engine, todas as notas da escala cromática dentro da tessitura utilizada no projeto são mapeadas, por exemplo:

img1

Essa abordagem permite que o compositor pense musicalmente — escolhendo notas da escala — enquanto escreve código de forma clara e legível, sem a necessidade de trabalhar diretamente com valores numéricos de frequência a cada melodia.
Além das notas, o valor especial REST (definido como 0) representa os silêncios musicais, que são tratados com a mesma importância que as notas, possuindo duração própria e papel fundamental na articulação rítmica.
Duração das notas e figuras rítmicas
A organização rítmica do sistema é baseada no conceito de figuras musicais relativas ao andamento (BPM). No código engine, as durações não são escritas diretamente em milissegundos, mas sim como valores simbólicos que representam figuras rítmicas, interpretadas por uma função de cálculo.
Musicalmente, isso significa que o compositor escreve ritmo, e não tempo absoluto. A duração real de cada nota será determinada automaticamente a partir do BPM definido no início do código.
Essa lógica aproxima o processo de composição em código da escrita musical tradicional, onde as figuras rítmicas se ajustam conforme o andamento da peça.

img2

Considerações musicais

Ao estruturar o projeto dessa forma, o código deixa de ser apenas uma sequência de comandos e passa a representar uma partitura executável, onde notas, silêncios e ritmos possuem significado musical explícito. Essa abordagem facilita tanto o aprendizado quanto a expansão do sistema para novas melodias, variações rítmicas e experimentações sonoras.

Lista de Materiais

Projeto em Arduino (saída de 5 V)
Projeto em ESP32 (saída 3,3 V com transistor)
Observação: No caso do ESP32, o uso do transistor é necessário para permitir o acionamento correto do buzzer passivo de 5 V, garantindo volume adequado e protegendo os pinos de saída do microcontrolador.

Desenvolvimento

Versão Engine – Composição e testes musicais

A versão engine do projeto foi desenvolvida como um ambiente de testes e composição, onde o foco principal está na clareza musical do código. Nessa etapa, o objetivo não é otimizar ou modularizar ao máximo, mas permitir que melodias sejam escritas, ajustadas e testadas de forma rápida e intuitiva.
Essa versão concentra todas as definições musicais em um único arquivo, incluindo:
  • Mapeamento completo das notas da escala cromática por frequência;
  • Definição do andamento global (BPM);
  • Funções responsáveis pelo cálculo das durações rítmicas;
  • Execução sequencial das melodias.
Essa abordagem facilita o processo criativo, permitindo que o compositor altere notas, ritmos e andamento sem a necessidade de modificar a lógica principal do programa.
Definição do andamento (BPM)
O andamento da música é definido por meio de uma variável global:

img1

Esse valor controla toda a lógica rítmica do sistema. A partir dele, a função responsável pelo cálculo da duração da semínima converte o BPM em tempo real, expresso em milissegundos:

img2

Dessa forma, qualquer alteração no BPM impacta automaticamente todas as figuras rítmicas da melodia, mantendo coerência musical.
Cálculo das figuras rítmicas
As figuras rítmicas são representadas por valores simbólicos (1, 2, 3, 4, 8, 16), interpretados por uma função baseada em switch-case. Essa função traduz cada figura musical em sua duração correspondente, sempre em relação à semínima:

img3

Esse modelo permite que o ritmo seja escrito de forma musical, sem a necessidade de trabalhar diretamente com valores absolutos de tempo.
Organização das melodias
Cada melodia é organizada em dois arrays paralelos:
  • um array contendo as notas;
  • um array contendo as durações correspondentes.
Essa separação reflete diretamente a lógica de uma partitura tradicional, onde altura e duração são informações independentes, mas sincronizadas no tempo.

img4

A execução ocorre por meio de uma função genérica, capaz de tocar qualquer melodia que siga esse padrão:

img5

Isso permite reutilizar a mesma lógica de execução para diferentes músicas, mantendo o código organizado e fácil de expandir.
Articulação e separação das notas
Dentro da função tocarMelodia(), após o acionamento da nota por meio da função tone(), duas linhas desempenham um papel fundamental para a clareza musical da execução, embora simples, essas instruções são responsáveis por definir como uma nota termina antes da próxima começar, algo diretamente relacionado ao conceito musical de articulação.
delay(duracao * 1.1): Ao utilizar um pequeno acréscimo no tempo de espera (duracao * 1.1), o código cria uma micropausa natural entre as notas, permitindo que cada frequência seja claramente percebida antes da próxima. Musicalmente, isso se aproxima de uma articulação non legato, muito comum em melodias tocadas em instrumentos de sopro, cordas dedilhadas ou teclados.
noTone(): A chamada da função noTone(buzzer) garante que o sinal sonoro seja efetivamente interrompido antes da próxima nota. Isso é especialmente importante em:
  • Melodias rápidas;
  • Sequências cromáticas;
  • Testes de frequência;
  • Execução em buzzers passivos.
Sem essa interrupção explícita, o buzzer poderia manter vibração residual, prejudicando a definição das notas e a percepção do ritmo.
Função da versão Engine no projeto
A versão engine cumpre um papel fundamental no projeto como um todo: ela serve como base conceitual e musical para as demais versões. É nela que as melodias são compostas, ajustadas e testadas antes de serem transportadas para a versão clean ou para aplicações específicas. Essa separação garante que o processo criativo musical permaneça claro e desacoplado das restrições de hardware ou arquitetura de software embarcado.

Versão Clean – Execução modular de melodias

Após a etapa de composição e testes realizada na versão engine, o projeto avança para uma abordagem mais organizada e reutilizável: a versão clean.
Nesta etapa, o foco deixa de ser a experimentação musical e passa a ser a integração prática em outros projetos, mantendo clareza, simplicidade e portabilidade do código.
A versão clean foi desenvolvida no formato de módulo, separando a lógica sonora em arquivos próprios (melodias.h e melodias.cpp). Essa organização permite que o sistema de reprodução de sons seja facilmente importado para qualquer projeto Arduino ou ESP32, sem a necessidade de reescrever ou adaptar o código principal.
Organização modular do código
Diferentemente da versão engine, onde todas as definições musicais e lógicas estão concentradas em um único arquivo, a versão clean adota uma organização modular do código, separando responsabilidades de forma clara. A estrutura do projeto é dividida em três partes principais:
  • Arquivo de cabeçalho (.h): contém as definições, constantes e os protótipos das funções utilizadas pelo módulo de melodias.
  • Arquivo de implementação (.cpp): concentra a lógica sonora, incluindo o acionamento do buzzer e o controle de frequência e duração das notas.
  • Arquivo principal (.ino): responsável apenas por inicializar o sistema e orquestrar a execução das melodias, sem conter detalhes internos de implementação.
Essa organização segue boas práticas de programação em sistemas embarcados, facilitando a leitura do código, a manutenção e a reutilização do módulo de melodias em outros projetos.

modulo_ide

Execução direta das notas com tone()
O núcleo da versão clean está na utilização direta da função:
[tone(pino, frequencia, duracao);]

Nessa abordagem, cada nota é executada a partir de dois parâmetros fundamentais da linguagem musical:

  • frequência (Hz): define a altura da nota
  • duração (tempo): define quanto tempo essa nota será sustentada
A correspondência entre notas musicais e frequências permanece a mesma utilizada na versão engine, garantindo consistência musical entre as versões.
Essa estratégia é ideal para projetos que precisam apenas executar sons ou melodias específicas, como alarmes, feedback sonoro, jogos, dispositivos interativos ou sistemas embarcados simples.
Relação entre BPM e duração das notas
Embora a versão clean não utilize diretamente o sistema simbólico de figuras rítmicas do engine, é possível manter coerência musical calculando as durações a partir do BPM.
A base desse cálculo é a semínima:
[duração da semínima (ms) = 60000 / BPM]

A partir desse valor, as demais figuras podem ser obtidas por proporção matemática, mantendo relação direta com a escrita musical tradicional.

exemplo_figuras

Esses valores podem ser utilizados diretamente como parâmetro de duração na função tone(), permitindo controle rítmico preciso mesmo em sistemas mais simples.
Articulação e interrupção do som
Assim como na versão engine, a versão clean também se beneficia da separação clara entre as notas. Após o tempo de execução da nota, a função noTone() é utilizada para interromper o sinal sonoro antes da próxima execução. Essa prática melhora significativamente a definição das notas, especialmente em:
  • Sequências rápidas;
  • Melodias com notas repetidas;
  • Testes de frequência;
  • Uso de buzzers passivos.
Do ponto de vista musical, essa interrupção contribui para uma articulação mais clara e inteligível.
Função da versão Clean no projeto
A versão clean representa o ponto de equilíbrio entre clareza musical e aplicação prática. Ela permite que melodias compostas na versão engine sejam facilmente transportadas para outros projetos, mantendo fidelidade rítmica e melódica, mas com uma estrutura de código mais profissional e reutilizável. 
Essa abordagem torna o sistema especialmente adequado para aplicações embarcadas reais, onde organização, simplicidade e confiabilidade são fundamentais.

Versão sem PWM por hardware – Geração de som por software

Em alguns cenários de projeto, o microcontrolador não dispõe de PWM por hardware no pino desejado, ou o buzzer já está integrado à placa, sem possibilidade de escolha do pino de saída. Nessas situações, o uso direto da função tone() pode não estar disponível ou não oferecer o controle necessário. 
Para resolver esse problema, o projeto inclui uma versão alternativa baseada na geração de sinal sonoro por software. Essa abordagem não depende de periféricos dedicados de PWM e permite gerar frequências audíveis mesmo em pinos digitais comuns.
O som como fenômeno físico e eletrônico
Do ponto de vista físico, o som percebido pelo ouvido humano é resultado de variações periódicas de pressão no ar. Em sistemas eletrônicos simples, como buzzers passivos, essas variações são produzidas quando o elemento vibratório é excitado por um sinal elétrico alternado. No contexto do microcontrolador, não é necessário gerar uma onda senoidal perfeita para produzir uma nota musical reconhecível.
Uma onda quadrada, alternando rapidamente entre nível alto e nível baixo, já é suficiente para excitar o buzzer e produzir um som com frequência bem definida. A frequência percebida pelo ouvido corresponde diretamente ao número de ciclos completos dessa onda por segundo, medido em Hertz (Hz).
Geração de onda quadrada por software
Na ausência de PWM por hardware, a onda quadrada pode ser simulada alternando manualmente o estado do pino digital:
  • Nível HIGH;
  • Nível LOW;
  • Repetição periódica.
O tempo que o pino permanece em cada estado define o período da onda, e consequentemente a frequência do som gerado.
A relação fundamental é:
  • Período (T) = 1 / frequência
  • Meio período = T / 2
Em termos práticos, isso significa que, para gerar uma nota de determinada frequência, o código alterna o pino entre HIGH e LOW, aguardando metade do período entre cada transição.
Essa técnica permite gerar frequências precisas mesmo sem suporte a PWM por hardware.
Relação com a percepção sonora
Embora a onda quadrada possua um conteúdo harmônico mais rico do que uma onda senoidal, o ouvido humano identifica principalmente a frequência fundamental. Por esse motivo, notas musicais geradas dessa forma permanecem afinadas e claramente reconhecíveis, especialmente em buzzers passivos.
Essa característica torna a técnica particularmente adequada para:
  • Testes de frequência
  • Escalas cromáticas
  • Melodias simples
  • Feedback sonoro em sistemas embarcados
Importância da temporização precisa
Como toda a geração do sinal é feita por software, a precisão do som depende diretamente da temporização utilizada. Pequenas variações nos atrasos podem resultar em desafinação perceptível, especialmente em notas mais agudas. Por esse motivo, essa versão do projeto utiliza cálculos diretos baseados na frequência desejada, garantindo que cada ciclo da onda quadrada seja gerado com o período correto.
Essa abordagem demonstra como conceitos de tempo, frequência e percepção auditiva se conectam diretamente no desenvolvimento de sistemas embarcados sonoros.
Função dessa versão no projeto
A versão sem PWM por hardware cumpre um papel complementar às demais: ela demonstra que a geração de som não depende exclusivamente de recursos avançados do microcontrolador, mas pode ser implementada a partir de princípios fundamentais de eletrônica e programação. 
Além de ampliar a compatibilidade com diferentes placas e cenários, essa abordagem reforça o entendimento do som como resultado direto de fenômenos físicos controlados por software, fechando o ciclo entre música, eletrônica e sistemas embarcados.

Diagramas do Circuito

Arduino Uno

diagrama1

ESP32

diagrama2


Códigos Usados no Projeto

Código Engine

// Melodias — Engine

int bpm = 180; // Andamento da melodia


#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))

#define buzzer 11

// Definição das notas

#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311   
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415   
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define REST      0

// Melodias

// Melodia 1 - O Guarani
int melodia1[] = {
  NOTE_E5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5,
  NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5,
  NOTE_A5, NOTE_A5, NOTE_C6,
  NOTE_B5, NOTE_C6, NOTE_B5, NOTE_A5, NOTE_G5, NOTE_A5,
  NOTE_B5, NOTE_G5
};
int duracoes1[] = {
  2, 4, 8, 8, 2,
  4, 8, 8, 2,
  4, 4, 4, 
  8, 8, 4, 8, 8, 4,
  4, 4
};

// Melodia 2 - Missão Impossível
int melodia2[] = {
  NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5,
  NOTE_A4, NOTE_A4, NOTE_G4, NOTE_GS4,

  NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5,
  NOTE_A4, NOTE_A4, NOTE_G4, NOTE_GS4,

  NOTE_C5, NOTE_A4, NOTE_E4,
  NOTE_C5, NOTE_A4, NOTE_DS4,

  NOTE_C5, NOTE_A4, NOTE_D4,
  NOTE_C4, NOTE_D4,
};
int duracoes2[] = {
  3, 3, 4, 4,
  3, 3, 4, 4,

  3, 3, 4, 4,
  3, 3, 4, 4,

  8, 8, 1,
  8, 8, 1,
  8, 8, 1,

  8,3
};

// Melodia 3 — Imperial March 
int melodia3[] = {
  NOTE_A4, NOTE_A4, NOTE_A4,
  NOTE_F4, NOTE_C5, NOTE_A4,
  NOTE_F4, NOTE_C5, NOTE_A4, REST,
  
  NOTE_E5, NOTE_E5, NOTE_E5,
  NOTE_F5, NOTE_C5, NOTE_A4,
  NOTE_F4, NOTE_C5, NOTE_A4
};
int duracoes3[] = {
  2, 2, 2,
  3, 8, 2,
  3, 8, 2, 2,

  2, 2, 2,
  3, 8, 2,
  3, 8, 2
};

// Melodia 4 — Nokia Theme
int melodia4[] = {
  NOTE_G5, NOTE_F5, NOTE_A4, NOTE_B4,
  NOTE_E5, NOTE_D5, NOTE_F4, NOTE_G4,
  NOTE_D5, NOTE_C5, NOTE_E4, NOTE_G4, NOTE_C5
};
int duracoes4[] = {
  8, 8, 4, 4,
  8, 8, 4, 4,
  8, 8, 4, 4, 4
};

// Melodia 5 — Escala Cromática Ascendente
int melodia5[] = {
  NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4,
  NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4,
  NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4,

  NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5,
  NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5,
  NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5,

  NOTE_C6, REST
};
int duracoes5[] = {
  16, 16, 16, 16,
  16, 16, 16, 16,
  16, 16, 16, 16,

  16, 16, 16, 16,
  16, 16, 16, 16,
  16, 16, 16, 16,

  16, 16
};

// Melodia 6 — Escala Cromática Descendente
int melodia6[] = {
  NOTE_C6,

  NOTE_B5, NOTE_AS5, NOTE_A5, NOTE_GS5,
  NOTE_G5, NOTE_FS5, NOTE_F5, NOTE_E5,
  NOTE_DS5, NOTE_D5, NOTE_CS5, NOTE_C5,

  NOTE_B4, NOTE_AS4, NOTE_A4, NOTE_GS4,
  NOTE_G4, NOTE_FS4, NOTE_F4, NOTE_E4,
  NOTE_DS4, NOTE_D4, NOTE_CS4, NOTE_C4, REST
};
int duracoes6[] = {
  16,

  16, 16, 16, 16,
  16, 16, 16, 16,
  16, 16, 16, 16,

  16, 16, 16, 16,
  16, 16, 16, 16,
  16, 16, 16, 16, 16
};

// ----------------------------------------  Setup

void setup() {
  pinMode(buzzer, OUTPUT);
  Serial.begin(9600);
  Serial.println("Melodias Engine");
  delay(1000);
}

// Funções musicais

int tempoSeminima() {
  return 60000 / bpm;
}

int calcularDuracao(int figura) {
  int base = tempoSeminima();

  switch (figura) {
    case 16: return base / 4;       // semicolcheia
    case 8: return base / 2;        // colcheia
    case 4: return base;            // semínima
    case 3: return base + base / 2; // semínima pontuada
    case 2: return base * 2;        // mínima
    case 1: return base * 3;        // mínima pontuada
  }
  return base;
}

// Função de execução

void tocarMelodia(const int* melodia,  const int* duracoes,  int totalNotas) {
  Serial.println("Tocando...");

  for (int i = 0; i < totalNotas; i++) {

    int duracao = calcularDuracao(duracoes[i]);

    if (melodia[i] != REST) {
      tone(buzzer, melodia[i], duracao);
    }

    delay(duracao * 1.1); // articulação
    noTone(buzzer);
  }
}

// ----------------------------------------  Loop
void loop() {

Serial.println("debug -> Melodia 1");
tocarMelodia(
  melodia1,
  duracoes1,
  ARRAY_LEN(melodia1)
      );
 delay(1000);
 Serial.println("debug -> Melodia 2");
tocarMelodia(
  melodia2,
  duracoes2,
  ARRAY_LEN(melodia2)
      );
 delay(1000);
 Serial.println("debug -> Melodia 3");
tocarMelodia(
  melodia3,
  duracoes3,
  ARRAY_LEN(melodia3)
      );
 delay(1000);
 Serial.println("debug -> Melodia 4");
tocarMelodia(
  melodia4,
  duracoes4,
  ARRAY_LEN(melodia4)
      );
 delay(1000);
 Serial.println("debug -> Melodia 5 (Cromática)");
tocarMelodia(
  melodia5,
  duracoes5,
  ARRAY_LEN(melodia5)
);
delay(1000);
Serial.println("debug -> Melodia 6 (Cromática Descendente)");
tocarMelodia(
  melodia6,
  duracoes6,
  ARRAY_LEN(melodia6)
);
delay(1000);
}

Código Clean

#include "melodias.h"

#define BUZZER_PIN 11

void setup() {
  Serial.begin(9600);
  Serial.println("Melodias Clean");
  delay(1000)

  melodias_init(BUZZER_PIN);
}

void loop() {
  delay(3000);
  Serial.println("Debug melodia 1");
  melodia1();

  delay(1000);
  Serial.println("Debug melodia 2");
  melodia2();

  delay(1000);
  Serial.println("Debug melodia 3");
  melodia3();

  delay(1000);
  Serial.println("Debug melodia 4");
  melodia4();

  delay(1000);
}

melodias.h

#ifndef MELODIAS_H
#define MELODIAS_H

#include <Arduino.h>

// inicialização do módulo
void melodias_init(uint8_t pin);

// melodias disponíveis
void melodia1();
void melodia2();
void melodia3();
void melodia4();

#endif

melodias.cpp

#include "melodias.h"

static uint8_t buzzer_pin;  // visível apenas neste arquivo

void melodias_init(uint8_t pin) {
  buzzer_pin = pin;
  pinMode(buzzer_pin, OUTPUT);
}

/* ================= MELODIA 1 ================= */

void melodia1() {
  tone(buzzer_pin, 659, 667); delay(667); delay(40);
  tone(buzzer_pin, 880, 333); delay(333); delay(40);
  tone(buzzer_pin, 784, 167); delay(167); delay(40);
  tone(buzzer_pin, 698, 167); delay(167); delay(40);
  tone(buzzer_pin, 659, 667); delay(667); delay(80);

  tone(buzzer_pin, 880, 333); delay(333); delay(40);
  tone(buzzer_pin, 784, 167); delay(167); delay(40);
  tone(buzzer_pin, 698, 167); delay(167); delay(40);
  tone(buzzer_pin, 659, 667); delay(667); delay(80);

  tone(buzzer_pin, 880, 333); delay(333); delay(40);
  tone(buzzer_pin, 880, 333); delay(333); delay(40);
  tone(buzzer_pin, 1047, 333); delay(333); delay(80);

  tone(buzzer_pin, 988, 167); delay(167); delay(40);
  tone(buzzer_pin, 1047, 167); delay(167); delay(40);
  tone(buzzer_pin, 988, 333); delay(333); delay(40);
  tone(buzzer_pin, 880, 167); delay(167); delay(40);
  tone(buzzer_pin, 784, 167); delay(167); delay(40);
  tone(buzzer_pin, 880, 333); delay(333); delay(80);

  tone(buzzer_pin, 988, 333); delay(333); delay(40);
  tone(buzzer_pin, 784, 333); delay(333);
}

/* ================= MELODIA 2 ================= */

void melodia2() {
  tone(buzzer_pin, 440, 500); delay(500); delay(40);
  tone(buzzer_pin, 440, 500); delay(500); delay(40);
  tone(buzzer_pin, 523, 333); delay(333); delay(40);
  tone(buzzer_pin, 659, 333); delay(333); delay(80);

  tone(buzzer_pin, 440, 500); delay(500); delay(40);
  tone(buzzer_pin, 440, 500); delay(500); delay(40);
  tone(buzzer_pin, 392, 333); delay(333); delay(40);
  tone(buzzer_pin, 415, 333); delay(333); delay(80);

  tone(buzzer_pin, 440, 500); delay(500); delay(40);
  tone(buzzer_pin, 440, 500); delay(500); delay(40);
  tone(buzzer_pin, 523, 333); delay(333); delay(40);
  tone(buzzer_pin, 659, 333); delay(333); delay(80);

  tone(buzzer_pin, 440, 500); delay(500); delay(40);
  tone(buzzer_pin, 440, 500); delay(500); delay(40);
  tone(buzzer_pin, 392, 333); delay(333); delay(40);
  tone(buzzer_pin, 415, 333); delay(333); delay(80);

  tone(buzzer_pin, 523, 167); delay(167); delay(40);
  tone(buzzer_pin, 440, 167); delay(167); delay(40);
  tone(buzzer_pin, 330, 1000); delay(1000); delay(80);

  tone(buzzer_pin, 523, 167); delay(167); delay(40);
  tone(buzzer_pin, 440, 167); delay(167); delay(40);
  tone(buzzer_pin, 311, 1000); delay(1000); delay(80);

  tone(buzzer_pin, 523, 167); delay(167); delay(40);
  tone(buzzer_pin, 440, 167); delay(167); delay(40);
  tone(buzzer_pin, 294, 1000); delay(1000); delay(80);

  tone(buzzer_pin, 262, 167); delay(167); delay(40);
  tone(buzzer_pin, 294, 500); delay(500);
}

/* ================= MELODIA 3 ================= */

void melodia3() {
  tone(buzzer_pin, 440, 667); delay(667); delay(40);
  tone(buzzer_pin, 440, 667); delay(667); delay(40);
  tone(buzzer_pin, 440, 667); delay(667); delay(80);

  tone(buzzer_pin, 349, 500); delay(500); delay(40);
  tone(buzzer_pin, 523, 167); delay(167); delay(40);
  tone(buzzer_pin, 440, 667); delay(667); delay(80);

  tone(buzzer_pin, 349, 500); delay(500); delay(40);
  tone(buzzer_pin, 523, 167); delay(167); delay(40);
  tone(buzzer_pin, 440, 667); delay(667); delay(120);

  tone(buzzer_pin, 659, 667); delay(667); delay(40);
  tone(buzzer_pin, 659, 667); delay(667); delay(40);
  tone(buzzer_pin, 659, 667); delay(667); delay(80);

  tone(buzzer_pin, 698, 500); delay(500); delay(40);
  tone(buzzer_pin, 523, 167); delay(167); delay(40);
  tone(buzzer_pin, 440, 667); delay(667); delay(80);

  tone(buzzer_pin, 349, 500); delay(500); delay(40);
  tone(buzzer_pin, 523, 167); delay(167); delay(40);
  tone(buzzer_pin, 440, 667); delay(667);
}

/* ================= MELODIA 4 ================= */

void melodia4() {
  tone(buzzer_pin, 784, 167); delay(167); delay(40);
  tone(buzzer_pin, 698, 167); delay(167); delay(40);
  tone(buzzer_pin, 440, 333); delay(333); delay(40);
  tone(buzzer_pin, 494, 333); delay(333); delay(80);

  tone(buzzer_pin, 659, 167); delay(167); delay(40);
  tone(buzzer_pin, 587, 167); delay(167); delay(40);
  tone(buzzer_pin, 349, 333); delay(333); delay(40);
  tone(buzzer_pin, 392, 333); delay(333); delay(80);

  tone(buzzer_pin, 587, 167); delay(167); delay(40);
  tone(buzzer_pin, 523, 167); delay(167); delay(40);
  tone(buzzer_pin, 330, 333); delay(333); delay(40);
  tone(buzzer_pin, 392, 333); delay(333); delay(40);
  tone(buzzer_pin, 523, 333); delay(333);
}

Código PWMless

#include "melodias.h"

#define BUZZER_PIN 11  

void setup() {
  Serial.begin(9600);
  Serial.println("Melodias Clean — Sem PWM");
  delay(1000);

  melodias_init(BUZZER_PIN);
}

void loop() {
  delay(3000);
  melodia1();
  delay(1000);
  melodia2();
  delay(1000);
  melodia3();
  delay(1000);
  melodia4();
  delay(1000);
}

melodias.h (PWMless)

#ifndef MELODIAS_H
#define MELODIAS_H

#include <Arduino.h>

// inicialização do módulo
void melodias_init(uint8_t pin);

// melodias disponíveis
void melodia1();
void melodia2();
void melodia3();
void melodia4();

#endif

melodias.cpp (PWMless)

#include "melodias.h"

static uint8_t buzzer_pin;

// ------------------------------------------------------
// Função beep: geração de onda quadrada por software
// ------------------------------------------------------
void beep(int freq, int duracaoMs) {
  if (freq <= 0) {
    delay(duracaoMs);
    return;
  }

  long periodo = 1000000L / freq;
  long meioPeriodo = periodo / 2;
  long ciclos = (duracaoMs * 1000L) / periodo;

  for (long i = 0; i < ciclos; i++) {
    digitalWrite(buzzer_pin, HIGH);
    delayMicroseconds(meioPeriodo);
    digitalWrite(buzzer_pin, LOW);
    delayMicroseconds(meioPeriodo);
  }
}

// ------------------------------------------------------
// Inicialização
// ------------------------------------------------------
void melodias_init(uint8_t pin) {
  buzzer_pin = pin;
  pinMode(buzzer_pin, OUTPUT);
}

// ------------------------------------------------------
// Melodia 1
// ------------------------------------------------------
void melodia1() {
  beep(659, 667); delay(40);
  beep(880, 333); delay(40);
  beep(784, 167); delay(40);
  beep(698, 167); delay(40);
  beep(659, 667); delay(80);

  beep(880, 333); delay(40);
  beep(784, 167); delay(40);
  beep(698, 167); delay(40);
  beep(659, 667); delay(80);

  beep(880, 333); delay(40);
  beep(880, 333); delay(40);
  beep(1047, 333); delay(80);

  beep(988, 167); delay(40);
  beep(1047, 167); delay(40);
  beep(988, 333); delay(40);
  beep(880, 167); delay(40);
  beep(784, 167); delay(40);
  beep(880, 333); delay(80);

  beep(988, 333); delay(40);
  beep(784, 333);
}

// ------------------------------------------------------
// Melodia 2 — Missão Impossível
// ------------------------------------------------------
void melodia2() {
  beep(440, 500); delay(40);
  beep(440, 500); delay(40);
  beep(523, 333); delay(40);
  beep(659, 333); delay(80);

  beep(440, 500); delay(40);
  beep(440, 500); delay(40);
  beep(392, 333); delay(40);
  beep(415, 333); delay(80);

  beep(440, 500); delay(40);
  beep(440, 500); delay(40);
  beep(523, 333); delay(40);
  beep(659, 333); delay(80);

  beep(440, 500); delay(40);
  beep(440, 500); delay(40);
  beep(392, 333); delay(40);
  beep(415, 333); delay(80);

  beep(523, 167); delay(40);
  beep(440, 167); delay(40);
  beep(330, 1000); delay(80);

  beep(523, 167); delay(40);
  beep(440, 167); delay(40);
  beep(311, 1000); delay(80);

  beep(523, 167); delay(40);
  beep(440, 167); delay(40);
  beep(294, 1000); delay(80);

  beep(262, 167); delay(40);
  beep(294, 500);
}

// ------------------------------------------------------
// Melodia 3 — Imperial March
// ------------------------------------------------------
void melodia3() {
  beep(440, 667); delay(40);
  beep(440, 667); delay(40);
  beep(440, 667); delay(80);

  beep(349, 500); delay(40);
  beep(523, 167); delay(40);
  beep(440, 667); delay(80);

  beep(349, 500); delay(40);
  beep(523, 167); delay(40);
  beep(440, 667); delay(120);

  beep(659, 667); delay(40);
  beep(659, 667); delay(40);
  beep(659, 667); delay(80);

  beep(698, 500); delay(40);
  beep(523, 167); delay(40);
  beep(440, 667); delay(80);

  beep(349, 500); delay(40);
  beep(523, 167); delay(40);
  beep(440, 667);
}

// ------------------------------------------------------
// Melodia 4 — Nokia Theme
// ------------------------------------------------------
void melodia4() {
  beep(784, 167); delay(40);
  beep(698, 167); delay(40);
  beep(440, 333); delay(40);
  beep(494, 333); delay(80);

  beep(659, 167); delay(40);
  beep(587, 167); delay(40);
  beep(349, 333); delay(40);
  beep(392, 333); delay(80);

  beep(587, 167); delay(40);
  beep(523, 167); delay(40);
  beep(330, 333); delay(40);
  beep(392, 333); delay(40);
  beep(523, 333);
}

Conclusão

Este projeto demonstra como um buzzer passivo, apesar de simples, pode servir como ponto de encontro entre música, eletrônica e programação embarcada. A abordagem modular apresentada permite compreender a tradução de conceitos musicais para código, ao mesmo tempo em que facilita a reutilização e adaptação do sistema a diferentes hardwares.

Ao organizar o código em versões complementares — engine, clean e sem PWM por hardware — o artigo evidencia que decisões de arquitetura são tão importantes quanto o resultado sonoro final. Mais do que tocar melodias, o sistema propõe uma forma estruturada de pensar o som dentro do código, priorizando clareza, portabilidade e controle.

A partir dessa base, o leitor pode expandir o projeto conforme suas necessidades, seja criando novas melodias, adaptando o sistema a outras placas ou explorando aplicações interativas que integrem som e lógica embarcada.


7. Referências

ARDUINO. tone() / noTone().

Documentação oficial das funções tone() e noTone(), descrevendo funcionamento, limitações e comportamento em diferentes placas Arduino.

Disponível em: https://www.arduino.cc/reference/en/language/functions/advanced-io/tone/

ARDUINO. Estrutura de arquivos (.ino, .h, .cpp).

Guia oficial sobre a organização de projetos no Arduino IDE e o uso de arquivos de cabeçalho e implementação.

Disponível em: https://docs.arduino.cc/learn/programming/sketches/

ESPRESSIF SYSTEMS. ESP32 Technical Reference Manual.

Documentação técnica do microcontrolador ESP32, incluindo GPIOs, timers e geração de sinais digitais.

Disponível em: https://www.espressif.com/en/support/documents/technical-documents

MED, Bohumil. Teoria da Música. 4. ed. Brasília: Musimed, 1996.

SOUZA, Vitor Amadeu. Programando o ESP32 no Arduino. São Paulo: Clube de Autores, 2020.

WIKIPÉDIA. Batidas por minuto (BPM).

Disponível em: https://pt.wikipedia.org/wiki/Batidas_por_minuto

WIKIPÉDIA. Som.

Disponível em: https://pt.wikipedia.org/wiki/Som


Sobre o Autor


Erwin de Mattos

 


Lista de Materiais

Eletrogate

13 de fevereiro de 2026

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!

Eletrogate Robô

Assine nossa newsletter e
receba  10% OFF  na sua
primeira compra!