diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.c b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.c index aa398cd4718d..f385801ed2ae 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.c +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.c @@ -1,4 +1,3 @@ - /* This example code is in the Public Domain (or CC0 licensed, at your option.) @@ -27,120 +26,237 @@ #include "sys/lock.h" -// AVRCP used transaction label +/* AVRCP used transaction labels */ #define APP_RC_CT_TL_GET_CAPS (0) #define APP_RC_CT_TL_GET_META_DATA (1) #define APP_RC_CT_TL_RN_TRACK_CHANGE (2) #define APP_RC_CT_TL_RN_PLAYBACK_CHANGE (3) #define APP_RC_CT_TL_RN_PLAY_POS_CHANGE (4) +/******************************* + * STATIC FUNCTION DECLARATIONS + ******************************/ + +/* allocate new meta buffer */ +static void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param); +/* handler for new track is loaded */ +static void bt_av_new_track(void); +/* handler for track status change */ +static void bt_av_playback_changed(void); +/* handler for track playing position change */ +static void bt_av_play_pos_changed(void); +/* notification event handler */ +static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter); +/* installation for i2s */ +static void bt_i2s_driver_install(void); +/* uninstallation for i2s */ +static void bt_i2s_driver_uninstall(void); +/* set volume by remote controller */ +static void volume_set_by_controller(uint8_t volume); +/* set volume by local host */ +static void volume_set_by_local_host(uint8_t volume); +/* simulation volume change */ +static void volume_change_simulation(void *arg); /* a2dp event handler */ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param); -/* avrc CT event handler */ +/* avrc controller event handler */ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param); -/* avrc TG event handler */ +/* avrc target event handler */ static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param); -static uint32_t s_pkt_cnt = 0; +/******************************* + * STATIC VARIABLE DEFINITIONS + ******************************/ + +static uint32_t s_pkt_cnt = 0; /* count for audio packet */ static esp_a2d_audio_state_t s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED; + /* audio stream datapath state */ static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"}; + /* connection state in string */ static const char *s_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"}; + /* audio stream datapath state in string */ static esp_avrc_rn_evt_cap_mask_t s_avrc_peer_rn_cap; + /* AVRC target notification capability bit mask */ static _lock_t s_volume_lock; -static xTaskHandle s_vcs_task_hdl = NULL; -static uint8_t s_volume = 0; -static bool s_volume_notify; +static xTaskHandle s_vcs_task_hdl = NULL; /* handle for volume change simulation task */ +static uint8_t s_volume = 0; /* local volume value */ +static bool s_volume_notify; /* notify volume change or not */ -/* callback for A2DP sink */ -void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +/******************************** + * STATIC FUNCTION DEFINITIONS + *******************************/ + +static void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param) { - switch (event) { - case ESP_A2D_CONNECTION_STATE_EVT: - case ESP_A2D_AUDIO_STATE_EVT: - case ESP_A2D_AUDIO_CFG_EVT: - case ESP_A2D_PROF_STATE_EVT: { - bt_app_work_dispatch(bt_av_hdl_a2d_evt, event, param, sizeof(esp_a2d_cb_param_t), NULL); - break; - } - default: - ESP_LOGE(BT_AV_TAG, "Invalid A2DP event: %d", event); - break; - } + esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param); + uint8_t *attr_text = (uint8_t *) malloc (rc->meta_rsp.attr_length + 1); + + memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length); + attr_text[rc->meta_rsp.attr_length] = 0; + rc->meta_rsp.attr_text = attr_text; } -void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len) +static void bt_av_new_track(void) { - write_ringbuf(data, len); - if (++s_pkt_cnt % 100 == 0) { - ESP_LOGI(BT_AV_TAG, "Audio packet count %u", s_pkt_cnt); + uint8_t attr_mask = ESP_AVRC_MD_ATTR_TITLE | + ESP_AVRC_MD_ATTR_ARTIST | + ESP_AVRC_MD_ATTR_ALBUM | + ESP_AVRC_MD_ATTR_GENRE; + esp_avrc_ct_send_metadata_cmd(APP_RC_CT_TL_GET_META_DATA, attr_mask); + + if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, + ESP_AVRC_RN_TRACK_CHANGE)) { + esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_TRACK_CHANGE, + ESP_AVRC_RN_TRACK_CHANGE, 0); } } -void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param) +static void bt_av_playback_changed(void) { - esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param); - uint8_t *attr_text = (uint8_t *) malloc (rc->meta_rsp.attr_length + 1); - memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length); - attr_text[rc->meta_rsp.attr_length] = 0; + if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, + ESP_AVRC_RN_PLAY_STATUS_CHANGE)) { + esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAYBACK_CHANGE, + ESP_AVRC_RN_PLAY_STATUS_CHANGE, 0); + } +} - rc->meta_rsp.attr_text = attr_text; +static void bt_av_play_pos_changed(void) +{ + if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, + ESP_AVRC_RN_PLAY_POS_CHANGED)) { + esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAY_POS_CHANGE, + ESP_AVRC_RN_PLAY_POS_CHANGED, 10); + } } -void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) +static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter) { - switch (event) { - case ESP_AVRC_CT_METADATA_RSP_EVT: - bt_app_alloc_meta_buffer(param); - /* fall through */ - case ESP_AVRC_CT_CONNECTION_STATE_EVT: - case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: - case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: - case ESP_AVRC_CT_REMOTE_FEATURES_EVT: - case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: { - bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL); + switch (event_id) { + /* when new track is loaded, this event comes */ + case ESP_AVRC_RN_TRACK_CHANGE: + bt_av_new_track(); break; - } + /* when track status changed, this event comes */ + case ESP_AVRC_RN_PLAY_STATUS_CHANGE: + ESP_LOGI(BT_AV_TAG, "Playback status changed: 0x%x", event_parameter->playback); + bt_av_playback_changed(); + break; + /* when track playing position changed, this event comes */ + case ESP_AVRC_RN_PLAY_POS_CHANGED: + ESP_LOGI(BT_AV_TAG, "Play position changed: %d-ms", event_parameter->play_pos); + bt_av_play_pos_changed(); + break; + /* others */ default: - ESP_LOGE(BT_RC_CT_TAG, "Invalid AVRC event: %d", event); + ESP_LOGI(BT_AV_TAG, "unhandled event: %d", event_id); break; } } -void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param) +void bt_i2s_driver_install(void) { - switch (event) { - case ESP_AVRC_TG_CONNECTION_STATE_EVT: - case ESP_AVRC_TG_REMOTE_FEATURES_EVT: - case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: - case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: - case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: - bt_app_work_dispatch(bt_av_hdl_avrc_tg_evt, event, param, sizeof(esp_avrc_tg_cb_param_t), NULL); - break; - default: - ESP_LOGE(BT_RC_TG_TAG, "Invalid AVRC event: %d", event); - break; + /* I2S configuration parameters */ + i2s_config_t i2s_config = { +#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC + .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, +#else + .mode = I2S_MODE_MASTER | I2S_MODE_TX, /* only TX */ +#endif + .sample_rate = 44100, + .bits_per_sample = 16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, /* 2-channels */ + .communication_format = I2S_COMM_FORMAT_STAND_MSB, + .dma_buf_count = 6, + .dma_buf_len = 60, + .intr_alloc_flags = 0, /* default interrupt priority */ + .tx_desc_auto_clear = true /* auto clear tx descriptor on underflow */ + }; + + /* enable I2S */ + i2s_driver_install(0, &i2s_config, 0, NULL); +#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC + i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); + i2s_set_pin(0, NULL); +#else + i2s_pin_config_t pin_config = { + .bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN, + .ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN, + .data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN, + .data_in_num = -1 /* not used */ + }; + i2s_set_pin(0, &pin_config); +#endif +} + +void bt_i2s_driver_uninstall(void) +{ + i2s_driver_uninstall(0); +} + +static void volume_set_by_controller(uint8_t volume) +{ + ESP_LOGI(BT_RC_TG_TAG, "Volume is set by remote controller to: %d%%", (uint32_t)volume * 100 / 0x7f); + /* set the volume in protection of lock */ + _lock_acquire(&s_volume_lock); + s_volume = volume; + _lock_release(&s_volume_lock); +} + +static void volume_set_by_local_host(uint8_t volume) +{ + ESP_LOGI(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f); + /* set the volume in protection of lock */ + _lock_acquire(&s_volume_lock); + s_volume = volume; + _lock_release(&s_volume_lock); + + /* send notification response to remote AVRCP controller */ + if (s_volume_notify) { + esp_avrc_rn_param_t rn_param; + rn_param.volume = s_volume; + esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_CHANGED, &rn_param); + s_volume_notify = false; + } +} + +static void volume_change_simulation(void *arg) +{ + ESP_LOGI(BT_RC_TG_TAG, "start volume change simulation"); + + for (;;) { + /* volume up locally every 10 seconds */ + vTaskDelay(10000 / portTICK_RATE_MS); + uint8_t volume = (s_volume + 5) & 0x7f; + volume_set_by_local_host(volume); } } static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param) { - ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event); + ESP_LOGD(BT_AV_TAG, "%s event: %d", __func__, event); + esp_a2d_cb_param_t *a2d = NULL; + switch (event) { + /* when connection state changed, this event comes */ case ESP_A2D_CONNECTION_STATE_EVT: { 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]", - s_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_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + bt_i2s_driver_uninstall(); bt_i2s_task_shut_down(); } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){ esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); bt_i2s_task_start_up(); + } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTING) { + bt_i2s_driver_install(); } break; } + /* when audio stream transmission state changed, this event comes */ case ESP_A2D_AUDIO_STATE_EVT: { a2d = (esp_a2d_cb_param_t *)(p_param); ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", s_a2d_audio_state_str[a2d->audio_stat.state]); @@ -150,12 +266,14 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param) } break; } + /* when audio codec is configured, this event comes */ case ESP_A2D_AUDIO_CFG_EVT: { a2d = (esp_a2d_cb_param_t *)(p_param); - ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type %d", a2d->audio_cfg.mcc.type); - // for now only SBC stream is supported + ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type: %d", a2d->audio_cfg.mcc.type); + /* for now only SBC stream is supported */ if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) { int sample_rate = 16000; + int ch_count = 2; char oct0 = a2d->audio_cfg.mcc.cie.sbc[0]; if (oct0 & (0x01 << 6)) { sample_rate = 32000; @@ -164,115 +282,81 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param) } else if (oct0 & (0x01 << 4)) { sample_rate = 48000; } - i2s_set_clk(0, sample_rate, 16, 2); + if (oct0 & (0x01 << 3)) { + ch_count = 1; + } + + i2s_set_clk(0, sample_rate, 16, ch_count); - ESP_LOGI(BT_AV_TAG, "Configure audio player %x-%x-%x-%x", + ESP_LOGI(BT_AV_TAG, "Configure audio player: %x-%x-%x-%x", a2d->audio_cfg.mcc.cie.sbc[0], a2d->audio_cfg.mcc.cie.sbc[1], a2d->audio_cfg.mcc.cie.sbc[2], a2d->audio_cfg.mcc.cie.sbc[3]); - ESP_LOGI(BT_AV_TAG, "Audio player configured, sample rate=%d", sample_rate); + ESP_LOGI(BT_AV_TAG, "Audio player configured, sample rate: %d", sample_rate); } break; } + /* when a2dp init or deinit completed, this event comes */ case ESP_A2D_PROF_STATE_EVT: { a2d = (esp_a2d_cb_param_t *)(p_param); if (ESP_A2D_INIT_SUCCESS == a2d->a2d_prof_stat.init_state) { - ESP_LOGI(BT_AV_TAG,"A2DP PROF STATE: Init Compl\n"); + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Init Complete"); } else { - ESP_LOGI(BT_AV_TAG,"A2DP PROF STATE: Deinit Compl\n"); + ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete"); } break; } + /* others */ default: - ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); - break; - } -} - -static void bt_av_new_track(void) -{ - // request metadata - uint8_t attr_mask = ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_GENRE; - esp_avrc_ct_send_metadata_cmd(APP_RC_CT_TL_GET_META_DATA, attr_mask); - - // register notification if peer support the event_id - if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, - ESP_AVRC_RN_TRACK_CHANGE)) { - esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_TRACK_CHANGE, ESP_AVRC_RN_TRACK_CHANGE, 0); - } -} - -static void bt_av_playback_changed(void) -{ - if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, - ESP_AVRC_RN_PLAY_STATUS_CHANGE)) { - esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAYBACK_CHANGE, ESP_AVRC_RN_PLAY_STATUS_CHANGE, 0); - } -} - -static void bt_av_play_pos_changed(void) -{ - if (esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_TEST, &s_avrc_peer_rn_cap, - ESP_AVRC_RN_PLAY_POS_CHANGED)) { - esp_avrc_ct_send_register_notification_cmd(APP_RC_CT_TL_RN_PLAY_POS_CHANGE, ESP_AVRC_RN_PLAY_POS_CHANGED, 10); - } -} - -void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter) -{ - switch (event_id) { - case ESP_AVRC_RN_TRACK_CHANGE: - bt_av_new_track(); - break; - case ESP_AVRC_RN_PLAY_STATUS_CHANGE: - ESP_LOGI(BT_AV_TAG, "Playback status changed: 0x%x", event_parameter->playback); - bt_av_playback_changed(); - break; - case ESP_AVRC_RN_PLAY_POS_CHANGED: - ESP_LOGI(BT_AV_TAG, "Play position changed: %d-ms", event_parameter->play_pos); - bt_av_play_pos_changed(); + ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event); break; } } static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param) { - ESP_LOGD(BT_RC_CT_TAG, "%s evt %d", __func__, event); + ESP_LOGD(BT_RC_CT_TAG, "%s event: %d", __func__, event); + esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(p_param); + switch (event) { + /* when connection state changed, this event comes */ case ESP_AVRC_CT_CONNECTION_STATE_EVT: { uint8_t *bda = rc->conn_stat.remote_bda; - ESP_LOGI(BT_RC_CT_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]", + ESP_LOGI(BT_RC_CT_TAG, "AVRC conn_state event: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]", rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); if (rc->conn_stat.connected) { - // get remote supported event_ids of peer AVRCP Target esp_avrc_ct_send_get_rn_capabilities_cmd(APP_RC_CT_TL_GET_CAPS); } else { - // clear peer notification capability record s_avrc_peer_rn_cap.bits = 0; } break; } + /* when passthrough responsed, this event comes */ case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: { ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state); break; } + /* when metadata responsed, this event comes */ case ESP_AVRC_CT_METADATA_RSP_EVT: { ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text); free(rc->meta_rsp.attr_text); break; } + /* when notified, this event comes */ case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: { ESP_LOGI(BT_RC_CT_TAG, "AVRC event notification: %d", rc->change_ntf.event_id); bt_av_notify_evt_handler(rc->change_ntf.event_id, &rc->change_ntf.event_parameter); break; } + /* when feature of remote device indicated, this event comes */ case ESP_AVRC_CT_REMOTE_FEATURES_EVT: { ESP_LOGI(BT_RC_CT_TAG, "AVRC remote features %x, TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag); break; } + /* when notification capability of peer device got, this event comes */ case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: { ESP_LOGI(BT_RC_CT_TAG, "remote rn_cap: count %d, bitmask 0x%x", rc->get_rn_caps_rsp.cap_count, rc->get_rn_caps_rsp.evt_set.bits); @@ -282,74 +366,46 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param) bt_av_play_pos_changed(); break; } + /* others */ default: - ESP_LOGE(BT_RC_CT_TAG, "%s unhandled evt %d", __func__, event); + ESP_LOGE(BT_RC_CT_TAG, "%s unhandled event: %d", __func__, event); break; } } -static void volume_set_by_controller(uint8_t volume) -{ - ESP_LOGI(BT_RC_TG_TAG, "Volume is set by remote controller %d%%\n", (uint32_t)volume * 100 / 0x7f); - _lock_acquire(&s_volume_lock); - s_volume = volume; - _lock_release(&s_volume_lock); -} - -static void volume_set_by_local_host(uint8_t volume) -{ - ESP_LOGI(BT_RC_TG_TAG, "Volume is set locally to: %d%%", (uint32_t)volume * 100 / 0x7f); - _lock_acquire(&s_volume_lock); - s_volume = volume; - _lock_release(&s_volume_lock); - - if (s_volume_notify) { - esp_avrc_rn_param_t rn_param; - rn_param.volume = s_volume; - esp_avrc_tg_send_rn_rsp(ESP_AVRC_RN_VOLUME_CHANGE, ESP_AVRC_RN_RSP_CHANGED, &rn_param); - s_volume_notify = false; - } -} - -static void volume_change_simulation(void *arg) -{ - ESP_LOGI(BT_RC_TG_TAG, "start volume change simulation"); - - for (;;) { - vTaskDelay(10000 / portTICK_RATE_MS); - - uint8_t volume = (s_volume + 5) & 0x7f; - volume_set_by_local_host(volume); - } -} - static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param) { - ESP_LOGD(BT_RC_TG_TAG, "%s evt %d", __func__, event); + ESP_LOGD(BT_RC_TG_TAG, "%s event: %d", __func__, event); + esp_avrc_tg_cb_param_t *rc = (esp_avrc_tg_cb_param_t *)(p_param); + switch (event) { + /* when connection state changed, this event comes */ case ESP_AVRC_TG_CONNECTION_STATE_EVT: { uint8_t *bda = rc->conn_stat.remote_bda; ESP_LOGI(BT_RC_TG_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]", rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); if (rc->conn_stat.connected) { - // create task to simulate volume change - xTaskCreate(volume_change_simulation, "vcsT", 2048, NULL, 5, &s_vcs_task_hdl); + /* create task to simulate volume change */ + xTaskCreate(volume_change_simulation, "vcsTask", 2048, NULL, 5, &s_vcs_task_hdl); } else { vTaskDelete(s_vcs_task_hdl); ESP_LOGI(BT_RC_TG_TAG, "Stop volume change simulation"); } break; } + /* when passthrough commanded, this event comes */ case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: { ESP_LOGI(BT_RC_TG_TAG, "AVRC passthrough cmd: key_code 0x%x, key_state %d", rc->psth_cmd.key_code, rc->psth_cmd.key_state); break; } + /* when absolute volume command from remote device set, this event comes */ case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: { - ESP_LOGI(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100/ 0x7f); + ESP_LOGI(BT_RC_TG_TAG, "AVRC set absolute volume: %d%%", (int)rc->set_abs_vol.volume * 100 / 0x7f); volume_set_by_controller(rc->set_abs_vol.volume); break; } + /* when notification registered, this event comes */ case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: { ESP_LOGI(BT_RC_TG_TAG, "AVRC register event notification: %d, param: 0x%x", rc->reg_ntf.event_id, rc->reg_ntf.event_parameter); if (rc->reg_ntf.event_id == ESP_AVRC_RN_VOLUME_CHANGE) { @@ -360,12 +416,81 @@ static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param) } break; } + /* when feature of remote device indicated, this event comes */ case ESP_AVRC_TG_REMOTE_FEATURES_EVT: { - ESP_LOGI(BT_RC_TG_TAG, "AVRC remote features %x, CT features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.ct_feat_flag); + ESP_LOGI(BT_RC_TG_TAG, "AVRC remote features: %x, CT features: %x", rc->rmt_feats.feat_mask, rc->rmt_feats.ct_feat_flag); break; } + /* others */ default: - ESP_LOGE(BT_RC_TG_TAG, "%s unhandled evt %d", __func__, event); + ESP_LOGE(BT_RC_TG_TAG, "%s unhandled event: %d", __func__, event); + break; + } +} + +/******************************** + * EXTERNAL FUNCTION DEFINITIONS + *******************************/ + +void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: + case ESP_A2D_AUDIO_STATE_EVT: + case ESP_A2D_AUDIO_CFG_EVT: + case ESP_A2D_PROF_STATE_EVT: { + bt_app_work_dispatch(bt_av_hdl_a2d_evt, event, param, sizeof(esp_a2d_cb_param_t), NULL); + break; + } + default: + ESP_LOGE(BT_AV_TAG, "Invalid A2DP event: %d", event); + break; + } +} + +void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len) +{ + write_ringbuf(data, len); + + /* log the number every 100 packets */ + if (++s_pkt_cnt % 100 == 0) { + ESP_LOGI(BT_AV_TAG, "Audio packet count: %u", s_pkt_cnt); + } +} + +void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) +{ + switch (event) { + case ESP_AVRC_CT_METADATA_RSP_EVT: + bt_app_alloc_meta_buffer(param); + /* fall through */ + case ESP_AVRC_CT_CONNECTION_STATE_EVT: + case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: + case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: + case ESP_AVRC_CT_REMOTE_FEATURES_EVT: + case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: { + bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL); + break; + } + default: + ESP_LOGE(BT_RC_CT_TAG, "Invalid AVRC event: %d", event); + break; + } +} + +void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param) +{ + switch (event) { + case ESP_AVRC_TG_CONNECTION_STATE_EVT: + case ESP_AVRC_TG_REMOTE_FEATURES_EVT: + case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT: + case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT: + case ESP_AVRC_TG_REGISTER_NOTIFICATION_EVT: + case ESP_AVRC_TG_SET_PLAYER_APP_VALUE_EVT: + bt_app_work_dispatch(bt_av_hdl_avrc_tg_evt, event, param, sizeof(esp_avrc_tg_cb_param_t), NULL); + break; + default: + ESP_LOGE(BT_RC_TG_TAG, "Invalid AVRC event: %d", event); break; } } diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.h b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.h index 46342daadba2..d23e71ed93cf 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.h +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_av.h @@ -13,27 +13,40 @@ #include "esp_a2dp_api.h" #include "esp_avrc_api.h" -#define BT_AV_TAG "BT_AV" -#define BT_RC_TG_TAG "RCTG" -#define BT_RC_CT_TAG "RCCT" +/* log tags */ +#define BT_AV_TAG "BT_AV" +#define BT_RC_TG_TAG "RC_TG" +#define BT_RC_CT_TAG "RC_CT" /** - * @brief callback function for A2DP sink + * @brief callback function for A2DP sink + * + * @param [in] event event id + * @param [in] param callback parameter */ void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); /** - * @brief callback function for A2DP sink audio data stream + * @brief callback function for A2DP sink audio data stream + * + * @param [out] data data stream writteen by application task + * @param [in] len length of data stream in byte */ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len); /** - * @brief callback function for AVRCP controller + * @brief callback function for AVRCP controller + * + * @param [in] event event id + * @param [in] param callback parameter */ void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param); /** - * @brief callback function for AVRCP target + * @brief callback function for AVRCP target + * + * @param [in] event event id + * @param [in] param callback parameter */ void bt_app_rc_tg_cb(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param); diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_core.c b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_core.c index 559402cc3efd..6baa1d922dc8 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_core.c +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_core.c @@ -13,47 +13,50 @@ #include "freertos/FreeRTOSConfig.h" #include "freertos/FreeRTOS.h" #include "freertos/queue.h" +#include "freertos/semphr.h" #include "freertos/task.h" #include "esp_log.h" #include "bt_app_core.h" #include "driver/i2s.h" #include "freertos/ringbuf.h" -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 s_bt_app_task_queue = NULL; -static xTaskHandle s_bt_app_task_handle = NULL; -static xTaskHandle s_bt_i2s_task_handle = NULL; -static RingbufHandle_t s_ringbuf_i2s = NULL;; +#define RINGBUF_HIGHEST_WATER_LEVEL (32 * 1024) +#define RINGBUF_PREFETCH_WATER_LEVEL (20 * 1024) -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) -{ - ESP_LOGD(BT_APP_CORE_TAG, "%s event 0x%x, param len %d", __func__, event, param_len); +enum { + RINGBUFFER_MODE_PROCESSING, /* ringbuffer is buffering incoming audio data, I2S is working */ + RINGBUFFER_MODE_PREFETCHING, /* ringbuffer is buffering incoming audio data, I2S is waiting */ + RINGBUFFER_MODE_DROPPING /* ringbuffer is not buffering (dropping) incoming audio data, I2S is working */ +}; - bt_app_msg_t msg; - memset(&msg, 0, sizeof(bt_app_msg_t)); +/******************************* + * STATIC FUNCTION DECLARATIONS + ******************************/ - msg.sig = BT_APP_SIG_WORK_DISPATCH; - msg.event = event; - msg.cb = p_cback; +/* handler for application task */ +static void bt_app_task_handler(void *arg); +/* handler for I2S task */ +static void bt_i2s_task_handler(void *arg); +/* message sender */ +static bool bt_app_send_msg(bt_app_msg_t *msg); +/* handle dispatched messages */ +static void bt_app_work_dispatched(bt_app_msg_t *msg); - if (param_len == 0) { - return bt_app_send_msg(&msg); - } else if (p_params && param_len > 0) { - if ((msg.param = malloc(param_len)) != NULL) { - memcpy(msg.param, p_params, param_len); - /* check if caller has provided a copy callback to do the deep copy */ - if (p_copy_cback) { - p_copy_cback(&msg, msg.param, p_params); - } - return bt_app_send_msg(&msg); - } - } +/******************************* + * STATIC VARIABLE DEFINITIONS + ******************************/ - return false; -} +static xQueueHandle s_bt_app_task_queue = NULL; /* handle of work queue */ +static xTaskHandle s_bt_app_task_handle = NULL; /* handle of application task */ +static xTaskHandle s_bt_i2s_task_handle = NULL; /* handle of I2S task */ +static RingbufHandle_t s_ringbuf_i2s = NULL; /* handle of ringbuffer for I2S */ +static SemaphoreHandle_t s_i2s_write_semaphore = NULL; +static uint16_t ringbuffer_mode = RINGBUFFER_MODE_PROCESSING; + +/******************************* + * STATIC FUNCTION DEFINITIONS + ******************************/ static bool bt_app_send_msg(bt_app_msg_t *msg) { @@ -78,17 +81,20 @@ static void bt_app_work_dispatched(bt_app_msg_t *msg) static void bt_app_task_handler(void *arg) { bt_app_msg_t msg; + for (;;) { + /* receive message from work queue and handle it */ 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); + ESP_LOGD(BT_APP_CORE_TAG, "%s, signal: 0x%x, event: 0x%x", __func__, msg.sig, msg.event); + switch (msg.sig) { case BT_APP_SIG_WORK_DISPATCH: bt_app_work_dispatched(&msg); break; default: - ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: %d", __func__, msg.sig); + ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled signal: %d", __func__, msg.sig); break; - } // switch (msg.sig) + } /* switch (msg.sig) */ if (msg.param) { free(msg.param); @@ -97,11 +103,71 @@ static void bt_app_task_handler(void *arg) } } +static void bt_i2s_task_handler(void *arg) +{ + uint8_t *data = NULL; + size_t item_size = 0; + /** + * The total length of DMA buffer of I2S is: + * `dma_frame_num * dma_desc_num * i2s_channel_num * i2s_data_bit_width / 8`. + * Transmit `dma_frame_num * dma_desc_num` bytes to DMA is trade-off. + */ + const size_t item_size_upto = 60 * 6; + size_t bytes_written = 0; + + for (;;) { + if (pdTRUE == xSemaphoreTake(s_i2s_write_semaphore, portMAX_DELAY)) { + for (;;) { + item_size = 0; + /* receive data from ringbuffer and write it to I2S DMA transmit buffer */ + data = (uint8_t *)xRingbufferReceiveUpTo(s_ringbuf_i2s, &item_size, (TickType_t)pdMS_TO_TICKS(5), item_size_upto); + if (item_size == 0) { + ESP_LOGI(BT_APP_CORE_TAG, "ringbuffer underflowed! mode changed: RINGBUFFER_MODE_PREFETCHING"); + ringbuffer_mode = RINGBUFFER_MODE_PREFETCHING; + break; + } + i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY); + vRingbufferReturnItem(s_ringbuf_i2s, (void *)data); + } + } + } +} + +/******************************** + * EXTERNAL FUNCTION DEFINITIONS + *******************************/ + +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) +{ + ESP_LOGD(BT_APP_CORE_TAG, "%s event: 0x%x, param len: %d", __func__, event, param_len); + + bt_app_msg_t msg; + memset(&msg, 0, sizeof(bt_app_msg_t)); + + msg.sig = BT_APP_SIG_WORK_DISPATCH; + msg.event = event; + msg.cb = p_cback; + + if (param_len == 0) { + return bt_app_send_msg(&msg); + } else if (p_params && param_len > 0) { + if ((msg.param = malloc(param_len)) != NULL) { + memcpy(msg.param, p_params, param_len); + /* check if caller has provided a copy callback to do the deep copy */ + if (p_copy_cback) { + p_copy_cback(msg.param, p_params, param_len); + } + return bt_app_send_msg(&msg); + } + } + + return false; +} + void bt_app_task_start_up(void) { s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t)); - xTaskCreate(bt_app_task_handler, "BtAppT", 3072, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle); - return; + xTaskCreate(bt_app_task_handler, "BtAppTask", 3072, NULL, 10, &s_bt_app_task_handle); } void bt_app_task_shut_down(void) @@ -116,30 +182,19 @@ void bt_app_task_shut_down(void) } } -static void bt_i2s_task_handler(void *arg) -{ - uint8_t *data = NULL; - size_t item_size = 0; - size_t bytes_written = 0; - - for (;;) { - data = (uint8_t *)xRingbufferReceive(s_ringbuf_i2s, &item_size, (portTickType)portMAX_DELAY); - if (item_size != 0){ - i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY); - vRingbufferReturnItem(s_ringbuf_i2s,(void *)data); - } - } -} - void bt_i2s_task_start_up(void) { - s_ringbuf_i2s = xRingbufferCreate(8 * 1024, RINGBUF_TYPE_BYTEBUF); - if(s_ringbuf_i2s == NULL){ + ESP_LOGI(BT_APP_CORE_TAG, "ringbuffer data empty! mode changed: RINGBUFFER_MODE_PREFETCHING"); + ringbuffer_mode = RINGBUFFER_MODE_PREFETCHING; + if ((s_i2s_write_semaphore = xSemaphoreCreateBinary()) == NULL) { + ESP_LOGE(BT_APP_CORE_TAG, "%s, Semaphore create failed", __func__); return; } - - xTaskCreate(bt_i2s_task_handler, "BtI2ST", 1024, NULL, configMAX_PRIORITIES - 3, &s_bt_i2s_task_handle); - return; + if ((s_ringbuf_i2s = xRingbufferCreate(RINGBUF_HIGHEST_WATER_LEVEL, RINGBUF_TYPE_BYTEBUF)) == NULL) { + ESP_LOGE(BT_APP_CORE_TAG, "%s, ringbuffer create failed", __func__); + return; + } + xTaskCreate(bt_i2s_task_handler, "BtI2STask", 2048, NULL, configMAX_PRIORITIES - 3, &s_bt_i2s_task_handle); } void bt_i2s_task_shut_down(void) @@ -148,19 +203,48 @@ void bt_i2s_task_shut_down(void) vTaskDelete(s_bt_i2s_task_handle); s_bt_i2s_task_handle = NULL; } - if (s_ringbuf_i2s) { vRingbufferDelete(s_ringbuf_i2s); s_ringbuf_i2s = NULL; } + if (s_i2s_write_semaphore) { + vSemaphoreDelete(s_i2s_write_semaphore); + s_i2s_write_semaphore = NULL; + } } size_t write_ringbuf(const uint8_t *data, size_t size) { - BaseType_t done = xRingbufferSend(s_ringbuf_i2s, (void *)data, size, (portTickType)portMAX_DELAY); - if(done){ - return size; - } else { + size_t item_size = 0; + BaseType_t done = pdFALSE; + + if (ringbuffer_mode == RINGBUFFER_MODE_DROPPING) { + ESP_LOGW(BT_APP_CORE_TAG, "ringbuffer is full, drop this packet!"); + vRingbufferGetInfo(s_ringbuf_i2s, NULL, NULL, NULL, NULL, &item_size); + if (item_size <= RINGBUF_PREFETCH_WATER_LEVEL) { + ESP_LOGI(BT_APP_CORE_TAG, "ringbuffer data decreased! mode changed: RINGBUFFER_MODE_PROCESSING"); + ringbuffer_mode = RINGBUFFER_MODE_PROCESSING; + } return 0; } + + done = xRingbufferSend(s_ringbuf_i2s, (void *)data, size, (TickType_t)0); + + if (!done) { + ESP_LOGW(BT_APP_CORE_TAG, "ringbuffer overflowed, ready to decrease data! mode changed: RINGBUFFER_MODE_DROPPING"); + ringbuffer_mode = RINGBUFFER_MODE_DROPPING; + } + + if (ringbuffer_mode == RINGBUFFER_MODE_PREFETCHING) { + vRingbufferGetInfo(s_ringbuf_i2s, NULL, NULL, NULL, NULL, &item_size); + if (item_size >= RINGBUF_PREFETCH_WATER_LEVEL) { + ESP_LOGI(BT_APP_CORE_TAG, "ringbuffer data increased! mode changed: RINGBUFFER_MODE_PROCESSING"); + ringbuffer_mode = RINGBUFFER_MODE_PROCESSING; + if (pdFALSE == xSemaphoreGive(s_i2s_write_semaphore)) { + ESP_LOGE(BT_APP_CORE_TAG, "semphore give failed"); + } + } + } + + return done ? size : 0; } diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_core.h b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_core.h index eaa390de946b..b5534108ce7d 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_core.h +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/bt_app_core.h @@ -13,41 +13,78 @@ #include #include -#define BT_APP_CORE_TAG "BT_APP_CORE" +/* log tag */ +#define BT_APP_CORE_TAG "BT_APP_CORE" -#define BT_APP_SIG_WORK_DISPATCH (0x01) +/* signal for `bt_app_work_dispatch` */ +#define BT_APP_SIG_WORK_DISPATCH (0x01) /** - * @brief handler for the dispatched work + * @brief handler for the dispatched work + * + * @param [in] event event id + * @param [in] param handler parameter */ typedef void (* bt_app_cb_t) (uint16_t event, void *param); /* message to be sent */ typedef struct { - uint16_t sig; /*!< signal to bt_app_task */ - uint16_t event; /*!< message event id */ - bt_app_cb_t cb; /*!< context switch callback */ - void *param; /*!< parameter area needs to be last */ + uint16_t sig; /*!< signal to bt_app_task */ + uint16_t event; /*!< message event id */ + bt_app_cb_t cb; /*!< context switch callback */ + void *param; /*!< parameter area needs to be last */ } bt_app_msg_t; /** - * @brief parameter deep-copy function to be customized + * @brief parameter deep-copy function to be customized + * + * @param [out] p_dest pointer to destination data + * @param [in] p_src pointer to source data + * @param [in] len data length in byte */ -typedef void (* bt_app_copy_cb_t) (bt_app_msg_t *msg, void *p_dest, void *p_src); +typedef void (* bt_app_copy_cb_t) (void *p_dest, void *p_src, int len); /** - * @brief work dispatcher for the application task + * @brief work dispatcher for the application task + * + * @param [in] p_cback callback function + * @param [in] event event id + * @param [in] p_params callback paramters + * @param [in] param_len parameter length in byte + * @param [in] p_copy_cback parameter deep-copy function + * + * @return true if work dispatch successfully, false otherwise */ 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); +/** + * @brief start up the application task + */ void bt_app_task_start_up(void); +/** + * @brief shut down the application task + */ void bt_app_task_shut_down(void); +/** + * @brief start up the is task + */ void bt_i2s_task_start_up(void); +/** + * @brief shut down the I2S task + */ void bt_i2s_task_shut_down(void); +/** + * @brief write data to ringbuffer + * + * @param [in] data pointer to data stream + * @param [in] size data length in byte + * + * @return size if writteen ringbuffer successfully, 0 others + */ size_t write_ringbuf(const uint8_t *data, size_t size); #endif /* __BT_APP_CORE_H__ */ diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c index 82b3390f957b..fcde2ee49cdd 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c @@ -6,7 +6,7 @@ CONDITIONS OF ANY KIND, either express or implied. */ -/**************************************************************************** +/********************************************************************************************************************************** * The demo shows BLE and classic Bluetooth coexistence. You can use BLE GATT server and classic bluetooth A2DP together. * The BLE GATT server part of the demo creates a GATT service and then starts advertising, waiting to be connected by a GATT client. * After the program is started, a GATT client can discover the device named "ESP_COEX_BLE_DEMO". Once the connection is established, @@ -16,7 +16,7 @@ * After the program is started, other bluetooth devices such as smart phones can discover the device named "ESP_COEX_A2DP_DEMO". * Once the connection is established, audio data can be transmitted. This will be visible in the application log including a count * of audio data packets. -****************************************************************************/ +***********************************************************************************************************************************/ #include #include @@ -44,7 +44,9 @@ #include "esp_bt_defs.h" #include "esp_gatt_common_api.h" +/* log tag */ #define BT_BLE_COEX_TAG "BT_BLE_COEX" +/* device name */ #define BT_DEVICE_NAME "ESP_COEX_A2DP_DEMO" #define BLE_ADV_NAME "ESP_COEX_BLE_DEMO" @@ -64,6 +66,11 @@ #define PROFILE_A_APP_ID 0 #define PROFILE_B_APP_ID 1 +/* event for handler "bt_av_hdl_stack_up */ +enum { + BT_APP_EVT_STACK_UP = 0, +}; + typedef struct { uint8_t *prepare_buf; int prepare_len; @@ -78,10 +85,6 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); -/* event for handler "bt_av_hdl_stack_up */ -enum { - BT_APP_EVT_STACK_UP = 0, -}; static uint8_t ble_char_value_str[] = {0x11, 0x22, 0x33}; esp_gatt_char_prop_t a_property = 0; esp_gatt_char_prop_t b_property = 0; @@ -600,79 +603,90 @@ static void ble_gatts_init(void) } } -void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +/******************************* + * STATIC FUNCTION DEFINITIONS + ******************************/ + +static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) { switch (event) { + /* when authentication completed, this event comes */ case ESP_BT_GAP_AUTH_CMPL_EVT: { if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { ESP_LOGI(BT_BLE_COEX_TAG, "authentication success: %s", param->auth_cmpl.device_name); esp_log_buffer_hex(BT_BLE_COEX_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN); } else { - ESP_LOGE(BT_BLE_COEX_TAG, "authentication failed, status:%d", param->auth_cmpl.stat); + ESP_LOGE(BT_BLE_COEX_TAG, "authentication failed, status: %d", param->auth_cmpl.stat); } break; } + #if (CONFIG_BT_SSP_ENABLED == true) + /* when Security Simple Pairing user confirmation requested, this event comes */ case ESP_BT_GAP_CFM_REQ_EVT: ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); break; + /* when Security Simple Pairing passkey notified, this event comes */ case ESP_BT_GAP_KEY_NOTIF_EVT: - ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); + ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey: %d", param->key_notif.passkey); break; + /* when Security Simple Pairing passkey requested, this event comes */ case ESP_BT_GAP_KEY_REQ_EVT: ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); break; #endif + + /* when GAP mode changed, this event comes */ case ESP_BT_GAP_MODE_CHG_EVT: - ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode); + ESP_LOGI(BT_BLE_COEX_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode: %d", param->mode_chg.mode); break; - + /* others */ default: { ESP_LOGI(BT_BLE_COEX_TAG, "event: %d", event); break; } } - return; } -/* handler for bluetooth stack enabled events */ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) { - ESP_LOGD(BT_BLE_COEX_TAG, "%s evt %d", __func__, event); + ESP_LOGD(BT_BLE_COEX_TAG, "%s event: %d", __func__, event); + switch (event) { + /* when do the stack up, this event comes */ case BT_APP_EVT_STACK_UP: { - /* set up bt device name */ esp_bt_dev_set_device_name(BT_DEVICE_NAME); - esp_bt_gap_register_callback(bt_app_gap_cb); - /* initialize AVRCP controller */ - esp_avrc_ct_init(); + assert(esp_avrc_ct_init() == ESP_OK); esp_avrc_ct_register_callback(bt_app_rc_ct_cb); - /* initialize AVRCP target */ - assert (esp_avrc_tg_init() == ESP_OK); + assert(esp_avrc_tg_init() == ESP_OK); esp_avrc_tg_register_callback(bt_app_rc_tg_cb); esp_avrc_rn_evt_cap_mask_t evt_set = {0}; esp_avrc_rn_evt_bit_mask_operation(ESP_AVRC_BIT_MASK_OP_SET, &evt_set, ESP_AVRC_RN_VOLUME_CHANGE); assert(esp_avrc_tg_set_rn_evt_cap(&evt_set) == ESP_OK); - /* initialize A2DP sink */ + assert(esp_a2d_sink_init() == ESP_OK); esp_a2d_register_callback(&bt_app_a2d_cb); esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); - esp_a2d_sink_init(); /* set discoverable and connectable mode, wait to be connected */ esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); break; } + /* others */ default: - ESP_LOGE(BT_BLE_COEX_TAG, "%s unhandled evt %d", __func__, event); + ESP_LOGE(BT_BLE_COEX_TAG, "%s unhandled event: %d", __func__, event); break; } } +/******************************* + * MAIN ENTRY POINT + ******************************/ + void app_main(void) { /* Initialize NVS — it is used to store PHY calibration data */ @@ -683,38 +697,6 @@ void app_main(void) } ESP_ERROR_CHECK(err); - i2s_config_t i2s_config = { -#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC - .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, -#else - .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX -#endif - .communication_format = I2S_COMM_FORMAT_STAND_MSB, - .sample_rate = 44100, - .bits_per_sample = 16, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels - .dma_buf_count = 6, - .dma_buf_len = 60, - .intr_alloc_flags = 0, //Default interrupt priority - .tx_desc_auto_clear = true //Auto clear tx descriptor on underflow - }; - - - i2s_driver_install(0, &i2s_config, 0, NULL); -#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC - i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); - i2s_set_pin(0, NULL); -#else - i2s_pin_config_t pin_config = { - .bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN, - .ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN, - .data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN, - .data_in_num = -1 //Not used - }; - - i2s_set_pin(0, &pin_config); -#endif - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { ESP_LOGE(BT_BLE_COEX_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err)); @@ -736,12 +718,6 @@ void app_main(void) return; } - /* create application task */ - bt_app_task_start_up(); - - /* Bluetooth device name, connection mode and profile set up */ - bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL); - #if (CONFIG_BT_SSP_ENABLED == true) /* Set default parameters for Secure Simple Pairing */ esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; @@ -749,10 +725,7 @@ void app_main(void) esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); #endif - /* - * Set default parameters for Legacy Pairing - * Use fixed pin code - */ + /* set default parameters for Legacy Pairing (use fixed pin code 1234) */ esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED; esp_bt_pin_code_t pin_code; pin_code[0] = '1'; @@ -761,6 +734,10 @@ void app_main(void) pin_code[3] = '4'; esp_bt_gap_set_pin(pin_type, 4, pin_code); + bt_app_task_start_up(); + /* bluetooth device name, connection mode and profile set up */ + bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL); + //gatt server init ble_gatts_init(); }