O processamento digital de sinais de áudio tem se tornado cada vez mais acessível com o avanço de microcontroladores de baixo custo e alto desempenho. Essa evolução tem permitido o desenvolvimento de aplicações que anteriormente estavam restritas a sistemas computacionais mais complexos
Este trabalho apresenta o desenvolvimento de um analisador de áudio em tempo real, implementado a partir do microcontrolador RP2040. O sistema utiliza técnicas de Processamento Digital de Sinais (DSP) para capturar e analisar sinais acústicos provenientes de um microfone amplificado.
A arquitetura do sistema foi projetada de forma modular, permitindo a separação entre as etapas de aquisição de dados, processamento de sinais e visualização gráfica. Essa abordagem facilita a expansão do sistema para diferentes tipos de análise de áudio (spectrum, envelope, waveform).
A base do sistema consiste na captura contínua de amostras (samples) do sinal de áudio, que são posteriormente processadas por diferentes módulos de análise. Durante o processo de aquisição de áudio, o sistema também realiza uma etapa de estimativa do nível de ruído de fundo (noise floor), utilizada para melhorar a estabilidade das visualizações e reduzir a influência de ruídos elétricos e ambientais.
Esse processo inclui uma rotina de autocalibração inicial, capaz de estimar o nível médio do sinal em repouso, bem como parâmetros de calibração manual, ajustáveis diretamente no código-fonte. Essa abordagem permite adaptar o sistema a diferentes módulos de microfone, níveis de ganho e condições acústicas do ambiente.
O sistema proposto é composto por três elementos principais de hardware: o microcontrolador RP2040, um módulo de microfone amplificado baseado no circuito MAX4466 e um display OLED de 128×64 pixels utilizado para visualização gráfica dos dados.




O RP2040 é um microcontrolador desenvolvido pela Raspberry Pi Foundation, baseado em um processador dual-core ARM Cortex-M0+.
Entre suas principais características destacam-se:
Essas características tornam o RP2040 particularmente adequado para aplicações de processamento digital de sinais em tempo real, como analisadores de áudio.
Em comparação com plataformas populares como o Arduino Uno, o RP2040 apresenta uma quantidade significativamente maior de memória RAM, o que permite o armazenamento de buffers de amostragem maiores, necessários para operações como o cálculo da Transformada Rápida de Fourier (FFT).
Em relação ao ESP32, embora ambos apresentem maior capacidade de processamento que microcontroladores tradicionais de 8 bits, em muitas aplicações práticas, o ADC do RP2040 tende a apresentar leituras mais estáveis que o ADC interno do ESP32, que frequentemente exige calibração adicional e pode apresentar maior variabilidade dependendo da configuração do sistema.
Essas características tornam o RP2040 uma escolha adequada para aplicações que envolvem aquisição e análise de sinais analógicos, como o sistema apresentado neste trabalho.


A captura do sinal acústico é realizada por meio de um microfone eletreto acoplado a um módulo amplificador baseado no circuito integrado MAX4466.
A escolha desse módulo foi motivada principalmente pela facilidade de integração em projetos embarcados, uma vez que o dispositivo já integra em uma única placa o microfone, o circuito de pré-amplificação e os componentes necessários para operação estável. Essa característica reduz a complexidade do circuito e simplifica a montagem do sistema.
O módulo oferece:
A possibilidade de ajuste de ganho é particularmente importante neste tipo de aplicação, pois permite adaptar a amplitude do sinal de áudio à faixa dinâmica do conversor analógico-digital (ADC) do microcontrolador. Um ganho adequado permite utilizar melhor a resolução do ADC, evitando tanto sinais muito fracos — que resultariam em baixa resolução efetiva — quanto sinais excessivamente amplificados, que poderiam causar saturação (clipping) das amostras.
Após a amplificação, o sinal analógico é enviado diretamente para uma das entradas ADC do RP2040, onde é convertido em amostras digitais utilizadas nas etapas posteriores de processamento de sinal.
Durante o desenvolvimento do sistema também foram consideradas questões relacionadas à estabilidade do sinal analógico e à filtragem de ruído na alimentação, fatores importantes para garantir uma aquisição de áudio adequada em sistemas embarcados.
Para reduzir interferências provenientes da alimentação do módulo amplificador, recomenda-se a utilização de capacitores de desacoplamento próximos ao circuito do microfone. Uma configuração comum consiste na utilização de um capacitor eletrolítico de maior valor em paralelo com um capacitor cerâmico de menor valor, conectados entre os pinos de alimentação (VIN) e terra (GND) do módulo.
Essa combinação permite atenuar ruídos em diferentes faixas de frequência: o capacitor eletrolítico atua principalmente na filtragem de variações de baixa frequência da alimentação, enquanto o capacitor cerâmico contribui para a supressão de ruídos de alta frequência. Essa prática é amplamente utilizada em circuitos analógicos para melhorar a estabilidade do sinal antes da conversão analógico-digital.


