ESP32 ESP-IDF 5.0 and a2dpsinkdemo.c problem

37 views
Skip to first unread message

Dawid Bańkowski

unread,
Apr 5, 2024, 11:58:27 AMApr 5
to btstack-dev
Hello,

I have problem with my own app.
Generally a2dp_sink_demo generated by example works fine but I want to port it to my own firmware.

Things which I made was:
1) Add/copy Btstack compoment to my project
2) Copy a2dp_sink_demo.c to project main directory
3) Update ckame file SRC section by "a2dp_sink_demo.c"
4) Update main function by btstack function and during the compilation I have error message line bellow:

The compilation log is like bellow:
Executing "ninja all"...
[0/1] Re-running CMake...-- ccache will be used for faster recompilation
-- Building ESP-IDF components for target esp32
Processing 1 dependencies:
[1/1] idf (5.0.0)-- Project sdkconfig file D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/sdkconfig
Loading defaults file D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/sdkconfig.defaults...
-- App "cpp_exceptions_example" version: 0811bd5-dirty
-- Adding linker script D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/build/esp-idf/esp_system/ld/memory.ld
-- Adding linker script C:/SysGCC/esp32/esp-idf/v5.0/components/esp_system/ld/esp32/sections.ld.in
-- Adding linker script C:/SysGCC/esp32/esp-idf/v5.0/components/esp_rom/esp32/ld/esp32.rom.ld
-- Adding linker script C:/SysGCC/esp32/esp-idf/v5.0/components/esp_rom/esp32/ld/esp32.rom.api.ld
-- Adding linker script C:/SysGCC/esp32/esp-idf/v5.0/components/esp_rom/esp32/ld/esp32.rom.libgcc.ld
-- Adding linker script C:/SysGCC/esp32/esp-idf/v5.0/components/esp_rom/esp32/ld/esp32.rom.newlib-data.ld
-- Adding linker script C:/SysGCC/esp32/esp-idf/v5.0/components/esp_rom/esp32/ld/esp32.rom.syscalls.ld
-- Adding linker script C:/SysGCC/esp32/esp-idf/v5.0/components/esp_rom/esp32/ld/esp32.rom.newlib-funcs.ld
-- Adding linker script C:/SysGCC/esp32/esp-idf/v5.0/components/soc/esp32/ld/esp32.peripherals.ld
-- Components: app_trace app_update bootloader bootloader_support bt btstack cmock console cxx driver efuse esp-tls esp_adc esp_app_format esp_common esp_eth esp_event esp_gdbstub esp_hid esp_http_client esp_http_server esp_https_ota esp_https_server esp_hw_support esp_lcd esp_local_ctrl esp_netif esp_partition esp_phy esp_pm esp_psram esp_ringbuf esp_rom esp_system esp_timer esp_wifi espcoredump espressif__led_strip esptool_py fatfs freertos hal heap http_parser idf_test ieee802154 json log lwip main mbedtls mqtt newlib nvs_flash openthread partition_table perfmon protobuf-c protocomm pthread sdmmc soc spi_flash spiffs tcp_transport ulp unity usb vfs wear_levelling wifi_provisioning wpa_supplicant xtensa
-- Component paths: C:/SysGCC/esp32/esp-idf/v5.0/components/app_trace C:/SysGCC/esp32/esp-idf/v5.0/components/app_update C:/SysGCC/esp32/esp-idf/v5.0/components/bootloader C:/SysGCC/esp32/esp-idf/v5.0/components/bootloader_support C:/SysGCC/esp32/esp-idf/v5.0/components/bt D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/Components/btstack C:/SysGCC/esp32/esp-idf/v5.0/components/cmock C:/SysGCC/esp32/esp-idf/v5.0/components/console C:/SysGCC/esp32/esp-idf/v5.0/components/cxx C:/SysGCC/esp32/esp-idf/v5.0/components/driver C:/SysGCC/esp32/esp-idf/v5.0/components/efuse C:/SysGCC/esp32/esp-idf/v5.0/components/esp-tls C:/SysGCC/esp32/esp-idf/v5.0/components/esp_adc C:/SysGCC/esp32/esp-idf/v5.0/components/esp_app_format C:/SysGCC/esp32/esp-idf/v5.0/components/esp_common C:/SysGCC/esp32/esp-idf/v5.0/components/esp_eth C:/SysGCC/esp32/esp-idf/v5.0/components/esp_event C:/SysGCC/esp32/esp-idf/v5.0/components/esp_gdbstub C:/SysGCC/esp32/esp-idf/v5.0/components/esp_hid C:/SysGCC/esp32/esp-idf/v5.0/components/esp_http_client C:/SysGCC/esp32/esp-idf/v5.0/components/esp_http_server C:/SysGCC/esp32/esp-idf/v5.0/components/esp_https_ota C:/SysGCC/esp32/esp-idf/v5.0/components/esp_https_server C:/SysGCC/esp32/esp-idf/v5.0/components/esp_hw_support C:/SysGCC/esp32/esp-idf/v5.0/components/esp_lcd C:/SysGCC/esp32/esp-idf/v5.0/components/esp_local_ctrl C:/SysGCC/esp32/esp-idf/v5.0/components/esp_netif C:/SysGCC/esp32/esp-idf/v5.0/components/esp_partition C:/SysGCC/esp32/esp-idf/v5.0/components/esp_phy C:/SysGCC/esp32/esp-idf/v5.0/components/esp_pm C:/SysGCC/esp32/esp-idf/v5.0/components/esp_psram C:/SysGCC/esp32/esp-idf/v5.0/components/esp_ringbuf C:/SysGCC/esp32/esp-idf/v5.0/components/esp_rom C:/SysGCC/esp32/esp-idf/v5.0/components/esp_system C:/SysGCC/esp32/esp-idf/v5.0/components/esp_timer C:/SysGCC/esp32/esp-idf/v5.0/components/esp_wifi C:/SysGCC/esp32/esp-idf/v5.0/components/espcoredump D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/Components/espressif__led_strip C:/SysGCC/esp32/esp-idf/v5.0/components/esptool_py C:/SysGCC/esp32/esp-idf/v5.0/components/fatfs C:/SysGCC/esp32/esp-idf/v5.0/components/freertos C:/SysGCC/esp32/esp-idf/v5.0/components/hal C:/SysGCC/esp32/esp-idf/v5.0/components/heap C:/SysGCC/esp32/esp-idf/v5.0/components/http_parser C:/SysGCC/esp32/esp-idf/v5.0/components/idf_test C:/SysGCC/esp32/esp-idf/v5.0/components/ieee802154 C:/SysGCC/esp32/esp-idf/v5.0/components/json C:/SysGCC/esp32/esp-idf/v5.0/components/log C:/SysGCC/esp32/esp-idf/v5.0/components/lwip D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/main C:/SysGCC/esp32/esp-idf/v5.0/components/mbedtls C:/SysGCC/esp32/esp-idf/v5.0/components/mqtt C:/SysGCC/esp32/esp-idf/v5.0/components/newlib C:/SysGCC/esp32/esp-idf/v5.0/components/nvs_flash C:/SysGCC/esp32/esp-idf/v5.0/components/openthread C:/SysGCC/esp32/esp-idf/v5.0/components/partition_table C:/SysGCC/esp32/esp-idf/v5.0/components/perfmon C:/SysGCC/esp32/esp-idf/v5.0/components/protobuf-c C:/SysGCC/esp32/esp-idf/v5.0/components/protocomm C:/SysGCC/esp32/esp-idf/v5.0/components/pthread C:/SysGCC/esp32/esp-idf/v5.0/components/sdmmc C:/SysGCC/esp32/esp-idf/v5.0/components/soc C:/SysGCC/esp32/esp-idf/v5.0/components/spi_flash C:/SysGCC/esp32/esp-idf/v5.0/components/spiffs C:/SysGCC/esp32/esp-idf/v5.0/components/tcp_transport C:/SysGCC/esp32/esp-idf/v5.0/components/ulp C:/SysGCC/esp32/esp-idf/v5.0/components/unity C:/SysGCC/esp32/esp-idf/v5.0/components/usb C:/SysGCC/esp32/esp-idf/v5.0/components/vfs C:/SysGCC/esp32/esp-idf/v5.0/components/wear_levelling C:/SysGCC/esp32/esp-idf/v5.0/components/wifi_provisioning C:/SysGCC/esp32/esp-idf/v5.0/components/wpa_supplicant C:/SysGCC/esp32/esp-idf/v5.0/components/xtensa
-- Configuring done
-- Generating done
-- Build files have been written to: D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/build
[1/1] cmd.exe /C "cd /D D:\!!!__GitHUB_repositories\MustangRadio_develepementVersion\02_Firmware\01_ESP...ustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/build/bootloader/bootloader.bin"Bootloader binary size 0x5ea0 bytes. 0x1160 bytes (16%) free.
[4/9] Building CXX object esp-idf/main/CMakeFiles/__idf_main.dir/exception_example_main.cpp.objD:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/main/exception_example_main.cpp: In function 'void init_uart()':
D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/main/exception_example_main.cpp:189:9: warning: missing initializer for member 'uart_config_t::rx_flow_ctrl_thresh' [-Wmissing-field-initializers]
  189 |         };
      |         ^
