From ecfceaf7b91668e265711f9441ed8fe839c91ca3 Mon Sep 17 00:00:00 2001 From: Chinmay Chhajed Date: Tue, 3 Nov 2020 15:08:09 +0530 Subject: [PATCH] NimBLE: example running BLE prph & ICMP echo reply simultaneously. This commit adds an example in NimBLE where esp32 runs a peripheral as well as a ping request to configured IP address. This example uses nimble host stack. Signed-off-by: Chinmay Chhajed --- examples/bluetooth/nimble/README.md | 7 + .../nimble/bleprph_wifi_coex/CMakeLists.txt | 6 + .../nimble/bleprph_wifi_coex/Makefile | 8 + .../nimble/bleprph_wifi_coex/README.md | 52 ++ .../bleprph_wifi_coex/main/CMakeLists.txt | 2 + .../bleprph_wifi_coex/main/Kconfig.projbuild | 32 + .../nimble/bleprph_wifi_coex/main/bleprph.h | 40 ++ .../bleprph_wifi_coex/main/component.mk | 4 + .../nimble/bleprph_wifi_coex/main/gatt_svr.c | 212 +++++++ .../nimble/bleprph_wifi_coex/main/main.c | 567 ++++++++++++++++++ .../bleprph_wifi_coex/sdkconfig.defaults | 13 + 11 files changed, 943 insertions(+) create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/CMakeLists.txt create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/Makefile create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/README.md create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/main/CMakeLists.txt create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/main/Kconfig.projbuild create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/main/bleprph.h create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/main/component.mk create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/main/gatt_svr.c create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c create mode 100644 examples/bluetooth/nimble/bleprph_wifi_coex/sdkconfig.defaults diff --git a/examples/bluetooth/nimble/README.md b/examples/bluetooth/nimble/README.md index d77a2cfe7f..37eadd420d 100644 --- a/examples/bluetooth/nimble/README.md +++ b/examples/bluetooth/nimble/README.md @@ -12,6 +12,13 @@ Shows how ESP32 acts as a BLE Peripheral. See the [README.md](./bleprph/README.md) file in the example [bleprph](./bleprph/). +## bleprph_wifi_coex + + +ESP32 acts as a BLE Peripheral and simultaneously performs WiFi ping to configured IP address. + +See the [README.md](./bleprph_wifi_coex/README.md) file in the example [bleprph_wifi_coex](./bleprph_wifi_coex/). + ## blecent Shows how ESP32 acts as a BLE central. diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/CMakeLists.txt b/examples/bluetooth/nimble/bleprph_wifi_coex/CMakeLists.txt new file mode 100644 index 0000000000..cc3037e2a0 --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(bleprph_wifi_coex) diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/Makefile b/examples/bluetooth/nimble/bleprph_wifi_coex/Makefile new file mode 100644 index 0000000000..aa79c71963 --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := bleprph_wifi_coex + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/README.md b/examples/bluetooth/nimble/bleprph_wifi_coex/README.md new file mode 100644 index 0000000000..ba5a551af1 --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/README.md @@ -0,0 +1,52 @@ +| Supported Targets | ESP32 | +| ----------------- | ----- | + +# BLE peripheral with ICMP Echo-Reply. + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example aims to run `ping` network utility along with BLE GATT server simultaneously using NimBLE host stack. It is a combination of 2 examples in IDF: `bluetooth/nimble/bleprph` and `protocols/icmp_echo`. See the README.md files of these examples to know more about them. + +### BLE peripheral + +This example creates GATT server and then starts advertising, waiting to be connected to a GATT client. + +It uses ESP32's Bluetooth controller and NimBLE stack based BLE host. + +### ICMP Echo-Reply + +Ping is a useful network utility used to test if a remote host is reachable on the IP network. It measures the round-trip time for messages sent from the source host to a destination target that are echoed back to the source. + +Ping operates by sending Internet Control Message Protocol (ICMP) echo request packets to the target host and waiting for an ICMP echo reply. + +**Notes:** Currently this example only supports IPv4. + + +## How to use example + +### Configure the project + +``` +idf.py menuconfig +``` +* Enter SSID and password of known WiFi AP with connectivity to internet. + +* Enter desired ping IP Address. Default is set to `93.184.216.34` ( This is the IP address of https://example.com ). + +* Enter other related parameters like count of ping and maximum numbers of retry. + +## Testing + +To test this demo, any BLE scanner app and a WiFi access point with internet connectivity can be used. + +### 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 +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/main/CMakeLists.txt b/examples/bluetooth/nimble/bleprph_wifi_coex/main/CMakeLists.txt new file mode 100644 index 0000000000..e8a76d0b09 --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" "gatt_svr.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/main/Kconfig.projbuild b/examples/bluetooth/nimble/bleprph_wifi_coex/main/Kconfig.projbuild new file mode 100644 index 0000000000..b79d6bcdd9 --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/main/Kconfig.projbuild @@ -0,0 +1,32 @@ +menu "Example Configuration" + + config EXAMPLE_ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config EXAMPLE_ESP_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + config EXAMPLE_ESP_MAXIMUM_RETRY + int "WIFI Connect Maximum retry" + default 5 + help + Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. + + config EXAMPLE_ESP_PING_IP + string "Ping IP Address" + default "93.184.216.34" + help + Set IP address to ping. + + config EXAMPLE_ESP_PING_COUNT + int "Count of pings" + default 10000 + help + Set number of pings to be sent. +endmenu diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/main/bleprph.h b/examples/bluetooth/nimble/bleprph_wifi_coex/main/bleprph.h new file mode 100644 index 0000000000..e6b6962797 --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/main/bleprph.h @@ -0,0 +1,40 @@ +// Copyright 2015-2020 The Apache Software Foundation +// Modifications Copyright 2017-2020 Espressif Systems (Shanghai) CO., LTD. +// +// Portions of this software were developed at Runtime Inc, copyright 2015. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef H_BLEPRPH_ +#define H_BLEPRPH_ + +#include +#include "nimble/ble.h" +#include "modlog/modlog.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct ble_hs_cfg; +struct ble_gatt_register_ctxt; + +/** GATT server. */ +#define GATT_SVR_SVC_ALERT_UUID 0x1811 + +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/bleprph_wifi_coex/main/component.mk b/examples/bluetooth/nimble/bleprph_wifi_coex/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/main/gatt_svr.c b/examples/bluetooth/nimble/bleprph_wifi_coex/main/gatt_svr.c new file mode 100644 index 0000000000..c77ff7156c --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/main/gatt_svr.c @@ -0,0 +1,212 @@ +// Copyright 2015-2020 The Apache Software Foundation +// Modifications Copyright 2017-2020 Espressif Systems (Shanghai) CO., LTD. +// +// Portions of this software were developed at Runtime Inc, copyright 2015. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#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 "bleprph.h" +#include "services/ans/ble_svc_ans.h" +#include "esp_log.h" + + +/** + * The vendor specific security test service consists of two characteristics: + * o random-number-generator: generates a random 32-bit number each time + * it is read. + * o static-value: a single-byte characteristic that can always be read. + */ + +/* 59462f12-9543-9999-12c8-58b459a2712d */ +static const ble_uuid128_t gatt_svr_svc_sec_test_uuid = + BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12, + 0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59); + +/* 5c3a659e-897e-45e1-b016-007107c96df6 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid = + BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +/* 5c3a659e-897e-45e1-b016-007107c96df7 */ +static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid = + BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0, + 0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c); + +static const char* TAG = "wifi_prph_coex"; + +static uint8_t gatt_svr_sec_test_static_val; + +static int +gatt_svr_chr_access_sec_test(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: Security test. */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &gatt_svr_svc_sec_test_uuid.u, + .characteristics = (struct ble_gatt_chr_def[]) + { { + /*** Characteristic: Random number generator. */ + .uuid = &gatt_svr_chr_sec_test_rand_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ + }, { + /*** Characteristic: Static value. */ + .uuid = &gatt_svr_chr_sec_test_static_uuid.u, + .access_cb = gatt_svr_chr_access_sec_test, + .flags = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE + }, { + 0, /* No more characteristics in this service. */ + } + }, + }, + + { + 0, /* No more services. */ + }, +}; + +static int +gatt_svr_chr_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; +} + +static int +gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rand_num; + int rc; + + uuid = ctxt->chr->uuid; + + /* Determine which characteristic is being accessed by examining its + * 128-bit UUID. + */ + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) { + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + + /* Respond with a 32-bit random number. */ + rand_num = rand(); + rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0) { + switch (ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: + rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + + case BLE_GATT_ACCESS_OP_WRITE_CHR: + rc = gatt_svr_chr_write(ctxt->om, + sizeof gatt_svr_sec_test_static_val, + sizeof gatt_svr_sec_test_static_val, + &gatt_svr_sec_test_static_val, NULL); + return rc; + + default: + assert(0); + return BLE_ATT_ERR_UNLIKELY; + } + } + + /* Unknown characteristic; the nimble stack 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: + ESP_LOGD(TAG, "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: + ESP_LOGD(TAG, "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: + ESP_LOGD(TAG, "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; + } + + return 0; +} diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c b/examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c new file mode 100644 index 0000000000..90807fb820 --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c @@ -0,0 +1,567 @@ +// Copyright 2015-2020 The Apache Software Foundation +// Modifications Copyright 2017-2020 Espressif Systems (Shanghai) CO., LTD. +// +// Portions of this software were developed at Runtime Inc, copyright 2015. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_log.h" +#include "nvs_flash.h" +/* BLE */ +#include "esp_nimble_hci.h" +#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" + +/* WIFI */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "ping/ping_sock.h" + +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/inet.h" +#include "lwip/netdb.h" +#include "lwip/sockets.h" + +#define EXAMPLE_ESP_WIFI_SSID CONFIG_EXAMPLE_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_EXAMPLE_ESP_WIFI_PASSWORD +#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_EXAMPLE_ESP_MAXIMUM_RETRY +#define EXAMPLE_PING_IP CONFIG_EXAMPLE_ESP_PING_IP +#define EXAMPLE_PING_COUNT CONFIG_EXAMPLE_ESP_PING_COUNT +#define EXAMPLE_PING_INTERVAL 1 + +static int bleprph_gap_event(struct ble_gap_event *event, void *arg); +static uint8_t own_addr_type; + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_wifi_event_group; + +/* The event group allows multiple bits for each event, but we only care about two events: + * - we are connected to the AP with an IP + * - we failed to connect after the maximum amount of retries */ +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +static const char *TAG = "wifi_prph_coex"; + +static int s_retry_num = 0; + +static void event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG, "retry to connect to the AP"); + } else { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + ESP_LOGI(TAG,"connect to the AP fail"); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +void wifi_init_sta(void) +{ + s_wifi_event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_netif_init()); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &event_handler, + NULL, + &instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, + IP_EVENT_STA_GOT_IP, + &event_handler, + NULL, + &instance_got_ip)); + + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_ESP_WIFI_SSID, + .password = EXAMPLE_ESP_WIFI_PASS, + /* Setting a password implies station will connect to all security modes including WEP/WPA. + * However these modes are deprecated and not advisable to be used. Incase your Access point + * doesn't support WPA2, these mode can be enabled by commenting below line */ + .threshold.authmode = WIFI_AUTH_WPA2_PSK, + + .pmf_cfg = { + .capable = true, + .required = false + }, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK(esp_wifi_start() ); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + + /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, + pdFALSE, + portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else if (bits & WIFI_FAIL_BIT) { + ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else { + ESP_LOGE(TAG, "UNEXPECTED EVENT"); + } + + /* The event will not be processed after unregister */ + ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); + ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); + vEventGroupDelete(s_wifi_event_group); +} + +static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) +{ + uint8_t ttl; + uint16_t seqno; + uint32_t elapsed_time, recv_len; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len)); + esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); + printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n", + recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time); +} + +static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) +{ + uint16_t seqno; + ip_addr_t target_addr; + esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno); +} + + +static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) +{ + ip_addr_t target_addr; + uint32_t transmitted; + uint32_t received; + uint32_t total_time_ms; + esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted)); + esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received)); + esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); + esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms)); + uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100); + if (IP_IS_V4(&target_addr)) { + printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr))); + } else { + printf("\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr))); + } + printf("%d packets transmitted, %d received, %d%% packet loss, time %dms\n", + transmitted, received, loss, total_time_ms); + // delete the ping sessions, so that we clean up all resources and can create a new ping session + // we don't have to call delete function in the callback, instead we can call delete function from other tasks + esp_ping_delete_session(hdl); +} + +static int do_ping_cmd(void) +{ + esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG(); + static esp_ping_handle_t ping; + + config.interval_ms = (uint32_t)(EXAMPLE_PING_INTERVAL * 1000); + config.count = (uint32_t)(EXAMPLE_PING_COUNT); + + // parse IP address + ip_addr_t target_addr; + struct addrinfo hint; + struct addrinfo *res = NULL; + memset(&hint, 0, sizeof(hint)); + memset(&target_addr, 0, sizeof(target_addr)); + + /* convert domain name to IP address */ + if (getaddrinfo(EXAMPLE_PING_IP, NULL, &hint, &res) != 0) { + printf("ping: unknown host %s\n", EXAMPLE_PING_IP); + return 1; + } + if (res->ai_family == AF_INET) { + struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr; + inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4); + } else { + struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr; + inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6); + } + freeaddrinfo(res); + config.target_addr = target_addr; + + /* set callback functions */ + esp_ping_callbacks_t cbs = { + .on_ping_success = cmd_ping_on_ping_success, + .on_ping_timeout = cmd_ping_on_ping_timeout, + .on_ping_end = cmd_ping_on_ping_end, + .cb_args = NULL + }; + + esp_ping_new_session(&config, &cbs, &ping); + esp_ping_start(ping); + + return 0; +} + +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) +{ + ESP_LOGI(TAG, "handle=%d our_ota_addr_type=%d our_ota_addr=%02x:%02x:%02x:%02x:%02x:%02x", + desc->conn_handle, desc->our_ota_addr.type, + desc->our_ota_addr.val[5], + desc->our_ota_addr.val[4], + desc->our_ota_addr.val[3], + desc->our_ota_addr.val[2], + desc->our_ota_addr.val[1], + desc->our_ota_addr.val[0]); + + ESP_LOGI(TAG, "our_id_addr_type=%d our_id_addr=%02x:%02x:%02x:%02x:%02x:%02x", + desc->our_id_addr.type, + desc->our_id_addr.val[5], + desc->our_id_addr.val[4], + desc->our_id_addr.val[3], + desc->our_id_addr.val[2], + desc->our_id_addr.val[1], + desc->our_id_addr.val[0]); + + ESP_LOGI(TAG, "peer_ota_addr_type=%d peer_ota_addr=%02x:%02x:%02x:%02x:%02x:%02x", + desc->peer_ota_addr.type, + desc->peer_ota_addr.val[5], + desc->peer_ota_addr.val[4], + desc->peer_ota_addr.val[3], + desc->peer_ota_addr.val[2], + desc->peer_ota_addr.val[1], + desc->peer_ota_addr.val[0]); + + ESP_LOGI(TAG, "peer_id_addr_type=%d peer_id_addr=%02x:%02x:%02x:%02x:%02x:%02x", + desc->peer_id_addr.type, + desc->peer_id_addr.val[5], + desc->peer_id_addr.val[4], + desc->peer_id_addr.val[3], + desc->peer_id_addr.val[2], + desc->peer_id_addr.val[1], + desc->peer_id_addr.val[0]); + + ESP_LOGI(TAG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d " + "encrypted=%d authenticated=%d bonded=%d", + desc->conn_itvl, desc->conn_latency, + desc->supervision_timeout, + desc->sec_state.encrypted, + desc->sec_state.authenticated, + desc->sec_state.bonded); +} + +/** + * 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) { + ESP_LOGE(TAG, "error setting advertisement data; rc=%d", 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) { + ESP_LOGE(TAG, "error enabling advertisement; rc=%d", rc); + 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 +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_CONNECT: + /* A new connection was established or a connection attempt failed. */ + ESP_LOGI(TAG, "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); + } + + if (event->connect.status != 0) { + /* Connection failed; resume advertising. */ + bleprph_advertise(); + } + return 0; + + case BLE_GAP_EVENT_DISCONNECT: + ESP_LOGI(TAG, "disconnect; reason=%d ", event->disconnect.reason); + bleprph_print_conn_desc(&event->disconnect.conn); + + /* Connection terminated; resume advertising. */ + bleprph_advertise(); + return 0; + + case BLE_GAP_EVENT_CONN_UPDATE: + /* The central has updated the connection parameters. */ + ESP_LOGI(TAG, "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); + return 0; + + case BLE_GAP_EVENT_ADV_COMPLETE: + ESP_LOGI(TAG, "advertise complete; reason=%d", + event->adv_complete.reason); + bleprph_advertise(); + return 0; + + case BLE_GAP_EVENT_ENC_CHANGE: + /* Encryption has been enabled or disabled for this connection. */ + ESP_LOGI(TAG, "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); + return 0; + + case BLE_GAP_EVENT_SUBSCRIBE: + ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d " + "reason=%d prevn=%d curn=%d previ=%d curi=%d", + 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: + ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d", + 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; + } + + return 0; +} + +static void +bleprph_on_reset(int reason) +{ + ESP_LOGE(TAG, "Resetting state; reason=%d", reason); +} + +static void +bleprph_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) { + ESP_LOGE(TAG, "error determining address type; rc=%d", rc); + return; + } + + /* Printing ADDR */ + uint8_t addr_val[6] = {0}; + rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL); + + ESP_LOGI(TAG, "Device Address:%02x:%02x:%02x:%02x:%02x:%02x", + addr_val[5], + addr_val[4], + addr_val[3], + addr_val[2], + addr_val[1], + addr_val[0]); + /* Begin advertising. */ + bleprph_advertise(); +} + +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); + + ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); + wifi_init_sta(); + do_ping_cmd(); + ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init()); + + nimble_port_init(); + /* 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; + + 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); +} diff --git a/examples/bluetooth/nimble/bleprph_wifi_coex/sdkconfig.defaults b/examples/bluetooth/nimble/bleprph_wifi_coex/sdkconfig.defaults new file mode 100644 index 0000000000..9ba94e31e9 --- /dev/null +++ b/examples/bluetooth/nimble/bleprph_wifi_coex/sdkconfig.defaults @@ -0,0 +1,13 @@ +# 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 + +# Disable backward compatible interface to tcpip_adapter. Use only esp-netif interface. +CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=n