diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino b/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino new file mode 100644 index 00000000..73f3dc05 --- /dev/null +++ b/libraries/BluetoothSerial/examples/SerialToSerialBTM/SerialToSerialBTM.ino @@ -0,0 +1,56 @@ +//This example code is in the Public Domain (or CC0 licensed, at your option.) +//By Victor Tchistiak - 2019 +// +//This example demostrates master mode bluetooth connection and pin +//it creates a bridge between Serial and Classical Bluetooth (SPP) +//this is an extention of the SerialToSerialBT example by Evandro Copercini - 2018 +// + +#include "BluetoothSerial.h" + +BluetoothSerial SerialBT; + +String MACadd = "AA:BB:CC:11:22:33"; +uint8_t address[6] = {0xAA, 0xBB, 0xCC, 0x11, 0x22, 0x33}; +//uint8_t address[6] = {0x00, 0x1D, 0xA5, 0x02, 0xC3, 0x22}; +String name = "OBDII"; +char *pin = "1234"; //<- standard pin would be provided by default +bool connected; + +void setup() { + Serial.begin(115200); + //SerialBT.setPin(pin); + SerialBT.begin("ESP32test", true); + //SerialBT.setPin(pin); + Serial.println("The device started in master mode, make sure remote BT device is on!"); + + // connect(address) is fast (upto 10 secs max), connect(name) is slow (upto 30 secs max) as it needs + // to resolve name to address first, but it allows to connect to different devices with the same name. + // Set CoreDebugLevel to Info to view devices bluetooth address and device names + connected = SerialBT.connect(name); + //connected = SerialBT.connect(address); + + if(connected) { + Serial.println("Connected Succesfully!"); + } else { + while(!SerialBT.connected(10000)) { + Serial.println("Failed to connect. Make sure remote device is available and in range, then restart app."); + } + } + // disconnect() may take upto 10 secs max + if (SerialBT.disconnect()) { + Serial.println("Disconnected Succesfully!"); + } + // this would reconnect to the name(will use address, if resolved) or address used with connect(name/address). + SerialBT.connect(); +} + +void loop() { + if (Serial.available()) { + SerialBT.write(Serial.read()); + } + if (SerialBT.available()) { + Serial.write(SerialBT.read()); + } + delay(20); +} 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 new file mode 100755 index 00000000..c316a73b --- /dev/null +++ b/libraries/BluetoothSerial/examples/bt_remove_paired_devices/bt_remove_paired_devices.ino @@ -0,0 +1,87 @@ +//This example code is in the Public Domain (or CC0 licensed, at your option.) +//By Victor Tchistiak - 2019 +// +//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" + +#define REMOVE_BONDED_DEVICES 0 // <- Set to 0 to view all bonded devices addresses, set to 1 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; +} + +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() { + Serial.begin(115200); + + initBluetooth(); + Serial.print("ESP32 bluetooth address: "); Serial.println(bda2str(esp_bt_dev_get_address(), bda_str, 18)); + // 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."); + } 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); + } + 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); + } + } + } + } +} + +void loop() {} diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.cpp b/libraries/BluetoothSerial/src/BluetoothSerial.cpp old mode 100644 new mode 100755 index d3d09216..758ee08c --- a/libraries/BluetoothSerial/src/BluetoothSerial.cpp +++ b/libraries/BluetoothSerial/src/BluetoothSerial.cpp @@ -53,15 +53,80 @@ static EventGroupHandle_t _spp_event_group = NULL; static boolean secondConnectionAttempt; static esp_spp_cb_t * custom_spp_callback = NULL; +#define INQ_LEN 0x10 +#define INQ_NUM_RSPS 20 +#define READY_TIMEOUT (10 * 1000) +#define SCAN_TIMEOUT (INQ_LEN * 2 * 1000) +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; + #define SPP_RUNNING 0x01 #define SPP_CONNECTED 0x02 #define SPP_CONGESTED 0x04 +#define SPP_DISCONNECTED 0x08 typedef struct { size_t len; uint8_t data[]; } spp_packet_t; +static char *bda2str(esp_bd_addr_t bda, char *str, size_t size) +{ + if (bda == NULL || str == NULL || size < 18) { + return NULL; + } + + uint8_t *p = bda; + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + p[0], p[1], p[2], p[3], p[4], p[5]); + return str; +} + +static bool get_name_from_eir(uint8_t *eir, char *bdname, uint8_t *bdname_len) +{ + if (!eir || !bdname || !bdname_len) { + return false; + } + + uint8_t *rmt_bdname, rmt_bdname_len; + *bdname = *bdname_len = rmt_bdname_len = 0; + + rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len); + if (!rmt_bdname) { + rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len); + } + if (rmt_bdname) { + rmt_bdname_len = rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN ? ESP_BT_GAP_MAX_BDNAME_LEN : rmt_bdname_len; + memcpy(bdname, rmt_bdname, rmt_bdname_len); + bdname[rmt_bdname_len] = 0; + *bdname_len = rmt_bdname_len; + return true; + } + 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"); @@ -159,29 +224,34 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) case ESP_SPP_INIT_EVT: log_i("ESP_SPP_INIT_EVT"); esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); - esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, 0, _spp_server_name); + if (!_isMaster) { + log_i("ESP_SPP_INIT_EVT: slave: start"); + esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, 0, _spp_server_name); + } xEventGroupSetBits(_spp_event_group, SPP_RUNNING); break; case ESP_SPP_SRV_OPEN_EVT://Server connection open + log_i("ESP_SPP_SRV_OPEN_EVT"); if (!_spp_client){ _spp_client = param->open.handle; } else { secondConnectionAttempt = true; esp_spp_disconnect(param->open.handle); } + xEventGroupClearBits(_spp_event_group, SPP_DISCONNECTED); xEventGroupSetBits(_spp_event_group, SPP_CONNECTED); - log_i("ESP_SPP_SRV_OPEN_EVT"); break; case ESP_SPP_CLOSE_EVT://Client connection closed + log_i("ESP_SPP_CLOSE_EVT"); if(secondConnectionAttempt) { secondConnectionAttempt = false; } else { _spp_client = 0; - } + xEventGroupSetBits(_spp_event_group, SPP_DISCONNECTED); + } xEventGroupClearBits(_spp_event_group, SPP_CONNECTED); - log_i("ESP_SPP_CLOSE_EVT"); break; case ESP_SPP_CONG_EVT://connection congestion status changed @@ -216,25 +286,149 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) } break; - //should maybe delete those. case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete log_i("ESP_SPP_DISCOVERY_COMP_EVT"); + if (param->disc_comp.status == ESP_SPP_SUCCESS) { + log_i("ESP_SPP_DISCOVERY_COMP_EVT: spp connect to remote"); + esp_spp_connect(ESP_SPP_SEC_AUTHENTICATE, ESP_SPP_ROLE_MASTER, param->disc_comp.scn[0], _peer_bd_addr); + } break; + case ESP_SPP_OPEN_EVT://Client connection open log_i("ESP_SPP_OPEN_EVT"); + if (!_spp_client){ + _spp_client = param->open.handle; + } else { + secondConnectionAttempt = true; + esp_spp_disconnect(param->open.handle); + } + xEventGroupClearBits(_spp_event_group, SPP_DISCONNECTED); + xEventGroupSetBits(_spp_event_group, SPP_CONNECTED); break; + case ESP_SPP_START_EVT://server started log_i("ESP_SPP_START_EVT"); break; + case ESP_SPP_CL_INIT_EVT://client initiated a connection log_i("ESP_SPP_CL_INIT_EVT"); break; + default: break; } if(custom_spp_callback)(*custom_spp_callback)(event, param); } +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: + log_i("ESP_BT_GAP_DISC_RES_EVT"); + char bda_str[18]; + log_i("Scanned device: %s", bda2str(param->disc_res.bda, bda_str, 18)); + for (int i = 0; i < param->disc_res.num_prop; i++) { + uint8_t peer_bdname_len; + char peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; + 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: + 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 + log_v("ESP_BT_GAP_DISC_RES_EVT : BDNAME : %s : %d", peer_bdname, peer_bdname_len); + if (strlen(_remote_name) == peer_bdname_len + && strncmp(peer_bdname, _remote_name, peer_bdname_len) == 0) { + log_i("ESP_BT_GAP_DISC_RES_EVT : SPP_START_DISCOVERY_BDNAME : %s", peer_bdname); + _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_COD: + //log_i("ESP_BT_GAP_DEV_PROP_COD"); + break; + + case ESP_BT_GAP_DEV_PROP_RSSI: + //log_i("ESP_BT_GAP_DEV_PROP_RSSI"); + break; + + default: + break; + } + if (_isRemoteAddressSet) + break; + } + break; + case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: + log_i("ESP_BT_GAP_DISC_STATE_CHANGED_EVT"); + break; + + case ESP_BT_GAP_RMT_SRVCS_EVT: + log_i( "ESP_BT_GAP_RMT_SRVCS_EVT"); + break; + + case ESP_BT_GAP_RMT_SRVC_REC_EVT: + log_i("ESP_BT_GAP_RMT_SRVC_REC_EVT"); + break; + + case ESP_BT_GAP_AUTH_CMPL_EVT: + if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { + log_v("authentication success: %s", param->auth_cmpl.device_name); + } else { + log_e("authentication failed, status:%d", param->auth_cmpl.stat); + } + 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); + } 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); + } + break; + + case ESP_BT_GAP_CFM_REQ_EVT: + log_i("ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); + esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); + break; + + case ESP_BT_GAP_KEY_NOTIF_EVT: + log_i("ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); + break; + + case ESP_BT_GAP_KEY_REQ_EVT: + log_i("ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); + break; + + default: + break; + } +} + static bool _init_bt(const char *deviceName) { if(!_spp_event_group){ @@ -245,6 +439,7 @@ static bool _init_bt(const char *deviceName) } xEventGroupClearBits(_spp_event_group, 0xFFFFFF); xEventGroupSetBits(_spp_event_group, SPP_CONGESTED); + xEventGroupSetBits(_spp_event_group, SPP_DISCONNECTED); } if (_spp_rx_queue == NULL){ _spp_rx_queue = xQueueCreate(RX_QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue @@ -297,6 +492,11 @@ static bool _init_bt(const char *deviceName) } } + if (_isMaster && esp_bt_gap_register_callback(esp_bt_gap_cb) != ESP_OK) { + log_e("gap register failed"); + return false; + } + if (esp_spp_register_callback(esp_spp_cb) != ESP_OK){ log_e("spp register failed"); return false; @@ -307,8 +507,20 @@ static bool _init_bt(const char *deviceName) return false; } + log_i("device name set"); esp_bt_dev_set_device_name(deviceName); + if (_isPinSet) { + btSetPin(); + } + + 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_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); + } + // the default BTA_DM_COD_LOUDSPEAKER does not work with the macOS BT stack esp_bt_cod_t cod; cod.major = 0b00001; @@ -318,7 +530,6 @@ static bool _init_bt(const char *deviceName) log_e("set cod failed"); return false; } - return true; } @@ -361,6 +572,11 @@ static bool _stop_bt() return true; } +static bool waitForConnect(int timeout) { + TickType_t xTicksToWait = timeout / portTICK_PERIOD_MS; + return (xEventGroupWaitBits(_spp_event_group, SPP_CONNECTED, pdFALSE, pdTRUE, xTicksToWait) != 0); +} + /* * Serial Bluetooth Arduino * @@ -376,8 +592,9 @@ BluetoothSerial::~BluetoothSerial(void) _stop_bt(); } -bool BluetoothSerial::begin(String localName) +bool BluetoothSerial::begin(String localName, bool isMaster) { + _isMaster = isMaster; if (localName.length()){ local_name = localName; } @@ -445,4 +662,129 @@ esp_err_t BluetoothSerial::register_callback(esp_spp_cb_t * callback) return ESP_OK; } +//Simple Secure Pairing +void BluetoothSerial::enableSSP() { + _enableSSP = true; +} +/* + * Set default parameters for Legacy Pairing + * Use fixed pin code +*/ +bool BluetoothSerial::setPin(const char *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(); + } + return true; +} + +bool BluetoothSerial::connect(String remoteName) +{ + if (!isReady(true, READY_TIMEOUT)) return false; + if (remoteName && remoteName.length() < 1) { + log_e("No remote name is provided"); + return false; + } + disconnect(); + _isRemoteAddressSet = false; + 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"); + // will first resolve name to address + esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); + if (esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, INQ_LEN, INQ_NUM_RSPS) == ESP_OK) { + return waitForConnect(SCAN_TIMEOUT); + } + return false; +} + +bool BluetoothSerial::connect(uint8_t remoteAddress[]) +{ + if (!isReady(true, READY_TIMEOUT)) return false; + if (!remoteAddress) { + log_e("No remote address is provided"); + return false; + } + disconnect(); + _remote_name[0] = 0; + _isRemoteAddressSet = true; + memcpy(_peer_bd_addr, remoteAddress, ESP_BD_ADDR_LEN); + log_i("master : remoteAddress"); + if (esp_spp_start_discovery(_peer_bd_addr) == ESP_OK) { + return waitForConnect(READY_TIMEOUT); + } + return false; +} + +bool BluetoothSerial::connect() +{ + if (!isReady(true, READY_TIMEOUT)) return false; + if (_isRemoteAddressSet){ + disconnect(); + // use resolved or set address first + log_i("master : remoteAddress"); + if (esp_spp_start_discovery(_peer_bd_addr) == ESP_OK) { + return waitForConnect(READY_TIMEOUT); + } + return false; + } else if (_remote_name[0]) { + disconnect(); + log_i("master : remoteName"); + // will resolve name to address first - it may take a while + esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); + if (esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, INQ_LEN, INQ_NUM_RSPS) == ESP_OK) { + return waitForConnect(SCAN_TIMEOUT); + } + return false; + } + log_e("Neither Remote name nor address was provided"); + return false; +} + +bool BluetoothSerial::disconnect() { + if (_spp_client) { + flush(); + log_i("disconnecting"); + if (esp_spp_disconnect(_spp_client) == ESP_OK) { + TickType_t xTicksToWait = READY_TIMEOUT / portTICK_PERIOD_MS; + return (xEventGroupWaitBits(_spp_event_group, SPP_DISCONNECTED, pdFALSE, pdTRUE, xTicksToWait) != 0); + } + } + return false; +} + +bool BluetoothSerial::unpairDevice(uint8_t remoteAddress[]) { + if (isReady(false, READY_TIMEOUT)) { + log_i("removing bonded device"); + return (esp_bt_gap_remove_bond_device(remoteAddress) == ESP_OK); + } + return false; +} + +bool BluetoothSerial::connected(int timeout) { + return waitForConnect(timeout); +} + +bool BluetoothSerial::isReady(bool checkMaster, int timeout) { + if (checkMaster && !_isMaster) { + log_e("Master mode is not active. Call begin(localName, true) to enable Master mode"); + return false; + } + if (!btStarted()) { + log_e("BT is not initialized. Call begin() first"); + return false; + } + TickType_t xTicksToWait = timeout / portTICK_PERIOD_MS; + return (xEventGroupWaitBits(_spp_event_group, SPP_RUNNING, pdFALSE, pdTRUE, xTicksToWait) != 0); +} #endif diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.h b/libraries/BluetoothSerial/src/BluetoothSerial.h old mode 100644 new mode 100755 index 3f4372e5..68269fa9 --- a/libraries/BluetoothSerial/src/BluetoothSerial.h +++ b/libraries/BluetoothSerial/src/BluetoothSerial.h @@ -30,7 +30,7 @@ class BluetoothSerial: public Stream BluetoothSerial(void); ~BluetoothSerial(void); - bool begin(String localName=String()); + bool begin(String localName=String(), bool isMaster=false); int available(void); int peek(void); bool hasClient(void); @@ -41,6 +41,16 @@ class BluetoothSerial: public Stream void end(void); esp_err_t register_callback(esp_spp_cb_t * callback); + void enableSSP(); + bool setPin(const char *pin); + bool connect(String remoteName); + bool connect(uint8_t remoteAddress[]); + bool connect(); + bool connected(int timeout=0); + bool isReady(bool checkMaster=false, int timeout=0); + bool disconnect(); + bool unpairDevice(uint8_t remoteAddress[]); + private: String local_name;