Skip to Content

Audio mit ESP32 und MAX98357 abspielen

Audio mit ESP32 und MAX98357 abspielen

In diesem Beitrag lernst du, wie du das MAX98357A Verstärkermodul mit dem ESP32 verwendest, um Audio abzuspielen.

Der MAX98357A ist ein kompakter Digital-Analog-Wandler (DAC) mit integriertem Verstärker. Er empfängt digitale Audiodaten über das I2S-Protokoll und gibt verstärktes Audio direkt an einen Lautsprecher aus. Diese Kombination vereinfacht deinen Hardwareaufbau und verbessert die Klangqualität.

In diesem Tutorial lernst du, wie du Audiosignale erzeugst, Text in Sprache umwandelst, Internetradio streamst, MP3-Dateien von einer SD-Karte abspielst und Bluetooth-Audio nutzt.

überblick

Benötigte Teile

Du benötigst mindestens ein MAX98357A Modul. Wenn du Stereo abspielen möchtest, brauchst du zwei MAX98357A Module. Ebenso benötigst du zwei Lautsprecher mit 4-8 Ω und mindestens 3 Watt.

Um MP3-Dateien von einer SD-Karte abzuspielen, benötigst du außerdem eine SD-Karte mit mindestens 1GB und ein SD-Kartenleser-Modul.

Schließlich brauchst du einen ESP32, ein Breadboard und einige Kabel. Ich habe einen ESP32 lite verwendet, aber die meisten anderen ESP32-Boards sollten ebenfalls funktionieren. Am besten nimmst du einen ESP32-S3 mit PSRAM, wenn du Musik im Speicher ablegen und abspielen möchtest.

2 x MAX98357A Verstärker

2x Lautsprecher 4Ω 5W

Micro SD Kartenleser

Micro SD Karte 8GB

ESP32 lite Lolin32

ESP32 lite

USB data cable

USB-Datenkabel

Dupont wire set

Dupont Kabelset

Half_breadboard56a

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

Starten wir mit einer kurzen Einführung in das I2S-Protokoll, das verwendet wird, um Audiodaten digital vom ESP32 zu einem Verstärkermodul wie dem MAX98357 zu übertragen.

I2S, oder Inter-IC Sound, ist ein serielles Bus-Interface-Standard zur Verbindung digitaler Audiogeräte. Es 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 entwickelt worden und sorgt für präzises Timing 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), Word Select (WS) und serielle Daten (SD). Der serielle Takt pulsiert mit der Bitrate und bestimmt, wann Bits gesendet werden. Das Word Select Signal schaltet um, um anzuzeigen, ob die aktuellen Daten zum linken oder rechten Audiokanal gehören. Die serielle Datenleitung überträgt die eigentlichen Audiobits, wobei das höchstwertige Bit (MSB) zuerst gesendet wird.

I2S Timing Diagram
I2S Timing-Diagramm (source)

Typischerweise werden Audiodaten in 16-Bit- oder 24-Bit-Wörtern gesendet, aber das Protokoll kann je nach Hardware auch andere Bit-Tiefen unterstützen.

Das I2S-Peripheriegerät des ESP32 unterstützt Vollduplex-Kommunikation, also gleichzeitige Audio-Ein- und Ausgabe. Es kann als Master oder Slave betrieben werden. Im Master-Modus erzeugt der ESP32 die Takt- und Word Select-Signale. Das ist die übliche Konfiguration beim Anschluss an den MAX98357A, der als I2S-Slave arbeitet. Im nächsten Abschnitt schauen wir uns den MAX98357A genauer an.

Technische Eigenschaften des MAX98357A Moduls

Das MAX98357A Modul ist ein digitaler Verstärker, der I2S-Eingang direkt akzeptiert. Es wandelt den eingehenden digitalen Audiostream in ein analoges Signal um und verstärkt dieses, um Lautsprecher anzutreiben. Es integriert einen 3,2W Class-D Verstärker mit eingebautem Digital-Analog-Wandler (DAC). Die digitale Audio-Schnittstelle erkennt bis zu 35 verschiedene PCM- und TDM-Taktmodi, wodurch keine I2C-Programmierung nötig ist.

Block Diagram of the MAX98357A
Blockdiagramm des MAX98357A (source)

Der MAX98357A unterstützt 16-Bit-, 24-Bit- und 32-Bit-Audiosamples bei Abtastraten von 8 kHz bis 96 kHz. Diese Flexibilität ermöglicht hochwertige Audiowiedergabe für Sprache, Musik und andere Anwendungen.

Das Modul arbeitet mit einer einzigen 3,3V oder 5V Stromversorgung und ist damit kompatibel mit den meisten Mikrocontrollern, einschließlich des ESP32. Es verfügt über einen integrierten Low-Dropout-Regler und einen Überhitzungsschutz. Das Bild unten zeigt die Vorder- und Rückseite eines typischen MAX98357A Moduls:

Front and Back of the MAX98357A Module
Vorder- und Rückseite des MAX98357A Moduls

Auf der Eingangsseite erwartet der MAX98357A drei Hauptsignale: Bit Clock (BCLK), Word Select (LRC) und serielle Daten (SD). Der Bit Clock synchronisiert die Datenbits, während Word Select angibt, ob die aktuellen Daten zum linken oder rechten Audiokanal gehören. Die serielle Datenleitung überträgt die eigentlichen Audiosamples in einem kontinuierlichen Stream.

Functional Diagram of the MAX98357A
Funktionsdiagramm des MAX98357A (source)

Im Inneren wandelt das Modul die eingehenden digitalen Audiodaten mit seinem integrierten DAC in ein analoges Signal um. Dieses analoge Signal wird dann von der Class-D-Verstärkerstufe verstärkt, die einen 4 oder 8 Ω Lautsprecher mit 3 Watt antreibt.

Der MAX98357A verfügt über eine Gain-Steuerung und kann so konfiguriert werden, dass er aus dem Stereo-Eingang einen linken, rechten oder gemischten Ausgang über den SD-Pin erzeugt. Im folgenden Abschnitt besprechen wir diese Pins im Detail.

Pinbelegung des MAX98357A Moduls

Ein typisches MAX98357A Modul hat Pins für die Stromversorgung (Vin, GND), für das I2S-Interface (LRC, BCLK, DIN), für die Gain-Steuerung (GAIN) und für die Kanalauswahl und Abschaltung (SD). Das Bild unten zeigt die Pinbelegung:

Pinout of MAX98357A Module
Pinbelegung des MAX98357A Moduls

Stromversorgung

Der VIN-Pin akzeptiert 3,3V bis 5V. Der GND-Pin ist die Masse und muss mit dem ESP32-GND verbunden werden.

Der Stromverbrauch des MAX98357A liegt bei moderat lauter Wiedergabe typischerweise zwischen 200mA und 400mA. Der Spannungsregler eines typischen ESP32-Boards liefert etwa 800mA, aber der ESP32 lite verwendet einen ME6211 mit nur 500mA.

Das bedeutet, du kannst einen MAX98357A vom 3,3V-Ausgangspin eines ESP32 betreiben, aber es ist riskant, da bei hoher Lautstärke und maximalem Gain der Strom bis zu 1,5A (bei 5V) erreichen kann. Es ist daher besser, den MAX98357A mit einer externen Stromquelle zu versorgen.

Beachte, dass der MAX98357A einen Lautsprecherausgangsstrombegrenzer von 2,8A hat. Wenn der Ausgangsstrom diesen Wert überschreitet, schaltet sich der Chip vorübergehend ab und startet neu, um sich zu schützen.

I2S-Schnittstelle

Der LRC-Pin steht für Left-Right Clock oder Word Select. Er zeigt an, ob die aktuellen Audiodaten zum linken oder rechten Audiokanal gehören. Der ESP32 erzeugt dieses Signal als Teil des I2S-Protokolls.

Der BCLK-Pin ist der Bit Clock. Er synchronisiert die über die DIN-Leitung gesendeten Datenbits. Auch dieses Taktsignal liefert der ESP32.

Der DIN-Pin ist die Data Input Leitung. Dieser Pin empfängt die eigentlichen Audiodaten vom ESP32.

Gain-Steuerung

Du kannst den Gain des MAX98357A steuern, indem du den Gain-Pin direkt oder über einen 100K Widerstand mit VCC oder GND verbindest. Die folgende Tabelle zeigt, welche Verbindung zu welchem Gain führt.

Gain Verbindung
15dB GAIN — 100K — GND
12dB GAIN — GND
9dB Keine
6dB GAIN — VCC
3dB GAIN — 100K — VCC

Der Gain von 9dB ist die Standardeinstellung, dann ist keine Verbindung nötig.

Kanalsteuerung

Der SD (Shutdown) Pin des MAX98357A ermöglicht es, den Verstärker abzuschalten oder den rechten, linken oder einen gemischten Ausgang aus dem Stereo-Eingang zu wählen. Das Bild unten zeigt den Schaltplan des MAX98357A Moduls mit dem SD Mode Pin und Informationen zur Kanalauswahl:

Schematics of MAX98357A Module
Schaltplan des MAX98357A Moduls

Wenn der SD-Pin nicht verbunden ist (Standard), erzeugt der Verstärker einen gemischten Ausgang aus linkem und rechtem Kanal ((L+R)/2). Wenn der SD-Pin mit Masse verbunden ist, ist der Verstärker ausgeschaltet. Ansonsten bestimmt die Spannung am SD-Pin, ob der linke oder rechte Kanal verstärkt wird. Die folgende Tabelle zeigt die erforderlichen Spannungen am SD-Pin für die verschiedenen Funktionen:

Spannung @ SD Funktion
< 0.16V Verstärker aus
0.16 … 0.77V Linker und rechter Kanal gemischt ((L+R)/2)
0.77 … 1.4V Rechter Kanal (370KΩ @ 5V, 210KΩ @ 3V3)
> 1.4V Nur linker Kanal (100kΩ)

Um die erforderliche Spannung am SD-Pin für den rechten Kanal zu erreichen, musst du den SD-Pin über einen passenden Widerstand mit VCC verbinden. Im Schaltplan steht folgende Formel:

R = (94 * VDD – 100) KΩ

Das bedeutet, bei 5V brauchst du einen 370KΩ Widerstand zwischen SD und 5V und bei 3,3V einen 210KΩ Widerstand.

Für den linken Kanal kannst du einen 100kΩ Widerstand bei 5V oder 3,3V verwenden. Und wenn du einen gemischten Ausgang (Summen-Mono) möchtest, lässt du den SD-Pin einfach unverbunden.

Datenblatt

Der folgende Button führt zum Datenblatt des MAX98357A, wo du weitere technische Details findest:

MAX98357A mit ESP32 verbinden

In diesem Abschnitt lernst du die verschiedenen Möglichkeiten kennen, wie du den MAX98357A und Lautsprecher an einen ESP32 anschließt.

Summen-Mono

Wir starten mit der einfachsten Konfiguration. Wir verwenden einen einzelnen MAX98357A und einen Lautsprecher, um ein gemischtes Stereo-Signal (je 50% linker und rechter Kanal) abzuspielen.

Verbinde zuerst Vin des MAX98357A mit 3,3V des ESP32 und GND mit G. Dann verbindest du die I2S-Pins wie in der folgenden Tabelle gezeigt:

MAX98357A ESP32
Vin 3V3
GND G
LRC 32
BCLK 25
DIN 33

Lasse den GAIN- und den SD-Pin des MAX98357A unverbunden. Dadurch wird ein Mix aus linkem und rechtem Kanal mit einem Gain von 9dB erzeugt.

Achte beim Anschluss des Lautsprechers auf die richtige Polarität und verwende einen 4Ω oder 8Ω Lautsprecher mit mindestens 3W. Du kannst Lautsprecher mit höherer Leistung verwenden, aber nicht mit geringerer.

Das folgende Bild zeigt die komplette Verdrahtung eines MAX98357A mit einem ESP32 lite für Summen-Mono-Sound (Mix), wobei die Stromversorgung über den 3,3V-Pin erfolgt:

Single MAX98357A with ESP32 at 3.3V for Mono Sound
Einzelner MAX98357A mit ESP32 bei 3,3V für Mono-Sound

Wie bereits erwähnt, kann der MAX98357A beim Abspielen von lauter Musik mit maximalem Gain bis zu 1,5A Strom ziehen. Der 3,3V-Pin am ESP32 kann diesen Strom nicht liefern, daher solltest du eine externe Stromversorgung verwenden. Das Bild unten zeigt, wie du eine externe 5V-Stromversorgung anschließt:

Single MAX98357A with ESP32 with external 5V for Mono Sound
Einzelner MAX98357A mit ESP32 und externer 5V für Mono-Sound

Stereo

Wenn du Stereo-Sound abspielen möchtest, brauchst du zwei Lautsprecher und zwei MAX98357A (je einen pro Kanal) sowie Widerstände zur Auswahl des linken oder rechten Kanals. Das Bild unten zeigt die komplette Verdrahtung:

Two MAX98357A with ESP32 at 3.3V for Stereo Sound
Zwei MAX98357A mit ESP32 bei 3,3V für Stereo-Sound

Beide MAX98357A werden parallel verdrahtet und nutzen die gleichen Strom- und I2S-Pins wie zuvor. Der einzige Unterschied ist, dass der MAX98357A für den linken Kanal einen 100KΩ Widerstand zwischen SD und 3,3V hat und der für den rechten Kanal einen 210KΩ Widerstand.

Beachte, dass der ESP32 ein Stereo-Signal erzeugen muss und der MAX98357A einfach je nach Spannung am SD-Pin den linken oder rechten Kanal des Stereo-Eingangs auswählt.

Wie zuvor ist es sicherer, den MAX98357A nicht über den 3,3V-Pin des ESP32 zu betreiben, sondern eine externe Stromversorgung zu verwenden. Das Schaltbild zeigt, wie du eine externe 5V-Stromquelle anschließt:

Two MAX98357A with ESP32 with external 5V for Stereo Sound
Zwei MAX98357A mit ESP32 und externer 5V für Stereo-Sound

Beachte, dass der SD-Pin des MAX98357A weiterhin über einen 100KΩ oder 210KΩ Widerstand mit 3,3V verbunden ist. Du kannst die Widerstände auch an die externe 5V-Stromversorgung anschließen, solltest dann aber für den rechten Kanal einen 370KΩ Widerstand statt des 210KΩ Widerstands verwenden. Der 100KΩ Widerstand für den linken Kanal kann bleiben.

SD-Karte

Wenn du Audiodateien abspielen möchtest, musst du einen SD-Kartenleser anschließen, der die Audiodateien auf einer SD-Karte speichert. Das folgende Schaltbild zeigt, wie du einen SD-Kartenleser und den MAX98357A an einen ESP32 anschließt:

Single MAX98357A with ESP32 and SD Card Reader for Mono Sound
Einzelner MAX98357A mit ESP32 und SD-Kartenleser für Mono-Sound

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 in das Find I2C and SPI default pins Tutorial.

Wenn du Stereo-Sound abspielen möchtest, musst du zwei MAX98357A-Module anschließen. Das folgende Diagramm zeigt, wie das geht:

Two MAX98357A with ESP32 and SD Card Reader for Stereo Sound
Zwei MAX98357A mit ESP32 und SD-Kartenleser für Stereo-Sound

Wenn du weitere Hilfe beim Anschluss des SD-Kartenlesers benötigst, sieh dir unser SD Card Module with ESP32 Tutorial an.

Bibliotheken installieren

Es gibt mehrere Arduino-Bibliotheken, mit denen du Audio für ein I2S-Gerät wie den MAX98357A erzeugen kannst. Im Folgenden stelle ich kurz die drei am häufigsten verwendeten vor.

Erstens gibt es die ESP8266Audio Bibliothek von Earle F. Philhower, die ESP8266, ESP32, Raspberry Pi Pico RP2040 und Pico 2 RP2350 Boards unterstützt.

Dann gibt es die ESP32-audioI2S Bibliothek von schreibfaul1. Beachte, dass diese Bibliothek nur auf Multi-Core-Chips wie ESP32, ESP32-S3, ESP32-P4 funktioniert und dein Board PSRAM haben muss. Sie funktioniert nicht auf ESP32-S2, ESP32-C3 Boards.

Schließlich gibt es die arduino-audio-tools Bibliothek von Phil Schatzmann, die die leistungsstärkste Bibliothek mit vielen, vielen Funktionen ist. Das ist die Bibliothek, die wir in diesem Tutorial verwenden werden, aber auch die anderen Bibliotheken sind definitiv einen Blick wert.

ESP32 Core installieren

Stand Januar 2026 konnte ich die arduino-audio-tools Bibliothek nicht mit dem aktuellen ESP32 Core (Version 3.3.6) zum Laufen bringen. Für die Beispiele in diesem Tutorial musst du den ESP32 Core auf Version 2.0.17 downgraden.

Angenommen, du hast den ESP32 Core bereits installiert, ist das Downgrade einfach. Öffne den BOARDS MANAGER, tippe „esp32“ in die Suchleiste und wähle dann die Version 2.0.17 für den „esp32 by Espressif“ Core aus und klicke auf „UPDATE“, wie unten gezeigt:

Installing ESP32 Core Version 2.0.17
ESP32 Core Version 2.0.17 installieren

Wenn du weitere Hilfe beim Downgrade oder bei der Installation des ESP Cores benötigst, sieh dir das Install ESP32 core in Arduino IDE Tutorial an.

arduino-audio-tools Bibliothek installieren

Um die arduino-audio-tools 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 … und installiere die heruntergeladene ZIP-Bibliothek (arduino-audio-tools-main.zip):

Für einige der 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 zum Github-Repo, klicke auf den grünen „<> Code“-Button, um die Bibliotheken (arduino-libhelix-main.zip, ESP32-A2DP-main.zip) herunterzuladen und installiere sie dann.

Testton abspielen

Bevor wir etwas Komplexes ausprobieren, spielen wir zuerst einen Testton ab. Damit kannst du die Verdrahtung des MAX98357A mit dem ESP32 überprüfen und sicherstellen, dass bei Stereo-Wiedergabe die Kanäle korrekt ausgewählt werden. Außerdem benötigt dieser Code keine zusätzlichen Bibliotheken und funktioniert sowohl mit dem aktuellen (3.x) als auch mit dem alten (2.x) ESP32 Core.

#include <driver/i2s.h>
#include <math.h>

#define I2S_PORT I2S_NUM_0

// MAX98357
#define MAX_DIN 33   
#define MAX_LRC 32   
#define MAX_BCLK 25  

// Audio parameters
#define SAMPLE_RATE 44100
#define TONE_FREQ 500
#define AMPLITUDE 1000  // Max 32767

// Channels
#define LEFT false
#define RIGHT true

// Buffer size (frames, not samples)
#define BUFFER_LEN 256

// Stereo buffer: Left, Right
int16_t samples[BUFFER_LEN * 2];

void setup() {
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = BUFFER_LEN,
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0
  };

  i2s_pin_config_t pin_config = {
    .bck_io_num = MAX_BCLK,
    .ws_io_num = MAX_LRC,
    .data_out_num = MAX_DIN,
    .data_in_num = I2S_PIN_NO_CHANGE
  };

  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);
}

void loop() {
  static float phase = 0.0;
  const float phaseIncrement = 2.0 * PI * TONE_FREQ / SAMPLE_RATE;

  for (int i = 0; i < BUFFER_LEN; i++) {
    int16_t sound = (int16_t)(AMPLITUDE * sin(phase));
    int16_t silence = 0;

    samples[2 * i] = (LEFT) ? sound : silence;       // Left channel
    samples[2 * i + 1] = (RIGHT) ? sound : silence;  // Right channel

    phase += phaseIncrement;
    if (phase >= 2.0 * PI) phase -= 2.0 * PI;
  }

  size_t bytes_written;
  i2s_write(I2S_PORT, samples, sizeof(samples), &bytes_written, portMAX_DELAY);
}

Der Code konfiguriert den ESP32 als I2S-Master-Transmitter, erzeugt in Echtzeit ein Sinuswellen-Audiosignal und sendet es an den Verstärker, um Ton auf dem linken, rechten oder beiden Kanälen zu erzeugen.

Imports

