<- Back to Projects

Picoaudio

A Bluetooth A2DP audio receiver for the Raspberry Pi Pico 2 W that outputs 16-bit stereo PCM over I2S.

C/C++Raspberry Pi Pico 2 WBTstackBluetoothHardware

picoaudio

Build Status License Platform Language Bluetooth

A Bluetooth A2DP audio receiver for the Raspberry Pi Pico 2 W. It receives Bluetooth audio and outputs 16-bit stereo PCM over I2S to a CJMCU-1334 (UDA1334A) DAC.

Built using the BTstack library and the Pico C/C++ SDK.

Architecture & Features

  • Dual-Core Architecture: Offloads the heavy SBC audio decoding entirely to Core 1 via a hardware-spinlock queue, ensuring that the Bluetooth stack on Core 0 never drops packets due to CPU starvation.
  • Ultra-Low Latency: Optimized buffer sizes (I2S DMA and thread-safe queue) to achieve ~55ms total audio latency, perfect for lip-synced video playback.
  • Multipoint Bluetooth (Dual Device): Supports 2 simultaneous A2DP and AVRCP connections. The firmware intelligently multiplexes incoming streams by maintaining independent connection states, decoding only the active stream, and dynamically switching to the standby device when the primary stream pauses. Volume is handled completely independently for each device.
  • A2DP Sink & SBC Decoder: Receives SBC encoded audio (up to Bitpool 53 / ~328 kbps) and decodes it to 16-bit PCM stereo.
  • Hardware I2S Output: Uses PIO and DMA to stream decoded audio to the I2S DAC at 44.1 kHz.
  • Drift Synchronization: Implements dynamic software resampling (btstack_resample) to synchronize the incoming Bluetooth clock with the RP2350 hardware I2S clock, preventing buffer under/overflows.
  • Hardware Mute & AVRCP Volume: Physical GPIO toggles hardware mute on the DAC. Processes AVRCP absolute volume commands and applies a logarithmic (quadratic) scaling function to the PCM data in software.
  • UI Sound Synthesizer: Contains a blocking square-wave synthesizer that injects status tones directly into the I2S hardware pool on boot, connection, and disconnection events.

Data Flow

  ┌──────────┐     Bluetooth      ┌─────────────────────────────────────┐
  │          │   A2DP / SBC       │        Raspberry Pi Pico 2 W        │
  │  Phone   │ ─── ~328 kbps ──> │                                     │
  │          │   (wireless)       │  CYW43439 Radio                     │
  └──────────┘                    │       │                             │
                                  │       v                             │
                                  │ [CORE 0] BTstack (A2DP Sink)        │
                                  │       │                             │
                                  │       v (Thread-Safe Queue)         │
                                  │ [CORE 1] SBC Decoder (bitpool 53)   │
                                  │       │                             │
                                  │       v                             │
                                  │ [CORE 1] Volume Scaling (AVRCP)     │
                                  │       │                             │
                                  │       v                             │
                                  │ [CORE 1] DMA -> PIO I2S State Mach. │
                                  │       │                             │
                                  └───────┼─────────────────────────────┘

                              GPIO 16 (BCLK)
                              GPIO 17 (WSEL)    I2S Bus (3 wires)
                              GPIO 18 (DIN)

                                          v
                                  ┌───────────────┐
                                  │  CJMCU-1334   │
                                  │  (UDA1334A)   │
                                  │               │
                                  │  I2S -> DAC   │
                                  │       │       │
                                  │   3.5mm Jack  │
                                  └───────┼───────┘

                                          v
                                    Speaker / Headphones

UI Sound Configuration

The status tones are generated mathematically via bt_audio.c. Configuration is managed via preprocessor macros:

  • ENABLE_UI_SOUNDS (default: 1): Set to 0 to disable the synthesizer.
  • UI_SOUND_VOLUME (default: 50): Sets the 16-bit PCM amplitude of the square wave (max 32767).

Hardware Requirements

ComponentRole
Raspberry Pi Pico 2 WMain controller (RP2350 + CYW43439)
CJMCU-1334 (UDA1334A)I2S DAC

Wiring Guide

