Guide contents
Build with ESPHome
Use ESPHome to build a Sendspin player for an ESP32 without writing C++. ESPHome ships a sendspin component that wraps sendspin-cpp and plugs it into the rest of the ESPHome audio stack. You describe the hardware in YAML, ESPHome builds the firmware, and Home Assistant takes care of onboarding, updates and pairing.
What ships
The component is a hub plus a set of platforms:
sendspin:is the hub. It takes optionalmanufacturer,modelandfirmware_versionfor thedevice_infoinclient/hello, andtask_stack_in_psramfor boards where internal RAM is tight. It turns on ESPHome's high-performance networking automatically.- A
media_sourceplatformsendspinthat is the player role. It feeds aspeaker_sourcemedia player, which drives any ESPHome speaker pipeline: I2S DAC, internal DAC, a speaker with its own resampler and mixer. Itsinitial_static_delayandstatic_delay_adjustableoptions are the output delay and whether the user may change it. - A
media_playerplatformsendspinthat is the controller role: it exposes the group the device is in as a Home Assistant media player, with play, pause, next and volume. - A
text_sensorplatform with atypeoftitle,artist,albumoralbum_artist, and asensorplatform withtrack_progress,track_duration,yearortrack. - An
imageplatform for artwork, in theformatandresizedimensions your display wants, from the album or the artist channel. switchentities that start and stop the Sendspin client, and allow guest access, which is the Home Assistant name for unpaired access.
Underneath is sendspin-cpp unchanged. The library is designed to be consumed by ESPHome but has no ESPHome dependencies, so what you learn about it here carries over if you ever leave ESPHome. The minimum ESPHome version is 2026.3. An ESP32-S3 with PSRAM is the comfortable target. A plain ESP32 works for the player role with care over RAM, which is what task_stack_in_psram and the choice of a single sample rate in the speaker pipeline are for.
The ESPHome documentation has the full option list for every platform.
Onboarding through Home Assistant
The experience for the user is the normal ESPHome one. They power the device, add it to Home Assistant through Improv or by adopting it in the ESPHome dashboard, and from then on they get OTA updates and a device page with its entities.
Home Assistant hands the device's pairing PSK to Music Assistant, so the device shows up in Music Assistant already paired, with no code to type and no QR to scan. The manual pairing methods stay available for other servers in the home: a phone app or a second music server still pairs the way the pairing chapter describes.
The guest-mode switch controls unpaired access from Home Assistant, so the user decides from the dashboard whether a server that is not paired may play on the device. For products that need an EN 18031 compliant default, an action lets your firmware enable Sendspin only once a per-device key has been set, so a device never listens with a shared or empty secret.
Music Assistant does it like this
Music Assistant receives the device's pairing PSK from Home Assistant and completes the pairing PSK flow the first time it connects. Both sides store an ordinary pairing record, as they would after code-based pairing. Other servers pair with the device through the methods it advertises.
An example configuration
Adapt the pins, board, sample rate and names to your hardware. The entity names are what the user sees in Home Assistant.
# Example: an ESP32-S3 driving an external I2S DAC. Adapt to your board.
esphome:
name: shelf-speaker
friendly_name: Shelf Speaker
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
psram:
mode: octal
speed: 80MHz
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
api:
ota:
- platform: esphome
improv_serial:
i2s_audio:
- id: i2s_out
i2s_lrclk_pin: GPIO45
i2s_bclk_pin: GPIO46
speaker:
- platform: i2s_audio
id: dac_speaker
i2s_audio_id: i2s_out
i2s_dout_pin: GPIO47
dac_type: external
sample_rate: 48000
bits_per_sample: 32bit
channel: stereo
sendspin:
manufacturer: Example Audio
model: Shelf Speaker
firmware_version: "1.0.0"
# The player role: Sendspin audio into the speaker pipeline.
media_source:
- platform: sendspin
id: sendspin_source
initial_static_delay: 0ms # delay of the DAC and amplifier behind the port
static_delay_adjustable: true # let the user fine-tune it from Home Assistant
media_player:
- platform: speaker_source
id: speaker_player
name: Speaker
media_pipeline:
speaker: dac_speaker
sources:
- sendspin_source
# The controller role: the group this device is in, as a media player entity.
- platform: sendspin
name: Sendspin group
text_sensor:
- platform: sendspin
type: title
name: Title
- platform: sendspin
type: artist
name: Artist
switch:
# Starts and stops the Sendspin client. The guest access switch is
# configured the same way; see the ESPHome documentation.
- platform: sendspin
name: Sendspin
Flash it, adopt the device in Home Assistant, and it appears in Music Assistant as a player. Measure the delay of the DAC and amplifier behind the port and put it in initial_static_delay, then compare the device against sendspin-cpp-cli on a Pi to check synchronization.
What ESPHome handles
- OTA and configuration. ESPHome's update mechanism and YAML configuration replace a build system, an updater and a settings store you would otherwise write.
- Home Assistant integration. Entities, device pages, automations and the pairing hand-off to Music Assistant all come with the ESPHome native API.
- A path to certification. A device built this way can qualify for Made for ESPHome and go through Sendspin testing and certification with the SDK the conformance suite is developed against.
- Protocol implementation. sendspin-cpp handles Noise, the time filter, decoding and sync corrections, and the project maintains it.
When to use sendspin-cpp directly
Go to sendspin-cpp without ESPHome when:
- your silicon is not an ESP32, or you run a custom RTOS or Linux build;
- you already have an update system and a configuration store and do not want a second one;
- you need control over threading, memory placement or the audio path that the component does not expose.
You keep the same library, the same hooks and the same behavior on the wire. The client step by step chapter is written for that path.
Hardware that already runs it
The Home Assistant Voice Preview Edition ships Sendspin through this component, so there is a production device to compare against, and its published configuration is a useful reference for pin maps, PSRAM configuration and speaker pipelines before you lay out your own board.