Skip to content

Getting Started

walinsky edited this page Nov 12, 2025 · 3 revisions

Getting Started

Quick start guide for the a2dpSinkHfpClient component.

Overview

The a2dpSinkHfpClient component turns your ESP32 into a Bluetooth speaker and hands-free kit with support for:

  • 🎵 Music streaming (A2DP)
  • 📞 Phone calls (HFP)
  • 🎛️ Music control (AVRC)
  • 📇 Contact sync (PBAP)

Hardware Requirements

  • ESP32 module
  • I2S DAC (e.g., PCM5102, MAX98357A)
  • I2S Microphone (e.g., INMP441) - optional, needed for calls
  • Speakers or headphones

Installation

Option 1: ESP Component Registry (Recommended)

idf.py add-dependency "a2dpSinkHfpClient"

Option 2: Manual Installation

cd your_project/components
git clone https://github.com/yourusername/a2dpSinkHfpClient.git

Basic Setup

1. Configure ESP-IDF

Run idf.py menuconfig and enable:

Component config → Bluetooth → Bluedroid Options
    [*] A2DP
    [*] Hands Free Profile
        [*] Audio (SCO) data path in controller → HCI
        [*] Use external codec for mSBC
    [*] Phone Book Access Profile
    [*] AVRCP

2. Minimal Example

Create main/main.c:

#include "a2dpSinkHfpHf.h"
#include "nvs_flash.h"

void app_main(void) {
    // Initialize NVS
    nvs_flash_init();

    // Configure pins and device name
    a2dpSinkHfpHf_config_t config = {
        .device_name = "ESP32-Speaker",
        .i2s_tx_bck = 26,    // Speaker I2S pins
        .i2s_tx_ws = 25,
        .i2s_tx_dout = 22,
        .i2s_rx_bck = 32,    // Microphone I2S pins
        .i2s_rx_ws = 33,
        .i2s_rx_din = 34
    };

    // Initialize component
    a2dpSinkHfpHf_init(&config);
}

3. Build and Flash

idf.py build flash monitor

Your ESP32 is now discoverable as "ESP32-Speaker" and ready to pair!

Adding HFP Call Control

#include "a2dpSinkHfpHf.h"
#include "nvs_flash.h"
#include "esp_log.h"

#define TAG "MAIN"

void app_main(void) {
    nvs_flash_init();

    a2dpSinkHfpHf_config_t config = {
        .device_name = "ESP32-HFP",
        .i2s_tx_bck = 26,
        .i2s_tx_ws = 25,
        .i2s_tx_dout = 22,
        .i2s_rx_bck = 32,
        .i2s_rx_ws = 33,
        .i2s_rx_din = 34
    };

    a2dpSinkHfpHf_init(&config);

    ESP_LOGI(TAG, "Waiting for connection...");

    // Example: Answer call after 5 seconds (for testing)
    vTaskDelay(pdMS_TO_TICKS(5000));
    a2dpSinkHfpHf_answer_call();
}

Adding Music Control

#include "a2dpSinkHfpHf.h"
#include "nvs_flash.h"

void metadata_callback(const bt_avrc_metadata_t *metadata) {
    if (metadata && metadata->valid) {
        printf("Now Playing: %s - %s\n", 
               metadata->artist, metadata->title);
    }
}

void app_main(void) {
    nvs_flash_init();

    a2dpSinkHfpHf_config_t config = {
        .device_name = "ESP32-Music",
        .i2s_tx_bck = 26,
        .i2s_tx_ws = 25,
        .i2s_tx_dout = 22,
        .i2s_rx_bck = -1,  // No mic needed for music only
        .i2s_rx_ws = -1,
        .i2s_rx_din = -1
    };

    // Register metadata callback
    a2dpSinkHfpHf_register_avrc_metadata_callback(metadata_callback);

    a2dpSinkHfpHf_init(&config);

    // Control playback
    vTaskDelay(pdMS_TO_TICKS(2000));
    a2dpSinkHfpHf_avrc_play();
}

Pin Configuration

Speaker Output (Required)

Signal ESP32 Pin DAC Pin
BCK 26 BCK
WS 25 LCK
DOUT 22 DIN

Microphone Input (Optional - for calls)

Signal ESP32 Pin Mic Pin
BCK 32 SCK
WS 33 WS
DIN 34 SD

Note: You can use any available GPIO pins. The pins shown are examples.

Setting a Custom PIN

// Set PIN before initialization
a2dpSinkHfpHf_set_pin("5678", 4);
a2dpSinkHfpHf_init(&config);

Enabling Phonebook Sync

  1. Add SPIFFS partition to partitions.csv:
# Name,   Type, SubType, Offset,  Size
nvs,      data, nvs,     0x9000,  0x6000
phy_init, data, phy,     0xf000,  0x1000
factory,  app,  factory, 0x10000, 1536K
storage,  data, spiffs,  ,        1M
  1. Set country code in your app:
a2dpSinkHfpHf_set_country_code("1");  // USA
a2dpSinkHfpHf_init(&config);

Next Steps

Quick Reference

HFP Call Control

a2dpSinkHfpHf_answer_call();              // Answer call
a2dpSinkHfpHf_reject_call();              // Reject call
a2dpSinkHfpHf_hangup_call();              // Hang up
a2dpSinkHfpHf_dial_number("5551234");     // Dial number
a2dpSinkHfpHf_redial();                   // Redial
a2dpSinkHfpHf_start_voice_recognition();  // Start Siri/Assistant

Music Control

a2dpSinkHfpHf_avrc_play();   // Play
a2dpSinkHfpHf_avrc_pause();  // Pause
a2dpSinkHfpHf_avrc_next();   // Next track
a2dpSinkHfpHf_avrc_prev();   // Previous track

Status

bool connected = a2dpSinkHfpHf_is_connected();
bool avrc_connected = a2dpSinkHfpHf_is_avrc_connected();
const bt_avrc_metadata_t *meta = a2dpSinkHfpHf_get_avrc_metadata();
Clone this wiki locally