Connection Diagram

                    ┌──────────────────┐
              ┌─────┤ USB              ├─────┐
              │     └──────────────────┘     │
   UART0 TX ──┤ GP0  (1)        (40) VBUS   ├── 5V USB Power ──────> CJMCU VIN
   UART0 RX ──┤ GP1  (2)        (39) VSYS   │
              ┤ GND  (3)        (38) GND    ├── Ground ─────────────> CJMCU GND
 Play/Pause ──┤ GP2  (4)        (37) 3V3_EN  │
       Mute ──┤ GP3  (5)        (36) 3V3 OUT │
  Volume Up ──┤ GP4  (6)        (35) ADC_REF │
Volume Down ──┤ GP5  (7)        (34) GP28/A2 │
              ┤ GND  (8)        (33) GND     │
  Next Song ──┤ GP6  (9)        (32) GP27/A1 │
  Prev Song ──┤ GP7  (10)       (31) GP26/A0 │
    Pairing ──┤ GP8  (11)       (30) RUN     │
              ┤ GP9  (12)       (29) GP22    │
              ┤ GND  (13)       (28) GND     │
              ┤ GP10 (14)       (27) GP21    │
              ┤ GP11 (15)       (26) GP20    │
              ┤ GP12 (16)       (25) GP19    │
              ┤ GP13 (17)       (24) GP18    ├── I2S Data (DIN) ────> CJMCU DIN
              ┤ GND  (18)       (23) GND     │
              ┤ GP14 (19)       (22) GP17    ├── I2S WSEL ──────────> CJMCU WSEL
   DAC Mute ──┤ GP15 (20)       (21) GP16    ├── I2S BCLK ──────────> CJMCU BCLK
              └──────────────────────────────┘
                     Raspberry Pi Pico 2 W

Pin Table

Pico 2 W PinPhysical PinCJMCU-1334Signal
VBUS40VIN5V USB Power
GND38GNDCommon Ground
GPIO 1621BCLKI2S Bit Clock
GPIO 1722WSELI2S Word Select (L/R)
GPIO 1824DINI2S Serial Data

[!NOTE] VBUS (5V) is used instead of 3V3. The Pico’s switching 3V3 regulator introduces noise; feeding 5V to the DAC’s onboard LDO provides cleaner power for the analog output.

DAC Configuration Pins (CJMCU-1334)

Most breakout boards pull these low by default:

  • SF0 / SF1: LOW (I2S format)
  • PLL: LOW (Audio PLL mode)
  • DEEM: LOW (De-emphasis OFF)
  • MUTE: LOW (Mute OFF)

Hardware Setup & Demo

Project in Action

Here is the receiver in action! The Raspberry Pi Pico 2 W and the CJMCU-1334 DAC board are connected directly to a 2.1 channel sound system through the 3.5mm audio jack.

Wiring Closeups

Breadboard Setup

Raspberry Pi Pico 2 W Closeup CJMCU-1334 DAC Closeup

Serial Logs

Download Latest Firmware

You can download the pre-compiled .uf2 firmware from the latest successful build here: Download Latest Firmware (pico-audio-firmware.zip)

(Extract the .zip to find the .uf2 file, then follow the flashing instructions below).

Build Instructions

  1. Install and configure the Raspberry Pi Pico C/C++ SDK for the RP2350.
  2. Clone the repository.
  3. Build the project:
mkdir -p build && cd build
cmake -DPICO_BOARD=pico2_w ..
ninja

Flashing

  1. Hold the BOOTSEL button on the Pico 2 W while plugging in the USB cable.
  2. A mass storage device named RP2350 will mount.
  3. Copy the compiled .uf2 binary to the device:
cp build/bluetooth_audio_receiver.uf2 /run/media/$USER/RP2350/
  1. The Pico will automatically reboot. The Bluetooth device name is Pico2W-Audio.

LED Status Indicator

LED StateMeaning
1 Hz BlinkDiscoverable / Waiting for pairing
4 Hz BlinkConnected, no active audio stream
Solid ONConnected, audio streaming
OFFInitialization error

Troubleshooting

SymptomCauseResolution
No LED blink on bootCYW43 init failureVerify USB power, reflash .uf2
Volume buttons unresponsiveAVRCP sync failureRe-pair the device; check UART logs for [avrcp]
Audio distortionFloating DAC config pinsBridge SF0 and SF1 to GND on the DAC board

References

License

Licensed under the MIT License. See LICENSE for details.

Background by Mymind