从ADF看蓝牙服务---BT Sink

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhejfl/article/details/88060394

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(&param, 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(&param);
		}
		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、蓝牙数据流

---未完待续

5、打印输出

[0;32mI (315) BLUETOOTH_EXAMPLE: [ 1 ] Create Bluetooth service[0m
[0;32mI (315) BTDM_INIT: BT controller compile version [d992edb][0m
[0;32mI (325) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE[0m


[2019-03-11 19:53:50.956]# RECV ASCII>
[0;32mI (395) phy: phy_version: 4000, b6198fa, Sep  3 2018, 15:11:06, 0, 0[0m


[2019-03-11 19:53:51.612]# RECV ASCII>
[0;32mI (1055) BLUETOOTH_EXAMPLE: [ 2 ] Start codec chip[0m
[0;32mI (1055) gpio: GPIO[19]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:3 [0m
[0;32mI (1065) gpio: GPIO[21]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 [0m
[0;32mI (1065) ES8388_DRIVER: init,out:02, in:00[0m
[0;32mI (1075) AUDIO_HAL: Codec mode is 2, Ctrl:1[0m
[0;32mI (1085) BLUETOOTH_EXAMPLE: [ 3 ] Create audio pipeline for playback[0m
[0;32mI (1085) BLUETOOTH_EXAMPLE: [3.1] 

[2019-03-11 19:53:51.656]# RECV ASCII>
Create i2s stream to write data to codec chip[0m
[0;32mI (1095) I2S: DMA Malloc info, datalen=blocksize=1200, dma_buf_count=3[0m
[0;32mI (1095) I2S: DMA Malloc info, datalen=blocksize=1200, dma_buf_count=3[0m
[0;32mI (1105) I2S: APLL: Req RATE: 44100, real rate: 43945.238, BITS: 16, CLKM: 1, BCK_M: 8, MCLK: 11249981.000, SCLK: 1406247.625000, diva: 1, divb: 0[0m
[0;32mI (1125) BLUETOOTH_EXAMPLE: [3.2] Get Bluetooth stream[0m
[0;32mI (1125) BLUETOOTH_EXAMPLE: [3.2] Register all elements to aud

[2019-03-11 19:53:51.701]# RECV ASCII>
io pipeline[0m
[0;32mI (1135) BLUETOOTH_EXAMPLE: [3.3] Link it together [Bluetooth]-->bt_stream_reader-->i2s_stream_writer-->[codec_chip][0m
[0;32mI (1145) AUDIO_PIPELINE: audio_pipeline_link:0x3f80017c, bt, 0x3f800270[0m
[0;32mI (1155) BLUETOOTH_EXAMPLE: [ 4 ] Initialize peripherals[0m
[0;31mE (1155) gpio: gpio_install_isr_service(394): GPIO isr service already installed[0m
[0;32mI (1165) BLUETOOTH_EXAMPLE: [4.1] Initialize Touch peripheral[0m
[0;32mI (1175) BLUETOOTH_EXAMPLE: [4.2] Creat

[2019-03-11 19:53:51.745]# RECV ASCII>
e Bluetooth peripheral[0m
[0;32mI (1175) BLUETOOTH_EXAMPLE: [4.2] Start all peripherals[0m
[0;32mI (1185) BLUETOOTH_EXAMPLE: [ 5 ] Setup event listener[0m
[0;32mI (1195) BLUETOOTH_EXAMPLE: [5.1] Listening event from all elements of pipeline[0m
[0;32mI (1195) BLUETOOTH_EXAMPLE: [5.2] Listening event from peripherals[0m
[0;32mI (1205) BLUETOOTH_EXAMPLE: [ 6 ] Start audio_pipeline[0m
[0;32mI (1215) AUDIO_ELEMENT: [bt] Element task created[0m
[0;32mI (1215) AUDIO_ELEMENT: [i2s] Element task

[2019-03-11 19:53:51.819]# RECV ASCII>
 created[0m
[0;32mI (1225) AUDIO_PIPELINE: Func:audio_pipeline_run, Line:278, MEM Total:4311308 Bytes, Inter:159848 Bytes, Dram:137412 Bytes

[0m
[0;32mI (1235) AUDIO_ELEMENT: [i2s] AEL_MSG_CMD_RESUME,state:1[0m
[0;32mI (1235) I2S_STREAM: AUDIO_STREAM_WRITER[0m
[0;32mI (1245) AUDIO_PIPELINE: Pipeline started[0m
[0;32mI (1245) BLUETOOTH_EXAMPLE: [ 7 ] Listen for all pipeline events[0m

 

 

 

 

 

展开阅读全文

请教下蓝牙服务如何查询!!!!!!

06-18

在远程蓝牙设备上执行服务查询rn提供Winsock的版本和实现细节的数据来初始化caller application。可以通过调用WSAStartup函数来获得这个数据。 rnWSADATA wsd;rnWSAStartup (MAKEWORD(1,0), &wsd);rn通过设置WSAQUERYSET结构体来指定搜索参数。 rnWSAQUERYSET wsaq;rnmemset (&wsaq, 0, sizeof(wsaq));rnwsaq.dwSize = sizeof(wsaq);rnwsaq.dwNameSpace = NS_BTH;rnwsaq.lpBlob = &blob;rnwsaq.lpcsaBuffer = &csai;rn设置dwNameSpace成员为NS_BTH将查询指定为蓝牙查询。rn调用WSALookupServiceBegin函数初始化搜索,将第一步中创建的WSAQUERYSET变量传递给pQuerySet参数来指定搜索标准 rnint iRet = WSALookupServiceBegin (&wsaq, 0, &hLookup);rn将dwFlags参数设置为0来在远程设备上执行一个服务查询, WSALookupServiceBegin 返回一个句柄到hLookup参数中。rn注意 将dwFlags参数设置为LUP_CONTAINERS, 调用 WSALookupServiceBegin将执行一个设备查询。详情参见“使用WinSock搜索蓝牙设备”。rn要返回在远程设备上所找到的服务的相关数据,使用从WSALookupServiceBegin返回的句柄调用 WSALookupServiceNext函数。 rnCHAR buf[5000];rnLPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf;rnDWORD dwSize = sizeof(buf);rnmemset(pwsaResults,0,sizeof(WSAQUERYSET));rnpwsaResults->dwSize = sizeof(WSAQUERYSET);rnpwsaResults->dwNameSpace = NS_BTH;rnpwsaResults->lpBlob = NULL;rniRet = WSALookupServiceNext (hLookup, 0, &dwSize, pwsaResults);rnWSALookupServiceNext返回了一个指向WSAQUERYSET的指针,它包含了lpBlob成员里的服务数据的引用。这个成员指向一个BLOB结构体,它包含了由WSALookupServiceNext一次性返回的二进制数据。 Windows CE提供了COM接口,你可以使用它们来分析服务数据。详情参见使用COM接口分析SDP记录.rn调用WSALookupServiceEnd函数来结束设备搜索。这个函数将释放由WSALookupServiceBegin创建的lookup句柄。rnWSALookupServiceEnd(hLookup);rn要结束对Winsock服务的使用,调用WSACleanup函数。在程序中对每个成功调用的WSAStartup都必须对应地调用WSACleanup。rnrnrnint iRet = WSALookupServiceBegin (&wsaq, 0, &hLookup);rniRet老是返回-1,错误为10022,说参数不对,请教下WSALookupServiceBegin中的第2个参数是0不行么,rn有蓝牙服务查询成功的么,本人只能得到设备列表,而且可以连接上某个服务,但是不知道是什么服务,怎么查询蓝牙服务,知道的告诉下好么,谢谢!!!! 论坛

没有更多推荐了,返回首页