A visualização dos resultados é realizada por meio de um display OLED monocromático com resolução de 128×64 pixels, baseado no controlador SSD1306.
Esse tipo de display é amplamente utilizado em projetos embarcados devido a características como:
No sistema desenvolvido, o display é utilizado para apresentar diferentes formas de visualização do sinal de áudio, incluindo:
A resolução gráfica do display permite representar essas informações de forma compacta e em tempo real, tornando-o adequado para aplicações de análise e visualização sonora em dispositivos embarcados.
Este capítulo descreve a arquitetura de software desenvolvida para o sistema de análise de áudio em tempo real. O firmware foi projetado de forma modular, separando claramente as etapas de aquisição, processamento e visualização do sinal de áudio.
A implementação foi realizada em linguagem C++ utilizando o ambiente Arduino compatível com o microcontrolador RP2040. A organização do código segue uma estrutura baseada em módulos independentes, o que facilita a manutenção do sistema e a expansão de funcionalidades.


O funcionamento geral pode ser descrito em quatro etapas principais:
Inicialização do sistema (setup)
Nesta etapa são configurados os periféricos necessários ao funcionamento do sistema, incluindo a interface I²C do display OLED, os pinos de entrada e saída e os parâmetros de aquisição de áudio.
Aquisição do sinal de áudio
O sinal proveniente do microfone é amostrado pelo conversor analógico-digital (ADC) do microcontrolador. As amostras são armazenadas em um buffer de memória que servirá como base para os algoritmos de processamento digital de sinais.
Processamento do sinal
Após a aquisição, o sinal pode ser analisado por três modos de processamento independentes:
Cada modo implementa seu próprio pipeline de processamento, utilizando o mesmo conjunto de amostras capturadas.
Renderização no display
Os resultados do processamento são convertidos em representações gráficas e exibidos em tempo real no display OLED do sistema.
Essa arquitetura permite que diferentes formas de análise do sinal de áudio sejam implementadas de maneira independente, mantendo uma estrutura clara e organizada do firmware.
Os modos de análise implementados no sistema utilizam técnicas clássicas de processamento digital de sinais para extrair informações relevantes do áudio capturado. As subseções seguintes descrevem os principais métodos utilizados.
A captura do áudio é realizada por meio da função captureSamples(), responsável por coletar uma janela de amostras do sinal proveniente do microfone.
O sistema utiliza uma frequência de amostragem de 8 kHz, coletando 256 amostras por janela de análise. Esses valores foram definidos considerando o equilíbrio entre resolução temporal e custo computacional do processamento.
A função implementa um mecanismo simples de temporização baseado no relógio interno do microcontrolador. O período entre amostras é calculado a partir da frequência de amostragem desejada:
onde:
Para uma frequência de 8 kHz, o período entre amostras é aproximadamente:
Durante a execução da função, cada amostra é obtida por meio da leitura do conversor analógico-digital (ADC) conectado ao microfone. Os valores capturados são armazenados em dois vetores:
vReal, que armazena os valores reais do sinal;vImag, utilizado posteriormente por algoritmos que operam com números complexos.O uso de buffers dedicados permite que diferentes algoritmos de processamento utilizem o mesmo conjunto de amostras sem interferir entre si.
Além disso, durante a aquisição é realizado o cálculo da média das amostras capturadas. Esse valor será utilizado posteriormente para a remoção do offset DC, garantindo que o sinal esteja centrado em torno de zero antes do processamento.
Devido às características do circuito de amplificação do microfone, o sinal digitalizado apresenta um deslocamento em relação ao zero, conhecido como offset DC. Esse deslocamento ocorre porque o sinal analógico é centrado em torno de metade da tensão de alimentação do circuito.
Para corrigir esse efeito, calcula-se inicialmente a média das amostras:
Em seguida, o valor médio é subtraído de cada amostra do sinal:
Esse procedimento recentraliza o sinal em torno de zero, condição necessária para análises espectrais e cálculos de energia.
O modo de envelope tem como objetivo estimar a intensidade do sinal de áudio ao longo do tempo. Para isso, utiliza-se o cálculo do valor RMS (Root Mean Square), que fornece uma medida da potência média do sinal.
Após o cálculo do RMS, o valor é normalizado e submetido a um limiar mínimo de ruído (noise threshold), eliminando pequenas variações causadas por interferências eletrônicas ou ruído ambiente.
Para melhorar a estabilidade da visualização, é aplicado um envelope follower, que suaviza variações abruptas entre amostras consecutivas. Esse processo pode ser descrito por:
onde:
A análise espectral é realizada por meio da Transformada Rápida de Fourier (FFT), que calcula as componentes de frequência presentes no sinal a partir de suas amostras no domínio do tempo.
O resultado da FFT consiste em um conjunto de coeficientes que representam a amplitude das componentes de frequência presentes no sinal.
Para facilitar a visualização no display, os bins da FFT são agrupados em oito bandas espectrais, cada uma representando a média de um conjunto de bins adjacentes. Esse agrupamento reduz a variabilidade do sinal e permite uma representação mais estável do espectro.
Após o cálculo das bandas, aplica-se um processo de suavização visual baseado em um fator de decaimento exponencial. Quando o valor de uma banda diminui, sua altura no display decai gradualmente segundo a relação:
Esse mecanismo reproduz o comportamento típico de analisadores de espectro comerciais, tornando a visualização mais clara e intuitiva.
O modo de forma de onda apresenta o sinal diretamente no domínio do tempo, de forma semelhante a um osciloscópio simplificado.
Após a remoção do offset DC, as amostras são mapeadas para a resolução vertical do display. Como o número de amostras capturadas é maior que a largura do display, realiza-se uma redução da resolução horizontal, na qual múltiplas amostras são representadas por um único pixel.
Essa visualização permite observar características temporais do sinal, como ataques sonoros, periodicidade e variações de amplitude.
Os três modos implementados fornecem diferentes perspectivas sobre o mesmo sinal de áudio:
| Modo | Domínio de Análise |
|---|---|
| Waveform | Tempo |
| Envelope | Energia |
| Spectrum | Frequência |
Essa abordagem permite uma análise mais completa do sinal utilizando um sistema embarcado de baixo custo.
Com o objetivo de facilitar a adaptação do projeto a diferentes aplicações, todas as configurações relevantes do firmware foram centralizadas no arquivo config.h. Esse arquivo reúne parâmetros relacionados ao hardware, aquisição de áudio, visualização gráfica e comportamento geral do sistema.
A centralização dessas definições permite modificar o comportamento do analisador sem a necessidade de alterar os módulos principais do código. Dessa forma, ajustes de hardware ou funcionamento podem ser realizados de maneira simples, mantendo a organização e a modularidade do firmware.
O arquivo config.h é dividido em grupos de parâmetros que correspondem às diferentes partes do sistema, facilitando a compreensão do papel de cada parâmetro e permite que alterações sejam feitas de forma clara e segura.
Os parâmetros relacionados ao display definem sua resolução gráfica. Esses valores são utilizados pelas rotinas responsáveis pela renderização das visualizações do sistema.
#define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64
Alterar esses parâmetros permite adaptar o sistema para displays com resoluções diferentes sem modificar o restante do código.
O comportamento da captura de áudio também pode ser ajustado por meio do arquivo de configuração.
#define SAMPLES 256 #define SAMPLING_FREQUENCY 8000 #define BANDS 8
Esses parâmetros controlam como o áudio é coletado e analisado pelo sistema. Ajustes nesses valores permitem adaptar o analisador para diferentes tipos de sinal ou níveis de detalhamento da análise.
O arquivo de configuração também define o mapeamento dos pinos utilizados pelo sistema.
#define MIC_PIN 26 #define BTN_MODE 8 #define I2C_SDA 4 #define I2C_SCL 5
Essa separação permite portar o projeto para diferentes placas ou layouts de hardware alterando apenas essas definições.
O sistema possui múltiplos modos de visualização do sinal de áudio, como forma de onda, envelope e espectro.
O número total de modos disponíveis é definido pela constante:
#define TOTAL_MODES 4
Esse valor é utilizado pelo sistema de controle para alternar entre os diferentes modos de visualização.
Alguns parâmetros permitem ajustar o comportamento da análise de áudio e da visualização dos resultados.
#define NOISE_THRESHOLD 0.02 #define SPECTRUM_DECAY 0.80
Essas configurações controlam aspectos como sensibilidade ao ruído e suavização da visualização do espectro. A possibilidade de ajustar esses valores facilita a adaptação do sistema a diferentes ambientes acústicos.
O arquivo config.h é incluído por todos os módulos do sistema, tornando seus parâmetros acessíveis a todo o firmware. Dessa forma, os diferentes componentes do analisador utilizam as mesmas configurações de forma consistente.
O arquivo principal do firmware é responsável pela coordenação geral do sistema. Nele são implementadas as rotinas de inicialização do hardware, o gerenciamento dos modos de operação e o loop principal responsável pela execução contínua do analisador.
O arquivo principal inclui os módulos responsáveis pelas diferentes funcionalidades do sistema.
#include "config.h" #include "audio.h" #include "spectrum.h" #include "display.h" #include "envelope.h" #include "waveform.h"
Cada um desses módulos implementa uma parte específica do analisador de áudio, mantendo o firmware organizado em componentes independentes.
A função setup() executa as rotinas de inicialização necessárias antes do início da execução do sistema.
Entre essas tarefas estão:
inicialização da comunicação serial
configuração do botão de controle
inicialização do display OLED
calibração do ruído ambiente
Essa fase prepara o sistema para a execução do analisador, garantindo que os periféricos estejam configurados corretamente.
Após a inicialização, o sistema entra no loop principal do firmware, que executa continuamente enquanto o dispositivo estiver ligado.
Nesse loop são realizadas duas tarefas principais:
verificar se o botão foi pressionado
executar o modo de visualização atualmente selecionado
A seleção do modo é realizada por meio de uma estrutura de controle que direciona a execução para a rotina correspondente enquanto cada modo executa suas próprias rotinas de captura de áudio, processamento e renderização no display.
A interação entre os diferentes componentes do sistema pode ser representada pelo seguinte fluxo de execução:


Este trabalho apresentou o desenvolvimento de um analisador de áudio em tempo real baseado no microcontrolador RP2040, combinando técnicas de aquisição de sinais analógicos, processamento digital de sinais e visualização gráfica em um display OLED.
O sistema implementado permite capturar o sinal proveniente de um microfone amplificado, realizar sua digitalização por meio do conversor analógico-digital do microcontrolador e aplicar diferentes métodos de análise do sinal. Entre os modos de processamento desenvolvidos destacam-se a visualização da forma de onda no domínio do tempo, o cálculo do envelope de energia do sinal e a análise espectral baseada na Transformada Rápida de Fourier (FFT). Essas diferentes formas de representação permitem observar características complementares do áudio capturado, como sua estrutura temporal, intensidade e distribuição de frequências.
Os resultados obtidos demonstram que microcontroladores modernos de baixo custo, como o RP2040, possuem capacidade suficiente para executar tarefas de processamento digital de sinais em tempo real, permitindo a construção de ferramentas de análise sonora compactas e acessíveis. A arquitetura modular adotada no firmware facilita a manutenção do sistema e possibilita a adição de novos modos de processamento ou visualização sem alterações significativas na estrutura principal do código.
Além de sua aplicação prática como visualizador de áudio, o sistema desenvolvido possui potencial como ferramenta didática para o ensino de conceitos fundamentais de áudio digital, eletrônica embarcada e processamento de sinais, permitindo observar de forma direta fenômenos como dinâmica sonora, comportamento temporal do sinal e composição espectral.
DINIZ, Paulo S. R.
Processamento Digital de Sinais.
Bookman, 2014.
https://www.pee.ufrj.br/area-livros/2004-processamento-digital-de-sinais/
SMITH, Julius O.
Introduction to Digital Filters with Audio Applications.
https://ccrma.stanford.edu/~jos/filters/
RASPBERRY PI LTD.
RP2040 Datasheet.
https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
ARDUINO.
Arduino Reference.
https://www.arduino.cc/reference/en/
ANALOG DEVICES.
MAX4466 Microphone Amplifier with Adjustable Gain.
https://www.analog.com/en/products/max4466.html
Controle (audioanalyzer.ino)
/*
==========================================================
Analisador de Áudio em Tempo Real
Arquivo: audioanalyzer.ino (main)
Responsável por:
- Inicialização do sistema
- Gerenciamento de modos de operação
- Leitura do botão de troca de modo
- Loop principal do firmware
Modos disponíveis:
- Splash (tela inicial)
- Spectrum (FFT / analisador de espectro)
- Envelope (VU meter / energia do sinal)
- Waveform (forma de onda em tempo real)
==========================================================
*/
#include "config.h"
#include "audio.h"
#include "spectrum.h"
#include "display.h"
#include "envelope.h"
#include "waveform.h"
// ==========================================================
// Controle do botão (debounce simples)
// ==========================================================
bool lastButtonState = HIGH;
unsigned long lastDebounce = 0;
int debounceDelay = 50;
bool buttonPressed() {
static bool lastState = HIGH;
bool state = digitalRead(BTN_MODE);
if(lastState == HIGH && state == LOW) {
delay(20); // pequeno debounce
lastState = state;
return true;
}
lastState = state;
return false;
}
// ==========================================================
// Definição dos modos do sistema
// ==========================================================
enum Mode {
MODE_SPLASH,
MODE_SPECTRUM,
MODE_ENVELOPE,
MODE_WAVEFORM
};
Mode currentMode = MODE_SPLASH; // modo inicial
// ==========================================================
// Setup
// ==========================================================
void setup() {
Serial.begin(921600);
// botão com pullup interno
pinMode(BTN_MODE, INPUT_PULLUP);
// inicializa display OLED
initDisplay();
// mede o ruído ambiente para calibrar FFT
calibrationNoise();
}
// ==========================================================
// Loop principal
// ==========================================================
void loop() {
// troca de modo ao pressionar o botão
if(buttonPressed()) {
currentMode = (Mode)((currentMode + 1) % TOTAL_MODES);
}
// executa o modo atual
switch(currentMode) {
case MODE_SPLASH:
drawSplash();
break;
case MODE_SPECTRUM:
modeSpectrum();
break;
case MODE_ENVELOPE:
modeEnvelope();
break;
case MODE_WAVEFORM:
modeWaveform();
break;
}
// controla taxa de atualização da interface
delay(35);
}
// ==========================================================
// Calibração de ruído do ambiente
// ==========================================================
void calibrationNoise() {
int calibrationFrames = 40;
for(int f=0; f<calibrationFrames; f++) {
// feedback visual de calibração
drawCalibrationScreen(f);
// captura amostras do ADC
captureSamples();
// executa FFT
computeFFT();
float bands[BANDS];
// calcula energia das bandas
computeBands(bands);
// acumula energia média
for(int b=0; b<BANDS; b++)
noiseFloor[b] += bands[b];
}
// calcula média final do ruído
for(int b=0; b<BANDS; b++)
noiseFloor[b] /= calibrationFrames;
}
Processador de Áudio (audio.cpp e audio.h)
/*
==========================================================
Analisador de Áudio em Tempo Real
Arquivo: audio.cpp
Responsável por:
- Captura das amostras de áudio via ADC
- Preparação dos buffers da FFT
- Remoção de offset DC do sinal
- Execução da FFT
O módulo produz dois buffers principais:
vReal[] -> sinal de áudio amostrado
vImag[] -> componente imaginária (inicialmente zero)
Após a execução da FFT, vReal passa a conter
a magnitude espectral de cada bin de frequência.
==========================================================
*/
#include <Arduino.h>
#include <arduinoFFT.h>
#include "audio.h"
#include "spectrum.h"
// ==========================================================
// Buffers da FFT
// ==========================================================
float vReal[SAMPLES];
float vImag[SAMPLES];
// ==========================================================
// Objeto FFT
// ==========================================================
ArduinoFFT<float> FFT = ArduinoFFT<float>(vReal, vImag, SAMPLES, SAMPLING_FREQUENCY);
// ==========================================================
// Execução da FFT
// ==========================================================
void computeFFT() {
FFT.windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.compute(FFT_FORWARD);
FFT.complexToMagnitude();
}
// ==========================================================
// Captura das amostras de áudio
// ==========================================================
void captureSamples() {
// período de amostragem em microssegundos
unsigned long period = 1000000 / SAMPLING_FREQUENCY;
float mean = 0;
for (int i = 0; i < SAMPLES; i++) {
unsigned long t = micros();
// leitura do ADC (microfone)
int raw = analogRead(MIC_PIN);
vReal[i] = raw;
vImag[i] = 0;
// acumula média para remover DC offset
mean += raw;
// espera até completar o período de amostragem
while (micros() - t < period);
}
mean /= SAMPLES;
// ==================================================
// Remoção do DC offset
// ==================================================
for (int i = 0; i < SAMPLES; i++)
vReal[i] -= mean;
}#ifndef AUDIO_H #define AUDIO_H #include "config.h" extern float vReal[SAMPLES]; extern float vImag[SAMPLES]; void captureSamples(); void computeFFT(); #endif
Configuração (config.h)
#ifndef CONFIG_H
#define CONFIG_H
/*
==========================================================
Analisador de Áudio em Tempo Real
Arquivo: config.h
Este arquivo centraliza todas as configurações do sistema:
- parâmetros de hardware
- resolução do display
- parâmetros de amostragem de áudio
- configurações de processamento DSP
Alterações aqui permitem ajustar o comportamento do
sistema sem modificar os módulos internos.
==========================================================
*/
// ==========================================================
// DISPLAY CONFIGURATION
// ==========================================================
/*
Resolução do display OLED SSD1306.
Esses valores são usados por todos os módulos de renderização.
*/
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// ==========================================================
// AUDIO SAMPLING PARAMETERS
// ==========================================================
/*
Número de amostras utilizadas na FFT.
Deve ser potência de 2 para funcionamento correto da
biblioteca ArduinoFFT.
*/
#define SAMPLES 256
/*
Frequência de amostragem do ADC (Hz).
Define o limite máximo de frequência analisável
pelo sistema (teorema de Nyquist):
Fmax ≈ SAMPLING_FREQUENCY / 2
*/
#define SAMPLING_FREQUENCY 8000
/*
Número de bandas exibidas no analisador de espectro.
*/
#define BANDS 8
// ==========================================================
// HARDWARE PINOUT
// ==========================================================
/*
Pino do microfone (entrada ADC).
*/
#define MIC_PIN 26
/*
Botão para troca de modos do sistema.
*/
#define BTN_MODE 8
/*
Pinos da interface I2C utilizada pelo display OLED.
*/
#define I2C_SDA 4
#define I2C_SCL 5
// ==========================================================
// SYSTEM CONFIGURATION
// ==========================================================
/*
Número total de modos disponíveis no sistema.
Usado para ciclar entre modos ao pressionar o botão.
*/
#define TOTAL_MODES 4
// ==========================================================
// AUDIO PROCESSING
// ==========================================================
/*
Limite mínimo de nível de áudio para ser considerado
sinal válido.
Valores abaixo desse limite são tratados como silêncio,
reduzindo ruído do microfone e interferências.
*/
#define NOISE_THRESHOLD 0.02
// ==========================================================
// SPECTRUM ANALYZER
// ==========================================================
/*
Fator de decaimento das barras do espectro.
Controla a velocidade com que as barras diminuem
quando o nível do sinal cai.
valores maiores → queda mais lenta
valores menores → queda mais rápida
*/
#define SPECTRUM_DECAY 0.80
#endifDisplay (display.cpp e display.h)
/*
==========================================================
Analisador de Áudio em Tempo Real
Arquivo: display.cpp
Responsável por:
- Inicialização do display OLED (SSD1306)
- Renderização das diferentes telas do sistema
Telas disponíveis:
- Tela de calibração de ruído
- Tela inicial (splash)
- Espectro de frequência
- Visualizador de envelope (VU)
- Forma de onda (osciloscópio)
Biblioteca gráfica utilizada:
Adafruit_GFX + Adafruit_SSD1306
==========================================================
*/
#include "display.h"
#include "config.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
// ==========================================================
// Instância do display OLED
// ==========================================================
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// ==========================================================
// Inicialização do display
// ==========================================================
void initDisplay() {
Wire.setSDA(I2C_SDA);
Wire.setSCL(I2C_SCL);
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
}
// ==========================================================
// Tela de calibração de ruído (Exibida durante a medição do ruído ambiente)
// ==========================================================
void drawCalibrationScreen(int progress) {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(2);
display.setCursor(20,5);
display.println("SILENCIO");
display.setTextSize(1);
display.setCursor(8,30);
display.println("Calibrando ruido");
display.setCursor(25,40);
display.println("ambiente...");
int barWidth = map(progress,0,40,0,100);
display.drawRect(14,52,100,8,SSD1306_WHITE);
display.fillRect(14,52,barWidth,8,SSD1306_WHITE);
display.display();
}
// ==========================================================
// Tela inicial (Splash)
// ==========================================================
void drawSplash() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10,10);
display.println("Analisador de");
display.setCursor(5,20);
display.println("Audio em tempo real");
display.setCursor(10,40);
display.println("Por Erwin de Mattos");
// pequena onda decorativa
for (int x=0; x<SCREEN_WIDTH; x+=4) {
int y = 55 + 3*sin(x*0.2);
display.drawPixel(x,y,SSD1306_WHITE);
}
display.display();
}
// ==========================================================
// Desenho do espectro de frequência
// ==========================================================
/*
Recebe as bandas calculadas pela FFT e
desenha um gráfico de barras vertical.
Passos:
1) Compressão logarítmica da energia
2) Normalização da altura
3) Renderização das barras
*/
void drawSpectrum(float bands[BANDS]) {
display.clearDisplay();
int barWidth = SCREEN_WIDTH / BANDS;
for (int i=0;i<BANDS;i++) {
// compressão logarítmica
float val = log10(bands[i] + 1);
val = val / 3.0;
if (val > 1) val = 1;
int height = val * 60;
int x = i * barWidth;
int y = SCREEN_HEIGHT - height;
display.fillRect(x, y, barWidth-2, height, SSD1306_WHITE);
}
display.display();
}
// ==========================================================
// Visualizador de Envelope (VU)
// ==========================================================
/*
Exibe a energia do sinal como barras
verticais simétricas em torno da linha central.
history[] contém o histórico de energia
do sinal ao longo do tempo.
*/
void drawEnvelope(float history[]) {
display.clearDisplay();
int centerY = SCREEN_HEIGHT / 2;
float gain = 1.8;
for(int x = 0; x < SCREEN_WIDTH; x++) {
int height = history[x] * gain * (SCREEN_HEIGHT / 2);
if(height > SCREEN_HEIGHT/2)
height = SCREEN_HEIGHT/2;
int yTop = centerY - height;
int yBottom = centerY + height;
display.drawLine(x, yTop, x, yBottom, SSD1306_WHITE);
}
// linha central de referência
display.drawLine(0, centerY, SCREEN_WIDTH, centerY, SSD1306_WHITE);
display.display();
}
// ==========================================================
// Forma de onda
// ==========================================================
/*
Renderiza a forma de onda do áudio capturado.
Características:
- Downsampling do buffer FFT → largura do display
- AGC (Automatic Gain Control) suave
- Conexão entre pontos para formar a curva
*/
void drawWaveform(float samples[]) {
display.clearDisplay();
int centerY = SCREEN_HEIGHT / 2;
int step = SAMPLES / SCREEN_WIDTH;
static float gain = 0.002; // ganho inicial
// ==================================================
// Detecção de pico do sinal
// ==================================================
float peak = 0;
for(int i=0;i<SAMPLES;i++) {
float v = abs(samples[i]);
if(v > peak)
peak = v;
}
// ==================================================
// Ajuste automático de ganho (AGC)
// ==================================================
float targetGain = (SCREEN_HEIGHT * 0.45) / peak;
// suavização do ganho
gain = gain * 0.9 + targetGain * 0.1;
// ==================================================
// Desenho da forma de onda
// ==================================================
int prevY = centerY;
for(int x = 0; x < SCREEN_WIDTH; x++) {
int i = x * step;
int y = centerY - samples[i] * gain;
if(y < 0) y = 0;
if(y > SCREEN_HEIGHT) y = SCREEN_HEIGHT;
if(x > 0)
display.drawLine(x-1, prevY, x, y, SSD1306_WHITE);
prevY = y;
}
display.display();
}#ifndef DISPLAY_H #define DISPLAY_H #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #include <Adafruit_SSD1306.h> #include "config.h" void initDisplay(); void drawCalibrationScreen(int progress); void drawSplash(); void drawSpectrum(float bands[]); void drawWaveform(float samples[]); void drawEnvelope(float history[]); #endif
Modos de análise (Envelope, Spectrum, Waveform)
/*
==========================================================
Analisador de Áudio em Tempo Real
Arquivo: envelope.cpp
Responsável por:
- Cálculo da energia do sinal de áudio
- Aplicação de noise gate
- Compressão dinâmica
- Envelope follower (suavização temporal)
- Geração de histórico para visualização
O resultado é usado para desenhar um VU meter
temporal no display OLED.
==========================================================
*/
#include "config.h"
#include "audio.h"
#include "display.h"
#include "envelope.h"
#include <math.h>
// ==========================================================
// Buffers de visualização
// ==========================================================
// Histórico do envelope para desenhar o gráfico horizontal no display.
float envelopeHistory[SCREEN_WIDTH];
// envelope atual suavizado
float envelope = 0;
// ==========================================================
// Modo Envelope (VU meter)
// ==========================================================
void modeEnvelope() {
// captura novo frame de áudio
captureSamples();
// ==================================================
// cálculo da média (remoção de DC)
// ==================================================
float mean = 0;
for(int i = 0; i < SAMPLES; i++)
mean += vReal[i];
mean /= SAMPLES;
// ==================================================
// cálculo da energia RMS
// ==================================================
/*
RMS = sqrt( (1/N) * Σ(x²) )
Mede a energia real do sinal de áudio.
*/
float level = 0;
for(int i = 0; i < SAMPLES; i++) {
float v = vReal[i] - mean;
level += v * v;
}
level = sqrt(level / SAMPLES);
// ==================================================
// normalização
// ==================================================
level /= 2048.0;
if(level > 1) level = 1;
// ==================================================
// noise gate
// ==================================================
if(level < NOISE_THRESHOLD)
level = 0;
// ==================================================
// compressão logarítmica (para melhor visualização)
// ==================================================
level = log10(level * 9 + 1);
// ==================================================
// envelope follower (suavização temporal)
// ==================================================
envelope = envelope * 0.85 + level * 0.15;
// ==================================================
// atualização do histórico
// ==================================================
for(int i = 0; i < SCREEN_WIDTH - 1; i++) {
envelopeHistory[i] = envelopeHistory[i + 1];
}
// novo valor no final do gráfico
envelopeHistory[SCREEN_WIDTH - 1] = envelope;
// renderiza no display
drawEnvelope(envelopeHistory);
}#ifndef ENVELOPE_H #define ENVELOPE_H void modeEnvelope(); #endif
/*
==========================================================
Analisador de Áudio em Tempo Real
Arquivo: spectrum.cpp
Responsável por:
- Conversão dos bins da FFT em bandas de frequência
- Subtração do ruído ambiente (noise floor)
- Aplicação de decay visual para suavizar barras
- Controle do modo Spectrum
O resultado é usado para desenhar um gráfico de barras
==========================================================
*/
#include "spectrum.h"
#include "config.h"
#include "audio.h"
#include "display.h"
// ==========================================================
// Buffers globais
// ==========================================================
float noiseFloor[BANDS] = {0};
float displayBands[BANDS] = {0};
// ==========================================================
// Limites das bandas de frequência
// ==========================================================
/*
Isso cria bandas progressivamente maiores,
aproximando um comportamento logarítmico.
*/
int bandLimits[BANDS + 1] = {
2,4,8,16,24,32,48,64,80
};
// ==========================================================
// Conversão FFT → bandas
// ==========================================================
void computeBands(float bands[BANDS]) {
for (int b = 0; b < BANDS; b++) {
bands[b] = 0;
for (int i = bandLimits[b]; i < bandLimits[b + 1]; i++)
bands[b] += vReal[i];
// média dos bins da banda
bands[b] /= (bandLimits[b + 1] - bandLimits[b]);
}
}
// ==========================================================
// Remoção de ruído
// ==========================================================
void removeNoise(float bands[BANDS]) {
for(int b=0;b<BANDS;b++) {
// subtrai o ruído médio medido
bands[b]-=noiseFloor[b];
if(bands[b]<0)
bands[b]=0;
// threshold adicional para eliminar resíduos
if (bands[b] < 10)
bands[b] = 0;
}
}
// ==========================================================
// Decaimento visual das barras
// ==========================================================
void applyDecay(float bands[BANDS]) {
for(int b=0;b<BANDS;b++) {
if(bands[b]>displayBands[b])
displayBands[b]=bands[b];
else
displayBands[b] *= SPECTRUM_DECAY;
}
}
// ==========================================================
// Modo Spectrum
// ==========================================================
void modeSpectrum() {
// captura novo frame de áudio
captureSamples();
// executa FFT
computeFFT();
float bands[BANDS];
// converte bins em bandas
computeBands(bands);
// remove ruído ambiente
removeNoise(bands);
// aplica suavização visual
applyDecay(bands);
// renderiza no display
drawSpectrum(displayBands);
}#ifndef SPECTRUM_H #define SPECTRUM_H #include "config.h" extern float noiseFloor[BANDS]; extern float displayBands[BANDS]; void computeBands(float bands[BANDS]); void removeNoise(float bands[BANDS]); void applyDecay(float bands[BANDS]); void modeSpectrum(); #endif
/*
==========================================================
Analisador de Áudio em Tempo Real
Arquivo: waveform.cpp
Responsável por:
- Capturar amostras de áudio
- Remover offset DC do sinal
- Enviar os samples para visualização no display
Este modo funciona como um pequeno osciloscópio,
exibindo a forma de onda do sinal de áudio em
tempo real no display OLED.
==========================================================
*/
#include "config.h"
#include "audio.h"
#include "display.h"
// ==========================================================
// Modo Waveform (Osciloscópio)
// ==========================================================
void modeWaveform() {
// captura um novo frame de áudio
captureSamples();
// ==================================================
// cálculo da média do sinal
// ==================================================
float mean = 0;
for(int i = 0; i < SAMPLES; i++)
mean += vReal[i];
mean /= SAMPLES;
// ==================================================
// remoção do DC offset
// ==================================================
for(int i = 0; i < SAMPLES; i++)
vReal[i] -= mean;
// ==================================================
// renderização da forma de onda
// ==================================================
drawWaveform(vReal);
}#ifndef WAVEFORM_H #define WAVEFORM_H void modeWaveform(); #endif
|
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.
Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!