mirror of https://github.com/arendst/Tasmota.git
Merge pull request #10600 from btsimonh/BLE_ESP32_FINAL
Ble esp32 final
This commit is contained in:
commit
ff6c328ba9
|
@ -2,6 +2,75 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- `NimBLEDevice::setOwnAddrType` added to enable the use of random and random-resolvable addresses, by asukiaaa
|
||||
|
||||
- New examples for securing and authenticating client/server connections, by mblasee.
|
||||
|
||||
- `NimBLEAdvertiseing::SetMinPreferred` and `NimBLEAdvertiseing::SetMinPreferred` re-added.
|
||||
|
||||
- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio.
|
||||
|
||||
- `NimBLEClient::setValue` Now takes an extra bool parameter `response` to enable the use of write with response (default = false).
|
||||
|
||||
- `NimBLEClient::getCharacteristic(uint16_t handle)` Enabling the use of the characteristic handle to be used to find
|
||||
the NimBLERemoteCharacteristic object.
|
||||
|
||||
- `NimBLEHIDDevice` class added by wakwak-koba.
|
||||
|
||||
- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application
|
||||
to obtain information about the disconnected client.
|
||||
|
||||
- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings.
|
||||
|
||||
### Changed
|
||||
- `NimBLEAdvertising::start` now returns a bool value to indicate success/failure.
|
||||
|
||||
- Some asserts were removed in `NimBLEAdvertising::start` and replaced with better return code handling and logging.
|
||||
|
||||
- If a host reset event occurs, scanning and advertising will now only be restarted if their previous duration was indefinite.
|
||||
|
||||
- `NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::registerForNotify` will now set the callback
|
||||
regardless of the existance of the CCCD and return true unless the descriptor write operation failed.
|
||||
|
||||
- Advertising tx power level is now sent in the advertisement packet instead of scan response.
|
||||
|
||||
- `NimBLEScan` When the scan ends the scan stopped flag is now set before calling the scan complete callback (if used)
|
||||
this allows the starting of a new scan from the callback function.
|
||||
|
||||
### Fixed
|
||||
- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock.
|
||||
A time limit has been added to timeout appropriately.
|
||||
|
||||
- When getting descriptors for a characterisic the end handle of the service was used as a proxy for the characteristic end
|
||||
handle. This would be rejected by some devices and has been changed to use the next characteristic handle as the end when possible.
|
||||
|
||||
- An exception could occur when deleting a client instance if a notification arrived while the attribute vectors were being
|
||||
deleted. A flag has been added to prevent this.
|
||||
|
||||
- An exception could occur after a host reset event when the host re-synced if the tasks that were stopped during the event did
|
||||
not finish processing. A yield has been added after re-syncing to allow tasks to finish before proceeding.
|
||||
|
||||
- Occasionally the controller would fail to send a disconnected event causing the client to indicate it is connected
|
||||
and would be unable to reconnect. A timer has been added to reset the host/controller if it expires.
|
||||
|
||||
- Occasionally the call to start scanning would get stuck in a loop on BLE_HS_EBUSY, this loop has been removed.
|
||||
|
||||
- 16bit and 32bit UUID's in some cases were not discovered or compared correctly if the device
|
||||
advertised them as 16/32bit but resolved them to 128bits. Both are now checked.
|
||||
|
||||
- `FreeRTOS` compile errors resolved in latest Ardruino core and IDF v3.3.
|
||||
|
||||
- Multiple instances of `time()` called inside critical sections caused sporadic crashes, these have been moved out of critical regions.
|
||||
|
||||
- Advertisement type now correctly set when using non-connectable (advertiser only) mode.
|
||||
|
||||
- Advertising payload length correction, now accounts for appearance.
|
||||
|
||||
- (Arduino) Ensure controller mode is set to BLE Only.
|
||||
|
||||
## [1.0.2] - 2020-09-13
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -68,9 +68,9 @@ such as increasing max connections, default is 3, absolute maximum connections i
|
|||
<br/>
|
||||
|
||||
# Development Status
|
||||
This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@95bd864.](https://github.com/espressif/esp-nimble)
|
||||
This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@f4ae049.](https://github.com/espressif/esp-nimble)
|
||||
|
||||
Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@2ef4890.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
|
||||
Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@3caa969.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
|
||||
<br/>
|
||||
|
||||
# Acknowledgments
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
# Arduino command line and platformio config options
|
||||
|
||||
`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED`
|
||||
|
||||
If defined, NimBLE Client functions will not be included.
|
||||
- Reduces flash size by approx. 7kB.
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED`
|
||||
|
||||
If defined, NimBLE Scan functions will not be included.
|
||||
- Reduces flash size by approx. 26kB.
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED`
|
||||
|
||||
If defined NimBLE Server functions will not be included.
|
||||
- Reduces flash size by approx. 16kB.
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED`
|
||||
|
||||
If defined, NimBLE Advertising functions will not be included.
|
||||
- Reduces flash size by approx. 5kB.
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_DEBUG`
|
||||
|
||||
If defined, enables debug log messages from the NimBLE host
|
||||
- Uses approx. 32kB of flash memory.
|
||||
<br>
|
||||
|
||||
`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT`
|
||||
|
||||
If defined, NimBLE host return codes will be printed as text in debug log messages.
|
||||
- Uses approx. 7kB of flash memory.
|
||||
<br>
|
||||
|
||||
`CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT`
|
||||
|
||||
If defined, GAP event codes will be printed as text in debug log messages.
|
||||
- Uses approx. 1kB of flash memory.
|
||||
<br>
|
||||
|
||||
`CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT`
|
||||
|
||||
If defined, advertisment types will be printed as text while scanning in debug log messages.
|
||||
- Uses approx. 250 bytes of flash memory.
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_PINNED_TO_CORE`
|
||||
|
||||
Sets the core the NimBLE host stack will run on
|
||||
- Options: 0 or 1
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_TASK_STACK_SIZE`
|
||||
|
||||
Set the task stack size for the NimBLE core.
|
||||
- Default is 4096
|
||||
<br>
|
||||
|
||||
|
||||
`CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL`
|
||||
|
||||
Sets the NimBLE stack to use external PSRAM will be loaded
|
||||
- Must be defined with a value of 1; Default is CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
|
||||
|
||||
Sets the number of simultaneous connections (esp controller max is 9)
|
||||
- Default value is 3
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_MAX_BONDS`
|
||||
|
||||
Sets the number of devices allowed to store/bond with
|
||||
- Default value is 3
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_MAX_CCCDS`
|
||||
|
||||
Sets the maximum number of CCCD subscriptions to store
|
||||
- Default value is 8
|
||||
<br>
|
||||
|
||||
`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
|
||||
|
||||
Set the default device name
|
||||
- Default value is "nimble"
|
||||
<br>
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/** NimBLE_Secure_Client Demo:
|
||||
*
|
||||
* This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
|
||||
* Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
|
||||
* To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
|
||||
*
|
||||
* Created: on Jan 08 2021
|
||||
* Author: mblasee
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
class ClientCallbacks : public NimBLEClientCallbacks
|
||||
{
|
||||
uint32_t onPassKeyRequest()
|
||||
{
|
||||
Serial.println("Client Passkey Request");
|
||||
/** return the passkey to send to the server */
|
||||
/** Change this to be different from NimBLE_Secure_Server if you want to test what happens on key mismatch */
|
||||
return 123456;
|
||||
};
|
||||
};
|
||||
static ClientCallbacks clientCB;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting NimBLE Client");
|
||||
|
||||
NimBLEDevice::init("");
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
NimBLEDevice::setSecurityAuth(true, true, true);
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY);
|
||||
NimBLEScan *pScan = NimBLEDevice::getScan();
|
||||
NimBLEScanResults results = pScan->start(5);
|
||||
|
||||
NimBLEUUID serviceUuid("ABCD");
|
||||
|
||||
for (int i = 0; i < results.getCount(); i++)
|
||||
{
|
||||
NimBLEAdvertisedDevice device = results.getDevice(i);
|
||||
Serial.println(device.getName().c_str());
|
||||
|
||||
if (device.isAdvertisingService(serviceUuid))
|
||||
{
|
||||
NimBLEClient *pClient = NimBLEDevice::createClient();
|
||||
pClient->setClientCallbacks(&clientCB, false);
|
||||
|
||||
if (pClient->connect(&device))
|
||||
{
|
||||
pClient->secureConnection();
|
||||
NimBLERemoteService *pService = pClient->getService(serviceUuid);
|
||||
if (pService != nullptr)
|
||||
{
|
||||
NimBLERemoteCharacteristic *pNonSecureCharacteristic = pService->getCharacteristic("1234");
|
||||
|
||||
if (pNonSecureCharacteristic != nullptr)
|
||||
{
|
||||
// Testing to read a non secured characteristic, you should be able to read this even if you have mismatching passkeys.
|
||||
std::string value = pNonSecureCharacteristic->readValue();
|
||||
// print or do whatever you need with the value
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
NimBLERemoteCharacteristic *pSecureCharacteristic = pService->getCharacteristic("1235");
|
||||
|
||||
if (pSecureCharacteristic != nullptr)
|
||||
{
|
||||
// Testing to read a secured characteristic, you should be able to read this only if you have matching passkeys, otherwise you should
|
||||
// get an error like this. E NimBLERemoteCharacteristic: "<< readValue rc=261"
|
||||
// This means you are trying to do something without the proper permissions.
|
||||
std::string value = pSecureCharacteristic->readValue();
|
||||
// print or do whatever you need with the value
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// failed to connect
|
||||
Serial.println("failed to connect");
|
||||
}
|
||||
|
||||
NimBLEDevice::deleteClient(pClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/** NimBLE_Secure_Server Demo:
|
||||
*
|
||||
* This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
|
||||
* Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
|
||||
* To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
|
||||
*
|
||||
* Created: on Jan 08 2021
|
||||
* Author: mblasee
|
||||
*/
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting NimBLE Server");
|
||||
NimBLEDevice::init("NimBLE");
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
NimBLEDevice::setSecurityAuth(true, true, true);
|
||||
NimBLEDevice::setSecurityPasskey(123456);
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
|
||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
||||
NimBLEService *pService = pServer->createService("ABCD");
|
||||
NimBLECharacteristic *pNonSecureCharacteristic = pService->createCharacteristic("1234", NIMBLE_PROPERTY::READ );
|
||||
NimBLECharacteristic *pSecureCharacteristic = pService->createCharacteristic("1235", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::READ_AUTHEN);
|
||||
|
||||
pService->start();
|
||||
pNonSecureCharacteristic->setValue("Hello Non Secure BLE");
|
||||
pSecureCharacteristic->setValue("Hello Secure BLE");
|
||||
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID("ABCD");
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
|
@ -116,9 +116,9 @@ void setup() {
|
|||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
/**This method is removed as it was no longer useful and consumed advertising space
|
||||
* pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
*/
|
||||
/** Note, this could be left out as that is the default value */
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
|
|
@ -44,10 +44,9 @@ void setup() {
|
|||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
/**These methods are removed as they are no longer useful and consumed advertising space
|
||||
* pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
* pAdvertising->setMinPreferred(0x12);
|
||||
*/
|
||||
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
pAdvertising->setMaxPreferred(0x12);
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Characteristic defined! Now you can read it in your phone!");
|
||||
}
|
||||
|
|
|
@ -120,9 +120,9 @@ void setup() {
|
|||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
/**This method is removed it was no longer useful and consumed advertising space
|
||||
* pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
*/
|
||||
/** Note, this could be left out as that is the default value */
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
|
|
@ -264,10 +264,14 @@ void FreeRTOS::Semaphore::setName(std::string name) {
|
|||
* @param [in] length The amount of storage to allocate for the ring buffer.
|
||||
* @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
|
||||
*/
|
||||
#if defined(ESP_IDF_VERSION) && !defined(ESP_IDF_VERSION_VAL) //Quick hack to detect if using IDF version that replaced ringbuf_type_t, ESP_IDF_VERSION_VAL is for IDF>4.0.0
|
||||
Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
|
||||
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
|
||||
Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
|
||||
#else
|
||||
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
|
||||
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
|
||||
#endif
|
||||
#else
|
||||
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
|
||||
#endif
|
||||
m_handle = ::xRingbufferCreate(length, type);
|
||||
} // Ringbuffer
|
||||
|
|
|
@ -68,8 +68,12 @@ public:
|
|||
*/
|
||||
class Ringbuffer {
|
||||
public:
|
||||
#if defined(ESP_IDF_VERSION) && !defined(ESP_IDF_VERSION_VAL) //Quick hack to detect if using IDF version that replaced ringbuf_type_t, ESP_IDF_VERSION_VAL is for IDF>4.0.0
|
||||
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
|
||||
Ringbuffer(size_t length, RingbufferType_t type = RINGBUF_TYPE_NOSPLIT);
|
||||
#else
|
||||
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
|
||||
#endif
|
||||
#else
|
||||
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
|
||||
#endif
|
||||
|
|
|
@ -37,7 +37,7 @@ NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic)
|
|||
m_data.m_unit = 0;
|
||||
m_data.m_description = 0;
|
||||
setValue((uint8_t*) &m_data, sizeof(m_data));
|
||||
} // BLE2902
|
||||
} // BLE2904
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,7 +32,7 @@ static const char* LOG_TAG = "NimBLEAdvertising";
|
|||
/**
|
||||
* @brief Construct a default advertising object.
|
||||
*/
|
||||
NimBLEAdvertising::NimBLEAdvertising() {
|
||||
NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() {
|
||||
memset(&m_advData, 0, sizeof m_advData);
|
||||
memset(&m_scanData, 0, sizeof m_scanData);
|
||||
memset(&m_advParams, 0, sizeof m_advParams);
|
||||
|
@ -41,15 +41,20 @@ NimBLEAdvertising::NimBLEAdvertising() {
|
|||
m_advData.name = (uint8_t *)name;
|
||||
m_advData.name_len = strlen(name);
|
||||
m_advData.name_is_complete = 1;
|
||||
m_scanData.tx_pwr_lvl_is_present = 1;
|
||||
m_scanData.tx_pwr_lvl = NimBLEDevice::getPower();
|
||||
m_advData.tx_pwr_lvl_is_present = 1;
|
||||
m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
|
||||
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
|
||||
m_advData.appearance = 0;
|
||||
m_advData.appearance_is_present = 0;
|
||||
m_advData.mfg_data_len = 0;
|
||||
m_advData.mfg_data = nullptr;
|
||||
m_advData.slave_itvl_range = nullptr;
|
||||
|
||||
#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||
m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
|
||||
#else
|
||||
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||
#endif
|
||||
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||
m_advParams.itvl_min = 0;
|
||||
m_advParams.itvl_max = 0;
|
||||
|
@ -58,6 +63,8 @@ NimBLEAdvertising::NimBLEAdvertising() {
|
|||
m_customScanResponseData = false;
|
||||
m_scanResp = true;
|
||||
m_advDataSet = false;
|
||||
// Set this to non-zero to prevent auto start if host reset before started by app.
|
||||
m_duration = BLE_HS_FOREVER;
|
||||
|
||||
} // NimBLEAdvertising
|
||||
|
||||
|
@ -86,7 +93,6 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
|
|||
* @param [in] serviceUUID The UUID of the service to expose.
|
||||
*/
|
||||
void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
|
||||
//m_serviceUUIDs.erase(std::remove_if(m_serviceUUIDs.begin(), m_serviceUUIDs.end(),[serviceUUID](const NimBLEUUID &s) {return serviceUUID == s;}), m_serviceUUIDs.end());
|
||||
for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) {
|
||||
if((*it) == serviceUUID) {
|
||||
m_serviceUUIDs.erase(it);
|
||||
|
@ -112,11 +118,9 @@ void NimBLEAdvertising::setAppearance(uint16_t appearance) {
|
|||
/**
|
||||
* @brief Set the type of advertisment to use.
|
||||
* @param [in] adv_type:
|
||||
* * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
|
||||
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle
|
||||
* * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
|
||||
* * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable
|
||||
* * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
|
||||
* * BLE_GAP_CONN_MODE_NON (0) - not connectable advertising
|
||||
* * BLE_GAP_CONN_MODE_DIR (1) - directed connectable advertising
|
||||
* * BLE_GAP_CONN_MODE_UND (2) - undirected connectable advertising
|
||||
*/
|
||||
void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){
|
||||
m_advParams.conn_mode = adv_type;
|
||||
|
@ -141,6 +145,64 @@ void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
|
|||
} // setMaxInterval
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the advertised min connection interval preferred by this device.
|
||||
* @param [in] mininterval the max interval value. Range = 0x0006 to 0x0C80.
|
||||
* @details Values not within the range will cancel advertising of this data.\n
|
||||
* Consumes 6 bytes of advertising space (combined with max interval).
|
||||
*/
|
||||
void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
|
||||
// invalid paramters, set the slave interval to null
|
||||
if(mininterval < 0x0006 || mininterval > 0x0C80) {
|
||||
m_advData.slave_itvl_range = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_advData.slave_itvl_range == nullptr) {
|
||||
m_advData.slave_itvl_range = m_slaveItvl;
|
||||
}
|
||||
|
||||
m_slaveItvl[0] = mininterval;
|
||||
m_slaveItvl[1] = mininterval >> 8;
|
||||
|
||||
uint16_t maxinterval = *(uint16_t*)(m_advData.slave_itvl_range+2);
|
||||
|
||||
// If mininterval is higher than the maxinterval make them the same
|
||||
if(mininterval > maxinterval) {
|
||||
m_slaveItvl[2] = m_slaveItvl[0];
|
||||
m_slaveItvl[3] = m_slaveItvl[1];
|
||||
}
|
||||
} // setMinPreferred
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the advertised max connection interval preferred by this device.
|
||||
* @param [in] maxinterval the max interval value. Range = 0x0006 to 0x0C80.
|
||||
* @details Values not within the range will cancel advertising of this data.\n
|
||||
* Consumes 6 bytes of advertising space (combined with min interval).
|
||||
*/
|
||||
void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
|
||||
// invalid paramters, set the slave interval to null
|
||||
if(maxinterval < 0x0006 || maxinterval > 0x0C80) {
|
||||
m_advData.slave_itvl_range = nullptr;
|
||||
return;
|
||||
}
|
||||
if(m_advData.slave_itvl_range == nullptr) {
|
||||
m_advData.slave_itvl_range = m_slaveItvl;
|
||||
}
|
||||
m_slaveItvl[2] = maxinterval;
|
||||
m_slaveItvl[3] = maxinterval >> 8;
|
||||
|
||||
uint16_t mininterval = *(uint16_t*)(m_advData.slave_itvl_range);
|
||||
|
||||
// If mininterval is higher than the maxinterval make them the same
|
||||
if(mininterval > maxinterval) {
|
||||
m_slaveItvl[0] = m_slaveItvl[2];
|
||||
m_slaveItvl[1] = m_slaveItvl[3];
|
||||
}
|
||||
} // setMaxPreferred
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set if scan response is available.
|
||||
* @param [in] set true = scan response available.
|
||||
|
@ -156,7 +218,8 @@ void NimBLEAdvertising::setScanResponse(bool set) {
|
|||
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
|
||||
*/
|
||||
void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
|
||||
NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d",
|
||||
scanRequestWhitelistOnly, connectWhitelistOnly);
|
||||
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
|
||||
m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE;
|
||||
NIMBLE_LOGD(LOG_TAG, "<< setScanFilter");
|
||||
|
@ -194,7 +257,8 @@ void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisem
|
|||
(uint8_t*)advertisementData.getPayload().data(),
|
||||
advertisementData.getPayload().length());
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
}
|
||||
m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
|
||||
NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData");
|
||||
|
@ -213,7 +277,8 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
|
|||
(uint8_t*)advertisementData.getPayload().data(),
|
||||
advertisementData.getPayload().length());
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
}
|
||||
m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
|
||||
NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData");
|
||||
|
@ -225,13 +290,14 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
|
|||
* @param [in] duration The duration, in seconds, to advertise, 0 == advertise forever.
|
||||
* @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends.
|
||||
*/
|
||||
void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
|
||||
bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d",
|
||||
m_customAdvData, m_customScanResponseData);
|
||||
|
||||
// If Host is not synced we cannot start advertising.
|
||||
if(!NimBLEDevice::m_synced) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||
|
@ -240,17 +306,21 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
if(!pServer->m_gattsStarted){
|
||||
pServer->start();
|
||||
} else if(pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) {
|
||||
NIMBLE_LOGW(LOG_TAG, "Max connections reached - not advertising");
|
||||
return;
|
||||
NIMBLE_LOGE(LOG_TAG, "Max connections reached - not advertising");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If already advertising just return
|
||||
if(ble_gap_adv_active()) {
|
||||
return;
|
||||
NIMBLE_LOGW(LOG_TAG, "Advertising already active");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the duration incase of host reset so we can restart with the same params
|
||||
m_duration = duration;
|
||||
|
||||
if(duration == 0){
|
||||
duration = BLE_HS_FOREVER;
|
||||
}
|
||||
|
@ -260,16 +330,31 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
|
||||
m_advCompCB = advCompleteCB;
|
||||
|
||||
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
|
||||
if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) {
|
||||
if(!m_scanResp) {
|
||||
m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON;
|
||||
m_advData.flags = BLE_HS_ADV_F_BREDR_UNSUP;
|
||||
}
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
|
||||
if (!m_customAdvData && !m_advDataSet) {
|
||||
//start with 3 bytes for the flags data
|
||||
uint8_t payloadLen = 3;
|
||||
uint8_t payloadLen = (2 + 1);
|
||||
if(m_advData.appearance_is_present)
|
||||
payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN);
|
||||
if(m_advData.tx_pwr_lvl_is_present)
|
||||
payloadLen += (2 + 1);
|
||||
if(m_advData.slave_itvl_range != nullptr)
|
||||
payloadLen += (2 + 4);
|
||||
|
||||
for(auto &it : m_serviceUUIDs) {
|
||||
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
|
||||
int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
|
||||
if((payloadLen + add) > 31){
|
||||
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
|
||||
m_advData.uuids16_is_complete = 0;
|
||||
continue;
|
||||
}
|
||||
|
@ -278,7 +363,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16,
|
||||
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
|
||||
{
|
||||
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
|
||||
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
|
||||
abort();
|
||||
}
|
||||
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
|
||||
|
@ -290,7 +375,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
}
|
||||
if(it.getNative()->u.type == BLE_UUID_TYPE_32) {
|
||||
int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
|
||||
if((payloadLen + add) > 31){
|
||||
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
|
||||
m_advData.uuids32_is_complete = 0;
|
||||
continue;
|
||||
}
|
||||
|
@ -299,7 +384,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32,
|
||||
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
|
||||
{
|
||||
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
|
||||
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
|
||||
abort();
|
||||
}
|
||||
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
|
||||
|
@ -311,7 +396,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
}
|
||||
if(it.getNative()->u.type == BLE_UUID_TYPE_128){
|
||||
int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
|
||||
if((payloadLen + add) > 31){
|
||||
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
|
||||
m_advData.uuids128_is_complete = 0;
|
||||
continue;
|
||||
}
|
||||
|
@ -320,7 +405,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
|
||||
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
|
||||
{
|
||||
NIMBLE_LOGE(LOG_TAG, "Error, no mem");
|
||||
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
|
||||
abort();
|
||||
}
|
||||
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
|
||||
|
@ -333,54 +418,74 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
}
|
||||
|
||||
// check if there is room for the name, if not put it in scan data
|
||||
if((payloadLen + m_advData.name_len) > 29) {
|
||||
if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) {
|
||||
if(m_scanResp){
|
||||
m_scanData.name = m_advData.name;
|
||||
m_scanData.name_len = m_advData.name_len;
|
||||
m_scanData.name_is_complete = m_advData.name_is_complete;
|
||||
if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) {
|
||||
m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2;
|
||||
m_scanData.name_is_complete = 0;
|
||||
} else {
|
||||
m_scanData.name_is_complete = 1;
|
||||
}
|
||||
m_advData.name = nullptr;
|
||||
m_advData.name_len = 0;
|
||||
m_advData.name_is_complete = 0;
|
||||
} else {
|
||||
if(m_advData.tx_pwr_lvl_is_present) {
|
||||
m_advData.tx_pwr_lvl = 0;
|
||||
m_advData.tx_pwr_lvl_is_present = 0;
|
||||
payloadLen -= (2 + 1);
|
||||
}
|
||||
// if not using scan response just cut the name down
|
||||
// leaving 2 bytes for the data specifier.
|
||||
m_advData.name_len = (29 - payloadLen);
|
||||
if(m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) {
|
||||
m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2);
|
||||
m_advData.name_is_complete = 0;
|
||||
}
|
||||
}
|
||||
m_advData.name_is_complete = 0;
|
||||
}
|
||||
|
||||
if(m_advData.name_len > 0) {
|
||||
payloadLen += (m_advData.name_len + 2);
|
||||
}
|
||||
|
||||
if(m_scanResp) {
|
||||
// name length + type byte + length byte + tx power type + length + data
|
||||
if((m_scanData.name_len + 5) > 31) {
|
||||
// prioritize name data over tx power
|
||||
m_scanData.tx_pwr_lvl_is_present = 0;
|
||||
m_scanData.tx_pwr_lvl = 0;
|
||||
// limit name to 29 to leave room for the data specifiers
|
||||
if(m_scanData.name_len > 29) {
|
||||
m_scanData.name_len = 29;
|
||||
m_scanData.name_is_complete = false;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ble_gap_adv_rsp_set_fields(&m_scanData);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGC(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
abort();
|
||||
switch(rc) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case BLE_HS_EBUSY:
|
||||
NIMBLE_LOGE(LOG_TAG, "Already advertising");
|
||||
break;
|
||||
|
||||
case BLE_HS_EMSGSIZE:
|
||||
NIMBLE_LOGE(LOG_TAG, "Scan data too long");
|
||||
break;
|
||||
|
||||
default:
|
||||
NIMBLE_LOGE(LOG_TAG, "Error setting scan response data; rc=%d, %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
break;
|
||||
}
|
||||
// if not using scan response and there is room,
|
||||
// put the tx power data into the advertisment
|
||||
} else if (payloadLen < 29) {
|
||||
m_advData.tx_pwr_lvl_is_present = 1;
|
||||
m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
|
||||
}
|
||||
|
||||
rc = ble_gap_adv_set_fields(&m_advData);
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGC(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
abort();
|
||||
if(rc == 0) {
|
||||
rc = ble_gap_adv_set_fields(&m_advData);
|
||||
switch(rc) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case BLE_HS_EBUSY:
|
||||
NIMBLE_LOGE(LOG_TAG, "Already advertising");
|
||||
break;
|
||||
|
||||
case BLE_HS_EMSGSIZE:
|
||||
NIMBLE_LOGE(LOG_TAG, "Advertisement data too long");
|
||||
break;
|
||||
|
||||
default:
|
||||
NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data; rc=%d, %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_advData.num_uuids128 > 0) {
|
||||
|
@ -401,24 +506,54 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
m_advData.num_uuids16 = 0;
|
||||
}
|
||||
|
||||
if(rc !=0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_advDataSet = true;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||
rc = ble_gap_adv_start(0, NULL, duration,
|
||||
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
|
||||
&m_advParams,
|
||||
(pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent,
|
||||
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
|
||||
NimBLEAdvertising::handleGapEvent,
|
||||
(pServer != nullptr) ? (void*)pServer : (void*)this);
|
||||
#else
|
||||
rc = ble_gap_adv_start(0, NULL, duration,
|
||||
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
|
||||
&m_advParams, NimBLEAdvertising::handleGapEvent, this);
|
||||
#endif
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGC(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
abort();
|
||||
switch(rc) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case BLE_HS_EINVAL:
|
||||
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
|
||||
break;
|
||||
|
||||
case BLE_HS_EPREEMPTED:
|
||||
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - busy");
|
||||
break;
|
||||
|
||||
case BLE_HS_ETIMEOUT_HCI:
|
||||
case BLE_HS_EOS:
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
|
||||
break;
|
||||
|
||||
default:
|
||||
NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
break;
|
||||
}
|
||||
|
||||
if(rc != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
|
||||
return true;
|
||||
} // start
|
||||
|
||||
|
||||
|
@ -427,9 +562,11 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
|
|||
*/
|
||||
void NimBLEAdvertising::stop() {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> stop");
|
||||
|
||||
int rc = ble_gap_adv_stop();
|
||||
if (rc != 0 && rc != BLE_HS_EALREADY) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -460,8 +597,17 @@ bool NimBLEAdvertising::isAdvertising() {
|
|||
* Host reset seems to clear advertising data,
|
||||
* we need clear the flag so it reloads it.
|
||||
*/
|
||||
void NimBLEAdvertising::onHostReset() {
|
||||
void NimBLEAdvertising::onHostSync() {
|
||||
NIMBLE_LOGD(LOG_TAG, "Host re-synced");
|
||||
|
||||
m_advDataSet = false;
|
||||
// If we were advertising forever, restart it now
|
||||
if(m_duration == 0) {
|
||||
start(m_duration, m_advCompCB);
|
||||
} else {
|
||||
// Otherwise we should tell the app that advertising stopped.
|
||||
advCompleteCB();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -475,6 +621,19 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|||
NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg;
|
||||
|
||||
if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
|
||||
switch(event->adv_complete.reason) {
|
||||
// Don't call the callback if host reset, we want to
|
||||
// preserve the active flag until re-sync to restart advertising.
|
||||
case BLE_HS_ETIMEOUT_HCI:
|
||||
case BLE_HS_EOS:
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
|
||||
NimBLEDevice::onReset(event->adv_complete.reason);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pAdv->advCompleteCB();
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -77,7 +77,7 @@ public:
|
|||
void addServiceUUID(const NimBLEUUID &serviceUUID);
|
||||
void addServiceUUID(const char* serviceUUID);
|
||||
void removeServiceUUID(const NimBLEUUID &serviceUUID);
|
||||
void start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
|
||||
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
|
||||
void stop();
|
||||
void setAppearance(uint16_t appearance);
|
||||
void setAdvertisementType(uint8_t adv_type);
|
||||
|
@ -87,13 +87,15 @@ public:
|
|||
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
|
||||
void setScanResponseData(NimBLEAdvertisementData& advertisementData);
|
||||
void setScanResponse(bool);
|
||||
void setMinPreferred(uint16_t);
|
||||
void setMaxPreferred(uint16_t);
|
||||
void advCompleteCB();
|
||||
bool isAdvertising();
|
||||
|
||||
private:
|
||||
friend class NimBLEDevice;
|
||||
|
||||
void onHostReset();
|
||||
void onHostSync();
|
||||
static int handleGapEvent(struct ble_gap_event *event, void *arg);
|
||||
|
||||
ble_hs_adv_fields m_advData;
|
||||
|
@ -104,8 +106,9 @@ private:
|
|||
bool m_customScanResponseData;
|
||||
bool m_scanResp;
|
||||
bool m_advDataSet;
|
||||
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
|
||||
|
||||
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
|
||||
uint8_t m_slaveItvl[4];
|
||||
uint32_t m_duration;
|
||||
};
|
||||
|
||||
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||
|
|
|
@ -473,9 +473,10 @@ void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
|
|||
return;
|
||||
}
|
||||
|
||||
time_t t = time(nullptr);
|
||||
portENTER_CRITICAL(&m_valMux);
|
||||
m_value = std::string((char*)data, length);
|
||||
m_timestamp = time(nullptr);
|
||||
m_timestamp = t;
|
||||
portEXIT_CRITICAL(&m_valMux);
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< setValue");
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "nimble/nimble_port.h"
|
||||
|
||||
|
||||
static const char* LOG_TAG = "NimBLEClient";
|
||||
static NimBLEClientCallbacks defaultCallbacks;
|
||||
|
||||
|
@ -56,11 +59,10 @@ static NimBLEClientCallbacks defaultCallbacks;
|
|||
NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) {
|
||||
m_pClientCallbacks = &defaultCallbacks;
|
||||
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
m_isConnected = false;
|
||||
m_waitingToConnect = false;
|
||||
m_connectTimeout = 30000;
|
||||
m_deleteCallbacks = false;
|
||||
m_pTaskData = nullptr;
|
||||
m_connEstablished = false;
|
||||
|
||||
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
|
||||
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
|
||||
|
@ -70,6 +72,9 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
|
|||
m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms
|
||||
m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
|
||||
m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
|
||||
|
||||
ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(),
|
||||
NimBLEClient::dcTimerCb, this);
|
||||
} // NimBLEClient
|
||||
|
||||
|
||||
|
@ -89,6 +94,19 @@ NimBLEClient::~NimBLEClient() {
|
|||
} // ~NimBLEClient
|
||||
|
||||
|
||||
/**
|
||||
* @brief If we have asked to disconnect and the event does not
|
||||
* occur within the supervision timeout + added delay, this will
|
||||
* be called to reset the host in the case of a stalled controller.
|
||||
*/
|
||||
void NimBLEClient::dcTimerCb(ble_npl_event *event) {
|
||||
NimBLEClient *pClient = (NimBLEClient*)event->arg;
|
||||
NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host",
|
||||
std::string(pClient->getPeerAddress()).c_str());
|
||||
ble_hs_sched_reset(BLE_HS_ECONTROLLER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Delete all service objects created by this client and clear the vector.
|
||||
*/
|
||||
|
@ -164,12 +182,9 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if(ble_gap_conn_active()) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!NimBLEDevice::getScan()->stop()) {
|
||||
if(isConnected() || m_pTaskData != nullptr) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d",
|
||||
std::string(m_peerAddress).c_str(), getConnId());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -180,54 +195,97 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
|
|||
m_peerAddress = address;
|
||||
}
|
||||
|
||||
ble_addr_t peerAddrt;
|
||||
memcpy(&peerAddrt.val, m_peerAddress.getNative(),6);
|
||||
peerAddrt.type = m_peerAddress.getType();
|
||||
ble_addr_t peerAddr_t;
|
||||
memcpy(&peerAddr_t.val, m_peerAddress.getNative(),6);
|
||||
peerAddr_t.type = m_peerAddress.getType();
|
||||
|
||||
|
||||
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
|
||||
m_pTaskData = &taskData;
|
||||
|
||||
int rc = 0;
|
||||
|
||||
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
|
||||
* timeout (default value of m_connectTimeout).
|
||||
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
|
||||
*/
|
||||
do{
|
||||
rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams,
|
||||
NimBLEClient::handleGapEvent, this);
|
||||
if(rc == BLE_HS_EBUSY) {
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
do {
|
||||
rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
|
||||
m_connectTimeout, &m_pConnParams,
|
||||
NimBLEClient::handleGapEvent, this);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
m_pTaskData = &taskData;
|
||||
break;
|
||||
|
||||
case BLE_HS_EBUSY:
|
||||
// Scan was still running, stop it and try again
|
||||
if (!NimBLEDevice::getScan()->stop()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case BLE_HS_EDONE:
|
||||
// A connection to this device already exists, do not connect twice.
|
||||
NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s",
|
||||
std::string(m_peerAddress).c_str());
|
||||
return false;
|
||||
|
||||
case BLE_HS_EALREADY:
|
||||
// Already attemting to connect to this device, cancel the previous
|
||||
// attempt and report failure here so we don't get 2 connections.
|
||||
NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling",
|
||||
std::string(m_peerAddress).c_str());
|
||||
ble_gap_conn_cancel();
|
||||
return false;
|
||||
|
||||
default:
|
||||
NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s",
|
||||
std::string(m_peerAddress).c_str(),
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
return false;
|
||||
}
|
||||
}while(rc == BLE_HS_EBUSY);
|
||||
|
||||
if (rc != 0 && rc != BLE_HS_EDONE) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; "
|
||||
"addr=%s, rc=%d; %s",
|
||||
std::string(m_peerAddress).c_str(),
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
} while (rc == BLE_HS_EBUSY);
|
||||
|
||||
// Wait for the connect timeout time +1 second for the connection to complete
|
||||
if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) {
|
||||
m_pTaskData = nullptr;
|
||||
m_waitingToConnect = false;
|
||||
// If a connection was made but no response from MTU exchange; disconnect
|
||||
if(isConnected()) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Connect timeout - no response");
|
||||
disconnect();
|
||||
} else {
|
||||
// workaround; if the controller doesn't cancel the connection
|
||||
// at the timeout cancel it here.
|
||||
NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling");
|
||||
ble_gap_conn_cancel();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_waitingToConnect = true;
|
||||
|
||||
// Wait for the connection to complete.
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
if(taskData.rc != 0){
|
||||
} else if(taskData.rc != 0){
|
||||
NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s",
|
||||
taskData.rc,
|
||||
NimBLEUtils::returnCodeToString(taskData.rc));
|
||||
// If the failure was not a result of a disconnection
|
||||
// make sure we disconnect now to avoid dangling connections
|
||||
if(isConnected()) {
|
||||
ble_gap_terminate(m_conn_id, BLE_ERR_REM_USER_CONN_TERM);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
NIMBLE_LOGI(LOG_TAG, "Connection established");
|
||||
}
|
||||
|
||||
if(deleteAttibutes) {
|
||||
deleteServices();
|
||||
}
|
||||
|
||||
m_connEstablished = true;
|
||||
m_pClientCallbacks->onConnect(this);
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< connect()");
|
||||
return true;
|
||||
// Check if still connected before returning
|
||||
return isConnected();
|
||||
} // connect
|
||||
|
||||
|
||||
|
@ -267,13 +325,31 @@ bool NimBLEClient::secureConnection() {
|
|||
*/
|
||||
int NimBLEClient::disconnect(uint8_t reason) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
|
||||
|
||||
int rc = 0;
|
||||
if(m_isConnected){
|
||||
if(isConnected()){
|
||||
rc = ble_gap_terminate(m_conn_id, reason);
|
||||
if(rc != 0){
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc,
|
||||
NimBLEUtils::returnCodeToString(rc));
|
||||
if (rc == 0) {
|
||||
ble_addr_t peerAddr_t;
|
||||
memcpy(&peerAddr_t.val, m_peerAddress.getNative(),6);
|
||||
peerAddr_t.type = m_peerAddress.getType();
|
||||
|
||||
// Set the disconnect timeout to the supervison timeout time + 1 second
|
||||
// In case the event triggers shortly after the supervision timeout.
|
||||
// We don't want to prematurely reset the host.
|
||||
ble_gap_conn_desc desc;
|
||||
if(ble_gap_conn_find_by_addr(&peerAddr_t, &desc) == 0){
|
||||
ble_npl_time_t ticks;
|
||||
ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks);
|
||||
ble_npl_callout_reset(&m_dcTimer, ticks);
|
||||
NIMBLE_LOGD(LOG_TAG, "DC TIMEOUT = %dms", (desc.supervision_timeout + 100) * 10);
|
||||
}
|
||||
} else if (rc != BLE_HS_EALREADY) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
}
|
||||
} else {
|
||||
NIMBLE_LOGD(LOG_TAG, "Not connected to any peers");
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
|
||||
|
@ -454,6 +530,16 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
|
|||
if(m_servicesVector.size() > prev_size) {
|
||||
return m_servicesVector.back();
|
||||
}
|
||||
|
||||
// If the request was successful but 16/32 bit service not found
|
||||
// try again with the 128 bit uuid.
|
||||
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
|
||||
uuid.bitSize() == BLE_UUID_TYPE_32)
|
||||
{
|
||||
NimBLEUUID uuid128(uuid);
|
||||
uuid128.to128();
|
||||
return getService(uuid128);
|
||||
}
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< getService: not found");
|
||||
|
@ -510,7 +596,7 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
|
|||
|
||||
NIMBLE_LOGD(LOG_TAG, ">> retrieveServices");
|
||||
|
||||
if(!m_isConnected){
|
||||
if(!isConnected()){
|
||||
NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting");
|
||||
return false;
|
||||
}
|
||||
|
@ -618,10 +704,11 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
|
|||
* @param [in] serviceUUID The service that owns the characteristic.
|
||||
* @param [in] characteristicUUID The characteristic whose value we wish to write.
|
||||
* @param [in] value The value to write to the characteristic.
|
||||
* @param [in] response If true, uses write with response operation.
|
||||
* @returns true if successful otherwise false
|
||||
*/
|
||||
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
|
||||
const std::string &value)
|
||||
const std::string &value, bool response)
|
||||
{
|
||||
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
|
||||
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
|
||||
|
@ -632,7 +719,7 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
|
|||
if(pService != nullptr) {
|
||||
NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID);
|
||||
if(pChar != nullptr) {
|
||||
ret = pChar->writeValue(value);
|
||||
ret = pChar->writeValue(value, response);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -641,6 +728,31 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
|
|||
} // setValue
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the remote characteristic with the specified handle.
|
||||
* @param [in] handle The handle of the desired characteristic.
|
||||
* @returns The matching remote characteristic, nullptr otherwise.
|
||||
*/
|
||||
NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handle)
|
||||
{
|
||||
NimBLERemoteService *pService = nullptr;
|
||||
for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) {
|
||||
if ((*it)->getStartHandle() <= handle && handle <= (*it)->getEndHandle()) {
|
||||
pService = *it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pService != nullptr) {
|
||||
for (auto it = pService->begin(); it != pService->end(); ++it) {
|
||||
if ((*it)->getHandle() == handle) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the current mtu of this connection.
|
||||
|
@ -656,7 +768,8 @@ uint16_t NimBLEClient::getMTU() {
|
|||
* @param [in] event The event structure sent by the NimBLE stack.
|
||||
* @param [in] arg A pointer to the client instance that registered for this callback.
|
||||
*/
|
||||
/*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
||||
/*STATIC*/
|
||||
int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
||||
NimBLEClient* client = (NimBLEClient*)arg;
|
||||
int rc;
|
||||
|
||||
|
@ -665,61 +778,66 @@ uint16_t NimBLEClient::getMTU() {
|
|||
switch(event->type) {
|
||||
|
||||
case BLE_GAP_EVENT_DISCONNECT: {
|
||||
if(!client->m_isConnected)
|
||||
return 0;
|
||||
|
||||
if(client->m_conn_id != event->disconnect.conn.conn_handle)
|
||||
return 0;
|
||||
|
||||
client->m_isConnected = false;
|
||||
client->m_waitingToConnect=false;
|
||||
// Remove the device from ignore list so we will scan it again
|
||||
NimBLEDevice::removeIgnored(client->m_peerAddress);
|
||||
|
||||
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason,
|
||||
NimBLEUtils::returnCodeToString(event->disconnect.reason));
|
||||
|
||||
rc = event->disconnect.reason;
|
||||
// If Host reset tell the device now before returning to prevent
|
||||
// any errors caused by calling host functions before resyncing.
|
||||
switch(event->disconnect.reason) {
|
||||
case BLE_HS_ETIMEOUT_HCI:
|
||||
case BLE_HS_EOS:
|
||||
switch(rc) {
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ETIMEOUT_HCI:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
|
||||
NimBLEDevice::onReset(event->disconnect.reason);
|
||||
case BLE_HS_EOS:
|
||||
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
|
||||
NimBLEDevice::onReset(rc);
|
||||
break;
|
||||
default:
|
||||
// Check that the event is for this client.
|
||||
if(client->m_conn_id != event->disconnect.conn.conn_handle) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
|
||||
// Stop the disconnect timer since we are now disconnected.
|
||||
ble_npl_callout_stop(&client->m_dcTimer);
|
||||
|
||||
// Remove the device from ignore list so we will scan it again
|
||||
NimBLEDevice::removeIgnored(client->m_peerAddress);
|
||||
|
||||
// If we received a connected event but did not get established (no PDU)
|
||||
// then a disconnect event will be sent but we should not send it to the
|
||||
// app for processing. Instead we will ensure the task is released
|
||||
// and report the error.
|
||||
if(!client->m_connEstablished)
|
||||
break;
|
||||
|
||||
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
|
||||
client->m_connEstablished = false;
|
||||
client->m_pClientCallbacks->onDisconnect(client);
|
||||
rc = event->disconnect.reason;
|
||||
break;
|
||||
} // BLE_GAP_EVENT_DISCONNECT
|
||||
|
||||
case BLE_GAP_EVENT_CONNECT: {
|
||||
|
||||
if(!client->m_waitingToConnect)
|
||||
// If we aren't waiting for this connection response
|
||||
// we should drop the connection immediately.
|
||||
if(client->isConnected() || client->m_pTaskData == nullptr) {
|
||||
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//if(client->m_conn_id != BLE_HS_CONN_HANDLE_NONE)
|
||||
// return 0;
|
||||
|
||||
client->m_waitingToConnect=false;
|
||||
|
||||
if (event->connect.status == 0) {
|
||||
client->m_isConnected = true;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "Connection established");
|
||||
rc = event->connect.status;
|
||||
if (rc == 0) {
|
||||
NIMBLE_LOGI(LOG_TAG, "Connected event");
|
||||
|
||||
client->m_conn_id = event->connect.conn_handle;
|
||||
|
||||
rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL);
|
||||
if(rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc,
|
||||
NimBLEUtils::returnCodeToString(rc));
|
||||
NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -727,14 +845,10 @@ uint16_t NimBLEClient::getMTU() {
|
|||
// scanning since we are already connected to it
|
||||
NimBLEDevice::addIgnored(client->m_peerAddress);
|
||||
} else {
|
||||
NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s",
|
||||
event->connect.status,
|
||||
NimBLEUtils::returnCodeToString(event->connect.status));
|
||||
|
||||
client->m_isConnected = false;
|
||||
rc = event->connect.status;
|
||||
client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} // BLE_GAP_EVENT_CONNECT
|
||||
|
||||
|
@ -742,7 +856,14 @@ uint16_t NimBLEClient::getMTU() {
|
|||
if(client->m_conn_id != event->notify_rx.conn_handle)
|
||||
return 0;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle);
|
||||
// If a notification comes before this flag is set we might
|
||||
// access a vector while it is being cleared in connect()
|
||||
if(!client->m_connEstablished) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",
|
||||
event->notify_rx.attr_handle);
|
||||
|
||||
for(auto &it: client->m_servicesVector) {
|
||||
// Dont waste cycles searching services without this handle in its range
|
||||
|
@ -752,8 +873,8 @@ uint16_t NimBLEClient::getMTU() {
|
|||
|
||||
auto cVector = &it->m_characteristicVector;
|
||||
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
|
||||
it->getUUID().toString().c_str(),
|
||||
event->notify_rx.attr_handle);
|
||||
it->getUUID().toString().c_str(),
|
||||
event->notify_rx.attr_handle);
|
||||
|
||||
auto characteristic = cVector->cbegin();
|
||||
for(; characteristic != cVector->cend(); ++characteristic) {
|
||||
|
@ -762,16 +883,19 @@ uint16_t NimBLEClient::getMTU() {
|
|||
}
|
||||
|
||||
if(characteristic != cVector->cend()) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", (*characteristic)->toString().c_str());
|
||||
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s",
|
||||
(*characteristic)->toString().c_str());
|
||||
|
||||
time_t t = time(nullptr);
|
||||
portENTER_CRITICAL(&(*characteristic)->m_valMux);
|
||||
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data, event->notify_rx.om->om_len);
|
||||
(*characteristic)->m_timestamp = time(nullptr);
|
||||
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data,
|
||||
event->notify_rx.om->om_len);
|
||||
(*characteristic)->m_timestamp = t;
|
||||
portEXIT_CRITICAL(&(*characteristic)->m_valMux);
|
||||
|
||||
if ((*characteristic)->m_notifyCallback != nullptr) {
|
||||
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
|
||||
(*characteristic)->toString().c_str());
|
||||
(*characteristic)->toString().c_str());
|
||||
(*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data,
|
||||
event->notify_rx.om->om_len,
|
||||
!event->notify_rx.indication);
|
||||
|
@ -790,10 +914,10 @@ uint16_t NimBLEClient::getMTU() {
|
|||
}
|
||||
NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters");
|
||||
NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d",
|
||||
event->conn_update_req.peer_params->itvl_min,
|
||||
event->conn_update_req.peer_params->itvl_max,
|
||||
event->conn_update_req.peer_params->latency,
|
||||
event->conn_update_req.peer_params->supervision_timeout);
|
||||
event->conn_update_req.peer_params->itvl_min,
|
||||
event->conn_update_req.peer_params->itvl_max,
|
||||
event->conn_update_req.peer_params->latency,
|
||||
event->conn_update_req.peer_params->supervision_timeout);
|
||||
|
||||
rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client,
|
||||
event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS;
|
||||
|
@ -827,7 +951,9 @@ uint16_t NimBLEClient::getMTU() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(event->enc_change.status == 0 || event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
|
||||
if(event->enc_change.status == 0 ||
|
||||
event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING))
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
|
@ -922,7 +1048,9 @@ uint16_t NimBLEClient::getMTU() {
|
|||
|
||||
if(client->m_pTaskData != nullptr) {
|
||||
client->m_pTaskData->rc = rc;
|
||||
xTaskNotifyGive(client->m_pTaskData->task);
|
||||
if(client->m_pTaskData->task) {
|
||||
xTaskNotifyGive(client->m_pTaskData->task);
|
||||
}
|
||||
client->m_pTaskData = nullptr;
|
||||
}
|
||||
|
||||
|
@ -935,7 +1063,7 @@ uint16_t NimBLEClient::getMTU() {
|
|||
* @return True if we are connected and false if we are not connected.
|
||||
*/
|
||||
bool NimBLEClient::isConnected() {
|
||||
return m_isConnected;
|
||||
return m_conn_id != BLE_HS_CONN_HANDLE_NONE;
|
||||
} // isConnected
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <string>
|
||||
|
||||
class NimBLERemoteService;
|
||||
class NimBLERemoteCharacteristic;
|
||||
class NimBLEClientCallbacks;
|
||||
class NimBLEAdvertisedDevice;
|
||||
|
||||
|
@ -54,7 +55,8 @@ public:
|
|||
size_t deleteService(const NimBLEUUID &uuid);
|
||||
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
|
||||
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
|
||||
const std::string &value);
|
||||
const std::string &value, bool response = false);
|
||||
NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle);
|
||||
bool isConnected();
|
||||
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
|
||||
bool deleteCallbacks = true);
|
||||
|
@ -82,16 +84,17 @@ private:
|
|||
const struct ble_gatt_error *error,
|
||||
const struct ble_gatt_svc *service,
|
||||
void *arg);
|
||||
static void dcTimerCb(ble_npl_event *event);
|
||||
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
|
||||
|
||||
NimBLEAddress m_peerAddress;
|
||||
uint16_t m_conn_id;
|
||||
bool m_isConnected;
|
||||
bool m_waitingToConnect;
|
||||
bool m_connEstablished;
|
||||
bool m_deleteCallbacks;
|
||||
int32_t m_connectTimeout;
|
||||
NimBLEClientCallbacks* m_pClientCallbacks;
|
||||
ble_task_data_t *m_pTaskData;
|
||||
ble_task_data_t* m_pTaskData;
|
||||
ble_npl_callout m_dcTimer;
|
||||
|
||||
std::vector<NimBLERemoteService*> m_servicesVector;
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_hs_pvcy.h"
|
||||
#include "host/util/util.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
|
@ -60,6 +61,7 @@ std::list <NimBLEClient*> NimBLEDevice::m_cList;
|
|||
#endif
|
||||
std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
|
||||
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
|
||||
uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -144,8 +146,8 @@ void NimBLEDevice::stopAdvertising() {
|
|||
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
||||
/* STATIC */ NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) {
|
||||
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
|
||||
NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)",
|
||||
NIMBLE_MAX_CONNECTIONS);
|
||||
NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d",
|
||||
m_cList.size(), NIMBLE_MAX_CONNECTIONS);
|
||||
}
|
||||
|
||||
NimBLEClient* pClient = new NimBLEClient(peerAddress);
|
||||
|
@ -165,26 +167,31 @@ void NimBLEDevice::stopAdvertising() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Set the connection established flag to false to stop notifications
|
||||
// from accessing the attribute vectors while they are being deleted.
|
||||
pClient->m_connEstablished = false;
|
||||
int rc =0;
|
||||
|
||||
if(pClient->m_isConnected) {
|
||||
if(pClient->isConnected()) {
|
||||
rc = pClient->disconnect();
|
||||
if (rc != 0 && rc != BLE_HS_EALREADY && rc != BLE_HS_ENOTCONN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(pClient->m_isConnected) {
|
||||
vTaskDelay(10);
|
||||
while(pClient->isConnected()) {
|
||||
taskYIELD();
|
||||
}
|
||||
}
|
||||
// Since we set the flag to false the app will not get a callback
|
||||
// in the disconnect event so we call it here for good measure.
|
||||
pClient->m_pClientCallbacks->onDisconnect(pClient);
|
||||
|
||||
if(pClient->m_waitingToConnect) {
|
||||
} else if(pClient->m_pTaskData != nullptr) {
|
||||
rc = ble_gap_conn_cancel();
|
||||
if (rc != 0 && rc != BLE_HS_EALREADY) {
|
||||
return false;
|
||||
}
|
||||
while(pClient->m_waitingToConnect) {
|
||||
vTaskDelay(10);
|
||||
while(pClient->m_pTaskData != nullptr) {
|
||||
taskYIELD();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,30 +412,16 @@ void NimBLEDevice::stopAdvertising() {
|
|||
|
||||
m_synced = false;
|
||||
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
if(m_pScan != nullptr) {
|
||||
m_pScan->onHostReset();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Not needed
|
||||
if(m_pServer != nullptr) {
|
||||
m_pServer->onHostReset();
|
||||
}
|
||||
|
||||
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
|
||||
(*it)->onHostReset();
|
||||
}
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
||||
if(m_bleAdvertising != nullptr) {
|
||||
m_bleAdvertising->onHostReset();
|
||||
}
|
||||
#endif
|
||||
|
||||
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
|
||||
NimBLEUtils::returnCodeToString(reason));
|
||||
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
if(initialized) {
|
||||
if(m_pScan != nullptr) {
|
||||
m_pScan->onHostReset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // onReset
|
||||
|
||||
|
||||
|
@ -448,20 +441,22 @@ void NimBLEDevice::stopAdvertising() {
|
|||
int rc = ble_hs_util_ensure_addr(0);
|
||||
assert(rc == 0);
|
||||
|
||||
// Yield for houskeeping before returning to operations.
|
||||
// Occasionally triggers exception without.
|
||||
taskYIELD();
|
||||
|
||||
m_synced = true;
|
||||
|
||||
if(initialized) {
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
||||
if(m_pScan != nullptr) {
|
||||
// Restart scanning with the last values sent, allow to clear results.
|
||||
m_pScan->start(m_pScan->m_duration, m_pScan->m_scanCompleteCB);
|
||||
m_pScan->onHostSync();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
||||
if(m_bleAdvertising != nullptr) {
|
||||
// Restart advertisng, parameters should already be set.
|
||||
m_bleAdvertising->start();
|
||||
m_bleAdvertising->onHostSync();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -705,6 +700,35 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
|
|||
} // setSecurityCallbacks
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the own address type.
|
||||
* @param [in] own_addr_type Own Bluetooth Device address type.\n
|
||||
* The available bits are defined as:
|
||||
* * 0x00: BLE_OWN_ADDR_PUBLIC
|
||||
* * 0x01: BLE_OWN_ADDR_RANDOM
|
||||
* * 0x02: BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT
|
||||
* * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
|
||||
* @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address.
|
||||
*/
|
||||
void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
|
||||
m_own_addr_type = own_addr_type;
|
||||
switch (own_addr_type) {
|
||||
case BLE_OWN_ADDR_PUBLIC:
|
||||
ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
|
||||
break;
|
||||
case BLE_OWN_ADDR_RANDOM:
|
||||
setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
|
||||
ble_hs_pvcy_rpa_config(useNRPA ? NIMBLE_HOST_ENABLE_NRPA : NIMBLE_HOST_ENABLE_RPA);
|
||||
break;
|
||||
case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
|
||||
case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
|
||||
setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
|
||||
ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
|
||||
break;
|
||||
}
|
||||
} // setOwnAddrType
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start the connection securing and authorization for this connection.
|
||||
* @param conn_id The connection id of the peer device.
|
||||
|
|
|
@ -116,6 +116,7 @@ public:
|
|||
static void setSecurityPasskey(uint32_t pin);
|
||||
static uint32_t getSecurityPasskey();
|
||||
static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks);
|
||||
static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false);
|
||||
static int startSecurity(uint16_t conn_id);
|
||||
static int setMTU(uint16_t mtu);
|
||||
static uint16_t getMTU();
|
||||
|
@ -182,6 +183,7 @@ private:
|
|||
static uint32_t m_passkey;
|
||||
static ble_gap_event_listener m_listener;
|
||||
static gap_event_handler m_customGapHandler;
|
||||
static uint8_t m_own_addr_type;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* NimBLEHIDDevice.cpp
|
||||
*
|
||||
* Created: on Oct 06 2020
|
||||
* Author wakwak-koba
|
||||
*
|
||||
* Originally:
|
||||
*
|
||||
* BLEHIDDevice.cpp
|
||||
*
|
||||
* Created on: Jan 03, 2018
|
||||
* Author: chegewara
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||
|
||||
#include "NimBLEHIDDevice.h"
|
||||
#include "NimBLE2904.h"
|
||||
|
||||
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), 40);
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
|
||||
batteryLevelDescriptor->setNamespace(1);
|
||||
batteryLevelDescriptor->setUnit(0x27ad);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
NimBLEHIDDevice::~NimBLEHIDDevice() {
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
void NimBLEHIDDevice::reportMap(uint8_t* map, uint16_t size) {
|
||||
m_reportMapCharacteristic->setValue(map, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service
|
||||
*/
|
||||
void NimBLEHIDDevice::startServices() {
|
||||
m_deviceInfoService->start();
|
||||
m_hidService->start();
|
||||
m_batteryService->start();
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Create manufacturer characteristic (this characteristic is optional)
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
|
||||
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ);
|
||||
return m_manufacturerCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Set manufacturer name
|
||||
* @param [in] name manufacturer name
|
||||
*/
|
||||
void NimBLEHIDDevice::manufacturer(std::string name) {
|
||||
m_manufacturerCharacteristic->setValue(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
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 };
|
||||
m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
|
||||
uint8_t info[] = { 0x11, 0x1, country, flags };
|
||||
m_hidInfoCharacteristic->setValue(info, sizeof(info));
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Create input report characteristic that need to be saved as new characteristic object so can be further used
|
||||
* @param [in] reportID input report ID, the same as in report map for input object related to created characteristic
|
||||
* @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);
|
||||
NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x01 };
|
||||
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
|
||||
return inputReportCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Create output report characteristic that need to be saved as new characteristic object so can be further used
|
||||
* @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic
|
||||
* @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);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x02 };
|
||||
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
|
||||
return outputReportCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used
|
||||
* @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic
|
||||
* @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);
|
||||
|
||||
uint8_t desc1_val[] = { reportID, 0x03 };
|
||||
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
|
||||
|
||||
return featureReportCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::bootInput() {
|
||||
return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::bootOutput() {
|
||||
return m_hidService->createCharacteristic((uint16_t) 0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::hidControl() {
|
||||
return m_hidControlCharacteristic;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
NimBLECharacteristic* NimBLEHIDDevice::protocolMode() {
|
||||
return m_protocolModeCharacteristic;
|
||||
}
|
||||
|
||||
void NimBLEHIDDevice::setBatteryLevel(uint8_t level) {
|
||||
m_batteryLevelCharacteristic->setValue(&level, 1);
|
||||
}
|
||||
/*
|
||||
* @brief Returns battery level characteristic
|
||||
* @ return battery level characteristic
|
||||
*//*
|
||||
BLECharacteristic* BLEHIDDevice::batteryLevel() {
|
||||
return m_batteryLevelCharacteristic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BLECharacteristic* BLEHIDDevice::reportMap() {
|
||||
return m_reportMapCharacteristic;
|
||||
}
|
||||
|
||||
BLECharacteristic* BLEHIDDevice::pnp() {
|
||||
return m_pnpCharacteristic;
|
||||
}
|
||||
|
||||
|
||||
BLECharacteristic* BLEHIDDevice::hidInfo() {
|
||||
return m_hidInfoCharacteristic;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
NimBLEService* NimBLEHIDDevice::deviceInfo() {
|
||||
return m_deviceInfoService;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
NimBLEService* NimBLEHIDDevice::hidService() {
|
||||
return m_hidService;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief
|
||||
*/
|
||||
NimBLEService* NimBLEHIDDevice::batteryService() {
|
||||
return m_batteryService;
|
||||
}
|
||||
|
||||
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
||||
#endif // #if defined(CONFIG_BT_ENABLED)
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* NimBLEHIDDevice.h
|
||||
*
|
||||
* Created: on Oct 06 2020
|
||||
* Author wakwak-koba
|
||||
*
|
||||
* Originally:
|
||||
*
|
||||
* BLEHIDDevice.h
|
||||
*
|
||||
* Created on: Jan 03, 2018
|
||||
* Author: chegewara
|
||||
*/
|
||||
|
||||
#ifndef _BLEHIDDEVICE_H_
|
||||
#define _BLEHIDDEVICE_H_
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
|
||||
#include "nimconfig.h"
|
||||
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
||||
|
||||
#include "NimBLECharacteristic.h"
|
||||
#include "NimBLEService.h"
|
||||
#include "NimBLEDescriptor.h"
|
||||
#include "HIDTypes.h"
|
||||
|
||||
#define GENERIC_HID 0x03C0
|
||||
#define HID_KEYBOARD 0x03C1
|
||||
#define HID_MOUSE 0x03C2
|
||||
#define HID_JOYSTICK 0x03C3
|
||||
#define HID_GAMEPAD 0x03C4
|
||||
#define HID_TABLET 0x03C5
|
||||
#define HID_CARD_READER 0x03C6
|
||||
#define HID_DIGITAL_PEN 0x03C7
|
||||
#define HID_BARCODE 0x03C8
|
||||
|
||||
class NimBLEHIDDevice {
|
||||
public:
|
||||
NimBLEHIDDevice(NimBLEServer*);
|
||||
virtual ~NimBLEHIDDevice();
|
||||
|
||||
void reportMap(uint8_t* map, uint16_t);
|
||||
void startServices();
|
||||
|
||||
NimBLEService* deviceInfo();
|
||||
NimBLEService* hidService();
|
||||
NimBLEService* batteryService();
|
||||
|
||||
NimBLECharacteristic* manufacturer();
|
||||
void manufacturer(std::string name);
|
||||
//NimBLECharacteristic* pnp();
|
||||
void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
|
||||
//NimBLECharacteristic* hidInfo();
|
||||
void hidInfo(uint8_t country, uint8_t flags);
|
||||
//NimBLECharacteristic* batteryLevel();
|
||||
void setBatteryLevel(uint8_t level);
|
||||
|
||||
|
||||
//NimBLECharacteristic* reportMap();
|
||||
NimBLECharacteristic* hidControl();
|
||||
NimBLECharacteristic* inputReport(uint8_t reportID);
|
||||
NimBLECharacteristic* outputReport(uint8_t reportID);
|
||||
NimBLECharacteristic* featureReport(uint8_t reportID);
|
||||
NimBLECharacteristic* protocolMode();
|
||||
NimBLECharacteristic* bootInput();
|
||||
NimBLECharacteristic* bootOutput();
|
||||
|
||||
private:
|
||||
NimBLEService* m_deviceInfoService; //0x180a
|
||||
NimBLEService* m_hidService; //0x1812
|
||||
NimBLEService* m_batteryService = 0; //0x180f
|
||||
|
||||
NimBLECharacteristic* m_manufacturerCharacteristic; //0x2a29
|
||||
NimBLECharacteristic* m_pnpCharacteristic; //0x2a50
|
||||
NimBLECharacteristic* m_hidInfoCharacteristic; //0x2a4a
|
||||
NimBLECharacteristic* m_reportMapCharacteristic; //0x2a4b
|
||||
NimBLECharacteristic* m_hidControlCharacteristic; //0x2a4c
|
||||
NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
|
||||
NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
|
||||
};
|
||||
#endif // CONFIG_BT_NIMBLE_ROLE_BROADCASTER
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* _BLEHIDDEVICE_H_ */
|
|
@ -38,7 +38,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
|||
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
|
||||
const struct ble_gatt_chr *chr)
|
||||
{
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()");
|
||||
switch (chr->uuid.u.type) {
|
||||
case BLE_UUID_TYPE_16:
|
||||
m_uuid = NimBLEUUID(chr->uuid.u16.value);
|
||||
|
@ -50,7 +50,6 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
|||
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&chr->uuid.u128));
|
||||
break;
|
||||
default:
|
||||
m_uuid = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -61,6 +60,8 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
|
|||
m_notifyCallback = nullptr;
|
||||
m_timestamp = 0;
|
||||
m_valMux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
|
||||
} // NimBLERemoteCharacteristic
|
||||
|
||||
|
||||
|
@ -208,15 +209,21 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
|
|||
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
|
||||
|
||||
uint16_t endHandle = getRemoteService()->getEndHandle(this);
|
||||
if(m_handle >= endHandle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
|
||||
desc_filter_t filter = {uuid_filter, &taskData};
|
||||
|
||||
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
|
||||
m_handle,
|
||||
getRemoteService()->getEndHandle(),
|
||||
endHandle,
|
||||
NimBLERemoteCharacteristic::descriptorDiscCB,
|
||||
&filter);
|
||||
|
||||
if (rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
||||
return false;
|
||||
|
@ -225,12 +232,13 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
|
|||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
if(taskData.rc != 0) {
|
||||
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: startHandle:%d endHandle:%d taskData.rc=%d %s", m_handle, endHandle, taskData.rc, NimBLEUtils::returnCodeToString(0x0100+taskData.rc));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
|
||||
} // getDescriptors
|
||||
} // retrieveDescriptors
|
||||
|
||||
|
||||
/**
|
||||
|
@ -243,7 +251,7 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
|
|||
|
||||
for(auto &it: m_descriptorVector) {
|
||||
if(it->getUUID() == uuid) {
|
||||
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found");
|
||||
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +261,18 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
|
|||
if(m_descriptorVector.size() > prev_size) {
|
||||
return m_descriptorVector.back();
|
||||
}
|
||||
|
||||
// If the request was successful but 16/32 bit descriptor not found
|
||||
// try again with the 128 bit uuid.
|
||||
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
|
||||
uuid.bitSize() == BLE_UUID_TYPE_32)
|
||||
{
|
||||
NimBLEUUID uuid128(uuid);
|
||||
uuid128.to128();
|
||||
return getDescriptor(uuid128);
|
||||
}
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
|
||||
return nullptr;
|
||||
} // getDescriptor
|
||||
|
@ -447,9 +466,10 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
|
|||
}
|
||||
} while(rc != 0 && retryCount--);
|
||||
|
||||
time_t t = time(nullptr);
|
||||
portENTER_CRITICAL(&m_valMux);
|
||||
m_value = value;
|
||||
m_timestamp = time(nullptr);
|
||||
m_timestamp = t;
|
||||
if(timestamp != nullptr) {
|
||||
*timestamp = m_timestamp;
|
||||
}
|
||||
|
@ -506,19 +526,19 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
|
|||
* @param [in] notifyCallback A callback to be invoked for a notification.
|
||||
* @param [in] response If write response required set this to true.
|
||||
* If NULL is provided then no callback is performed.
|
||||
* @return true if successful.
|
||||
* @return false if writing to the descriptor failed.
|
||||
*/
|
||||
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
|
||||
|
||||
m_notifyCallback = notifyCallback;
|
||||
|
||||
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
|
||||
if(desc == nullptr) {
|
||||
NIMBLE_LOGE(LOG_TAG, "<< setNotify(): Could not get descriptor");
|
||||
return false;
|
||||
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_notifyCallback = notifyCallback;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
|
||||
|
||||
return desc->writeValue((uint8_t *)&val, 2, response);
|
||||
|
@ -531,7 +551,7 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
|
|||
* @param [in] notifyCallback A callback to be invoked for a notification.
|
||||
* @param [in] response If true, require a write response from the descriptor write operation.
|
||||
* If NULL is provided then no callback is performed.
|
||||
* @return true if successful.
|
||||
* @return false if writing to the descriptor failed.
|
||||
*/
|
||||
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
|
||||
if(notifications) {
|
||||
|
@ -545,7 +565,7 @@ bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback n
|
|||
/**
|
||||
* @brief Unsubscribe for notifications or indications.
|
||||
* @param [in] response bool if true, require a write response from the descriptor write operation.
|
||||
* @return true if successful.
|
||||
* @return false if writing to the descriptor failed.
|
||||
*/
|
||||
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
|
||||
return setNotify(0x00, nullptr, response);
|
||||
|
|
|
@ -31,6 +31,7 @@ static const char* LOG_TAG = "NimBLERemoteDescriptor";
|
|||
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
|
||||
const struct ble_gatt_dsc *dsc)
|
||||
{
|
||||
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()");
|
||||
switch (dsc->uuid.u.type) {
|
||||
case BLE_UUID_TYPE_16:
|
||||
m_uuid = NimBLEUUID(dsc->uuid.u16.value);
|
||||
|
@ -42,12 +43,13 @@ NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemo
|
|||
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&dsc->uuid.u128));
|
||||
break;
|
||||
default:
|
||||
m_uuid = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
m_handle = dsc->handle;
|
||||
m_pRemoteCharacteristic = pRemoteCharacteristic;
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -44,12 +44,11 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
|
|||
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&service->uuid.u128));
|
||||
break;
|
||||
default:
|
||||
m_uuid = nullptr;
|
||||
break;
|
||||
}
|
||||
m_startHandle = service->start_handle;
|
||||
m_endHandle = service->end_handle;
|
||||
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService()");
|
||||
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str());
|
||||
}
|
||||
|
||||
|
||||
|
@ -95,8 +94,11 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u
|
|||
* @return A pointer to the characteristic object, or nullptr if not found.
|
||||
*/
|
||||
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str());
|
||||
|
||||
for(auto &it: m_characteristicVector) {
|
||||
if(it->getUUID() == uuid) {
|
||||
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
@ -106,8 +108,19 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
|
|||
if(m_characteristicVector.size() > prev_size) {
|
||||
return m_characteristicVector.back();
|
||||
}
|
||||
|
||||
// If the request was successful but 16/32 bit characteristic not found
|
||||
// try again with the 128 bit uuid.
|
||||
if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
|
||||
uuid.bitSize() == BLE_UUID_TYPE_32)
|
||||
{
|
||||
NimBLEUUID uuid128(uuid);
|
||||
uuid128.to128();
|
||||
return getCharacteristic(uuid128);
|
||||
}
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
|
||||
return nullptr;
|
||||
} // getCharacteristic
|
||||
|
||||
|
@ -236,6 +249,23 @@ uint16_t NimBLERemoteService::getEndHandle() {
|
|||
return m_endHandle;
|
||||
} // getEndHandle
|
||||
|
||||
/**
|
||||
* @brief Get the end handle of specified NimBLERemoteCharacteristic.
|
||||
*/
|
||||
|
||||
uint16_t NimBLERemoteService::getEndHandle(NimBLERemoteCharacteristic *pCharacteristic) {
|
||||
uint16_t endHandle = m_endHandle;
|
||||
|
||||
for(auto &it: m_characteristicVector) {
|
||||
uint16_t defHandle = it->getDefHandle() - 1;
|
||||
if(defHandle > pCharacteristic->getDefHandle() && endHandle > defHandle) {
|
||||
endHandle = defHandle;
|
||||
}
|
||||
}
|
||||
|
||||
return endHandle;
|
||||
} // getEndHandle
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the service start handle.
|
||||
|
|
|
@ -70,6 +70,7 @@ private:
|
|||
|
||||
uint16_t getStartHandle();
|
||||
uint16_t getEndHandle();
|
||||
uint16_t getEndHandle(NimBLERemoteCharacteristic *pCharacteristic);
|
||||
void releaseSemaphores();
|
||||
|
||||
// Properties
|
||||
|
|
|
@ -30,7 +30,6 @@ static const char* LOG_TAG = "NimBLEScan";
|
|||
* @brief Scan constuctor.
|
||||
*/
|
||||
NimBLEScan::NimBLEScan() {
|
||||
m_own_addr_type = 0;
|
||||
m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL;
|
||||
m_scan_params.passive = 1; // If set, don’t send scan requests to advertisers (i.e., don’t request additional advertising data).
|
||||
m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec)
|
||||
|
@ -38,9 +37,10 @@ NimBLEScan::NimBLEScan() {
|
|||
m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode.
|
||||
m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device.
|
||||
m_pAdvertisedDeviceCallbacks = nullptr;
|
||||
m_stopped = true;
|
||||
m_ignoreResults = false;
|
||||
m_wantDuplicates = false;
|
||||
m_pTaskData = nullptr;
|
||||
m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset
|
||||
}
|
||||
|
||||
|
||||
|
@ -63,8 +63,8 @@ NimBLEScan::~NimBLEScan() {
|
|||
switch(event->type) {
|
||||
|
||||
case BLE_GAP_EVENT_DISC: {
|
||||
if(pScan->m_stopped) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Scan stop called, ignoring results.");
|
||||
if(pScan->m_ignoreResults) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Scan op in progress - ignoring results");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,6 @@ NimBLEScan::~NimBLEScan() {
|
|||
pScan->m_scanCompleteCB(pScan->m_scanResults);
|
||||
}
|
||||
|
||||
pScan->m_stopped = true;
|
||||
if(pScan->m_pTaskData != nullptr) {
|
||||
pScan->m_pTaskData->rc = event->disc_complete.reason;
|
||||
xTaskNotifyGive(pScan->m_pTaskData->task);
|
||||
|
@ -238,7 +237,7 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) {
|
|||
* @return true if scanning or scan starting.
|
||||
*/
|
||||
bool NimBLEScan::isScanning() {
|
||||
return !m_stopped;
|
||||
return ble_gap_disc_active();
|
||||
}
|
||||
|
||||
|
||||
|
@ -252,25 +251,6 @@ bool NimBLEScan::isScanning() {
|
|||
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
|
||||
NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
|
||||
|
||||
// If Host is not synced we cannot start scanning.
|
||||
if(!NimBLEDevice::m_synced) {
|
||||
NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ble_gap_conn_active()) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are already scanning don't start again or we will get stuck on the semaphore.
|
||||
if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset.
|
||||
NIMBLE_LOGE(LOG_TAG, "Scan already in progress");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_stopped = false;
|
||||
|
||||
// Save the callback to be invoked when the scan completes.
|
||||
m_scanCompleteCB = scanCompleteCB;
|
||||
// Save the duration in the case that the host is reset so we can reuse it.
|
||||
|
@ -281,32 +261,51 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
|
|||
duration = BLE_HS_FOREVER;
|
||||
}
|
||||
else{
|
||||
duration = duration*1000; // convert duration to milliseconds
|
||||
// convert duration to milliseconds
|
||||
duration = duration * 1000;
|
||||
}
|
||||
|
||||
// if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
|
||||
// then we should not clear vector or we will connect the same device few times
|
||||
// Set the flag to ignore the results while we are deleting the vector
|
||||
if(!is_continue) {
|
||||
clearResults();
|
||||
m_ignoreResults = true;
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
do{
|
||||
rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params,
|
||||
NimBLEScan::handleGapEvent, this);
|
||||
if(rc == BLE_HS_EBUSY) {
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
}
|
||||
} while(rc == BLE_HS_EBUSY);
|
||||
int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params,
|
||||
NimBLEScan::handleGapEvent, this);
|
||||
|
||||
if (rc != 0 && rc != BLE_HS_EDONE) {
|
||||
NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
m_stopped = true;
|
||||
switch(rc) {
|
||||
case 0:
|
||||
if(!is_continue) {
|
||||
clearResults();
|
||||
}
|
||||
break;
|
||||
|
||||
case BLE_HS_EALREADY:
|
||||
break;
|
||||
|
||||
case BLE_HS_EBUSY:
|
||||
NIMBLE_LOGE(LOG_TAG, "Unable to scan - connection in progress.");
|
||||
break;
|
||||
|
||||
case BLE_HS_ETIMEOUT_HCI:
|
||||
case BLE_HS_EOS:
|
||||
case BLE_HS_ECONTROLLER:
|
||||
case BLE_HS_ENOTSYNCED:
|
||||
NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset");
|
||||
break;
|
||||
|
||||
default:
|
||||
NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
|
||||
rc, NimBLEUtils::returnCodeToString(rc));
|
||||
break;
|
||||
}
|
||||
|
||||
m_ignoreResults = false;
|
||||
NIMBLE_LOGD(LOG_TAG, "<< start()");
|
||||
|
||||
if(rc != 0 && rc != BLE_HS_EALREADY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NIMBLE_LOGD(LOG_TAG, "<< start()");
|
||||
return true;
|
||||
} // start
|
||||
|
||||
|
@ -347,8 +346,6 @@ bool NimBLEScan::stop() {
|
|||
return false;
|
||||
}
|
||||
|
||||
m_stopped = true;
|
||||
|
||||
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
|
||||
m_scanCompleteCB(m_scanResults);
|
||||
}
|
||||
|
@ -381,13 +378,25 @@ void NimBLEScan::erase(const NimBLEAddress &address) {
|
|||
|
||||
|
||||
/**
|
||||
* @brief If the host reset the scan will have stopped so we should set the flag as stopped.
|
||||
* @brief Called when host reset, we set a flag to stop scanning until synced.
|
||||
*/
|
||||
void NimBLEScan::onHostReset() {
|
||||
m_stopped = true;
|
||||
m_ignoreResults = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief If the host reset and re-synced this is called.
|
||||
* If the application was scanning indefinitely with a callback, restart it.
|
||||
*/
|
||||
void NimBLEScan::onHostSync() {
|
||||
m_ignoreResults = false;
|
||||
|
||||
if(m_duration == 0 && m_pAdvertisedDeviceCallbacks != nullptr) {
|
||||
start(m_duration, m_scanCompleteCB);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the results of the scan.
|
||||
* @return NimBLEScanResults object.
|
||||
|
|
|
@ -83,12 +83,12 @@ private:
|
|||
~NimBLEScan();
|
||||
static int handleGapEvent(ble_gap_event* event, void* arg);
|
||||
void onHostReset();
|
||||
void onHostSync();
|
||||
|
||||
NimBLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr;
|
||||
void (*m_scanCompleteCB)(NimBLEScanResults scanResults);
|
||||
ble_gap_disc_params m_scan_params;
|
||||
uint8_t m_own_addr_type;
|
||||
bool m_stopped;
|
||||
bool m_ignoreResults;
|
||||
bool m_wantDuplicates;
|
||||
NimBLEScanResults m_scanResults;
|
||||
uint32_t m_duration;
|
||||
|
|
|
@ -296,6 +296,7 @@ size_t NimBLEServer::getConnectedCount() {
|
|||
}
|
||||
|
||||
server->m_pServerCallbacks->onDisconnect(server);
|
||||
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
|
||||
|
||||
if(server->m_advertiseOnDisconnect) {
|
||||
server->startAdvertising();
|
||||
|
@ -658,6 +659,10 @@ void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) {
|
|||
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
|
||||
} // onDisconnect
|
||||
|
||||
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
|
||||
} // onDisconnect
|
||||
|
||||
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
|
||||
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
|
||||
return 123456;
|
||||
|
|
|
@ -114,6 +114,15 @@ public:
|
|||
*/
|
||||
virtual void onDisconnect(NimBLEServer* pServer);
|
||||
|
||||
/**
|
||||
* @brief Handle a client disconnection.
|
||||
* This is called when a client discconnects.
|
||||
* @param [in] pServer A pointer to the %BLE server that received the client disconnection.
|
||||
* @param [in] desc A pointer to the connection description structure containig information
|
||||
* about the connection.
|
||||
*/
|
||||
virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
|
||||
|
||||
/**
|
||||
* @brief Called when a client requests a passkey for pairing.
|
||||
* @return The passkey to be sent to the client.
|
||||
|
|
|
@ -264,6 +264,37 @@ std::string NimBLEUUID::toString() const {
|
|||
*/
|
||||
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
|
||||
if(m_valueSet && rhs.m_valueSet) {
|
||||
NIMBLE_LOGD(LOG_TAG,"Comparing UUIDs; type %u to %u; UUID %s to %s",
|
||||
m_uuid.u.type, rhs.m_uuid.u.type,
|
||||
this->toString().c_str(), rhs.toString().c_str());
|
||||
|
||||
if(m_uuid.u.type != rhs.m_uuid.u.type) {
|
||||
uint8_t uuidBase[16] = {
|
||||
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
if(m_uuid.u.type == BLE_UUID_TYPE_128){
|
||||
if(rhs.m_uuid.u.type == BLE_UUID_TYPE_16){
|
||||
memcpy(uuidBase+12, &rhs.m_uuid.u16.value, 2);
|
||||
} else if (rhs.m_uuid.u.type == BLE_UUID_TYPE_32){
|
||||
memcpy(uuidBase+12, &rhs.m_uuid.u32.value, 4);
|
||||
}
|
||||
return memcmp(m_uuid.u128.value,uuidBase,16) == 0;
|
||||
|
||||
} else if(rhs.m_uuid.u.type == BLE_UUID_TYPE_128) {
|
||||
if(m_uuid.u.type == BLE_UUID_TYPE_16){
|
||||
memcpy(uuidBase+12, &m_uuid.u16.value, 2);
|
||||
} else if (m_uuid.u.type == BLE_UUID_TYPE_32){
|
||||
memcpy(uuidBase+12, &m_uuid.u32.value, 4);
|
||||
}
|
||||
return memcmp(rhs.m_uuid.u128.value,uuidBase,16) == 0;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file has been modified by Ryan Powell, aka h2zero.
|
||||
* The modifications are for the purpose of improving performance and support
|
||||
* for Esprssif versions used by the ardruino-esp32 core that are less current
|
||||
* than the esp-idf releases.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "sysinit/sysinit.h"
|
||||
#include "nimble/hci_common.h"
|
||||
|
@ -30,8 +37,10 @@
|
|||
#include "esp_bt.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_compiler.h"
|
||||
/* IPC is used to improve performance when calls come from a processor not running the NimBLE stack */
|
||||
/* but does not exist for solo */
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
#include "esp_ipc.h"
|
||||
#include "esp_ipc.h"
|
||||
#endif
|
||||
|
||||
#define NIMBLE_VHCI_TIMEOUT_MS 2000
|
||||
|
@ -81,31 +90,40 @@ void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
|
|||
ble_hci_rx_acl_hs_arg = acl_arg;
|
||||
}
|
||||
|
||||
void ble_hci_trans_hs_cmd_tx_on_core_0(void *arg)
|
||||
/* Added; Called from the core NimBLE is running on, not used for unicore */
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
void ble_hci_trans_hs_cmd_tx_on_core(void *arg)
|
||||
{
|
||||
uint8_t *cmd = arg;
|
||||
uint16_t len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
|
||||
esp_vhci_host_send_packet(cmd, len);
|
||||
// Ugly but necessary as the arduino core does not provide enough IPC stack for variables.
|
||||
esp_vhci_host_send_packet((uint8_t*)arg, *((uint8_t*)arg + 3) + 1 + BLE_HCI_CMD_HDR_LEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Modified to use ipc calls in arduino to correct performance issues */
|
||||
int ble_hci_trans_hs_cmd_tx(uint8_t *cmd)
|
||||
{
|
||||
uint16_t len;
|
||||
uint8_t rc = 0;
|
||||
|
||||
assert(cmd != NULL);
|
||||
*cmd = BLE_HCI_UART_H4_CMD;
|
||||
len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
|
||||
if (!esp_vhci_host_check_send_available()) {
|
||||
ESP_LOGD(TAG, "Controller not ready to receive packets");
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
if (xPortGetCoreID() != 0) {
|
||||
/* esp_ipc_call_blocking does not exist for solo */
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
esp_ipc_call_blocking(0, ble_hci_trans_hs_cmd_tx_on_core_0, cmd);
|
||||
#endif
|
||||
if (xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE) {
|
||||
esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE,
|
||||
ble_hci_trans_hs_cmd_tx_on_core, cmd);
|
||||
} else {
|
||||
ble_hci_trans_hs_cmd_tx_on_core_0(cmd);
|
||||
esp_vhci_host_send_packet(cmd, len);
|
||||
}
|
||||
#else /* Unicore */
|
||||
esp_vhci_host_send_packet(cmd, len);
|
||||
#endif
|
||||
} else {
|
||||
rc = BLE_HS_ETIMEOUT_HCI;
|
||||
}
|
||||
|
@ -124,21 +142,21 @@ int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev)
|
|||
return rc;
|
||||
}
|
||||
|
||||
void ble_hci_trans_hs_acl_tx_on_core_0(void *arg)
|
||||
/* Added; Called from the core NimBLE is running on, not used for unicore */
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
void ble_hci_trans_hs_acl_tx_on_core(void *arg)
|
||||
{
|
||||
uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 1];
|
||||
struct os_mbuf *om = arg;
|
||||
uint16_t len = 1 + OS_MBUF_PKTLEN(om);
|
||||
|
||||
data[0] = BLE_HCI_UART_H4_ACL;
|
||||
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
|
||||
|
||||
esp_vhci_host_send_packet(data, len);
|
||||
// Ugly but necessary as the arduino core does not provide enough IPC stack for variables.
|
||||
esp_vhci_host_send_packet((uint8_t*)arg + 2, *(uint16_t*)arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Modified to use ipc calls in arduino to correct performance issues */
|
||||
int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
|
||||
{
|
||||
uint8_t rc = 0;
|
||||
uint16_t len = 0;
|
||||
uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 3], rc = 0;
|
||||
bool tx_using_nimble_core = 0;
|
||||
/* If this packet is zero length, just free it */
|
||||
if (OS_MBUF_PKTLEN(om) == 0) {
|
||||
os_mbuf_free_chain(om);
|
||||
|
@ -149,14 +167,36 @@ int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
|
|||
ESP_LOGD(TAG, "Controller not ready to receive packets");
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
if (xPortGetCoreID() != 0) {
|
||||
len = 1 + OS_MBUF_PKTLEN(om);
|
||||
/* Don't check core ID if unicore */
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
esp_ipc_call_blocking(0, ble_hci_trans_hs_acl_tx_on_core_0, om);
|
||||
tx_using_nimble_core = xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE;
|
||||
if (tx_using_nimble_core) {
|
||||
data[0] = len;
|
||||
data[1] = (len >> 8);
|
||||
data[2] = BLE_HCI_UART_H4_ACL;
|
||||
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[3]);
|
||||
} else {
|
||||
data[0] = BLE_HCI_UART_H4_ACL;
|
||||
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
|
||||
}
|
||||
#else /* Unicore */
|
||||
data[0] = BLE_HCI_UART_H4_ACL;
|
||||
os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
|
||||
#endif
|
||||
|
||||
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
|
||||
/* esp_ipc_call_blocking does not exist for solo */
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
if (tx_using_nimble_core) {
|
||||
esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE,
|
||||
ble_hci_trans_hs_acl_tx_on_core, data);
|
||||
} else {
|
||||
ble_hci_trans_hs_acl_tx_on_core_0(om);
|
||||
esp_vhci_host_send_packet(data, len);
|
||||
}
|
||||
#else /* Unicore */
|
||||
esp_vhci_host_send_packet(data, len);
|
||||
#endif
|
||||
} else {
|
||||
rc = BLE_HS_ETIMEOUT_HCI;
|
||||
}
|
||||
|
@ -367,6 +407,13 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len)
|
|||
totlen = BLE_HCI_EVENT_HDR_LEN + data[2];
|
||||
assert(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN);
|
||||
|
||||
if (totlen > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) {
|
||||
ESP_LOGE(TAG, "Received HCI data length at host (%d) exceeds maximum configured HCI event buffer size (%d).",
|
||||
totlen, MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE));
|
||||
ble_hs_sched_reset(BLE_HS_ECONTROLLER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data[1] == BLE_HCI_EVCODE_HW_ERROR) {
|
||||
assert(0);
|
||||
}
|
||||
|
@ -476,6 +523,9 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
|
|||
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
/* Added to ensure BLE only mode */
|
||||
bt_cfg.mode = ESP_BT_MODE_BLE;
|
||||
/* Added to set max connections from nimconfig */
|
||||
bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
|
||||
|
||||
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
|
||||
|
@ -485,6 +535,7 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
|
|||
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return esp_nimble_hci_init();
|
||||
}
|
||||
|
||||
|
|
|
@ -483,6 +483,10 @@
|
|||
#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_L2CAP_COC_MPS
|
||||
#define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE - 8)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS
|
||||
#define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1)
|
||||
#endif
|
||||
|
|
|
@ -76,7 +76,7 @@ ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields *adv_fields,
|
|||
if (adv_fields->num_uuids16 > BLE_EDDYSTONE_MAX_UUIDS16) {
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
if (svc_data_len > BLE_EDDYSTONE_MAX_SVC_DATA_LEN) {
|
||||
if (svc_data_len > (BLE_EDDYSTONE_MAX_SVC_DATA_LEN - BLE_EDDYSTONE_SVC_DATA_BASE_SZ)) {
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
if (adv_fields->num_uuids16 > 0 && !adv_fields->uuids16_is_complete) {
|
||||
|
|
|
@ -1017,7 +1017,7 @@ ble_gap_master_failed(int status)
|
|||
#endif
|
||||
|
||||
default:
|
||||
BLE_HS_DBG_ASSERT(0);
|
||||
//BLE_HS_DBG_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1458,8 +1458,8 @@ ble_gap_rx_periodic_adv_rpt(struct hci_le_subev_periodic_adv_rpt *evt)
|
|||
{
|
||||
struct ble_hs_periodic_sync *psync;
|
||||
struct ble_gap_event event;
|
||||
ble_gap_event_fn *cb;
|
||||
void *cb_arg;
|
||||
ble_gap_event_fn *cb = NULL;
|
||||
void *cb_arg = NULL;
|
||||
|
||||
ble_hs_lock();
|
||||
psync = ble_hs_periodic_sync_find_by_handle(evt->sync_handle);
|
||||
|
|
|
@ -470,29 +470,52 @@ ble_hs_conn_timer(void)
|
|||
int32_t time_diff;
|
||||
uint16_t conn_handle;
|
||||
|
||||
conn_handle = BLE_HS_CONN_HANDLE_NONE;
|
||||
next_exp_in = BLE_HS_FOREVER;
|
||||
now = ble_npl_time_get();
|
||||
for (;;) {
|
||||
conn_handle = BLE_HS_CONN_HANDLE_NONE;
|
||||
next_exp_in = BLE_HS_FOREVER;
|
||||
now = ble_npl_time_get();
|
||||
|
||||
ble_hs_lock();
|
||||
ble_hs_lock();
|
||||
|
||||
/* This loop performs one of two tasks:
|
||||
* 1. Determine if any connections need to be terminated due to timeout.
|
||||
* If so, break out of the loop and terminate the connection. This
|
||||
* function will need to be executed again.
|
||||
* 2. Otherwise, determine when the next timeout will occur.
|
||||
*/
|
||||
SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
|
||||
if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
|
||||
/* This loop performs one of two tasks:
|
||||
* 1. Determine if any connections need to be terminated due to timeout.
|
||||
* If so, break out of the loop and terminate the connection. This
|
||||
* function will need to be executed again.
|
||||
* 2. Otherwise, determine when the next timeout will occur.
|
||||
*/
|
||||
SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
|
||||
if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
|
||||
|
||||
#if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
|
||||
/* Check each connection's rx fragment timer. If too much time
|
||||
* passes after a partial packet is received, the connection is
|
||||
* terminated.
|
||||
*/
|
||||
if (conn->bhc_rx_chan != NULL) {
|
||||
time_diff = conn->bhc_rx_timeout - now;
|
||||
/* Check each connection's rx fragment timer. If too much time
|
||||
* passes after a partial packet is received, the connection is
|
||||
* terminated.
|
||||
*/
|
||||
if (conn->bhc_rx_chan != NULL) {
|
||||
time_diff = conn->bhc_rx_timeout - now;
|
||||
|
||||
if (time_diff <= 0) {
|
||||
/* ACL reassembly has timed out. Remember the connection
|
||||
* handle so it can be terminated after the mutex is
|
||||
* unlocked.
|
||||
*/
|
||||
conn_handle = conn->bhc_handle;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine if this connection is the soonest to time out. */
|
||||
if (time_diff < next_exp_in) {
|
||||
next_exp_in = time_diff;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
|
||||
/* Check each connection's rx queued write timer. If too much
|
||||
* time passes after a prep write is received, the queue is
|
||||
* cleared.
|
||||
*/
|
||||
time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
|
||||
if (time_diff <= 0) {
|
||||
/* ACL reassembly has timed out. Remember the connection
|
||||
* handle so it can be terminated after the mutex is
|
||||
|
@ -506,45 +529,22 @@ ble_hs_conn_timer(void)
|
|||
if (time_diff < next_exp_in) {
|
||||
next_exp_in = time_diff;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
|
||||
/* Check each connection's rx queued write timer. If too much
|
||||
* time passes after a prep write is received, the queue is
|
||||
* cleared.
|
||||
*/
|
||||
time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
|
||||
if (time_diff <= 0) {
|
||||
/* ACL reassembly has timed out. Remember the connection
|
||||
* handle so it can be terminated after the mutex is
|
||||
* unlocked.
|
||||
*/
|
||||
conn_handle = conn->bhc_handle;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Determine if this connection is the soonest to time out. */
|
||||
if (time_diff < next_exp_in) {
|
||||
next_exp_in = time_diff;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ble_hs_unlock();
|
||||
|
||||
/* If a connection has timed out, terminate it. We need to repeatedly
|
||||
* call this function again to determine when the next timeout is.
|
||||
*/
|
||||
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
||||
continue;
|
||||
}
|
||||
|
||||
return next_exp_in;
|
||||
}
|
||||
|
||||
ble_hs_unlock();
|
||||
|
||||
/* If a connection has timed out, terminate it. We need to recursively
|
||||
* call this function again to determine when the next timeout is. This
|
||||
* is a tail-recursive call, so it should be optimized to execute in the
|
||||
* same stack frame.
|
||||
*/
|
||||
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
|
||||
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
||||
return ble_hs_conn_timer();
|
||||
}
|
||||
|
||||
return next_exp_in;
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -180,7 +180,9 @@ static int
|
|||
get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
|
||||
{
|
||||
union ble_store_value cur = {0};
|
||||
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
|
||||
struct ble_hs_dev_records p_dev_rec = {0};
|
||||
#endif
|
||||
esp_err_t err;
|
||||
int i, count = 0, max_limit = 0;
|
||||
char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN];
|
||||
|
@ -190,11 +192,15 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
|
|||
for (i = 1; i <= max_limit; i++) {
|
||||
get_nvs_key_string(obj_type, i, key_string);
|
||||
|
||||
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
|
||||
if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
|
||||
#endif
|
||||
err = get_nvs_db_value(obj_type, key_string, &cur);
|
||||
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
|
||||
} else {
|
||||
err = get_nvs_peer_record(key_string, &p_dev_rec);
|
||||
}
|
||||
#endif
|
||||
/* Check if the user is searching for empty index to write to */
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
if (empty) {
|
||||
|
@ -206,10 +212,13 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
|
|||
/* If user has provided value, then the purpose is to find
|
||||
* non-matching entry from NVS */
|
||||
if (value) {
|
||||
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
|
||||
if (obj_type == BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
|
||||
err = get_nvs_matching_index(&p_dev_rec, value, num_value,
|
||||
sizeof(struct ble_hs_dev_records));
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (obj_type != BLE_STORE_OBJ_TYPE_CCCD) {
|
||||
err = get_nvs_matching_index(&cur.sec, value, num_value,
|
||||
sizeof(struct ble_store_value_sec));
|
||||
|
@ -376,7 +385,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
|
|||
{
|
||||
uint8_t *db_item = (uint8_t *)dst;
|
||||
union ble_store_value cur = {0};
|
||||
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
|
||||
struct ble_hs_dev_records p_dev_rec = {0};
|
||||
#endif
|
||||
|
||||
esp_err_t err;
|
||||
int i;
|
||||
|
@ -385,8 +396,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
|
|||
for (i = 1; i <= get_nvs_max_obj_value(obj_type); i++) {
|
||||
get_nvs_key_string(obj_type, i, key_string);
|
||||
|
||||
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
|
||||
if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
|
||||
|
||||
#endif
|
||||
err = get_nvs_db_value(obj_type, key_string, &cur);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
continue;
|
||||
|
@ -394,6 +406,7 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
|
|||
ESP_LOGE(TAG, "NVS read operation failed !!");
|
||||
return -1;
|
||||
}
|
||||
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
|
||||
} else {
|
||||
err = get_nvs_peer_record(key_string, &p_dev_rec);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
|
@ -410,7 +423,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
|
|||
memcpy(db_item, &p_dev_rec, sizeof(struct ble_hs_dev_records));
|
||||
db_item += sizeof(struct ble_hs_dev_records);
|
||||
(*db_num)++;
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
|
||||
ESP_LOGD(TAG, "CCCD in RAM is filled up from NVS index = %d", i);
|
||||
memcpy(db_item, &cur.cccd, sizeof(struct ble_store_value_cccd));
|
||||
|
@ -492,6 +507,11 @@ int ble_store_config_persist_cccds(void)
|
|||
union ble_store_value val;
|
||||
|
||||
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_CCCD, 0, NULL, 0);
|
||||
if (nvs_count == -1) {
|
||||
ESP_LOGE(TAG, "NVS operation failed while persisting CCCD");
|
||||
return BLE_HS_ESTORE_FAIL;
|
||||
}
|
||||
|
||||
if (nvs_count < ble_store_config_num_cccds) {
|
||||
|
||||
/* NVS db count less than RAM count, write operation */
|
||||
|
@ -518,6 +538,11 @@ int ble_store_config_persist_peer_secs(void)
|
|||
union ble_store_value val;
|
||||
|
||||
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_SEC, 0, NULL, 0);
|
||||
if (nvs_count == -1) {
|
||||
ESP_LOGE(TAG, "NVS operation failed while persisting peer sec");
|
||||
return BLE_HS_ESTORE_FAIL;
|
||||
}
|
||||
|
||||
if (nvs_count < ble_store_config_num_peer_secs) {
|
||||
|
||||
/* NVS db count less than RAM count, write operation */
|
||||
|
@ -544,6 +569,11 @@ int ble_store_config_persist_our_secs(void)
|
|||
union ble_store_value val;
|
||||
|
||||
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_OUR_SEC, 0, NULL, 0);
|
||||
if (nvs_count == -1) {
|
||||
ESP_LOGE(TAG, "NVS operation failed while persisting our sec");
|
||||
return BLE_HS_ESTORE_FAIL;
|
||||
}
|
||||
|
||||
if (nvs_count < ble_store_config_num_our_secs) {
|
||||
|
||||
/* NVS db count less than RAM count, write operation */
|
||||
|
@ -573,7 +603,13 @@ int ble_store_persist_peer_records(void)
|
|||
struct ble_hs_dev_records *peer_dev_rec = ble_rpa_get_peer_dev_records();
|
||||
|
||||
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, 0, NULL, 0);
|
||||
if (nvs_count == -1) {
|
||||
ESP_LOGE(TAG, "NVS operation failed while persisting peer_dev_rec");
|
||||
return BLE_HS_ESTORE_FAIL;
|
||||
}
|
||||
|
||||
if (nvs_count < ble_store_num_peer_dev_rec) {
|
||||
|
||||
/* NVS db count less than RAM count, write operation */
|
||||
ESP_LOGD(TAG, "Persisting peer dev record to NVS...");
|
||||
peer_rec = peer_dev_rec[ble_store_num_peer_dev_rec - 1];
|
||||
|
|
|
@ -15,8 +15,13 @@
|
|||
* This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
|
||||
*/
|
||||
|
||||
/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig) */
|
||||
#if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
|
||||
/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig)
|
||||
*
|
||||
* Note: We do not use #ifdef CONFIG_BT_NIMBLE_ENABLED since we cannot enable NimBLE when using
|
||||
* Arduino as a component and the esp-nimble-compnent, so we check if other config options are defined.
|
||||
* We also need to use a config parameter that must be present and not likely defined in the command line.
|
||||
*/
|
||||
#if defined(CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN) || defined(CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN)
|
||||
|
||||
#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
|
||||
#define CONFIG_BT_NIMBLE_ENABLED
|
||||
|
@ -51,22 +56,30 @@
|
|||
/** @brief Comment out if not using NimBLE Client functions \n
|
||||
* Reduces flash size by approx. 7kB.
|
||||
*/
|
||||
#ifndef CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
|
||||
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
|
||||
#endif
|
||||
|
||||
/** @brief Comment out if not using NimBLE Scan functions \n
|
||||
* Reduces flash size by approx. 26kB.
|
||||
*/
|
||||
#ifndef CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
|
||||
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
|
||||
#endif
|
||||
|
||||
/** @brief Comment out if not using NimBLE Server functions \n
|
||||
* Reduces flash size by approx. 16kB.
|
||||
*/
|
||||
// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
|
||||
#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
|
||||
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
|
||||
#endif
|
||||
|
||||
/** @brief Comment out if not using NimBLE Advertising functions \n
|
||||
* Reduces flash size by approx. 5kB.
|
||||
*/
|
||||
// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
|
||||
#ifndef CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
|
||||
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
|
||||
#endif
|
||||
|
||||
/* Uncomment to see debug log messages from the NimBLE host
|
||||
* Uses approx. 32kB of flash memory.
|
||||
|
@ -89,29 +102,46 @@
|
|||
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
|
||||
|
||||
/** @brief Sets the core NimBLE host runs on */
|
||||
#ifndef CONFIG_BT_NIMBLE_PINNED_TO_CORE
|
||||
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
|
||||
#endif
|
||||
|
||||
/** @brief Sets the stack size for the NimBLE host task */
|
||||
#ifndef CONFIG_BT_NIMBLE_TASK_STACK_SIZE
|
||||
#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Sets the memory pool where NimBLE will be loaded
|
||||
* @details By default NimBLE is loaded in internal ram.\n
|
||||
* To use external PSRAM you must change this to `#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1`
|
||||
*/
|
||||
#ifndef CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL
|
||||
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
|
||||
#endif
|
||||
|
||||
/** @brief Sets the number of simultaneous connections (esp controller max is 9) */
|
||||
#ifndef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
|
||||
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
|
||||
#endif
|
||||
|
||||
/** @brief Sets the number of devices allowed to store/bond with */
|
||||
#ifndef CONFIG_BT_NIMBLE_MAX_BONDS
|
||||
#define CONFIG_BT_NIMBLE_MAX_BONDS 3
|
||||
#endif
|
||||
|
||||
/** @brief Sets the maximum number of CCCD subscriptions to store */
|
||||
#ifndef CONFIG_BT_NIMBLE_MAX_CCCDS
|
||||
#define CONFIG_BT_NIMBLE_MAX_CCCDS 8
|
||||
#endif
|
||||
|
||||
/** @brief Default device name */
|
||||
#ifndef CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME
|
||||
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
|
||||
#endif
|
||||
|
||||
/** @brief Set if CCCD's and bond data should be stored in NVS */
|
||||
#define CONFIG_BT_NIMBLE_NVS_PERSIST 0
|
||||
#define CONFIG_BT_NIMBLE_NVS_PERSIST 1
|
||||
|
||||
/** @brief Allow legacy paring */
|
||||
#define CONFIG_BT_NIMBLE_SM_LEGACY 1
|
||||
|
@ -119,9 +149,6 @@
|
|||
/** @brief Allow BLE secure connections */
|
||||
#define CONFIG_BT_NIMBLE_SM_SC 1
|
||||
|
||||
/** @brief Default device name */
|
||||
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
|
||||
|
||||
/** @brief Max device name length (bytes) */
|
||||
#define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31
|
||||
|
||||
|
@ -154,7 +181,6 @@
|
|||
*/
|
||||
#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
|
||||
|
||||
|
||||
/** @brief Random address refresh time in seconds */
|
||||
#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
|
||||
|
||||
|
|
|
@ -861,6 +861,9 @@
|
|||
|
||||
//#define USE_SPI // Add support for hardware SPI
|
||||
#define USE_MI_ESP32 // Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
|
||||
//#define USE_BLE_ESP32 // Add support for ESP32 as a BLE-bridge (+9k2? mem, +292k? flash)
|
||||
//#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
|
||||
//#define USE_IBEACON_ESP32
|
||||
//#define USE_WEBCAM // Add support for webcam
|
||||
|
||||
#endif // ESP32
|
||||
|
|
|
@ -690,6 +690,12 @@ void CmndSleep(void)
|
|||
|
||||
}
|
||||
|
||||
#ifdef USE_BLE_ESP32
|
||||
// declare the fn
|
||||
int ExtStopBLE();
|
||||
#endif
|
||||
|
||||
|
||||
void CmndUpgrade(void)
|
||||
{
|
||||
// Check if the payload is numerically 1, and had no trailing chars.
|
||||
|
@ -700,6 +706,11 @@ void CmndUpgrade(void)
|
|||
TasmotaGlobal.ota_state_flag = 3;
|
||||
char stemp1[TOPSZ];
|
||||
Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, TasmotaGlobal.version, GetOtaUrl(stemp1, sizeof(stemp1)));
|
||||
|
||||
#ifdef USE_BLE_ESP32
|
||||
ExtStopBLE();
|
||||
#endif
|
||||
|
||||
} else {
|
||||
Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, TasmotaGlobal.version);
|
||||
}
|
||||
|
|
|
@ -2374,6 +2374,11 @@ void UploadServices(uint32_t start_service) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef USE_BLE_ESP32
|
||||
// declare the fn
|
||||
int ExtStopBLE();
|
||||
#endif
|
||||
|
||||
void HandleUploadLoop(void) {
|
||||
// Based on ESP8266HTTPUpdateServer.cpp uses ESP8266WebServer Parsing.cpp and Cores Updater.cpp (Update)
|
||||
static uint32_t upload_size;
|
||||
|
@ -2409,6 +2414,11 @@ void HandleUploadLoop(void) {
|
|||
}
|
||||
SettingsSave(1); // Free flash for upload
|
||||
|
||||
#ifdef USE_BLE_ESP32
|
||||
ExtStopBLE();
|
||||
#endif
|
||||
|
||||
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s"), upload.filename.c_str());
|
||||
|
||||
if (UPL_SETTINGS == Web.upload_file_type) {
|
||||
|
@ -2632,7 +2642,9 @@ void HandleUploadLoop(void) {
|
|||
Web.upload_error = 7; // Upload aborted
|
||||
if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); }
|
||||
}
|
||||
delay(0);
|
||||
// do actually wait a little to allow ESP32 tasks to tick
|
||||
// fixes task timeout in ESP32Solo1 style unicore code.
|
||||
delay(10);
|
||||
OsWatchLoop();
|
||||
// Scheduler(); // Feed OsWatch timer to prevent restart on long uploads
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,6 +17,10 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// for testing of BLE_ESP32, we remove this completely, and instead add the modified xsns_52_ibeacon_BLE_ESP32.ino
|
||||
// in the future this may be more fine-grained, e.g. to allow hm17 for this, and BLE-ESP32 for other
|
||||
#ifndef USE_BLE_ESP32
|
||||
|
||||
#ifdef USE_IBEACON
|
||||
|
||||
#define XSNS_52 52
|
||||
|
@ -279,13 +283,16 @@ void ESP32Init() {
|
|||
|
||||
if (TasmotaGlobal.global_state.wifi_down) { return; }
|
||||
|
||||
TasmotaGlobal.wifi_stay_asleep = true;
|
||||
if (WiFi.getSleep() == false) {
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Put WiFi modem in sleep mode"),"BLE");
|
||||
WiFi.setSleep(true); // Sleep
|
||||
if (0 == Settings.flag3.sleep_normal) {
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: About to restart to put WiFi modem in sleep mode"),"BLE");
|
||||
Settings.flag3.sleep_normal = 1; // SetOption60 - Enable normal sleep instead of dynamic sleep
|
||||
TasmotaGlobal.restart_flag = 2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Initializing Bluetooth..."),"BLE");
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Initializing Blueetooth..."),"BLE");
|
||||
|
||||
if (!ESP32BLE.mode.init) {
|
||||
NimBLEDevice::init("");
|
||||
|
@ -1012,3 +1019,5 @@ bool Xsns52(byte function)
|
|||
}
|
||||
|
||||
#endif // USE_IBEACON
|
||||
|
||||
#endif
|
|
@ -0,0 +1,952 @@
|
|||
/*
|
||||
xsns_52_ibeacon.ino - Support for HM17 BLE Module + ibeacon reader on Tasmota
|
||||
|
||||
Copyright (C) 2020 Gerhard Mutz and Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// for testing of BLE_ESP32, we remove xsns_52_ibeacon.ino completely, and instead add this modified xsns_52_ibeacon_BLE_ESP32.ino
|
||||
// in the future this may be more fine-grained, e.g. to allow hm17 for this, and BLE-ESP32 for other
|
||||
#ifdef USE_BLE_ESP32
|
||||
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
|
||||
#ifdef USE_IBEACON
|
||||
|
||||
#define XSNS_52 52
|
||||
|
||||
// keyfob expires after N seconds
|
||||
#define IB_TIMEOUT_INTERVAL 30
|
||||
// does a passive scan every N seconds
|
||||
#define IB_UPDATE_TIME_INTERVAL 10
|
||||
|
||||
// should be in Settings
|
||||
#if 1
|
||||
uint8_t ib_upd_interval,ib_tout_interval;
|
||||
#define IB_UPDATE_TIME ib_upd_interval
|
||||
#define IB_TIMEOUT_TIME ib_tout_interval
|
||||
#else
|
||||
#undef IB_UPDATE_TIME
|
||||
#undef IB_TIMEOUT_TIME
|
||||
#define IB_UPDATE_TIME Settings.ib_upd_interval
|
||||
#define IB_TIMEOUT_TIME Settings.ib_tout_interval
|
||||
#endif
|
||||
|
||||
char ib_mac[14];
|
||||
|
||||
|
||||
struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t init:1;
|
||||
};
|
||||
uint32_t all = 0;
|
||||
} mode;
|
||||
} ESP32BLE;
|
||||
|
||||
void *beaconmutex = nullptr;
|
||||
|
||||
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8))
|
||||
|
||||
#else
|
||||
|
||||
#include <TasmotaSerial.h>
|
||||
|
||||
#define TMSBSIZ52 512
|
||||
|
||||
#define HM17_BAUDRATE 9600
|
||||
|
||||
#define IBEACON_DEBUG
|
||||
|
||||
// use this for Version 110
|
||||
#define HM17_V110
|
||||
|
||||
TasmotaSerial *IBEACON_Serial = nullptr;
|
||||
|
||||
uint8_t hm17_found,hm17_cmd,hm17_flag;
|
||||
|
||||
#ifdef IBEACON_DEBUG
|
||||
uint8_t hm17_debug=0;
|
||||
#endif
|
||||
|
||||
// 78 is max serial response
|
||||
#define HM17_BSIZ 128
|
||||
char hm17_sbuffer[HM17_BSIZ];
|
||||
uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting;
|
||||
uint32_t hm17_lastms;
|
||||
|
||||
enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON};
|
||||
#define HM17_SUCESS 99
|
||||
|
||||
#endif
|
||||
|
||||
struct IBEACON {
|
||||
char FACID[8];
|
||||
char UID[32];
|
||||
char MAJOR[4];
|
||||
char MINOR[4];
|
||||
char PWR[2];
|
||||
char MAC[12];
|
||||
char RSSI[4];
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
char NAME[16];
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
#define MAX_IBEACONS 32
|
||||
#else
|
||||
#define MAX_IBEACONS 16
|
||||
#endif
|
||||
|
||||
struct IBEACON_UID {
|
||||
char MAC[12];
|
||||
char RSSI[4];
|
||||
char UID[32];
|
||||
char MAJOR[4];
|
||||
char MINOR[4];
|
||||
uint8_t FLAGS;
|
||||
uint8_t TIME;
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
uint8_t REPORTED;
|
||||
uint8_t REPTIME;
|
||||
char NAME[16];
|
||||
#endif
|
||||
} ibeacons[MAX_IBEACONS];
|
||||
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
|
||||
uint32_t ibeacon_add(struct IBEACON *ib);
|
||||
|
||||
void ESP32BLE_ReverseStr(uint8_t _mac[], uint8_t len=6){
|
||||
uint8_t _reversedMAC[len];
|
||||
for (uint8_t i=0; i<len; i++){
|
||||
_reversedMAC[len-1-i] = _mac[i];
|
||||
}
|
||||
memcpy(_mac,_reversedMAC, sizeof(_reversedMAC));
|
||||
}
|
||||
|
||||
void DumpHex(const unsigned char * in, size_t insz, char * out)
|
||||
{
|
||||
static const char * hex = "0123456789ABCDEF";
|
||||
const unsigned char * pin = in;
|
||||
char * pout = out;
|
||||
for (; pin < in+insz; pout+=2, pin++) {
|
||||
pout[0] = hex[(pgm_read_byte(pin)>>4) & 0xF];
|
||||
pout[1] = hex[ pgm_read_byte(pin) & 0xF];
|
||||
}
|
||||
}
|
||||
|
||||
int advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct)
|
||||
{
|
||||
struct IBEACON ib;
|
||||
BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice;
|
||||
|
||||
char sRSSI[6];
|
||||
itoa(pStruct->RSSI,sRSSI,10);
|
||||
const uint8_t *MAC = pStruct->addr;
|
||||
|
||||
int manufacturerDataLen = 0;
|
||||
std::string data;
|
||||
if (advertisedDevice->haveManufacturerData()){
|
||||
data = advertisedDevice->getManufacturerData();
|
||||
manufacturerDataLen = data.length();
|
||||
}
|
||||
if (manufacturerDataLen){
|
||||
const uint8_t *manufacturerData = (const uint8_t *)data.data();
|
||||
DumpHex(manufacturerData, 2, ib.FACID);
|
||||
if (manufacturerDataLen == 25 &&
|
||||
manufacturerData[0] == 0x4C &&
|
||||
manufacturerData[1] == 0x00)
|
||||
{
|
||||
BLEBeacon oBeacon = BLEBeacon();
|
||||
oBeacon.setData(std::string((char *)manufacturerData, manufacturerDataLen));
|
||||
uint8_t UUID[16];
|
||||
memcpy(UUID,oBeacon.getProximityUUID().getNative()->u128.value,16);
|
||||
ESP32BLE_ReverseStr(UUID,16);
|
||||
|
||||
uint16_t Major = ENDIAN_CHANGE_U16(oBeacon.getMajor());
|
||||
uint16_t Minor = ENDIAN_CHANGE_U16(oBeacon.getMinor());
|
||||
uint8_t PWR = oBeacon.getSignalPower();
|
||||
|
||||
DumpHex((const unsigned char*)&UUID,16,ib.UID);
|
||||
DumpHex((const unsigned char*)&Major,2,ib.MAJOR);
|
||||
DumpHex((const unsigned char*)&Minor,2,ib.MINOR);
|
||||
DumpHex((const unsigned char*)&PWR,1,ib.PWR);
|
||||
DumpHex((const unsigned char*)MAC,6,ib.MAC);
|
||||
memcpy(ib.RSSI,sRSSI,4);
|
||||
memset(ib.NAME,0x0,16);
|
||||
|
||||
// if we added it
|
||||
if (ibeacon_add(&ib) == 1){
|
||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: MAC: %s Major: %d Minor: %d UUID: %s Power: %d RSSI: %d"),
|
||||
"iBeacon",
|
||||
advertisedDevice->getAddress().toString().c_str(),
|
||||
Major, Minor,
|
||||
oBeacon.getProximityUUID().toString().c_str(),
|
||||
PWR, pStruct->RSSI);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// no manufacturer data, or not recognised.
|
||||
// still have an RSSi....
|
||||
memset(ib.UID,'0',32);
|
||||
memset(ib.MAJOR,'0',4);
|
||||
memset(ib.MINOR,'0',4);
|
||||
memset(ib.PWR,'0',2);
|
||||
DumpHex((const unsigned char*)MAC,6,ib.MAC);
|
||||
memcpy(ib.RSSI,sRSSI,4);
|
||||
|
||||
if (advertisedDevice->haveName()) {
|
||||
strncpy(ib.NAME,advertisedDevice->getName().c_str(),16);
|
||||
} else {
|
||||
memset(ib.NAME,0x0,16);
|
||||
}
|
||||
|
||||
ibeacon_add(&ib);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ESP32Init() {
|
||||
|
||||
if (!ESP32BLE.mode.init) {
|
||||
ESP32BLE.mode.init = 1;
|
||||
IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL;
|
||||
IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
void IBEACON_Init() {
|
||||
|
||||
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
BLE_ESP32::registerForAdvertismentCallbacks((const char *)"iBeacon", advertismentCallback);
|
||||
#else
|
||||
|
||||
hm17_found=0;
|
||||
|
||||
// actually doesnt work reliably with software serial
|
||||
if (PinUsed(GPIO_IBEACON_RX) && PinUsed(GPIO_IBEACON_TX)) {
|
||||
IBEACON_Serial = new TasmotaSerial(Pin(GPIO_IBEACON_RX), Pin(GPIO_IBEACON_TX),1,0,TMSBSIZ52);
|
||||
if (IBEACON_Serial->begin(HM17_BAUDRATE)) {
|
||||
if (IBEACON_Serial->hardwareSerial()) {
|
||||
ClaimSerial();
|
||||
}
|
||||
hm17_sendcmd(HM17_TEST);
|
||||
hm17_lastms=millis();
|
||||
// in case of using Settings this has to be moved
|
||||
IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL;
|
||||
IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
|
||||
void esp32_every_second(void) {
|
||||
for (uint32_t cnt=0; cnt < MAX_IBEACONS; cnt++) {
|
||||
if (ibeacons[cnt].FLAGS) {
|
||||
uint8_t mac[6];
|
||||
char tmp[13];
|
||||
memcpy(tmp, ibeacons[cnt].MAC, 12);
|
||||
tmp[12] = 0;
|
||||
BLE_ESP32::fromHex(mac, tmp, 6);
|
||||
// use global device timeouts from BLE_ESP32.
|
||||
|
||||
uint32_t ageS = BLE_ESP32::devicePresent(mac);
|
||||
|
||||
// if device not present at all.
|
||||
if (!ageS){
|
||||
//AddLog_P(LOG_LEVEL_INFO, PSTR("iBeacon no device %s %02x%02x%02x%02x%02x%02x"),tmp, mac[0],mac[1], mac[2],mac[3], mac[4],mac[5]);
|
||||
ibeacons[cnt].FLAGS=0;
|
||||
ibeacon_mqtt(ibeacons[cnt].MAC,"0000",ibeacons[cnt].UID,ibeacons[cnt].MAJOR,ibeacons[cnt].MINOR,ibeacons[cnt].NAME);
|
||||
} else {
|
||||
//AddLog_P(LOG_LEVEL_INFO, PSTR("iBeacon device %s %02x%02x%02x%02x%02x%02x"),tmp, mac[0],mac[1], mac[2],mac[3], mac[4],mac[5]);
|
||||
}
|
||||
//ibeacons[cnt].TIME++;
|
||||
ibeacons[cnt].REPTIME++; // counter used to send mqtt for a dev regularly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void hm17_every_second(void) {
|
||||
if (!IBEACON_Serial) return;
|
||||
|
||||
if (hm17_found) {
|
||||
if (IB_UPDATE_TIME && (TasmotaGlobal.uptime%IB_UPDATE_TIME==0)) {
|
||||
if (hm17_cmd!=99) {
|
||||
if (hm17_flag&2) {
|
||||
ib_sendbeep();
|
||||
} else {
|
||||
if (!hm17_connecting) {
|
||||
hm17_sendcmd(HM17_DISI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t cnt=0;cnt<MAX_IBEACONS;cnt++) {
|
||||
if (ibeacons[cnt].FLAGS) {
|
||||
ibeacons[cnt].TIME++;
|
||||
if (ibeacons[cnt].TIME>IB_TIMEOUT_TIME) {
|
||||
ibeacons[cnt].FLAGS=0;
|
||||
ibeacon_mqtt(ibeacons[cnt].MAC,"0000",ibeacons[cnt].UID,ibeacons[cnt].MAJOR,ibeacons[cnt].MINOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (TasmotaGlobal.uptime%20==0) {
|
||||
hm17_sendcmd(HM17_TEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hm17_sbclr(void) {
|
||||
memset(hm17_sbuffer,0,HM17_BSIZ);
|
||||
hm17_sindex=0;
|
||||
//IBEACON_Serial->flush();
|
||||
}
|
||||
|
||||
void hm17_sendcmd(uint8_t cmd) {
|
||||
hm17_sbclr();
|
||||
hm17_cmd=cmd;
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd);
|
||||
#endif
|
||||
switch (cmd) {
|
||||
case HM17_TEST:
|
||||
IBEACON_Serial->write("AT");
|
||||
break;
|
||||
case HM17_ROLE:
|
||||
IBEACON_Serial->write("AT+ROLE1");
|
||||
break;
|
||||
case HM17_IMME:
|
||||
IBEACON_Serial->write("AT+IMME1");
|
||||
break;
|
||||
case HM17_DISI:
|
||||
IBEACON_Serial->write("AT+DISI?");
|
||||
hm17_scanning=1;
|
||||
break;
|
||||
case HM17_IBEA:
|
||||
IBEACON_Serial->write("AT+IBEA1");
|
||||
break;
|
||||
case HM17_RESET:
|
||||
IBEACON_Serial->write("AT+RESET");
|
||||
break;
|
||||
case HM17_RENEW:
|
||||
IBEACON_Serial->write("AT+RENEW");
|
||||
break;
|
||||
case HM17_SCAN:
|
||||
IBEACON_Serial->write("AT+SCAN5");
|
||||
break;
|
||||
case HM17_DISC:
|
||||
IBEACON_Serial->write("AT+DISC?");
|
||||
hm17_scanning=1;
|
||||
break;
|
||||
case HM17_CON:
|
||||
IBEACON_Serial->write((const uint8_t*)"AT+CON",6);
|
||||
IBEACON_Serial->write((const uint8_t*)ib_mac,12);
|
||||
hm17_connecting=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uint32_t ibeacon_add(struct IBEACON *ib) {
|
||||
/* if (!strncmp(ib->MAJOR,"4B1C",4)) {
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
if (!strncmp(ib->RSSI,"0",1)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// don't bother protecting this.
|
||||
//TasAutoMutex localmutex(&beaconmutex, "iBeacAdd");
|
||||
|
||||
// keyfob starts with ffff, ibeacon has valid facid
|
||||
if (!strncmp(ib->MAC,"FFFF",4) || strncmp(ib->FACID,"00000000",8)) {
|
||||
for (uint32_t cnt=0;cnt<MAX_IBEACONS;cnt++) {
|
||||
if (ibeacons[cnt].FLAGS) {
|
||||
if (!strncmp_P(ib->UID,PSTR("00000000000000000000000000000000"),32)) {
|
||||
if (!strncmp(ibeacons[cnt].MAC,ib->MAC,12)) {
|
||||
// exists
|
||||
memcpy(ibeacons[cnt].RSSI,ib->RSSI,4);
|
||||
ibeacons[cnt].TIME=0;
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
if (ibeacons[cnt].REPTIME >= IB_UPDATE_TIME) {
|
||||
ibeacons[cnt].REPTIME = 0;
|
||||
ibeacons[cnt].REPORTED = 0;
|
||||
}
|
||||
#endif
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
if (!strncmp(ibeacons[cnt].UID,ib->UID,32)) {
|
||||
// exists
|
||||
memcpy(ibeacons[cnt].RSSI,ib->RSSI,4);
|
||||
ibeacons[cnt].TIME=0;
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
if (ibeacons[cnt].REPTIME >= IB_UPDATE_TIME) {
|
||||
ibeacons[cnt].REPTIME = 0;
|
||||
ibeacons[cnt].REPORTED = 0;
|
||||
}
|
||||
#endif
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t cnt=0;cnt<MAX_IBEACONS;cnt++) {
|
||||
if (!ibeacons[cnt].FLAGS) {
|
||||
memcpy(ibeacons[cnt].MAC,ib->MAC,12);
|
||||
memcpy(ibeacons[cnt].RSSI,ib->RSSI,4);
|
||||
memcpy(ibeacons[cnt].UID,ib->UID,32);
|
||||
memcpy(ibeacons[cnt].MAJOR,ib->MAJOR,4);
|
||||
memcpy(ibeacons[cnt].MINOR,ib->MINOR,4);
|
||||
ibeacons[cnt].FLAGS=1;
|
||||
ibeacons[cnt].TIME=0;
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
memcpy(ibeacons[cnt].NAME,ib->NAME,16);
|
||||
ibeacons[cnt].REPTIME = 0;
|
||||
ibeacons[cnt].REPORTED = 0;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef USE_IBEACON_ESP32
|
||||
|
||||
void hm17_decode(void) {
|
||||
struct IBEACON ib;
|
||||
switch (hm17_cmd) {
|
||||
case HM17_TEST:
|
||||
if (!strncmp(hm17_sbuffer,"OK",2)) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("AT OK"));
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
hm17_result=HM17_SUCESS;
|
||||
hm17_found=1;
|
||||
}
|
||||
break;
|
||||
case HM17_ROLE:
|
||||
if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("ROLE OK"));
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
hm17_result=HM17_SUCESS;
|
||||
}
|
||||
break;
|
||||
case HM17_IMME:
|
||||
if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("IMME OK"));
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
hm17_result=HM17_SUCESS;
|
||||
}
|
||||
break;
|
||||
case HM17_IBEA:
|
||||
if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("IBEA OK"));
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
hm17_result=HM17_SUCESS;
|
||||
}
|
||||
break;
|
||||
case HM17_SCAN:
|
||||
if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("SCAN OK"));
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
hm17_result=HM17_SUCESS;
|
||||
}
|
||||
break;
|
||||
case HM17_RESET:
|
||||
if (!strncmp(hm17_sbuffer,"OK+RESET",8)) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("RESET OK"));
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
hm17_result=HM17_SUCESS;
|
||||
}
|
||||
break;
|
||||
case HM17_RENEW:
|
||||
if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("RENEW OK"));
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
hm17_result=HM17_SUCESS;
|
||||
}
|
||||
break;
|
||||
case HM17_CON:
|
||||
if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) {
|
||||
hm17_sbclr();
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("CONNA OK"));
|
||||
#endif
|
||||
hm17_connecting=2;
|
||||
break;
|
||||
}
|
||||
if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) {
|
||||
hm17_sbclr();
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("CONNE ERROR"));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) {
|
||||
hm17_sbclr();
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("CONNF ERROR"));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) {
|
||||
hm17_sbclr();
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("CONN OK"));
|
||||
#endif
|
||||
hm17_connecting=3;
|
||||
hm17_sendcmd(HM17_TEST);
|
||||
hm17_connecting=0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case HM17_DISI:
|
||||
case HM17_DISC:
|
||||
if (!strncmp(hm17_sbuffer,"OK+DISCS",8)) {
|
||||
hm17_sbclr();
|
||||
hm17_result=1;
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("DISCS OK"));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
if (!strncmp(hm17_sbuffer,"OK+DISIS",8)) {
|
||||
hm17_sbclr();
|
||||
hm17_result=1;
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("DISIS OK"));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
if (!strncmp(hm17_sbuffer,"OK+DISCE",8)) {
|
||||
hm17_sbclr();
|
||||
hm17_result=HM17_SUCESS;
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("DISCE OK"));
|
||||
#endif
|
||||
hm17_scanning=0;
|
||||
break;
|
||||
}
|
||||
if (!strncmp(hm17_sbuffer,"OK+NAME:",8)) {
|
||||
if (hm17_sbuffer[hm17_sindex-1]=='\n') {
|
||||
hm17_result=HM17_SUCESS;
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) {
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("NAME OK"));
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]);
|
||||
}
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!strncmp(hm17_sbuffer,"OK+DIS0:",8)) {
|
||||
if (hm17_cmd==HM17_DISI) {
|
||||
#ifdef HM17_V110
|
||||
goto hm17_v110;
|
||||
#endif
|
||||
} else {
|
||||
if (hm17_sindex==20) {
|
||||
hm17_result=HM17_SUCESS;
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) {
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("DIS0 OK"));
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]);
|
||||
}
|
||||
#endif
|
||||
hm17_sbclr();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!strncmp(hm17_sbuffer,"OK+DISC:",8)) {
|
||||
hm17_v110:
|
||||
if (hm17_cmd==HM17_DISI) {
|
||||
if (hm17_sindex==78) {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) {
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("DISC: OK"));
|
||||
//OK+DISC:4C 000C0E:003 A9144081A8 3B16849611 862EC1005: 0B1CE7485D :4DB4E940F C0E:-078
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]);
|
||||
}
|
||||
#endif
|
||||
memcpy(ib.FACID,&hm17_sbuffer[8],8);
|
||||
memcpy(ib.UID,&hm17_sbuffer[8+8+1],32);
|
||||
memcpy(ib.MAJOR,&hm17_sbuffer[8+8+1+32+1],4);
|
||||
memcpy(ib.MINOR,&hm17_sbuffer[8+8+1+32+1+4],4);
|
||||
memcpy(ib.PWR,&hm17_sbuffer[8+8+1+32+1+4+4],2);
|
||||
memcpy(ib.MAC,&hm17_sbuffer[8+8+1+32+1+4+4+2+1],12);
|
||||
memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4);
|
||||
|
||||
if (ibeacon_add(&ib)) {
|
||||
ibeacon_mqtt(ib.MAC,ib.RSSI,ib.UID,ib.MAJOR,ib.MINOR);
|
||||
}
|
||||
hm17_sbclr();
|
||||
hm17_result=1;
|
||||
}
|
||||
} else {
|
||||
#ifdef IBEACON_DEBUG
|
||||
if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR(">->%s"),&hm17_sbuffer[8]);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void IBEACON_loop() {
|
||||
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
//TasAutoMutex localmutex(&beaconmutex, "iBeacLoop");
|
||||
for (uint32_t cnt=0;cnt<MAX_IBEACONS;cnt++) {
|
||||
if (ibeacons[cnt].FLAGS && ! ibeacons[cnt].REPORTED) {
|
||||
ibeacon_mqtt(ibeacons[cnt].MAC,ibeacons[cnt].RSSI,ibeacons[cnt].UID,ibeacons[cnt].MAJOR,ibeacons[cnt].MINOR,ibeacons[cnt].NAME);
|
||||
ibeacons[cnt].REPORTED=1;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (!IBEACON_Serial) return;
|
||||
|
||||
uint32_t difftime=millis()-hm17_lastms;
|
||||
|
||||
while (IBEACON_Serial->available()) {
|
||||
hm17_lastms=millis();
|
||||
// shift in
|
||||
if (hm17_sindex<HM17_BSIZ) {
|
||||
hm17_sbuffer[hm17_sindex]=IBEACON_Serial->read();
|
||||
hm17_sindex++;
|
||||
hm17_decode();
|
||||
} else {
|
||||
hm17_sindex=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hm17_cmd==99) {
|
||||
if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) {
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer);
|
||||
hm17_sbclr();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
const char HTTP_IBEACON_HL[] PROGMEM = "{s}<hr>{m}<hr>{e}";
|
||||
const char HTTP_IBEACON_mac[] PROGMEM =
|
||||
"{s}IBEACON-MAC : %s" " {m} RSSI : %s" "{e}";
|
||||
const char HTTP_IBEACON_uid[] PROGMEM =
|
||||
"{s}IBEACON-UID : %s" " {m} RSSI : %s" "{e}";
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
const char HTTP_IBEACON_name[] PROGMEM =
|
||||
"{s}IBEACON-NAME : %s (%s)" " {m} RSSI : %s" "{e}";
|
||||
#endif
|
||||
void IBEACON_Show(void) {
|
||||
char mac[14];
|
||||
char rssi[6];
|
||||
char uid[34];
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
char name[18];
|
||||
//TasAutoMutex localmutex(&beaconmutex, "iBeacShow");
|
||||
#endif
|
||||
int total = 0;
|
||||
|
||||
for (uint32_t cnt=0;cnt<MAX_IBEACONS;cnt++) {
|
||||
if (ibeacons[cnt].FLAGS) {
|
||||
total++;
|
||||
memcpy(mac,ibeacons[cnt].MAC,12);
|
||||
mac[12]=0;
|
||||
memcpy(rssi,ibeacons[cnt].RSSI,4);
|
||||
rssi[4]=0;
|
||||
memcpy(uid,ibeacons[cnt].UID,32);
|
||||
uid[32]=0;
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
memcpy(name,ibeacons[cnt].NAME,16);
|
||||
name[16]=0;
|
||||
#endif
|
||||
if (!strncmp_P(uid,PSTR("00000000000000000000000000000000"),32)) {
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
if (name[0]) {
|
||||
WSContentSend_PD(HTTP_IBEACON_name,name,mac,rssi);
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_IBEACON_mac,mac,rssi);
|
||||
}
|
||||
#else
|
||||
WSContentSend_PD(HTTP_IBEACON_mac,mac,rssi);
|
||||
#endif
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_IBEACON_uid,uid,rssi);
|
||||
}
|
||||
}
|
||||
}
|
||||
// add a break line after us, if we showed anything.
|
||||
if (total) WSContentSend_PD(HTTP_IBEACON_HL);
|
||||
|
||||
}
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
/*
|
||||
commands sensor52
|
||||
to initialyze hm17 the cmds
|
||||
sensor52 1
|
||||
sensor52 2
|
||||
must be given only once (stored in module)
|
||||
|
||||
uT = sets update interval in seconds (scan tags every T seonds) default=10
|
||||
tT = sets timeout interval in seconds (after T seconds if tag is not detected send rssi=0) default=30
|
||||
sending IBEACON_FFFF3D1B1E9D_RSSI with data 99 causes tag to beep (ID to be replaced with actual ID)
|
||||
|
||||
*** debugging
|
||||
dx = sets debug mode to 0,1 (shows hm17 cmds + reactions in console)
|
||||
c = clears sensor list
|
||||
s AT+xxx = send native cmds to module
|
||||
e.g. s AT+CONFFFF3D1B1E9D connects to module with ID, then send s AT to disconnect which activates the beeper in the TAG
|
||||
*/
|
||||
|
||||
|
||||
|
||||
bool xsns52_cmd(void) {
|
||||
bool serviced = true;
|
||||
const char S_JSON_IBEACON[] = "{\"" D_CMND_SENSOR "%d\":%s:%d}";
|
||||
const char S_JSON_IBEACON1[] = "{\"" D_CMND_SENSOR "%d\":%s:%s}";
|
||||
uint16_t len=XdrvMailbox.data_len;
|
||||
if (len > 0) {
|
||||
char *cp=XdrvMailbox.data;
|
||||
if (*cp=='u') {
|
||||
cp++;
|
||||
if (*cp) IB_UPDATE_TIME=atoi(cp);
|
||||
Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME);
|
||||
} else if (*cp=='t') {
|
||||
cp++;
|
||||
if (*cp) IB_TIMEOUT_TIME=atoi(cp);
|
||||
Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME);
|
||||
} else if (*cp=='c') {
|
||||
for (uint32_t cnt=0;cnt<MAX_IBEACONS;cnt++) ibeacons[cnt].FLAGS=0;
|
||||
Response_P(S_JSON_IBEACON1, XSNS_52,"clr list","");
|
||||
}
|
||||
#ifndef USE_IBEACON_ESP32
|
||||
else if (*cp>='0' && *cp<='8') {
|
||||
hm17_sendcmd(*cp&7);
|
||||
Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7);
|
||||
} else if (*cp=='s') {
|
||||
cp++;
|
||||
len--;
|
||||
while (*cp==' ') {
|
||||
len--;
|
||||
cp++;
|
||||
}
|
||||
IBEACON_Serial->write((uint8_t*)cp,len);
|
||||
hm17_cmd=99;
|
||||
Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp);
|
||||
}
|
||||
#endif
|
||||
#ifdef IBEACON_DEBUG
|
||||
else if (*cp=='d') {
|
||||
cp++;
|
||||
hm17_debug=atoi(cp);
|
||||
Response_P(S_JSON_IBEACON, XSNS_52,"debug",hm17_debug);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
serviced=false;
|
||||
}
|
||||
return serviced;
|
||||
}
|
||||
|
||||
#define D_CMND_IBEACON "IBEACON"
|
||||
|
||||
#ifndef USE_IBEACON_ESP32
|
||||
//"IBEACON_FFFF3D1B1E9D_RSSI", Data "99" causes TAG to beep
|
||||
bool ibeacon_cmd(void) {
|
||||
ib_mac[0]=0;
|
||||
int16_t rssi=0;
|
||||
const char S_JSON_IBEACON[] = "{\"" D_CMND_IBEACON "_%s_RSSI\":%d}";
|
||||
uint8_t cmd_len = strlen(D_CMND_IBEACON);
|
||||
if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_IBEACON), cmd_len)) {
|
||||
// IBEACON prefix
|
||||
rssi = XdrvMailbox.payload;
|
||||
if (rssi==99) {
|
||||
memcpy(ib_mac,XdrvMailbox.topic+cmd_len+1,12);
|
||||
ib_mac[12]=0;
|
||||
if (hm17_scanning) {
|
||||
// postpone sendbeep
|
||||
hm17_flag|=2;
|
||||
} else {
|
||||
ib_sendbeep();
|
||||
}
|
||||
}
|
||||
Response_P(S_JSON_IBEACON,ib_mac,rssi);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ib_sendbeep(void) {
|
||||
hm17_flag=0;
|
||||
hm17_sendcmd(HM17_CON);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
void ibeacon_mqtt(const char *mac,const char *rssi,const char *uid,const char *major,const char *minor, const char *name) {
|
||||
#else
|
||||
void ibeacon_mqtt(const char *mac,const char *rssi,const char *uid,const char *major,const char *minor) {
|
||||
#endif
|
||||
char s_mac[14];
|
||||
char s_uid[34];
|
||||
char s_major[6];
|
||||
char s_minor[6];
|
||||
char s_rssi[6];
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
char *s_state;
|
||||
#endif
|
||||
char s_name[18];
|
||||
memcpy(s_mac,mac,12);
|
||||
s_mac[12]=0;
|
||||
memcpy(s_uid,uid,32);
|
||||
s_uid[32]=0;
|
||||
memcpy(s_major,major,4);
|
||||
s_major[4]=0;
|
||||
memcpy(s_minor,minor,4);
|
||||
s_minor[4]=0;
|
||||
memcpy(s_rssi,rssi,4);
|
||||
s_rssi[4]=0;
|
||||
int16_t n_rssi=atoi(s_rssi);
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
if (n_rssi) {
|
||||
s_state=(char *)"ON";
|
||||
} else {
|
||||
s_state=(char *)"OFF";
|
||||
}
|
||||
#endif
|
||||
// if uid == all zeros, take mac
|
||||
if (!strncmp_P(s_uid,PSTR("00000000000000000000000000000000"),32)) {
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
if (name[0]) {
|
||||
memcpy(s_name,name,16);
|
||||
s_name[16]=0;
|
||||
ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"MAC\":\"%s\",\"NAME\":\"%s\",\"RSSI\":%d,\"STATE\":\"%s\"}}"),s_mac,s_name,n_rssi,s_state);
|
||||
} else {
|
||||
ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"MAC\":\"%s\",\"RSSI\":%d,\"STATE\":\"%s\"}}"),s_mac,n_rssi,s_state);
|
||||
}
|
||||
#else
|
||||
ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"MAC\":\"%s\",\"UID\":\"%s\",\"MAJOR\":\"%s\",\"MINOR\":\"%s\",\"RSSI\":%d}}"),s_mac,s_uid,s_major,s_minor,n_rssi);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"UID\":\"%s\",\"MAJOR\":\"%s\",\"MINOR\":\"%s\",\"MAC\":\"%s\",\"RSSI\":%d,\"STATE\":\"%s\"}}"),s_uid,s_major,s_minor,s_mac,n_rssi,s_state);
|
||||
#else
|
||||
ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"UID\":\"%s\",\"MAJOR\":\"%s\",\"MINOR\":\"%s\",\"MAC\":\"%s\",\"RSSI\":%d}}"),s_uid,s_major,s_minor,s_mac,n_rssi);
|
||||
#endif
|
||||
}
|
||||
|
||||
MqttPublishTeleSensor();
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns52(byte function)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_INIT:
|
||||
IBEACON_Init();
|
||||
break;
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
case FUNC_EVERY_250_MSECOND:
|
||||
if (!ESP32BLE.mode.init) {
|
||||
ESP32Init();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case FUNC_LOOP:
|
||||
IBEACON_loop();
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
#ifdef USE_IBEACON_ESP32
|
||||
esp32_every_second();
|
||||
#else
|
||||
hm17_every_second();
|
||||
#endif
|
||||
break;
|
||||
case FUNC_COMMAND_SENSOR:
|
||||
if (XSNS_52 == XdrvMailbox.index) {
|
||||
result = xsns52_cmd();
|
||||
}
|
||||
break;
|
||||
#ifndef USE_IBEACON_ESP32
|
||||
case FUNC_COMMAND:
|
||||
result=ibeacon_cmd();
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
#ifndef USE_IBEACON_ESP32
|
||||
if (hm17_found) IBEACON_Show();
|
||||
#else
|
||||
IBEACON_Show();
|
||||
#endif
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_IBEACON
|
||||
|
||||
#endif
|
|
@ -45,6 +45,7 @@
|
|||
forked - from arendst/tasmota - https://github.com/arendst/Tasmota
|
||||
|
||||
*/
|
||||
#ifndef USE_BLE_ESP32
|
||||
#ifdef ESP32 // ESP32 only. Use define USE_HM10 for ESP8266 support
|
||||
|
||||
#ifdef USE_MI_ESP32
|
||||
|
@ -589,17 +590,17 @@ int MI32_decryptPacket(char *_buf, uint16_t _bufSize, uint32_t _type){
|
|||
MI32_ReverseMAC(packet->MAC);
|
||||
uint8_t _bindkey[16] = {0x0};
|
||||
bool foundNoKey = true;
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Search key for MAC: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]);
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: search key for MAC: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]);
|
||||
for(uint32_t i=0; i<MIBLEbindKeys.size(); i++){
|
||||
if(memcmp(packet->MAC,MIBLEbindKeys[i].MAC,sizeof(packet->MAC))==0){
|
||||
memcpy(_bindkey,MIBLEbindKeys[i].key,sizeof(_bindkey));
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Decryption Key found"));
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: decryption Key found"));
|
||||
foundNoKey = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(foundNoKey){
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: No Key found !!"));
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: no Key found !!"));
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
@ -619,7 +620,7 @@ int MI32_decryptPacket(char *_buf, uint16_t _bufSize, uint32_t _type){
|
|||
|
||||
ret = br_ccm_check_tag(&ctx, &tag);
|
||||
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, packet->payload[1],packet->payload[2],packet->payload[3],packet->payload[4],packet->payload[5]);
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, packet->payload[1],packet->payload[2],packet->payload[3],packet->payload[4],packet->payload[5]);
|
||||
return ret-1;
|
||||
}
|
||||
#endif // USE_MI_DECRYPTION
|
||||
|
@ -660,7 +661,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter)
|
|||
bool _success = false;
|
||||
for (uint32_t i=0;i<MI32_TYPES;i++){ // i < sizeof(kMI32DeviceID) gives compiler warning
|
||||
if(_type == kMI32DeviceID[i]){
|
||||
DEBUG_SENSOR_LOG(PSTR("M32: ID is type %u"), i);
|
||||
DEBUG_SENSOR_LOG(PSTR("MI32: ID is type %u"), i);
|
||||
_type = i+1;
|
||||
_success = true;
|
||||
}
|
||||
|
@ -779,23 +780,26 @@ void MI32PreInit(void) {
|
|||
MI32.option.showRSSI = 1;
|
||||
MI32.option.ignoreBogusBattery = 1; // from advertisements
|
||||
MI32.option.holdBackFirstAutodiscovery = 1;
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("M32: pre-init"));
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: pre-init"));
|
||||
}
|
||||
|
||||
void MI32Init(void) {
|
||||
if (MI32.mode.init) { return; }
|
||||
|
||||
if (TasmotaGlobal.global_state.wifi_down) { return; }
|
||||
|
||||
TasmotaGlobal.wifi_stay_asleep = true;
|
||||
if (WiFi.getSleep() == false) {
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Put WiFi modem in sleep mode"));
|
||||
WiFi.setSleep(true); // Sleep
|
||||
// AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: WiFi modem not in sleep mode, BLE cannot start yet"));
|
||||
if (0 == Settings.flag3.sleep_normal) {
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: About to restart to put WiFi modem in sleep mode"));
|
||||
Settings.flag3.sleep_normal = 1; // SetOption60 - Enable normal sleep instead of dynamic sleep
|
||||
TasmotaGlobal.restart_flag = 2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MI32.mode.init) {
|
||||
NimBLEDevice::init("");
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Init BLE device"));
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: init BLE device"));
|
||||
MI32.mode.canScan = 1;
|
||||
MI32.mode.init = 1;
|
||||
MI32.period = Settings.tele_period;
|
||||
|
@ -1283,7 +1287,7 @@ void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){
|
|||
break;
|
||||
}
|
||||
if(decryptRet!=0){
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Decryption failed with error: %d"),decryptRet);
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: decryption failed with error: %d"),decryptRet);
|
||||
return;
|
||||
}
|
||||
#endif //USE_MI_DECRYPTION
|
||||
|
@ -1456,7 +1460,7 @@ void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI
|
|||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG_SENSOR_LOG(PSTR("M32: Unexpected CGD1-packet"));
|
||||
DEBUG_SENSOR_LOG(PSTR("MI32: unexpected CGD1-packet"));
|
||||
}
|
||||
if(MIBLEsensors[_slot].eventType.raw == 0) return;
|
||||
MIBLEsensors[_slot].shallSendMQTT = 1;
|
||||
|
@ -1488,7 +1492,7 @@ void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) {
|
|||
* @param UUID
|
||||
*/
|
||||
void MI32ParseGenericBeacon(uint8_t* payload, size_t payloadLength, uint16_t* CID, uint16_t*SVC, uint16_t* UUID){
|
||||
AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Beacon:____________"));
|
||||
AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("MI32: Beacon:____________"));
|
||||
for (uint32_t i = 0; i<payloadLength;){
|
||||
uint32_t ADtype = payload[i+1];
|
||||
uint32_t offset = payload[i];
|
||||
|
@ -1546,13 +1550,13 @@ void MI32HandleGenericBeacon(uint8_t* payload, size_t payloadLength, int RSSI, u
|
|||
}
|
||||
// else handle scan
|
||||
if(MIBLEscanResult.size()>19) {
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Scan buffer full"));
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: Scan buffer full"));
|
||||
MI32.state.beaconScanCounter = 1;
|
||||
return;
|
||||
}
|
||||
for(auto _scanResult : MIBLEscanResult){
|
||||
if(memcmp(addr,_scanResult.MAC,6)==0){
|
||||
// AddLog_P(LOG_LEVEL_INFO,PSTR("M32: known device"));
|
||||
// AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: known device"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1582,12 +1586,12 @@ void MI32addBeacon(uint8_t index, char* data){
|
|||
_new.time = 0;
|
||||
if(memcmp(_empty,_new.MAC,6) == 0){
|
||||
_new.active = false;
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Beacon%u deactivated"), index);
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: beacon%u deactivated"), index);
|
||||
}
|
||||
else{
|
||||
_new.active = true;
|
||||
MI32.mode.activeBeacon = 1;
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Beacon added with MAC: %s"), _MAC);
|
||||
AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: beacon added with MAC: %s"), _MAC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1842,7 +1846,7 @@ void CmndMi32Time(void) {
|
|||
if (XdrvMailbox.data_len > 0) {
|
||||
if (MIBLEsensors.size() > XdrvMailbox.payload) {
|
||||
if ((LYWSD02 == MIBLEsensors[XdrvMailbox.payload].type) || (MHOC303 == MIBLEsensors[XdrvMailbox.payload].type)) {
|
||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Will set Time"));
|
||||
AddLog_P(LOG_LEVEL_DEBUG, PSTR("MI32: will set Time"));
|
||||
MI32.state.sensor = XdrvMailbox.payload;
|
||||
MI32.mode.canScan = 0;
|
||||
MI32.mode.canConnect = 0;
|
||||
|
@ -1872,7 +1876,7 @@ void CmndMi32Unit(void) {
|
|||
if (XdrvMailbox.data_len > 0) {
|
||||
if (MIBLEsensors.size() > XdrvMailbox.payload) {
|
||||
if ((LYWSD02 == MIBLEsensors[XdrvMailbox.payload].type) || (MHOC303 == MIBLEsensors[XdrvMailbox.payload].type)) {
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Will set Unit"));
|
||||
AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: will set Unit"));
|
||||
MI32.state.sensor = XdrvMailbox.payload;
|
||||
MI32.mode.canScan = 0;
|
||||
MI32.mode.canConnect = 0;
|
||||
|
@ -1922,11 +1926,11 @@ void CmndMi32Block(void){
|
|||
switch (XdrvMailbox.index) {
|
||||
case 0:
|
||||
MIBLEBlockList.clear();
|
||||
// AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Size of ilist: %u"), MIBLEBlockList.size());
|
||||
ResponseCmndIdxChar(PSTR("Block list cleared"));
|
||||
// AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: size of ilist: %u"), MIBLEBlockList.size());
|
||||
ResponseCmndIdxChar(PSTR("block list cleared"));
|
||||
break;
|
||||
case 1:
|
||||
ResponseCmndIdxChar(PSTR("Show block list"));
|
||||
ResponseCmndIdxChar(PSTR("show block list"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1952,7 +1956,7 @@ void CmndMi32Block(void){
|
|||
ResponseCmndIdxChar(XdrvMailbox.data);
|
||||
MI32removeMIBLEsensor(_MACasBytes.buf);
|
||||
}
|
||||
// AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Size of ilist: %u"), MIBLEBlockList.size());
|
||||
// AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: size of ilist: %u"), MIBLEBlockList.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2319,3 +2323,4 @@ bool Xsns62(uint8_t function)
|
|||
}
|
||||
#endif // USE_MI_ESP32
|
||||
#endif // ESP32
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue