This turns a cheap ESP32-S3 board into a wireless AirPlay 2 speaker. Plug it into any amplifier or powered speakers, and it shows up on your iPhone/iPad/Mac just like a HomePod or AirPlay TV. Also supports the SqueezeAMP board (ESP32 + TAS5756 DAC with built-in amplifier).
No cloud. No app. Just tap and play.
You only need 2 boards and a few wires. Everything is available on AliExpress / Amazon for under 10$.
| Component | What to search for | Price |
|---|---|---|
| ESP32-S3 dev board | "ESP32-S3 N16R8" (must have 8MB PSRAM) | ~5$ |
| PCM5102A DAC board | "PCM5102A I2S DAC" (the small purple board with 3.5mm jack) | ~3$ |
| Female 2.54mm header | "Female pin header 2.54mm single row" (1x6 or longer, then cut to size) | ~0.5$ |
Important: The ESP32-S3 must have 8MB PSRAM (the N16R8 variant). Boards without enough PSRAM will not work — the audio decoding needs the extra memory.
Alternative: If you have a SqueezeAMP board (ESP32 with TAS5756 DAC and built-in amplifier), you don't need a separate DAC — just flash the SqueezeAMP firmware target. See the SqueezeAMP section below.
Here is what the PCM5102A board looks like: Verify the solder bridges are in the same position as the picture.
The PCM5102A plugs directly onto the ESP32 pins using a female header — no breadboard, no jumper wires.
The pins on one side of the ESP32 need to be removed (or not soldered on) so the assembly fits inside the 3D-printed case. Only the side with GPIO11–GPIO14 needs pins.
If your board came with pins on both sides already soldered, you can carefully desolder or clip the pins on the opposite side.
Take a female 2.54mm pin header (6 pins) and plug it onto the ESP32 pins on the side with GPIO11–14. Then insert the PCM5102A board into the female header from the other side.
The connections through the header are:
ESP32-S3 pin → PCM5102A pin What it does
─────────────────────────────────────────────────
5V → VIN Power for the DAC
GPIO11 → BCK Bit clock (audio timing)
GPIO12 → DIN Audio data
GPIO13 → LCK Left/right channel select
GPIO14 → GND Software ground (pulled low by code)
Or GND → GND Ground (GPIO14 software ground is sufficient)
Tip: On the ESP32S3 board, bridge the VIN/VOUT solder pads if they are not already connected. This lets the board use 5V power directly.
Your assembly should look like this:
The PCM5102A sits on top of the ESP32 and the 3.5mm audio jack sticks out the end. Plug a USB-C cable into the ESP32 for power.
A 3D-printable case is provided in docs/boite esp32.stl. Print it with standard PLA settings. The case is designed for the assembly with pins on one side only.
You have two options: PlatformIO (easier) or ESP-IDF (official Espressif toolchain).
PlatformIO handles all the toolchain setup for you.
# 1. Install PlatformIO CLI
pip install platformio
# 2. Clone this project
git clone https://github.com/rbouteiller/airplay-esp32
cd airplay-esp32
# 3. Plug in your ESP32 via USB-C and flash
pio run -e esp32s3 -t upload
# 4. (Optional) Watch serial output for debugging
pio run -e esp32s3 -t monitor# 1. Install ESP-IDF v5.x following:
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/
# 2. Clone and enter the project
git clone https://github.com/rbouteiller/airplay-esp32
cd airplay-esp32
git submodule update --init --recursive
# 3. Activate ESP-IDF environment
source /path/to/esp-idf/export.sh
# 4. Build and flash
idf.py set-target esp32s3
idf.py build
idf.py -p /dev/ttyUSB0 flash
# 5. (Optional) Monitor serial output
idf.py -p /dev/ttyUSB0 monitor- Power up the ESP32 via USB-C
- On your phone or laptop, connect to the WiFi network
ESP32-AirPlay-Setup - A captive portal will open. (IP: 192.168.4.1)
- Set a name for your speaker (e.g. "Kitchen Speaker")
- Select your home WiFi network and set a password
- The device restarts and connects to your WiFi
- Open any music app, tap the AirPlay icon, and select your speaker
That's it! Settings are saved and persist across reboots.
If WiFi connection fails after several retries, the ESP32 automatically goes back into setup mode so you can reconfigure it.
Once the device is connected to your WiFi, you can update the firmware wirelessly without unplugging anything:
- Build the new firmware (
idf.py buildorpio run) - Open the device's web interface (find its IP in your router's connected devices list)
- Use the firmware upload page to flash the new version
The SqueezeAMP is an ESP32-based board with a TAS5756 DAC and built-in Class-D amplifier. No external DAC needed — just connect speakers directly.
# PlatformIO
pio run -e squeezeamp -t upload
# ESP-IDF
idf.py set-target esp32
idf.py build
idf.py -p /dev/ttyUSB0 flashThe SqueezeAMP build uses CONFIG_SQUEEZEAMP to enable the TAS57xx I2C DAC driver and configures the correct I2S pins via Kconfig. Buffer sizes are automatically reduced (2500 frames vs 5000) to fit the ESP32's more limited PSRAM access.
A 4MB flash variant is also supported (squeezeamp-4m PlatformIO environment).
- AirPlay 2 protocol — shows up natively in Control Center and all AirPlay apps
- ALAC & AAC decoding — handles both live streaming (Siri, calls) and music playback
- Multi-room support — PTP-based timing for synchronized playback across devices
- Web configuration — set up WiFi and device name from your browser
- OTA updates — update firmware over WiFi, no USB needed after first flash
- LED indicator — visual feedback for playback status
- SqueezeAMP support — ESP32 + TAS5756 DAC with built-in amplifier
- Audio only (no AirPlay video or photos)
- One speaker per ESP32 board
- Needs decent WiFi signal for stable streaming
┌─────────────────┐ WiFi ┌─────────────┐
│ iPhone / Mac │ ─────────────► │ ESP32-S3 │
│ (AirPlay) │ │ │
└─────────────────┘ └──────┬──────┘
│ I2S
┌──────▼──────┐
│ PCM5102A │
│ DAC │
└──────┬──────┘
│ Analog
┌──────▼──────┐
│ Amplifier │
│ + Speakers │
└─────────────┘
| Signal | Function |
|---|---|
| BCK | Bit clock — 44100 × 16 × 2 = 1.41 MHz |
| LCK | Word select — toggles at 44.1 kHz |
| DIN | Serial audio data (16-bit stereo) |
MCLK is not used; the PCM5102A generates it internally.
┌────────────────────────────────────────────────┐
│ AirPlay 2 Source │
│ (iPhone, iPad, Mac, Apple TV) │
└───────────────────────┬────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ mDNS │ │ RTSP │ │ PTP │
│ Discovery│ │ Control │ │ Timing │
└──────────┘ └──────────┘ └──────────┘
│ │ │
└─────────────┼─────────────┘
▼
┌──────────────────┐
│ HAP Pairing │
│ (Transient) │
└──────────────────┘
│
┌───────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ ALAC │ │ AAC │
└──────────┘ └──────────┘
│ │
└─────┬─────┘
▼
┌──────────────────┐
│ Audio Buffer │
│ + Timing Sync │
└──────────────────┘
│
▼
┌──────────────────┐
│ I2S Output │
│ (44.1kHz/16b) │
└──────────────────┘
| Format | Use Case |
|---|---|
| ALAC (realtime) | Live streaming, Siri |
| AAC (buffered) | Music playback |
| Module | Location | Purpose |
|---|---|---|
| RTSP Server | main/rtsp/ |
Handles AirPlay control messages |
| HAP Pairing | main/hap/ |
Cryptographic device pairing |
| Audio Pipeline | main/audio/ |
Decoding, buffering, timing |
| PTP Clock | main/network/ |
Synchronization with source |
| Web Server | main/network/ |
Configuration interface |
main/
├── audio/ # Decoders, buffers, timing sync
├── rtsp/ # RTSP server and handlers
├── hap/ # HomeKit pairing (SRP, Ed25519)
├── plist/ # Binary plist parsing
├── network/ # WiFi, mDNS, PTP, web server
├── main.c # Entry point
└── settings.c # NVS persistence
- Shairport Sync — The reference AirPlay implementation
- openairplay/airplay2-receiver — Python AirPlay 2 implementation
- Espressif — ESP-IDF framework and codec libraries
Non-commercial use only. Commercial use requires explicit permission. See LICENSE.
This is an independent project based on protocol analysis. Not affiliated with Apple Inc. Not guaranteed to work with future iOS/macOS versions. Provided as-is without warranty.



