Skip to content
Buy 10+ on select items — save 10% auto-applied
Free US shipping on orders $35+
Order by 3pm ET — ships same-day from the US
Worldwide shipping via DHL
Skip to main content

ESP32-S3 MAX98357: Wi-Fi Speaker with TTS + Radio

November 11, 2026 9 views

ESP32-S3 MAX98357: Wi-Fi Speaker with TTS + Radio | ShillehTek
Project

Build an ESP32-S3 MAX98357 Wi-Fi speaker that speaks typed text and streams internet radio over I2S, with strong signal using an external antenna from ShillehTek.

1 hr Beginner to Intermediate8 parts

Project Overview

ESP32-S3 Wi-Fi Speaker: Pair an ESP32-S3 N16R8 with a MAX98357 I2S amplifier and a 4 Ω speaker to build a small speaker that reads out any text you type and streams internet radio, with an external antenna for a stronger Wi-Fi link.

  • Time: about 1 hour
  • Skill level: Beginner to Intermediate
  • What you will build: A breadboard Wi-Fi speaker controlled from the Serial Monitor: type a sentence to hear it spoken, or switch between two radio stations and set the volume.
ESP32 development board and MAX98357A I2S amplifier on a breadboard with speakers for a text-to-speech Wi-Fi speaker build
The reference build by Quartz Components: a 30-pin ESP32 and a MAX98357A amplifier on a breadboard, with a pair of speakers. This guide uses one 4 Ω speaker on the amplifier and an ESP32-S3 with an external antenna.

Parts List

From ShillehTek

External

  • USB-C data cable
  • A 2.4 GHz Wi-Fi network with internet access
  • Arduino IDE with the ESP32 board package (core 3.x)

Note: the text-to-speech feature uses an online speech service through the audio library, so it needs internet access and may change if that service changes. The radio part only needs a working stream URL.

Step-by-Step Guide

Step 1 - Wire the amplifier and speaker

Goal: Connect the MAX98357 to the ESP32-S3's I2S pins and attach the speaker.

What to do: The MAX98357 takes three digital audio lines and runs from 5 V. On most MAX98357 boards, leaving SD and GAIN unconnected gives a mono mix of both channels at 9 dB gain, which suits a single speaker.

  • MAX98357 Vin → ESP32-S3 5V (or VIN), GND → GND
  • MAX98357 LRC → GPIO5
  • MAX98357 BCLK → GPIO6
  • MAX98357 DIN → GPIO7
  • Speaker → the amplifier's + and − screw terminals
Wiring diagram showing MAX98357A I2S amplifier connections to an ESP32 board with speaker connected to the amplifier screw terminals
The reference wiring on a classic 30-pin ESP32: DIN to D25, BCLK to D27 and LRC to D26, with Vin and GND from the board. On a classic ESP32 you can keep those pins; on the ESP32-S3 use GPIO7, GPIO6 and GPIO5 as listed above.

Expected result: Five wires between the boards plus the speaker on the screw terminals. Use one 4 Ω speaker per amplifier; two 4 Ω speakers in parallel would present 2 Ω, which is below what the MAX98357 is designed for.

Step 2 - Fit the external antenna

Goal: Give the ESP32-S3 a solid Wi-Fi link for streaming.

What to do: Press the IPEX (U.FL) end of the adapter cable straight down onto the board's antenna socket until it clicks; do not slide it on at an angle. Screw the rod antenna onto the RP-SMA end. RP-SMA and SMA look alike but have opposite center contacts, so the rod antenna (RP-SMA male) needs the RP-SMA female version of the adapter. Fit the antenna before running Wi-Fi code; a module wired for an external antenna will have poor range without one.

Expected result: The rod antenna stands upright next to the breadboard, connected through the 10 cm lead.

Step 3 - Install the board package and audio library

Goal: Prepare the Arduino IDE for the ESP32-S3 and I2S audio.

What to do: Install esp32 by Espressif Systems (version 3.x) in the Boards Manager. Download the ESP32-audioI2S library by schreibfaul1 from GitHub as a ZIP and add it with Sketch → Include Library → Add .ZIP Library. Then set the board options:

  • Board: ESP32S3 Dev Module
  • Flash Size: 16MB
  • PSRAM: OPI PSRAM (the N16R8 has 8 MB, and recent versions of the audio library expect PSRAM)
  • Partition Scheme: a 16MB scheme with a 3MB app, or Huge APP
  • USB: if your board has two USB-C ports, use the one marked COM or UART; if you use the native USB port instead, set USB CDC On Boot to Enabled so the Serial Monitor works

Expected result: A blank sketch compiles and uploads to the ESP32-S3.

Step 4 - Upload the speaker sketch

Goal: Connect to Wi-Fi, set up I2S and listen for commands.

What to do: Enter your Wi-Fi name and password at the top, then upload. Plain text you type is sent to connecttospeech(); lines that start with a slash are commands. audio.loop() must run constantly, so the sketch reads the Serial Monitor without any blocking delays.

// ShillehTek ESP32-S3 Wi-Fi speaker: type text to hear it, or stream internet radio (MAX98357)
#include <WiFi.h>
#include "Audio.h"                       // ESP32-audioI2S by schreibfaul1

const char *WIFI_SSID = "YOUR_WIFI_NAME";
const char *WIFI_PASS = "YOUR_WIFI_PASSWORD";

const int I2S_LRC  = 5;                  // MAX98357 LRC  (word select)
const int I2S_BCLK = 6;                  // MAX98357 BCLK (bit clock)
const int I2S_DOUT = 7;                  // MAX98357 DIN  (audio data)

const char *STATIONS[] = {
  "http://ice1.somafm.com/groovesalad-128-mp3",   // SomaFM Groove Salad
  "http://stream.srg-ssr.ch/m/rsj/mp3_128",       // Radio Swiss Jazz
};
const int STATION_COUNT = sizeof(STATIONS) / sizeof(STATIONS[0]);
const char *TTS_LANG = "en";
const size_t MAX_LINE = 200;             // keep each sentence short for the speech service

Audio audio;
char line[MAX_LINE + 1];
size_t lineLen = 0;
int volume = 12;                         // 0..21

void printHelp() {
  Serial.println("Type any text and press Enter to hear it.");
  Serial.println("Commands: /radio 1-2, /vol 0-21, /stop, /rssi, /help");
}

// Reads an integer after a command; returns false if no digits follow
bool readNumber(const char *p, int &out) {
  while (*p == ' ') p++;
  if (*p < '0' || *p > '9') return false;
  out = atoi(p);
  return true;
}

void handleLine(char *s) {
  while (*s == ' ') s++;
  if (*s == '\0') return;
  if (s[0] != '/') {                     // plain text: speak it
    Serial.printf("Speaking: %s\n", s);
    audio.connecttospeech(s, TTS_LANG);
    return;
  }
  int n = 0;
  if (strncmp(s, "/radio", 6) == 0) {
    if (readNumber(s + 6, n) && n >= 1 && n <= STATION_COUNT) {
      Serial.printf("Station %d: %s\n", n, STATIONS[n - 1]);
      audio.connecttohost(STATIONS[n - 1]);
    } else {
      Serial.printf("Pick a station from 1 to %d\n", STATION_COUNT);
    }
  } else if (strncmp(s, "/vol", 4) == 0) {
    if (readNumber(s + 4, n)) {
      volume = constrain(n, 0, 21);
      audio.setVolume(volume);
    }
    Serial.printf("Volume %d of 21\n", volume);
  } else if (strcmp(s, "/stop") == 0) {
    audio.stopSong();
    Serial.println("Stopped");
  } else if (strcmp(s, "/rssi") == 0) {
    Serial.printf("Wi-Fi signal: %d dBm\n", WiFi.RSSI());
  } else {
    printHelp();
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.print("Connecting to Wi-Fi");
  uint32_t start = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - start < 20000) {
    delay(500);
    Serial.print('.');
  }
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("\nWi-Fi failed - check the name, password and antenna");
    while (true) delay(1000);
  }
  Serial.printf("\nConnected, IP %s, signal %d dBm\n", WiFi.localIP().toString().c_str(), WiFi.RSSI());

  audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
  audio.setVolume(volume);
  printHelp();
  audio.connecttospeech("Hello from ShillehTek. Your speaker is ready.", TTS_LANG);
}

void loop() {
  audio.loop();                          // keep this fast: it feeds the I2S buffer
  while (Serial.available()) {
    char c = (char)Serial.read();
    if (c == '\r') continue;
    if (c == '\n') {
      line[lineLen] = '\0';
      handleLine(line);
      lineLen = 0;
    } else if (lineLen < MAX_LINE) {
      line[lineLen++] = c;
    }
  }
}

Expected result: The Serial Monitor (115200 baud) shows the IP address and Wi-Fi signal, and the speaker says "Hello from ShillehTek. Your speaker is ready."

Step 5 - Talk to it and tune in

Goal: Use the text-to-speech and radio features.

What to do: Set the Serial Monitor line ending to Newline. Then try:

  • The coffee is ready speaks the sentence (keep each line under 200 characters)
  • /radio 1 plays SomaFM Groove Salad, /radio 2 plays Radio Swiss Jazz
  • /vol 15 sets the volume from 0 to 21
  • /stop stops playback, /rssi prints the Wi-Fi signal strength

Speaking a sentence replaces whatever is playing, so send /radio 1 again to resume the station. To add your own stations, put any direct MP3 or AAC stream URL in the STATIONS list.

If you want to verify streaming headroom, compare /rssi readings with the antenna upright in the same room versus another room. Readings around -50 to -65 dBm stream comfortably; below about -75 dBm you may hear dropouts. If the sound crackles or distorts, lower the volume to 10 to 14, make sure Vin is on 5 V rather than 3V3, and keep the speaker wires away from the antenna lead.

Expected result: Typed sentences are read out within a second or two, and the radio plays continuously.

Conclusion

You built a Wi-Fi speaker with an ESP32-S3 N16R8, a MAX98357 I2S amplifier and a 4 Ω speaker: it reads out typed text, streams internet radio, takes volume and station commands over serial, and uses an external antenna for a stronger connection.

Want the exact parts used in this build? Grab them from ShillehTek.com. If you want help customizing this project or building something for your product, check out our IoT consulting services.

Credits

All photos and images in this tutorial are credited to Quartz Components on Hackster.io. The original guide by Quartz Components served as the reference for this ShillehTek version. We thank them for their excellent work in the maker community.

Parts for this build

Everything used in this tutorial. Uncheck what you already have.

All 8 in stock
0 parts selected $0.00