Add HFP Unit example code.
This commit is contained in:
parent
626c58fc67
commit
1e7933049d
19 changed files with 1835 additions and 0 deletions
|
@ -0,0 +1,7 @@
|
|||
# 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)
|
||||
|
||||
set(SUPPORTED_TARGETS esp32)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(hfp_hf)
|
9
examples/bluetooth/bluedroid/classic_bt/hfp_hf/Makefile
Normal file
9
examples/bluetooth/bluedroid/classic_bt/hfp_hf/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := hfp_hf
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
332
examples/bluetooth/bluedroid/classic_bt/hfp_hf/README.md
Normal file
332
examples/bluetooth/bluedroid/classic_bt/hfp_hf/README.md
Normal file
|
@ -0,0 +1,332 @@
|
|||
# Hands-Free Unit
|
||||
|
||||
This example is to show how to use the APIs of Hands-Free (HF) Unit Component and the effects of them by providing a set of commands. You can use this example to communicate with an Audio Gateway (AG) device (e.g. a smart phone). This example uses UART for user commands.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. To operate it should be connected to an AG running on a smartphone or on another ESP32 development board loaded with Hands Free Audio Gateway (hfp_ag) example from ESP-IDF.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
ESP32 supports two types of audio data path: PCM() and HCI(Host-Controller Interface) but the default sdkconfig of this example does not config the data path in a specific way. You should config the data path you want:
|
||||
|
||||
- PCM : When using PCM, audio data stream is mapped to GPIO pins and you should link these GPIO pins to a speaker via I2S port. And you should choose PCM in `menuconfig` path: `Component config --> Bluetooth controller --> BR/EDR Sync(SCO/eSCO) default data path --> PCM`and also `Component config --> Bluetooth --> Bluedroid Options -->Hands Free/Handset Profile --> audio(SCO) data path --> PCM`.
|
||||
- HCI : When using HCI, audio data stream will be transport to HF unit app via HCI. And you should choose HCI in `menuconfig` path: `Component config -->Bluetooth controller -->BR/EDR Sync(SCO/eSCO) default data path --> HCI` and also `Component config --> Bluetooth --> Bluedroid Options -->Hands Free/Handset Profile --> audio(SCO) data path --> HCI`.
|
||||
|
||||
**Note: Wide Band Speech is disabled by default, if you want to use it please select it in menuconfig path: `Component config --> Bluetooth --> Bluedroid Options --> Wide Band Speech`.**
|
||||
|
||||
### 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](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
When you run this example, the commands help table 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
|
||||
########################################################################
|
||||
```
|
||||
|
||||
**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:
|
||||
|
||||
```
|
||||
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
|
||||
I (79642) BT_HF: --connection state connected, peer feats 0x0, chld_feats 0x0
|
||||
I (79792) BT_HF: APP HFP event: CALL_IND_EVT
|
||||
I (79792) BT_HF: --Call indicator NO call in progress
|
||||
I (79792) BT_HF: APP HFP event: CALL_SETUP_IND_EVT
|
||||
I (79802) BT_HF: --Call setup indicator NONE
|
||||
I (79802) BT_HF: APP HFP event: NETWORK_STATE_EVT
|
||||
I (79812) BT_HF: --NETWORK STATE available
|
||||
I (79812) BT_HF: APP HFP event: SIGNAL_STRENGTH_IND_EVT
|
||||
I (79822) BT_HF: -- signal strength: 4
|
||||
I (79822) BT_HF: APP HFP event: ROAMING_STATUS_IND_EVT
|
||||
I (79832) BT_HF: --ROAMING: inactive
|
||||
I (79832) BT_HF: APP HFP event: BATTERY_LEVEL_IND_EVT
|
||||
I (79842) BT_HF: --battery level 3
|
||||
I (79842) BT_HF: APP HFP event: CALL_HELD_IND_EVT
|
||||
I (79852) BT_HF: --Call held indicator NONE held
|
||||
I (79852) BT_HF: APP HFP event: CONNECTION_STATE_EVT
|
||||
I (79862) BT_HF: --connection state slc_connected, peer feats 0x16e, chld_feats 0x0
|
||||
I (79872) BT_HF: APP HFP event: INBAND_RING_TONE_EVT
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
I (93712) BT_HF: APP HFP event: CONNECTION_STATE_EVT
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
E (117262) BT_BTM: btm_sco_connected, handle 181
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
#### Scenarios for Audio Connection
|
||||
|
||||
- Answer an incoming call
|
||||
- Enable voice recognition
|
||||
- Dial an outgoing call
|
||||
|
||||
#### Scenarios for Audio Disconnection
|
||||
|
||||
- Reject an incoming call
|
||||
- Disable the voice recognition
|
||||
|
||||
#### Choise of Codec
|
||||
|
||||
ESP32 supports both CVSD and mSBC codec. HF Unit and AG device determine which codec to use by exchanging features during service level connection. The choice of codec also depends on the your configuration in `menuconfig`.
|
||||
|
||||
Since CVSD is the default codec in HFP, we just show the scenarios using mSBC:
|
||||
|
||||
- If you enable `BT_HFP_WBS_ENABLE` in `menuconfig`, mSBC will be available.
|
||||
- If both HF Unit and AG support mSBC and `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 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
|
||||
E (197232) BT_BTM: btm_sco_connected, handle 181
|
||||
I (197232) BT_HF: APP HFP event: CALL_IND_EVT
|
||||
I (197232) BT_HF: --Call indicator call in progress
|
||||
I (197232) BT_HF: APP HFP event: AUDIO_STATE_EVT
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
I (210842) BT_HF: APP HFP event: AUDIO_STATE_EVT
|
||||
I (210842) BT_HF: --audio state disconnected
|
||||
I (210902) BT_HF: APP HFP event: CALL_IND_EVT
|
||||
I (210902) BT_HF: --Call indicator NO call in progress
|
||||
```
|
||||
|
||||
#### Dial Number
|
||||
|
||||
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.
|
||||
|
||||
For example, type `hf 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
|
||||
I (229712) BT_HF: --Call setup indicator OUTGOING_DIALING
|
||||
I (229712) BT_HF: APP HFP event: CALL_IND_EVT
|
||||
I (229712) BT_HF: --Call indicator call in progress
|
||||
I (229722) BT_HF: APP HFP event: AUDIO_STATE_EVT
|
||||
I (229722) BT_HF: --audio state connected
|
||||
I (229732) BT_HF: APP HFP event: CALL_SETUP_IND_EVT
|
||||
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:
|
||||
|
||||
- `<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:
|
||||
|
||||
- `<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:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
I (177254) BT_APP_HF: APP HFP event: VOLUME_CONTROL_EVT
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
E (293702) BT_BTM: btm_sco_connected, handle 181
|
||||
I (293702) BT_HF: APP HFP event: AUDIO_STATE_EVT
|
||||
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:
|
||||
|
||||
```
|
||||
E (338322) CNSL: Command [hf qop;]
|
||||
Query operator
|
||||
I (339202) BT_HF: APP HFP event: CURRENT_OPERATOR_EVT
|
||||
I (339202) BT_HF: --operator name: 中国联通
|
||||
I (339202) BT_HF: APP HFP event: AT_RESPONSE
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
I (353702) BT_HF: APP HFP event: AT_RESPONSE
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
I (354582) BT_HF: APP HFP event: AT_RESPONSE
|
||||
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:
|
||||
|
||||
```
|
||||
I (196284) BT_APP_HF: APP HFP event: DTMF_RESPONSE_EVT
|
||||
I (196284) BT_APP_HF: --DTMF code is: 9.
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
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};`
|
||||
- Use `esp_hf_client_register_callback()` and `esp_hf_client_init();` before establishing a service level connection.
|
||||
|
||||
## Example Breakdown
|
||||
|
||||
Due to the complexity of HFP, this example has more source files than other bluetooth examples. To show functions of HFP in a simple way, we use the Commands and Effects scheme to illustrate APIs of HFP in ESP-IDF.
|
||||
|
||||
- The example will respond to user command through UART console. Please go to `console_uart.c` for the configuration details.
|
||||
- For voice interface, ESP32 has provided PCM input/output signals which can be mapped to GPIO pins. So, please go to `gpio_pcm_config.c` for the configuration details.
|
||||
- If you want to update the command table, please refer to `app_hf_msg_set.c`.
|
||||
- If you want to update the command parse rules, please refer to `app_hf_msg_prs.c`.
|
||||
- If you want to update the responses of HF Unit or want to update the log, please refer to `bt_app_hf.c`.
|
||||
- Task configuration part is in `bt_app_core.c`.
|
|
@ -0,0 +1,8 @@
|
|||
idf_component_register(SRCS "app_hf_msg_prs.c"
|
||||
"app_hf_msg_set.c"
|
||||
"bt_app_core.c"
|
||||
"bt_app_hf.c"
|
||||
"console_uart.c"
|
||||
"gpio_pcm_config.c"
|
||||
"main.c"
|
||||
INCLUDE_DIRS ".")
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
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 terminator to '\0'
|
||||
char *end = buf + len - HF_MSG_TAIL_LEN;
|
||||
*end = '\0';
|
||||
|
||||
hf_msg_split_args(start, end, argv, &argn);
|
||||
|
||||
if (argn == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool cmd_supported = false;
|
||||
|
||||
hf_msg_hdl_t *cmd_tbl = hf_get_cmd_tbl();
|
||||
size_t cmd_tbl_size = hf_get_cmd_tbl_size();
|
||||
for (int i = 0; i < cmd_tbl_size; ++i) {
|
||||
hf_msg_hdl_t *hdl = &cmd_tbl[i];
|
||||
if (strcmp(argv[0], hdl->str) == 0) {
|
||||
if (hdl->handler) {
|
||||
hdl->handler(argn, argv);
|
||||
cmd_supported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cmd_supported) {
|
||||
printf("unsupported command\n");
|
||||
hf_msg_show_usage();
|
||||
}
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __APP_HF_MSG_PRS_H__
|
||||
#define __APP_HF_MSG_PRS_H__
|
||||
|
||||
typedef enum {
|
||||
HF_MSG_PRS_ERR_OK = 0, // a complete message is finished
|
||||
HF_MSG_PRS_ERR_IN_PROGRESS, // message parsing is in progress
|
||||
HF_MSG_PRS_ERR_HDR_UNDETECTED, // header not detected
|
||||
HF_MSG_PRS_ERR_HDR_SYNC_FAILED, // failed to sync header
|
||||
HF_MSG_PRS_ERR_BUF_OVERFLOW, // exceeds the buffer size: HF_MSG_LEN_MAX
|
||||
} hf_msg_prs_err_t;
|
||||
|
||||
typedef enum {
|
||||
HF_MSG_PRS_IDLE = 0,
|
||||
HF_MSG_PRS_HDR,
|
||||
HF_MSG_PRS_PAYL,
|
||||
} hf_msg_prs_state_t;
|
||||
|
||||
typedef void (*hf_msg_callback)(char *buf, int len);
|
||||
|
||||
#define HF_MSG_LEN_MAX (128)
|
||||
|
||||
typedef struct {
|
||||
hf_msg_prs_state_t state;
|
||||
char buf[HF_MSG_LEN_MAX + 1];
|
||||
int cnt;
|
||||
int h_idx;
|
||||
int t_idx;
|
||||
hf_msg_callback callback;
|
||||
} hf_msg_prs_cb_t;
|
||||
|
||||
void hf_msg_parser_reset_state(hf_msg_prs_cb_t *prs);
|
||||
|
||||
void hf_msg_parser_register_callback(hf_msg_prs_cb_t *prs, hf_msg_callback cb);
|
||||
|
||||
hf_msg_prs_err_t hf_msg_parse(char c, hf_msg_prs_cb_t *prs);
|
||||
|
||||
void hf_msg_show_usage(void);
|
||||
|
||||
#endif /* __APP_HF_MSG_PRS_H__*/
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
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_client_api.h"
|
||||
#include "app_hf_msg_set.h"
|
||||
|
||||
extern esp_bd_addr_t peer_addr;
|
||||
|
||||
void hf_msg_show_usage(void)
|
||||
{
|
||||
printf("########################################################################\n");
|
||||
printf("HF client command usage manual\n");
|
||||
printf("HF client commands begin with \"hf\" and end with \";\"\n");
|
||||
printf("Supported commands are as follows, arguments are embraced with < and >\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 qop; -- query current operator name\n");
|
||||
printf("hf qc; -- query current call status\n");
|
||||
printf("hf ac; -- answer incoming call\n");
|
||||
printf("hf rc; -- reject incoming call\n");
|
||||
printf("hf d <num>; -- dial <num>, e.g. hf d 11223344\n");
|
||||
printf("hf rd; -- redial\n");
|
||||
printf("hf dm <index>; -- dial memory\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 rs; -- retrieve subscriber information\n");
|
||||
printf("hf rv; -- retrieve last voice tag number\n");
|
||||
printf("hf rh <btrh>; -- response and hold\n");
|
||||
printf(" btrh:\n");
|
||||
printf(" 0 - put call on hold,\n");
|
||||
printf(" 1 - accept the held call,\n");
|
||||
printf(" 2 -reject the held call\n");
|
||||
printf("hf k <dtmf>; -- send dtmf code.\n");
|
||||
printf(" dtmf: single character in set 0-9, *, #, A-D\n");
|
||||
printf("hf h; -- show command manual\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_hf_client_connect(peer_addr);
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(disc)
|
||||
{
|
||||
printf("disconnect\n");
|
||||
esp_hf_client_disconnect(peer_addr);
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(conn_audio)
|
||||
{
|
||||
printf("connect audio\n");
|
||||
esp_hf_client_connect_audio(peer_addr);
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(disc_audio)
|
||||
{
|
||||
printf("disconnect audio\n");
|
||||
esp_hf_client_disconnect_audio(peer_addr);
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(query_op)
|
||||
{
|
||||
printf("Query operator\n");
|
||||
esp_hf_client_query_current_operator_name();
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(answer)
|
||||
{
|
||||
printf("Answer call\n");
|
||||
esp_hf_client_answer_call();
|
||||
}
|
||||
HF_CMD_HANDLER(reject)
|
||||
{
|
||||
printf("Reject call\n");
|
||||
esp_hf_client_reject_call();
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(dial)
|
||||
{
|
||||
if (argn != 2) {
|
||||
printf("Insufficient number of arguments");
|
||||
} else {
|
||||
printf("Dial number %s\n", argv[1]);
|
||||
esp_hf_client_dial(argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(redial)
|
||||
{
|
||||
printf("Dial number\n");
|
||||
esp_hf_client_dial(NULL);
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(dial_mem)
|
||||
{
|
||||
if (argn != 2) {
|
||||
printf("Insufficient number of arguments");
|
||||
return;
|
||||
}
|
||||
int index;
|
||||
if (sscanf(argv[1], "%d", &index) != 1) {
|
||||
printf("Invalid argument %s\n", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Dial memory %d\n", index);
|
||||
esp_hf_client_dial_memory(index);
|
||||
}
|
||||
|
||||
|
||||
HF_CMD_HANDLER(start_vr)
|
||||
{
|
||||
printf("Start voice recognition\n");
|
||||
esp_hf_client_start_voice_recognition();
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(stop_vr)
|
||||
{
|
||||
printf("Stop voice recognition\n");
|
||||
esp_hf_client_stop_voice_recognition();
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(volume_update)
|
||||
{
|
||||
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_hf_client_volume_update(target, volume);
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(query_call)
|
||||
{
|
||||
printf("Query current call status\n");
|
||||
esp_hf_client_query_current_calls();
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(retrieve_subscriber)
|
||||
{
|
||||
printf("Retrieve subscriber information\n");
|
||||
esp_hf_client_retrieve_subscriber_info();
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(request_last_voice_tag)
|
||||
{
|
||||
printf("Request last voice tag\n");
|
||||
esp_hf_client_request_last_voice_tag_number();
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(btrh)
|
||||
{
|
||||
if (argn != 2) {
|
||||
printf("Insufficient number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
int btrh;
|
||||
if (sscanf(argv[1], "%d", &btrh) != 1) {
|
||||
printf("Invalid argument %s\n", argv[1]);
|
||||
return;
|
||||
}
|
||||
if (btrh < ESP_HF_BTRH_CMD_HOLD || btrh > ESP_HF_BTRH_CMD_REJECT) {
|
||||
printf("Invalid argument %s\n", argv[1]);
|
||||
return;
|
||||
}
|
||||
printf("respond and hold command: %d\n", btrh);
|
||||
esp_hf_client_send_btrh_cmd(btrh);
|
||||
}
|
||||
|
||||
static bool is_dtmf_code(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') {
|
||||
return true;
|
||||
}
|
||||
if (c == '#' || c == '*') {
|
||||
return true;
|
||||
}
|
||||
if (c >= 'A' && c <= 'D') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HF_CMD_HANDLER(dtmf)
|
||||
{
|
||||
if (argn != 2) {
|
||||
printf("Insufficient number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(argv[1]) != 1 || !is_dtmf_code(argv[1][0])) {
|
||||
printf("Invalid argument %s\n", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("send dtmf code: %s\n", argv[1]);
|
||||
esp_hf_client_send_dtmf(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, "qop", hf_query_op_handler},
|
||||
{120, "qc", hf_query_call_handler},
|
||||
{50, "ac", hf_answer_handler},
|
||||
{60, "rc", hf_reject_handler},
|
||||
{70, "d", hf_dial_handler},
|
||||
{80, "rd", hf_redial_handler},
|
||||
{85, "dm", hf_dial_mem_handler},
|
||||
{90, "vron", hf_start_vr_handler},
|
||||
{100, "vroff", hf_stop_vr_handler},
|
||||
{110, "vu", hf_volume_update_handler},
|
||||
{130, "rs", hf_retrieve_subscriber_handler},
|
||||
{140, "rv", hf_request_last_voice_tag_handler},
|
||||
{150, "rh", hf_btrh_handler},
|
||||
{160, "k", hf_dtmf_handler},
|
||||
};
|
||||
|
||||
hf_msg_hdl_t *hf_get_cmd_tbl(void)
|
||||
{
|
||||
return hf_cmd_tbl;
|
||||
}
|
||||
|
||||
size_t hf_get_cmd_tbl_size(void)
|
||||
{
|
||||
return sizeof(hf_cmd_tbl) / sizeof(hf_msg_hdl_t);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __APP_HF_MSG_SET_H__
|
||||
#define __APP_HF_MSG_SET_H__
|
||||
|
||||
#define HF_MSG_ARGS_MAX (5)
|
||||
|
||||
typedef void (* hf_cmd_handler)(int argn, char **argv);
|
||||
|
||||
typedef struct {
|
||||
uint16_t opcode;
|
||||
const char *str;
|
||||
hf_cmd_handler handler;
|
||||
} hf_msg_hdl_t;
|
||||
|
||||
extern hf_msg_hdl_t *hf_get_cmd_tbl(void);
|
||||
extern size_t hf_get_cmd_tbl_size(void);
|
||||
|
||||
void hf_msg_show_usage(void);
|
||||
#endif /* __APP_HF_MSG_SET_H__*/
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/xtensa_api.h"
|
||||
#include "freertos/FreeRTOSConfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "bt_app_core.h"
|
||||
|
||||
static void bt_app_task_handler(void *arg);
|
||||
static bool bt_app_send_msg(bt_app_msg_t *msg);
|
||||
static void bt_app_work_dispatched(bt_app_msg_t *msg);
|
||||
|
||||
static xQueueHandle bt_app_task_queue = NULL;
|
||||
static xTaskHandle bt_app_task_handle = NULL;
|
||||
|
||||
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback)
|
||||
{
|
||||
ESP_LOGD(BT_APP_CORE_TAG, "%s event 0x%x, param len %d", __func__, event, param_len);
|
||||
|
||||
bt_app_msg_t msg;
|
||||
memset(&msg, 0, sizeof(bt_app_msg_t));
|
||||
|
||||
msg.sig = BT_APP_SIG_WORK_DISPATCH;
|
||||
msg.event = event;
|
||||
msg.cb = p_cback;
|
||||
|
||||
if (param_len == 0) {
|
||||
return bt_app_send_msg(&msg);
|
||||
} else if (p_params && param_len > 0) {
|
||||
if ((msg.param = malloc(param_len)) != NULL) {
|
||||
memcpy(msg.param, p_params, param_len);
|
||||
/* check if caller has provided a copy callback to do the deep copy */
|
||||
if (p_copy_cback) {
|
||||
p_copy_cback(&msg, msg.param, p_params);
|
||||
}
|
||||
return bt_app_send_msg(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool bt_app_send_msg(bt_app_msg_t *msg)
|
||||
{
|
||||
if (msg == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xQueueSend(bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
|
||||
ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bt_app_work_dispatched(bt_app_msg_t *msg)
|
||||
{
|
||||
if (msg->cb) {
|
||||
msg->cb(msg->event, msg->param);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_app_task_handler(void *arg)
|
||||
{
|
||||
bt_app_msg_t msg;
|
||||
for (;;) {
|
||||
if (pdTRUE == xQueueReceive(bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) {
|
||||
ESP_LOGD(BT_APP_CORE_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event);
|
||||
switch (msg.sig) {
|
||||
case BT_APP_SIG_WORK_DISPATCH:
|
||||
{
|
||||
bt_app_work_dispatched(&msg);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: %d", __func__, msg.sig);
|
||||
break;
|
||||
} // switch (msg.sig)
|
||||
|
||||
if (msg.param) {
|
||||
free(msg.param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bt_app_task_start_up(void)
|
||||
{
|
||||
bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t));
|
||||
xTaskCreate(bt_app_task_handler, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, &bt_app_task_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
void bt_app_task_shut_down(void)
|
||||
{
|
||||
if (bt_app_task_handle) {
|
||||
vTaskDelete(bt_app_task_handle);
|
||||
bt_app_task_handle = NULL;
|
||||
}
|
||||
if (bt_app_task_queue) {
|
||||
vQueueDelete(bt_app_task_queue);
|
||||
bt_app_task_queue = NULL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __BT_APP_CORE_H__
|
||||
#define __BT_APP_CORE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define BT_APP_CORE_TAG "BT_APP_CORE"
|
||||
|
||||
#define BT_APP_SIG_WORK_DISPATCH (0x01)
|
||||
|
||||
/**
|
||||
* @brief handler for the dispatched work
|
||||
*/
|
||||
typedef void (* bt_app_cb_t) (uint16_t event, void *param);
|
||||
|
||||
/* message to be sent */
|
||||
typedef struct {
|
||||
uint16_t sig; /*!< signal to bt_app_task */
|
||||
uint16_t event; /*!< message event id */
|
||||
bt_app_cb_t cb; /*!< context switch callback */
|
||||
void *param; /*!< parameter area needs to be the 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__ */
|
402
examples/bluetooth/bluedroid/classic_bt/hfp_hf/main/bt_app_hf.c
Normal file
402
examples/bluetooth/bluedroid/classic_bt/hfp_hf/main/bt_app_hf.c
Normal file
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
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 esp_hf_ag_apiied.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "bt_app_core.h"
|
||||
#include "bt_app_hf.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_hf_client_api.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"
|
||||
|
||||
const char *c_hf_evt_str[] = {
|
||||
"CONNECTION_STATE_EVT", /*!< connection state changed event */
|
||||
"AUDIO_STATE_EVT", /*!< audio connection state change event */
|
||||
"VR_STATE_CHANGE_EVT", /*!< voice recognition state changed */
|
||||
"CALL_IND_EVT", /*!< call indication event */
|
||||
"CALL_SETUP_IND_EVT", /*!< call setup indication event */
|
||||
"CALL_HELD_IND_EVT", /*!< call held indicator event */
|
||||
"NETWORK_STATE_EVT", /*!< network state change event */
|
||||
"SIGNAL_STRENGTH_IND_EVT", /*!< signal strength indication event */
|
||||
"ROAMING_STATUS_IND_EVT", /*!< roaming status indication event */
|
||||
"BATTERY_LEVEL_IND_EVT", /*!< battery level indication event */
|
||||
"CURRENT_OPERATOR_EVT", /*!< current operator name event */
|
||||
"RESP_AND_HOLD_EVT", /*!< response and hold event */
|
||||
"CLIP_EVT", /*!< Calling Line Identification notification event */
|
||||
"CALL_WAITING_EVT", /*!< call waiting notification */
|
||||
"CLCC_EVT", /*!< listing current calls event */
|
||||
"VOLUME_CONTROL_EVT", /*!< audio volume control event */
|
||||
"AT_RESPONSE", /*!< audio volume control event */
|
||||
"SUBSCRIBER_INFO_EVT", /*!< subscriber information event */
|
||||
"INBAND_RING_TONE_EVT", /*!< in-band ring tone settings */
|
||||
"LAST_VOICE_TAG_NUMBER_EVT", /*!< requested number from AG event */
|
||||
"RING_IND_EVT", /*!< ring indication event */
|
||||
};
|
||||
|
||||
// esp_hf_client_connection_state_t
|
||||
const char *c_connection_state_str[] = {
|
||||
"disconnected",
|
||||
"connecting",
|
||||
"connected",
|
||||
"slc_connected",
|
||||
"disconnecting",
|
||||
};
|
||||
|
||||
// esp_hf_client_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_service_availability_status_t
|
||||
const char *c_service_availability_status_str[] = {
|
||||
"unavailable",
|
||||
"available",
|
||||
};
|
||||
|
||||
// esp_hf_roaming_status_t
|
||||
const char *c_roaming_status_str[] = {
|
||||
"inactive",
|
||||
"active",
|
||||
};
|
||||
|
||||
// esp_hf_client_call_state_t
|
||||
const char *c_call_str[] = {
|
||||
"NO call in progress",
|
||||
"call in progress",
|
||||
};
|
||||
|
||||
// esp_hf_client_callsetup_t
|
||||
const char *c_call_setup_str[] = {
|
||||
"NONE",
|
||||
"INCOMING",
|
||||
"OUTGOING_DIALING",
|
||||
"OUTGOING_ALERTING"
|
||||
};
|
||||
|
||||
// esp_hf_client_callheld_t
|
||||
const char *c_call_held_str[] = {
|
||||
"NONE held",
|
||||
"Held and Active",
|
||||
"Held",
|
||||
};
|
||||
|
||||
// esp_hf_response_and_hold_status_t
|
||||
const char *c_resp_and_hold_str[] = {
|
||||
"HELD",
|
||||
"HELD ACCEPTED",
|
||||
"HELD REJECTED",
|
||||
};
|
||||
|
||||
// esp_hf_client_call_direction_t
|
||||
const char *c_call_dir_str[] = {
|
||||
"outgoing",
|
||||
"incoming",
|
||||
};
|
||||
|
||||
// esp_hf_client_call_state_t
|
||||
const char *c_call_state_str[] = {
|
||||
"active",
|
||||
"held",
|
||||
"dialing",
|
||||
"alerting",
|
||||
"incoming",
|
||||
"waiting",
|
||||
"held_by_resp_hold",
|
||||
};
|
||||
|
||||
// esp_hf_current_call_mpty_type_t
|
||||
const char *c_call_mpty_type_str[] = {
|
||||
"single",
|
||||
"multi",
|
||||
};
|
||||
|
||||
// esp_hf_volume_control_target_t
|
||||
const char *c_volume_control_target_str[] = {
|
||||
"SPEAKER",
|
||||
"MICROPHONE"
|
||||
};
|
||||
|
||||
// esp_hf_at_response_code_t
|
||||
const char *c_at_response_code_str[] = {
|
||||
"OK",
|
||||
"ERROR"
|
||||
"ERR_NO_CARRIER",
|
||||
"ERR_BUSY",
|
||||
"ERR_NO_ANSWER",
|
||||
"ERR_DELAYED",
|
||||
"ERR_BLACKLILSTED",
|
||||
"ERR_CME",
|
||||
};
|
||||
|
||||
// esp_hf_subscriber_service_type_t
|
||||
const char *c_subscriber_service_type_str[] = {
|
||||
"unknown",
|
||||
"voice",
|
||||
"fax",
|
||||
};
|
||||
|
||||
// esp_hf_client_in_band_ring_state_t
|
||||
const char *c_inband_ring_state_str[] = {
|
||||
"NOT provided",
|
||||
"Provided",
|
||||
};
|
||||
|
||||
// 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};
|
||||
|
||||
#if CONFIG_BTDM_CONTROLLER_BR_EDR_SCO_DATA_PATH_HCI
|
||||
|
||||
#define ESP_HFP_RINGBUF_SIZE 3600
|
||||
static RingbufHandle_t m_rb = NULL;
|
||||
|
||||
static void bt_app_hf_client_audio_open(void)
|
||||
{
|
||||
m_rb = xRingbufferCreate(ESP_HFP_RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
|
||||
}
|
||||
|
||||
static void bt_app_hf_client_audio_close(void)
|
||||
{
|
||||
if (!m_rb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
vRingbufferDelete(m_rb);
|
||||
}
|
||||
|
||||
static uint32_t bt_app_hf_client_outgoing_cb(uint8_t *p_buf, uint32_t sz)
|
||||
{
|
||||
if (!m_rb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t item_size = 0;
|
||||
uint8_t *data = xRingbufferReceiveUpTo(m_rb, &item_size, 0, sz);
|
||||
if (item_size == sz) {
|
||||
memcpy(p_buf, data, item_size);
|
||||
vRingbufferReturnItem(m_rb, data);
|
||||
return sz;
|
||||
} else if (0 < item_size) {
|
||||
vRingbufferReturnItem(m_rb, data);
|
||||
return 0;
|
||||
} else {
|
||||
// data not enough, do not read
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_app_hf_client_incoming_cb(const uint8_t *buf, uint32_t sz)
|
||||
{
|
||||
if (! m_rb) {
|
||||
return;
|
||||
}
|
||||
BaseType_t done = xRingbufferSend(m_rb, (uint8_t *)buf, sz, 0);
|
||||
if (! done) {
|
||||
ESP_LOGE(BT_HF_TAG, "rb send fail");
|
||||
}
|
||||
|
||||
esp_hf_client_outgoing_data_ready();
|
||||
}
|
||||
#endif /* #if CONFIG_BTDM_CONTROLLER_BR_EDR_SCO_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)
|
||||
{
|
||||
if (event <= ESP_HF_CLIENT_RING_IND_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_CLIENT_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(peer_addr,param->conn_stat.remote_bda,ESP_BD_ADDR_LEN);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_AUDIO_STATE_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--audio state %s",
|
||||
c_audio_state_str[param->audio_stat.state]);
|
||||
#if CONFIG_BTDM_CONTROLLER_BR_EDR_SCO_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,
|
||||
bt_app_hf_client_outgoing_cb);
|
||||
bt_app_hf_client_audio_open();
|
||||
} else if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
|
||||
bt_app_hf_client_audio_close();
|
||||
}
|
||||
#endif /* #if CONFIG_BTDM_CONTROLLER_BR_EDR_SCO_DATA_PATH_HCI */
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_BVRA_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--VR state %s",
|
||||
c_vr_state_str[param->bvra.value]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--NETWORK STATE %s",
|
||||
c_service_availability_status_str[param->service_availability.status]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CIND_ROAMING_STATUS_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--ROAMING: %s",
|
||||
c_roaming_status_str[param->roaming.status]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CIND_SIGNAL_STRENGTH_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "-- signal strength: %d",
|
||||
param->signal_strength.value);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CIND_BATTERY_LEVEL_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--battery level %d",
|
||||
param->battery_level.value);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_COPS_CURRENT_OPERATOR_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--operator name: %s",
|
||||
param->cops.name);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CIND_CALL_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--Call indicator %s",
|
||||
c_call_str[param->call.status]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CIND_CALL_SETUP_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--Call setup indicator %s",
|
||||
c_call_setup_str[param->call_setup.status]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CIND_CALL_HELD_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--Call held indicator %s",
|
||||
c_call_held_str[param->call_held.status]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_BTRH_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--response and hold %s",
|
||||
c_resp_and_hold_str[param->btrh.status]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CLIP_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--clip number %s",
|
||||
(param->clip.number == NULL) ? "NULL" : (param->clip.number));
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CCWA_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--call_waiting %s",
|
||||
(param->ccwa.number == NULL) ? "NULL" : (param->ccwa.number));
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CLCC_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--Current call: idx %d, dir %s, state %s, mpty %s, number %s",
|
||||
param->clcc.idx,
|
||||
c_call_dir_str[param->clcc.dir],
|
||||
c_call_state_str[param->clcc.status],
|
||||
c_call_mpty_type_str[param->clcc.mpty],
|
||||
(param->clcc.number == NULL) ? "NULL" : (param->clcc.number));
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_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_CLIENT_AT_RESPONSE_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--AT response event, code %d, cme %d",
|
||||
param->at_response.code, param->at_response.cme);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_CNUM_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--subscriber type %s, number %s",
|
||||
c_subscriber_service_type_str[param->cnum.type],
|
||||
(param->cnum.number == NULL) ? "NULL" : param->cnum.number);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_BSIR_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--inband ring state %s",
|
||||
c_inband_ring_state_str[param->bsir.state]);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_HF_CLIENT_BINP_EVT:
|
||||
{
|
||||
ESP_LOGI(BT_HF_TAG, "--last voice tag number: %s",
|
||||
(param->binp.number == NULL) ? "NULL" : param->binp.number);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ESP_LOGE(BT_HF_TAG, "HF_CLIENT EVT: %d", event);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
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_client_api.h"
|
||||
|
||||
|
||||
#define BT_HF_TAG "BT_HF"
|
||||
|
||||
/**
|
||||
* @brief callback function for HF client
|
||||
*/
|
||||
void bt_app_hf_client_cb(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_t *param);
|
||||
|
||||
|
||||
#endif /* __BT_APP_HF_H__*/
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding include to include path.)
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __CONSOLE_UART_H__
|
||||
#define __CONSOLE_UART_H__
|
||||
|
||||
#define TAG_CNSL "CNSL"
|
||||
|
||||
/**
|
||||
* @brief configure uart console for command input and process
|
||||
*/
|
||||
esp_err_t console_uart_init(void);
|
||||
|
||||
#endif /* __BT_APP_HF_H__*/
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "gpio_pcm_config.h"
|
||||
|
||||
#define GPIO_OUTPUT_PCM_FSYNC (25)
|
||||
#define GPIO_OUTPUT_PCM_CLK_OUT (5)
|
||||
#define GPIO_OUTPUT_PCM_DOUT (26)
|
||||
#define GPIO_INPUT_PCM_DIN (35)
|
||||
|
||||
#define GPIO_OUTPUT_PCM_PIN_SEL ((1ULL<<GPIO_OUTPUT_PCM_FSYNC) | (1ULL<<GPIO_OUTPUT_PCM_CLK_OUT) | (1ULL<<GPIO_OUTPUT_PCM_DOUT))
|
||||
|
||||
#define GPIO_INPUT_PCM_PIN_SEL (1ULL<<GPIO_INPUT_PCM_DIN)
|
||||
|
||||
void app_gpio_pcm_io_cfg(void)
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
/// configure the PCM output pins
|
||||
//disable interrupt
|
||||
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
//set as output mode
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
||||
io_conf.pin_bit_mask = GPIO_OUTPUT_PCM_PIN_SEL;
|
||||
//disable pull-down mode
|
||||
io_conf.pull_down_en = 0;
|
||||
//disable pull-up mode
|
||||
io_conf.pull_up_en = 0;
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
/// configure the PCM input pin
|
||||
//interrupt of rising edge
|
||||
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
//bit mask of the pins, use GPIO4/5 here
|
||||
io_conf.pin_bit_mask = GPIO_INPUT_PCM_PIN_SEL;
|
||||
//set as input mode
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
//enable pull-up mode
|
||||
io_conf.pull_up_en = 0;
|
||||
io_conf.pull_down_en = 0;
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
/// matrix out | in the internal PCM signals to the GPIOs
|
||||
gpio_matrix_out(GPIO_OUTPUT_PCM_FSYNC, PCMFSYNC_OUT_IDX, false, false);
|
||||
gpio_matrix_out(GPIO_OUTPUT_PCM_CLK_OUT, PCMCLK_OUT_IDX, false, false);
|
||||
gpio_matrix_out(GPIO_OUTPUT_PCM_DOUT, PCMDOUT_IDX, false, false);
|
||||
gpio_matrix_in(GPIO_INPUT_PCM_DIN, PCMDIN_IDX, false);
|
||||
}
|
||||
|
||||
#if ACOUSTIC_ECHO_CANCELLATION_ENABLE
|
||||
|
||||
#define GPIO_OUTPUT_AEC_1 (19)
|
||||
#define GPIO_OUTPUT_AEC_2 (21)
|
||||
#define GPIO_OUTPUT_AEC_3 (22)
|
||||
#define GPIO_OUTPUT_AEC_PIN_SEL ((1ULL<<GPIO_OUTPUT_AEC_1) | (1ULL<<GPIO_OUTPUT_AEC_2) | (1ULL<<GPIO_OUTPUT_AEC_3))
|
||||
|
||||
void app_gpio_aec_io_cfg(void)
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
//disable interrupt
|
||||
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
//set as output mode
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
||||
io_conf.pin_bit_mask = GPIO_OUTPUT_AEC_PIN_SEL;
|
||||
//disable pull-down mode
|
||||
io_conf.pull_down_en = 0;
|
||||
//disable pull-up mode
|
||||
io_conf.pull_up_en = 0;
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
// set the output pins
|
||||
gpio_set_level(GPIO_OUTPUT_AEC_2, 0);
|
||||
|
||||
gpio_set_level(GPIO_OUTPUT_AEC_1, 0);
|
||||
|
||||
gpio_set_level(GPIO_OUTPUT_AEC_1, 1);
|
||||
|
||||
gpio_set_level(GPIO_OUTPUT_AEC_3, 1);
|
||||
}
|
||||
#endif /* ACOUSTIC_ECHO_CANCELLATION_ENABLE */
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#ifndef __GPIO_PCM_CONFIG_H__
|
||||
#define __GPIO_PCM_CONFIG_H__
|
||||
|
||||
#define ACOUSTIC_ECHO_CANCELLATION_ENABLE 1
|
||||
|
||||
void app_gpio_pcm_io_cfg(void);
|
||||
|
||||
#if ACOUSTIC_ECHO_CANCELLATION_ENABLE
|
||||
void app_gpio_aec_io_cfg(void);
|
||||
#endif /* ACOUSTIC_ECHO_CANCELLATION_ENABLE */
|
||||
|
||||
#endif /* #define __GPIO_PCM_CONFIG_H__ */
|
118
examples/bluetooth/bluedroid/classic_bt/hfp_hf/main/main.c
Normal file
118
examples/bluetooth/bluedroid/classic_bt/hfp_hf/main/main.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
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_client_api.h"
|
||||
#include "bt_app_hf.h"
|
||||
#include "gpio_pcm_config.h"
|
||||
#include "console_uart.h"
|
||||
|
||||
/* event for handler "bt_av_hdl_stack_up */
|
||||
enum {
|
||||
BT_APP_EVT_STACK_UP = 0,
|
||||
};
|
||||
|
||||
/* handler for bluetooth stack enabled events */
|
||||
static void bt_hf_client_hdl_stack_evt(uint16_t event, void *p_param);
|
||||
|
||||
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_client_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 */
|
||||
}
|
||||
|
||||
|
||||
static void bt_hf_client_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_HF";
|
||||
esp_bt_dev_set_device_name(dev_name);
|
||||
|
||||
esp_hf_client_register_callback(bt_app_hf_client_cb);
|
||||
esp_hf_client_init();
|
||||
|
||||
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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_CLIENT_ENABLE=y
|
Loading…
Reference in a new issue