Merge branch 'feature/hfp_console' into 'master'

Feature/modify hfp console style by using esp console

Closes BT-1052

See merge request espressif/esp-idf!10973
This commit is contained in:
Jiang Jiang Jian 2021-01-20 23:39:45 +08:00
commit ffa29f8098
21 changed files with 916 additions and 452 deletions

View File

@ -464,9 +464,6 @@ 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

@ -1482,12 +1482,19 @@ void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_ON);
#endif
/* open SCO codec if SCO is routed through transport */
bta_ag_sco_co_open(bta_ag_scb_to_idx(p_scb), p_scb->air_mode, BTA_HFP_SCO_OUT_PKT_SIZE, BTA_AG_CI_SCO_DATA_EVT);
bta_ag_sco_co_open(bta_ag_scb_to_idx(p_scb), p_scb->air_mode, p_scb->out_pkt_len, BTA_AG_CI_SCO_DATA_EVT);
#endif
#if (BTM_WBS_INCLUDED == TRUE)
/* call app callback */
if (p_scb->sco_codec == BTA_AG_CODEC_MSBC) {
bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_MSBC_OPEN_EVT);
} else {
bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT);
}
#else
bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT);
#endif
p_scb->retry_with_sco_only = FALSE;
#if (BTM_WBS_INCLUDED == TRUE)
/* reset to mSBC T2 settings as the preferred */

View File

@ -145,6 +145,7 @@ typedef UINT8 tBTA_AG_RES;
#if (BTM_WBS_INCLUDED == TRUE )
#define BTA_AG_WBS_EVT 31 /* SCO codec nego */
#endif
#define BTA_AG_AUDIO_MSBC_OPEN_EVT 32 /* Audio connection with mSBC codec open */
/* Values below are for HFP only */
#define BTA_AG_AT_A_EVT 10 /* Answer a incoming call */

View File