D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/main/exception_example_main.cpp:189:9: warning: missing initializer for member 'uart_config_t::source_clk' [-Wmissing-field-initializers]
[7/9] Linking CXX executable cpp_exceptions_example.elfFAILED: cpp_exceptions_example.elf
cmd.exe /C "cd . && C:\Users\dbank\.espressif\tools\xtensa-esp32-elf\esp-2022r1-11.2.0\xtensa-esp32-elf\bin\xtensa-esp32-elf-g++.exe -mlongcalls -Wno-frame-address  @CMakeFiles\cpp_exceptions_example.elf.rsp -o cpp_exceptions_example.elf  && cd ."
c:/users/dbank/.espressif/tools/xtensa-esp32-elf/esp-2022r1-11.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/11.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/main/libmain.a(exception_example_main.cpp.obj):(.literal.app_main+0x90): undefined reference to `btstack_main(int, char const**)'
c:/users/dbank/.espressif/tools/xtensa-esp32-elf/esp-2022r1-11.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/11.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/main/libmain.a(exception_example_main.cpp.obj): in function `app_main':
D:/!!!__GitHUB_repositories/MustangRadio_develepementVersion/02_Firmware/01_ESP32/HMI_and_bluetooth/main/exception_example_main.cpp:84: undefined reference to `btstack_main(int, char const**)'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
ninja failed with exit code 1, output of the command is in the d:\!!!__github_repositories\mustangradio_develepementversion\02_firmware\01_esp32\hmi_and_bluetooth\build\log\idf_py_stderr_output_8252 and d:\!!!__github_repositories\mustangradio_develepementversion\02_firmware\01_esp32\hmi_and_bluetooth\build\log\idf_py_stdout_output_8252


My main code is like this (this is C++ code):
 /* C++ exception handling example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

//#include <iostream>
#include "hwConfigFile.h"
//#include "WS2812/WS2812.h"
//#include "keyboard/keyboard.h"


//using std::cout;
//using std::endl;
//using std::runtime_error;

#include <inttypes.h>
#include "cstdio"
#include "driver/gpio.h"

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "esp_log.h"

#include "keyboard/keyboard.h"
#include "Leds/Leds_backlight.h"
//#include "MCP23008/MCP23008.h"
#include "StepperOpto/StepperOpto.h"
#include "tasksFunctions/tasksFunctions.h"
#include "NVSeeprom/NVSeeprom.h"

#include "driver/uart.h"
void init_uart();

#include "btstack_port_esp32.h"
#include "btstack_run_loop.h"
#include "btstack_stdio_esp32.h"
#include "hci_dump.h"
#include "hci_dump_embedded_stdout.h"

#include <stddef.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// warn about unsuitable sdkconfig
#include "sdkconfig.h"
#if !CONFIG_BT_ENABLED
#error "Bluetooth disabled - please set CONFIG_BT_ENABLED via menuconfig -> Component Config -> Bluetooth -> [x] Bluetooth"
#endif
#if !CONFIG_BT_CONTROLLER_ONLY
#error "Different Bluetooth Host stack selected - please set CONFIG_BT_CONTROLLER_ONLY via menuconfig -> Component Config -> Bluetooth -> Host -> Disabled"
#endif
#if ESP_IDF_VERSION_MAJOR >= 5
#if !CONFIG_BT_CONTROLLER_ENABLED
#error "Different Bluetooth Host stack selected - please set CONFIG_BT_CONTROLLER_ENABLED via menuconfig -> Component Config -> Bluetooth -> Controller -> Enabled"
#endif
#endif

extern int btstack_main(int argc, const char * argv[]);

extern "C" void app_main(void)
{
/* CAUTION */
//this project had been created with:
//- Visual Studio 2019
//- VisualGDB-5.6r9.msi
//- esp32-gcc11.2.0-r2.exe (ESP32 toolchain)
/* CAUTION */

btstack_init();

btstack_main(0, NULL);

btstack_run_loop_execute(); //write just at the begiining to test compileing errors

init_uart();

const char *main_TAG = "Main function:";
//ESP_LOGI(main_TAG, "starting");
printf("\n\n\n\n\n\n\n\n\n\n%s: starting...\n", main_TAG);



displayLedsColors.equaliserLed.primary.blue = 0;
displayLedsColors.equaliserLed.primary.green = 0;
displayLedsColors.equaliserLed.primary.red = 25;
displayLedsColors.equaliserLed.secondary.blue = 0;
displayLedsColors.equaliserLed.secondary.green = 0;
displayLedsColors.equaliserLed.secondary.red = 0;


displayLedsColors.backlightLeds.primary.red = 0;
displayLedsColors.backlightLeds.primary.green=0;
displayLedsColors.backlightLeds.primary.blue=0;

//tworzy obiekt obsługujący NVS flash radio
printf("%s: NVS storage init\n", main_TAG);
//ESP_LOGI(main_TAG, "NVS storage init");
NVS * storage = NULL;
assert(storage = new NVS(NVS_RADIO_CONFIG_NAMESPACE));
//storage->CAUTION_NVS_ereaseAndInit(NVS_EREASE_COUNTDOWN_TIME);

//tworzy obiekt obsługujący ledy sygnalizacyjne i podświetlenia
//ESP_LOGI(main_TAG, "Backlight and display leds init");
printf("%s: Backlight and display leds init\n", main_TAG);
LEDS_BACKLIGHT *ledDisplay = NULL;
assert(ledDisplay = new LEDS_BACKLIGHT(LED_DISPLAY_GPIO, LED_DISPLAY_LEDS_QUANTITY, LED_PIXEL_FORMAT_GRB, LED_MODEL_WS2812));
ledDisplay->ledStripClearAll();

handlerMutex_ledDisplay_Backlight = NULL; //czyści wskaźnik mutex'u dla podświetlenia i diód sygnalizacyjnych, bo kilka tasków bedzi ekorzystać z linii komunikacyjnej WS2812
assert(handlerMutex_ledDisplay_Backlight = xSemaphoreCreateBinary()); //tworzy mutex dla podświetlenia
xSemaphoreGive(handlerMutex_ledDisplay_Backlight); //oddaje mutex, zasób jest dostępny dla pierwszego tasku, który się po niego zgłosi
printf("%s: Display leds task starting\n", main_TAG);
//ESP_LOGI(main_TAG, "Display leds task starting");
assert(xTaskCreate(humanMahineDisplayLeds, "Leds control", 850, ledDisplay, tskIDLE_PRIORITY, &handlerTask_ledDisplay)); //tworzy task dla diód sygnalizacyjnych (korzystają z WS2812)

printf("%s: Backlight leds task starting\n", main_TAG);
//ESP_LOGI(main_TAG, "Backlight leds task starting");
assert(xTaskCreate(humanMahineBacklightLeds, "Backlight control", 850, ledDisplay, tskIDLE_PRIORITY, &handlerTask_backlightDisplay)); //tworzy task dla dod podświetlenia (korzystają z WS2812)

//konfiguruje kolejkę, która będzie zawierać elementy odpowiedzi z debounceAndGpiosCheckCallback
printf("%s: Buttons and encoders (aka keyboard) init\n", main_TAG);
//ESP_LOGI(main_TAG, "Buttons and encoders (aka keyboard) init");
handlerQueue_MainKeyboard = NULL;
handlerQueue_MainKeyboard = xQueueCreate(QueueHandlerMainKeyboard_len, sizeof(keyboardUnion));
assert(handlerQueue_MainKeyboard);
//ESP_LOGI(main_TAG, "handlerQueue_MainKeyboard created at: %lx", (long) handlerQueue_MainKeyboard);

//tworzy obiekt obsługujący klawiaturę
KEYBOARD *klawiatura = NULL;
assert(klawiatura = new KEYBOARD(handlerQueue_MainKeyboard, handlerTask_backlightDisplay));
handlerTask_keyboardQueueParametersParser = NULL;
printf("%s: Keyboard queue pareser task starting\n", main_TAG);
//ESP_LOGI(main_TAG, "Keyboard queue pareser task starting");
assert(xTaskCreate(keyboardQueueParametersParser, "Keyboard Param", 2048, NULL, tskIDLE_PRIORITY, &handlerTask_keyboardQueueParametersParser)); //tworzy taska, który parsuje, sprawdza dane które przerwania od klawiatury wipsały w kolejkę: handlerQueue_MainKeyboard, w przerwaniach nie można tego zrobić, bo zajęło by to za dużo czasu


StepperOpto * motor = NULL;
assert(motor = new StepperOpto());

motorTaskParam motorTaskParamStruct;
motorTaskParamStruct.motorPointer = motor;
motorTaskParamStruct.storagePointer = storage;

//motor->measureSliderRange();
//assert(xTaskCreate(stepperMotor, "Stepper morot", 2048, &motorTaskParamStruct, tskIDLE_PRIORITY+2, &handlerTask_stepperMotor));
assert(xTaskCreatePinnedToCore(stepperMotor, "Stepper morot", 2048, &motorTaskParamStruct, tskIDLE_PRIORITY + 2, &handlerTask_stepperMotor, TASK_TO_CORE1));

// Motor.measureSliderRange();

//Motor.enableStepperMotor();
//Motor.disableStepperMotor();

// Motor.moveXSteps(-3000);

// Motor.moveTo_xPercent(80.1);

while (true)
{
         
}

}




void init_uart() {
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
//.rx_flow_ctrl_thresh =
//.source_clk =
};

uart_param_config(UART_NUM_0, &uart_config);
//uart_set_pin(UART_NUM_0, 1, 3, 0, 0);
uart_driver_install(UART_NUM_0, 256, 0, 0, NULL, 0);
}


And cmake file:
idf_component_register(
                    SRCS
                    "exception_example_main.cpp"
                    keyboard/keyboard.cpp
                    i2c_master/i2c_master.cpp
                    MCP23008/MCP23008.cpp
                    Leds/Leds_backlight.cpp
                    tasksFunctions/tasksFunctions.cpp
                    NVSeeprom/NVSeeprom.cpp
                    StepperOpto/StepperOpto.cpp
                    a2dp_sink_demo.c
                    INCLUDE_DIRS ".")

I assume that there is something with the way I added "a2dp_sink_demo.c" file to my project. It is not standard "a2dp_sink_demo.h" and "a2dp_sink_demo.c".

Can You give me adwice?



Matthias Ringwald

unread,
Apr 5, 2024, 12:25:51 PMApr 5
to btsta...@googlegroups.com
Hi Dawid

You know your application best! If you can compile the a2dp_sink_demo for ESP32 as is, I would suggest to start with that and slowly add your application to the provided example.
By this, you can get closer to the combined goal step by step and each step is easier to understand and to debug if it fails.

Here's an example that uses BTstack as a Bluetooth Guitar Footpedal:
https://github.com/mringwal/spark-control

btw.. our examples copy BTstack into the esp-idf folder. I don't know it will work if you copy it into your project folder. People tried something like this before, see Pull Requests.

Best Regards
Matthias
> --
> You received this message because you are subscribed to the Google Groups "btstack-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to btstack-dev...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/btstack-dev/ab2ed431-987c-40a8-8bed-d021cc44b8b6n%40googlegroups.com.

Dawid Bańkowski

unread,
Apr 6, 2024, 3:08:16 PMApr 6
to btstack-dev
@Matthias Ringwald,

Are You able to reorganice examples to standard header + source pair? In ESP-IDE for C++ there is no compile error in this way. I have rearange it and it is compileing but now I have some Bluetooth device pairing issu. They are not pairing. :/

Dawid Bańkowski

unread,
Apr 6, 2024, 3:08:16 PMApr 6
to btstack-dev
I have considered "slowly add your application to the provided example" but it is in C and my app is written in C++, :/

piątek, 5 kwietnia 2024 o 18:25:51 UTC+2 Matthias Ringwald napisał(a):

Dawid Bańkowski

unread,
Apr 6, 2024, 3:08:16 PMApr 6
to btstack-dev
My bt_A2DPSink.h code is:
#pragma once


#include "btstack_port_esp32.h"
#include "btstack_run_loop.h"
#include "btstack_stdio_esp32.h"
#include "hci_dump.h"
#include "hci_dump_embedded_stdout.h"

#include <stddef.h>

// warn about unsuitable sdkconfig
#include "sdkconfig.h"
#if !CONFIG_BT_ENABLED
#error "Bluetooth disabled - please set CONFIG_BT_ENABLED via menuconfig -> Component Config -> Bluetooth -> [x] Bluetooth"
#endif
#if !CONFIG_BT_CONTROLLER_ONLY
#error "Different Bluetooth Host stack selected - please set CONFIG_BT_CONTROLLER_ONLY via menuconfig -> Component Config -> Bluetooth -> Host -> Disabled"
#endif
#if ESP_IDF_VERSION_MAJOR >= 5
#if !CONFIG_BT_CONTROLLER_ENABLED
#error "Different Bluetooth Host stack selected - please set CONFIG_BT_CONTROLLER_ENABLED via menuconfig -> Component Config -> Bluetooth -> Controller -> Enabled"
#endif
#endif

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "btstack.h"

#include "btstack_resample.h"

int setup_demo(void);





typedef struct {
uint8_t  a2dp_local_seid;
uint8_t  media_sbc_codec_configuration[4];
} a2dp_sink_demo_stream_endpoint_t;

typedef enum {
STREAM_STATE_CLOSED,
STREAM_STATE_OPEN,
STREAM_STATE_PLAYING,
STREAM_STATE_PAUSED,
} stream_state_t;

typedef struct {
uint8_t  reconfigure;
uint8_t  num_channels;
uint16_t sampling_frequency;
uint8_t  block_length;
uint8_t  subbands;
uint8_t  min_bitpool_value;
uint8_t  max_bitpool_value;
btstack_sbc_channel_mode_t      channel_mode;
btstack_sbc_allocation_method_t allocation_method;
} media_codec_configuration_sbc_t;

typedef struct {
bd_addr_t addr;
uint16_t  a2dp_cid;
uint8_t   a2dp_local_seid;
stream_state_t stream_state;
media_codec_configuration_sbc_t sbc_configuration;
} a2dp_sink_demo_a2dp_connection_t;



piątek, 5 kwietnia 2024 o 18:25:51 UTC+2 Matthias Ringwald napisał(a):

Dawid Bańkowski

unread,
Apr 6, 2024, 3:08:16 PMApr 6
to btstack-dev
And bt_A2DPSink.c code:
#include "bt_A2DPSink.h"

static void a2dp_sink_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size);
static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
static void dump_sbc_configuration(media_codec_configuration_sbc_t * configuration);
static void media_processing_close(void);
static int media_processing_init(media_codec_configuration_sbc_t * configuration);


static a2dp_sink_demo_stream_endpoint_t a2dp_sink_demo_stream_endpoint;
static btstack_packet_callback_registration_t hci_event_callback_registration;
static a2dp_sink_demo_a2dp_connection_t a2dp_sink_demo_a2dp_connection;

static uint8_t  sdp_avdtp_sink_service_buffer[150];
static uint8_t  sdp_avrcp_target_service_buffer[150];
static uint8_t  sdp_avrcp_controller_service_buffer[200];
static uint8_t  device_id_sdp_service_buffer[100];

static uint8_t media_sbc_codec_capabilities[] = {
0xFF,
//(AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
0xFF,
//(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
2,
53
};

static int volume_percentage = 0;







int setup_demo(void) {

// init protocols
l2cap_init();
sdp_init();
#ifdef ENABLE_BLE
// Initialize LE Security Manager. Needed for cross-transport key derivation
sm_init();
#endif
#ifdef ENABLE_AVRCP_COVER_ART
goep_client_init();
avrcp_cover_art_client_init();
#endif

// Init profiles
a2dp_sink_init();
avrcp_init();
avrcp_controller_init();
avrcp_target_init();


// Configure A2DP Sink
a2dp_sink_register_packet_handler(&a2dp_sink_packet_handler);
a2dp_sink_register_media_handler(&handle_l2cap_media_data_packet);
a2dp_sink_demo_stream_endpoint_t * stream_endpoint = &a2dp_sink_demo_stream_endpoint;
avdtp_stream_endpoint_t * local_stream_endpoint = a2dp_sink_create_stream_endpoint(AVDTP_AUDIO,
AVDTP_CODEC_SBC,
media_sbc_codec_capabilities,
sizeof(media_sbc_codec_capabilities),
stream_endpoint->media_sbc_codec_configuration,
sizeof(stream_endpoint->media_sbc_codec_configuration));
btstack_assert(local_stream_endpoint != NULL);
// - Store stream enpoint's SEP ID, as it is used by A2DP API to identify the stream endpoint
stream_endpoint->a2dp_local_seid = avdtp_local_seid(local_stream_endpoint);


// Configure AVRCP Controller + Target
avrcp_register_packet_handler(&avrcp_packet_handler);
avrcp_controller_register_packet_handler(&avrcp_controller_packet_handler);
avrcp_target_register_packet_handler(&avrcp_target_packet_handler);
   

// Configure SDP

// - Create and register A2DP Sink service record
memset(sdp_avdtp_sink_service_buffer, 0, sizeof(sdp_avdtp_sink_service_buffer));
a2dp_sink_create_sdp_record(sdp_avdtp_sink_service_buffer,
sdp_create_service_record_handle(),
AVDTP_SINK_FEATURE_MASK_HEADPHONE,
NULL,
NULL);
btstack_assert(de_get_len(sdp_avdtp_sink_service_buffer) <= sizeof(sdp_avdtp_sink_service_buffer));
sdp_register_service(sdp_avdtp_sink_service_buffer);

// - Create AVRCP Controller service record and register it with SDP. We send Category 1 commands to the media player, e.g. play/pause
memset(sdp_avrcp_controller_service_buffer, 0, sizeof(sdp_avrcp_controller_service_buffer));
uint16_t controller_supported_features = 1 << AVRCP_CONTROLLER_SUPPORTED_FEATURE_CATEGORY_PLAYER_OR_RECORDER;
#ifdef AVRCP_BROWSING_ENABLED
controller_supported_features |= 1 << AVRCP_CONTROLLER_SUPPORTED_FEATURE_BROWSING;
#endif
#ifdef ENABLE_AVRCP_COVER_ART
controller_supported_features |= 1 << AVRCP_CONTROLLER_SUPPORTED_FEATURE_COVER_ART_GET_LINKED_THUMBNAIL;
#endif
avrcp_controller_create_sdp_record(sdp_avrcp_controller_service_buffer,
sdp_create_service_record_handle(),
controller_supported_features,
NULL,
NULL);
btstack_assert(de_get_len(sdp_avrcp_controller_service_buffer) <= sizeof(sdp_avrcp_controller_service_buffer));
sdp_register_service(sdp_avrcp_controller_service_buffer);

// - Create and register A2DP Sink service record
//   -  We receive Category 2 commands from the media player, e.g. volume up/down
memset(sdp_avrcp_target_service_buffer, 0, sizeof(sdp_avrcp_target_service_buffer));
uint16_t target_supported_features = 1 << AVRCP_TARGET_SUPPORTED_FEATURE_CATEGORY_MONITOR_OR_AMPLIFIER;
avrcp_target_create_sdp_record(sdp_avrcp_target_service_buffer,
sdp_create_service_record_handle(),
target_supported_features,
NULL,
NULL);
btstack_assert(de_get_len(sdp_avrcp_target_service_buffer) <= sizeof(sdp_avrcp_target_service_buffer));
sdp_register_service(sdp_avrcp_target_service_buffer);

// - Create and register Device ID (PnP) service record
memset(device_id_sdp_service_buffer, 0, sizeof(device_id_sdp_service_buffer));
device_id_create_sdp_record(device_id_sdp_service_buffer,
sdp_create_service_record_handle(),
DEVICE_ID_VENDOR_ID_SOURCE_BLUETOOTH,
BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH,
1,
1);
btstack_assert(de_get_len(device_id_sdp_service_buffer) <= sizeof(device_id_sdp_service_buffer));
sdp_register_service(device_id_sdp_service_buffer);


// Configure GAP - discovery / connection

// - Set local name with a template Bluetooth address, that will be automatically
//   replaced with an actual address once it is available, i.e. when BTstack boots
//   up and starts talking to a Bluetooth module.
gap_set_local_name("A2DP Sink Demo 00:00:00:00:00:00");

// - Allow to show up in Bluetooth inquiry
gap_discoverable_control(1);

// - Set Class of Device - Service Class: Audio, Major Device Class: Audio, Minor: Loudspeaker
gap_set_class_of_device(0x200414);

// - Allow for role switch in general and sniff mode
gap_set_default_link_policy_settings(LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE);

// - Allow for role switch on outgoing connections
//   - This allows A2DP Source, e.g. smartphone, to become master when we re-connect to it.
gap_set_allow_role_switch(true);


// Register for HCI events
hci_event_callback_registration.callback = &hci_packet_handler;
hci_add_event_handler(&hci_event_callback_registration);

// Inform about audio playback / test options
#ifdef HAVE_POSIX_FILE_IO
if (!btstack_audio_sink_get_instance()) {
printf("No audio playback.\n");
}
else {
printf("Audio playback supported.\n");
}
#ifdef STORE_TO_WAV_FILE
printf("Audio will be stored to \'%s\' file.\n", wav_filename);
#endif
#endif
return 0;
}
/* LISTING_END */

static void a2dp_sink_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(channel);
UNUSED(size);
uint8_t status;

uint8_t allocation_method;

if (packet_type != HCI_EVENT_PACKET) return;
if (hci_event_packet_get_type(packet) != HCI_EVENT_A2DP_META) return;

a2dp_sink_demo_a2dp_connection_t * a2dp_conn = &a2dp_sink_demo_a2dp_connection;

switch (packet[2]) {
case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION:
printf("A2DP  Sink      : Received non SBC codec - not implemented\n");
break;
case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION: {
printf("A2DP  Sink      : Received SBC codec configuration\n");
a2dp_conn->sbc_configuration.reconfigure = a2dp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(packet);
a2dp_conn->sbc_configuration.num_channels = a2dp_subevent_signaling_media_codec_sbc_configuration_get_num_channels(packet);
a2dp_conn->sbc_configuration.sampling_frequency = a2dp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(packet);
a2dp_conn->sbc_configuration.block_length = a2dp_subevent_signaling_media_codec_sbc_configuration_get_block_length(packet);
a2dp_conn->sbc_configuration.subbands = a2dp_subevent_signaling_media_codec_sbc_configuration_get_subbands(packet);
a2dp_conn->sbc_configuration.min_bitpool_value = a2dp_subevent_signaling_media_codec_sbc_configuration_get_min_bitpool_value(packet);
a2dp_conn->sbc_configuration.max_bitpool_value = a2dp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(packet);
           
allocation_method = a2dp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method(packet);
           
// Adapt Bluetooth spec definition to SBC Encoder expected input
a2dp_conn->sbc_configuration.allocation_method = (btstack_sbc_allocation_method_t)(allocation_method - 1);
           
switch (a2dp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode(packet)) {
case AVDTP_CHANNEL_MODE_JOINT_STEREO:
a2dp_conn->sbc_configuration.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
break;
case AVDTP_CHANNEL_MODE_STEREO:
a2dp_conn->sbc_configuration.channel_mode = SBC_CHANNEL_MODE_STEREO;
break;
case AVDTP_CHANNEL_MODE_DUAL_CHANNEL:
a2dp_conn->sbc_configuration.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
break;
case AVDTP_CHANNEL_MODE_MONO:
a2dp_conn->sbc_configuration.channel_mode = SBC_CHANNEL_MODE_MONO;
break;
default:
btstack_assert(false);
break;
}
dump_sbc_configuration(&a2dp_conn->sbc_configuration);
break;
}

case A2DP_SUBEVENT_STREAM_ESTABLISHED:
status = a2dp_subevent_stream_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS) {
printf("A2DP  Sink      : Streaming connection failed, status 0x%02x\n", status);
break;
}

a2dp_subevent_stream_established_get_bd_addr(packet, a2dp_conn->addr);
a2dp_conn->a2dp_cid = a2dp_subevent_stream_established_get_a2dp_cid(packet);
a2dp_conn->a2dp_local_seid = a2dp_subevent_stream_established_get_local_seid(packet);
a2dp_conn->stream_state = STREAM_STATE_OPEN;

printf("A2DP  Sink      : Streaming connection is established, address %s, cid 0x%02x, local seid %d\n",
bd_addr_to_str(a2dp_conn->addr),
a2dp_conn->a2dp_cid,
a2dp_conn->a2dp_local_seid);
#ifdef HAVE_BTSTACK_STDIN
// use address for outgoing connections
memcpy(device_addr, a2dp_conn->addr, 6);
#endif
break;
       
#ifdef ENABLE_AVDTP_ACCEPTOR_EXPLICIT_START_STREAM_CONFIRMATION
case A2DP_SUBEVENT_START_STREAM_REQUESTED:
printf("A2DP  Sink      : Explicit Accept to start stream, local_seid %d\n", a2dp_subevent_start_stream_requested_get_local_seid(packet));
a2dp_sink_start_stream_accept(a2dp_cid, a2dp_local_seid);
break;
#endif
case A2DP_SUBEVENT_STREAM_STARTED:
printf("A2DP  Sink      : Stream started\n");
a2dp_conn->stream_state = STREAM_STATE_PLAYING;
if (a2dp_conn->sbc_configuration.reconfigure) {
media_processing_close();
}
// prepare media processing
media_processing_init(&a2dp_conn->sbc_configuration);
// audio stream is started when buffer reaches minimal level
break;
       
case A2DP_SUBEVENT_STREAM_SUSPENDED:
printf("A2DP  Sink      : Stream paused\n");
a2dp_conn->stream_state = STREAM_STATE_PAUSED;
media_processing_pause();
break;
       
case A2DP_SUBEVENT_STREAM_RELEASED:
printf("A2DP  Sink      : Stream released\n");
a2dp_conn->stream_state = STREAM_STATE_CLOSED;
media_processing_close();
break;
       
case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
printf("A2DP  Sink      : Signaling connection released\n");
a2dp_conn->a2dp_cid = 0;
media_processing_close();
break;
       
default:
break;
}
}


static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size) {
UNUSED(seid);
int pos = 0;
     
avdtp_media_packet_header_t media_header;
if (!read_media_data_header(packet, size, &pos, &media_header)) return;
   
avdtp_sbc_codec_header_t sbc_header;
if (!read_sbc_header(packet, size, &pos, &sbc_header)) return;

int packet_length = size - pos;
uint8_t *packet_begin = packet + pos;

const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance();
// process data right away if there's no audio implementation active, e.g. on posix systems to store as .wav
if (!audio) {
sbc_decoder_instance->decode_signed_16(&sbc_decoder_context, 0, packet_begin, packet_length);
return;
}

// store sbc frame size for buffer management
sbc_frame_size = packet_length / sbc_header.num_frames;
int status = btstack_ring_buffer_write(&sbc_frame_ring_buffer, packet_begin, packet_length);
if (status != ERROR_CODE_SUCCESS) {
printf("Error storing samples in SBC ring buffer!!!\n");
}

// decide on audio sync drift based on number of sbc frames in queue
int sbc_frames_in_buffer = btstack_ring_buffer_bytes_available(&sbc_frame_ring_buffer) / sbc_frame_size;
#ifdef HAVE_BTSTACK_AUDIO_EFFECTIVE_SAMPLERATE
if (!l2cap_stream_started && audio_stream_started) {
l2cap_stream_started = 1;
btstack_sample_rate_compensation_init(&sample_rate_compensation, btstack_run_loop_get_time_ms(), a2dp_sink_demo_a2dp_connection.sbc_configuration.sampling_frequency, FLOAT_TO_Q15(1.f));
}
// update sample rate compensation
if (audio_stream_started && (audio != NULL)) {
uint32_t resampling_factor = btstack_sample_rate_compensation_update(&sample_rate_compensation, btstack_run_loop_get_time_ms(), sbc_header.num_frames * 128, audio->get_samplerate());
btstack_resample_set_factor(&resample_instance, resampling_factor);
//        printf("sbc buffer level :            %"PRIu32"\n", btstack_ring_buffer_bytes_available(&sbc_frame_ring_buffer));
}
#else
uint32_t resampling_factor;

// nominal factor (fixed-point 2^16) and compensation offset
uint32_t nominal_factor = 0x10000;
uint32_t compensation   = 0x00100;

if (sbc_frames_in_buffer < OPTIMAL_FRAMES_MIN) {
resampling_factor = nominal_factor - compensation; // stretch samples
}
else if (sbc_frames_in_buffer <= OPTIMAL_FRAMES_MAX) {
resampling_factor = nominal_factor; // nothing to do
}
else {
resampling_factor = nominal_factor + compensation; // compress samples
}

btstack_resample_set_factor(&resample_instance, resampling_factor);
#endif
// start stream if enough frames buffered
if (!audio_stream_started && sbc_frames_in_buffer >= OPTIMAL_FRAMES_MIN) {
media_processing_start();
}
}



static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(channel);
UNUSED(size);
uint16_t local_cid;
uint8_t  status;
bd_addr_t address;

a2dp_sink_demo_avrcp_connection_t * connection = &a2dp_sink_demo_avrcp_connection;

if (packet_type != HCI_EVENT_PACKET) return;
if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
switch (packet[2]) {
case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
status = avrcp_subevent_connection_established_get_status(packet);
if (status != ERROR_CODE_SUCCESS) {
printf("AVRCP: Connection failed, status 0x%02x\n", status);
connection->avrcp_cid = 0;
return;
}

connection->avrcp_cid = local_cid;
avrcp_subevent_connection_established_get_bd_addr(packet, address);
printf("AVRCP: Connected to %s, cid 0x%02x\n", bd_addr_to_str(address), connection->avrcp_cid);

#ifdef HAVE_BTSTACK_STDIN
// use address for outgoing connections
avrcp_subevent_connection_established_get_bd_addr(packet, device_addr);
#endif

avrcp_target_support_event(connection->avrcp_cid, AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED);
avrcp_target_support_event(connection->avrcp_cid, AVRCP_NOTIFICATION_EVENT_BATT_STATUS_CHANGED);
avrcp_target_battery_status_changed(connection->avrcp_cid, battery_status);
       
// query supported events:
avrcp_controller_get_supported_events(connection->avrcp_cid);
return;
}
       
case AVRCP_SUBEVENT_CONNECTION_RELEASED:
printf("AVRCP: Channel released: cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));
connection->avrcp_cid = 0;
connection->notifications_supported_by_target = 0;
return;
default:
break;
}
}



static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(channel);
UNUSED(size);

// helper to print c strings
uint8_t avrcp_subevent_value[256];
uint8_t play_status;
uint8_t event_id;

a2dp_sink_demo_avrcp_connection_t * avrcp_connection = &a2dp_sink_demo_avrcp_connection;

if (packet_type != HCI_EVENT_PACKET) return;
if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
if (avrcp_connection->avrcp_cid == 0) return;

memset(avrcp_subevent_value, 0, sizeof(avrcp_subevent_value));
switch (packet[2]) {
case AVRCP_SUBEVENT_GET_CAPABILITY_EVENT_ID:
avrcp_connection->notifications_supported_by_target |= (1 << avrcp_subevent_get_capability_event_id_get_event_id(packet));
break;
case AVRCP_SUBEVENT_GET_CAPABILITY_EVENT_ID_DONE:
           
printf("AVRCP Controller: supported notifications by target:\n");
for (event_id = (uint8_t) AVRCP_NOTIFICATION_EVENT_FIRST_INDEX; event_id < (uint8_t) AVRCP_NOTIFICATION_EVENT_LAST_INDEX; event_id++) {
printf("   - [%s] %s\n",
(avrcp_connection->notifications_supported_by_target & (1 << event_id)) != 0 ? "X" : " ",
avrcp_notification2str((avrcp_notification_event_id_t)event_id));
}
printf("\n\n");

// automatically enable notifications
avrcp_controller_enable_notification(avrcp_connection->avrcp_cid, AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED);
avrcp_controller_enable_notification(avrcp_connection->avrcp_cid, AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED);
avrcp_controller_enable_notification(avrcp_connection->avrcp_cid, AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED);

#ifdef ENABLE_AVRCP_COVER_ART
// image handles become invalid on player change, registe for notifications
avrcp_controller_enable_notification(a2dp_sink_demo_avrcp_connection.avrcp_cid, AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED);
// trigger cover art client connection
a2dp_sink_demo_cover_art_connect();
#endif
break;

case AVRCP_SUBEVENT_NOTIFICATION_STATE:
event_id = (avrcp_notification_event_id_t)avrcp_subevent_notification_state_get_event_id(packet);
printf("AVRCP Controller: %s notification registered\n", avrcp_notification2str(event_id));
break;

case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_POS_CHANGED:
printf("AVRCP Controller: Playback position changed, position %d ms\n", (unsigned int) avrcp_subevent_notification_playback_pos_changed_get_playback_position_ms(packet));
break;
case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED:
printf("AVRCP Controller: Playback status changed %s\n", avrcp_play_status2str(avrcp_subevent_notification_playback_status_changed_get_play_status(packet)));
play_status = avrcp_subevent_notification_playback_status_changed_get_play_status(packet);
switch (play_status) {
case AVRCP_PLAYBACK_STATUS_PLAYING:
avrcp_connection->playing = true;
break;
default:
avrcp_connection->playing = false;
break;
}
break;

case AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED:
printf("AVRCP Controller: Playing content changed\n");
break;

case AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED:
printf("AVRCP Controller: Track changed\n");
break;

case AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED:
printf("AVRCP Controller: Available Players Changed\n");
break;

case AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE: {
uint8_t shuffle_mode = avrcp_subevent_shuffle_and_repeat_mode_get_shuffle_mode(packet);
uint8_t repeat_mode  = avrcp_subevent_shuffle_and_repeat_mode_get_repeat_mode(packet);
printf("AVRCP Controller: %s, %s\n", avrcp_shuffle2str(shuffle_mode), avrcp_repeat2str(repeat_mode));
break;
}
case AVRCP_SUBEVENT_NOW_PLAYING_TRACK_INFO:
printf("AVRCP Controller: Track %d\n", avrcp_subevent_now_playing_track_info_get_track(packet));
break;

case AVRCP_SUBEVENT_NOW_PLAYING_TOTAL_TRACKS_INFO:
printf("AVRCP Controller: Total Tracks %d\n", avrcp_subevent_now_playing_total_tracks_info_get_total_tracks(packet));
break;

case AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO:
if (avrcp_subevent_now_playing_title_info_get_value_len(packet) > 0) {
memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_title_info_get_value(packet), avrcp_subevent_now_playing_title_info_get_value_len(packet));
printf("AVRCP Controller: Title %s\n", avrcp_subevent_value);
}  
break;

case AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO:
if (avrcp_subevent_now_playing_artist_info_get_value_len(packet) > 0) {
memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_artist_info_get_value(packet), avrcp_subevent_now_playing_artist_info_get_value_len(packet));
printf("AVRCP Controller: Artist %s\n", avrcp_subevent_value);
}  
break;
       
case AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO:
if (avrcp_subevent_now_playing_album_info_get_value_len(packet) > 0) {
memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_album_info_get_value(packet), avrcp_subevent_now_playing_album_info_get_value_len(packet));
printf("AVRCP Controller: Album %s\n", avrcp_subevent_value);
}  
break;
       
case AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO:
if (avrcp_subevent_now_playing_genre_info_get_value_len(packet) > 0) {
memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_genre_info_get_value(packet), avrcp_subevent_now_playing_genre_info_get_value_len(packet));
printf("AVRCP Controller: Genre %s\n", avrcp_subevent_value);
}  
break;

case AVRCP_SUBEVENT_PLAY_STATUS:
printf("AVRCP Controller: Song length %"PRIu32" ms, Song position %"PRIu32" ms, Play status %s\n",
avrcp_subevent_play_status_get_song_length(packet),
avrcp_subevent_play_status_get_song_position(packet),
avrcp_play_status2str(avrcp_subevent_play_status_get_play_status(packet)));
break;
       
case AVRCP_SUBEVENT_OPERATION_COMPLETE:
printf("AVRCP Controller: %s complete\n", avrcp_operation2str(avrcp_subevent_operation_complete_get_operation_id(packet)));
break;
       
case AVRCP_SUBEVENT_OPERATION_START:
printf("AVRCP Controller: %s start\n", avrcp_operation2str(avrcp_subevent_operation_start_get_operation_id(packet)));
break;
       
case AVRCP_SUBEVENT_NOTIFICATION_EVENT_TRACK_REACHED_END:
printf("AVRCP Controller: Track reached end\n");
break;

case AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE:
printf("AVRCP Controller: Set Player App Value %s\n", avrcp_ctype2str(avrcp_subevent_player_application_value_response_get_command_type(packet)));
break;

#ifdef ENABLE_AVRCP_COVER_ART
case AVRCP_SUBEVENT_NOTIFICATION_EVENT_UIDS_CHANGED:
if (a2dp_sink_demo_cover_art_client_connected) {
printf("AVRCP Controller: UIDs changed -> disconnect cover art client\n");
avrcp_cover_art_client_disconnect(a2dp_sink_demo_cover_art_cid);
}
break;

case AVRCP_SUBEVENT_NOW_PLAYING_COVER_ART_INFO:
if (avrcp_subevent_now_playing_cover_art_info_get_value_len(packet) == 7) {
memcpy(a2dp_sink_demo_image_handle, avrcp_subevent_now_playing_cover_art_info_get_value(packet), 7);
printf("AVRCP Controller: Cover Art %s\n", a2dp_sink_demo_image_handle);
}
break;
#endif

default:
break;
}  
}



static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(channel);
UNUSED(size);

if (packet_type != HCI_EVENT_PACKET) return;
if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return;
   
uint8_t volume;
char const * button_state;
avrcp_operation_id_t operation_id;

switch (packet[2]) {
case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED:
volume = avrcp_subevent_notification_volume_changed_get_absolute_volume(packet);
volume_percentage = volume * 100 / 127;
printf("AVRCP Target    : Volume set to %d%% (%d)\n", volume_percentage, volume);
avrcp_volume_changed(volume);
break;
       
case AVRCP_SUBEVENT_OPERATION:
operation_id = avrcp_subevent_operation_get_operation_id(packet);
button_state = avrcp_subevent_operation_get_button_pressed(packet) > 0 ? "PRESS" : "RELEASE";
switch (operation_id) {
case AVRCP_OPERATION_ID_VOLUME_UP:
printf("AVRCP Target    : VOLUME UP (%s)\n", button_state);
break;
case AVRCP_OPERATION_ID_VOLUME_DOWN:
printf("AVRCP Target    : VOLUME DOWN (%s)\n", button_state);
break;
default:
return;
}
break;
default:
printf("AVRCP Target    : Event 0x%02x is not parsed\n", packet[2]);
break;
}
}

static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
UNUSED(channel);
UNUSED(size);
if (packet_type != HCI_EVENT_PACKET) return;
if (hci_event_packet_get_type(packet) == HCI_EVENT_PIN_CODE_REQUEST) {
bd_addr_t address;
printf("Pin code request - using '0000'\n");
hci_event_pin_code_request_get_bd_addr(packet, address);
gap_pin_code_response(address, "0000");
}
}



static void dump_sbc_configuration(media_codec_configuration_sbc_t * configuration) {
printf("    - num_channels: %d\n", configuration->num_channels);
printf("    - sampling_frequency: %d\n", configuration->sampling_frequency);
printf("    - channel_mode: %d\n", configuration->channel_mode);
printf("    - block_length: %d\n", configuration->block_length);
printf("    - subbands: %d\n", configuration->subbands);
printf("    - allocation_method: %d\n", configuration->allocation_method);
printf("    - bitpool_value [%d, %d] \n", configuration->min_bitpool_value, configuration->max_bitpool_value);
printf("\n");
}



static void media_processing_close(void) {
if (!media_initialized) return;

media_initialized = 0;
audio_stream_started = 0;
sbc_frame_size = 0;
#ifdef HAVE_BTSTACK_AUDIO_EFFECTIVE_SAMPLERATE
l2cap_stream_started = 0;
#endif

#ifdef STORE_TO_WAV_FILE                
wav_writer_close();
uint32_t total_frames_nr = sbc_decoder_context.good_frames_nr + sbc_decoder_context.bad_frames_nr + sbc_decoder_context.zero_frames_nr;

printf("WAV Writer: Decoding done. Processed %u SBC frames:\n - %d good\n - %d bad\n", total_frames_nr, sbc_decoder_context.good_frames_nr, total_frames_nr - sbc_decoder_context.good_frames_nr);
printf("WAV Writer: Wrote %u audio frames to wav file: %s\n", audio_frame_count, wav_filename);
#endif

// stop audio playback
const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance();
if (audio) {
printf("close stream\n");
audio->close();
}
}



static int media_processing_init(media_codec_configuration_sbc_t * configuration) {
if (media_initialized) return 0;
sbc_decoder_instance = btstack_sbc_decoder_bluedroid_init_instance(&sbc_decoder_context);
sbc_decoder_instance->configure(&sbc_decoder_context, SBC_MODE_STANDARD, handle_pcm_data, NULL);

#ifdef STORE_TO_WAV_FILE
wav_writer_open(wav_filename, configuration->num_channels, configuration->sampling_frequency);
#endif

btstack_ring_buffer_init(&sbc_frame_ring_buffer, sbc_frame_storage, sizeof(sbc_frame_storage));
btstack_ring_buffer_init(&decoded_audio_ring_buffer, decoded_audio_storage, sizeof(decoded_audio_storage));
btstack_resample_init(&resample_instance, configuration->num_channels);

// setup audio playback
const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance();
if (audio) {
audio->init(NUM_CHANNELS, configuration->sampling_frequency, &playback_handler);
}

audio_stream_started = 0;
media_initialized = 1;
return 0;
}


By as I sad it is compileing but have pairing issues. Your help will be appreciate, :-)

Matthias Ringwald

unread,
Apr 6, 2024, 3:11:56 PMApr 6
to btsta...@googlegroups.com
Hi Dawid

all BTstack API files (should) have C++ guards ( #if defined __cplusplus \n extern "C" { \n #endif )
If there's one missing, please let us know and we can add it.

Best
Matthias

Dawid Bańkowski

unread,
Apr 7, 2024, 7:13:27 AMApr 7
to btsta...@googlegroups.com
I found an error(s):
1) BT devices pairing issue. IESP-IDF created project have some lacks of configuration in sdkconfig file.
If example there is pointed that given bellow values on menuconfig should be configured:
Bluetooth disabled - please set CONFIG_BT_ENABLED via menuconfig -> Component Config -> Bluetooth -> [x] Bluetooth
CONFIG_BT_CONTROLLER_ONLY via menuconfig -> Component Config -> Bluetooth -> Host -> Disabled"
CONFIG_BT_CONTROLLER_ENABLED via menuconfig -> Component Config -> Bluetooth -> Controller -> Enabled"

but for good pairing there is something else. Just to check if I am right I have quickly copied to my project   sdkconfig from brstack example. Compiled example and it started to pair.

To Your knowledge in attachment I send example  sdkconfig and generated by ESP-IDF so You can compare it later I will do it by myself

2) ESP and C++ issue:
a) I have changed *,.c extension to *.cpp and compiler started to link function but
b) type casting of variables started to appear. 2 lines were corrected like bellow
      avrcp_notification2str((avrcp_notification_event_id_t)event_id));
     operation_id = (avrcp_operation_id_t) avrcp_subevent_operation_get_operation_id(packet) ;

and it started to compile. Of course there were problems with pairing which was corrected with steps written in point "1".

--- 
Pozdrowienia / Best regards
Dawid Bańkowski

“In the middle of difficulty lies opportunity.”  A. Einstein

View Dawid Bankowski's profile on LinkedIn


--
You received this message because you are subscribed to a topic in the Google Groups "btstack-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/btstack-dev/6BbIqHuICeI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to btstack-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/btstack-dev/155AE58E-DAD9-4CB7-8ECF-5C99C646FA24%40gmail.com.
sdkconfig-btstack
sdkconfig-ESP-ISF

Matthias Ringwald

unread,
Apr 8, 2024, 5:08:59 AMApr 8
to btsta...@googlegroups.com
Hi Dawid

Glad you got it working.

In the README for the ESP32, we have the following instructions

> 2. re-enable Bluetooth Controller via menuconfig
>
> 1. `idf.py menuconfig`
> 2. select `Component Config`
> 3. select `Bluetooth` and enable
> 4. select `Bluetooth Host`
> 5. select `Controller Only`
> 6. exit and save config


Could you check if that works for you?

Regarding this modifications:
- avrcp_notification2str((avrcp_notification_event_id_t)event_id));
- operation_id = (avrcp_operation_id_t) avrcp_subevent_operation_get_operation_id(packet) ;

Which line is this? Could you check if the fixes have been done on the develop branch?

Cheers
Matthias

Dawid Bańkowski

unread,
Apr 8, 2024, 6:50:12 AMApr 8
to btsta...@googlegroups.com
Those steps with menuconfig (1,2,3,4,5,6) where made by me. Without this btstack will not compile proprarely (component compile error will appear). But this is to few to ensure pairing. :/

Also there is some strange error (in the original example that every second/even pairing is performed) odd pairing are not shown in mobile phone (android) but I have to make some deeper investigation and give You feedback but first I have to sort out the pairing in my app.

Regarding to C++ type casting error:
 - avrcp_notification2str((avrcp_notification_event_id_t)event_id));
 - operation_id = (avrcp_operation_id_t) avrcp_subevent_operation_get_operation_id(packet);

The text marked on red is added by me to make corrections.
The original lines are:
printf("AVRCP Controller: %s notification registered\n", avrcp_notification2str(event_id)); - a2dp_sink_demo.c line #846; on line #827 You made correct type casting  :-)
operation_id = avrcp_subevent_operation_get_operation_id(packet);  - a2dp_sink_demo.c line #990


--- 
Pozdrowienia / Best regards
Dawid Bańkowski

“In the middle of difficulty lies opportunity.”  A. Einstein

View Dawid Bankowski's profile on LinkedIn

--
You received this message because you are subscribed to a topic in the Google Groups "btstack-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/btstack-dev/6BbIqHuICeI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to btstack-dev...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages