Neste artigo, vais aprender a usar um módulo PCM5102A com o ESP32 para reproduzir áudio. O PCM5102A é um conversor digital-analógico (DAC) de alto desempenho, que comunica através da interface I2S (Inter-IC Sound).
Converte dados de áudio num sinal analógico de nível de linha, adequado para amplificação ou uso direto com auscultadores de alta impedância. O módulo inclui uma saída estéreo de 3,5 mm e funciona com uma alimentação padrão de 3,3 V a 5 V, tornando-o compatível com microcontroladores comuns como o ESP32 ou placas Arduino.
Ao longo deste tutorial, vais aprender a gerar sinais de áudio, converter texto em fala, fazer streaming de rádio da internet, reproduzir ficheiros MP3 a partir de um cartão SD e utilizar áudio por Bluetooth.
Componentes Necessários
Vais precisar de um módulo PCM5102A. Existem diferentes versões de placas baseadas no PCM5102A, mas todas devem funcionar para este tutorial. Usei a placa listada abaixo, que tem uma saída de linha com jack de 3,5 mm.

Também vais precisar de um par de colunas ativas, de preferência com ficha de 3,5 mm para ligar diretamente a esta placa PCM5102A.
Para reproduzir ficheiros MP3 a partir de um cartão SD, vais precisar ainda de um cartão SD com pelo menos 1GB e de um módulo leitor de cartões SD.
Por fim, vais precisar de um ESP32, uma breadboard e alguns cabos. Usei um ESP32 lite, mas a maioria das outras placas ESP32 também deve funcionar. Deves optar por uma placa ESP32-S3 com PSRAM se planeias guardar e reproduzir música a partir da memória.

Amplificador PCM5102

Coluna Ativa

Leitor de Cartões Micro SD

Cartão Micro SD 8GB

ESP32 lite

Cabo de Dados USB

Conjunto de Cabos Dupont

Breadboard
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
O Protocolo de Áudio I2S
Vamos começar com uma breve introdução ao protocolo I2S, usado para transferir dados de áudio digitalmente de um ESP32 para um DAC como o PCM5102A.
I2S, ou Inter-IC Sound, é um padrão de interface de barramento série utilizado para ligar dispositivos de áudio digitais. Foi introduzido pela Philips nos anos 80 para simplificar a transmissão de dados de áudio entre circuitos integrados. Ao contrário de protocolos como SPI ou I2C, o I2S foi desenhado especificamente para aplicações de áudio, garantindo temporização e sincronização precisas dos fluxos de áudio.
No essencial, o I2S transfere dados de áudio em Modulação por Código de Pulsos (PCM) de forma síncrona. O protocolo utiliza três sinais principais: o relógio série (SCK), a seleção de palavra (WS) e os dados série (SD). O relógio série pulsa à taxa de bits, ditando quando os bits são enviados. O sinal de seleção de palavra alterna para indicar se os dados atuais correspondem ao canal de áudio esquerdo ou direito. Por fim, a linha de dados série transporta os bits de áudio reais, transmitindo primeiro o bit mais significativo (MSB).

Normalmente, os dados de áudio são enviados em palavras de 16 ou 24 bits, mas o protocolo pode suportar outras profundidades de bits dependendo do hardware.
O periférico I2S do ESP32 suporta comunicação full-duplex, permitindo entrada e saída de áudio em simultâneo. Pode ser configurado para funcionar em modo mestre ou escravo. Quando atua como mestre, o ESP32 gera os sinais de relógio e seleção de palavra. Esta é a configuração mais comum ao ligar ao DAC PCM5102A, que funciona como dispositivo escravo I2S. Na próxima secção vamos analisar o PCM5102A com mais detalhe.
Características Técnicas do Módulo PCM5102
Os módulos PCM5102A são baseados no Texas Instruments PCM5102A, um conversor digital-analógico estéreo desenhado para reprodução de áudio de alta resolução. Converte fluxos de áudio digitais em sinais analógicos de nível de linha usando um caminho de sinal totalmente integrado que minimiza a necessidade de componentes externos.
O PCM5102A suporta saída estéreo com uma saída analógica típica de cerca de 2,1 Vrms centrada à terra, permitindo ligação direta de nível de linha sem necessidade de condensadores de bloqueio DC externos.
O módulo usado neste tutorial vem com um jack estéreo de 3,5 mm para saída de áudio, além de componentes passivos integrados para filtragem de saída e acoplamento AC. Existem também pontes de solda para seleção de funções – mais sobre isso adiante. A imagem abaixo mostra a frente e o verso do Módulo PCM5102:

Entrada de Áudio Digital e Clock
Os dados de áudio digitais são aceites através de uma interface PCM padrão compatível com os formatos I2S e left-justified, suportando dados de 16, 24 e 32 bits. São suportadas taxas de amostragem de 8 kHz até 384 kHz, tornando o dispositivo adequado tanto para áudio standard como de alta resolução.
O PCM5102A inclui um PLL integrado de alto desempenho que pode sintetizar o relógio interno de alta velocidade necessário para os motores de interpolação e DAC, a partir do bit clock (BCK) ou de um relógio de sistema externo opcional (SCK). Isto reduz a necessidade de uma fonte de relógio mestre externa e permite operar com uma ligação I2S de 3 fios (LRCK, BCK, DIN).
Processamento Digital de Sinal e Filtros
Depois de receber os dados de áudio digitais, o caminho de sinal interno do PCM5102A inclui filtros digitais de interpolação que sobremostram o sinal de entrada por um fator (por exemplo, 128× a taxa de amostragem) antes da conversão. Estas etapas de interpolação reduzem o ruído de quantização e simplificam os requisitos do filtro de reconstrução analógico.
Etapa de Saída Analógica e Driver de Linha
A etapa de saída analógica do módulo integra um driver de linha DirectPath™ com bomba de carga, capaz de fornecer uma saída de nível de linha para cargas tão baixas quanto 1 kΩ por canal. Circuitos inteligentes de soft-ramp e mute analógico ajudam a evitar ruídos audíveis durante o arranque, desligar ou erros de clock.
Nota que não podes ligar uma coluna passiva diretamente à saída de linha do PCM5102A. Vais precisar de um amplificador extra ou de uma coluna ativa!
Alimentação, Controlo e Condições de Funcionamento
O módulo PCM5102A foi desenhado para funcionar com uma única fonte de alimentação, entre 3,3V e 5V. Internamente, o DAC usa reguladores de baixa queda integrados para gerar as tensões necessárias para o núcleo e para a alimentação analógica.
O PCM5102A entra automaticamente em modo de baixo consumo quando os clocks I2S estão inativos para reduzir o consumo de energia. Pinos de hardware e pontes de solda permitem configurar o formato de áudio (por exemplo, I2S vs left-justified) e o estado de mute suave, sem necessidade de interface de controlo série (I2C/SPI) para funcionamento básico.
Pinout
A imagem seguinte mostra o pinout da placa PCM5102A:

Na parte superior da placa estão os pinos para Line Out: LROUT = Canal Esquerdo, ROUT = Canal Direito, AGNG = Terra) e pinos para controlar funções específicas da placa (mais sobre isso na próxima secção).
As funções também podem ser definidas através de pontes de solda na parte de trás da placa (H1L, H2L, H3L, H4L). E o jack de 3,5 mm duplica os pinos de Line Out.
No lado direito encontras os pinos para a interface I2S (SCK, BCK, DIN, LRCK) e os pinos para a alimentação (VIN, GND). Os pinos I2S passam por um pack de resistências que deve permitir sinais de 5V, por exemplo, de um Arduino UNO. A tensão de alimentação pode ser 3,3V ou 5V, já que a placa tem reguladores internos de baixa queda.
Jumpers de Função
O módulo PCM5102A tem cinco jumpers/pontes de solda que precisam de ser configurados corretamente para a placa funcionar com um ESP32. Eles alternam as funções abaixo, dependendo se estão ligados a High ou Low. As configurações recomendadas estão a negrito:
- SCK = Master Clock: Low se não for fornecido sinal de relógio mestre externo
- H1L / FLT = Seleção de filtro: Latência normal ( Low ) / Latência baixa (High)
- H2L / DEMP = Controlo de de-ênfase para taxa de amostragem de 44,1kHz: Off ( Low ) / On (High)
- H3L / XSMT = Controlo de mute suave: Mute suave (Low) / un-mute suave ( High )
- H4L / FMT = Seleção do formato de áudio I2S: Right justified ( Low ) / Left justified (High)
A seleção de filtro com latência normal é um FIR com boa resposta, atrasando o sinal cerca de 500us (a 44,1 kHz). O filtro de baixa latência é um IIR com resposta ligeiramente inferior e atrasa o sinal cerca de 80us. Muito poucas (se algumas) fontes de áudio têm pré-ênfase aplicada, por isso o controlo de de-ênfase deve estar desligado. O pino XSMT permite silenciar a saída via GPO ou interruptor se a ponte H3L não estiver soldada. E para o formato de áudio I2S escolhemos right justified.
A imagem abaixo mostra o esquema do módulo PCM5102A com estes jumpers de função assinalados a amarelo:

Pontes de Solda
Podes encontrar quatro pontes de solda H1L, H2L, H3L e H4L na parte de trás da placa. A imagem abaixo mostra-te quais as pontes que deves soldar a High (H) ou Low (H):

Em alternativa, há também 4 pinos (FLT, DEMP, XMST, FMT) na parte superior da placa que podes ligar a 3,3V (H) ou GND (L) para controlar as funções da placa. Isto é útil se quiseres usar interruptores ou GPIO para controlar funções. Especialmente a função de mute (XMST/H3L) pode ser interessante. Mas certifica-te de que usas ou os pinos ou as pontes, mas nunca ambos ao mesmo tempo!
Ponte SCK
Há uma única ponte na frente da placa que também precisas de soldar. Controla se é fornecido ou não um sinal de relógio mestre externo (SCK). No nosso caso não fornecemos SCK e, por isso, é necessário soldar a ponte SCK para ligar o SCK à terra.

Especificações Técnicas
A tabela seguinte resume as especificações técnicas do PCM5102A:
| Parâmetro | Especificação |
|---|---|
| Dispositivo DAC | Texas Instruments PCM5102A |
| Arquitetura Interna | DAC delta-sigma de alto desempenho com filtros digitais de interpolação |
| Interface de Áudio Digital | I2S standard, left-justified, right-justified, DSP (PCM) |
| Taxas de Amostragem Suportadas | 8 kHz a 384 kHz |
| Profundidade de Bits Suportada | 16-bit, 24-bit, 32-bit |
| Requisitos de Clock de Entrada | Bit Clock (BCK), Word Clock (LRCK); relógio de sistema externo opcional (SCK) |
| Clocking | PLL integrado com multiplicador de clock de alto desempenho; geração interna de clock mestre |
| Sobreamostragem de Filtro Digital | Vários estágios com alta taxa de sobreamostragem (ex: 128 x fs) |
| Tipo de Saída Analógica | Saídas internas diferenciais com filtragem passiva onboard para single-ended |
| Conector de Saída | Jack estéreo de 3,5 mm |
| Tensão de Saída em Escala Total | Aprox. 2,1 Vrms (centrado à terra) |
| Carga de Saída Suportada | Desenhado para cargas de nível de linha ≥ 1 kΩ por canal |
| Relação Sinal-Ruído (SNR) | ~112 dB (A-weighted, típico) |
| Distorção Harmónica Total + Ruído (THD+N) | ~-93 dB (típico) |
| Driver de Linha | Driver de linha DirectPath™ com bomba de carga integrado |
| Mute/Soft-Ramp | Mute analógico integrado no arranque/desligar ou perda de clock |
| Tensão de Alimentação (AVDD) | 3,3 V … 5V |
| Níveis Lógicos | Compatível LVCMOS com níveis de I/O do ESP32 |
| Interface de Controlo | Configuração por pinos de hardware (não é necessário I2C/SPI para funcionamento básico) |
| Consumo de Energia | Modo de baixo consumo automático quando os clocks estão inativos |
Para mais informações consulta a folha de dados do PCM5102A que está ligada abaixo:
Ligar o PCM5102A ao ESP32
Nesta secção vamos ligar o PCM5102A ao ESP32. Começamos por ligar o VIN do PCM5102A aos 3,3V do ESP32 e o GND ao G. Depois ligamos os pinos I2S conforme mostra a tabela seguinte:
| PCM5102A | ESP32 |
|---|---|
| VIN | 3V3 |
| GND | G |
| LRCK | 32 |
| BCK | 25 |
| DIN | 33 |
| SCK | G |
Se soldaste a ponte SCK, na verdade não precisas de ligar o SCK à terra, mas não faz mal se o fizeres.
A imagem seguinte mostra toda a cablagem do módulo PCM5102A com um ESP32 lite. Nota que deves ligar a saída de linha do PCM5102A a um amplificador ou coluna ativa. Não deves ligar uma coluna passiva ou auscultadores de baixa impedância diretamente ao PCM5102A.

Leitor de Cartões SD
Se quiseres reproduzir ficheiros de áudio, precisas de ligar um leitor de cartões SD que armazene os ficheiros de áudio num cartão SD. O diagrama de ligação abaixo mostra como ligar um leitor de cartões SD e o PCM5102A a um ESP32:

O Leitor de Cartões SD comunica via SPI e os pinos SPI por defeito do ESP32 são CS=5, MOSI=23, CLK=18 e MISO=19. A tabela abaixo resume as ligações que precisas de fazer entre o Leitor de Cartões SD e o ESP32:
| Leitor de Cartões SD | ESP32 |
|---|---|
| 3V3 | 3V |
| GND | G |
| CS/SS | 5 |
| MOSI | 23 |
| CLK/SCK | 18 |
| MISO | 19 |
Se não tens a certeza de quais são os pinos SPI por defeito do teu ESP32, consulta o tutorial Find I2C and SPI default pins.
A foto seguinte mostra a minha ligação do PCM5102A com um Leitor de Cartões SD, um ESP32 e uma pequena coluna ativa mono para testes:

Instalar Bibliotecas
Neste tutorial vamos usar a biblioteca arduino-audio-tools de Phil Schatzmann para enviar dados de áudio para o PCM5102A. Para instalar a biblioteca, vai ao arduino-audio-tools repo, clica no botão verde “<> Code” e depois em “Download ZIP” para descarregar a biblioteca em formato ZIP, como mostrado abaixo:

Depois abre um Sketch, vai a Sketch -> Include Library -> Add .ZIP Library … para instalar a biblioteca ZIP descarregada (arduino-audio-tools-main.zip):