@ -1312,6 +1312,16 @@ void btc_hf_cb_handler(btc_msg_t *msg)
break;
}
case BTA_AG_AUDIO_MSBC_OPEN_EVT:
{
do {
memset(&param, 0, sizeof(esp_hf_cb_param_t));
param.audio_stat.state = ESP_HF_AUDIO_STATE_CONNECTED_MSBC;
memcpy(param.audio_stat.remote_addr, &hf_local_param[idx].btc_hf_cb.connected_bda,sizeof(esp_bd_addr_t));
btc_hf_cb_to_app(ESP_HF_AUDIO_STATE_EVT, &param);
} while (0);
break;
}
case BTA_AG_AUDIO_CLOSE_EVT:
{
do {

View File

@ -43,47 +43,24 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l
When you flash and monitor this example, the commands help table prints the following log at the very begining:
```
########################################################################
HFP AG command usage manual
HFP AG commands begins with "hf" and end with ";"
Supported commands are as follows, arguments are embraced with < and >
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
==================================================
| Steps to test hfp_ag |
| |
| 1. Print 'help' to gain overview of commands |
| 2. Setup a service level connection |
| 3. Run hfp_ag to test |
| |
=================================================
hf con; -- set up connection with peer device
hf dis; -- release connection with peer device
hf cona; -- set up audio connection with peer device
hf disa; -- release audio 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 indication device status 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; -- in-band ring tone provided
hf iroff; -- in-band 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 a call by AG
hf h; -- to see the command for HFP AG
########################################################################
```
**Note:**
- This command help table will print out in monitor whenever you type `hf h;` or if you input a command that is not required by the command parse rule.
- The command you type will not echo in monitor and your command should always start with `hf` and end with `;` or the example will not respond.
- The command you type in will not echo in monitor.
### Service Level Connection and Disconnection
You can type `hf con;` to establish a service level connection with HF Unit device and log prints such as:
You can type `con` to establish a service level connection with HF Unit device and log prints such as:
```
W (2211) BT_APPL: new conn_srvc id:5, app_id:0
@ -97,10 +74,9 @@ I (2331) BT_APP_HF: --connection state SLC_CONNECTED, peer feats 0xff, chld_feat
**Note: Only after Hands-free Profile(HFP) service is initialized and a service level connection exists between an HF Unit and an AG device, could other commands be available.**
You can type `hf dis;` to disconnect with the connected HF Unit device, and log prints such as:
You can type `dis` to disconnect with the connected HF Unit device, and log prints such as:
```
E (100147) CNSL: Command [hf dis;]
disconnect
W (77321) BT_RFCOMM: port_rfc_closed RFCOMM connection in state 2 closed: Closed (res: 19)
I (77321) BT_APP_HF: APP HFP event: CONNECTION_STATE_EVT
@ -111,7 +87,7 @@ W (77381) BT_RFCOMM: RFCOMM_DisconnectInd LCID:0x41
### 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.
You can type `cona` to establish the audio connection between HF Unit and AG device. Also, you can type `disa` to close the audio data stream.
#### Scenarios for Audio Connection
@ -138,10 +114,9 @@ Since CVSD is the default codec in HFP, we just show the scenarios using mSBC:
#### Answer an Incoming Call
You can type `hf ac;` to answer an incoming call and log prints such as:
You can type `ac` to answer an incoming call and log prints such as:
```
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
@ -153,10 +128,9 @@ 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 such as:
You can type `rc` to reject an incoming call and log prints such as:
```
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
@ -164,10 +138,9 @@ I (1067240) BT_APP_HF: --Audio State disconnected
#### End a Call
You can type `hf end;` to end the current ongoing call and log prints such as:
You can type `end` to end the current ongoing call and log prints such as:
```
E (157741) CNSL: Command [hf end;]
End Call from AG.
W (157741) BT_APPL: BTA_AG_SCO_CLOSING_ST: Ignoring event 3
I (159311) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT
@ -177,10 +150,9 @@ I (159311) BT_APP_HF: --ESP AG Audio Connection Disconnected.
### Dial Number
You can type `hf d <num>;` to dial `<num>` from AG and log prints such as:
You can type `d <num>` to dial `<num>` from AG and log prints such as:
```
E (207351) CNSL: Command [hf d 123456;]
Dial number 123456
I (207361) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT
I (207361) BT_APP_HF: --Audio State connecting
@ -193,19 +165,19 @@ I (208811) BT_APP_HF: --Audio State connected
### Volume Control
You can type `hf vu <tgt> <vol>;` to update the volume of a headset or microphone. The parameter should be set as follows:
You can type `vu <tgt> <vol>` to update the volume of a headset or microphone. The parameter should be set as follows:
- `<tgt>` : 0 - headset, 1 - microphone.
- `<vol>` : Integer among 0 - 15.
For example, `hf vu 0 9;` updates the volume of headset and the log on the AG side prints `Volume Update`, while on the HF Unit side the log prints:
For example, `vu 0 9;` updates the volume of headset and the log on the AG side prints `Volume Update`, while on the HF Unit side the log prints:
```
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;` updates the volume of a microphone and the log on the HF Unit side prints:
And also, `vu 1 9` updates the volume of a microphone and the log on the HF Unit side prints:
```
E (32087) BT_HF: APP HFP event: VOLUME_CONTROL_EVT
@ -214,10 +186,9 @@ E (32087) BT_HF: --volume_target: MICROPHONE, volume 9
#### Voice Recognition
You can type `hf vron;` to start the voice recognition and type `hf vroff;` to terminate this function in the AG device. Both commands will notify the HF Unit the status of voice recognition. For example, type `hf vron;` and the log will print:
You can type `vron` to start the voice recognition and type `vroff` to terminate this function in the AG device. Both commands will notify the HF Unit the status of voice recognition. For example, type `vron` and the log will print:
```
E (244131) CNSL: Command [hf vron;]
Start Voice Recognition.
I (244141) BT_APP_HF: APP HFP event: AUDIO_STATE_EVT
I (244141) BT_APP_HF: --Audio State connecting
@ -228,7 +199,7 @@ I (245311) BT_APP_HF: --Audio State connected
#### Device Status Indication
You can type `hf ind <call> <ntk> <callsetup> <sig>` to send device status of AG to HF Unit. Log on AG prints such as: `Device Indicator Changed!` and on HF Unit side prints such as:
You can type `ind <call> <ntk> <callsetup> <sig>` to send device status of AG to HF Unit. Log on AG prints such as: `Device Indicator Changed!` and on HF Unit side prints such as:
```
E (293641) BT_HF: APP HFP event: CALL_IND_EVT
@ -243,12 +214,12 @@ E (293661) BT_HF: -- signal strength: 5
#### Send Extended AT Error Code
You can type `hf ate <rep> <err>` to send extended AT error code to HF Unit. The parameter should be set as follows:
You can type `ate <rep> <err>` to send extended AT error code to HF Unit. The parameter should be set as follows:
- `<rep>` : integer among 0 - 7.
- `<err>` : integer among 0 - 32.
When you type `hf ate 7 7;` the log on the AG side prints `Send CME Error.` while on the HF Unit side prints:
When you type `ate 7 7;` the log on the AG side prints `Send CME Error.` while on the HF Unit side prints:
```
E (448146) BT_HF: APP HFP event: AT_RESPONSE
@ -257,7 +228,7 @@ E (448146) BT_HF: --AT response event, code 7, cme 7
#### In-Band Ring Tone Setting
You can type `hf iron;` to enable the in-band ring tone and type `hf iroff;` to disable it. The log on the AG side prints such as `Device Indicator Changed!` and on HF Unit side it prints such as:
You can type `iron` to enable the in-band ring tone and type `iroff` to disable it. The log on the AG side prints such as `Device Indicator Changed!` and on HF Unit side it prints such as:
```
E (19546) BT_HF: APP HFP event: IN-BAND_RING_TONE_EVT

View File

@ -2,7 +2,6 @@ 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

@ -11,10 +11,12 @@
#include "esp_hf_ag_api.h"
#include "app_hf_msg_set.h"
#include "bt_app_hf.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_log.h"
// if you want to connect a specific device, add it's bda here
// esp_bd_addr_t hf_peer_addr = {0x70,0x26,0x05,0xca,0xeb,0x21};
esp_bd_addr_t hf_peer_addr = {0xB0, 0xF1, 0xA3, 0x01, 0x2D,0x2E};
void hf_msg_show_usage(void)
{
printf("########################################################################\n");
@ -48,35 +50,40 @@ void hf_msg_show_usage(void)
printf("########################################################################\n");
}
#define HF_CMD_HANDLER(cmd) static void hf_##cmd##_handler(int argn, char **argv)
#define HF_CMD_HANDLER(cmd) static int hf_##cmd##_handler(int argn, char **argv)
HF_CMD_HANDLER(help)
{
hf_msg_show_usage();
return 0;
}
HF_CMD_HANDLER(conn)
{
printf("Connect.\n");
esp_bt_hf_connect(hf_peer_addr);
return 0;
}
HF_CMD_HANDLER(disc)
{
printf("Disconnect\n");
esp_bt_hf_disconnect(hf_peer_addr);
return 0;
}
HF_CMD_HANDLER(conn_audio)
{
printf("Connect Audio\n");
esp_bt_hf_connect_audio(hf_peer_addr);
return 0;
}
HF_CMD_HANDLER(disc_audio)
{
printf("Disconnect Audio\n");
esp_bt_hf_disconnect_audio(hf_peer_addr);
return 0;
}
//AT+BVRA
@ -84,12 +91,14 @@ HF_CMD_HANDLER(vra_on)
{
printf("Start Voice Recognition.\n");
esp_bt_hf_vra(hf_peer_addr,1);
return 0;
}
//AT+BVRA
HF_CMD_HANDLER(vra_off)
{
printf("Stop Voicer Recognition.\n");
esp_bt_hf_vra(hf_peer_addr,0);
return 0;
}
//AT+VGS or AT+VGM
@ -97,22 +106,23 @@ HF_CMD_HANDLER(volume_control)
{
if (argn != 3) {
printf("Insufficient number of arguments");
return;
return 1;
}
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;
return 1;
}
if (sscanf(argv[2], "%d", &volume) != 1 ||
(volume < 0 || volume > 15)) {
printf("Invalid argument for volume %s\n", argv[2]);
return;
return 1;
}
printf("Volume Update\n");
esp_bt_hf_volume_control(hf_peer_addr, target, volume);
return 0;
}
//+CIEV
@ -120,7 +130,7 @@ HF_CMD_HANDLER(ind_change)
{
if (argn != 5) {
printf("Insufficient number of arguments");
return;
return 1;
}
int call_state, ntk_state, call_setup_state, signal;
@ -129,26 +139,27 @@ HF_CMD_HANDLER(ind_change)
(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;
return 1;
}
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;
return 1;
}
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;
return 1;
}
if (sscanf(argv[4], "%d", &signal) != 1 ||
(signal < 0 || signal > 5)) {
printf("Invalid argument for signal %s\n", argv[4]);
return;
return 1;
}
printf("Device Indicator Changed!\n");
esp_bt_hf_indchange_notification(hf_peer_addr, call_state, call_setup_state, ntk_state, signal);
return 0;
}
//AT+CMEE
@ -156,24 +167,25 @@ HF_CMD_HANDLER(cme_err)
{
if (argn != 3) {
printf("Insufficient number of arguments");
return;
return 1;
}
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;
return 1;
}
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;
return 1;
}
printf("Send CME Error.\n");
esp_bt_hf_cmee_response(hf_peer_addr,response_code,error_code);
return 0;
}
//+BSIR:1
@ -181,6 +193,7 @@ HF_CMD_HANDLER(ir_on)
{
printf("Enable Voicer Recognition.\n");
esp_bt_hf_bsir(hf_peer_addr,1);
return 0;
}
//+BSIR:0
@ -188,6 +201,7 @@ HF_CMD_HANDLER(ir_off)
{
printf("Disable Voicer Recognition.\n");
esp_bt_hf_bsir(hf_peer_addr,0);
return 0;
}
//Answer Call from AG
@ -196,6 +210,7 @@ HF_CMD_HANDLER(ac)
printf("Answer Call from AG.\n");
char *number = {"123456"};
esp_bt_hf_answer_call(hf_peer_addr,1,0,1,1,number,0);
return 0;
}
//Reject Call from AG
@ -204,6 +219,7 @@ HF_CMD_HANDLER(rc)
printf("Reject Call from AG.\n");
char *number = {"123456"};
esp_bt_hf_reject_call(hf_peer_addr,0,0,0,0,number,0);
return 0;
}
//End Call from AG
@ -212,6 +228,7 @@ HF_CMD_HANDLER(end)
printf("End Call from AG.\n");
char *number = {"123456"};
esp_bt_hf_end_call(hf_peer_addr,0,0,0,0,number,0);
return 0;
}
//Dial Call from AG
@ -223,6 +240,7 @@ HF_CMD_HANDLER(d)
printf("Dial number %s\n", argv[1]);
esp_bt_hf_out_call(hf_peer_addr,1,0,1,2,argv[1],0);
}
return 0;
}
static hf_msg_hdl_t hf_cmd_tbl[] = {
@ -253,3 +271,206 @@ size_t hf_get_cmd_tbl_size(void)
{
return sizeof(hf_cmd_tbl) / sizeof(hf_msg_hdl_t);
}
#define HF_ORDER(name) name##_cmd
enum hf_cmd_name {
h = 0, /*show command manual*/
con, /*set up connection with peer device*/
dis, /*disconnection with peer device*/
cona, /*set up audio connection with peer device*/
disa, /*release connection with peer device*/
vu, /*volume update*/
ind, /*unsolicited indication device status to HF Client*/
vron, /*start voice recognition*/
vroff, /*stop voice recognition*/
ate, /*send extended at error code*/
iron, /*in-band ring tone provided*/
iroff, /*in-band ring tone not provided*/
ac, /*Answer Incoming Call from AG*/
rc, /*Reject Incoming Call from AG*/
end, /*End up a call by AG*/
d /*Dial Number by AG, e.g. d 11223344*/
};
static char *hf_cmd_explain[] = {
"show command manual",
"set up connection with peer device",
"disconnection with peer device",
"set up audio connection with peer device",
"release connection with peer device",
"volume update",
"unsolicited indication device status to HF Client",
"start voice recognition",
"stop voice recognition",
"send extended at error code",
"in-band ring tone provided",
"in-band ring tone not provided",
"Answer Incoming Call from AG",
"Reject Incoming Call from AG",
"End up a call by AG",
"Dial Number by AG, e.g. d 11223344",
};
typedef struct {
struct arg_str *tgt;
struct arg_str *vol;
struct arg_end *end;
} vu_args_t;
typedef struct {
struct arg_str *call;
struct arg_str *ntk;
struct arg_str *callsetup;
struct arg_str *sig;
struct arg_end *end;
} ind_args_t;
typedef struct {
struct arg_str *rep;
struct arg_str *err;
struct arg_end *end;
} ate_args_t;
static vu_args_t vu_args;
static ind_args_t ind_args;
static ate_args_t ate_args;
void register_hfp_ag(void)
{
const esp_console_cmd_t HF_ORDER(con) = {
.command = "con",
.help = hf_cmd_explain[con],
.hint = NULL,
.func = hf_cmd_tbl[con].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(con)));
const esp_console_cmd_t HF_ORDER(dis) = {
.command = "dis",
.help = hf_cmd_explain[dis],
.hint = NULL,
.func = hf_cmd_tbl[dis].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(dis)));
const esp_console_cmd_t HF_ORDER(cona) = {
.command = "cona",
.help = hf_cmd_explain[cona],
.hint = NULL,
.func = hf_cmd_tbl[cona].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(cona)));
const esp_console_cmd_t HF_ORDER(disa) = {
.command = "disa",
.help = hf_cmd_explain[disa],
.hint = NULL,
.func = hf_cmd_tbl[disa].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(disa)));
const esp_console_cmd_t HF_ORDER(ac) = {
.command = "ac",
.help = hf_cmd_explain[ac],
.hint = NULL,
.func = hf_cmd_tbl[ac].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(ac)));
const esp_console_cmd_t HF_ORDER(rc) = {
.command = "rc",
.help = hf_cmd_explain[rc],
.hint = NULL,
.func = hf_cmd_tbl[rc].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(rc)));
const esp_console_cmd_t HF_ORDER(d) = {
.command = "d",
.help = hf_cmd_explain[d],
.hint = "<num>",
.func = hf_cmd_tbl[d].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(d)));
const esp_console_cmd_t HF_ORDER(vron) = {
.command = "vron",
.help = hf_cmd_explain[vron],
.hint = NULL,
.func = hf_cmd_tbl[vron].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(vron)));
const esp_console_cmd_t HF_ORDER(vroff) = {
.command = "vroff",
.help = hf_cmd_explain[vroff],
.hint = NULL,
.func = hf_cmd_tbl[vroff].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(vroff)));
vu_args.tgt = arg_str1(NULL, NULL, "<tgt>", "\n 0-speaker\n 1-microphone");
vu_args.vol = arg_str1(NULL, NULL, "<vol>", "volume gain ranges from 0 to 15");
vu_args.end = arg_end(1);
const esp_console_cmd_t HF_ORDER(vu) = {
.command = "vu",
.help = hf_cmd_explain[vu],
.hint = NULL,
.func = hf_cmd_tbl[vu].handler,
.argtable = &vu_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(vu)));
const esp_console_cmd_t HF_ORDER(end) = {
.command = "end",
.help = hf_cmd_explain[end],
.hint = NULL,
.func = hf_cmd_tbl[end].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(end)));
const esp_console_cmd_t HF_ORDER(iron) = {
.command = "iron",
.help = hf_cmd_explain[iron],
.hint = NULL,
.func = hf_cmd_tbl[iron].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(iron)));
const esp_console_cmd_t HF_ORDER(iroff) = {
.command = "iroff",
.help = hf_cmd_explain[iroff],
.hint = NULL,
.func = hf_cmd_tbl[iroff].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(iroff)));
ind_args.call = arg_str1(NULL, NULL, "<call>", "call status [0,1]");
ind_args.callsetup = arg_str1(NULL, NULL, "<callsetup>", "call setup status [0,3]");
ind_args.ntk = arg_str1(NULL, NULL, "<ntk>", "network status [0,1]");
ind_args.sig = arg_str1(NULL, NULL, "<sig>", "signal strength value from 0~5");
ind_args.end = arg_end(1);
const esp_console_cmd_t HF_ORDER(ind) = {
.command = "ind",
.help = hf_cmd_explain[ind],
.hint = NULL,
.func = hf_cmd_tbl[ind].handler,
.argtable = &ind_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(ind)));
ate_args.err = arg_str1(NULL, NULL, "<err>", "error code from 0 to 32");
ate_args.rep = arg_str1(NULL, NULL, "<rep>", "response code from 0 to 7");
ate_args.end = arg_end(1);
const esp_console_cmd_t HF_ORDER(ate) = {
.command = "ate",
.help = hf_cmd_explain[ate],
.hint = NULL,
.func = hf_cmd_tbl[ate].handler,
.argtable = &ate_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&HF_ORDER(ate)));
}

View File

@ -11,7 +11,7 @@
#define HF_MSG_ARGS_MAX (8)
typedef void (* hf_cmd_handler)(int argn, char **argv);
typedef int (* hf_cmd_handler)(int argn, char **argv);
typedef struct {
uint16_t opcode;
@ -23,4 +23,6 @@ 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);
void register_hfp_ag(void);
#endif /* __APP_HF_MSG_SET_H__*/

View File

@ -15,15 +15,18 @@
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_hf_ag_api.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/ringbuf.h"
#include "time.h"
#include "sys/time.h"
#include "sdkconfig.h"
#include "bt_app_core.h"
#include "bt_app_hf.h"
#include "osi/allocator.h"
const char *c_hf_evt_str[] = {
"CONNECTION_STATE_EVT", /*!< SERVICE LEVEL CONNECTION STATE CONTROL */
@ -101,9 +104,10 @@ const char *c_codec_mode_str[] = {
"Use MSBC",
};
#if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI
#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
#define TABLE_SIZE 100
// Produce a sine audio
static const int16_t sine_int16[] = {
static const int16_t sine_int16[TABLE_SIZE] = {
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,
@ -116,8 +120,64 @@ static const int16_t sine_int16[] = {
-19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057,
};
#define TABLE_SIZE_CVSD 100
#define ESP_HFP_RINGBUF_SIZE 3600
// 7500 microseconds(=12 slots) is aligned to 1 msbc frame duration, and is multiple of common Tesco for eSCO link with EV3 or 2-EV3 packet type
#define PCM_BLOCK_DURATION_US (7500)
#define WBS_PCM_SAMPLING_RATE_KHZ (16)
#define PCM_SAMPLING_RATE_KHZ (8)
#define BYTES_PER_SAMPLE (2)
// input can refer to Enhanced Setup Synchronous Connection Command in core spec4.2 Vol2, Part E
#define WBS_PCM_INPUT_DATA_SIZE (WBS_PCM_SAMPLING_RATE_KHZ * PCM_BLOCK_DURATION_US / 1000 * BYTES_PER_SAMPLE) //240
#define PCM_INPUT_DATA_SIZE (PCM_SAMPLING_RATE_KHZ * PCM_BLOCK_DURATION_US / 1000 * BYTES_PER_SAMPLE) //120
#define PCM_GENERATOR_TICK_US (4000)
static long s_data_num = 0;
static RingbufHandle_t s_m_rb = NULL;
static uint64_t s_time_new, s_time_old;
static esp_timer_handle_t s_periodic_timer;
static uint64_t s_last_enter_time, s_now_enter_time;
static uint64_t s_us_duration;
static xSemaphoreHandle s_send_data_Semaphore = NULL;
static xTaskHandle s_bt_app_send_data_task_handler = NULL;
static esp_hf_audio_state_t s_audio_code;
static void print_speed(void);
static uint32_t bt_app_hf_outgoing_cb(uint8_t *p_buf, uint32_t sz)
{
size_t item_size = 0;
uint8_t *data;
if (!s_m_rb) {
return 0;
}
vRingbufferGetInfo(s_m_rb, NULL, NULL, NULL, NULL, &item_size);
if (item_size >= sz) {
data = xRingbufferReceiveUpTo(s_m_rb, &item_size, 0, sz);
memcpy(p_buf, data, item_size);
vRingbufferReturnItem(s_m_rb, data);
return sz;
} else {
// data not enough, do not read\n
return 0;
}
return 0;
}
static void bt_app_hf_incoming_cb(const uint8_t *buf, uint32_t sz)
{
s_time_new = esp_timer_get_time();
s_data_num += sz;
if ((s_time_new - s_time_old) >= 3000000) {
print_speed();
}
}
static uint32_t bt_app_hf_create_audio_data(uint8_t *p_buf, uint32_t sz)
{
static int sine_phase = 0;
@ -125,19 +185,108 @@ static uint32_t bt_app_hf_outgoing_cb(uint8_t *p_buf, uint32_t sz)
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;
if (sine_phase >= TABLE_SIZE) {
sine_phase -= TABLE_SIZE;
}
}
return sz;
}
static void bt_app_hf_incoming_cb(const uint8_t *buf, uint32_t sz)
static void print_speed(void)
{
// direct to i2s
esp_hf_outgoing_data_ready();
float tick_s = (s_time_new - s_time_old) / 1000000.0;
float speed = s_data_num * 8 / tick_s / 1000.0;
ESP_LOGI(BT_HF_TAG, "speed(%fs ~ %fs): %f kbit/s" , s_time_old / 1000000.0, s_time_new / 1000000.0, speed);
s_data_num = 0;
s_time_old = s_time_new;
}
#endif /* #if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI */
static void bt_app_send_data_timer_cb(void *arg)
{
if (!xSemaphoreGive(s_send_data_Semaphore)) {
ESP_LOGE(BT_HF_TAG, "%s xSemaphoreGive failed", __func__);
return;
}
return;
}
static void bt_app_send_data_task(void *arg)
{
uint64_t frame_data_num;
uint32_t item_size = 0;
uint8_t *buf = NULL;
for (;;) {
if (xSemaphoreTake(s_send_data_Semaphore, (portTickType)portMAX_DELAY)) {
s_now_enter_time = esp_timer_get_time();
s_us_duration = s_now_enter_time - s_last_enter_time;
if(s_audio_code == ESP_HF_AUDIO_STATE_CONNECTED_MSBC) {
// time of a frame is 7.5ms, sample is 120, data is 2 (byte/sample), so a frame is 240 byte (HF_SBC_ENC_RAW_DATA_SIZE)
frame_data_num = s_us_duration / (PCM_BLOCK_DURATION_US / WBS_PCM_INPUT_DATA_SIZE);
s_last_enter_time += frame_data_num * (PCM_BLOCK_DURATION_US / WBS_PCM_INPUT_DATA_SIZE);
} else {
frame_data_num = s_us_duration / (PCM_BLOCK_DURATION_US / PCM_INPUT_DATA_SIZE);
s_last_enter_time += frame_data_num * (PCM_BLOCK_DURATION_US / PCM_INPUT_DATA_SIZE);
}
buf = osi_malloc(frame_data_num);
if (!buf) {
ESP_LOGE(BT_HF_TAG, "%s, no mem", __FUNCTION__);
continue;
}
bt_app_hf_create_audio_data(buf, frame_data_num);
BaseType_t done = xRingbufferSend(s_m_rb, buf, frame_data_num, 0);
if (!done) {
ESP_LOGE(BT_HF_TAG, "rb send fail\n");
}
osi_free(buf);
vRingbufferGetInfo(s_m_rb, NULL, NULL, NULL, NULL, &item_size);
if(s_audio_code == ESP_HF_AUDIO_STATE_CONNECTED_MSBC) {
if(item_size >= WBS_PCM_INPUT_DATA_SIZE) {
esp_hf_outgoing_data_ready();
}
} else {
if(item_size >= PCM_INPUT_DATA_SIZE) {
esp_hf_outgoing_data_ready();
}
}
}
}
}
void bt_app_send_data(void)
{
s_send_data_Semaphore = xSemaphoreCreateBinary();
xTaskCreate(bt_app_send_data_task, "BtAppSendDataTask", 2048, NULL, configMAX_PRIORITIES - 3, &s_bt_app_send_data_task_handler);
s_m_rb = xRingbufferCreate(ESP_HFP_RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
const esp_timer_create_args_t c_periodic_timer_args = {
.callback = &bt_app_send_data_timer_cb,
.name = "periodic"
};
ESP_ERROR_CHECK(esp_timer_create(&c_periodic_timer_args, &s_periodic_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(s_periodic_timer, PCM_GENERATOR_TICK_US));
s_last_enter_time = esp_timer_get_time();
return;
}
void bt_app_send_data_shut_down(void)
{
if (s_bt_app_send_data_task_handler) {
vTaskDelete(s_bt_app_send_data_task_handler);
s_bt_app_send_data_task_handler = NULL;
}
if (s_send_data_Semaphore) {
vSemaphoreDelete(s_send_data_Semaphore);
s_send_data_Semaphore = NULL;
}
if(s_periodic_timer) {
ESP_ERROR_CHECK(esp_timer_stop(s_periodic_timer));
ESP_ERROR_CHECK(esp_timer_delete(s_periodic_timer));
}
if (s_m_rb) {
vRingbufferDelete(s_m_rb);
}
return;
}
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */
void bt_app_hf_cb(esp_hf_cb_event_t event, esp_hf_cb_param_t *param)
{
@ -161,15 +310,24 @@ void bt_app_hf_cb(esp_hf_cb_event_t event, esp_hf_cb_param_t *param)
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 CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
if (param->audio_stat.state == ESP_HF_AUDIO_STATE_CONNECTED ||
param->audio_stat.state == ESP_HF_AUDIO_STATE_CONNECTED_MSBC)
{
if(param->audio_stat.state == ESP_HF_AUDIO_STATE_CONNECTED) {
s_audio_code = ESP_HF_AUDIO_STATE_CONNECTED;
} else {
s_audio_code = ESP_HF_AUDIO_STATE_CONNECTED_MSBC;
}
s_time_old = esp_timer_get_time();
esp_bt_hf_register_data_callback(bt_app_hf_incoming_cb, bt_app_hf_outgoing_cb);
/* Begin send esco data task */
bt_app_send_data();
} else if (param->audio_stat.state == ESP_HF_AUDIO_STATE_DISCONNECTED) {
ESP_LOGI(BT_HF_TAG, "--ESP AG Audio Connection Disconnected.");
bt_app_send_data_shut_down();
}
#endif /* #if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI */
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */
break;
}
@ -290,7 +448,7 @@ void bt_app_hf_cb(esp_hf_cb_event_t event, esp_hf_cb_param_t *param)
}
break;
}
#if (BTM_WBS_INCLUDED == TRUE)
#if (CONFIG_BT_HFP_WBS_ENABLE)
case ESP_HF_WBS_RESPONSE_EVT:
{
ESP_LOGI(BT_HF_TAG, "--Current codec: %s",c_codec_mode_str[param->wbs_rep.codec]);
@ -306,5 +464,6 @@ void bt_app_hf_cb(esp_hf_cb_event_t event, esp_hf_cb_param_t *param)
default:
ESP_LOGI(BT_HF_TAG, "Unsupported HF_AG EVT: %d.", event);
break;
}
}

View File

@ -21,6 +21,4 @@ esp_bd_addr_t hf_peer_addr; // Declaration of peer device bdaddr
* @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

@ -1,108 +0,0 @@
/*
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,
.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");
break;
//Event of UART ring buffer full
case UART_BUFFER_FULL:
ESP_LOGI(TAG_CNSL, "ring buffer full");
break;
//Event of UART RX break detected
case UART_BREAK:
ESP_LOGI(TAG_CNSL, "uart rx break");
break;
//Event of UART parity check error
case UART_PARITY_ERR:
ESP_LOGI(TAG_CNSL, "uart parity error");
break;
//Event of UART frame error
case UART_FRAME_ERR:
ESP_LOGI(TAG_CNSL, "uart frame error");
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", 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

@ -1,19 +0,0 @@
/*
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

@ -24,7 +24,8 @@
#include "esp_hf_ag_api.h"
#include "bt_app_hf.h"
#include "gpio_pcm_config.h"
#include "console_uart.h"
#include "esp_console.h"
#include "app_hf_msg_set.h"
#define BT_HF_AG_TAG "HF_AG_DEMO_MAIN"
@ -108,14 +109,35 @@ void app_main(void)
/* 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();
#if CONFIG_BT_HFP_AUDIO_DATA_PATH_PCM
/* configure the PCM interface and PINs used */
app_gpio_pcm_io_cfg();
#endif
/* configure externel chip for acoustic echo cancellation */
#if ACOUSTIC_ECHO_CANCELLATION_ENABLE
app_gpio_aec_io_cfg();
#endif /* ACOUSTIC_ECHO_CANCELLATION_ENABLE */
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
repl_config.prompt = "hfp_ag>";
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
/* Register commands */
register_hfp_ag();
printf("\n ==================================================\n");
printf(" | Steps to test hfp_ag |\n");
printf(" | |\n");
printf(" | 1. Print 'help' to gain overview of commands |\n");
printf(" | 2. Setup a service level connection |\n");
printf(" | 3. Run hfp_ag to test |\n");
printf(" | |\n");
printf(" =================================================\n\n");
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}

View File

@ -40,54 +40,29 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l
## Example Output
When you run this example, the commands help table prints the following at the very begining:
When you run this example, the explain prints the following at the very begining:
```
########################################################################
HF client command usage manual
HF client commands begin with "hf" and end 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 qop; -- query current operator name
hf qc; -- query current call status
hf ac; -- answer incoming call
hf rc; -- reject incoming call
hf d <num>; -- dial <num>, e.g. hf d 11223344
hf rd; -- redial
hf dm <index>; -- dial memory
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 rs; -- retrieve subscriber information
hf rv; -- retrieve last voice tag number
hf rh <btrh>; -- response and hold
btrh:
0 - put call on hold,
1 - accept the held call,
2 -reject the held call
hf k <dtmf>; -- send dtmf code.
dtmf: single character in set 0-9, *, #, A-D
hf h; -- show command manual
########################################################################
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
==================================================
| Steps to test hfp_hf |
| |
| 1. Print 'help' to gain overview of commands |
| 2. Setup a service level connection |
| 3. Run hfp_hf to test |
| |
=================================================
```
**Note:**
- This command help table will print out in monitor whenever you type `hf h;` or if you input a command that is not required by the command parse rule.
- The command you type will not echo in monitor and your command should always start with `hf` and end with `;` or the example will not responds.
- The command you typed will not echo in monitor.
### Service Level Connection and Disconnection
You can type `hf con;` to establish a service level connection with AG device and log prints such as:
You can type `con` to establish a service level connection with AG device and log prints such as:
```
E (78502) CNSL: Command [hf con;]
connect
W (79632) BT_APPL: new conn_srvc id:27, app_id:1
I (79642) BT_HF: APP HFP event: CONNECTION_STATE_EVT
@ -114,10 +89,9 @@ I (79872) BT_HF: --inband ring state Provided
**Note: Only after Hands-Free Profile(HFP) service is initialized and a service level connection exists between an HF Unit and an AG device, could other commands be available.**
You can type `hf dis;` to disconnect with the connected AG device, and log prints such as:
You can type `dis` to disconnect with the connected AG device, and log prints such as:
```
E (93382) CNSL: Command [hf dis;]
disconnect
W (93702) BT_RFCOMM: port_rfc_closed RFCOMM connection in state 3 closed: Closed (res: 19)
W (93712) BT_APPL: BTA_HF_CLIENT_SCO_SHUTDOWN_ST: Ignoring event 3
@ -127,10 +101,9 @@ I (93712) 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. Log prints such as:
You can type `cona` to establish the audio connection between HF Unit and AG device. Log prints such as:
```
E (117232) CNSL: Command [hf cona;]
connect audio
I (117232) BT_HF: APP HFP event: AUDIO_STATE_EVT
I (117232) BT_HF: --audio state connecting
@ -139,10 +112,9 @@ I (117262) BT_HF: APP HFP event: AUDIO_STATE_EVT
I (117262) BT_HF: --audio state connected
```
Also, you can type `hf disa;` to close the audio data stream. Log prints such as:
Also, you can type `disa` to close the audio data stream. Log prints such as:
```
E (133002) CNSL: Command [hf disa;]
disconnect audio
I (133262) BT_HF: APP HFP event: AUDIO_STATE_EVT
I (133262) BT_HF: --audio state disconnected
@ -172,10 +144,9 @@ Since CVSD is the default codec in HFP, we just show the scenarios using mSBC:
#### Answer an incoming call
You can type `hf ac;` to answer an incoming call and log prints such as:
You can type `ac` to answer an incoming call and log prints such as:
```
E (196982) CNSL: Command [hf ac;]
Answer call
I (197102) BT_HF: APP HFP event: AT_RESPONSE
I (197102) BT_HF: --AT response event, code 0, cme 0
@ -188,10 +159,9 @@ I (197242) BT_HF: --audio state connected
#### Reject an incoming call
You can type `hf rc;` to reject an incoming call and log prints such as:
You can type `rc` to reject an incoming call and log prints such as:
```
E (210112) CNSL: Command [hf rc;]
Reject call
I (210822) BT_HF: APP HFP event: AT_RESPONSE
I (210822) BT_HF: --AT response event, code 0, cme 0
@ -205,14 +175,13 @@ I (210902) BT_HF: --Call indicator NO call in progress
This example supports three dialing commands:
- `hf d <num>;` Dial the specific number.
- `hf rd;` Redial the last number.
- `hf dm <index>` Dial the specific indexed number in the AG memory.
- `d <num>` Dial the specific number.
- `rd` Redial the last number.
- `dm <index>` Dial the specific indexed number in the AG memory.
For example, type `hf d 186xxxx5549;` to make an outgoing call to `186xxxx5549` and log prints such as:
For example, type `d 186xxxx5549` to make an outgoing call to `186xxxx5549` and log prints such as:
```
E (228882) CNSL: Command [hf d 186xxxx5549;]
Dial number 186xxxx5549
E (229702) BT_BTM: btm_sco_connected, handle 181
I (229712) BT_HF: APP HFP event: CALL_SETUP_IND_EVT
@ -227,25 +196,25 @@ I (229732) BT_HF: --Call setup indicator NONE
#### Respond and Hold
You can type `hf rh <btrh>;` to respond or hold the current call. The parameter should be set as follows:
You can type `rh <btrh>` to respond or hold the current call. The parameter should be set as follows:
- `<btrh>` : 0 - hold current call, 1 - answer held call, 2 - end held call.
#### Volume Control
You can type `hf vu <tgt> <vol>;` to update volume gain of speaker or microphone. The parameter should be set as follows:
You can type `vu <tgt> <vol>` to update volume gain of speaker or microphone. The parameter should be set as follows:
- `<tgt>` : 0 - speaker, 1 - microphone.
- `<vol>` : Integer among 0 - 15.
For example, type `hf vu 0 9;` to update the volume of speaker and log on AG prints:
For example, type `vu 0 9` to update the volume of speaker and log on AG prints:
```
I (43684) BT_APP_HF: APP HFP event: VOLUME_CONTROL_EVT
I (43684) BT_APP_HF: --Volume Target: SPEAKER, Volume 9
```
And also, `hf vu 1 9;` update the volume gain of microphone and log on AG prints:
And also, `vu 1 9` update the volume gain of microphone and log on AG prints:
```
I (177254) BT_APP_HF: APP HFP event: VOLUME_CONTROL_EVT
@ -254,10 +223,9 @@ I (177254) BT_APP_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 terminate this function. For example, type `hf vron;` and log prints such as:
You can type `vron` to start the voice recognition of AG and type `vroff` to terminate this function. For example, type `vron` and log prints such as:
```
E (292432) CNSL: Command [hf vron;]
Start voice recognition
I (293172) BT_HF: APP HFP event: AT_RESPONSE
I (293172) BT_HF: --AT response event, code 0, cme 0
@ -268,10 +236,9 @@ I (293702) BT_HF: --audio state connecte
#### Query Current Operator Name
You can type `hf qop;` to query the current operator name and log prints like:
You can type `qop` to query the current operator name and log prints like:
```
E (338322) CNSL: Command [hf qop;]
Query operator
I (339202) BT_HF: APP HFP event: CURRENT_OPERATOR_EVT
I (339202) BT_HF: --operator name: 中国联通
@ -281,10 +248,9 @@ I (339202) BT_HF: --AT response event, code 0, cme 0
#### Retrieve Subscriber Information
You can type `hf rs;` to retrieve subscriber information and log prints such as:
You can type `rs` to retrieve subscriber information and log prints such as:
```
E (352902) CNSL: Command [hf rs;]
Retrieve subscriber information
I (353702) BT_HF: APP HFP event: SUBSCRIBER_INFO_EVT
I (353702) BT_HF: --subscriber type unknown, number 186xxxx5549
@ -294,10 +260,9 @@ I (353702) BT_HF: --AT response event, code 0, cme 0
#### Query Current Call Status
You can type `hf qc;` to query current call status and log prints like:
You can type `qc` to query current call status and log prints like:
```
E (354522) CNSL: Command [hf qc;]
Query current call status
I (354582) BT_HF: APP HFP event: CLCC_EVT
I (354582) BT_HF: --Current call: idx 1, dir incoming, state active, mpty single, number 186xxxx5549
@ -307,7 +272,7 @@ I (354592) BT_HF: --AT response event, code 0, cme 0
#### Transport DTMF Code
You can type `hf k <dtmf>;` to transport a DTMF code to AG. Log on HF unit side prints like:`send dtmf code: 9` and log on AG side prints such as:
You can type `k <dtmf>` to transport a DTMF code to AG. Log on HF unit side prints like:`send dtmf code: 9` and log on AG side prints such as:
```
I (196284) BT_APP_HF: APP HFP event: DTMF_RESPONSE_EVT
@ -320,7 +285,7 @@ If you encounter any problems, please check if the following rules are followed:
- You should type the command in the terminal according to the format described in the commands help table.
- Not all commands in the table are supported by AG device like _Hands Free Audio Gateway (hfp_ag)_ example from ESP-IDF.
- If you want to use `hf con;` to establish a service level connection with specific AG device, you should add the MAC address of the AG device in `bt_app.c`, for example: `esp_bd_addr_t peer_addr = {0xb4, 0xe6, 0x2d, 0xeb, 0x09, 0x93};`
- If you want to use `con` to establish a service level connection with specific AG device, you should add the MAC address of the AG device in `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

View File

@ -2,7 +2,6 @@ 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

@ -10,6 +10,9 @@
#include <string.h>
#include "esp_hf_client_api.h"
#include "app_hf_msg_set.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_log.h"
extern esp_bd_addr_t peer_addr;
@ -48,53 +51,61 @@ void hf_msg_show_usage(void)
printf("########################################################################\n");
}
#define HF_CMD_HANDLER(cmd) static void hf_##cmd##_handler(int argn, char **argv)
#define HF_CMD_HANDLER(cmd) static int hf_##cmd##_handler(int argn, char **argv)
HF_CMD_HANDLER(help)
{
hf_msg_show_usage();
return 0;
}
HF_CMD_HANDLER(conn)
{
printf("connect\n");
esp_hf_client_connect(peer_addr);
return 0;
}
HF_CMD_HANDLER(disc)
{
printf("disconnect\n");
esp_hf_client_disconnect(peer_addr);
return 0;
}
HF_CMD_HANDLER(conn_audio)
{
printf("connect audio\n");
esp_hf_client_connect_audio(peer_addr);
return 0;
}
HF_CMD_HANDLER(disc_audio)
{
printf("disconnect audio\n");
esp_hf_client_disconnect_audio(peer_addr);
return 0;
}
HF_CMD_HANDLER(query_op)
{
printf("Query operator\n");
esp_hf_client_query_current_operator_name();
return 0;
}
HF_CMD_HANDLER(answer)
{
printf("Answer call\n");
esp_hf_client_answer_call();
return 0;
}
HF_CMD_HANDLER(reject)
{
printf("Reject call\n");
esp_hf_client_reject_call();
return 0;
}
HF_CMD_HANDLER(dial)
@ -105,28 +116,31 @@ HF_CMD_HANDLER(dial)
printf("Dial number %s\n", argv[1]);
esp_hf_client_dial(argv[1]);
}
return 0;
}
HF_CMD_HANDLER(redial)
{
printf("Dial number\n");
esp_hf_client_dial(NULL);
return 0;
}
HF_CMD_HANDLER(dial_mem)
{
if (argn != 2) {
printf("Insufficient number of arguments");
return;
return 1;
}
int index;
if (sscanf(argv[1], "%d", &index) != 1) {
printf("Invalid argument %s\n", argv[1]);
return;
return 1;
}
printf("Dial memory %d\n", index);
esp_hf_client_dial_memory(index);
return 0;
}
@ -134,74 +148,81 @@ HF_CMD_HANDLER(start_vr)
{
printf("Start voice recognition\n");
esp_hf_client_start_voice_recognition();
return 0;
}
HF_CMD_HANDLER(stop_vr)
{
printf("Stop voice recognition\n");
esp_hf_client_stop_voice_recognition();
return 0;
}
HF_CMD_HANDLER(volume_update)
{
if (argn != 3) {
printf("Insufficient number of arguments");
return;
return 1;
}
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;
return 1;
}
if (sscanf(argv[2], "%d", &volume) != 1 ||
(volume < 0 || volume > 15)) {
printf("Invalid argument for volume %s\n", argv[2]);
return;
return 1;
}
printf("volume update\n");
esp_hf_client_volume_update(target, volume);
return 0;
}
HF_CMD_HANDLER(query_call)
{
printf("Query current call status\n");
esp_hf_client_query_current_calls();
return 0;
}
HF_CMD_HANDLER(retrieve_subscriber)
{
printf("Retrieve subscriber information\n");
esp_hf_client_retrieve_subscriber_info();
return 0;
}
HF_CMD_HANDLER(request_last_voice_tag)
{
printf("Request last voice tag\n");
esp_hf_client_request_last_voice_tag_number();
return 0;
}
HF_CMD_HANDLER(btrh)
{
if (argn != 2) {
printf("Insufficient number of arguments");
return;
return 1;
}
int btrh;
if (sscanf(argv[1], "%d", &btrh) != 1) {
printf("Invalid argument %s\n", argv[1]);
return;
return 1;
}
if (btrh < ESP_HF_BTRH_CMD_HOLD || btrh > ESP_HF_BTRH_CMD_REJECT) {
printf("Invalid argument %s\n", argv[1]);
return;
return 1;
}
printf("respond and hold command: %d\n", btrh);
esp_hf_client_send_btrh_cmd(btrh);
return 0;
}
static bool is_dtmf_code(char c)
@ -222,16 +243,17 @@ HF_CMD_HANDLER(dtmf)
{
if (argn != 2) {
printf("Insufficient number of arguments");
return;
return 1;
}
if (strlen(argv[1]) != 1 || !is_dtmf_code(argv[1][0])) {
printf("Invalid argument %s\n", argv[1]);
return;
return 1;
}
printf("send dtmf code: %s\n", argv[1]);
esp_hf_client_send_dtmf(argv[1][0]);
return 0;
}
static hf_msg_hdl_t hf_cmd_tbl[] = {
@ -265,3 +287,215 @@ size_t hf_get_cmd_tbl_size(void)
{
return sizeof(hf_cmd_tbl) / sizeof(hf_msg_hdl_t);
}
#define HF_ORDER(name) name##_cmd
enum hf_cmd_name {
h = 0, /*show command manual*/
con, /*set up connection with peer device*/
dis, /*release connection with peer device*/
cona, /*setup audio connection with peer device*/
disa, /*setup audio connection with peer device*/
qop, /*query current operator name*/
qc, /*query current call status*/
ac, /*answer incoming call*/
rc, /*reject incoming call*/
d, /*dial <num>, e.g. d 11223344*/
rd, /*redial*/
dm, /*dial memory*/
vron, /*start voice recognition*/
vroff, /*stop voice recognition*/
vu, /*volume update*/
rs, /*retrieve subscriber information*/
rv, /*retrieve last voice tag number*/
rh, /*response and hold*/
k /*send dtmf code*/
};
static char *hf_cmd_explain[] = {
"show command manual",
"set up connection with peer device",
"release connection with peer device",
"setup audio connection with peer device",
"release connection with peer device",
"query current operator name",
"query current call status",
"answer incoming call",
"reject incoming call",
"dial <num>, e.g. d 11223344",
"redial",
"dial memory",
"start voice recognition",
"stop voice recognition",
"volume update",
"retrieve subscriber information",
"retrieve last voice tag number",
"response and hold",
"send dtmf code.\n <dtmf> single character in set 0-9, *, #, A-D",
};
typedef struct {
struct arg_str *tgt;
struct arg_str *vol;
struct arg_end *end;
} vu_args_t;
typedef struct {
struct arg_str *btrh;
struct arg_end *end;
} rh_args_t;
static vu_args_t vu_args;
static rh_args_t rh_args;
void register_hfp_hf(void)
{
const esp_console_cmd_t con_cmd = {
.command = "con",
.help = hf_cmd_explain[con],
.hint = NULL,
.func = hf_cmd_tbl[con].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&con_cmd));
const esp_console_cmd_t dis_cmd = {
.command = "dis",
.help = hf_cmd_explain[dis],
.hint = NULL,
.func = hf_cmd_tbl[dis].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&dis_cmd));
const esp_console_cmd_t cona_cmd = {
.command = "cona",
.help = hf_cmd_explain[cona],
.hint = NULL,
.func = hf_cmd_tbl[cona].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cona_cmd));
const esp_console_cmd_t disa_cmd = {
.command = "disa",
.help = hf_cmd_explain[disa],
.hint = NULL,
.func = hf_cmd_tbl[disa].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&disa_cmd));
const esp_console_cmd_t qop_cmd = {
.command = "qop",
.help = hf_cmd_explain[qop],
.hint = NULL,
.func = hf_cmd_tbl[qop].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&qop_cmd));
const esp_console_cmd_t qc_cmd = {
.command = "qc",
.help = hf_cmd_explain[qc],
.hint = NULL,
.func = hf_cmd_tbl[qc].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&qc_cmd));
const esp_console_cmd_t ac_cmd = {
.command = "ac",
.help = hf_cmd_explain[ac],
.hint = NULL,
.func = hf_cmd_tbl[ac].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&ac_cmd));
const esp_console_cmd_t rc_cmd = {
.command = "rc",
.help = hf_cmd_explain[rc],
.hint = NULL,
.func = hf_cmd_tbl[rc].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&rc_cmd));
const esp_console_cmd_t d_cmd = {
.command = "d",
.help = hf_cmd_explain[d],
.hint = "<num>",
.func = hf_cmd_tbl[d].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&d_cmd));
const esp_console_cmd_t rd_cmd = {
.command = "rd",
.help = hf_cmd_explain[rd],
.hint = NULL,
.func = hf_cmd_tbl[rd].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&rd_cmd));
const esp_console_cmd_t dm_cmd = {
.command = "dm",
.help = hf_cmd_explain[dm],
.hint = "<index>",
.func = hf_cmd_tbl[dm].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&dm_cmd));
const esp_console_cmd_t vron_cmd = {
.command = "vron",
.help = hf_cmd_explain[vron],
.hint = NULL,
.func = hf_cmd_tbl[vron].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&vron_cmd));
const esp_console_cmd_t vroff_cmd = {
.command = "vroff",
.help = hf_cmd_explain[vroff],
.hint = NULL,
.func = hf_cmd_tbl[vroff].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&vroff_cmd));
vu_args.tgt = arg_str1(NULL, NULL, "<tgt>", "\n 0-speaker\n 1-microphone");
vu_args.vol = arg_str1(NULL, NULL, "<vol>", "volume gain ranges from 0 to 15");
vu_args.end = arg_end(1);
const esp_console_cmd_t vu_cmd = {
.command = "vu",
.help = hf_cmd_explain[vu],
.hint = NULL,
.func = hf_cmd_tbl[vu].handler,
.argtable = &vu_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&vu_cmd));
const esp_console_cmd_t rs_cmd = {
.command = "rs",
.help = hf_cmd_explain[rs],
.hint = NULL,
.func = hf_cmd_tbl[rs].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&rs_cmd));
const esp_console_cmd_t rv_cmd = {
.command = "rv",
.help = hf_cmd_explain[rv],
.hint = NULL,
.func = hf_cmd_tbl[rv].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&rv_cmd));
rh_args.btrh = arg_str1(NULL, NULL, "<btrh>", "\n 0 - put call on hold,\n 1 - accept the held call,\n 2 -reject the held call");
rh_args.end = arg_end(1);
const esp_console_cmd_t rh_cmd = {
.command = "rh",
.help = hf_cmd_explain[rh],
.hint = NULL,
.func = hf_cmd_tbl[rh].handler,
.argtable = &rh_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&rh_cmd));
const esp_console_cmd_t k_cmd = {
.command = "k",
.help = hf_cmd_explain[k],
.hint = "<dtmf>",
.func = hf_cmd_tbl[k].handler,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&k_cmd));
}

View File

@ -11,7 +11,7 @@
#define HF_MSG_ARGS_MAX (5)
typedef void (* hf_cmd_handler)(int argn, char **argv);
typedef int (* hf_cmd_handler)(int argn, char **argv);
typedef struct {
uint16_t opcode;
@ -23,4 +23,6 @@ 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);
void register_hfp_hf(void);
#endif /* __APP_HF_MSG_SET_H__*/

View File

@ -168,11 +168,11 @@ const char *c_inband_ring_state_str[] = {
"Provided",
};
// esp_bd_addr_t peer_addr;
extern esp_bd_addr_t peer_addr;
// If you want to connect a specific device, add it's address here
esp_bd_addr_t peer_addr = {0xb4, 0xe6, 0x2d, 0xeb, 0x09, 0x93};
// esp_bd_addr_t peer_addr = {0xac, 0x67, 0xb2, 0x53, 0x77, 0xbe};
#if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI
#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
#define ESP_HFP_RINGBUF_SIZE 3600
static RingbufHandle_t m_rb = NULL;
@ -224,7 +224,7 @@ static void bt_app_hf_client_incoming_cb(const uint8_t *buf, uint32_t sz)
esp_hf_client_outgoing_data_ready();
}
#endif /* #if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI */
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */
/* callback for HF_CLIENT */
void bt_app_hf_client_cb(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_t *param)
@ -250,7 +250,7 @@ void bt_app_hf_client_cb(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_
{
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 CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED ||
param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC) {
esp_hf_client_register_data_callback(bt_app_hf_client_incoming_cb,
@ -259,7 +259,7 @@ void bt_app_hf_client_cb(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_
} else if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
bt_app_hf_client_audio_close();
}
#endif /* #if CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI */
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */
break;
}

View File

@ -1,119 +0,0 @@
/*
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[128] = {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");
break;
}
//Event of UART ring buffer full
case UART_BUFFER_FULL:
{
ESP_LOGI(TAG_CNSL, "ring buffer full");
break;
}
//Event of UART RX break detected
case UART_BREAK:
{
ESP_LOGI(TAG_CNSL, "uart rx break");
break;
}
//Event of UART parity check error
case UART_PARITY_ERR:
{
ESP_LOGI(TAG_CNSL, "uart parity error");
break;
}
//Event of UART frame error
case UART_FRAME_ERR:
{
ESP_LOGI(TAG_CNSL, "uart frame error");
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", 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

@ -1,19 +0,0 @@
/*
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

@ -24,7 +24,123 @@
#include "esp_hf_client_api.h"
#include "bt_app_hf.h"
#include "gpio_pcm_config.h"
#include "console_uart.h"
#include "esp_console.h"
#include "app_hf_msg_set.h"
esp_bd_addr_t peer_addr = {0};
static char peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
static uint8_t peer_bdname_len;
static const char remote_device_name[] = "ESP_HFP_AG";
static bool get_name_from_eir(uint8_t *eir, char *bdname, uint8_t *bdname_len)
{
uint8_t *rmt_bdname = NULL;
uint8_t rmt_bdname_len = 0;
if (!eir) {
return false;
}
rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len);
if (!rmt_bdname) {
rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len);
}
if (rmt_bdname) {
if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) {
rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN;
}
if (bdname) {
memcpy(bdname, rmt_bdname, rmt_bdname_len);
bdname[rmt_bdname_len] = '\0';
}
if (bdname_len) {
*bdname_len = rmt_bdname_len;
}
return true;
}
return false;
}
void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch (event) {
case ESP_BT_GAP_DISC_RES_EVT: {
for (int i = 0; i < param->disc_res.num_prop; i++){
if (param->disc_res.prop[i].type == ESP_BT_GAP_DEV_PROP_EIR
&& get_name_from_eir(param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)){
if (strcmp(peer_bdname, remote_device_name) == 0) {
memcpy(peer_addr, param->disc_res.bda, ESP_BD_ADDR_LEN);
ESP_LOGI(BT_HF_TAG, "Found a target device address: \n");
esp_log_buffer_hex(BT_HF_TAG, peer_addr, ESP_BD_ADDR_LEN);
ESP_LOGI(BT_HF_TAG, "Found a target device name: %s", peer_bdname);
printf("Connect.\n");
esp_hf_client_connect(peer_addr);
esp_bt_gap_cancel_discovery();
}
}
}
break;
}
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT:
ESP_LOGI(BT_HF_TAG, "ESP_BT_GAP_DISC_STATE_CHANGED_EVT");
case ESP_BT_GAP_RMT_SRVCS_EVT:
case ESP_BT_GAP_RMT_SRVC_REC_EVT:
break;
case ESP_BT_GAP_AUTH_CMPL_EVT: {
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
ESP_LOGI(BT_HF_TAG, "authentication success: %s", param->auth_cmpl.device_name);
esp_log_buffer_hex(BT_HF_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
} else {
ESP_LOGE(BT_HF_TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
}
break;
}
case ESP_BT_GAP_PIN_REQ_EVT: {
ESP_LOGI(BT_HF_TAG, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit);
if (param->pin_req.min_16_digit) {
ESP_LOGI(BT_HF_TAG, "Input pin code: 0000 0000 0000 0000");
esp_bt_pin_code_t pin_code = {0};
esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code);
} else {
ESP_LOGI(BT_HF_TAG, "Input pin code: 1234");
esp_bt_pin_code_t pin_code;
pin_code[0] = '1';
pin_code[1] = '2';
pin_code[2] = '3';
pin_code[3] = '4';
esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code);
}
break;
}
#if (CONFIG_BT_SSP_ENABLED == true)
case ESP_BT_GAP_CFM_REQ_EVT:
ESP_LOGI(BT_HF_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val);
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
break;
case ESP_BT_GAP_KEY_NOTIF_EVT:
ESP_LOGI(BT_HF_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey);
break;
case ESP_BT_GAP_KEY_REQ_EVT:
ESP_LOGI(BT_HF_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
break;
#endif
case ESP_BT_GAP_MODE_CHG_EVT:
ESP_LOGI(BT_HF_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode);
break;
default: {
ESP_LOGI(BT_HF_TAG, "event: %d", event);
break;
}
}
return;
}
/* event for handler "bt_av_hdl_stack_up */
enum {
@ -74,16 +190,36 @@ void app_main(void)
/* Bluetooth device name, connection mode and profile set up */
bt_app_work_dispatch(bt_hf_client_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL);
/* initialize console via UART */
console_uart_init();
#if CONFIG_BT_HFP_AUDIO_DATA_PATH_PCM
/* configure the PCM interface and PINs used */
app_gpio_pcm_io_cfg();
#endif
/* configure externel chip for acoustic echo cancellation */
#if ACOUSTIC_ECHO_CANCELLATION_ENABLE
app_gpio_aec_io_cfg();
#endif /* ACOUSTIC_ECHO_CANCELLATION_ENABLE */
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
repl_config.prompt = "hfp_hf>";
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
/* Register commands */
register_hfp_hf();
printf("\n ==================================================\n");
printf(" | Steps to test hfp_hf |\n");
printf(" | |\n");
printf(" | 1. Print 'help' to gain overview of commands |\n");
printf(" | 2. Setup a service level connection |\n");
printf(" | 3. Run hfp_hf to test |\n");
printf(" | |\n");
printf(" =================================================\n\n");
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}
@ -96,6 +232,8 @@ static void bt_hf_client_hdl_stack_evt(uint16_t event, void *p_param)
char *dev_name = "ESP_HFP_HF";
esp_bt_dev_set_device_name(dev_name);
/* register GAP callback function */
esp_bt_gap_register_callback(esp_bt_gap_cb);
esp_hf_client_register_callback(bt_app_hf_client_cb);
esp_hf_client_init();
@ -109,6 +247,10 @@ static void bt_hf_client_hdl_stack_evt(uint16_t event, void *p_param)
/* set discoverable and connectable mode, wait to be connected */
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
/* start device discovery */
ESP_LOGI(BT_HF_TAG, "Starting device discovery...");
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
break;
}
default: