P4 fixes
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# 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.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(ble_compatibility_test)
|
||||
@@ -0,0 +1,59 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-S3 | ESP32-P4 | ESP32-H2 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# ESP-IDF BLE Compatibility Test Example
|
||||
|
||||
This example is to test the Bluetooth compatibility and mobile phones.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
This example has been modified to work with ESP-Hosted. The original ESP-IDF example is at [ https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/ble/ble_compatibility_test ].
|
||||
|
||||
This example is able to run on the ESP32-P4 Dev Board, acting as the BT Host, connected to a ESP32 co-processor via the GPIO header, using SPI FD (full duplex) as Hosted HCI transport. The ESP32-P4 acts as the BT Controller. The following GPIO settings were used:
|
||||
|
||||
| SPI Function | ESP32 GPIO | ESP32-P4 GPIO |
|
||||
| :--- | ---: | ---: |
|
||||
| MOSI | 13 | 4 |
|
||||
| MISO | 12 | 5 |
|
||||
| CLK | 14 | 26 |
|
||||
| CS | 15 | 6 |
|
||||
| Handshake | 26 | 20 |
|
||||
| Data Ready | 4 | 36 |
|
||||
| Reset | -1 | 2 |
|
||||
|
||||
> [!NOTE]
|
||||
> SPI Mode 2 was used on both the ESP32-P4 and ESP32.
|
||||
|
||||
Users are free to choose which supported ESP-Hosted transport to use. See the [main ESP-Hosted README](https://github.com/espressif/esp-hosted-mcu/blob/main/README.md#6-decide-the-communication-bus-in-between-host-and-slave) for a list of supported transports.
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Test Scenario
|
||||
|
||||
* ESP32-P4-Function-EV-Board connected to a ESP32 via the GPIO header
|
||||
* [Test case](ble_compatibility_test_case.md)
|
||||
* Test APK: LightBlue V2.0.5
|
||||
|
||||
### Configure the project
|
||||
|
||||
On the ESP32-P4 Dev Board, run `idf.py menuconfig`.
|
||||
|
||||
* Check and enable Classic Bluetooth and Classic BT HID Device under `Component config --> Bluetooth --> Bluedroid Options`
|
||||
* Ensure that `Component config --> Bluetooth --> Controller` is `Disabled`.
|
||||
* Under `Component config --> ESP-Hosted config`:
|
||||
* Configure ESP-Hosted to use `SPI Full-duplex` as the transport
|
||||
* Set the Slave chipset used as `ESP32`
|
||||
* Check and enable `Bluetooth Support`
|
||||
* Configure the GPIOs used for SPI FD on both the ESP32-P4 and ESP32, following the table above
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||
@@ -0,0 +1,180 @@
|
||||
# Test Case for BLE Smartphone Compatibility
|
||||
|
||||
This document provides a test case for BLE smartphone compatibility and includes detailed procedures for various test items.
|
||||
|
||||
## Preparation
|
||||
|
||||
### What You Need
|
||||
|
||||
* ESP device which needs to flash [this test program](main/ble_compatibility_test.c)
|
||||
* Smartphone with LightBlue® Explorer app
|
||||
|
||||
### Initialization
|
||||
|
||||
Prior to conducting tests, please initialize the smartphone and the ESP device as follows:
|
||||
|
||||
* Set the device name as `BLE_COMP_TEST`.
|
||||
* Set the maximum transmission unit (MTU) of the device to 33 bytes, to test the assembly and division of data packets.
|
||||
* If the smartphone has been paired with the ESP device before, please delete the pairing in the Bluetooth setting as follows: `Bluetooth` -> `My Devices` -> `Find this device with "i" in a circle on the right` -> `Forget this device`. Then restart the Bluetooth service.
|
||||
* Before flashing the test program onto the ESP device, make sure to erase the contents of the flash by executing the command `make erase_flash flash` in the Terminal.
|
||||
* When the ESP device restarts, the pairing information will be erased automatically. After that, make sure that the pairing information in the Bluetooth setting of the smartphone is deleted.
|
||||
|
||||
**Note:**
|
||||
|
||||
* For tests marked with (*) further in the document, please bear in mind the following:
|
||||
* Your phone performance may affect the results of these tests. If such a test fails, it does not mean the phone fails to meet the test requirements, but that you need to arrange targeted tests.
|
||||
* Taking "Test for Connection Success Rate" as an example: if the test cannot be passed for 10 consecutive times, you need to record how many times the test was passed and then arrange targeted tests.
|
||||
* For extended testing, please use the [examples] (https://github.com/espressif/esp-idf/tree/master/examples/bluetooth) provided by Espressif.
|
||||
|
||||
## Test for ADV Performance (*)
|
||||
|
||||
### Search Device
|
||||
|
||||
Refresh the scanning in LightBlue® Explorer to check if the device to be tested can be found quickly. Please repeat this action 10 times.
|
||||
|
||||
### Test Results
|
||||
|
||||
The test is passed, if you get the following results:
|
||||
|
||||
* The device starts advertizing and outputs the log `(0) ***** advertising start successfully *****`.
|
||||
* LightBlue® Explorer scans and successfully discovers ` BLE_COMP_TEST` each time.
|
||||
|
||||
**Note:**
|
||||
|
||||
* The device broadcasts on 3 channels, with an ADV interval of 40 ms.
|
||||
* Check if the ADV packet can be received.
|
||||
* Check if the Scan Response packet can be received.
|
||||
* The device name is included in Scan Response packets only and cannot be found in ADV packets.
|
||||
|
||||
## Test for Pairing Performance
|
||||
|
||||
### Connect Device
|
||||
|
||||
* Open the LightBlue® Explorer scan list and tap on the device name ` BLE_COMP_TEST` to establish connection.
|
||||
* ESP device prints a passkey: `The passkey notify number: 123456`.
|
||||
* A prompt on the smartphone appears asking if you want to pair. Tap on *Pair*, and then enter the passkey "123456".
|
||||
|
||||
### Test Results
|
||||
|
||||
The test is passed, if you get the following results:
|
||||
|
||||
* If the connection is successful:
|
||||
* Smartphone shows DATA beginning with `ADVERTISEMENT DATA`
|
||||
* ESP device outputs the log: `ESP_GATTS_CONNECT_EVT`
|
||||
* When the pairing is established, the device shows the following log in green: `(1) ***** pair status = success *****`
|
||||
|
||||
## Test for Service Discovery Performance
|
||||
|
||||
### Test Procedures
|
||||
|
||||
In LightBlue® Explorer, check the contents of `GATT SERVICES & CHARACTERISTICS`.
|
||||
|
||||
### Test Results
|
||||
|
||||
The test is passed, if you get the following results:
|
||||
|
||||
* Service that starts with ``000000ff`` appears at the bottom of your smartphone.
|
||||
* This service contains 3 characteristics
|
||||
* `Char_1_Short_WR`
|
||||
* `Char_2_Long_WR`
|
||||
* `Char_3_Short_Notify`
|
||||
|
||||
## Test for Read and Encrypt
|
||||
|
||||
### Test Procedures
|
||||
|
||||
Read the value of `Char_1` in LightBlue, and tap on `READ AGAIN`.
|
||||
|
||||
### Test Results
|
||||
|
||||
* Encryption is successful, if your smartphone shows the value "11 22 33 44", and the ESP device prints the log: `(2) ***** read char_1 *****`.
|
||||
* Encryption fails, if your smartphone shows a blank screen, and the ESP device outputs the error log in red: `GATT_INSUF_AUTHENTICATION: MITM Required`.
|
||||
|
||||
## Test for Short Read and Write
|
||||
|
||||
### Test Procedures
|
||||
|
||||
* Navigate to the WRITE interface in LightBlue® Explorer, and write the value "88 99" to `Char_1`.
|
||||
* Read `Char_1` and check if its value is consistent with the data you have written to it.
|
||||
|
||||
### Test Results
|
||||
|
||||
The test is passed, if you get the following results:
|
||||
|
||||
* ESP device prints the log: `(3)***** short write success *****`.
|
||||
* LightBlue® Explorer shows "88 99" below `READ AGAIN`.
|
||||
|
||||
## Test for Long Read and Write
|
||||
|
||||
### Test Procedures
|
||||
|
||||
* Navigate to the WRITE interface in LightBlue® Explorer, and write the string `0x001122…FF001122…FF` of 256 bytes to `Char_2`. The data takes up 16 lines and looks as follows:
|
||||
|
||||
```
|
||||
00112233445566778899AABBCCDDEEFF
|
||||
00112233445566778899AABBCCDDEEFF
|
||||
…
|
||||
00112233445566778899AABBCCDDEEFF
|
||||
00112233445566778899AABBCCDDEEFF
|
||||
```
|
||||
|
||||
* Read `Char_2` and check if its value is consistent with the data you have written to it.
|
||||
|
||||
### Test Results
|
||||
|
||||
The test is passed, if you get the following results:
|
||||
|
||||
* The device prints the log: ``ESP_GATTS_EXEC_WRITE_EVT, Length=256`` and ``(4) ***** long write success *****``.
|
||||
* LightBlue® Explorer shows `(5) ***** read char_2 *****` below `READ AGAIN`.
|
||||
|
||||
**Note:**
|
||||
|
||||
The data to be written can be copied from a text file and pasted into LightBlue® Explorer.
|
||||
|
||||
## Test for Short Notify
|
||||
|
||||
### Test Procedures
|
||||
|
||||
* Enter `Char_3` and tap on `SUBSCRIBE` to enable its Notify function.
|
||||
* Your phone automatically receives Notify data from the device.
|
||||
|
||||
### Test Results
|
||||
|
||||
The test is passed, if you get the following results:
|
||||
|
||||
* ESP device prints the log: `(6) ***** send notify AA BB *****`.
|
||||
* "AA BB" appears on your smartphone.
|
||||
|
||||
## Test for Connection Success Rate (*)
|
||||
|
||||
### Test procedures
|
||||
|
||||
* Break the connection
|
||||
* Re-establish the connection
|
||||
* Repeat 10 times
|
||||
|
||||
### Test Results
|
||||
|
||||
The test is passed, if you get the following results:
|
||||
|
||||
* Your phone establishes the connection successfully, and the ESP device outputs the log: `(1) ***** pair status = success *****`.
|
||||
* Your phone breaks the connection, and the device outputs the log: `ESP_GATTS_DISCONNECT_EVT`.
|
||||
* Connection can be set up each time with no issues.
|
||||
|
||||
## Test for Long Connection Stability
|
||||
|
||||
The connection must be stable throughout the tests.
|
||||
|
||||
**Note:**
|
||||
|
||||
If the existing connection breaks:
|
||||
|
||||
* LightBlue® Explorer prints `Disconnected`.
|
||||
* ESP device outputs the log: ``ESP_GATTS_DISCONNECT_EVT, reason = (0) ***** advertising start successfully *****``.
|
||||
|
||||
## Further Information
|
||||
|
||||
* If you see any log entry in red, please record it for future reference or feedback it to our engineer.
|
||||
* Tests to be added in the future:
|
||||
* Multi-connection Test
|
||||
* Automatic Re-connection Test
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "ble_compatibility_test.c"
|
||||
PRIV_REQUIRES bt nvs_flash esp_hosted
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,730 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/********************************************************************************
|
||||
*
|
||||
* This file is for gatt server. It can send adv data, and get connected by client.
|
||||
*
|
||||
*********************************************************************************/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
// #include "esp_bt.h"
|
||||
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gatts_api.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "ble_compatibility_test.h"
|
||||
#include "esp_gatt_common_api.h"
|
||||
|
||||
#include "esp_hosted_bt.h"
|
||||
|
||||
#define DEBUG_ON 0
|
||||
|
||||
#if DEBUG_ON
|
||||
#define EXAMPLE_DEBUG ESP_LOGI
|
||||
#else
|
||||
#define EXAMPLE_DEBUG( tag, format, ... )
|
||||
#endif
|
||||
|
||||
#define EXAMPLE_TAG "BLE_COMP"
|
||||
|
||||
#define PROFILE_NUM 1
|
||||
#define PROFILE_APP_IDX 0
|
||||
#define ESP_APP_ID 0x55
|
||||
#define SAMPLE_DEVICE_NAME "BLE_COMP_TEST"
|
||||
#define SVC_INST_ID 0
|
||||
|
||||
/* The max length of characteristic value. When the gatt client write or prepare write,
|
||||
* the data length must be less than GATTS_EXAMPLE_CHAR_VAL_LEN_MAX.
|
||||
*/
|
||||
#define GATTS_EXAMPLE_CHAR_VAL_LEN_MAX 500
|
||||
#define LONG_CHAR_VAL_LEN 500
|
||||
#define SHORT_CHAR_VAL_LEN 10
|
||||
#define GATTS_NOTIFY_FIRST_PACKET_LEN_MAX 20
|
||||
|
||||
#define PREPARE_BUF_MAX_SIZE 1024
|
||||
#define CHAR_DECLARATION_SIZE (sizeof(uint8_t))
|
||||
|
||||
#define ADV_CONFIG_FLAG (1 << 0)
|
||||
#define SCAN_RSP_CONFIG_FLAG (1 << 1)
|
||||
|
||||
static uint8_t adv_config_done = 0;
|
||||
|
||||
uint16_t gatt_db_handle_table[HRS_IDX_NB];
|
||||
|
||||
typedef struct {
|
||||
uint8_t *prepare_buf;
|
||||
int prepare_len;
|
||||
} prepare_type_env_t;
|
||||
|
||||
static prepare_type_env_t prepare_write_env;
|
||||
|
||||
//#define CONFIG_SET_RAW_ADV_DATA
|
||||
#ifdef CONFIG_SET_RAW_ADV_DATA
|
||||
static uint8_t raw_adv_data[] = {
|
||||
/* Flags */
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
/* TX Power */
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb,
|
||||
/* Service UUID */
|
||||
0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xFF, 0x00,
|
||||
/* Device Name */
|
||||
0x0E, ESP_BLE_AD_TYPE_NAME_CMPL, 'B', 'L', 'E', '_', 'C', 'O', 'M', 'P', '_', 'T', 'E', 'S', 'T'
|
||||
};
|
||||
|
||||
static uint8_t raw_scan_rsp_data[] = {
|
||||
/* Flags */
|
||||
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
||||
/* TX Power */
|
||||
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0xeb,
|
||||
/* Service UUID */
|
||||
0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xFF, 0x00
|
||||
};
|
||||
|
||||
#else
|
||||
static uint8_t service_uuid[16] = {
|
||||
/* LSB <--------------------------------------------------------------------------------> MSB */
|
||||
//first uuid, 16bit, [12],[13] is the value
|
||||
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
/* The length of adv data must be less than 31 bytes */
|
||||
static esp_ble_adv_data_t adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x20,
|
||||
.max_interval = 0x40,
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //test_manufacturer,
|
||||
.service_data_len = 0,
|
||||
.p_service_data = NULL,
|
||||
.service_uuid_len = sizeof(service_uuid),
|
||||
.p_service_uuid = service_uuid,
|
||||
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
|
||||
};
|
||||
|
||||
// scan response data
|
||||
static esp_ble_adv_data_t scan_rsp_data = {
|
||||
.set_scan_rsp = true,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x20,
|
||||
.max_interval = 0x40,
|
||||
.appearance = 0x00,
|
||||
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
|
||||
.p_manufacturer_data = NULL, //&test_manufacturer[0],
|
||||
.service_data_len = 0,
|
||||
.p_service_data = NULL,
|
||||
.service_uuid_len = 16,
|
||||
.p_service_uuid = service_uuid,
|
||||
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
|
||||
};
|
||||
#endif /* CONFIG_SET_RAW_ADV_DATA */
|
||||
|
||||
static esp_ble_adv_params_t adv_params = {
|
||||
.adv_int_min = 0x40,
|
||||
.adv_int_max = 0x40,
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
};
|
||||
|
||||
struct gatts_profile_inst {
|
||||
esp_gatts_cb_t gatts_cb;
|
||||
uint16_t gatts_if;
|
||||
uint16_t app_id;
|
||||
uint16_t conn_id;
|
||||
uint16_t service_handle;
|
||||
esp_gatt_srvc_id_t service_id;
|
||||
uint16_t char_handle;
|
||||
esp_bt_uuid_t char_uuid;
|
||||
esp_gatt_perm_t perm;
|
||||
esp_gatt_char_prop_t property;
|
||||
uint16_t descr_handle;
|
||||
esp_bt_uuid_t descr_uuid;
|
||||
};
|
||||
|
||||
static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
||||
|
||||
/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
|
||||
static struct gatts_profile_inst heart_rate_profile_tab[PROFILE_NUM] = {
|
||||
[PROFILE_APP_IDX] = {
|
||||
.gatts_cb = gatts_profile_event_handler,
|
||||
.gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
|
||||
},
|
||||
};
|
||||
|
||||
/* Service */
|
||||
static const uint16_t GATTS_SERVICE_UUID_TEST = 0x00FF;
|
||||
static const uint16_t CHAR_1_SHORT_WR = 0xFF01;
|
||||
static const uint16_t CHAR_2_LONG_WR = 0xFF02;
|
||||
static const uint16_t CHAR_3_SHORT_NOTIFY = 0xFF03;
|
||||
|
||||
static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
|
||||
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
|
||||
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
|
||||
static const uint16_t character_user_description = ESP_GATT_UUID_CHAR_DESCRIPTION;
|
||||
static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY;
|
||||
static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ;
|
||||
static const uint8_t char1_name[] = "Char_1_Short_WR";
|
||||
static const uint8_t char2_name[] = "Char_2_Long_WR";
|
||||
static const uint8_t char3_name[] = "Char_3_Short_Notify";
|
||||
static const uint8_t char_ccc[2] = {0x00, 0x00};
|
||||
static const uint8_t char_value[4] = {0x11, 0x22, 0x33, 0x44};
|
||||
|
||||
|
||||
/* Full Database Description - Used to add attributes into the database */
|
||||
static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] =
|
||||
{
|
||||
// Service Declaration
|
||||
[IDX_SVC] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
|
||||
sizeof(uint16_t), sizeof(GATTS_SERVICE_UUID_TEST), (uint8_t *)&GATTS_SERVICE_UUID_TEST}},
|
||||
|
||||
/* Characteristic Declaration */
|
||||
[IDX_CHAR_A] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
|
||||
CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}},
|
||||
|
||||
/* Characteristic Value */
|
||||
[IDX_CHAR_VAL_A] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&CHAR_1_SHORT_WR, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE | ESP_GATT_PERM_READ_ENC_MITM,
|
||||
SHORT_CHAR_VAL_LEN, sizeof(char_value), (uint8_t *)char_value}},
|
||||
|
||||
/* Characteristic User Descriptor */
|
||||
[IDX_CHAR_CFG_A] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_user_description, ESP_GATT_PERM_READ,
|
||||
sizeof(char1_name), sizeof(char1_name), (uint8_t *)char1_name}},
|
||||
|
||||
/* Characteristic Declaration */
|
||||
[IDX_CHAR_B] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
|
||||
CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}},
|
||||
|
||||
/* Characteristic Value */
|
||||
[IDX_CHAR_VAL_B] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&CHAR_2_LONG_WR, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
||||
LONG_CHAR_VAL_LEN, sizeof(char_value), (uint8_t *)char_value}},
|
||||
|
||||
/* Characteristic User Descriptor */
|
||||
[IDX_CHAR_CFG_B] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_user_description, ESP_GATT_PERM_READ,
|
||||
sizeof(char2_name), sizeof(char2_name), (uint8_t *)char2_name}},
|
||||
|
||||
/* Characteristic Declaration */
|
||||
[IDX_CHAR_C] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
|
||||
CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}},
|
||||
|
||||
/* Characteristic Value */
|
||||
[IDX_CHAR_VAL_C] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&CHAR_3_SHORT_NOTIFY, 0,
|
||||
LONG_CHAR_VAL_LEN, sizeof(char_value), (uint8_t *)char_value}},
|
||||
|
||||
/* Characteristic User Descriptor */
|
||||
[IDX_CHAR_CFG_C] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_user_description, ESP_GATT_PERM_READ,
|
||||
sizeof(char3_name), sizeof(char3_name), (uint8_t *)char3_name}},
|
||||
|
||||
/* Characteristic Client Configuration Descriptor */
|
||||
[IDX_CHAR_CFG_C_2] =
|
||||
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
||||
sizeof(uint16_t), sizeof(char_ccc), (uint8_t *)char_ccc}},
|
||||
|
||||
};
|
||||
|
||||
static void show_bonded_devices(void)
|
||||
{
|
||||
int dev_num = esp_ble_get_bond_device_num();
|
||||
if (dev_num == 0) {
|
||||
ESP_LOGI(EXAMPLE_TAG, "Bonded devices number zero\n");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
|
||||
if (!dev_list) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "malloc failed, return\n");
|
||||
return;
|
||||
}
|
||||
esp_ble_get_bond_device_list(&dev_num, dev_list);
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "Bonded devices number : %d\n", dev_num);
|
||||
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "Bonded devices list : %d\n", dev_num);
|
||||
for (int i = 0; i < dev_num; i++) {
|
||||
#if DEBUG_ON
|
||||
ESP_LOG_BUFFER_HEX(EXAMPLE_TAG, (void *)dev_list[i].bd_addr, sizeof(esp_bd_addr_t));
|
||||
#endif
|
||||
}
|
||||
|
||||
free(dev_list);
|
||||
}
|
||||
|
||||
static void __attribute__((unused)) remove_all_bonded_devices(void)
|
||||
{
|
||||
int dev_num = esp_ble_get_bond_device_num();
|
||||
if (dev_num == 0) {
|
||||
ESP_LOGI(EXAMPLE_TAG, "Bonded devices number zero\n");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
|
||||
if (!dev_list) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "malloc failed, return\n");
|
||||
return;
|
||||
}
|
||||
esp_ble_get_bond_device_list(&dev_num, dev_list);
|
||||
for (int i = 0; i < dev_num; i++) {
|
||||
esp_ble_remove_bond_device(dev_list[i].bd_addr);
|
||||
}
|
||||
|
||||
free(dev_list);
|
||||
}
|
||||
|
||||
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
#ifdef CONFIG_SET_RAW_ADV_DATA
|
||||
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
|
||||
adv_config_done &= (~ADV_CONFIG_FLAG);
|
||||
if (adv_config_done == 0){
|
||||
esp_ble_gap_start_advertising(&adv_params);
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
|
||||
adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
|
||||
if (adv_config_done == 0){
|
||||
esp_ble_gap_start_advertising(&adv_params);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||
adv_config_done &= (~ADV_CONFIG_FLAG);
|
||||
if (adv_config_done == 0){
|
||||
esp_ble_gap_start_advertising(&adv_params);
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
||||
adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
|
||||
if (adv_config_done == 0){
|
||||
esp_ble_gap_start_advertising(&adv_params);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||
/* advertising start complete event to indicate advertising start successfully or failed */
|
||||
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "advertising start failed");
|
||||
}else{
|
||||
ESP_LOGI(EXAMPLE_TAG, "(0) ***** advertising start successfully ***** ");
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "Advertising stop failed");
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(EXAMPLE_TAG, "Stop adv successfully");
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "update connection params status = %d, conn_int = %d, latency = %d, timeout = %d",
|
||||
param->update_conn_params.status,
|
||||
param->update_conn_params.conn_int,
|
||||
param->update_conn_params.latency,
|
||||
param->update_conn_params.timeout);
|
||||
break;
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT");
|
||||
//esp_ble_passkey_reply(heart_rate_profile_tab[HEART_PROFILE_APP_IDX].remote_bda, true, 0x00);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_NC_REQ_EVT:
|
||||
/* The app will receive this event when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability.
|
||||
show the passkey number to the user to confirm it with the number displayed by peer device. */
|
||||
ESP_LOGI(EXAMPLE_TAG, "ESP_GAP_BLE_NC_REQ_EVT, the passkey Notify number:%" PRIu32, param->ble_security.key_notif.passkey);
|
||||
break;
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
/* send the positive(true) security response to the peer device to accept the security request.
|
||||
If not accept the security request, should send the security response with negative(false) accept value*/
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
break;
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
|
||||
///show the passkey number to the user to input it in the peer device.
|
||||
ESP_LOGI(EXAMPLE_TAG, "The passkey notify number:%06" PRIu32, param->ble_security.key_notif.passkey);
|
||||
break;
|
||||
case ESP_GAP_BLE_KEY_EVT:
|
||||
//shows the ble key info share with peer device to the user.
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "key type = %s", esp_key_type_to_str(param->ble_security.ble_key.key_type));
|
||||
break;
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT: {
|
||||
esp_bd_addr_t bd_addr;
|
||||
memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "remote BD_ADDR: %08x%04x",\
|
||||
(bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
|
||||
(bd_addr[4] << 8) + bd_addr[5]);
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "address type = %d", param->ble_security.auth_cmpl.addr_type);
|
||||
if (param->ble_security.auth_cmpl.success){
|
||||
ESP_LOGI(EXAMPLE_TAG, "(1) ***** pair status = success ***** ");
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(EXAMPLE_TAG, "***** pair status = fail, reason = 0x%x *****", param->ble_security.auth_cmpl.fail_reason);
|
||||
}
|
||||
show_bonded_devices();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: {
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT status = %d", param->remove_bond_dev_cmpl.status);
|
||||
#if DEBUG_ON
|
||||
ESP_LOG_BUFFER_HEX(EXAMPLE_TAG, (void *)param->remove_bond_dev_cmpl.bd_addr, sizeof(esp_bd_addr_t));
|
||||
#endif
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "------------------------------------");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void example_prepare_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param)
|
||||
{
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "prepare write, handle = %d, value len = %d", param->write.handle, param->write.len);
|
||||
esp_gatt_status_t status = ESP_GATT_OK;
|
||||
if (param->write.offset > PREPARE_BUF_MAX_SIZE) {
|
||||
status = ESP_GATT_INVALID_OFFSET;
|
||||
} else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) {
|
||||
status = ESP_GATT_INVALID_ATTR_LEN;
|
||||
}
|
||||
|
||||
if (status == ESP_GATT_OK && prepare_write_env->prepare_buf == NULL) {
|
||||
prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t));
|
||||
prepare_write_env->prepare_len = 0;
|
||||
if (prepare_write_env->prepare_buf == NULL) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "%s, Gatt_server prep no mem", __func__);
|
||||
status = ESP_GATT_NO_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/*send response when param->write.need_rsp is true */
|
||||
if (param->write.need_rsp){
|
||||
esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t));
|
||||
if (gatt_rsp != NULL){
|
||||
gatt_rsp->attr_value.len = param->write.len;
|
||||
gatt_rsp->attr_value.handle = param->write.handle;
|
||||
gatt_rsp->attr_value.offset = param->write.offset;
|
||||
gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
|
||||
memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);
|
||||
esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp);
|
||||
if (response_err != ESP_OK){
|
||||
ESP_LOGE(EXAMPLE_TAG, "Send response error");
|
||||
}
|
||||
free(gatt_rsp);
|
||||
}else{
|
||||
ESP_LOGE(EXAMPLE_TAG, "%s, malloc failed, and no resource to send response", __func__);
|
||||
status = ESP_GATT_NO_RESOURCES;
|
||||
}
|
||||
}
|
||||
if (status != ESP_GATT_OK){
|
||||
return;
|
||||
}
|
||||
memcpy(prepare_write_env->prepare_buf + param->write.offset,
|
||||
param->write.value,
|
||||
param->write.len);
|
||||
prepare_write_env->prepare_len += param->write.len;
|
||||
|
||||
}
|
||||
uint8_t long_write[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
||||
void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param){
|
||||
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC && prepare_write_env->prepare_buf){
|
||||
if(prepare_write_env->prepare_len == 256) {
|
||||
bool long_write_success = true;
|
||||
for(uint16_t i = 0; i < prepare_write_env->prepare_len; i ++) {
|
||||
if(prepare_write_env->prepare_buf[i] != long_write[i%16]) {
|
||||
long_write_success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(long_write_success) {
|
||||
ESP_LOGI(EXAMPLE_TAG, "(4) ***** long write success ***** ");
|
||||
}
|
||||
}
|
||||
}else{
|
||||
ESP_LOGI(EXAMPLE_TAG,"ESP_GATT_PREP_WRITE_CANCEL");
|
||||
}
|
||||
if (prepare_write_env->prepare_buf) {
|
||||
free(prepare_write_env->prepare_buf);
|
||||
prepare_write_env->prepare_buf = NULL;
|
||||
}
|
||||
prepare_write_env->prepare_len = 0;
|
||||
}
|
||||
|
||||
static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
case ESP_GATTS_REG_EVT:{
|
||||
esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);
|
||||
if (set_dev_name_ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "set device name failed, error code = %x", set_dev_name_ret);
|
||||
}
|
||||
#ifdef CONFIG_SET_RAW_ADV_DATA
|
||||
esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data));
|
||||
if (raw_adv_ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "config raw adv data failed, error code = %x ", raw_adv_ret);
|
||||
}
|
||||
adv_config_done |= ADV_CONFIG_FLAG;
|
||||
esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_scan_rsp_data, sizeof(raw_scan_rsp_data));
|
||||
if (raw_scan_ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "config raw scan rsp data failed, error code = %x", raw_scan_ret);
|
||||
}
|
||||
adv_config_done |= SCAN_RSP_CONFIG_FLAG;
|
||||
#else
|
||||
//config adv data
|
||||
esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
|
||||
if (ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "config adv data failed, error code = %x", ret);
|
||||
}
|
||||
adv_config_done |= ADV_CONFIG_FLAG;
|
||||
//config scan response data
|
||||
ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
|
||||
if (ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "config scan response data failed, error code = %x", ret);
|
||||
}
|
||||
adv_config_done |= SCAN_RSP_CONFIG_FLAG;
|
||||
#endif
|
||||
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID);
|
||||
if (create_attr_ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "create attr table failed, error code = %x", create_attr_ret);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_READ_EVT:
|
||||
//ESP_LOGE(EXAMPLE_TAG, "ESP_GATTS_READ_EVT, handle=0x%d, offset=%d", param->read.handle, param->read.offset);
|
||||
if(gatt_db_handle_table[IDX_CHAR_VAL_A] == param->read.handle) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "(2) ***** read char1 ***** ");
|
||||
}
|
||||
if(gatt_db_handle_table[IDX_CHAR_VAL_B] == param->read.handle) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "(5) ***** read char2 ***** ");
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_WRITE_EVT:
|
||||
if (!param->write.is_prep){
|
||||
// the data length of gattc write must be less than GATTS_EXAMPLE_CHAR_VAL_LEN_MAX.
|
||||
if (gatt_db_handle_table[IDX_CHAR_CFG_C_2] == param->write.handle && param->write.len == 2){
|
||||
uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0];
|
||||
uint8_t notify_data[2];
|
||||
notify_data[0] = 0xAA;
|
||||
notify_data[1] = 0xBB;
|
||||
|
||||
if (descr_value == 0x0001){
|
||||
//the size of notify_data[] need less than MTU size
|
||||
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gatt_db_handle_table[IDX_CHAR_VAL_C],
|
||||
sizeof(notify_data), notify_data, false);
|
||||
ESP_LOGI(EXAMPLE_TAG, "(6) ***** send notify AA BB ***** ");
|
||||
}else if (descr_value == 0x0002){
|
||||
//the size of indicate_data[] need less than MTU size
|
||||
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gatt_db_handle_table[IDX_CHAR_VAL_C],
|
||||
sizeof(notify_data), notify_data, true);
|
||||
}
|
||||
else if (descr_value == 0x0000){
|
||||
ESP_LOGI(EXAMPLE_TAG, "notify/indicate disable ");
|
||||
}else{
|
||||
ESP_LOGE(EXAMPLE_TAG, "unknown descr value");
|
||||
ESP_LOG_BUFFER_HEX(EXAMPLE_TAG, param->write.value, param->write.len);
|
||||
}
|
||||
|
||||
}
|
||||
if(gatt_db_handle_table[IDX_CHAR_VAL_A] == param->write.handle && param->write.len == 2) {
|
||||
uint8_t write_data[2] = {0x88, 0x99};
|
||||
if(memcmp(write_data, param->write.value, param->write.len) == 0) {
|
||||
ESP_LOGI(EXAMPLE_TAG, "(3)***** short write success ***** ");
|
||||
}
|
||||
}
|
||||
|
||||
/* send response when param->write.need_rsp is true*/
|
||||
if (param->write.need_rsp){
|
||||
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
|
||||
}
|
||||
}else{
|
||||
/* handle prepare write */
|
||||
example_prepare_write_event_env(gatts_if, &prepare_write_env, param);
|
||||
}
|
||||
break;
|
||||
case ESP_GATTS_EXEC_WRITE_EVT:
|
||||
// the length of gattc prepare write data must be less than GATTS_EXAMPLE_CHAR_VAL_LEN_MAX.
|
||||
ESP_LOGI(EXAMPLE_TAG, "ESP_GATTS_EXEC_WRITE_EVT, Length=%d", prepare_write_env.prepare_len);
|
||||
example_exec_write_event_env(&prepare_write_env, param);
|
||||
break;
|
||||
case ESP_GATTS_MTU_EVT:
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
|
||||
break;
|
||||
case ESP_GATTS_CONF_EVT:
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "ESP_GATTS_CONF_EVT, status = %d", param->conf.status);
|
||||
break;
|
||||
case ESP_GATTS_START_EVT:
|
||||
EXAMPLE_DEBUG(EXAMPLE_TAG, "SERVICE_START_EVT, status %d, service_handle %d", param->start.status, param->start.service_handle);
|
||||
break;
|
||||
case ESP_GATTS_CONNECT_EVT:
|
||||
ESP_LOGI(EXAMPLE_TAG, "ESP_GATTS_CONNECT_EVT, conn_id = %d", param->connect.conn_id);
|
||||
/* start security connect with peer device when receive the connect event sent by the master */
|
||||
esp_ble_set_encryption(param->connect.remote_bda, ESP_BLE_SEC_ENCRYPT_MITM);
|
||||
break;
|
||||
case ESP_GATTS_DISCONNECT_EVT:
|
||||
ESP_LOGI(EXAMPLE_TAG, "ESP_GATTS_DISCONNECT_EVT, reason = %d", param->disconnect.reason);
|
||||
esp_ble_gap_start_advertising(&adv_params);
|
||||
break;
|
||||
case ESP_GATTS_CREAT_ATTR_TAB_EVT:{
|
||||
if (param->add_attr_tab.status != ESP_GATT_OK){
|
||||
ESP_LOGE(EXAMPLE_TAG, "create attribute table failed, error code=0x%x", param->add_attr_tab.status);
|
||||
}
|
||||
else if (param->add_attr_tab.num_handle != HRS_IDX_NB){
|
||||
ESP_LOGE(EXAMPLE_TAG, "create attribute table abnormally, num_handle (%d) \
|
||||
doesn't equal to HRS_IDX_NB(%d)", param->add_attr_tab.num_handle, HRS_IDX_NB);
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(EXAMPLE_TAG, "create attribute table successfully, the number handle = %d",param->add_attr_tab.num_handle);
|
||||
memcpy(gatt_db_handle_table, param->add_attr_tab.handles, sizeof(gatt_db_handle_table));
|
||||
esp_ble_gatts_start_service(gatt_db_handle_table[IDX_SVC]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
|
||||
{
|
||||
|
||||
/* If event is register event, store the gatts_if for each profile */
|
||||
if (event == ESP_GATTS_REG_EVT) {
|
||||
if (param->reg.status == ESP_GATT_OK) {
|
||||
heart_rate_profile_tab[PROFILE_APP_IDX].gatts_if = gatts_if;
|
||||
} else {
|
||||
ESP_LOGE(EXAMPLE_TAG, "reg app failed, app_id %04x, status %d",
|
||||
param->reg.app_id,
|
||||
param->reg.status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
do {
|
||||
int idx;
|
||||
for (idx = 0; idx < PROFILE_NUM; idx++) {
|
||||
/* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
|
||||
if (gatts_if == ESP_GATT_IF_NONE || gatts_if == heart_rate_profile_tab[idx].gatts_if) {
|
||||
if (heart_rate_profile_tab[idx].gatts_cb) {
|
||||
heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* Initialize NVS. */
|
||||
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 );
|
||||
|
||||
/* initialize TRANSPORT first */
|
||||
hosted_hci_bluedroid_open();
|
||||
|
||||
/* get HCI driver operations */
|
||||
esp_bluedroid_hci_driver_operations_t operations = {
|
||||
.send = hosted_hci_bluedroid_send,
|
||||
.check_send_available = hosted_hci_bluedroid_check_send_available,
|
||||
.register_host_callback = hosted_hci_bluedroid_register_host_callback,
|
||||
};
|
||||
esp_bluedroid_attach_hci_driver(&operations);
|
||||
|
||||
|
||||
#if 0
|
||||
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
|
||||
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
ret = esp_bt_controller_init(&bt_cfg);
|
||||
if (ret) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
if (ret) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = esp_bluedroid_init();
|
||||
if (ret) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_enable();
|
||||
if (ret) {
|
||||
ESP_LOGE(EXAMPLE_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_ble_gatts_register_callback(gatts_event_handler);
|
||||
if (ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "gatts register error, error code = %x", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_ble_gap_register_callback(gap_event_handler);
|
||||
if (ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "gap register error, error code = %x", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_ble_gatts_app_register(ESP_APP_ID);
|
||||
if (ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "gatts app register error, error code = %x", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(33);
|
||||
if (local_mtu_ret){
|
||||
ESP_LOGE(EXAMPLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
|
||||
}
|
||||
|
||||
/* set the security iocap & auth_req & key size & init key response key parameters to the stack*/
|
||||
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND; //bonding with peer device after authentication
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT; //set the IO capability to No output No input
|
||||
uint8_t key_size = 16; //the key size should be 7~16 bytes
|
||||
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint32_t passkey = 123456;
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
|
||||
/* If your BLE device act as a Slave, the init_key means you hope which types of key of the master should distribute to you,
|
||||
and the response key means which key you can distribute to the Master;
|
||||
If your BLE device act as a master, the response key means you hope which types of key of the slave should distribute to you,
|
||||
and the init key means which key you can distribute to the slave. */
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Attributes State Machine */
|
||||
enum
|
||||
{
|
||||
IDX_SVC,
|
||||
IDX_CHAR_A,
|
||||
IDX_CHAR_VAL_A,
|
||||
IDX_CHAR_CFG_A,
|
||||
|
||||
IDX_CHAR_B,
|
||||
IDX_CHAR_VAL_B,
|
||||
IDX_CHAR_CFG_B,
|
||||
|
||||
IDX_CHAR_C,
|
||||
IDX_CHAR_VAL_C,
|
||||
IDX_CHAR_CFG_C,
|
||||
IDX_CHAR_CFG_C_2,
|
||||
|
||||
HRS_IDX_NB,
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
dependencies:
|
||||
espressif/esp_hosted:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: ~2
|
||||
espressif/esp_wifi_remote:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: '>=0.10'
|
||||
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# BT config
|
||||
# - ESP32 co-processor only supports BLE 4.2
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
# CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3.
|
||||
CONFIG_BT_LE_50_FEATURE_SUPPORT=n
|
||||
|
||||
#
|
||||
# Wi-Fi Remote
|
||||
#
|
||||
CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y
|
||||
|
||||
#
|
||||
# Enable ESP Hosted BT
|
||||
# Used as VHCI transport between BT Host and Controller
|
||||
#
|
||||
CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID=y
|
||||
CONFIG_ESP_HOSTED_BLUEDROID_HCI_VHCI=y
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
# 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.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(bt_hid_mouse_device)
|
||||
@@ -0,0 +1,298 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-S3 | ESP32-P4 | ESP32-H2 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# Bluetooth HID Device example
|
||||
|
||||
This example aims to show how to implement a Bluetooth HID device using the APIs provided by Classic Bluetooth HID profile.
|
||||
|
||||
This example simulates a Bluetooth HID mouse device that periodically sends report to remote Bluetooth HID host after connection. The report indicates a horizontally moving pointer and can be observed on the display on the HID host side. If you want to build an HID device, this can be your first example to look at.
|
||||
|
||||
## How to use example
|
||||
|
||||
This example has been modified to work with ESP-Hosted. The original ESP-IDF example is at [ https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/ ].
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* This example is able to run on the ESP32-P4 Dev Board, acting as the BT Host, connected to a ESP32 co-processor via the GPIO header, using SPI FD (full duplex) as Hosted HCI transport. The ESP32 acts as the BT Controller. The following GPIO settings were used:
|
||||
|
||||
| SPI Function | ESP32 GPIO | ESP32-P4 GPIO |
|
||||
| :--- | ---: | ---: |
|
||||
| MOSI | 13 | 4 |
|
||||
| MISO | 12 | 5 |
|
||||
| CLK | 14 | 26 |
|
||||
| CS | 15 | 6 |
|
||||
| Handshake | 26 | 20 |
|
||||
| Data Ready | 4 | 36 |
|
||||
| Reset | -1 | 2 |
|
||||
|
||||
> [!NOTE]
|
||||
> SPI Mode 2 was used on both the ESP32-P4 and ESP32.
|
||||
|
||||
Users are free to choose which supported ESP-Hosted transport to use. See the [main ESP-Hosted README](https://github.com/espressif/esp-hosted-mcu/blob/main/README.md#6-decide-the-communication-bus-in-between-host-and-slave) for a list of supported transports.
|
||||
|
||||
* This example is supposed to connect to a Classic Bluetooth HID Host device, e.g. laptop or tablet.
|
||||
|
||||
### Configure the project
|
||||
|
||||
On the ESP32-P4 Dev Board, run `idf.py menuconfig`.
|
||||
|
||||
* Check and enable Classic Bluetooth and Classic BT HID Device under `Component config --> Bluetooth --> Bluedroid Options`
|
||||
* Ensure that `Component config --> Bluetooth --> Controller` is `Disabled`.
|
||||
* Under `Component config --> ESP-Hosted config`:
|
||||
* Configure ESP-Hosted to use `SPI Full-duplex` as the transport
|
||||
* Set the Slave chipset used as `ESP32`
|
||||
* Check and enable `Bluetooth Support`
|
||||
* Configure the GPIOs used for SPI FD on both the ESP32-P4 and ESP32, following the table above
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
The following log will be shown on the IDF monitor console:
|
||||
|
||||
```
|
||||
I (499) main_task: Calling app_main()
|
||||
I (509) transport: Attempt connection with slave: retry[0]
|
||||
I (509) transport: Reset slave using GPIO[2]
|
||||
I (509) os_wrapper_esp: GPIO [2] configured
|
||||
I (509) gpio: GPIO[2]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (1699) transport: Received INIT event from ESP32 peripheral
|
||||
I (1699) transport: EVENT: 12
|
||||
I (1699) transport: EVENT: 11
|
||||
I (1699) transport: capabilities: 0xf8
|
||||
I (1709) transport: Features supported are:
|
||||
I (1709) transport: - HCI over SPI
|
||||
I (1709) transport: - BT/BLE dual mode
|
||||
I (1719) transport: EVENT: 13
|
||||
I (1719) transport: ESP board type is : 0
|
||||
|
||||
I (1719) transport: Base transport is set-up
|
||||
|
||||
I (1729) transport: Slave chip Id[12]
|
||||
I (1729) vhci_drv: Host BT Support: Enabled
|
||||
I (1729) vhci_drv: BT Transport Type: VHCI
|
||||
I (1739) spi: Received INIT event
|
||||
I (2799) app_main: setting device name
|
||||
I (2799) esp_bt_gap_cb: event: 10
|
||||
I (2799) app_main: setting cod major, peripheral
|
||||
I (4799) app_main: register hid device callback
|
||||
I (4799) app_main: starting hid device
|
||||
I (4799) esp_bt_hidd_cb: setting hid parameters
|
||||
I (4799) esp_bt_gap_cb: event: 10
|
||||
I (4799) esp_bt_hidd_cb: setting hid parameters success!
|
||||
I (4799) esp_bt_hidd_cb: setting to connectable, discoverable
|
||||
I (4809) app_main: Own address:[10:97:bd:d5:8a:62]
|
||||
I (4809) app_main: exiting
|
||||
```
|
||||
|
||||
The messages show the successful initialization of Bluetooth stack and HID application. ESP32-P4 will become discoverable with the Bluetooth device name as "HID Mouse Example", by nearby Bluetooth HID Host device.
|
||||
|
||||
Connect to ESP32-P4 on the HID Host side, then finish bonding. After that the HID connection will be established. IDF monitor console will continue to print messages like:
|
||||
|
||||
```
|
||||
W (21229) BT_HCI: hcif conn complete: hdl 0x80, st 0x0
|
||||
I (21229) esp_bt_gap_cb: event: 16
|
||||
I (21859) esp_bt_gap_cb: authentication success: XXXXXXXX
|
||||
I (21859) esp_bt_gap_cb: 64 49 7d d0 fd 99
|
||||
I (21889) esp_bt_gap_cb: event: 21
|
||||
W (22299) BT_HIDD: hidd_l2cif_config_cfm: config failed, retry
|
||||
W (22319) BT_APPL: new conn_srvc id:20, app_id:1
|
||||
I (22319) esp_bt_hidd_cb: connected to 64:49:7d:d0:fd:99
|
||||
I (22319) mouse_move_task: starting
|
||||
I (22319) esp_bt_hidd_cb: making self non-discoverable and non-connectable.
|
||||
W (22329) BT_HCI: hci cmd send: sniff: hdl 0x80, intv(10 18)
|
||||
I (22339) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
|
||||
W (22349) BT_HCI: hcif mode change: hdl 0x80, mode 2, intv 18, status 0x0
|
||||
I (22349) esp_bt_gap_cb: ESP_BT_GAP_MODE_CHG_EVT mode:2
|
||||
I (22369) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
|
||||
I (22419) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
|
||||
I (22469) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
|
||||
I (22519) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
|
||||
I (22569) esp_bt_hidd_cb: ESP_HIDD_SEND_REPORT_EVT id:0x00, type:1
|
||||
```
|
||||
|
||||
ESP32-P4 will generate and send HID mouse reports periodically. On the screen of HID Host, the cursor will move horizontally from left to right and then right to left, and so on so forth.
|
||||
|
||||
## Example Breakdown
|
||||
|
||||
### Initial settings for Bluetooth HID device profile
|
||||
|
||||
Bluetooth HID device requires the specific major and minor device type in the Class of Device (CoD), the following lines of source code performs the configuration of CoD:
|
||||
|
||||
```
|
||||
void app_main(void) {
|
||||
...
|
||||
ESP_LOGI(TAG, "setting cod major, peripheral");
|
||||
esp_bt_cod_t cod;
|
||||
cod.major = ESP_BT_COD_MAJOR_DEV_PERIPHERAL;
|
||||
esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_MAJOR_MINOR);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Bluetooth HID device profile requires the information of service name, provide, device subclass, report descriptor for SDP server, as well as L2CAP QoS configurations from the application. Following lines in function `app_main` initialize these information fields:
|
||||
|
||||
```
|
||||
void app_main(void) {
|
||||
...
|
||||
// Initialize HID SDP information and L2CAP parameters.
|
||||
// to be used in the call of `esp_bt_hid_device_register_app` after profile initialization finishes
|
||||
do {
|
||||
s_local_param.app_param.name = "Mouse";
|
||||
s_local_param.app_param.description = "Mouse Example";
|
||||
s_local_param.app_param.provider = "ESP32";
|
||||
s_local_param.app_param.subclass = ESP_HID_CLASS_MIC;
|
||||
s_local_param.app_param.desc_list = hid_mouse_descriptor;
|
||||
s_local_param.app_param.desc_list_len = hid_mouse_descriptor_len;
|
||||
|
||||
memset(&s_local_param.both_qos, 0, sizeof(esp_hidd_qos_param_t)); // don't set the qos parameters
|
||||
} while (0);
|
||||
|
||||
// Report Protocol Mode is the default mode, according to Bluetooth HID specification
|
||||
s_local_param.protocol_mode = ESP_HIDD_REPORT_MODE;
|
||||
|
||||
ESP_LOGI(TAG, "register hid device callback");
|
||||
esp_bt_hid_device_register_callback(esp_bt_hidd_cb);
|
||||
|
||||
ESP_LOGI(TAG, "starting hid device");
|
||||
esp_bt_hid_device_init();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The information is set to global struct `s_local_param` and will be used upon successful profile initialization, i.e. reception of `ESP_HIDD_INIT_EVT` which is generated after the call of `esp_bt_hid_device_init()`:
|
||||
|
||||
```
|
||||
void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
|
||||
{
|
||||
...
|
||||
switch (event) {
|
||||
case ESP_HIDD_INIT_EVT:
|
||||
if (param->init.status == ESP_HIDD_SUCCESS) {
|
||||
ESP_LOGI(TAG, "setting hid parameters");
|
||||
esp_bt_hid_device_register_app(&s_local_param.app_param, &s_local_param.both_qos, &s_local_param.both_qos);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "init hidd failed!");
|
||||
}
|
||||
break;
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Determination of HID Report Mode
|
||||
|
||||
There are two HID report modes: Report Protocol Mode and Boot Protocol Mode. The former is the default mode. The two report modes differ in the report contents and format. The example supports both of the two modes.
|
||||
|
||||
Report Mode requires report descriptor to describe the usage and format of the reports. For Bluetooth HID device, the report descriptor shall be provided in the SDP server, which can be discovered and used by remote HID Host.
|
||||
|
||||
Boot Mode only supports keyboards and mice, with pre-defined report formats. Therefore it does not require a report descriptor parser on the remote HID Host. It is originally used to simplify the design of PC BIOSs.
|
||||
|
||||
The following code lines set Report Protocol Mode as the default Report Mode:
|
||||
|
||||
```
|
||||
void app_main(void) {
|
||||
...
|
||||
// Report Protocol Mode is the default mode, according to Bluetooth HID specification
|
||||
s_local_param.protocol_mode = ESP_HIDD_REPORT_MODE;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Report Mode can be choosen by remote HID Host through the SET_PROTOCOL request:
|
||||
|
||||
```
|
||||
void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
|
||||
{
|
||||
...
|
||||
switch (event) {
|
||||
...
|
||||
case ESP_HIDD_SET_PROTOCOL_EVT:
|
||||
ESP_LOGI(TAG, "ESP_HIDD_SET_PROTOCOL_EVT");
|
||||
if (param->set_protocol.protocol_mode == ESP_HIDD_BOOT_MODE) {
|
||||
ESP_LOGI(TAG, " - boot protocol");
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
s_local_param.x_dir = -1;
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
} else if (param->set_protocol.protocol_mode == ESP_HIDD_REPORT_MODE) {
|
||||
ESP_LOGI(TAG, " - report protocol");
|
||||
}
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
s_local_param.protocol_mode = param->set_protocol.protocol_mode;
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
break;
|
||||
....
|
||||
}
|
||||
....
|
||||
}
|
||||
```
|
||||
|
||||
### Report generation
|
||||
|
||||
The example simulates a mouse by creating a FreeRTOS task that periodically generates and sends the HID mouse report:
|
||||
|
||||
```
|
||||
// move the mouse left and right
|
||||
void mouse_move_task(void* pvParameters)
|
||||
{
|
||||
const char* TAG = "mouse_move_task";
|
||||
|
||||
ESP_LOGI(TAG, "starting");
|
||||
for(;;) {
|
||||
s_local_param.x_dir = 1;
|
||||
int8_t step = 10;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
s_local_param.x_dir *= -1;
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
for (int j = 0; j < 100; j++) {
|
||||
send_mouse_report(0, s_local_param.x_dir * step, 0, 0);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Function `send_mouse_report` is used to pack the information into a mouse HID report and sends it to HID Host, according to the Report Mode applied:
|
||||
|
||||
```
|
||||
// send the buttons, change in x, and change in y
|
||||
void send_mouse_report(uint8_t buttons, char dx, char dy, char wheel)
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint16_t report_size;
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) {
|
||||
report_id = 0;
|
||||
report_size = REPORT_PROTOCOL_MOUSE_REPORT_SIZE;
|
||||
s_local_param.buffer[0] = buttons;
|
||||
s_local_param.buffer[1] = dx;
|
||||
s_local_param.buffer[2] = dy;
|
||||
s_local_param.buffer[3] = wheel;
|
||||
} else {
|
||||
// Boot Mode
|
||||
report_id = ESP_HIDD_BOOT_REPORT_ID_MOUSE;
|
||||
report_size = ESP_HIDD_BOOT_REPORT_SIZE_MOUSE - 1;
|
||||
s_local_param.buffer[0] = buttons;
|
||||
s_local_param.buffer[1] = dx;
|
||||
s_local_param.buffer[2] = dy;
|
||||
}
|
||||
esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, report_id, report_size, s_local_param.buffer);
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
#set(COMPONENT_SRCS "main.c")
|
||||
#set(COMPONENT_ADD_INCLUDEDIRS "")
|
||||
|
||||
#register_component()
|
||||
|
||||
idf_component_register(SRCS "main.c"
|
||||
PRIV_REQUIRES bt nvs_flash esp_hosted
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,15 @@
|
||||
menu "HID Example Configuration"
|
||||
config EXAMPLE_SSP_ENABLED
|
||||
bool "Secure Simple Pairing"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
default y
|
||||
help
|
||||
This enables the Secure Simple Pairing. If disable this option,
|
||||
Bluedroid will only support Legacy Pairing
|
||||
|
||||
config EXAMPLE_LOCAL_DEVICE_NAME
|
||||
string "Local Device Name"
|
||||
default "HID Mouse Example"
|
||||
help
|
||||
Use this option to set local device name.
|
||||
endmenu
|
||||
@@ -0,0 +1,9 @@
|
||||
dependencies:
|
||||
espressif/esp_hosted:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: ~2
|
||||
espressif/esp_wifi_remote:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: '>=0.10'
|
||||
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_hidd_api.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_err.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_hosted_bt.h"
|
||||
|
||||
#define REPORT_PROTOCOL_MOUSE_REPORT_SIZE (4)
|
||||
#define REPORT_BUFFER_SIZE REPORT_PROTOCOL_MOUSE_REPORT_SIZE
|
||||
|
||||
static const char local_device_name[] = CONFIG_EXAMPLE_LOCAL_DEVICE_NAME;
|
||||
|
||||
typedef struct {
|
||||
esp_hidd_app_param_t app_param;
|
||||
esp_hidd_qos_param_t both_qos;
|
||||
uint8_t protocol_mode;
|
||||
SemaphoreHandle_t mouse_mutex;
|
||||
TaskHandle_t mouse_task_hdl;
|
||||
uint8_t buffer[REPORT_BUFFER_SIZE];
|
||||
int8_t x_dir;
|
||||
} local_param_t;
|
||||
|
||||
static local_param_t s_local_param = {0};
|
||||
|
||||
// HID report descriptor for a generic mouse. The contents of the report are:
|
||||
// 3 buttons, moving information for X and Y cursors, information for a wheel.
|
||||
uint8_t hid_mouse_descriptor[] = {
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x02, // USAGE (Mouse)
|
||||
0xa1, 0x01, // COLLECTION (Application)
|
||||
|
||||
0x09, 0x01, // USAGE (Pointer)
|
||||
0xa1, 0x00, // COLLECTION (Physical)
|
||||
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||
0x95, 0x03, // REPORT_COUNT (3)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x95, 0x01, // REPORT_COUNT (1)
|
||||
0x75, 0x05, // REPORT_SIZE (5)
|
||||
0x81, 0x03, // INPUT (Cnst,Var,Abs)
|
||||
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x30, // USAGE (X)
|
||||
0x09, 0x31, // USAGE (Y)
|
||||
0x09, 0x38, // USAGE (Wheel)
|
||||
0x15, 0x81, // LOGICAL_MINIMUM (-127)
|
||||
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
|
||||
0x75, 0x08, // REPORT_SIZE (8)
|
||||
0x95, 0x03, // REPORT_COUNT (3)
|
||||
0x81, 0x06, // INPUT (Data,Var,Rel)
|
||||
|
||||
0xc0, // END_COLLECTION
|
||||
0xc0 // END_COLLECTION
|
||||
};
|
||||
|
||||
static char *bda2str(esp_bd_addr_t bda, char *str, size_t size)
|
||||
{
|
||||
if (bda == NULL || str == NULL || size < 18) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *p = bda;
|
||||
sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
p[0], p[1], p[2], p[3], p[4], p[5]);
|
||||
return str;
|
||||
}
|
||||
|
||||
const int hid_mouse_descriptor_len = sizeof(hid_mouse_descriptor);
|
||||
|
||||
/**
|
||||
* @brief Integrity check of the report ID and report type for GET_REPORT request from HID host.
|
||||
* Boot Protocol Mode requires report ID. For Report Protocol Mode, when the report descriptor
|
||||
* does not declare report ID Global ITEMS, the report ID does not exist in the GET_REPORT request,
|
||||
* and a value of 0 for report_id will occur in ESP_HIDD_GET_REPORT_EVT callback parameter.
|
||||
*/
|
||||
bool check_report_id_type(uint8_t report_id, uint8_t report_type)
|
||||
{
|
||||
bool ret = false;
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
do {
|
||||
if (report_type != ESP_HIDD_REPORT_TYPE_INPUT) {
|
||||
break;
|
||||
}
|
||||
if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) {
|
||||
if (report_id == ESP_HIDD_BOOT_REPORT_ID_MOUSE) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (report_id == 0) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (!ret) {
|
||||
if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) {
|
||||
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID);
|
||||
} else {
|
||||
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID);
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// send the buttons, change in x, and change in y
|
||||
void send_mouse_report(uint8_t buttons, char dx, char dy, char wheel)
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint16_t report_size;
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) {
|
||||
report_id = 0;
|
||||
report_size = REPORT_PROTOCOL_MOUSE_REPORT_SIZE;
|
||||
s_local_param.buffer[0] = buttons;
|
||||
s_local_param.buffer[1] = dx;
|
||||
s_local_param.buffer[2] = dy;
|
||||
s_local_param.buffer[3] = wheel;
|
||||
} else {
|
||||
// Boot Mode
|
||||
report_id = ESP_HIDD_BOOT_REPORT_ID_MOUSE;
|
||||
report_size = ESP_HIDD_BOOT_REPORT_SIZE_MOUSE - 1;
|
||||
s_local_param.buffer[0] = buttons;
|
||||
s_local_param.buffer[1] = dx;
|
||||
s_local_param.buffer[2] = dy;
|
||||
}
|
||||
esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, report_id, report_size, s_local_param.buffer);
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
}
|
||||
|
||||
// move the mouse left and right
|
||||
void mouse_move_task(void *pvParameters)
|
||||
{
|
||||
const char *TAG = "mouse_move_task";
|
||||
|
||||
ESP_LOGI(TAG, "starting");
|
||||
for (;;) {
|
||||
s_local_param.x_dir = 1;
|
||||
int8_t step = 10;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
s_local_param.x_dir *= -1;
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
for (int j = 0; j < 100; j++) {
|
||||
send_mouse_report(0, s_local_param.x_dir * step, 0, 0);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
|
||||
{
|
||||
const char *TAG = "esp_bt_gap_cb";
|
||||
switch (event) {
|
||||
case ESP_BT_GAP_AUTH_CMPL_EVT: {
|
||||
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGI(TAG, "authentication success: %s", param->auth_cmpl.device_name);
|
||||
ESP_LOG_BUFFER_HEX(TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "authentication failed, status:%d", param->auth_cmpl.stat);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_PIN_REQ_EVT: {
|
||||
ESP_LOGI(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(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(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_EXAMPLE_SSP_ENABLED == true)
|
||||
case ESP_BT_GAP_CFM_REQ_EVT:
|
||||
ESP_LOGI(TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %"PRIu32, 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(TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%"PRIu32, param->key_notif.passkey);
|
||||
break;
|
||||
case ESP_BT_GAP_KEY_REQ_EVT:
|
||||
ESP_LOGI(TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!");
|
||||
break;
|
||||
#endif
|
||||
case ESP_BT_GAP_MODE_CHG_EVT:
|
||||
ESP_LOGI(TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "event: %d", event);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void bt_app_task_start_up(void)
|
||||
{
|
||||
s_local_param.mouse_mutex = xSemaphoreCreateMutex();
|
||||
memset(s_local_param.buffer, 0, REPORT_BUFFER_SIZE);
|
||||
xTaskCreate(mouse_move_task, "mouse_move_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3, &s_local_param.mouse_task_hdl);
|
||||
return;
|
||||
}
|
||||
|
||||
void bt_app_task_shut_down(void)
|
||||
{
|
||||
if (s_local_param.mouse_task_hdl) {
|
||||
vTaskDelete(s_local_param.mouse_task_hdl);
|
||||
s_local_param.mouse_task_hdl = NULL;
|
||||
}
|
||||
|
||||
if (s_local_param.mouse_mutex) {
|
||||
vSemaphoreDelete(s_local_param.mouse_mutex);
|
||||
s_local_param.mouse_mutex = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
|
||||
{
|
||||
static const char *TAG = "esp_bt_hidd_cb";
|
||||
switch (event) {
|
||||
case ESP_HIDD_INIT_EVT:
|
||||
if (param->init.status == ESP_HIDD_SUCCESS) {
|
||||
ESP_LOGI(TAG, "setting hid parameters");
|
||||
esp_bt_hid_device_register_app(&s_local_param.app_param, &s_local_param.both_qos, &s_local_param.both_qos);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "init hidd failed!");
|
||||
}
|
||||
break;
|
||||
case ESP_HIDD_DEINIT_EVT:
|
||||
break;
|
||||
case ESP_HIDD_REGISTER_APP_EVT:
|
||||
if (param->register_app.status == ESP_HIDD_SUCCESS) {
|
||||
ESP_LOGI(TAG, "setting hid parameters success!");
|
||||
ESP_LOGI(TAG, "setting to connectable, discoverable");
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
|
||||
if (param->register_app.in_use) {
|
||||
ESP_LOGI(TAG, "start virtual cable plug!");
|
||||
esp_bt_hid_device_connect(param->register_app.bd_addr);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "setting hid parameters failed!");
|
||||
}
|
||||
break;
|
||||
case ESP_HIDD_UNREGISTER_APP_EVT:
|
||||
if (param->unregister_app.status == ESP_HIDD_SUCCESS) {
|
||||
ESP_LOGI(TAG, "unregister app success!");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "unregister app failed!");
|
||||
}
|
||||
break;
|
||||
case ESP_HIDD_OPEN_EVT:
|
||||
if (param->open.status == ESP_HIDD_SUCCESS) {
|
||||
if (param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTING) {
|
||||
ESP_LOGI(TAG, "connecting...");
|
||||
} else if (param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTED) {
|
||||
ESP_LOGI(TAG, "connected to %02x:%02x:%02x:%02x:%02x:%02x", param->open.bd_addr[0],
|
||||
param->open.bd_addr[1], param->open.bd_addr[2], param->open.bd_addr[3], param->open.bd_addr[4],
|
||||
param->open.bd_addr[5]);
|
||||
bt_app_task_start_up();
|
||||
ESP_LOGI(TAG, "making self non-discoverable and non-connectable.");
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "unknown connection status");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "open failed!");
|
||||
}
|
||||
break;
|
||||
case ESP_HIDD_CLOSE_EVT:
|
||||
ESP_LOGI(TAG, "ESP_HIDD_CLOSE_EVT");
|
||||
if (param->close.status == ESP_HIDD_SUCCESS) {
|
||||
if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTING) {
|
||||
ESP_LOGI(TAG, "disconnecting...");
|
||||
} else if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTED) {
|
||||
ESP_LOGI(TAG, "disconnected!");
|
||||
bt_app_task_shut_down();
|
||||
ESP_LOGI(TAG, "making self discoverable and connectable again.");
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "unknown connection status");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "close failed!");
|
||||
}
|
||||
break;
|
||||
case ESP_HIDD_SEND_REPORT_EVT:
|
||||
if (param->send_report.status == ESP_HIDD_SUCCESS) {
|
||||
ESP_LOGI(TAG, "ESP_HIDD_SEND_REPORT_EVT id:0x%02x, type:%d", param->send_report.report_id,
|
||||
param->send_report.report_type);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "ESP_HIDD_SEND_REPORT_EVT id:0x%02x, type:%d, status:%d, reason:%d",
|
||||
param->send_report.report_id, param->send_report.report_type, param->send_report.status,
|
||||
param->send_report.reason);
|
||||
}
|
||||
break;
|
||||
case ESP_HIDD_REPORT_ERR_EVT:
|
||||
ESP_LOGI(TAG, "ESP_HIDD_REPORT_ERR_EVT");
|
||||
break;
|
||||
case ESP_HIDD_GET_REPORT_EVT:
|
||||
ESP_LOGI(TAG, "ESP_HIDD_GET_REPORT_EVT id:0x%02x, type:%d, size:%d", param->get_report.report_id,
|
||||
param->get_report.report_type, param->get_report.buffer_size);
|
||||
if (check_report_id_type(param->get_report.report_id, param->get_report.report_type)) {
|
||||
uint8_t report_id;
|
||||
uint16_t report_len;
|
||||
if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) {
|
||||
report_id = 0;
|
||||
report_len = REPORT_PROTOCOL_MOUSE_REPORT_SIZE;
|
||||
} else {
|
||||
// Boot Mode
|
||||
report_id = ESP_HIDD_BOOT_REPORT_ID_MOUSE;
|
||||
report_len = ESP_HIDD_BOOT_REPORT_SIZE_MOUSE - 1;
|
||||
}
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
esp_bt_hid_device_send_report(param->get_report.report_type, report_id, report_len, s_local_param.buffer);
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "check_report_id failed!");
|
||||
}
|
||||
break;
|
||||
case ESP_HIDD_SET_REPORT_EVT:
|
||||
ESP_LOGI(TAG, "ESP_HIDD_SET_REPORT_EVT");
|
||||
break;
|
||||
case ESP_HIDD_SET_PROTOCOL_EVT:
|
||||
ESP_LOGI(TAG, "ESP_HIDD_SET_PROTOCOL_EVT");
|
||||
if (param->set_protocol.protocol_mode == ESP_HIDD_BOOT_MODE) {
|
||||
ESP_LOGI(TAG, " - boot protocol");
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
s_local_param.x_dir = -1;
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
} else if (param->set_protocol.protocol_mode == ESP_HIDD_REPORT_MODE) {
|
||||
ESP_LOGI(TAG, " - report protocol");
|
||||
}
|
||||
xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY);
|
||||
s_local_param.protocol_mode = param->set_protocol.protocol_mode;
|
||||
xSemaphoreGive(s_local_param.mouse_mutex);
|
||||
break;
|
||||
case ESP_HIDD_INTR_DATA_EVT:
|
||||
ESP_LOGI(TAG, "ESP_HIDD_INTR_DATA_EVT");
|
||||
break;
|
||||
case ESP_HIDD_VC_UNPLUG_EVT:
|
||||
ESP_LOGI(TAG, "ESP_HIDD_VC_UNPLUG_EVT");
|
||||
if (param->vc_unplug.status == ESP_HIDD_SUCCESS) {
|
||||
if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTED) {
|
||||
ESP_LOGI(TAG, "disconnected!");
|
||||
bt_app_task_shut_down();
|
||||
ESP_LOGI(TAG, "making self discoverable and connectable again.");
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "unknown connection status");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "close failed!");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
const char *TAG = "app_main";
|
||||
esp_err_t ret;
|
||||
char bda_str[18] = {0};
|
||||
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
/* initialize TRANSPORT first */
|
||||
hosted_hci_bluedroid_open();
|
||||
|
||||
/* get HCI driver operations */
|
||||
esp_bluedroid_hci_driver_operations_t operations = {
|
||||
.send = hosted_hci_bluedroid_send,
|
||||
.check_send_available = hosted_hci_bluedroid_check_send_available,
|
||||
.register_host_callback = hosted_hci_bluedroid_register_host_callback,
|
||||
};
|
||||
esp_bluedroid_attach_hci_driver(&operations);
|
||||
|
||||
if ((ret = esp_bluedroid_init()) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s initialize bluedroid failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ret = esp_bluedroid_enable()) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "enable bluedroid failed: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "gap register failed: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "setting device name");
|
||||
esp_bt_gap_set_device_name(local_device_name);
|
||||
|
||||
ESP_LOGI(TAG, "setting cod major, peripheral");
|
||||
esp_bt_cod_t cod = {0};
|
||||
cod.major = ESP_BT_COD_MAJOR_DEV_PERIPHERAL;
|
||||
cod.minor = ESP_BT_COD_MINOR_PERIPHERAL_POINTING;
|
||||
esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_MAJOR_MINOR);
|
||||
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
|
||||
// Initialize HID SDP information and L2CAP parameters.
|
||||
// to be used in the call of `esp_bt_hid_device_register_app` after profile initialization finishes
|
||||
do {
|
||||
s_local_param.app_param.name = "Mouse";
|
||||
s_local_param.app_param.description = "Mouse Example";
|
||||
s_local_param.app_param.provider = "ESP32";
|
||||
s_local_param.app_param.subclass = ESP_HID_CLASS_MIC; // keep same with minor class of COD
|
||||
s_local_param.app_param.desc_list = hid_mouse_descriptor;
|
||||
s_local_param.app_param.desc_list_len = hid_mouse_descriptor_len;
|
||||
|
||||
memset(&s_local_param.both_qos, 0, sizeof(esp_hidd_qos_param_t)); // don't set the qos parameters
|
||||
} while (0);
|
||||
|
||||
// Report Protocol Mode is the default mode, according to Bluetooth HID specification
|
||||
s_local_param.protocol_mode = ESP_HIDD_REPORT_MODE;
|
||||
|
||||
ESP_LOGI(TAG, "register hid device callback");
|
||||
esp_bt_hid_device_register_callback(esp_bt_hidd_cb);
|
||||
|
||||
ESP_LOGI(TAG, "starting hid device");
|
||||
esp_bt_hid_device_init();
|
||||
|
||||
#if (CONFIG_EXAMPLE_SSP_ENABLED == true)
|
||||
/* Set default parameters for Secure Simple Pairing */
|
||||
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
|
||||
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_NONE;
|
||||
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set default parameters for Legacy Pairing
|
||||
* Use variable pin, input pin code when pairing
|
||||
*/
|
||||
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
|
||||
esp_bt_pin_code_t pin_code;
|
||||
esp_bt_gap_set_pin(pin_type, 0, pin_code);
|
||||
|
||||
ESP_LOGI(TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str)));
|
||||
ESP_LOGI(TAG, "exiting");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# BT config
|
||||
# - ESP32 co-processor only supports BLE 4.2
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_HID_ENABLED=y
|
||||
CONFIG_BT_HID_DEVICE_ENABLED=y
|
||||
CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
|
||||
#
|
||||
# Wi-Fi Remote
|
||||
# - set ESP32 as default co-processor
|
||||
#
|
||||
CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y
|
||||
CONFIG_SLAVE_IDF_TARGET_ESP32=y
|
||||
|
||||
#
|
||||
# Enable ESP Hosted BT
|
||||
# Used as VHCI transport between BT Host and Controller
|
||||
#
|
||||
CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID=y
|
||||
CONFIG_ESP_HOSTED_BLUEDROID_HCI_VHCI=y
|
||||
@@ -0,0 +1,8 @@
|
||||
# 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.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(host_hci_uart)
|
||||
@@ -0,0 +1,100 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-S3 | ESP32-P4 | ESP32-H2 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
ESP-IDF ESP-Hosted Hosted HCI Host
|
||||
==================================
|
||||
|
||||
This is a Bluetooth Host using ESP-Hosted as HCI IO to the BT Controller.
|
||||
|
||||
## Example Layout
|
||||
|
||||
This example is modified based on [bt_discovery](https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/classic_bt/bt_discovery), and all modifications are listed below:
|
||||
|
||||
- Removed all dependencies on controller from `main.c`.
|
||||
|
||||
```
|
||||
#include "esp_bt.h"
|
||||
|
||||
...
|
||||
|
||||
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
|
||||
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
|
||||
ESP_LOGE(GAP_TAG, "%s initialize controller failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) {
|
||||
ESP_LOGE(GAP_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
- Add support for ESP-Hosted HCI interface: `esp_hosted_bt.h`.
|
||||
|
||||
- Open HCI interface in `main.c`.
|
||||
|
||||
```
|
||||
#include "esp_hosted_bt.h"
|
||||
|
||||
...
|
||||
|
||||
/* initialize TRANSPORT first */
|
||||
hosted_hci_bluedroid_open();
|
||||
|
||||
/* get HCI driver operations */
|
||||
esp_bluedroid_hci_driver_operations_t operations = {
|
||||
.send = hosted_hci_bluedroid_uart_send,
|
||||
.check_send_available = hosted_hci_bluedroid_check_send_available,
|
||||
.register_host_callback = hosted_hci_bluedroid_register_host_callback,
|
||||
};
|
||||
esp_bluedroid_attach_hci_driver(&operations);
|
||||
```
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example runs on the ESP32-P4 Dev Board connected to a ESP32 via the GPIO header, using SPI FD (full duplex) as Hosted HCI transport. The following GPIO settings were used:
|
||||
|
||||
| SPI Function | ESP32 GPIO | ESP32-P4 GPIO |
|
||||
| :--- | ---: | ---: |
|
||||
| MOSI | 13 | 4 |
|
||||
| MISO | 12 | 5 |
|
||||
| CLK | 14 | 26 |
|
||||
| CS | 15 | 6 |
|
||||
| Handshake | 26 | 20 |
|
||||
| Data Ready | 4 | 36 |
|
||||
| Reset | -1 | 2 |
|
||||
|
||||
> [!NOTE]
|
||||
> SPI Mode 2 was used on both the ESP32-P4 and ESP32.
|
||||
|
||||
Users are free to choose which supported ESP-Hosted transport to use. See the [main ESP-Hosted README](https://github.com/espressif/esp-hosted-mcu/blob/main/README.md#6-decide-the-communication-bus-in-between-host-and-slave) for a list of supported transports.
|
||||
|
||||
For standard HCI, configure the co-processor Bluetooth Controller to use UART as the HCI transport, then select appropriate GPIOs on the ESP32-P4 to configure as a UART. In this mode, ESP-Hosted is not involved in transporting the HCI data.
|
||||
|
||||
See the ESP-IDF [UART HCI Host example](https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/bluedroid/bluedroid_host_only/bluedroid_host_only_uart) on how to set-up UART for the Bluetooth Host.
|
||||
|
||||
### Configure the project
|
||||
|
||||
For the ESP32 co-processor, run `idf.py menuconfig` and configure `Example Configuration` for SPI Full-duplex with the correct SPI mode and GPIOs.
|
||||
|
||||
For the ESP32-P4 co-processor, run `idf.py menuconfig` and under `Component config ---> ESP-Hosted config`:
|
||||
|
||||
* set the transport to be `SPI Full-duplex` with the correct SPI modem GPIOs (see above table) and SPI Clock frequency (10 MHz max).
|
||||
* set the Slave chipset used as `ESP32`.
|
||||
* set `Bluetooth Support ---> Enable Hosted Bluedroid Bluetooth support` to enable Bluedroid support. Leave the HCI type as `VHCI`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build and flash the co-processor and host projects, then run monitor tool to view serial output on both the ESP32 and ESP32-P4:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
PRIV_REQUIRES bt nvs_flash esp_hosted
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,9 @@
|
||||
dependencies:
|
||||
espressif/esp_hosted:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: ~2
|
||||
espressif/esp_wifi_remote:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: '>=0.10'
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* This file is for Classic Bluetooth device and service discovery Demo.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.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_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_bluedroid_hci.h"
|
||||
|
||||
#include "esp_hosted_bt.h"
|
||||
|
||||
#define GAP_TAG "GAP"
|
||||
|
||||
typedef enum {
|
||||
APP_GAP_STATE_IDLE = 0,
|
||||
APP_GAP_STATE_DEVICE_DISCOVERING,
|
||||
APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE,
|
||||
APP_GAP_STATE_SERVICE_DISCOVERING,
|
||||
APP_GAP_STATE_SERVICE_DISCOVER_COMPLETE,
|
||||
} app_gap_state_t;
|
||||
|
||||
typedef struct {
|
||||
bool dev_found;
|
||||
uint8_t bdname_len;
|
||||
uint8_t eir_len;
|
||||
uint8_t rssi;
|
||||
uint32_t cod;
|
||||
uint8_t eir[ESP_BT_GAP_EIR_DATA_LEN];
|
||||
uint8_t bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
|
||||
esp_bd_addr_t bda;
|
||||
app_gap_state_t state;
|
||||
} app_gap_cb_t;
|
||||
|
||||
static app_gap_cb_t m_dev_info;
|
||||
|
||||
static char *bda2str(esp_bd_addr_t bda, char *str, size_t size)
|
||||
{
|
||||
if (bda == NULL || str == NULL || size < 18) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *p = bda;
|
||||
sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
p[0], p[1], p[2], p[3], p[4], p[5]);
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *uuid2str(esp_bt_uuid_t *uuid, char *str, size_t size)
|
||||
{
|
||||
if (uuid == NULL || str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (uuid->len == 2 && size >= 5) {
|
||||
sprintf(str, "%04x", uuid->uuid.uuid16);
|
||||
} else if (uuid->len == 4 && size >= 9) {
|
||||
sprintf(str, "%08"PRIx32, uuid->uuid.uuid32);
|
||||
} else if (uuid->len == 16 && size >= 37) {
|
||||
uint8_t *p = uuid->uuid.uuid128;
|
||||
sprintf(str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
p[15], p[14], p[13], p[12], p[11], p[10], p[9], p[8],
|
||||
p[7], p[6], p[5], p[4], p[3], p[2], p[1], p[0]);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static bool get_name_from_eir(uint8_t *eir, uint8_t *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;
|
||||
}
|
||||
|
||||
static void update_device_info(esp_bt_gap_cb_param_t *param)
|
||||
{
|
||||
char bda_str[18];
|
||||
uint32_t cod = 0;
|
||||
int32_t rssi = -129; /* invalid value */
|
||||
uint8_t *bdname = NULL;
|
||||
uint8_t bdname_len = 0;
|
||||
uint8_t *eir = NULL;
|
||||
uint8_t eir_len = 0;
|
||||
esp_bt_gap_dev_prop_t *p;
|
||||
|
||||
ESP_LOGI(GAP_TAG, "Device found: %s", bda2str(param->disc_res.bda, bda_str, 18));
|
||||
for (int i = 0; i < param->disc_res.num_prop; i++) {
|
||||
p = param->disc_res.prop + i;
|
||||
switch (p->type) {
|
||||
case ESP_BT_GAP_DEV_PROP_COD:
|
||||
cod = *(uint32_t *)(p->val);
|
||||
ESP_LOGI(GAP_TAG, "--Class of Device: 0x%"PRIx32, cod);
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_RSSI:
|
||||
rssi = *(int8_t *)(p->val);
|
||||
ESP_LOGI(GAP_TAG, "--RSSI: %"PRId32, rssi);
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_BDNAME:
|
||||
bdname_len = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN :
|
||||
(uint8_t)p->len;
|
||||
bdname = (uint8_t *)(p->val);
|
||||
break;
|
||||
case ESP_BT_GAP_DEV_PROP_EIR: {
|
||||
eir_len = p->len;
|
||||
eir = (uint8_t *)(p->val);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* search for device with Major device type "PHONE" or "Audio/Video" in COD */
|
||||
app_gap_cb_t *p_dev = &m_dev_info;
|
||||
if (p_dev->dev_found) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!esp_bt_gap_is_valid_cod(cod) ||
|
||||
(!(esp_bt_gap_get_cod_major_dev(cod) == ESP_BT_COD_MAJOR_DEV_PHONE) &&
|
||||
!(esp_bt_gap_get_cod_major_dev(cod) == ESP_BT_COD_MAJOR_DEV_AV))) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(p_dev->bda, param->disc_res.bda, ESP_BD_ADDR_LEN);
|
||||
p_dev->dev_found = true;
|
||||
|
||||
p_dev->cod = cod;
|
||||
p_dev->rssi = rssi;
|
||||
if (bdname_len > 0) {
|
||||
memcpy(p_dev->bdname, bdname, bdname_len);
|
||||
p_dev->bdname[bdname_len] = '\0';
|
||||
p_dev->bdname_len = bdname_len;
|
||||
}
|
||||
if (eir_len > 0) {
|
||||
memcpy(p_dev->eir, eir, eir_len);
|
||||
p_dev->eir_len = eir_len;
|
||||
}
|
||||
|
||||
if (p_dev->bdname_len == 0) {
|
||||
get_name_from_eir(p_dev->eir, p_dev->bdname, &p_dev->bdname_len);
|
||||
}
|
||||
|
||||
ESP_LOGI(GAP_TAG, "Found a target device, address %s, name %s", bda_str, p_dev->bdname);
|
||||
p_dev->state = APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE;
|
||||
ESP_LOGI(GAP_TAG, "Cancel device discovery ...");
|
||||
esp_bt_gap_cancel_discovery();
|
||||
}
|
||||
|
||||
static void bt_app_gap_init(void)
|
||||
{
|
||||
app_gap_cb_t *p_dev = &m_dev_info;
|
||||
memset(p_dev, 0, sizeof(app_gap_cb_t));
|
||||
|
||||
p_dev->state = APP_GAP_STATE_IDLE;
|
||||
}
|
||||
|
||||
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
|
||||
{
|
||||
app_gap_cb_t *p_dev = &m_dev_info;
|
||||
char bda_str[18];
|
||||
char uuid_str[37];
|
||||
|
||||
switch (event) {
|
||||
case ESP_BT_GAP_DISC_RES_EVT: {
|
||||
update_device_info(param);
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: {
|
||||
if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
|
||||
ESP_LOGI(GAP_TAG, "Device discovery stopped.");
|
||||
if ( (p_dev->state == APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE ||
|
||||
p_dev->state == APP_GAP_STATE_DEVICE_DISCOVERING)
|
||||
&& p_dev->dev_found) {
|
||||
p_dev->state = APP_GAP_STATE_SERVICE_DISCOVERING;
|
||||
ESP_LOGI(GAP_TAG, "Discover services ...");
|
||||
esp_bt_gap_get_remote_services(p_dev->bda);
|
||||
}
|
||||
} else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) {
|
||||
ESP_LOGI(GAP_TAG, "Discovery started.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_RMT_SRVCS_EVT: {
|
||||
if (memcmp(param->rmt_srvcs.bda, p_dev->bda, ESP_BD_ADDR_LEN) == 0 &&
|
||||
p_dev->state == APP_GAP_STATE_SERVICE_DISCOVERING) {
|
||||
p_dev->state = APP_GAP_STATE_SERVICE_DISCOVER_COMPLETE;
|
||||
if (param->rmt_srvcs.stat == ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGI(GAP_TAG, "Services for device %s found", bda2str(p_dev->bda, bda_str, 18));
|
||||
for (int i = 0; i < param->rmt_srvcs.num_uuids; i++) {
|
||||
esp_bt_uuid_t *u = param->rmt_srvcs.uuid_list + i;
|
||||
ESP_LOGI(GAP_TAG, "--%s", uuid2str(u, uuid_str, 37));
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(GAP_TAG, "Services for device %s not found", bda2str(p_dev->bda, bda_str, 18));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_RMT_SRVC_REC_EVT:
|
||||
default: {
|
||||
ESP_LOGI(GAP_TAG, "event: %d", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void bt_app_gap_start_up(void)
|
||||
{
|
||||
/* register GAP callback function */
|
||||
esp_bt_gap_register_callback(bt_app_gap_cb);
|
||||
|
||||
char *dev_name = "ESP_GAP_INQRUIY";
|
||||
esp_bt_gap_set_device_name(dev_name);
|
||||
|
||||
/* set discoverable and connectable mode, wait to be connected */
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
|
||||
|
||||
/* initialize device information and status */
|
||||
bt_app_gap_init();
|
||||
|
||||
/* start to discover nearby Bluetooth devices */
|
||||
app_gap_cb_t *p_dev = &m_dev_info;
|
||||
p_dev->state = APP_GAP_STATE_DEVICE_DISCOVERING;
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/* Initialize NVS — it is used to store PHY calibration data and save key-value pairs in flash memory*/
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
/* initialize TRANSPORT first */
|
||||
hosted_hci_bluedroid_open();
|
||||
|
||||
/* get HCI driver operations */
|
||||
esp_bluedroid_hci_driver_operations_t operations = {
|
||||
.send = hosted_hci_bluedroid_send,
|
||||
.check_send_available = hosted_hci_bluedroid_check_send_available,
|
||||
.register_host_callback = hosted_hci_bluedroid_register_host_callback,
|
||||
};
|
||||
esp_bluedroid_attach_hci_driver(&operations);
|
||||
|
||||
if ((ret = esp_bluedroid_init()) != ESP_OK) {
|
||||
ESP_LOGE(GAP_TAG, "%s initialize bluedroid failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ret = esp_bluedroid_enable()) != ESP_OK) {
|
||||
ESP_LOGE(GAP_TAG, "%s enable bluedroid failed: %s", __func__, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
bt_app_gap_start_up();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# BT config
|
||||
# - ESP32 co-processor only supports BLE 4.2
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
|
||||
#
|
||||
# Wi-Fi Remote
|
||||
# - set ESP32 as default co-processor
|
||||
#
|
||||
CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y
|
||||
CONFIG_SLAVE_IDF_TARGET_ESP32=y
|
||||
|
||||
#
|
||||
# Enable ESP Hosted BT
|
||||
# Used as VHCI transport between BT Host and Controller
|
||||
#
|
||||
CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID=y
|
||||
CONFIG_ESP_HOSTED_BLUEDROID_HCI_VHCI=
|
||||
@@ -0,0 +1,8 @@
|
||||
# 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.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(bleprph_host_only)
|
||||
@@ -0,0 +1,235 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-S3 | ESP32-P4 | ESP32-H2 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# BLE Peripheral Example
|
||||
|
||||
This example uses the UART transport written by application itself. Refer the file [main/uart_driver.c](main/uart_driver.c).
|
||||
|
||||
To write the custom transport in the application, the controller should be disabled and the default uart-transport should be disabled (when the controller is disabled, by default the uart-transport is selected). In order to compile the custom transport in the application, the default uart-transport should be disabled. Refer to the sdkconfig.defaults.
|
||||
|
||||
As an example, the [BLE Peripheral Example Walkthrough](https://github.com/espressif/esp-hosted-mcu/blob/main/examples/host_nimble_bleprph_host_only_uart_hci/tutorial/bleprph_host_only_walkthrough.md) shows an ESP32-P4 connected to a ESP32 over UART. See the Walkthrough for information on hardware setup.
|
||||
|
||||
For more information about the application, refer to the bleprph [README file](https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/nimble/bleprph/README.md).
|
||||
To test this demo, any BLE scanner app can be used.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Project Configuration for Host
|
||||
Before project configuration and build, be sure to set the correct chip target on both the host and co-processor using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Project Configuration for Host
|
||||
|
||||
Open the project configuration menu:
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
In the `Component config-> Bluetooth` menu:
|
||||
|
||||
* Select `controller` to Disabled.
|
||||
* Disable `Nimble Options-> Host-controller Transport -> Enable Uart Transport`.
|
||||
|
||||
>[!Important]
|
||||
> Co-processor selection is done by wifi-remote. Ensure the correct co-processor chip is selected in `Component config` -> `Wi-Fi Remote` -> `choose slave target`. The target selected will affect the ESP-Hosted transport options and default GPIOs used.
|
||||
|
||||
### Setup and Configuration for Co-processor
|
||||
|
||||
On the co-processor, UART HCI setup is done through the Bluetooth Component kconfig settings. In menuconfig, select `Component config` -> `Bluetooth` -> `Controller Options` -> `HCI mode` or `HCI Config` and set it to `UART(H4)`.
|
||||
|
||||
Depending on the selected co-processor, you can configure various UART parameters (Tx, Rx pins, hardware flow control, RTS, CTS pins, baudrate) through the Bluetooth Component. Other UART parameters not handled by the Bluetooth Component are configured by ESP-Hosted through `Example Configuration` -> `HCI UART Settings`.
|
||||
|
||||
> [!NOTE]
|
||||
> Make sure the UART GPIO pins selected do not conflict with the GPIO
|
||||
> pins used for the selected ESP-Hosted transport.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project on both the host and co-processor.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Console output when `host_nimble_bleprph_host_only_uart_hci` is running on ESP32-P4 and using ESP32 as the BT controller:
|
||||
|
||||
```
|
||||
I (25) boot: ESP-IDF v5.5-dev-1868-g39f34a65a9-dirty 2nd stage bootloader
|
||||
I (26) boot: compile time Feb 13 2025 17:15:22
|
||||
I (26) boot: Multicore bootloader
|
||||
I (29) boot: chip revision: v1.0
|
||||
I (31) boot: efuse block revision: v0.1
|
||||
I (34) boot.esp32p4: SPI Speed : 80MHz
|
||||
I (38) boot.esp32p4: SPI Mode : DIO
|
||||
I (42) boot.esp32p4: SPI Flash Size : 2MB
|
||||
I (46) boot: Enabling RNG early entropy source...
|
||||
I (50) boot: Partition Table:
|
||||
I (53) boot: ## Label Usage Type ST Offset Length
|
||||
I (59) boot: 0 nvs WiFi data 01 02 00009000 00006000
|
||||
I (66) boot: 1 phy_init RF data 01 01 0000f000 00001000
|
||||
I (72) boot: 2 factory factory app 00 00 00010000 00100000
|
||||
I (80) boot: End of partition table
|
||||
I (82) esp_image: segment 0: paddr=00010020 vaddr=40070020 size=252a0h (152224) map
|
||||
I (116) esp_image: segment 1: paddr=000352c8 vaddr=30100000 size=00044h ( 68) load
|
||||
I (118) esp_image: segment 2: paddr=00035314 vaddr=4ff00000 size=0ad04h ( 44292) load
|
||||
I (130) esp_image: segment 3: paddr=00040020 vaddr=40000020 size=68930h (428336) map
|
||||
I (202) esp_image: segment 4: paddr=000a8958 vaddr=4ff0ad04 size=069e0h ( 27104) load
|
||||
I (209) esp_image: segment 5: paddr=000af340 vaddr=4ff11700 size=026d8h ( 9944) load
|
||||
I (217) boot: Loaded app from partition at offset 0x10000
|
||||
I (217) boot: Disabling RNG early entropy source...
|
||||
I (228) cpu_start: Multicore app
|
||||
I (238) cpu_start: Pro cpu start user code
|
||||
I (239) cpu_start: cpu freq: 360000000 Hz
|
||||
I (239) app_init: Application information:
|
||||
I (239) app_init: Project name: bleprph_host_only
|
||||
I (243) app_init: App version: 6eaa9b1
|
||||
I (247) app_init: Compile time: Feb 13 2025 17:15:16
|
||||
I (252) app_init: ELF file SHA256: 8861453bb...
|
||||
I (257) app_init: ESP-IDF: v5.5-dev-1868-g39f34a65a9-dirty
|
||||
I (263) efuse_init: Min chip rev: v0.1
|
||||
I (266) efuse_init: Max chip rev: v1.99
|
||||
I (270) efuse_init: Chip rev: v1.0
|
||||
I (274) heap_init: Initializing. RAM available for dynamic allocation:
|
||||
I (281) heap_init: At 4FF17130 len 00023E90 (143 KiB): RAM
|
||||
I (286) heap_init: At 4FF3AFC0 len 00004BF0 (18 KiB): RAM
|
||||
I (291) heap_init: At 4FF40000 len 00060000 (384 KiB): RAM
|
||||
I (296) heap_init: At 50108080 len 00007F80 (31 KiB): RTCRAM
|
||||
I (301) heap_init: At 30100044 len 00001FBC (7 KiB): TCM
|
||||
I (307) spi_flash: detected chip: generic
|
||||
I (310) spi_flash: flash io: dio
|
||||
W (313) spi_flash: Detected size(16384k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
|
||||
I (326) host_init: ESP Hosted : Host chip_ip[18]
|
||||
I (357) H_API: ESP-Hosted starting. Hosted_Tasks: prio:23, stack: 5120 RPC_task_stack: 5120
|
||||
sdio_mempool_create free:177280 min-free:177280 lfb-def:131072 lfb-8bit:131072
|
||||
|
||||
I (361) gpio: GPIO[18]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (369) gpio: GPIO[19]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (378) gpio: GPIO[14]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (386) gpio: GPIO[15]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (394) gpio: GPIO[16]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (403) gpio: GPIO[17]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (411) H_API: ** add_esp_wifi_remote_channels **
|
||||
I (415) transport: Add ESP-Hosted channel IF[1]: S[0] Tx[0x4000f988] Rx[0x4001ddee]
|
||||
--- 0x4000f988: transport_drv_sta_tx at /home/kysoh/projects/gitlab_esp_hosted_mcu/examples/host_nimble_bleprph_host_only_uart_hci/components/esp_hosted/host/drivers/transport/transport_drv.c:208
|
||||
0x4001ddee: esp_wifi_remote_channel_rx at /home/kysoh/projects/gitlab_esp_hosted_mcu/examples/host_nimble_bleprph_host_only_uart_hci/managed_components/espressif__esp_wifi_remote/esp_wifi_remote_net.c:19
|
||||
|
||||
I (423) transport: Add ESP-Hosted channel IF[2]: S[0] Tx[0x4000f8d0] Rx[0x4001ddee]
|
||||
--- 0x4000f8d0: transport_drv_ap_tx at /home/kysoh/projects/gitlab_esp_hosted_mcu/examples/host_nimble_bleprph_host_only_uart_hci/components/esp_hosted/host/drivers/transport/transport_drv.c:238
|
||||
0x4001ddee: esp_wifi_remote_channel_rx at /home/kysoh/projects/gitlab_esp_hosted_mcu/examples/host_nimble_bleprph_host_only_uart_hci/managed_components/espressif__esp_wifi_remote/esp_wifi_remote_net.c:19
|
||||
|
||||
I (431) main_task: Started on CPU0
|
||||
I (441) main_task: Calling app_main()
|
||||
I (451) NimBLE_BLE_PRPH: BLE Host Task Started
|
||||
I (451) uart: queue free spaces: 8
|
||||
I (471) NimBLE: GAP procedure initiated: stop advertising.
|
||||
|
||||
<EFBFBD>I (501) main_task: Returned from app_main()
|
||||
I (501) NimBLE: ogf=0x08, ocf=0x004e, hci_err=0x201 : BLE_ERR_UNKNOWN_HCI_CMD (Unknown HCI Command)
|
||||
|
||||
I (501) NimBLE: Device Address:
|
||||
I (511) NimBLE: 10:97:bd:d5:8a:62
|
||||
I (511) NimBLE:
|
||||
|
||||
I (521) NimBLE: GAP procedure initiated: advertise;
|
||||
I (521) NimBLE: disc_mode=2
|
||||
I (521) NimBLE: adv_channel_map=0 own_addr_type=0 adv_filter_policy=0 adv_itvl_min=0 adv_itvl_max=0
|
||||
I (531) NimBLE:
|
||||
```
|
||||
|
||||
Console output on ESP32 as the BT controller:
|
||||
|
||||
```
|
||||
I (29) boot: ESP-IDF v5.5-dev-1868-g39f34a65a9-dirty 2nd stage bootloader
|
||||
I (29) boot: compile time Feb 13 2025 17:15:30
|
||||
I (29) boot: Multicore bootloader
|
||||
I (33) boot: chip revision: v3.0
|
||||
I (36) boot.esp32: SPI Speed : 40MHz
|
||||
I (39) boot.esp32: SPI Mode : DIO
|
||||
I (43) boot.esp32: SPI Flash Size : 4MB
|
||||
I (46) boot: Enabling RNG early entropy source...
|
||||
I (51) boot: Partition Table:
|
||||
I (53) boot: ## Label Usage Type ST Offset Length
|
||||
I (60) boot: 0 nvs WiFi data 01 02 00009000 00004000
|
||||
I (66) boot: 1 otadata OTA data 01 00 0000d000 00002000
|
||||
I (73) boot: 2 phy_init RF data 01 01 0000f000 00001000
|
||||
I (79) boot: 3 ota_0 OTA app 00 10 00010000 001c0000
|
||||
I (86) boot: 4 ota_1 OTA app 00 11 001d0000 001c0000
|
||||
I (92) boot: End of partition table
|
||||
I (96) boot: No factory image, trying OTA 0
|
||||
I (100) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=285e0h (165344) map
|
||||
I (164) esp_image: segment 1: paddr=00038608 vaddr=3ff80000 size=0001ch ( 28) load
|
||||
I (164) esp_image: segment 2: paddr=0003862c vaddr=3ffbdb60 size=05da4h ( 23972) load
|
||||
I (176) esp_image: segment 3: paddr=0003e3d8 vaddr=40080000 size=01c40h ( 7232) load
|
||||
I (180) esp_image: segment 4: paddr=00040020 vaddr=400d0020 size=9c69ch (640668) map
|
||||
I (401) esp_image: segment 5: paddr=000dc6c4 vaddr=40081c40 size=1dc1ch (121884) load
|
||||
I (465) boot: Loaded app from partition at offset 0x10000
|
||||
I (503) boot: Set actual ota_seq=1 in otadata[0]
|
||||
I (503) boot: Disabling RNG early entropy source...
|
||||
I (513) cpu_start: Multicore app
|
||||
I (521) cpu_start: Pro cpu start user code
|
||||
I (521) cpu_start: cpu freq: 240000000 Hz
|
||||
I (521) app_init: Application information:
|
||||
I (521) app_init: Project name: network_adapter
|
||||
I (526) app_init: App version: 6eaa9b1
|
||||
I (530) app_init: Compile time: Feb 13 2025 17:15:24
|
||||
I (535) app_init: ELF file SHA256: b3b6ed47b...
|
||||
I (539) app_init: ESP-IDF: v5.5-dev-1868-g39f34a65a9-dirty
|
||||
I (545) efuse_init: Min chip rev: v0.0
|
||||
I (549) efuse_init: Max chip rev: v3.99
|
||||
I (553) efuse_init: Chip rev: v3.0
|
||||
I (557) heap_init: Initializing. RAM available for dynamic allocation:
|
||||
I (563) heap_init: At 3FFAFF10 len 000000F0 (0 KiB): DRAM
|
||||
I (568) heap_init: At 3FFB6388 len 00001C78 (7 KiB): DRAM
|
||||
I (573) heap_init: At 3FFB9A20 len 00004108 (16 KiB): DRAM
|
||||
I (578) heap_init: At 3FFC8F18 len 000170E8 (92 KiB): DRAM
|
||||
I (584) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
|
||||
I (589) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
|
||||
I (594) heap_init: At 4009F85C len 000007A4 (1 KiB): IRAM
|
||||
I (601) spi_flash: detected chip: generic
|
||||
I (603) spi_flash: flash io: dio
|
||||
W (606) spi_flash: Detected size(8192k) larger than the size in the binary image header(4096k). Using the size in the binary image header.
|
||||
I (620) coexist: coex firmware version: e727207
|
||||
I (624) main_task: Started on CPU0
|
||||
I (626) main_task: Calling app_main()
|
||||
I (628) fg_mcu_slave: *********************************************************************
|
||||
I (634) fg_mcu_slave: ESP-Hosted-MCU Slave FW version :: 1.1.3
|
||||
|
||||
I (642) fg_mcu_slave: Transport used :: SPI + UART
|
||||
I (648) fg_mcu_slave: *********************************************************************
|
||||
I (654) fg_mcu_slave: Supported features are:
|
||||
I (656) fg_mcu_slave: - WLAN over SPI
|
||||
I (660) h_bt: - BT/BLE
|
||||
I (662) h_bt: - HCI Over UART
|
||||
I (664) h_bt: - BT/BLE dual mode
|
||||
I (666) fg_mcu_slave: capabilities: 0xba
|
||||
I (670) fg_mcu_slave: Supported extended features are:
|
||||
I (674) h_bt: - BT/BLE (extended)
|
||||
I (676) fg_mcu_slave: extended capabilities: 0x0
|
||||
I (686) h_bt: ESP Bluetooth MAC addr: 10:97:bd:d5:8a:62
|
||||
I (686) bt_uart: UART1 Pins: TX 5, RX 18, RTS 19, CTS 23 Baudrate:921600
|
||||
I (688) BTDM_INIT: BT controller compile version [194dd63]
|
||||
I (694) BTDM_INIT: Bluetooth MAC: 10:97:bd:d5:8a:62
|
||||
I (698) phy_init: phy_version 4840,02e0d70,Sep 2 2024,19:39:07
|
||||
I (1006) SPI_DRIVER: Using SPI interface
|
||||
I (1008) gpio: GPIO[26]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (1008) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (1014) SPI_DRIVER: SPI Ctrl:1 mode: 2, Freq:ConfigAtHost
|
||||
GPIOs: MOSI: 13, MISO: 12, CS: 15, CLK: 14 HS: 26 DR: 4
|
||||
|
||||
I (1022) SPI_DRIVER: Hosted SPI queue size: Tx:10 Rx:10
|
||||
I (1026) gpio: GPIO[15]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1032) gpio: GPIO[15]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1042) SPI_DRIVER: Slave chip Id[12]
|
||||
I (1042) fg_mcu_slave: Initial set up done
|
||||
I (1046) slave_ctrl: event ESPInit
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-hosted-mcu/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,6 @@
|
||||
set(srcs "main.c"
|
||||
"gatt_svr.c"
|
||||
"uart_driver.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,144 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
menu "Uart Configuration"
|
||||
|
||||
config EXAMPLE_HCI_UART_BAUDRATE
|
||||
int "UART Baudrate for HCI"
|
||||
range 115200 921600
|
||||
default 921600
|
||||
help
|
||||
UART Baudrate for HCI. Please use standard baudrate.
|
||||
|
||||
config EXAMPLE_HCI_UART_TX_PIN
|
||||
int "UART Tx Pin"
|
||||
default 24 if IDF_TARGET_ESP32P4
|
||||
default 4
|
||||
help
|
||||
UART HCI Tx pin
|
||||
|
||||
config EXAMPLE_HCI_UART_RX_PIN
|
||||
int "UART Rx Pin"
|
||||
default 33 if IDF_TARGET_ESP32P4
|
||||
default 5
|
||||
help
|
||||
UART HCI Rx pin
|
||||
|
||||
choice EXAMPLE_USE_HCI_UART_FLOW_CTRL
|
||||
prompt "Uart Flow Control"
|
||||
default EXAMPLE_UART_HW_FLOWCTRL_DISABLE
|
||||
help
|
||||
Uart Flow Control
|
||||
|
||||
config EXAMPLE_UART_HW_FLOWCTRL_DISABLE
|
||||
bool "Disable"
|
||||
config EXAMPLE_UART_HW_FLOWCTRL_CTS_RTS
|
||||
bool "Enable hardware flow control"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_HCI_UART_FLOW_CTRL
|
||||
int
|
||||
default 0 if EXAMPLE_UART_HW_FLOWCTRL_DISABLE
|
||||
default 3 if EXAMPLE_UART_HW_FLOWCTRL_CTS_RTS
|
||||
|
||||
config EXAMPLE_HCI_UART_RTS_PIN
|
||||
int "UART Rts Pin"
|
||||
default 20 if IDF_TARGET_ESP32P4
|
||||
default 19
|
||||
help
|
||||
UART HCI RTS pin
|
||||
|
||||
config EXAMPLE_HCI_UART_CTS_PIN
|
||||
int "UART Cts Pin"
|
||||
default 21 if IDF_TARGET_ESP32P4
|
||||
default 23
|
||||
help
|
||||
UART HCI CTS pin
|
||||
|
||||
config EXAMPLE_HCI_UART_INVERT_RTS
|
||||
depends on EXAMPLE_UART_HW_FLOWCTRL_CTS_RTS
|
||||
bool "Invert RTS Signal"
|
||||
default y if SLAVE_IDF_TARGET_ESP32 || SLAVE_IDF_TARGET_ESP32S3 || SLAVE_IDF_TARGET_ESP32C3
|
||||
default n
|
||||
help
|
||||
RTS signal may need to be inverted to work with some ESP chips (ESP32, ESP32-C3, ESP32-S3)
|
||||
|
||||
endmenu
|
||||
|
||||
choice EXAMPLE_USE_IO_TYPE
|
||||
prompt "I/O Capability"
|
||||
default BLE_SM_IO_CAP_NO_IO
|
||||
help
|
||||
I/O capability of device.
|
||||
|
||||
config BLE_SM_IO_CAP_DISP_ONLY
|
||||
bool "DISPLAY ONLY"
|
||||
config BLE_SM_IO_CAP_DISP_YES_NO
|
||||
bool "DISPLAY YESNO"
|
||||
config BLE_SM_IO_CAP_KEYBOARD_ONLY
|
||||
bool "KEYBOARD ONLY"
|
||||
config BLE_SM_IO_CAP_NO_IO
|
||||
bool "Just works"
|
||||
config BLE_SM_IO_CAP_KEYBOARD_DISP
|
||||
bool "Both KEYBOARD & DISPLAY"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_IO_TYPE
|
||||
int
|
||||
default 0 if BLE_SM_IO_CAP_DISP_ONLY
|
||||
default 1 if BLE_SM_IO_CAP_DISP_YES_NO
|
||||
default 2 if BLE_SM_IO_CAP_KEYBOARD_ONLY
|
||||
default 3 if BLE_SM_IO_CAP_NO_IO
|
||||
default 4 if BLE_SM_IO_CAP_KEYBOARD_DISP
|
||||
|
||||
config EXAMPLE_BONDING
|
||||
bool
|
||||
default n
|
||||
prompt "Use Bonding"
|
||||
help
|
||||
Use this option to enable/disable bonding.
|
||||
|
||||
config EXAMPLE_MITM
|
||||
bool
|
||||
default n
|
||||
prompt "MITM security"
|
||||
help
|
||||
Use this option to enable/disable MITM security.
|
||||
|
||||
config EXAMPLE_USE_SC
|
||||
bool
|
||||
depends on BT_NIMBLE_SM_SC
|
||||
default n
|
||||
prompt "Use Secure Connection feature"
|
||||
help
|
||||
Use this option to enable/disable Security Manager Secure Connection 4.2 feature.
|
||||
|
||||
config EXAMPLE_EXTENDED_ADV
|
||||
bool
|
||||
depends on SOC_BLE_50_SUPPORTED && BT_NIMBLE_50_FEATURE_SUPPORT
|
||||
default y if SOC_ESP_NIMBLE_CONTROLLER
|
||||
select BT_NIMBLE_EXT_ADV
|
||||
prompt "Enable Extended Adv"
|
||||
help
|
||||
Use this option to enable extended advertising in the example.
|
||||
If this option is disabled, ensure config BT_NIMBLE_EXT_ADV is
|
||||
also disabled from Nimble stack menuconfig
|
||||
|
||||
config EXAMPLE_RANDOM_ADDR
|
||||
bool
|
||||
prompt "Advertise RANDOM Address"
|
||||
help
|
||||
Use this option to advertise a random address instead of public address
|
||||
|
||||
config EXAMPLE_ENCRYPTION
|
||||
bool
|
||||
prompt "Enable Link Encryption"
|
||||
help
|
||||
This adds Encrypted Read and Write permissions in the custom GATT server.
|
||||
|
||||
config EXAMPLE_RESOLVE_PEER_ADDR
|
||||
bool
|
||||
prompt "Enable resolving peer address"
|
||||
help
|
||||
Use this option to enable resolving peer's address.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef H_BLEPRPH_
|
||||
#define H_BLEPRPH_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nimble/ble.h"
|
||||
#include "modlog/modlog.h"
|
||||
#include "esp_peripheral.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ble_hs_cfg;
|
||||
struct ble_gatt_register_ctxt;
|
||||
|
||||
/** GATT server. */
|
||||
#define GATT_SVR_SVC_ALERT_UUID 0x1811
|
||||
#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
|
||||
#define GATT_SVR_CHR_NEW_ALERT 0x2A46
|
||||
#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
|
||||
#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45
|
||||
#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44
|
||||
|
||||
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
|
||||
int gatt_svr_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_uuid.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
#include "bleprph.h"
|
||||
#include "services/ans/ble_svc_ans.h"
|
||||
|
||||
/*** Maximum number of characteristics with the notify flag ***/
|
||||
#define MAX_NOTIFY 5
|
||||
|
||||
static const ble_uuid128_t gatt_svr_svc_uuid =
|
||||
BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
|
||||
0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
|
||||
|
||||
/* A characteristic that can be subscribed to */
|
||||
static uint8_t gatt_svr_chr_val;
|
||||
static uint16_t gatt_svr_chr_val_handle;
|
||||
static const ble_uuid128_t gatt_svr_chr_uuid =
|
||||
BLE_UUID128_INIT(0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11,
|
||||
0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33);
|
||||
|
||||
/* A custom descriptor */
|
||||
static uint8_t gatt_svr_dsc_val;
|
||||
static const ble_uuid128_t gatt_svr_dsc_uuid =
|
||||
BLE_UUID128_INIT(0x01, 0x01, 0x01, 0x01, 0x12, 0x12, 0x12, 0x12,
|
||||
0x23, 0x23, 0x23, 0x23, 0x34, 0x34, 0x34, 0x34);
|
||||
|
||||
static int
|
||||
gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt,
|
||||
void *arg);
|
||||
|
||||
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
|
||||
{
|
||||
/*** Service ***/
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = &gatt_svr_svc_uuid.u,
|
||||
.characteristics = (struct ble_gatt_chr_def[])
|
||||
{ {
|
||||
/*** This characteristic can be subscribed to by writing 0x00 and 0x01 to the CCCD ***/
|
||||
.uuid = &gatt_svr_chr_uuid.u,
|
||||
.access_cb = gatt_svc_access,
|
||||
#if CONFIG_EXAMPLE_ENCRYPTION
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE |
|
||||
BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC |
|
||||
BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
|
||||
#else
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
|
||||
#endif
|
||||
.val_handle = &gatt_svr_chr_val_handle,
|
||||
.descriptors = (struct ble_gatt_dsc_def[])
|
||||
{ {
|
||||
.uuid = &gatt_svr_dsc_uuid.u,
|
||||
#if CONFIG_EXAMPLE_ENCRYPTION
|
||||
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC,
|
||||
#else
|
||||
.att_flags = BLE_ATT_F_READ,
|
||||
#endif
|
||||
.access_cb = gatt_svc_access,
|
||||
}, {
|
||||
0, /* No more descriptors in this characteristic */
|
||||
}
|
||||
},
|
||||
}, {
|
||||
0, /* No more characteristics in this service. */
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
0, /* No more services. */
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
gatt_svr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
|
||||
void *dst, uint16_t *len)
|
||||
{
|
||||
uint16_t om_len;
|
||||
int rc;
|
||||
|
||||
om_len = OS_MBUF_PKTLEN(om);
|
||||
if (om_len < min_len || om_len > max_len) {
|
||||
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
||||
}
|
||||
|
||||
rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
|
||||
if (rc != 0) {
|
||||
return BLE_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback whenever a characteristic/descriptor is read or written to.
|
||||
* Here reads and writes need to be handled.
|
||||
* ctxt->op tells weather the operation is read or write and
|
||||
* weather it is on a characteristic or descriptor,
|
||||
* ctxt->dsc->uuid tells which characteristic/descriptor is accessed.
|
||||
* attr_handle give the value handle of the attribute being accessed.
|
||||
* Accordingly do:
|
||||
* Append the value to ctxt->om if the operation is READ
|
||||
* Write ctxt->om to the value if the operation is WRITE
|
||||
**/
|
||||
static int
|
||||
gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
const ble_uuid_t *uuid;
|
||||
int rc;
|
||||
|
||||
switch (ctxt->op) {
|
||||
case BLE_GATT_ACCESS_OP_READ_CHR:
|
||||
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||
MODLOG_DFLT(INFO, "Characteristic read; conn_handle=%d attr_handle=%d\n",
|
||||
conn_handle, attr_handle);
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Characteristic read by NimBLE stack; attr_handle=%d\n",
|
||||
attr_handle);
|
||||
}
|
||||
uuid = ctxt->chr->uuid;
|
||||
if (attr_handle == gatt_svr_chr_val_handle) {
|
||||
rc = os_mbuf_append(ctxt->om,
|
||||
&gatt_svr_chr_val,
|
||||
sizeof(gatt_svr_chr_val));
|
||||
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
}
|
||||
goto unknown;
|
||||
|
||||
case BLE_GATT_ACCESS_OP_WRITE_CHR:
|
||||
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||
MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d",
|
||||
conn_handle, attr_handle);
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Characteristic write by NimBLE stack; attr_handle=%d",
|
||||
attr_handle);
|
||||
}
|
||||
uuid = ctxt->chr->uuid;
|
||||
if (attr_handle == gatt_svr_chr_val_handle) {
|
||||
rc = gatt_svr_write(ctxt->om,
|
||||
sizeof(gatt_svr_chr_val),
|
||||
sizeof(gatt_svr_chr_val),
|
||||
&gatt_svr_chr_val, NULL);
|
||||
ble_gatts_chr_updated(attr_handle);
|
||||
MODLOG_DFLT(INFO, "Notification/Indication scheduled for "
|
||||
"all subscribed peers.\n");
|
||||
return rc;
|
||||
}
|
||||
goto unknown;
|
||||
|
||||
case BLE_GATT_ACCESS_OP_READ_DSC:
|
||||
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||
MODLOG_DFLT(INFO, "Descriptor read; conn_handle=%d attr_handle=%d\n",
|
||||
conn_handle, attr_handle);
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Descriptor read by NimBLE stack; attr_handle=%d\n",
|
||||
attr_handle);
|
||||
}
|
||||
uuid = ctxt->dsc->uuid;
|
||||
if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) {
|
||||
rc = os_mbuf_append(ctxt->om,
|
||||
&gatt_svr_dsc_val,
|
||||
sizeof(gatt_svr_chr_val));
|
||||
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
}
|
||||
goto unknown;
|
||||
|
||||
case BLE_GATT_ACCESS_OP_WRITE_DSC:
|
||||
goto unknown;
|
||||
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
|
||||
unknown:
|
||||
/* Unknown characteristic/descriptor;
|
||||
* The NimBLE host should not have called this function;
|
||||
*/
|
||||
assert(0);
|
||||
return BLE_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
|
||||
void
|
||||
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
|
||||
{
|
||||
char buf[BLE_UUID_STR_LEN];
|
||||
|
||||
switch (ctxt->op) {
|
||||
case BLE_GATT_REGISTER_OP_SVC:
|
||||
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
|
||||
ctxt->svc.handle);
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_CHR:
|
||||
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
|
||||
"def_handle=%d val_handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
|
||||
ctxt->chr.def_handle,
|
||||
ctxt->chr.val_handle);
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_DSC:
|
||||
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
|
||||
ctxt->dsc.handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
gatt_svr_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ble_svc_gap_init();
|
||||
ble_svc_gatt_init();
|
||||
ble_svc_ans_init();
|
||||
|
||||
rc = ble_gatts_count_cfg(gatt_svr_svcs);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ble_gatts_add_svcs(gatt_svr_svcs);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Setting a value for the read-only descriptor */
|
||||
gatt_svr_dsc_val = 0x99;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
dependencies:
|
||||
espressif/esp_hosted:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: ~2
|
||||
espressif/esp_wifi_remote:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: '>=0.10'
|
||||
nimble_peripheral_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils
|
||||
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "console/console.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "bleprph.h"
|
||||
#include "uart_driver.h"
|
||||
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
static uint8_t ext_adv_pattern_1[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x03, 0x03, 0xab, 0xcd,
|
||||
0x03, 0x03, 0x18, 0x11,
|
||||
0x11, 0X09, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'e',
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char *tag = "NimBLE_BLE_PRPH";
|
||||
static int bleprph_gap_event(struct ble_gap_event *event, void *arg);
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
static uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM;
|
||||
#else
|
||||
static uint8_t own_addr_type;
|
||||
#endif
|
||||
|
||||
void ble_store_config_init(void);
|
||||
|
||||
/**
|
||||
* Logs information about a connection to the console.
|
||||
*/
|
||||
static void
|
||||
bleprph_print_conn_desc(struct ble_gap_conn_desc *desc)
|
||||
{
|
||||
MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
|
||||
desc->conn_handle, desc->our_ota_addr.type);
|
||||
print_addr(desc->our_ota_addr.val);
|
||||
MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
|
||||
desc->our_id_addr.type);
|
||||
print_addr(desc->our_id_addr.val);
|
||||
MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
|
||||
desc->peer_ota_addr.type);
|
||||
print_addr(desc->peer_ota_addr.val);
|
||||
MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
|
||||
desc->peer_id_addr.type);
|
||||
print_addr(desc->peer_id_addr.val);
|
||||
MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
|
||||
"encrypted=%d authenticated=%d bonded=%d\n",
|
||||
desc->conn_itvl, desc->conn_latency,
|
||||
desc->supervision_timeout,
|
||||
desc->sec_state.encrypted,
|
||||
desc->sec_state.authenticated,
|
||||
desc->sec_state.bonded);
|
||||
}
|
||||
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
/**
|
||||
* Enables advertising with the following parameters:
|
||||
* o General discoverable mode.
|
||||
* o Undirected connectable mode.
|
||||
*/
|
||||
static void
|
||||
ext_bleprph_advertise(void)
|
||||
{
|
||||
struct ble_gap_ext_adv_params params;
|
||||
struct os_mbuf *data;
|
||||
uint8_t instance = 0;
|
||||
int rc;
|
||||
|
||||
/* First check if any instance is already active */
|
||||
if(ble_gap_ext_adv_active(instance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* use defaults for non-set params */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
|
||||
/* enable connectable advertising */
|
||||
params.connectable = 1;
|
||||
|
||||
/* advertise using random addr */
|
||||
params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
|
||||
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M;
|
||||
//params.tx_power = 127;
|
||||
params.sid = 1;
|
||||
|
||||
params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
|
||||
params.itvl_max = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
|
||||
|
||||
/* configure instance 0 */
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL,
|
||||
bleprph_gap_event, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
/* in this case only scan response is allowed */
|
||||
|
||||
/* get mbuf for scan rsp data */
|
||||
data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0);
|
||||
assert(data);
|
||||
|
||||
/* fill mbuf with scan rsp data */
|
||||
rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1));
|
||||
assert(rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
assert (rc == 0);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
assert (rc == 0);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Enables advertising with the following parameters:
|
||||
* o General discoverable mode.
|
||||
* o Undirected connectable mode.
|
||||
*/
|
||||
static void
|
||||
bleprph_advertise(void)
|
||||
{
|
||||
struct ble_gap_adv_params adv_params;
|
||||
struct ble_hs_adv_fields fields;
|
||||
const char *name;
|
||||
int rc;
|
||||
|
||||
/**
|
||||
* Set the advertisement data included in our advertisements:
|
||||
* o Flags (indicates advertisement type and other general info).
|
||||
* o Advertising tx power.
|
||||
* o Device name.
|
||||
* o 16-bit service UUIDs (alert notifications).
|
||||
*/
|
||||
|
||||
memset(&fields, 0, sizeof fields);
|
||||
|
||||
/* Advertise two flags:
|
||||
* o Discoverability in forthcoming advertisement (general)
|
||||
* o BLE-only (BR/EDR unsupported).
|
||||
*/
|
||||
fields.flags = BLE_HS_ADV_F_DISC_GEN |
|
||||
BLE_HS_ADV_F_BREDR_UNSUP;
|
||||
|
||||
/* Indicate that the TX power level field should be included; have the
|
||||
* stack fill this value automatically. This is done by assigning the
|
||||
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
|
||||
*/
|
||||
fields.tx_pwr_lvl_is_present = 1;
|
||||
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||
|
||||
name = ble_svc_gap_device_name();
|
||||
fields.name = (uint8_t *)name;
|
||||
fields.name_len = strlen(name);
|
||||
fields.name_is_complete = 1;
|
||||
|
||||
fields.uuids16 = (ble_uuid16_t[]) {
|
||||
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
|
||||
};
|
||||
fields.num_uuids16 = 1;
|
||||
fields.uuids16_is_complete = 1;
|
||||
|
||||
rc = ble_gap_adv_set_fields(&fields);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Begin advertising. */
|
||||
memset(&adv_params, 0, sizeof adv_params);
|
||||
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
|
||||
&adv_params, bleprph_gap_event, NULL);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_POWER_CONTROL)
|
||||
static void bleprph_power_control(uint16_t conn_handle)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01 ); // Attempting on LE 1M phy
|
||||
assert (rc == 0);
|
||||
|
||||
rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x1, 0x1);
|
||||
assert (rc == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The nimble host executes this callback when a GAP event occurs. The
|
||||
* application associates a GAP event callback with each connection that forms.
|
||||
* bleprph uses the same callback for all connections.
|
||||
*
|
||||
* @param event The type of event being signalled.
|
||||
* @param ctxt Various information pertaining to the event.
|
||||
* @param arg Application-specified argument; unused by
|
||||
* bleprph.
|
||||
*
|
||||
* @return 0 if the application successfully handled the
|
||||
* event; nonzero on failure. The semantics
|
||||
* of the return code is specific to the
|
||||
* particular GAP event being signalled.
|
||||
*/
|
||||
static int
|
||||
bleprph_gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
int rc;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_LINK_ESTAB:
|
||||
/* A new connection was established or a connection attempt failed. */
|
||||
MODLOG_DFLT(INFO, "connection %s; status=%d ",
|
||||
event->link_estab.status == 0 ? "established" : "failed",
|
||||
event->link_estab.status);
|
||||
if (event->link_estab.status == 0) {
|
||||
rc = ble_gap_conn_find(event->link_estab.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
bleprph_print_conn_desc(&desc);
|
||||
}
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
if (event->link_estab.status != 0) {
|
||||
/* Connection failed; resume advertising. */
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
ext_bleprph_advertise();
|
||||
#else
|
||||
bleprph_advertise();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MYNEWT_VAL(BLE_POWER_CONTROL)
|
||||
bleprph_power_control(event->link_estab.conn_handle);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
|
||||
bleprph_print_conn_desc(&event->disconnect.conn);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* Connection terminated; resume advertising. */
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
ext_bleprph_advertise();
|
||||
#else
|
||||
bleprph_advertise();
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
/* The central has updated the connection parameters. */
|
||||
MODLOG_DFLT(INFO, "connection updated; status=%d ",
|
||||
event->conn_update.status);
|
||||
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
bleprph_print_conn_desc(&desc);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ADV_COMPLETE:
|
||||
MODLOG_DFLT(INFO, "advertise complete; reason=%d",
|
||||
event->adv_complete.reason);
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
ext_bleprph_advertise();
|
||||
#else
|
||||
bleprph_advertise();
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
/* Encryption has been enabled or disabled for this connection. */
|
||||
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
|
||||
event->enc_change.status);
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
bleprph_print_conn_desc(&desc);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_TX:
|
||||
MODLOG_DFLT(INFO, "notify_tx event; conn_handle=%d attr_handle=%d "
|
||||
"status=%d is_indication=%d",
|
||||
event->notify_tx.conn_handle,
|
||||
event->notify_tx.attr_handle,
|
||||
event->notify_tx.status,
|
||||
event->notify_tx.indication);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_SUBSCRIBE:
|
||||
MODLOG_DFLT(INFO, "subscribe event; conn_handle=%d attr_handle=%d "
|
||||
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
|
||||
event->subscribe.conn_handle,
|
||||
event->subscribe.attr_handle,
|
||||
event->subscribe.reason,
|
||||
event->subscribe.prev_notify,
|
||||
event->subscribe.cur_notify,
|
||||
event->subscribe.prev_indicate,
|
||||
event->subscribe.cur_indicate);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_MTU:
|
||||
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
|
||||
event->mtu.conn_handle,
|
||||
event->mtu.channel_id,
|
||||
event->mtu.value);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_REPEAT_PAIRING:
|
||||
/* We already have a bond with the peer, but it is attempting to
|
||||
* establish a new secure link. This app sacrifices security for
|
||||
* convenience: just throw away the old bond and accept the new link.
|
||||
*/
|
||||
|
||||
/* Delete the old bond. */
|
||||
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||
|
||||
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
|
||||
* continue with the pairing operation.
|
||||
*/
|
||||
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||
ESP_LOGI(tag, "PASSKEY_ACTION_EVENT started");
|
||||
struct ble_sm_io pkey = {0};
|
||||
int key = 0;
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = 123456; // This is the passkey to be entered on peer
|
||||
ESP_LOGI(tag, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey);
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
ESP_LOGI(tag, "Passkey on device's display: %" PRIu32 , event->passkey.params.numcmp);
|
||||
ESP_LOGI(tag, "Accept or reject the passkey through console in this format -> key Y or key N");
|
||||
pkey.action = event->passkey.params.action;
|
||||
if (scli_receive_key(&key)) {
|
||||
pkey.numcmp_accept = key;
|
||||
} else {
|
||||
pkey.numcmp_accept = 0;
|
||||
ESP_LOGE(tag, "Timeout! Rejecting the key");
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
||||
static uint8_t tem_oob[16] = {0};
|
||||
pkey.action = event->passkey.params.action;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
pkey.oob[i] = tem_oob[i];
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
ESP_LOGI(tag, "Enter the passkey through console in this format-> key 123456");
|
||||
pkey.action = event->passkey.params.action;
|
||||
if (scli_receive_key(&key)) {
|
||||
pkey.passkey = key;
|
||||
} else {
|
||||
pkey.passkey = 0;
|
||||
ESP_LOGE(tag, "Timeout! Passing 0 as the key");
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
}
|
||||
return 0;
|
||||
|
||||
#if MYNEWT_VAL(BLE_POWER_CONTROL)
|
||||
case BLE_GAP_EVENT_TRANSMIT_POWER:
|
||||
MODLOG_DFLT(INFO, "Transmit power event : status=%d conn_handle=%d reason=%d "
|
||||
"phy=%d power_level=%x power_level_flag=%d delta=%d",
|
||||
event->transmit_power.status,
|
||||
event->transmit_power.conn_handle,
|
||||
event->transmit_power.reason,
|
||||
event->transmit_power.phy,
|
||||
event->transmit_power.transmit_power_level,
|
||||
event->transmit_power.transmit_power_level_flag,
|
||||
event->transmit_power.delta);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PATHLOSS_THRESHOLD:
|
||||
MODLOG_DFLT(INFO, "Pathloss threshold event : conn_handle=%d current path loss=%d "
|
||||
"zone_entered =%d",
|
||||
event->pathloss_threshold.conn_handle,
|
||||
event->pathloss_threshold.current_path_loss,
|
||||
event->pathloss_threshold.zone_entered);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
bleprph_on_reset(int reason)
|
||||
{
|
||||
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
|
||||
}
|
||||
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
static void
|
||||
ble_app_set_addr(void)
|
||||
{
|
||||
ble_addr_t addr;
|
||||
int rc;
|
||||
|
||||
/* generate new non-resolvable private address */
|
||||
rc = ble_hs_id_gen_rnd(0, &addr);
|
||||
assert(rc == 0);
|
||||
|
||||
/* set generated address */
|
||||
rc = ble_hs_id_set_rnd(addr.val);
|
||||
|
||||
assert(rc == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
bleprph_on_sync(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
/* Generate a non-resolvable private address. */
|
||||
ble_app_set_addr();
|
||||
#endif
|
||||
|
||||
/* Make sure we have proper identity address set (public preferred) */
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
rc = ble_hs_util_ensure_addr(1);
|
||||
#else
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
#endif
|
||||
assert(rc == 0);
|
||||
|
||||
/* Figure out address to use while advertising (no privacy for now) */
|
||||
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Printing ADDR */
|
||||
uint8_t addr_val[6] = {0};
|
||||
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
|
||||
|
||||
MODLOG_DFLT(INFO, "Device Address: ");
|
||||
print_addr(addr_val);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
/* Begin advertising. */
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
ext_bleprph_advertise();
|
||||
#else
|
||||
bleprph_advertise();
|
||||
#endif
|
||||
}
|
||||
|
||||
void bleprph_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(tag, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* 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 || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
hci_uart_open();
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = bleprph_on_reset;
|
||||
ble_hs_cfg.sync_cb = bleprph_on_sync;
|
||||
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
|
||||
ble_hs_cfg.sm_io_cap = CONFIG_EXAMPLE_IO_TYPE;
|
||||
#ifdef CONFIG_EXAMPLE_BONDING
|
||||
ble_hs_cfg.sm_bonding = 1;
|
||||
/* Enable the appropriate bit masks to make sure the keys
|
||||
* that are needed are exchanged
|
||||
*/
|
||||
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
|
||||
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_MITM
|
||||
ble_hs_cfg.sm_mitm = 1;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_USE_SC
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
#else
|
||||
ble_hs_cfg.sm_sc = 0;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_RESOLVE_PEER_ADDR
|
||||
/* Stores the IRK */
|
||||
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ID;
|
||||
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID;
|
||||
#endif
|
||||
|
||||
rc = gatt_svr_init();
|
||||
assert(rc == 0);
|
||||
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble-bleprph");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
nimble_port_freertos_init(bleprph_host_task);
|
||||
|
||||
/* Initialize command line interface to accept input from user */
|
||||
rc = scli_init();
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGE(tag, "scli_init() failed");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_attr.h"
|
||||
#include "uart_driver.h"
|
||||
#include "nimble/hci_common.h"
|
||||
#include "host/ble_hs_mbuf.h"
|
||||
|
||||
#define TAG "UART_HCI"
|
||||
|
||||
#define UART_NO (1)
|
||||
#define UART_BUF_SZ (1024)
|
||||
|
||||
#define UART_TX_PIN (CONFIG_EXAMPLE_HCI_UART_TX_PIN)
|
||||
#define UART_RX_PIN (CONFIG_EXAMPLE_HCI_UART_RX_PIN)
|
||||
#define UART_RTS_PIN (CONFIG_EXAMPLE_HCI_UART_RTS_PIN)
|
||||
#define UART_CTS_PIN (CONFIG_EXAMPLE_HCI_UART_CTS_PIN)
|
||||
#define HCI_H4_ACL (0x02)
|
||||
#define HCI_H4_CMD (0x01)
|
||||
#define HCI_H4_EVT (0x04)
|
||||
#define BLE_HCI_EVENT_HDR_LEN (2)
|
||||
#define BLE_HCI_CMD_HDR_LEN (3)
|
||||
|
||||
enum {
|
||||
UART_RX_TYPE = 0,
|
||||
UART_RX_LEN,
|
||||
UART_RX_DATA,
|
||||
};
|
||||
|
||||
enum {
|
||||
DATA_TYPE_COMMAND = 1,
|
||||
DATA_TYPE_ACL = 2,
|
||||
DATA_TYPE_EVENT = 4
|
||||
};
|
||||
|
||||
TaskHandle_t s_rx_task_hdl;
|
||||
|
||||
static void IRAM_ATTR hci_uart_rx_task(void *arg)
|
||||
{
|
||||
uint8_t buf[1026];
|
||||
int len_now_read = -1;
|
||||
uint32_t len_to_read = 1;
|
||||
uint32_t len_total_read = 0;
|
||||
uint8_t rx_st = UART_RX_TYPE;
|
||||
|
||||
while (1) {
|
||||
len_now_read = uart_read_bytes(UART_NO, &buf[len_total_read], len_to_read, portMAX_DELAY);
|
||||
assert(len_now_read == len_to_read);
|
||||
len_total_read += len_now_read;
|
||||
|
||||
switch (rx_st) {
|
||||
case UART_RX_TYPE: {
|
||||
assert(buf[0] >= DATA_TYPE_ACL && buf[0] <= DATA_TYPE_EVENT);
|
||||
if (buf[0] == DATA_TYPE_ACL) {
|
||||
len_to_read = 4;
|
||||
} else if (buf[0] == DATA_TYPE_EVENT) {
|
||||
len_to_read = 2;
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
rx_st = UART_RX_LEN;
|
||||
}
|
||||
break;
|
||||
|
||||
case UART_RX_LEN: {
|
||||
if (buf[0] == DATA_TYPE_ACL) {
|
||||
len_to_read = buf[3] | (buf[4] << 8);
|
||||
} else if (buf[0] == DATA_TYPE_EVENT) {
|
||||
len_to_read = buf[2];
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
rx_st = UART_RX_DATA;
|
||||
}
|
||||
break;
|
||||
|
||||
case UART_RX_DATA: {
|
||||
uint8_t *data = buf;
|
||||
int rc;
|
||||
|
||||
if (data[0] == HCI_H4_EVT) {
|
||||
uint8_t *evbuf;
|
||||
int totlen;
|
||||
|
||||
totlen = BLE_HCI_EVENT_HDR_LEN + data[2];
|
||||
assert(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN);
|
||||
|
||||
if (totlen > MYNEWT_VAL(BLE_TRANSPORT_EVT_SIZE)) {
|
||||
ESP_LOGE(TAG, "Received HCI data length at host (%d)"
|
||||
"exceeds maximum configured HCI event buffer size (%d).",
|
||||
totlen, MYNEWT_VAL(BLE_TRANSPORT_EVT_SIZE));
|
||||
break;
|
||||
}
|
||||
|
||||
if (data[1] == BLE_HCI_EVCODE_HW_ERROR) {
|
||||
rx_st = UART_RX_TYPE;
|
||||
len_to_read = 1;
|
||||
len_total_read = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Allocate LE Advertising Report Event from lo pool only */
|
||||
if ((data[1] == BLE_HCI_EVCODE_LE_META) &&
|
||||
(data[3] == BLE_HCI_LE_SUBEV_ADV_RPT || data[3] == BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
|
||||
evbuf = ble_transport_alloc_evt(1);
|
||||
/* Skip advertising report if we're out of memory */
|
||||
if (!evbuf) {
|
||||
ESP_LOGE(TAG, "No buffers");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
evbuf = ble_transport_alloc_evt(0);
|
||||
assert(evbuf != NULL);
|
||||
}
|
||||
|
||||
memset(evbuf, 0, sizeof * evbuf);
|
||||
memcpy(evbuf, &data[1], totlen);
|
||||
|
||||
rc = ble_transport_to_hs_evt(evbuf);
|
||||
assert(rc == 0);
|
||||
} else if (data[0] == HCI_H4_ACL) {
|
||||
struct os_mbuf *m = NULL;
|
||||
m = ble_transport_alloc_acl_from_ll();
|
||||
if (!m) {
|
||||
ESP_LOGE(TAG, "No buffers");
|
||||
}
|
||||
|
||||
if ((rc = os_mbuf_append(m, &data[1], len_total_read - 1)) != 0) {
|
||||
ESP_LOGE(TAG, "%s failed to os_mbuf_append; rc = %d", __func__, rc);
|
||||
os_mbuf_free_chain(m);
|
||||
return;
|
||||
}
|
||||
|
||||
ble_transport_to_hs_acl(m);
|
||||
}
|
||||
|
||||
rx_st = UART_RX_TYPE;
|
||||
len_to_read = 1;
|
||||
len_total_read = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void hci_uart_send(uint8_t *buf, uint16_t len)
|
||||
{
|
||||
uint8_t *p = buf;
|
||||
int len_write = 0;
|
||||
|
||||
while (len) {
|
||||
len_write = uart_write_bytes(UART_NO, p, len);
|
||||
assert(len_write > 0);
|
||||
len -= len_write;
|
||||
p += len_write;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ble_transport_ll_init(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ble_transport_ll_deinit(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
ble_transport_to_ll_acl_impl(struct os_mbuf *om)
|
||||
{
|
||||
uint8_t buf[OS_MBUF_PKTLEN(om) + 1];
|
||||
int rc;
|
||||
buf[0] = HCI_H4_ACL;
|
||||
rc = ble_hs_mbuf_to_flat(om, buf + 1, OS_MBUF_PKTLEN(om), NULL);
|
||||
if(rc) {
|
||||
ESP_LOGE(TAG, "Error copying data %d", rc);
|
||||
os_mbuf_free_chain(om);
|
||||
return rc;
|
||||
}
|
||||
hci_uart_send(buf, OS_MBUF_PKTLEN(om) + 1);
|
||||
os_mbuf_free_chain(om);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_transport_to_ll_cmd_impl(void *buf)
|
||||
{
|
||||
int len = 3 + ((uint8_t *)buf)[2] + 1;
|
||||
uint8_t data[258];
|
||||
data[0] = HCI_H4_CMD;
|
||||
memcpy(data + 1, buf, len - 1);
|
||||
hci_uart_send(data, len);
|
||||
ble_transport_free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hci_uart_open(void)
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = CONFIG_EXAMPLE_HCI_UART_BAUDRATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = CONFIG_EXAMPLE_HCI_UART_FLOW_CTRL,
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
};
|
||||
|
||||
int intr_alloc_flags = 0;
|
||||
#if CONFIG_UART_ISR_IN_IRAM
|
||||
intr_alloc_flags = ESP_INTR_FLAG_IRAM;
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(uart_driver_install(UART_NO, UART_BUF_SZ * 2, UART_BUF_SZ * 2, 0, NULL, intr_alloc_flags));
|
||||
ESP_ERROR_CHECK(uart_param_config(UART_NO, &uart_config));
|
||||
|
||||
#if CONFIG_EXAMPLE_HCI_UART_INVERT_RTS
|
||||
/* to communicate with co-processor with UART hw
|
||||
* flowcontrol, logic of RTS signal must be inverted
|
||||
*/
|
||||
uint32_t invert_mask = 0;
|
||||
invert_mask |= UART_SIGNAL_RTS_INV;
|
||||
|
||||
ESP_ERROR_CHECK(uart_set_line_inverse(UART_NO, invert_mask));
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(uart_set_pin(UART_NO, UART_TX_PIN, UART_RX_PIN, UART_RTS_PIN, UART_CTS_PIN));
|
||||
|
||||
xTaskCreate(hci_uart_rx_task, "hci_uart_rx_task", 2048, NULL, 12, &s_rx_task_hdl);
|
||||
}
|
||||
|
||||
void hci_uart_close(void)
|
||||
{
|
||||
if (s_rx_task_hdl) {
|
||||
vTaskDelete(s_rx_task_hdl);
|
||||
}
|
||||
uart_driver_delete(UART_NO);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#ifndef __UART_DRIVER_H__
|
||||
#define __UART_DRIVER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief open HCI transport of uart
|
||||
*/
|
||||
void hci_uart_open(void);
|
||||
|
||||
/**
|
||||
* @brief close HCI transport of uart
|
||||
*/
|
||||
void hci_uart_close(void);
|
||||
|
||||
/**
|
||||
* @brief send data from host to HCI transport
|
||||
*
|
||||
* @param[in] data pointer to data buffer
|
||||
* @param[in] len length of data
|
||||
*/
|
||||
void hci_uart_send(uint8_t *data, uint16_t len);
|
||||
|
||||
#endif /* __UART_DRIVER_H__ */
|
||||
@@ -0,0 +1,11 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_TRANSPORT_UART=n
|
||||
@@ -0,0 +1,83 @@
|
||||
# BLE Peripheral Example Walkthrough
|
||||
|
||||
## Introduction
|
||||
|
||||
The tutorial is focused on how to implement the custom transport in the application and register it with nimble-host. This example has the exact same working as bleprph except the transport used is custom UART transport written in `"main/uart_driver.c"` file. The controller is disabled for this example, the nimble-host tries to communicate with the external connected controller using UART transport.
|
||||
|
||||
Note: This walkthrough only covers the transport used by the application, for detailed explanation see the [bleprph_walkthrough](https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/nimble/bleprph/tutorial/bleprph_walkthrough.md).
|
||||
|
||||
## Hardware Setup for Host
|
||||
|
||||
This example uses the ESP32-P4 chip to run the `host_nimble_bleprph_host_only_uart_hci` example. The example runs the nimble-host and sends and receives the commands and events using uart. The ESP32 co-procesor runs ESP-Hosted slave, with the BT controller configured to use the UART.
|
||||
|
||||
Below is the setup of ESP32-P4 and ESP32.
|
||||
|
||||
<img src="hardware_setup.jpg" alt="ESP32-P4-Function-EV-Board with ESP32, connected via UART" width="600" />
|
||||
|
||||
The ESP32-P4 gpio pins are configurable using `idf.py menuconfig` -> `Example Configuration` -> `Uart Configuration`. The tx pin of one chip connects to the rx pin of the other and vice versa. If you enable `Uart Flow Control`, connect the `CTS` pin on one chip to the `RTS` pin on the other and vice versa.
|
||||
|
||||
## Setup for Co-processor
|
||||
|
||||
UART HCI setup is done through the Bluetooth Component kconfig
|
||||
settings. In menuconfig, select `Component config` -> `Bluetooth` ->
|
||||
`Controller Options` -> `HCI mode` or `HCI Config` and set it to
|
||||
`UART(H4)`.
|
||||
|
||||
Depending on the selected co-processor, you can configure various UART
|
||||
parameters (Tx, Rx pins, hardware flow control, RTS, CTS pins,
|
||||
baudrate) through the Bluetooth Component. Other UART parameters not
|
||||
handled by the Bluetooth Component are configured by ESP-Hosted
|
||||
through `Example Configuration` -> `HCI UART Settings`.
|
||||
|
||||
> [!NOTE]
|
||||
> Make sure the UART GPIO pins selected do not conflict with the GPIO
|
||||
> pins used for the selected ESP-Hosted transport.
|
||||
|
||||
Co-processor setup for HCI over UART can also be found in the ESP-Hosted Bluetooth Design document on how to [configure the co-processor to use UART for HCI](https://github.com/espressif/esp-hosted-mcu/blob/main/docs/bluetooth_design.md#6-configuring-the-co-processor-for-uart-hci).
|
||||
|
||||
|
||||
## GPIO Setup for Host and Co-processor
|
||||
|
||||
Below is the pin connection chart for the ESP32-P4 (as host) and ESP32 (as BT controller).
|
||||
|
||||
| ESP32-P4 Signal | ESP32-P4 GPIO Pin | ESP32 Signal | ESP32 GPIO Pin |
|
||||
|:---------------:|------------------:|:------------:|---------------:|
|
||||
| Rx | 33 | Tx | 5 |
|
||||
| Tx | 24 | Rx | 18 |
|
||||
| RTS | 20 | CTS | 23 |
|
||||
| CTS | 21 | RTS | 19 |
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
The program’s entry point is the app_main() function:
|
||||
|
||||
```c
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* 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 || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
hci_uart_open();
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
....
|
||||
....
|
||||
}
|
||||
```
|
||||
In the main function `hci_uart_open()` configures the uart with necessary parameters like baud_rate, parity, etc. The file [uart_driver.c](../main/uart_driver.c) has all the uart transport code along with the necessary apis mandated by nimble-host. `ble_transport_to_ll_acl_impl` is the api used to send the data to the controller. and `ble_transport_to_ll_cmd_impl` is used to send the command to the controller. These two apis are implemented as they are necessary. Along with these two apis, any data that is received in the rx direction is redirected to the host using `ble_transport_to_hs_evt` and `ble_transport_to_hs_acl` apis.
|
||||
|
||||
## Conclusion
|
||||
1. This example covered how to write the custom transport layer and use it with nimble-host.
|
||||
2. The example used the esp-hosted network-adapter example flashed on esp32c6 connected through UART to test this demo.
|
||||
3. In the similar fashion the external controller could be connected.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 483 KiB |
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(bleprph_host_only_vhci)
|
||||
@@ -0,0 +1,202 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-S3 | ESP32-P4 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# BLE Host-Only Peripheral Hosted HCI Example
|
||||
|
||||
This example uses the Bluetooth Hosted HCI transport provided by ESP-Hosted.
|
||||
|
||||
To use the Hosted HCI transport in the application, the Bluetooth
|
||||
controller should be disabled and the default uart-transport should
|
||||
also be disabled (when the controller is disabled, by default the
|
||||
uart-transport is selected). The Bluetooth Hosted HCI transport in
|
||||
ESP-Hosted should also be enabled.
|
||||
|
||||
Refer to the `sdkconfig.defaults` file which has the required
|
||||
configuration.
|
||||
|
||||
To test this demo, any BLE scanner app can be used.
|
||||
|
||||
## How to Use This Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct
|
||||
chip target using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu:
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
In the `Component config-> Bluetooth` menu:
|
||||
|
||||
* Select `controller` to Disabled.
|
||||
* Disable `Nimble Options-> Host-controller Transport -> Enable Uart Transport`.
|
||||
|
||||
In the `Component config-> ESP-Hosted config` menu:
|
||||
|
||||
* Enable `Bluetooth Support-> Enable Hosted Bluetooth support`
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the
|
||||
project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://idf.espressif.com/) for full
|
||||
steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
This is the console output when `bleprph_host_only_vhci` is running on
|
||||
the ESP32-P4 and using the Bluetooth Controller of the ESP32C6 on the
|
||||
ESP32-P4-Function-EV-Board. The data is transferred through SDIO
|
||||
between the ESP32-P4 and ESP32-C6:
|
||||
|
||||
```
|
||||
I (25) boot: ESP-IDF v5.4-dev-2793-g24047f9a04-dirty 2nd stage bootloader
|
||||
I (26) boot: compile time Sep 9 2024 16:23:48
|
||||
I (27) boot: Multicore bootloader
|
||||
I (32) boot: chip revision: v0.1
|
||||
I (34) boot: efuse block revision: v0.0
|
||||
I (39) boot.esp32p4: SPI Speed : 80MHz
|
||||
I (44) boot.esp32p4: SPI Mode : DIO
|
||||
I (49) boot.esp32p4: SPI Flash Size : 2MB
|
||||
I (53) boot: Enabling RNG early entropy source...
|
||||
I (59) boot: Partition Table:
|
||||
I (62) boot: ## Label Usage Type ST Offset Length
|
||||
I (70) boot: 0 nvs WiFi data 01 02 00009000 00006000
|
||||
I (77) boot: 1 phy_init RF data 01 01 0000f000 00001000
|
||||
I (84) boot: 2 factory factory app 00 00 00010000 00100000
|
||||
I (93) boot: End of partition table
|
||||
I (96) esp_image: segment 0: paddr=00010020 vaddr=40070020 size=2ae08h (175624) map
|
||||
I (135) esp_image: segment 1: paddr=0003ae30 vaddr=30100000 size=0000ch ( 12) load
|
||||
I (137) esp_image: segment 2: paddr=0003ae44 vaddr=3010000c size=00038h ( 56) load
|
||||
I (142) esp_image: segment 3: paddr=0003ae84 vaddr=4ff00000 size=05194h ( 20884) load
|
||||
I (154) esp_image: segment 4: paddr=00040020 vaddr=40000020 size=638b0h (407728) map
|
||||
I (226) esp_image: segment 5: paddr=000a38d8 vaddr=4ff05194 size=0b9a0h ( 47520) load
|
||||
I (237) esp_image: segment 6: paddr=000af280 vaddr=4ff10b80 size=02328h ( 9000) load
|
||||
I (244) boot: Loaded app from partition at offset 0x10000
|
||||
I (244) boot: Disabling RNG early entropy source...
|
||||
I (258) cpu_start: Multicore app
|
||||
W (267) clk: esp_perip_clk_init() has not been implemented yet
|
||||
I (274) cpu_start: Pro cpu start user code
|
||||
I (274) cpu_start: cpu freq: 360000000 Hz
|
||||
I (274) app_init: Application information:
|
||||
I (277) app_init: Project name: bleprph_host_only_vhci
|
||||
I (283) app_init: App version: 1c568c9-dirty
|
||||
I (288) app_init: Compile time: Sep 9 2024 16:23:43
|
||||
I (294) app_init: ELF file SHA256: ee5a16527...
|
||||
I (299) app_init: ESP-IDF: v5.4-dev-2793-g24047f9a04-dirty
|
||||
I (306) efuse_init: Min chip rev: v0.1
|
||||
I (311) efuse_init: Max chip rev: v0.99
|
||||
I (316) efuse_init: Chip rev: v0.1
|
||||
I (321) heap_init: Initializing. RAM available for dynamic allocation:
|
||||
I (328) heap_init: At 4FF160B0 len 00024F10 (147 KiB): RAM
|
||||
I (334) heap_init: At 4FF3AFC0 len 00004BF0 (18 KiB): RAM
|
||||
I (340) heap_init: At 4FF40000 len 00060000 (384 KiB): RAM
|
||||
I (347) heap_init: At 50108080 len 00007F80 (31 KiB): RTCRAM
|
||||
I (353) heap_init: At 30100044 len 00001FBC (7 KiB): TCM
|
||||
I (360) spi_flash: detected chip: generic
|
||||
I (363) spi_flash: flash io: dio
|
||||
W (367) spi_flash: Detected size(16384k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
|
||||
I (381) host_init: ESP Hosted : Host chip_ip[18]
|
||||
I (409) H_API: ESP-Hosted starting. Hosted_Tasks: prio:23, stack: 5120 RPC_task_stack: 5120
|
||||
sdio_mempool_create free:181028 min-free:181028 lfb-def:139264 lfb-8bit:139264
|
||||
|
||||
I (414) gpio: GPIO[18]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (423) gpio: GPIO[19]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (433) gpio: GPIO[14]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (442) gpio: GPIO[15]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (451) gpio: GPIO[16]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (461) gpio: GPIO[17]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (470) H_API: ** add_esp_wifi_remote_channels **
|
||||
I (475) transport: Add ESP-Hosted channel IF[1]: S[0] Tx[0x4000d110] Rx[0x4001b4aa]
|
||||
0x4000d110: transport_drv_sta_tx at /home/kysoh/projects/esp_as_mcu_host/examples/bleprph_host_only_vhci/components/esp_hosted/host/drivers/transport/transport_drv.c:208
|
||||
0x4001b4aa: esp_wifi_remote_channel_rx at /home/kysoh/projects/esp_as_mcu_host/examples/bleprph_host_only_vhci/managed_components/espressif__esp_wifi_remote/esp_wifi_remote_net.c:19
|
||||
|
||||
I (484) transport: Add ESP-Hosted channel IF[2]: S[0] Tx[0x4000d058] Rx[0x4001b4aa]
|
||||
0x4000d058: transport_drv_ap_tx at /home/kysoh/projects/esp_as_mcu_host/examples/bleprph_host_only_vhci/components/esp_hosted/host/drivers/transport/transport_drv.c:238
|
||||
0x4001b4aa: esp_wifi_remote_channel_rx at /home/kysoh/projects/esp_as_mcu_host/examples/bleprph_host_only_vhci/managed_components/espressif__esp_wifi_remote/esp_wifi_remote_net.c:19
|
||||
|
||||
I (493) main_task: Started on CPU0
|
||||
I (503) main_task: Calling app_main()
|
||||
I (513) transport: Attempt connection with slave: retry[0]
|
||||
I (513) transport: Reset slave using GPIO[54]
|
||||
I (513) os_wrapper_esp: GPIO [54] configured
|
||||
I (513) gpio: GPIO[54]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (1693) sdio_wrapper: SDIO master: Data-Lines: 4-bit Freq(KHz)[40000 KHz]
|
||||
I (1693) sdio_wrapper: GPIOs: CLK[18] CMD[19] D0[14] D1[15] D2[16] D3[17] Slave_Reset[54]
|
||||
I (1693) H_SDIO_DRV: Starting SDIO process rx task
|
||||
I (1693) sdio_wrapper: Queues: Tx[20] Rx[20] SDIO-Rx-Mode[1]
|
||||
I (1733) gpio: GPIO[15]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1733) gpio: GPIO[17]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
Name:
|
||||
Type: SDIO
|
||||
Speed: 40.00 MHz (limit: 40.00 MHz)
|
||||
Size: 0MB
|
||||
CSD: ver=1, sector_size=0, capacity=0 read_bl_len=0
|
||||
SCR: sd_spec=0, bus_width=0
|
||||
TUPLE: DEVICE, size: 3: D9 01 FF
|
||||
TUPLE: MANFID, size: 4
|
||||
MANF: 0092, CARD: 6666
|
||||
TUPLE: FUNCID, size: 2: 0C 00
|
||||
TUPLE: FUNCE, size: 4: 00 00 02 32
|
||||
TUPLE: CONFIG, size: 5: 01 01 00 02 07
|
||||
TUPLE: CFTABLE_ENTRY, size: 8
|
||||
INDX: C1, Intface: 1, Default: 1, Conf-Entry-Num: 1
|
||||
IF: 41
|
||||
FS: 30, misc: 0, mem_space: 1, irq: 1, io_space: 0, timing: 0, power: 0
|
||||
IR: 30, mask: 1, IRQ: FF FF
|
||||
LEN: FFFF
|
||||
TUPLE: END
|
||||
I (1783) sdio_wrapper: Function 0 Blocksize: 512
|
||||
I (1793) sdio_wrapper: Function 1 Blocksize: 512
|
||||
I (1793) H_SDIO_DRV: SDIO Host operating in STREAMING MODE
|
||||
I (1803) H_SDIO_DRV: generate slave intr
|
||||
I (1813) transport: Received INIT event from ESP32 peripheral
|
||||
I (1813) transport: EVENT: 12
|
||||
I (1813) transport: EVENT: 11
|
||||
I (1823) transport: capabilities: 0xd
|
||||
I (1823) transport: Features supported are:
|
||||
I (1833) transport: * WLAN
|
||||
I (1833) transport: - HCI over SDIO
|
||||
I (1843) transport: - BLE only
|
||||
I (1843) transport: EVENT: 13
|
||||
I (1843) transport: ESP board type is : 13
|
||||
|
||||
I (1853) transport: Base transport is set-up
|
||||
|
||||
I (1853) transport: Slave chip Id[12]
|
||||
I (1863) vhci_drv: Host BT Support: Enabled
|
||||
I (1863) vhci_drv: BT Transport Type: VHCI
|
||||
I (1873) H_SDIO_DRV: Received INIT event
|
||||
I (1883) rpc_wrap: Received Slave ESP Init
|
||||
I (2623) NimBLE_BLE_PRPH: BLE Host Task Started
|
||||
I (2623) uart: queue free spaces: 8
|
||||
I (2623) main_task: Returned from app_main()
|
||||
I (2623) NimBLE: GAP procedure initiated: stop advertising.
|
||||
|
||||
I (2633) NimBLE: Device Address:
|
||||
I (2633) NimBLE: 40:4c:ca:5b:9a:e2
|
||||
I (2633) NimBLE:
|
||||
|
||||
I (2643) NimBLE: GAP procedure initiated: advertise;
|
||||
I (2643) NimBLE: disc_mode=2
|
||||
I (2653) NimBLE: adv_channel_map=0 own_addr_type=0 adv_filter_policy=0 adv_itvl_min=0 adv_itvl_max=0
|
||||
I (2663) NimBLE:
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-hosted-mcu/issues) on ESP-Hosted on GitHub. We will get back to you soon.
|
||||
|
||||
## References
|
||||
|
||||
* Bluetooth Implementation in ESP-Hosted: https://github.com/espressif/esp-hosted-mcu/blob/main/docs/bluetooth_design.md
|
||||
@@ -0,0 +1,5 @@
|
||||
set(srcs "main.c"
|
||||
"gatt_svr.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,80 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_USE_IO_TYPE
|
||||
prompt "I/O Capability"
|
||||
default BLE_SM_IO_CAP_NO_IO
|
||||
help
|
||||
I/O capability of device.
|
||||
|
||||
config BLE_SM_IO_CAP_DISP_ONLY
|
||||
bool "DISPLAY ONLY"
|
||||
config BLE_SM_IO_CAP_DISP_YES_NO
|
||||
bool "DISPLAY YESNO"
|
||||
config BLE_SM_IO_CAP_KEYBOARD_ONLY
|
||||
bool "KEYBOARD ONLY"
|
||||
config BLE_SM_IO_CAP_NO_IO
|
||||
bool "Just works"
|
||||
config BLE_SM_IO_CAP_KEYBOARD_DISP
|
||||
bool "Both KEYBOARD & DISPLAY"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_IO_TYPE
|
||||
int
|
||||
default 0 if BLE_SM_IO_CAP_DISP_ONLY
|
||||
default 1 if BLE_SM_IO_CAP_DISP_YES_NO
|
||||
default 2 if BLE_SM_IO_CAP_KEYBOARD_ONLY
|
||||
default 3 if BLE_SM_IO_CAP_NO_IO
|
||||
default 4 if BLE_SM_IO_CAP_KEYBOARD_DISP
|
||||
|
||||
config EXAMPLE_BONDING
|
||||
bool
|
||||
default n
|
||||
prompt "Use Bonding"
|
||||
help
|
||||
Use this option to enable/disable bonding.
|
||||
|
||||
config EXAMPLE_MITM
|
||||
bool
|
||||
default n
|
||||
prompt "MITM security"
|
||||
help
|
||||
Use this option to enable/disable MITM security.
|
||||
|
||||
config EXAMPLE_USE_SC
|
||||
bool
|
||||
depends on BT_NIMBLE_SM_SC
|
||||
default n
|
||||
prompt "Use Secure Connection feature"
|
||||
help
|
||||
Use this option to enable/disable Security Manager Secure Connection 4.2 feature.
|
||||
|
||||
config EXAMPLE_EXTENDED_ADV
|
||||
bool
|
||||
depends on SOC_BLE_50_SUPPORTED && BT_NIMBLE_50_FEATURE_SUPPORT
|
||||
default y if SOC_ESP_NIMBLE_CONTROLLER
|
||||
select BT_NIMBLE_EXT_ADV
|
||||
prompt "Enable Extended Adv"
|
||||
help
|
||||
Use this option to enable extended advertising in the example.
|
||||
If this option is disabled, ensure config BT_NIMBLE_EXT_ADV is
|
||||
also disabled from Nimble stack menuconfig
|
||||
|
||||
config EXAMPLE_RANDOM_ADDR
|
||||
bool
|
||||
prompt "Advertise RANDOM Address"
|
||||
help
|
||||
Use this option to advertise a random address instead of public address
|
||||
|
||||
config EXAMPLE_ENCRYPTION
|
||||
bool
|
||||
prompt "Enable Link Encryption"
|
||||
help
|
||||
This adds Encrypted Read and Write permissions in the custom GATT server.
|
||||
|
||||
config EXAMPLE_RESOLVE_PEER_ADDR
|
||||
bool
|
||||
prompt "Enable resolving peer address"
|
||||
help
|
||||
Use this option to enable resolving peer's address.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef H_BLEPRPH_
|
||||
#define H_BLEPRPH_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "nimble/ble.h"
|
||||
#include "modlog/modlog.h"
|
||||
#include "esp_peripheral.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ble_hs_cfg;
|
||||
struct ble_gatt_register_ctxt;
|
||||
|
||||
/** GATT server. */
|
||||
#define GATT_SVR_SVC_ALERT_UUID 0x1811
|
||||
#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
|
||||
#define GATT_SVR_CHR_NEW_ALERT 0x2A46
|
||||
#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
|
||||
#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45
|
||||
#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44
|
||||
|
||||
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
|
||||
int gatt_svr_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_uuid.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
#include "bleprph.h"
|
||||
#include "services/ans/ble_svc_ans.h"
|
||||
|
||||
/*** Maximum number of characteristics with the notify flag ***/
|
||||
#define MAX_NOTIFY 5
|
||||
|
||||
static const ble_uuid128_t gatt_svr_svc_uuid =
|
||||
BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
|
||||
0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
|
||||
|
||||
/* A characteristic that can be subscribed to */
|
||||
static uint8_t gatt_svr_chr_val;
|
||||
static uint16_t gatt_svr_chr_val_handle;
|
||||
static const ble_uuid128_t gatt_svr_chr_uuid =
|
||||
BLE_UUID128_INIT(0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11,
|
||||
0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33);
|
||||
|
||||
/* A custom descriptor */
|
||||
static uint8_t gatt_svr_dsc_val;
|
||||
static const ble_uuid128_t gatt_svr_dsc_uuid =
|
||||
BLE_UUID128_INIT(0x01, 0x01, 0x01, 0x01, 0x12, 0x12, 0x12, 0x12,
|
||||
0x23, 0x23, 0x23, 0x23, 0x34, 0x34, 0x34, 0x34);
|
||||
|
||||
static int
|
||||
gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt,
|
||||
void *arg);
|
||||
|
||||
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
|
||||
{
|
||||
/*** Service ***/
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = &gatt_svr_svc_uuid.u,
|
||||
.characteristics = (struct ble_gatt_chr_def[])
|
||||
{ {
|
||||
/*** This characteristic can be subscribed to by writing 0x00 and 0x01 to the CCCD ***/
|
||||
.uuid = &gatt_svr_chr_uuid.u,
|
||||
.access_cb = gatt_svc_access,
|
||||
#if CONFIG_EXAMPLE_ENCRYPTION
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE |
|
||||
BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC |
|
||||
BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
|
||||
#else
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE,
|
||||
#endif
|
||||
.val_handle = &gatt_svr_chr_val_handle,
|
||||
.descriptors = (struct ble_gatt_dsc_def[])
|
||||
{ {
|
||||
.uuid = &gatt_svr_dsc_uuid.u,
|
||||
#if CONFIG_EXAMPLE_ENCRYPTION
|
||||
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC,
|
||||
#else
|
||||
.att_flags = BLE_ATT_F_READ,
|
||||
#endif
|
||||
.access_cb = gatt_svc_access,
|
||||
}, {
|
||||
0, /* No more descriptors in this characteristic */
|
||||
}
|
||||
},
|
||||
}, {
|
||||
0, /* No more characteristics in this service. */
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
0, /* No more services. */
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
gatt_svr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
|
||||
void *dst, uint16_t *len)
|
||||
{
|
||||
uint16_t om_len;
|
||||
int rc;
|
||||
|
||||
om_len = OS_MBUF_PKTLEN(om);
|
||||
if (om_len < min_len || om_len > max_len) {
|
||||
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
||||
}
|
||||
|
||||
rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
|
||||
if (rc != 0) {
|
||||
return BLE_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback whenever a characteristic/descriptor is read or written to.
|
||||
* Here reads and writes need to be handled.
|
||||
* ctxt->op tells weather the operation is read or write and
|
||||
* weather it is on a characteristic or descriptor,
|
||||
* ctxt->dsc->uuid tells which characteristic/descriptor is accessed.
|
||||
* attr_handle give the value handle of the attribute being accessed.
|
||||
* Accordingly do:
|
||||
* Append the value to ctxt->om if the operation is READ
|
||||
* Write ctxt->om to the value if the operation is WRITE
|
||||
**/
|
||||
static int
|
||||
gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
const ble_uuid_t *uuid;
|
||||
int rc;
|
||||
|
||||
switch (ctxt->op) {
|
||||
case BLE_GATT_ACCESS_OP_READ_CHR:
|
||||
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||
MODLOG_DFLT(INFO, "Characteristic read; conn_handle=%d attr_handle=%d\n",
|
||||
conn_handle, attr_handle);
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Characteristic read by NimBLE stack; attr_handle=%d\n",
|
||||
attr_handle);
|
||||
}
|
||||
uuid = ctxt->chr->uuid;
|
||||
if (attr_handle == gatt_svr_chr_val_handle) {
|
||||
rc = os_mbuf_append(ctxt->om,
|
||||
&gatt_svr_chr_val,
|
||||
sizeof(gatt_svr_chr_val));
|
||||
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
}
|
||||
goto unknown;
|
||||
|
||||
case BLE_GATT_ACCESS_OP_WRITE_CHR:
|
||||
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||
MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d",
|
||||
conn_handle, attr_handle);
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Characteristic write by NimBLE stack; attr_handle=%d",
|
||||
attr_handle);
|
||||
}
|
||||
uuid = ctxt->chr->uuid;
|
||||
if (attr_handle == gatt_svr_chr_val_handle) {
|
||||
rc = gatt_svr_write(ctxt->om,
|
||||
sizeof(gatt_svr_chr_val),
|
||||
sizeof(gatt_svr_chr_val),
|
||||
&gatt_svr_chr_val, NULL);
|
||||
ble_gatts_chr_updated(attr_handle);
|
||||
MODLOG_DFLT(INFO, "Notification/Indication scheduled for "
|
||||
"all subscribed peers.\n");
|
||||
return rc;
|
||||
}
|
||||
goto unknown;
|
||||
|
||||
case BLE_GATT_ACCESS_OP_READ_DSC:
|
||||
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||
MODLOG_DFLT(INFO, "Descriptor read; conn_handle=%d attr_handle=%d\n",
|
||||
conn_handle, attr_handle);
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Descriptor read by NimBLE stack; attr_handle=%d\n",
|
||||
attr_handle);
|
||||
}
|
||||
uuid = ctxt->dsc->uuid;
|
||||
if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) {
|
||||
rc = os_mbuf_append(ctxt->om,
|
||||
&gatt_svr_dsc_val,
|
||||
sizeof(gatt_svr_chr_val));
|
||||
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
|
||||
}
|
||||
goto unknown;
|
||||
|
||||
case BLE_GATT_ACCESS_OP_WRITE_DSC:
|
||||
goto unknown;
|
||||
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
|
||||
unknown:
|
||||
/* Unknown characteristic/descriptor;
|
||||
* The NimBLE host should not have called this function;
|
||||
*/
|
||||
assert(0);
|
||||
return BLE_ATT_ERR_UNLIKELY;
|
||||
}
|
||||
|
||||
void
|
||||
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
|
||||
{
|
||||
char buf[BLE_UUID_STR_LEN];
|
||||
|
||||
switch (ctxt->op) {
|
||||
case BLE_GATT_REGISTER_OP_SVC:
|
||||
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
|
||||
ctxt->svc.handle);
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_CHR:
|
||||
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
|
||||
"def_handle=%d val_handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
|
||||
ctxt->chr.def_handle,
|
||||
ctxt->chr.val_handle);
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_DSC:
|
||||
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
|
||||
ctxt->dsc.handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
gatt_svr_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ble_svc_gap_init();
|
||||
ble_svc_gatt_init();
|
||||
ble_svc_ans_init();
|
||||
|
||||
rc = ble_gatts_count_cfg(gatt_svr_svcs);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ble_gatts_add_svcs(gatt_svr_svcs);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Setting a value for the read-only descriptor */
|
||||
gatt_svr_dsc_val = 0x99;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
dependencies:
|
||||
espressif/esp_hosted:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: ~2
|
||||
espressif/esp_wifi_remote:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: '>=0.10'
|
||||
nimble_peripheral_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils
|
||||
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "console/console.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "bleprph.h"
|
||||
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
static uint8_t ext_adv_pattern_1[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x03, 0x03, 0xab, 0xcd,
|
||||
0x03, 0x03, 0x18, 0x11,
|
||||
0x11, 0X09, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'e',
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char *tag = "NimBLE_BLE_PRPH";
|
||||
static int bleprph_gap_event(struct ble_gap_event *event, void *arg);
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
static uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM;
|
||||
#else
|
||||
static uint8_t own_addr_type;
|
||||
#endif
|
||||
|
||||
void ble_store_config_init(void);
|
||||
|
||||
/**
|
||||
* Logs information about a connection to the console.
|
||||
*/
|
||||
static void
|
||||
bleprph_print_conn_desc(struct ble_gap_conn_desc *desc)
|
||||
{
|
||||
MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
|
||||
desc->conn_handle, desc->our_ota_addr.type);
|
||||
print_addr(desc->our_ota_addr.val);
|
||||
MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
|
||||
desc->our_id_addr.type);
|
||||
print_addr(desc->our_id_addr.val);
|
||||
MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
|
||||
desc->peer_ota_addr.type);
|
||||
print_addr(desc->peer_ota_addr.val);
|
||||
MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
|
||||
desc->peer_id_addr.type);
|
||||
print_addr(desc->peer_id_addr.val);
|
||||
MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
|
||||
"encrypted=%d authenticated=%d bonded=%d\n",
|
||||
desc->conn_itvl, desc->conn_latency,
|
||||
desc->supervision_timeout,
|
||||
desc->sec_state.encrypted,
|
||||
desc->sec_state.authenticated,
|
||||
desc->sec_state.bonded);
|
||||
}
|
||||
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
/**
|
||||
* Enables advertising with the following parameters:
|
||||
* o General discoverable mode.
|
||||
* o Undirected connectable mode.
|
||||
*/
|
||||
static void
|
||||
ext_bleprph_advertise(void)
|
||||
{
|
||||
struct ble_gap_ext_adv_params params;
|
||||
struct os_mbuf *data;
|
||||
uint8_t instance = 0;
|
||||
int rc;
|
||||
|
||||
/* First check if any instance is already active */
|
||||
if(ble_gap_ext_adv_active(instance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* use defaults for non-set params */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
|
||||
/* enable connectable advertising */
|
||||
params.connectable = 1;
|
||||
|
||||
/* advertise using random addr */
|
||||
params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
|
||||
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M;
|
||||
//params.tx_power = 127;
|
||||
params.sid = 1;
|
||||
|
||||
params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
|
||||
params.itvl_max = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
|
||||
|
||||
/* configure instance 0 */
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL,
|
||||
bleprph_gap_event, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
/* in this case only scan response is allowed */
|
||||
|
||||
/* get mbuf for scan rsp data */
|
||||
data = os_msys_get_pkthdr(sizeof(ext_adv_pattern_1), 0);
|
||||
assert(data);
|
||||
|
||||
/* fill mbuf with scan rsp data */
|
||||
rc = os_mbuf_append(data, ext_adv_pattern_1, sizeof(ext_adv_pattern_1));
|
||||
assert(rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
assert (rc == 0);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
assert (rc == 0);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Enables advertising with the following parameters:
|
||||
* o General discoverable mode.
|
||||
* o Undirected connectable mode.
|
||||
*/
|
||||
static void
|
||||
bleprph_advertise(void)
|
||||
{
|
||||
struct ble_gap_adv_params adv_params;
|
||||
struct ble_hs_adv_fields fields;
|
||||
const char *name;
|
||||
int rc;
|
||||
|
||||
/**
|
||||
* Set the advertisement data included in our advertisements:
|
||||
* o Flags (indicates advertisement type and other general info).
|
||||
* o Advertising tx power.
|
||||
* o Device name.
|
||||
* o 16-bit service UUIDs (alert notifications).
|
||||
*/
|
||||
|
||||
memset(&fields, 0, sizeof fields);
|
||||
|
||||
/* Advertise two flags:
|
||||
* o Discoverability in forthcoming advertisement (general)
|
||||
* o BLE-only (BR/EDR unsupported).
|
||||
*/
|
||||
fields.flags = BLE_HS_ADV_F_DISC_GEN |
|
||||
BLE_HS_ADV_F_BREDR_UNSUP;
|
||||
|
||||
/* Indicate that the TX power level field should be included; have the
|
||||
* stack fill this value automatically. This is done by assigning the
|
||||
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
|
||||
*/
|
||||
fields.tx_pwr_lvl_is_present = 1;
|
||||
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||
|
||||
name = ble_svc_gap_device_name();
|
||||
fields.name = (uint8_t *)name;
|
||||
fields.name_len = strlen(name);
|
||||
fields.name_is_complete = 1;
|
||||
|
||||
fields.uuids16 = (ble_uuid16_t[]) {
|
||||
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
|
||||
};
|
||||
fields.num_uuids16 = 1;
|
||||
fields.uuids16_is_complete = 1;
|
||||
|
||||
rc = ble_gap_adv_set_fields(&fields);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Begin advertising. */
|
||||
memset(&adv_params, 0, sizeof adv_params);
|
||||
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
|
||||
&adv_params, bleprph_gap_event, NULL);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_POWER_CONTROL)
|
||||
static void bleprph_power_control(uint16_t conn_handle)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01 ); // Attempting on LE 1M phy
|
||||
assert (rc == 0);
|
||||
|
||||
rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x1, 0x1);
|
||||
assert (rc == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The nimble host executes this callback when a GAP event occurs. The
|
||||
* application associates a GAP event callback with each connection that forms.
|
||||
* bleprph uses the same callback for all connections.
|
||||
*
|
||||
* @param event The type of event being signalled.
|
||||
* @param ctxt Various information pertaining to the event.
|
||||
* @param arg Application-specified argument; unused by
|
||||
* bleprph.
|
||||
*
|
||||
* @return 0 if the application successfully handled the
|
||||
* event; nonzero on failure. The semantics
|
||||
* of the return code is specific to the
|
||||
* particular GAP event being signalled.
|
||||
*/
|
||||
static int
|
||||
bleprph_gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
int rc;
|
||||
|
||||
switch (event->type) {
|
||||
#if defined(BLE_GAP_EVENT_LINK_ESTAB)
|
||||
case BLE_GAP_EVENT_LINK_ESTAB:
|
||||
#else
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
#endif
|
||||
/* A new connection was established or a connection attempt failed. */
|
||||
MODLOG_DFLT(INFO, "connection %s; status=%d ",
|
||||
event->connect.status == 0 ? "established" : "failed",
|
||||
event->connect.status);
|
||||
if (event->connect.status == 0) {
|
||||
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
bleprph_print_conn_desc(&desc);
|
||||
}
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
if (event->connect.status != 0) {
|
||||
/* Connection failed; resume advertising. */
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
ext_bleprph_advertise();
|
||||
#else
|
||||
bleprph_advertise();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MYNEWT_VAL(BLE_POWER_CONTROL)
|
||||
bleprph_power_control(event->connect.conn_handle);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
|
||||
bleprph_print_conn_desc(&event->disconnect.conn);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* Connection terminated; resume advertising. */
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
ext_bleprph_advertise();
|
||||
#else
|
||||
bleprph_advertise();
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
/* The central has updated the connection parameters. */
|
||||
MODLOG_DFLT(INFO, "connection updated; status=%d ",
|
||||
event->conn_update.status);
|
||||
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
bleprph_print_conn_desc(&desc);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ADV_COMPLETE:
|
||||
MODLOG_DFLT(INFO, "advertise complete; reason=%d",
|
||||
event->adv_complete.reason);
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
ext_bleprph_advertise();
|
||||
#else
|
||||
bleprph_advertise();
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
/* Encryption has been enabled or disabled for this connection. */
|
||||
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
|
||||
event->enc_change.status);
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
bleprph_print_conn_desc(&desc);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_TX:
|
||||
MODLOG_DFLT(INFO, "notify_tx event; conn_handle=%d attr_handle=%d "
|
||||
"status=%d is_indication=%d",
|
||||
event->notify_tx.conn_handle,
|
||||
event->notify_tx.attr_handle,
|
||||
event->notify_tx.status,
|
||||
event->notify_tx.indication);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_SUBSCRIBE:
|
||||
MODLOG_DFLT(INFO, "subscribe event; conn_handle=%d attr_handle=%d "
|
||||
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
|
||||
event->subscribe.conn_handle,
|
||||
event->subscribe.attr_handle,
|
||||
event->subscribe.reason,
|
||||
event->subscribe.prev_notify,
|
||||
event->subscribe.cur_notify,
|
||||
event->subscribe.prev_indicate,
|
||||
event->subscribe.cur_indicate);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_MTU:
|
||||
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
|
||||
event->mtu.conn_handle,
|
||||
event->mtu.channel_id,
|
||||
event->mtu.value);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_REPEAT_PAIRING:
|
||||
/* We already have a bond with the peer, but it is attempting to
|
||||
* establish a new secure link. This app sacrifices security for
|
||||
* convenience: just throw away the old bond and accept the new link.
|
||||
*/
|
||||
|
||||
/* Delete the old bond. */
|
||||
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||
|
||||
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
|
||||
* continue with the pairing operation.
|
||||
*/
|
||||
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||
ESP_LOGI(tag, "PASSKEY_ACTION_EVENT started");
|
||||
struct ble_sm_io pkey = {0};
|
||||
int key = 0;
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = 123456; // This is the passkey to be entered on peer
|
||||
ESP_LOGI(tag, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey);
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
ESP_LOGI(tag, "Passkey on device's display: %" PRIu32 , event->passkey.params.numcmp);
|
||||
ESP_LOGI(tag, "Accept or reject the passkey through console in this format -> key Y or key N");
|
||||
pkey.action = event->passkey.params.action;
|
||||
if (scli_receive_key(&key)) {
|
||||
pkey.numcmp_accept = key;
|
||||
} else {
|
||||
pkey.numcmp_accept = 0;
|
||||
ESP_LOGE(tag, "Timeout! Rejecting the key");
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
||||
static uint8_t tem_oob[16] = {0};
|
||||
pkey.action = event->passkey.params.action;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
pkey.oob[i] = tem_oob[i];
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
ESP_LOGI(tag, "Enter the passkey through console in this format-> key 123456");
|
||||
pkey.action = event->passkey.params.action;
|
||||
if (scli_receive_key(&key)) {
|
||||
pkey.passkey = key;
|
||||
} else {
|
||||
pkey.passkey = 0;
|
||||
ESP_LOGE(tag, "Timeout! Passing 0 as the key");
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
}
|
||||
return 0;
|
||||
|
||||
#if MYNEWT_VAL(BLE_POWER_CONTROL)
|
||||
case BLE_GAP_EVENT_TRANSMIT_POWER:
|
||||
MODLOG_DFLT(INFO, "Transmit power event : status=%d conn_handle=%d reason=%d "
|
||||
"phy=%d power_level=%x power_level_flag=%d delta=%d",
|
||||
event->transmit_power.status,
|
||||
event->transmit_power.conn_handle,
|
||||
event->transmit_power.reason,
|
||||
event->transmit_power.phy,
|
||||
event->transmit_power.transmit_power_level,
|
||||
event->transmit_power.transmit_power_level_flag,
|
||||
event->transmit_power.delta);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PATHLOSS_THRESHOLD:
|
||||
MODLOG_DFLT(INFO, "Pathloss threshold event : conn_handle=%d current path loss=%d "
|
||||
"zone_entered =%d",
|
||||
event->pathloss_threshold.conn_handle,
|
||||
event->pathloss_threshold.current_path_loss,
|
||||
event->pathloss_threshold.zone_entered);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
bleprph_on_reset(int reason)
|
||||
{
|
||||
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
|
||||
}
|
||||
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
static void
|
||||
ble_app_set_addr(void)
|
||||
{
|
||||
ble_addr_t addr;
|
||||
int rc;
|
||||
|
||||
/* generate new non-resolvable private address */
|
||||
rc = ble_hs_id_gen_rnd(0, &addr);
|
||||
assert(rc == 0);
|
||||
|
||||
/* set generated address */
|
||||
rc = ble_hs_id_set_rnd(addr.val);
|
||||
|
||||
assert(rc == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
bleprph_on_sync(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
/* Generate a non-resolvable private address. */
|
||||
ble_app_set_addr();
|
||||
#endif
|
||||
|
||||
/* Make sure we have proper identity address set (public preferred) */
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
rc = ble_hs_util_ensure_addr(1);
|
||||
#else
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
#endif
|
||||
assert(rc == 0);
|
||||
|
||||
/* Figure out address to use while advertising (no privacy for now) */
|
||||
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Printing ADDR */
|
||||
uint8_t addr_val[6] = {0};
|
||||
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
|
||||
|
||||
MODLOG_DFLT(INFO, "Device Address: ");
|
||||
print_addr(addr_val);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
/* Begin advertising. */
|
||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||
ext_bleprph_advertise();
|
||||
#else
|
||||
bleprph_advertise();
|
||||
#endif
|
||||
}
|
||||
|
||||
void bleprph_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(tag, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* 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 || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = bleprph_on_reset;
|
||||
ble_hs_cfg.sync_cb = bleprph_on_sync;
|
||||
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
|
||||
ble_hs_cfg.sm_io_cap = CONFIG_EXAMPLE_IO_TYPE;
|
||||
#ifdef CONFIG_EXAMPLE_BONDING
|
||||
ble_hs_cfg.sm_bonding = 1;
|
||||
/* Enable the appropriate bit masks to make sure the keys
|
||||
* that are needed are exchanged
|
||||
*/
|
||||
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
|
||||
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_MITM
|
||||
ble_hs_cfg.sm_mitm = 1;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_USE_SC
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
#else
|
||||
ble_hs_cfg.sm_sc = 0;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_RESOLVE_PEER_ADDR
|
||||
/* Stores the IRK */
|
||||
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ID;
|
||||
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID;
|
||||
#endif
|
||||
|
||||
rc = gatt_svr_init();
|
||||
assert(rc == 0);
|
||||
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble-bleprph");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
nimble_port_freertos_init(bleprph_host_task);
|
||||
|
||||
/* Initialize command line interface to accept input from user */
|
||||
rc = scli_init();
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGE(tag, "scli_init() failed");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_TRANSPORT_UART=n
|
||||
|
||||
#
|
||||
# Wi-Fi Remote
|
||||
#
|
||||
CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y
|
||||
|
||||
#
|
||||
# Enable ESP Hosted BT
|
||||
# Used as VHCI transport between BT Host and Controller
|
||||
#
|
||||
CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
CONFIG_ESP_HOSTED_NIMBLE_HCI_VHCI=y
|
||||
Reference in New Issue
Block a user