HFP AG Example

This commit is contained in:
weitianhua 2019-10-25 15:53:16 +08:00 committed by bot
parent ca8e9a7589
commit 78cbdfa332
27 changed files with 1685 additions and 21 deletions

View file

@ -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);

View file

@ -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)
/*****************************************************************************

View file

@ -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;
}

View file

@ -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),

View file

@ -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,

View file

@ -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 */

View file

@ -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

View file

@ -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

View file

@ -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)

View 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

View 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`.

View file

@ -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 ".")

View file

@ -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;
}

View file

@ -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__*/

View file

@ -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);
}

View file

@ -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__*/

View file

@ -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;
}
}

View file

@ -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__ */

View 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;
}
}

View file

@ -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__*/

View file

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -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;
}

View file

@ -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__*/

View file

@ -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 */

View file

@ -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__ */

View 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 */
}

View file

@ -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