Merge branch 'development' of github.com:arendst/Tasmota into pr2_tm1637

This commit is contained in:
Ajith Vasudevan 2021-02-17 16:24:58 +05:30
commit 78e1051b32
37 changed files with 1675 additions and 728 deletions

View File

@ -10,9 +10,9 @@ All notable changes to this project will be documented in this file.
- Support for Frysk language translations by Christiaan Heerze - Support for Frysk language translations by Christiaan Heerze
- ESP8266 Fallback to ``*.bin.gz`` binary when OTA upload of ``*.bin`` binary fails - ESP8266 Fallback to ``*.bin.gz`` binary when OTA upload of ``*.bin`` binary fails
- Berry language improved Tasmota integration - Berry language improved Tasmota integration
- Berry file system support
- Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun`` - Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun``
- Basic support for filesystem ``autoexec.bat`` - Support for filesystem ``autoexec.bat`` to execute sequential commands like backlog
- Berry add file system support
### Changed ### Changed
- IRremoteESP8266 library from v2.7.14 to v2.7.15 - IRremoteESP8266 library from v2.7.14 to v2.7.15

View File

@ -96,7 +96,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Commands ``ZbNoAutoBind``, ``ZbReceivedTopic`` and ``ZbOmitDevice`` as synonyms for ``SetOption116, 118`` and ``119`` - Commands ``ZbNoAutoBind``, ``ZbReceivedTopic`` and ``ZbOmitDevice`` as synonyms for ``SetOption116, 118`` and ``119``
- Commands ``BuzzerActive`` and ``BuzzerPwm`` as synonyms for ``SetOption67`` and ``111`` - Commands ``BuzzerActive`` and ``BuzzerPwm`` as synonyms for ``SetOption67`` and ``111``
- Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun`` - Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun``
- Basic support for filesystem ``autoexec.bat`` - Support for filesystem ``autoexec.bat`` to execute sequential commands like backlog
- Milliseconds to console output [#10152](https://github.com/arendst/Tasmota/issues/10152) - Milliseconds to console output [#10152](https://github.com/arendst/Tasmota/issues/10152)
- Gpio ``Option_a1`` enabling PWM2 high impedance if powered off as used by Wyze bulbs [#10196](https://github.com/arendst/Tasmota/issues/10196) - Gpio ``Option_a1`` enabling PWM2 high impedance if powered off as used by Wyze bulbs [#10196](https://github.com/arendst/Tasmota/issues/10196)
- Rotary No Pullup GPIO selection ``Rotary A/B_n`` [#10407](https://github.com/arendst/Tasmota/issues/10407) - Rotary No Pullup GPIO selection ``Rotary A/B_n`` [#10407](https://github.com/arendst/Tasmota/issues/10407)
@ -121,6 +121,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for ESP32 ``Module 3`` Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630) - Support for ESP32 ``Module 3`` Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
- Support for ESP32 ``Module 5`` Wireless Tag Eth01 [#9496](https://github.com/arendst/Tasmota/issues/9496) - Support for ESP32 ``Module 5`` Wireless Tag Eth01 [#9496](https://github.com/arendst/Tasmota/issues/9496)
- Support for ESP32 ``Module 7`` M5stack core2 16MB binary tasmota32-core2.bin [#10635](https://github.com/arendst/Tasmota/issues/10635) - Support for ESP32 ``Module 7`` M5stack core2 16MB binary tasmota32-core2.bin [#10635](https://github.com/arendst/Tasmota/issues/10635)
- Support for Berry language on ESP32
- Support rotary encoder on Shelly Dimmer [#10407](https://github.com/arendst/Tasmota/issues/10407#issuecomment-756240920) - Support rotary encoder on Shelly Dimmer [#10407](https://github.com/arendst/Tasmota/issues/10407#issuecomment-756240920)
- Support character `#` to be replaced by `space`-character in command ``Publish`` topic [#10258](https://github.com/arendst/Tasmota/issues/10258) - Support character `#` to be replaced by `space`-character in command ``Publish`` topic [#10258](https://github.com/arendst/Tasmota/issues/10258)
- Support trailing silence in buzzer tune [#10694](https://github.com/arendst/Tasmota/issues/10694) - Support trailing silence in buzzer tune [#10694](https://github.com/arendst/Tasmota/issues/10694)

View File

@ -2,6 +2,70 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [1.2.0] - 2021-02-08
### Added
- `NimBLECharacteristic::getDescriptorByHandle`: Return the BLE Descriptor for the given handle.
- `NimBLEDescriptor::getStringValue`: Get the value of this descriptor as a string.
- `NimBLEServer::getServiceByHandle`: Get a service by its handle.
- `NimBLEService::getCharacteristicByHandle`: Get a pointer to the characteristic object with the specified handle.
- `NimBLEService::getCharacteristics`: Get the vector containing pointers to each characteristic associated with this service.
Overloads to get a vector containing pointers to all the characteristics in a service with the UUID. (supports multiple same UUID's in a service)
- `NimBLEService::getCharacteristics(const char *uuid)`
- `NimBLEService::getCharacteristics(const NimBLEUUID &uuid)`
- `NimBLEAdvertisementData` New methods:
- `NimBLEAdvertisementData::addTxPower`: Adds transmission power to the advertisement.
- `NimBLEAdvertisementData::setPreferredParams`: Adds connection parameters to the advertisement.
- `NimBLEAdvertisementData::setURI`: Adds URI data to the advertisement.
- `NimBLEAdvertising` New methods:
- `NimBLEAdvertising::setName`: Set the name advertised.
- `NimBLEAdvertising::setManufacturerData`: Adds manufacturer data to the advertisement.
- `NimBLEAdvertising::setURI`: Adds URI data to the advertisement.
- `NimBLEAdvertising::setServiceData`: Adds service data to the advertisement.
- `NimBLEAdvertising::addTxPower`: Adds transmission power to the advertisement.
- `NimBLEAdvertising::reset`: Stops the current advertising and resets the advertising data to the default values.
- `NimBLEDevice::setScanFilterMode`: Set the controller duplicate filter mode for filtering scanned devices.
- `NimBLEDevice::setScanDuplicateCacheSize`: Sets the number of advertisements filtered before the cache is reset.
- `NimBLEScan::setMaxResults`: This allows for setting a maximum number of advertised devices stored in the results vector.
- `NimBLEAdvertisedDevice` New data retrieval methods added:
- `haveAdvInterval/getAdvInterval`: checks if the interval is advertised / gets the advertisement interval value.
- `haveConnParams/getMinInterval/getMaxInterval`: checks if the parameters are advertised / get min value / get max value.
- `haveURI/getURI`: checks if a URI is advertised / gets the URI data.
- `haveTargetAddress/getTargetAddressCount/getTargetAddress(index)`: checks if a target address is present / gets a count of the addresses targeted / gets the address of the target at index.
### Changed
- `nimconfig.h` (Arduino) is now easier to use.
- `NimBLEServer::getServiceByUUID` Now takes an extra parameter of instanceID to support multiple services with the same UUID.
- `NimBLEService::getCharacteristic` Now takes an extra parameter of instanceID to support multiple characteristics with the same UUID.
- `NimBLEAdvertising` Transmission power is no longer advertised by default and can be added to the advertisement by calling `NimBLEAdvertising::addTxPower`
- `NimBLEAdvertising` Custom scan response data can now be used without custom advertisment.
- `NimBLEScan` Now uses the controller duplicate filter.
- `NimBLEAdvertisedDevice` Has been refactored to store the complete advertisement payload and no longer parses the data from each advertisement.
Instead the data will be parsed on-demand when the user application asks for specific data.
### Fixed
- `NimBLEHIDDevice` Characteristics now use encryption, this resolves an issue with communicating with devices requiring encryption for HID devices.
## [1.1.0] - 2021-01-20 ## [1.1.0] - 2021-01-20
### Added ### Added
@ -9,7 +73,7 @@ All notable changes to this project will be documented in this file.
- New examples for securing and authenticating client/server connections, by mblasee. - New examples for securing and authenticating client/server connections, by mblasee.
- `NimBLEAdvertiseing::SetMinPreferred` and `NimBLEAdvertiseing::SetMinPreferred` re-added. - `NimBLEAdvertising::SetMinPreferred` and `NimBLEAdvertising::SetMinPreferred` re-added.
- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio. - Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio.
@ -71,6 +135,7 @@ advertised them as 16/32bit but resolved them to 128bits. Both are now checked.
- (Arduino) Ensure controller mode is set to BLE Only. - (Arduino) Ensure controller mode is set to BLE Only.
## [1.0.2] - 2020-09-13 ## [1.0.2] - 2020-09-13
### Changed ### Changed
@ -84,6 +149,7 @@ Any changes to the controller max connection settings in `sdkconfig.h` will now
- (Arduino) Revert the previous change to fix the advertising start delay. Instead a replacement fix that routes all BLE controller commands from - (Arduino) Revert the previous change to fix the advertising start delay. Instead a replacement fix that routes all BLE controller commands from
a task running on core 0 (same as the controller) has been implemented. This improves response times and reliability for all BLE functions. a task running on core 0 (same as the controller) has been implemented. This improves response times and reliability for all BLE functions.
## [1.0.1] - 2020-09-02 ## [1.0.1] - 2020-09-02
### Added ### Added

View File

@ -1,93 +1,117 @@
# Arduino command line and platformio config options # Arduino command line and platformio config options
`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED` `CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
If defined, NimBLE Client functions will not be included.
- Reduces flash size by approx. 7kB.
<br>
`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED` Sets the number of simultaneous connections (esp controller max is 9)
- Default value is 3
<br/>
If defined, NimBLE Scan functions will not be included. `CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
- Reduces flash size by approx. 26kB.
<br>
`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED` Sets the default MTU size.
- Default value is 255
<br/>
If defined NimBLE Server functions will not be included. `CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
- Reduces flash size by approx. 16kB.
<br>
`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED` Set the default device name
- Default value is "nimble"
If defined, NimBLE Advertising functions will not be included. <br/>
- Reduces flash size by approx. 5kB.
<br>
`CONFIG_BT_NIMBLE_DEBUG` `CONFIG_BT_NIMBLE_DEBUG`
If defined, enables debug log messages from the NimBLE host If defined, enables debug log messages from the NimBLE host
- Uses approx. 32kB of flash memory. - Uses approx. 32kB of flash memory.
<br> <br/>
`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT` `CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT`
If defined, NimBLE host return codes will be printed as text in debug log messages. If defined, NimBLE host return codes will be printed as text in debug log messages.
- Uses approx. 7kB of flash memory. - Uses approx. 7kB of flash memory.
<br> <br/>
`CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT` `CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT`
If defined, GAP event codes will be printed as text in debug log messages. If defined, GAP event codes will be printed as text in debug log messages.
- Uses approx. 1kB of flash memory. - Uses approx. 1kB of flash memory.
<br> <br/>
`CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT` `CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT`
If defined, advertisment types will be printed as text while scanning in debug log messages. If defined, advertisment types will be printed as text while scanning in debug log messages.
- Uses approx. 250 bytes of flash memory. - Uses approx. 250 bytes of flash memory.
<br> <br/>
`CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE`
Set the default appearance.
- Default value is 0x00
<br/>
`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_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_RPA_TIMEOUT`
Sets the random address refresh time in seconds.
- Default value is 900
<br/>
`CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT`
Set the number of msys blocks For prepare write & prepare responses. This may need to be increased if
you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
- Default value is 12
<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_PINNED_TO_CORE` `CONFIG_BT_NIMBLE_PINNED_TO_CORE`
Sets the core the NimBLE host stack will run on Sets the core the NimBLE host stack will run on
- Options: 0 or 1 - Options: 0 or 1
<br> <br/>
`CONFIG_BT_NIMBLE_TASK_STACK_SIZE` `CONFIG_BT_NIMBLE_TASK_STACK_SIZE`
Set the task stack size for the NimBLE core. Set the task stack size for the NimBLE core.
- Default is 4096 - Default is 4096
<br> <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>

View File

@ -69,6 +69,8 @@ For example `BLEServer::createService(SERVICE_UUID)` will work just as it did be
<a name="characteristics"></a> <a name="characteristics"></a>
### Characteristics ### Characteristics
The constructor for `(Nim)BLECharacteristic` is now private, so if you currently subclass it to add logic you should switch to use `NimBLEService::createCharacteristic` instead. Any custom processing logic previously in a `BLECharacteristic` subclass should be moved to a `NimBLECharacteristicCallbacks` subclass instead, and passed into `NimBLECharacteristic::setCallbacks`.
`BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed. `BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed.
When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`. When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`.
@ -218,10 +220,11 @@ If you wish to advertise these parameters you can still do so manually via `BLEA
<br/> <br/>
Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or
`NimBLEAdvertising::setAppearance`. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead. `NimBLEAdvertising::setAppearance` or similar methods. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead.
Calling `NimBLEAdvertising::setScanResponseData` without also calling `NimBLEAdvertising::setAdvertisementData` will have no effect. ~~Calling `NimBLEAdvertising::setScanResponseData` without also calling `NimBLEAdvertising::setAdvertisementData` will have no effect.
When using custom scan response data you must also use custom advertisement data. When using custom scan response data you must also use custom advertisement data.~~
No longer true as of release 1.2.0 and above, custom scan response is now supported without custom advertisement data.
<br/> <br/>
> BLEAdvertising::start (NimBLEAdvertising::start) > BLEAdvertising::start (NimBLEAdvertising::start)

View File

@ -0,0 +1,41 @@
# Usage Tips
## Put BLE functions in a task running on the NimBLE stack core
When commands are sent to the stack from a differnt core they can experience delays in execution.
This library detects this and invokes the esp32 IPC to reroute these commands through the correct core but this also increases overhead.
Therefore it is highly recommended to create tasks for BLE to run on the same core, the macro `CONFIG_BT_NIMBLE_PINNED_TO_CORE` can be used to set the core.
<br/>
## Do not delete client instances unless necessary or unused
When a client instance has been created and has connected to a peer device and it has retrieved service/characteristic information it will store that data for the life of the client instance.
If you are periodically connecting to the same devices and you have deleted the client instance or the services when connecting again it will cause a retrieval of that information from the peer again.
This results in significant energy drain on the battery of the devices, fragments heap, and reduces connection performance.
Client instances in this library use approximately 20% of the original bluedroid library, deleteing them will provide much less gain than it did before.
It is recommended to retain the client instance in cases where the time between connecting to the same device is less than 5 minutes.
<br/>
## Only retrieve the services and characteriscs needed
As a client the use of `NimBLEClient::getServices` or `NimBLERemoteService::getCharacteristics` and using `true` for the parameter should be limited to devices that are not known.
Instead `NimBLEClient::getService(NimBLEUUID)` or `NimBLERemoteService::getCharacteristic(NimBLEUUID)` should be used to access certain attributes that are useful to the application.
This reduces energy consumed, heap allocated, connection time and improves overall efficiency.
<br/>
## Check return values
Many user issues can be avoided by checking if a function returned successfully, by either testing for true/false such as when calling `NimBLEClient::connect`,
or nullptr such as when calling `NimBLEClient::getService`. The latter being a must, as calling a method on a nullptr will surely result in a crash.
Most of the functions in this library return something that should be checked before proceeding.
<br/>
## There will be bugs - please report them
No code is bug free and unit testing will not find them all on it's own. If you encounter a bug, please report it along with any logs and decoded backtrace if applicable.
Best efforts will be made to correct any errors ASAP.
Bug reports can be made at https://github.com/h2zero/NimBLE-Arduino/issues or https://github.com/h2zero/esp-nimble-cpp/issues.
Questions and suggestions will be happily accepted there as well.

View File

@ -0,0 +1,71 @@
/** Example of continuous scanning for BLE advertisements.
* This example will scan forever while consuming as few resources as possible
* and report all advertisments on the serial monitor.
*
* Created: on January 31 2021
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
NimBLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
}
};
void setup() {
Serial.begin(115200);
Serial.println("Scanning...");
/** *Optional* Sets the filtering mode used by the scanner in the BLE controller.
*
* Can be one of:
* CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default)
* Filter by device address only, advertisements from the same address will be reported only once.
*
* CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1)
* Filter by data only, advertisements with the same data will only be reported once,
* even from different addresses.
*
* CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2)
* Filter by address and data, advertisements from the same address will be reported only once,
* except if the data in the advertisement has changed, then it will be reported again.
*
* Can only be used BEFORE calling NimBLEDevice::init.
*/
NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE);
/** *Optional* Sets the scan filter cache size in the BLE controller.
* When the number of duplicate advertisements seen by the controller
* reaches this value it will clear the cache and start reporting previously
* seen devices. The larger this number, the longer time between repeated
* device reports. Range 10 - 1000. (default 20)
*
* Can only be used BEFORE calling NimBLEDevice::init.
*/
NimBLEDevice::setScanDuplicateCacheSize(200);
NimBLEDevice::init("");
pBLEScan = NimBLEDevice::getScan(); //create new scan
// Set the callback for when devices are discovered, no duplicates.
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), false);
pBLEScan->setActiveScan(true); // Set active scanning, this will get more data from the advertiser.
pBLEScan->setInterval(97); // How often the scan occurs / switches channels; in milliseconds,
pBLEScan->setWindow(37); // How long to scan during the interval; in milliseconds.
pBLEScan->setMaxResults(0); // do not store the scan results, use callback only.
}
void loop() {
// If an error occurs that stops the scan, it will be restarted here.
if(pBLEScan->isScanning() == false) {
// Start scan with: duration = 0 seconds(forever), no scan end callback, not a continuation of a previous scan.
pBLEScan->start(0, nullptr, false);
}
delay(2000);
}

View File

@ -0,0 +1,33 @@
/** NimBLE_Service_Data_Advertiser Demo:
*
* Simple demo of advertising service data that changes every 5 seconds
*
* Created: on February 7 2021
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
static NimBLEUUID dataUuid(SERVICE_UUID);
static NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
static uint32_t count = 0;
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE work!");
NimBLEDevice::init("svc data");
}
void loop() {
pAdvertising->stop();
pAdvertising->setServiceData(dataUuid, std::string((char*)&count, sizeof(count)));
pAdvertising->start();
Serial.printf("Advertising count = %d\n", count);
count++;
delay(5000);
}

View File

@ -2,7 +2,7 @@
"name": "NimBLE-Arduino", "name": "NimBLE-Arduino",
"keywords": "esp32, bluetooth", "keywords": "esp32, bluetooth",
"description": "Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE", "description": "Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE",
"version": "1.1.0", "version": "1.2.0",
"frameworks": "arduino", "frameworks": "arduino",
"platforms": "espressif32" "platforms": "espressif32"
} }

View File

@ -1,5 +1,5 @@
name=NimBLE-Arduino name=NimBLE-Arduino
version=1.1.0 version=1.2.0
author=h2zero author=h2zero
maintainer=h2zero <powellperalta@gmail.com> maintainer=h2zero <powellperalta@gmail.com>
sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE. sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE.

View File

@ -28,25 +28,14 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
/** /**
* @brief Constructor * @brief Constructor
*/ */
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() { NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() :
m_payload(62,0)
{
m_advType = 0; m_advType = 0;
m_appearance = 0;
m_manufacturerData = "";
m_name = "";
m_rssi = -9999; m_rssi = -9999;
m_txPower = 0; m_callbackSent = false;
m_payloadLength = 0; m_timestamp = 0;
m_payload = nullptr; m_advLength = 0;
m_haveAppearance = false;
m_haveManufacturerData = false;
m_haveName = false;
m_haveRSSI = false;
m_haveServiceData = false;
m_haveServiceUUID = false;
m_haveTXPower = false;
m_callbackSent = false;
} // NimBLEAdvertisedDevice } // NimBLEAdvertisedDevice
@ -82,25 +71,126 @@ uint8_t NimBLEAdvertisedDevice::getAdvType() {
* @return The appearance of the advertised device. * @return The appearance of the advertised device.
*/ */
uint16_t NimBLEAdvertisedDevice::getAppearance() { uint16_t NimBLEAdvertisedDevice::getAppearance() {
return m_appearance; uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_APPEARANCE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_APPEARANCE_LEN + 1) {
return *field->value | *(field->value + 1) << 8;
}
}
return 0;
} // getAppearance } // getAppearance
/**
* @brief Get the advertisement interval.
* @return The advertisement interval in 0.625ms units.
*/
uint16_t NimBLEAdvertisedDevice::getAdvInterval() {
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_ADV_ITVL_LEN + 1) {
return *field->value | *(field->value + 1) << 8;
}
}
return 0;
} // getAdvInterval
/**
* @brief Get the preferred min connection interval.
* @return The preferred min connection interval in 1.25ms units.
*/
uint16_t NimBLEAdvertisedDevice::getMinInterval() {
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) {
return *field->value | *(field->value + 1) << 8;
}
}
return 0;
} // getMinInterval
/**
* @brief Get the preferred max connection interval.
* @return The preferred max connection interval in 1.25ms units.
*/
uint16_t NimBLEAdvertisedDevice::getMaxInterval() {
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1) {
return *(field->value + 2) | *(field->value + 3) << 8;
}
}
return 0;
} // getMaxInterval
/** /**
* @brief Get the manufacturer data. * @brief Get the manufacturer data.
* @return The manufacturer data of the advertised device. * @return The manufacturer data of the advertised device.
*/ */
std::string NimBLEAdvertisedDevice::getManufacturerData() { std::string NimBLEAdvertisedDevice::getManufacturerData() {
return m_manufacturerData; uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_MFG_DATA, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > 1) {
return std::string((char*)field->value, field->length - 1);
}
}
return "";
} // getManufacturerData } // getManufacturerData
/**
* @brief Get the URI from the advertisement.
* @return The URI data.
*/
std::string NimBLEAdvertisedDevice::getURI() {
uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_URI, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > 1) {
return std::string((char*)field->value, field->length - 1);
}
}
return "";
} // getURI
/** /**
* @brief Get the advertised name. * @brief Get the advertised name.
* @return The name of the advertised device. * @return The name of the advertised device.
*/ */
std::string NimBLEAdvertisedDevice::getName() { std::string NimBLEAdvertisedDevice::getName() {
return m_name; uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_COMP_NAME, 0, &data_loc) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME, 0, &data_loc) > 0)
{
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > 1) {
return std::string((char*)field->value, field->length - 1);
}
}
return "";
} // getName } // getName
@ -122,17 +212,70 @@ NimBLEScan* NimBLEAdvertisedDevice::getScan() {
} // getScan } // getScan
/**
* @brief Get the number of target addresses.
* @return The number of addresses.
*/
size_t NimBLEAdvertisedDevice::getTargetAddressCount() {
uint8_t count = 0;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR);
count += findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR);
return count;
}
/**
* @brief Get the target address at the index.
* @param [in] index The index of the target address.
* @return The target address.
*/
NimBLEAddress NimBLEAdvertisedDevice::getTargetAddress(uint8_t index) {
ble_hs_adv_field *field = nullptr;
uint8_t count = 0;
uint8_t data_loc = 0xFF;
index++;
count = findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR, index, &data_loc);
if (count < index) {
index -= count;
count = findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR, index, &data_loc);
}
if(count > 0 && data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length < index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
index -= count - field->length / BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
if(field->length > index * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN) {
return NimBLEAddress(field->value + (index - 1) * BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN);
}
}
return NimBLEAddress("");
}
/** /**
* @brief Get the service data. * @brief Get the service data.
* @param [in] index The vector index of the service data requested. * @param [in] index The index of the service data requested.
* @return The advertised service data or empty string if no data. * @return The advertised service data or empty string if no data.
*/ */
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) { std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
if(index > m_serviceDataVec.size()) { ble_hs_adv_field *field = nullptr;
NIMBLE_LOGW(LOG_TAG, "getServiceData: index out of range"); uint8_t bytes;
return ""; uint8_t data_loc = findServiceData(index, &bytes);
if(data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length > bytes) {
return std::string((char*)(field->value + bytes), field->length - bytes - 1);
}
} }
return m_serviceDataVec[index].second;
return "";
} //getServiceData } //getServiceData
@ -141,51 +284,148 @@ std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
* @param [in] uuid The uuid of the service data requested. * @param [in] uuid The uuid of the service data requested.
* @return The advertised service data or empty string if no data. * @return The advertised service data or empty string if no data.
*/ */
std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) const { std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
for(auto &it : m_serviceDataVec) { ble_hs_adv_field *field = nullptr;
if(it.first == uuid) { uint8_t bytes;
return it.second; uint8_t index = 0;
uint8_t data_loc = findServiceData(index, &bytes);
uint8_t uuidBytes = uuid.bitSize() / 8;
uint8_t plSize = m_payload.size() - 2;
while(data_loc < plSize) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(bytes == uuidBytes && NimBLEUUID(field->value, bytes, false) == uuid) {
return std::string((char*)(field->value + bytes), field->length - bytes - 1);
} }
index++;
data_loc = findServiceData(index, &bytes);
} }
NIMBLE_LOGW(LOG_TAG, "getServiceData: uuid not found");
NIMBLE_LOGI(LOG_TAG, "No service data found");
return ""; return "";
} //getServiceData } //getServiceData
/** /**
* @brief Get the advertised service UUID. * @brief Get the UUID of the serice data at the index.
* @param [in] index The vector index of the service data UUID requested. * @param [in] index The index of the service data UUID requested.
* @return The advertised service UUID or an empty UUID if not found. * @return The advertised service data UUID or an empty UUID if not found.
*/ */
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) { NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
if(!haveServiceData() || index > m_serviceDataVec.size()) { ble_hs_adv_field *field = nullptr;
NIMBLE_LOGW(LOG_TAG, "getServiceDataUUID: index out of range"); uint8_t bytes;
return NimBLEUUID(""); uint8_t data_loc = findServiceData(index, &bytes);
if(data_loc != 0xFF) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length >= bytes) {
return NimBLEUUID(field->value, bytes, false);
}
} }
return m_serviceDataVec[index].first;
return NimBLEUUID("");
} // getServiceDataUUID } // getServiceDataUUID
/**
* @brief Find the service data at the index.
* @param [in] index The index of the service data to find.
* @param [in] bytes A pointer to storage for the number of the bytes in the UUID.
* @return The index in the vector where the data is located, 0xFF if not found.
*/
uint8_t NimBLEAdvertisedDevice::findServiceData(uint8_t index, uint8_t *bytes) {
uint8_t data_loc = 0;
uint8_t found = 0;
*bytes = 0;
index++;
found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16, index, &data_loc);
if(found == index) {
*bytes = 2;
return data_loc;
}
index -= found;
found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32, index, &data_loc);
if(found == index) {
*bytes = 4;
return data_loc;
}
index -= found;
found = findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128, index, &data_loc);
if(found == index) {
*bytes = 16;
return data_loc;
}
return 0xFF;
}
/** /**
* @brief Get the count of advertised service data UUIDS * @brief Get the count of advertised service data UUIDS
* @return The number of service data UUIDS in the vector. * @return The number of service data UUIDS in the vector.
*/ */
size_t NimBLEAdvertisedDevice::getServiceDataCount() { size_t NimBLEAdvertisedDevice::getServiceDataCount() {
return m_serviceDataVec.size(); uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID16);
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID32);
count += findAdvField(BLE_HS_ADV_TYPE_SVC_DATA_UUID128);
return count;
} // getServiceDataCount } // getServiceDataCount
/** /**
* @brief Get the Service UUID. * @brief Get the Service UUID.
* @param [in] index The vector index of the service UUID requested. * @param [in] index The index of the service UUID requested.
* @return The Service UUID of the advertised service, or an empty UUID if not found. * @return The Service UUID of the advertised service, or an empty UUID if not found.
*/ */
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) { NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
if(!haveServiceUUID() || index > m_serviceUUIDs.size()) { uint8_t count = 0;
NIMBLE_LOGW(LOG_TAG, "getServiceUUID: index out of range"); uint8_t data_loc = 0;
return NimBLEUUID(""); uint8_t uuidBytes = 0;
uint8_t type = BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
ble_hs_adv_field *field = nullptr;
index++;
do {
count = findAdvField(type, index, &data_loc);
if(count >= index) {
if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS32) {
uuidBytes = 2;
} else if(type < BLE_HS_ADV_TYPE_INCOMP_UUIDS128) {
uuidBytes = 4;
} else {
uuidBytes = 16;
}
break;
} else {
type++;
index -= count;
}
} while(type <= BLE_HS_ADV_TYPE_COMP_UUIDS128);
if(uuidBytes > 0) {
field = (ble_hs_adv_field *)&m_payload[data_loc];
// In the case of more than one field of service uuid's we need to adjust
// the index to account for the uuids of the previous fields.
if(field->length < index * uuidBytes) {
index -= count - field->length / uuidBytes;
}
if(field->length > uuidBytes * index) {
return NimBLEUUID(field->value + uuidBytes * (index - 1), uuidBytes, false);
}
} }
return m_serviceUUIDs[index];
return NimBLEUUID("");
} // getServiceUUID } // getServiceUUID
@ -194,18 +434,32 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
* @return The count of services in the advertising packet. * @return The count of services in the advertising packet.
*/ */
size_t NimBLEAdvertisedDevice::getServiceUUIDCount() { size_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
return m_serviceUUIDs.size(); uint8_t count = 0;
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS16);
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS16);
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS32);
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS32);
count += findAdvField(BLE_HS_ADV_TYPE_INCOMP_UUIDS128);
count += findAdvField(BLE_HS_ADV_TYPE_COMP_UUIDS128);
return count;
} // getServiceUUIDCount } // getServiceUUIDCount
/** /**
* @brief Check advertised services for existance of the required UUID * @brief Check advertised services for existance of the required UUID
* @param [in] uuid The service uuid to look for in the advertisement.
* @return Return true if service is advertised * @return Return true if service is advertised
*/ */
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const { bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) {
for (int i = 0; i < m_serviceUUIDs.size(); i++) { size_t count = getServiceUUIDCount();
if (m_serviceUUIDs[i].equals(uuid)) return true; for(size_t i = 0; i < count; i++) {
if(uuid == getServiceUUID(i)) {
return true;
}
} }
return false; return false;
} // isAdvertisingService } // isAdvertisingService
@ -215,16 +469,43 @@ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const
* @return The TX Power of the advertised device. * @return The TX Power of the advertised device.
*/ */
int8_t NimBLEAdvertisedDevice::getTXPower() { int8_t NimBLEAdvertisedDevice::getTXPower() {
return m_txPower; uint8_t data_loc = 0;
if(findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL, 0, &data_loc) > 0) {
ble_hs_adv_field *field = (ble_hs_adv_field *)&m_payload[data_loc];
if(field->length == BLE_HS_ADV_TX_PWR_LVL_LEN + 1) {
return *(int8_t*)field->value;
}
}
return -99;
} // getTXPower } // getTXPower
/**
* @brief Does this advertisement have preferred connection parameters?
* @return True if connection parameters are present.
*/
bool NimBLEAdvertisedDevice::haveConnParams() {
return findAdvField(BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE) > 0;
} // haveConnParams
/**
* @brief Does this advertisement have have the advertising interval?
* @return True if the advertisement interval is present.
*/
bool NimBLEAdvertisedDevice::haveAdvInterval() {
return findAdvField(BLE_HS_ADV_TYPE_ADV_ITVL) > 0;
} // haveAdvInterval
/** /**
* @brief Does this advertisement have an appearance value? * @brief Does this advertisement have an appearance value?
* @return True if there is an appearance value present. * @return True if there is an appearance value present.
*/ */
bool NimBLEAdvertisedDevice::haveAppearance() { bool NimBLEAdvertisedDevice::haveAppearance() {
return m_haveAppearance; return findAdvField(BLE_HS_ADV_TYPE_APPEARANCE) > 0;
} // haveAppearance } // haveAppearance
@ -233,16 +514,36 @@ bool NimBLEAdvertisedDevice::haveAppearance() {
* @return True if there is manufacturer data present. * @return True if there is manufacturer data present.
*/ */
bool NimBLEAdvertisedDevice::haveManufacturerData() { bool NimBLEAdvertisedDevice::haveManufacturerData() {
return m_haveManufacturerData; return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA) > 0;
} // haveManufacturerData } // haveManufacturerData
/**
* @brief Does this advertisement have a URI?
* @return True if there is a URI present.
*/
bool NimBLEAdvertisedDevice::haveURI() {
return findAdvField(BLE_HS_ADV_TYPE_URI) > 0;
} // haveURI
/**
* @brief Does the advertisement contain a target address?
* @return True if an address is present.
*/
bool NimBLEAdvertisedDevice::haveTargetAddress() {
return findAdvField(BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR) > 0;
}
/** /**
* @brief Does this advertisement have a name value? * @brief Does this advertisement have a name value?
* @return True if there is a name value present. * @return True if there is a name value present.
*/ */
bool NimBLEAdvertisedDevice::haveName() { bool NimBLEAdvertisedDevice::haveName() {
return m_haveName; return findAdvField(BLE_HS_ADV_TYPE_COMP_NAME) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME) > 0;
} // haveName } // haveName
@ -251,7 +552,7 @@ bool NimBLEAdvertisedDevice::haveName() {
* @return True if there is a signal strength value present. * @return True if there is a signal strength value present.
*/ */
bool NimBLEAdvertisedDevice::haveRSSI() { bool NimBLEAdvertisedDevice::haveRSSI() {
return m_haveRSSI; return m_rssi != -9999;
} // haveRSSI } // haveRSSI
@ -260,7 +561,7 @@ bool NimBLEAdvertisedDevice::haveRSSI() {
* @return True if there is a service data value present. * @return True if there is a service data value present.
*/ */
bool NimBLEAdvertisedDevice::haveServiceData() { bool NimBLEAdvertisedDevice::haveServiceData() {
return m_haveServiceData; return getServiceDataCount() > 0;
} // haveServiceData } // haveServiceData
@ -269,7 +570,7 @@ bool NimBLEAdvertisedDevice::haveServiceData() {
* @return True if there is a service UUID value present. * @return True if there is a service UUID value present.
*/ */
bool NimBLEAdvertisedDevice::haveServiceUUID() { bool NimBLEAdvertisedDevice::haveServiceUUID() {
return m_haveServiceUUID; return getServiceUUIDCount() > 0;
} // haveServiceUUID } // haveServiceUUID
@ -278,143 +579,71 @@ bool NimBLEAdvertisedDevice::haveServiceUUID() {
* @return True if there is a transmission power value present. * @return True if there is a transmission power value present.
*/ */
bool NimBLEAdvertisedDevice::haveTXPower() { bool NimBLEAdvertisedDevice::haveTXPower() {
return m_haveTXPower; return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0;
} // haveTXPower } // haveTXPower
/** uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_t *data_loc) {
* @brief Parse the advertising pay load. ble_hs_adv_field *field = nullptr;
* uint8_t data = 0;
* The pay load is a buffer of bytes that is either 31 bytes long or terminated by uint8_t length = m_payload.size();
* a 0 length value. Each entry in the buffer has the format: uint8_t count = 0;
* [length][type][data...]
* if(length < 2) {
* The length does not include itself but does include everything after it until the next record. A record return count;
* with a length value of 0 indicates a terminator.
*
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*/
void NimBLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, uint8_t length) {
struct ble_hs_adv_fields fields;
int rc = ble_hs_adv_parse_fields(&fields, payload, length);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR.");
return;
} }
m_payload = payload; while (length > 1) {
m_payloadLength = length; field = (ble_hs_adv_field*)&m_payload[data];
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4) if (field->length >= length) {
char* pHex = NimBLEUtils::buildHexData(nullptr, m_payload, m_payloadLength); return count;
NIMBLE_LOGD(LOG_TAG,"payload: %s", pHex);
free(pHex);
#endif
if (fields.uuids16 != NULL) {
for (int i = 0; i < fields.num_uuids16; i++) {
setServiceUUID(NimBLEUUID(fields.uuids16[i].value));
} }
}
if (fields.uuids32 != NULL) { if (field->type == type) {
for (int i = 0; i < fields.num_uuids32; i++) { switch(type) {
setServiceUUID(NimBLEUUID(fields.uuids32[i].value)); case BLE_HS_ADV_TYPE_INCOMP_UUIDS16:
} case BLE_HS_ADV_TYPE_COMP_UUIDS16:
} count += field->length / 2;
break;
if (fields.uuids128 != NULL) { case BLE_HS_ADV_TYPE_INCOMP_UUIDS32:
for (int i = 0; i < fields.num_uuids128; i++) { case BLE_HS_ADV_TYPE_COMP_UUIDS32:
setServiceUUID(NimBLEUUID(&fields.uuids128[i])); count += field->length / 4;
} break;
}
if (fields.name != NULL) { case BLE_HS_ADV_TYPE_INCOMP_UUIDS128:
setName(std::string(reinterpret_cast<char*>(fields.name), fields.name_len)); case BLE_HS_ADV_TYPE_COMP_UUIDS128:
} count += field->length / 16;
break;
if (fields.tx_pwr_lvl_is_present) { case BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR:
setTXPower(fields.tx_pwr_lvl); case BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR:
} count += field->length / 6;
break;
if (fields.svc_data_uuid16 != NULL || default:
fields.svc_data_uuid32 != NULL || count++;
fields.svc_data_uuid128 != NULL) break;
{
ble_hs_adv_field *field;
uint8_t *data = payload;
while(length > 1) {
field = (ble_hs_adv_field*)data;
if(field->length > length) {
break;
} }
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID16) { if(data_loc != nullptr) {
if(field->length > 2) { if(index == 0 || count >= index) {
uint16_t uuid; break;
memcpy(&uuid, field->value, 2);
setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast<char*>(field->value + 2), field->length - 3));
} }
} }
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID32) {
if(field->length > 4) {
uint32_t uuid;
memcpy(&uuid, field->value, 4);
setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast<char*>(field->value + 4), field->length - 5));
}
}
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID128) {
if(field->length > 16) {
NimBLEUUID uuid(field->value, (size_t)16, false);
setServiceData(uuid, std::string(reinterpret_cast<char*>(field->value + 16), field->length - 17));
}
}
length -= 1 + field->length;
data += 1 + field->length;
} }
length -= 1 + field->length;
data += 1 + field->length;
} }
if (fields.appearance_is_present) { if(data_loc != nullptr && field != nullptr) {
setAppearance(fields.appearance); *data_loc = data;
} }
if (fields.mfg_data != NULL) { return count;
setManufacturerData(std::string(reinterpret_cast<char*>(fields.mfg_data), fields.mfg_data_len)); }
}
/* TODO: create storage and fucntions for these parameters
if (fields.public_tgt_addr != NULL) {
NIMBLE_LOGD(LOG_TAG, " public_tgt_addr=");
u8p = fields.public_tgt_addr;
for (i = 0; i < fields.num_public_tgt_addrs; i++) {
NIMBLE_LOGD(LOG_TAG, "public_tgt_addr=%s ", addr_str(u8p));
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
NIMBLE_LOGD(LOG_TAG, "\n");
}
if (fields.slave_itvl_range != NULL) {
NIMBLE_LOGD(LOG_TAG, " slave_itvl_range=");
print_bytes(fields.slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
NIMBLE_LOGD(LOG_TAG, "\n");
}
if (fields.adv_itvl_is_present) {
NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields.adv_itvl);
}
if (fields.uri != NULL) {
NIMBLE_LOGD(LOG_TAG, " uri=");
print_bytes(fields.uri, fields.uri_len);
NIMBLE_LOGD(LOG_TAG, "\n");
}
*/
} //parseAdvertisement
/** /**
@ -428,106 +657,22 @@ void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) {
/** /**
* @brief Set the adFlag for this device. * @brief Set the adFlag for this device.
* @param [in] The discovered adFlag. * @param [in] advType The advertisement flag data from the advertisement.
*/ */
void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) { void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) {
m_advType = advType; m_advType = advType;
} // setAdvType } // setAdvType
/**
* @brief Set the appearance for this device.
* @param [in] The discovered appearance.
*/
void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
m_appearance = appearance;
m_haveAppearance = true;
} // setAppearance
/**
* @brief Set the manufacturer data for this device.
* @param [in] The discovered manufacturer data.
*/
void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
m_manufacturerData = manufacturerData;
m_haveManufacturerData = true;
} // setManufacturerData
/**
* @brief Set the name for this device.
* @param [in] name The discovered name.
*/
void NimBLEAdvertisedDevice::setName(std::string name) {
m_name = name;
m_haveName = true;
} // setName
/** /**
* @brief Set the RSSI for this device. * @brief Set the RSSI for this device.
* @param [in] rssi The discovered RSSI. * @param [in] rssi The RSSI of the discovered device.
*/ */
void NimBLEAdvertisedDevice::setRSSI(int rssi) { void NimBLEAdvertisedDevice::setRSSI(int rssi) {
m_rssi = rssi; m_rssi = rssi;
m_haveRSSI = true;
} // setRSSI } // setRSSI
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
*/
void NimBLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
return setServiceUUID(NimBLEUUID(serviceUUID));
} // setServiceUUID
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
*/
void NimBLEAdvertisedDevice::setServiceUUID(NimBLEUUID serviceUUID) {
// Don't add duplicates
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
if (m_serviceUUIDs[i] == serviceUUID) {
return;
}
}
m_serviceUUIDs.push_back(serviceUUID);
m_haveServiceUUID = true;
} // setServiceUUID
/**
* @brief Set the ServiceData value.
* @param [in] uuid The UUID that the service data belongs to.
* @param [in] data The service data.
*/
void NimBLEAdvertisedDevice::setServiceData(NimBLEUUID uuid, std::string data) {
m_haveServiceData = true;
for(auto &it : m_serviceDataVec) {
if(it.first == uuid) {
it.second = data;
return;
}
}
m_serviceDataVec.push_back({uuid, data});
} //setServiceData
/**
* @brief Set the power level for this device.
* @param [in] txPower The discovered power level.
*/
void NimBLEAdvertisedDevice::setTXPower(int8_t txPower) {
m_txPower = txPower;
m_haveTXPower = true;
} // setTXPower
/** /**
* @brief Create a string representation of this device. * @brief Create a string representation of this device.
* @return A string representation of this device. * @return A string representation of this device.
@ -579,10 +724,35 @@ std::string NimBLEAdvertisedDevice::toString() {
* @return The advertisement payload. * @return The advertisement payload.
*/ */
uint8_t* NimBLEAdvertisedDevice::getPayload() { uint8_t* NimBLEAdvertisedDevice::getPayload() {
return m_payload; return &m_payload[0];
} // getPayload } // getPayload
/**
* @brief Stores the payload of the advertised device in a vector.
* @param [in] payload The advertisement payload.
* @param [in] length The length of the payload in bytes.
* @param [in] append Indicates if the the data should be appended (scan response).
*/
void NimBLEAdvertisedDevice::setPayload(uint8_t *payload, uint8_t length, bool append) {
if(!append) {
m_advLength = length;
m_payload.assign(payload, payload + length);
} else {
m_payload.insert(m_payload.end(), payload, payload + length);
}
}
/**
* @brief Get the length of the advertisement data in the payload.
* @return The number of bytes in the payload that is from the advertisment.
*/
uint8_t NimBLEAdvertisedDevice::getAdvLength() {
return m_advLength;
}
/** /**
* @brief Get the advertised device address type. * @brief Get the advertised device address type.
* @return The device address type: * @return The device address type:
@ -610,7 +780,7 @@ time_t NimBLEAdvertisedDevice::getTimestamp() {
* @return The size of the payload in bytes. * @return The size of the payload in bytes.
*/ */
size_t NimBLEAdvertisedDevice::getPayloadLength() { size_t NimBLEAdvertisedDevice::getPayloadLength() {
return m_payloadLength; return m_payload.size();
} // getPayloadLength } // getPayloadLength

View File

@ -44,7 +44,11 @@ public:
NimBLEAddress getAddress(); NimBLEAddress getAddress();
uint8_t getAdvType(); uint8_t getAdvType();
uint16_t getAppearance(); uint16_t getAppearance();
uint16_t getAdvInterval();
uint16_t getMinInterval();
uint16_t getMaxInterval();
std::string getManufacturerData(); std::string getManufacturerData();
std::string getURI();
/** /**
* @brief A template to convert the service data to <type\>. * @brief A template to convert the service data to <type\>.
@ -67,7 +71,7 @@ public:
NimBLEScan* getScan(); NimBLEScan* getScan();
size_t getServiceDataCount(); size_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0); std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid) const; std::string getServiceData(const NimBLEUUID &uuid);
/** /**
* @brief A template to convert the service data to <tt><type\></tt>. * @brief A template to convert the service data to <tt><type\></tt>.
@ -106,12 +110,15 @@ public:
NimBLEUUID getServiceDataUUID(uint8_t index = 0); NimBLEUUID getServiceDataUUID(uint8_t index = 0);
NimBLEUUID getServiceUUID(uint8_t index = 0); NimBLEUUID getServiceUUID(uint8_t index = 0);
size_t getServiceUUIDCount(); size_t getServiceUUIDCount();
NimBLEAddress getTargetAddress(uint8_t index = 0);
size_t getTargetAddressCount();
int8_t getTXPower(); int8_t getTXPower();
uint8_t* getPayload(); uint8_t* getPayload();
uint8_t getAdvLength();
size_t getPayloadLength(); size_t getPayloadLength();
uint8_t getAddressType(); uint8_t getAddressType();
time_t getTimestamp(); time_t getTimestamp();
bool isAdvertisingService(const NimBLEUUID &uuid) const; bool isAdvertisingService(const NimBLEUUID &uuid);
bool haveAppearance(); bool haveAppearance();
bool haveManufacturerData(); bool haveManufacturerData();
bool haveName(); bool haveName();
@ -119,46 +126,30 @@ public:
bool haveServiceData(); bool haveServiceData();
bool haveServiceUUID(); bool haveServiceUUID();
bool haveTXPower(); bool haveTXPower();
bool haveConnParams();
bool haveAdvInterval();
bool haveTargetAddress();
bool haveURI();
std::string toString(); std::string toString();
private: private:
friend class NimBLEScan; friend class NimBLEScan;
void parseAdvertisement(uint8_t* payload, uint8_t length); void setAddress(NimBLEAddress address);
void setAddress(NimBLEAddress address); void setAdvType(uint8_t advType);
void setAdvType(uint8_t advType); void setPayload(uint8_t *payload, uint8_t length, bool append);
void setAppearance(uint16_t appearance); void setRSSI(int rssi);
void setManufacturerData(std::string manufacturerData); uint8_t findAdvField(uint8_t type, uint8_t index = 0, uint8_t *data_loc = nullptr);
void setName(std::string name); uint8_t findServiceData(uint8_t index, uint8_t* bytes);
void setRSSI(int rssi);
void setServiceData(NimBLEUUID serviceUUID, std::string data);
void setServiceUUID(const char* serviceUUID);
void setServiceUUID(NimBLEUUID serviceUUID);
void setTXPower(int8_t txPower);
bool m_haveAppearance;
bool m_haveManufacturerData;
bool m_haveName;
bool m_haveRSSI;
bool m_haveServiceData;
bool m_haveServiceUUID;
bool m_haveTXPower;
NimBLEAddress m_address = NimBLEAddress(""); NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType; uint8_t m_advType;
uint16_t m_appearance;
std::string m_manufacturerData;
std::string m_name;
int m_rssi; int m_rssi;
int8_t m_txPower;
uint8_t* m_payload;
size_t m_payloadLength;
time_t m_timestamp; time_t m_timestamp;
bool m_callbackSent; bool m_callbackSent;
uint8_t m_advLength;
std::vector<NimBLEUUID> m_serviceUUIDs; std::vector<uint8_t> m_payload;
std::vector<std::pair<NimBLEUUID, std::string>>m_serviceDataVec;
}; };
/** /**

View File

@ -32,23 +32,29 @@ static const char* LOG_TAG = "NimBLEAdvertising";
/** /**
* @brief Construct a default advertising object. * @brief Construct a default advertising object.
*/ */
NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() { NimBLEAdvertising::NimBLEAdvertising() {
reset();
} // NimBLEAdvertising
/**
* @brief Stops the current advertising and resets the advertising data to the default values.
*/
void NimBLEAdvertising::reset() {
if(NimBLEDevice::getInitialized() && isAdvertising()) {
stop();
}
memset(&m_advData, 0, sizeof m_advData); memset(&m_advData, 0, sizeof m_advData);
memset(&m_scanData, 0, sizeof m_scanData); memset(&m_scanData, 0, sizeof m_scanData);
memset(&m_advParams, 0, sizeof m_advParams); memset(&m_advParams, 0, sizeof m_advParams);
memset(&m_slaveItvl, 0, sizeof m_slaveItvl);
const char *name = ble_svc_gap_device_name(); const char *name = ble_svc_gap_device_name();
m_advData.name = (uint8_t *)name; m_advData.name = (uint8_t *)name;
m_advData.name_len = strlen(name); m_advData.name_len = strlen(name);
m_advData.name_is_complete = 1; m_advData.name_is_complete = 1;
m_advData.tx_pwr_lvl_is_present = 1;
m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); 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) #if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON; m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
@ -56,17 +62,13 @@ NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() {
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
#endif #endif
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
m_advParams.itvl_min = 0;
m_advParams.itvl_max = 0;
m_customAdvData = false; m_customAdvData = false;
m_customScanResponseData = false; m_customScanResponseData = false;
m_scanResp = true; m_scanResp = true;
m_advDataSet = false; m_advDataSet = false;
// Set this to non-zero to prevent auto start if host reset before started by app. // Set this to non-zero to prevent auto start if host reset before started by app.
m_duration = BLE_HS_FOREVER; m_duration = BLE_HS_FOREVER;
} // reset
} // NimBLEAdvertising
/** /**
@ -85,6 +87,7 @@ void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) {
*/ */
void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) { void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
addServiceUUID(NimBLEUUID(serviceUUID)); addServiceUUID(NimBLEUUID(serviceUUID));
m_advDataSet = false;
} // addServiceUUID } // addServiceUUID
@ -112,9 +115,95 @@ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
void NimBLEAdvertising::setAppearance(uint16_t appearance) { void NimBLEAdvertising::setAppearance(uint16_t appearance) {
m_advData.appearance = appearance; m_advData.appearance = appearance;
m_advData.appearance_is_present = 1; m_advData.appearance_is_present = 1;
m_advDataSet = false;
} // setAppearance } // setAppearance
/**
* @brief Add the transmission power level to the advertisement packet.
*/
void NimBLEAdvertising::addTxPower() {
m_advData.tx_pwr_lvl_is_present = 1;
m_advDataSet = false;
} // addTxPower
/**
* @brief Set the advertised name of the device.
* @param [in] name The name to advertise.
*/
void NimBLEAdvertising::setName(const std::string &name) {
m_name.assign(name.begin(), name.end());
m_advData.name = &m_name[0];
m_advData.name_len = m_name.size();
m_advDataSet = false;
} // setName
/**
* @brief Set the advertised manufacturer data.
* @param [in] data The data to advertise.
*/
void NimBLEAdvertising::setManufacturerData(const std::string &data) {
m_mfgData.assign(data.begin(), data.end());
m_advData.mfg_data = &m_mfgData[0];
m_advData.mfg_data_len = m_mfgData.size();
m_advDataSet = false;
} // setManufacturerData
/**
* @brief Set the advertised URI.
* @param [in] uri The URI to advertise.
*/
void NimBLEAdvertising::setURI(const std::string &uri) {
m_uri.assign(uri.begin(), uri.end());
m_advData.uri = &m_uri[0];
m_advData.uri_len = m_uri.size();
m_advDataSet = false;
} // setURI
/**
* @brief Set the service data advertised for the UUID.
* @param [in] uuid The UUID the service data belongs to.
* @param [in] data The data to advertise.
* @note If data length is 0 the service data will not be advertised.
*/
void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
switch (uuid.bitSize()) {
case 16: {
m_svcData16.assign((uint8_t*)&uuid.getNative()->u16.value, (uint8_t*)&uuid.getNative()->u16.value + 2);
m_svcData16.insert(m_svcData16.end(), data.begin(), data.end());
m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0];
m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0;
break;
}
case 32: {
m_svcData32.assign((uint8_t*)&uuid.getNative()->u32.value, (uint8_t*)&uuid.getNative()->u32.value + 4);
m_svcData32.insert(m_svcData32.end(), data.begin(), data.end());
m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0];
m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0;
break;
}
case 128: {
m_svcData128.assign(uuid.getNative()->u128.value, uuid.getNative()->u128.value + 16);
m_svcData128.insert(m_svcData128.end(), data.begin(), data.end());
m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0];
m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0;
break;
}
default:
return;
}
m_advDataSet = false;
} // setServiceData
/** /**
* @brief Set the type of advertisment to use. * @brief Set the type of advertisment to use.
* @param [in] adv_type: * @param [in] adv_type:
@ -172,6 +261,8 @@ void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
m_slaveItvl[2] = m_slaveItvl[0]; m_slaveItvl[2] = m_slaveItvl[0];
m_slaveItvl[3] = m_slaveItvl[1]; m_slaveItvl[3] = m_slaveItvl[1];
} }
m_advDataSet = false;
} // setMinPreferred } // setMinPreferred
@ -200,6 +291,8 @@ void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
m_slaveItvl[0] = m_slaveItvl[2]; m_slaveItvl[0] = m_slaveItvl[2];
m_slaveItvl[1] = m_slaveItvl[3]; m_slaveItvl[1] = m_slaveItvl[3];
} }
m_advDataSet = false;
} // setMaxPreferred } // setMaxPreferred
@ -209,6 +302,7 @@ void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
*/ */
void NimBLEAdvertising::setScanResponse(bool set) { void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set; m_scanResp = set;
m_advDataSet = false;
} // setScanResponse } // setScanResponse
@ -344,12 +438,29 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if (!m_customAdvData && !m_advDataSet) { if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data //start with 3 bytes for the flags data
uint8_t payloadLen = (2 + 1); uint8_t payloadLen = (2 + 1);
if(m_advData.mfg_data_len > 0)
payloadLen += (2 + m_advData.mfg_data_len);
if(m_advData.svc_data_uuid16_len > 0)
payloadLen += (2 + m_advData.svc_data_uuid16_len);
if(m_advData.svc_data_uuid32_len > 0)
payloadLen += (2 + m_advData.svc_data_uuid32_len);
if(m_advData.svc_data_uuid128_len > 0)
payloadLen += (2 + m_advData.svc_data_uuid128_len);
if(m_advData.uri_len > 0)
payloadLen += (2 + m_advData.uri_len);
if(m_advData.appearance_is_present) if(m_advData.appearance_is_present)
payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN); payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN);
if(m_advData.tx_pwr_lvl_is_present) if(m_advData.tx_pwr_lvl_is_present)
payloadLen += (2 + 1); payloadLen += (2 + BLE_HS_ADV_TX_PWR_LVL_LEN);
if(m_advData.slave_itvl_range != nullptr) if(m_advData.slave_itvl_range != nullptr)
payloadLen += (2 + 4); payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
for(auto &it : m_serviceUUIDs) { for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) { if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
@ -419,7 +530,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
// check if there is room for the name, if not put it in scan data // check if there is room for the name, if not put it in scan data
if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) { if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) {
if(m_scanResp){ if(m_scanResp && !m_customScanResponseData){
m_scanData.name = m_advData.name; m_scanData.name = m_advData.name;
m_scanData.name_len = m_advData.name_len; m_scanData.name_len = m_advData.name_len;
if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) { if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) {
@ -433,7 +544,6 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advData.name_is_complete = 0; m_advData.name_is_complete = 0;
} else { } else {
if(m_advData.tx_pwr_lvl_is_present) { if(m_advData.tx_pwr_lvl_is_present) {
m_advData.tx_pwr_lvl = 0;
m_advData.tx_pwr_lvl_is_present = 0; m_advData.tx_pwr_lvl_is_present = 0;
payloadLen -= (2 + 1); payloadLen -= (2 + 1);
} }
@ -446,7 +556,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
} }
} }
if(m_scanResp) { if(m_scanResp && !m_customScanResponseData) {
rc = ble_gap_adv_rsp_set_fields(&m_scanData); rc = ble_gap_adv_rsp_set_fields(&m_scanData);
switch(rc) { switch(rc) {
case 0: case 0:
@ -581,7 +691,7 @@ void NimBLEAdvertising::advCompleteCB() {
if(m_advCompCB != nullptr) { if(m_advCompCB != nullptr) {
m_advCompCB(this); m_advCompCB(this);
} }
} } // advCompleteCB
/** /**
@ -590,7 +700,7 @@ void NimBLEAdvertising::advCompleteCB() {
*/ */
bool NimBLEAdvertising::isAdvertising() { bool NimBLEAdvertising::isAdvertising() {
return ble_gap_adv_active(); return ble_gap_adv_active();
} } // isAdvertising
/* /*
@ -608,7 +718,7 @@ void NimBLEAdvertising::onHostSync() {
// Otherwise we should tell the app that advertising stopped. // Otherwise we should tell the app that advertising stopped.
advCompleteCB(); advCompleteCB();
} }
} } // onHostSync
/** /**
@ -646,6 +756,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
*/ */
void NimBLEAdvertisementData::addData(const std::string &data) { void NimBLEAdvertisementData::addData(const std::string &data) {
if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) { if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceded");
return; return;
} }
m_payload.append(data); m_payload.append(data);
@ -657,11 +768,8 @@ void NimBLEAdvertisementData::addData(const std::string &data) {
* @param [in] data The data to be added to the payload. * @param [in] data The data to be added to the payload.
* @param [in] length The size of data to be added to the payload. * @param [in] length The size of data to be added to the payload.
*/ */
void NimBLEAdvertisementData::addData(char * data, size_t length){ void NimBLEAdvertisementData::addData(char * data, size_t length) {
if ((m_payload.length() + length) > BLE_HS_ADV_MAX_SZ) { addData(std::string(data, length));
return;
}
m_payload.append(data,length);
} // addData } // addData
@ -680,43 +788,6 @@ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
} // setAppearance } // setAppearance
/**
* @brief Set the complete services to advertise.
* @param [in] uuid The UUID of the service.
*/
void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x02] [LL] [HH]
cdata[0] = 3;
cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS16; // 0x03
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2));
break;
}
case 32: {
// [Len] [0x04] [LL] [LL] [HH] [HH]
cdata[0] = 5;
cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS32; // 0x05
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4));
break;
}
case 128: {
// [Len] [0x04] [0] [1] ... [15]
cdata[0] = 17;
cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS128; // 0x07
addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->u128.value, 16));
break;
}
default:
return;
}
} // setCompleteServices
/** /**
* @brief Set the advertisement flags. * @brief Set the advertisement flags.
* @param [in] flag The flags to be set in the advertisement. * @param [in] flag The flags to be set in the advertisement.
@ -738,64 +809,141 @@ void NimBLEAdvertisementData::setFlags(uint8_t flag) {
* @param [in] data The manufacturer data to advertise. * @param [in] data The manufacturer data to advertise.
*/ */
void NimBLEAdvertisementData::setManufacturerData(const std::string &data) { void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setManufacturerData");
char cdata[2]; char cdata[2];
cdata[0] = data.length() + 1; cdata[0] = data.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
addData(std::string(cdata, 2) + data); addData(std::string(cdata, 2) + data);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< setManufacturerData");
} // setManufacturerData } // setManufacturerData
/**
* @brief Set the URI to advertise.
* @param [in] uri The uri to advertise.
*/
void NimBLEAdvertisementData::setURI(const std::string &uri) {
char cdata[2];
cdata[0] = uri.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_URI;
addData(std::string(cdata, 2) + uri);
} // setURI
/** /**
* @brief Set the complete name of this device. * @brief Set the complete name of this device.
* @param [in] name The name to advertise. * @param [in] name The name to advertise.
*/ */
void NimBLEAdvertisementData::setName(const std::string &name) { void NimBLEAdvertisementData::setName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str());
char cdata[2]; char cdata[2];
cdata[0] = name.length() + 1; cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09 cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09
addData(std::string(cdata, 2) + name); addData(std::string(cdata, 2) + name);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< setName");
} // setName } // setName
/** /**
* @brief Set the partial services to advertise. * @brief Set a single service to advertise as a complete list of services.
* @param [in] uuid The single service to advertise. * @param [in] uuid The service to advertise.
*/
void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
setServices(true, uuid.bitSize(), {uuid});
} // setCompleteServices
/**
* @brief Set the complete list of 16 bit services to advertise.
* @param [in] v_uuid A vector of 16 bit UUID's to advertise.
*/
void NimBLEAdvertisementData::setCompleteServices16(const std::vector<NimBLEUUID>& v_uuid) {
setServices(true, 16, v_uuid);
} // setCompleteServices16
/**
* @brief Set the complete list of 32 bit services to advertise.
* @param [in] v_uuid A vector of 32 bit UUID's to advertise.
*/
void NimBLEAdvertisementData::setCompleteServices32(const std::vector<NimBLEUUID>& v_uuid) {
setServices(true, 32, v_uuid);
} // setCompleteServices32
/**
* @brief Set a single service to advertise as a partial list of services.
* @param [in] uuid The service to advertise.
*/ */
void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) { void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) {
setServices(false, uuid.bitSize(), {uuid});
} // setPartialServices
/**
* @brief Set the partial list of services to advertise.
* @param [in] v_uuid A vector of 16 bit UUID's to advertise.
*/
void NimBLEAdvertisementData::setPartialServices16(const std::vector<NimBLEUUID>& v_uuid) {
setServices(false, 16, v_uuid);
} // setPartialServices16
/**
* @brief Set the partial list of services to advertise.
* @param [in] v_uuid A vector of 32 bit UUID's to advertise.
*/
void NimBLEAdvertisementData::setPartialServices32(const std::vector<NimBLEUUID>& v_uuid) {
setServices(false, 32, v_uuid);
} // setPartialServices32
/**
* @brief Utility function to create the list of service UUID's from a vector.
* @param [in] complete If true the vector is the complete set of services.
* @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128).
* @param [in] v_uuid The vector of service UUID's to advertise.
*/
void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid)
{
char cdata[2]; char cdata[2];
switch (uuid.bitSize()) { cdata[0] = (size / 8) * v_uuid.size() + 1;
case 16: { switch(size) {
// [Len] [0x02] [LL] [HH] case 16:
cdata[0] = 3; cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; // 0x02
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u16.value, 2));
break; break;
} case 32:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
case 32: {
// [Len] [0x04] [LL] [LL] [HH] [HH]
cdata[0] = 5;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS32; // 0x04
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u32.value, 4));
break; break;
} case 128:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
case 128: {
// [Len] [0x04] [0] [1] ... [15]
cdata[0] = 17;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS128; // 0x06
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u128.value, 16));
break; break;
}
default: default:
return; return;
} }
} // setPartialServices
std::string uuids;
for(auto &it : v_uuid){
if(it.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
return;
} else {
switch(size) {
case 16:
uuids += std::string((char*)&it.getNative()->u16.value, 2);
break;
case 32:
uuids += std::string((char*)&it.getNative()->u32.value, 4);
break;
case 128:
uuids += std::string((char*)&it.getNative()->u128.value, 16);
break;
default:
return;
}
}
}
addData(std::string(cdata, 2) + uuids);
} // setServices
/** /**
@ -841,15 +989,42 @@ void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::
* @param [in] name The short name of the device. * @param [in] name The short name of the device.
*/ */
void NimBLEAdvertisementData::setShortName(const std::string &name) { void NimBLEAdvertisementData::setShortName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str());
char cdata[2]; char cdata[2];
cdata[0] = name.length() + 1; cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08 cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08
addData(std::string(cdata, 2) + name); addData(std::string(cdata, 2) + name);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< setShortName");
} // setShortName } // setShortName
/**
* @brief Adds Tx power level to the advertisement data.
*/
void NimBLEAdvertisementData::addTxPower() {
char cdata[3];
cdata[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1;
cdata[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL;
cdata[2] = NimBLEDevice::getPower();
addData(cdata, 3);
} // addTxPower
/**
* @brief Set the preferred connection interval parameters.
* @param [in] min The minimum interval desired.
* @param [in] max The maximum interval desired.
*/
void NimBLEAdvertisementData::setPreferredParams(uint16_t min, uint16_t max) {
char cdata[6];
cdata[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1;
cdata[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE;
cdata[2] = min;
cdata[3] = min >> 8;
cdata[4] = max;
cdata[5] = max >> 8;
addData(cdata, 6);
} // setPreferredParams
/** /**
* @brief Retrieve the payload that is to be advertised. * @brief Retrieve the payload that is to be advertised.
* @return The payload that is to be advertised. * @return The payload that is to be advertised.

View File

@ -50,18 +50,27 @@ class NimBLEAdvertisementData {
public: public:
void setAppearance(uint16_t appearance); void setAppearance(uint16_t appearance);
void setCompleteServices(const NimBLEUUID &uuid); void setCompleteServices(const NimBLEUUID &uuid);
void setCompleteServices16(const std::vector<NimBLEUUID> &v_uuid);
void setCompleteServices32(const std::vector<NimBLEUUID> &v_uuid);
void setFlags(uint8_t); void setFlags(uint8_t);
void setManufacturerData(const std::string &data); void setManufacturerData(const std::string &data);
void setURI(const std::string &uri);
void setName(const std::string &name); void setName(const std::string &name);
void setPartialServices(const NimBLEUUID &uuid); void setPartialServices(const NimBLEUUID &uuid);
void setPartialServices16(const std::vector<NimBLEUUID> &v_uuid);
void setPartialServices32(const std::vector<NimBLEUUID> &v_uuid);
void setServiceData(const NimBLEUUID &uuid, const std::string &data); void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setShortName(const std::string &name); void setShortName(const std::string &name);
void addData(const std::string &data); // Add data to the payload. void addData(const std::string &data); // Add data to the payload.
void addData(char * data, size_t length); void addData(char * data, size_t length);
void addTxPower();
void setPreferredParams(uint16_t min, uint16_t max);
std::string getPayload(); // Retrieve the current advert payload. std::string getPayload(); // Retrieve the current advert payload.
private: private:
friend class NimBLEAdvertising; friend class NimBLEAdvertising;
void setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid);
std::string m_payload; // The payload of the advertisement. std::string m_payload; // The payload of the advertisement.
}; // NimBLEAdvertisementData }; // NimBLEAdvertisementData
@ -80,6 +89,10 @@ public:
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr); bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop(); void stop();
void setAppearance(uint16_t appearance); void setAppearance(uint16_t appearance);
void setName(const std::string &name);
void setManufacturerData(const std::string &data);
void setURI(const std::string &uri);
void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setAdvertisementType(uint8_t adv_type); void setAdvertisementType(uint8_t adv_type);
void setMaxInterval(uint16_t maxinterval); void setMaxInterval(uint16_t maxinterval);
void setMinInterval(uint16_t mininterval); void setMinInterval(uint16_t mininterval);
@ -89,6 +102,8 @@ public:
void setScanResponse(bool); void setScanResponse(bool);
void setMinPreferred(uint16_t); void setMinPreferred(uint16_t);
void setMaxPreferred(uint16_t); void setMaxPreferred(uint16_t);
void addTxPower();
void reset();
void advCompleteCB(); void advCompleteCB();
bool isAdvertising(); bool isAdvertising();
@ -109,6 +124,12 @@ private:
void (*m_advCompCB)(NimBLEAdvertising *pAdv); void (*m_advCompCB)(NimBLEAdvertising *pAdv);
uint8_t m_slaveItvl[4]; uint8_t m_slaveItvl[4];
uint32_t m_duration; uint32_t m_duration;
std::vector<uint8_t> m_svcData16;
std::vector<uint8_t> m_svcData32;
std::vector<uint8_t> m_svcData128;
std::vector<uint8_t> m_name;
std::vector<uint8_t> m_mfgData;
std::vector<uint8_t> m_uri;
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View File

@ -101,9 +101,9 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
/** /**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor that we wish to retrieve. * @param [in] uuid The UUID of the descriptor.
* @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned. * @return A pointer to the descriptor object or nullptr if not found.
*/ */
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return getDescriptorByUUID(NimBLEUUID(uuid)); return getDescriptorByUUID(NimBLEUUID(uuid));
@ -111,9 +111,9 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
/** /**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor that we wish to retrieve. * @param [in] uuid The UUID of the descriptor.
* @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned. * @return A pointer to the descriptor object or nullptr if not found.
*/ */
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
for (auto &it : m_dscVec) { for (auto &it : m_dscVec) {
@ -124,6 +124,20 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu
return nullptr; return nullptr;
} // getDescriptorByUUID } // getDescriptorByUUID
/**
* @brief Return the BLE Descriptor for the given handle.
* @param [in] handle The handle of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) {
for (auto &it : m_dscVec) {
if (it->getHandle() == handle) {
return it;
}
}
return nullptr;
}
/** /**
* @brief Get the handle of the characteristic. * @brief Get the handle of the characteristic.

View File

@ -59,6 +59,17 @@ class NimBLECharacteristicCallbacks;
*/ */
class NimBLECharacteristic { class NimBLECharacteristic {
public: public:
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
void indicate();
void notify(bool is_notification = true);
size_t getSubscribedCount();
NimBLEDescriptor* createDescriptor(const char* uuid, NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties = uint32_t properties =
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ |
@ -72,9 +83,10 @@ public:
NimBLEDescriptor* getDescriptorByUUID(const char* uuid); NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid); NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEUUID getUUID(); NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
std::string getValue(time_t *timestamp = nullptr);
std::string getValue(time_t *timestamp = nullptr);
size_t getDataLength();
/** /**
* @brief A template to convert the characteristic data to <type\>. * @brief A template to convert the characteristic data to <type\>.
* @tparam T The type to convert the data to. * @tparam T The type to convert the data to.
@ -92,25 +104,20 @@ public:
return *((T *)pData); return *((T *)pData);
} }
size_t getDataLength();
void indicate();
void notify(bool is_notification = true);
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
void setValue(const uint8_t* data, size_t size); void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value); void setValue(const std::string &value);
/** /**
* @brief Convenience template to set the characteristic value to <type\>val. * @brief Convenience template to set the characteristic value to <type\>val.
* @param [in] s The value to set. * @param [in] s The value to set.
*/ */
template<typename T> template<typename T>
void setValue(const T &s) { void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T)); setValue((uint8_t*)&s, sizeof(T));
} }
std::string toString();
uint16_t getHandle();
size_t getSubscribedCount();
private: private:

View File

@ -122,11 +122,17 @@ uint8_t* NimBLEDescriptor::getValue() {
return m_value.attr_value; return m_value.attr_value;
} // getValue } // getValue
/**
* @brief Get the value of this descriptor as a string.
* @return A std::string instance containing a copy of the descriptor's value.
*/
std::string NimBLEDescriptor::getStringValue() {
return std::string((char *) m_value.attr_value, m_value.attr_len);
}
int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, struct ble_gatt_access_ctxt *ctxt,
void *arg) void *arg) {
{
const ble_uuid_t *uuid; const ble_uuid_t *uuid;
int rc; int rc;
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg; NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;

View File

@ -44,20 +44,23 @@ class NimBLEDescriptorCallbacks;
class NimBLEDescriptor { class NimBLEDescriptor {
public: public:
uint16_t getHandle(); uint16_t getHandle();
size_t getLength();
NimBLEUUID getUUID(); NimBLEUUID getUUID();
uint8_t* getValue();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
std::string toString(); std::string toString();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
size_t getLength();
uint8_t* getValue();
std::string getStringValue();
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
/** /**
* @brief Convenience template to set the descriptor value to <type\>val. * @brief Convenience template to set the descriptor value to <type\>val.
* @param [in] s The value to set. * @param [in] s The value to set.
*/ */
template<typename T> template<typename T>
void setValue(const T &s) { void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T)); setValue((uint8_t*)&s, sizeof(T));
} }

View File

@ -62,6 +62,8 @@ std::list <NimBLEClient*> NimBLEDevice::m_cList;
std::list <NimBLEAddress> NimBLEDevice::m_ignoreList; std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC; uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
uint16_t NimBLEDevice::m_scanDuplicateSize = CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE;
uint8_t NimBLEDevice::m_scanFilterMode = CONFIG_BTDM_SCAN_DUPL_TYPE;
/** /**
@ -303,7 +305,7 @@ void NimBLEDevice::stopAdvertising() {
/** /**
* @brief Set the transmission power. * @brief Get the transmission power.
* @param [in] powerType The power level to set, can be one of: * @param [in] powerType The power level to set, can be one of:
* * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0 * * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0
* * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1 * * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1
@ -342,7 +344,7 @@ void NimBLEDevice::stopAdvertising() {
default: default:
return BLE_HS_ADV_TX_PWR_LVL_AUTO; return BLE_HS_ADV_TX_PWR_LVL_AUTO;
} }
} // setPower } // getPower
/** /**
@ -400,6 +402,53 @@ void NimBLEDevice::stopAdvertising() {
} }
/**
* @brief Set the duplicate filter cache size for filtering scanned devices.
* @param [in] cacheSize The number of advertisements filtered before the cache is reset.\n
* Range is 10-1000, a larger value will reduce how often the same devices are reported.
* @details Must only be called before calling NimBLEDevice::init.
*/
/*STATIC*/
void NimBLEDevice::setScanDuplicateCacheSize(uint16_t cacheSize) {
if(initialized) {
NIMBLE_LOGE(LOG_TAG, "Cannot change scan cache size while initialized");
return;
} else if(cacheSize > 1000 || cacheSize <10) {
NIMBLE_LOGE(LOG_TAG, "Invalid scan cache size; min=10 max=1000");
return;
}
m_scanDuplicateSize = cacheSize;
}
/**
* @brief Set the duplicate filter mode for filtering scanned devices.
* @param [in] mode One of three possible options:
* * CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE (0) (default)\n
Filter by device address only, advertisements from the same address will be reported only once.
* * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA (1)\n
Filter by data only, advertisements with the same data will only be reported once,\n
even from different addresses.
* * CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE (2)\n
Filter by address and data, advertisements from the same address will be reported only once,\n
except if the data in the advertisement has changed, then it will be reported again.
* @details Must only be called before calling NimBLEDevice::init.
*/
/*STATIC*/
void NimBLEDevice::setScanFilterMode(uint8_t mode) {
if(initialized) {
NIMBLE_LOGE(LOG_TAG, "Cannot change scan duplicate type while initialized");
return;
} else if(mode > 2) {
NIMBLE_LOGE(LOG_TAG, "Invalid scan duplicate type");
return;
}
m_scanFilterMode = mode;
}
/** /**
* @brief Host reset, we pass the message so we don't make calls until resynced. * @brief Host reset, we pass the message so we don't make calls until resynced.
* @param [in] reason The reason code for the reset. * @param [in] reason The reason code for the reset.
@ -499,8 +548,17 @@ void NimBLEDevice::stopAdvertising() {
ESP_ERROR_CHECK(errRc); ESP_ERROR_CHECK(errRc);
ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init()); esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
bt_cfg.mode = ESP_BT_MODE_BLE;
bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
bt_cfg.normal_adv_size = m_scanDuplicateSize;
bt_cfg.scan_duplicate_type = m_scanFilterMode;
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
ESP_ERROR_CHECK(esp_nimble_hci_init());
nimble_port_init(); nimble_port_init();
// Setup callbacks for host events // Setup callbacks for host events

View File

@ -123,6 +123,8 @@ public:
static bool isIgnored(const NimBLEAddress &address); static bool isIgnored(const NimBLEAddress &address);
static void addIgnored(const NimBLEAddress &address); static void addIgnored(const NimBLEAddress &address);
static void removeIgnored(const NimBLEAddress &address); static void removeIgnored(const NimBLEAddress &address);
static void setScanDuplicateCacheSize(uint16_t cacheSize);
static void setScanFilterMode(uint8_t type);
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
static NimBLEAdvertising* getAdvertising(); static NimBLEAdvertising* getAdvertising();
@ -184,6 +186,8 @@ private:
static ble_gap_event_listener m_listener; static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler; static gap_event_handler m_customGapHandler;
static uint8_t m_own_addr_type; static uint8_t m_own_addr_type;
static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode;
}; };

View File

@ -103,7 +103,7 @@ void NimBLEHIDDevice::manufacturer(std::string name) {
/** /**
* @brief Sets the Plug n Play characterisc value. * @brief Sets the Plug n Play characterisc value.
* @param [in] sig The vendor ID source number. * @param [in] sig The vendor ID source number.
* @param [in[ vid The vendor ID number. * @param [in] vid The vendor ID number.
* @param [in] pid The product ID number. * @param [in] pid The product ID number.
* @param [in] version The produce version number. * @param [in] version The produce version number.
*/ */
@ -128,8 +128,8 @@ void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
* @return pointer to new input report characteristic * @return pointer to new input report characteristic
*/ */
NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) { NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY); NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908); NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
uint8_t desc1_val[] = { reportID, 0x01 }; uint8_t desc1_val[] = { reportID, 0x01 };
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2); inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
@ -144,7 +144,7 @@ NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
*/ */
NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) { 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); 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); NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = { reportID, 0x02 }; uint8_t desc1_val[] = { reportID, 0x02 };
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2); outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
@ -159,7 +159,7 @@ NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
*/ */
NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) { 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); 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); NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
uint8_t desc1_val[] = { reportID, 0x03 }; uint8_t desc1_val[] = { reportID, 0x03 };
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2); featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);

View File

@ -38,9 +38,9 @@ NimBLEScan::NimBLEScan() {
m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device. m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device.
m_pAdvertisedDeviceCallbacks = nullptr; m_pAdvertisedDeviceCallbacks = nullptr;
m_ignoreResults = false; m_ignoreResults = false;
m_wantDuplicates = false;
m_pTaskData = nullptr; m_pTaskData = nullptr;
m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset
m_maxResults = 0xFF;
} }
@ -64,7 +64,7 @@ NimBLEScan::~NimBLEScan() {
case BLE_GAP_EVENT_DISC: { case BLE_GAP_EVENT_DISC: {
if(pScan->m_ignoreResults) { if(pScan->m_ignoreResults) {
NIMBLE_LOGE(LOG_TAG, "Scan op in progress - ignoring results"); NIMBLE_LOGI(LOG_TAG, "Scan op in progress - ignoring results");
return 0; return 0;
} }
@ -88,34 +88,49 @@ NimBLEScan::~NimBLEScan() {
// If we haven't seen this device before; create a new instance and insert it in the vector. // If we haven't seen this device before; create a new instance and insert it in the vector.
// Otherwise just update the relevant parameters of the already known device. // Otherwise just update the relevant parameters of the already known device.
if(advertisedDevice == nullptr){ if(advertisedDevice == nullptr && event->disc.event_type != BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP){
// Check if we have reach the scan results limit, ignore this one if so.
// We still need to store each device when maxResults is 0 to be able to append the scan results
if(pScan->m_maxResults > 0 && pScan->m_maxResults < 0xFF &&
(pScan->m_scanResults.m_advertisedDevicesVector.size() >= pScan->m_maxResults))
{
return 0;
}
advertisedDevice = new NimBLEAdvertisedDevice(); advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress); advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event->disc.event_type); advertisedDevice->setAdvType(event->disc.event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice); pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
NIMBLE_LOGI(LOG_TAG, "NEW DEVICE FOUND: %s", advertisedAddress.toString().c_str()); NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
} } else if(advertisedDevice != nullptr) {
else{ NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str());
NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str()); } else {
} // Scan response from unknown device
advertisedDevice->setRSSI(event->disc.rssi); return 0;
if(event->disc.length_data > 0) {
advertisedDevice->parseAdvertisement(event->disc.data, event->disc.length_data);
} }
advertisedDevice->m_timestamp = time(nullptr); advertisedDevice->m_timestamp = time(nullptr);
advertisedDevice->setRSSI(event->disc.rssi);
advertisedDevice->setPayload(event->disc.data, event->disc.length_data,
event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP);
if (pScan->m_pAdvertisedDeviceCallbacks) { if (pScan->m_pAdvertisedDeviceCallbacks) {
if(pScan->m_wantDuplicates || !advertisedDevice->m_callbackSent) { // If not active scanning or scan response is not available
// If not active scanning report the result to the listener. // report the result to the callback now.
if(pScan->m_scan_params.passive || event->disc.event_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) { if(pScan->m_scan_params.passive ||
advertisedDevice->m_callbackSent = true; (advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND &&
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_SCAN_IND))
{
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
// Otherwise wait for the scan response so we can report all of the data at once. // Otherwise, wait for the scan response so we can report the complete data.
} else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true; advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
} }
// If not storing results and we have invoked the callback, delete the device.
if(pScan->m_maxResults == 0 && advertisedDevice->m_callbackSent) {
pScan->erase(advertisedAddress);
} }
} }
@ -123,7 +138,21 @@ NimBLEScan::~NimBLEScan() {
} }
case BLE_GAP_EVENT_DISC_COMPLETE: { case BLE_GAP_EVENT_DISC_COMPLETE: {
NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d",
event->disc_complete.reason); event->disc_complete.reason);
// If a device advertised with scan reponse available and it was not received
// the callback would not have been invoked, so do it here.
if(pScan->m_pAdvertisedDeviceCallbacks) {
for(auto &it : pScan->m_scanResults.m_advertisedDevicesVector) {
if(!it->m_callbackSent) {
pScan->m_pAdvertisedDeviceCallbacks->onResult(it);
}
}
}
if(pScan->m_maxResults == 0) {
pScan->clearResults();
}
if (pScan->m_scanCompleteCB != nullptr) { if (pScan->m_scanCompleteCB != nullptr) {
pScan->m_scanCompleteCB(pScan->m_scanResults); pScan->m_scanCompleteCB(pScan->m_scanResults);
@ -145,15 +174,11 @@ NimBLEScan::~NimBLEScan() {
/** /**
* @brief Should we perform an active or passive scan? * @brief Should we perform an active or passive scan?
* The default is a passive scan. An active scan means that we will wish a scan response. * The default is a passive scan. An active scan means that we will request a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan. * @param [in] active If true, we perform an active scan otherwise a passive scan.
*/ */
void NimBLEScan::setActiveScan(bool active) { void NimBLEScan::setActiveScan(bool active) {
if (active) { m_scan_params.passive = !active;
m_scan_params.passive = 0;
} else {
m_scan_params.passive = 1;
}
} // setActiveScan } // setActiveScan
@ -202,6 +227,16 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
} // setFilterPolicy } // setFilterPolicy
/**
* @brief Sets the max number of results to store.
* @param [in] maxResults The number of results to limit storage to\n
* 0 == none (callbacks only) 0xFF == unlimited, any other value is the limit.
*/
void NimBLEScan::setMaxResults(uint8_t maxResults) {
m_maxResults = maxResults;
}
/** /**
* @brief Set the call backs to be invoked. * @brief Set the call backs to be invoked.
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
@ -209,7 +244,7 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
*/ */
void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
bool wantDuplicates) { bool wantDuplicates) {
m_wantDuplicates = wantDuplicates; setDuplicateFilter(!wantDuplicates);
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
} // setAdvertisedDeviceCallbacks } // setAdvertisedDeviceCallbacks
@ -342,10 +377,14 @@ bool NimBLEScan::stop() {
int rc = ble_gap_disc_cancel(); int rc = ble_gap_disc_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) { if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d\n", rc); NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d", rc);
return false; return false;
} }
if(m_maxResults == 0) {
clearResults();
}
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) { if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults); m_scanCompleteCB(m_scanResults);
} }
@ -365,7 +404,7 @@ bool NimBLEScan::stop() {
* @details After disconnecting, it may be required in the case we were connected to a device without a public address. * @details After disconnecting, it may be required in the case we were connected to a device without a public address.
*/ */
void NimBLEScan::erase(const NimBLEAddress &address) { void NimBLEScan::erase(const NimBLEAddress &address) {
NIMBLE_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str()); NIMBLE_LOGD(LOG_TAG, "erase device: %s", address.toString().c_str());
for(auto it = m_scanResults.m_advertisedDevicesVector.begin(); it != m_scanResults.m_advertisedDevicesVector.end(); ++it) { for(auto it = m_scanResults.m_advertisedDevicesVector.begin(); it != m_scanResults.m_advertisedDevicesVector.end(); ++it) {
if((*it)->getAddress() == address) { if((*it)->getAddress() == address) {

View File

@ -73,6 +73,7 @@ public:
bool stop(); bool stop();
void clearResults(); void clearResults();
NimBLEScanResults getResults(); NimBLEScanResults getResults();
void setMaxResults(uint8_t maxResults);
void erase(const NimBLEAddress &address); void erase(const NimBLEAddress &address);
@ -89,10 +90,10 @@ private:
void (*m_scanCompleteCB)(NimBLEScanResults scanResults); void (*m_scanCompleteCB)(NimBLEScanResults scanResults);
ble_gap_disc_params m_scan_params; ble_gap_disc_params m_scan_params;
bool m_ignoreResults; bool m_ignoreResults;
bool m_wantDuplicates;
NimBLEScanResults m_scanResults; NimBLEScanResults m_scanResults;
uint32_t m_duration; uint32_t m_duration;
ble_task_data_t *m_pTaskData; ble_task_data_t *m_pTaskData;
uint8_t m_maxResults;
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)

View File

@ -104,28 +104,47 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
/** /**
* @brief Get a %BLE Service by its UUID * @brief Get a %BLE Service by its UUID
* @param [in] uuid The UUID of the new service. * @param [in] uuid The UUID of the service.
* @return A reference to the service object. * @param instanceId The index of the service to return (used when multiple services have the same UUID).
* @return A pointer to the service object or nullptr if not found.
*/ */
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) { NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) {
return getServiceByUUID(NimBLEUUID(uuid)); return getServiceByUUID(NimBLEUUID(uuid), instanceId);
} // getServiceByUUID } // getServiceByUUID
/** /**
* @brief Get a %BLE Service by its UUID * @brief Get a %BLE Service by its UUID
* @param [in] uuid The UUID of the new service. * @param [in] uuid The UUID of the service.
* @return A reference to the service object. * @param instanceId The index of the service to return (used when multiple services have the same UUID).
* @return A pointer to the service object or nullptr if not found.
*/ */
NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) { NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId) {
uint16_t position = 0;
for (auto &it : m_svcVec) { for (auto &it : m_svcVec) {
if (it->getUUID() == uuid) { if (it->getUUID() == uuid) {
return it; if (position == instanceId){
return it;
}
position++;
} }
} }
return nullptr; return nullptr;
} // getServiceByUUID } // getServiceByUUID
/**
* @brief Get a %BLE Service by its handle
* @param handle The handle of the service.
* @return A pointer to the service object or nullptr if not found.
*/
NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) {
for (auto &it : m_svcVec) {
if (it->getHandle() == handle) {
return it;
}
}
return nullptr;
}
/** /**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server. * @brief Retrieve the advertising object that can be used to advertise the existence of the server.
@ -646,7 +665,7 @@ void NimBLEServer::updateConnParams(uint16_t conn_handle,
if(rc != 0) { if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
} }
} // updateConnParams }// updateConnParams
/** Default callback handlers */ /** Default callback handlers */

View File

@ -49,8 +49,9 @@ public:
void startAdvertising(); void startAdvertising();
void stopAdvertising(); void stopAdvertising();
void start(); void start();
NimBLEService* getServiceByUUID(const char* uuid); NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid); NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByHandle(uint16_t handle);
int disconnect(uint16_t connID, int disconnect(uint16_t connID,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle, void updateConnParams(uint16_t conn_handle,

View File

@ -233,9 +233,9 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
*/ */
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) { NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this); NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
// Check that we don't add the same characteristic twice.
if (getCharacteristic(uuid) != nullptr) { if (getCharacteristic(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s", NIMBLE_LOGD(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
std::string(uuid).c_str()); std::string(uuid).c_str());
} }
@ -249,28 +249,72 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid
/** /**
* @brief Get a pointer to the characteristic object with the specified UUID. * @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic. * @param [in] uuid The UUID of the characteristic.
* @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found. * @return A pointer to the characteristic object or nullptr if not found.
*/ */
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) { NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) {
return getCharacteristic(NimBLEUUID(uuid)); return getCharacteristic(NimBLEUUID(uuid), instanceId);
} }
/** /**
* @brief Get a pointer to the characteristic object with the specified UUID. * @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic. * @param [in] uuid The UUID of the characteristic.
* @param instanceId The index of the characteristic to return (used when multiple characteristics have the same UUID).
* @return A pointer to the characteristic object or nullptr if not found. * @return A pointer to the characteristic object or nullptr if not found.
*/ */
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) { NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId) {
uint16_t position = 0;
for (auto &it : m_chrVec) { for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) { if (it->getUUID() == uuid) {
return it; if (position == instanceId) {
return it;
}
position++;
} }
} }
return nullptr; return nullptr;
} }
/**
* @brief Get a pointer to the characteristic object with the specified handle.
* @param handle The handle of the characteristic.
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic *NimBLEService::getCharacteristicByHandle(uint16_t handle) {
for (auto &it : m_chrVec) {
if (it->getHandle() == handle) {
return it;
}
}
return nullptr;
}
/**
* @return A vector containing pointers to each characteristic associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics() {
return m_chrVec;
}
/**
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics(const char *uuid) {
return getCharacteristics(NimBLEUUID(uuid));
}
/**
* @return A vector containing pointers to each characteristic with the provided UUID associated with this service.
*/
std::vector<NimBLECharacteristic *> NimBLEService::getCharacteristics(const NimBLEUUID &uuid) {
std::vector<NimBLECharacteristic*> result;
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
result.push_back(it);
}
}
return result;
}
/** /**
* @brief Return a string representation of this service. * @brief Return a string representation of this service.
@ -295,7 +339,7 @@ std::string NimBLEService::toString() {
*/ */
NimBLEServer* NimBLEService::getServer() { NimBLEServer* NimBLEService::getServer() {
return m_pServer; return m_pServer;
} // getServer }// getServer
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED #endif // CONFIG_BT_ENABLED

View File

@ -35,6 +35,16 @@ class NimBLECharacteristic;
*/ */
class NimBLEService { class NimBLEService {
public: public:
NimBLEServer* getServer();
NimBLEUUID getUUID();
uint16_t getHandle();
std::string toString();
void dump();
bool start();
NimBLECharacteristic* createCharacteristic(const char* uuid, NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties = uint32_t properties =
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ |
@ -45,14 +55,14 @@ public:
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE); NIMBLE_PROPERTY::WRITE);
void dump(); NimBLECharacteristic* getCharacteristic(const char* uuid, uint16_t instanceId = 0);
NimBLECharacteristic* getCharacteristic(const char* uuid); NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId = 0);
NimBLECharacteristic* getCharacteristic(const NimBLEUUID &uuid); NimBLECharacteristic* getCharacteristicByHandle(uint16_t handle);
NimBLEUUID getUUID();
NimBLEServer* getServer(); std::vector<NimBLECharacteristic*> getCharacteristics();
bool start(); std::vector<NimBLECharacteristic*> getCharacteristics(const char* uuid);
std::string toString(); std::vector<NimBLECharacteristic*> getCharacteristics(const NimBLEUUID &uuid);
uint16_t getHandle();
private: private:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer); NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);

View File

@ -65,29 +65,42 @@ static const char* LOG_TAG = "NimBLEUUID";
*this = NimBLEUUID(first, second, third, (uint64_t(fourth) << 48) + fifth); *this = NimBLEUUID(first, second, third, (uint64_t(fourth) << 48) + fifth);
} }
else { else {
NIMBLE_LOGE(LOG_TAG,"ERROR: UUID value not 2, 4, 16 or 36 bytes");
m_valueSet = false; m_valueSet = false;
} }
} // NimBLEUUID(std::string) } // NimBLEUUID(std::string)
/** /**
* @brief Create a UUID from 16 bytes of memory. * @brief Create a UUID from 2, 4, 16 bytes of memory.
* @param [in] pData The pointer to the start of the UUID. * @param [in] pData The pointer to the start of the UUID.
* @param [in] size The size of the data. * @param [in] size The size of the data.
* @param [in] msbFirst Is the MSB first in pData memory? * @param [in] msbFirst Is the MSB first in pData memory?
*/ */
NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) { NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
if (size != 16) { uint8_t *uuidValue = nullptr;
NIMBLE_LOGE(LOG_TAG,"ERROR: UUID length not 16 bytes");
return;
}
m_uuid.u.type = BLE_UUID_TYPE_128;
switch(size) {
case 2:
uuidValue = (uint8_t*)&m_uuid.u16.value;
m_uuid.u.type = BLE_UUID_TYPE_16;
break;
case 4:
uuidValue = (uint8_t*)&m_uuid.u32.value;
m_uuid.u.type = BLE_UUID_TYPE_32;
break;
case 16:
uuidValue = m_uuid.u128.value;
m_uuid.u.type = BLE_UUID_TYPE_128;
break;
default:
m_valueSet = false;
NIMBLE_LOGE(LOG_TAG, "Invalid UUID size");
return;
}
if (msbFirst) { if (msbFirst) {
std::reverse_copy(pData, pData + 16, m_uuid.u128.value); std::reverse_copy(pData, pData + size, uuidValue);
} else { } else {
memcpy(m_uuid.u128.value, pData, 16); memcpy(uuidValue, pData, size);
} }
m_valueSet = true; m_valueSet = true;
} // NimBLEUUID } // NimBLEUUID

View File

@ -523,10 +523,6 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); 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) { if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
return ret; return ret;

View File

@ -1,145 +1,156 @@
/** @file #pragma once
*
* IGNORE THIS FILE IF USING ESP-IDF, USE MENUCONFIG TO SET NIMBLE OPTIONS.
*
* The config options here are for Arduino use only.
*/
#pragma once
#include "sdkconfig.h" #include "sdkconfig.h"
#include "nimconfig_rename.h"
/*
* For ESP-IDF compatibility
* Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
* 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)
*
* 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
#endif
#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif
#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif
#if defined(CONFIG_NIMBLE_DEBUG) && !defined(CONFIG_BT_NIMBLE_DEBUG)
#define CONFIG_BT_NIMBLE_DEBUG
#endif
#else // Using Arduino
/*********************************************** /***********************************************
* Arduino config options start here * Arduino user-config options start here
**********************************************/ **********************************************/
/** @brief Comment out if not using NimBLE Client functions \n /** @brief Un-comment to change the number of simultaneous connections (esp controller max is 9) */
* Reduces flash size by approx. 7kB. // #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
*/
#ifndef CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif
/** @brief Comment out if not using NimBLE Scan functions \n /** @brief Un-comment to change the default MTU size */
* Reduces flash size by approx. 26kB. // #define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255
*/
#ifndef CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
/** @brief Comment out if not using NimBLE Server functions \n /** @brief Un-comment to change default device name */
* Reduces flash size by approx. 16kB. // #define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
*/
#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif
/** @brief Comment out if not using NimBLE Advertising functions \n /** @brief Un-comment to see debug log messages from the NimBLE host
* Reduces flash size by approx. 5kB.
*/
#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. * Uses approx. 32kB of flash memory.
*/ */
// #define CONFIG_BT_NIMBLE_DEBUG // #define CONFIG_BT_NIMBLE_DEBUG
/* Uncomment to see NimBLE host return codes as text debug log messages. /** @brief Un-comment to see NimBLE host return codes as text debug log messages.
* Uses approx. 7kB of flash memory. * Uses approx. 7kB of flash memory.
*/ */
// #define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT // #define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
/* Uncomment to see GAP event codes as text in debug log messages. /** @brief Un-comment to see GAP event codes as text in debug log messages.
* Uses approx. 1kB of flash memory. * Uses approx. 1kB of flash memory.
*/ */
// #define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT // #define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
/* Uncomment to see advertisment types as text while scanning in debug log messages. /** @brief Un-comment to see advertisment types as text while scanning in debug log messages.
* Uses approx. 250 bytes of flash memory. * Uses approx. 250 bytes of flash memory.
*/ */
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT // #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
/** @brief Sets the core NimBLE host runs on */ /** @brief Un-comment to change the default GAP appearance */
// #define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0
/** @brief Un-comment if not using NimBLE Client functions \n
* Reduces flash size by approx. 7kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
/** @brief Un-comment if not using NimBLE Scan functions \n
* Reduces flash size by approx. 26kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
/** @brief Un-comment if not using NimBLE Server functions \n
* Reduces flash size by approx. 16kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
/** @brief Un-comment if not using NimBLE Advertising functions \n
* Reduces flash size by approx. 5kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
/** @brief Un-comment to change the number of devices allowed to store/bond with */
// #define CONFIG_BT_NIMBLE_MAX_BONDS 3
/** @brief Un-comment to change the maximum number of CCCD subscriptions to store */
// #define CONFIG_BT_NIMBLE_MAX_CCCDS 8
/** @brief Un-comment to change the random address refresh time (in seconds) */
// #define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
/**
* @brief Un-comment to change the number of MSYS buffers available.
* @details MSYS is a system level mbuf registry. For prepare write & prepare \n
* responses MBUFs are allocated out of msys_1 pool. This may need to be increased if\n
* you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
*/
// #define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
/** @brief Un-comment to use external PSRAM for the NimBLE host */
// #define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1
/** @brief Un-comment to change the core NimBLE host runs on */
// #define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
/** @brief Un-comment to change the stack size for the NimBLE host task */
// #define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
/**********************************
End Arduino user-config
**********************************/
/* This section should not be altered */
#ifndef CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif
#ifndef CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif
#ifndef CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
#ifndef CONFIG_BT_NIMBLE_PINNED_TO_CORE #ifndef CONFIG_BT_NIMBLE_PINNED_TO_CORE
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0 #define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
#endif #endif
/** @brief Sets the stack size for the NimBLE host task */
#ifndef CONFIG_BT_NIMBLE_TASK_STACK_SIZE #ifndef CONFIG_BT_NIMBLE_TASK_STACK_SIZE
#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096 #define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
#endif #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 #ifndef CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1 #define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
#endif #endif
/** @brief Sets the number of simultaneous connections (esp controller max is 9) */
#ifndef CONFIG_BT_NIMBLE_MAX_CONNECTIONS #ifndef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3 #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
#endif #endif
/** @brief Sets the number of devices allowed to store/bond with */
#ifndef CONFIG_BT_NIMBLE_MAX_BONDS #ifndef CONFIG_BT_NIMBLE_MAX_BONDS
#define CONFIG_BT_NIMBLE_MAX_BONDS 3 #define CONFIG_BT_NIMBLE_MAX_BONDS 3
#endif #endif
/** @brief Sets the maximum number of CCCD subscriptions to store */
#ifndef CONFIG_BT_NIMBLE_MAX_CCCDS #ifndef CONFIG_BT_NIMBLE_MAX_CCCDS
#define CONFIG_BT_NIMBLE_MAX_CCCDS 8 #define CONFIG_BT_NIMBLE_MAX_CCCDS 8
#endif #endif
/** @brief Default device name */
#ifndef CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME #ifndef CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble" #define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
#endif #endif
#ifndef CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU
#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255
#endif
#ifndef CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE
#define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0
#endif
#ifndef CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT
#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
#endif
#ifndef CONFIG_BT_NIMBLE_RPA_TIMEOUT
#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
#endif
/** @brief Set if CCCD's and bond data should be stored in NVS */ /** @brief Set if CCCD's and bond data should be stored in NVS */
#define CONFIG_BT_NIMBLE_NVS_PERSIST 1 #define CONFIG_BT_NIMBLE_NVS_PERSIST 1
@ -152,12 +163,6 @@
/** @brief Max device name length (bytes) */ /** @brief Max device name length (bytes) */
#define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31 #define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31
/** @brief Default MTU size */
#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 256
/** @brief Default GAP appearance */
#define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0
/** @brief ACL Buffer count */ /** @brief ACL Buffer count */
#define CONFIG_BT_NIMBLE_ACL_BUF_COUNT 12 #define CONFIG_BT_NIMBLE_ACL_BUF_COUNT 12
@ -173,21 +178,9 @@
/** @brief Number of low priority HCI event buffers */ /** @brief Number of low priority HCI event buffers */
#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8 #define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8
/**
* @brief Sets the number of MSYS buffers available.
* @details MSYS is a system level mbuf registry. For prepare write & prepare \n
* responses MBUFs are allocated out of msys_1 pool. This may need to be increased if\n
* you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
*/
#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
/** @brief Random address refresh time in seconds */
#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
/** @brief Maximum number of connection oriented channels */ /** @brief Maximum number of connection oriented channels */
#define CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM 0 #define CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM 0
/* These should not be altered */
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL 1 #define CONFIG_BT_NIMBLE_HS_FLOW_CTRL 1
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL 1000 #define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL 1000
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH 2 #define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH 2
@ -197,14 +190,9 @@
#define CONFIG_BT_ENABLED #define CONFIG_BT_ENABLED
#endif #endif
#ifndef CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY
#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY #define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY
#endif
#endif // #if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
/**********************************
End Arduino config
**********************************/
/* Cannot use client without scan */ /* Cannot use client without scan */
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
@ -216,26 +204,3 @@
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif #endif
#ifdef _DOXYGEN_
/** @brief Uncomment to see debug log messages from the NimBLE host \n
* Uses approx. 32kB of flash memory.
*/
#define CONFIG_BT_NIMBLE_DEBUG
/** @brief Uncomment to see NimBLE host return codes as text debug log messages. \n
* Uses approx. 7kB of flash memory.
*/
#define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
/** @brief Uncomment to see GAP event codes as text in debug log messages. \n
* Uses approx. 1kB of flash memory.
*/
#define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
/** @brief Uncomment to see advertisment types as text while scanning in debug log messages. \n
* Uses approx. 250 bytes of flash memory.
*/
#define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
#endif // _DOXYGEN_

View File

@ -0,0 +1,53 @@
/*
* For ESP-IDF compatibility
* Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
* This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
*/
#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
#define CONFIG_BT_NIMBLE_ENABLED
#endif
#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif
#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif
#if defined(CONFIG_NIMBLE_DEBUG) && !defined(CONFIG_BT_NIMBLE_DEBUG)
#define CONFIG_BT_NIMBLE_DEBUG
#endif
#if defined(CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR
#endif
#if defined(CONFIG_SCAN_DUPLICATE_BY_ADV_DATA ) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA CONFIG_SCAN_DUPLICATE_BY_ADV_DATA
#endif
#if defined(CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR
#endif
#if defined(CONFIG_SCAN_DUPLICATE_TYPE) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE)
#define CONFIG_BTDM_SCAN_DUPL_TYPE CONFIG_SCAN_DUPLICATE_TYPE
#endif
#if defined(CONFIG_DUPLICATE_SCAN_CACHE_SIZE) && !defined(CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE)
#define CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE CONFIG_DUPLICATE_SCAN_CACHE_SIZE
#endif
#if defined(CONFIG_NIMBLE_MAX_CONNECTIONS ) && !defined(CONFIG_BT_NIMBLE_MAX_CONNECTIONS)
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
#endif

View File

@ -124,12 +124,12 @@ lib_extra_dirs = ${library.lib_extra_dirs}
[core32] [core32]
; Activate Stage Core32 by removing ";" in next 3 lines, if you want to override the standard core32 ; Activate Stage Core32 by removing ";" in next 3 lines, if you want to override the standard core32
;platform_packages = ${core32_stage.platform_packages} platform_packages = ${core32_stage.platform_packages}
;build_unflags = ${core32_stage.build_unflags} build_unflags = ${core32_stage.build_unflags}
;build_flags = ${core32_stage.build_flags} build_flags = ${core32_stage.build_flags}
[core32_stage] [core32_stage]
platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc6/esp32-1.0.5-rc6.zip platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc7/esp32-1.0.5-rc7.zip
platformio/tool-mklittlefs @ ~1.203.200522 platformio/tool-mklittlefs @ ~1.203.200522
build_unflags = ${esp32_defaults.build_unflags} build_unflags = ${esp32_defaults.build_unflags}
build_flags = ${esp32_defaults.build_flags} build_flags = ${esp32_defaults.build_flags}

View File

@ -480,7 +480,7 @@ cleanup:
} }
} }
bool _SendDeviceGroupMessage(uint32_t device, DevGroupMessageType message_type, ...) bool _SendDeviceGroupMessage(int32_t device, DevGroupMessageType message_type, ...)
{ {
// If device groups is not up, ignore this request. // If device groups is not up, ignore this request.
if (!device_groups_up) return 1; if (!device_groups_up) return 1;

View File

@ -603,11 +603,10 @@ void HueLightStatus2(uint8_t device, String *response)
// last 24 bits of Mac address + 4 bits of local light + high bit for relays 16-31, relay 32 is mapped to 0 // last 24 bits of Mac address + 4 bits of local light + high bit for relays 16-31, relay 32 is mapped to 0
// Zigbee extension: bit 29 = 1, and last 16 bits = short address of Zigbee device // Zigbee extension: bit 29 = 1, and last 16 bits = short address of Zigbee device
#ifndef USE_ZIGBEE #ifndef USE_ZIGBEE
uint32_t EncodeLightId(uint8_t relay_id) uint32_t EncodeLightId(uint8_t relay_id) {
#else #else
uint32_t EncodeLightId(uint8_t relay_id, uint16_t z_shortaddr = 0) uint32_t EncodeLightId(uint8_t relay_id, uint16_t z_shortaddr = 0) {
#endif #endif
{
uint8_t mac[6]; uint8_t mac[6];
WiFi.macAddress(mac); WiFi.macAddress(mac);
uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4); uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4);
@ -636,11 +635,10 @@ uint32_t EncodeLightId(uint8_t relay_id, uint16_t z_shortaddr = 0)
// If the Id encodes a Zigbee device (meaning bit 29 is set) // If the Id encodes a Zigbee device (meaning bit 29 is set)
// it returns 0 and sets the 'shortaddr' to the device short address // it returns 0 and sets the 'shortaddr' to the device short address
#ifndef USE_ZIGBEE #ifndef USE_ZIGBEE
uint32_t DecodeLightId(uint32_t hue_id) uint32_t DecodeLightId(uint32_t hue_id) {
#else #else
uint32_t DecodeLightId(uint32_t hue_id, uint16_t * shortaddr = nullptr) uint32_t DecodeLightId(uint32_t hue_id, uint16_t * shortaddr = nullptr) {
#endif #endif
{
uint8_t relay_id = hue_id & 0xF; uint8_t relay_id = hue_id & 0xF;
if (hue_id & (1 << 28)) { // check if bit 25 is set, if so we have if (hue_id & (1 << 28)) { // check if bit 25 is set, if so we have
relay_id += 16; relay_id += 16;

View File

@ -491,7 +491,7 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed)
} }
// If we need to adjust the brightness, do it. // If we need to adjust the brightness, do it.
uint32_t negated_device_group_index = -power_button_index; int32_t negated_device_group_index = -power_button_index;
if (bri_offset) { if (bri_offset) {
int32_t bri; int32_t bri;
#ifdef USE_PWM_DIMMER_REMOTE #ifdef USE_PWM_DIMMER_REMOTE
@ -510,7 +510,7 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed)
} }
if (new_bri != bri) { if (new_bri != bri) {
#ifdef USE_DEVICE_GROUPS #ifdef USE_DEVICE_GROUPS
SendDeviceGroupMessage(negated_device_group_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE_DIRECT), DGR_ITEM_LIGHT_BRI, new_bri); SendDeviceGroupMessage(negated_device_group_index, (dgr_more_to_come ? DGR_MSGTYP_UPDATE_MORE_TO_COME : DGR_MSGTYP_UPDATE_DIRECT), DGR_ITEM_LIGHT_BRI, new_bri, DGR_ITEM_BRI_POWER_ON, new_bri);
#endif // USE_DEVICE_GROUPS #endif // USE_DEVICE_GROUPS
#ifdef USE_PWM_DIMMER_REMOTE #ifdef USE_PWM_DIMMER_REMOTE
if (active_remote_pwm_dimmer) { if (active_remote_pwm_dimmer) {

View File

@ -360,7 +360,7 @@ bool UfsExecuteCommandFileReady(void) {
void UfsExecuteCommandFileLoop(void) { void UfsExecuteCommandFileLoop(void) {
if (UfsExecuteCommandFileReady() || !ffs_type) { return; } if (UfsExecuteCommandFileReady() || !ffs_type) { return; }
if (strlen(UfsData.run_file) && !UfsData.run_file_mutex) { if (TimeReached(TasmotaGlobal.backlog_timer) && strlen(UfsData.run_file) && !UfsData.run_file_mutex) {
File file = ffsp->open(UfsData.run_file, "r"); File file = ffsp->open(UfsData.run_file, "r");
if (!file || !file.seek(UfsData.run_file_pos)) { if (!file || !file.seek(UfsData.run_file_pos)) {
UfsData.run_file_pos = -1; // Signal file ready UfsData.run_file_pos = -1; // Signal file ready
@ -401,7 +401,11 @@ void UfsExecuteCommandFileLoop(void) {
UfsData.run_file_pos = (file.available()) ? file.position() : -1; UfsData.run_file_pos = (file.available()) ? file.position() : -1;
file.close(); file.close();
if (strlen(cmd_line)) { if (strlen(cmd_line)) {
bool nodelay = (!(!strncasecmp_P(cmd_line, PSTR(D_CMND_DELAY), strlen(D_CMND_DELAY))));
ExecuteCommand(cmd_line, SRC_FILE); ExecuteCommand(cmd_line, SRC_FILE);
if (nodelay) {
TasmotaGlobal.backlog_timer = millis(); // Reset backlog_timer which has been set by ExecuteCommand (CommandHandler)
}
} }
UfsData.run_file_mutex = false; UfsData.run_file_mutex = false;
@ -507,6 +511,10 @@ void UFSRun(void) {
} else { } else {
ResponseCmndFailed(); ResponseCmndFailed();
} }
} else {
bool not_active = UfsExecuteCommandFileReady();
UfsData.run_file_pos = -1;
ResponseCmndChar(not_active ? PSTR(D_JSON_DONE) : PSTR(D_JSON_ABORTED));
} }
} }

View File

@ -5,6 +5,7 @@
Copyright (C) 2020 Christian Baars and Theo Arends Copyright (C) 2020 Christian Baars and Theo Arends
Also Simon Hailes and Robert Klauco
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -23,6 +24,9 @@
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
Version yyyymmdd Action Description Version yyyymmdd Action Description
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
0.9.2.1 20210217 changed - make features alos depend on received data - i.e. 'unknown' devices will show what they send.
Add MI32Option6 1 to switch to tele/tasmota_ble/<somename> style MQTT independent of HASS discovery.
-------
0.9.2.0 20210127 changed - Officially includes as the mi driver when using USE_BLE_ESP32. 0.9.2.0 20210127 changed - Officially includes as the mi driver when using USE_BLE_ESP32.
------- -------
0.9.1.9 20201226 changed - All change now. 0.9.1.9 20201226 changed - All change now.
@ -124,6 +128,7 @@ struct {
uint32_t ignoreBogusBattery:1; uint32_t ignoreBogusBattery:1;
uint32_t minimalSummary:1; // DEPRECATED!! uint32_t minimalSummary:1; // DEPRECATED!!
uint32_t onlyAliased:1; // only include sensors that are aliased uint32_t onlyAliased:1; // only include sensors that are aliased
uint32_t MQTTType:1;
} option; } option;
} MI32; } MI32;
@ -279,6 +284,8 @@ struct mi_sensor_t{
uint32_t NMT:1; uint32_t NMT:1;
uint32_t PIR:1; uint32_t PIR:1;
uint32_t Btn:1; uint32_t Btn:1;
uint32_t events:1;
uint32_t pairing:1;
}; };
uint32_t raw; uint32_t raw;
} feature; } feature;
@ -369,8 +376,9 @@ void (*const MI32_Commands[])(void) PROGMEM = {
#define MI_MHOC401 11 #define MI_MHOC401 11
#define MI_MHOC303 12 #define MI_MHOC303 12
#define MI_ATC 13 #define MI_ATC 13
#define MI_DOOR 14
#define MI_MI32_TYPES 13 //count this manually #define MI_MI32_TYPES 14 //count this manually
const uint16_t kMI32DeviceID[MI_MI32_TYPES]={ const uint16_t kMI32DeviceID[MI_MI32_TYPES]={
0x0000, // Unkown 0x0000, // Unkown
@ -385,7 +393,8 @@ const uint16_t kMI32DeviceID[MI_MI32_TYPES]={
0x0153, // yee-rc 0x0153, // yee-rc
0x0387, // MHO-C401 0x0387, // MHO-C401
0x06d3, // MHO-C303 0x06d3, // MHO-C303
0x0a1c // ATC -> this is a fake ID 0x0a1c, // ATC -> this is a fake ID
0x098b // door/window sensor
}; };
const char kMI32DeviceType0[] PROGMEM = "Unknown"; const char kMI32DeviceType0[] PROGMEM = "Unknown";
@ -401,7 +410,8 @@ const char kMI32DeviceType9[] PROGMEM = "YEERC";
const char kMI32DeviceType10[] PROGMEM ="MHOC401"; const char kMI32DeviceType10[] PROGMEM ="MHOC401";
const char kMI32DeviceType11[] PROGMEM ="MHOC303"; const char kMI32DeviceType11[] PROGMEM ="MHOC303";
const char kMI32DeviceType12[] PROGMEM ="ATC"; const char kMI32DeviceType12[] PROGMEM ="ATC";
const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType0,kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12}; const char kMI32DeviceType13[] PROGMEM ="DOOR";
const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType0,kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12,kMI32DeviceType13};
typedef int BATREAD_FUNCTION(int slot); typedef int BATREAD_FUNCTION(int slot);
typedef int UNITWRITE_FUNCTION(int slot, int unit); typedef int UNITWRITE_FUNCTION(int slot, int unit);
@ -1406,6 +1416,7 @@ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter)
_newSensor.events=0x00; _newSensor.events=0x00;
_newSensor.feature.PIR=1; _newSensor.feature.PIR=1;
_newSensor.feature.NMT=1; _newSensor.feature.NMT=1;
_newSensor.feature.events=1;
break; break;
case MI_MJYD2S: case MI_MJYD2S:
_newSensor.NMT=0; _newSensor.NMT=0;
@ -1414,8 +1425,10 @@ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter)
_newSensor.feature.NMT=1; _newSensor.feature.NMT=1;
_newSensor.feature.lux=1; _newSensor.feature.lux=1;
_newSensor.feature.bat=1; _newSensor.feature.bat=1;
_newSensor.feature.events=1;
break; break;
case MI_YEERC: case MI_YEERC:
case MI_DOOR:
_newSensor.feature.Btn=1; _newSensor.feature.Btn=1;
break; break;
default: default:
@ -1633,15 +1646,25 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
char tmp[20]; char tmp[20];
BLE_ESP32::dump(tmp, 20, (uint8_t*)&(parsed->payload), parsed->payload.size+3); BLE_ESP32::dump(tmp, 20, (uint8_t*)&(parsed->payload), parsed->payload.size+3);
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: MI%d payload %s"), _slot, tmp); if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: MI%d payload %s"), _slot, tmp);
// clear this for every payload
MIBLEsensors[_slot].pairing = 0;
MIBLEsensors[_slot].eventType.PairBtn = 0;
switch(parsed->payload.type){ switch(parsed->payload.type){
case 0x01: // button press case 0x01: // button press
MIBLEsensors[_slot].Btn = pld->Btn.num + (pld->Btn.longPress/2)*6; MIBLEsensors[_slot].Btn = pld->Btn.num + (pld->Btn.longPress/2)*6;
MIBLEsensors[_slot].feature.Btn = 1;
MIBLEsensors[_slot].eventType.Btn = 1; MIBLEsensors[_slot].eventType.Btn = 1;
MI32.mode.shallTriggerTele = 1; MI32.mode.shallTriggerTele = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
break; break;
case 0x02: case 0x02: // related to pair button?
res = 0; MIBLEsensors[_slot].pairing = 1;
MIBLEsensors[_slot].eventType.PairBtn = 1;
MIBLEsensors[_slot].feature.pairing = 1;
MI32.mode.shallTriggerTele = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
break; break;
case 0x03: {// motion? 1 byte case 0x03: {// motion? 1 byte
uint8_t motion = parsed->payload.data[0]; uint8_t motion = parsed->payload.data[0];
@ -1651,6 +1674,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
float _tempFloat=(float)(pld->temp)/10.0f; float _tempFloat=(float)(pld->temp)/10.0f;
if(_tempFloat<60){ if(_tempFloat<60){
MIBLEsensors[_slot].temp=_tempFloat; MIBLEsensors[_slot].temp=_tempFloat;
MIBLEsensors[_slot].feature.temp = 1;
MIBLEsensors[_slot].eventType.temp = 1; MIBLEsensors[_slot].eventType.temp = 1;
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 4: temp updated")); if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 4: temp updated"));
} else { } else {
@ -1662,6 +1686,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
float _tempFloat=(float)(pld->hum)/10.0f; float _tempFloat=(float)(pld->hum)/10.0f;
if(_tempFloat<101){ if(_tempFloat<101){
MIBLEsensors[_slot].hum=_tempFloat; MIBLEsensors[_slot].hum=_tempFloat;
MIBLEsensors[_slot].feature.hum = 1;
MIBLEsensors[_slot].eventType.hum = 1; MIBLEsensors[_slot].eventType.hum = 1;
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 6: hum updated")); if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 6: hum updated"));
} else { } else {
@ -1675,17 +1700,20 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
MIBLEsensors[_slot].eventType.noMotion = 1; MIBLEsensors[_slot].eventType.noMotion = 1;
} }
MIBLEsensors[_slot].eventType.lux = 1; MIBLEsensors[_slot].eventType.lux = 1;
MIBLEsensors[_slot].feature.lux = 1;
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff);
break; break;
case 0x08: case 0x08:
MIBLEsensors[_slot].moisture=pld->moist; MIBLEsensors[_slot].moisture=pld->moist;
MIBLEsensors[_slot].eventType.moist = 1; MIBLEsensors[_slot].eventType.moist = 1;
MIBLEsensors[_slot].feature.moist = 1;
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 8: moisture updated")); if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 8: moisture updated"));
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 8: U8: %u Moisture"), _beacon.moist); // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 8: U8: %u Moisture"), _beacon.moist);
break; break;
case 0x09: // 'conductivity' case 0x09: // 'conductivity'
MIBLEsensors[_slot].fertility=pld->fert; MIBLEsensors[_slot].fertility=pld->fert;
MIBLEsensors[_slot].eventType.fert = 1; MIBLEsensors[_slot].eventType.fert = 1;
MIBLEsensors[_slot].feature.fert = 1;
if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 9: fertility updated")); if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 9: fertility updated"));
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 9: U16: %u Fertility"), _beacon.fert); // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 9: U16: %u Fertility"), _beacon.fert);
break; break;
@ -1696,6 +1724,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
break; break;
} }
} }
MIBLEsensors[_slot].feature.bat = 1;
if(pld->bat<101){ if(pld->bat<101){
MIBLEsensors[_slot].bat = pld->bat; MIBLEsensors[_slot].bat = pld->bat;
MIBLEsensors[_slot].eventType.bat = 1; MIBLEsensors[_slot].eventType.bat = 1;
@ -1708,6 +1737,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode a: U8: %u %%"), _beacon.bat); // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode a: U8: %u %%"), _beacon.bat);
break; break;
case 0x0d:{ case 0x0d:{
MIBLEsensors[_slot].feature.tempHum = 1;
float _tempFloat=(float)(pld->HT.temp)/10.0f; float _tempFloat=(float)(pld->HT.temp)/10.0f;
if(_tempFloat < 60){ if(_tempFloat < 60){
MIBLEsensors[_slot].temp = _tempFloat; MIBLEsensors[_slot].temp = _tempFloat;
@ -1734,6 +1764,11 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
MIBLEsensors[_slot].eventType.lux = 1; MIBLEsensors[_slot].eventType.lux = 1;
MIBLEsensors[_slot].NMT = 0; MIBLEsensors[_slot].NMT = 0;
MI32.mode.shallTriggerTele = 1; MI32.mode.shallTriggerTele = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
MIBLEsensors[_slot].feature.lux = 1;
MIBLEsensors[_slot].feature.NMT = 1;
MIBLEsensors[_slot].feature.events=1;
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: PIR: primary"),MIBLEsensors[_slot].lux ); // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: PIR: primary"),MIBLEsensors[_slot].lux );
break; break;
case 0x10:{ // 'formaldehide' case 0x10:{ // 'formaldehide'
@ -1758,9 +1793,20 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){
MIBLEsensors[_slot].NMT = pld->NMT; MIBLEsensors[_slot].NMT = pld->NMT;
MIBLEsensors[_slot].eventType.NMT = 1; MIBLEsensors[_slot].eventType.NMT = 1;
MI32.mode.shallTriggerTele = 1; MI32.mode.shallTriggerTele = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
MIBLEsensors[_slot].feature.NMT = 1;
// AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 17: NMT: %u seconds"), _beacon.NMT); // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 17: NMT: %u seconds"), _beacon.NMT);
} break; } break;
case 0x19:{
MIBLEsensors[_slot].Btn = uint8_t(parsed->payload.data[0]); // just an 8 bit value in a union.
MIBLEsensors[_slot].eventType.Btn = 1;
MI32.mode.shallTriggerTele = 1;
MIBLEsensors[_slot].shallSendMQTT = 1;
MIBLEsensors[_slot].feature.Btn = 1;
} break;
default: { default: {
AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Unknown MI pld")); AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Unknown MI pld"));
res = 0; res = 0;
@ -1894,10 +1940,12 @@ void MI32EverySecond(bool restart){
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("M32: onesec")); // AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("M32: onesec"));
MI32TimeoutSensors(); MI32TimeoutSensors();
MI32ShowSomeSensors(); if (MI32.option.MQTTType == 0){
MI32ShowSomeSensors();
MI32DiscoveryOneMISensor(); } else {
MI32ShowOneMISensor(); MI32DiscoveryOneMISensor();
MI32ShowOneMISensor();
}
// read a battery if // read a battery if
// MI32.batteryreader.slot < filled and !MI32.batteryreader.active // MI32.batteryreader.slot < filled and !MI32.batteryreader.active
@ -2122,16 +2170,24 @@ void CmndMi32Option(void){
bool onOff = atoi(XdrvMailbox.data); bool onOff = atoi(XdrvMailbox.data);
switch(XdrvMailbox.index) { switch(XdrvMailbox.index) {
case 0: case 0:
MI32.option.allwaysAggregate = onOff; MI32.option.allwaysAggregate = onOff;
ResponseCmndNumber(onOff);
return;
break; break;
case 1: case 1:
MI32.option.noSummary = onOff; MI32.option.noSummary = onOff;
ResponseCmndNumber(onOff);
return;
break; break;
case 2: case 2:
MI32.option.directBridgeMode = onOff; MI32.option.directBridgeMode = onOff;
ResponseCmndNumber(onOff);
return;
break; break;
case 4:{ case 4:{
MI32.option.ignoreBogusBattery = onOff; MI32.option.ignoreBogusBattery = onOff;
ResponseCmndNumber(onOff);
return;
} break; } break;
case 5:{ case 5:{
MI32.option.onlyAliased = onOff; MI32.option.onlyAliased = onOff;
@ -2139,7 +2195,15 @@ void CmndMi32Option(void){
// discard all sensors for a restart // discard all sensors for a restart
MIBLEsensors.clear(); MIBLEsensors.clear();
} }
ResponseCmndNumber(onOff);
return;
} break; } break;
case 6:{
MI32.option.MQTTType = onOff;
ResponseCmndNumber(onOff);
return;
} break;
} }
ResponseCmndDone(); ResponseCmndDone();
} }
@ -2239,7 +2303,7 @@ void CmndMi32Keys(void){
* Presentation * Presentation
\*********************************************************************************************/ \*********************************************************************************************/
const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0920{m}%u%s / %u{e}"; const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0921{m}%u%s / %u{e}";
const char HTTP_MI32_ALIAS[] PROGMEM = "{s}%s Alias {m}%s{e}"; const char HTTP_MI32_ALIAS[] PROGMEM = "{s}%s Alias {m}%s{e}";
const char HTTP_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}"; const char HTTP_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}";
const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}"; const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}";
@ -2517,9 +2581,13 @@ void MI32ShowSomeSensors(){
ResponseTime_P(PSTR("")); ResponseTime_P(PSTR(""));
int cnt = 0; int cnt = 0;
for (; (MI32.mqttCurrentSlot < numsensors) && (cnt < 4); MI32.mqttCurrentSlot++, cnt++) { int maxcnt = 4;
mi_sensor_t *p;
for (; (MI32.mqttCurrentSlot < numsensors) && (cnt < maxcnt); MI32.mqttCurrentSlot++, cnt++) {
ResponseAppend_P(PSTR(",")); ResponseAppend_P(PSTR(","));
MI32GetOneSensorJson(MI32.mqttCurrentSlot, 0); p = &MIBLEsensors[MI32.mqttCurrentSlot];
MI32GetOneSensorJson(MI32.mqttCurrentSlot, (maxcnt == 1));
int mlen = strlen(TasmotaGlobal.mqtt_data); int mlen = strlen(TasmotaGlobal.mqtt_data);
// if we ran out of room, leave here. // if we ran out of room, leave here.
@ -2527,6 +2595,7 @@ void MI32ShowSomeSensors(){
MI32.mqttCurrentSlot++; MI32.mqttCurrentSlot++;
break; break;
} }
cnt++;
} }
ResponseAppend_P(PSTR("}")); ResponseAppend_P(PSTR("}"));
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
@ -2557,8 +2626,13 @@ void MI32ShowOneMISensor(){
return; return;
} }
if(
#ifdef USE_HOME_ASSISTANT #ifdef USE_HOME_ASSISTANT
if(Settings.flag.hass_discovery){ Settings.flag.hass_discovery
||
#endif //USE_HOME_ASSISTANT
MI32.option.MQTTType == 1
){
ResponseTime_P(PSTR(",")); ResponseTime_P(PSTR(","));
MI32GetOneSensorJson(MI32.mqttCurrentSingleSlot, 1); MI32GetOneSensorJson(MI32.mqttCurrentSingleSlot, 1);
@ -2584,7 +2658,6 @@ void MI32ShowOneMISensor(){
MqttPublish(SensorTopic); MqttPublish(SensorTopic);
//AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: show some %d %s"),D_CMND_MI32, MI32.mqttCurrentSlot, TasmotaGlobal.mqtt_data); //AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: show some %d %s"),D_CMND_MI32, MI32.mqttCurrentSlot, TasmotaGlobal.mqtt_data);
} }
#endif //USE_HOME_ASSISTANT
MI32.mqttCurrentSingleSlot++; MI32.mqttCurrentSingleSlot++;
} }
@ -2739,11 +2812,23 @@ void MI32ShowTriggeredSensors(){
int sensor = 0; int sensor = 0;
int maxcnt = 4;
if(
#ifdef USE_HOME_ASSISTANT
Settings.flag.hass_discovery
||
#endif //USE_HOME_ASSISTANT
MI32.option.MQTTType == 1
){
maxcnt = 1;
}
do { do {
ResponseTime_P(PSTR("")); ResponseTime_P(PSTR(""));
int cnt = 0; int cnt = 0;
for (; (sensor < numsensors) && (cnt < 4); sensor++) { mi_sensor_t *p;
mi_sensor_t *p; for (; (sensor < numsensors) && (cnt < maxcnt); sensor++) {
p = &MIBLEsensors[sensor]; p = &MIBLEsensors[sensor];
if(p->eventType.raw == 0) continue; if(p->eventType.raw == 0) continue;
if(p->shallSendMQTT==0) continue; if(p->shallSendMQTT==0) continue;
@ -2761,7 +2846,30 @@ void MI32ShowTriggeredSensors(){
} }
if (cnt){ // if we got one, then publish if (cnt){ // if we got one, then publish
ResponseAppend_P(PSTR("}")); ResponseAppend_P(PSTR("}"));
MqttPublishPrefixTopic_P(STAT, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); if(
#ifdef USE_HOME_ASSISTANT
Settings.flag.hass_discovery
||
#endif //USE_HOME_ASSISTANT
MI32.option.MQTTType == 1
){
char SensorTopic[60];
char idstr[32];
const char *alias = BLE_ESP32::getAlias(p->MAC);
const char *id = idstr;
if (alias && *alias){
id = alias;
} else {
sprintf(idstr, PSTR("%s%02x%02x%02x"),
kMI32DeviceType[p->type-1],
p->MAC[3], p->MAC[4], p->MAC[5]);
}
sprintf(SensorTopic, "tele/tasmota_ble/%s",
id);
MqttPublish(SensorTopic);
} else {
MqttPublishPrefixTopic_P(STAT, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
}
AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: triggered %d %s"),D_CMND_MI32, sensor, TasmotaGlobal.mqtt_data); AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: triggered %d %s"),D_CMND_MI32, sensor, TasmotaGlobal.mqtt_data);
#ifdef USE_RULES #ifdef USE_RULES
@ -2877,21 +2985,25 @@ void MI32Show(bool json)
WSContentSend_PD(HTTP_NEEDKEY, typeName, _MAC, Webserver->client().localIP().toString().c_str(), tmp ); WSContentSend_PD(HTTP_NEEDKEY, typeName, _MAC, Webserver->client().localIP().toString().c_str(), tmp );
} }
if (p->type==MI_NLIGHT || p->type==MI_MJYD2S) {
#else
if (p->type==MI_NLIGHT) {
#endif //USE_MI_DECRYPTION #endif //USE_MI_DECRYPTION
if (p->feature.events){
WSContentSend_PD(HTTP_EVENTS, typeName, p->events); WSContentSend_PD(HTTP_EVENTS, typeName, p->events);
}
if (p->feature.NMT){
// no motion time
if(p->NMT>0) WSContentSend_PD(HTTP_NMT, typeName, p->NMT); if(p->NMT>0) WSContentSend_PD(HTTP_NMT, typeName, p->NMT);
} }
if (p->lux!=0x00ffffff) { // this is the error code -> no valid value if (p->feature.lux){
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, typeName, p->lux); if (p->lux!=0x00ffffff) { // this is the error code -> no valid value
WSContentSend_PD(HTTP_SNS_ILLUMINANCE, typeName, p->lux);
}
} }
if(p->bat!=0x00){ if(p->bat!=0x00){
WSContentSend_PD(HTTP_BATTERY, typeName, p->bat); WSContentSend_PD(HTTP_BATTERY, typeName, p->bat);
} }
if (p->type==MI_YEERC){ if (p->feature.Btn){
WSContentSend_PD(HTTP_LASTBUTTON, typeName, p->Btn); WSContentSend_PD(HTTP_LASTBUTTON, typeName, p->Btn);
} }
if (p->pairing){ if (p->pairing){