一、使用元件:
- VS Code
- ESP-IDF extension
四、Bluetooth classic Audio:
ESP32 Bluetooth 架構主要參考文件為下列官方網址:
https://www.espressif.com/sites/default/files/documentation/esp32_bluetooth_architecture_en.pdf
bluetooth audio 使用bluetooth classic A2DP and AVRCP因此只能使用Bluedroid,架構如下
https://www.espressif.com/sites/default/files/documentation/esp32_bluetooth_architecture_en.pdf
bluetooth audio 使用bluetooth classic A2DP and AVRCP因此只能使用Bluedroid,架構如下
memuconfig的bluetooth選項如下:
controller: init/enable --> bluedroid init/enable --> AVRCP register callback/init --> A2DP register cb --> A2DP sink register cb and data cb/init --> GAP register cb, security param, set device name and scan mode:詳細程式碼如文末所附程式碼(或成果展示影片的詳細步驟)。相關API參考網址:
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/bluetooth/classic_bt.html
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/bluetooth/classic_bt.html
五、ESP32 使用的GPIO Pins:
六、程式碼:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_bt.h"
#include "esp_bt_device.h"
#include "esp_bt_main.h"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "esp_gap_bt_api.h"
#include "string.h"
#include "nvs_flash.h"
#include "driver/i2s.h"
#include "driver/gpio.h"
#include "esp_log.h"
#define BUTTON_FORWARD_PIN 5
#define BUTTON_PLAY_PAUSE_PIN 23
#define BUTTON_BACKWARD_PIN 19
#define LED_RED_PIN 17
#define LED_GREEN_PIN 16
#define TAG "MY_BT_SPEAKER"
uint8_t my_bt_speaker_state=ESP_AVRC_PLAYBACK_STOPPED;
const char bt_device_name[]="MY_BT_SPEAKER";
bool avrc_conn=false;
static bool ct_button_play_pause_press=false;
static bool ct_button_backward_press=false;
static bool ct_button_forward_press=false;
void my_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) {
switch (event) {
case ESP_BT_GAP_DISC_RES_EVT:
break;
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
break;
case ESP_BT_GAP_RMT_SRVCS_EVT:
break;
case ESP_BT_GAP_RMT_SRVC_REC_EVT:
break;
case ESP_BT_GAP_AUTH_CMPL_EVT:
ESP_LOGI(TAG,"auth: state:%d, remote_name:%s", param->auth_cmpl.stat, param->auth_cmpl.device_name);
break;
case ESP_BT_GAP_PIN_REQ_EVT:
break;
case ESP_BT_GAP_CFM_REQ_EVT:
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
break;
case ESP_BT_GAP_KEY_NOTIF_EVT:
break;
case ESP_BT_GAP_MODE_CHG_EVT:
break;
default:
ESP_LOGI(TAG, "GAP EVENT ID:%d",event);
break;
}
}
void my_bt_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) {
switch(event) {
case ESP_A2D_CONNECTION_STATE_EVT:
if (param->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
gpio_set_level(LED_GREEN_PIN, 1);
gpio_set_level(LED_RED_PIN, 0);
}
if (param->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
gpio_set_level(LED_GREEN_PIN, 0);
gpio_set_level(LED_RED_PIN, 1);
}
break;
case ESP_A2D_AUDIO_STATE_EVT:
break;
case ESP_A2D_AUDIO_CFG_EVT:
if (param->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
int sample_rate = 16000;
char oct0 = param->audio_cfg.mcc.cie.sbc[0];
if (oct0 & (0x01 << 6)) {
sample_rate = 32000;
} else if (oct0 & (0x01 << 5)) {
sample_rate = 44100;
} else if (oct0 & (0x01 << 4)) {
sample_rate = 48000;
}
i2s_set_clk(0, sample_rate, 16, 2);
}
break;
case ESP_A2D_MEDIA_CTRL_ACK_EVT:
break;
case ESP_A2D_PROF_STATE_EVT:
break;
default:
break;
}
}
void my_bt_a2d_sink_data_cb(const uint8_t *buf, uint32_t len) {
size_t wb;
i2s_write(0, buf, len, &wb, portMAX_DELAY);
}
esp_avrc_rn_evt_cap_mask_t rn_cap_mask={0};
void my_bt_avrc_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) {
char *text;
switch(event) {
case ESP_AVRC_CT_CONNECTION_STATE_EVT:
if (param->conn_stat.connected) {
esp_avrc_ct_send_get_rn_capabilities_cmd(1);
avrc_conn=1;
esp_avrc_ct_send_register_notification_cmd(1, ESP_AVRC_RN_PLAY_STATUS_CHANGE, 0);
} else {
rn_cap_mask.bits=0;
avrc_conn=0;
}
break;
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
break;
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
switch(param->change_ntf.event_id) {
case ESP_AVRC_RN_VOLUME_CHANGE:
break;
case ESP_AVRC_RN_PLAY_STATUS_CHANGE:
my_bt_speaker_state = param->change_ntf.event_parameter.playback;
break;
default:
break;
}
break;
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT:
rn_cap_mask.bits = param->get_rn_caps_rsp.evt_set.bits;
break;
case ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT:
break;
case ESP_AVRC_CT_METADATA_RSP_EVT:
text = (char*)param->meta_rsp.attr_text;
text[param->meta_rsp.attr_length]='\0';
ESP_LOGI(TAG,"metadata_rsp:%d, %s, %d", param->meta_rsp.attr_id, text, param->meta_rsp.attr_length);
break;
default:
break;
}
}
static void ct_play_forward(void *param) {
if (!ct_button_forward_press) {
ct_button_forward_press=true;
}
}
static void ct_play_play_pause(void *param) {
if (!ct_button_play_pause_press) {
ct_button_play_pause_press=true;
}
}
static void ct_play_backward(void *param) {
if (!ct_button_backward_press) {
ct_button_backward_press=true;
}
}
void my_bt_set_gpio_pins() {
gpio_set_direction(BUTTON_BACKWARD_PIN, GPIO_MODE_INPUT);
gpio_set_direction(BUTTON_FORWARD_PIN, GPIO_MODE_INPUT);
gpio_set_direction(BUTTON_PLAY_PAUSE_PIN, GPIO_MODE_INPUT);
gpio_set_direction(LED_GREEN_PIN, GPIO_MODE_OUTPUT);
gpio_set_direction(LED_RED_PIN, GPIO_MODE_OUTPUT);
gpio_set_intr_type(BUTTON_BACKWARD_PIN, GPIO_INTR_POSEDGE);
gpio_set_intr_type(BUTTON_FORWARD_PIN, GPIO_INTR_POSEDGE);
gpio_set_intr_type(BUTTON_PLAY_PAUSE_PIN, GPIO_INTR_POSEDGE);
gpio_install_isr_service(ESP_INTR_FLAG_EDGE);
gpio_isr_handler_add(BUTTON_FORWARD_PIN, ct_play_forward, 0);
gpio_isr_handler_add(BUTTON_PLAY_PAUSE_PIN, ct_play_play_pause, 0);
gpio_isr_handler_add(BUTTON_BACKWARD_PIN, ct_play_backward, 0);
gpio_set_level(LED_GREEN_PIN,0);
gpio_set_level(LED_RED_PIN,1);
}
void app_main(void)
{
esp_err_t err;
err = nvs_flash_init();
if (err != ESP_OK) {
ESP_LOGI(TAG, "nvs init error");
return;
}
my_bt_set_gpio_pins();
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
.sample_rate = 44100,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
.communication_format = I2S_COMM_FORMAT_STAND_I2S, // MAX98357A I2S Philips
.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);
i2s_pin_config_t pin_config = {
.bck_io_num = 26,
.ws_io_num = 22,
.data_out_num = 25,
.data_in_num = -1 //Not used
};
i2s_set_pin(0, &pin_config);
esp_bt_controller_config_t bt_controller = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
err = esp_bt_controller_init(&bt_controller);
err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);
err= esp_bluedroid_init();
err = esp_bluedroid_enable();
err = esp_avrc_ct_register_callback(my_bt_avrc_cb);
err = esp_avrc_ct_init();
err = esp_a2d_register_callback(my_bt_a2d_cb);
err = esp_a2d_sink_register_data_callback(my_bt_a2d_sink_data_cb);
err = esp_a2d_sink_init();
err = esp_bt_gap_register_callback(my_bt_gap_cb);
err = esp_bt_dev_set_device_name(bt_device_name);
esp_bt_io_cap_t io_cap=ESP_BT_IO_CAP_IO;
err = esp_bt_gap_set_security_param(ESP_BT_SP_IOCAP_MODE, &io_cap, sizeof(io_cap));
err = esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
while(1) {
if (avrc_conn) {
vTaskDelay(5);
if (ct_button_backward_press) {
esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);
vTaskDelay(300/portTICK_PERIOD_MS);
//esp_avrc_ct_send_metadata_cmd(1, ESP_AVRC_MD_ATTR_TITLE);
ct_button_backward_press=false;
}
if (ct_button_forward_press) {
esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);
vTaskDelay(300/portTICK_PERIOD_MS);
//esp_avrc_ct_send_metadata_cmd(1, ESP_AVRC_MD_ATTR_TITLE);
ct_button_forward_press=false;
}
if (ct_button_play_pause_press) {
if (my_bt_speaker_state == ESP_AVRC_PLAYBACK_STOPPED || my_bt_speaker_state == ESP_AVRC_PLAYBACK_PAUSED) {
esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED);
my_bt_speaker_state = ESP_AVRC_PLAYBACK_PLAYING;
} else if (my_bt_speaker_state == ESP_AVRC_PLAYBACK_PLAYING) {
esp_avrc_ct_send_passthrough_cmd(1, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED);
my_bt_speaker_state = ESP_AVRC_PLAYBACK_PAUSED;
}
//esp_avrc_ct_send_metadata_cmd(1, ESP_AVRC_MD_ATTR_TITLE);
vTaskDelay(300/portTICK_PERIOD_MS);
ct_button_play_pause_press=false;
}
} else {
vTaskDelay(5);
}
}
}




沒有留言:
張貼留言