In diesem Beitrag lernst du, wie du ein PCM5102A-Modul mit dem ESP32 verwendest, um Audio abzuspielen. Der PCM5102A ist ein leistungsstarker Digital-Analog-Wandler (DAC), der über die I2S (Inter-IC Sound) Schnittstelle kommuniziert.
Er wandelt Audiodaten in ein analoges Signal auf Line-Pegel um, das für Verstärkung oder die direkte Nutzung mit hochohmigen Kopfhörern geeignet ist. Das Modul verfügt über einen 3,5-mm-Stereoausgang und arbeitet mit einer Standardversorgung von 3,3 V bis 5 V, was es kompatibel mit gängigen Mikrocontrollern wie dem ESP32 oder Arduino-Boards macht.
Im Verlauf dieses Tutorials lernst du, wie man Audiosignale erzeugt, Text in Sprache umwandelt, Internetradio streamt, MP3-Dateien von einer SD-Karte abspielt und Bluetooth-Audio verwendet.
Benötigte Teile
Du benötigst ein PCM5102A-Modul. Es gibt verschiedene Versionen von PCM5102A-basierten Boards, aber alle sollten für dieses Tutorial funktionieren. Ich habe das unten aufgeführte Board verwendet, das einen 3,5-mm-Klinkenanschluss für Line-Out besitzt.

Du benötigst ein Paar aktive Lautsprecher, idealerweise mit einem 3,5-mm-Stecker, um sie direkt an das PCM5102A-Board anzuschließen.
Um MP3-Dateien von einer SD-Karte abzuspielen, brauchst du außerdem eine SD-Karte mit mindestens 1 GB und ein SD-Kartenlesermodul. Wir verwenden auch ein 10-kOhm-Potentiometer als Lautstärkeregler und einige Taster für den MP3-Player und das Internetradio, die wir bauen werden.
Schließlich benötigst du einen ESP32, ein Breadboard und einige Kabel. Ich habe einen ESP32 lite verwendet, aber die meisten anderen ESP32-Boards sollten ebenfalls funktionieren. Wenn du Musik im Speicher speichern und abspielen möchtest, solltest du ein ESP32-S3-Board mit PSRAM verwenden.

PCM5102 Verstärker

Aktiver Lautsprecher

10-kOhm-Potentiometer

Taster

Micro SD-Kartenleser

Micro SD-Karte 8GB

ESP32 lite

USB-Datenkabel

Dupont-Kabelsatz

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.
Das I2S Audio-Protokoll
Beginnen wir mit einer kurzen Einführung in das I2S-Protokoll, das verwendet wird, um Audiodaten digital vom ESP32 zu einem DAC wie dem PCM5102A zu übertragen.
I2S oder Inter-IC Sound ist ein serieller Bus-Interface-Standard zur Verbindung digitaler Audiogeräte. Er wurde in den 1980er Jahren von Philips eingeführt, um die Übertragung von Audiodaten zwischen integrierten Schaltkreisen zu vereinfachen. Im Gegensatz zu Protokollen wie SPI oder I2C ist I2S speziell für Audioanwendungen konzipiert und gewährleistet präzise Zeitsteuerung und Synchronisation von Audioströmen.
Im Kern überträgt I2S Puls-Code-Modulation (PCM) Audiodaten synchron. Das Protokoll verwendet drei Hauptsignale: den seriellen Takt (SCK), Wortauswahl (WS) und serielle Daten (SD). Der serielle Takt pulsiert mit der Bitrate und bestimmt, wann Bits gesendet werden. Das Wortauswahlsignal wechselt, um anzuzeigen, ob die aktuellen Daten dem linken oder rechten Audiokanal entsprechen. Die serielle Datenleitung überträgt die eigentlichen Audiobits, wobei das höchstwertige Bit (MSB) zuerst gesendet wird.

Typischerweise werden Audiodaten in 16-Bit- oder 24-Bit-Wörtern gesendet, aber das Protokoll unterstützt je nach Hardware auch andere Bit-Tiefen.
Das I2S-Peripheriegerät des ESP32 unterstützt Vollduplex-Kommunikation, was gleichzeitigen Audioeingang und -ausgang ermöglicht. Es kann als Master oder Slave konfiguriert werden. Als Master erzeugt der ESP32 die Takt- und Wortauswahlsignale. Dies ist die übliche Konfiguration beim Anschluss an den PCM5102A DAC, der als I2S-Slave fungiert. Im nächsten Abschnitt betrachten wir den PCM5102A genauer.
Technische Merkmale des PCM5102 Moduls
PCM5102A-Module basieren auf dem Texas Instruments PCM5102A, einem Stereo-Digital-Analog-Wandler, der für hochauflösende Audiowiedergabe entwickelt wurde. Er wandelt digitale Audioströme in analoge Line-Pegel-Signale um und verwendet einen vollständig integrierten Signalweg, der den Bedarf an externen Bauteilen minimiert.
Der PCM5102A unterstützt Stereoausgang mit einem typischen Vollaussteuerungs-Analogausgang von etwa 2,1 Vrms, bezogen auf Masse, was eine direkte Line-Pegel-Verbindung ohne externe DC-Blockkondensatoren ermöglicht.
Das in diesem Tutorial verwendete Modul verfügt über eine 3,5-mm-Stereo-Buchse für den Audioausgang sowie passive Bauteile an Bord für Ausgangsfilterung und AC-Kopplung. Es gibt auch Lötbrücken zur Funktionsauswahl – mehr dazu später. Das Bild unten zeigt Vorder- und Rückseite des PCM5102 Moduls:

Digitaler Audioeingang und Taktung
Digitale Audiodaten werden über eine standardisierte PCM-Schnittstelle akzeptiert, die I2S- und linksbündige Formate unterstützt, mit 16-, 24- und 32-Bit-Daten. Abtastraten von 8 kHz bis 384 kHz werden unterstützt, was das Gerät für Standard- und hochauflösende Audioanwendungen geeignet macht.
Der PCM5102A enthält einen leistungsstarken integrierten PLL, der den hochfrequenten internen Takt, der für die Interpolation und DAC-Engines benötigt wird, entweder aus dem Bit-Takt (BCK) oder einem optionalen externen Systemtakt (SCK) synthetisieren kann. Dies reduziert den Bedarf an einer externen Mastertaktquelle und ermöglicht den Betrieb mit einer 3-Draht-I2S-Verbindung (LRCK, BCK, DIN).
Digitale Signalverarbeitung und Filter
Nach Empfang der digitalen Audiodaten umfasst der interne Signalweg des PCM5102A digitale Interpolationsfilter, die das Eingangssignal mit einem Faktor (z. B. 128× der Abtastrate) überabtasten, bevor die Umwandlung erfolgt. Diese Interpolationsstufen reduzieren Quantisierungsrauschen und vereinfachen die Anforderungen an den analogen Rekonstruktionsfilter.
Analoge Ausgangsstufe und Line-Treiber
Die analoge Ausgangsstufe des Moduls integriert einen DirectPath™ Charge-Pump-Line-Treiber, der in der Lage ist, einen Line-Pegel-Ausgang an Lasten von bis zu 1 kΩ pro Kanal zu liefern. Intelligente Soft-Ramp- und analoge Stummschalt-Schaltungen verhindern hörbare Transienten beim Ein- und Ausschalten oder bei Taktfehlern.
Beachte, dass du einen passiven Lautsprecher nicht direkt mit dem Line-Pegel-Ausgang des PCM5102A betreiben kannst. Du benötigst einen zusätzlichen Verstärker oder aktive Lautsprecher!
Stromversorgung, Steuerung und Betriebsbedingungen
Das PCM5102A-Modul ist für den Betrieb mit einer einzigen Versorgungsspannung im Bereich von 3,3 V bis 5 V ausgelegt. Intern verwendet der DAC integrierte Low-Dropout-Regler, um die erforderlichen Kern- und Analogversorgungen zu erzeugen.
Der PCM5102A wechselt automatisch in einen Energiesparmodus, wenn die I2S-Takte inaktiv sind, um den Stromverbrauch zu reduzieren. Hardware-Pins und Lötbrücken ermöglichen die Konfiguration des Audioformats (z. B. I2S vs. linksbündig) und des Soft-Mute-Zustands, ohne dass eine serielle Steuerungsschnittstelle (I2C/SPI) für den Basisbetrieb erforderlich ist.
Pinbelegung
Das folgende Bild zeigt die Pinbelegung des PCM5102A-Boards:

Oben am Board befinden sich Pins für Line Out: LROUT = linker Kanal, ROUT = rechter Kanal, AGND = Masse, sowie Pins zur Steuerung spezifischer Funktionen des Boards (dazu mehr im nächsten Abschnitt).
Die Funktionen sind auch als Lötbrücken auf der Rückseite des Boards einstellbar (H1L, H2L, H3L, H4L). Die 3,5-mm-Buchse dupliziert die Line-Out-Pins.
Auf der rechten Seite findest du die Pins für die I2S-Schnittstelle (SCK, BCK, DIN, LRCK) und die Pins für die Stromversorgung (VIN, GND). Die I2S-Pins sind über einen Widerstandspack geführt, der 5V-Signale zulässt, z. B. von einem Arduino UNO. Die Versorgungsspannung kann 3,3 V oder 5 V betragen, da das Board interne Low-Dropout-Spannungsregler besitzt.
Funktionsjumper
Das PCM5102A-Modul verfügt über fünf Jumper/Lötbrücken, die für den Betrieb mit einem ESP32 korrekt gesetzt werden müssen. Sie schalten die folgenden Funktionen, je nachdem, ob sie auf High oder Low verbunden sind. Die gewünschten Einstellungen sind fett markiert:
- SCK = Master Clock: Low, wenn kein externer Mastertakt bereitgestellt wird
- H1L / FLT = Filterauswahl: Normaler Latenzmodus ( Low ) / niedrige Latenz (High)
- H2L / DEMP = De-Emphasis-Steuerung für 44,1 kHz Abtastrate: Aus ( Low ) / Ein (High)
- H3L / XSMT = Soft-Mute-Steuerung: Soft-Mute (Low) / Soft-Unmute ( High )
- H4L / FMT = Audioformat-Auswahl I2S: Rechtsbündig ( Low ) / Linksbündig (High)
Der Filter mit normaler Latenz ist ein FIR-Filter mit guter Impulsantwort, der das Signal um ca. 500 µs (bei 44,1 kHz) verzögert. Der Low-Latency-Filter ist ein IIR-Filter mit etwas schlechterer Impulsantwort und verzögert das Signal um ca. 80 µs. Sehr wenige (wenn überhaupt) Audioquellen haben Pre-Emphasis angewendet, daher sollte die De-Emphasis-Steuerung ausgeschaltet sein. Der XSMT-Pin ermöglicht das Stummschalten des Ausgangs über einen GPO oder Schalter, wenn die H3L-Brücke nicht verlötet ist. Für das I2S-Audioformat wählen wir rechtsbündig.
Das Bild unten zeigt das Schaltbild des PCM5102A-Moduls mit den gelb markierten Funktionsjumpern:

Lötbrücken
Auf der Rückseite des Boards findest du vier Lötbrücken H1L, H2L, H3L und H4L. Das Bild unten zeigt, welche Brücken du auf High (H) oder Low (L) löten musst:

Alternativ gibt es oben am Board auch 4 Pins (FLT, DEMP, XMST, FMT), die du mit 3,3 V (H) oder GND (L) verbinden kannst, um die Boardfunktionen zu steuern. Das ist nützlich, wenn du Schalter oder GPIOs zur Steuerung verwenden möchtest. Besonders die Stummschaltfunktion (XMST/H3L) könnte interessant sein. Achte darauf, entweder die Pins oder die Brücken zu verwenden, aber nicht beides gleichzeitig!
SCK-Brücke
Es gibt eine einzelne Brücke an der Vorderseite des Boards, die du ebenfalls löten musst. Sie steuert, ob ein externer Mastertakt (SCK) bereitgestellt wird oder nicht. In unserem Fall stellen wir keinen SCK bereit und müssen daher die SCK-Brücke löten, um SCK mit Masse zu verbinden.

Technische Spezifikation
Die folgende Tabelle fasst die technischen Spezifikationen des PCM5102A zusammen:
| Parameter | Spezifikation |
|---|---|
| DAC-Gerät | Texas Instruments PCM5102A |
| Interne Architektur | Leistungsstarker Delta-Sigma-DAC mit digitalen Interpolationsfiltern |
| Digitale Audio-Schnittstelle | Standard I2S, linksbündig, rechtsbündig, DSP (PCM) Formate |
| Unterstützte Abtastraten | 8 kHz bis 384 kHz |
| Unterstützte Bit-Tiefe | 16-Bit, 24-Bit, 32-Bit |
| Eingangs-Taktanforderungen | Bit-Takt (BCK), Wort-Takt (LRCK); optional externer Systemtakt (SCK) |
| Taktung | Integrierter PLL mit leistungsstarkem Takt-Multiplier; interne Master-Takterzeugung |
| Digitale Filter-Überabtastung | Mehrere Stufen mit hoher Überabtastrate (z. B. 128 × fs) |
| Analoger Ausgangstyp | Differenzielle interne Ausgänge mit passiver Onboard-Filterung zu Single-Ended |
| Ausgangsanschluss | 3,5 mm Stereo-Klinke |
| Vollaussteuerungs-Ausgangsspannung | Ca. 2,1 Vrms (massebezogen) |
| Ausgangslast-Treibfähigkeit | Für Line-Pegel-Lasten ≥ 1 kΩ pro Kanal ausgelegt |
| Signal-Rausch-Verhältnis (SNR) | ~112 dB (A-bewertet, typisch) |
| Gesamtharmonische Verzerrung + Rauschen (THD+N) | ~ -93 dB (typisch) |
| Line-Treiber | Integrierter DirectPath™ Charge-Pump-Line-Treiber |
| Mute/Soft-Ramp | Integrierte analoge Soft-Mute-Schaltung beim Ein-/Ausschalten oder Taktverlust |
| Versorgungsspannung (AVDD) | 3,3 V … 5 V |
| Logikpegel | LVCMOS kompatibel mit ESP32 I/O-Pegeln |
| Steuerungsschnittstelle | Hardware-Pin-Konfiguration (keine I2C/SPI für Basisbetrieb erforderlich) |
| Stromverbrauch | Automatischer Energiesparmodus bei inaktiven Takten |
Weitere Informationen findest du im Datenblatt des PCM5102A, das unten verlinkt ist:
PCM5102A mit ESP32 verbinden
In diesem Abschnitt verdrahten wir den PCM5102A mit dem ESP32. Wir beginnen damit, VIN des PCM5102A mit 3,3 V des ESP32 und GND mit G zu verbinden. Anschließend verbinden wir die I2S-Pins wie in der folgenden Tabelle gezeigt:
| PCM5102A | ESP32 |
|---|---|
| VIN | 3V3 |
| GND | G |
| LRCK | 32 |
| BCK | 25 |
| DIN | 33 |
| SCK | G |
Wenn du die SCK-Brücke gelötet hast, benötigst du eigentlich keine SCK-zu-Masse-Verbindung, aber sie schadet auch nicht.
Das folgende Bild zeigt die komplette Verdrahtung des PCM5102A-Moduls mit einem ESP32 lite. Beachte, dass du den Line-Out-Ausgang des PCM5102A an einen Verstärker oder aktive Lautsprecher anschließen musst. Ein passiver Lautsprecher oder niederohmige Kopfhörer dürfen nicht direkt an den PCM5102A angeschlossen werden.

SD-Kartenleser
Wenn du Audiodateien abspielen möchtest, musst du einen SD-Kartenleser anschließen, der die Audiodateien von einer SD-Karte liest. Das folgende Schaltbild zeigt, wie du einen SD-Kartenleser und den PCM5102A mit einem ESP32 verbindest:

Der SD-Kartenleser kommuniziert über SPI, und die Standard-SPI-Pins des ESP32 sind CS=5, MOSI=23, CLK=18 und MISO=19. Die folgende Tabelle fasst die Verbindungen zwischen SD-Kartenleser und ESP32 zusammen:
| SD-Kartenleser | ESP32 |
|---|---|
| 3V3 | 3V |
| GND | G |
| CS/SS | 5 |
| MOSI | 23 |
| CLK/SCK | 18 |
| MISO | 19 |
Wenn du dir nicht sicher bist, welche Pins die Standard-SPI-Pins deines ESP32 sind, schau dir das Find I2C and SPI default pins Tutorial an.
Das folgende Foto zeigt meine Verdrahtung des PCM5102A mit einem SD-Kartenleser, einem ESP32 und einem kleinen aktiven Mono-Lautsprecher zum Testen:

Bibliotheken installieren
In diesem Tutorial verwenden wir die arduino-audio-tools Bibliothek von Phil Schatzmann, um Audiodaten an den PCM5102A zu senden. Um die Bibliothek zu installieren, gehe zum arduino-audio-tools repo, klicke auf den grünen „<> Code“-Button und dann auf „Download ZIP“, um die Bibliothek als ZIP-Datei herunterzuladen, wie unten gezeigt:

Öffne dann einen Sketch, gehe zu Sketch -> Include Library -> Add .ZIP Library …, um die heruntergeladene ZIP-Bibliothek (arduino-audio-tools-main.zip) zu installieren.

