diff --git a/libraries/BluetoothSerial/README.md b/libraries/BluetoothSerial/README.md index 9d25dbca0a9..4989b3a088c 100644 --- a/libraries/BluetoothSerial/README.md +++ b/libraries/BluetoothSerial/README.md @@ -1,19 +1,78 @@ -### Bluetooth Serial Library +## Bluetooth Serial Library -A simple Serial compatible library using ESP32 classical bluetooth (SPP) +A simple Serial compatible library using ESP32 classical Bluetooth Serial Port Profile (SPP) +Note: Since version 3.0.0 this library does not support legacy pairing (using fixed PIN consisting of 4 digits). +### How to use it? -#### How to use it? +There are 3 basic use cases: phone, other ESP32 or any MCU with a Bluetooth serial module -- Download one bluetooth terminal app in your smartphone
-For Android: https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal
-For iOS: https://itunes.apple.com/us/app/hm10-bluetooth-serial-lite/id1030454675 +#### Phone + +- Download one of the Bluetooth terminal apps to your smartphone + + - For [Android](https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal) + - For [iOS](https://itunes.apple.com/us/app/hm10-bluetooth-serial-lite/id1030454675) - Flash an example sketch to your ESP32 -- Scan and pair the device in your smartphone +- Scan and pair the device to your smartphone -- Open the bluetooth terminal app +- Open the Bluetooth terminal app and connect - Enjoy + +#### ESP32 + +You can flash one of the ESP32 with the example [`SerialToSerialBTM`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino) (the Master) and another ESP32 with [`SerialToSerialBT`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino) (the Slave). +Those examples are preset to work out-of-the-box but they should be scalable to connect multiple Slaves to the Master. + +#### 3rd party Serial Bluetooth module + +Using a 3rd party Serial Bluetooth module will require to study the documentation of the particular module in order to make it work, however, one side can utilize the mentioned [`SerialToSerialBTM`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino) (the Master) or [`SerialToSerialBT`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino) (the Slave). + +### Pairing options + +There are two easy options and one difficult. + +The easy options can be used as usual. These offer pairing with and without Secure Simple Pairing (SSP). + +The difficult option offers legacy pairing (using fixed PIN) however this must be compiled with Arduino as an IDF component with disabled sdkconfig option `CONFIG_BT_SSP_ENABLED`. + +#### Without SSP + +This method will authenticate automatically any attempt to pair and should not be used if security is a concern! This option is used for the examples [`SerialToSerialBTM`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino) and [`SerialToSerialBT`](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino). + +### With SSP + +The usage of SSP provides a secure connection. This option is demonstrated in the example `SerialToSerialBT_SSP``](https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/SerialToSerialBT_SSP.ino) + +The Secure Simple Pairing is enabled by calling method `enableSSP` which has two variants - one is backward compatible without parameter `enableSSP()` and second with parameters `enableSSP(bool inputCapability, bool outputCapability)`. Similarly, the SSP can be disabled by calling `disableSSP()`. + +Both options must be called before `begin()` or if it is called after `begin()` the driver needs to be restarted (call `end()` followed by `begin()`) in order to take in effect enabling or disabling the SSP. + +#### The parameters define the method of authentication: + +**inputCapability** - Defines if ESP32 device has input method (Serial terminal, keyboard or similar) + +**outputCapability** - Defines if ESP32 device has output method (Serial terminal, display or similar) + +* **inputCapability=true and outputCapability=true** + * Both devices display randomly generated code and if they match the user will authenticate pairing on both devices. + * This must be implemented by registering a callback via `onConfirmRequest()` and in this callback the user will input the response and call `confirmReply(true)` if the authenticated, otherwise call `confirmReply(false)` to reject the pairing. +* **inputCapability=false and outputCapability=false** + * Only the other device authenticates pairing without any pin. +* **inputCapability=false and outputCapability=true** + * Only the other device authenticates pairing without any pin. +* **inputCapability=true and outputCapability=false** + * The user will be required to input the passkey to the ESP32 device to authenticate. + * This must be implemented by registering a callback via `onKeyRequest`()` and in this callback the entered passkey will be responded via `respondPasskey(passkey)` + +### Legacy Pairing (IDF component) + +To use Legacy pairing you will have to use [Arduino as an IDF component](https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/esp-idf_component.html) and disable option `CONFIG_BT_SSP_ENABLED`. +Please refer to the documentation on how to setup Arduino as an IDF component and when you are done, run `idf.py menuconfig` navigate to `Component Config -> Bluetooth -> Bluedroid -> [ ] Secure Simple Pairing` and disable it. +While in the menuconfig you will also need to change the partition scheme `Partition Table -> Partition Table -> (X) Single Factory app (large), no OTA`. +After these changes save & quit menuconfig and you are ready to go: `idf.py monitor flash`. +Please note that to use the PIN in smartphones and computers you need to use characters `SerialBT.setPin("1234", 4);` not a number `SerialBT.setPin(1234, 4);` . Numbers CAN be used if the other side uses them too, but phones and computers use characters. \ No newline at end of file diff --git a/libraries/BluetoothSerial/examples/DiscoverConnect/DiscoverConnect.ino b/libraries/BluetoothSerial/examples/DiscoverConnect/DiscoverConnect.ino index 168ea9d19c4..4269598b80f 100644 --- a/libraries/BluetoothSerial/examples/DiscoverConnect/DiscoverConnect.ino +++ b/libraries/BluetoothSerial/examples/DiscoverConnect/DiscoverConnect.ino @@ -19,19 +19,18 @@ #include #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it #endif #if !defined(CONFIG_BT_SPP_ENABLED) -#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. + #error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. #endif BluetoothSerial SerialBT; - #define BT_DISCOVER_TIME 10000 -esp_spp_sec_t sec_mask=ESP_SPP_SEC_NONE; // or ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE to request pincode confirmation -esp_spp_role_t role=ESP_SPP_ROLE_SLAVE; // or ESP_SPP_ROLE_MASTER +esp_spp_sec_t sec_mask = ESP_SPP_SEC_NONE; // or ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE to request pincode confirmation +esp_spp_role_t role = ESP_SPP_ROLE_SLAVE; // or ESP_SPP_ROLE_MASTER // std::map btDeviceList; diff --git a/libraries/BluetoothSerial/examples/GetLocalMAC/GetLocalMAC.ino b/libraries/BluetoothSerial/examples/GetLocalMAC/GetLocalMAC.ino index a3ca6b026b6..2930067bf5d 100644 --- a/libraries/BluetoothSerial/examples/GetLocalMAC/GetLocalMAC.ino +++ b/libraries/BluetoothSerial/examples/GetLocalMAC/GetLocalMAC.ino @@ -6,11 +6,11 @@ String device_name = "ESP32-example"; #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it #endif #if !defined(CONFIG_BT_SPP_ENABLED) -#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. + #error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. #endif BluetoothSerial SerialBT; diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino b/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino index 5027ae6a7e3..c5ac06b992f 100644 --- a/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino +++ b/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino @@ -1,22 +1,22 @@ -//This example code is in the Public Domain (or CC0 licensed, at your option.) -//By Evandro Copercini - 2018 +// This example code is in the Public Domain (or CC0 licensed, at your option.) +// By Evandro Copercini - 2018 // -//This example creates a bridge between Serial and Classical Bluetooth (SPP) -//and also demonstrate that SerialBT have the same functionalities of a normal Serial +// This example creates a bridge between Serial and Classical Bluetooth (SPP) +// and also demonstrate that SerialBT have the same functionalities of a normal Serial +// Note: Pairing is authenticated automatically by this device #include "BluetoothSerial.h" -//#define USE_PIN // Uncomment this to use PIN during pairing. The pin is specified on the line below -const char *pin = "1234"; // Change this to more secure PIN. - String device_name = "ESP32-BT-Slave"; +// Check if Bluetooth is available #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it #endif +// Check Serial Port Profile #if !defined(CONFIG_BT_SPP_ENABLED) -#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. + #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip. #endif BluetoothSerial SerialBT; @@ -24,12 +24,8 @@ BluetoothSerial SerialBT; void setup() { Serial.begin(115200); SerialBT.begin(device_name); //Bluetooth device name + //SerialBT.deleteAllBondedDevices(); // Uncomment this to delete paired devices; Must be called after begin Serial.printf("The device with name \"%s\" is started.\nNow you can pair it with Bluetooth!\n", device_name.c_str()); - //Serial.printf("The device with name \"%s\" and MAC address %s is started.\nNow you can pair it with Bluetooth!\n", device_name.c_str(), SerialBT.getMacString()); // Use this after the MAC method is implemented - #ifdef USE_PIN - SerialBT.setPin(pin); - Serial.println("Using PIN"); - #endif } void loop() { diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino b/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino index d71941b002d..a4917bfa141 100644 --- a/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino +++ b/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino @@ -1,27 +1,33 @@ // This example code is in the Public Domain (or CC0 licensed, at your option.) // By Victor Tchistiak - 2019 // -// This example demonstrates master mode Bluetooth connection to a slave BT device using PIN (password) -// defined either by String "slaveName" by default "OBDII" or by MAC address +// This example demonstrates master mode Bluetooth connection to a slave BT device +// defined either by String "slaveName" by default "ESP32-BT-Slave" or by MAC address // // This example creates a bridge between Serial and Classical Bluetooth (SPP) // This is an extension of the SerialToSerialBT example by Evandro Copercini - 2018 // // DO NOT try to connect to phone or laptop - they are master -// devices, same as the ESP using this code - it will NOT work! +// devices, same as the ESP using this code - you will be able +// to pair, but the serial communication will NOT work! // // You can try to flash a second ESP32 with the example SerialToSerialBT - it should // automatically pair with ESP32 running this code +// Note: Pairing is authenticated automatically by this device #include "BluetoothSerial.h" #define USE_NAME // Comment this to use MAC address instead of a slaveName -const char *pin = "1234"; // Change this to reflect the pin expected by the real slave BT device -#if !defined(CONFIG_BT_SPP_ENABLED) -#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. +// Check if Bluetooth is available +#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it #endif +// Check Serial Port Profile +#if !defined(CONFIG_BT_SPP_ENABLED) + #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip. +#endif BluetoothSerial SerialBT; #ifdef USE_NAME @@ -38,6 +44,7 @@ void setup() { Serial.begin(115200); SerialBT.begin(myName, true); + //SerialBT.deleteAllBondedDevices(); // Uncomment this to delete paired devices; Must be called after begin Serial.printf("The device \"%s\" started in master mode, make sure slave BT device is on!\n", myName.c_str()); #ifndef USE_NAME diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32c3 b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32c3 similarity index 100% rename from libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32c3 rename to libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32c3 diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32c6 b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32c6 similarity index 100% rename from libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32c6 rename to libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32c6 diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32h2 b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32h2 similarity index 100% rename from libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32h2 rename to libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32h2 diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32s2 b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32s2 similarity index 100% rename from libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32s2 rename to libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32s2 diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32s3 b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32s3 similarity index 100% rename from libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/.skip.esp32s3 rename to libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/.skip.esp32s3 diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/SerialToSerialBT_Legacy.ino b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/SerialToSerialBT_Legacy.ino new file mode 100644 index 00000000000..2343b5ca93d --- /dev/null +++ b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/SerialToSerialBT_Legacy.ino @@ -0,0 +1,65 @@ +// This example code is in the Public Domain (or CC0 licensed, at your option.) +// +// This example creates a bridge between Serial and Classical Bluetooth (SPP with authentication) +// and also demonstrate that SerialBT have the same functionalities of a normal Serial +// Legacy pairing TODO +// Must be run as idf component ... todo + +#include "BluetoothSerial.h" + +// Check if Bluetooth is available +#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it +#endif + +// Check Serial Port Profile +#if !defined(CONFIG_BT_SPP_ENABLED) + #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip. +#endif + +// Check Simple Secure Pairing +#if defined(CONFIG_BT_SSP_ENABLED) + #warning Legacy Pairing is disabled (CONFIG_BT_SSP_ENABLED is enabled. Disable it in menuconfig). + void setup(){} + void loop(){} +#else +const char * deviceName = "ESP32_Legacy_example"; + +BluetoothSerial SerialBT; +bool confirmRequestDone = false; + +void BTAuthCompleteCallback(boolean success){ + if (success){ + confirmRequestDone = true; + Serial.println("Pairing success!!"); + } else { + Serial.println("Pairing failed, rejected by user!!"); + } +} + +void serial_response(){ + if (Serial.available()){ + SerialBT.write(Serial.read()); + } + if (SerialBT.available()){ + Serial.write(SerialBT.read()); + } + delay(20); +} + +void setup(){ + Serial.begin(115200); + SerialBT.onAuthComplete(BTAuthCompleteCallback); + SerialBT.begin(deviceName); // Initiate Bluetooth device with name in parameter + SerialBT.setPin("1234", 4); + Serial.printf("The device started with name \"%s\", now you can pair it with Bluetooth!\n", deviceName); +} + +void loop(){ + if (confirmRequestDone){ + serial_response(); + } else { + delay(1); // Feed the watchdog + } +} +#endif diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32c3 b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32c6 b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32c6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32h2 b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32s2 b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32s2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32s3 b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/.skip.esp32s3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/SerialToSerialBT_SSP.ino b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/SerialToSerialBT_SSP.ino new file mode 100644 index 00000000000..a39dbf1a064 --- /dev/null +++ b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/SerialToSerialBT_SSP.ino @@ -0,0 +1,134 @@ +// This example code is in the Public Domain (or CC0 licensed, at your option.) +// By Richard Li - 2020 +// +// This example creates a bridge between Serial and Classical Bluetooth (SPP with authentication) +// and also demonstrate that SerialBT have the same functionalities of a normal Serial +// SSP - Simple Secure Pairing - The device (ESP32) will display random number and the user is responsible of comparing it to the number +// displayed on the other device (for example phone). +// If the numbers match the user authenticates the pairing on both devices - on phone simply press "Pair" and in terminal for the sketch send 'Y' or 'y' to confirm. +// Alternatively uncomment AUTO_PAIR to skip the terminal confirmation. + +#include "BluetoothSerial.h" + +//#define AUTO_PAIR // Uncomment to automatically authenticate ESP32 side + +// Check if Bluetooth is available +#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it +#endif + +// Check Serial Port Profile +#if !defined(CONFIG_BT_SPP_ENABLED) + #error Serial Port Profile for Bluetooth is not available or not enabled. It is only available for the ESP32 chip. +#endif + +// Check Simple Secure Pairing +#if !defined(CONFIG_BT_SSP_ENABLED) + #error Simple Secure Pairing for Bluetooth is not available or not enabled. +#endif + +const char * deviceName = "ESP32_SSP_example"; + +// The following lines defines the method of pairing +// When both Input and Output are false only the other device authenticates pairing without any pin. +// When Output is true and Input is false only the other device authenticates pairing without any pin. +// When both Input and Output are true both devices display randomly generated code and if they match authenticate pairing on both devices +// - This must be implemented by registering callback via onConfirmRequest() and in this callback request user input and call confirmReply(true); if the authenticated +// otherwise call `confirmReply(false)` to reject the pairing. +// When Input is true and Output is false User will be required to input the passkey to the ESP32 device to authenticate. +// - This must be implemented by registering callback via onKeyRequest() and in this callback the entered passkey will be responded via respondPasskey(passkey); +const bool INPUT_CAPABILITY = false; // Defines if ESP32 device has input method (Serial terminal, keyboard or similar) +const bool OUTPUT_CAPABILITY = true; // Defines if ESP32 device has output method (Serial terminal, display or similar) + +BluetoothSerial SerialBT; +bool confirmRequestDone = false; + +void BTConfirmRequestCallback(uint32_t numVal){ + confirmRequestDone = false; +#ifndef AUTO_PAIR + Serial.printf("The PIN is: %06lu. If it matches number displayed on the other device write \'Y\' or \'y\':\n", numVal); // Note the formatting "%06lu" - PIN can start with zero(s) which would be ignored with simple "%lu" + while (!Serial.available()) { + delay(1); // Feed the watchdog + // Wait until data is available on the Serial port. + } + Serial.printf("Oh you sent %d Bytes, lets see...", Serial.available()); + int dat = Serial.read(); + if (dat == 'Y' || dat == 'y'){ + SerialBT.confirmReply(true); + } + else{ + SerialBT.confirmReply(false); + } +#else + SerialBT.confirmReply(true); +#endif +} + +void BTKeyRequestCallback(){ + Serial.println("BTKeyRequestCallback"); // debug + char buffer[7] = {0}; // 6 bytes for number, one for termination '0' + while (1) { + Serial.print("Enter the passkey displayed on the other device: "); + while (!Serial.available()) { + delay(1); // Feed the watchdog + // Wait until data is available on the Serial port. + } + size_t len = Serial.readBytesUntil('\n', buffer, sizeof(buffer) - 1); + buffer[len] = '\0'; // Null-terminate the string. + try { + uint32_t passkey = std::stoi(buffer); + Serial.printf("Entered PIN: %lu\n", passkey); + SerialBT.respondPasskey(passkey); + return; + } catch (...) { + Serial.print("Wrong PIN! Try again."); + } // try + } // while(1) +} + +void BTAuthCompleteCallback(boolean success){ + if (success){ + confirmRequestDone = true; + Serial.println("Pairing success!!"); + } else { + Serial.println("Pairing failed, rejected by user!!"); + } +} + +void serial_response(){ + if (Serial.available()){ + SerialBT.write(Serial.read()); + } + if (SerialBT.available()){ + Serial.write(SerialBT.read()); + } + delay(20); +} + +void setup(){ + Serial.begin(115200); + SerialBT.enableSSP(INPUT_CAPABILITY, OUTPUT_CAPABILITY); // Must be called before begin + SerialBT.onConfirmRequest(BTConfirmRequestCallback); + SerialBT.onKeyRequest(BTKeyRequestCallback); + SerialBT.onAuthComplete(BTAuthCompleteCallback); + SerialBT.begin(deviceName); // Initiate Bluetooth device with name in parameter + //SerialBT.deleteAllBondedDevices(); // Uncomment this to delete paired devices; Must be called after begin + Serial.printf("The device started with name \"%s\", now you can pair it with Bluetooth!\n", deviceName); + if(INPUT_CAPABILITY and OUTPUT_CAPABILITY){ + Serial.println("Both devices will display randomly generated code and if they match authenticate pairing on both devices"); + }else if(not INPUT_CAPABILITY and not OUTPUT_CAPABILITY){ + Serial.println("Authenticate pairing on the other device. No PIN is used"); + }else if(not INPUT_CAPABILITY and OUTPUT_CAPABILITY){ + Serial.println("Authenticate pairing on the other device. No PIN is used"); + }else if(INPUT_CAPABILITY and not OUTPUT_CAPABILITY){ + Serial.println("After pairing is initiated you will be required to enter the passkey to the ESP32 device to authenticate\n > The Passkey will displayed on the other device"); + } +} + +void loop(){ + if (confirmRequestDone){ + serial_response(); + } else { + delay(1); // Feed the watchdog + } +} diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/SerialToSerialBT_SSP_pairing.ino b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/SerialToSerialBT_SSP_pairing.ino deleted file mode 100644 index c440545fcf3..00000000000 --- a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP_pairing/SerialToSerialBT_SSP_pairing.ino +++ /dev/null @@ -1,79 +0,0 @@ -//This example code is in the Public Domain (or CC0 licensed, at your option.) -//By Richard Li - 2020 -// -//This example creates a bridge between Serial and Classical Bluetooth (SPP with authentication) -//and also demonstrate that SerialBT have the same functionalities of a normal Serial - -#include "BluetoothSerial.h" - -#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it -#endif - -#if !defined(CONFIG_BT_SPP_ENABLED) -#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. -#endif - -BluetoothSerial SerialBT; -boolean confirmRequestPending = true; - -void BTConfirmRequestCallback(uint32_t numVal) -{ - confirmRequestPending = true; - Serial.println(numVal); -} - -void BTAuthCompleteCallback(boolean success) -{ - confirmRequestPending = false; - if (success) - { - Serial.println("Pairing success!!"); - } - else - { - Serial.println("Pairing failed, rejected by user!!"); - } -} - - -void setup() -{ - Serial.begin(115200); - SerialBT.enableSSP(); - SerialBT.onConfirmRequest(BTConfirmRequestCallback); - SerialBT.onAuthComplete(BTAuthCompleteCallback); - SerialBT.begin("ESP32test"); //Bluetooth device name - Serial.println("The device started, now you can pair it with bluetooth!"); -} - -void loop() -{ - if (confirmRequestPending) - { - if (Serial.available()) - { - int dat = Serial.read(); - if (dat == 'Y' || dat == 'y') - { - SerialBT.confirmReply(true); - } - else - { - SerialBT.confirmReply(false); - } - } - } - else - { - if (Serial.available()) - { - SerialBT.write(Serial.read()); - } - if (SerialBT.available()) - { - Serial.write(SerialBT.read()); - } - delay(20); - } -} diff --git a/libraries/BluetoothSerial/examples/bt_classic_device_discovery/bt_classic_device_discovery.ino b/libraries/BluetoothSerial/examples/bt_classic_device_discovery/bt_classic_device_discovery.ino index 8d44f262174..b4ee741924d 100644 --- a/libraries/BluetoothSerial/examples/bt_classic_device_discovery/bt_classic_device_discovery.ino +++ b/libraries/BluetoothSerial/examples/bt_classic_device_discovery/bt_classic_device_discovery.ino @@ -1,17 +1,17 @@ #include #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it + #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it #endif #if !defined(CONFIG_BT_SPP_ENABLED) -#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. + #error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. #endif BluetoothSerial SerialBT; -#define BT_DISCOVER_TIME 10000 +#define BT_DISCOVER_TIME 10000 static bool btScanAsync = true; @@ -19,7 +19,7 @@ static bool btScanSync = true; void btAdvertisedDeviceFound(BTAdvertisedDevice* pDevice) { - Serial.printf("Found a device asynchronously: %s\n", pDevice->toString().c_str()); + Serial.printf("Found a device asynchronously: %s\n", pDevice->toString().c_str()); } void setup() { @@ -29,7 +29,7 @@ void setup() { if (btScanAsync) { - Serial.print("Starting discoverAsync..."); + Serial.print("Starting asynchronous discovery... "); if (SerialBT.discoverAsync(btAdvertisedDeviceFound)) { Serial.println("Findings will be reported in \"btAdvertisedDeviceFound\""); delay(10000); @@ -37,12 +37,12 @@ void setup() { SerialBT.discoverAsyncStop(); Serial.println("stopped"); } else { - Serial.println("Error on discoverAsync f.e. not workin after a \"connect\""); + Serial.println("Error on discoverAsync f.e. not working after a \"connect\""); } } if (btScanSync) { - Serial.println("Starting discover..."); + Serial.println("Starting synchronous discovery... "); BTScanResults *pResults = SerialBT.discover(BT_DISCOVER_TIME); if (pResults) pResults->dump(&Serial); diff --git a/libraries/BluetoothSerial/examples/bt_remove_paired_devices/bt_remove_paired_devices.ino b/libraries/BluetoothSerial/examples/bt_remove_paired_devices/bt_remove_paired_devices.ino index 0d49fe46414..d6f6786828a 100755 --- a/libraries/BluetoothSerial/examples/bt_remove_paired_devices/bt_remove_paired_devices.ino +++ b/libraries/BluetoothSerial/examples/bt_remove_paired_devices/bt_remove_paired_devices.ino @@ -1,91 +1,74 @@ -//This example code is in the Public Domain (or CC0 licensed, at your option.) -//By Victor Tchistiak - 2019 +// This example code is in the Public Domain (or CC0 licensed, at your option.) +// Originally by Victor Tchistiak - 2019 +// Rewritten with new API by Tomas Pilny - 2023 // -//This example demonstrates reading and removing paired devices stored on the ESP32 flash memory -//Sometimes you may find your ESP32 device could not connect to the remote device despite -//many successful connections earlier. This is most likely a result of client replacing your paired -//device info with new one from other device. The BT clients store connection info for paired devices, -//but it is limited to a few devices only. When new device pairs and number of stored devices is exceeded, -//one of the previously paired devices would be replaced with new one. -//The only remedy is to delete this saved bound device from your device flash memory -//and pair with the other device again. -// -#include "esp_bt_main.h" -#include "esp_bt_device.h" -#include"esp_gap_bt_api.h" -#include "esp_err.h" +// This example demonstrates reading and removing paired devices stored on the ESP32 flash memory +// Sometimes you may find your ESP32 device could not connect to the remote device despite +// many successful connections earlier. This is most likely a result of client replacing your paired +// device info with new one from other device. The BT clients store connection info for paired devices, +// but it is limited to a few devices only. When new device pairs and number of stored devices is exceeded, +// one of the previously paired devices would be replaced with new one. +// The only remedy is to delete this saved bound device from your device flash memory +// and pair with the other device again. + +#include "BluetoothSerial.h" +//#include "esp_bt_device.h" #if !defined(CONFIG_BT_SPP_ENABLED) -#error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. + #error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip. #endif -#define REMOVE_BONDED_DEVICES 0 // <- Set to 0 to view all bonded devices addresses, set to 1 to remove - +#define REMOVE_BONDED_DEVICES true // <- Set to `false` to view all bonded devices addresses, set to `true` to remove #define PAIR_MAX_DEVICES 20 -uint8_t pairedDeviceBtAddr[PAIR_MAX_DEVICES][6]; -char bda_str[18]; - -bool initBluetooth() -{ - if(!btStart()) { - Serial.println("Failed to initialize controller"); - return false; - } - - if(esp_bluedroid_init() != ESP_OK) { - Serial.println("Failed to initialize bluedroid"); - return false; - } - - if(esp_bluedroid_enable() != ESP_OK) { - Serial.println("Failed to enable bluedroid"); - return false; - } - return true; -} +BluetoothSerial SerialBT; -char *bda2str(const uint8_t* bda, char *str, size_t size) -{ - if (bda == NULL || str == NULL || size < 18) { +char *bda2str(const uint8_t* bda, char *str, size_t size){ + if (bda == NULL || str == NULL || size < 18){ return NULL; } sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); return str; } - -void setup() { + +void setup(){ + char bda_str[18]; + uint8_t pairedDeviceBtAddr[PAIR_MAX_DEVICES][6]; Serial.begin(115200); - - initBluetooth(); - Serial.print("ESP32 bluetooth address: "); Serial.println(bda2str(esp_bt_dev_get_address(), bda_str, 18)); + + SerialBT.begin(); + Serial.printf("ESP32 bluetooth address: %s\n", SerialBT.getBtAddressString().c_str()); + // SerialBT.deleteAllBondedDevices(); // If you want just delete all, this is the way // Get the numbers of bonded/paired devices in the BT module - int count = esp_bt_gap_get_bond_device_num(); - if(!count) { - Serial.println("No bonded device found."); + int count = SerialBT.getNumberOfBondedDevices(); + if(!count){ + Serial.println("No bonded devices found."); } else { - Serial.print("Bonded device count: "); Serial.println(count); - if(PAIR_MAX_DEVICES < count) { - count = PAIR_MAX_DEVICES; - Serial.print("Reset bonded device count: "); Serial.println(count); + Serial.printf("Bonded device count: %d\n", count); + if(PAIR_MAX_DEVICES < count){ + count = PAIR_MAX_DEVICES; + Serial.printf("Reset %d bonded devices\n", count); } - esp_err_t tError = esp_bt_gap_get_bond_device_list(&count, pairedDeviceBtAddr); - if(ESP_OK == tError) { - for(int i = 0; i < count; i++) { - Serial.print("Found bonded device # "); Serial.print(i); Serial.print(" -> "); - Serial.println(bda2str(pairedDeviceBtAddr[i], bda_str, 18)); - if(REMOVE_BONDED_DEVICES) { - esp_err_t tError = esp_bt_gap_remove_bond_device(pairedDeviceBtAddr[i]); - if(ESP_OK == tError) { - Serial.print("Removed bonded device # "); - } else { - Serial.print("Failed to remove bonded device # "); - } - Serial.println(i); + count = SerialBT.getBondedDevices(count, pairedDeviceBtAddr); + char rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; + if(count > 0){ + for(int i = 0; i < count; i++){ + SerialBT.requestRemoteName(pairedDeviceBtAddr[i]); + while(!SerialBT.readRemoteName(rmt_name)){ + delay(1); // Wait for response with the device name } - } - } - } + Serial.printf("Found bonded device #%d BDA:%s; Name:\"%s\"\n", i, bda2str(pairedDeviceBtAddr[i], bda_str, 18), rmt_name); + SerialBT.invalidateRemoteName(); // Allows waiting for next reading + if(REMOVE_BONDED_DEVICES){ + if(SerialBT.deleteBondedDevice(pairedDeviceBtAddr[i])){ + Serial.printf("Removed bonded device # %d\n", i); + } else { + Serial.printf("Failed to remove bonded device # %d", i); + } // if(ESP_OK == tError) + } // if(REMOVE_BONDED_DEVICES) + } // for(int i = 0; i < count; i++) + } // if(ESP_OK == tError) + } // if(!count) } - + void loop() {} diff --git a/libraries/BluetoothSerial/src/BTScan.h b/libraries/BluetoothSerial/src/BTScan.h index 2851fdd3626..2fa1b65c50a 100644 --- a/libraries/BluetoothSerial/src/BTScan.h +++ b/libraries/BluetoothSerial/src/BTScan.h @@ -20,21 +20,21 @@ class BTAdvertisedDeviceSet; class BTScanResults { public: - virtual ~BTScanResults() = default; + virtual ~BTScanResults() = default; - virtual void dump(Print *print = nullptr); - virtual int getCount(); + virtual void dump(Print *print = nullptr); + virtual int getCount(); virtual BTAdvertisedDevice* getDevice(int i); }; class BTScanResultsSet : public BTScanResults { public: - void dump(Print *print = nullptr); - int getCount(); - BTAdvertisedDevice* getDevice(int i); + void dump(Print *print = nullptr); + int getCount(); + BTAdvertisedDevice* getDevice(int i); - bool add(BTAdvertisedDeviceSet advertisedDevice, bool unique = true); - void clear(); + bool add(BTAdvertisedDeviceSet advertisedDevice, bool unique = true); + void clear(); std::map m_vectorAdvertisedDevices; }; diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.cpp b/libraries/BluetoothSerial/src/BluetoothSerial.cpp index 52254437923..419809d3f25 100644 --- a/libraries/BluetoothSerial/src/BluetoothSerial.cpp +++ b/libraries/BluetoothSerial/src/BluetoothSerial.cpp @@ -24,10 +24,11 @@ #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) #ifdef ARDUINO_ARCH_ESP32 -#include "esp32-hal-log.h" + #include "esp32-hal-log.h" #endif #include "BluetoothSerial.h" +#include "BTAdvertisedDevice.h" #include "esp_bt.h" #include "esp_bt_main.h" @@ -58,7 +59,10 @@ static esp_spp_cb_t * custom_spp_callback = NULL; static BluetoothSerialDataCb custom_data_callback = NULL; static esp_bd_addr_t current_bd_addr; static ConfirmRequestCb confirm_request_callback = NULL; +static KeyRequestCb key_request_callback = NULL; static AuthCompleteCb auth_complete_callback = NULL; +static bool _rmt_name_valid = false; +static uint8_t _rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1] = {0}; #define INQ_LEN 0x10 #define INQ_NUM_RSPS 20 @@ -68,10 +72,13 @@ static esp_bd_addr_t _peer_bd_addr; static char _remote_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; static bool _isRemoteAddressSet; static bool _isMaster; -static esp_bt_pin_code_t _pin_code; -static int _pin_len; -static bool _isPinSet; -static bool _enableSSP; +#ifdef CONFIG_BT_SSP_ENABLED + static bool _enableSSP; + static bool _IO_CAP_INPUT; + static bool _IO_CAP_OUTPUT; +#endif +esp_bt_pin_code_t _pin_code = {0}; +uint8_t _pin_code_len = 0; // Number of valid Bytes in the esp_bt_pin_code_t array static esp_spp_sec_t _sec_mask; static esp_spp_role_t _role; // start connect on ESP_SPP_DISCOVERY_COMP_EVT or save entry for getChannels @@ -139,22 +146,6 @@ static bool get_name_from_eir(uint8_t *eir, char *bdname, uint8_t *bdname_len) return false; } -static bool btSetPin() { - esp_bt_pin_type_t pin_type; - if (_isPinSet) { - if (_pin_len) { - log_i("pin set"); - pin_type = ESP_BT_PIN_TYPE_FIXED; - } else { - _isPinSet = false; - log_i("pin reset"); - pin_type = ESP_BT_PIN_TYPE_VARIABLE; // pin_code would be ignored (default) - } - return (esp_bt_gap_set_pin(pin_type, _pin_len, _pin_code) == ESP_OK); - } - return false; -} - static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){ if(!data || !len){ log_w("No data provided"); @@ -259,7 +250,7 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { switch (event) { - case ESP_SPP_INIT_EVT: + case ESP_SPP_INIT_EVT: // Enum 0 - When SPP is initialized log_i("ESP_SPP_INIT_EVT"); #ifdef ESP_IDF_VERSION_MAJOR esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); @@ -273,80 +264,11 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) xEventGroupSetBits(_spp_event_group, SPP_RUNNING); break; - case ESP_SPP_SRV_OPEN_EVT://Server connection open - if (param->srv_open.status == ESP_SPP_SUCCESS) { - log_i("ESP_SPP_SRV_OPEN_EVT: %u", _spp_client); - if (!_spp_client){ - _spp_client = param->srv_open.handle; - _spp_tx_buffer_len = 0; - } else { - secondConnectionAttempt = true; - esp_spp_disconnect(param->srv_open.handle); - } - xEventGroupClearBits(_spp_event_group, SPP_DISCONNECTED); - xEventGroupSetBits(_spp_event_group, SPP_CONNECTED); - } else { - log_e("ESP_SPP_SRV_OPEN_EVT Failed!, status:%d", param->srv_open.status); - } - break; - - case ESP_SPP_CLOSE_EVT://Client connection closed - if ((param->close.async == false && param->close.status == ESP_SPP_SUCCESS) || param->close.async) { - log_i("ESP_SPP_CLOSE_EVT status:%d handle:%d close_by_remote:%d attempt %u", param->close.status, - param->close.handle, param->close.async, secondConnectionAttempt); - if(secondConnectionAttempt) { - secondConnectionAttempt = false; - } else { - _spp_client = 0; - xEventGroupSetBits(_spp_event_group, SPP_DISCONNECTED); - xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); - xEventGroupSetBits(_spp_event_group, SPP_CLOSED); - xEventGroupClearBits(_spp_event_group, SPP_CONNECTED); - } - } else { - log_e("ESP_SPP_CLOSE_EVT failed!, status:%d", param->close.status); - } - break; - - case ESP_SPP_CONG_EVT://connection congestion status changed - if(param->cong.cong){ - xEventGroupClearBits(_spp_event_group, SPP_CONGESTED); - } else { - xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); - } - log_v("ESP_SPP_CONG_EVT: %s", param->cong.cong?"CONGESTED":"FREE"); - break; - - case ESP_SPP_WRITE_EVT://write operation completed - if (param->write.status == ESP_SPP_SUCCESS) { - if(param->write.cong){ - xEventGroupClearBits(_spp_event_group, SPP_CONGESTED); - } - log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":""); - } else { - log_e("ESP_SPP_WRITE_EVT failed!, status:%d", param->write.status); - } - xSemaphoreGive(_spp_tx_done);//we can try to send another packet - break; - - case ESP_SPP_DATA_IND_EVT://connection received data - log_v("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); //for low level debug - //ets_printf("r:%u\n", param->data_ind.len); - - if(custom_data_callback){ - custom_data_callback(param->data_ind.data, param->data_ind.len); - } else if (_spp_rx_queue != NULL){ - for (int i = 0; i < param->data_ind.len; i++){ - if(xQueueSend(_spp_rx_queue, param->data_ind.data + i, (TickType_t)0) != pdTRUE){ - log_e("RX Full! Discarding %u bytes", param->data_ind.len - i); - break; - } - } - } + case ESP_SPP_UNINIT_EVT: // Enum 1 - When SPP is deinitialized + log_i("ESP_SPP_UNINIT_EVT: SPP is deinitialized"); break; - case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete + case ESP_SPP_DISCOVERY_COMP_EVT: // Enum 8 - When SDP discovery complete log_i("ESP_SPP_DISCOVERY_COMP_EVT num=%d", param->disc_comp.scn_num); if (param->disc_comp.status == ESP_SPP_SUCCESS) { for(int i=0; i < param->disc_comp.scn_num; i++) { @@ -380,7 +302,7 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) xEventGroupSetBits(_bt_event_group, BT_SDP_COMPLETED); break; - case ESP_SPP_OPEN_EVT://Client connection open + case ESP_SPP_OPEN_EVT: // Enum 26 - When SPP Client connection open log_i("ESP_SPP_OPEN_EVT"); if (!_spp_client){ _spp_client = param->open.handle; @@ -393,11 +315,29 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); break; - case ESP_SPP_START_EVT://server started + case ESP_SPP_CLOSE_EVT: // Enum 27 - When SPP connection closed + if ((param->close.async == false && param->close.status == ESP_SPP_SUCCESS) || param->close.async) { + log_i("ESP_SPP_CLOSE_EVT status:%d handle:%d close_by_remote:%d attempt %u", param->close.status, + param->close.handle, param->close.async, secondConnectionAttempt); + if(secondConnectionAttempt) { + secondConnectionAttempt = false; + } else { + _spp_client = 0; + xEventGroupSetBits(_spp_event_group, SPP_DISCONNECTED); + xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); + xEventGroupSetBits(_spp_event_group, SPP_CLOSED); + xEventGroupClearBits(_spp_event_group, SPP_CONNECTED); + } + } else { + log_e("ESP_SPP_CLOSE_EVT failed!, status:%d", param->close.status); + } + break; + + case ESP_SPP_START_EVT: // Enum 28 - When SPP server started log_i("ESP_SPP_START_EVT"); break; - case ESP_SPP_CL_INIT_EVT://client initiated a connection + case ESP_SPP_CL_INIT_EVT: // Enum 29 - When SPP client initiated a connection if (param->cl_init.status == ESP_SPP_SUCCESS) { log_i("ESP_SPP_CL_INIT_EVT handle:%d sec_id:%d", param->cl_init.handle, param->cl_init.sec_id); } else { @@ -405,8 +345,75 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) } break; + case ESP_SPP_DATA_IND_EVT: // Enum 30 - When SPP connection received data, only for ESP_SPP_MODE_CB + log_v("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); //for low level debug + //ets_printf("r:%u\n", param->data_ind.len); + + if(custom_data_callback){ + custom_data_callback(param->data_ind.data, param->data_ind.len); + } else if (_spp_rx_queue != NULL){ + for (int i = 0; i < param->data_ind.len; i++){ + if(xQueueSend(_spp_rx_queue, param->data_ind.data + i, (TickType_t)0) != pdTRUE){ + log_e("RX Full! Discarding %u bytes", param->data_ind.len - i); + break; + } + } + } + break; + + case ESP_SPP_CONG_EVT: // Enum 31 - When SPP connection congestion status changed, only for ESP_SPP_MODE_CB + if(param->cong.cong){ + xEventGroupClearBits(_spp_event_group, SPP_CONGESTED); + } else { + xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); + } + log_v("ESP_SPP_CONG_EVT: %s", param->cong.cong?"CONGESTED":"FREE"); + break; + + case ESP_SPP_WRITE_EVT: // Enum 33 - When SPP write operation completes, only for ESP_SPP_MODE_CB + if (param->write.status == ESP_SPP_SUCCESS) { + if(param->write.cong){ + xEventGroupClearBits(_spp_event_group, SPP_CONGESTED); + } + log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":""); + } else { + log_e("ESP_SPP_WRITE_EVT failed!, status:%d", param->write.status); + } + xSemaphoreGive(_spp_tx_done);//we can try to send another packet + break; + + case ESP_SPP_SRV_OPEN_EVT: // Enum 34 - When SPP Server connection open + if (param->srv_open.status == ESP_SPP_SUCCESS) { + log_i("ESP_SPP_SRV_OPEN_EVT: %u", _spp_client); + if (!_spp_client){ + _spp_client = param->srv_open.handle; + _spp_tx_buffer_len = 0; + } else { + secondConnectionAttempt = true; + esp_spp_disconnect(param->srv_open.handle); + } + xEventGroupClearBits(_spp_event_group, SPP_DISCONNECTED); + xEventGroupSetBits(_spp_event_group, SPP_CONNECTED); + } else { + log_e("ESP_SPP_SRV_OPEN_EVT Failed!, status:%d", param->srv_open.status); + } + break; + + case ESP_SPP_SRV_STOP_EVT: // Enum 35 - When SPP server stopped + log_i("ESP_SPP_SRV_STOP_EVT"); + break; + + case ESP_SPP_VFS_REGISTER_EVT: // Enum 36 - When SPP VFS register + log_i("ESP_SPP_VFS_REGISTER_EVT"); + break; + + case ESP_SPP_VFS_UNREGISTER_EVT: // Enum 37 - When SPP VFS unregister + log_i("ESP_SPP_VFS_UNREGISTER_EVT"); + break; + default: - log_i("ESP_SPP_* event unhandled %d", event); + log_i("ESP_SPP_* event #%d unhandled", event); break; } if(custom_spp_callback)(*custom_spp_callback)(event, param); @@ -416,10 +423,11 @@ void BluetoothSerial::onData(BluetoothSerialDataCb cb){ custom_data_callback = cb; } + static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) { switch(event){ - case ESP_BT_GAP_DISC_RES_EVT: { + case ESP_BT_GAP_DISC_RES_EVT: { // Enum 0 - Device discovery result event log_i("ESP_BT_GAP_DISC_RES_EVT properties=%d", param->disc_res.num_prop); #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) char bda_str[18]; @@ -430,21 +438,7 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa char peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; for (int i = 0; i < param->disc_res.num_prop; i++) { switch(param->disc_res.prop[i].type) { - case ESP_BT_GAP_DEV_PROP_EIR: - if (get_name_from_eir((uint8_t*)param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)) { - log_i("ESP_BT_GAP_DISC_RES_EVT : EIR : %s : %d", peer_bdname, peer_bdname_len); - if (strlen(_remote_name) == peer_bdname_len - && strncmp(peer_bdname, _remote_name, peer_bdname_len) == 0) { - log_v("ESP_BT_GAP_DISC_RES_EVT : SPP_START_DISCOVERY_EIR : %s", peer_bdname, peer_bdname_len); - _isRemoteAddressSet = true; - memcpy(_peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN); - esp_bt_gap_cancel_discovery(); - esp_spp_start_discovery(_peer_bd_addr); - } - } - break; - - case ESP_BT_GAP_DEV_PROP_BDNAME: + case ESP_BT_GAP_DEV_PROP_BDNAME: // Enum 1 - Bluetooth device name, value type is int8_t [] peer_bdname_len = param->disc_res.prop[i].len; memcpy(peer_bdname, param->disc_res.prop[i].val, peer_bdname_len); peer_bdname_len--; // len includes 0 terminator @@ -456,10 +450,10 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa memcpy(_peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN); esp_bt_gap_cancel_discovery(); esp_spp_start_discovery(_peer_bd_addr); - } + } break; - case ESP_BT_GAP_DEV_PROP_COD: + case ESP_BT_GAP_DEV_PROP_COD: // Enum 2 - Class of Device, value type is uint32_t if (param->disc_res.prop[i].len <= sizeof(int)) { uint32_t cod = 0; memcpy(&cod, param->disc_res.prop[i].val, param->disc_res.prop[i].len); @@ -470,7 +464,7 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa } break; - case ESP_BT_GAP_DEV_PROP_RSSI: + case ESP_BT_GAP_DEV_PROP_RSSI: // Enum 3 - Received Signal strength Indication, value type is int8_t, ranging from -128 to 127 if (param->disc_res.prop[i].len <= sizeof(int)) { uint8_t rssi = 0; memcpy(&rssi, param->disc_res.prop[i].val, param->disc_res.prop[i].len); @@ -480,7 +474,21 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa log_d("ESP_BT_GAP_DEV_PROP_RSSI invalid RSSI: Value size larger than integer"); } break; - + + case ESP_BT_GAP_DEV_PROP_EIR: // Enum 4 - Extended Inquiry Response, value type is uint8_t [] + if (get_name_from_eir((uint8_t*)param->disc_res.prop[i].val, peer_bdname, &peer_bdname_len)) { + log_i("ESP_BT_GAP_DISC_RES_EVT : EIR : %s : %d", peer_bdname, peer_bdname_len); + if (strlen(_remote_name) == peer_bdname_len + && strncmp(peer_bdname, _remote_name, peer_bdname_len) == 0) { + log_v("ESP_BT_GAP_DISC_RES_EVT : SPP_START_DISCOVERY_EIR : %s", peer_bdname, peer_bdname_len); + _isRemoteAddressSet = true; + memcpy(_peer_bd_addr, param->disc_res.bda, ESP_BD_ADDR_LEN); + esp_bt_gap_cancel_discovery(); + esp_spp_start_discovery(_peer_bd_addr); + } + } + break; + default: log_i("ESP_BT_GAP_DISC_RES_EVT unknown property [%d]:type:%d", i, param->disc_res.prop[i].type); break; @@ -498,7 +506,7 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa } break; - case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: + case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: // Enum 1 - Discovery state changed event if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) { log_i("ESP_BT_GAP_DISC_STATE_CHANGED_EVT stopped"); xEventGroupClearBits(_bt_event_group, BT_DISCOVERY_RUNNING); @@ -510,15 +518,15 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa } break; - case ESP_BT_GAP_RMT_SRVCS_EVT: + case ESP_BT_GAP_RMT_SRVCS_EVT: // Enum 2 - Get remote services event log_i( "ESP_BT_GAP_RMT_SRVCS_EVT: status = %d, num_uuids = %d", param->rmt_srvcs.stat, param->rmt_srvcs.num_uuids); break; - case ESP_BT_GAP_RMT_SRVC_REC_EVT: + case ESP_BT_GAP_RMT_SRVC_REC_EVT: // Enum 3 - Get remote service record event log_i("ESP_BT_GAP_RMT_SRVC_REC_EVT: status = %d", param->rmt_srvc_rec.stat); break; - case ESP_BT_GAP_AUTH_CMPL_EVT: + case ESP_BT_GAP_AUTH_CMPL_EVT: // Enum 4 - Authentication complete event if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { log_v("authentication success: %s", param->auth_cmpl.device_name); if (auth_complete_callback) { @@ -531,58 +539,89 @@ static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa } } break; - - case ESP_BT_GAP_PIN_REQ_EVT: - // default pairing pins - log_i("ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); - if (param->pin_req.min_16_digit) { - log_i("Input pin code: 0000 0000 0000 0000"); - esp_bt_pin_code_t pin_code; - memset(pin_code, '0', ESP_BT_PIN_CODE_LEN); - esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code); + case ESP_BT_GAP_PIN_REQ_EVT: // Enum 5 - Legacy Pairing Pin code request + log_i("ESP_BT_GAP_PIN_REQ_EVT (min_16_digit=%d)", param->pin_req.min_16_digit); + if (param->pin_req.min_16_digit && _pin_code_len < 16) { + esp_bt_gap_pin_reply(param->pin_req.bda, false, 0, NULL); } else { - log_i("Input pin code: 1234"); - esp_bt_pin_code_t pin_code; - memcpy(pin_code, "1234", 4); - esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code); + //log_i("Input pin code: \"%s\"=0x%x", _pin_code); + log_i("Input pin code: \"%.*s\"=0x%x", _pin_code_len, _pin_code, *(int*)_pin_code); + esp_bt_gap_pin_reply(param->pin_req.bda, true, _pin_code_len, _pin_code); } break; - - case ESP_BT_GAP_CFM_REQ_EVT: +#ifdef CONFIG_BT_SSP_ENABLED + case ESP_BT_GAP_CFM_REQ_EVT: // Enum 6 - Security Simple Pairing User Confirmation request. log_i("ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); if (confirm_request_callback) { memcpy(current_bd_addr, param->cfm_req.bda, sizeof(esp_bd_addr_t)); confirm_request_callback(param->cfm_req.num_val); } else { - esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); + log_w("ESP_BT_GAP_CFM_REQ_EVT: confirm_request_callback does not exist - refusing pairing"); + esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, false); } break; +#endif - case ESP_BT_GAP_KEY_NOTIF_EVT: + case ESP_BT_GAP_KEY_NOTIF_EVT: // Enum 7 - Security Simple Pairing Passkey Notification log_i("ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); break; - case ESP_BT_GAP_KEY_REQ_EVT: +#ifdef CONFIG_BT_SSP_ENABLED + case ESP_BT_GAP_KEY_REQ_EVT: // Enum 8 - Security Simple Pairing Passkey request log_i("ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); + if (key_request_callback) { + memcpy(current_bd_addr, param->cfm_req.bda, sizeof(esp_bd_addr_t)); + key_request_callback(); + } else { + log_w("ESP_BT_GAP_KEY_REQ_EVT: key_request_callback does not exist - refuseing pairing"); + esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, false); + } break; +#endif - case ESP_BT_GAP_CONFIG_EIR_DATA_EVT: + case ESP_BT_GAP_READ_RSSI_DELTA_EVT: // Enum 9 - Read rssi event + log_i("ESP_BT_GAP_READ_RSSI_DELTA_EVT Read rssi event"); + break; + case ESP_BT_GAP_CONFIG_EIR_DATA_EVT: // Enum 10 - Config EIR data event log_i("ESP_BT_GAP_CONFIG_EIR_DATA_EVT: stat:%d num:%d", param->config_eir_data.stat, param->config_eir_data.eir_type_num); break; - case ESP_BT_GAP_READ_REMOTE_NAME_EVT: + case ESP_BT_GAP_SET_AFH_CHANNELS_EVT: // Enum 11 - Set AFH channels event + log_i("ESP_BT_GAP_SET_AFH_CHANNELS_EVT Set AFH channels event"); + break; + + case ESP_BT_GAP_READ_REMOTE_NAME_EVT: // Enum 12 - Read Remote Name event if (param->read_rmt_name.stat == ESP_BT_STATUS_SUCCESS ) { log_i("ESP_BT_GAP_READ_REMOTE_NAME_EVT: %s", param->read_rmt_name.rmt_name); + memcpy(_rmt_name, param->read_rmt_name.rmt_name, ESP_BT_GAP_MAX_BDNAME_LEN + 1); + _rmt_name_valid = true; } else { log_i("ESP_BT_GAP_READ_REMOTE_NAME_EVT: no success stat:%d", param->read_rmt_name.stat); } break; - case ESP_BT_GAP_MODE_CHG_EVT: + + case ESP_BT_GAP_MODE_CHG_EVT: // Enum 13 log_i("ESP_BT_GAP_MODE_CHG_EVT: mode: %d", param->mode_chg.mode); break; + case ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT: // Enum - 14 remove bond device complete event + log_i("ESP_BT_GAP_REMOVE_BOND_DEV_COMPLETE_EVT remove bond device complete event"); + break; + + case ESP_BT_GAP_QOS_CMPL_EVT: // Enum 15 - QOS complete event + log_i("ESP_BT_GAP_QOS_CMPL_EVT QOS complete event"); + break; + + case ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT: // Enum 16 - ACL connection complete status event + log_i("ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT ACL connection complete status event"); + break; + + case ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT: // Enum 17 - ACL disconnection complete status event + log_i("ESP_BT_GAP_ACL_DISCONN_CMPL_STAT_EVT ACL disconnection complete status event: reason %d, handle %d", param->acl_disconn_cmpl_stat.reason, param->acl_disconn_cmpl_stat.handle); + break; + default: log_i("ESP-BT_GAP_* unknown message: %d", event); break; @@ -678,24 +717,26 @@ static bool _init_bt(const char *deviceName) return false; } - // if (esp_bt_sleep_disable() != ESP_OK){ - // log_e("esp_bt_sleep_disable failed"); - // } - log_i("device name set"); esp_bt_dev_set_device_name(deviceName); - if (_isPinSet) { - log_i("pin set"); - btSetPin(); - } - +#ifdef CONFIG_BT_SSP_ENABLED if (_enableSSP) { log_i("Simple Secure Pairing"); esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; - esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; + esp_bt_io_cap_t iocap; + if(_IO_CAP_INPUT && _IO_CAP_OUTPUT){ + iocap = ESP_BT_IO_CAP_IO; // Display with prompt + }else if(!_IO_CAP_INPUT && _IO_CAP_OUTPUT){ + iocap = ESP_BT_IO_CAP_OUT; // DisplayOnly + }else if(_IO_CAP_INPUT && !_IO_CAP_OUTPUT){ + iocap = ESP_BT_IO_CAP_IN; // Input only + }else if(!_IO_CAP_INPUT && !_IO_CAP_OUTPUT){ + iocap = ESP_BT_IO_CAP_NONE; // No input/output + } esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); } +#endif // the default BTA_DM_COD_LOUDSPEAKER does not work with the macOS BT stack esp_bt_cod_t cod; @@ -871,11 +912,22 @@ void BluetoothSerial::end() _stop_bt(); } +#ifdef CONFIG_BT_SSP_ENABLED void BluetoothSerial::onConfirmRequest(ConfirmRequestCb cb) { confirm_request_callback = cb; } +void BluetoothSerial::onKeyRequest(KeyRequestCb cb) +{ + key_request_callback = cb; +} + +void BluetoothSerial::respondPasskey(uint32_t passkey){ + esp_bt_gap_ssp_passkey_reply(current_bd_addr, true, passkey); +} +#endif + void BluetoothSerial::onAuthComplete(AuthCompleteCb cb) { auth_complete_callback = cb; @@ -883,7 +935,7 @@ void BluetoothSerial::onAuthComplete(AuthCompleteCb cb) void BluetoothSerial::confirmReply(boolean confirm) { - esp_bt_gap_ssp_confirm_reply(current_bd_addr, confirm); + esp_bt_gap_ssp_confirm_reply(current_bd_addr, confirm); } @@ -893,32 +945,54 @@ esp_err_t BluetoothSerial::register_callback(esp_spp_cb_t * callback) return ESP_OK; } -//Simple Secure Pairing +#ifdef CONFIG_BT_SSP_ENABLED +// Enable Simple Secure Pairing (using generated PIN) +// This must be called before calling begin, otherwise has no effect! void BluetoothSerial::enableSSP() { + if(isReady(false, READY_TIMEOUT)){ + log_i("Attempted to enable SSP for already initialized driver. Restart to take effect with end() followed by begin()"); + return; + } _enableSSP = true; + _IO_CAP_INPUT = true; + _IO_CAP_OUTPUT = true; } -/* - * Set default parameters for Legacy Pairing - * Use fixed pin code -*/ -bool BluetoothSerial::setPin(const char *pin) { - log_i("pin: %s", pin); - bool isEmpty = !(pin && *pin); - if (isEmpty && !_isPinSet) { - return true; // nothing to do - } else if (!isEmpty){ - _pin_len = strlen(pin); - memcpy(_pin_code, pin, _pin_len); - } else { - _pin_len = 0; // resetting pin to none (default) - } - _pin_code[_pin_len] = 0; - _isPinSet = true; - if (isReady(false, READY_TIMEOUT)) { - btSetPin(); + +// Enable Simple Secure Pairing (using generated PIN) +// This must be called before calling begin, otherwise has no effect! +// Behavior description: +// When both Input and Output are false only the other device authenticates pairing without any pin. +// When Output is true and Input is false only the other device authenticates pairing without any pin. +// When both Input and Output are true both devices display randomly generated code and if they match authenticate pairing on both devices +// - This must be implemented by registering callback via onConfirmRequest() and in this callback request user input and call confirmReply(true); if the authenticated +// otherwise call `confirmReply(false)` to reject the pairing. +// When Input is true and Output is false User will be required to input the passkey to the ESP32 device to authenticate. +// - This must be implemented by registering callback via onKeyRequest() and in this callback the entered passkey will be responded via respondPasskey(passkey); +void BluetoothSerial::enableSSP(bool inputCpability, bool outputCapability) { + log_i("Enabling SSP: input capability=%d; output capability=%d", inputCpability, outputCapability); + _enableSSP = true; + _IO_CAP_INPUT = inputCpability; + _IO_CAP_OUTPUT = outputCapability; +} + +// Disable Simple Secure Pairing (using generated PIN) +// This must be called before calling begin, otherwise has no effect! +void BluetoothSerial::disableSSP() { + _enableSSP = false; +} + +#else + +bool BluetoothSerial::setPin(const char *pin, uint8_t pin_code_len){ + if(pin_code_len == 0 || pin_code_len > 16){ + log_e("PIN code must be 1-16 Bytes long! Called with length %d", pin_code_len); + return false; } - return true; + _pin_code_len = pin_code_len; + memcpy(_pin_code, pin, pin_code_len); + return (esp_bt_gap_set_pin(ESP_BT_PIN_TYPE_FIXED, _pin_code_len, _pin_code) == ESP_OK); } +#endif bool BluetoothSerial::connect(String remoteName) { @@ -931,9 +1005,9 @@ bool BluetoothSerial::connect(String remoteName) } disconnect(); _doConnect = true; - _isRemoteAddressSet = false; - _sec_mask = ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE; - _role = ESP_SPP_ROLE_MASTER; + _isRemoteAddressSet = true; + _sec_mask = ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE; + _role = ESP_SPP_ROLE_MASTER; strncpy(_remote_name, remoteName.c_str(), ESP_BT_GAP_MAX_BDNAME_LEN); _remote_name[ESP_BT_GAP_MAX_BDNAME_LEN] = 0; log_i("master : remoteName"); @@ -974,8 +1048,8 @@ bool BluetoothSerial::connect(uint8_t remoteAddress[], int channel, esp_spp_sec_ _doConnect = true; _remote_name[0] = 0; _isRemoteAddressSet = true; - _sec_mask = sec_mask; - _role = role; + _sec_mask = sec_mask; + _role = role; memcpy(_peer_bd_addr, remoteAddress, ESP_BD_ADDR_LEN); log_i("master : remoteAddress"); xEventGroupClearBits(_spp_event_group, SPP_CLOSED); @@ -987,7 +1061,7 @@ bool BluetoothSerial::connect(uint8_t remoteAddress[], int channel, esp_spp_sec_ channel); #endif if(esp_spp_connect(sec_mask, role, channel, _peer_bd_addr) != ESP_OK ) { - log_e("spp connect failed"); + log_e("spp connect failed"); retval = false; } else { retval = waitForConnect(READY_TIMEOUT); @@ -1088,14 +1162,16 @@ bool BluetoothSerial::isReady(bool checkMaster, int timeout) { /** * @brief RemoteName or address are not allowed to be set during discovery - * (otherwhise it might connect automatically and stop discovery) + * (otherwise it might connect automatically and stop discovery) * @param[in] timeoutMs can range from MIN_INQ_TIME to MAX_INQ_TIME * @return in case of Error immediately Empty ScanResults. */ BTScanResults* BluetoothSerial::discover(int timeoutMs) { scanResults.clear(); - if (timeoutMs < MIN_INQ_TIME || timeoutMs > MAX_INQ_TIME || strlen(_remote_name) || _isRemoteAddressSet) + if (timeoutMs < MIN_INQ_TIME || timeoutMs > MAX_INQ_TIME){ + log_e("Timeout out of bounds: MIN=%d; MAX=%d; requested=%d", MIN_INQ_TIME, MAX_INQ_TIME, timeoutMs); return nullptr; + } int timeout = timeoutMs / INQ_TIME; log_i("discover::disconnect"); disconnect(); @@ -1112,11 +1188,11 @@ BTScanResults* BluetoothSerial::discover(int timeoutMs) { /** * @brief RemoteName or address are not allowed to be set during discovery - * (otherwhise it might connect automatically and stop discovery) + * (otherwise it might connect automatically and stop discovery) * @param[in] cb called when a [b]new[/b] device has been discovered * @param[in] timeoutMs can be 0 or range from MIN_INQ_TIME to MAX_INQ_TIME * - * @return Wheter start was successfull or problems with params + * @return Whether start was successful or problems with params */ bool BluetoothSerial::discoverAsync(BTAdvertisedDeviceCb cb, int timeoutMs) { scanResults.clear(); @@ -1139,7 +1215,7 @@ void BluetoothSerial::discoverAsyncStop() { advertisedDeviceCb = nullptr; } -/** @brief Clears scanresult entries */ +/** @brief Clears scanResult entries */ void BluetoothSerial::discoverClear() { scanResults.clear(); } @@ -1211,4 +1287,108 @@ BTAddress BluetoothSerial::getBtAddressObject() { String BluetoothSerial::getBtAddressString() { return getBtAddressObject().toString(true); } -#endif + +// Send a request to the remote device defined by the remoteAddress to send back its name. +// The name will be read by background task and stored. It can be later read with radRemoteName() +void BluetoothSerial::requestRemoteName(uint8_t remoteAddress[]){ + if(isReady(false, READY_TIMEOUT)){ + esp_bt_gap_read_remote_name(remoteAddress); + } +} + +// If remote name is valid (was already received) this function will copy the name to the aprameter rmt_name +// The buffer must have size at least ESP_BT_GAP_MAX_BDNAME_LEN + 1 +// If the name is valid the function will return true +// If the name is not valid (was not read yet) returns false +bool BluetoothSerial::readRemoteName(char rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1]){ + if(_rmt_name_valid){ + memcpy(rmt_name, _rmt_name, ESP_BT_GAP_MAX_BDNAME_LEN + 1); + return true; + } + return false; +} + +// Set validity of remote name before reading name from different device +void BluetoothSerial::invalidateRemoteName(){ + _rmt_name_valid = false; +} + +int BluetoothSerial::getNumberOfBondedDevices(){ + return esp_bt_gap_get_bond_device_num(); +} + +// Accepts the maximum number of devices that can fit in given array dev_list. +// Create you list this way: esp_bd_addr_t dev_list[dev_num]; +// Returns number of retrieved devices (on error returns 0) +int BluetoothSerial::getBondedDevices(uint dev_num, esp_bd_addr_t *dev_list){ + // typedef uint8_t esp_bd_addr_t[ESP_BD_ADDR_LEN] + if(dev_list == NULL){ + log_e("Device list is NULL"); + return 0; + } + if(dev_num == 0){ + log_e("Device number must be larger than 0!"); + return 0; + } + int _dev_num = dev_num; + esp_bt_gap_get_bond_device_list(&_dev_num, dev_list); + return _dev_num; +} + +bool BluetoothSerial::deleteBondedDevice(uint8_t *remoteAddress){ + esp_err_t ret = esp_bt_gap_remove_bond_device(remoteAddress); + if(ret == ESP_OK){ + return true; + }else{ + return false; + } +} + +void BluetoothSerial::deleteAllBondedDevices(){ + if(!isReady(false, READY_TIMEOUT)){ + log_w("Attempted to drop cache for uninitialized driver. First call begin()"); + return; + } + + int expected_dev_num = esp_bt_gap_get_bond_device_num(); + if(expected_dev_num == 0){ + log_i("No devices in cache."); + return; + } else { + log_d("Found %d bonded devices", expected_dev_num); + } + esp_err_t ret; + + // typedef uint8_t esp_bd_addr_t[ESP_BD_ADDR_LEN] // ESP_BD_ADDR_LEN = 6 + esp_bd_addr_t *dev_list = NULL; + log_d("Allocate buffer: sizeof(esp_bd_addr_t)=%d * expected_dev_num=%d", sizeof(esp_bd_addr_t), expected_dev_num); + dev_list = (esp_bd_addr_t*) malloc(sizeof(esp_bd_addr_t) * expected_dev_num); + if(dev_list == NULL){ + log_e("Could not allocated BT device buffer!"); + return; + } + //uint8_t dev_list [20][6]; + + int dev_num; + ret = esp_bt_gap_get_bond_device_list(&dev_num, dev_list); + log_d("esp_bt_gap_get_bond_device_list ret = %d", ret); + if(ret == ESP_OK){ + if(dev_num != expected_dev_num){ + log_w("Inconsistent number of bonded devices. Expected %d; returned %d",expected_dev_num, dev_num); + } + for(int i=0; i #include #include "BTScan.h" +#include "BTAdvertisedDevice.h" + typedef std::function BluetoothSerialDataCb; typedef std::function ConfirmRequestCb; +typedef std::function KeyRequestCb; typedef std::function AuthCompleteCb; typedef std::function BTAdvertisedDeviceCb; @@ -55,16 +58,25 @@ class BluetoothSerial: public Stream void onData(BluetoothSerialDataCb cb); esp_err_t register_callback(esp_spp_cb_t * callback); +#ifdef CONFIG_BT_SSP_ENABLED void onConfirmRequest(ConfirmRequestCb cb); + void onKeyRequest(KeyRequestCb cb); + void respondPasskey(uint32_t passkey); +#endif void onAuthComplete(AuthCompleteCb cb); void confirmReply(boolean confirm); +#ifdef CONFIG_BT_SSP_ENABLED void enableSSP(); - bool setPin(const char *pin); + void enableSSP(bool inputCapability, bool outputCapability); + void disableSSP(); +#else + bool setPin(const char *pin, uint8_t pin_code_len); +#endif bool connect(String remoteName); bool connect(uint8_t remoteAddress[], int channel=0, esp_spp_sec_t sec_mask=(ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE), esp_spp_role_t role=ESP_SPP_ROLE_MASTER); bool connect(const BTAddress &remoteAddress, int channel=0, esp_spp_sec_t sec_mask=(ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE), esp_spp_role_t role=ESP_SPP_ROLE_MASTER) { - return connect(*remoteAddress.getNative(), channel, sec_mask); }; + return connect(*remoteAddress.getNative(), channel, sec_mask); }; bool connect(); bool connected(int timeout=0); bool isClosed(); @@ -88,6 +100,14 @@ class BluetoothSerial: public Stream void getBtAddress(uint8_t *mac); BTAddress getBtAddressObject(); String getBtAddressString(); + //void dropCache(); // To be replaced + void requestRemoteName(uint8_t *remoteAddress); + bool readRemoteName(char rmt_name[ESP_BT_GAP_MAX_BDNAME_LEN + 1]); + void invalidateRemoteName(); + int getNumberOfBondedDevices(); + int getBondedDevices(uint dev_num, esp_bd_addr_t *dev_list); + bool deleteBondedDevice(uint8_t *remoteAddress); + void deleteAllBondedDevices(); private: String local_name; int timeoutTicks=0;