Skip to the content
PCBKitNew board
2 layers61.3 × 62.5 mmEnergyOptPCB, the printed circuit board in 3D

A Bluetooth audio receiver built on an ESP32-WROOM-32D in ESP-IDF C++. The system receives stereo audio from a phone over Bluetooth Classic, outputs the decoded audio through PCM5102A DAC, and displays the current song…

Size
61.3 × 62.5 mm
Copper layers
2
Thickness
1.6 mm
Parts
15
Nets
52
Pads
69
Tracks, vias
54, 0
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
30 Jul 2026
Updated
22 Aug 2026

Overview

EnergyOptPCB is an open-source printed circuit board by devanmehta, from the devanmehta/Bluetooth-Receiver repository on GitHub. It is a 2-layer board of 61.3 × 62.5 mm with 15 parts on 52 nets, routed with 54 tracks and 0 vias.

It is built around the L293D. Its parts include 2 connectors, 1 IC and 1 switch.

Main components

1

README

README.md

Bluetooth Audio Receiver — ESP32

A Bluetooth audio receiver built on an ESP32-WROOM-32D in ESP-IDF C++. The system receives stereo audio from a phone over Bluetooth Classic, outputs the decoded audio through PCM5102A DAC, and displays the current song, artist, album, and playback status to an LCD Display in real time, all housed in a custom PCB.

Features

  • Operates as a Bluetooth Classic A2DP sink
  • Receives stereo audio from compatible phones and computers
  • Outputs decoded PCM audio at 44.1 kHz, 16-bit stereo through a PCM5102A DAC over I2S
  • Retrieves song title, artist, album, and playback status through AVRCP and Requests new metadata whenever the track changes. It displays media information on a 128 × 160 ST7735R TFT over SPI
  • Uses a separate low-priority FreeRTOS task for the connection-status LED
  • Includes a custom PCB design, schematic, and board renders

Hardware

Components and Estimated Cost

ItemEstimated cost (USD)
ESP32-WROOM-32D development board$6–$10
1.8-inch ST7735R TFT module$5–$20
PCM5102A I2S DAC module$5–$10
Headers, connectors, and miscellaneous hardware$2–$5
USB cable and power source, if needed$3–$8
(Optional) two-layer PCB, allocated per board from a small order$4–$10
Estimated total$25–$63

IMPORTANT: This project requires an ESP32 variant with Bluetooth Classic support. A2DP and AVRCP cannot run on ESP32 variants that provide only Bluetooth Low Energy (BLE).

Pinout

PCM5102A I2S Audio Interface
PCM5102A signalESP32-WROOM-32D GPIOPurpose
BCKGPIO 26I2S bit clock generated by the ESP32
LCK / LRCK / WSGPIO 25Left/right word-select clock
DINGPIO 19PCM audio data from the ESP32 to the DAC
MCLK / SCKNot connectedThe current configuration does not require an ESP32 MCLK output
GNDGNDShared electrical reference
ST7735R SPI Display Interface
ST7735R signalESP32-WROOM-32D GPIOPurpose
SCL / SCLKGPIO 18SPI clock
SDA / MOSI / DINGPIO 23SPI data from the ESP32 to the display
DC / A0GPIO 21Selects display data or commands
CSGPIO 22Display chip select
RST / RESETGPIO 16Display hardware reset
MISONot connectedThe application only writes to the display
GNDGNDShared electrical reference
Status LED
SignalESP32-WROOM-32D GPIOPurpose
Onboard LEDGPIO 2Indicates Bluetooth connection status

PCB

KiCad source lives in the Hardware/

PCB Top

PCB Top

PCB Bottom

PCB Bottom

Schematic

Build and Flash

Requirements:

  • PlatformIO (VS Code extension or CLI)

The repository includes platformio.ini, ESP-IDF CMake files, and board-specific SDK configuration files. PlatformIO uses them to select the framework, board environment, dependencies, and required ESP-IDF features.

bash
git clone <this-repo-url>
cd <project-folder>
pio run --target upload
pio device monitor

Once flashed, the board advertises itself over Bluetooth as "BT Audio" — pair your phone and start playing music.

File Structure

text
├── Hardware/              # KiCad PCB and Design files
├── lib/                   # Project-local libraries, if used
├── src/
│   ├── CMakeLists.txt     # ESP-IDF component registration
│   └── main.cpp           # Main Firmware
├── test/                 
├── .gitignore
├── CMakeLists.txt         
├── platformio.ini         # PlatformIO environment and dependencies
├── sdkconfig.esp32dev     # ESP-IDF board configuration
└── README.md

Code Architecture

Execution Contexts

Several pieces of the program can run independently:

Execution contextCreated byWork performed
Main application taskESP-IDFInitialization and deferred TFT updates
Bluedroid Bluetooth taskESP-IDF Bluetooth stackInvokes the A2DP and AVRCP callbacks
Bluetooth status LED taskApplicationControls the onboard LED from connection state

The firmware explicitly creates the LED task with xTaskCreate(). Bluetooth tasks are created internally when the controller and Bluedroid stack are enabled.

1. Display Configuration (LovyanGFX Library)

Our LGFX class inherits from lgfx::LGFX_Device and owns the panel and SPI bus objects:

cpp
lgfx::Panel_ST7735S _panel_instance;
lgfx::Bus_SPI       _bus_instance;

The bus configuration selects SPI2_HOST, SPI mode 0, and a 20 MHz write clock. MISO is disabled because the application does not read data from the TFT.

The panel configuration defines the chip-select and reset pins, 128 × 160 drawing area, panel offsets, display inversion, and RGB order. The SPI bus is attached to the panel, and the panel is attached to the parent LovyanGFX device. The resulting global lcd object performs all drawing operations.

2. I2S Audio Configuration

setup_i2s() creates a transmit-only channel:

cpp
i2s_chan_config_t chan_cfg =
    I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);

The ESP32 acts as I2S master and generates the clocks for the PCM5102A. The standard-mode configuration selects:

  • 44.1 kHz sample rate (CD quality)
  • 16-bit samples
  • Stereo slot mode
  • Philips I2S timing
  • No receive channel
  • No master-clock output

i2s_channel_init_std_mode() applies the format and pins. i2s_channel_enable() starts the peripheral.

3. Bluetooth Initialization

app_main() initializes NVS before Bluetooth because ESP-IDF's Bluetooth stack depends on non-volatile storage. If NVS has an incompatible version or no free pages, the firmware erases that partition and retries initialization.

The application then:

  1. Creates the default Bluetooth controller configuration.
  2. Initializes the Bluetooth controller.
  3. Enables Bluetooth Classic mode.
  4. Initializes and enables the Bluedroid host stack.
  5. Sets the device name to BT Audio.
  6. Initializes the AVRCP controller.
  7. Registers the AVRCP callback.
  8. Registers the A2DP PCM callback.
  9. Initializes the A2DP sink.
  10. Makes the ESP32 connectable and discoverable.

4. A2DP Audio Path

A2DP carries the stereo Bluetooth stream from the source to the ESP32. After Bluedroid decodes a chunk of audio into PCM samples data buffer, it invokes bt_audio_data_callback which writes the audio samples to the DAC with i2s_channel_write

5. AVRCP Metadata and Playback State

AVRCP provides information about the media session. The callback receives an event identifier and a union containing the corresponding event data.

AVRCP eventFirmware response
Connection-state eventRaises the is_connected flag and requests metadata and playback status using the attr_mask and subscribes to get notified when play status or track changes
Metadata-response eventCopies title, artist, or album into our UI arrays`
Play-status responseStores playing, paused, or stopped state and puts into our UI arrays
Change-notification eventRequests new track data or updates playback state

6. Deferred Display Updates

Title, artist, and album commonly arrive as separate AVRCP events. Drawing immediately for every event would cause multiple rapid full-screen redraws. Instead, the callback updates the appropriate buffer and raises need_screen_update

The main loop checks the flag every 50 ms. When an update is pending, it clears the flag and calls update_screen(). This groups closely timed metadata events and reduces visible flicker.

update_screen() opens one LovyanGFX write transaction, clears the screen, and renders the playback indicator, song title, artist, album, labels, and divider.

7. Bluetooth Status LED Task

Made BluetoothStatusLED() as a separate FreeRTOS task that has a 2,048-byte stack and priority 1 (low priority) which uses the is_connected flag (gets updated in bt_avrc_ct_callback) to update LED:

Bluetooth stateOnboard LED behavior
ConnectedContinuously on
DisconnectedBlinks (700 ms off, then 700 ms on)

vTaskDelay() blocks the task between transitions instead of consuming CPU time in a busy-wait loop.

8. Main Application Loop

After initialization, app_main() remains alive as the UI service loop that polls need_screen_update and updates the screen

Parts

15
QtyValueFootprintReferences
2Conn_01x19_SocketConnector_PinSocket_2.54mm:PinSocket_1x19_P2.54mm_VerticalJ1, J2
15VTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP10
1Device_Hot_OutTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP3
1Device_Neutral_OutTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP6
1GNDTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP9
1L293DPackage_DIP:DIP-16_W7.62mm_SocketU1
1LOGOUTimage:convertedUTimageG***
1PZEM_Hot_OutTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP2
1PZEM_NeutralTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP5
1PZEM_RXTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP7
1PZEM_TXTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP8
1Wall_Hot_InTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP4
1Wall_Neutral_InTestPoint:TestPoint_Loop_D1.80mm_Drill1.0mm_BeadedTP1
1~Relay_THT:Relay_SPDT_SANYOU_SRD_Series_Form_CK1

Questions

How big is the EnergyOptPCB PCB?

It measures 61.3 × 62.5 mm and has 2 copper layers, 1.6 mm thick.

What parts does the EnergyOptPCB use?

15 parts, built around the L293D. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the EnergyOptPCB KiCad files?

From the devanmehta/Bluetooth-Receiver repository on GitHub, at the commit this page was made from: the board is Hardware/EnergyOptPCB.kicad_pcb and its schematic Hardware/EnergyOptPCB.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the EnergyOptPCB design in my own project?

Its repository states no licence, so its author keeps all rights: ask devanmehta before reusing the design.

Similar boards

  • 2 layers178 × 145 mmUltra DB X51 v1, the printed circuit board in 3D

    Ultra DB X51 v1

    ExploreEmbedded/8051_DevelopmentBoard

    Design files and sample code for 8051 development Ultra Boards

    145 parts70
  • 2 layers84.3 × 58.4 mmEnocderDriver, the printed circuit board in 3D

    EnocderDriver

    NikodemBartnik/Small-Projects

    All of smaller projects (Arduino, PCBs, electronics, code)

    35 parts55
  • 2 layers56.4 × 72.6 mmRas pi hat, the printed circuit board in 3D

    Ras pi hat

    geno543/Redropi_highway

    the project aims to detect asteroids and meteor movement using high-quality cameras and radar detection methods using an AI model for accurate analysis, and it classifies bodies be in three categories: non-meteor…

    8 parts13
  • 2 layers87.5 × 70 mmLaberinto, the printed circuit board in 3D

    Laberinto

    CRM-UAM/CRMaze

    Open source robot for maze solving + line following

    34 parts12
  • 2 layers92.5 × 62.5 mmDropGenPCB, the printed circuit board in 3D

    DropGenPCB

    harrislab-brown/DropGen

    DropGen uses a fluid reservoir and a piezoelectric buzzer to precisely and repeatably dispense small droplets on demand.

    25 parts12CC-BY-SA-4.0
  • 2 layers47.5 × 49.5 mmElectronic, the printed circuit board in 3D

    Electronic

    zosko/magspoof

    Magspoof with Arduino Pro Micro

    5 parts11
  • 2 layers120.7 × 184.1 mmBeachRover circuit, the printed circuit board in 3D

    BeachRover circuit

    wizardofrobots/roverCode

    Jetson Nano powered Arduino Rover

    11 parts10
  • 2 layers90 × 53.4 mmOpenOx, the printed circuit board in 3D

    OpenOx

    hacklabkyiv/openox-pcb

    PCB design for OpenOx - open source oxygen concentrator

    43 parts7GPL-3.0