Für einige Codebeispiele benötigen wir zwei weitere Bibliotheken von Phil Schatzmann; nämlich die arduino-libhelix Bibliothek und die ESP32-A2DP Bibliothek. Du kannst sie auf die gleiche Weise installieren. Klicke auf den Link, um zum GitHub-Repo zu gelangen, klicke auf den grünen „<> Code“-Button, um die Bibliotheken (arduino-libhelix-main.zip, ESP32-A2DP-main.zip) herunterzuladen, und installiere sie dann.
Wenn du zum ersten Mal ein ESP32-Board mit deiner Arduino IDE programmierst, musst du außerdem den ESP32-Core installieren. Details findest du im Install ESP32 core in Arduino IDE Tutorial.
Codebeispiel: Testton
Im ersten Beispiel erzeugen wir einfach einen Sinuston-Testton und geben ihn über die I2S-Schnittstelle an den DAC aus.
/*
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
Der Code beginnt mit dem Einbinden der AudioTools.h Bibliothek, die Klassen und Funktionen bereitstellt, um Audiosignale auf dem ESP32 einfach zu erzeugen und zu streamen.
#include "AudioTools.h";
Konstanten
Als Nächstes definieren wir die Pins, die mit dem PCM5102A DAC-Modul verbunden sind. Diese Pins entsprechen den I2S-Schnittstellensignalen: DIN_PIN für serielle Daten, LRCK_PIN für Wortauswahl (Links-Rechts-Takt) und BCLK_PIN für den seriellen Takt. Außerdem definieren wir eine VOLUME Konstante, um die Amplitude der erzeugten Sinuswelle einzustellen, mit einer maximalen Amplitude von 32000.
#define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock #define VOLUME 1000 // max 32000
Audio-Konfiguration und Objekte
Das AudioInfo Objekt info definiert das Audioformat: eine Abtastrate von 44.100 Hz, 2 Kanäle (Stereo) und 16 Bit pro Sample. Das entspricht Standard-CD-Qualität.
Das SineWaveGenerator Objekt sineWave wird verwendet, um ein Sinuswellen-Audiosignal mit der angegebenen Lautstärke zu erzeugen. Es produziert 16-Bit vorzeichenbehaftete Ganzzahlsamples.
Das GeneratedSoundStream Objekt sound kapselt den Sinuswellengenerator in eine Stream-Schnittstelle ein, was das Senden der Audiodaten an einen Ausgang erleichtert.
Das I2SStream Objekt out repräsentiert die I2S-Ausgangsschnittstelle auf dem ESP32, die die Audiodaten an den PCM5102A DAC sendet.
Schließlich ist das StreamCopy Objekt copier dafür verantwortlich, Audiodaten kontinuierlich vom erzeugten Soundstream zum I2S-Ausgangsstream zu kopieren.
AudioInfo info(44100, 2, 16); SineWaveGenerator<int16_t> sineWave(VOLUME); GeneratedSoundStream<int16_t> sound(sineWave); I2SStream out; StreamCopy copier(out, sound);
Setup-Funktion
Innerhalb der setup() Funktion konfigurieren wir den I2S-Ausgangsstream. Wir beginnen mit einer Standardkonfiguration für den Übertragungsmodus mittels out.defaultConfig(TX_MODE). Dann kopieren wir die Audioformateinstellungen vom info Objekt in diese Konfiguration.
Als Nächstes weisen wir die I2S-Pins der Konfiguration zu: BCLK_PIN für den Bit-Takt, LRCK_PIN für die Wortauswahl und DIN_PIN für die serielle Datenleitung.
Nach der Pin-Konfiguration initialisieren wir den I2S-Ausgangsstream mit out.begin(config). Schließlich starten wir den Sinuswellengenerator mit dem Audioformat und einer vordefinierten musikalischen Tonhöhe N_B4 (entspricht dem Ton 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);
}
Loop-Funktion
Die loop() Funktion kopiert kontinuierlich Audiodaten vom Sinuswellengenerator-Stream zum I2S-Ausgangsstream mit copier.copy(). So wird der Ton ununterbrochen abgespielt.
void loop() {
copier.copy();
}
Codebeispiel: Bluetooth
Dieser Code zeigt, wie man Audio über Bluetooth auf einem ESP32 mit dem PCM5102A DAC abspielt. Er nutzt mehrere Bibliotheken, um Audio-Streaming und Bluetooth A2DP (Advanced Audio Distribution Profile) zu handhaben. Der ESP32 empfängt Audiodaten via Bluetooth und gibt sie über die I2S-Schnittstelle an den PCM5102A Digital-Analog-Wandler aus.
/*
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
Der Code beginnt mit dem Einbinden der notwendigen Bibliotheken. AudioTools.h bietet Audio-Streaming-Funktionalität, während BluetoothA2DPSink.h die Bluetooth A2DP-Sink-Funktionalität übernimmt, sodass der ESP32 Audio-Streams von Bluetooth-Quellen wie Smartphones empfangen kann.
#include "AudioTools.h" #include "BluetoothA2DPSink.h"
Konstanten
Drei Konstanten definieren die GPIO-Pins für die I2S-Schnittstelle, das Kommunikationsprotokoll zwischen ESP32 und PCM5102A DAC. Diese Pins entsprechen der seriellen Datenleitung (DIN_PIN), der Wortauswahl- oder Links-Rechts-Takt-Leitung (LRCK_PIN) und der Bit-Takt-Leitung (BCLK_PIN).
#define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock
Objekte
Ein I2SStream Objekt namens i2s wird erstellt, um den I2S-Audiodatenstrom zu verwalten. Dann wird ein BluetoothA2DPSink Objekt namens a2dp_sink instanziiert, dem der i2s Stream als Parameter übergeben wird. Dies verbindet den Bluetooth-Audioeingang direkt mit dem I2S-Ausgang und ermöglicht nahtloses Audio-Playback.
I2SStream i2s; BluetoothA2DPSink a2dp_sink(i2s);
Setup-Funktion
Innerhalb der setup() Funktion wird die I2S-Schnittstelle konfiguriert. Die Standard-I2S-Konfiguration wird über i2s.defaultConfig() abgerufen, und die relevanten Pins für Bit-Takt, Wortauswahl und Daten werden entsprechend den zuvor definierten Konstanten zugewiesen. Die I2S-Schnittstelle wird dann mit dieser Konfiguration mittels i2s.begin(config) initialisiert.
Nach der Einrichtung der I2S-Schnittstelle wird der Bluetooth A2DP-Sink mit dem Gerätenamen "MyMusic" gestartet. Dadurch wird der ESP32 als Bluetooth-Audioempfänger mit diesem Namen sichtbar, sodass andere Geräte eine Verbindung herstellen und Audio streamen können.
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");
}
Öffne dein Telefon, suche nach Bluetooth-Geräten, finde das "MyMusic" Gerät und starte die Musikwiedergabe.
Loop-Funktion
Die loop() Funktion ist leer, da der Bluetooth A2DP-Sink und der I2S-Stream die Audioverarbeitung und Wiedergabe asynchron übernehmen. Sobald die Einrichtung abgeschlossen ist, hört der ESP32 kontinuierlich auf Bluetooth-Audiostreams und gibt sie über die I2S-Schnittstelle aus, ohne dass zusätzlicher Code in der Hauptschleife erforderlich ist.
void loop() { }
Codebeispiel: Internetradio
Dieser Code zeigt, wie man einen Internetradio-Player mit einem ESP32-Mikrocontroller und einem PCM5102A DAC-Modul baut. Er verbindet sich mit einem WLAN-Netzwerk, streamt MP3-Audio von einem Online-Radiosender, dekodiert das Audio und gibt es über die I2S-Schnittstelle an den DAC aus. Der Code verarbeitet auch Metadaten aus dem Stream, wie z. B. Songtitel.
/*
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
Der Code beginnt mit dem Einbinden mehrerer Bibliotheken, die für WLAN-Konnektivität, I2S-Audioausgabe und MP3-Dekodierung notwendig sind. Die Arduino.h und WiFi.h Bibliotheken bieten grundlegende Arduino- und WLAN-Funktionen. Die Wire.h Bibliothek ist eingebunden, wird hier aber nicht explizit verwendet. Die AudioTools Bibliothek und ihre zugehörigen Komponenten übernehmen Audio-Streaming, Dekodierung und Wiedergabe.
#include <Arduino.h> #include <WiFi.h> #include <Wire.h> #include "AudioTools.h" #include "AudioTools/AudioCodecs/CodecMP3Helix.h" #include "AudioTools/Communication/HTTP/ICYStream.h"
Konstanten und Pin-Definitionen
Anschließend definiert der Code die Pins für die I2S-Schnittstelle zur Kommunikation mit dem PCM5102A DAC. Diese Pins entsprechen seriellen Daten, Wortauswahl (Links-Rechts-Takt) und Bit-Takt. Zusätzlich wird ein Lautstärkepegel als Fließkommazahl festgelegt.
#define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock #define VOLUME 0.3 // Volume
Die WLAN-Zugangsdaten und die URL des Internetradio-Streams werden als konstante Zeichenketten gespeichert.
const char* ssid = "ssid"; const char* password = "pwd"; const char* url = "https://jazz.stream.laut.fm/jazz";
Hier eine Liste von URLs für weitere Internetradiosender, die du ausprobieren kannst:
"https://jazz.stream.laut.fm/jazz" "http://vis.media-ice.musicradio.com/CapitalMP3"; "http://stream.srg-ssr.ch/m/rsj/mp3_128" "http://stream.live.vc.bbcmedia.co.uk/bbc_world_service" "http://icecast.omroep.nl/radio1-bb-mp3" "http://stream-02-eu.relaxingjazz.com/stream/1/"
Audio-Stream-Objekte
Mehrere Objekte werden erstellt, um die Audio-Streaming-Pipeline zu verwalten. Das ICYStream Objekt übernimmt das HTTP-Streaming der MP3-Daten vom Internetradio-URL, während das I2SStream Objekt die I2S-Kommunikation mit dem DAC steuert. Das VolumeStream Objekt ermöglicht die Lautstärkeregelung und ist mit dem I2S-Stream verbunden. Das EncodedAudioStream Objekt dekodiert die MP3-Daten mit dem Helix MP3-Dekoder und gibt das dekodierte Audio an den Lautstärkestream aus. Schließlich kopiert das StreamCopy Objekt die dekodierten Audiodaten vom MP3-Dekoder zum ICY-Stream.
ICYStream icystream; I2SStream i2s; VolumeStream volume(i2s); EncodedAudioStream mp3decode(&volume, new MP3DecoderHelix()); StreamCopy copier(mp3decode, icystream);
Metadaten-Callback-Funktion
Die Funktion callbackMetadata wird definiert, um Metadaten vom Internetradio-Stream zu verarbeiten, wie z. B. den aktuellen Songtitel oder Künstler. Sie gibt den Metadaten-Typ und Inhalt zur Fehlerbehebung oder Anzeige auf dem seriellen Monitor aus.
void callbackMetadata(MetaDataType type, const char* str, int len) {
Serial.printf("%s: %s\n", toStr(type), str);
}
Setup-Funktion
In der setup() Funktion wird die serielle Kommunikation mit 115200 Baud für Debug-Ausgaben initialisiert. Der Audio-Logger wird gestartet, um Warnungen und Fehler zu erfassen. Der ESP32 versucht dann, sich mit dem angegebenen WLAN zu verbinden und wiederholt dies alle 500 Millisekunden, bis die Verbindung steht.
Nach der WLAN-Verbindung wird die I2S-Schnittstelle für den Übertragungsmodus mit den definierten Pins für Bit-Takt, Wortauswahl und Daten konfiguriert. Der I2S-Stream und der Lautstärkestream werden mit dieser Konfiguration initialisiert, und die Lautstärke wird auf den vordefinierten Wert gesetzt.
Der MP3-Dekoder wird gestartet, und der ICY-Stream wird mit der Internetradio-URL initialisiert. Die Metadaten-Callback-Funktion wird registriert, um Metadaten aus dem Stream zu empfangen und zu verarbeiten.
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);
}
Loop-Funktion
Die loop() Funktion kopiert kontinuierlich Audiodaten vom Internetradio-Stream über den MP3-Dekoder und die Lautstärkeregelung zum I2S-Ausgang. So läuft die Audiowiedergabe ununterbrochen.
void loop() {
copier.copy();
}
Codebeispiel: Internetradio mit Lautstärkeregelung
In diesem Codebeispiel fügen wir ein Potentiometer hinzu, das als Lautstärkeregler für das zuvor implementierte Internetradio dient. Das Bild unten zeigt die Verdrahtung:

Der Schaltkreis ist im Wesentlichen derselbe wie zuvor. Wir fügen nur ein 10-kOhm-Potentiometer und drei Kabel hinzu. Verbinde einen der äußeren Pins des Potentiometers mit 3V und den anderen äußeren Pin mit Masse (G) des ESP32. Verbinde dann den mittleren Pin (Wischer) des Potentiometers mit GPIO 35 des ESP32. Hier eine Tabelle mit den erforderlichen Verbindungen:
| Potentiometer | ESP32 |
|---|---|
| 1 | G |
| 2 | 35 |
| 3 | 3V |
Du kannst die Anschlüsse an Pin 1 und 3 des Potentiometers tauschen, wenn du die Drehrichtung zur Lautstärkeerhöhung ändern möchtest.
TickTwo Bibliothek
Der folgende Code für das Internetradio mit Lautstärkeregelung benötigt die TickTwo Bibliothek. Um sie zu installieren, öffne den LIBRARY MANAGER, tippe „TickTwo“ in die Suchleiste und klicke auf den INSTALL-Button für die „TickTwo by Stefan Staub“ Bibliothek:

Unten ist der Code für das Internetradio mit Lautstärkeregelung. Er ist weitgehend derselbe wie zuvor. Neu ist die updateVolume() Funktion, die alle 100 ms aufgerufen wird und die Lautstärke basierend auf der Potentiometerstellung aktualisiert:
/*
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
- [TickTwo](https://github.com/sstaub/TickTwo)
Version: 4.4.0
*/
#include <Arduino.h>
#include <WiFi.h>
#include <Wire.h>
#include "TickTwo.h"
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#include "AudioTools/Communication/HTTP/ICYStream.h"
#define DIN_PIN 33 // serial data
#define LRCK_PIN 32 // word select
#define BCLK_PIN 25 // serial clock
#define POT_PIN 35 // Poti for 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 updateVolume() {
static float oldVol = -1;
long pot = analogRead(POT_PIN);
float vol = map(pot, 0, 4095, 0, 100) / 100.0;
if (fabs(vol - oldVol) >= 0.02) {
oldVol = vol;
volume.setVolume(vol);
}
}
TickTwo timer(updateVolume, 100);
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(0.1);
mp3decode.begin();
icystream.begin(url);
icystream.setMetadataCallback(callbackMetadata);
pinMode(POT_PIN, INPUT);
timer.start();
}
void loop() {
timer.update();
copier.copy();
}
Statt eines 10-kOhm linearen Potentiometers könntest du auch ein logarithmisches Potentiometer verwenden, das eine natürlichere Beziehung zwischen der wahrgenommenen Lautstärke und der Potentiometereinstellung bietet.
Die updateVolume() Funktion liest die Potentiometereinstellung aus, mappt sie auf einen Lautstärkepegel und passt die Lautstärke an, wenn der neue Pegel sich ausreichend vom alten unterscheidet. In der loop() Funktion müssen wir nur timer.update() wiederholt aufrufen, um alle 100 ms das Potentiometer auszulesen und die Lautstärke anzupassen.
Du könntest auch Taster hinzufügen, um Internetradiosender zu wechseln oder den Ton stummzuschalten. Wie man Taster in Schaltung und Code einbindet, wird im folgenden Beispiel für einen MP3-Player erklärt.
Codebeispiel: MP3 von SD-Karte abspielen
Der folgende Code zeigt, wie man MP3-Audiodateien von einer SD-Karte mit einem ESP32-Mikrocontroller und dem PCM5102A DAC-Modul abspielt. Er nutzt die arduino-audio-tools und arduino-libhelix Bibliotheken, um Audio zu dekodieren und über die I2S-Schnittstelle wiederzugeben.
/*
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
Zu Beginn bindet der Code mehrere Header-Dateien der Audiobibliotheken ein. Diese stellen die notwendigen Klassen und Funktionen bereit, um Audiodateien von der SD-Karte zu lesen, MP3-Streams zu dekodieren und Audio über I2S auszugeben.
#include "AudioTools.h" #include "AudioTools/Disk/AudioSourceSD.h" #include "AudioTools/AudioCodecs/CodecMP3Helix.h"
Konstanten und Pin-Definitionen
Der Code definiert Pins, die den ESP32 mit dem PCM5102A DAC verbinden. Diese Pins entsprechen den I2S-Signalen: serielle Daten (DIN), Wortauswahl (LRCK) und serieller Takt (BCLK). Zusätzlich wird ein Lautstärkepegel als Fließkommazahl zwischen 0,0 und 1,0 festgelegt.
#define DIN_PIN 33 // serial data #define LRCK_PIN 32 // word select #define BCLK_PIN 25 // serial clock #define VOLUME 0.5 // Volume
Die PATH und EXT Konstanten geben das Stammverzeichnis und die Dateiendung der abzuspielenden Audiodateien an, in diesem Fall MP3-Dateien im Root-Verzeichnis der SD-Karte.
#define PATH "/" #define EXT "mp3"
Audio-Objekte
Mehrere Objekte werden instanziiert, um die Audiowiedergabe zu steuern. AudioSourceSD liest Audiodateien von der SD-Karte, die dem angegebenen Pfad und der Endung entsprechen. I2SStream verwaltet den I2S-Audioausgangsstream. MP3DecoderHelix ist der MP3-Dekoder basierend auf der Helix-Bibliothek. Schließlich AudioPlayer verbindet diese Komponenten zur Koordination der Wiedergabe.
AudioSourceSD source(PATH, EXT); I2SStream i2s; MP3DecoderHelix decoder; AudioPlayer player(source, i2s, decoder);
Metadaten-Callback-Funktion
Die Funktion printMetaData() wird definiert, um Metadaten wie Künstler, Titel oder Album von den MP3-Dateien während der Wiedergabe zu empfangen. Sie gibt diese Informationen mit Serial.printf auf dem seriellen Monitor aus. Die Funktion erhält den Metadaten-Typ, eine Zeichenkette mit den Metadaten und deren Länge als Parameter.
void printMetaData(MetaDataType type, const char* str, int len){
Serial.printf("%s: %s\n", toStr(type), str);
}
Setup-Funktion
In der setup() Funktion wird die serielle Kommunikation mit 115200 Baud für Debug-Ausgaben initialisiert. Das Audiologging-Level wird auf Warning gesetzt, um die Ausgabe zu reduzieren.
Anschließend wird die I2S-Schnittstelle für den Übertragungsmodus konfiguriert. Die Standard-I2S-Konfiguration wird abgerufen und modifiziert, um die zuvor definierten Pins für Bit-Takt, Wortauswahl und Daten zuzuweisen. Der I2S-Stream wird mit dieser Konfiguration gestartet.
Der Audioplayer wird so konfiguriert, dass er den printMetaData Callback verwendet, um Metadaten während der Wiedergabe anzuzeigen. Die Lautstärke wird auf den definierten Wert gesetzt, und der Player wird mit player.begin() gestartet.
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();
}
Loop-Funktion
Die loop() Funktion ruft kontinuierlich player.copy() auf, das Audiodaten verarbeitet, indem es von der SD-Karte liest, den MP3-Stream dekodiert und die Audiosamples an den I2S-Ausgang sendet. So läuft die Wiedergabe reibungslos.
void loop() {
player.copy();
}
Codebeispiel: MP3-Player mit Lautstärke- und Titeltasten
Ein typischer MP3-Player verfügt über Funktionen zur Lautstärkeregelung und zum Vor- und Zurückspringen von Titeln. In diesem Codebeispiel fügen wir ein Potentiometer für die Lautstärkeregelung und zwei Taster zum Überspringen von Titeln hinzu. Das folgende Schaltbild zeigt, wie man Potentiometer und Taster anschließt:

Die folgende Tabelle listet die Verbindungen auf, die du herstellen musst. Achte beim Anschluss der Taster darauf, die richtigen Pole zu verbinden.
| Potentiometer | Next-Taste | Prev-Taste | ESP32 |
|---|---|---|---|
| 1 | 4 | ||
| 1 | 15 | ||
| 1 | 2 | 2 | G |
| 2 | 35 | ||
| 3 | 3V |
Wir verwenden die EasyButton-Bibliothek, um den Status der Taster auszulesen. Um sie zu installieren, öffne den LIBRARY MANAGER, suche nach der „EasyButton by Evert Arias“ Bibliothek und klicke auf INSTALL:

Der folgende Code ist der Code für den MP3-Player mit den hinzugefügten Funktionen onNextTrack(), onPrevTrack() und updateVolume(), die zum Überspringen von Titeln oder zur Lautstärkeregelung verwendet werden:
/*
www.makerguides.com
Libraries:
- ESP32 Core 3.3.7
- [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
- [TickTwo](https://github.com/sstaub/TickTwo)
Version: 4.4.0
- [EasyButton](https://github.com/evert-arias/EasyButton)
Version: 2.0.3
*/
#include "TickTwo.h"
#include "EasyButton.h"
#include "AudioTools.h"
#include "AudioTools/Disk/AudioSourceSD.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#define DIN_PIN 33 // serial data
#define LRCK_PIN 32 // word select
#define BCLK_PIN 25 // serial clock
#define POT_PIN 35 // Poti for Volume
#define PATH "/"
#define EXT "mp3"
EasyButton nextBtn(4);
EasyButton prevBtn(15);
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 updateVolume() {
static float oldVol = -1;
long pot = analogRead(POT_PIN);
float vol = map(pot, 0, 4095, 0, 100) / 100.0;
if (fabs(vol - oldVol) >= 0.02) {
oldVol = vol;
player.setVolume(vol);
}
}
void onPrevTrack() {
Serial.println("Prev track");
player.next(-1);
}
void onNextTrack() {
Serial.println("Next track");
player.next(+1);
}
TickTwo timer(updateVolume, 100);
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(0.1);
player.begin();
pinMode(POT_PIN, INPUT);
timer.start();
nextBtn.begin();
nextBtn.onPressed(onNextTrack);
prevBtn.begin();
prevBtn.onPressed(onPrevTrack);
}
void loop() {
timer.update();
nextBtn.read();
prevBtn.read();
player.copy();
}
Codebeispiel: Text-zu-Sprache
Dieses Codebeispiel zeigt, wie man ein Text-zu-Sprache (TTS) System auf einem ESP32 mit der OpenAI TTS API implementiert und den Ton über einen PCM5102A DAC ausgibt. Die Audiodaten werden im MP3-Format gestreamt, dekodiert und über die I2S-Schnittstelle abgespielt. Das Programm verbindet sich mit dem WLAN, sendet Text an die OpenAI API, empfängt die synthetisierte Sprache und spielt sie in Echtzeit ab.
/*
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
Der Code beginnt mit dem Einbinden der notwendigen Bibliotheken. Arduino.h stellt die Kernfunktionen von Arduino bereit. WiFi.h und WiFiClientSecure.h übernehmen die WLAN-Verbindung und sichere HTTPS-Kommunikation. Die AudioTools Bibliothek und ihr MP3-Codec CodecMP3Helix werden für Audio-Streaming und Dekodierung verwendet.
#include <Arduino.h> #include <WiFi.h> #include <WiFiClientSecure.h> #include "AudioTools.h" #include "AudioTools/AudioCodecs/CodecMP3Helix.h"
Konstanten und Pin-Definitionen
Die Pins für den PCM5102A DAC sind definiert, um die I2S-Schnittstelle zu konfigurieren. Dazu gehören der serielle Datenpin (DIN_PIN), der Wortauswahlpin (LRCK_PIN) und der serielle Taktpin (BCLK_PIN). Zusätzlich werden Konstanten für das TTS-Modell, die Stimme und den Lautstärkepegel gesetzt. WLAN-Zugangsdaten und OpenAI API-Details sind ebenfalls als konstante Zeichenketten deklariert.
// 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";
Du kannst andere TTS-Modelle ausprobieren, wie „tts-1“ und andere Stimmen wie „alloy“, „ash“, „coral“, „echo“, „fable“, „onyx“, „nova“, „sage“, „shimmer“, „marin“, „cedar“. Für mehr Details siehe platform.openai.com/docs/guides/text-to-speech.
WLAN-Zugangsdaten und OpenAI API-Details sind ebenfalls als konstante Strings gespeichert. Du musst „ssid“ und „pwd“ durch deine WLAN-Zugangsdaten ersetzen.
// WiFi credentials const char* ssid = "ssid"; const char* password = "pwd";
Du benötigst außerdem einen „apikey“ von OpenAI. Gehe zu https://platform.openai.com und melde dich mit einer E-Mail-Adresse oder einem bestehenden Google- oder Microsoft-Konto an.
// OpenAI configuration const char* openaiHost = "api.openai.com"; const int openaiPort = 443; const char* openaiApiKey = "apikey";
Nach der Verifizierung deiner E-Mail und dem Abschluss der Ersteinrichtung logge dich im OpenAI-Dashboard ein, platform.openai.com/api-keys und finde oder erstelle deinen API-Schlüssel (=SECRET KEY), wie unten gezeigt:

Der API-Schlüssel ist eine einzigartige, lange Zeichenkette, die mit „sk-proj-“ beginnt und zur Authentifizierung deiner API-Anfragen benötigt wird (siehe unten).
sk-proj-xcA.......................OtDu0U
Das ist alles, was du brauchst, um einen API-Schlüssel zu erhalten, aber ich empfehle, auch ein Nutzungslimit für dein Konto festzulegen. Für mehr Details siehe das Vision Chatbot with DFRobot ESP32-S3 AI Camera and OpenAI Tutorial.
Audio- und Netzwerk-Objekte
Mehrere Objekte werden instanziiert, um Audio-Streaming und Netzwerkkommunikation zu verwalten. WiFiClientSecure übernimmt die HTTPS-Verbindung zum OpenAI-Server. I2SStream steuert die I2S-Audioausgabe. Das VolumeStream Objekt kapselt den I2S-Stream, um die Lautstärke zu regeln. EncodedAudioStream wird verwendet, um den MP3-Audiostream mit dem Helix MP3-Dekoder zu dekodieren. Schließlich StreamCopy kopiert den dekodierten Audiostream zum Netzwerk-Client.
WiFiClientSecure client; I2SStream i2s; VolumeStream volume(i2s); EncodedAudioStream mp3decode(&volume, new MP3DecoderHelix()); StreamCopy copier(mp3decode, client);
Text-zu-Sprache-Funktion
Die text2speech() Funktion nimmt einen Textstring als Eingabe und sendet ihn an die OpenAI TTS API, um Sprach-Audio zu erzeugen. Zuerst konfiguriert sie den Client, um unsichere SSL-Verbindungen zu akzeptieren (praktisch für die Entwicklung). Dann versucht sie, eine Verbindung zum OpenAI-Server auf Port 443 herzustellen. Wenn die Verbindung fehlschlägt, wird eine Fehlermeldung ausgegeben und die Funktion beendet.
Die Funktion erstellt einen JSON-Body mit dem TTS-Modell, der Stimme, dem Ausgabeformat (MP3) und dem Eingabetext. Sie sendet eine HTTP-POST-Anfrage mit den passenden Headern, einschließlich des Autorisierungstokens mit dem API-Schlüssel. Nach dem Senden des Anfragetexts liest sie die HTTP-Antwortheader aus und überspringt sie, um für das Streaming der Audiodaten bereit zu sein.
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;
}
}
Setup-Funktion
In der setup() Funktion wird die serielle Kommunikation für Debug-Ausgaben initialisiert. Der Audio-Logger wird so konfiguriert, dass er Warnungen meldet. Der ESP32 verbindet sich mit dem angegebenen WLAN und wartet, bis die Verbindung steht.
Anschließend wird die I2S-Schnittstelle mit den zuvor definierten Pins für Bit-Takt, Wortauswahl und Daten konfiguriert. Der MP3-Dekoder und die Lautstärkeregelung werden mit dieser Konfiguration initialisiert, und die Lautstärke wird auf den definierten Wert gesetzt.
Schließlich wird die text2speech() Funktion mit einem Beispieltext aufgerufen, der den TTS-Prozess startet und das Audio vom OpenAI API streamt.
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?");
}
Loop-Funktion
Die loop() Funktion kopiert kontinuierlich den dekodierten MP3-Audiostream vom OpenAI-Server zum I2S-Ausgang. So läuft die Audiowiedergabe, solange die Verbindung offen bleibt.
void loop() {
copier.copy();
}
Fazit
In diesem Projekt hast du gelernt, wie man Audio mit dem ESP32 und dem PCM5102A DAC abspielt. Wir haben die technischen Details des PCM5102A-Moduls und dessen Verdrahtung zum ESP32 betrachtet. Du hast außerdem gelernt, wie man Text in Sprache umwandelt, Internetradio streamt, MP3-Dateien von einer SD-Karte abspielt und Audio via Bluetooth wiedergibt.
Für besseren und lauteren Klang kannst du einen Verstärker an den Line-Ausgang des PCM5102A anschließen. Siehe die folgenden Tutorials für mehr Informationen:
- TDA7379 Class AB Audio Amplifier with ESP32
- High-Power ESP32 Audio with TPA3116D2 and PCM5102
- Audio with PAM8403, PCM5102 and ESP32
- Stereo Amplifier with TPA31110 XH-A232, PCM5102 and ESP32
- Playing Audio with ESP32 and MAX98357
Wenn du Fragen hast, kannst du sie gerne im Kommentarbereich stellen.
Viel Spaß beim Tüfteln ; )
FAQ
F: Was ist der PCM5102A und warum sollte ich ihn verwenden?
Der PCM5102A ist ein hochwertiger Digital-Analog-Wandler, der über I2S mit dem ESP32 verbunden wird und digitale Audiosignale in ein sauberes analoges Stereo-Signal umwandelt. Das ist viel besser als der interne DAC des ESP32 und führt zu klarerem Klang, weniger Rauschen und insgesamt besserer Audioqualität für Musik, Sprache oder Streaming-Anwendungen.
F: Wie wird der PCM5102A mit dem ESP32 verbunden?
Der PCM5102A verwendet drei Haupt-I2S-Signale plus Stromversorgung.
Beispielverdrahtung:
ESP32_3V3 ----> VIN ESP32_GND ----> GND ESP32_GPIO32 ----> LRCK ESP32_GPIO25 ----> BCK ESP32_GPIO33 ----> DIN SCK ----> GND // Optional
Der interne Takt des PCM5102A macht eine externe Mastertaktquelle überflüssig, was die Verdrahtung vereinfacht.
F: Kann ich einen Lautsprecher direkt an den PCM5102A anschließen?
Nein, denn der PCM5102A liefert nur ein Line-Pegel-Signal, das zu schwach ist, um einen Lautsprecher direkt anzutreiben und zuerst verstärkt werden muss.
Korrekte Einrichtung:
PCM5102A --> Amplifier --> Speaker
Oder mit aktiven Lautsprechern:
PCM5102A --> Active Speakers
F: Welchen Verstärker sollte ich mit dem PCM5102A verwenden?
Du kannst einen kleinen Class-D-Verstärker für einfache Projekte oder einen leistungsstärkeren Stereo-Verstärker für besseren Klang und höhere Lautstärke verwenden.
Siehe z. B. das High-Power ESP32 Audio with TPA3116D2 and PCM5102 oder TDA7379 Class AB Audio Amplifier with ESP32 Tutorial.
F: Wie kann ich die Klangqualität mit einer geeigneten Stromversorgung verbessern?
Eine saubere Stromversorgung ist sehr wichtig, da Störungen auf der Versorgungsleitung direkt den DAC-Ausgang beeinflussen. Du kannst einige Kondensatoren hinzufügen:
VIN -- 100nF -- GND VIN -- 100µF -- GND
Der kleine Kondensator entfernt hochfrequentes Rauschen, während der große Kondensator Spannungsschwankungen glättet und stabile Versorgung für den DAC bietet.
Wenn möglich, filtere auch die Verstärker-Versorgung:
5V -- 10Ω --+-- AMP_VCC | 220µF | GND
Das reduziert Störungen, die vom ESP32 oder USB-Netzteil kommen.
F: Sollte ich separate Stromversorgungen für DAC und Verstärker verwenden?
Getrennte oder gefilterte Versorgungen können die Audioqualität verbessern, da der Verstärker hohe Ströme zieht, die Störungen in die DAC-Versorgung einbringen können. Zum Beispiel:
5V ----> DAC 5V ----> Filter ----> Amplifier
Das reduziert Interferenzen zwischen digitalen und Leistungsstufen.
F: Brauche ich Kondensatoren am Audioausgang?
Normalerweise nicht, da der PCM5102A bereits eine DC-freie Ausgangsstufe hat, aber in manchen Fällen kann ein Kopplungskondensator helfen, wenn der Verstärkereingang empfindlich ist:
DAC_OUT -- 1µF -- AMP_IN
Das kann niederfrequentes Rauschen oder Offset-Probleme reduzieren.
F: Wie kann ich die Lautstärke erhöhen?
Die Lautstärke hängt hauptsächlich vom Verstärker und den Lautsprechern ab, nicht vom DAC. Um die Lautstärke zu erhöhen:
- Verwende einen leistungsstärkeren Verstärker
- Verwende eine höhere Versorgungsspannung (innerhalb der Grenzen)
- Verwende effiziente Lautsprecher
Der DAC liefert bereits ein starkes Line-Signal, die Verstärkerstufe bestimmt die endgültige Lautstärke.
F: Was ist der Unterschied zwischen 4Ω- und 8Ω-Lautsprechern?
Die Lautsprecherimpedanz hat großen Einfluss auf Lautstärke und Verstärkerlast.
- 4Ω-Lautsprecher ziehen mehr Strom und erzeugen höhere Lautstärke
- 8Ω-Lautsprecher ziehen weniger Strom und sind leichter anzutreiben
Viele kleine Verstärker wie der PAM8403 liefern mehr Leistung an 4Ω-Lautsprecher, aber du musst sicherstellen, dass der Verstärker die Last ohne Überhitzung oder Verzerrung bewältigt.
F: Wie kann ich die Klangqualität insgesamt weiter verbessern?
Es gibt mehrere praktische Verbesserungen, die zusammen einen großen Unterschied machen.
Halte die Verkabelung kurz:
DAC_L ----> AMP_L DAC_R ----> AMP_R
Vermeide es, Audiokabel in der Nähe von Strom- oder Lautsprecherkabeln zu verlegen.
Verwende eine Stern-Masseführung:
ESP32_GND ----+---- DAC_GND | AMP_GND
Das verhindert, dass Störströme das Audiosignal beeinflussen.
Verwende gute Lautsprecher, denn selbst die beste Elektronik kann schlechte Lautsprecherqualität nicht ausgleichen.
F: Warum ist die PCM5102A-Konfiguration viel sauberer als ESP32 DAC + Verstärker?
Du kannst einen Verstärker direkt an die DACs des ESP32 anschließen. Siehe das Playing Sound with PAM8403 and ESP32 Tutorial für Details, aber die Klangqualität ist wegen der niedrigen Auflösung (8 Bit) und der analogen Signalverarbeitung nicht sehr gut.
Der PCM5102A hingegen verwendet hochauflösende digitale Verarbeitung und interne Filterung, die den Großteil von Rauschen und Verzerrungen entfernen, bevor das Signal analog wird.
F: Was ist die beste Konfiguration für maximale Qualität und Lautstärke?
Für beste Ergebnisse kombiniere einen hochwertigen DAC, einen guten Verstärker und passende Lautsprecher. Siehe z. B. das High-Power ESP32 Audio with TPA3116D2 and PCM5102 oder TDA7379 Class AB Audio Amplifier with ESP32 Tutorial.

