Page 1 of 1

AVRCP without A2DP ?

Posted: Tue Jan 22, 2019 7:32 pm
by Marc-Aurele
I'm currently trying to build myself some kind of basic Bluetooth Classic remote control.

The most straightforward way at this point for me seemed to use the a2dp_sink-example and leverage the AVRCP-CT-APIs (using esp_avrc_ct_send_passthrough_cmd()).

Let's have a look at the info-output of bluetoothctl:

Code: Select all

[bluetooth]# info 20:47:DA:FB:93:65
Device 20:47:DA:FB:93:65 (public)
	Name: ESP_COEX_A2DP_DEMO
	Alias: ESP_COEX_A2DP_DEMO
	Class: 0x002c0414
	Icon: audio-card
	Paired: no
	Trusted: no
	Blocked: no
	Connected: no
	LegacyPairing: no
	UUID: Audio Sink                (0000110b-0000-1000-8000-00805f9b34fb)
	UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
	UUID: A/V Remote Control        (0000110e-0000-1000-8000-00805f9b34fb)
	RSSI: -50
That is working nicely enough so far - but now I want to get rid of the A2DP-part. My intuition was to just remove the esp_a2d_

Code: Select all

/* initialize A2DP sink */
esp_a2d_register_callback(&bt_app_a2d_cb);
esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb);
esp_a2d_sink_init();

/* initialize AVRCP controller */
esp_avrc_ct_init();
esp_avrc_ct_register_callback(bt_app_rc_ct_cb);
I would have expected to now only see the UUIDs for "A/V Remote Control" - but there is just no UUIDs at all.

Switching things around and removing the esp_avrc_ct_ and keeping esp_a2d_, I would have expected to only have the "Audio"-UUIDs left. But wrong again: It's still all four UUIDs.

So now I am wondering: Can AVRC only be used in conjunction with A2DP? Or is Espressif just implementing AVRC "the wrong way"?

Thank you in advance for your help.

Best regards,

Aurélien

Re: AVRCP without A2DP ?

Posted: Wed Jan 23, 2019 3:40 pm
by Marc-Aurele
Has someone already found out how to solve this issue ?

Feel free to share :)

Best regards,

Aurélien

Re: AVRCP without A2DP ?

Posted: Thu Jul 18, 2019 7:58 pm
by EdanPotter
Hello there! Any news? I'm trying to get rid off A2DP too.

Re: AVRCP without A2DP ?

Posted: Sun Jun 01, 2025 10:50 am
by JakubKral
Hello, I figured out that a2dp is needed for my PC (if not initalized, my archlinux PC refuses to connect), even though it is not used. This code should pause and play music:

Code: Select all

#include "freertos/FreeRTOS.h"
#include "esp_err.h"
#include "esp_log.h"
#include "nvs.h"
#include "nvs_flash.h"

#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_avrc_api.h"
#include "esp_gap_bt_api.h"
#include "esp_bt_device.h"
#include "esp_a2dp_api.h"

static void vMainNvsInit(void);
static void vMainBluedroidInit(void);

SemaphoreHandle_t xSemaphore = NULL;

void app_main(void)
{
    xSemaphore = xSemaphoreCreateBinary();

    // initialize nvs - needed for bluetooth
    vMainNvsInit();

    // initialize bluedroid
    vMainBluedroidInit();

    if (xSemaphoreTake(xSemaphore, portMAX_DELAY))
        while (1)
        {
            esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED);
            vTaskDelay(100 / portTICK_PERIOD_MS);
            esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_RELEASED);
            vTaskDelay(3000 / portTICK_PERIOD_MS);

            esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED);
            vTaskDelay(100 / portTICK_PERIOD_MS);
            esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_RELEASED);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
}

static void vMainNvsInit(void)
{
    // initialize nvs
    esp_err_t err = nvs_flash_init();

    // check for error
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
}

static void bt_gap_callback(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param);
static void bt_avrc_callback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t * params);

static void vMainBluedroidInit(void)
{
    // release memory for bluetooth low energy
    esp_bt_controller_mem_release(ESP_BT_MODE_BLE);

    // initialize peripherial
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
    ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT));

    // initialize bluedroid
    esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
#if 0
    bluedroid_cfg.ssp_en = false;
#endif
    ESP_ERROR_CHECK(esp_bluedroid_init_with_cfg(&bluedroid_cfg));
    ESP_ERROR_CHECK(esp_bluedroid_enable());

    // setup seurity features
    esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
    esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
    esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));

    // setup BT GAP pin
    esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
    esp_bt_pin_code_t pin_code;
    pin_code[0] = '1';
    pin_code[1] = '2';
    pin_code[2] = '3';
    pin_code[3] = '4';
    esp_bt_gap_set_pin(pin_type, 4, pin_code);

    // finish GAP setup
    esp_bt_gap_set_device_name("JetJack");
    esp_bt_gap_register_callback(bt_gap_callback);

    // init a2dp
    esp_avrc_ct_init();
    esp_avrc_ct_register_callback(bt_avrc_callback);
    esp_a2d_sink_init();

    // setup device connectable
    esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
}

#define TAG "bt_gap_callback"
static void bt_gap_callback(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
    switch (event)
    {
        case ESP_BT_GAP_PIN_REQ_EVT:
        {
            esp_bt_pin_code_t pin_code;
            pin_code[0] = '1';
            pin_code[1] = '2';
            pin_code[2] = '3';
            pin_code[3] = '4';
            esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code);
            break;
        }
        case ESP_BT_GAP_CFM_REQ_EVT:
            ESP_LOGI(TAG, "Accepting ssp");
            esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
            break;
        default:
            break;
    }
}

#undef TAG
#define TAG "bt_avrc_callback"
static void bt_avrc_callback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t * params)
{
    switch (event)
    {
        case ESP_AVRC_CT_CONNECTION_STATE_EVT:
            if (params->conn_stat.connected)
            {
                ESP_LOGI(TAG, "Connected");
                xSemaphoreGive(xSemaphore);
            }
            else
                ESP_LOGI(TAG, "Disconnected");
            break;
        default:
            ESP_LOGW(TAG, "Unhandled event: %d", event);
            break;
    }
}

Please note that the a2dp is NOT USED.

Please do not forget to configure esp-idf menuconfig correctly:
  • enable bluetooth (menuconfig->component config->Bluetooth->Bluetooth; see sdkconfig BT_ENABLED)
  • setup host to bluedroid dual mode (menuconfig->component config->Bluetooth->Host->Bluedroid; see sdkconfig BT_BLUEDROID_ENABLED in choice BT_HOST)
  • make sure controller is enabled (menuconfig->component config->Bluetooth->Controller->Enabled; see sdkconfig BT_CONTROLLER_ENABLED in choice BT_CONTROLLER)
  • enable a2dp (menuconfig->component config->Bluetooth->Bluedroid options->A2DP; see sdkconfig BT_A2DP_ENABLE)