From 78cbdfa3328c48ca0b4faef65c2b282a5fc00b2b Mon Sep 17 00:00:00 2001 From: weitianhua Date: Fri, 25 Oct 2019 15:53:16 +0800 Subject: [PATCH] HFP AG Example --- .../bt/host/bluedroid/bta/hf_ag/bta_ag_act.c | 5 +- .../bt/host/bluedroid/bta/hf_ag/bta_ag_main.c | 2 +- .../bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c | 2 + .../bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c | 26 +- .../bluedroid/bta/hf_ag/include/bta_ag_int.h | 2 - .../bluedroid/bta/include/bta/bta_ag_co.h | 9 +- components/bt/host/bluedroid/hci/hci_hal_h4.c | 3 +- components/bt/sdkconfig.rename | 1 + .../classic_bt/hfp_ag/CMakeLists.txt | 6 + .../bluedroid/classic_bt/hfp_ag/Makefile | 9 + .../bluedroid/classic_bt/hfp_ag/README.md | 282 +++++++++++++++++ .../classic_bt/hfp_ag/main/CMakeLists.txt | 8 + .../classic_bt/hfp_ag/main/app_hf_msg_prs.c | 168 ++++++++++ .../classic_bt/hfp_ag/main/app_hf_msg_prs.h | 48 +++ .../classic_bt/hfp_ag/main/app_hf_msg_set.c | 252 +++++++++++++++ .../classic_bt/hfp_ag/main/app_hf_msg_set.h | 26 ++ .../classic_bt/hfp_ag/main/bt_app_core.c | 112 +++++++ .../classic_bt/hfp_ag/main/bt_app_core.h | 47 +++ .../classic_bt/hfp_ag/main/bt_app_hf.c | 297 ++++++++++++++++++ .../classic_bt/hfp_ag/main/bt_app_hf.h | 27 ++ .../classic_bt/hfp_ag/main/component.mk | 5 + .../classic_bt/hfp_ag/main/console_uart.c | 108 +++++++ .../classic_bt/hfp_ag/main/console_uart.h | 19 ++ .../classic_bt/hfp_ag/main/gpio_pcm_config.c | 92 ++++++ .../classic_bt/hfp_ag/main/gpio_pcm_config.h | 20 ++ .../bluedroid/classic_bt/hfp_ag/main/main.c | 121 +++++++ .../classic_bt/hfp_ag/sdkconfig.defaults | 9 + 27 files changed, 1685 insertions(+), 21 deletions(-) create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/Makefile create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/README.md create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/CMakeLists.txt create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_prs.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_prs.h create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_set.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_set.h create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_core.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_core.h create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.h create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/component.mk create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/console_uart.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/console_uart.h create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/gpio_pcm_config.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/gpio_pcm_config.h create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/main.c create mode 100644 examples/bluetooth/bluedroid/classic_bt/hfp_ag/sdkconfig.defaults diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c index 39387f594..db0766470 100644 --- a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_act.c @@ -398,8 +398,9 @@ void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) bta_sys_conn_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); /* call close call-out */ - // bta_ag_sco_co_close(close.hdr.handle); +#if (BTM_SCO_HCI_INCLUDED == TRUE) bta_ag_sco_co_close(); +#endif /* call close cback */ (*bta_ag_cb.p_cback)(BTA_AG_CLOSE_EVT, (tBTA_AG *) &close); @@ -463,7 +464,9 @@ void bta_ag_rfc_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) bta_ag_at_init(&p_scb->at_cb); /* call app open call-out */ +#if (BTM_SCO_HCI_INCLUDED == TRUE) bta_ag_sco_co_open(bta_ag_scb_to_idx(p_scb), p_scb->air_mode, BTA_HFP_SCO_OUT_PKT_SIZE, bta_ag_svc_id[p_scb->conn_service]); +#endif bta_sys_conn_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); bta_ag_cback_open(p_scb, NULL, BTA_AG_SUCCESS); diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c index 529047ac8..4e146d038 100644 --- a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_main.c @@ -24,6 +24,7 @@ #include #include +#include "bta_ag_int.h" #include "bta/bta_api.h" #include "bta/bta_sys.h" #include "bta/bta_ag_api.h" @@ -32,7 +33,6 @@ #include "common/bt_defs.h" #include "common/bt_trace.h" #include "osi/allocator.h" -#include "bta_ag_int.h" #if (BTA_AG_INCLUDED == TRUE) /***************************************************************************** diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c index b6288da9f..7af0ba320 100644 --- a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_rfc.c @@ -188,7 +188,9 @@ static int bta_ag_data_cback(UINT16 port_handle, void *p_data, UINT16 len, UINT1 UNUSED(port_handle); /* call data call-out directly */ +#if (BTM_SCO_HCI_INCLUDED == TRUE) bta_ag_co_tx_write(handle, (UINT8 *) p_data, len); +#endif return 0; } diff --git a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c index 31c852cc7..351b7bca7 100644 --- a/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c +++ b/components/bt/host/bluedroid/bta/hf_ag/bta_ag_sco.c @@ -66,9 +66,7 @@ enum BTA_AG_SCO_SHUTDOWN_E, /* shutdown request */ BTA_AG_SCO_CONN_OPEN_E, /* sco open */ BTA_AG_SCO_CONN_CLOSE_E, /* sco closed */ -#if (BTM_SCO_HCI_INCLUDED == TRUE) BTA_AG_SCO_CI_DATA_E /* SCO data ready */ -#endif }; #if (BTM_WBS_INCLUDED == TRUE ) @@ -579,6 +577,7 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig) /* tell sys to stop av if any */ bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); +#if (BTM_SCO_HCI_INCLUDED == TRUE) #if (BTM_WBS_INCLUDED == TRUE) /* Allow any platform specific pre-SCO set up to take place */ bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, esco_codec); @@ -595,6 +594,7 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig) /* Allow any platform specific pre-SCO set up to take place */ bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); #endif +#endif #if (BTM_SCO_HCI_INCLUDED == TRUE) #if (BTM_WBS_INCLUDED == TRUE) @@ -735,10 +735,7 @@ void bta_ag_codec_negotiate(tBTA_AG_SCB *p_scb) *******************************************************************************/ static void bta_ag_sco_event(tBTA_AG_SCB *p_scb, UINT8 event) { -#if (BTM_SCO_HCI_INCLUDED == TRUE) tBTA_AG_SCO_CB *p_sco = &bta_ag_cb.sco; - BT_HDR *p_buf; -#endif #if (BTM_WBS_INCLUDED == TRUE) tBTA_AG_SCB *p_cn_scb = NULL; /* For codec negotiation */ #endif @@ -748,6 +745,7 @@ static void bta_ag_sco_event(tBTA_AG_SCB *p_scb, UINT8 event) bta_ag_sco_state_str(p_sco->state), event, bta_ag_sco_evt_str(event)); #if (BTM_SCO_HCI_INCLUDED == TRUE) + BT_HDR *p_buf; if (event == BTA_AG_SCO_CI_DATA_E) { UINT16 pkt_offset = 1 + HCI_SCO_PREAMBLE_SIZE; @@ -1512,7 +1510,9 @@ void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) *******************************************************************************/ void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) { +#if (BTM_SCO_HCI_INCLUDED == TRUE) UINT16 handle = bta_ag_scb_to_idx(p_scb); +#endif UNUSED(p_data); /* clear current scb */ @@ -1542,6 +1542,7 @@ void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) #endif else { +#if (BTM_SCO_HCI_INCLUDED == TRUE) sco_state_t sco_state = bta_ag_cb.sco.p_xfer_scb ? SCO_STATE_OFF_TRANSFER : SCO_STATE_OFF; #if (BTM_WBS_INCLUDED == TRUE) /* Indicate if the closing of audio is because of transfer */ @@ -1549,6 +1550,7 @@ void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) #else /* Indicate if the closing of audio is because of transfer */ bta_ag_sco_audio_state(handle, p_scb->app_id, sco_state); +#endif #endif bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_CLOSE_E); @@ -1626,17 +1628,15 @@ void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data /* tell sys to stop av if any */ bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); -#if (BTM_WBS_INCLUDED == FALSE) - /* Allow any platform specific pre-SCO set up to take place */ - bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); -#else +#if (BTM_SCO_HCI_INCLUDED == TRUE) +#if (BTM_WBS_INCLUDED == TRUE) /* When HS initiated SCO, it cannot be WBS. */ /* Allow any platform specific pre-SCO set up to take place */ - bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, - BTA_AG_CODEC_CVSD); + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, BTA_AG_CODEC_CVSD); +#else + /* Allow any platform specific pre-SCO set up to take place */ + bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); #endif - -#if (BTM_SCO_HCI_INCLUDED == TRUE) pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */ BTM_ConfigScoPath(bta_ag_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id), diff --git a/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h b/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h index 440fe1931..ae63ba470 100644 --- a/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h +++ b/components/bt/host/bluedroid/bta/hf_ag/include/bta_ag_int.h @@ -110,9 +110,7 @@ enum BTA_AG_CI_RX_WRITE_EVT, BTA_AG_RING_TOUT_EVT, BTA_AG_SVC_TOUT_EVT, - #if (BTM_SCO_HCI_INCLUDED == TRUE ) BTA_AG_CI_SCO_DATA_EVT, - #endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ BTA_AG_CI_SLC_READY_EVT, BTA_AG_MAX_EVT, diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_ag_co.h b/components/bt/host/bluedroid/bta/include/bta/bta_ag_co.h index 818611d8f..fb1c1bfde 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_ag_co.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_ag_co.h @@ -21,13 +21,14 @@ * This is the interface file for audio gateway call-out and call-in functions. * ******************************************************************************/ -#ifndef BTA_AG_CIO_H -#define BTA_AG_CIO_H +#ifndef BTA_AG_CO_H +#define BTA_AG_CO_H #include "bta/bta_ag_api.h" #include "hci/hci_audio.h" #if (BTA_AG_INCLUDED == TRUE) +#if (BTM_SCO_HCI_INCLUDED == TRUE) /******************************************************************************* ** ** Function bta_ag_sco_audio_state @@ -155,6 +156,8 @@ extern void bta_ag_ci_rx_write(UINT16 handle, char *p_data, UINT16 len); ******************************************************************************/ extern void bta_ag_ci_slc_ready(UINT16 handle); +#endif /* #if (BTM_SCO_HCI_INCLUDED == TRUE) */ + #endif /* #if (BTA_AG_INCLUDED == TRUE) */ -#endif /* BTA_AG_CIO_H */ \ No newline at end of file +#endif /* BTA_AG_CO_H */ \ No newline at end of file diff --git a/components/bt/host/bluedroid/hci/hci_hal_h4.c b/components/bt/host/bluedroid/hci/hci_hal_h4.c index 85d3598dc..5508841ea 100644 --- a/components/bt/host/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/host/bluedroid/hci/hci_hal_h4.c @@ -228,14 +228,15 @@ static void hci_update_adv_report_flow_control(BT_HDR *packet) // update adv free number hci_hal_env.adv_free_num ++; if (esp_vhci_host_check_send_available()){ +#if (BLE_INCLUDED == TRUE) // send hci cmd btsnd_hcic_ble_update_adv_report_flow_control(hci_hal_env.adv_free_num); +#endif hci_hal_env.adv_free_num = 0; } else { //do nothing } } - } #endif diff --git a/components/bt/sdkconfig.rename b/components/bt/sdkconfig.rename index 8c67aa5d3..c0fa93caa 100644 --- a/components/bt/sdkconfig.rename +++ b/components/bt/sdkconfig.rename @@ -45,6 +45,7 @@ CONFIG_A2DP_ENABLE CONFIG_BT_A2DP_ENABL CONFIG_HFP_ENABLE CONFIG_BT_HFP_ENABLE CONFIG_HFP_ROLE CONFIG_BT_HFP_ROLE CONFIG_HFP_CLIENT_ENABLE CONFIG_BT_HFP_CLIENT_ENABLE +CONFIG_HFP_AG_ENABLE CONFIG_BT_HFP_AG_ENABLE CONFIG_HFP_AUDIO_DATA_PATH CONFIG_BT_HFP_AUDIO_DATA_PATH CONFIG_HFP_AUDIO_DATA_PATH_PCM CONFIG_BT_HFP_AUDIO_DATA_PATH_PCM CONFIG_HFP_AUDIO_DATA_PATH_HCI CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/CMakeLists.txt new file mode 100644 index 000000000..ab8aa0ac1 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(hfp_ag) diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/Makefile b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/Makefile new file mode 100644 index 000000000..b539a2d52 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := hfp_ag + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/README.md b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/README.md new file mode 100644 index 000000000..c0e694e72 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/README.md @@ -0,0 +1,282 @@ +# Hands-Free Audio Gateway + +This example is to show how to use the APIs of Hands-Free (HF) Audio Gateway (AG) Component and the effects of them with the help of user commands. You can use this example to communicate with a Hands-Free Unit (e.g. a headphone set). This example uses UART as a transportation of user commands and configured GPIO for PCM audio data stream. + +## How to use example + +### Hardware Required + +If possible, example should be able to run on any commonly available ESP32 development board and is supposed to connect to _Hands Free Unit example (hfp_hf)_ in ESP-IDF. + +### Configure the project + +``` +idf.py menuconfig +``` + +- Enable `Classic Bluetooth`, `Hands Free/Handset` and `Aduio Gateway` under `Component config ---> Bluetooth ---> Bluedroid Options`. +- When using PCM as the data path and this example configures PCM audio data to GPIO pins. You can link the GPIO pins to a speaker via i2s port. PCM data path does not support mSBC codec but CVSD codec. +- When using HCI data path, ESP32 support both CVSD and mSBC codec. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +When you run this example the command table will prints like: + +``` +######################################################################## +HFP AG command usage manual +HFP AG commands begins with "hf" and ends with ";" +Supported commands are as follows, arguments are embraced with < and > + +hf con; -- setup connection with peer device +hf dis; -- release connection with peer device +hf cona; -- setup audio connection with peer device +hf disa; -- release connection with peer device +hf vron; -- start voice recognition +hf vroff; -- stop voice recognition +hf vu ; -- volume update + tgt: 0-speaker, 1-microphone + vol: volume gain ranges from 0 to 15 +hf ind ; -- unsolicited notify device notification to HF Client + call: call status [0,1] + callsetup: call setup status [0,3] + ntk: network status [0,1] + sig: signal strength value from 0~5 +hf ate ; -- send extended at error code + rep: response code from 0 to 7 + err: error code from 0 to 32 +hf iron; -- inband ring tone provided +hf iroff; -- inband ring tone not provided +hf ac; -- Answer Incoming Call from AG +hf rc; -- Reject Incoming Call from AG +hf d ; -- Dial Number by AG, e.g. hf d 11223344 +hf end; -- End up a call by AG +hf h; -- to see the command for HFP AG +######################################################################## +``` + +The commands help table will print out in monitor whenever you type `hf h;` or input a command that is not required by the command parse rule. + +### Service Level Connection and Disconnection + +You can type `hf con;` to establish a service level connection with HF Unit device and log prints like: + +``` +E (100147) CNSL: Command [hf dis;] +disconnect +W (100427) BT_RFCOMM: port_rfc_closed RFCOMM connection in state 3 closed: Closed (res: 19) +W (100427) BT_APPL: BTA_HF_CLIENT_SCO_SHUTDOWN_ST: Ignoring event 3 +E (100427) BT_HF: APP HFP event: CONNECTION_STATE_EVT +E (100437) BT_HF: --connection state disconnected, peer feats 0x0, chld_feats 0x0 +``` + +**Note: Only after HF service is initiated and a service level connection exists between the HF Unit and AG that other commands are available.** + +You can type `hf dis;` to disconnect with the connected HF Unit device, and log prints like: + +``` +E (100147) CNSL: Command [hf dis;] +disconnect +W (100427) BT_RFCOMM: port_rfc_closed RFCOMM connection in state 3 closed: Closed (res: 19) +W (100427) BT_APPL: BTA_HF_CLIENT_SCO_SHUTDOWN_ST: Ignoring event 3 +E (100427) BT_HF: APP HFP event: CONNECTION_STATE_EVT +E (100437) BT_HF: --connection state disconnected, peer feats 0x0, chld_feats 0x0 +``` + +### Audio Connection and Disconnection + +You can type `hf cona;` to establish the audio connection between HF Unit and AG device. Also, you can type `hf disa;` to close the audio data stream. + +#### Situations for Audio Connection + +- Answer an incoming call +- Enable voice recognition +- Dial an outgoing call + +#### Situations for Audio Disconnection + +- Reject an incoming call +- Disable the voice recognition + +#### Audio Data path + +ESP32 supports two type of audio data pth: PCM and HCI. + +- PCM : When using PCM audio data stream is "matrixed" to GPIO pins and you should link these GPIO pins to a speaker via i2s port. +- HCI : When using HCI audio data stream will act in "loopback" mode. For example, you can hear your own voice when you place a call to a phone connected with a ESP32 development borad. + +#### Codec + +ESP32 supports both CVSD and mSBC codec. HF Unit and AG device determine which codec to use when establishing a service level connection. The choice of codec also depends on the user's configuration in `menuconfig`. + +Since CVSD is the default codec in HFP, we just show the situation using mSBC : + +- If you enable `BT_HFP_WBS_ENABLE` in `menuconfig`, mSBC will be available. +- If both HF Unit and AG support mSBC and also `BT_HFP_WBS_ENABLE` is enabled, ESP32 chooses mSBC. +- If you use PCM data path, mSBC is not available. + +### Answer or Reject an Incoming Call + +#### Answer an Incoming Call + +You can type `hf ac;` to answer an incoming call and log prints like: + +``` +E (1066280) CNSL: Command [hf ac;] +Answer Call from AG. +W (1066280) BT_APPL: BTA_AG_SCO_CODEC_ST: Ignoring event 1 +I (1067200) BT_APP_HF: APP HFP event: BCS_EVT +I (1067200) BT_APP_HF: --AG choose codec mode: CVSD Only +E (1067230) BT_BTM: btm_sco_connected, handle 180 +I (1067240) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT +I (1067240) BT_APP_HF: --Audio State connected +``` + +#### Reject an Incoming Call + +You can type `hf rc;` to reject an incoming call and log prints like: + +``` +E (10040) CNSL: Command [hf rc;] +Reject Call from AG. +I (1067240) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT +I (1067240) BT_APP_HF: --Audio State disconnected +``` + +#### End Up a Call + +You can type `hf end;` to end up the current ongoing call and log prints like: + +``` +E (40390) CNSL: Command [hf end;] +End Call from AG. +I (1067240) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT +I (1067240) BT_APP_HF: --Audio State disconnected +``` + +### Dial Number + +You can type `hf d ;` to dial `` from AG and log prints like: + +``` +E (105600) CNSL: Command [hf d 18629485549;] +Dial number 18629485549 +I (105610) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT +I (105610) BT_APP_HF: --Audio State connecting +I (106120) BT_APP_HF: APP HFP event: BCS_EVT +I (106130) BT_APP_HF: --AG choose codec mode: CVSD Only +E (106160) BT_BTM: btm_sco_connected, handle 180 +I (106160) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT +I (106160) BT_APP_HF: --Audio State connected + +``` + +### Volume Control + +You can type `hf vu ;` to sync volume gain of headset or microphone. The parameter set: + +- `` : 0 - headset, 1 - microphone. +- `` : Integer among 0 - 15. + +For example, `hf vu 0 9;` sync the volume of headset and log on AG side prints `volume update`, on HF Unit side log prints like: + +``` +E (17087) BT_HF: APP HFP event: VOLUME_CONTROL_EVT +E (17087) BT_HF: --volume_target: SPEAKER, volume 9 + +``` + +And also, `hf vu 1 9;` sync the volume gain of microphone and log on HF Unit side prints like: + +``` +E (32087) BT_HF: APP HFP event: VOLUME_CONTROL_EVT +E (32087) BT_HF: --volume_target: MICROPHONE, volume 9 + +``` + +#### Voice Recognition + +You can type `hf vron;` to start the voice recognition of AG and type `hf vroff;` to end the voice recognition. Both command will notify the HF Unit the status of voice recognition. For example, type `hf vron;` and log prints like: + +``` +E (203128) CNSL: Command [hf vron;] +Start Voice Recognition. +I (203138) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT +I (203138) BT_APP_HF: --Audio State connecting +I (203148) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT +I (1014138) BT_APP_HF: --Audio State connected + +``` + +#### Notify Device Notification + +When use `hf ind ` AG will send some device status to HF Unit. Log on AG will printout like: `Device Indicator Changed!` and on HF Unit side will prints like: + +``` +E (293641) BT_HF: APP HFP event: CALL_IND_EVT +E (293641) BT_HF: --Call indicator call in progress +E (293641) BT_HF: APP HFP event: CALL_SETUP_IND_EVT +E (293651) BT_HF: --Call setup indicator INCOMING +E (293651) BT_HF: APP HFP event: SIGNAL_STRENGTH_IND_EVT +E (293661) BT_HF: -- signal strength: 5 + +``` + +**Note: AG only sends changed status to HF Unit.** + +#### Send Extended AT Error Code + +You can type `hf ate ` to send extended AT error coed to HF Unit. Parameter set: + +- `` : integer among 0 - 7. +- `` : integer among 0 - 32. + +When you type `hf ate 7 7;` log on AG side prints like `Send CME Error.` and on HF Unit side prints like: + +``` +E (448146) BT_HF: APP HFP event: AT_RESPONSE +E (448146) BT_HF: --AT response event, code 7, cme 7 + +``` + +#### Inband Ring Tone Enable and Disable + +You can type `hf iron;` to enable inband ring tone and type `hf iroff;` to disable inband ring tone. Log on AG side prints like `Device Indicator Changed!` and on HF Unit side prints like: + +``` +E (19546) BT_HF: APP HFP event: INBAND_RING_TONE_EVT +E (19556) BT_HF: --inband ring state Provided + +``` + +## Troubleshooting + +- You should type the command in the terminal according to the format described in the command help table. +- Not all commands in the table are supported by HF Unit. +- If you want to use AG to establish a service level connection with HF Unit, you should add the MAC address of HF Unit in `hfp_hf/main/bt_app.c`, for example: `esp_bd_addr_t peer_addr = {0xb4, 0xe6, 0x2d, 0xeb, 0x09, 0x93};` +- Use `esp_hf_client_register_callback()` and `esp_hf_client_init();` before establishing a service level connection. + +## Example Breakdown + +This example has relatively more source files than other bluetooth examples because _Hands Free Profile_ is somehow complex. But we want to show the functions of _Hands Free Profile_ in a simple way, so we use the _Commands and Effects_ scheme to show the usage of APIs of HFP in ESP-IDF. + +- The example will respond to user command through UART console. Please go to `hfp_hf/main/console_uart.c` for the configuration details. +- For voice interface, ESP32 has provided PCM input/output signals which can be mapped to GPIO pins, please go to `hfp_hf/main/gpio_pcm_config.c` for the configuration details. +- If you want to fix the command table, please refer to `hfp_hf/main/app_hf_msg_set.c`. +- If you want to fix the command parse rules, please refer to `hfp_hf/main/app_hf_msg_prs.c`. +- If you want to fix the responses of AG or want to fix the log, please refer to `hfp_hf/main/bt_app_hf.c`. +- Task configuration part is in `hfp_hf/main/bt_app_core.c`. \ No newline at end of file diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/CMakeLists.txt new file mode 100644 index 000000000..eff055634 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register(SRCS "app_hf_msg_prs.c" + "app_hf_msg_set.c" + "bt_app_core.c" + "bt_app_hf.c" + "console_uart.c" + "gpio_pcm_config.c" + "main.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_prs.c b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_prs.c new file mode 100644 index 000000000..2610bf239 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_prs.c @@ -0,0 +1,168 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include +#include +#include "app_hf_msg_prs.h" +#include "app_hf_msg_set.h" + +// according to the design, message header length shall be no less than 2. +#define HF_MSG_HDR_LEN (3) +const static char hf_msg_hdr[HF_MSG_HDR_LEN] = {'h', 'f', ' '}; + +// according to the design, message header length shall be no less than 2. +#define HF_MSG_TAIL_LEN (1) +const static char hf_msg_tail[HF_MSG_TAIL_LEN] = {';'}; + +void hf_msg_parser_reset_state(hf_msg_prs_cb_t *prs) +{ + prs->state = HF_MSG_PRS_IDLE; + prs->cnt = 0; + prs->h_idx = 0; + prs->t_idx = 0; +} + +void hf_msg_parser_register_callback(hf_msg_prs_cb_t *prs, hf_msg_callback cb) +{ + prs->callback = cb; +} + +hf_msg_prs_err_t hf_msg_parse(char c, hf_msg_prs_cb_t *prs) +{ + hf_msg_prs_err_t err = HF_MSG_PRS_ERR_IN_PROGRESS; + switch (prs->state) + { + case HF_MSG_PRS_IDLE: + { + if (c == hf_msg_hdr[0]) { + prs->state = HF_MSG_PRS_HDR; + prs->buf[0] = c; + prs->cnt = 1; + prs->h_idx = 1; + } else { + err = HF_MSG_PRS_ERR_HDR_UNDETECTED; + } + } + break; + + case HF_MSG_PRS_HDR: + { + if (c == hf_msg_hdr[prs->h_idx]) { + prs->buf[prs->cnt++] = c; + if (++(prs->h_idx) == HF_MSG_HDR_LEN) { + prs->state = HF_MSG_PRS_PAYL; + prs->t_idx = 0; + } + } else { + hf_msg_parser_reset_state(prs); + err = HF_MSG_PRS_ERR_HDR_SYNC_FAILED; + } + } + break; + + case HF_MSG_PRS_PAYL: + { + prs->buf[prs->cnt++] = c; + if (c == hf_msg_tail[prs->t_idx]) { + if (++(prs->t_idx) == HF_MSG_TAIL_LEN) { + prs->buf[prs->cnt] = '\0'; + prs->callback(prs->buf, prs->cnt); + hf_msg_parser_reset_state(prs); + err = HF_MSG_PRS_ERR_OK; + break; + } + } else { + prs->t_idx = 0; + } + + if (prs->cnt >= HF_MSG_LEN_MAX) { + hf_msg_parser_reset_state(prs); + err = HF_MSG_PRS_ERR_BUF_OVERFLOW; + } + } + break; + } + return err; +} + + +void hf_msg_split_args(char *start, char *end, char **argv, int *argn) +{ + if (argn == NULL || *argn == 0) { + return; + } + + memset(argv, 0, (*argn) * sizeof(void *)); + + int max_argn = *argn; + *argn = 0; + + char *p = start; + for (int i = 0; i < max_argn; ++i) { + while (isspace((int)*p) && p != end) { + ++p; + } + if (p == end) { + return; + } + + argv[i] = p++; + ++(*argn); + + while (!isspace((int)*p) && p != end) { + ++p; + } + + if (p == end) { + return; + } else { + *p = '\0'; + ++p; + } + } +} + +void hf_msg_args_parser(char *buf, int len) +{ + char *argv[HF_MSG_ARGS_MAX]; + int argn = HF_MSG_ARGS_MAX; + char *start = buf + HF_MSG_HDR_LEN; + // set the command terminitor to '\0' + char *end = buf + len - HF_MSG_TAIL_LEN; + *end = '\0'; + + hf_msg_split_args(start, end, argv, &argn); + + if (argn == 0) { + return; + } + + bool cmd_supported = false; + + hf_msg_hdl_t *cmd_tbl = hf_get_cmd_tbl(); + size_t cmd_tbl_size = hf_get_cmd_tbl_size(); + for (int i = 0; i < cmd_tbl_size; ++i) { + hf_msg_hdl_t *hdl = &cmd_tbl[i]; + if (strcmp(argv[0], hdl->str) == 0) { + if (hdl->handler) { + hdl->handler(argn, argv); + cmd_supported = true; + break; + } + } + } + if (!cmd_supported) { + printf("unsupported command\n"); + hf_msg_show_usage(); + } + return; +} diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_prs.h b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_prs.h new file mode 100644 index 000000000..131a7a541 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_prs.h @@ -0,0 +1,48 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef __APP_HF_MSG_PRS_H__ +#define __APP_HF_MSG_PRS_H__ + +typedef enum { + HF_MSG_PRS_ERR_OK = 0, // a complete message is finished + HF_MSG_PRS_ERR_IN_PROGRESS, // message parsing is in progress + HF_MSG_PRS_ERR_HDR_UNDETECTED, // header not detected + HF_MSG_PRS_ERR_HDR_SYNC_FAILED, // failed to sync header + HF_MSG_PRS_ERR_BUF_OVERFLOW, // exceeds the buffer size: HF_MSG_LEN_MAX +} hf_msg_prs_err_t; + +typedef enum { + HF_MSG_PRS_IDLE = 0, + HF_MSG_PRS_HDR, + HF_MSG_PRS_PAYL, +} hf_msg_prs_state_t; + +typedef void (*hf_msg_callback)(char *buf, int len); + +#define HF_MSG_LEN_MAX (128) + +typedef struct { + hf_msg_prs_state_t state; + char buf[HF_MSG_LEN_MAX + 1]; + int cnt; + int h_idx; + int t_idx; + hf_msg_callback callback; +} hf_msg_prs_cb_t; + +void hf_msg_parser_reset_state(hf_msg_prs_cb_t *prs); + +void hf_msg_parser_register_callback(hf_msg_prs_cb_t *prs, hf_msg_callback cb); + +hf_msg_prs_err_t hf_msg_parse(char c, hf_msg_prs_cb_t *prs); + +void hf_msg_show_usage(void); + +#endif /* __APP_HF_MSG_PRS_H__*/ + diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_set.c b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_set.c new file mode 100644 index 000000000..00aadb3a4 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_set.c @@ -0,0 +1,252 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include "esp_hf_ag_api.h" +#include "app_hf_msg_set.h" +#include "bt_app_hf.h" + +void hf_msg_show_usage(void) +{ + printf("########################################################################\n"); + printf("HFP AG command usage manual\n"); + printf("HFP AG commands begins with \"hf\" and ends with \";\"\n"); + printf("Supported commands are as follows, arguments are embraced with < and >\n\n"); + printf("hf con; -- setup connection with peer device\n"); + printf("hf dis; -- release connection with peer device\n"); + printf("hf cona; -- setup audio connection with peer device\n"); + printf("hf disa; -- release connection with peer device\n"); + printf("hf vron; -- start voice recognition\n"); + printf("hf vroff; -- stop voice recognition\n"); + printf("hf vu ; -- volume update\n"); + printf(" tgt: 0-speaker, 1-microphone\n"); + printf(" vol: volume gain ranges from 0 to 15\n"); + printf("hf ind ; -- unsolicited notify device notification to HF Client\n"); + printf(" call: call status [0,1]\n"); + printf(" callsetup: call setup status [0,3]\n"); + printf(" ntk: network status [0,1]\n"); + printf(" sig: signal strength value from 0~5\n"); + printf("hf ate ; -- send extended at error code\n"); + printf(" rep: response code from 0 to 7\n"); + printf(" err: error code from 0 to 32\n"); + printf("hf iron; -- inband ring tone provided\n"); + printf("hf iroff; -- inband ring tone not provided\n"); + printf("hf ac; -- Answer Incoming Call from AG\n"); + printf("hf rc; -- Reject Incoming Call from AG\n"); + printf("hf d ; -- Dial Number by AG, e.g. hf d 11223344\n"); + printf("hf end; -- End up a call by AG\n"); + printf("hf h; -- to see the command for HFP AG\n"); + printf("########################################################################\n"); +} + +#define HF_CMD_HANDLER(cmd) static void hf_##cmd##_handler(int argn, char **argv) + +HF_CMD_HANDLER(help) +{ + hf_msg_show_usage(); +} + +HF_CMD_HANDLER(conn) +{ + printf("Connect.\n"); + esp_bt_hf_connect(hf_peer_addr); +} + +HF_CMD_HANDLER(disc) +{ + printf("Disconnect\n"); + esp_bt_hf_disconnect(hf_peer_addr); +} + +HF_CMD_HANDLER(conn_audio) +{ + printf("Connect Audio\n"); + esp_bt_hf_connect_audio(hf_peer_addr); +} + +HF_CMD_HANDLER(disc_audio) +{ + printf("Disconnect Audio\n"); + esp_bt_hf_disconnect_audio(hf_peer_addr); +} + +//AT+BVRA +HF_CMD_HANDLER(vra_on) +{ + printf("Start Voice Recognition.\n"); + esp_bt_hf_vra(hf_peer_addr,1); +} +//AT+BVRA +HF_CMD_HANDLER(vra_off) +{ + printf("Stop Voicer Recognition.\n"); + esp_bt_hf_vra(hf_peer_addr,0); +} + +//AT+VGS or AT+VGM +HF_CMD_HANDLER(volume_control) +{ + if (argn != 3) { + printf("Insufficient number of arguments"); + return; + } + int target, volume; + if (sscanf(argv[1], "%d", &target) != 1 || + (target != ESP_HF_VOLUME_CONTROL_TARGET_SPK && + target != ESP_HF_VOLUME_CONTROL_TARGET_MIC)) { + printf("Invalid argument for target %s\n", argv[1]); + return; + } + if (sscanf(argv[2], "%d", &volume) != 1 || + (volume < 0 || volume > 15)) { + printf("Invalid argument for volume %s\n", argv[2]); + return; + } + printf("Volume Update\n"); + esp_bt_hf_volume_control(hf_peer_addr, target, volume); +} + +//+CIEV +HF_CMD_HANDLER(ind_change) +{ + if (argn != 5) { + printf("Insufficient number of arguments"); + return; + } + + int call_state, ntk_state, call_setup_state, signal; + + if (sscanf(argv[1], "%d", &call_state) != 1 || + (call_state != ESP_HF_CALL_STATUS_NO_CALLS && + call_state != ESP_HF_CALL_STATUS_CALL_IN_PROGRESS)) { + printf("Invalid argument for call state %s\n", argv[1]); + return; + } + if (sscanf(argv[2], "%d", &call_setup_state) != 1 || + (call_setup_state < ESP_HF_CALL_SETUP_STATUS_IDLE || call_setup_state > ESP_HF_CALL_SETUP_STATUS_OUTGOING_ALERTING)) { + printf("Invalid argument for callsetup state %s\n", argv[2]); + return; + } + if (sscanf(argv[3], "%d", &ntk_state) != 1 || + (ntk_state != ESP_HF_NETWORK_STATE_NOT_AVAILABLE && + ntk_state != ESP_HF_NETWORK_STATE_AVAILABLE)) { + printf("Invalid argument for netwrok state %s\n", argv[3]); + return; + } + if (sscanf(argv[4], "%d", &signal) != 1 || + (signal < 0 || signal > 5)) { + printf("Invalid argument for signal %s\n", argv[4]); + return; + } + printf("Device Indicator Changed!\n"); + esp_bt_hf_indchange_notification(hf_peer_addr, call_state, call_setup_state, ntk_state, signal); +} + +//AT+CMEE +HF_CMD_HANDLER(cme_err) +{ + if (argn != 3) { + printf("Insufficient number of arguments"); + return; + } + + int response_code, error_code; + if (sscanf(argv[1], "%d", &response_code) != 1 || + (response_code < ESP_HF_AT_RESPONSE_CODE_OK && response_code > ESP_HF_AT_RESPONSE_CODE_CME)) { + printf("Invalid argument for response_code %s\n", argv[1]); + return; + } + + if (sscanf(argv[2], "%d", &error_code) != 1 || + (error_code < ESP_HF_CME_AG_FAILURE || error_code > ESP_HF_CME_NETWORK_NOT_ALLOWED)) { + printf("Invalid argument for volume %s\n", argv[2]); + return; + } + + printf("Send CME Error.\n"); + esp_bt_hf_cmee_response(hf_peer_addr,response_code,error_code); +} + +//+BSIR:1 +HF_CMD_HANDLER(ir_on) +{ + printf("Enable Voicer Recognition.\n"); + esp_bt_hf_bsir(hf_peer_addr,1); +} + +//+BSIR:0 +HF_CMD_HANDLER(ir_off) +{ + printf("Disable Voicer Recognition.\n"); + esp_bt_hf_bsir(hf_peer_addr,0); +} + +//Answer Call from AG +HF_CMD_HANDLER(ac) +{ + printf("Answer Call from AG.\n"); + char *number = {"186xxxx5549"}; + esp_bt_hf_answer_call(hf_peer_addr,1,0,1,1,number,0); +} + +//Reject Call from AG +HF_CMD_HANDLER(rc) +{ + printf("Reject Call from AG.\n"); + char *number = {"186xxxx5549"}; + esp_bt_hf_reject_call(hf_peer_addr,0,0,0,0,number,0); +} + +//End Call from AG +HF_CMD_HANDLER(end) +{ + printf("End Call from AG.\n"); + char *number = {"186xxxx5549"}; + esp_bt_hf_end_call(hf_peer_addr,0,0,0,0,number,0); +} + +//Dial Call from AG +HF_CMD_HANDLER(d) +{ + if (argn != 2) { + printf("Insufficient number of arguments"); + } else { + printf("Dial number %s\n", argv[1]); + esp_bt_hf_out_call(hf_peer_addr,1,0,1,2,argv[1],0); + } +} + +static hf_msg_hdl_t hf_cmd_tbl[] = { + {0, "h", hf_help_handler}, + {5, "con", hf_conn_handler}, + {10, "dis", hf_disc_handler}, + {20, "cona", hf_conn_audio_handler}, + {30, "disa", hf_disc_audio_handler}, + {40, "vu", hf_volume_control_handler}, + {50, "ind", hf_ind_change_handler}, + {60, "vron", hf_vra_on_handler}, + {70, "vroff", hf_vra_off_handler}, + {80, "ate", hf_cme_err_handler}, + {90, "iron", hf_ir_on_handler}, + {100, "iroff", hf_ir_off_handler}, + {110, "ac", hf_ac_handler}, + {120, "rc", hf_rc_handler}, + {130, "end", hf_end_handler}, + {140, "d", hf_d_handler}, +}; + +hf_msg_hdl_t *hf_get_cmd_tbl(void) +{ + return hf_cmd_tbl; +} + +size_t hf_get_cmd_tbl_size(void) +{ + return sizeof(hf_cmd_tbl) / sizeof(hf_msg_hdl_t); +} diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_set.h b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_set.h new file mode 100644 index 000000000..de59abf83 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/app_hf_msg_set.h @@ -0,0 +1,26 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef __APP_HF_MSG_SET_H__ +#define __APP_HF_MSG_SET_H__ + +#define HF_MSG_ARGS_MAX (8) + +typedef void (* hf_cmd_handler)(int argn, char **argv); + +typedef struct { + uint16_t opcode; + const char *str; + hf_cmd_handler handler; +} hf_msg_hdl_t; + +extern hf_msg_hdl_t *hf_get_cmd_tbl(void); +extern size_t hf_get_cmd_tbl_size(void); + +void hf_msg_show_usage(void); +#endif /* __APP_HF_MSG_SET_H__*/ diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_core.c b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_core.c new file mode 100644 index 000000000..89749c674 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_core.c @@ -0,0 +1,112 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include "freertos/xtensa_api.h" +#include "freertos/FreeRTOSConfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "bt_app_core.h" + +static void bt_app_task_handler(void *arg); +static bool bt_app_send_msg(bt_app_msg_t *msg); +static void bt_app_work_dispatched(bt_app_msg_t *msg); + +static xQueueHandle bt_app_task_queue = NULL; +static xTaskHandle bt_app_task_handle = NULL; + +bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback) +{ + ESP_LOGD(BT_APP_CORE_TAG, "%s event 0x%x, param len %d", __func__, event, param_len); + + bt_app_msg_t msg; + memset(&msg, 0, sizeof(bt_app_msg_t)); + + msg.sig = BT_APP_SIG_WORK_DISPATCH; + msg.event = event; + msg.cb = p_cback; + + if (param_len == 0) { + return bt_app_send_msg(&msg); + } else if (p_params && param_len > 0) { + if ((msg.param = malloc(param_len)) != NULL) { + memcpy(msg.param, p_params, param_len); + /* check if caller has provided a copy callback to do the deep copy */ + if (p_copy_cback) { + p_copy_cback(&msg, msg.param, p_params); + } + return bt_app_send_msg(&msg); + } + } + return false; +} + +static bool bt_app_send_msg(bt_app_msg_t *msg) +{ + if (msg == NULL) { + return false; + } + + if (xQueueSend(bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) { + ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__); + return false; + } + return true; +} + +static void bt_app_work_dispatched(bt_app_msg_t *msg) +{ + if (msg->cb) { + msg->cb(msg->event, msg->param); + } +} + +static void bt_app_task_handler(void *arg) +{ + bt_app_msg_t msg; + for (;;) { + if (pdTRUE == xQueueReceive(bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) { + ESP_LOGD(BT_APP_CORE_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event); + switch (msg.sig) { + case BT_APP_SIG_WORK_DISPATCH: + bt_app_work_dispatched(&msg); + break; + default: + ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: %d", __func__, msg.sig); + break; + } // switch (msg.sig) + + if (msg.param) { + free(msg.param); + } + } + } +} + +void bt_app_task_start_up(void) +{ + bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t)); + xTaskCreate(bt_app_task_handler, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, &bt_app_task_handle); + return; +} + +void bt_app_task_shut_down(void) +{ + if (bt_app_task_handle) { + vTaskDelete(bt_app_task_handle); + bt_app_task_handle = NULL; + } + if (bt_app_task_queue) { + vQueueDelete(bt_app_task_queue); + bt_app_task_queue = NULL; + } +} diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_core.h b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_core.h new file mode 100644 index 000000000..4415058a7 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_core.h @@ -0,0 +1,47 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef __BT_APP_CORE_H__ +#define __BT_APP_CORE_H__ + +#include +#include +#include + +#define BT_APP_CORE_TAG "BT_APP_CORE" + +#define BT_APP_SIG_WORK_DISPATCH (0x01) + +/** + * @brief handler for the dispatched work + */ +typedef void (* bt_app_cb_t) (uint16_t event, void *param); + +/* message to be sent */ +typedef struct { + uint16_t sig; /*!< signal to bt_app_task */ + uint16_t event; /*!< message event id */ + bt_app_cb_t cb; /*!< context switch callback */ + void *param; /*!< parameter area needs to be last */ +} bt_app_msg_t; + +/** + * @brief parameter deep-copy function to be customized + */ +typedef void (* bt_app_copy_cb_t) (bt_app_msg_t *msg, void *p_dest, void *p_src); + +/** + * @brief work dispatcher for the application task + */ +bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback); + +void bt_app_task_start_up(void); + +void bt_app_task_shut_down(void); + +#endif /* __BT_APP_CORE_H__ */ diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.c b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.c new file mode 100644 index 000000000..bf77eac0a --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.c @@ -0,0 +1,297 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include "esp_log.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" +#include "esp_gap_bt_api.h" +#include "esp_hf_ag_api.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "time.h" +#include "sys/time.h" +#include "sdkconfig.h" +#include "bt_app_core.h" +#include "bt_app_hf.h" + +const char *c_hf_evt_str[] = { + "CONNECTION_STATE_EVT", /*!< SERVICE LEVEL CONNECTION STATE CONTROL */ + "AUDIO_STATE_EVT", /*!< AUDIO CONNECTION STATE CONTROL */ + "VR_STATE_CHANGE_EVT", /*!< VOICE RECOGNITION CHANGE */ + "VOLUME_CONTROL_EVT", /*!< AUDIO VOLUME CONTROL */ + "UNKNOW_AT_CMD", /*!< UNKNOW AT COMMAND RECIEVED */ + "CIND_RESPONSE_EVT", /*!< CALL & DEVICE INDICATION */ + "COPS_RESPONSE_EVT", /*!< CURRENT OPERATOR EVENT */ + "CLCC_RESPONSE_EVT", /*!< LIST OF CURRENT CALL EVENT */ + "CNUM_RESPONSE_EVT", /*!< SUBSCRIBER INFORTMATION OF CALL EVENT */ + "DTMF_RESPONSE_EVT", /*!< DTMF TRANSFER EVT */ + "NREC_RESPONSE_EVT", /*!< NREC RESPONSE EVT */ + "ANSWER_INCOMING_EVT", /*!< ANSWER INCOMING EVT */ + "REJECT_INCOMING_EVT", /*!< AREJECT INCOMING EVT */ + "DIAL_EVT", /*!< DIAL INCOMING EVT */ + "BAC_EVT", /*!< CODEC NEGO EVT */ + "BCS_EVT", /*!< CODEC NEGO EVT */ +}; + +//esp_hf_connection_state_t +const char *c_connection_state_str[] = { + "DISCONNECTED", + "CONNECTING", + "CONNECTED", + "SLC_CONNECTED", + "DISCONNECTING", +}; + +// esp_hf_audio_state_t +const char *c_audio_state_str[] = { + "disconnected", + "connecting", + "connected", + "connected_msbc", +}; + +/// esp_hf_vr_state_t +const char *c_vr_state_str[] = { + "Disabled", + "Enabled", +}; + +// esp_hf_nrec_t +const char *c_nrec_status_str[] = { + "NREC DISABLE", + "NREC ABLE", +}; + +// esp_hf_control_target_t +const char *c_volume_control_target_str[] = { + "SPEAKER", + "MICROPHONE", +}; + +// esp_hf_subscriber_service_type_t +char *c_operator_name_str[] = { + "中国移动", + "中国联通", + "中国电信", +}; + +// esp_hf_subscriber_service_type_t +char *c_subscriber_service_type_str[] = { + "UNKNOWN", + "VOICE", + "FAX", +}; + +// esp_hf_nego_codec_status_t +const char *c_codec_mode_str[] = { + "CVSD Only", + "Use CVSD", + "Use MSBC", +}; + +#if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI +// Produce a sine audio +static const int16_t sine_int16[] = { + 0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557, + 19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466, + 31163, 31738, 32187, 32509, 32702, 32767, 32702, 32509, 32187, 31738, + 31163, 30466, 29648, 28714, 27666, 26509, 25247, 23886, 22431, 20886, + 19260, 17557, 15786, 13952, 12062, 10126, 8149, 6140, 4107, 2057, + 0, -2057, -4107, -6140, -8149, -10126, -12062, -13952, -15786, -17557, +-19260, -20886, -22431, -23886, -25247, -26509, -27666, -28714, -29648, -30466, +-31163, -31738, -32187, -32509, -32702, -32767, -32702, -32509, -32187, -31738, +-31163, -30466, -29648, -28714, -27666, -26509, -25247, -23886, -22431, -20886, +-19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057, +}; + +#define TABLE_SIZE_CVSD 100 +static uint32_t bt_app_hf_outgoing_cb(uint8_t *p_buf, uint32_t sz) +{ + int sine_phase = esp_random(); + + for (int i = 0; i < TABLE_SIZE_CVSD; i++) { + p_buf[i * 2] = sine_int16[sine_phase]; + p_buf[i * 2 + 1] = sine_int16[sine_phase]; + ++sine_phase; + if (sine_phase >= TABLE_SIZE_CVSD) { + sine_phase -= TABLE_SIZE_CVSD; + } + } + return sz; +} + +static void bt_app_hf_incoming_cb(const uint8_t *buf, uint32_t sz) +{ + // direct to i2s + esp_hf_outgoing_data_ready(); +} +#endif /* #if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI */ + +void bt_app_hf_cb(esp_hf_cb_event_t event, esp_hf_cb_param_t *param) +{ + if (event <= ESP_HF_BCS_RESPONSE_EVT) { + ESP_LOGI(BT_HF_TAG, "APP HFP event: %s", c_hf_evt_str[event]); + } else { + ESP_LOGE(BT_HF_TAG, "APP HFP invalid event %d", event); + } + + switch (event) { + case ESP_HF_CONNECTION_STATE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--connection state %s, peer feats 0x%x, chld_feats 0x%x", + c_connection_state_str[param->conn_stat.state], + param->conn_stat.peer_feat, + param->conn_stat.chld_feat); + memcpy(hf_peer_addr, param->conn_stat.remote_bda, ESP_BD_ADDR_LEN); + if (param->conn_stat.state == ESP_HF_CONNECTION_STATE_CONNECTING) { + esp_bt_hf_connect(hf_peer_addr); + } else if (param->conn_stat.state == ESP_HF_CONNECTION_STATE_DISCONNECTING) { + esp_bt_hf_disconnect(hf_peer_addr); + } + break; + } + + case ESP_HF_AUDIO_STATE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--Audio State %s", c_audio_state_str[param->audio_stat.state]); +#if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI + if (param->audio_stat.state == ESP_HF_AUDIO_STATE_CONNECTED || + param->audio_stat.state == ESP_HF_AUDIO_STATE_CONNECTED_MSBC) { + esp_bt_hf_register_data_callback(bt_app_hf_incoming_cb, bt_app_hf_outgoing_cb); + } else if (param->audio_stat.state == ESP_HF_AUDIO_STATE_DISCONNECTED) { + ESP_LOGI(BT_HF_TAG, "--ESP AG Audio Connection Disconnected."); + } +#endif /* #if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI */ + break; + } + + case ESP_HF_BVRA_EVT: + { + ESP_LOGI(BT_HF_TAG, "--Voice Recognition is %s", c_vr_state_str[param->vra_rep.value]); + break; + } + + case ESP_HF_VOLUME_CONTROL_EVT: + { + ESP_LOGI(BT_HF_TAG, "--Volume Target: %s, Volume %d", c_volume_control_target_str[param->volume_control.type], param->volume_control.volume); + break; + } + + case ESP_HF_UNAT_RESPONSE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--UNKOW AT CMD: %s", param->unat_rep.unat); + esp_hf_unat_response(hf_peer_addr, param->unat_rep.unat); + break; + } + + case ESP_HF_CIND_RESPONSE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--CIND Start."); + esp_hf_call_status_t call_status = 0; + esp_hf_call_setup_status_t call_setup_status = 0; + esp_hf_network_state_t ntk_state = 1; + int signal = 4; + esp_hf_roaming_status_t roam = 0; + int batt_lev = 3; + esp_hf_call_held_status_t call_held_status = 0; + esp_bt_hf_cind_response(hf_peer_addr,call_status,call_setup_status,ntk_state,signal,roam,batt_lev,call_held_status); + break; + } + + case ESP_HF_COPS_RESPONSE_EVT: + { + const int svc_type = 1; + esp_bt_hf_cops_response(hf_peer_addr, c_operator_name_str[svc_type]); + break; + } + + case ESP_HF_CLCC_RESPONSE_EVT: + { + int index = 1; + //mandatory + esp_hf_current_call_direction_t dir = 1; + esp_hf_current_call_status_t current_call_status = 0; + esp_hf_current_call_mode_t mode = 0; + esp_hf_current_call_mpty_type_t mpty = 0; + //option + char *number = {"186xxxx5549"}; + esp_hf_call_addr_type_t type = ESP_HF_CALL_ADDR_TYPE_UNKNOWN; + + ESP_LOGI(BT_HF_TAG, "--Calling Line Identification."); + esp_bt_hf_clcc_response(hf_peer_addr, index, dir, current_call_status, mode, mpty, number, type); + break; + } + + case ESP_HF_CNUM_RESPONSE_EVT: + { + char *number = {"186xxxx5549"}; + esp_hf_subscriber_service_type_t type = 1; + ESP_LOGI(BT_HF_TAG, "--Current Number is %s ,Type is %s.", number, c_subscriber_service_type_str[type]); + esp_bt_hf_cnum_response(hf_peer_addr, number,type); + break; + } + + case ESP_HF_VTS_RESPONSE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--DTMF code is: %s.", param->vts_rep.code); + break; + } + + case ESP_HF_NREC_RESPONSE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--NREC status is: %s.", c_nrec_status_str[param->nrec.state]); + break; + } + + case ESP_HF_ATA_RESPONSE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--Asnwer Incoming Call."); + char *number = {"186xxxx5549"}; + esp_bt_hf_answer_call(hf_peer_addr,1,0,1,0,number,0); + break; + } + + case ESP_HF_CHUP_RESPONSE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--Reject Incoming Call."); + char *number = {"186xxxx5549"}; + esp_bt_hf_reject_call(hf_peer_addr,0,0,0,0,number,0); + break; + } + + case ESP_HF_DIAL_EVT: + { + if (param->out_call.num_or_loc) { + //dia_num_or_mem + ESP_LOGI(BT_HF_TAG, "--Dial \"%s\".", param->out_call.num_or_loc); + esp_bt_hf_out_call(hf_peer_addr,1,0,1,0,param->out_call.num_or_loc,0); + } else { + //dia_last + ESP_LOGI(BT_HF_TAG, "--Dial last number."); + } + break; + } + + // case ESP_HF_BAC_RESPONSE_EVT: + case ESP_HF_BCS_RESPONSE_EVT: + { + ESP_LOGI(BT_HF_TAG, "--AG choose codec mode: %s",c_codec_mode_str[param->codec.mode]); + break; + } + + default: + ESP_LOGI(BT_HF_TAG, "Unsupported HF_AG EVT: %d.", event); + break; + } +} \ No newline at end of file diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.h b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.h new file mode 100644 index 000000000..e341d0f68 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.h @@ -0,0 +1,27 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef __BT_APP_HF_H__ +#define __BT_APP_HF_H__ + +#include +#include "esp_hf_ag_api.h" +#include "esp_bt_defs.h" + +esp_bd_addr_t hf_peer_addr; // Declaration of peer device bdaddr + +#define BT_HF_TAG "BT_APP_HF" + +/** + * @brief callback function for HF client + */ +void bt_app_hf_cb(esp_hf_cb_event_t event, esp_hf_cb_param_t *param); + + +#endif /* __BT_APP_HF_H__*/ + \ No newline at end of file diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/component.mk b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/component.mk new file mode 100644 index 000000000..0b9d7585e --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/console_uart.c b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/console_uart.c new file mode 100644 index 000000000..bb0870c46 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/console_uart.c @@ -0,0 +1,108 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "driver/uart.h" +#include "freertos/xtensa_api.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_log.h" +#include "console_uart.h" +#include "app_hf_msg_prs.h" + +#define CONSOLE_UART_NUM UART_NUM_0 + +static QueueHandle_t uart_queue; +static hf_msg_prs_cb_t hf_msg_parser; + +static const uart_config_t uart_cfg = { + .baud_rate = 115200, //1.5M + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 127, +}; + +extern void hf_msg_args_parser(char *buf, int len); + +void hf_msg_handler(char *buf, int len) +{ + ESP_LOGE(TAG_CNSL, "Command [%s]", buf); + hf_msg_args_parser(buf, len); +} + +static void console_uart_task(void *pvParameters) +{ + int len; + uart_event_t event; + hf_msg_prs_cb_t *parser = &hf_msg_parser; + hf_msg_parser_reset_state(parser); + hf_msg_parser_register_callback(parser, hf_msg_handler); + hf_msg_show_usage(); +#define TMP_BUF_LEN 128 + uint8_t tmp_buf[TMP_BUF_LEN] = {0}; + + for (;;) { + //Waiting for UART event. + if (xQueueReceive(uart_queue, (void * )&event, (portTickType)portMAX_DELAY)) { + switch (event.type) { + //Event of UART receving data + case UART_DATA: { + len = uart_read_bytes(CONSOLE_UART_NUM, tmp_buf, TMP_BUF_LEN, 0); + for (int i = 0; i < len; i++) { + hf_msg_parse(tmp_buf[i], parser); + } + break; + } + //Event of HW FIFO overflow detected + case UART_FIFO_OVF: + ESP_LOGI(TAG_CNSL, "hw fifo overflow\n"); + break; + //Event of UART ring buffer full + case UART_BUFFER_FULL: + ESP_LOGI(TAG_CNSL, "ring buffer full\n"); + break; + //Event of UART RX break detected + case UART_BREAK: + ESP_LOGI(TAG_CNSL, "uart rx break\n"); + break; + //Event of UART parity check error + case UART_PARITY_ERR: + ESP_LOGI(TAG_CNSL, "uart parity error\n"); + break; + //Event of UART frame error + case UART_FRAME_ERR: + ESP_LOGI(TAG_CNSL, "uart frame error\n"); + break; + //Others + default: + break; + } + } + } + vTaskDelete(NULL); +} + + +esp_err_t console_uart_init(void) +{ + esp_err_t ret; + + ret = uart_param_config(CONSOLE_UART_NUM, &uart_cfg); + if (ret != ESP_OK) { + ESP_LOGE(TAG_CNSL, "Uart %d initialize err %04x\n", CONSOLE_UART_NUM, ret); + return ret; + } + + uart_set_pin(CONSOLE_UART_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + uart_driver_install(CONSOLE_UART_NUM, 1024, 1024, 8, &uart_queue, 0); + xTaskCreate(console_uart_task, "uTask", 2048, NULL, 8, NULL); + + return ESP_OK; +} diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/console_uart.h b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/console_uart.h new file mode 100644 index 000000000..753950de9 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/console_uart.h @@ -0,0 +1,19 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#ifndef __CONSOLE_UART_H__ +#define __CONSOLE_UART_H__ + +#define TAG_CNSL "CNSL" + +/** + * @brief configure uart console for command input and process + */ +esp_err_t console_uart_init(void); + +#endif /* __BT_APP_HF_H__*/ diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/gpio_pcm_config.c b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/gpio_pcm_config.c new file mode 100644 index 000000000..e56da1574 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/gpio_pcm_config.c @@ -0,0 +1,92 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "driver/gpio.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "gpio_pcm_config.h" + +#define GPIO_OUTPUT_PCM_FSYNC (25) +#define GPIO_OUTPUT_PCM_CLK_OUT (5) +#define GPIO_OUTPUT_PCM_DOUT (26) +#define GPIO_INPUT_PCM_DIN (35) + +#define GPIO_OUTPUT_PCM_PIN_SEL ((1ULL< +#include +#include +#include +#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 "esp_bt_main.h" +#include "esp_bt_device.h" +#include "esp_gap_bt_api.h" +#include "esp_hf_ag_api.h" +#include "bt_app_hf.h" +#include "gpio_pcm_config.h" +#include "console_uart.h" + +#define BT_HF_AG_TAG "HF_AG_DEMO_MAIN" + +/* event for handler "hf_ag_hdl_stack_up */ +enum { + BT_APP_EVT_STACK_UP = 0, +}; + +/* handler for bluetooth stack enabled events */ +static void bt_hf_hdl_stack_evt(uint16_t event, void *p_param) +{ + ESP_LOGD(BT_HF_TAG, "%s evt %d", __func__, event); + switch (event) + { + case BT_APP_EVT_STACK_UP: + { + /* set up device name */ + char *dev_name = "ESP_HFP_AG"; + esp_bt_dev_set_device_name(dev_name); + + esp_bt_hf_register_callback(bt_app_hf_cb); + + // init and register for HFP_AG functions + esp_bt_hf_init(hf_peer_addr); + + /* + * 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; + pin_code[0] = '0'; + pin_code[1] = '0'; + pin_code[2] = '0'; + pin_code[3] = '0'; + esp_bt_gap_set_pin(pin_type, 4, pin_code); + + /* set discoverable and connectable mode, wait to be connected */ + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + break; + } + default: + ESP_LOGE(BT_HF_TAG, "%s unhandled evt %d", __func__, event); + break; + } +} + +void app_main(void) +{ + /* Initialize NVS — it is used to store PHY calibration data */ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); + + esp_err_t err; + 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_HF_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + if ((err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { + ESP_LOGE(BT_HF_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + if ((err = esp_bluedroid_init()) != ESP_OK) { + ESP_LOGE(BT_HF_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + if ((err = esp_bluedroid_enable()) != ESP_OK) { + ESP_LOGE(BT_HF_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + return; + } + + /* create application task */ + bt_app_task_start_up(); + + /* Bluetooth device name, connection mode and profile set up */ + bt_app_work_dispatch(bt_hf_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL); + + /* initialize console via UART */ + console_uart_init(); + + /* configure the PCM interface and PINs used */ + app_gpio_pcm_io_cfg(); + + /* configure externel chip for acoustic echo cancellation */ +#if ACOUSTIC_ECHO_CANCELLATION_ENABLE + app_gpio_aec_io_cfg(); +#endif /* ACOUSTIC_ECHO_CANCELLATION_ENABLE */ +} \ No newline at end of file diff --git a/examples/bluetooth/bluedroid/classic_bt/hfp_ag/sdkconfig.defaults b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/sdkconfig.defaults new file mode 100644 index 000000000..0229678ea --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/hfp_ag/sdkconfig.defaults @@ -0,0 +1,9 @@ +# Override some defaults so BT stack is enabled and +# Classic BT is enabled and BT_DRAM_RELEASE is disabled +CONFIG_BT_ENABLED=y +CONFIG_BT_BLE_ENABLED=n +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_CLASSIC_ENABLED=y +CONFIG_BT_HFP_ENABLE=y +CONFIG_BT_HFP_AG_ENABLE=y \ No newline at end of file