From ec92e964061ed42211be56dd56f1de7d8ecee151 Mon Sep 17 00:00:00 2001 From: "isha.pardikar@espressif.com" Date: Fri, 10 Mar 2023 18:33:51 +0530 Subject: [PATCH] NimbLE: Added multi advertising example --- examples/bluetooth/.build-test-rules.yml | 10 + .../nimble/ble_multi_adv/CMakeLists.txt | 8 + .../bluetooth/nimble/ble_multi_adv/README.md | 74 +++ .../nimble/ble_multi_adv/main/CMakeLists.txt | 5 + .../nimble/ble_multi_adv/main/gatt_svr.c | 169 ++++++ .../nimble/ble_multi_adv/main/main.c | 499 ++++++++++++++++++ .../nimble/ble_multi_adv/main/multi_adv.h | 35 ++ .../nimble/ble_multi_adv/sdkconfig.defaults | 14 + .../ble_multi_adv/sdkconfig.defaults.esp32c2 | 7 + .../ble_multi_adv/sdkconfig.defaults.esp32c6 | 6 + 10 files changed, 827 insertions(+) create mode 100644 examples/bluetooth/nimble/ble_multi_adv/CMakeLists.txt create mode 100644 examples/bluetooth/nimble/ble_multi_adv/README.md create mode 100644 examples/bluetooth/nimble/ble_multi_adv/main/CMakeLists.txt create mode 100644 examples/bluetooth/nimble/ble_multi_adv/main/gatt_svr.c create mode 100644 examples/bluetooth/nimble/ble_multi_adv/main/main.c create mode 100644 examples/bluetooth/nimble/ble_multi_adv/main/multi_adv.h create mode 100644 examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults create mode 100644 examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c2 create mode 100644 examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c6 diff --git a/examples/bluetooth/.build-test-rules.yml b/examples/bluetooth/.build-test-rules.yml index 02ac702801d..282e30bea97 100644 --- a/examples/bluetooth/.build-test-rules.yml +++ b/examples/bluetooth/.build-test-rules.yml @@ -180,6 +180,16 @@ examples/bluetooth/nimble/ble_l2cap_coc: temporary: true reason: The runner doesn't support yet +examples/bluetooth/nimble/ble_multi_adv: + enable: + - if: IDF_TARGET in ["esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32h4", "esp32s3"] + temporary: true + reason: the other targets are not tested yet + disable_test: + - if: IDF_TARGET in ["esp32"] + temporary: true + reason: The runner doesn't support yet + examples/bluetooth/nimble/ble_periodic_adv: enable: - if: IDF_TARGET in ["esp32c2", "esp32c3", "esp32c6" , "esp32s3", "esp32h2", "esp32h4" ] diff --git a/examples/bluetooth/nimble/ble_multi_adv/CMakeLists.txt b/examples/bluetooth/nimble/ble_multi_adv/CMakeLists.txt new file mode 100644 index 00000000000..13fcb1c54dc --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/CMakeLists.txt @@ -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) + +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_multi_adv) diff --git a/examples/bluetooth/nimble/ble_multi_adv/README.md b/examples/bluetooth/nimble/ble_multi_adv/README.md new file mode 100644 index 00000000000..48bebaf34a5 --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/README.md @@ -0,0 +1,74 @@ +| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-H4 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | -------- | -------- | -------- | + +# BLE Multi Adv Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example support legacy as well as extended advertisement for all phy. For multi advertising, random addresses are generated for each instance. + +It starts 4 types of advertising: +* Non connectable extended +* Connectable extended +* Scannable legacy +* Legacy withe specified duration(5 sec) + +## How to Use Example + +Before project configuration and build, be sure to set the correct chip target using: + +```bash +idf.py set-target +``` + +### Configure the Project + +Open the project configuration menu: +``` +idf.py menuconfig +``` + +To configure number of advertising instances: + +* Component config → Bluetooth → NimBLE Options → Enable BLE 5 feature → Maximum number of extended advertising instances + +### Hardware Required + +* A development board with ESP32-C3 SoC,ESP32-S3/ESP32-H4/ESP32-C2 SoC and BLE5.0 supported chips (e.g., ESP32-C3-DevKitC, etc.) +* A USB cable for Power supply and programming + +See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it. + +### 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 + +``` +I (404) NimBLE_MULTI_ADV: BLE Host Task Started + +I (414) NimBLE_MULTI_ADV: Instance 0 started +I (414) NimBLE_MULTI_ADV: Instance 1 started +I (434) NimBLE_MULTI_ADV: Instance 2 started +I (444) NimBLE_MULTI_ADV: Instance 3 started + +I (454) main_task: Returned from app_main() +I (5404) NimBLE: advertise complete; reason=13 +I (5404) NimBLE_MULTI_ADV: In ble_legacy_duration_cb, instance = 3 + +I (5404) NimBLE_MULTI_ADV: Instance 3 started +I (10384) NimBLE: advertise complete; reason=13 +I (10384) NimBLE_MULTI_ADV: In ble_legacy_duration_cb, instance = 3 + +I (10394) NimBLE_MULTI_ADV: Instance 3 started + +``` +## Troubleshooting + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/bluetooth/nimble/ble_multi_adv/main/CMakeLists.txt b/examples/bluetooth/nimble/ble_multi_adv/main/CMakeLists.txt new file mode 100644 index 00000000000..9e539a9fc04 --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/main/CMakeLists.txt @@ -0,0 +1,5 @@ +set(srcs "main.c" + "gatt_svr.c") + +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/nimble/ble_multi_adv/main/gatt_svr.c b/examples/bluetooth/nimble/ble_multi_adv/main/gatt_svr.c new file mode 100644 index 00000000000..a142dc9091b --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/main/gatt_svr.c @@ -0,0 +1,169 @@ +/* + * SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#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 "multi_adv.h" + +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 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, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_INDICATE, + .val_handle = &gatt_svr_chr_val_handle, + .descriptors = (struct ble_gatt_dsc_def[]) { + { + .uuid = &gatt_svr_dsc_uuid.u, + .att_flags = BLE_ATT_F_READ, + .access_cb = gatt_svc_access, + }, { + 0, /* No more descriptors in this characteristic */ + } + }, + }, { + 0, /* No more characteristics in this service. */ + } + }, + }, + + { + 0, /* No more services. */ + }, +}; + +/** + * 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) +{ + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: + MODLOG_DFLT(INFO, "Characteristic read; conn_handle=%d attr_handle=%d\n", + conn_handle, attr_handle); + return 0; + + case BLE_GATT_ACCESS_OP_WRITE_CHR: + MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d", + conn_handle, attr_handle); + return 0; + + case BLE_GATT_ACCESS_OP_READ_DSC: + MODLOG_DFLT(INFO, "Descriptor read; conn_handle=%d attr_handle=%d\n", + conn_handle, attr_handle); + return 0; + + 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(); + + 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; +} diff --git a/examples/bluetooth/nimble/ble_multi_adv/main/main.c b/examples/bluetooth/nimble/ble_multi_adv/main/main.c new file mode 100644 index 00000000000..aad9a116a3d --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/main/main.c @@ -0,0 +1,499 @@ +/* + * SPDX-FileCopyrightText: 2017-2023 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 "multi_adv.h" + +static const char *tag = "NimBLE_MULTI_ADV"; +static int ble_multi_adv_gap_event(struct ble_gap_event *event, void *arg); +static uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM; +struct ble_instance_cb_register ble_instance_cb[BLE_ADV_INSTANCES]; + +void ble_store_config_init(void); +static int ble_connectable_ext_cb(uint16_t instance); +static int ble_scannable_legacy_ext_cb(uint16_t instance); +static int ble_legacy_duration_cb(uint16_t instance); +static int ble_non_conn_ext_cb(uint16_t instance); +static void ble_multi_advertise(ble_addr_t addr); + +/* Advertising patterns */ +static uint8_t legacy_dur_adv_pattern[] = { + 0x02, 0x01, 0x06, + 0x03, 0x03, 0xab, 0xcd, + 0x03, 0x03, 0x18, 0x11, + 0x12, 0X09, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'l', 'e', 'g', 'a', 'c', 'y', '-', 'd', 'u', + 'r' +}; + +static uint8_t scannable_legacy_adv_pattern[] = { + 0x02, 0x01, 0x06, + 0x03, 0x03, 0xab, 0xcd, + 0x03, 0x03, 0x18, 0x11, + 0x13, 0X09, 'n', 'i', 'm', 'b', 'l', 'e', '-', 's', 'c', 'a', 'n', '-', 'l', 'e', 'g', 'a', + 'c', 'y' +}; + +static uint8_t connectable_adv_pattern[] = { + 0x02, 0x01, 0x06, + 0x03, 0x03, 0xab, 0xcd, + 0x03, 0x03, 0x18, 0x11, + 0x12, 0X09, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'c', 'o', 'n', 'n', 'e', 't', 'a', 'b', 'l', 'e' +}; + +static uint8_t non_conn_adv_pattern[] = { + 0x02, 0x01, 0x06, + 0x03, 0x03, 0xab, 0xcd, + 0x03, 0x03, 0x18, 0x11, + 0x10, 0X09, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'n', 'o', 'n', '-', 'c', 'o', 'n', 'n' +}; + +/** + * Logs information about a connection to the console. + */ +static void +ble_multi_adv_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); +} + +/* Generates and sets random address */ +static int +ble_multi_adv_set_addr(uint16_t instance) +{ + ble_addr_t addr; + int rc; + + /* generate new non-resolvable private address */ + rc = ble_hs_id_gen_rnd(1, &addr); + if (rc != 0) { + return rc; + } + + /* Set generated address */ + rc = ble_gap_ext_adv_set_addr(instance, &addr); + if (rc != 0) { + return rc; + } + + memcpy(&ble_instance_cb[instance].addr, &addr, sizeof(addr)); + return 0; +} + +/* Configures extended advertsing params*/ +static void +ble_multi_adv_conf_set_addr(uint16_t instance, struct ble_gap_ext_adv_params *params, + uint8_t *pattern, int size_pattern, int duration) +{ + int rc; + struct os_mbuf *data; + + if (ble_gap_ext_adv_active(instance)) { + ESP_LOGI(tag, "Instance already advertising"); + return; + } + + rc = ble_gap_ext_adv_configure(instance, params, NULL, + ble_multi_adv_gap_event, NULL); + assert (rc == 0); + + rc = ble_multi_adv_set_addr(instance); + assert (rc == 0); + + /* get mbuf for adv data */ + data = os_msys_get_pkthdr(size_pattern, 0); + assert(data); + + /* fill mbuf with adv data */ + rc = os_mbuf_append(data, pattern, size_pattern); + assert(rc == 0); + + rc = ble_gap_ext_adv_set_data(instance, data); + assert (rc == 0); + + /* start advertising */ + rc = ble_gap_ext_adv_start(instance, duration, 0); + assert (rc == 0); + + ESP_LOGI(tag, "Instance %d started", instance); +} + +/* Starts advertising instance with 5sec timeout */ +static void +start_legacy_duration(void) +{ + uint8_t instance = 3; + int duration = 500; /* 5seconds, 10ms units */ + struct ble_gap_ext_adv_params params; + int size_pattern = sizeof(legacy_dur_adv_pattern) / sizeof(legacy_dur_adv_pattern[0]); + + params.legacy_pdu = 1; + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + params.sid = 3; + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + + ble_multi_adv_conf_set_addr(instance, ¶ms, legacy_dur_adv_pattern, + size_pattern, duration); + + ble_instance_cb[instance].cb = &ble_legacy_duration_cb; +} + +/* Starts simple non-connectable scannable instance using legacy PUDs that + * runs forever + */ +static void +start_scannable_legacy_ext(void) +{ + uint8_t instance = 2; + struct ble_gap_ext_adv_params params; + int size_pattern = sizeof(scannable_legacy_adv_pattern)/sizeof(scannable_legacy_adv_pattern[0]); + + params.legacy_pdu = 1; + params.scannable = 1; + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + params.sid = 2; + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + + ble_multi_adv_conf_set_addr(instance, ¶ms, scannable_legacy_adv_pattern + , size_pattern, 0); + + ble_instance_cb[instance].cb = &ble_scannable_legacy_ext_cb; +} + +/* this is simple scannable instance that runs forever + * TODO Get scan request notifications. + */ +static void +start_connectable_ext(void) +{ + uint8_t instance = 1; + struct ble_gap_ext_adv_params params; + int size_pattern = sizeof(connectable_adv_pattern) / sizeof(connectable_adv_pattern[0]); + + params.connectable = 1; + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + params.sid = 1; + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_1M; + params.tx_power = 127; + + ble_multi_adv_conf_set_addr(instance, ¶ms, connectable_adv_pattern, + size_pattern, 0); + + ble_instance_cb[instance].cb = &ble_connectable_ext_cb; +} + +/* Starts a connectable instance */ +static void +start_non_connectable_ext(void) +{ + uint8_t instance = 0; + struct ble_gap_ext_adv_params params; + int size_pattern = sizeof(non_conn_adv_pattern) / sizeof(non_conn_adv_pattern[0]); + + params.own_addr_type = BLE_OWN_ADDR_RANDOM; + params.sid = 0; + params.primary_phy = BLE_HCI_LE_PHY_1M; + params.secondary_phy = BLE_HCI_LE_PHY_2M; + params.tx_power = 127; + + ble_multi_adv_conf_set_addr(instance, ¶ms, non_conn_adv_pattern, + size_pattern, 0); + + ble_instance_cb[instance].cb = &ble_non_conn_ext_cb; +} + +/* Callbacks */ +static int +ble_connectable_ext_cb(uint16_t instance) +{ + ESP_LOGI(tag, "In %s, instance = %d", __func__, instance); + return 0; +} + +static int +ble_scannable_legacy_ext_cb(uint16_t instance) +{ + ESP_LOGI(tag, "In %s, instance = %d", __func__, instance); + return 0; +} + +static int +ble_legacy_duration_cb(uint16_t instance) +{ + ESP_LOGI(tag, "In %s, instance = %d", __func__, instance); + return 0; +} + +static int +ble_non_conn_ext_cb(uint16_t instance) +{ + ESP_LOGI(tag, "In %s, instance = %d", __func__, instance); + return 0; +} + +/* Finds instance associated with the random address passed and calls its callback function */ +static void +ble_multi_advertise(ble_addr_t addr) +{ + for (int i = 0; i < BLE_ADV_INSTANCES; i++) { + if (memcmp(&addr, &ble_instance_cb[i].addr, sizeof(addr)) == 0) { + switch(i) { + case 0: + start_non_connectable_ext(); + break; + case 1: + start_connectable_ext(); + break; + case 2: + start_scannable_legacy_ext(); + break; + case 3: + start_legacy_duration(); + break; + default: + ESP_LOGI(tag, "Instance not found"); + } + } + } +} + +static void +ble_multi_perform_gatt_proc(ble_addr_t addr) +{ + /* GATT procedures like notify, indicate can be perfomed now */ + for (int i = 0; i < BLE_ADV_INSTANCES; i++) { + if (memcmp(&addr, &ble_instance_cb[i].addr, sizeof(addr)) == 0) { + if (ble_instance_cb[i].cb) { + ble_instance_cb[i].cb(i); + } + } + } + return; +} + +/** + * 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 +ble_multi_adv_gap_event(struct ble_gap_event *event, void *arg) +{ + struct ble_gap_conn_desc desc; + int rc; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* 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); + ble_multi_adv_print_conn_desc(&desc); + + ble_multi_perform_gatt_proc(desc.our_id_addr); + } + MODLOG_DFLT(INFO, "\n"); + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason); + ble_multi_adv_print_conn_desc(&event->disconnect.conn); + MODLOG_DFLT(INFO, "\n"); + + 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); + ble_multi_adv_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); + ble_multi_advertise(ble_instance_cb[event->adv_complete.instance].addr); + 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); + ble_multi_adv_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; + } + + return 0; +} + +static void +ble_multi_adv_on_reset(int reason) +{ + MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); +} + +static void +ble_multi_adv_on_sync(void) +{ + int rc; + + rc = ble_hs_util_ensure_addr(0); + 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; + } + + start_non_connectable_ext(); + + start_connectable_ext(); + + start_scannable_legacy_ext(); + + start_legacy_duration(); +} + +void ble_multi_adv_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 = ble_multi_adv_on_reset; + ble_hs_cfg.sync_cb = ble_multi_adv_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_bonding = 1; + ble_hs_cfg.sm_mitm = 1; + ble_hs_cfg.sm_sc = 1; + ble_hs_cfg.sm_sc = 0; + /* 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; + + /* Initialize callbacks to NULL */ + for (int i = 0; i < BLE_ADV_INSTANCES; i++) { + ble_instance_cb[i].cb = NULL; + } + + rc = gatt_svr_init(); + assert(rc == 0); + + /* Set the default device name. */ + rc = ble_svc_gap_device_name_set("nimble-multi-adv"); + assert(rc == 0); + + /* XXX Need to have template for store */ + ble_store_config_init(); + + nimble_port_freertos_init(ble_multi_adv_host_task); +} diff --git a/examples/bluetooth/nimble/ble_multi_adv/main/multi_adv.h b/examples/bluetooth/nimble/ble_multi_adv/main/multi_adv.h new file mode 100644 index 00000000000..4d8dce44743 --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/main/multi_adv.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef H_BLE_MULTI_ADV_ +#define H_BLE_MULTI_ADV_ + +#include +#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; + +typedef int ble_instance_cb_fn(uint16_t instance); + +struct ble_instance_cb_register { + ble_addr_t addr; + ble_instance_cb_fn *cb; +}; + +void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); +int gatt_svr_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults new file mode 100644 index 00000000000..bca207ac41c --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults @@ -0,0 +1,14 @@ +# Override some defaults so BT stack is enabled +# in this example + +# +# BT config +# +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n +CONFIG_BTDM_CTRL_MODE_BTDM=n +CONFIG_BT_BLUEDROID_ENABLED=n +CONFIG_BT_NIMBLE_ENABLED=y +CONFIG_BT_NIMBLE_EXT_ADV=y +CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=4 diff --git a/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c2 b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c2 new file mode 100644 index 00000000000..1da03222de5 --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c2 @@ -0,0 +1,7 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c2" +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y +CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 diff --git a/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c6 b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c6 new file mode 100644 index 00000000000..ad0515b3cce --- /dev/null +++ b/examples/bluetooth/nimble/ble_multi_adv/sdkconfig.defaults.esp32c6 @@ -0,0 +1,6 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y +CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70