Der Code bindet die driver/i2s.h Bibliothek ein, die die ESP32 I2S-Treiberfunktionen für die Audiokommunikation bereitstellt. Außerdem wird die Standard-Mathebibliothek math.h eingebunden, um mathematische Funktionen wie sin() zur Erzeugung der Audio-Wellenform zu verwenden.

#include <driver/i2s.h>;
#include <math.h>;

Konstanten

Es werden mehrere Konstanten definiert, um die I2S-Schnittstelle und die Audio-Parameter zu konfigurieren. I2S_PORT wählt das I2S-Peripheriegerät Nummer 0 auf dem ESP32 aus. Die Pins MAX_DIN, MAX_LRC und MAX_BCLK entsprechen den Daten-, Word Select (Left-Right Clock)- und Bit Clock-Leitungen, die mit dem MAX98357 Verstärker verbunden sind.

Zu den Audio-Parametern gehören die Abtastrate von 44.100 Hz, eine Tonfrequenz von 500 Hz und eine Amplitude von 1000 (im Bereich eines 16-Bit-Signed-Integer). Am wichtigsten: Der Code definiert auch die booleschen Flags LEFT und RIGHT, um zu steuern, welche Audiokanäle Ton ausgeben.

Schließlich legt BUFFER_LEN die Anzahl der Audio-Frames pro Buffer fest, und ein Stereo-Buffer-Array samples enthält die ineinander verschachtelten Audio-Samples für linken und rechten Kanal.

#define I2S_PORT I2S_NUM_0

// MAX98357
#define MAX_DIN 33   
#define MAX_LRC 32   
#define MAX_BCLK 25  

// Audio parameters
#define SAMPLE_RATE 44100
#define TONE_FREQ 500
#define AMPLITUDE 1000  // Max 32767

// Channels
#define LEFT false
#define RIGHT true

// Buffer size (frames, not samples)
#define BUFFER_LEN 256

// Stereo buffer: Left, Right
int16_t samples[BUFFER_LEN * 2];

Setup-Funktion

Die setup() Funktion initialisiert den I2S-Treiber und konfiguriert die Pins für die Kommunikation mit dem MAX98357 Verstärker.

Eine i2s_config_t Struktur wird erstellt, um den I2S-Modus als Master-Transmitter, 16-Bit-Samples, Stereo-Kanalformat (rechts und links) und eine Abtastrate von 44,1 kHz festzulegen. Außerdem werden DMA-Buffer-Parameter für effizienten Datentransfer gesetzt.

Als nächstes weist eine i2s_pin_config_t Struktur die GPIO-Pins für Bit Clock (bck_io_num), Word Select (ws_io_num) und Data Output (data_out_num) zu. Der Data Input Pin wird nicht verwendet und auf I2S_PIN_NO_CHANGE gesetzt.

Abschließend wird der I2S-Treiber mit i2s_driver_install() installiert und die Pin-Konfiguration mit i2s_set_pin() angewendet.

void setup() {
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 8,
    .dma_buf_len = BUFFER_LEN,
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0
  };

  i2s_pin_config_t pin_config = {
    .bck_io_num = MAX_BCLK,
    .ws_io_num = MAX_LRC,
    .data_out_num = MAX_DIN,
    .data_in_num = I2S_PIN_NO_CHANGE
  };

  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_PORT, &pin_config);
}

Loop-Funktion

Die loop() Funktion erzeugt kontinuierlich Audiosamples und sendet sie zur Wiedergabe an das I2S-Peripheriegerät.

Eine statische Variable phase verfolgt die aktuelle Position innerhalb des Sinuswellen-Zyklus. Das phaseIncrement wird basierend auf der gewünschten Tonfrequenz und Abtastrate berechnet und bestimmt, wie stark die Phase pro Sample fortschreitet.

Im Loop füllt der Code den samples Buffer mit ineinander verschachtelten Stereo-Audiodaten. Für jedes Frame berechnet er das Sinus der aktuellen Phase, skaliert es mit der Amplitude und wandelt es in einen 16-Bit-Integer um. Je nach LEFT und RIGHT Flags wird der Ton dem linken und/oder rechten Kanal zugewiesen, während der andere Kanal auf Stille (Null) gesetzt wird.

Die Phase wird inkrementiert und bleibt durch Modulo-Operation im Bereich von 0 bis 2π Radiant, um eine kontinuierliche Wellenform zu gewährleisten.

Nach dem Füllen des Buffers sendet die i2s_write() Funktion die Audiodaten an den I2S-Treiber, der sie an den Verstärker überträgt. Die Funktion blockiert, bis alle Bytes geschrieben sind, um eine gleichmäßige Audiowiedergabe zu gewährleisten.

void loop() {
  static float phase = 0.0;
  const float phaseIncrement = 2.0 * PI * TONE_FREQ / SAMPLE_RATE;

  for (int i = 0; i < BUFFER_LEN; i++) {
    int16_t sound = (int16_t)(AMPLITUDE * sin(phase));
    int16_t silence = 0;

    samples[2 * i] = (LEFT) ? sound : silence;       // Left channel
    samples[2 * i + 1] = (RIGHT) ? sound : silence;  // Right channel

    phase += phaseIncrement;
    if (phase >= 2.0 * PI) phase -= 2.0 * PI;
  }

  size_t bytes_written;
  i2s_write(I2S_PORT, samples, sizeof(samples), &bytes_written, portMAX_DELAY);
}

Du kannst diesen Code verwenden, um zu überprüfen, ob Stereo-Sound korrekt abgespielt wird, wenn du zwei MAX98357-Module und zwei Lautsprecher verwendest. Wenn du die LEFT-Konstante auf true und die RIGHT-Konstante auf false setzt, sollte nur der linke Lautsprecher Ton ausgeben. Genauso kannst du prüfen, ob der rechte Kanal korrekt funktioniert. Falls nicht, überprüfe die Spannung und die Widerstände am SD-Pin des MAX98357, der den Kanal steuert.

Für die folgenden Beispiele verwenden wir die arduino-audio-tools Bibliothek, die alle Details der I2S-Kommunikation übernimmt und den Code vereinfacht.

Text zu Sprache

Im nächsten Beispiel wird gezeigt, wie du Text in Sprache umwandelst und über die I2S-Schnittstelle mit einem MAX98357 Verstärker abspielst. Es verbindet sich mit WiFi, sendet Text an die OpenAI Text-to-Speech (TTS) API, empfängt einen MP3-Audiostream, dekodiert ihn und spielt ihn über den Verstärker ab. Der Code verwendet die arduino-audio-tools Bibliothek für Audio-Streaming und Dekodierung.

/*
www.makerguides.com

Libraries:
- ESP32 Core 2.0.17
- [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"

// MAX98357 I2S pins
#define MAX_DIN   33
#define MAX_LRC   32
#define MAX_BCLK  25

// 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  = MAX_BCLK;
  config.pin_ws   = MAX_LRC;
  config.pin_data = MAX_DIN;
  i2s.begin(config);

  mp3decode.begin();
  volume.begin(config);
  volume.setVolume(TTS_VOLUME);

  text2speech("Hello, this a test for text to speech.");
}

void loop() {
  copier.copy();
}

Imports

Der Code beginnt mit dem Einbinden mehrerer Bibliotheken. Arduino.h ist die zentrale Arduino-Bibliothek. WiFi.h und WiFiClientSecure.h sorgen für WiFi-Konnektivität und sichere HTTPS-Client-Funktionalität. 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

Als nächstes werden die Pins für die I2S-Schnittstelle zum MAX98357 Verstärker definiert. Diese Pins entsprechen dem Data Input (MAX_DIN), Word Select bzw. Left-Right Clock (MAX_LRC) und Bit Clock (MAX_BCLK).

// MAX98357 I2S pins
#define MAX_DIN   33
#define MAX_LRC   32
#define MAX_BCLK  25

Zusätzlich werden Konstanten für das TTS-Modell, die Stimme und die Lautstärke festgelegt.

// Text to Speech
#define TTS_MODEL "gpt-4o-mini-tts"
#define TTS_VOICE "marin"
#define TTS_VOLUME 0.6

Du kannst auch andere TTS-Modelle wie „tts-1“ und andere Stimmen wie „alloy“, „ash“, „coral“, „echo“, „fable“, „onyx“, „nova“, ’sage“, „shimmer“, „marin“, „cedar“ ausprobieren. Für mehr Details siehe platform.openai.com/docs/guides/text-to-speech.

Auch die WiFi-Zugangsdaten und OpenAI API-Details werden als konstante Strings gespeichert. Du musst „ssid“ und „pwd“ durch die Zugangsdaten deines WLANs ersetzen.

// WiFi credentials
const char* ssid     = "ssid";
const char* password = "pwd";

Du musst dir außerdem einen „apikey“ von OpenAI besorgen. Gehe zu https://platform.openai.com und registriere dich mit einer E-Mail-Adresse oder einem bestehenden Google- oder Microsoft-Konto.

// OpenAI configuration
const char* openaiHost = "api.openai.com";
const int   openaiPort = 443;
const char* openaiApiKey = "apikey";

Nach der Bestätigung deiner E-Mail und dem Abschluss der Ersteinrichtung logge dich ins OpenAI Dashboard ein, platform.openai.com/api-keys und finde oder erstelle deinen API Key (=SECRET KEY), wie unten gezeigt:

OpenAI API keys
OpenAI API-Schlüssel

Der API Key ist eine eindeutige, 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 Key zu bekommen, aber ich empfehle dir, auch ein Nutzungslimit für dein Konto zu setzen. Für mehr Details siehe das Vision Chatbot with DFRobot ESP32-S3 AI Camera and OpenAI Tutorial.

Audio- und Netzwerkobjekte

Es werden mehrere Objekte instanziiert, um Audio-Streaming und Netzwerkkommunikation zu verwalten. WiFiClientSecure übernimmt die HTTPS-Verbindung zur OpenAI API. I2SStream verwaltet die I2S-Audioausgabe. VolumeStream kapselt den I2S-Stream, um die Lautstärke zu steuern. EncodedAudioStream dekodiert die MP3-Audiodaten mit dem Helix MP3 Decoder. Schließlich kopiert StreamCopy den dekodierten Audiostream vom Netzwerk-Client zur Audioausgabe.

WiFiClientSecure client;
I2SStream i2s;
VolumeStream volume(i2s);
EncodedAudioStream mp3decode(&volume, new MP3DecoderHelix());
StreamCopy copier(mp3decode, client);

Text-zu-Sprache-Funktion

Die text2speech() Funktion sendet einen Textstring an die OpenAI TTS API und bereitet den Audiostream zur Wiedergabe vor. Zuerst wird der Client so konfiguriert, dass unsichere Zertifikate akzeptiert werden (praktisch für die Entwicklung). Dann wird versucht, eine Verbindung zum OpenAI-Server auf Port 443 herzustellen. Falls die Verbindung fehlschlägt, wird eine Fehlermeldung ausgegeben und die Funktion beendet.

Die Funktion baut einen JSON-Body mit TTS-Modell, Stimme, Ausgabeformat (MP3) und dem Eingabetext. Sie sendet eine HTTP POST-Anfrage mit den passenden Headern, einschließlich der Autorisierung mit dem API Key. Nach dem Senden des Request-Bodys werden die HTTP-Response-Header übersprungen, um den Client-Stream an den Anfang der MP3-Audiodaten zu setzen.

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

Die setup() Funktion initialisiert die serielle Kommunikation zum Debuggen und stellt den Audio-Logger auf Warnungen ein. Dann wird die Verbindung zum angegebenen WLAN hergestellt und gewartet, bis sie steht.

Nach erfolgreicher WLAN-Verbindung wird die I2S-Schnittstelle für den Sendemodus mit den zuvor definierten Pins konfiguriert. Der I2S-Stream, der MP3-Decoder und die Lautstärkeregelung werden initialisiert. Die Lautstärke wird auf den voreingestellten Wert gesetzt.

Abschließend wird die text2speech() Funktion mit einem Beispieltext aufgerufen, um das Streaming und die Wiedergabe der synthetisierten Sprache zu starten.

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  = MAX_BCLK;
  config.pin_ws   = MAX_LRC;
  config.pin_data = MAX_DIN;
  i2s.begin(config);

  mp3decode.begin();
  volume.begin(config);
  volume.setVolume(TTS_VOLUME);

  text2speech("Hello, this a test for text to speech.");
}

Loop-Funktion

Die loop() Funktion kopiert kontinuierlich Daten vom MP3-Decoder-Stream zur I2S-Audioausgabe. So bleibt die Audiowiedergabe aktiv, solange Daten vom Netzwerk-Client verfügbar sind.

void loop() {
  copier.copy();
}

Internetradio

Dieses Beispiel zeigt, wie du ein einfaches Webradio umsetzt. Es verbindet sich mit einem WLAN, streamt Internetradio im MP3-Format, dekodiert das Audio, passt die Lautstärke an und gibt den Ton über den digitalen MAX98357 Verstärker aus.

/*
www.makerguides.com

Libraries:
- ESP32 Core 2.0.17
- [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"

// MAX98357
#define MAX_DIN 33   // serial data
#define MAX_LRC 32   // word select
#define MAX_BCLK 25  // serial clock
#define MAX_VOL 0.5  // 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 = MAX_BCLK;
  config.pin_ws = MAX_LRC;
  config.pin_data = MAX_DIN;

  i2s.begin(config);
  volume.begin(config);
  volume.setVolume(MAX_VOL);
  mp3decode.begin();
  icystream.begin(url);
  icystream.setMetadataCallback(callbackMetadata);
}

void loop() {
  copier.copy();
}

Imports

Der Code beginnt mit dem Einbinden der Bibliotheken, die für WLAN-Konnektivität, I2C-Kommunikation und Audiobearbeitung nötig sind. Die Arduino.h und WiFi.h Bibliotheken bieten grundlegende Arduino- und WLAN-Funktionen. Die Wire.h Bibliothek ist für die I2C-Kommunikation zuständig, die oft zur Steuerung von Peripheriegeräten verwendet wird. Die AudioTools Bibliothek und ihre 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

Als nächstes werden die Pins für den MAX98357 Verstärker definiert. Diese Pins entsprechen den I2S-Signalen: MAX_DIN für den seriellen Dateneingang, MAX_LRC für Word Select (Left-Right Clock) und MAX_BCLK für den seriellen Takt. Die Lautstärke wird als Fließkommazahl zwischen 0 und 1 gesetzt, wobei MAX_VOL 0,5 ist, also 50% Lautstärke.

#define MAX_DIN 33   // serial data
#define MAX_LRC 32   // word select
#define MAX_BCLK 25  // serial clock
#define MAX_VOL 0.5  // Volume

WLAN-Zugangsdaten und Stream-URL

Die WLAN-Zugangsdaten werden in den Konstanten ssid und password gespeichert. Die Konstante url enthält die Adresse des Internetradio-Streams, der abgespielt werden soll, in diesem Fall ein Jazz-Stream.

const char* ssid = "ssid";
const char* password = "pwd";
const char* url = "https://jazz.stream.laut.fm/jazz";

Hier sind noch ein paar weitere URLs von Internetradio-Streams, 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-Objekte

Es werden mehrere Objekte instanziiert, um die Audiokette zu verwalten. Das ICYStream Objekt übernimmt das HTTP-Streaming des Internetradios und verwaltet die I2S-Audioausgabe. Das VolumeStream Objekt kapselt den I2S-Stream zur Lautstärkeregelung. Und das EncodedAudioStream Objekt dekodiert die MP3-Daten mit dem MP3DecoderHelix Codec. Schließlich kopiert das StreamCopy Objekt die dekodierten Audiodaten vom MP3-Decoder zum ICY-Stream.

ICYStream icystream;
I2SStream i2s;
VolumeStream volume(i2s);
EncodedAudioStream mp3decode(&volume, new MP3DecoderHelix());
StreamCopy copier(mp3decode, icystream);

Metadata Callback Funktion

Die callbackMetadata() Funktion wird definiert, um Metadaten vom Internetradio-Stream zu verarbeiten, wie Songtitel oder Künstlerinformationen. Sie gibt den Metadaten-Typ und den Inhalt zur Fehlersuche oder Information 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 initialisiert, um Logging und Debug-Ausgaben zu ermöglichen. Der Audio-Logger wird so konfiguriert, dass er Warnungen und höher auf dem seriellen Monitor ausgibt.

Der ESP32 versucht dann, sich mit dem angegebenen WLAN zu verbinden, und prüft alle 500 Millisekunden den Verbindungsstatus, bis die Verbindung steht.

Nach der Verbindung wird die I2S-Konfiguration im Standard-Transmit-Modus abgerufen. Die I2S-Pins werden den zuvor definierten Konstanten für Bit Clock, Word Select und Data Input zugewiesen. Die I2S-Schnittstelle und die Lautstärkeregelung werden mit dieser Konfiguration initialisiert und die Lautstärke auf 50% gesetzt.

Der MP3-Decoder wird gestartet und der Internetradio-Stream mit der angegebenen URL initialisiert. Die Metadata Callback Funktion wird registriert, um eingehende Metadaten während der Wiedergabe 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 = MAX_BCLK;
  config.pin_ws = MAX_LRC;
  config.pin_data = MAX_DIN;

  i2s.begin(config);
  volume.begin(config);
  volume.setVolume(MAX_VOL);
  mp3decode.begin();
  icystream.begin(url);
  icystream.setMetadataCallback(callbackMetadata);
}

Loop-Funktion

Die loop() Funktion kopiert kontinuierlich Audiodaten vom Internetradio-Stream durch den MP3-Decoder und die Lautstärkeregelung zur I2S-Ausgabe. Dieser Prozess hält die Audiowiedergabe dauerhaft am Laufen.

void loop() {
  copier.copy();
}

Wenn du mehr Funktionen wie einen Lautstärkeregler hinzufügen möchtest, sieh dir unser Playing Audio with ESP32 and PCM5102A Tutorial an. Und wenn du ein Display hinzufügen möchtest, siehe das Internet Radio with ESP32 and MAX 98357A Tutorial.

MP3 von SD-Karte abspielen

Dieses Beispiel zeigt, wie du MP3-Audiodateien, die auf einer SD-Karte gespeichert sind, mit einem ESP32, dem MAX98357 I2S-Verstärker und der AudioTools-Bibliothek abspielst. Der Code initialisiert die Audio-Hardware, richtet den MP3-Decoder ein und streamt kontinuierlich Audiodaten an den Verstärker.

/*
www.makerguides.com

Libraries:
- ESP32 Core 2.0.17
- [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"

// MAX98357
#define MAX_DIN 33   
#define MAX_LRC 32   
#define MAX_BCLK 25  

#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 cfg = i2s.defaultConfig(TX_MODE);
  cfg.pin_bck = MAX_BCLK;  
  cfg.pin_ws = MAX_LRC;    
  cfg.pin_data = MAX_DIN;    
  i2s.begin(cfg);

  //source.setFileFilter("*Bob Dylan*");
  player.setMetadataCallback(printMetaData);
  player.setVolume(0.4);
  player.begin();
}

void loop() {
  player.copy();
}

Imports

Der Code beginnt mit dem Einbinden der Header-Dateien aus der AudioTools-Bibliothek. Diese stellen die notwendigen Klassen und Funktionen zum Verarbeiten von Audioquellen, Dekodieren von MP3-Dateien und Streamen von Audiodaten über I2S bereit.

#include "AudioTools.h"
#include "AudioTools/Disk/AudioSourceSD.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"

Konstanten

Als nächstes werden die Pins definiert, die mit dem MAX98357 Verstärker verbunden sind. Diese geben die I2S Data Input (MAX_DIN), Word Select bzw. Left-Right Clock (MAX_LRC) und Bit Clock (MAX_BCLK) Pins am ESP32 an.

#define MAX_DIN 33   
#define MAX_LRC 32   
#define MAX_BCLK 25  

Zusätzlich werden Konstanten für den Pfad der Audioquelle und die Dateiendung gesetzt. Hier ist PATH das Stammverzeichnis auf der SD-Karte und EXT gibt an, dass nur MP3-Dateien berücksichtigt werden.

#define PATH "/"
#define EXT "mp3"

Objekte

Es werden mehrere Objekte instanziiert, um die Audiowiedergabe zu verwalten. AudioSourceSD repräsentiert die SD-Karten-Audioquelle und filtert Dateien nach Pfad und Endung. I2SStream übernimmt den I2S-Audioausgabestream. MP3DecoderHelix ist der MP3-Decoder auf Basis des Helix-Codecs. Schließlich verbindet AudioPlayer diese Komponenten zur Steuerung der Wiedergabe.

AudioSourceSD source(PATH, EXT);
I2SStream i2s;
MP3DecoderHelix decoder;
AudioPlayer player(source, i2s, decoder);

Metadata Callback Funktion

Die Funktion printMetaData() ist dafür zuständig, Metadaten wie Künstler oder Titel zu verarbeiten. Sie erhält den Metadaten-Typ und -String und gibt ihn zur Information oder Fehlersuche auf der seriellen Konsole aus.

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 Logging initialisiert. Der AudioTools-Logger wird ebenfalls mit Warnlevel gestartet, um wichtige Meldungen zu erfassen.

Die I2S-Konfiguration wird aus den Standardeinstellungen für den Sendemodus übernommen. Die Pins für Bit Clock, Word Select und Data werden den zuvor definierten Konstanten entsprechend den MAX98357-Anschlüssen zugewiesen. Der I2S-Stream wird dann mit dieser Konfiguration initialisiert.

Optional kann ein Dateifilter auf die Audioquelle gesetzt werden, um nur Dateien mit einem bestimmten Muster abzuspielen (in diesem Beispiel auskommentiert). Die Metadata Callback Funktion wird auf die printMetaData() Funktion gesetzt, um Metadaten während der Wiedergabe zu empfangen. Die Lautstärke wird auf 40% gesetzt, um die Ausgangslautstärke zu steuern. Schließlich wird der Audioplayer gestartet.

void setup() {
  Serial.begin(115200);
  AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);

  auto cfg = i2s.defaultConfig(TX_MODE);
  cfg.pin_bck = MAX_BCLK;  
  cfg.pin_ws = MAX_LRC;    
  cfg.pin_data = MAX_DIN;    
  i2s.begin(cfg);

  //source.setFileFilter("*Bob Dylan*");
  player.setMetadataCallback(printMetaData);
  player.setVolume(0.4);
  player.begin();
}

Loop-Funktion

Die loop() Funktion ruft kontinuierlich player.copy() auf, das den Audiostream von der SD-Karte durch den Decoder und über I2S an den Verstärker überträgt. So läuft die Audiowiedergabe reibungslos, ohne andere Prozesse zu blockieren.

void loop() {
  player.copy();
}

Wenn du mehr Funktionen wie einen Lautstärkeregler und Tasten zum Überspringen von Titeln hinzufügen möchtest, sieh dir das Playing Audio with ESP32 and PCM5102A Tutorial an.

Audio von Bluetooth abspielen

Dieses Beispiel zeigt, wie du Bluetooth-Audio streamst. Der Code richtet die I2S-Schnittstelle mit bestimmten Pins ein und initialisiert einen Bluetooth A2DP Sink, sodass der ESP32 Audio von Bluetooth-Geräten empfangen und abspielen kann.

/*
www.makerguides.com

Libraries:
- ESP32 Core 2.0.17
- [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 MAX_DIN 33   // serial data
#define MAX_LRC 32   // word select
#define MAX_BCLK 25  // serial clock

I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);

void setup() {
  auto cfg = i2s.defaultConfig();
  cfg.pin_bck = MAX_BCLK;
  cfg.pin_ws = MAX_LRC;
  cfg.pin_data = MAX_DIN;
  i2s.begin(cfg);

  a2dp_sink.start("MyMusic");
}

void loop() { }

Imports

Der Code beginnt mit dem Einbinden von zwei wichtigen Bibliotheken. Die AudioTools.h Bibliothek stellt Werkzeuge zum Verarbeiten von Audiostreams und zur Konfiguration der I2S-Schnittstelle bereit. Die BluetoothA2DPSink.h Bibliothek ermöglicht es dem ESP32, als Bluetooth A2DP Sink zu fungieren, also Audiodaten von Bluetooth-Quellen wie Smartphones zu empfangen.

#include "AudioTools.h"
#include "BluetoothA2DPSink.h"

Konstanten

Als nächstes werden drei Konstanten definiert, die die GPIO-Pins für die I2S-Schnittstelle festlegen. Diese Pins verbinden den ESP32 mit dem MAX98357 Verstärker. MAX_DIN ist der serielle Dateneingang, MAX_LRC ist der Word Select bzw. Left-Right Clock Pin und MAX_BCLK ist der serielle Taktpin.

#define MAX_DIN 33   // serial data
#define MAX_LRC 32   // word select
#define MAX_BCLK 25  // serial clock

Objekte

Ein I2SStream Objekt namens i2s wird erstellt, um den I2S-Audiostream zu verwalten. Dann wird ein BluetoothA2DPSink Objekt namens a2dp_sink instanziiert, dem das i2s Objekt übergeben wird. Damit wird der Bluetooth-Audioeingang direkt mit dem I2S-Ausgang verbunden und ermöglicht nahtlose Audiowiedergabe über den Verstärker.

I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);

Setup-Funktion

In der setup() Funktion wird die I2S-Schnittstelle konfiguriert und gestartet. Zuerst wird die Standard-I2S-Konfiguration mit i2s.defaultConfig() abgerufen. Dann werden die Pin-Zuweisungen für Bit Clock (pin_bck), Word Select (pin_ws) und Data Input (pin_data) auf die zuvor definierten Konstanten gesetzt. Schließlich wird die I2S-Schnittstelle mit dieser Konfiguration durch Aufruf von i2s.begin(cfg) initialisiert.

Nach dem Einrichten von I2S wird der Bluetooth A2DP Sink mit dem Gerätenamen "MyMusic" gestartet. Dieser Name erscheint, wenn andere Bluetooth-Geräte nach verfügbaren Audio-Sinks suchen.

void setup() {
  auto cfg = i2s.defaultConfig();
  cfg.pin_bck = MAX_BCLK;
  cfg.pin_ws = MAX_LRC;
  cfg.pin_data = MAX_DIN;
  i2s.begin(cfg);

  a2dp_sink.start("MyMusic");
}

Um das auszuprobieren, öffne dein Handy, suche nach „Verbundene Geräte“ oder „Bluetooth-Geräte“, suche nach dem Gerät „MyMusic“, verbinde dich damit und spiele Musik ab. Du solltest sie dann über deinen ESP32 und MAX98357 hören.

Loop-Funktion

Die loop() Funktion bleibt leer, da das gesamte Audio-Streaming und die Wiedergabe asynchron vom Bluetooth A2DP Sink und der I2S-Schnittstelle übernommen werden. Nach dem Start hört der ESP32 kontinuierlich auf Bluetooth-Audiostreams und gibt sie über I2S an den Verstärker aus, ohne dass weiterer Code in der Hauptschleife nötig ist.

void loop() { }

Fazit

In diesem Projekt hast du gelernt, wie du Audio mit dem ESP32 und dem MAX98357 Verstärker abspielst. Wir haben die technischen Details des MAX98357A Moduls und die Verdrahtung mit dem ESP32 für Mono- oder Stereo-Sound behandelt. Außerdem hast du gelernt, wie man Text in Sprache umwandelt, Internetradio streamt, MP3-Dateien von einer SD-Karte abspielt und Audio über Bluetooth wiedergibt.

Wenn du mehr Funktionen wie einen Lautstärkeregler und Tasten zum Überspringen von Titeln hinzufügen möchtest, sieh dir unser Playing Audio with ESP32 and PCM5102A Tutorial an. Wenn du mehr Hintergrund zum hier verwendeten SD-Kartenleser-Modul brauchst, schau in das SD Card Module with ESP32 Tutorial.

Für besseren und lauteren Sound kannst du einen PCM5102A DAC verwenden und einen Verstärker hinzufügen. Sieh dir dazu die folgenden Tutorials an:

Wenn du Fragen hast, hinterlasse sie gerne im Kommentarbereich.

Viel Spaß beim Tüfteln ; )

FAQ

F: Was ist der MAX98357A und warum sollte ich ihn verwenden?

Der MAX98357A ist ein digitaler Audioverstärker mit eingebautem DAC. Das bedeutet, er nimmt digitale Audiodaten direkt vom ESP32 über I2S entgegen und gibt ein bereits verstärktes Signal aus, das direkt einen Lautsprecher antreiben kann – ohne zusätzlichen Verstärker. Dadurch wird die Schaltung einfacher und die Klangqualität ist besser als mit dem internen DAC des ESP32.

F: Wie wird der MAX98357A an den ESP32 angeschlossen?

Das Modul verwendet drei I2S-Signale plus Stromversorgung.

Typische Verdrahtung:

ESP32_3V3 or 5V ----> VIN
ESP32_GND ---------> GND

ESP32_GPIO25 -----> BCLK
ESP32_GPIO32 -----> LRC
ESP32_GPIO33 -----> DIN

Der ESP32 arbeitet als I2S-Master und sendet Audiodaten, während der MAX98357A sie empfängt und in Ton umwandelt.

F: Kann ich einen Lautsprecher direkt an den MAX98357A anschließen?

Ja, denn der MAX98357A hat bereits eine Verstärkerstufe integriert, sodass du einen Lautsprecher direkt anschließen kannst.

SPK+ ---- Speaker ---- SPK-

Typische Lautsprecher sind 4Ω bis 8Ω, 3W oder mehr. Der Verstärker liefert etwa 3W Leistung, was für kleine Lautsprecher ausreicht.

F: Wie bekomme ich Stereo-Sound?

Der MAX98357A ist mono, für Stereo brauchst du also zwei Module.

ESP32 --> 2x MAX98357A --> 2x Speakers

Beide Module teilen sich die gleichen I2S-Signale, aber du verwendest Widerstände, um linken und rechten Kanal auszuwählen.

SD -- 100kΩ -- 3.3V (left channel)
SD -- 210kΩ -- 3.3V (right channel)

So spielt jeder Verstärker einen Kanal ab.

F: Welche Stromversorgung sollte ich verwenden?

Das Modul funktioniert mit 3,3V oder 5V, aber die Stromversorgung ist wichtig für guten Klang. Bei hoher Lautstärke kann das Modul viel Strom ziehen, daher reicht die Versorgung über den 3,3V-Pin des ESP32 oft nicht aus und kann zu Verzerrungen oder Resets führen.

F: Wie kann ich die Klangqualität mit Stromversorgungsfiltern verbessern?

Eine saubere Stromversorgung ist entscheidend, da Störungen direkt den Audioausgang beeinflussen. Du kannst Kondensatoren hinzufügen:

VIN -- 100nF -- GND
VIN -- 100µF -- GND

Der kleine Kondensator entfernt schnelle Schaltstörungen, der große glättet Spannungsschwankungen und verhindert Einbrüche bei lauten Audiospitzen.

Zusätzlich kannst du einen Filterkondensator verwenden:

5V -- 10Ω --+-- AMP_VIN
|
220µF
|
GND

Das reduziert Störungen durch USB oder Schaltregler.

F: Sollte ich eine externe Stromversorgung verwenden?

Ja, besonders bei hoher Lautstärke, da der MAX98357A bei lauter Wiedergabe hohe Ströme ziehen kann und der ESP32-Regler das nicht immer zuverlässig schafft.

External 5V ----> MAX98357A
ESP32_GND ------> Shared GND

Das verbessert die Stabilität und reduziert Verzerrungen.

F: Wie kann ich die Lautstärke erhöhen?

Die Lautstärke hängt von drei Hauptfaktoren ab: Versorgungsspannung, Verstärker-Gain und Lautsprechertyp. Um die Lautstärke zu erhöhen:

  • 5V statt 3,3V verwenden
  • Einen Lautsprecher mit niedrigerer Impedanz (4Ω statt 8Ω) verwenden

Ein 4Ω-Lautsprecher zieht mehr Strom und ist lauter, belastet aber auch den Verstärker stärker.

F: Was ist der GAIN-Pin und wie beeinflusst er den Klang?

Der GAIN-Pin legt den Verstärkungspegel des Verstärkers fest.

GAIN -- GND (lower gain)
GAIN -- VCC (higher gain)
GAIN -- FLOAT (default)

Höherer Gain erhöht die Lautstärke, kann aber auch Rauschen und Verzerrungen verstärken. Es ist oft besser, mit niedrigem Gain zu starten und die Lautstärke per Software zu regeln.

F: Warum höre ich Rauschen oder Zischen, auch wenn kein Audio abgespielt wird?

Rauschen kann von verschiedenen Quellen kommen, z.B. Netzteilwelligkeit, schlechter Masseführung oder Störungen vom ESP32. Häufige Lösungen:

  • Entkopplungskondensatoren hinzufügen
  • Saubere externe Stromversorgung verwenden
  • Kabel kurz halten

F: Welche Verdrahtung verbessert die Klangqualität?

Gute Verdrahtung ist sehr wichtig, besonders bei digitalem Audio und Schaltverstärkern. Halte Kabel kurz:

ESP32 --> MAX98357A (short lines)

und verwende Sternmasse:

ESP32_GND ----+---- AMP_GND
|
POWER_GND

Vermeide es, Lautsprecherkabel in der Nähe von Signalkabeln zu führen, da die hohen Schaltströme Störungen verursachen können.

F: Welche Audioformate und Funktionen werden unterstützt?

Der MAX98357A unterstützt typische digitale Audioformate, die mit dem ESP32 verwendet werden.

  • 16-Bit, 24-Bit oder 32-Bit Audio
  • Abtastraten von 8 kHz bis 96 kHz