mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
HFP AG Example
This commit is contained in:
parent
ca8e9a7589
commit
78cbdfa332
@ -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);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#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)
|
||||
/*****************************************************************************
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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 */
|
||||
#endif /* BTA_AG_CO_H */
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
9
examples/bluetooth/bluedroid/classic_bt/hfp_ag/Makefile
Normal file
9
examples/bluetooth/bluedroid/classic_bt/hfp_ag/Makefile
Normal file
@ -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
|
||||
|
282
examples/bluetooth/bluedroid/classic_bt/hfp_ag/README.md
Normal file
282
examples/bluetooth/bluedroid/classic_bt/hfp_ag/README.md
Normal file
@ -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 <tgt> <vol>; -- volume update
|
||||
tgt: 0-speaker, 1-microphone
|
||||
vol: volume gain ranges from 0 to 15
|
||||
hf ind <call> <ntk> <callsetup> <sig>; -- 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 <rep> <err>; -- 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 <num>; -- 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 <num>;` to dial `<num>` 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 <tgt> <vol>;` to sync volume gain of headset or microphone. The parameter set:
|
||||
|
||||
- `<tgt>` : 0 - headset, 1 - microphone.
|
||||
- `<vol>` : 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 <call> <ntk> <callsetup> <sig>` 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 <rep> <err>` to send extended AT error coed to HF Unit. Parameter set:
|
||||
|
||||
- `<rep>` : integer among 0 - 7.
|
||||
- `<err>` : 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`.
|
@ -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 ".")
|
@ -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 <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
}
|
@ -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__*/
|
||||
|
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#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 <tgt> <vol>; -- volume update\n");
|
||||
printf(" tgt: 0-speaker, 1-microphone\n");
|
||||
printf(" vol: volume gain ranges from 0 to 15\n");
|
||||
printf("hf ind <call> <ntk> <callsetup> <sig>; -- 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 <rep> <err>; -- 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 <num>; -- 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);
|
||||
}
|
@ -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__*/
|
@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#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;
|
||||
}
|
||||
}
|
@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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__ */
|
297
examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.c
Normal file
297
examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/bt_app_hf.c
Normal file
@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
}
|
@ -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 <stdint.h>
|
||||
#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__*/
|
||||
|
@ -0,0 +1,5 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
@ -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;
|
||||
}
|
@ -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__*/
|
@ -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<<GPIO_OUTPUT_PCM_FSYNC) | (1ULL<<GPIO_OUTPUT_PCM_CLK_OUT) | (1ULL<<GPIO_OUTPUT_PCM_DOUT))
|
||||
|
||||
#define GPIO_INPUT_PCM_PIN_SEL (1ULL<<GPIO_INPUT_PCM_DIN)
|
||||
|
||||
void app_gpio_pcm_io_cfg(void)
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
/// configure the PCM output pins
|
||||
//disable interrupt
|
||||
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
//set as output mode
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
||||
io_conf.pin_bit_mask = GPIO_OUTPUT_PCM_PIN_SEL;
|
||||
//disable pull-down mode
|
||||
io_conf.pull_down_en = 0;
|
||||
//disable pull-up mode
|
||||
io_conf.pull_up_en = 0;
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
/// configure the PCM input pin
|
||||
//interrupt of rising edge
|
||||
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
//bit mask of the pins, use GPIO4/5 here
|
||||
io_conf.pin_bit_mask = GPIO_INPUT_PCM_PIN_SEL;
|
||||
//set as input mode
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
//enable pull-up mode
|
||||
io_conf.pull_up_en = 0;
|
||||
io_conf.pull_down_en = 0;
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
/// matrix out | in the internal PCM signals to the GPIOs
|
||||
gpio_matrix_out(GPIO_OUTPUT_PCM_FSYNC, PCMFSYNC_OUT_IDX, false, false);
|
||||
gpio_matrix_out(GPIO_OUTPUT_PCM_CLK_OUT, PCMCLK_OUT_IDX, false, false);
|
||||
gpio_matrix_out(GPIO_OUTPUT_PCM_DOUT, PCMDOUT_IDX, false, false);
|
||||
gpio_matrix_in(GPIO_INPUT_PCM_DIN, PCMDIN_IDX, false);
|
||||
}
|
||||
|
||||
#if ACOUSTIC_ECHO_CANCELLATION_ENABLE
|
||||
|
||||
#define GPIO_OUTPUT_AEC_1 (19)
|
||||
#define GPIO_OUTPUT_AEC_2 (21)
|
||||
#define GPIO_OUTPUT_AEC_3 (22)
|
||||
#define GPIO_OUTPUT_AEC_PIN_SEL ((1ULL<<GPIO_OUTPUT_AEC_1) | (1ULL<<GPIO_OUTPUT_AEC_2) | (1ULL<<GPIO_OUTPUT_AEC_3))
|
||||
|
||||
void app_gpio_aec_io_cfg(void)
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
//disable interrupt
|
||||
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
//set as output mode
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
||||
io_conf.pin_bit_mask = GPIO_OUTPUT_AEC_PIN_SEL;
|
||||
//disable pull-down mode
|
||||
io_conf.pull_down_en = 0;
|
||||
//disable pull-up mode
|
||||
io_conf.pull_up_en = 0;
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
// set the output pins
|
||||
gpio_set_level(GPIO_OUTPUT_AEC_2, 0);
|
||||
|
||||
gpio_set_level(GPIO_OUTPUT_AEC_1, 0);
|
||||
|
||||
gpio_set_level(GPIO_OUTPUT_AEC_1, 1);
|
||||
|
||||
gpio_set_level(GPIO_OUTPUT_AEC_3, 1);
|
||||
}
|
||||
#endif /* ACOUSTIC_ECHO_CANCELLATION_ENABLE */
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
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 __GPIO_PCM_CONFIG_H__
|
||||
#define __GPIO_PCM_CONFIG_H__
|
||||
|
||||
#define ACOUSTIC_ECHO_CANCELLATION_ENABLE 1
|
||||
|
||||
void app_gpio_pcm_io_cfg(void);
|
||||
|
||||
#if ACOUSTIC_ECHO_CANCELLATION_ENABLE
|
||||
void app_gpio_aec_io_cfg(void);
|
||||
#endif /* ACOUSTIC_ECHO_CANCELLATION_ENABLE */
|
||||
|
||||
#endif /* #define __GPIO_PCM_CONFIG_H__ */
|
121
examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/main.c
Normal file
121
examples/bluetooth/bluedroid/classic_bt/hfp_ag/main/main.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_bt.h"
|
||||
#include "bt_app_core.h"
|
||||
#include "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 */
|
||||
}
|
@ -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
|
Loading…
Reference in New Issue
Block a user