Support for Master mode, Pin and SSP (#3219)
* 20190916 - initial: support for Master mode, Pin and SSP * 20190916 - initial: Add example app for Master mode * 20190916 - initial: Force another build * 20190916 - connect would use resolved address as preference and remove now redundant _remote_address * 20190916 - rework set/reset/default pin logic * 20190916 - cleanup: remove static vars, add/use constants, fix typos * 20190916 - fix build issues and implement geoup events for status verification. * 20190916 - remove extra lines,misc * 20190916 - rework ESP_BT_GAP_DISC_RES_EVT, added SPP_DISCONNECTED bit for disconnect event. + timeout for disconnect() * 20190916 - Small log change to improve log sequencing * 20190916 - remove static from local vars * 20190916 - Limited scope and duration for the scan, log device address during scan in info mode as it is very difficult to find out sometimes. Fixed get_name_from_eir() not resetting the name when called. * 20190916 - break property for loop during scan when name matches. * 20190916 - misc * 20190916 - SPP_DISCONNECT state updates * 20190916 - formatting, remove some strange syntax from initiator code * 20190916 - Add comments to the example about connect(...) usage and timeouts * 20190916 - fix disconnect() without timeout * 20190916 - Add example comment to view BT address and name during connect(name) * 20190916 - wording in example comments * 20190916 - rework connect() and disconnect() methods to help with concurrency and more authoritative status returned back to caller. Automatic disconnect in connect() methods * 20190916 - optimize code * 20190916 - optimize code - more * 20190916 - add timeout for pin set * 20190916 - change scan mode to ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE * 20190916 - update example code slightly * 20190916 - increase READY_TIMEOUT to 10 secs * 20190916 - typo in example and move waitForConnect() to static area * 20190916 - update example comments * 20190916 - update example comments * 20190916 - update example comments * 20190916 - add new example to remove paired devices from ESP32 * 20190916 - correct typo in example * 20190916 - update example comment, add remove_bond_device() method for convenience. * 20190916 - reword example comment. * 20190916 - rename remove_bond_device() * 20190916 - rename removePairedDevice() to unpairDevice() * 20190916 - code review changes * 20190916 - fix return value in setup() od example
This commit is contained in:
parent
b334b2c2f9
commit
38c4c06108
@ -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);
|
||||||
|
}
|
@ -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() {}
|
352
libraries/BluetoothSerial/src/BluetoothSerial.cpp
Normal file → Executable file
352
libraries/BluetoothSerial/src/BluetoothSerial.cpp
Normal file → Executable file
@ -53,15 +53,80 @@ static EventGroupHandle_t _spp_event_group = NULL;
|
|||||||
static boolean secondConnectionAttempt;
|
static boolean secondConnectionAttempt;
|
||||||
static esp_spp_cb_t * custom_spp_callback = NULL;
|
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_RUNNING 0x01
|
||||||
#define SPP_CONNECTED 0x02
|
#define SPP_CONNECTED 0x02
|
||||||
#define SPP_CONGESTED 0x04
|
#define SPP_CONGESTED 0x04
|
||||||
|
#define SPP_DISCONNECTED 0x08
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t len;
|
size_t len;
|
||||||
uint8_t data[];
|
uint8_t data[];
|
||||||
} spp_packet_t;
|
} 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){
|
static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){
|
||||||
if(!data || !len){
|
if(!data || !len){
|
||||||
log_w("No data provided");
|
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:
|
case ESP_SPP_INIT_EVT:
|
||||||
log_i("ESP_SPP_INIT_EVT");
|
log_i("ESP_SPP_INIT_EVT");
|
||||||
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||||
|
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);
|
esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, 0, _spp_server_name);
|
||||||
|
}
|
||||||
xEventGroupSetBits(_spp_event_group, SPP_RUNNING);
|
xEventGroupSetBits(_spp_event_group, SPP_RUNNING);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESP_SPP_SRV_OPEN_EVT://Server connection open
|
case ESP_SPP_SRV_OPEN_EVT://Server connection open
|
||||||
|
log_i("ESP_SPP_SRV_OPEN_EVT");
|
||||||
if (!_spp_client){
|
if (!_spp_client){
|
||||||
_spp_client = param->open.handle;
|
_spp_client = param->open.handle;
|
||||||
} else {
|
} else {
|
||||||
secondConnectionAttempt = true;
|
secondConnectionAttempt = true;
|
||||||
esp_spp_disconnect(param->open.handle);
|
esp_spp_disconnect(param->open.handle);
|
||||||
}
|
}
|
||||||
|
xEventGroupClearBits(_spp_event_group, SPP_DISCONNECTED);
|
||||||
xEventGroupSetBits(_spp_event_group, SPP_CONNECTED);
|
xEventGroupSetBits(_spp_event_group, SPP_CONNECTED);
|
||||||
log_i("ESP_SPP_SRV_OPEN_EVT");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESP_SPP_CLOSE_EVT://Client connection closed
|
case ESP_SPP_CLOSE_EVT://Client connection closed
|
||||||
|
log_i("ESP_SPP_CLOSE_EVT");
|
||||||
if(secondConnectionAttempt) {
|
if(secondConnectionAttempt) {
|
||||||
secondConnectionAttempt = false;
|
secondConnectionAttempt = false;
|
||||||
} else {
|
} else {
|
||||||
_spp_client = 0;
|
_spp_client = 0;
|
||||||
|
xEventGroupSetBits(_spp_event_group, SPP_DISCONNECTED);
|
||||||
}
|
}
|
||||||
xEventGroupClearBits(_spp_event_group, SPP_CONNECTED);
|
xEventGroupClearBits(_spp_event_group, SPP_CONNECTED);
|
||||||
log_i("ESP_SPP_CLOSE_EVT");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESP_SPP_CONG_EVT://connection congestion status changed
|
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;
|
break;
|
||||||
|
|
||||||
//should maybe delete those.
|
|
||||||
case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete
|
case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete
|
||||||
log_i("ESP_SPP_DISCOVERY_COMP_EVT");
|
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;
|
break;
|
||||||
|
|
||||||
case ESP_SPP_OPEN_EVT://Client connection open
|
case ESP_SPP_OPEN_EVT://Client connection open
|
||||||
log_i("ESP_SPP_OPEN_EVT");
|
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;
|
break;
|
||||||
|
|
||||||
case ESP_SPP_START_EVT://server started
|
case ESP_SPP_START_EVT://server started
|
||||||
log_i("ESP_SPP_START_EVT");
|
log_i("ESP_SPP_START_EVT");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESP_SPP_CL_INIT_EVT://client initiated a connection
|
case ESP_SPP_CL_INIT_EVT://client initiated a connection
|
||||||
log_i("ESP_SPP_CL_INIT_EVT");
|
log_i("ESP_SPP_CL_INIT_EVT");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(custom_spp_callback)(*custom_spp_callback)(event, param);
|
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)
|
static bool _init_bt(const char *deviceName)
|
||||||
{
|
{
|
||||||
if(!_spp_event_group){
|
if(!_spp_event_group){
|
||||||
@ -245,6 +439,7 @@ static bool _init_bt(const char *deviceName)
|
|||||||
}
|
}
|
||||||
xEventGroupClearBits(_spp_event_group, 0xFFFFFF);
|
xEventGroupClearBits(_spp_event_group, 0xFFFFFF);
|
||||||
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
|
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
|
||||||
|
xEventGroupSetBits(_spp_event_group, SPP_DISCONNECTED);
|
||||||
}
|
}
|
||||||
if (_spp_rx_queue == NULL){
|
if (_spp_rx_queue == NULL){
|
||||||
_spp_rx_queue = xQueueCreate(RX_QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue
|
_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){
|
if (esp_spp_register_callback(esp_spp_cb) != ESP_OK){
|
||||||
log_e("spp register failed");
|
log_e("spp register failed");
|
||||||
return false;
|
return false;
|
||||||
@ -307,8 +507,20 @@ static bool _init_bt(const char *deviceName)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_i("device name set");
|
||||||
esp_bt_dev_set_device_name(deviceName);
|
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
|
// the default BTA_DM_COD_LOUDSPEAKER does not work with the macOS BT stack
|
||||||
esp_bt_cod_t cod;
|
esp_bt_cod_t cod;
|
||||||
cod.major = 0b00001;
|
cod.major = 0b00001;
|
||||||
@ -318,7 +530,6 @@ static bool _init_bt(const char *deviceName)
|
|||||||
log_e("set cod failed");
|
log_e("set cod failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,6 +572,11 @@ static bool _stop_bt()
|
|||||||
return true;
|
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
|
* Serial Bluetooth Arduino
|
||||||
*
|
*
|
||||||
@ -376,8 +592,9 @@ BluetoothSerial::~BluetoothSerial(void)
|
|||||||
_stop_bt();
|
_stop_bt();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BluetoothSerial::begin(String localName)
|
bool BluetoothSerial::begin(String localName, bool isMaster)
|
||||||
{
|
{
|
||||||
|
_isMaster = isMaster;
|
||||||
if (localName.length()){
|
if (localName.length()){
|
||||||
local_name = localName;
|
local_name = localName;
|
||||||
}
|
}
|
||||||
@ -445,4 +662,129 @@ esp_err_t BluetoothSerial::register_callback(esp_spp_cb_t * callback)
|
|||||||
return ESP_OK;
|
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
|
#endif
|
||||||
|
12
libraries/BluetoothSerial/src/BluetoothSerial.h
Normal file → Executable file
12
libraries/BluetoothSerial/src/BluetoothSerial.h
Normal file → Executable file
@ -30,7 +30,7 @@ class BluetoothSerial: public Stream
|
|||||||
BluetoothSerial(void);
|
BluetoothSerial(void);
|
||||||
~BluetoothSerial(void);
|
~BluetoothSerial(void);
|
||||||
|
|
||||||
bool begin(String localName=String());
|
bool begin(String localName=String(), bool isMaster=false);
|
||||||
int available(void);
|
int available(void);
|
||||||
int peek(void);
|
int peek(void);
|
||||||
bool hasClient(void);
|
bool hasClient(void);
|
||||||
@ -41,6 +41,16 @@ class BluetoothSerial: public Stream
|
|||||||
void end(void);
|
void end(void);
|
||||||
esp_err_t register_callback(esp_spp_cb_t * callback);
|
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:
|
private:
|
||||||
String local_name;
|
String local_name;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user