Visualizador de Espectro de Som com Arduino Nano 33 BLE: Display de Áudio em Tempo Real
Componentes e Materiais
| Arduino Nano 33 BLE | × | 1 |
| Amplificador de Microfone Eletreto MAX9814 | × | 1 |
| Display OLED I2C 0.91" 128x32 pixels | × | 1 |
| Capacitor 4.7 nF — Depende do filtro RC passa-baixa e frequência de corte escolhida | × | 1 |
| Capacitor 470 µF | × | 1 |
| Resistor 2K Ω — Depende do filtro RC passa-baixa e frequência de corte escolhida | × | 1 |
Aplicativos e Serviços Online
- Arduino IDE
Sobre o Projeto
Introdução
Veja um gráfico de barras responder a música e som num pequeno display OLED. Frequências mais graves à esquerda, mais agudas à direita.
Construção
Conexões
- Conecte GAIN do microfone conforme sensibilidade desejada:
- VCC → GAIN do microfone para 40dB (baixa sensibilidade)
- GND → GAIN do microfone para 50dB (sensibilidade média)
- Flutuante → GAIN do microfone para 60dB (alta sensibilidade)
- Pino analógico A0 → saída OUT do amplificador de microfone
- 3.3V, GND, SDA (ou A4) e SCL (ou A5) → display I2C SSD1306
- Capacitor 470 µF entre VCC e GND no barramento de alimentação
Ruído de baixa frequência?
O ruído vem de dispositivos elétricos próximos funcionando. Todos operam em frequência e tensão fixas de corrente alternada (CA): 50 Hz ou 60 Hz conforme o país. Use uma gaiola de Faraday para eliminar: coloque o circuito numa caixa metálica e conecte a caixa ao terra. Aterre bem o circuito.
Usando analogRead() do core Arduino no Nano 33 BLE, obtemos ~35.000 amostras/segundo. Não dá para usar FFT acima de 17.5 kHz (Nyquist). Como lemos o clock e fazemos comparações em loop de espera ocupada, vamos ser conservadores e limitar a 8 kHz. Um filtro RC passa-baixa simples evita frequências acima disso.
R = 180Ω, C = 0.1µF → frequência de corte fc = 8842 Hz
Aqui: resistor 2K Ω e capacitor 4.7 nF → fc = 17000 Hz
Cálculo Aproximado da Taxa Máxima de Amostragem
analogRead() normal é lento para amostragem de áudio. Vamos estimar o máximo.
Programa de teste para estimar taxa com Arduino Nano 33 BLE:
/* Cálculo aproximado da taxa máxima de amostragem com AnalogRead */
#define ANALOG_PIN A0
#define SAMPLES 1000000.0
int newTime;
int analogValue;
void setup() {
Serial.begin(9600);
Serial.println("Amostrando... aguarde.");
}
void loop() {
newTime = micros();
for (int i = 0; i < (int)SAMPLES; i++) {
analogValue = analogRead(ANALOG_PIN);
}
float elapsedTimePerSampleUs = (micros() - newTime) / SAMPLES;
Serial.print("Tempo por amostra (us): ");
Serial.println(elapsedTimePerSampleUs);
Serial.print("Freq. máx. amostragem (Hz): ");
Serial.println(pow(10,6) / elapsedTimePerSampleUs);
}
Resultado: ~35.000 amostras/seg. Requisito de Nyquist (2x) limita visualizador a <17 kHz. Usamos filtro passa-baixa para cortar acima disso. Como há overhead de leitura de clock e comparações em busy wait, limitamos a 8 kHz.
// Captura de amostras
for (int i = 0; i < SAMPLES; i++) {
unsigned long newTime = micros();
int value = analogRead(ANALOG_PIN);
vReal[i] = value;
vImag[i] = 0;
while (micros() < (newTime + sampling_period_us)) {
yield();
}
}
Interface do Usuário
A tela é dividida em 11 zonas de frequência com escala logarítmica, agrupando frequências similarmente à percepção auditiva humana.
Ouvimos de ~20 Hz a 20 kHz (bebês ouvem ligeiramente acima; adultos 15-17 kHz). Usamos zonas 3 a 13 (11 zonas).
Cada barra mostra: valor máximo na banda (linhas tracejadas) e valor médio (retângulo preenchido). Valores em decibéis com referência pré-selecionada heuristicamente.
Como Funciona
Amostras de áudio → espectro de frequência via FFT (Transformada Rápida de Fourier). Espectro dividido/agrupado em 14 zonas; 11 representadas em dB.
Sobre o Módulo de Microfone
Microfone Eletreto
Eletreto é dielétrico quasi-permanentemente carregado. Feito aquecendo material cerâmico, colocando em campo magnético e resfriando no campo. Equivalente eletrostático de ímã permanente. No microfone eletreto, uma fatia desse material faz parte do dielétrico de um capacitor onde o diafragma forma uma placa. Pressão sonora move o diafragma → variação de capacitância → variação de tensão (devido à carga fixa do dielétrico). Capacitor eletreto conectado a amplificador FET embutido. Microfones eletretos são pequenos, excelente sensibilidade, ampla resposta em frequência e custo muito baixo.
Amplificador MAX9814
Amplificador de microfone com controle automático de ganho (AGC), bias de microfone de baixo ruído e algumas opções configuráveis no breakout.
- Três ganhos: 40dB, 50dB, 60dB. Padrão: 60dB. Jumper no pino Gain para VCC (40dB) ou GND (50dB).
- Bias interno baixo ruído: 1.25V, 2Vpp. Saída ~2Vpp máx em bias 1.25V DC → compatível com ADC até 3.3V.
- Taxa Attack/Release modificável: padrão 1:4000, ou 1:2000, 1:500.
- Para entrada Line-In direta: capacitor de bloqueio/acoplamento em série >1µF (33-100µF se em dúvida). Bloqueia componente DC. Em circuitos de áudio, evita distorção por DC. Efetividade depende de parâmetros dependentes de frequência: perda de inserção, ESR, frequência ressonante série.
- Alimentação: 2.7V a 5.5V
Sensibilidade
Sensibilidade = razão entre tensão de saída analógica (ou valor digital) e pressão de entrada. Especificação-chave de qualquer microfone. Mapear unidades acústicas para elétricas determina magnitude do sinal de saída para entrada conhecida.
Sensibilidade = resposta elétrica na saída para entrada acústica padrão. Referência padrão: onda senoidal 1 kHz a 94 dB SPL (1 Pa). Microfone com maior sensibilidade = maior nível de saída para mesma entrada acústica. Sensibilidade em dB costuma ser negativa; maior sensibilidade = menor valor absoluto.
Para microfones analógicos, sensibilidade típica em dBV (dB referenciado a 1.0 V rms).
Nível de Referência e Resposta em Frequência
Resposta em frequência = nível de saída ao longo do espectro. Limites alto/baixo = pontos onde resposta cai 3 dB abaixo do nível de referência a 1 kHz. Nível de referência a 1 kHz usualmente normalizado a 0 dB.
Display OLED I2C 0.91" 128x32
Display pequeno, ~1" diagonal. 128x32 pixels OLED brancos individuais, cada um ligado/desligado pelo controlador. Sem backlight → menor consumo.
Modelo usado tem 4 pinos, comunica via I2C. Sem pino RESET. Conecte ao Arduino Nano I2C: SDA (A4), SCL (A5).
Para controlar: bibliotecas adafruit_SSD1306.h e adafruit_GFX.h. Instale via Gerenciador de Bibliotecas.
Próximos passos: Arduino Nano 33 BLE pode fazer leituras analógicas melhores. Próxima versão usarei FFT e entrada analógica otimizada para nRF52840.
Código
Visualizador de Espectro de Som para Arduino Nano 33 BLE Arduino
Usa arduinoFFT, Adafruit_GFX, Adafruit_SSD1306
/* Sound Spectrum Visualizer for Arduino Nano 33 BLE
Arduino based sound visualizer
@author Enrique Albertos
Hardware:
- Arduino Nano 33 BLE
- Display OLED SSD1306 I2C 128x32
- Amplificador Microfone Eletreto MAX9814
- Opcional: bateria para uso portátil (senão USB)
Software:
- arduinoFFT
- Adafruit_GFX
- Adafruit_SSD1306
Conexões:
- Mic GAIN: VCC=40dB, GND=50dB, flutuante=60dB
- A0 → mic amp OUT
- 3.3V, GND, SDA (A4), SCL (A5) → SSD1306 I2C
@uses arduinoFFT, Adafruit_GFX, Adafruit_SSD1306
Licença: GPL v3 ou posterior
*/
#include "arduinoFFT.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SAMPLES 1024 // potência de 2
#define SAMPLING_FREQ 24000 // 12 kHz Fmax = sampleF/2
#define AMPLITUDE 100 // sensibilidade
#define FREQUENCY_BANDS 14
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define BARWIDTH 11
#define BARS 11
#define ANALOG_PIN A0
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
double vImag[SAMPLES];
double vReal[SAMPLES];
unsigned long sampling_period_us;
arduinoFFT fft = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQ);
float reference = log10(50.0);
double coutoffFrequencies[FREQUENCY_BANDS];
void setup() {
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
for (;;); // loop forever
}
display.clearDisplay();
display.display();
display.setRotation(0);
display.invertDisplay(false);
sampling_period_us = (1.0 / SAMPLING_FREQ) * pow(10.0, 6);
// Frequências de corte - escala logarítmica
double basePot = pow(SAMPLING_FREQ / 2.0, 1.0 / FREQUENCY_BANDS);
coutoffFrequencies[0] = basePot;
for (int i = 1; i < FREQUENCY_BANDS; i++) {
coutoffFrequencies[i] = basePot * coutoffFrequencies[i - 1];
}
// Linhas tracejadas separando bandas
for (int i = 0; i < BARS - 1; i++) {
for (int j = 0; j < SCREEN_HEIGHT; j += 4) {
display.writePixel((i + 1) * BARWIDTH + 2, j, SSD1306_WHITE);
}
}
display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
}
int oldHeight[20];
int oldMax[20];
double maxInFreq;
void loop() {
// Captura amostras
for (int i = 0; i < SAMPLES; i++) {
unsigned long newTime = micros();
int value = analogRead(ANALOG_PIN);
vReal[i] = value;
vImag[i] = 0;
while (micros() < (newTime + sampling_period_us)) {
yield();
}
}
// FFT
fft.DCRemoval();
fft.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
fft.Compute(FFT_FORWARD);
fft.ComplexToMagnitude();
double median[20];
double max[20];
int index = 0;
double hzPerSample = (1.0 * SAMPLING_FREQ) / SAMPLES;
double hz = 0;
double maxinband = 0;
double sum = 0;
int count = 0;
for (int i = 2; i < (SAMPLES / 2); i++) {
count++;
sum += vReal[i];
if (vReal[i] > max[index]) max[index] = vReal[i];
if (hz > coutoffFrequencies[index]) {
median[index] = sum / count;
sum = 0.0; count = 0; index++;
max[index] = 0; median[index] = 0;
}
hz += hzPerSample;
}
if (sum > 0.0) {
median[index] = sum / count;
if (median[index] > maxinband) maxinband = median[index];
}
int bar = 0;
for (int i = FREQUENCY_BANDS - 1; i >= 3; i--) {
int newHeight = 0, newMax = 0;
if (median[i] > 0 && max[i] > 0) {
newHeight = 20.0 * (log10(median[i]) - reference);
newMax = 20.0 * (log10(max[i]) - reference);
}
if (newHeight < 0 || newMax < 0) { newHeight = 1; newMax = 1; }
if (newHeight >= SCREEN_HEIGHT - 2) newHeight = SCREEN_HEIGHT - 3;
if (newMax >= SCREEN_HEIGHT - 2) newMax = SCREEN_HEIGHT - 3;
int barX = bar * BARWIDTH + 5;
// Apaga nível médio antigo
if (oldHeight[i] > newHeight) {
display.fillRect(barX, newHeight + 1, 7, oldHeight[i], SSD1306_BLACK);
}
// Apaga pico antigo
if (oldMax[i] > newHeight) {
for (int j = oldMax[i]; j > newHeight; j -= 2) {
display.drawFastHLine(barX, j, 7, SSD1306_BLACK);
}
}
// Desenha novo pico
for (int j = newMax; j > newHeight; j -= 2) {
display.drawFastHLine(barX, j, 7, SSD1306_WHITE);
}
// Desenha novo nível médio
display.fillRect(barX, 1, 7, newHeight, SSD1306_WHITE);
oldMax[i] = newMax;
oldHeight[i] = newHeight;
bar++;
}
display.drawFastHLine(0, SCREEN_HEIGHT - 1, SCREEN_WIDTH, SSD1306_WHITE);
display.display();
}
Esquemáticos
soundspectrumvisualizer_qLzRsVPVlc.fzz

Processo de manufatura
- Construa o Clássico Jogo Pong no Arduino UNO com Display OLED – Tutorial Passo a Passo
- Sensor K30 CO₂: Monitoramento de Qualidade do Ar Interno em Tempo Real
- Facilitando Comunicação Surdo-Cego com 1Sheeld e Arduino
- Mealheiro Inteligente: Controlar Aceitador de Moedas com Arduino Nano
- Controlar um LED via Bluetooth com Arduino – Guia DIY Simples
- Controle Dois Motores de Passo com Arduino Nano e Joystick – Tutorial Simples
- Transmissão de Dados de Áudio com Arduino Nano 33 BLE Sense
- Contador Geiger Portátil com Arduino Nano: Construa o Seu
- Medição Precisa de Radiação Solar com Arduino UNO e Ethernet Shield
- Robô Comedor com Arduino Nano – Guia Passo a Passo