component/bt: fix the format and add more content to A2DP sink example document and source code

This commit is contained in:
wangmengyang 2018-09-12 10:44:58 +08:00
parent 35785d08fd
commit 2a5802241f
3 changed files with 79 additions and 23 deletions

View file

@ -5,6 +5,16 @@ Demo of A2DP audio sink role
This is the demo of API implementing Advanced Audio Distribution Profile to receive an audio stream.
This example involves the use of Bluetooth legacy profile A2DP for audio stream reception, AVRCP for media information notifications, and I2S for audio stream output interface.
Applications such as bluetooth speakers can take advantage of this example as a reference of basic functionalities.
## How to use this example
### Hardware Required
To play the sound, there is a need of loudspeaker and possibly an external I2S codec. Otherwise the example will only show a count of audio data packets received silently. Internal DAC can be selected and in this case external I2S codec may not be needed.
For the I2S codec, pick whatever chip or board works for you; this code was written using a PCM5102 chip, but other I2S boards and chips will probably work as well. The default I2S connections are shown below, but these can be changed in menuconfig:
| ESP pin | I2S signal |
@ -15,5 +25,51 @@ For the I2S codec, pick whatever chip or board works for you; this code was writ
If the internal DAC is selected, analog audio will be available on GPIO25 and GPIO26. The output resolution on these pins will always be limited to 8 bit because of the internal structure of the DACs.
### Configure the project
After the program is started, other bluetooth devices such as smart phones can discover a device named "ESP_SPEAKER". Once a connection is established, audio data can be transmitted. This will be visible in the application log including a count of audio data packets.
```
make menuconfig
```
* Set serial port under Serial Flasher Options.
* Set the use of external I2S codec or internal DAC for audio output, and configure the output PINs under A2DP Example Configuration
* Enable Classic Bluetooth and A2DP under Component config --> Bluetooth --> Bluedroid Enable
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output.
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
## Example Output
After the program is started, the example starts inquiry scan and page scan, awaiting being discovered and connected. Other bluetooth devices such as smart phones can discover a device named "ESP_SPEAKER". A smartphone or another ESP-IDF example of A2DP source can be used to connect to the local device.
Once A2DP connection is set up, there will be a notification message with the remote device's bluetooth MAC address like the following:
```
I (106427) BT_AV: A2DP connection state: Connected, [64:a2:f9:69:57:a4]
```
If a smartphone is used to connect to local device, starting to play music with an APP will result in the transmission of audio stream. The transmitting of audio stream will be visible in the application log including a count of audio data packets, like this:
```
I (120627) BT_AV: A2DP audio state: Started
I (122697) BT_AV: Audio packet count 100
I (124697) BT_AV: Audio packet count 200
I (126697) BT_AV: Audio packet count 300
I (128697) BT_AV: Audio packet count 400
```
Also, the sound will be heard if a loudspeaker is connected and possible external I2S codec is correctly configured. For ESP32 A2DP source example, the sound is noise as the audio source generates the samples with a random sequence.
## Troubleshooting
* For current stage, the single audio codec supported in ESP32 A2DP is SBC. SBC data stream is transmitted to A2DP sink and then decoded into PCM samples as output. The PCM data format is normally of 44.1kHz sampling rate, two-channel 16-bit sample stream. Other decoder configurations in ESP32 A2DP sink are supported but need additional modifications of protocol stack settings.
* As a usage limitation, ESP32 A2DP sink can support at most one connection with remote A2DP source devices. Also, A2DP sink cannot be used together with A2DP source at the same time, but can be used with other profiles such as SPP and HFP.

View file

@ -30,10 +30,10 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param);
/* avrc event handler */
static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param);
static uint32_t m_pkt_cnt = 0;
static esp_a2d_audio_state_t m_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
static const char *m_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
static const char *m_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"};
static uint32_t s_pkt_cnt = 0;
static esp_a2d_audio_state_t s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
static const char *s_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"};
/* callback for A2DP sink */
void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
@ -55,8 +55,8 @@ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
{
size_t bytes_written;
i2s_write(0, data, len, &bytes_written, portMAX_DELAY);
if (++m_pkt_cnt % 100 == 0) {
ESP_LOGI(BT_AV_TAG, "Audio packet count %u", m_pkt_cnt);
if (++s_pkt_cnt % 100 == 0) {
ESP_LOGI(BT_AV_TAG, "Audio packet count %u", s_pkt_cnt);
}
}
@ -97,7 +97,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
a2d = (esp_a2d_cb_param_t *)(p_param);
uint8_t *bda = a2d->conn_stat.remote_bda;
ESP_LOGI(BT_AV_TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]",
m_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
s_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){
@ -107,10 +107,10 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
}
case ESP_A2D_AUDIO_STATE_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", m_a2d_audio_state_str[a2d->audio_stat.state]);
m_audio_state = a2d->audio_stat.state;
ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", s_a2d_audio_state_str[a2d->audio_stat.state]);
s_audio_state = a2d->audio_stat.state;
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
m_pkt_cnt = 0;
s_pkt_cnt = 0;
}
break;
}

View file

@ -21,8 +21,8 @@ static void bt_app_task_handler(void *arg);
static bool bt_app_send_msg(bt_app_msg_t *msg);
static void bt_app_work_dispatched(bt_app_msg_t *msg);
static xQueueHandle bt_app_task_queue = NULL;
static xTaskHandle bt_app_task_handle = NULL;
static xQueueHandle s_bt_app_task_queue = NULL;
static xTaskHandle s_bt_app_task_handle = NULL;
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback)
{
@ -57,7 +57,7 @@ static bool bt_app_send_msg(bt_app_msg_t *msg)
return false;
}
if (xQueueSend(bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__);
return false;
}
@ -75,7 +75,7 @@ static void bt_app_task_handler(void *arg)
{
bt_app_msg_t msg;
for (;;) {
if (pdTRUE == xQueueReceive(bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) {
if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) {
ESP_LOGD(BT_APP_CORE_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event);
switch (msg.sig) {
case BT_APP_SIG_WORK_DISPATCH:
@ -95,19 +95,19 @@ static void bt_app_task_handler(void *arg)
void bt_app_task_start_up(void)
{
bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
xTaskCreate(bt_app_task_handler, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, &bt_app_task_handle);
s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
xTaskCreate(bt_app_task_handler, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle);
return;
}
void bt_app_task_shut_down(void)
{
if (bt_app_task_handle) {
vTaskDelete(bt_app_task_handle);
bt_app_task_handle = NULL;
if (s_bt_app_task_handle) {
vTaskDelete(s_bt_app_task_handle);
s_bt_app_task_handle = NULL;
}
if (bt_app_task_queue) {
vQueueDelete(bt_app_task_queue);
bt_app_task_queue = NULL;
if (s_bt_app_task_queue) {
vQueueDelete(s_bt_app_task_queue);
s_bt_app_task_queue = NULL;
}
}