I finished making an iPod UART remote to Bluetooth A2DP adaptor using the YourCee AI Thinker ESP32-A1S dev kit!
Code: Select all
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*
* To fix crackling audio:
* /Users/peter/esp/esp-idf/components/bt/host/bluedroid/external/sbc/decoder/srce/synthesis-8-generated.c
* #define MUL_16S_16S(_x, _y) ((_x)/16 * (_y))
*
* To build:
* C:\Users\burkipe\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\ESP-IDF\ESP-IDF 5.2 CMD
* cd C:\Espressif\frameworks\esp-idf-v5.2.2\examples\bluetooth\bluedroid\classic_bt\a2dp_sink
* idf.py build flash monitor
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "bt_app_core.h"
#include "bt_app_av.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "driver/i2c.h"
/* device name */
#define LOCAL_DEVICE_NAME "ESP_SPEAKER"
#define I2C_MASTER_SCL_IO (GPIO_NUM_32) /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO (GPIO_NUM_33) /*!< gpio number for I2C master data */
#define I2C_MASTER_NUM (0) /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ (100000) /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 1000
#define I2C_SLAVE_ADDR 16
#define ES8388_ADDR 0x20
#define ACK_CHECK_EN 0x1
/* ES8388 register */
#define ES8388_CONTROL1 0x00
#define ES8388_CONTROL2 0x01
#define ES8388_CHIPPOWER 0x02
#define ES8388_ADCPOWER 0x03
#define ES8388_DACPOWER 0x04
#define ES8388_CHIPLOPOW1 0x05
#define ES8388_CHIPLOPOW2 0x06
#define ES8388_ANAVOLMANAG 0x07
#define ES8388_MASTERMODE 0x08
/* ADC */
#define ES8388_ADCCONTROL1 0x09
#define ES8388_ADCCONTROL2 0x0a
#define ES8388_ADCCONTROL3 0x0b
#define ES8388_ADCCONTROL4 0x0c
#define ES8388_ADCCONTROL5 0x0d
#define ES8388_ADCCONTROL6 0x0e
#define ES8388_ADCCONTROL7 0x0f
#define ES8388_ADCCONTROL8 0x10
#define ES8388_ADCCONTROL9 0x11
#define ES8388_ADCCONTROL10 0x12
#define ES8388_ADCCONTROL11 0x13
#define ES8388_ADCCONTROL12 0x14
#define ES8388_ADCCONTROL13 0x15
#define ES8388_ADCCONTROL14 0x16
/* DAC */
#define ES8388_DACCONTROL1 0x17
#define ES8388_DACCONTROL2 0x18
#define ES8388_DACCONTROL3 0x19
#define ES8388_DACCONTROL4 0x1a
#define ES8388_DACCONTROL5 0x1b
#define ES8388_DACCONTROL6 0x1c
#define ES8388_DACCONTROL7 0x1d
#define ES8388_DACCONTROL8 0x1e
#define ES8388_DACCONTROL9 0x1f
#define ES8388_DACCONTROL10 0x20
#define ES8388_DACCONTROL11 0x21
#define ES8388_DACCONTROL12 0x22
#define ES8388_DACCONTROL13 0x23
#define ES8388_DACCONTROL14 0x24
#define ES8388_DACCONTROL15 0x25
#define ES8388_DACCONTROL16 0x26
#define ES8388_DACCONTROL17 0x27
#define ES8388_DACCONTROL18 0x28
#define ES8388_DACCONTROL19 0x29
#define ES8388_DACCONTROL20 0x2a
#define ES8388_DACCONTROL21 0x2b
#define ES8388_DACCONTROL22 0x2c
#define ES8388_DACCONTROL23 0x2d
#define ES8388_DACCONTROL24 0x2e
#define ES8388_DACCONTROL25 0x2f
#define ES8388_DACCONTROL26 0x30
#define ES8388_DACCONTROL27 0x31
#define ES8388_DACCONTROL28 0x32
#define ES8388_DACCONTROL29 0x33
#define ES8388_DACCONTROL30 0x34
/* event for stack up */
enum {
BT_APP_EVT_STACK_UP = 0,
};
/********************************
* STATIC FUNCTION DECLARATIONS
*******************************/
/* GAP callback function */
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param);
/* handler for bluetooth stack enabled events */
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param);
static void writeReg(uint8_t reg_add, uint8_t data);
/*******************************
* STATIC FUNCTION DEFINITIONS
******************************/
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
uint8_t *bda = NULL;
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_AV_TAG, "authentication success: %s", param->auth_cmpl.device_name);
esp_log_buffer_hex(BT_AV_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
} else {
ESP_LOGE(BT_AV_TAG, "authentication failed, status: %d", param->auth_cmpl.stat);
}
ESP_LOGI(BT_AV_TAG, "link key type of current link is: %d", param->auth_cmpl.lk_type);
break;
}
case ESP_BT_GAP_ENC_CHG_EVT: {
char *str_enc[3] = {"OFF", "E0", "AES"};
bda = (uint8_t *)param->enc_chg.bda;
ESP_LOGI(BT_AV_TAG, "Encryption mode to [%02x:%02x:%02x:%02x:%02x:%02x] changed to %s",
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5], str_enc[param->enc_chg.enc_mode]);
break;
}
#if (CONFIG_EXAMPLE_A2DP_SINK_SSP_ENABLED == true)
/* when Security Simple Pairing user confirmation requested, this event comes */
case ESP_BT_GAP_CFM_REQ_EVT:
ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %"PRIu32, 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_AV_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey: %"PRIu32, param->key_notif.passkey);
break;
/* when Security Simple Pairing passkey requested, this event comes */
case ESP_BT_GAP_KEY_REQ_EVT:
ESP_LOGI(BT_AV_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_AV_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode: %d", param->mode_chg.mode);
break;
/* when ACL connection completed, this event comes */
case ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT:
bda = (uint8_t *)param->acl_conn_cmpl_stat.bda;
ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT Connected to [%02x:%02x:%02x:%02x:%02x:%02x], status: 0x%x",
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5], param->acl_conn_cmpl_stat.stat);
break;
/* when ACL disconnection completed, this event comes */
case ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT:
bda = (uint8_t *)param->acl_disconn_cmpl_stat.bda;
ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_ACL_DISC_CMPL_STAT_EVT Disconnected from [%02x:%02x:%02x:%02x:%02x:%02x], reason: 0x%x",
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5], param->acl_disconn_cmpl_stat.reason);
break;
/* others */
default: {
ESP_LOGI(BT_AV_TAG, "event: %d", event);
break;
}
}
}
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
{
ESP_LOGD(BT_AV_TAG, "%s event: %d", __func__, event);
switch (event) {
/* when do the stack up, this event comes */
case BT_APP_EVT_STACK_UP: {
esp_bt_gap_set_device_name(LOCAL_DEVICE_NAME);
esp_bt_gap_register_callback(bt_app_gap_cb);
assert(esp_avrc_ct_init() == ESP_OK);
esp_avrc_ct_register_callback(bt_app_rc_ct_cb);
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);
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);
/* Get the default value of the delay value */
esp_a2d_sink_get_delay_value();
/* Get local device name */
esp_bt_gap_get_device_name();
/* 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_AV_TAG, "%s unhandled event: %d", __func__, event);
break;
}
}
static void writeReg(uint8_t reg_add, uint8_t data) {
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
res |= i2c_master_write_byte(cmd, ES8388_ADDR, ACK_CHECK_EN);
res |= i2c_master_write_byte(cmd, reg_add, ACK_CHECK_EN);
res |= i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
res |= i2c_master_stop(cmd);
res |= i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (res != ESP_OK) {
ESP_LOGE("RR", "Unable to write to ES8388: %d", res);
} else {
ESP_LOGE("RR", "register successfull written.");
}
}
/*******************************
* MAIN ENTRY POINT
******************************/
void app_main(void)
{
/* initialize NVS — it is used to store PHY calibration data */
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 33,
.scl_io_num = 32,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
};
i2c_config.master.clk_speed = 100000;
err = i2c_param_config(I2C_NUM_0, &i2c_config);
if (err != ESP_OK) {
ESP_LOGE("OI", "i2c param config error: %d", err);
}
err = i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
if (err != ESP_OK) {
ESP_LOGE("OI", "i2c driver installation error: %d", err);
}
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
err = i2c_master_start(i2c_cmd);
if (err != ESP_OK) {
ESP_LOGE("OI", "i2c master start error: %d", err);
}
/* mute DAC during setup, power up all systems, slave mode */
writeReg(ES8388_DACCONTROL3, 0x04);
writeReg(ES8388_CONTROL2, 0x50);
writeReg(ES8388_CHIPPOWER, 0x00);
writeReg(ES8388_MASTERMODE, 0x00);
/* power up DAC and enable LOUT1+2 / ROUT1+2, ADC sample rate = DAC sample rate */
writeReg(ES8388_DACPOWER, 0x3e);
writeReg(ES8388_CONTROL1, 0x12);
/* DAC I2S setup: 16 bit word length, I2S format; MCLK / Fs = 256*/
writeReg(ES8388_DACCONTROL1, 0x18);
writeReg(ES8388_DACCONTROL2, 0x02);
/* DAC to output route mixer configuration: ADC MIX TO OUTPUT */
writeReg(ES8388_DACCONTROL16, 0x1B);
writeReg(ES8388_DACCONTROL17, 0x90);
writeReg(ES8388_DACCONTROL20, 0x90);
/* DAC and ADC use same LRCK, enable MCLK input; output resistance setup */
writeReg(ES8388_DACCONTROL21, 0x80);
writeReg(ES8388_DACCONTROL23, 0x00);
// /* DAC volume control: 0dB (maximum, unattented) */
writeReg(ES8388_DACCONTROL5, 0x00);
writeReg(ES8388_DACCONTROL4, 0x00);
// writeReg(ES8388_DACCONTROL5, 0x30);
// writeReg(ES8388_DACCONTROL4, 0x30);
/* power down ADC while configuring; volume: +9dB for both channels */
writeReg(ES8388_ADCPOWER, 0xff);
writeReg(ES8388_ADCCONTROL1, 0x88); // +24db
/* select LINPUT2 / RINPUT2 as ADC input; stereo; 16 bit word length, format right-justified, MCLK / Fs = 256 */
writeReg(ES8388_ADCCONTROL2, 0xf0); // 50
writeReg(ES8388_ADCCONTROL3, 0x80); // 00
writeReg(ES8388_ADCCONTROL4, 0x0e);
writeReg(ES8388_ADCCONTROL5, 0x02);
/* set ADC volume */
writeReg(ES8388_ADCCONTROL8, 0x20);
writeReg(ES8388_ADCCONTROL9, 0x20);
/* set LOUT1 / ROUT1 volume: 0dB (unattenuated) */
writeReg(ES8388_DACCONTROL24, 0x1e);
writeReg(ES8388_DACCONTROL25, 0x1e);
// writeReg(ES8388_DACCONTROL24, 0x00);
// writeReg(ES8388_DACCONTROL25, 0x00);
/* set LOUT2 / ROUT2 volume: 0dB (unattenuated) */
// writeReg(ES8388_DACCONTROL26, 0x1e);
// writeReg(ES8388_DACCONTROL27, 0x1e);
writeReg(ES8388_DACCONTROL26, 0x10);
writeReg(ES8388_DACCONTROL27, 0x10);
/* power up and enable DAC; power up ADC (no MIC bias) */
writeReg(ES8388_DACPOWER, 0x3c);
writeReg(ES8388_DACCONTROL3, 0x00);
// writeReg(ES8388_ADCPOWER, 0x00);
/*
* This example only uses the functions of Classical Bluetooth.
* So release the controller memory for Bluetooth Low Energy.
*/
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
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_AV_TAG, "%s initialize controller failed: %s", __func__, esp_err_to_name(err));
return;
}
if ((err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) {
ESP_LOGE(BT_AV_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(err));
return;
}
esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
#if (CONFIG_EXAMPLE_A2DP_SINK_SSP_ENABLED == false)
bluedroid_cfg.ssp_en = false;
#endif
if ((err = esp_bluedroid_init_with_cfg(&bluedroid_cfg)) != ESP_OK) {
ESP_LOGE(BT_AV_TAG, "%s initialize bluedroid failed: %s", __func__, esp_err_to_name(err));
return;
}
if ((err = esp_bluedroid_enable()) != ESP_OK) {
ESP_LOGE(BT_AV_TAG, "%s enable bluedroid failed: %s", __func__, esp_err_to_name(err));
return;
}
#if (CONFIG_EXAMPLE_A2DP_SINK_SSP_ENABLED == true)
/* set default parameters for Secure Simple Pairing */
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
#endif
/* 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';
pin_code[1] = '2';
pin_code[2] = '3';
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);
}
Code: Select all
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "bt_app_core.h"
#include "bt_app_av.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
#include "driver/dac_continuous.h"
#else
#include "driver/i2s_std.h"
#endif
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "sys/lock.h"
/* 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)
/* Application layer causes delay value */
#define APP_DELAY_VALUE 50 // 5ms
#define I2C_MASTER_SCL_IO (GPIO_NUM_32) /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO (GPIO_NUM_33) /*!< gpio number for I2C master data */
#define I2C_MASTER_NUM (0) /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ (100000) /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 1000
#define I2C_SLAVE_ADDR 16
#define ES8388_ADDR 0x20
#define ACK_CHECK_EN 0x1
/* ES8388 register */
#define ES8388_CONTROL1 0x00
#define ES8388_CONTROL2 0x01
#define ES8388_CHIPPOWER 0x02
#define ES8388_ADCPOWER 0x03
#define ES8388_DACPOWER 0x04
#define ES8388_CHIPLOPOW1 0x05
#define ES8388_CHIPLOPOW2 0x06
#define ES8388_ANAVOLMANAG 0x07
#define ES8388_MASTERMODE 0x08
/* ADC */
#define ES8388_ADCCONTROL1 0x09
#define ES8388_ADCCONTROL2 0x0a
#define ES8388_ADCCONTROL3 0x0b
#define ES8388_ADCCONTROL4 0x0c
#define ES8388_ADCCONTROL5 0x0d
#define ES8388_ADCCONTROL6 0x0e
#define ES8388_ADCCONTROL7 0x0f
#define ES8388_ADCCONTROL8 0x10
#define ES8388_ADCCONTROL9 0x11
#define ES8388_ADCCONTROL10 0x12
#define ES8388_ADCCONTROL11 0x13
#define ES8388_ADCCONTROL12 0x14
#define ES8388_ADCCONTROL13 0x15
#define ES8388_ADCCONTROL14 0x16
/* DAC */
#define ES8388_DACCONTROL1 0x17
#define ES8388_DACCONTROL2 0x18
#define ES8388_DACCONTROL3 0x19
#define ES8388_DACCONTROL4 0x1a
#define ES8388_DACCONTROL5 0x1b
#define ES8388_DACCONTROL6 0x1c
#define ES8388_DACCONTROL7 0x1d
#define ES8388_DACCONTROL8 0x1e
#define ES8388_DACCONTROL9 0x1f
#define ES8388_DACCONTROL10 0x20
#define ES8388_DACCONTROL11 0x21
#define ES8388_DACCONTROL12 0x22
#define ES8388_DACCONTROL13 0x23
#define ES8388_DACCONTROL14 0x24
#define ES8388_DACCONTROL15 0x25
#define ES8388_DACCONTROL16 0x26
#define ES8388_DACCONTROL17 0x27
#define ES8388_DACCONTROL18 0x28
#define ES8388_DACCONTROL19 0x29
#define ES8388_DACCONTROL20 0x2a
#define ES8388_DACCONTROL21 0x2b
#define ES8388_DACCONTROL22 0x2c
#define ES8388_DACCONTROL23 0x2d
#define ES8388_DACCONTROL24 0x2e
#define ES8388_DACCONTROL25 0x2f
#define ES8388_DACCONTROL26 0x30
#define ES8388_DACCONTROL27 0x31
#define ES8388_DACCONTROL28 0x32
#define ES8388_DACCONTROL29 0x33
#define ES8388_DACCONTROL30 0x34
#define BLINK_GPIO_1 22 // output GPIO
#define GPIO_KEY6 5 // input GPIO
#define GPIO_KEY5 18 // input GPIO
#define GPIO_KEY4 23 // input GPIO
#define GPIO_KEY3 19 // input GPIO
#define GPIO_KEY2 13 // input GPIO
#define GPIO_KEY1 36 // input GPIO
// # Audiokit Buttons
// KEY1: "36"
// KEY2: "13" # may be in use for other purposes, see onboard config switch
// KEY3: "19" # also activates LED D4 if pressed
// KEY4: "23" # do not use on A1S V2.3 with initial pinout -> I2C ES8388 // works
// KEY5: "18" # do not use on A1S V2.3 with initial pinout -> I2C ES8388
// KEY6: "5" # do not use on A1S V2.3 with initial pinout -> I2S
//
// # Audiokit LEDs
// LED_D4: "22"
// LED_D5: ${KEY3}
bool gpio_value = false;
bool key5_value = false;
bool key6_value = false;
bool playing = false;
bool key3_value = false;
bool key2_value = false;
bool key1_value = false;
#define ECHO_TEST_TXD (GPIO_NUM_23)
// #define ECHO_TEST_RXD (GPIO_NUM_18)
#define ECHO_TEST_RXD (GPIO_NUM_21)
#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)
#define ECHO_TEST_CTS (UART_PIN_NO_CHANGE)
#define ECHO_UART_PORT_NUM (1)
#define ECHO_UART_BAUD_RATE (19200)
#define ECHO_TASK_STACK_SIZE (2048)
#define BUF_SIZE (1024)
#define UART_CTRL_EVT (1)
/*******************************
* STATIC FUNCTION DECLARATIONS
******************************/
// Use a lookup table to control the volume because it's faster to lookup than calculate, and allows finer-grained control of volume steps
static uint8_t volumeLevels[128] = {
0, 0, 1, 1, 2, 2, 3, 3,
4, 5, 4, 4, 5, 5, 5, 5,
6, 6, 6, 6, 7, 7, 7, 7,
8, 8, 8, 8, 9, 9, 9, 9,
10, 10, 10, 10, 11, 11, 11, 11,
12, 12, 12, 12, 13, 13, 13, 13,
14, 14, 14, 14, 15, 15, 15, 15,
16, 16, 16, 16, 17, 17, 17, 17,
18, 18, 18, 18, 19, 19, 19, 19,
20, 20, 20, 20, 21, 21, 21, 21,
22, 22, 22, 22, 23, 23, 23, 23,
24, 24, 24, 24, 25, 25, 25, 25,
26, 26, 26, 26, 27, 27, 27, 27,
28, 28, 28, 28, 29, 29, 29, 29,
30, 30, 30, 30, 31, 31, 31, 31,
32, 32, 32, 32, 33, 33, 33, 33
};
enum uartCommand {
CMD_VOLUP,
CMD_VOLDOWN,
CMD_PLAYPAUSE,
CMD_NEXT,
CMD_BACK
};
typedef struct {
uint8_t cmd;
} uart_cb_param_t;
/* 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);
/* a2dp event handler */
static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param);
/* avrc controller event handler */
static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param);
/* avrc target event handler */
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param);
/* button or UART event handler */
static void bt_av_hdl_uart_evt(uint16_t event, void *p_param);
static void writeReg(uint8_t reg_add, uint8_t data);
/*******************************
* 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", "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 uint8_t s_volume = 0; /* local volume value */
static bool s_volume_notify; /* notify volume change or not */
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_chan_handle_t tx_chan = NULL;
#else
dac_continuous_handle_t tx_chan;
#endif
/********************************
* STATIC FUNCTION DEFINITIONS
*******************************/
static void echo_task(void *arg)
{
/* Configure parameters of an UART driver,
* communication pins and install the driver */
uart_config_t uart_config = {
.baud_rate = ECHO_UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
int intr_alloc_flags = 0;
#if CONFIG_UART_ISR_IN_IRAM
intr_alloc_flags = ESP_INTR_FLAG_IRAM;
#endif
ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS));
uint8_t my_buffer[128];
while(1) {
// Wait for at least one byte to be received via UART
if (uart_read_bytes(ECHO_UART_PORT_NUM, &(my_buffer[0]), 1, portMAX_DELAY) > 0) {
uint32_t byte_cnt = 0;
// There may be more bytes in the driver's buffer. Check how many there are.
size_t bytes_available = 0;
uart_get_buffered_data_len(ECHO_UART_PORT_NUM, &bytes_available);
if( bytes_available > 0 ) {
// Get as much data as is available and will fit in my_buffer
byte_cnt = bytes_available < (sizeof(my_buffer)-1) ? bytes_available : (sizeof(my_buffer)-1);
uart_read_bytes(ECHO_UART_PORT_NUM, &(my_buffer[1]), byte_cnt, 0);
}
byte_cnt += 1; // The one byte we read initially...
// We now have received byte_cnt bytes in my_buffer. Do something with it.
if ( (my_buffer[0] == 0xFF)
&& (my_buffer[1] == 0x55)
&& (my_buffer[2] == 0x03)
&& (my_buffer[3] == 0x02)
&& (my_buffer[4] == 0x00)
&& (my_buffer[5] == 0x02)
&& (my_buffer[6] == 0xF9) )
{
uart_cb_param_t p_param;
p_param.cmd = CMD_VOLUP;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
// ESP_LOGI(TAG, "VolUp");
}
if ( (my_buffer[0] == 0xFF)
&& (my_buffer[1] == 0x55)
&& (my_buffer[2] == 0x03)
&& (my_buffer[3] == 0x02)
&& (my_buffer[4] == 0x00)
&& (my_buffer[5] == 0x04)
&& (my_buffer[6] == 0xF7) )
{
uart_cb_param_t p_param;
p_param.cmd = CMD_VOLDOWN;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
// ESP_LOGI(TAG, "VolDown");
}
if ( (my_buffer[0] == 0xFF)
&& (my_buffer[1] == 0x55)
&& (my_buffer[2] == 0x03)
&& (my_buffer[3] == 0x02)
&& (my_buffer[4] == 0x00)
&& (my_buffer[5] == 0x01)
&& (my_buffer[6] == 0xFA) )
{
uart_cb_param_t p_param;
p_param.cmd = CMD_PLAYPAUSE;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
// ESP_LOGI(TAG, "PlayPause");
}
if ( (my_buffer[0] == 0xFF)
&& (my_buffer[1] == 0x55)
&& (my_buffer[2] == 0x03)
&& (my_buffer[3] == 0x02)
&& (my_buffer[4] == 0x00)
&& (my_buffer[5] == 0x10)
&& (my_buffer[6] == 0xEB) )
{
uart_cb_param_t p_param;
p_param.cmd = CMD_BACK;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
// ESP_LOGI(TAG, "Previous");
}
if ( (my_buffer[0] == 0xFF)
&& (my_buffer[1] == 0x55)
&& (my_buffer[2] == 0x03)
&& (my_buffer[3] == 0x02)
&& (my_buffer[4] == 0x00)
&& (my_buffer[5] == 0x08)
&& (my_buffer[6] == 0xF3) )
{
uart_cb_param_t p_param;
p_param.cmd = CMD_NEXT;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
// ESP_LOGI(TAG, "Next");
}
}
}
}
static void IRAM_ATTR key3_isr_handler(void* arg)
{
/*
TODO - Implement the debouncing algorithm
to eliminate the Spurious interrupt trigger.
*/
/* Disable the Interrupt */
gpio_intr_disable(GPIO_KEY3);
gpio_isr_handler_remove(GPIO_KEY3);
/* Button is pressed. Toggle the LED */
gpio_set_level(BLINK_GPIO_1, gpio_value);
gpio_value = !gpio_value;
uart_cb_param_t p_param;
p_param.cmd = CMD_VOLUP;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
/* Re-Enable the Interrupt */
gpio_isr_handler_add(GPIO_KEY3, key3_isr_handler, NULL);
gpio_intr_enable(GPIO_KEY3);
}
static void IRAM_ATTR key2_isr_handler(void* arg)
{
/*
TODO - Implement the debouncing algorithm
to eliminate the Spurious interrupt trigger.
*/
/* Disable the Interrupt */
gpio_intr_disable(GPIO_KEY2);
gpio_isr_handler_remove(GPIO_KEY2);
/* Button is pressed. Toggle the LED */
gpio_set_level(BLINK_GPIO_1, gpio_value);
gpio_value = !gpio_value;
uart_cb_param_t p_param;
p_param.cmd = CMD_VOLDOWN;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
/* Re-Enable the Interrupt */
gpio_isr_handler_add(GPIO_KEY2, key2_isr_handler, NULL);
gpio_intr_enable(GPIO_KEY2);
}
static void IRAM_ATTR key1_isr_handler(void* arg)
{
/*
TODO - Implement the debouncing algorithm
to eliminate the Spurious interrupt trigger.
*/
/* Disable the Interrupt */
gpio_intr_disable(GPIO_KEY1);
gpio_isr_handler_remove(GPIO_KEY1);
/* Button is pressed. Toggle the LED */
gpio_set_level(BLINK_GPIO_1, gpio_value);
gpio_value = !gpio_value;
uart_cb_param_t p_param;
p_param.cmd = CMD_VOLDOWN;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
/* Re-Enable the Interrupt */
gpio_isr_handler_add(GPIO_KEY1, key1_isr_handler, NULL);
gpio_intr_enable(GPIO_KEY1);
}
static void IRAM_ATTR key4_isr_handler(void* arg)
{
/*
TODO - Implement the debouncing algorithm
to eliminate the Spurious interrupt trigger.
*/
/* Disable the Interrupt */
gpio_intr_disable(GPIO_KEY4);
gpio_isr_handler_remove(GPIO_KEY4);
/* Button is pressed. Toggle the LED */
gpio_set_level(BLINK_GPIO_1, gpio_value);
gpio_value = !gpio_value;
uart_cb_param_t p_param;
p_param.cmd = CMD_BACK;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
/* Re-Enable the Interrupt */
gpio_isr_handler_add(GPIO_KEY4, key4_isr_handler, NULL);
gpio_intr_enable(GPIO_KEY4);
}
static void IRAM_ATTR key5_isr_handler(void* arg)
{
/*
TODO - Implement the debouncing algorithm
to eliminate the Spurious interrupt trigger.
*/
/* Disable the Interrupt */
gpio_intr_disable(GPIO_KEY5);
gpio_isr_handler_remove(GPIO_KEY5);
/* Button is pressed. Toggle the LED */
gpio_set_level(BLINK_GPIO_1, gpio_value);
gpio_value = !gpio_value;
uart_cb_param_t p_param;
p_param.cmd = CMD_NEXT;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
/* Re-Enable the Interrupt */
gpio_isr_handler_add(GPIO_KEY5, key5_isr_handler, NULL);
gpio_intr_enable(GPIO_KEY5);
}
static void IRAM_ATTR key6_isr_handler(void* arg)
{
/*
TODO - Implement the debouncing algorithm
to eliminate the Spurious interrupt trigger.
*/
/* Disable the Interrupt */
gpio_intr_disable(GPIO_KEY6);
gpio_isr_handler_remove(GPIO_KEY6);
/* Button is pressed. Toggle the LED */
gpio_set_level(BLINK_GPIO_1, gpio_value);
gpio_value = !gpio_value;
uart_cb_param_t p_param;
p_param.cmd = CMD_PLAYPAUSE;
bt_app_work_dispatch(bt_av_hdl_uart_evt, UART_CTRL_EVT, &p_param, sizeof(uart_cb_param_t), NULL);
/* Re-Enable the Interrupt */
gpio_isr_handler_add(GPIO_KEY6, key6_isr_handler, NULL);
gpio_intr_enable(GPIO_KEY6);
}
static void writeReg(uint8_t reg_add, uint8_t data) {
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
res |= i2c_master_write_byte(cmd, ES8388_ADDR, ACK_CHECK_EN);
res |= i2c_master_write_byte(cmd, reg_add, ACK_CHECK_EN);
res |= i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
res |= i2c_master_stop(cmd);
res |= i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (res != ESP_OK) {
ESP_LOGE("RR", "Unable to write to ES8388: %d", res);
} else {
//ESP_LOGI(BT_AV_TAG, "register successfull written. %d", data);
}
}
static void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param)
{
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;
}
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)
{
/* 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_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)
{
/* 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_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);
}
}
static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *event_parameter)
{
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: %"PRIu32"-ms", event_parameter->play_pos);
bt_av_play_pos_changed();
break;
/* others */
default:
ESP_LOGI(BT_AV_TAG, "unhandled event: %d", event_id);
break;
}
}
void bt_i2s_driver_install(void)
{
ESP_LOGI(BT_AV_TAG, "bt_i2s_driver_install");
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
dac_continuous_config_t cont_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = 44100,
.offset = 127,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range
.chan_mode = DAC_CHANNEL_MODE_ALTER,
};
/* Allocate continuous channels */
ESP_ERROR_CHECK(dac_continuous_new_channels(&cont_cfg, &tx_chan));
/* Enable the continuous channels */
ESP_ERROR_CHECK(dac_continuous_enable(tx_chan));
#else
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = GPIO_NUM_0,
.bclk = GPIO_NUM_27,
.ws = GPIO_NUM_25,
.dout = GPIO_NUM_26,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
/* enable I2S */
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
#endif
/* Reset the pin */
gpio_reset_pin(BLINK_GPIO_1);
gpio_reset_pin(GPIO_KEY4);
gpio_reset_pin(GPIO_KEY3);
gpio_reset_pin(GPIO_KEY2);
// gpio_reset_pin(GPIO_KEY1);
gpio_reset_pin(GPIO_KEY5);
gpio_reset_pin(GPIO_KEY6);
/* Set the GPIOs to Output mode */
gpio_set_direction(BLINK_GPIO_1, GPIO_MODE_OUTPUT);
gpio_set_direction(GPIO_KEY4, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_KEY5, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_KEY6, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_KEY3, GPIO_MODE_INPUT);
gpio_set_direction(GPIO_KEY2, GPIO_MODE_INPUT);
// gpio_set_direction(GPIO_KEY1, GPIO_MODE_INPUT);
/* Enable Pullup for Input Pin */
gpio_pullup_en(GPIO_KEY4);
gpio_pullup_en(GPIO_KEY5);
gpio_pullup_en(GPIO_KEY6);
gpio_pullup_en(GPIO_KEY3);
gpio_pullup_en(GPIO_KEY2);
// gpio_pullup_en(GPIO_KEY1);
/* Disable pulldown for Input Pin */
gpio_pulldown_dis(GPIO_KEY4);
gpio_pulldown_dis(GPIO_KEY5);
gpio_pulldown_dis(GPIO_KEY6);
gpio_pulldown_dis(GPIO_KEY3);
gpio_pulldown_dis(GPIO_KEY2);
// gpio_pulldown_dis(GPIO_KEY1);
/* Configure Raising Edge detection Interrupt for Input Pin */
gpio_set_intr_type(GPIO_KEY4, GPIO_INTR_POSEDGE);
gpio_set_intr_type(GPIO_KEY5, GPIO_INTR_POSEDGE);
gpio_set_intr_type(GPIO_KEY6, GPIO_INTR_POSEDGE);
gpio_set_intr_type(GPIO_KEY3, GPIO_INTR_POSEDGE);
gpio_set_intr_type(GPIO_KEY2, GPIO_INTR_POSEDGE);
// gpio_set_intr_type(GPIO_KEY1, GPIO_INTR_POSEDGE);
/* install gpio isr service to default values */
gpio_install_isr_service(0);
/* Attach the ISR to the GPIO interrupt */
gpio_isr_handler_add(GPIO_KEY4, key4_isr_handler, NULL);
gpio_isr_handler_add(GPIO_KEY5, key5_isr_handler, NULL);
gpio_isr_handler_add(GPIO_KEY6, key6_isr_handler, NULL);
gpio_isr_handler_add(GPIO_KEY3, key3_isr_handler, NULL);
gpio_isr_handler_add(GPIO_KEY2, key2_isr_handler, NULL);
// gpio_isr_handler_add(GPIO_KEY1, key1_isr_handler, NULL);
/* Enable the Interrupt */
gpio_intr_enable(GPIO_KEY4);
gpio_intr_enable(GPIO_KEY5);
gpio_intr_enable(GPIO_KEY6);
gpio_intr_enable(GPIO_KEY3);
gpio_intr_enable(GPIO_KEY2);
// gpio_intr_enable(GPIO_KEY1);
xTaskCreate(echo_task, "uart_echo_task", ECHO_TASK_STACK_SIZE, NULL, 10, NULL);
}
void bt_i2s_driver_uninstall(void)
{
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
ESP_ERROR_CHECK(dac_continuous_disable(tx_chan));
ESP_ERROR_CHECK(dac_continuous_del_channels(tx_chan));
#else
ESP_ERROR_CHECK(i2s_channel_disable(tx_chan));
ESP_ERROR_CHECK(i2s_del_channel(tx_chan));
#endif
}
static void volume_set_by_controller(uint8_t volume)
{
ESP_LOGI(BT_RC_TG_TAG, "Volume is set by remote controller to: %"PRIu32"%%", (uint32_t)volume * 100 / 0x7f);
/* set the volume in protection of lock */
_lock_acquire(&s_volume_lock);
s_volume = volume;
uint8_t volumeHex = volumeLevels[s_volume];
writeReg(ES8388_DACCONTROL26, volumeHex);
writeReg(ES8388_DACCONTROL27, volumeHex);
_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: %"PRIu32"%%", (uint32_t)volume * 100 / 0x7f);
/* set the volume in protection of lock */
_lock_acquire(&s_volume_lock);
s_volume = volume;
uint8_t volumeHex = volumeLevels[s_volume];
writeReg(ES8388_DACCONTROL26, volumeHex);
writeReg(ES8388_DACCONTROL27, volumeHex);
_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_down(void *arg)
{
ESP_LOGI(BT_RC_TG_TAG, "volume down");
uint8_t volume = (s_volume - 5) & 0x7f;
if ((s_volume - 5) < 0)
{
volume = 0;
}
volume_set_by_local_host(volume);
}
static void volume_change_up(void *arg)
{
ESP_LOGI(BT_RC_TG_TAG, "volume up");
uint8_t volume = (s_volume + 5) & 0x7f;
if ((s_volume + 5) > 127)
{
volume = 127;
}
volume_set_by_local_host(volume);
}
static void bt_av_hdl_uart_evt(uint16_t event, void *p_param)
{
ESP_LOGD(BT_AV_TAG, "%s event: %d", __func__, event);
uart_cb_param_t *uartCmd = NULL;
uartCmd = (uart_cb_param_t *)(p_param);
switch (event) {
case UART_CTRL_EVT: {
if (uartCmd->cmd == CMD_PLAYPAUSE)
{
if (playing == false)
{
esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED);
playing = true;
} else {
esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED);
playing = false;
}
}
if (uartCmd->cmd == CMD_BACK)
{
esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);
}
if (uartCmd->cmd == CMD_NEXT)
{
esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);
}
if (uartCmd->cmd == CMD_VOLDOWN)
{
volume_change_down(0);
}
if (uartCmd->cmd == CMD_VOLUP)
{
volume_change_up(0);
}
break;
}
/* others */
default:
ESP_LOGE(BT_AV_TAG, "%s unhandled event: %d", __func__, event);
break;
}
}
static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
{
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]);
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]);
s_audio_state = a2d->audio_stat.state;
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
s_pkt_cnt = 0;
}
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 */
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;
} else if (oct0 & (0x01 << 5)) {
sample_rate = 44100;
} else if (oct0 & (0x01 << 4)) {
sample_rate = 48000;
}
if (oct0 & (0x01 << 3)) {
ch_count = 1;
}
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
dac_continuous_disable(tx_chan);
dac_continuous_del_channels(tx_chan);
dac_continuous_config_t cont_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = sample_rate,
.offset = 127,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range
.chan_mode = (ch_count == 1) ? DAC_CHANNEL_MODE_SIMUL : DAC_CHANNEL_MODE_ALTER,
};
/* Allocate continuous channels */
dac_continuous_new_channels(&cont_cfg, &tx_chan);
/* Enable the continuous channels */
dac_continuous_enable(tx_chan);
#else
i2s_channel_disable(tx_chan);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);
i2s_std_slot_config_t slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, ch_count);
i2s_channel_reconfig_std_clock(tx_chan, &clk_cfg);
i2s_channel_reconfig_std_slot(tx_chan, &slot_cfg);
i2s_channel_enable(tx_chan);
#endif
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);
}
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 Complete");
} else {
ESP_LOGI(BT_AV_TAG, "A2DP PROF STATE: Deinit Complete");
}
break;
}
/* When protocol service capabilities configured, this event comes */
case ESP_A2D_SNK_PSC_CFG_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGI(BT_AV_TAG, "protocol service capabilities configured: 0x%x ", a2d->a2d_psc_cfg_stat.psc_mask);
if (a2d->a2d_psc_cfg_stat.psc_mask & ESP_A2D_PSC_DELAY_RPT) {
ESP_LOGI(BT_AV_TAG, "Peer device support delay reporting");
} else {
ESP_LOGI(BT_AV_TAG, "Peer device unsupport delay reporting");
}
break;
}
/* when set delay value completed, this event comes */
case ESP_A2D_SNK_SET_DELAY_VALUE_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
if (ESP_A2D_SET_INVALID_PARAMS == a2d->a2d_set_delay_value_stat.set_state) {
ESP_LOGI(BT_AV_TAG, "Set delay report value: fail");
} else {
ESP_LOGI(BT_AV_TAG, "Set delay report value: success, delay_value: %u * 1/10 ms", a2d->a2d_set_delay_value_stat.delay_value);
}
break;
}
/* when get delay value completed, this event comes */
case ESP_A2D_SNK_GET_DELAY_VALUE_EVT: {
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGI(BT_AV_TAG, "Get delay report value: delay_value: %u * 1/10 ms", a2d->a2d_get_delay_value_stat.delay_value);
/* Default delay value plus delay caused by application layer */
esp_a2d_sink_set_delay_value(a2d->a2d_get_delay_value_stat.delay_value + APP_DELAY_VALUE);
break;
}
/* when volume changed down, this event comes */
// case ESP_A2D_MEDIA_CTRL_ACK_EVT: {
// a2d = (esp_a2d_cb_param_t *)(p_param);
// if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_NONE)
// {
// if (playing == false)
// {
// esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED);
// playing = true;
// } else {
// esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED);
// playing = false;
// }
// //volume_change_down(0);
// }
// if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START)
// {
// esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);
// //volume_change_down(0);
// }
// if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_SUSPEND)
// {
// esp_avrc_ct_send_passthrough_cmd(0, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);
// //volume_change_up(0);
// }
// if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY)
// {
// volume_change_down(0);
// }
// if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP)
// {
// volume_change_up(0);
// }
// break;
// }
/* others */
default:
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 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 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, rsp_code %d", rc->psth_rsp.key_code,
rc->psth_rsp.key_state, rc->psth_rsp.rsp_code);
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 %"PRIx32", 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);
s_avrc_peer_rn_cap.bits = rc->get_rn_caps_rsp.evt_set.bits;
bt_av_new_track();
bt_av_playback_changed();
bt_av_play_pos_changed();
break;
}
/* others */
default:
ESP_LOGE(BT_RC_CT_TAG, "%s unhandled event: %d", __func__, event);
break;
}
}
static void bt_av_hdl_avrc_tg_evt(uint16_t event, void *p_param)
{
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]);
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);
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%"PRIx32, rc->reg_ntf.event_id, rc->reg_ntf.event_parameter);
if (rc->reg_ntf.event_id == ESP_AVRC_RN_VOLUME_CHANGE) {
s_volume_notify = true;
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_INTERIM, &rn_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: %"PRIx32", 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 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:
case ESP_A2D_SNK_PSC_CFG_EVT:
case ESP_A2D_SNK_SET_DELAY_VALUE_EVT:
case ESP_A2D_SNK_GET_DELAY_VALUE_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: %"PRIu32, 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;
}
}