mirror of https://github.com/arendst/Tasmota.git
track BLE devices with RPA (#22300)
This commit is contained in:
parent
0f84211898
commit
e39f1cc83f
|
@ -31,6 +31,7 @@ idf_component_register(
|
|||
"esp32c2"
|
||||
"esp32c3"
|
||||
"esp32c6"
|
||||
"esp32h2"
|
||||
INCLUDE_DIRS
|
||||
"src"
|
||||
SRCS
|
||||
|
@ -58,6 +59,7 @@ idf_component_register(
|
|||
REQUIRES
|
||||
bt
|
||||
nvs_flash
|
||||
driver
|
||||
PRIV_REQUIRES
|
||||
${ESP_NIMBLE_PRIV_REQUIRES}
|
||||
)
|
||||
|
|
|
@ -69,4 +69,11 @@ config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
|
|||
characteristic or descriptor is constructed before a value is read/notifed.
|
||||
Increasing this will reduce reallocations but increase memory footprint.
|
||||
|
||||
config NIMBLE_CPP_DEBUG_ASSERT_ENABLED
|
||||
bool "Enable debug asserts."
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will add debug asserts to the NimBLE CPP library.
|
||||
This will use approximately 1kB of flash memory.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -66,7 +66,7 @@ If false the service is only removed from visibility by clients. The pointers to
|
|||
# Advertising
|
||||
`NimBLEAdvertising::start`
|
||||
|
||||
Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
|
||||
Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
|
||||
|
||||
This provides an opportunity to update the advertisement data if desired.
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data
|
|||
|
||||
> BLEAdvertising::start (NimBLEAdvertising::start)
|
||||
|
||||
Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
|
||||
Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
|
||||
This provides an opportunity to update the advertisement data if desired.
|
||||
<br/>
|
||||
|
||||
|
@ -383,18 +383,23 @@ The security callback methods are now incorporated in the `NimBLEServerCallbacks
|
|||
|
||||
The callback methods are:
|
||||
|
||||
> `bool onConfirmPIN(uint32_t pin)`
|
||||
> `bool onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin)`
|
||||
|
||||
Receives the pin when using numeric comparison authentication, `return true;` to accept.
|
||||
Receives the pin when using numeric comparison authentication.
|
||||
Call `NimBLEDevice::injectConfirmPIN(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPIN(connInfo, false);` to reject.
|
||||
<br/>
|
||||
|
||||
> `uint32_t onPassKeyRequest()`
|
||||
> `void onPassKeyEntry(const NimBLEConnInfo& connInfo)`
|
||||
|
||||
For server callback; return the passkey expected from the client.
|
||||
For client callback; return the passkey to send to the server.
|
||||
Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
|
||||
<br/>
|
||||
|
||||
> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)`
|
||||
> `uint32_t onPassKeyDisplay()`
|
||||
|
||||
Server callback; should return the passkey (pin) expected from the client.
|
||||
<br/>
|
||||
|
||||
> `void onAuthenticationComplete(const NimBLEConnInfo& connInfo)`
|
||||
|
||||
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
|
||||
<br/>
|
||||
|
|
|
@ -39,20 +39,22 @@ class ClientCallbacks : public NimBLEClientCallbacks {
|
|||
|
||||
/********************* Security handled here **********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
printf("Client Passkey Request\n");
|
||||
/** return the passkey to send to the server */
|
||||
return 123456;
|
||||
}
|
||||
void onPassKeyEntry(const NimBLEConnInfo& connInfo){
|
||||
printf("Server Passkey Entry\n");
|
||||
/** This should prompt the user to enter the passkey displayed
|
||||
* on the peer device.
|
||||
*/
|
||||
NimBLEDevice::injectPassKey(connInfo, 123456);
|
||||
};
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
|
||||
/** Return false if passkeys don't match. */
|
||||
return true;
|
||||
}
|
||||
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||
/** Inject false if passkeys don't match. */
|
||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||
};
|
||||
|
||||
/** Pairing process complete, we can check the results in connInfo */
|
||||
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
||||
if(!connInfo.isEncrypted()) {
|
||||
printf("Encrypt connection failed - disconnecting\n");
|
||||
/** Find the client with the connection handle provided in desc */
|
||||
|
@ -146,8 +148,8 @@ bool connectToServer() {
|
|||
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout
|
||||
*/
|
||||
pClient->setConnectionParams(6,6,0,15);
|
||||
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
|
||||
pClient->setConnectTimeout(5);
|
||||
/** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
|
||||
pClient->setConnectTimeout(5 * 1000);
|
||||
|
||||
|
||||
if (!pClient->connect(advDevice)) {
|
||||
|
@ -358,7 +360,7 @@ void app_main (void){
|
|||
* but will use more energy from both devices
|
||||
*/
|
||||
pScan->setActiveScan(true);
|
||||
/** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
|
||||
/** Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
|
||||
* Optional callback for when scanning stops.
|
||||
*/
|
||||
pScan->start(scanTime);
|
||||
|
|
|
@ -44,21 +44,21 @@ class ServerCallbacks: public NimBLEServerCallbacks {
|
|||
|
||||
/********************* Security handled here **********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
printf("Server Passkey Request\n");
|
||||
uint32_t onPassKeyDisplay(){
|
||||
printf("Server Passkey Display\n");
|
||||
/** This should return a random 6 digit number for security
|
||||
* or make your own static passkey as done here.
|
||||
*/
|
||||
return 123456;
|
||||
};
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
|
||||
/** Return false if passkeys don't match. */
|
||||
return true;
|
||||
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||
/** Inject false if passkeys don't match. */
|
||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||
};
|
||||
|
||||
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
||||
/** Check that encryption was successful, if not we disconnect the client */
|
||||
if(!connInfo.isEncrypted()) {
|
||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||
|
|
|
@ -154,7 +154,7 @@ void app_main (void) {
|
|||
*/
|
||||
pScan->setActiveScan(true);
|
||||
|
||||
/* Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
|
||||
/* Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
|
||||
* Optional callback for when scanning stops.
|
||||
*/
|
||||
pScan->start(scanTime);
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* NimBLE Scan active/passive switching demo
|
||||
*
|
||||
* Demonstrates the use of the scan callbacks while alternating between passive and active scanning.
|
||||
*/
|
||||
|
||||
#include "NimBLEDevice.h"
|
||||
int scanTime = 5 * 1000; // In milliseconds, 0 = scan forever
|
||||
BLEScan* pBLEScan;
|
||||
|
||||
bool active = false;
|
||||
|
||||
class scanCallbacks: public NimBLEScanCallbacks {
|
||||
|
||||
void onDiscovered(NimBLEAdvertisedDevice* advertisedDevice) {
|
||||
Serial.printf("Discovered Advertised Device: %s \n", advertisedDevice->toString().c_str());
|
||||
}
|
||||
|
||||
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
|
||||
Serial.printf("Advertised Device Result: %s \n", advertisedDevice->toString().c_str());
|
||||
}
|
||||
|
||||
void onScanEnd(NimBLEScanResults results){
|
||||
Serial.println("Scan Ended");
|
||||
active = !active;
|
||||
pBLEScan->setActiveScan(active);
|
||||
Serial.printf("scan start, active = %u\n", active);
|
||||
pBLEScan->start(scanTime);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
NimBLEDevice::init("");
|
||||
pBLEScan = NimBLEDevice::getScan();
|
||||
pBLEScan->setScanCallbacks(new scanCallbacks());
|
||||
pBLEScan->setActiveScan(active);
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99);
|
||||
pBLEScan->start(scanTime);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(SUPPORTED_TARGETS esp32)
|
||||
project(NimBLE_server_get_client_name)
|
|
@ -0,0 +1,4 @@
|
|||
set(COMPONENT_SRCS "main.cpp")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
|
@ -0,0 +1,83 @@
|
|||
/** NimBLE_server_get_client_name
|
||||
*
|
||||
* Demonstrates 2 ways for the server to read the device name from the connected client.
|
||||
*
|
||||
* Created: on June 24 2024
|
||||
* Author: H2zero
|
||||
*
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
#define ENC_CHARACTERISTIC_UUID "9551f35b-8d91-42e4-8f7e-1358dfe272dc"
|
||||
|
||||
NimBLEServer* pServer;
|
||||
|
||||
class ServerCallbacks : public NimBLEServerCallbacks {
|
||||
// Same as before but now includes the name parameter
|
||||
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) override {
|
||||
printf("Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
|
||||
}
|
||||
|
||||
// Same as before but now includes the name parameter
|
||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name) override {
|
||||
if (!connInfo.isEncrypted()) {
|
||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||
printf("Encrypt connection failed - disconnecting client\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Encrypted Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" void app_main(void) {
|
||||
printf("Starting BLE Server!\n");
|
||||
|
||||
NimBLEDevice::init("Connect to me!");
|
||||
NimBLEDevice::setSecurityAuth(true, false, true); // Enable bonding to see full name on phones.
|
||||
|
||||
pServer = NimBLEDevice::createServer();
|
||||
NimBLEService* pService = pServer->createService(SERVICE_UUID);
|
||||
NimBLECharacteristic* pCharacteristic =
|
||||
pService->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE);
|
||||
pCharacteristic->setValue("Hello World says NimBLE!");
|
||||
|
||||
NimBLECharacteristic* pEncCharacteristic = pService->createCharacteristic(
|
||||
ENC_CHARACTERISTIC_UUID,
|
||||
(NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC));
|
||||
pEncCharacteristic->setValue("Hello World says NimBLE Encrypted");
|
||||
|
||||
pService->start();
|
||||
|
||||
pServer->setCallbacks(new ServerCallbacks());
|
||||
pServer->getPeerNameOnConnect(true); // Setting this will enable the onConnect callback that provides the name.
|
||||
|
||||
BLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
|
||||
pAdvertising->start();
|
||||
printf("Advertising started, connect with your phone.\n");
|
||||
|
||||
while (true) {
|
||||
auto clientCount = pServer->getConnectedCount();
|
||||
if (clientCount) {
|
||||
printf("Connected clients:\n");
|
||||
for (auto i = 0; i < clientCount; ++i) {
|
||||
NimBLEConnInfo peerInfo = pServer->getPeerInfo(i);
|
||||
printf("Client address: %s Name: %s\n", peerInfo.getAddress().toString().c_str(),
|
||||
// This function blocks until the name is retrieved, so cannot be used in callback functions.
|
||||
pServer->getPeerName(peerInfo).c_str());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||||
}
|
||||
}
|
|
@ -51,17 +51,28 @@ class MyClientCallback : public BLEClientCallbacks {
|
|||
}
|
||||
/***************** New - Security handled here ********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
printf("Client PassKeyRequest\n");
|
||||
return 123456;
|
||||
}
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
|
||||
return true;
|
||||
}
|
||||
void onPassKeyEntry(const NimBLEConnInfo& connInfo){
|
||||
printf("Server Passkey Entry\n");
|
||||
/** This should prompt the user to enter the passkey displayed
|
||||
* on the peer device.
|
||||
*/
|
||||
NimBLEDevice::injectPassKey(connInfo, 123456);
|
||||
};
|
||||
|
||||
void onAuthenticationComplete(BLEConnInfo& connInfo){
|
||||
printf("Starting BLE work!\n");
|
||||
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||
/** Inject false if passkeys don't match. */
|
||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||
};
|
||||
|
||||
/** Pairing process complete, we can check the results in connInfo */
|
||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
||||
if(!connInfo.isEncrypted()) {
|
||||
printf("Encrypt connection failed - disconnecting\n");
|
||||
/** Find the client with the connection handle provided in desc */
|
||||
NimBLEDevice::getClientByID(connInfo.getConnHandle())->disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*******************************************************************/
|
||||
};
|
||||
|
|
|
@ -57,19 +57,29 @@ class MyServerCallbacks: public BLEServerCallbacks {
|
|||
}
|
||||
/***************** New - Security handled here ********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
printf("Server PassKeyRequest\n");
|
||||
uint32_t onPassKeyDisplay(){
|
||||
printf("Server Passkey Display\n");
|
||||
/** This should return a random 6 digit number for security
|
||||
* or make your own static passkey as done here.
|
||||
*/
|
||||
return 123456;
|
||||
}
|
||||
};
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
|
||||
return true;
|
||||
}
|
||||
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||
/** Inject false if passkeys don't match. */
|
||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||
};
|
||||
|
||||
void onAuthenticationComplete(BLEConnInfo& connInfo){
|
||||
printf("Starting BLE work!\n");
|
||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
||||
/** Check that encryption was successful, if not we disconnect the client */
|
||||
if(!connInfo.isEncrypted()) {
|
||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||
printf("Encrypt connection failed - disconnecting client\n");
|
||||
return;
|
||||
}
|
||||
printf("Starting BLE work!");
|
||||
};
|
||||
/*******************************************************************/
|
||||
};
|
||||
|
||||
|
@ -128,7 +138,6 @@ void app_main(void) {
|
|||
NIMBLE_PROPERTY::INDICATE
|
||||
);
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
|
||||
// Create a BLE Descriptor
|
||||
/***************************************************
|
||||
NOTE: DO NOT create a 2902 descriptor.
|
||||
|
|
|
@ -59,19 +59,29 @@ class MyServerCallbacks: public BLEServerCallbacks {
|
|||
}
|
||||
/***************** New - Security handled here ********************
|
||||
****** Note: these are the same return values as defaults ********/
|
||||
uint32_t onPassKeyRequest(){
|
||||
printf("Server PassKeyRequest\n");
|
||||
uint32_t onPassKeyDisplay(){
|
||||
printf("Server Passkey Display\n");
|
||||
/** This should return a random 6 digit number for security
|
||||
* or make your own static passkey as done here.
|
||||
*/
|
||||
return 123456;
|
||||
}
|
||||
};
|
||||
|
||||
bool onConfirmPIN(uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32"\n", pass_key);
|
||||
return true;
|
||||
}
|
||||
void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pass_key){
|
||||
printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
|
||||
/** Inject false if passkeys don't match. */
|
||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||
};
|
||||
|
||||
void onAuthenticationComplete(BLEConnInfo& connInfo){
|
||||
printf("Starting BLE work!\n");
|
||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
||||
/** Check that encryption was successful, if not we disconnect the client */
|
||||
if(!connInfo.isEncrypted()) {
|
||||
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
|
||||
printf("Encrypt connection failed - disconnecting client\n");
|
||||
return;
|
||||
}
|
||||
printf("Starting BLE work!");
|
||||
};
|
||||
/*******************************************************************/
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "esp-nimble-cpp",
|
||||
"version": "1.5.0",
|
||||
"description": "NimBLE, BLE stack for the Espressif ESP32, ESP32-S and ESP32-C series of SoCs",
|
||||
"keywords": [
|
||||
"BLE",
|
||||
"espidf",
|
||||
"arduino",
|
||||
"espressif",
|
||||
"esp32"
|
||||
],
|
||||
"license": "LGPL-2.1-or-later",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/h2zero/esp-nimble-cpp"
|
||||
}
|
||||
}
|
|
@ -12,10 +12,6 @@
|
|||
* Author: kolban
|
||||
*/
|
||||
|
||||
/*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
|
||||
*/
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||
|
||||
|
|
|
@ -33,9 +33,6 @@ struct BLE2904_Data {
|
|||
* @brief Descriptor for Characteristic Presentation Format.
|
||||
*
|
||||
* This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
|
||||
*/
|
||||
class NimBLE2904: public NimBLEDescriptor {
|
||||
public:
|
||||
|
|
|
@ -138,6 +138,15 @@ uint8_t NimBLEAddress::getType() const {
|
|||
} // getType
|
||||
|
||||
|
||||
/**
|
||||
* @brief Determine if this address is a Resolvable Private Address.
|
||||
* @return True if the address is a RPA.
|
||||
*/
|
||||
bool NimBLEAddress::isRpa() const {
|
||||
return (m_addrType && ((m_address[5] & 0xc0) == 0x40));
|
||||
} // isRpa
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a BLE address to a string.
|
||||
*
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC);
|
||||
NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
|
||||
NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC);
|
||||
bool isRpa() const;
|
||||
bool equals(const NimBLEAddress &otherAddress) const;
|
||||
const uint8_t* getNative() const;
|
||||
std::string toString() const;
|
||||
|
|
|
@ -203,6 +203,24 @@ std::string NimBLEAdvertisedDevice::getURI() {
|
|||
return "";
|
||||
} // getURI
|
||||
|
||||
/**
|
||||
* @brief Get the data from any type available in the advertisement
|
||||
* @param [in] type The advertised data type BLE_HS_ADV_TYPE
|
||||
* @return The data available under the type `type`
|
||||
*/
|
||||
std::string NimBLEAdvertisedDevice::getPayloadByType(uint16_t type) {
|
||||
size_t data_loc = 0;
|
||||
|
||||
if(findAdvField(type, 0, &data_loc) > 0) {
|
||||
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
|
||||
if(field->length > 1) {
|
||||
return std::string((char*)field->value, field->length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
} // getPayloadByType
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the advertised name.
|
||||
|
@ -556,6 +574,14 @@ bool NimBLEAdvertisedDevice::haveURI() {
|
|||
return findAdvField(BLE_HS_ADV_TYPE_URI) > 0;
|
||||
} // haveURI
|
||||
|
||||
/**
|
||||
* @brief Does this advertisement have a adv type `type`?
|
||||
* @return True if there is a `type` present.
|
||||
*/
|
||||
bool NimBLEAdvertisedDevice::haveType(uint16_t type) {
|
||||
return findAdvField(type) > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Does the advertisement contain a target address?
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
uint8_t getManufacturerDataCount();
|
||||
std::string getManufacturerData(uint8_t index = 0);
|
||||
std::string getURI();
|
||||
std::string getPayloadByType(uint16_t type);
|
||||
|
||||
/**
|
||||
* @brief A template to convert the service data to <type\>.
|
||||
|
@ -134,6 +135,7 @@ public:
|
|||
bool haveAdvInterval();
|
||||
bool haveTargetAddress();
|
||||
bool haveURI();
|
||||
bool haveType(uint16_t type);
|
||||
std::string toString();
|
||||
bool isConnectable();
|
||||
bool isLegacyAdvertisement();
|
||||
|
|
|
@ -96,8 +96,8 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Add a service uuid to exposed list of services.
|
||||
* @param [in] serviceUUID The UUID of the service to expose.
|
||||
* @brief Remove a service UUID from the advertisment.
|
||||
* @param [in] serviceUUID The UUID of the service to remove.
|
||||
*/
|
||||
void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
|
||||
for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) {
|
||||
|
@ -110,10 +110,17 @@ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
|
|||
} // addServiceUUID
|
||||
|
||||
|
||||
/**
|
||||
* @brief Remove all service UUIDs from the advertisment.
|
||||
*/
|
||||
void NimBLEAdvertising::removeServices() {
|
||||
std::vector<NimBLEUUID>().swap(m_serviceUUIDs);
|
||||
m_advDataSet = false;
|
||||
} // removeServices
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the device appearance in the advertising data.
|
||||
* The codes for distinct appearances can be found here:\n
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
|
||||
* @param [in] appearance The appearance of the device in the advertising data.
|
||||
*/
|
||||
void NimBLEAdvertising::setAppearance(uint16_t appearance) {
|
||||
|
@ -137,7 +144,7 @@ void NimBLEAdvertising::addTxPower() {
|
|||
* @param [in] name The name to advertise.
|
||||
*/
|
||||
void NimBLEAdvertising::setName(const std::string &name) {
|
||||
m_name.assign(name.begin(), name.end());
|
||||
std::vector<uint8_t>(name.begin(), name.end()).swap(m_name);
|
||||
m_advData.name = &m_name[0];
|
||||
m_advData.name_len = m_name.size();
|
||||
m_advDataSet = false;
|
||||
|
@ -149,7 +156,7 @@ void NimBLEAdvertising::setName(const std::string &name) {
|
|||
* @param [in] data The data to advertise.
|
||||
*/
|
||||
void NimBLEAdvertising::setManufacturerData(const std::string &data) {
|
||||
m_mfgData.assign(data.begin(), data.end());
|
||||
std::vector<uint8_t>(data.begin(), data.end()).swap(m_mfgData);
|
||||
m_advData.mfg_data = &m_mfgData[0];
|
||||
m_advData.mfg_data_len = m_mfgData.size();
|
||||
m_advDataSet = false;
|
||||
|
@ -173,7 +180,7 @@ void NimBLEAdvertising::setManufacturerData(const std::vector<uint8_t> &data) {
|
|||
* @param [in] uri The URI to advertise.
|
||||
*/
|
||||
void NimBLEAdvertising::setURI(const std::string &uri) {
|
||||
m_uri.assign(uri.begin(), uri.end());
|
||||
std::vector<uint8_t>(uri.begin(), uri.end()).swap(m_uri);
|
||||
m_advData.uri = &m_uri[0];
|
||||
m_advData.uri_len = m_uri.size();
|
||||
m_advDataSet = false;
|
||||
|
@ -189,7 +196,8 @@ void NimBLEAdvertising::setURI(const std::string &uri) {
|
|||
void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
|
||||
switch (uuid.bitSize()) {
|
||||
case 16: {
|
||||
m_svcData16.assign((uint8_t*)&uuid.getNative()->u16.value, (uint8_t*)&uuid.getNative()->u16.value + 2);
|
||||
std::vector<uint8_t>((uint8_t*)&uuid.getNative()->u16.value,
|
||||
(uint8_t*)&uuid.getNative()->u16.value + 2).swap(m_svcData16);
|
||||
m_svcData16.insert(m_svcData16.end(), data.begin(), data.end());
|
||||
m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0];
|
||||
m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0;
|
||||
|
@ -197,7 +205,8 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string
|
|||
}
|
||||
|
||||
case 32: {
|
||||
m_svcData32.assign((uint8_t*)&uuid.getNative()->u32.value, (uint8_t*)&uuid.getNative()->u32.value + 4);
|
||||
std::vector<uint8_t>((uint8_t*)&uuid.getNative()->u32.value,
|
||||
(uint8_t*)&uuid.getNative()->u32.value + 4).swap(m_svcData32);
|
||||
m_svcData32.insert(m_svcData32.end(), data.begin(), data.end());
|
||||
m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0];
|
||||
m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0;
|
||||
|
@ -205,7 +214,8 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string
|
|||
}
|
||||
|
||||
case 128: {
|
||||
m_svcData128.assign(uuid.getNative()->u128.value, uuid.getNative()->u128.value + 16);
|
||||
std::vector<uint8_t>(uuid.getNative()->u128.value,
|
||||
uuid.getNative()->u128.value + 16).swap(m_svcData128);
|
||||
m_svcData128.insert(m_svcData128.end(), data.begin(), data.end());
|
||||
m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0];
|
||||
m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0;
|
||||
|
@ -402,7 +412,7 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
|
|||
* @param [in] dirAddr The address of a peer to directly advertise to.
|
||||
* @return True if advertising started successfully.
|
||||
*/
|
||||
bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv), NimBLEAddress* dirAddr) {
|
||||
bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB, NimBLEAddress* dirAddr) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d",
|
||||
m_customAdvData, m_customScanResponseData);
|
||||
|
||||
|
@ -490,8 +500,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16,
|
||||
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
|
||||
{
|
||||
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
|
||||
abort();
|
||||
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
|
||||
return false;
|
||||
}
|
||||
memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16],
|
||||
&it.getNative()->u16, sizeof(ble_uuid16_t));
|
||||
|
@ -509,8 +519,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32,
|
||||
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
|
||||
{
|
||||
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
|
||||
abort();
|
||||
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
|
||||
return false;
|
||||
}
|
||||
memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32],
|
||||
&it.getNative()->u32, sizeof(ble_uuid32_t));
|
||||
|
@ -528,8 +538,8 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128,
|
||||
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
|
||||
{
|
||||
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
|
||||
abort();
|
||||
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
|
||||
return false;
|
||||
}
|
||||
memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128],
|
||||
&it.getNative()->u128, sizeof(ble_uuid128_t));
|
||||
|
@ -762,7 +772,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
case BLE_HS_EOS:
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
|
||||
NIMBLE_LOGE(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
|
||||
NimBLEDevice::onReset(event->adv_complete.reason);
|
||||
return 0;
|
||||
default:
|
||||
|
@ -800,9 +810,6 @@ void NimBLEAdvertisementData::addData(char * data, size_t length) {
|
|||
/**
|
||||
* @brief Set the appearance.
|
||||
* @param [in] appearance The appearance code value.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
|
||||
*/
|
||||
void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
|
||||
char cdata[2];
|
||||
|
@ -1069,4 +1076,12 @@ std::string NimBLEAdvertisementData::getPayload() {
|
|||
return m_payload;
|
||||
} // getPayload
|
||||
|
||||
|
||||
/**
|
||||
* @brief Clear the advertisement data for reuse.
|
||||
*/
|
||||
void NimBLEAdvertisementData::clearData() {
|
||||
m_payload.clear();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "NimBLEUUID.h"
|
||||
#include "NimBLEAddress.h"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
/* COMPATIBILITY - DO NOT USE */
|
||||
|
@ -44,6 +45,9 @@
|
|||
#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 )
|
||||
/* ************************* */
|
||||
|
||||
class NimBLEAdvertising;
|
||||
|
||||
typedef std::function<void(NimBLEAdvertising*)> advCompleteCB_t;
|
||||
|
||||
/**
|
||||
* @brief Advertisement data set by the programmer to be published by the %BLE server.
|
||||
|
@ -72,6 +76,7 @@ public:
|
|||
void addTxPower();
|
||||
void setPreferredParams(uint16_t min, uint16_t max);
|
||||
std::string getPayload(); // Retrieve the current advert payload.
|
||||
void clearData(); // Clear the advertisement data.
|
||||
|
||||
private:
|
||||
friend class NimBLEAdvertising;
|
||||
|
@ -92,7 +97,8 @@ public:
|
|||
void addServiceUUID(const NimBLEUUID &serviceUUID);
|
||||
void addServiceUUID(const char* serviceUUID);
|
||||
void removeServiceUUID(const NimBLEUUID &serviceUUID);
|
||||
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr, NimBLEAddress* dirAddr = nullptr);
|
||||
bool start(uint32_t duration = 0, advCompleteCB_t advCompleteCB = nullptr, NimBLEAddress* dirAddr = nullptr);
|
||||
void removeServices();
|
||||
bool stop();
|
||||
void setAppearance(uint16_t appearance);
|
||||
void setName(const std::string &name);
|
||||
|
@ -129,7 +135,7 @@ private:
|
|||
bool m_customScanResponseData;
|
||||
bool m_scanResp;
|
||||
bool m_advDataSet;
|
||||
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
|
||||
advCompleteCB_t m_advCompCB{nullptr};
|
||||
uint8_t m_slaveItvl[4];
|
||||
uint32_t m_duration;
|
||||
std::vector<uint8_t> m_svcData16;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ctime>
|
||||
|
||||
#ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
|
||||
|
@ -41,7 +42,6 @@
|
|||
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
|
||||
#endif
|
||||
|
||||
|
||||
/* Used to determine if the type passed to a template has a c_str() and length() method. */
|
||||
template <typename T, typename = void, typename = void>
|
||||
struct Has_c_str_len : std::false_type {};
|
||||
|
@ -266,7 +266,8 @@ public:
|
|||
|
||||
/** @brief Subscript operator */
|
||||
uint8_t operator [](int pos) const {
|
||||
assert(pos < m_attr_len && "out of range"); return m_attr_value[pos]; }
|
||||
NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len);
|
||||
return m_attr_value[pos]; }
|
||||
|
||||
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
|
||||
operator std::vector<uint8_t>() const {
|
||||
|
@ -311,7 +312,7 @@ public:
|
|||
|
||||
inline NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len) {
|
||||
m_attr_value = (uint8_t*)calloc(init_len + 1, 1);
|
||||
assert(m_attr_value && "No Mem");
|
||||
NIMBLE_CPP_DEBUG_ASSERT(m_attr_value);
|
||||
m_attr_max_len = std::min(BLE_ATT_ATTR_MAX_LEN, (int)max_len);
|
||||
m_attr_len = 0;
|
||||
m_capacity = init_len;
|
||||
|
@ -354,7 +355,7 @@ inline NimBLEAttValue& NimBLEAttValue::operator =(const NimBLEAttValue & source)
|
|||
|
||||
inline void NimBLEAttValue::deepCopy(const NimBLEAttValue & source) {
|
||||
uint8_t* res = (uint8_t*)realloc( m_attr_value, source.m_capacity + 1);
|
||||
assert(res && "deepCopy: realloc failed");
|
||||
NIMBLE_CPP_DEBUG_ASSERT(res);
|
||||
|
||||
ble_npl_hw_enter_critical();
|
||||
m_attr_value = res;
|
||||
|
@ -389,7 +390,7 @@ inline bool NimBLEAttValue::setValue(const uint8_t *value, uint16_t len) {
|
|||
res = (uint8_t*)realloc(m_attr_value, (len + 1));
|
||||
m_capacity = len;
|
||||
}
|
||||
assert(res && "setValue: realloc failed");
|
||||
NIMBLE_CPP_DEBUG_ASSERT(res);
|
||||
|
||||
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||
time_t t = time(nullptr);
|
||||
|
@ -424,7 +425,7 @@ inline NimBLEAttValue& NimBLEAttValue::append(const uint8_t *value, uint16_t len
|
|||
res = (uint8_t*)realloc(m_attr_value, (new_len + 1));
|
||||
m_capacity = new_len;
|
||||
}
|
||||
assert(res && "append: realloc failed");
|
||||
NIMBLE_CPP_DEBUG_ASSERT(res);
|
||||
|
||||
#if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
|
||||
time_t t = time(nullptr);
|
||||
|
|
|
@ -87,9 +87,7 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
|
|||
*/
|
||||
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
|
||||
NimBLEDescriptor* pDescriptor = nullptr;
|
||||
if(uuid == NimBLEUUID(uint16_t(0x2902))) {
|
||||
assert(0 && "0x2902 descriptors cannot be manually created");
|
||||
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
|
||||
if (uuid == NimBLEUUID(uint16_t(0x2904))) {
|
||||
pDescriptor = new NimBLE2904(this);
|
||||
} else {
|
||||
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
|
||||
|
@ -279,12 +277,12 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
|
|||
if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
|
||||
switch(ctxt->op) {
|
||||
case BLE_GATT_ACCESS_OP_READ_CHR: {
|
||||
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
|
||||
// If the packet header is only 8 bytes this is a follow up of a long read
|
||||
// so we don't want to call the onRead() callback again.
|
||||
if(ctxt->om->om_pkthdr_len > 8 ||
|
||||
conn_handle == BLE_HS_CONN_HANDLE_NONE ||
|
||||
pCharacteristic->m_value.size() <= (ble_att_mtu(peerInfo.m_desc.conn_handle) - 3)) {
|
||||
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, peerInfo);
|
||||
}
|
||||
|
@ -316,8 +314,8 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
|
|||
len += next->om_len;
|
||||
next = SLIST_NEXT(next, om_next);
|
||||
}
|
||||
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
|
||||
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
pCharacteristic->setValue(buf, len);
|
||||
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, peerInfo);
|
||||
return 0;
|
||||
|
|
|
@ -111,7 +111,7 @@ NimBLEClient::~NimBLEClient() {
|
|||
*/
|
||||
void NimBLEClient::dcTimerCb(ble_npl_event *event) {
|
||||
/* NimBLEClient *pClient = (NimBLEClient*)event->arg;
|
||||
NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host",
|
||||
NIMBLE_LOGE(LOG_TAG, "Timed out disconnecting from %s - resetting host",
|
||||
std::string(pClient->getPeerAddress()).c_str());
|
||||
*/
|
||||
ble_hs_sched_reset(BLE_HS_ECONTROLLER);
|
||||
|
@ -189,7 +189,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttributes)
|
|||
NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
|
||||
|
||||
if(!NimBLEDevice::m_synced) {
|
||||
NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync.");
|
||||
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -458,9 +458,6 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
|
|||
// These are not used by NimBLE at this time - Must leave at defaults
|
||||
//m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units
|
||||
//m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units
|
||||
|
||||
int rc = NimBLEUtils::checkConnParams(&m_pConnParams);
|
||||
assert(rc == 0 && "Invalid Connection parameters");
|
||||
} // setConnectionParams
|
||||
|
||||
|
||||
|
@ -551,6 +548,66 @@ uint16_t NimBLEClient::getConnId() {
|
|||
return m_conn_id;
|
||||
} // getConnId
|
||||
|
||||
/**
|
||||
* @brief Clear the connection information for this client.
|
||||
* @note This is designed to be used to reset the connection information after
|
||||
* calling setConnection(), and should not be used to disconnect from a
|
||||
* peer. To disconnect from a peer, use disconnect().
|
||||
*/
|
||||
void NimBLEClient::clearConnection() {
|
||||
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
m_connEstablished = false;
|
||||
m_peerAddress = NimBLEAddress();
|
||||
} // clearConnection
|
||||
|
||||
/**
|
||||
* @brief Set the connection information for this client.
|
||||
* @param [in] connInfo The connection information.
|
||||
* @return True on success.
|
||||
* @note Sets the connection established flag to true.
|
||||
* @note If the client is already connected to a peer, this will return false.
|
||||
* @note This is designed to be used when a connection is made outside of the
|
||||
* NimBLEClient class, such as when a connection is made by the
|
||||
* NimBLEServer class and the client is passed the connection id. This use
|
||||
* enables the GATT Server to read the name of the device that has
|
||||
* connected to it.
|
||||
*/
|
||||
bool NimBLEClient::setConnection(NimBLEConnInfo &connInfo) {
|
||||
if (isConnected() || m_connEstablished) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Already connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_peerAddress = connInfo.getAddress();
|
||||
m_conn_id = connInfo.getConnHandle();
|
||||
m_connEstablished = true;
|
||||
|
||||
return true;
|
||||
} // setConnection
|
||||
|
||||
/**
|
||||
* @brief Set the connection information for this client.
|
||||
* @param [in] conn_id The connection id.
|
||||
* @note Sets the connection established flag to true.
|
||||
* @note This is designed to be used when a connection is made outside of the
|
||||
* NimBLEClient class, such as when a connection is made by the
|
||||
* NimBLEServer class and the client is passed the connection id. This use
|
||||
* enables the GATT Server to read the name of the device that has
|
||||
* connected to it.
|
||||
* @note If the client is already connected to a peer, this will return false.
|
||||
* @note This will look up the peer address using the connection id.
|
||||
*/
|
||||
bool NimBLEClient::setConnection(uint16_t conn_id) {
|
||||
// we weren't provided the peer address, look it up using ble_gap_conn_find
|
||||
NimBLEConnInfo connInfo;
|
||||
int rc = ble_gap_conn_find(m_conn_id, &connInfo.m_desc);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
return setConnection(connInfo);
|
||||
} // setConnection
|
||||
|
||||
/**
|
||||
* @brief Retrieve the address of the peer.
|
||||
|
@ -945,7 +1002,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
case BLE_HS_ETIMEOUT_HCI:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
case BLE_HS_EOS:
|
||||
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
|
||||
NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
|
||||
NimBLEDevice::onReset(rc);
|
||||
break;
|
||||
default:
|
||||
|
@ -1111,7 +1168,11 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
{
|
||||
NimBLEConnInfo peerInfo;
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
|
||||
// Key is missing, try deleting.
|
||||
|
@ -1125,6 +1186,19 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
break;
|
||||
} //BLE_GAP_EVENT_ENC_CHANGE
|
||||
|
||||
case BLE_GAP_EVENT_IDENTITY_RESOLVED: {
|
||||
NimBLEConnInfo peerInfo;
|
||||
rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &peerInfo.m_desc);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pClient->m_pClientCallbacks->onIdentity(peerInfo);
|
||||
break;
|
||||
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
|
||||
|
||||
case BLE_GAP_EVENT_MTU: {
|
||||
if(pClient->m_conn_id != event->mtu.conn_handle){
|
||||
return 0;
|
||||
|
@ -1143,20 +1217,17 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
if(pClient->m_conn_id != event->passkey.conn_handle)
|
||||
return 0;
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
|
||||
NimBLEConnInfo peerInfo;
|
||||
rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.numcmp_accept = pClient->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp);
|
||||
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
|
||||
|
||||
pClient->m_pClientCallbacks->onConfirmPIN(peerInfo, event->passkey.params.numcmp);
|
||||
//TODO: Handle out of band pairing
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
||||
static uint8_t tem_oob[16] = {0};
|
||||
|
@ -1169,12 +1240,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
////////
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Enter the passkey");
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = pClient->m_pClientCallbacks->onPassKeyRequest();
|
||||
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
|
||||
|
||||
pClient->m_pClientCallbacks->onPassKeyEntry(peerInfo);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
|
||||
NIMBLE_LOGD(LOG_TAG, "No passkey action required");
|
||||
}
|
||||
|
@ -1261,17 +1327,22 @@ bool NimBLEClientCallbacks::onConnParamsUpdateRequest(NimBLEClient* pClient, con
|
|||
return true;
|
||||
}
|
||||
|
||||
uint32_t NimBLEClientCallbacks::onPassKeyRequest(){
|
||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456");
|
||||
return 123456;
|
||||
}
|
||||
void NimBLEClientCallbacks::onPassKeyEntry(const NimBLEConnInfo& connInfo){
|
||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyEntry: default: 123456");
|
||||
NimBLEDevice::injectPassKey(connInfo, 123456);
|
||||
} //onPassKeyEntry
|
||||
|
||||
void NimBLEClientCallbacks::onAuthenticationComplete(NimBLEConnInfo& peerInfo){
|
||||
void NimBLEClientCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default");
|
||||
}
|
||||
bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){
|
||||
|
||||
void NimBLEClientCallbacks::onIdentity(const NimBLEConnInfo& connInfo){
|
||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onIdentity: default");
|
||||
} // onIdentity
|
||||
|
||||
void NimBLEClientCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){
|
||||
NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true");
|
||||
return true;
|
||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||
|
|
|
@ -61,6 +61,9 @@ public:
|
|||
bool deleteCallbacks = true);
|
||||
std::string toString();
|
||||
uint16_t getConnId();
|
||||
void clearConnection();
|
||||
bool setConnection(NimBLEConnInfo &conn_info);
|
||||
bool setConnection(uint16_t conn_id);
|
||||
uint16_t getMTU();
|
||||
bool secureConnection();
|
||||
void setConnectTimeout(uint32_t timeout);
|
||||
|
@ -144,23 +147,29 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Called when server requests a passkey for pairing.
|
||||
* @return The passkey to be sent to the server.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||
*/
|
||||
virtual uint32_t onPassKeyRequest();
|
||||
virtual void onPassKeyEntry(const NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Called when the pairing procedure is complete.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n
|
||||
* This can be used to check the status of the connection encryption/pairing.
|
||||
*/
|
||||
virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
|
||||
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Called when using numeric comparision for pairing.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
|
||||
* @param [in] pin The pin to compare with the server.
|
||||
* @return True to accept the pin.
|
||||
*/
|
||||
virtual bool onConfirmPIN(uint32_t pin);
|
||||
virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin);
|
||||
|
||||
/**
|
||||
* @brief Called when the peer identity address is resolved.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
*/
|
||||
virtual void onIdentity(const NimBLEConnInfo& connInfo);
|
||||
};
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
|
||||
|
|
|
@ -12,47 +12,47 @@ friend class NimBLEClient;
|
|||
friend class NimBLECharacteristic;
|
||||
friend class NimBLEDescriptor;
|
||||
|
||||
ble_gap_conn_desc m_desc;
|
||||
NimBLEConnInfo() { m_desc = {}; }
|
||||
ble_gap_conn_desc m_desc{};
|
||||
NimBLEConnInfo(){};
|
||||
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
|
||||
public:
|
||||
/** @brief Gets the over-the-air address of the connected peer */
|
||||
NimBLEAddress getAddress() { return NimBLEAddress(m_desc.peer_ota_addr); }
|
||||
NimBLEAddress getAddress() const { return NimBLEAddress(m_desc.peer_ota_addr); }
|
||||
|
||||
/** @brief Gets the ID address of the connected peer */
|
||||
NimBLEAddress getIdAddress() { return NimBLEAddress(m_desc.peer_id_addr); }
|
||||
NimBLEAddress getIdAddress() const { return NimBLEAddress(m_desc.peer_id_addr); }
|
||||
|
||||
/** @brief Gets the connection handle of the connected peer */
|
||||
uint16_t getConnHandle() { return m_desc.conn_handle; }
|
||||
/** @brief Gets the connection handle (also known as the connection id) of the connected peer */
|
||||
uint16_t getConnHandle() const { return m_desc.conn_handle; }
|
||||
|
||||
/** @brief Gets the connection interval for this connection (in 1.25ms units) */
|
||||
uint16_t getConnInterval() { return m_desc.conn_itvl; }
|
||||
uint16_t getConnInterval() const { return m_desc.conn_itvl; }
|
||||
|
||||
/** @brief Gets the supervision timeout for this connection (in 10ms units) */
|
||||
uint16_t getConnTimeout() { return m_desc.supervision_timeout; }
|
||||
uint16_t getConnTimeout() const { return m_desc.supervision_timeout; }
|
||||
|
||||
/** @brief Gets the allowable latency for this connection (unit = number of intervals) */
|
||||
uint16_t getConnLatency() { return m_desc.conn_latency; }
|
||||
uint16_t getConnLatency() const { return m_desc.conn_latency; }
|
||||
|
||||
/** @brief Gets the maximum transmission unit size for this connection (in bytes) */
|
||||
uint16_t getMTU() { return ble_att_mtu(m_desc.conn_handle); }
|
||||
uint16_t getMTU() const { return ble_att_mtu(m_desc.conn_handle); }
|
||||
|
||||
/** @brief Check if we are in the master role in this connection */
|
||||
bool isMaster() { return (m_desc.role == BLE_GAP_ROLE_MASTER); }
|
||||
bool isMaster() const { return (m_desc.role == BLE_GAP_ROLE_MASTER); }
|
||||
|
||||
/** @brief Check if we are in the slave role in this connection */
|
||||
bool isSlave() { return (m_desc.role == BLE_GAP_ROLE_SLAVE); }
|
||||
bool isSlave() const { return (m_desc.role == BLE_GAP_ROLE_SLAVE); }
|
||||
|
||||
/** @brief Check if we are connected to a bonded peer */
|
||||
bool isBonded() { return (m_desc.sec_state.bonded == 1); }
|
||||
bool isBonded() const { return (m_desc.sec_state.bonded == 1); }
|
||||
|
||||
/** @brief Check if the connection in encrypted */
|
||||
bool isEncrypted() { return (m_desc.sec_state.encrypted == 1); }
|
||||
bool isEncrypted() const { return (m_desc.sec_state.encrypted == 1); }
|
||||
|
||||
/** @brief Check if the the connection has been authenticated */
|
||||
bool isAuthenticated() { return (m_desc.sec_state.authenticated == 1); }
|
||||
bool isAuthenticated() const { return (m_desc.sec_state.authenticated == 1); }
|
||||
|
||||
/** @brief Gets the key size used to encrypt the connection */
|
||||
uint8_t getSecKeySize() { return m_desc.sec_state.key_size; }
|
||||
uint8_t getSecKeySize() const { return m_desc.sec_state.key_size; }
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -55,7 +55,14 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
|
|||
m_pCharacteristic = pCharacteristic;
|
||||
m_pCallbacks = &defaultCallbacks; // No initial callback.
|
||||
m_properties = 0;
|
||||
|
||||
// Check if this is the client configuration descriptor and set to removed if true.
|
||||
if (uuid == NimBLEUUID((uint16_t)0x2902)) {
|
||||
NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove.");
|
||||
m_removed = 1;
|
||||
} else {
|
||||
m_removed = 0;
|
||||
}
|
||||
|
||||
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
|
||||
m_properties |= BLE_ATT_F_READ;
|
||||
|
@ -155,7 +162,7 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|||
|
||||
const ble_uuid_t *uuid;
|
||||
int rc;
|
||||
NimBLEConnInfo peerInfo;
|
||||
NimBLEConnInfo peerInfo{};
|
||||
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(),
|
||||
|
@ -165,12 +172,12 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|||
if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){
|
||||
switch(ctxt->op) {
|
||||
case BLE_GATT_ACCESS_OP_READ_DSC: {
|
||||
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
|
||||
// If the packet header is only 8 bytes this is a follow up of a long read
|
||||
// so we don't want to call the onRead() callback again.
|
||||
if(ctxt->om->om_pkthdr_len > 8 ||
|
||||
conn_handle == BLE_HS_CONN_HANDLE_NONE ||
|
||||
pDescriptor->m_value.size() <= (ble_att_mtu(peerInfo.getConnHandle()) - 3)) {
|
||||
pDescriptor->m_pCallbacks->onRead(pDescriptor, peerInfo);
|
||||
}
|
||||
|
@ -182,11 +189,9 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
|
|||
}
|
||||
|
||||
case BLE_GATT_ACCESS_OP_WRITE_DSC: {
|
||||
rc = ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
assert(rc == 0);
|
||||
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
|
||||
uint16_t att_max_len = pDescriptor->m_value.max_size();
|
||||
|
||||
if (ctxt->om->om_len > att_max_len) {
|
||||
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
|
||||
}
|
||||
|
|
|
@ -299,7 +299,7 @@ size_t NimBLEDevice::getClientListSize() {
|
|||
/**
|
||||
* @brief Get a reference to a client by connection ID.
|
||||
* @param [in] conn_id The client connection ID to search for.
|
||||
* @return A pointer to the client object with the spcified connection ID.
|
||||
* @return A pointer to the client object with the specified connection ID or nullptr.
|
||||
*/
|
||||
/* STATIC */
|
||||
NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
|
||||
|
@ -308,7 +308,7 @@ NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
|
|||
return (*it);
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
|
||||
return nullptr;
|
||||
} // getClientByID
|
||||
|
||||
|
@ -567,10 +567,16 @@ int NimBLEDevice::getNumBonds() {
|
|||
|
||||
/**
|
||||
* @brief Deletes all bonding information.
|
||||
* @returns true on success, false on failure.
|
||||
*/
|
||||
/*STATIC*/
|
||||
void NimBLEDevice::deleteAllBonds() {
|
||||
ble_store_clear();
|
||||
bool NimBLEDevice::deleteAllBonds() {
|
||||
int rc = ble_store_clear();
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Failed to delete all bonds; rc=%d", rc);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -685,6 +691,7 @@ bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) {
|
|||
int rc = ble_gap_wl_set(&wlVec[0], wlVec.size());
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc);
|
||||
m_whiteList.pop_back();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -771,7 +778,7 @@ void NimBLEDevice::onReset(int reason)
|
|||
|
||||
m_synced = false;
|
||||
|
||||
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
|
||||
NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason,
|
||||
NimBLEUtils::returnCodeToString(reason));
|
||||
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
|
@ -799,7 +806,10 @@ void NimBLEDevice::onSync(void)
|
|||
|
||||
/* Make sure we have proper identity address set (public preferred) */
|
||||
int rc = ble_hs_util_ensure_addr(0);
|
||||
assert(rc == 0);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "error ensuring address; rc=%d", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef ESP_PLATFORM
|
||||
rc = ble_hs_id_infer_auto(m_own_addr_type, &m_own_addr_type);
|
||||
|
@ -871,9 +881,11 @@ void NimBLEDevice::init(const std::string &deviceName) {
|
|||
|
||||
ESP_ERROR_CHECK(errRc);
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) | !defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
# if defined (CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE;
|
||||
|
@ -902,14 +914,16 @@ void NimBLEDevice::init(const std::string &deviceName) {
|
|||
ble_hs_cfg.sm_bonding = 0;
|
||||
ble_hs_cfg.sm_mitm = 0;
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
ble_hs_cfg.sm_our_key_dist = 1;
|
||||
ble_hs_cfg.sm_their_key_dist = 3;
|
||||
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
|
||||
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
|
||||
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/
|
||||
|
||||
// Set the device name.
|
||||
rc = ble_svc_gap_device_name_set(deviceName.c_str());
|
||||
assert(rc == 0);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_svc_gap_device_name_set() failed; rc=%d", rc);
|
||||
}
|
||||
|
||||
ble_store_config_init();
|
||||
|
||||
|
@ -935,13 +949,13 @@ void NimBLEDevice::deinit(bool clearAll) {
|
|||
int ret = nimble_port_stop();
|
||||
if (ret == 0) {
|
||||
nimble_port_deinit();
|
||||
#ifdef ESP_PLATFORM
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#ifdef CONFIG_NIMBLE_CPP_IDF
|
||||
# if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
ret = esp_nimble_hci_and_controller_deinit();
|
||||
if (ret != ESP_OK) {
|
||||
NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret);
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
#endif
|
||||
initialized = false;
|
||||
m_synced = false;
|
||||
|
@ -1150,6 +1164,43 @@ int NimBLEDevice::startSecurity(uint16_t conn_id) {
|
|||
} // startSecurity
|
||||
|
||||
|
||||
/**
|
||||
* @brief Inject the provided passkey into the Security Manager
|
||||
* @param [in] peerInfo Connection information for the peer
|
||||
* @param [in] pin The 6-digit pin to inject
|
||||
* @return true if the passkey was injected successfully.
|
||||
*/
|
||||
bool NimBLEDevice::injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin) {
|
||||
int rc = 0;
|
||||
struct ble_sm_io pkey = {0,0};
|
||||
|
||||
pkey.action = BLE_SM_IOACT_INPUT;
|
||||
pkey.passkey = pin;
|
||||
|
||||
rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Inject the provided numeric comparison response into the Security Manager
|
||||
* @param [in] peerInfo Connection information for the peer
|
||||
* @param [in] accept Whether the user confirmed or declined the comparison
|
||||
*/
|
||||
bool NimBLEDevice::injectConfirmPIN(const NimBLEConnInfo& peerInfo, bool accept) {
|
||||
int rc = 0;
|
||||
struct ble_sm_io pkey = {0,0};
|
||||
|
||||
pkey.action = BLE_SM_IOACT_NUMCMP;
|
||||
pkey.numcmp_accept = accept;
|
||||
|
||||
rc = ble_sm_inject_io(peerInfo.getConnHandle(), &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Check if the device address is on our ignore list.
|
||||
* @param [in] address The address to look for.
|
||||
|
@ -1202,10 +1253,22 @@ void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
|
|||
int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL);
|
||||
if(rc == BLE_HS_EALREADY){
|
||||
NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events.");
|
||||
} else if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_event_listener_register: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
}
|
||||
else{
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
} // setCustomGapHandler
|
||||
|
||||
#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED || __DOXYGEN__
|
||||
/**
|
||||
* @brief Debug assert - weak function.
|
||||
* @param [in] file The file where the assert occurred.
|
||||
* @param [in] line The line number where the assert occurred.
|
||||
*/
|
||||
void nimble_cpp_assert(const char *file, unsigned line) {
|
||||
NIMBLE_LOGC("", "Assertion failed at %s:%u\n", file, line);
|
||||
abort();
|
||||
}
|
||||
#endif // CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
|
@ -136,6 +136,8 @@ public:
|
|||
static void setSecurityPasskey(uint32_t pin);
|
||||
static uint32_t getSecurityPasskey();
|
||||
static int startSecurity(uint16_t conn_id);
|
||||
static bool injectConfirmPIN(const NimBLEConnInfo& peerInfo, bool accept);
|
||||
static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin);
|
||||
static int setMTU(uint16_t mtu);
|
||||
static uint16_t getMTU();
|
||||
static bool isIgnored(const NimBLEAddress &address);
|
||||
|
@ -172,7 +174,7 @@ public:
|
|||
static bool deleteBond(const NimBLEAddress &address);
|
||||
static int getNumBonds();
|
||||
static bool isBonded(const NimBLEAddress &address);
|
||||
static void deleteAllBonds();
|
||||
static bool deleteAllBonds();
|
||||
static NimBLEAddress getBondedAddress(int index);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ uint16_t NimBLEEddystoneTLM::getVolt() {
|
|||
* @return The temperature value.
|
||||
*/
|
||||
float NimBLEEddystoneTLM::getTemp() {
|
||||
return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f;
|
||||
return (int16_t)ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f;
|
||||
} // getTemp
|
||||
|
||||
/**
|
||||
|
@ -203,7 +203,7 @@ void NimBLEEddystoneTLM::setVolt(uint16_t volt) {
|
|||
* @param [in] temp The temperature value.
|
||||
*/
|
||||
void NimBLEEddystoneTLM::setTemp(float temp) {
|
||||
m_eddystoneData.temp = (uint16_t)temp;
|
||||
m_eddystoneData.temp = ENDIAN_CHANGE_U16((int16_t)(temp * 256.0f));
|
||||
} // setTemp
|
||||
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg)
|
|||
case BLE_HS_EOS:
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGC(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
|
||||
NIMBLE_LOGE(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
|
||||
NimBLEDevice::onReset(event->adv_complete.reason);
|
||||
return 0;
|
||||
default:
|
||||
|
@ -623,9 +623,6 @@ void NimBLEExtAdvertisement::addData(const uint8_t * data, size_t length) {
|
|||
/**
|
||||
* @brief Set the appearance.
|
||||
* @param [in] appearance The appearance code value.
|
||||
*
|
||||
* See also:
|
||||
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
|
||||
*/
|
||||
void NimBLEExtAdvertisement::setAppearance(uint16_t appearance) {
|
||||
char cdata[2];
|
||||
|
|
|
@ -26,28 +26,34 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
|
|||
/*
|
||||
* Here we create mandatory services described in bluetooth specification
|
||||
*/
|
||||
m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a));
|
||||
m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812));
|
||||
m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f));
|
||||
m_deviceInfoService = server->createService(NimBLEUUID((uint16_t)0x180a));
|
||||
m_hidService = server->createService(NimBLEUUID((uint16_t)0x1812));
|
||||
m_batteryService = server->createService(NimBLEUUID((uint16_t)0x180f));
|
||||
|
||||
/*
|
||||
* Mandatory characteristic for device info service
|
||||
*/
|
||||
m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, NIMBLE_PROPERTY::READ);
|
||||
m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, NIMBLE_PROPERTY::READ);
|
||||
|
||||
/*
|
||||
* Non-mandatory characteristics for device info service
|
||||
* Will be created on demand
|
||||
*/
|
||||
m_manufacturerCharacteristic = nullptr;
|
||||
|
||||
/*
|
||||
* Mandatory characteristics for HID service
|
||||
*/
|
||||
m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, NIMBLE_PROPERTY::READ);
|
||||
m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, NIMBLE_PROPERTY::READ);
|
||||
m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, NIMBLE_PROPERTY::WRITE_NR);
|
||||
m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ);
|
||||
m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4a, NIMBLE_PROPERTY::READ);
|
||||
m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4b, NIMBLE_PROPERTY::READ);
|
||||
m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4c, NIMBLE_PROPERTY::WRITE_NR);
|
||||
m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ);
|
||||
|
||||
/*
|
||||
* Mandatory battery level characteristic with notification and presence descriptor
|
||||
*/
|
||||
m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
|
||||
NimBLE2904* batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t) 0x2904);
|
||||
m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
|
||||
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t)0x2904);
|
||||
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
|
||||
batteryLevelDescriptor->setNamespace(1);
|
||||
batteryLevelDescriptor->setUnit(0x27ad);
|
||||
|
@ -56,8 +62,8 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
|
|||
* This value is setup here because its default value in most usage cases, its very rare to use boot mode
|
||||
* and we want to simplify library using as much as possible
|
||||
*/
|
||||
const uint8_t pMode[] = { 0x01 };
|
||||
protocolMode()->setValue((uint8_t*) pMode, 1);
|
||||
const uint8_t pMode[] = {0x01};
|
||||
protocolMode()->setValue((uint8_t*)pMode, 1);
|
||||
}
|
||||
|
||||
NimBLEHIDDevice::~NimBLEHIDDevice() {
|
||||
|
@ -86,7 +92,10 @@ void NimBLEHIDDevice::startServices() {
|
|||
* @brief Create a manufacturer characteristic (this characteristic is optional).
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
|
||||
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ);
|
||||
if (m_manufacturerCharacteristic == nullptr) {
|
||||
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, NIMBLE_PROPERTY::READ);
|
||||
}
|
||||
|
||||
return m_manufacturerCharacteristic;
|
||||
}
|
||||
|
||||
|
@ -95,7 +104,7 @@ NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
|
|||
* @param [in] name The manufacturer name of this HID device.
|
||||
*/
|
||||
void NimBLEHIDDevice::manufacturer(std::string name) {
|
||||
m_manufacturerCharacteristic->setValue(name);
|
||||
manufacturer()->setValue(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,7 +115,15 @@ void NimBLEHIDDevice::manufacturer(std::string name) {
|
|||
* @param [in] version The produce version number.
|
||||
*/
|
||||
void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
|
||||
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version };
|
||||
uint8_t pnp[] = {
|
||||
sig,
|
||||
((uint8_t*)&vid)[0],
|
||||
((uint8_t*)&vid)[1],
|
||||
((uint8_t*)&pid)[0],
|
||||
((uint8_t*)&pid)[1],
|
||||
((uint8_t*)&version)[0],
|
||||
((uint8_t*)&version)[1]
|
||||
};
|
||||
m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
|
||||
}
|
||||
|
||||
|
@ -116,7 +133,7 @@ void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t vers
|
|||
* @param [in] flags The HID Class Specification release number to use.
|
||||
*/
|
||||
void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
|
||||
uint8_t info[] = { 0x11, 0x1, country, flags };
|
||||
uint8_t info[] = {0x11, 0x1, country, flags};
|
||||
m_hidInfoCharacteristic->setValue(info, sizeof(info));
|
||||
}
|
||||
|
||||
|
@ -126,11 +143,11 @@ void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
|
|||
* @return pointer to new input report characteristic
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
|
||||
NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
|
||||
NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
|
||||
NimBLECharacteristic *inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
|
||||
NimBLEDescriptor *inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x01 };
|
||||
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
uint8_t desc1_val[] = {reportID, 0x01};
|
||||
inputReportDescriptor->setValue((uint8_t*)desc1_val, 2);
|
||||
|
||||
return inputReportCharacteristic;
|
||||
}
|
||||
|
@ -141,11 +158,11 @@ NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
|
|||
* @return Pointer to new output report characteristic
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
|
||||
NimBLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
NimBLECharacteristic *outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
NimBLEDescriptor *outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x02 };
|
||||
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
uint8_t desc1_val[] = {reportID, 0x02};
|
||||
outputReportDescriptor->setValue((uint8_t*)desc1_val, 2);
|
||||
|
||||
return outputReportCharacteristic;
|
||||
}
|
||||
|
@ -156,11 +173,11 @@ NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
|
|||
* @return Pointer to new feature report characteristic
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) {
|
||||
NimBLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
NimBLECharacteristic *featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
NimBLEDescriptor *featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t)0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x03 };
|
||||
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
uint8_t desc1_val[] = {reportID, 0x03};
|
||||
featureReportDescriptor->setValue((uint8_t*)desc1_val, 2);
|
||||
|
||||
return featureReportCharacteristic;
|
||||
}
|
||||
|
@ -169,14 +186,14 @@ NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) {
|
|||
* @brief Creates a keyboard boot input report characteristic
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::bootInput() {
|
||||
return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY);
|
||||
return m_hidService->createCharacteristic((uint16_t)0x2a22, NIMBLE_PROPERTY::NOTIFY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a keyboard boot output report characteristic
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::bootOutput() {
|
||||
return m_hidService->createCharacteristic((uint16_t) 0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
|
||||
return m_hidService->createCharacteristic((uint16_t)0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#define HID_DIGITAL_PEN 0x03C7
|
||||
#define HID_BARCODE 0x03C8
|
||||
|
||||
#define PNPVersionField(MajorVersion, MinorVersion, PatchVersion) ((MajorVersion << 16) & 0xFF00) | ((MinorVersion << 8) & 0x00F0) | (PatchVersion & 0x000F)
|
||||
|
||||
/**
|
||||
* @brief A model of a %BLE Human Interface Device.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF) // using esp-idf
|
||||
# include "esp_log.h"
|
||||
# include "console/console.h"
|
||||
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
|
||||
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
|
||||
# endif
|
||||
|
@ -35,22 +36,6 @@
|
|||
# define NIMBLE_LOGE(tag, format, ...) \
|
||||
NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
|
||||
|
||||
# define NIMBLE_LOGC(tag, format, ...) \
|
||||
NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
|
||||
|
||||
// These defines pollute the global namespace and conflict with Tasmota and basically always turn on `seriallog 3`
|
||||
#ifdef LOG_LEVEL_DEBUG
|
||||
#undef LOG_LEVEL_DEBUG
|
||||
#endif
|
||||
|
||||
#ifdef LOG_LEVEL_INFO
|
||||
#undef LOG_LEVEL_INFO
|
||||
#endif
|
||||
|
||||
#ifdef LOG_LEVEL_ERROR
|
||||
#undef LOG_LEVEL_ERROR
|
||||
#endif
|
||||
|
||||
#else // using Arduino
|
||||
# include "nimble/porting/nimble/include/syscfg/syscfg.h"
|
||||
# include "nimble/console/console.h"
|
||||
|
@ -82,12 +67,13 @@
|
|||
|
||||
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1
|
||||
# define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
|
||||
# define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__)
|
||||
# else
|
||||
# define NIMBLE_LOGE( tag, format, ... ) (void)tag
|
||||
# define NIMBLE_LOGC( tag, format, ... ) (void)tag
|
||||
# endif
|
||||
|
||||
#endif /* CONFIG_NIMBLE_CPP_IDF */
|
||||
|
||||
#define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\n", tag, ##__VA_ARGS__)
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* MAIN_NIMBLELOG_H_ */
|
||||
|
|
|
@ -359,7 +359,7 @@ bool NimBLEScan::start(uint32_t duration, bool is_continue) {
|
|||
case BLE_HS_EOS:
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset");
|
||||
NIMBLE_LOGE(LOG_TAG, "Unable to scan - Host Reset");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -459,7 +459,7 @@ void NimBLEScan::onHostSync() {
|
|||
|
||||
/**
|
||||
* @brief Start scanning and block until scanning has been completed.
|
||||
* @param [in] duration The duration in seconds for which to scan.
|
||||
* @param [in] duration The duration in milliseconds for which to scan.
|
||||
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
|
||||
* @return The scan results.
|
||||
*/
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
#include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#define NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB 0
|
||||
#define NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB 1
|
||||
|
||||
static const char* LOG_TAG = "NimBLEServer";
|
||||
static NimBLEServerCallbacks defaultCallbacks;
|
||||
|
||||
|
@ -47,6 +52,7 @@ NimBLEServer::NimBLEServer() {
|
|||
#endif
|
||||
m_svcChanged = false;
|
||||
m_deleteCallbacks = true;
|
||||
m_getPeerNameOnConnect = false;
|
||||
} // NimBLEServer
|
||||
|
||||
|
||||
|
@ -186,9 +192,8 @@ void NimBLEServer::start() {
|
|||
|
||||
int rc = ble_gatts_start();
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc,
|
||||
NimBLEUtils::returnCodeToString(rc));
|
||||
abort();
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
return;
|
||||
}
|
||||
|
||||
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
|
||||
|
@ -215,7 +220,9 @@ void NimBLEServer::start() {
|
|||
if(svc->m_removed == 0) {
|
||||
rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle);
|
||||
if(rc != 0) {
|
||||
abort();
|
||||
NIMBLE_LOGW(LOG_TAG, "GATT Server started without service: %s, Service %s",
|
||||
svc->getUUID().toString().c_str(), svc->isStarted() ? "missing" : "not started");
|
||||
continue; // Skip this service as it was not started
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,6 +259,15 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
|
|||
return rc;
|
||||
} // disconnect
|
||||
|
||||
/**
|
||||
* @brief Disconnect the specified client with optional reason.
|
||||
* @param [in] connInfo Connection of the client to disconnect.
|
||||
* @param [in] reason code for disconnecting.
|
||||
* @return NimBLE host return code.
|
||||
*/
|
||||
int NimBLEServer::disconnect(const NimBLEConnInfo &connInfo, uint8_t reason) {
|
||||
return disconnect(connInfo.getConnHandle(), reason);
|
||||
} // disconnect
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
||||
/**
|
||||
|
@ -263,6 +279,15 @@ void NimBLEServer::advertiseOnDisconnect(bool aod) {
|
|||
} // advertiseOnDisconnect
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set the server to automatically read the name from the connected peer before
|
||||
* the onConnect callback is called and enables the override callback with name parameter.
|
||||
* @param [in] enable Enable reading the connected peer name upon connection.
|
||||
*/
|
||||
void NimBLEServer::getPeerNameOnConnect(bool enable) {
|
||||
m_getPeerNameOnConnect = enable;
|
||||
} // getPeerNameOnConnect
|
||||
|
||||
/**
|
||||
* @brief Return the number of connected clients.
|
||||
* @return The number of connected clients.
|
||||
|
@ -328,6 +353,113 @@ NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
|
|||
return peerInfo;
|
||||
} // getPeerIDInfo
|
||||
|
||||
/**
|
||||
* @brief Callback that is called after reading from the peer name characteristic.
|
||||
* @details This will check the task pointer in the task data struct to determine
|
||||
* the action to take once the name has been read. If there is a task waiting then
|
||||
* it will be woken, if not, the the RC value is checked to determine which callback
|
||||
* should be called.
|
||||
*/
|
||||
int NimBLEServer::peerNameCB(uint16_t conn_handle,
|
||||
const struct ble_gatt_error *error,
|
||||
struct ble_gatt_attr *attr,
|
||||
void *arg) {
|
||||
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
|
||||
std::string *name = (std::string*)pTaskData->buf;
|
||||
int rc = error->status;
|
||||
|
||||
if (rc == 0) {
|
||||
if (attr) {
|
||||
name->append(OS_MBUF_DATA(attr->om, char*), OS_MBUF_PKTLEN(attr->om));
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == BLE_HS_EDONE) {
|
||||
// No ask means this was read for a callback.
|
||||
if (pTaskData->task == nullptr) {
|
||||
NimBLEServer* pServer = (NimBLEServer*)pTaskData->pATT;
|
||||
NimBLEConnInfo peerInfo{};
|
||||
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
|
||||
// Use the rc value as a flag to indicate which callback should be called.
|
||||
if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
|
||||
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo, *name);
|
||||
} else if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) {
|
||||
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo, *name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NIMBLE_LOGE(LOG_TAG, "NimBLEServerPeerNameCB rc=%d; %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
}
|
||||
|
||||
if (pTaskData->task != nullptr) {
|
||||
pTaskData->rc = rc;
|
||||
xTaskNotifyGive(pTaskData->task);
|
||||
} else {
|
||||
// If the read was triggered for callback use then these were allocated.
|
||||
delete name;
|
||||
delete pTaskData;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Internal method that sends the read command.
|
||||
*/
|
||||
std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type) {
|
||||
std::string *buf = new std::string{};
|
||||
ble_task_data_t *taskData = new ble_task_data_t{this, task, cb_type, buf};
|
||||
ble_uuid16_t uuid {{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME};
|
||||
int rc = ble_gattc_read_by_uuid(conn_handle,
|
||||
1,
|
||||
0xffff,
|
||||
((ble_uuid_t*)&uuid),
|
||||
NimBLEServer::peerNameCB,
|
||||
taskData);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
NimBLEConnInfo peerInfo{};
|
||||
ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
|
||||
if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
|
||||
m_pServerCallbacks->onConnect(this, peerInfo, *buf);
|
||||
} else if (cb_type == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) {
|
||||
m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf);
|
||||
}
|
||||
delete buf;
|
||||
delete taskData;
|
||||
} else if (task != nullptr) {
|
||||
#ifdef ulTaskNotifyValueClear
|
||||
// Clear the task notification value to ensure we block
|
||||
ulTaskNotifyValueClear(task, ULONG_MAX);
|
||||
#endif
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
rc = taskData->rc;
|
||||
std::string name{*(std::string*)taskData->buf};
|
||||
delete buf;
|
||||
delete taskData;
|
||||
|
||||
if (rc != 0 && rc != BLE_HS_EDONE) {
|
||||
NIMBLE_LOGE(LOG_TAG, "getPeerName rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
// TaskData and name buffer will be deleted in the callback.
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the name of the connected peer.
|
||||
* @param connInfo A reference to a NimBLEConnInfo instance to read the name from.
|
||||
* @returns A string containing the name.
|
||||
* @note This is a blocking call and should NOT be called from any callbacks!
|
||||
*/
|
||||
std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) {
|
||||
std::string name = getPeerNameInternal(connInfo.getConnHandle(), xTaskGetCurrentTaskHandle());
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle a GATT Server Event.
|
||||
|
@ -354,18 +486,24 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
||||
NimBLEDevice::startAdvertising();
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
pServer->m_connectedPeersVec.push_back(event->connect.conn_handle);
|
||||
|
||||
} else {
|
||||
rc = ble_gap_conn_find(event->connect.conn_handle, &peerInfo.m_desc);
|
||||
if (rc != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pServer->m_connectedPeersVec.push_back(event->connect.conn_handle);
|
||||
|
||||
if (pServer->m_getPeerNameOnConnect) {
|
||||
pServer->getPeerNameInternal(event->connect.conn_handle,
|
||||
nullptr,
|
||||
NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB);
|
||||
} else {
|
||||
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // BLE_GAP_EVENT_CONNECT
|
||||
|
||||
|
@ -378,7 +516,7 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
case BLE_HS_EOS:
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
|
||||
NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
|
||||
NimBLEDevice::onReset(event->disconnect.reason);
|
||||
break;
|
||||
default:
|
||||
|
@ -514,10 +652,26 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
return BLE_ATT_ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (pServer->m_getPeerNameOnConnect) {
|
||||
pServer->getPeerNameInternal(event->enc_change.conn_handle,
|
||||
nullptr,
|
||||
NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB);
|
||||
} else {
|
||||
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);
|
||||
}
|
||||
return 0;
|
||||
} // BLE_GAP_EVENT_ENC_CHANGE
|
||||
|
||||
case BLE_GAP_EVENT_IDENTITY_RESOLVED: {
|
||||
rc = ble_gap_conn_find(event->identity_resolved.conn_handle, &peerInfo.m_desc);
|
||||
if(rc != 0) {
|
||||
return BLE_ATT_ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
pServer->m_pServerCallbacks->onIdentity(peerInfo);
|
||||
return 0;
|
||||
} // BLE_GAP_EVENT_IDENTITY_RESOLVED
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION: {
|
||||
struct ble_sm_io pkey = {0,0};
|
||||
|
||||
|
@ -528,19 +682,20 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
// if the (static)passkey is the default, check the callback for custom value
|
||||
// both values default to the same.
|
||||
if(pkey.passkey == 123456) {
|
||||
pkey.passkey = pServer->m_pServerCallbacks->onPassKeyRequest();
|
||||
pkey.passkey = pServer->m_pServerCallbacks->onPassKeyDisplay();
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc);
|
||||
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.numcmp_accept = pServer->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp);
|
||||
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
|
||||
rc = ble_gap_conn_find(event->passkey.conn_handle, &peerInfo.m_desc);
|
||||
if(rc != 0) {
|
||||
return BLE_ATT_ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
pServer->m_pServerCallbacks->onConfirmPIN(peerInfo, event->passkey.params.numcmp);
|
||||
//TODO: Handle out of band pairing
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
||||
static uint8_t tem_oob[16] = {0};
|
||||
|
@ -551,14 +706,6 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc);
|
||||
//////////////////////////////////
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Enter the passkey");
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = pServer->m_pServerCallbacks->onPassKeyRequest();
|
||||
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
|
||||
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
|
||||
NIMBLE_LOGD(LOG_TAG, "No passkey action required");
|
||||
}
|
||||
|
@ -842,6 +989,10 @@ void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& con
|
|||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
|
||||
} // onConnect
|
||||
|
||||
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) {
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
|
||||
} // onConnect
|
||||
|
||||
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer,
|
||||
NimBLEConnInfo& connInfo, int reason) {
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
|
||||
|
@ -851,18 +1002,26 @@ void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo)
|
|||
NIMBLE_LOGD("NimBLEServerCallbacks", "onMTUChange(): Default");
|
||||
} // onMTUChange
|
||||
|
||||
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
|
||||
uint32_t NimBLEServerCallbacks::onPassKeyDisplay(){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyDisplay: default: 123456");
|
||||
return 123456;
|
||||
} //onPassKeyRequest
|
||||
} //onPassKeyDisplay
|
||||
|
||||
void NimBLEServerCallbacks::onAuthenticationComplete(NimBLEConnInfo& connInfo){
|
||||
void NimBLEServerCallbacks::onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
|
||||
NimBLEDevice::injectConfirmPIN(connInfo, true);
|
||||
} // onConfirmPIN
|
||||
|
||||
void NimBLEServerCallbacks::onIdentity(const NimBLEConnInfo& connInfo){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onIdentity: default");
|
||||
} // onIdentity
|
||||
|
||||
void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
|
||||
} // onAuthenticationComplete
|
||||
|
||||
bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
|
||||
return true;
|
||||
} // onConfirmPIN
|
||||
void NimBLEServerCallbacks::onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
|
||||
} // onAuthenticationComplete
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
||||
|
|
|
@ -69,6 +69,8 @@ public:
|
|||
NimBLEService* getServiceByHandle(uint16_t handle);
|
||||
int disconnect(uint16_t connID,
|
||||
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
|
||||
int disconnect(const NimBLEConnInfo &connInfo,
|
||||
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
|
||||
void updateConnParams(uint16_t conn_handle,
|
||||
uint16_t minInterval, uint16_t maxInterval,
|
||||
uint16_t latency, uint16_t timeout);
|
||||
|
@ -78,6 +80,8 @@ public:
|
|||
NimBLEConnInfo getPeerInfo(size_t index);
|
||||
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
|
||||
NimBLEConnInfo getPeerIDInfo(uint16_t id);
|
||||
std::string getPeerName(const NimBLEConnInfo& connInfo);
|
||||
void getPeerNameOnConnect(bool enable);
|
||||
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
||||
void advertiseOnDisconnect(bool);
|
||||
#endif
|
||||
|
@ -98,6 +102,7 @@ private:
|
|||
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
||||
bool m_advertiseOnDisconnect;
|
||||
#endif
|
||||
bool m_getPeerNameOnConnect;
|
||||
bool m_svcChanged;
|
||||
NimBLEServerCallbacks* m_pServerCallbacks;
|
||||
bool m_deleteCallbacks;
|
||||
|
@ -110,10 +115,14 @@ private:
|
|||
std::vector<NimBLECharacteristic*> m_notifyChrVec;
|
||||
|
||||
static int handleGapEvent(struct ble_gap_event *event, void *arg);
|
||||
static int peerNameCB(uint16_t conn_handle, const struct ble_gatt_error *error,
|
||||
struct ble_gatt_attr *attr, void *arg);
|
||||
std::string getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type = -1);
|
||||
void serviceChanged();
|
||||
void resetGATT();
|
||||
bool setIndicateWait(uint16_t conn_handle);
|
||||
void clearIndicateWait(uint16_t conn_handle);
|
||||
|
||||
}; // NimBLEServer
|
||||
|
||||
|
||||
|
@ -128,11 +137,21 @@ public:
|
|||
* @brief Handle a client connection.
|
||||
* This is called when a client connects.
|
||||
* @param [in] pServer A pointer to the %BLE server that received the client connection.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information.
|
||||
* about the peer connection parameters.
|
||||
*/
|
||||
virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Handle a client connection.
|
||||
* This is called when a client connects.
|
||||
* @param [in] pServer A pointer to the %BLE server that received the client connection.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information.
|
||||
* @param [in] name The name of the connected peer device.
|
||||
* about the peer connection parameters.
|
||||
*/
|
||||
virtual void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name);
|
||||
|
||||
/**
|
||||
* @brief Handle a client disconnection.
|
||||
* This is called when a client discconnects.
|
||||
|
@ -152,24 +171,39 @@ public:
|
|||
virtual void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Called when a client requests a passkey for pairing.
|
||||
* @brief Called when a client requests a passkey for pairing (display).
|
||||
* @return The passkey to be sent to the client.
|
||||
*/
|
||||
virtual uint32_t onPassKeyRequest();
|
||||
virtual uint32_t onPassKeyDisplay();
|
||||
|
||||
/**
|
||||
* @brief Called when using numeric comparision for pairing.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
* Should be passed back to NimBLEDevice::injectConfirmPIN
|
||||
* @param [in] pin The pin to compare with the client.
|
||||
*/
|
||||
virtual void onConfirmPIN(const NimBLEConnInfo& connInfo, uint32_t pin);
|
||||
|
||||
/**
|
||||
* @brief Called when the pairing procedure is complete.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
* about the peer connection parameters.
|
||||
*/
|
||||
virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
|
||||
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo);
|
||||
|
||||
/**
|
||||
* @brief Called when using numeric comparision for pairing.
|
||||
* @param [in] pin The pin to compare with the client.
|
||||
* @return True to accept the pin.
|
||||
* @brief Called when the pairing procedure is complete.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
* @param [in] name The name of the connected peer device.
|
||||
* about the peer connection parameters.
|
||||
*/
|
||||
virtual bool onConfirmPIN(uint32_t pin);
|
||||
virtual void onAuthenticationComplete(const NimBLEConnInfo& connInfo, const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief Called when the peer identity address is resolved.
|
||||
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
|
||||
*/
|
||||
virtual void onIdentity(const NimBLEConnInfo& connInfo);
|
||||
}; // NimBLEServerCallbacks
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
||||
|
|
|
@ -126,7 +126,7 @@ bool NimBLEService::start() {
|
|||
// Nimble requires an array of services to be sent to the api
|
||||
// Since we are adding 1 at a time we create an array of 2 and set the type
|
||||
// of the second service to 0 to indicate the end of the array.
|
||||
ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
|
||||
ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]{};
|
||||
ble_gatt_chr_def* pChr_a = nullptr;
|
||||
ble_gatt_dsc_def* pDsc_a = nullptr;
|
||||
|
||||
|
@ -188,7 +188,7 @@ bool NimBLEService::start() {
|
|||
pChr_a[i].descriptors = NULL;
|
||||
} else {
|
||||
// Must have last descriptor uuid = 0 so we have to create 1 extra
|
||||
pDsc_a = new ble_gatt_dsc_def[numDscs+1];
|
||||
pDsc_a = new ble_gatt_dsc_def[numDscs+1]{};
|
||||
int d = 0;
|
||||
for(auto dsc_it = (*chr_it)->m_dscVec.begin(); dsc_it != (*chr_it)->m_dscVec.end(); ++dsc_it ) {
|
||||
if((*dsc_it)->m_removed > 0) {
|
||||
|
@ -434,4 +434,14 @@ NimBLEServer* NimBLEService::getServer() {
|
|||
return NimBLEDevice::getServer();
|
||||
}// getServer
|
||||
|
||||
|
||||
/**
|
||||
* @brief Checks if the service has been started.
|
||||
* @return True if the service has been started.
|
||||
*/
|
||||
bool NimBLEService::isStarted() {
|
||||
return m_pSvcDef != nullptr;
|
||||
}
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
uint16_t getHandle();
|
||||
std::string toString();
|
||||
void dump();
|
||||
|
||||
bool isStarted();
|
||||
bool start();
|
||||
|
||||
NimBLECharacteristic* createCharacteristic(const char* uuid,
|
||||
|
|
|
@ -16,45 +16,6 @@
|
|||
|
||||
static const char* LOG_TAG = "NimBLEUtils";
|
||||
|
||||
|
||||
/**
|
||||
* @brief A function for checking validity of connection parameters.
|
||||
* @param [in] params A pointer to the structure containing the parameters to check.
|
||||
* @return valid == 0 or error code.
|
||||
*/
|
||||
int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
|
||||
/* Check connection interval min */
|
||||
if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) ||
|
||||
(params->itvl_min > BLE_HCI_CONN_ITVL_MAX)) {
|
||||
return BLE_ERR_INV_HCI_CMD_PARMS;
|
||||
}
|
||||
/* Check connection interval max */
|
||||
if ((params->itvl_max < BLE_HCI_CONN_ITVL_MIN) ||
|
||||
(params->itvl_max > BLE_HCI_CONN_ITVL_MAX) ||
|
||||
(params->itvl_max < params->itvl_min)) {
|
||||
return BLE_ERR_INV_HCI_CMD_PARMS;
|
||||
}
|
||||
|
||||
/* Check connection latency */
|
||||
if (params->latency > BLE_HCI_CONN_LATENCY_MAX) {
|
||||
return BLE_ERR_INV_HCI_CMD_PARMS;
|
||||
}
|
||||
|
||||
/* Check supervision timeout */
|
||||
if ((params->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) ||
|
||||
(params->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) {
|
||||
return BLE_ERR_INV_HCI_CMD_PARMS;
|
||||
}
|
||||
|
||||
/* Check connection event length */
|
||||
if (params->min_ce_len > params->max_ce_len) {
|
||||
return BLE_ERR_INV_HCI_CMD_PARMS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a return code from the NimBLE stack to a text string.
|
||||
* @param [in] rc The return code to convert.
|
||||
|
|
|
@ -43,7 +43,6 @@ public:
|
|||
static char* buildHexData(uint8_t* target, const uint8_t* source, uint8_t length);
|
||||
static const char* advTypeToString(uint8_t advType);
|
||||
static const char* returnCodeToString(int rc);
|
||||
static int checkConnParams(ble_gap_conn_params* params);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -32,6 +32,18 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_NIMBLE_CPP_DEBUG_ASSERT_ENABLED && !defined NDEBUG
|
||||
void nimble_cpp_assert(const char *file, unsigned line) __attribute((weak, noreturn));
|
||||
# define NIMBLE_ATT_VAL_FILE (__builtin_strrchr(__FILE__, '/') ? \
|
||||
__builtin_strrchr (__FILE__, '/') + 1 : __FILE__)
|
||||
# define NIMBLE_CPP_DEBUG_ASSERT(cond) \
|
||||
if (!(cond)) { \
|
||||
nimble_cpp_assert(NIMBLE_ATT_VAL_FILE, __LINE__); \
|
||||
}
|
||||
#else
|
||||
# define NIMBLE_CPP_DEBUG_ASSERT(cond) (void(0))
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
|
||||
#ifdef _DOXYGEN_
|
||||
|
|
|
@ -198,6 +198,7 @@ struct {
|
|||
uint32_t runningScan:1;
|
||||
uint32_t updateScan:1;
|
||||
uint32_t deleteScanTask:1;
|
||||
uint32_t IRKinCfg:1;
|
||||
|
||||
|
||||
uint32_t canConnect:1;
|
||||
|
@ -401,7 +402,7 @@ const char kMI32DeviceType[] PROGMEM = {"Flora|MJ_HT_V1|LYWSD02|LYWSD03|CGG1|CGD
|
|||
|
||||
const char kMI32_ConnErrorMsg[] PROGMEM = "no Error|could not connect|did disconnect|got no service|got no characteristic|can not read|can not notify|can not write|did not write|notify time out";
|
||||
|
||||
const char kMI32_BLEInfoMsg[] PROGMEM = "Scan ended|Got Notification|Did connect|Did disconnect|Still connected|Start passive scanning|Start active scanning|Server characteristic set|Server advertisement set|Server scan response set|Server client did connect|Server client did disconnect";
|
||||
const char kMI32_BLEInfoMsg[] PROGMEM = "Scan ended|Got Notification|Did connect|Did disconnect|Still connected|Start passive scanning|Start active scanning|Server characteristic set|Server advertisement set|Server scan response set|Server client did connect|Server client did disconnect| Server client did authenticate";
|
||||
|
||||
const char kMI32_ButtonMsg[] PROGMEM = "Single|Double|Hold"; //mapping: in Tasmota: 1,2,3 ; for HomeKit and Xiaomi 0,1,2
|
||||
/*********************************************************************************************\
|
||||
|
@ -444,6 +445,7 @@ BLE_OP_ON_SUBSCRIBE_TO_NOTIFICATIONS_AND_INDICATIONS,
|
|||
BLE_OP_ON_CONNECT,
|
||||
BLE_OP_ON_DISCONNECT,
|
||||
BLE_OP_ON_STATUS,
|
||||
BLE_OP_ON_AUTHENTICATED
|
||||
};
|
||||
|
||||
enum MI32_ConnErrorMsg {
|
||||
|
@ -471,7 +473,8 @@ enum MI32_BLEInfoMsg {
|
|||
MI32_SERV_ADVERTISEMENT_ADDED,
|
||||
MI32_SERV_SCANRESPONSE_ADDED,
|
||||
MI32_SERV_CLIENT_CONNECTED,
|
||||
MI32_SERV_CLIENT_DISCONNECTED
|
||||
MI32_SERV_CLIENT_DISCONNECTED,
|
||||
MI32_SERV_CLIENT_AUTHENTICATED
|
||||
};
|
||||
|
||||
/*********************************************************************************************\
|
||||
|
|
|
@ -208,6 +208,35 @@ extern "C" {
|
|||
be_raisef(vm, "ble_error", "BLE: could not add MAC to watch list");
|
||||
}
|
||||
|
||||
// helper function
|
||||
NimBLEConnInfo be_BLE_get_ConnInfo(NimBLEClient *device);
|
||||
NimBLEConnInfo be_BLE_get_ConnInfo(NimBLEClient *device){
|
||||
if(!device){
|
||||
return NimBLEDevice::getServer()->getPeerInfo(0);
|
||||
} else {
|
||||
return NimBLEDevice::getClientList()->front()->getConnInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// from esp-nimble/ble_sm.c
|
||||
int ble_sm_read_bond(uint16_t conn_handle, struct ble_store_value_sec *out_bond)
|
||||
{
|
||||
struct ble_store_key_sec key_sec;
|
||||
struct ble_gap_conn_desc desc;
|
||||
int rc;
|
||||
|
||||
rc = ble_gap_conn_find(conn_handle, &desc);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
memset(&key_sec, 0, sizeof key_sec);
|
||||
key_sec.peer_addr = desc.peer_id_addr;
|
||||
|
||||
rc = ble_store_read_peer_sec(&key_sec, out_bond);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// BLE.info(void) -> map
|
||||
int32_t be_BLE_info(struct bvm *vm);
|
||||
int32_t be_BLE_info(struct bvm *vm) {
|
||||
|
@ -225,26 +254,43 @@ extern "C" {
|
|||
#else
|
||||
be_map_insert_int(vm, "version", 4);
|
||||
#endif
|
||||
#ifdef CONFIG_BT_NIMBLE_PERSIST
|
||||
// #ifdef CONFIG_BT_NIMBLE_PERSIST
|
||||
be_map_insert_int(vm, "bonds", NimBLEDevice::getNumBonds());
|
||||
#else
|
||||
be_map_insert_nil(vm, "bonds");
|
||||
#endif
|
||||
// #else
|
||||
// be_map_insert_nil(vm, "bonds");
|
||||
// #endif
|
||||
if(MI32.mode.connected == 1 || MI32.ServerTask != nullptr){
|
||||
NimBLEClient* _device = nullptr;
|
||||
if(MI32.mode.connected == 1){
|
||||
_device = NimBLEDevice::getClientList()->front();
|
||||
}
|
||||
NimBLEConnInfo _info = be_BLE_get_ConnInfo(_device);
|
||||
|
||||
be_pushstring(vm, "connection");
|
||||
be_newobject(vm, "map");
|
||||
auto _info = NimBLEDevice::getClientList()->front()->getConnInfo();
|
||||
|
||||
be_map_insert_str(vm, "peer_addr", _info.getAddress().toString().c_str());
|
||||
be_map_insert_int(vm, "RSSI", NimBLEDevice::getClientList()->front()->getRssi());
|
||||
be_map_insert_str(vm, "peerID_addr", _info.getIdAddress().toString().c_str());
|
||||
if(_device != nullptr) be_map_insert_int(vm, "RSSI", _device->getRssi()); // ESP32 is client
|
||||
be_map_insert_int(vm, "MTU", _info.getMTU());
|
||||
be_map_insert_bool(vm, "bonded", _info.isBonded());
|
||||
be_map_insert_bool(vm, "master", _info.isMaster());
|
||||
be_map_insert_bool(vm, "encrypted", _info.isEncrypted());
|
||||
be_map_insert_bool(vm, "authenticated", _info.isAuthenticated());
|
||||
if(_device == nullptr) be_map_insert_str(vm, "name", NimBLEDevice::getServer()->getPeerName(_info).c_str()); // ESP32 is server
|
||||
|
||||
ble_store_value_sec value_sec;
|
||||
ble_sm_read_bond(_info.getConnHandle(), &value_sec);
|
||||
if(value_sec.irk_present == 1){
|
||||
char IRK[33];
|
||||
ToHex_P(value_sec.irk,16,IRK,33);
|
||||
be_map_insert_str(vm, "IRK",IRK );
|
||||
}
|
||||
|
||||
be_pop(vm, 1);
|
||||
be_data_insert(vm, -3);
|
||||
be_pop(vm, 2);
|
||||
|
||||
}
|
||||
|
||||
be_pop(vm, 1);
|
||||
|
@ -271,6 +317,9 @@ be_BLE_op:
|
|||
3 subscribe
|
||||
4 unsubscribe - maybe later
|
||||
5 disconnect
|
||||
6 discover services
|
||||
7 discover characteristics
|
||||
|
||||
|
||||
11 read once, then disconnect
|
||||
12 write once, then disconnect
|
||||
|
@ -294,6 +343,7 @@ __response
|
|||
227 onConnect
|
||||
228 onDisconnect
|
||||
229 onStatus
|
||||
230 onAuthenticated
|
||||
|
||||
|
||||
BLE.conn_cb(cb,buffer)
|
||||
|
|
|
@ -87,7 +87,7 @@ class MI32SensorCallback : public NimBLEClientCallbacks {
|
|||
MI32.infoMsg = MI32_DID_CONNECT;
|
||||
MI32.mode.willConnect = 0;
|
||||
MI32.mode.connected = 1;
|
||||
pclient->updateConnParams(8,11,0,1000);
|
||||
pclient->updateConnParams(8,16,0,1000);
|
||||
}
|
||||
void onDisconnect(NimBLEClient* pclient, int reason) {
|
||||
MI32.mode.connected = 0;
|
||||
|
@ -186,6 +186,19 @@ class MI32ServerCallbacks: public NimBLEServerCallbacks {
|
|||
NimBLEDevice::startAdvertising();
|
||||
#endif
|
||||
};
|
||||
void onAuthenticationComplete(const NimBLEConnInfo& connInfo) {
|
||||
struct{
|
||||
BLERingBufferItem_t header;
|
||||
uint8_t buffer[sizeof(ble_store_value_sec)];
|
||||
} item;
|
||||
item.header.length = sizeof(ble_store_value_sec);
|
||||
item.header.type = BLE_OP_ON_AUTHENTICATED;
|
||||
ble_store_value_sec value_sec;
|
||||
ble_sm_read_bond(connInfo.getConnHandle(), &value_sec);
|
||||
memcpy(item.buffer,(uint8_t*)&value_sec,sizeof(ble_store_value_sec));
|
||||
xRingbufferSend(BLERingBufferQueue, (const void*)&item, sizeof(BLERingBufferItem_t), pdMS_TO_TICKS(1));
|
||||
MI32.infoMsg = MI32_SERV_CLIENT_AUTHENTICATED;
|
||||
}
|
||||
};
|
||||
|
||||
class MI32CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
|
||||
|
@ -691,6 +704,12 @@ void MI32Init(void) {
|
|||
#endif
|
||||
const std::string name(TasmotaGlobal.hostname);
|
||||
NimBLEDevice::init(name);
|
||||
#ifdef CONFIG_BT_NIMBLE_NVS_PERSIST
|
||||
NimBLEDevice::setSecurityAuth(true, true, true);
|
||||
#else
|
||||
NimBLEDevice::setSecurityAuth(false, true, true);
|
||||
#endif
|
||||
|
||||
AddLog(LOG_LEVEL_INFO,PSTR("M32: Init BLE device: %s"),TasmotaGlobal.hostname);
|
||||
MI32.mode.init = 1;
|
||||
MI32.mode.readyForNextConnJob = 1;
|
||||
|
@ -947,6 +966,9 @@ void MI32loadCfg(){
|
|||
MI32HexStringToBytes(_pidStr,_pid);
|
||||
uint16_t _pid16 = _pid[0]*256 + _pid[1];
|
||||
_numberOfDevices = MIBLEgetSensorSlot(_mac,_pid16,0);
|
||||
if (MIBLEsensors[_numberOfDevices].PID == 0) { // no Xiaomi sensor
|
||||
MI32.option.handleEveryDevice = 1; // if in config, we assume to handle it
|
||||
}
|
||||
_error = false;
|
||||
}
|
||||
}
|
||||
|
@ -958,6 +980,9 @@ void MI32loadCfg(){
|
|||
uint8_t *_key = (uint8_t*) malloc(16);
|
||||
MI32HexStringToBytes(_keyStr,_key);
|
||||
MIBLEsensors[_numberOfDevices].key = _key;
|
||||
if (MIBLEsensors[_numberOfDevices].PID == 0) { // no Xiaomi sensor
|
||||
MI32.mode.IRKinCfg = 1; // key is treated as IRK for RPA
|
||||
}
|
||||
}
|
||||
else{
|
||||
_error = true;
|
||||
|
@ -1239,12 +1264,6 @@ bool MI32StartConnectionTask(){
|
|||
void MI32ConnectionTask(void *pvParameters){
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3) || !defined(CONFIG_IDF_TARGET_ESP32C6) //needs more testing ...
|
||||
// NimBLEDevice::setOwnAddrType(BLE_OWN_ADDR_RANDOM,false); //seems to be important for i.e. xbox controller, hopefully not breaking other things
|
||||
#ifdef CONFIG_BT_NIMBLE_NVS_PERSIST
|
||||
NimBLEDevice::setSecurityAuth(true, true, true);
|
||||
#else
|
||||
NimBLEDevice::setSecurityAuth(false, true, true);
|
||||
#endif
|
||||
|
||||
#endif //CONFIG_IDF_TARGET_ESP32C3
|
||||
MI32.conCtx->error = MI32_CONN_NO_ERROR;
|
||||
if (MI32ConnectActiveSensor()){
|
||||
|
@ -2049,11 +2068,33 @@ void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) {
|
|||
}
|
||||
}
|
||||
|
||||
void MI32HandleEveryDevice(NimBLEAdvertisedDevice* advertisedDevice, uint8_t addr[6], int RSSI) {
|
||||
if(advertisedDevice->getAddressType() != BLE_ADDR_PUBLIC) {
|
||||
return;
|
||||
uint16_t MI32checkRPA(uint8_t *addr) {
|
||||
br_aes_small_cbcenc_keys cbc_ctx;
|
||||
size_t data_len = 16;
|
||||
int idx = -1;
|
||||
for (auto _sensor : MIBLEsensors) {
|
||||
idx += 1;
|
||||
if (_sensor.PID != 0) continue;
|
||||
if (_sensor.key == nullptr) continue;
|
||||
uint8_t iv[16] = {0};
|
||||
uint8_t data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,addr[0],addr[1],addr[2]};
|
||||
br_aes_small_cbcenc_init(&cbc_ctx, _sensor.key, 16);
|
||||
br_aes_small_cbcenc_run( &cbc_ctx, iv, data, data_len );
|
||||
if(data[13] == addr[3] && data[14] == addr[4] && data[15] == addr[5]) {
|
||||
MIBLEsensors[idx].lastTime = Rtc.local_time;
|
||||
return idx;
|
||||
}
|
||||
uint16_t _slot = MIBLEgetSensorSlot(addr, 0, 0);
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void MI32HandleEveryDevice(NimBLEAdvertisedDevice* advertisedDevice, uint8_t addr[6], int RSSI) {
|
||||
|
||||
uint16_t _slot;
|
||||
if (advertisedDevice->getAddressType() == BLE_ADDR_PUBLIC) { _slot = MIBLEgetSensorSlot(addr, 0, 0);}
|
||||
else if (advertisedDevice->getAddress().isRpa() && MI32.mode.IRKinCfg == 1) { _slot = MI32checkRPA(addr);}
|
||||
else {return;}
|
||||
|
||||
if(_slot==0xff) {
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue