From 3b1bdce81640e853d368c238979041d73767cc70 Mon Sep 17 00:00:00 2001 From: liqigan Date: Thu, 23 Dec 2021 17:23:04 +0800 Subject: [PATCH 1/2] optimize SPP documents --- .../classic_bt/bt_spp_acceptor/README.md | 38 ++-- .../bt_spp_acceptor/main/CMakeLists.txt | 2 +- .../{example_spp_acceptor_demo.c => main.c} | 63 +++++-- .../classic_bt/bt_spp_initiator/README.md | 31 ++-- .../bt_spp_initiator/main/CMakeLists.txt | 2 +- .../{example_spp_initiator_demo.c => main.c} | 167 +++++++++++++----- .../classic_bt/bt_spp_vfs_acceptor/README.md | 59 +++++-- .../bt_spp_vfs_acceptor/main/CMakeLists.txt | 2 +- ...example_spp_vfs_acceptor_demo.c => main.c} | 75 ++++++-- .../classic_bt/bt_spp_vfs_initiator/README.md | 51 ++++-- .../bt_spp_vfs_initiator/main/CMakeLists.txt | 2 +- ...xample_spp_vfs_initiator_demo.c => main.c} | 93 ++++++++-- 12 files changed, 449 insertions(+), 136 deletions(-) rename examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/{example_spp_acceptor_demo.c => main.c} (72%) rename examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/{example_spp_initiator_demo.c => main.c} (63%) rename examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/{example_spp_vfs_acceptor_demo.c => main.c} (73%) rename examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/{example_spp_vfs_initiator_demo.c => main.c} (74%) diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/README.md index aef56168b9..340ed259c9 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/README.md @@ -3,22 +3,37 @@ ## ESP-IDF BT-SPP-ACCEPTOR demo -This is the demo for user to use ESP_APIs to create a **Serial Port Protocol** (**SPP**) acceptor and we aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. +This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP acceptor which performs as a server. We aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. We also provide the demo `bt_spp_initiator` or the demo `bt_spp_vfs_initiator` to create an SPP initiator which performs as a client. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. ## How to use example ### Hardware Required -This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. - -To operate it should be connected to an SPP Initiator running on a smartphone or on another ESP32 development board. +This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. To operate the example, it should be connected to an SPP Initiator running on a smartphone, a computer or on another ESP32 development board. ### Configure the project +1. Open the project configuration menu: + ``` idf.py menuconfig ``` +2. Enable the SPP functionality by choosing the path as following: + +`Component config --> Bluetooth --> Bluedroid Options --> SPP` + +3. If you want to limit the number of connected devices, please make sure set the `BT/BLE MAX ACL CONNECTIONS` and `BR/EDR ACL Max Connections` with same value you want. + +`Component config --> Bluetooth --> Bluedroid Options --> BT/BLE MAX ACL CONNECTIONS(1~7)` +and +`Component config --> Bluetooth --> Bluetooth --> Bluetooth controller --> BR/EDR ACL Max Connections` + + +4. SSP is enabled as default in this example. If you prefer the legacy pairing, you can disable it in the following path. + +`Component config --> Bluetooth--> Bluedroid Options --> Secure Simple Pair`. + ### Build and Flash Build the project and flash it to the board, then run monitor tool to view serial output: @@ -35,16 +50,19 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l ## Example Description -After the program started, `bt_spp_initator` will connect it and send data. +After the program starts, the example will start an SPP acceptor. The example will calculate the data rate or just print the received data after the SPP connection is established. You can connect to the server and send data with another ESP32 development board, Andriod phone or computer which performs as the SPP initiator. ## Trouble shooting -- Acceptor is expected to start before `bt_spp_initator` start. - -- To see the information of data, users shall set `SPP_SHOW_MODE` as `SPP_SHOW_DATA` or `SPP_SHOW_SPEED` in code(should be same with `bt_spp_initator`). - -- We also show the Security Simple Pair in this SPP demo. Users can set the IO Capability and Security Mode for their device (security level is fixed level 4). The default security mode of this demo is `ESP_SPP_SEC_AUTHENTICATE` which support MITM (Man In The Middle) protection. For more information about Security Simple Pair on ESP32, please refer to [ESP32_SSP](./ESP32_SSP.md). +- To see the information of data, users shall set `SPP_SHOW_MODE` as `SPP_SHOW_DATA` or `SPP_SHOW_SPEED` in code(should be same with `bt_spp_initiator`, if the peer device runs it). When setting `SPP_SHOW_MODE` as `SPP_SHOW_DATA`, if the data rate is too high or the data length is too long, it is strongly recommended to process them in other lower priority application task rather than in this callback directly. Since the printing takes too much time, and it may stuck the Bluetooth stack. ## FAQ +Q: How to change the process of SSP? +A: Users can set the IO Capability and Security Mask for their device (fixed Security Mode, Security Mode 4). In short, the Security Mask sets the security level for authentication stage and the IO Capability determines the way of user interaction during pairing. The default Security Mask of this demo is `ESP_SPP_SEC_AUTHENTICATE` which support MITM (Man In The Middle) protection. For more information about Security Simple Pair on ESP32, please refer to [ESP32_SSP](./ESP32_SSP.md). + + Q: How many SPP servers does ESP32 support? A: For now, the maximum number of SPP servers is 6, which is limited by the maximum number of SDP records. When the SPP server is successfully started, the unique SCN (Server Channel Number) will be mapped to the SPP server. + +Q: Is SPP absolutely reliable? +A: For now, most Bluetooth stacks implement the SPP based on the L2CAP Basic Mode, and the reliability only depends on the controller. If errors(e.g. CRC) are missed by controller, the L2CAP packet will not be retransmitted, so it is recommended that you should implement the retransmission at the application layer. For more information about L2CAP operation modes, please refer to Bluetooth Core Specification, Version 4.2 or later, Volume 3, Part A: Logical Link Control and Adaptation Protocol Specification. \ No newline at end of file diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/CMakeLists.txt index e75017250b..cf2c455cb5 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/CMakeLists.txt +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/CMakeLists.txt @@ -1,2 +1,2 @@ -idf_component_register(SRCS "example_spp_acceptor_demo.c" +idf_component_register(SRCS "main.c" INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/example_spp_acceptor_demo.c b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/main.c similarity index 72% rename from examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/example_spp_acceptor_demo.c rename to examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/main.c index f68ceb2540..4b7582dd21 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/example_spp_acceptor_demo.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/main.c @@ -39,6 +39,18 @@ static long data_num = 0; static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_AUTHENTICATE; static const esp_spp_role_t role_slave = ESP_SPP_ROLE_SLAVE; +static char *bda2str(uint8_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 void print_speed(void) { float time_old_s = time_old.tv_sec + time_old.tv_usec / 1000000.0; @@ -53,10 +65,16 @@ static void print_speed(void) static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { + char bda_str[18] = {0}; + switch (event) { case ESP_SPP_INIT_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); - esp_spp_start_srv(sec_mask,role_slave, 0, SPP_SERVER_NAME); + if (param->init.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); + esp_spp_start_srv(sec_mask, role_slave, 0, SPP_SERVER_NAME); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_INIT_EVT status:%d", param->init.status); + } break; case ESP_SPP_DISCOVERY_COMP_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT"); @@ -65,21 +83,35 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT"); break; case ESP_SPP_CLOSE_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT"); + ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT status:%d handle:%d close_by_remote:%d", param->close.status, + param->close.handle, param->close.async); break; case ESP_SPP_START_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT"); - esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); - esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + if (param->start.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT handle:%d sec_id:%d scn:%d", param->start.handle, param->start.sec_id, + param->start.scn); + esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_START_EVT status:%d", param->start.status); + } break; case ESP_SPP_CL_INIT_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT"); break; case ESP_SPP_DATA_IND_EVT: #if (SPP_SHOW_MODE == SPP_SHOW_DATA) - ESP_LOGI(SPP_TAG, "ESP_SPP_DATA_IND_EVT len=%d handle=%d", + /* + * We only show the data in which the data length is less than 128 here. If you want to print the data and + * the data rate is high, it is strongly recommended to process them in other lower priority application task + * rather than in this callback directly. Since the printing takes too much time, it may stuck the Bluetooth + * stack and also have a effect on the throughput! + */ + ESP_LOGI(SPP_TAG, "ESP_SPP_DATA_IND_EVT len:%d handle:%d", param->data_ind.len, param->data_ind.handle); - esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); + if (param->data_ind.len < 128) { + esp_log_buffer_hex("", param->data_ind.data, param->data_ind.len); + } #else gettimeofday(&time_new, NULL); data_num += param->data_ind.len; @@ -95,7 +127,8 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) ESP_LOGI(SPP_TAG, "ESP_SPP_WRITE_EVT"); break; case ESP_SPP_SRV_OPEN_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT"); + ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT status:%d handle:%d, rem_bda:[%s]", param->srv_open.status, + param->srv_open.handle, bda2str(param->srv_open.rem_bda, bda_str, sizeof(bda_str))); gettimeofday(&time_old, NULL); break; case ESP_SPP_SRV_STOP_EVT: @@ -111,11 +144,13 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) { + char bda_str[18] = {0}; + switch (event) { case ESP_BT_GAP_AUTH_CMPL_EVT:{ if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { - ESP_LOGI(SPP_TAG, "authentication success: %s", param->auth_cmpl.device_name); - esp_log_buffer_hex(SPP_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN); + ESP_LOGI(SPP_TAG, "authentication success: %s bda:[%s]", param->auth_cmpl.device_name, + bda2str(param->auth_cmpl.bda, bda_str, sizeof(bda_str))); } else { ESP_LOGE(SPP_TAG, "authentication failed, status:%d", param->auth_cmpl.stat); } @@ -153,7 +188,8 @@ void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) #endif case ESP_BT_GAP_MODE_CHG_EVT: - ESP_LOGI(SPP_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode); + ESP_LOGI(SPP_TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d bda:[%s]", param->mode_chg.mode, + bda2str(param->mode_chg.bda, bda_str, sizeof(bda_str))); break; default: { @@ -166,6 +202,7 @@ void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) void app_main(void) { + char bda_str[18] = {0}; 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()); @@ -225,4 +262,6 @@ void app_main(void) 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(SPP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str))); } diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/README.md index 1a3a509436..544c2a1dfc 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/README.md @@ -3,21 +3,28 @@ # ESP-IDF BT-SPP-INITATOR demo -This is the demo for user to use ESP_APIs to create a **Serial Port Protocol** (**SPP**) initiator and we aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. +This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP initiator which performs as a client. we aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. We also provide the demo `bt_spp_acceptor` or the demo `bt_spp_vfs_acceptor` to create an SPP acceptor which performs as a server. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. ## How to use example ### Hardware Required -This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. To operate it should be connected to an SPP Acceptor running on another device. +This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. To operate the example, you should be connect to an SPP acceptor running on a smartphone, a computer or on another ESP32 development board. ### Configure the project - +1. Open the project configuration menu: ``` idf.py menuconfig ``` -In `menuconfig` path: `Coponent config --> Bluetooth--> Bluedroid Options -->SPP` and `Coponent config --> Bluetooth--> Bluedroid Options -->Secure Simple Pair`. +2. Enable the SPP functionality by choosing the path as following: + +`Component config --> Bluetooth --> Bluedroid Options --> SPP`. + +3. SSP is enabled as default in this example. If you prefer the legacy pairing, you can disable it in the following path. + +`Component config --> Bluetooth--> Bluedroid Options --> Secure Simple Pair`. + ### Build and Flash @@ -33,6 +40,9 @@ idf.py -p PORT flash monitor See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. +## Example Description + +After the program starts, the example will initiate a Bluetooth discovery procedure and filter out the peer device by the name in the EIR(Extended Inquiry Response). After discovering the SPP service, it will connect to the SPP acceptor and send data. The example will calculate the data rate or print the sent data after the SPP connection is established. ### Example Output When you run this example and the IO capability is `ESP_IO_CAP_IO` or `ESP_IO_CAP_IN` , the commands help table prints the following at the very beginning: @@ -98,14 +108,10 @@ Whether you should passkey or confirm the number also depends on the IO capabili ## Troubleshouting -- Set `SPP_SHOW_MODE` as `SPP_SHOW_DATA` or `SPP_SHOW_SPEED` in code (should be same with bt_spp_acceptor). - -- After the program started, It will connect to bt_spp_acceptor and send data. +- Set `SPP_SHOW_MODE` as `SPP_SHOW_DATA` or `SPP_SHOW_SPEED` in code (should be same with `bt_spp_acceptor`, if the peer device runs it). When setting `SPP_SHOW_MODE` as `SPP_SHOW_DATA`, if the data rate is too high or the data length is too long, it is strongly recommended to process them in other lower priority application task rather than in this callback directly. Since the printing takes too much time, and it may stuck the Bluetooth stack. - We haven't do the same update to `bt_acceptor_demo` for the sake of reducing the size of ESP_IDF, but transplanting of input module is supported. -- We also show the Security Simple Pair in this SPP demo. Users can set the IO Capability and Security Mode for their device (security level is fixed level 4). The default security mode of this demo is `ESP_SPP_SEC_AUTHENTICATE` which support MITM (Man In The Middle) protection. For more information about Security Simple Pair on ESP32, please refer to [ESP32_SSP](../bt_spp_acceptor/ESP32_SSP.md). - ## Example Breakdown To clearly show how the SSP aggregate with the SPP , we use the Commands and Effects scheme to illustrate the process of secure paring and connection establishment. @@ -116,11 +122,10 @@ To clearly show how the SSP aggregate with the SPP , we use the Commands and Eff - If you want to update the command parse rules, please refer to `app_spp_msg_prs.c`. -- If you want to update the responses of HF Unit or want to update the log, please refer to `bt_app_spp.c`. - -- Task configuration part is in `example_spp_initiator_demo.c`. - ## FAQ +Q: How to change the process of SSP? +A: Users can set the IO Capability and Security Mask for their device (fixed Security Mode, Security Mode 4). In short, the Security Mask sets the security level for authentication stage and the IO Capability determines the way of user interaction during pairing. The default Security Mask of this demo is `ESP_SPP_SEC_AUTHENTICATE` which support MITM (Man In The Middle) protection. For more information about Security Simple Pair on ESP32, please refer to [ESP32_SSP](../bt_spp_acceptor/ESP32_SSP.md). + Q: How can we reach the maximum throughput when using SPP? A: The default MTU size of classic Bluetooth SPP on ESP32 is 990 bytes, and higher throughput can be achieved in the case that data chunck size is close to the MTU size or multiple of MTU size. For example, sending 100 bytes data per second is much better than sending 10 bytes every 100 milliseconds. diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/CMakeLists.txt index 5881e2e9bb..a9c07d5c0c 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/CMakeLists.txt +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "example_spp_initiator_demo.c" +idf_component_register(SRCS "main.c" SRCS "app_spp_msg_prs.c" SRCS "app_spp_msg_set.c" SRCS "console_uart.c" diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/example_spp_initiator_demo.c b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/main.c similarity index 63% rename from examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/example_spp_initiator_demo.c rename to examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/main.c index dab35f4c94..3b337878b4 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/example_spp_initiator_demo.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/main.c @@ -54,6 +54,19 @@ static const uint8_t inq_num_rsps = 0; #define SPP_DATA_LEN ESP_SPP_MAX_MTU #endif static uint8_t spp_data[SPP_DATA_LEN]; +static uint8_t *s_p_data = NULL; /* data pointer of spp_data */ + +static char *bda2str(uint8_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 void print_speed(void) { @@ -101,58 +114,123 @@ static bool get_name_from_eir(uint8_t *eir, char *bdname, uint8_t *bdname_len) static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { + uint8_t i = 0; + char bda_str[18] = {0}; + switch (event) { case ESP_SPP_INIT_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); - esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); - esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); - esp_bt_gap_start_discovery(inq_mode, inq_len, inq_num_rsps); - + if (param->init.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); + esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + esp_bt_gap_start_discovery(inq_mode, inq_len, inq_num_rsps); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_INIT_EVT status:%d", param->init.status); + } break; case ESP_SPP_DISCOVERY_COMP_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT status=%d scn_num=%d",param->disc_comp.status, param->disc_comp.scn_num); if (param->disc_comp.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT scn_num:%d", param->disc_comp.scn_num); + for (i = 0; i < param->disc_comp.scn_num; i++) { + ESP_LOGI(SPP_TAG, "-- [%d] scn:%d service_name:%s", i, param->disc_comp.scn[i], + param->disc_comp.service_name[i]); + } + /* We only connect to the first found server on the remote SPP acceptor here */ esp_spp_connect(sec_mask, role_master, param->disc_comp.scn[0], peer_bd_addr); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT status=%d", param->disc_comp.status); } break; case ESP_SPP_OPEN_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT"); - esp_spp_write(param->open.handle, SPP_DATA_LEN, spp_data); - gettimeofday(&time_old, NULL); + if (param->open.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT handle:%d rem_bda:[%s]", param->open.handle, + bda2str(param->open.rem_bda, bda_str, sizeof(bda_str))); + /* Start to write the first data packet */ + esp_spp_write(param->open.handle, SPP_DATA_LEN, spp_data); + s_p_data = spp_data; + gettimeofday(&time_old, NULL); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_OPEN_EVT status:%d", param->open.status); + } break; case ESP_SPP_CLOSE_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT"); + ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT status:%d handle:%d close_by_remote:%d", param->close.status, + param->close.handle, param->close.async); break; case ESP_SPP_START_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT"); break; case ESP_SPP_CL_INIT_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT"); + if (param->cl_init.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT handle:%d sec_id:%d", param->cl_init.handle, param->cl_init.sec_id); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_CL_INIT_EVT status:%d", param->cl_init.status); + } break; case ESP_SPP_DATA_IND_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_DATA_IND_EVT"); break; + case ESP_SPP_WRITE_EVT: + if (param->write.status == ESP_SPP_SUCCESS) { + if (s_p_data + param->write.len == spp_data + SPP_DATA_LEN) { + /* Means the previous data packet be sent completely, send a new data packet */ + s_p_data = spp_data; + } else { + /* + * Means the previous data packet only be sent partially due to the lower layer congestion, resend the + * remainning data. + */ + s_p_data += param->write.len; + } +#if (SPP_SHOW_MODE == SPP_SHOW_DATA) + /* + * We only show the data in which the data length is less than 128 here. If you want to print the data and + * the data rate is high, it is strongly recommended to process them in other lower priority application task + * rather than in this callback directly. Since the printing takes too much time, it may stuck the Bluetooth + * stack and also have a effect on the throughput! + */ + ESP_LOGI(SPP_TAG, "ESP_SPP_WRITE_EVT len:%d handle:%d cong:%d", param->write.len, param->write.handle, + param->write.cong); + if (param->write.len < 128) { + esp_log_buffer_hex("", spp_data, param->write.len); + /* Delay a little to avoid the task watch dog */ + vTaskDelay(pdMS_TO_TICKS(10)); + } +#else + gettimeofday(&time_new, NULL); + data_num += param->write.len; + if (time_new.tv_sec - time_old.tv_sec >= 3) { + print_speed(); + } +#endif + } else { + /* Means the prevous data packet is not sent at all, need to send the whole data packet again. */ + ESP_LOGE(SPP_TAG, "ESP_SPP_WRITE_EVT status:%d", param->write.status); + } + + if (!param->write.cong) { + /* The lower layer is not congested, you can send the next data packet now. */ + esp_spp_write(param->write.handle, spp_data + SPP_DATA_LEN - s_p_data, s_p_data); + } else { + /* + * The lower layer is congested now, don't send the next data packet until receiving the + * ESP_SPP_CONG_EVT with param->cong.cong == 0. + */ + ; + } + + /* + * If you don't want to manage this complicated process, we also provide the SPP VFS mode that hides the + * implementation details. However, it is less efficient and will block the caller until all data has been sent. + */ + break; case ESP_SPP_CONG_EVT: #if (SPP_SHOW_MODE == SPP_SHOW_DATA) - ESP_LOGI(SPP_TAG, "ESP_SPP_CONG_EVT cong=%d", param->cong.cong); + ESP_LOGI(SPP_TAG, "ESP_SPP_CONG_EVT cong:%d", param->cong.cong); #endif if (param->cong.cong == 0) { - esp_spp_write(param->cong.handle, SPP_DATA_LEN, spp_data); - } - break; - case ESP_SPP_WRITE_EVT: -#if (SPP_SHOW_MODE == SPP_SHOW_DATA) - ESP_LOGI(SPP_TAG, "ESP_SPP_WRITE_EVT len=%d cong=%d", param->write.len , param->write.cong); - esp_log_buffer_hex("",spp_data,SPP_DATA_LEN); -#else - gettimeofday(&time_new, NULL); - data_num += param->write.len; - if (time_new.tv_sec - time_old.tv_sec >= 3) { - print_speed(); - } -#endif - if (param->write.cong == 0) { - esp_spp_write(param->write.handle, SPP_DATA_LEN, spp_data); + /* Send the privous (partial) data packet or the next data packet. */ + esp_spp_write(param->write.handle, spp_data + SPP_DATA_LEN - s_p_data, s_p_data); } break; case ESP_SPP_SRV_OPEN_EVT: @@ -172,6 +250,7 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa case ESP_BT_GAP_DISC_RES_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_DISC_RES_EVT"); esp_log_buffer_hex(SPP_TAG, param->disc_res.bda, ESP_BD_ADDR_LEN); + /* Find the target peer device name in the EIR data */ for (int i = 0; i < param->disc_res.num_prop; i++){ if (param->disc_res.prop[i].type == ESP_BT_GAP_DEV_PROP_EIR && get_name_from_eir(param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)){ @@ -179,8 +258,10 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa if (strlen(remote_device_name) == peer_bdname_len && strncmp(peer_bdname, remote_device_name, peer_bdname_len) == 0) { memcpy(peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN); - esp_spp_start_discovery(peer_bd_addr); + /* Have found the target peer device, cancel the previous GAP discover procedure. And go on + * dsicovering the SPP service on the peer device */ esp_bt_gap_cancel_discovery(); + esp_spp_start_discovery(peer_bd_addr); } } } @@ -247,11 +328,14 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa void app_main(void) { + esp_err_t ret = ESP_OK; + char bda_str[18] = {0}; + for (int i = 0; i < SPP_DATA_LEN; ++i) { spp_data[i] = i; } - esp_err_t ret = nvs_flash_init(); + 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(); @@ -286,6 +370,17 @@ void app_main(void) return; } +#if (CONFIG_BT_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_IN; + esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); + if (iocap == ESP_BT_IO_CAP_IN || iocap == ESP_BT_IO_CAP_IO) { + console_uart_init(); + vTaskDelay(pdMS_TO_TICKS(500)); + } +#endif + if ((ret = esp_spp_register_callback(esp_spp_cb)) != ESP_OK) { ESP_LOGE(SPP_TAG, "%s spp register failed: %s\n", __func__, esp_err_to_name(ret)); return; @@ -296,16 +391,6 @@ void app_main(void) return; } -#if (CONFIG_BT_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_IN; - esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); - if (iocap == ESP_BT_IO_CAP_IN || iocap == ESP_BT_IO_CAP_IO) { - console_uart_init(); - } -#endif - /* * Set default parameters for Legacy Pairing * Use variable pin, input pin code when pairing @@ -313,4 +398,6 @@ void app_main(void) 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(SPP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str))); } diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md index 290015264b..615f557c72 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md @@ -1,17 +1,56 @@ | Supported Targets | ESP32 | | ----------------- | ----- | -ESP-IDF BT-SPP-VFS-ACCEPTOR demo -====================== +## ESP-IDF BT-SPP-VFS-ACCEPTOR demo -Demo of SPP acceptor role +This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP acceptor which performs as a server, and it will register into the VFS. We aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. We also provide the demo `bt_spp_initiator` or the demo `bt_spp_vfs_initiator` to create an SPP initiator which performs as a client. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. -This is the demo for user to use ESP_APIs to create a SPP acceptor. +## How to use example -Options choose step: -1. `idf.py menuconfig` -2. enter menuconfig `Component config`, choose `Bluetooth` -3. enter menu Bluetooth, choose `Classic Bluetooth` and `SPP Profile` -4. choose your options. +### Hardware Required -After the program started, bt_spp_vfs_initator will connect it and send data. +This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. To operate the example, it should be connected to an SPP Initiator running on a smartphone, a computer or on another ESP32 development board. + +### Configure the project + +1. Open the project configuration menu: + +``` +idf.py menuconfig +``` + +2. Enable the SPP functionality by choosing the path as following: + +`Component config --> Bluetooth --> Bluedroid Options --> SPP` + +3. If you want to limit the number of connected devices, please make sure set the `BT/BLE MAX ACL CONNECTIONS` and `BR/EDR ACL Max Connections` with same value you want. + +`Component config --> Bluetooth --> Bluedroid Options --> BT/BLE MAX ACL CONNECTIONS(1~7)` +and +`Component config --> Bluetooth --> Bluetooth --> Bluetooth controller --> BR/EDR ACL Max Connections` + + +4. SSP is enabled as default in this example. If you prefer the legacy pairing, you can disable it in the following path. + +`Component config --> Bluetooth--> Bluedroid Options --> Secure Simple Pair`. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Description + +After the program starts, the example will start an SPP acceptor. The example will print the received data after the SPP connection is established. You can connect to the server and send data with another ESP32 development board, Andriod phone or computer which performs as the SPP initiator. + +## FAQ +Please refer the FAQ part in the [README.md](../bt_spp_acceptor/README.md) \ No newline at end of file diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/CMakeLists.txt index cb65049dfe..89b4347dcd 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/CMakeLists.txt +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/CMakeLists.txt @@ -1,3 +1,3 @@ -idf_component_register(SRCS "example_spp_vfs_acceptor_demo.c" +idf_component_register(SRCS "main.c" "spp_task.c" INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/example_spp_vfs_acceptor_demo.c b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/main.c similarity index 73% rename from examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/example_spp_vfs_acceptor_demo.c rename to examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/main.c index db608a36ee..10d396a452 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/example_spp_vfs_acceptor_demo.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/main.c @@ -46,27 +46,51 @@ static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_AUTHENTICATE; static const esp_spp_role_t role_slave = ESP_SPP_ROLE_SLAVE; #define SPP_DATA_LEN 100 -static uint8_t spp_data[SPP_DATA_LEN]; + +static char *bda2str(uint8_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 void spp_read_handle(void * param) { int size = 0; int fd = (int)param; - do { - /* controll the log frequency, retry after 1s */ - vTaskDelay(1000 / portTICK_PERIOD_MS); + uint8_t *spp_data = NULL; - size = read (fd, spp_data, SPP_DATA_LEN); - ESP_LOGI(SPP_TAG, "fd = %d data_len = %d", fd, size); - if (size == -1) { + spp_data = malloc(SPP_DATA_LEN); + if (!spp_data) { + ESP_LOGE(SPP_TAG, "malloc spp_data failed, fd:%d", fd); + goto done; + } + + do { + /* The frequency of calling this function also limits the speed at which the peer device can send data. */ + size = read(fd, spp_data, SPP_DATA_LEN); + if (size < 0) { break; - } - esp_log_buffer_hex(SPP_TAG, spp_data, size); - if (size == 0) { - /*read fail due to there is no data, retry after 1s*/ - vTaskDelay(1000 / portTICK_PERIOD_MS); + } else if (size == 0) { + /* There is no data, retry after 500 ms */ + vTaskDelay(500 / portTICK_PERIOD_MS); + } else { + ESP_LOGI(SPP_TAG, "fd = %d data_len = %d", fd, size); + esp_log_buffer_hex(SPP_TAG, spp_data, size); + /* To avoid task watchdog */ + vTaskDelay(10 / portTICK_PERIOD_MS); } } while (1); +done: + if (spp_data) { + free(spp_data); + } spp_wr_task_shut_down(); } @@ -74,13 +98,17 @@ static void esp_spp_cb(uint16_t e, void *p) { esp_spp_cb_event_t event = e; esp_spp_cb_param_t *param = p; + char bda_str[18] = {0}; switch (event) { case ESP_SPP_INIT_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT status=%d", param->init.status); if (param->init.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); + /* Enable SPP VFS mode */ esp_spp_vfs_register(); esp_spp_start_srv(sec_mask, role_slave, 0, SPP_SERVER_NAME); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_INIT_EVT status:%d", param->init.status); } break; case ESP_SPP_DISCOVERY_COMP_EVT: @@ -90,18 +118,25 @@ static void esp_spp_cb(uint16_t e, void *p) ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT"); break; case ESP_SPP_CLOSE_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT"); + ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT status:%d handle:%d close_by_remote:%d", param->close.status, + param->close.handle, param->close.async); break; case ESP_SPP_START_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT"); - esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); - esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + if (param->start.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT handle:%d sec_id:%d scn:%d", param->start.handle, param->start.sec_id, + param->start.scn); + esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_START_EVT status:%d", param->start.status); + } break; case ESP_SPP_CL_INIT_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT"); break; case ESP_SPP_SRV_OPEN_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT status=%d", param->srv_open.status); + ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT status:%d handle:%d, rem_bda:[%s]", param->srv_open.status, + param->srv_open.handle, bda2str(param->srv_open.rem_bda, bda_str, sizeof(bda_str))); if (param->srv_open.status == ESP_SPP_SUCCESS) { spp_wr_task_start_up(spp_read_handle, param->srv_open.fd); } @@ -113,6 +148,7 @@ static void esp_spp_cb(uint16_t e, void *p) static void esp_spp_stack_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { + /* To avoid stucking Bluetooth stack, we dispatch the SPP callback event to the other lower priority task */ spp_task_work_dispatch(esp_spp_cb, event, param, sizeof(esp_spp_cb_param_t), NULL); } @@ -173,6 +209,7 @@ void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) void app_main(void) { + char bda_str[18] = {0}; 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()); @@ -234,4 +271,6 @@ void app_main(void) 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(SPP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str))); } diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/README.md index 374a9344f3..67f30933e5 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/README.md @@ -1,17 +1,48 @@ | Supported Targets | ESP32 | | ----------------- | ----- | -ESP-IDF BT-SPP-VFS-INITATOR demo -====================== +# ESP-IDF BT-SPP-INITATOR demo -Demo of SPP initator role +This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP initiator which performs as a client, and it will register into the VFS. we aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. We also provide the demo `bt_spp_acceptor` or the demo `bt_spp_vfs_acceptor` to create an SPP acceptor which performs as a server. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. -This is the demo for user to use ESP_APIs to create a SPP initator. +## How to use example -Options choose step: -1. `idf.py menuconfig` -2. enter menuconfig `Component config`, choose `Bluetooth` -3. enter menu Bluetooth, choose `Classic Bluetooth` and `SPP Profile` -4. choose your options. +### Hardware Required -After the program started, It will connect to bt_spp_vfs_acceptor and send data. +This example is designed to run on commonly available ESP32 development board, e.g. ESP32-DevKitC. To operate the example, you should be connect to an SPP acceptor running on a smartphone, a computer or on another ESP32 development board. + +### Configure the project +1. Open the project configuration menu: +``` +idf.py menuconfig +``` + +2. Enable the SPP functionality by choosing the path as following: + +`Component config --> Bluetooth --> Bluedroid Options --> SPP`. + +3. SSP is enabled as default in this example. If you prefer the legacy pairing, you can disable it in the following path. + +`Component config --> Bluetooth--> Bluedroid Options --> Secure Simple Pair`. + + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Description + +After the program starts, the example will initiate a Bluetooth discovery procedure and filter out the peer device by the name in the EIR(Extended Inquiry Response). After discovering the SPP service, it will connect to the SPP acceptor and send data. + +## FAQ +Please refer the FAQ part in the [README.md](../bt_spp_initiator/README.md) diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/CMakeLists.txt index e853defaa2..89b4347dcd 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/CMakeLists.txt +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/CMakeLists.txt @@ -1,3 +1,3 @@ -idf_component_register(SRCS "example_spp_vfs_initiator_demo.c" +idf_component_register(SRCS "main.c" "spp_task.c" INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/example_spp_vfs_initiator_demo.c b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/main.c similarity index 74% rename from examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/example_spp_vfs_initiator_demo.c rename to examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/main.c index 7c6d01c287..987b5cac0f 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/example_spp_vfs_initiator_demo.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/main.c @@ -53,26 +53,56 @@ static const uint8_t inq_len = 30; static const uint8_t inq_num_rsps = 0; #define SPP_DATA_LEN 20 -static uint8_t spp_data[SPP_DATA_LEN]; + +static char *bda2str(uint8_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 void spp_write_handle(void * param) { int size = 0; int fd = (int)param; - printf("%s %d %p\n", __func__,fd,param); - do { - /*Controll the log frequency, retry after 1s*/ - vTaskDelay(1000 / portTICK_PERIOD_MS); + uint8_t *spp_data = NULL; + uint16_t i = 0; - size = write (fd, spp_data, SPP_DATA_LEN); - ESP_LOGI(SPP_TAG, "fd = %d data_len = %d",fd, size); + spp_data = malloc(SPP_DATA_LEN); + if (!spp_data) { + ESP_LOGE(SPP_TAG, "malloc spp_data failed, fd:%d", fd); + goto done; + } + + for (i = 0; i < SPP_DATA_LEN; ++i) { + spp_data[i] = i; + } + + do { + /* + * The write function is blocked until all the target length of data has been sent to the lower layer + * successfully an error occurs. + */ + size = write(fd, spp_data, SPP_DATA_LEN); if (size == -1) { break; } else if ( size == 0) { - /*write fail due to ringbuf is full, retry after 1s*/ - vTaskDelay(1000 / portTICK_PERIOD_MS); + /*write fail due to ringbuf is full, retry after 500 ms*/ + vTaskDelay(500 / portTICK_PERIOD_MS); + } else { + ESP_LOGI(SPP_TAG, "fd = %d data_len = %d", fd, size); + vTaskDelay(10 / portTICK_PERIOD_MS); } } while (1); +done: + if (spp_data) { + free(spp_data); + } spp_wr_task_shut_down(); } @@ -112,37 +142,57 @@ static void esp_spp_cb(uint16_t e, void *p) { esp_spp_cb_event_t event = e; esp_spp_cb_param_t *param = p; + uint8_t i = 0; + char bda_str[18] = {0}; switch (event) { case ESP_SPP_INIT_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT status=%d", param->init.status); if (param->init.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_INIT_EVT"); + /* Enable SPP VFS mode */ esp_spp_vfs_register(); esp_bt_dev_set_device_name(EXAMPLE_DEVICE_NAME); esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); esp_bt_gap_start_discovery(inq_mode, inq_len, inq_num_rsps); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_INIT_EVT status:%d", param->init.status); } break; case ESP_SPP_DISCOVERY_COMP_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT status=%d scn_num=%d",param->disc_comp.status, param->disc_comp.scn_num); if (param->disc_comp.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT scn_num:%d", param->disc_comp.scn_num); + for (i = 0; i < param->disc_comp.scn_num; i++) { + ESP_LOGI(SPP_TAG, "-- [%d] scn:%d service_name:%s", i, param->disc_comp.scn[i], + param->disc_comp.service_name[i]); + } + /* We only connect to the first found server on the remote SPP acceptor here */ esp_spp_connect(sec_mask, role_master, param->disc_comp.scn[0], peer_bd_addr); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_DISCOVERY_COMP_EVT status=%d", param->disc_comp.status); } break; case ESP_SPP_OPEN_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT status=%d", param->open.status); if (param->open.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_OPEN_EVT handle:%d fd:%d rem_bda:[%s]", param->open.handle, param->open.fd, + bda2str(param->open.rem_bda, bda_str, sizeof(bda_str))); spp_wr_task_start_up(spp_write_handle, param->open.fd); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_OPEN_EVT status:%d", param->open.status); } break; case ESP_SPP_CLOSE_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT"); + ESP_LOGI(SPP_TAG, "ESP_SPP_CLOSE_EVT status:%d handle:%d close_by_remote:%d", param->close.status, + param->close.handle, param->close.async); break; case ESP_SPP_START_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_START_EVT"); break; case ESP_SPP_CL_INIT_EVT: - ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT"); + if (param->cl_init.status == ESP_SPP_SUCCESS) { + ESP_LOGI(SPP_TAG, "ESP_SPP_CL_INIT_EVT handle:%d sec_id:%d", param->cl_init.handle, param->cl_init.sec_id); + } else { + ESP_LOGE(SPP_TAG, "ESP_SPP_CL_INIT_EVT status:%d", param->cl_init.status); + } break; case ESP_SPP_SRV_OPEN_EVT: ESP_LOGI(SPP_TAG, "ESP_SPP_SRV_OPEN_EVT"); @@ -158,6 +208,7 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa case ESP_BT_GAP_DISC_RES_EVT: ESP_LOGI(SPP_TAG, "ESP_BT_GAP_DISC_RES_EVT"); esp_log_buffer_hex(SPP_TAG, param->disc_res.bda, ESP_BD_ADDR_LEN); + /* Find the target peer device name in the EIR data */ for (int i = 0; i < param->disc_res.num_prop; i++){ if (param->disc_res.prop[i].type == ESP_BT_GAP_DEV_PROP_EIR && get_name_from_eir(param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)){ @@ -165,8 +216,10 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa if (strlen(remote_device_name) == peer_bdname_len && strncmp(peer_bdname, remote_device_name, peer_bdname_len) == 0) { memcpy(peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN); - esp_spp_start_discovery(peer_bd_addr); + /* Have found the target peer device, cancel the previous GAP discover procedure. And go on + * dsicovering the SPP service on the peer device */ esp_bt_gap_cancel_discovery(); + esp_spp_start_discovery(peer_bd_addr); } } } @@ -231,16 +284,16 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa static void esp_spp_stack_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { + /* To avoid stucking Bluetooth stack, we dispatch the SPP callback event to the other lower priority task */ spp_task_work_dispatch(esp_spp_cb, event, param, sizeof(esp_spp_cb_param_t), NULL); } void app_main(void) { - for (int i = 0; i < SPP_DATA_LEN; ++i) { - spp_data[i] = i; - } + esp_err_t ret = ESP_OK; + char bda_str[18] = {0}; - esp_err_t ret = nvs_flash_init(); + 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(); @@ -300,4 +353,6 @@ void app_main(void) 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(SPP_TAG, "Own address:[%s]", bda2str((uint8_t *)esp_bt_dev_get_address(), bda_str, sizeof(bda_str))); } From f29492dc9d12907fa7a5bc0e76119642308df26d Mon Sep 17 00:00:00 2001 From: liqigan Date: Fri, 31 Dec 2021 11:18:17 +0800 Subject: [PATCH 2/2] fix typos and format --- .../bluedroid/classic_bt/bt_spp_acceptor/README.md | 6 +++--- .../bluedroid/classic_bt/bt_spp_acceptor/main/main.c | 2 +- .../bluedroid/classic_bt/bt_spp_initiator/README.md | 8 ++++---- .../bluedroid/classic_bt/bt_spp_initiator/main/main.c | 2 +- .../bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md | 4 ++-- .../bluedroid/classic_bt/bt_spp_vfs_acceptor/main/main.c | 2 +- .../bluedroid/classic_bt/bt_spp_vfs_initiator/README.md | 4 ++-- .../bluedroid/classic_bt/bt_spp_vfs_initiator/main/main.c | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/README.md index 340ed259c9..2a630479bd 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/README.md @@ -1,9 +1,9 @@ | Supported Targets | ESP32 | | ----------------- | ----- | -## ESP-IDF BT-SPP-ACCEPTOR demo +## ESP-IDF BT-SPP-ACCEPTOR Example -This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP acceptor which performs as a server. We aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. We also provide the demo `bt_spp_initiator` or the demo `bt_spp_vfs_initiator` to create an SPP initiator which performs as a client. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. +This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP acceptor which performs as a server. We aggregate **Secure Simple Pair** (**SSP**) into this example to show how to use SPP when creating your own APPs. We also provide the example `bt_spp_initiator` or the example `bt_spp_vfs_initiator` to create an SPP initiator which performs as a client. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. ## How to use example @@ -58,7 +58,7 @@ After the program starts, the example will start an SPP acceptor. The example wi ## FAQ Q: How to change the process of SSP? -A: Users can set the IO Capability and Security Mask for their device (fixed Security Mode, Security Mode 4). In short, the Security Mask sets the security level for authentication stage and the IO Capability determines the way of user interaction during pairing. The default Security Mask of this demo is `ESP_SPP_SEC_AUTHENTICATE` which support MITM (Man In The Middle) protection. For more information about Security Simple Pair on ESP32, please refer to [ESP32_SSP](./ESP32_SSP.md). +A: Users can set the IO Capability and Security Mask for their device (fixed Security Mode, Security Mode 4). In short, the Security Mask sets the security level for authentication stage and the IO Capability determines the way of user interaction during pairing. The default Security Mask of this example is `ESP_SPP_SEC_AUTHENTICATE` which support MITM (Man In The Middle) protection. For more information about Security Simple Pair on ESP32, please refer to [ESP32_SSP](./ESP32_SSP.md). Q: How many SPP servers does ESP32 support? diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/main.c b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/main.c index 4b7582dd21..f6ffc0d156 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_acceptor/main/main.c @@ -39,7 +39,7 @@ static long data_num = 0; static const esp_spp_sec_t sec_mask = ESP_SPP_SEC_AUTHENTICATE; static const esp_spp_role_t role_slave = ESP_SPP_ROLE_SLAVE; -static char *bda2str(uint8_t * bda, char *str, size_t size) +static char *bda2str(uint8_t *bda, char *str, size_t size) { if (bda == NULL || str == NULL || size < 18) { return NULL; diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/README.md index 544c2a1dfc..703fe745b0 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/README.md @@ -1,9 +1,9 @@ | Supported Targets | ESP32 | | ----------------- | ----- | -# ESP-IDF BT-SPP-INITATOR demo +# ESP-IDF BT-SPP-INITATOR Example -This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP initiator which performs as a client. we aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. We also provide the demo `bt_spp_acceptor` or the demo `bt_spp_vfs_acceptor` to create an SPP acceptor which performs as a server. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. +This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP initiator which performs as a client. we aggregate **Secure Simple Pair** (**SSP**) into this example to show how to use SPP when creating your own APPs. We also provide the example `bt_spp_acceptor` or the example `bt_spp_vfs_acceptor` to create an SPP acceptor which performs as a server. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. ## How to use example @@ -110,7 +110,7 @@ Whether you should passkey or confirm the number also depends on the IO capabili - Set `SPP_SHOW_MODE` as `SPP_SHOW_DATA` or `SPP_SHOW_SPEED` in code (should be same with `bt_spp_acceptor`, if the peer device runs it). When setting `SPP_SHOW_MODE` as `SPP_SHOW_DATA`, if the data rate is too high or the data length is too long, it is strongly recommended to process them in other lower priority application task rather than in this callback directly. Since the printing takes too much time, and it may stuck the Bluetooth stack. -- We haven't do the same update to `bt_acceptor_demo` for the sake of reducing the size of ESP_IDF, but transplanting of input module is supported. +- We haven't do the same update to the example `bt_spp_acceptor` for the sake of reducing the size of ESP_IDF, but transplanting of input module is supported. ## Example Breakdown @@ -124,7 +124,7 @@ To clearly show how the SSP aggregate with the SPP , we use the Commands and Eff ## FAQ Q: How to change the process of SSP? -A: Users can set the IO Capability and Security Mask for their device (fixed Security Mode, Security Mode 4). In short, the Security Mask sets the security level for authentication stage and the IO Capability determines the way of user interaction during pairing. The default Security Mask of this demo is `ESP_SPP_SEC_AUTHENTICATE` which support MITM (Man In The Middle) protection. For more information about Security Simple Pair on ESP32, please refer to [ESP32_SSP](../bt_spp_acceptor/ESP32_SSP.md). +A: Users can set the IO Capability and Security Mask for their device (fixed Security Mode, Security Mode 4). In short, the Security Mask sets the security level for authentication stage and the IO Capability determines the way of user interaction during pairing. The default Security Mask of this example is `ESP_SPP_SEC_AUTHENTICATE` which support MITM (Man In The Middle) protection. For more information about Security Simple Pair on ESP32, please refer to [ESP32_SSP](../bt_spp_acceptor/ESP32_SSP.md). Q: How can we reach the maximum throughput when using SPP? A: The default MTU size of classic Bluetooth SPP on ESP32 is 990 bytes, and higher throughput can be achieved in the case that data chunck size is close to the MTU size or multiple of MTU size. For example, sending 100 bytes data per second is much better than sending 10 bytes every 100 milliseconds. diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/main.c b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/main.c index 3b337878b4..480ee98eec 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_initiator/main/main.c @@ -56,7 +56,7 @@ static const uint8_t inq_num_rsps = 0; static uint8_t spp_data[SPP_DATA_LEN]; static uint8_t *s_p_data = NULL; /* data pointer of spp_data */ -static char *bda2str(uint8_t * bda, char *str, size_t size) +static char *bda2str(uint8_t *bda, char *str, size_t size) { if (bda == NULL || str == NULL || size < 18) { return NULL; diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md index 615f557c72..1c4d282bac 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/README.md @@ -1,9 +1,9 @@ | Supported Targets | ESP32 | | ----------------- | ----- | -## ESP-IDF BT-SPP-VFS-ACCEPTOR demo +## ESP-IDF BT-SPP-VFS-ACCEPTOR Example -This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP acceptor which performs as a server, and it will register into the VFS. We aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. We also provide the demo `bt_spp_initiator` or the demo `bt_spp_vfs_initiator` to create an SPP initiator which performs as a client. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. +This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP acceptor which performs as a server, and it will register into the VFS. We aggregate **Secure Simple Pair** (**SSP**) into this example to show how to use SPP when creating your own APPs. We also provide the example `bt_spp_initiator` or the example `bt_spp_vfs_initiator` to create an SPP initiator which performs as a client. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. ## How to use example diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/main.c b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/main.c index 10d396a452..31205ba7e2 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_acceptor/main/main.c @@ -47,7 +47,7 @@ static const esp_spp_role_t role_slave = ESP_SPP_ROLE_SLAVE; #define SPP_DATA_LEN 100 -static char *bda2str(uint8_t * bda, char *str, size_t size) +static char *bda2str(uint8_t *bda, char *str, size_t size) { if (bda == NULL || str == NULL || size < 18) { return NULL; diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/README.md index 67f30933e5..1822cc0fb6 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/README.md +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/README.md @@ -1,9 +1,9 @@ | Supported Targets | ESP32 | | ----------------- | ----- | -# ESP-IDF BT-SPP-INITATOR demo +# ESP-IDF BT-SPP-INITATOR Example -This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP initiator which performs as a client, and it will register into the VFS. we aggregate **Secure Simple Pair** (**SSP**) into this demo to show how to use SPP when creating your own APPs. We also provide the demo `bt_spp_acceptor` or the demo `bt_spp_vfs_acceptor` to create an SPP acceptor which performs as a server. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. +This example is to show how to use the APIs of **Serial Port Protocol** (**SPP**) to create an SPP initiator which performs as a client, and it will register into the VFS. we aggregate **Secure Simple Pair** (**SSP**) into this example to show how to use SPP when creating your own APPs. We also provide the example `bt_spp_acceptor` or the example `bt_spp_vfs_acceptor` to create an SPP acceptor which performs as a server. In fact, you can create SPP acceptors and SPP initiators on a single device at the same time. ## How to use example diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/main.c b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/main.c index 987b5cac0f..8c099fbae6 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_spp_vfs_initiator/main/main.c @@ -54,7 +54,7 @@ static const uint8_t inq_num_rsps = 0; #define SPP_DATA_LEN 20 -static char *bda2str(uint8_t * bda, char *str, size_t size) +static char *bda2str(uint8_t *bda, char *str, size_t size) { if (bda == NULL || str == NULL || size < 18) { return NULL;