Como Transformar um Microcontrolador de $5 em uma Caixa de Som Bluetooth ou Player Sem Fio
Se você já abriu os exemplos oficiais da Espressif para áudio Bluetooth, provavelmente lembra aquela leve sensação de horror. Dezenas de linhas de mágica de baixo nível, vários callbacks do Bluedroid, configuração de filas do FreeRTOS, e aquela sensação constante de que um ponteiro está prestes a escapar para algum lugar.
O desenvolvedor suíço Phil Schatzmann encontrou exatamente essa frustração e escreveu a biblioteca ESP32-A2DP. Ela esconde toda a complexidade do boilerplate sob o capô e fornece uma interface C++ limpa para streaming de áudio.

O que está sob o capô e como funciona
A biblioteca lida com duas tarefas principais:
- A2DP Sink (receptor): o ESP32 finge ser uma caixa de som ou fone de ouvido sem fio, recebe o fluxo do seu celular e envia para um DAC externo, o DAC interno da placa ou um buffer de memória bruto.
- A2DP Source (transmissor): o microcontrolador transmite áudio por conta própria (gerado algoritmicamente, lido de um cartão SD ou de um microfone) para caixas de som Bluetooth externas.
O projeto depende do perfil A2DP padrão e suporta transmissão de sinais de controle via AVRCP. Isso significa que o microcontrolador pode trocar faixas no seu celular, pausar a reprodução ou ler metadados (título da faixa, nome do artista, duração).
Quanto à saída de áudio, a biblioteca funciona sobre o barramento I2S. No ESP-IDF v5 e Arduino ESP32 v3, a API I2S foi significativamente reescrita. Para evitar quebrar a compatibilidade ao atualizar o framework, o autor recomenda vincular a biblioteca diretamente ao seu próprio projeto arduino-audio-tools, embora você possa usar a classe padrão I2SClass ou simplesmente capturar amostras PCM brutas.
Como montar um receptor sem fio em cinco linhas
Aqui está um exemplo mínimo para colocar o receptor de áudio para funcionar:
#include "AudioTools.h"
#include "BluetoothA2DPSink.h"
I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);
void setup() {
Serial.begin(115200);
a2dp_sink.start("MyMusic");
}
void loop() {
}
Após carregar o firmware, a placa aparece na lista de dispositivos Bluetooth como "MyMusic". Por padrão, o fluxo de áudio (estéreo 16 bits, 44.1 kHz) vai para os pinos I2S GPIO14 (BCK), GPIO15 (WS) e GPIO22 (Data Out). Se sua fiação for diferente, os pinos são configurados em duas linhas via config i2s.defaultConfig().
Se você não tem um chip DAC externo (como o popular PCM5102A ou MAX98357A) à mão, pode usar o DAC interno de 8 bits nos pinos GPIO25 e GPIO26:
#include "AudioTools.h"
#include "BluetoothA2DPSink.h"
AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink(out);
void setup() {
Serial.begin(115200);
a2dp_sink.start("MyMusic");
}
void loop() {}
O som através do DAC interno será um pouco ruidoso para audiófilos, mas é perfeitamente adequado para notificações de voz ou rádio na cozinha.
Lendo metadados e controlando faixas
Um detalhe útil: a biblioteca permite obter informações da faixa via AVRCP. Se você conectar um pequeno display, pode facilmente criar uma tela de informações para um media center:
void avrc_metadata_callback(uint8_t data1, const uint8_t *data2) {
Serial.printf("Метаданные: id 0x%x, текст: %s\n", data1, data2);
}
void setup() {
Serial.begin(115200);
a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
a2dp_sink.start("ESP32_Receiver");
}
void loop() {}
Para controlar o player no seu celular a partir de botões físicos, os métodos play(), pause(), next(), previous(), fast_forward() e rewind() estão disponíveis.
Modo transmissor: quando o ESP32 gera o som
Se você precisa enviar áudio para fones de ouvido Bluetooth ou soundbar, mude para BluetoothA2DPSource. O fluxo é alimentado para a biblioteca via callback:
#include "BluetoothA2DPSource.h"
BluetoothA2DPSource a2dp_source;
int32_t get_sound_data(uint8_t *data, int32_t byteCount) {
// Здесь заполняем буфер PCM данными
return byteCount;
}
void setup() {
a2dp_source.set_data_callback(get_sound_data);
a2dp_source.start("Target_Speaker");
}
void loop() {}
A propósito, você pode passar uma lista de nomes de dispositivos para o método start() (via std::vector<const char*>). A placa se conectará automaticamente ao primeiro que encontrar na lista.
Acessando fluxo de áudio bruto e codecs
Se você não precisa do I2S e precisa processar áudio algoritmicamente diretamente no microcontrolador (por exemplo, criar um show de luzes via FFT ou gravar um fragmento na memória), você pode interceptar pacotes PCM via set_stream_reader():
void read_data_stream(const uint8_t *data, uint32_t length) {
int16_t *samples = (int16_t*) data;
uint32_t sample_count = length / 2;
// Обработка семплов
}
void setup() {
a2dp_sink.set_stream_reader(read_data_stream, false); // false отключает вывод на I2S
a2dp_sink.start("DSP_Node");
}
O codec padrão é SBC padrão. Em versões recentes, o autor adicionou suporte experimental para conectar decodificadores externos via add_decoder(), incluindo AAC (embora para funcionalidade completa no lado do IDF, será necessário ESP-IDF versão 6.1 ou superior).
Limitações que você deve conhecer
- Os perfis HFP e HSP (chamadas viva voz, chamadas com microfone) não são suportados. O projeto é estritamente sobre transmissão de fluxo de mídia.
- A biblioteca é exigente em recursos da pilha Bluetooth. Combinar tráfego Wi-Fi pesado e streaming A2DP estável em um ESP32 básico pode ser complicado, e gagueira no áudio é possível.
- A documentação no repositório é detalhada, mas o autor é rigoroso quanto ao formato dos relatórios de bugs: antes de abrir uma issue no GitHub, certifique-se de verificar as versões das bibliotecas e do core ESP32.
Para quem será útil
O projeto será útil se você quiser dar nova vida a um gravador de fitas antigo, integrar um receptor sem fio a um amplificador vintage ou construir um sintetizador personalizado que transmite áudio diretamente para fones de ouvido sem fio.
A biblioteca é compatível com Arduino IDE, PlatformIO e ESP-IDF puro como componente. Para começar, você só precisa de uma placa ESP32 WROOM e uma placa DAC baseada em PCM5102A por poucos dólares.
Projetos relacionados