1、背景
初始连接时蓝牙打印信息如下
[2019-03-01 16:40:06.600]# RECV ASCII>
ITER[0m
[0;31mE (15045) BLUETOOTH_EXAMPLE: [ * ] Action command error: src_type:1048585, source:0x3ffe18dc cmd:1, data:0x0, data_len:0[0m
[2019-03-01 16:40:23.996]# RECV ASCII>
[0;32mI (32445) BT_LOG: bta_av_link_role_ok hndl:x41 role:1 conn_audio:x1 bits:1 features:x824b
[0m
[0;33mW (32445) BT_APPL: new conn_srvc id:19, app_id:1[0m
[2019-03-01 16:41:51.950]# RECV ASCII>
[0;32mI (120405) BT_LOG: bta_av_link_role_ok hndl:x41 role:1 conn_audio:x1 bits:1 features:x824b
[0m
从关闭蓝牙到重新连上蓝牙播音之间的打印信息如下:
[2019-03-01 15:30:35.115]# RECV ASCII>
[0;31mE (11409595) BT_APPL: bta_av_str_stopped:audio_open_cnt=1, p_data 0x0[0m
[0;33mW (11409595) BT_L2CAP: L2CAP - no LCB for L2CA_SetAclPriority[0m
[0;33mW (11409595) BT_L2CAP: WARNING L2CA_SetFlushTimeout No lcb for bd_addr [...;b12b19][0m
[0;31mE (11409605) BT_APPL: bta_dm_pm_sniff BTM_SetPowerMode() returns ERROR status=7[0m
[0;33mW (11409605) BT_AVCT: ccb 0 not allocated[0m
[0;31mE (11409615) BT_APPL: bta_av_rc_create found duplicated handle:0[0m
[0;33mW (11409625) BLUETOOTH_EXAMPLE
[2019-03-01 15:30:35.194]# RECV ASCII>
: [ * ] Bluetooth disconnected[0m
[0;32mI (11409625) BLUETOOTH_EXAMPLE: [ 8 ] Stop audio_pipeline[0m
[0;33mW (11409665) AUDIO_PIPELINE: There are no listener registered[0m
[0;32mI (11409665) AUDIO_PIPELINE: audio_pipeline_unlinked[0m
[2019-03-01 15:31:45.635]# RECV ASCII>
[0;31mE (11480105) BT_APPL: bta_av_rc_create ACP handle exist for shdl:0[0m
[2019-03-01 15:31:46.310]# RECV ASCII>
[0;33mW (11480765) BT_APPL: new conn_srvc id:19, app_id:0[0m
[2019-03-01 15:32:10.626]# RECV ASCII>
[0;32mI (11505085) BT_LOG: bta_av_link_role_ok hndl:x41 role:1 conn_audio:x1 bits:1 features:x824b
[0m
问题:Demo中蓝牙断开后重新连接后,不能播音问题。
从打印结果看,蓝牙连接基本一样。
问题应该出在 [0;32mI (11409625) BLUETOOTH_EXAMPLE: [ 8 ] Stop audio_pipeline[0m
pipeline 已经关闭,因此在此连接上不能播放出音乐。看样子,我有点傻,有点蠢......
if (msg.source_type == PERIPH_ID_BLUETOOTH
&& msg.source == (void *)bt_periph) {
if (msg.cmd == PERIPH_BLUETOOTH_DISCONNECTED) {
ESP_LOGW(TAG, "[ * ] Bluetooth disconnected");
break;
}
}
这其实都不是问题,这是一种正常处理而已。
为彻底解决这个问题,也为了明白这个例程,下面从头看蓝牙服务。
1.1 参考资料
ADF文档 https://docs.espressif.com/projects/esp-adf/en/latest/get-started/index.html
2、ADF 蓝牙服务
涉及到蓝牙服务初始化的代码如下所示
bluetooth_service_cfg_t bt_cfg = {
.device_name = "Somnic_MusicPillow",
.mode = BLUETOOTH_A2DP_SINK,
};
bluetooth_service_start(&bt_cfg);esp_periph_handle_t bt_periph = bluetooth_service_create_periph();
esp_periph_start(bt_periph);
audio_event_iface_set_listener(esp_periph_get_event_iface(), evt);
audio_pipeline_run(pipeline);
2.1 蓝牙服务启动esp_err_t bluetooth_service_start
(bluetooth_service_cfg_t *config)
初始化并启动蓝牙服务。这个函数只会被成功调用一次,必须用bluetooth_service_destroy()来关闭。分析下源码
typedef struct bluetooth_service {
audio_element_handle_t stream;
esp_periph_handle_t periph;
audio_stream_type_t stream_type;
esp_bd_addr_t remote_bda;
esp_peer_bdname_t peer_bdname;
esp_a2d_connection_state_t connection_state;
esp_a2d_source_state_t source_a2d_state;
esp_a2d_audio_state_t audio_state;
uint64_t pos;
uint8_t tl;
bool avrc_connected;
} bluetooth_service_t;
bluetooth_service_t *g_bt_service = NULL;
esp_err_t bluetooth_service_start(bluetooth_service_cfg_t *config)
{
if (g_bt_service) {
ESP_LOGE(TAG, "Bluetooth service have been initialized");
return ESP_FAIL;
}
g_bt_service = calloc(1, sizeof(bluetooth_service_t));
AUDIO_MEM_CHECK(TAG, g_bt_service, return ESP_ERR_NO_MEM);
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 (esp_bt_controller_init(&bt_cfg) != ESP_OK) {
AUDIO_ERROR(TAG, "initialize controller failed");
return ESP_FAIL;
}
if (esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK) {
AUDIO_ERROR(TAG, "enable controller failed");
return ESP_FAIL;
}
if (esp_bluedroid_init() != ESP_OK) {
AUDIO_ERROR(TAG, "initialize bluedroid failed");
return ESP_FAIL;
}
if (esp_bluedroid_enable() != ESP_OK) {
AUDIO_ERROR(TAG, "enable bluedroid failed");
return ESP_FAIL;
}
if (config->device_name) {
esp_bt_dev_set_device_name(config->device_name);
} else {
if(config->mode == BLUETOOTH_A2DP_SINK) {
esp_bt_dev_set_device_name("ESP-ADF-SPEAKER");
} else {
esp_bt_dev_set_device_name("ESP-ADF-SOURCE");
}
}
/* set discoverable and connectable mode */
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
if (config->mode == BLUETOOTH_A2DP_SINK) {
esp_a2d_sink_init();
esp_a2d_sink_register_data_callback(bt_a2d_sink_data_cb);
esp_a2d_register_callback(bt_a2d_sink_cb);
// TODO: Use this function for IDF version higher than v3.0
// esp_a2d_sink_register_data_callback(bt_a2d_data_cb);
g_bt_service->stream_type = AUDIO_STREAM_READER;
} else {
/*
* Set default parameters for Legacy Pairing
* Use variable pin, input pin code when pairing
*/
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
esp_bt_pin_code_t pin_code;
esp_bt_gap_set_pin(pin_type, 0, pin_code);
esp_a2d_register_callback(bt_a2d_source_cb);
esp_bt_gap_register_callback(bt_app_gap_cb);
esp_a2d_source_register_data_callback(bt_a2d_source_data_cb);
esp_a2d_source_init();
/* start device discovery */
ESP_LOGI(TAG, "Starting device discovery...");
if (config->remote_name) {
memcpy(&g_bt_service->peer_bdname, config->remote_name, strlen(config->remote_name) + 1);
} else {
memcpy(&g_bt_service->peer_bdname, "ESP-ADF-SPEAKER", ESP_BT_GAP_MAX_BDNAME_LEN + 1);
}
g_bt_service->source_a2d_state = BT_SOURCE_STATE_DISCOVERING;
g_bt_service->stream_type = AUDIO_STREAM_WRITER;
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
}
return ESP_OK;
}
上述代码主要完成了
1、释放BLE资源到堆中;
2、初始化并使能控制器和经典蓝牙主机;
3、设置广播名;
4、A2DP_SINK 初始化,设置回调函数、数据输出回调函数;
5、设置广播可连接、可发现的性能;
看样子,更深入的东西,要看回调函数的处理。这里先放一放。
2.2 使能创建蓝牙服务外设
esp_periph_handle_t bluetooth_service_create_periph()
{
if (g_bt_service && g_bt_service->periph) {
ESP_LOGE(TAG, "Bluetooth periph have been created");
return NULL;
}
g_bt_service->periph = esp_periph_create(PERIPH_ID_BLUETOOTH, "periph_bt");
esp_periph_set_function(g_bt_service->periph, _bt_periph_init, _bt_periph_run, _bt_periph_destroy);
return g_bt_service->periph;
}
在_bt_periph_init中,是对avrcp的初始化以及回调函数的设置。
static esp_err_t _bt_periph_init(esp_periph_handle_t periph)
{
esp_avrc_ct_init();
esp_avrc_ct_register_callback(bt_avrc_ct_cb);
return ESP_OK;
}
3、回调函数处理
回调函数的处理,目前看是最重要的一部分,因此有必要对这部分仔细分析,找到解决方法。
3.1A2DP的回调函数
3.1.0 bt_a2d_sink_cb函数的参量项(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *p_param)结构体的组成
static void bt_a2d_sink_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *p_param)
{
esp_a2d_cb_param_t *a2d = NULL;
switch (event) {
case ESP_A2D_CONNECTION_STATE_EVT:
a2d = (esp_a2d_cb_param_t *)(p_param);
uint8_t *bda = a2d->conn_stat.remote_bda;
ESP_LOGD(TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]",
conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
if (g_bt_service->connection_state == ESP_A2D_CONNECTION_STATE_DISCONNECTED
&& a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
memcpy(&g_bt_service->remote_bda, &a2d->conn_stat.remote_bda, sizeof(esp_bd_addr_t));
g_bt_service->connection_state = a2d->conn_stat.state;
ESP_LOGD(TAG, "A2DP connection state = CONNECTED");
if (g_bt_service->periph) {
esp_periph_send_event(g_bt_service->periph, PERIPH_BLUETOOTH_CONNECTED, NULL, 0);
}
}
if (memcmp(&g_bt_service->remote_bda, &a2d->conn_stat.remote_bda, sizeof(esp_bd_addr_t)) == 0
&& g_bt_service->connection_state == ESP_A2D_CONNECTION_STATE_CONNECTED
&& a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
memset(&g_bt_service->remote_bda, 0, sizeof(esp_bd_addr_t));
g_bt_service->connection_state = ESP_A2D_CONNECTION_STATE_DISCONNECTED;
ESP_LOGD(TAG, "A2DP connection state = DISCONNECTED");
if (g_bt_service->stream) {
audio_element_report_status(g_bt_service->stream, AEL_STATUS_INPUT_DONE);
}
if (g_bt_service->periph) {
esp_periph_send_event(g_bt_service->periph, PERIPH_BLUETOOTH_DISCONNECTED, NULL, 0);
}
}
break;
case ESP_A2D_AUDIO_STATE_EVT:
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGD(TAG, "A2DP audio state: %s", audio_state_str[a2d->audio_stat.state]);
g_bt_service->audio_state = a2d->audio_stat.state;
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
g_bt_service->pos = 0;
}
if (g_bt_service->periph == NULL) {
break;
}
if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
esp_periph_send_event(g_bt_service->periph, PERIPH_BLUETOOTH_AUDIO_STARTED, NULL, 0);
} else if (ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND == a2d->audio_stat.state) {
esp_periph_send_event(g_bt_service->periph, PERIPH_BLUETOOTH_AUDIO_SUSPENDED, NULL, 0);
} else if (ESP_A2D_AUDIO_STATE_STOPPED == a2d->audio_stat.state) {
esp_periph_send_event(g_bt_service->periph, PERIPH_BLUETOOTH_AUDIO_STOPPED, NULL, 0);
}
break;
case ESP_A2D_AUDIO_CFG_EVT:
a2d = (esp_a2d_cb_param_t *)(p_param);
ESP_LOGD(TAG, "A2DP audio stream configuration, codec type %d", a2d->audio_cfg.mcc.type);
if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
int sample_rate = 16000;
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;
}
ESP_LOGD(TAG, "Bluetooth configured, sample rate=%d", sample_rate);
if (g_bt_service->stream == NULL) {
break;
}
audio_element_info_t bt_info = {0};
audio_element_getinfo(g_bt_service->stream, &bt_info);
bt_info.sample_rates = sample_rate;
bt_info.channels = 2;
bt_info.bits = 16;
audio_element_setinfo(g_bt_service->stream, &bt_info);
audio_element_report_info(g_bt_service->stream);
}
break;
default:
ESP_LOGD(TAG, "Unhandled A2DP event: %d", event);
break;
}
}
看看这个回调函数能处理哪几类事件
/// A2DP callback events
typedef enum {
ESP_A2D_CONNECTION_STATE_EVT = 0, /*!< connection state changed event */
ESP_A2D_AUDIO_STATE_EVT, /*!< audio stream transmission state changed event */
ESP_A2D_AUDIO_CFG_EVT, /*!< audio codec is configured, only used for A2DP SINK */
ESP_A2D_MEDIA_CTRL_ACK_EVT, /*!< acknowledge event in response to media control commands */
} esp_a2d_cb_event_t;
1、连接状态改变事件;
2、音频数据流传输状态改变事件;
3、音频编解码配置事件,只用于SINK
4、响应媒体控制命令确认事件
接下去esp_a2d_cb_param_t 是一个联合体,根据不同的event选择其中不同的成员结构体;先看看ESP_A2D_CONNECTION_STATE_EVT------连接状态改变的事件
/// A2DP state callback parameters
typedef union {
/**
* @brief ESP_A2D_CONNECTION_STATE_EVT
*/
struct a2d_conn_stat_param {
esp_a2d_connection_state_t state; /*!< one of values from esp_a2d_connection_state_t */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_a2d_disc_rsn_t disc_rsn; /*!< reason of disconnection for "DISCONNECTED" */
} conn_stat; /*!< A2DP connection status */
/**
* @brief ESP_A2D_AUDIO_STATE_EVT
*/
struct a2d_audio_stat_param {
esp_a2d_audio_state_t state; /*!< one of the values from esp_a2d_audio_state_t */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
} audio_stat; /*!< audio stream playing state */
/**
* @brief ESP_A2D_AUDIO_CFG_EVT
*/
struct a2d_audio_cfg_param {
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_a2d_mcc_t mcc; /*!< A2DP media codec capability information */
} audio_cfg; /*!< media codec configuration information */
/**
* @brief ESP_A2D_MEDIA_CTRL_ACK_EVT
*/
struct media_ctrl_stat_param {
esp_a2d_media_ctrl_t cmd; /*!< media control commands to acknowledge */
esp_a2d_media_ctrl_ack_t status; /*!< acknowledgement to media control commands */
} media_ctrl_stat; /*!< status in acknowledgement to media control commands */
} esp_a2d_cb_param_t;
3.1.1 sink中连接事件的处理
联合体中这个连接事件的结构体包括了当前的连接状态、远端蓝牙设备地址、断开连接的原因 三个成员变量。
连接状态有断开、连接中、连接上、断开中,其枚举类型如下所示
/// Bluetooth A2DP connection states
typedef enum {
ESP_A2D_CONNECTION_STATE_DISCONNECTED = 0, /*!< connection released */
ESP_A2D_CONNECTION_STATE_CONNECTING, /*!< connecting remote device */
ESP_A2D_CONNECTION_STATE_CONNECTED, /*!< connection established */
ESP_A2D_CONNECTION_STATE_DISCONNECTING /*!< disconnecting remote device */
} esp_a2d_connection_state_t;断开连接的原因(1、由远端设备发起的已完成的断开;2、信号丢失引起的异常断开),也如下枚举类型所示
/// Bluetooth A2DP disconnection reason
typedef enum {
ESP_A2D_DISC_RSN_NORMAL = 0, /*!< Finished disconnection that is initiated by local or remote device */
ESP_A2D_DISC_RSN_ABNORMAL /*!< Abnormal disconnection caused by signal loss */
} esp_a2d_disc_rsn_t;
主要是看联合体中的连接状态事件这个成员,该成员包括了以下三个子成员
1、连接状态state 四种状态之一
2、bda(远端蓝牙设备地址) 6个字节长度的蓝牙设备地址
3、断开连接的原因 两种断开原因之一
程序分支:
1、当设备从断开转为连上时,g_bt_service 全局变量的各成员值改变,并发送PERIPH_BLUETOOTH_CONNNECTED事件给外部队列。
2、对于从连接转为断开时,除了g_bt_service个成员值改变外,还要向pipeline报告流的状态AEL_STATUS_INPUT_DONE,把PERIPH_BLUETOOTH_DISCONNNECTED事件发送给外部队列。
看看这个函数audio_element_report_status()这个API
audio_element_report_status(g_bt_service->stream, AEL_STATUS_INPUT_DONE);
//发送音频事件给事件处理函数
esp_err_t audio_element_report_status(audio_element_handle_t el, audio_element_status_t status)
{
audio_event_iface_msg_t msg = { 0 };
msg.cmd = AEL_MSG_CMD_REPORT_STATUS;
msg.data = (void *)status;
msg.data_len = sizeof(status);
ESP_LOGD(TAG, "REPORT_STATUS,[%s]evt out cmd = %d,status:%d", el->tag, msg.cmd, status);
return audio_element_msg_sendout(el, &msg);
}
static esp_err_t audio_element_msg_sendout(audio_element_handle_t el, audio_event_iface_msg_t *msg)
{
msg->source = el;
msg->source_type = AUDIO_ELEMENT_TYPE_ELEMENT;
return audio_event_iface_sendout(el->event, msg);
}
看看音频事件有哪些类
/**
* Audio element status report
*/
typedef enum {
AEL_STATUS_NONE = 0,
AEL_STATUS_ERROR_OPEN = 1, //打开错误
AEL_STATUS_ERROR_INPUT = 2, //输入错误
AEL_STATUS_ERROR_PROCESS = 3, //处理错误
AEL_STATUS_ERROR_OUTPUT = 4, //输出错误
AEL_STATUS_ERROR_CLOSE = 5, //关闭错误
AEL_STATUS_ERROR_TIMEOUT = 6, //超时错误
AEL_STATUS_ERROR_UNKNOWN = 7, //未知错误
AEL_STATUS_INPUT_DONE = 8, //输入完成
AEL_STATUS_INPUT_BUFFERING = 9, //输入缓冲
AEL_STATUS_OUTPUT_DONE = 10, //输出完成
AEL_STATUS_OUTPUT_BUFFERING = 11, //输出缓冲
AEL_STATUS_STATE_RUNNING = 12, //运行状态
AEL_STATUS_STATE_PAUSED = 13, //暂停状态
AEL_STATUS_STATE_STOPPED = 14, //停止状态
AEL_STATUS_MOUNTED = 15, //挂载状态
AEL_STATUS_UNMOUNTED = 16, //卸载状态
} audio_element_status_t;
//关于蓝牙连接状态改变的讨论,先告一段落。
3.1.2 sink中音频数据传输状态改变的处理
设置g_bt_service全局变量中的audio_state,根据不同的a2d->audio_stat.state 发送不同的cmd给外部队列。
3.2下面看看sink数据的回调函数。
static void bt_a2d_sink_data_cb(const uint8_t *data, uint32_t len)
{
if (g_bt_service->stream) {
if (audio_element_get_state(g_bt_service->stream) == AEL_STATE_RUNNING) {
audio_element_output(g_bt_service->stream, (char *)data, len);
}
}
}
当数据流的状态是运行中,发送元素输出数据给下一个元素的读入buffer,即本元素的输出buffer;
写数据到ringbuffer或调用写回调函数。write_type类型包括IO_TYPE_CB回调和IO_TYPE_RB ringbuffer
audio_element_err_t audio_element_output(audio_element_handle_t el, char *buffer, int write_size)
{
int output_len = 0;
if (el->write_type == IO_TYPE_CB) {
if (el->out.write_cb.cb && write_size) {
output_len = el->out.write_cb.cb(el, buffer, write_size, el->output_wait_time,
el->out.write_cb.ctx);
}
} else if (el->write_type == IO_TYPE_RB) {
if (el->out.output_rb && write_size) {
output_len = rb_write(el->out.output_rb, buffer, write_size, el->output_wait_time);
if ((rb_bytes_filled(el->out.output_rb) > el->out_buf_size_expect) || (output_len < 0)) {
xEventGroupSetBits(el->state_event, BUFFER_REACH_LEVEL_BIT);
}
}
}
if (output_len <= 0) {
switch (output_len) {
case AEL_IO_ABORT:
ESP_LOGW(TAG, "OUT-[%s] AEL_IO_ABORT", el->tag);
audio_element_set_ringbuf_done(el);
audio_element_stop(el);
break;
case AEL_IO_DONE:
case AEL_IO_OK:
ESP_LOGI(TAG, "OUT-[%s] AEL_IO_DONE,%d", el->tag, output_len);
break;
case AEL_IO_FAIL:
ESP_LOGE(TAG, "OUT-[%s] AEL_STATUS_ERROR_OUTPUT", el->tag);
audio_element_report_status(el, AEL_STATUS_ERROR_OUTPUT);
audio_element_cmd_send(el, AEL_MSG_CMD_ERROR);
break;
case AEL_IO_TIMEOUT:
ESP_LOGW(TAG, "OUT-[%s] AEL_IO_TIMEOUT", el->tag);
audio_element_cmd_send(el, AEL_MSG_CMD_PAUSE);
break;
default:
ESP_LOGE(TAG, "OUT-[%s] Output return not support,ret:%d", el->tag, output_len);
audio_element_cmd_send(el, AEL_MSG_CMD_PAUSE);
break;
}
}
return output_len;
}
3.3对于bt_avrc_ct_cb回调函数----
static void bt_avrc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *p_param)
{
esp_avrc_ct_cb_param_t *rc = p_param;
switch (event) {
case ESP_AVRC_CT_CONNECTION_STATE_EVT: {
uint8_t *bda = rc->conn_stat.remote_bda;
g_bt_service->avrc_connected = rc->conn_stat.connected;
if (rc->conn_stat.connected) {
ESP_LOGD(TAG, "ESP_AVRC_CT_CONNECTION_STATE_EVT");
bt_key_act_sm_init();
} else if (0 == rc->conn_stat.connected){
bt_key_act_sm_deinit();
}
ESP_LOGD(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;
}
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
if(g_bt_service->avrc_connected) {
ESP_LOGD(TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state);
bt_key_act_param_t param;
memset(¶m, 0, sizeof(bt_key_act_param_t));
param.evt = event;
param.tl = rc->psth_rsp.tl;
param.key_code = rc->psth_rsp.key_code;
param.key_state = rc->psth_rsp.key_state;
bt_key_act_state_machine(¶m);
}
break;
}
case ESP_AVRC_CT_METADATA_RSP_EVT: {
ESP_LOGD(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;
}
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: {
ESP_LOGD(TAG, "AVRC event notification: %d, param: %d", rc->change_ntf.event_id, rc->change_ntf.event_parameter);
break;
}
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
ESP_LOGD(TAG, "AVRC remote features %x", rc->rmt_feats.feat_mask);
break;
}
default:
ESP_LOGE(TAG, "%s unhandled evt %d", __func__, event);
break;
}
}
其中最主要的是关于蓝牙键控的操作,主要有bt_key_act_sm_init和bt_key_act_state_machine。
typedef enum {
KEY_ACT_STATE_IDLE,
KEY_ACT_STATE_PRESS,
KEY_ACT_STATE_RELEASE
} key_act_state_t;
typedef struct {
key_act_state_t state;
uint32_t key_code;
TimerHandle_t key_tmr;
uint8_t tl;
} key_act_cb_t;
static key_act_cb_t key_cb;
全局静态变量key_cb的初始化
esp_err_t bt_key_act_sm_init(void)
{
if (key_cb.key_tmr) {
ESP_LOGW(BTKEYCTRL_TAG, "%s timer not released", __func__);
xTimerDelete(key_cb.key_tmr, portMAX_DELAY);
key_cb.key_tmr = NULL;
}
memset(&key_cb, 0, sizeof(key_act_cb_t));
int tmr_id = 0xfe;
key_cb.tl = 0;
key_cb.state = KEY_ACT_STATE_IDLE;
key_cb.key_code = 0;
key_cb.key_tmr = xTimerCreate("key_tmr", portMAX_DELAY,
pdFALSE, (void *)tmr_id, key_act_time_out);
if (key_cb.key_tmr == NULL) {
ESP_LOGW(BTKEYCTRL_TAG, "%s timer creation failure", __func__);
return false;
}
return true;
}
全局静态变量key_cb bt_key_act_state_machine
/**
*brief Bluetooth key action parameter
*/
typedef struct {
uint32_t evt; /*!< AVRC Controller callback events */
uint32_t key_code; /*!< passthrough command code */
uint8_t key_state; /*!< 0 for PRESSED, 1 for RELEASED */
uint8_t tl; /*!< transaction label, 0 to 15 */
} bt_key_act_param_t;
void bt_key_act_state_machine(bt_key_act_param_t *param)
{
ESP_LOGD(BTKEYCTRL_TAG, "key_ctrl cb: tl %d, state: %d", key_cb.tl, key_cb.state);
switch (key_cb.state) {
case KEY_ACT_STATE_IDLE:
key_act_state_hdl_idle(&key_cb, param);
break;
case KEY_ACT_STATE_PRESS:
key_act_state_hdl_press(&key_cb, param);
break;
case KEY_ACT_STATE_RELEASE:
key_act_state_hdl_release(&key_cb, param);
break;
default:
ESP_LOGD(BTKEYCTRL_TAG, "Invalid key_ctrl state: %d", key_cb.state);
break;
}
}
启动定时器,设备状态KEY_ACT_STATE_PRESS
static void key_act_state_hdl_idle(key_act_cb_t *key_cb, bt_key_act_param_t *param)
{
AUDIO_MEM_CHECK(BTKEYCTRL_TAG, key_cb, return);
AUDIO_MEM_CHECK(BTKEYCTRL_TAG, param, return);
if(key_cb->state != KEY_ACT_STATE_IDLE) {
ESP_LOGE(BTKEYCTRL_TAG, "ERROR STATE: bluetooth key action state should be KEY_ACT_STATE_IDLE!");
return;
}
if (param->evt == ESP_AVRC_CT_KEY_STATE_CHG_EVT) {
key_cb->tl = param->tl;
key_cb->key_code = param->key_code;
esp_avrc_ct_send_passthrough_cmd(param->tl, param->key_code, ESP_AVRC_PT_CMD_STATE_PRESSED);
xTimerStart(key_cb->key_tmr, 500 / portTICK_RATE_MS);
key_cb->state = KEY_ACT_STATE_PRESS;
}
}
esp_avrc_ct_send_passthrough_cmd(param->tl, param->key_code, ESP_AVRC_PT_CMD_STATE_PRESSED)这个接口
发送passthrough 命令到AVRCP目标端(target)。这个函数一般在建立起AVTCP链接后,收到ESP_AVRC CT CONNECTION STATE EVT事件后调用 。
4、蓝牙数据流
---未完待续