Para alguns exemplos de código precisamos de mais duas bibliotecas do Phil Schatzmann; nomeadamente a arduino-libhelix e a ESP32-A2DP. Podes instalá-las da mesma forma. Clica no link para ir ao repositório github, clica no botão verde “<> Code” para descarregar as bibliotecas (arduino-libhelix-main.zip, ESP32-A2DP-main.zip) e depois instala-as.
Finalmente, se é a primeira vez que programas uma placa ESP32 a partir do Arduino IDE, também vais precisar de instalar o core do ESP32. Para mais detalhes consulta o tutorial Install ESP32 core in Arduino IDE.
Exemplo de Código: Tom de Teste
Neste primeiro exemplo vamos apenas gerar um tom de teste em forma de onda senoidal e enviá-lo pela interface I2S para o DAC
/*
www.makerguides.com
Libraries:
- ESP32 Core 3.3.6
- [arduino-audio-tools](https://github.com/pschatzmann/arduino-audio-tools)
Version: 1.2.2
- [arduino-libhelix](https://github.com/pschatzmann/arduino-libhelix)
*/
#include "AudioTools.h"
// PCM5102A
#define DIN_PIN 33 // serial data
#define LRCK_PIN 32 // word select
#define BCLK_PIN 25 // serial clock
#define VOLUME 1000 // max 32000
AudioInfo info(44100, 2, 16);
SineWaveGenerator<int16_t> sineWave(VOLUME);
GeneratedSoundStream<int16_t> sound(sineWave);
I2SStream out;
StreamCopy copier(out, sound);
void setup(void) {
auto config = out.defaultConfig(TX_MODE);
config.copyFrom(info);
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
out.begin(config);
sineWave.begin(info, N_B4);
}
void loop() {
copier.copy();
}
Imports
O código começa por incluir a biblioteca AudioTools.h, que fornece classes e funções para gerar e transmitir sinais de áudio facilmente no ESP32.
#include "AudioTools.h";
Constantes
De seguida, definimos os pinos ligados ao módulo DAC PCM5102A. Estes pinos correspondem aos sinais da interface I2S: DIN_PIN para dados série, LRCK_PIN para seleção de palavra (relógio esquerda-direita), e BCLK_PIN para o relógio série. Também definimos uma constante VOLUME para definir a amplitude da onda senoidal gerada, com amplitude máxima de 32000.
#define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock #define VOLUME 1000 // max 32000
Configuração de Áudio e Objetos
O objeto AudioInfoinfo define o formato de áudio: taxa de amostragem de 44.100 Hz, 2 canais (estéreo) e 16 bits por amostra. Isto corresponde a áudio de qualidade de CD.
O objeto SineWaveGeneratorsineWave é usado para gerar um sinal de áudio em onda senoidal com o volume especificado. Produz amostras de inteiros de 16 bits com sinal.
O objeto GeneratedSoundStreamsound envolve o gerador de onda senoidal numa interface de stream, facilitando o envio dos dados de áudio para uma saída.
O objeto I2SStreamout representa a interface de saída I2S no ESP32, que irá enviar os dados de áudio para o DAC PCM5102A.
Por fim, o objeto StreamCopycopier é responsável por copiar continuamente os dados de áudio do stream gerado para o stream de saída I2S.
AudioInfo info(44100, 2, 16); SineWaveGenerator<int16_t> sineWave(VOLUME); GeneratedSoundStream<int16_t> sound(sineWave); I2SStream out; StreamCopy copier(out, sound);
Função Setup
Dentro da função setup(), configuramos o stream de saída I2S. Começamos por obter uma configuração por defeito para o modo de transmissão usando out.defaultConfig(TX_MODE). Depois, copiamos as definições de formato de áudio do objeto info para esta configuração.
De seguida, atribuímos os pinos I2S à configuração: BCLK_PIN para o bit clock, LRCK_PIN para o word select, e DIN_PIN para a linha de dados série.
Depois de configurar os pinos, inicializamos o stream de saída I2S com out.begin(config). Por fim, iniciamos o gerador de onda senoidal com o formato de áudio e uma frequência de nota musical predefinida N_B4 (que corresponde à nota B4).
void setup(void) {
auto config = out.defaultConfig(TX_MODE);
config.copyFrom(info);
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
out.begin(config);
sineWave.begin(info, N_B4);
}
Função Loop
A função loop() copia continuamente os dados de áudio do stream do gerador de onda senoidal para o stream de saída I2S usando copier.copy(). Isto mantém o áudio a tocar indefinidamente sem interrupção.
void loop() {
copier.copy();
}
Exemplo de Código: Bluetooth
Este próximo código demonstra como reproduzir áudio via Bluetooth num ESP32 usando o DAC PCM5102A. Utiliza várias bibliotecas para gerir o streaming de áudio e a funcionalidade Bluetooth A2DP (Advanced Audio Distribution Profile). O ESP32 recebe dados de áudio via Bluetooth e envia-os pela interface I2S para o conversor digital-analógico PCM5102A.
/*
www.makerguides.com
Libraries:
- ESP32 Core 3.3.6
- [arduino-audio-tools](https://github.com/pschatzmann/arduino-audio-tools)
Version: 1.2.2
- [arduino-libhelix](https://github.com/pschatzmann/arduino-libhelix)
Version: 0.9.2
- [ESP32-A2DP](https://github.com/pschatzmann/ESP32-A2DP)
Version: 1.8.8
*/
#include "AudioTools.h"
#include "BluetoothA2DPSink.h"
#define DIN_PIN 33 // serial data
#define LRCK_PIN 32 // word select
#define BCLK_PIN 25 // serial clock
I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);
void setup() {
auto config = i2s.defaultConfig();
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
i2s.begin(config);
a2dp_sink.start("MyMusic");
}
void loop() { }
Imports
O código começa por incluir as bibliotecas necessárias. AudioTools.h fornece capacidades de streaming de áudio, enquanto BluetoothA2DPSink.h trata da funcionalidade Bluetooth A2DP sink, permitindo ao ESP32 receber streams de áudio de fontes Bluetooth como smartphones.
#include "AudioTools.h" #include "BluetoothA2DPSink.h"
Constantes
De seguida, três constantes definem os pinos GPIO usados para a interface I2S, que é o protocolo de comunicação entre o ESP32 e o DAC PCM5102A. Estes pinos correspondem à linha de dados série (DIN_PIN), à linha de seleção de palavra ou relógio esquerda-direita (LRCK_PIN) e à linha de bit clock (BCLK_PIN).
#define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock
Objetos
É criado um objeto I2SStreami2s para gerir o stream de dados de áudio I2S. Depois, é instanciado um objeto BluetoothA2DPSinka2dp_sink, passando o stream i2s como parâmetro. Isto liga diretamente a entrada de áudio Bluetooth à saída I2S, permitindo reprodução de áudio sem interrupções.
I2SStream i2s; BluetoothA2DPSink a2dp_sink(i2s);
Função Setup
Dentro da função setup(), a interface I2S é configurada. A configuração I2S por defeito é obtida via i2s.defaultConfig(), e os pinos relevantes para bit clock, word select e dados são atribuídos conforme as constantes definidas anteriormente. A interface I2S é então inicializada com esta configuração usando i2s.begin(config).
Depois de configurar a interface I2S, o Bluetooth A2DP sink é iniciado com o nome de dispositivo "MyMusic". Isto torna o ESP32 visível como recetor de áudio Bluetooth com esse nome, permitindo que outros dispositivos se liguem e transmitam áudio.
void setup() {
auto config = i2s.defaultConfig();
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
i2s.begin(config);
a2dp_sink.start("MyMusic");
}
Abre o teu telemóvel e procura dispositivos Bluetooth para encontrar o dispositivo "MyMusic" e começa a reproduzir música.
Função Loop
A função loop() está vazia porque o Bluetooth A2DP sink e o stream I2S tratam do processamento e reprodução de áudio de forma assíncrona. Depois da configuração, o ESP32 fica continuamente à escuta de streams de áudio Bluetooth e envia-os pela interface I2S sem necessidade de código adicional no loop principal.
void loop() { }
Exemplo de Código: Rádio da Internet
Este código demonstra como construir um leitor de rádio da internet usando um microcontrolador ESP32 e um módulo DAC PCM5102A. Liga-se a uma rede WiFi, faz streaming de áudio MP3 de uma estação de rádio online, descodifica o áudio e envia-o pela interface I2S para o DAC. O código também trata dos metadados do stream, como títulos de músicas.
/*
www.makerguides.com
Libraries:
- ESP32 Core 3.3.6
- [arduino-audio-tools](https://github.com/pschatzmann/arduino-audio-tools)
Version: 1.2.2
- [arduino-libhelix](https://github.com/pschatzmann/arduino-libhelix)
Version: 0.9.2
*/
#include <Arduino.h>
#include <WiFi.h>
#include <Wire.h>
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#include "AudioTools/Communication/HTTP/ICYStream.h"
// PCM5102A
#define DIN_PIN 33 // serial data
#define LRCK_PIN 32 // word select
#define BCLK_PIN 25 // serial clock
#define VOLUME 0.3 // Volume
const char* ssid = "ssid";
const char* password = "pwd";
const char* url = "https://jazz.stream.laut.fm/jazz";
ICYStream icystream;
I2SStream i2s;
VolumeStream volume(i2s);
EncodedAudioStream mp3decode(&volume, new MP3DecoderHelix());
StreamCopy copier(mp3decode, icystream);
void callbackMetadata(MetaDataType type, const char* str, int len) {
Serial.printf("%s: %s\n", toStr(type), str);
}
void setup() {
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
auto config = i2s.defaultConfig(TX_MODE);
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
i2s.begin(config);
volume.begin(config);
volume.setVolume(VOLUME);
mp3decode.begin();
icystream.begin(url);
icystream.setMetadataCallback(callbackMetadata);
}
void loop() {
copier.copy();
}
Imports
O código começa por incluir várias bibliotecas necessárias para conectividade WiFi, saída de áudio I2S e descodificação MP3. As bibliotecas Arduino.h e WiFi.h fornecem funções básicas de Arduino e WiFi. A biblioteca Wire.h é incluída mas não é usada explicitamente aqui. A biblioteca AudioTools e os seus componentes relacionados tratam do streaming, descodificação e reprodução de áudio.
#include <Arduino.h> #include <WiFi.h> #include <Wire.h> #include "AudioTools.h" #include "AudioTools/AudioCodecs/CodecMP3Helix.h" #include "AudioTools/Communication/HTTP/ICYStream.h"
Constantes e Definições de Pinos
De seguida, o código define os pinos usados para a interface I2S para comunicar com o DAC PCM5102A. Estes pinos correspondem aos sinais de dados série, seleção de palavra (relógio esquerda-direita) e bit clock. Adicionalmente, é definido um nível de volume como constante float.
#define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock #define VOLUME 0.3 // Volume
As credenciais WiFi e o URL do stream de rádio da internet são guardados como ponteiros constantes para caracteres.
const char* ssid = "ssid"; const char* password = "pwd"; const char* url = "https://jazz.stream.laut.fm/jazz";
Objetos de Stream de Áudio
São criados vários objetos para gerir o pipeline de streaming de áudio. O objeto ICYStream trata do streaming HTTP dos dados MP3 a partir do URL da rádio da internet, enquanto o objeto I2SStream gere a comunicação I2S com o DAC. O objeto VolumeStream permite ajustar o volume de reprodução e está ligado ao stream I2S. E o objeto EncodedAudioStream descodifica os dados MP3 usando o descodificador Helix MP3 e envia o áudio descodificado para o stream de volume. Por fim, o objeto StreamCopy copia os dados de áudio descodificados do descodificador MP3 para o stream ICY.
ICYStream icystream; I2SStream i2s; VolumeStream volume(i2s); EncodedAudioStream mp3decode(&volume, new MP3DecoderHelix()); StreamCopy copier(mp3decode, icystream);
Função de Callback de Metadados
A função callbackMetadata é definida para tratar metadados recebidos do stream de rádio da internet, como o título ou artista da música atual. Imprime o tipo de metadados e o conteúdo no monitor série para debug ou apresentação.
void callbackMetadata(MetaDataType type, const char* str, int len) {
Serial.printf("%s: %s\n", toStr(type), str);
}
Função Setup
Na função setup(), a comunicação série é inicializada a 115200 baud para saída de debug. O logger de áudio também é iniciado para capturar avisos e erros. O ESP32 tenta ligar-se à rede WiFi especificada, tentando novamente a cada 500 milissegundos até ter sucesso.
Depois de estabelecida a ligação WiFi, a interface I2S é configurada para modo de transmissão com os pinos especificados para bit clock, word select e dados. O stream I2S e o stream de volume são inicializados com esta configuração, e o volume é definido para o nível predefinido.
O descodificador MP3 é iniciado e o stream ICY é inicializado com o URL da rádio da internet. A função de callback de metadados é registada para receber e tratar metadados do stream.
void setup() {
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
auto config = i2s.defaultConfig(TX_MODE);
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
i2s.begin(config);
volume.begin(config);
volume.setVolume(VOLUME);
mp3decode.begin();
icystream.begin(url);
icystream.setMetadataCallback(callbackMetadata);
}
Função Loop
A função loop() copia continuamente os dados de áudio do stream de rádio da internet através do descodificador MP3 e controlo de volume para a saída I2S. Isto mantém a reprodução de áudio a funcionar indefinidamente.
void loop() {
copier.copy();
}
Exemplo de Código: Reproduzir MP3 do Cartão SD
O código seguinte demonstra como reproduzir ficheiros de áudio MP3 guardados num cartão SD usando um microcontrolador ESP32 e o módulo DAC PCM5102A. Utiliza as bibliotecas arduino-audio-tools e arduino-libhelix para tratar da descodificação e reprodução de áudio via interface I2S.
/*
www.makerguides.com
Libraries:
- ESP32 Core 3.3.6
- [arduino-audio-tools](https://github.com/pschatzmann/arduino-audio-tools)
Version: 1.2.2
- [arduino-libhelix](https://github.com/pschatzmann/arduino-libhelix)
Version: 0.9.2
*/
#include "AudioTools.h"
#include "AudioTools/Disk/AudioSourceSD.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
// PCM5102A
#define DIN_PIN 33 // serial data
#define LRCK_PIN 32 // word select
#define BCLK_PIN 25 // serial clock
#define VOLUME 0.5 // Volume
#define PATH "/"
#define EXT "mp3"
AudioSourceSD source(PATH, EXT);
I2SStream i2s;
MP3DecoderHelix decoder;
AudioPlayer player(source, i2s, decoder);
void printMetaData(MetaDataType type, const char* str, int len){
Serial.printf("%s: %s\n", toStr(type), str);
}
void setup() {
Serial.begin(115200);
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
auto config = i2s.defaultConfig(TX_MODE);
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
i2s.begin(config);
player.setMetadataCallback(printMetaData);
player.setVolume(VOLUME);
player.begin();
}
void loop() {
player.copy();
}
Imports
No início, o código inclui vários ficheiros de cabeçalho das bibliotecas de áudio. Estes fornecem as classes e funções necessárias para ler ficheiros de áudio do cartão SD, descodificar streams MP3 e enviar áudio via I2S.
#include "AudioTools.h" #include "AudioTools/Disk/AudioSourceSD.h" #include "AudioTools/AudioCodecs/CodecMP3Helix.h"
Constantes e Definições de Pinos
O código define os pinos usados para ligar o ESP32 ao DAC PCM5102A. Estes pinos correspondem aos sinais I2S: dados série (DIN), seleção de palavra (LRCK) e relógio série (BCLK). Adicionalmente, é definido um nível de volume como valor float entre 0.0 e 1.0.
#define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock #define VOLUME 0.5 // Volume
As constantes PATH e EXT especificam o diretório raiz e a extensão dos ficheiros de áudio a reproduzir, que neste caso são ficheiros MP3 localizados na raiz do cartão SD.
#define PATH "/" #define EXT "mp3"
Objetos de Áudio
São instanciados vários objetos para gerir a reprodução de áudio. AudioSourceSD trata da leitura de ficheiros de áudio do cartão SD que correspondam ao caminho e extensão especificados. I2SStream gere o stream de saída de áudio I2S. MP3DecoderHelix é o descodificador MP3 baseado na biblioteca Helix. Por fim, AudioPlayer liga estes componentes para coordenar a reprodução.
AudioSourceSD source(PATH, EXT); I2SStream i2s; MP3DecoderHelix decoder; AudioPlayer player(source, i2s, decoder);
Função de Callback de Metadados
A função printMetaData() é definida para receber informações de metadados como artista, título ou álbum dos ficheiros MP3 durante a reprodução. Imprime esta informação no monitor série usando Serial.printf. A função recebe o tipo de metadados, uma string com os metadados e o seu comprimento como parâmetros.
void printMetaData(MetaDataType type, const char* str, int len){
Serial.printf("%s: %s\n", toStr(type), str);
}
Função Setup
Na função setup(), a comunicação série é inicializada a 115200 baud para permitir saída de debug. O nível de logging de áudio é definido para Warning para reduzir a verbosidade.
De seguida, a interface I2S é configurada para modo de transmissão. A configuração I2S por defeito é obtida e modificada para atribuir os pinos definidos anteriormente para bit clock, word select e dados. O stream I2S é então iniciado com esta configuração.
O leitor de áudio é configurado para usar o callback printMetaData para mostrar metadados durante a reprodução. O volume é definido para o nível especificado e o leitor é iniciado com player.begin().
void setup() {
Serial.begin(115200);
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
auto config = i2s.defaultConfig(TX_MODE);
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
i2s.begin(config);
player.setMetadataCallback(printMetaData);
player.setVolume(VOLUME);
player.begin();
}
Função Loop
A função loop() chama continuamente player.copy(), que processa os dados de áudio lendo do cartão SD, descodificando o stream MP3 e enviando as amostras de áudio para a saída I2S. Isto mantém a reprodução de áudio a funcionar sem interrupções.
void loop() {
player.copy();
}
Exemplo de Código: Texto para Fala
Este exemplo de código demonstra como implementar um sistema Texto-para-Fala (TTS) num ESP32 usando a API TTS da OpenAI e enviar o áudio através de um DAC PCM5102A. Os dados de áudio são transmitidos em formato MP3, descodificados e reproduzidos via interface I2S. O programa liga-se ao WiFi, envia texto para a API da OpenAI, recebe a fala sintetizada e reproduz em tempo real.
/*
www.makerguides.com
Libraries:
- ESP32 Core 3.3.6
- [arduino-audio-tools](https://github.com/pschatzmann/arduino-audio-tools)
Version: 1.2.2
- [arduino-libhelix](https://github.com/pschatzmann/arduino-libhelix)
Version: 0.9.2
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
// PCM5102A
#define DIN_PIN 33 // serial data
#define LRCK_PIN 32 // word select
#define BCLK_PIN 25 // serial clock
// Text to Speech
#define TTS_MODEL "gpt-4o-mini-tts"
#define TTS_VOICE "marin"
#define TTS_VOLUME 0.6
// WiFi credentials
const char* ssid = "ssid";
const char* password = "pwd";
// OpenAI configuration
const char* openaiHost = "api.openai.com";
const int openaiPort = 443;
const char* openaiApiKey = "apikey";
WiFiClientSecure client;
I2SStream i2s;
VolumeStream volume(i2s);
EncodedAudioStream mp3decode(&volume, new MP3DecoderHelix());
StreamCopy copier(mp3decode, client);
void text2speech(const char* text) {
client.setInsecure();
if (!client.connect("api.openai.com", 443)) {
Serial.println("Connection failed");
return;
}
String body = String("{") +
"\"model\":\"" + TTS_MODEL + "\"," +
"\"voice\":\"" + TTS_VOICE + "\"," +
"\"format\":\"mp3\"," +
"\"input\":\"" + text + "\"" +
"}";
client.println("POST /v1/audio/speech HTTP/1.1");
client.println("Host: api.openai.com");
client.println("Authorization: Bearer " + String(openaiApiKey));
client.println("Content-Type: application/json");
client.print("Content-Length: ");
client.println(body.length());
client.println();
client.print(body);
// ---- Skip HTTP headers ----
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") break;
}
}
void setup() {
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
auto config = i2s.defaultConfig(TX_MODE);
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
i2s.begin(config);
mp3decode.begin();
volume.begin(config);
volume.setVolume(TTS_VOLUME);
text2speech("Hello, how are you doing today?");
}
void loop() {
copier.copy();
}
Imports
O código começa por incluir as bibliotecas necessárias. Arduino.h fornece as funções principais do Arduino. WiFi.h e WiFiClientSecure.h tratam da ligação WiFi e comunicação HTTPS segura. A biblioteca AudioTools e o seu codec MP3 CodecMP3Helix são usados para streaming e descodificação de áudio.
#include <Arduino.h> #include <WiFi.h> #include <WiFiClientSecure.h> #include "AudioTools.h" #include "AudioTools/AudioCodecs/CodecMP3Helix.h"
Constantes e Definições de Pinos
Os pinos para o DAC PCM5102A são definidos para configurar a interface I2S. Incluem o pino de dados série (DIN_PIN), pino de seleção de palavra (LRCK_PIN) e pino de relógio série (BCLK_PIN). Adicionalmente, são definidas constantes para o modelo TTS, voz e nível de volume. As credenciais WiFi e detalhes da API OpenAI também são declarados como ponteiros constantes para caracteres.
// PCM5102A #define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock // Text to Speech #define TTS_MODEL "gpt-4o-mini-tts" #define TTS_VOICE "marin" #define TTS_VOLUME 0.6 // WiFi credentials const char* ssid = "ssid"; const char* password = "pwd"; // OpenAI configuration const char* openaiHost = "api.openai.com"; const int openaiPort = 443; const char* openaiApiKey = "apikey";
Podes experimentar outros modelos TTS, como “tts-1” e outras vozes como “alloy”, “ash”, “coral”, “echo”, “fable”, “onyx”, “nova”, ‘sage”, “shimmer”, “marin”, “cedar”. Para mais detalhes consulta platform.openai.com/docs/guides/text-to-speech.
As credenciais WiFi e detalhes da API OpenAI também são guardados como strings constantes. Terás de substituir “ssid” e “pwd” pelas credenciais da tua rede WiFi.
// WiFi credentials const char* ssid = "ssid"; const char* password = "pwd";
Também vais precisar de obter uma “apikey” da OpenAI. Vai a https://platform.openai.com e regista-te com um email ou uma conta Google ou Microsoft existente.
// OpenAI configuration const char* openaiHost = "api.openai.com"; const int openaiPort = 443; const char* openaiApiKey = "apikey";
Depois de verificares o teu email e completares a configuração inicial, faz login no dashboard da OpenAI, platform.openai.com/api-keys e encontra ou cria a tua API Key (=SECRET KEY) como mostrado abaixo:

A API Key é uma string única e longa, começando por “sk-proj-“, necessária para autenticar os teus pedidos à API (ver abaixo).
sk-proj-xcA.......................OtDu0U
Isto é tudo o que precisas para obter uma API key, mas recomendo que definas um limite de utilização para a tua conta. Para mais detalhes consulta o tutorial Vision Chatbot with DFRobot ESP32-S3 AI Camera and OpenAI.
Objetos de Áudio e Rede
São instanciados vários objetos para gerir o streaming de áudio e a comunicação de rede. WiFiClientSecure trata da ligação HTTPS ao servidor da OpenAI. I2SStream gere a saída de áudio I2S. O objeto VolumeStream envolve o stream I2S para controlar o volume de áudio. EncodedAudioStream é usado para descodificar o stream de áudio MP3 usando o descodificador Helix MP3. Por fim, StreamCopy copia o stream de áudio descodificado para o cliente de rede.
WiFiClientSecure client; I2SStream i2s; VolumeStream volume(i2s); EncodedAudioStream mp3decode(&volume, new MP3DecoderHelix()); StreamCopy copier(mp3decode, client);
Função Texto-para-Fala
A função text2speech() recebe uma string de texto como entrada e envia-a para a API TTS da OpenAI para gerar áudio de fala. Primeiro, configura o cliente para aceitar ligações SSL inseguras (útil para desenvolvimento). Depois tenta ligar-se ao servidor da OpenAI na porta 443. Se a ligação falhar, imprime uma mensagem de erro e retorna.
A função constrói um corpo JSON contendo o modelo TTS, voz, formato de saída (MP3) e o texto de entrada. Envia um pedido HTTP POST com os headers apropriados, incluindo o token de autorização com a API key. Depois de enviar o corpo do pedido, lê e ignora os headers da resposta HTTP para preparar o streaming dos dados de áudio.
void text2speech(const char* text) {
client.setInsecure();
if (!client.connect("api.openai.com", 443)) {
Serial.println("Connection failed");
return;
}
String body = String("{") +
"\"model\":\"" + TTS_MODEL + "\"," +
"\"voice\":\"" + TTS_VOICE + "\"," +
"\"format\":\"mp3\"," +
"\"input\":\"" + text + "\"" +
"}";
client.println("POST /v1/audio/speech HTTP/1.1");
client.println("Host: api.openai.com");
client.println("Authorization: Bearer " + String(openaiApiKey));
client.println("Content-Type: application/json");
client.print("Content-Length: ");
client.println(body.length());
client.println();
client.print(body);
// ---- Skip HTTP headers ----
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") break;
}
}
Função Setup
Na função setup(), a comunicação série é inicializada para saída de debug. O logger de áudio é configurado para reportar avisos. O ESP32 liga-se à rede WiFi especificada, aguardando até que a ligação seja estabelecida.
De seguida, a interface I2S é configurada com os pinos definidos anteriormente para bit clock, word select e saída de dados. O descodificador MP3 e o controlo de volume são inicializados com esta configuração, e o volume é definido para o nível especificado.
Por fim, a função text2speech() é chamada com um texto de saudação de exemplo, o que inicia o processo TTS e começa a transmitir áudio da API da OpenAI.
void setup() {
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
auto config = i2s.defaultConfig(TX_MODE);
config.pin_bck = BCLK_PIN;
config.pin_ws = LRCK_PIN;
config.pin_data = DIN_PIN;
i2s.begin(config);
mp3decode.begin();
volume.begin(config);
volume.setVolume(TTS_VOLUME);
text2speech("Hello, how are you doing today?");
}
Função Loop
A função loop() copia continuamente o stream de áudio MP3 descodificado do servidor da OpenAI para a saída I2S. Isto mantém a reprodução de áudio enquanto a ligação estiver aberta.
void loop() {
copier.copy();
}
Conclusões
Neste projeto, aprendeste como reproduzir áudio usando o ESP32 e o DAC PCM5102A. Explorámos os detalhes técnicos do módulo PCM5102A e como ligá-lo ao ESP32. Também aprendeste a converter texto em fala, fazer streaming de rádio da internet, reproduzir ficheiros MP3 de um cartão SD e reproduzir áudio via Bluetooth.
Se tiveres alguma dúvida, deixa-a na secção de comentários.
Boas experiências e bons projetos ; )

