Compare commits

...

36 Commits

Author SHA1 Message Date
c218a27dd8 esp_nimble_hci_and_controller_deinit() does not exist in idf anymore, I hope we never need it 2022-09-17 22:14:05 +02:00
8883f44d9f New compiler new printf chars 2022-09-17 22:13:46 +02:00
9c7b690771 New compiler new printf chars 2022-09-17 22:13:25 +02:00
641f297120 fix compiling issues when nimble is disabled in sdkconfig 2021-09-21 16:49:45 +02:00
7f853fa04b Fixed shitty nimble logging 2021-05-29 01:21:34 +02:00
df4eb9f7d8 Removed arduino bullshit from nimble 2021-05-28 10:46:37 +02:00
d3a0f95aaf Add whitelist API.
This adds the functions:
- bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address);
- bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address);
- bool NimBLEDevice::onWhiteList(const NimBLEAddress & address);
- size_t NimBLEDevice::getWhiteListCount();
- NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index);
2021-05-23 13:12:44 -06:00
d9d794ae40 [Server] Add ability to create static attributes.
This allows creating instances of services/characteristics and decriptors without
needing to call the parent createX() methods. This allows creation of the attibutes
within a class or other structure and the attributes can be linked to the parent by
calling the parent addX() method.

Additonally this increases the backward compatibility with the original bluedroid library
to further support user migration.
2021-05-23 13:07:00 -06:00
30d6c399b8 [Client] Fix for truncation of strings with null in writeValue std::string overload 2021-05-21 19:54:08 -06:00
7815d89dbf Call onSubscribe() after adding connection to subscribedVector
This allows for sending BLE messages in the onSubscribe() handler
2021-05-19 22:28:30 -06:00
9fa9531e50 Add method to get the callbacks from a characteristic.
This is useul to be able to unit/integration test the behavior of a
characteristic.
2021-05-19 22:23:40 -06:00
946b971750 Properly find the remote characteristic end handle for descriptor discovery.
Previously we used the service end handle or, if available, the handle of the next characteristic in the service
as the end handle when retrieving the descriptors of a characteristic. This process would fail when connecting to some
devices if the characteristics were retrieved individually instead of all together. The cause of failure was requesting
a handle range that is out of characteristic scope which is also out of line with BLE
specifications.

The fix included in this is to set the end handles of the characteristics either after retrieving all the characteristics
in a service by using the next charactertistic definition handle or, if the characteristic was retrieved separately,
we retrieve the next characteristic just before retrieving the descriptors.
2021-05-17 14:37:03 -06:00
b62358a520 Remove task notification for server indications.
This resolves an issue when sending an indication from a callback that can cause the server to hang.
2021-05-17 14:08:02 -06:00
e45fb8616a Cleanup logs. 2021-05-15 09:52:00 -06:00
d38865e022 Remove some asserts. 2021-05-07 11:06:45 -06:00
62d1f67d8b Add connection info class and access methods to server and client.
This adds the ability to access information about the current connection.
A new class was created to wrap the struct ble_gap_conn_desc with methods to retrieve the connection information.

Example server use:
```
for(auto i=0; i<pServer->getConnectedCount();i++) {
    NimBLEConnInfo connInfo = pServer->getPeerInfo(i);
    printf("Connected client %d info:\n", i);
    printf("Peer address: %s\n", connInfo.getAddress().toString().c_str());
    printf("Peer ID address: %s\n", connInfo.getIdAddress().toString().c_str());
    printf("Handle: %u\n", connInfo.getConnHandle());
    printf("Interval: %u\n", connInfo.getConnInterval());
    printf("Timeout: %u\n", connInfo.getConnTimeout());
    printf("Latency: %u\n", connInfo.getConnLatency());
    printf("MTU: %u\n", connInfo.getMTU());
    printf("Master: %s\n", connInfo.isMaster()? "true":"false");
    printf("Slave: %s\n", connInfo.isSlave()? "true":"false");
    printf("Bonded: %s\n", connInfo.isBonded()? "true":"false");
    printf("Authenticated: %s\n", connInfo.isAuthenticated()? "true":"false");
    printf("Encrypted: %s\n", connInfo.isEncrypted()? "true":"false");
    printf("Encryption Key Size: %u\n", connInfo.getSecKeySize());
}
```

Example client use:
```
if (pClient->isConnected()) {
    NimBLEConnInfo connInfo = pClient->getConnInfo();
    printf("Connection info:\n");
    printf("Peer address: %s\n", connInfo.getAddress().toString().c_str());
    printf("Peer ID address: %s\n", connInfo.getIdAddress().toString().c_str());
    printf("Handle: %u\n", connInfo.getConnHandle());
    printf("Interval: %u\n", connInfo.getConnInterval());
    printf("Timeout: %u\n", connInfo.getConnTimeout());
    printf("Latency: %u\n", connInfo.getConnLatency());
    printf("MTU: %u\n", connInfo.getMTU());
    printf("Master: %s\n", connInfo.isMaster()? "true":"false");
    printf("Slave: %s\n", connInfo.isSlave()? "true":"false");
    printf("Bonded: %s\n", connInfo.isBonded()? "true":"false");
    printf("Authenticated: %s\n", connInfo.isAuthenticated()? "true":"false");
    printf("Encrypted: %s\n", connInfo.isEncrypted()? "true":"false");
    printf("Encryption Key Size: %u\n", connInfo.getSecKeySize());
}
```
2021-05-07 09:02:43 -06:00
4f8342e275 Add bond management API.
* Adds these new methods to NimBLEDevice to manage bonded peers:

- NimBLEDevice::deleteBond(const NimBLEAddress &address);
- NimBLEDevice::getNumBonds();
- NimBLEDevice::isBonded(const NimBLEAddress &address);
- NimBLEDevice::deleteAllBonds();
- NimBLEDevice::getBondedAddress(int index);
2021-04-25 08:06:38 -06:00
05080abad4 Add clear scan duplicate filter cache method + clear on start.
This allows for clearing the duplicate filter cache when required and will also clear it
automatically when starting a scan.

This is useful when unexpectedly disconnected from a device and attempting to reconnect.
Previously the scan filter would not report advertisements from a device if it was multi-connectable
and advertising while connected. The result was long delays until the device would be scanned again.
This resolves it by clearing the filter cache on each scan start/clearResults call
and adding a method to NimBLEScan for manually clearing the cache on demand.
2021-03-31 20:24:57 -06:00
f5eab87a87 Fix missing data in long write to descriptor. 2021-03-31 17:43:19 -06:00
3c33129600 Remove extra Kconfig and add cmakelists for IDF3 2021-03-28 15:06:27 -06:00
3227681476 Use ESP_LOGx macros for logging (#42)
This allows filtering of this components logging when using higher log levels in IDF.
2021-03-28 14:47:30 -06:00
cf3227503b Use more stable arduino detection methods (#38)
* Use a more stable arduino detection macro.

* Detection method for both IDF config phases. Newer IDF API and targets.
2021-03-04 21:00:21 -07:00
ef049ddf19 Fix compile errors in latest IDF (NimBLE 1.3.0) see (#37).
Const qualifiers were added to multiple struct members in NimBLE 1.3.0.
This solves compilation errors related to those.
2021-02-10 10:54:53 -07:00
7bec9c240a Release 1.2.0 2021-02-08 11:46:11 -07:00
1210772332 Update changelog. 2021-02-08 10:18:59 -07:00
7dd4d68806 [NimBLEServer] Support duplicate characteristics (#36)
* Add method to get all characteristics of a service

* Added method on NimBLEDescriptor to get the value out as a std::string

* Added methods to get things by handle where UUID could be used already.

* Added helper methods for getting a list of characteristics with a given UUID and service

* Demote the warning log for adding duplicate characteristics to a debug level log.

* Add methods to get services and characteristics using UUID + index
2021-02-08 08:28:32 -07:00
28d6492ea4 NimBLEAdvertising::reset check if the stack is initialized before stop.
This prevents an exception when initializing a NimBLEAdvertising instance before calling NimBLEDevice::init().
The constructor calls reset() which calls stop(), if the stack was not yet initialized it will cause an exception.
2021-02-07 22:26:02 -07:00
dff5122ce2 Add complementary methods to NimBLEAdvertising and NimBLEAdvertisementData
Previous to this NimBLEAdvertising did not have some functionality that was in NimBLEAdvertisementData.
Also NimBLEAdvertisementData was missing functionality that exists in NimBLEAdvertising.

This resolves that by adding the missing functions to both classes.

Changed:
NimBLEAdvertising: Transmission power is no longer advertised by default and can be added to the advertisement by calling ::addTxPower()

Added:
- NimBLEAdvertising::setName
- NimBLEAdvertising::setManufacturerData
- NimBLEAdvertising::setURI
- NimBLEAdvertising::setServiceData
- NimBLEAdvertising::addTxPower
- NimBLEAdvertising::reset

- NimBLEAdvertisementData::addTxPower
- NimBLEAdvertisementData::setPreferredParams
- NimBLEAdvertisementData::setURI
2021-02-07 20:33:57 -07:00
6e104dfb45 Allow custom scan response data without custom advertising.
This lets applications use custom scan response data while using the advertisment data added by the NimBLEAdvertising add/set methods.
2021-02-06 14:29:03 -07:00
b290ca9077 [NimBLEAdvertisementData] Add setServices methods for multiple UUID's
Adds:
- setPartialServices16
- setPartialServices32
- setCompleteServices16
- setCompleteServices32

Each takes an input parameter of std::vector<NimBLEUUID> to allow for advertising multiple services with a simple interface.
2021-02-06 13:27:12 -07:00
ae2ff3a638 Fix Documentation. 2021-02-06 11:12:40 -07:00
43c9d96373 Update migration guide to include Characteristic constructor now being private 2021-02-03 20:18:44 -07:00
af24cd7720 Add controller scan filter options and refactor nimconfig.h
* Add the option to change the scan filter mode through NimBLEDevice::setScanFilterMode

* Add the option to change the scan filter cache size through NimBLEDevice::setScanDuplicateCacheSize

* Refactor nimconfig to make it for doxygen use only.
2021-01-31 21:15:31 -07:00
82524c80e3 [NimBLEScan] Use controller duplicate filter and add max result limit.
* Adds setMaxResults to NimBLEScan to limit the number of advertised devices stored.
  If set to 0 no devices will be stored in memory and only the callback will be invoked.
  If set to 0xFF (default) stored devices will be unlimited. Any other value will be the limit.

* Uses the controller to filter duplicate devices from the scan results when wantDuplicates == false.
2021-01-30 18:27:06 -07:00
9527c41486 Refactor advertisedDevice (#181)
* Stores complete advertisement payload and only performs parsing on demand. The advertised data is no longer parsed automatically as it is discovered, instead it will be parsed to find only the data requested by the user application when it makes a call to do so. This saves processing time and approximately 2.4k in flash size, with the new methods listed below included.

Add more advertised device field methods:
* Adds haveAdvInterval/getAdvInterval - checks if the interval is advertised / gets the advertisement interval value.

* Adds haveConnParams/getMinInterval/getMaxInterval - checks if the parameters are advertised / get min value / get max value.

* Adds haveURI/getURI - checks if a URI is advertised / gets the URI data.

* Adds 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.
2021-01-30 18:06:29 -07:00
51c49ba9fc [NimBLEHIDDevice] Modern Apple devices require READ_ENC and WRITE_ENC (#182)
- Added `READ_ENC` to the attribute of characteristics in 1812/2a4d
- Added `READ_ENC` and `WRITE_ENC` to the default values ​​of properties of `createDescriptor()`
2021-01-30 09:04:51 -07:00
51 changed files with 2143 additions and 1015 deletions

View File

@ -2,6 +2,70 @@
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
### 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.
- `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.
@ -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.
## [1.0.2] - 2020-09-13
### 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
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
### Added

View File

@ -2,9 +2,33 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(SUPPORTED_TARGETS esp32)
idf_build_get_property(__hack_component_targets __COMPONENT_TARGETS)
set(COMPONENT_SRCS
if("esp-nimble-component" IN_LIST BUILD_COMPONENTS OR "__esp-nimble-component" IN_LIST __hack_component_targets)
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
esp-nimble-component
)
elseif("nimble" IN_LIST BUILD_COMPONENTS OR "__nimble" IN_LIST __hack_component_targets)
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
nimble
)
endif()
# Dont use arduino bullshit
#if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf_arduino")
# list(APPEND ESP_NIMBLE_PRIV_REQUIRES
# arduino
# )
#endif()
idf_component_register(
REQUIRED_IDF_TARGETS
"esp32"
"esp32s3"
"esp32c3"
INCLUDE_DIRS
"src"
SRCS
"src/FreeRTOS.cpp"
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
@ -17,6 +41,7 @@ set(COMPONENT_SRCS
"src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
"src/NimBLEHIDDevice.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"
@ -26,31 +51,15 @@ set(COMPONENT_SRCS
"src/NimBLEService.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp"
)
set(COMPONENT_ADD_INCLUDEDIRS
src
)
set(COMPONENT_PRIV_REQUIRES
nvs_flash
REQUIRES
bt
nvs_flash
esp_ringbuf
PRIV_REQUIRES
${ESP_NIMBLE_PRIV_REQUIRES}
)
if(COMPONENTS MATCHES "esp-nimble-component")
list(APPEND COMPONENT_PRIV_REQUIRES
esp-nimble-component
)
elseif(COMPONENTS MATCHES "nimble")
list(APPEND COMPONENT_PRIV_REQUIRES
nimble
)
endif()
if(COMPONENTS MATCHES "arduino")
list(APPEND COMPONENT_PRIV_REQUIRES
arduino
)
endif()
register_component()
target_compile_options(${COMPONENT_TARGET}
PUBLIC
-DDONT_USE_ARDUINO_BULLSHIT
)

57
CMakeLists.txt_idf3 Normal file
View File

@ -0,0 +1,57 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(SUPPORTED_TARGETS esp32)
set(COMPONENT_SRCS
"src/FreeRTOS.cpp"
"src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertising.cpp"
"src/NimBLEBeacon.cpp"
"src/NimBLECharacteristic.cpp"
"src/NimBLEClient.cpp"
"src/NimBLEDescriptor.cpp"
"src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp"
"src/NimBLEHIDDevice.cpp"
"src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp"
"src/NimBLEScan.cpp"
"src/NimBLESecurity.cpp"
"src/NimBLEServer.cpp"
"src/NimBLEService.cpp"
"src/NimBLEUtils.cpp"
"src/NimBLEUUID.cpp"
)
set(COMPONENT_ADD_INCLUDEDIRS
src
)
set(COMPONENT_PRIV_REQUIRES
nvs_flash
bt
)
if(COMPONENTS MATCHES "esp-nimble-component")
list(APPEND COMPONENT_PRIV_REQUIRES
esp-nimble-component
)
elseif(COMPONENTS MATCHES "nimble")
list(APPEND COMPONENT_PRIV_REQUIRES
nimble
)
endif()
if(COMPONENTS MATCHES "arduino")
list(APPEND COMPONENT_PRIV_REQUIRES
arduino
)
endif()
register_component()

View File

@ -1,27 +0,0 @@
menu "ESP-NimBLE-CPP configuration"
config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
bool "Show NimBLE return codes as text in debug log."
default "n"
help
Enabling this option will display return code values as text
messages in the debug log. This will use approximately 8kB
of flash memory.
config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
bool "Show NimBLE gap events as text in debug log."
default "n"
help
Enabling this option will display gap event codes as text
messages in the debug log. This will use approximately 1kB
of flash memory.
config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
bool "Show advertisment types as text in debug log."
default "n"
help
Enabling this option will display advertisment types recieved
while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory.
endmenu

View File

@ -1,27 +1,21 @@
# Arduino command line and platformio config options
`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED`
If defined, NimBLE Client functions will not be included.
- Reduces flash size by approx. 7kB.
`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
Sets the number of simultaneous connections (esp controller max is 9)
- Default value is 3
<br/>
`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED`
`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
If defined, NimBLE Scan functions will not be included.
- Reduces flash size by approx. 26kB.
Sets the default MTU size.
- Default value is 255
<br/>
`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED`
`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
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.
Set the default device name
- Default value is "nimble"
<br/>
`CONFIG_BT_NIMBLE_DEBUG`
@ -48,6 +42,67 @@ If defined, advertisment types will be printed as text while scanning in debug l
- Uses approx. 250 bytes of flash memory.
<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`
Sets the core the NimBLE host stack will run on
@ -56,37 +111,7 @@ Sets the core the NimBLE host stack will run on
`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
<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

@ -38,7 +38,7 @@ PROJECT_NAME = "esp-nimble-cpp / NimBLE-Arduino"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.1.0
PROJECT_NUMBER = 1.2.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@ -2179,8 +2179,12 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = CONFIG_BT_ENABLED \
_DOXYGEN_
PREDEFINED = _DOXYGEN_ \
CONFIG_BT_ENABLED \
CONFIG_BT_NIMBLE_ROLE_CENTRAL \
CONFIG_BT_NIMBLE_ROLE_OBSERVER \
CONFIG_BT_NIMBLE_ROLE_PERIPHERAL \
CONFIG_BT_NIMBLE_ROLE_BROADCASTER
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View File

@ -69,6 +69,8 @@ For example `BLEServer::createService(SERVICE_UUID)` will work just as it did be
<a name="characteristics"></a>
### 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.
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/>
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.
When using custom scan response data you must also use custom advertisement data.
~~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.~~
No longer true as of release 1.2.0 and above, custom scan response is now supported without custom advertisement data.
<br/>
> BLEAdvertising::start (NimBLEAdvertising::start)

View File

@ -155,7 +155,7 @@ void FreeRTOS::Semaphore::give() {
} else {
xSemaphoreGive(m_semaphore);
}
// #ifdef ARDUINO_ARCH_ESP32
// #if defined(ARDUINO_ARCH_ESP32) && !defined(DONT_USE_ARDUINO_BULLSHIT)
// FreeRTOS::sleep(10);
// #endif
@ -243,7 +243,7 @@ bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) {
std::string FreeRTOS::Semaphore::toString() {
char hex[9];
std::string res = "name: " + m_name + " (0x";
snprintf(hex, sizeof(hex), "%08x", (uint32_t)m_semaphore);
snprintf(hex, sizeof(hex), "%08lx", (uint32_t)m_semaphore);
res += hex;
res += "), owner: " + m_owner;
return res;

View File

@ -45,7 +45,7 @@
/* of data as per HID Class standard */
/* Main items */
#ifdef ARDUINO_ARCH_ESP32
#if defined(ARDUINO_ARCH_ESP32) && !defined(DONT_USE_ARDUINO_BULLSHIT)
#define HIDINPUT(size) (0x80 | size)
#define HIDOUTPUT(size) (0x90 | size)
#else

View File

@ -42,6 +42,7 @@ struct BLE2904_Data {
*/
class NimBLE2904: public NimBLEDescriptor {
public:
NimBLE2904(NimBLECharacteristic* pCharacterisitic = nullptr);
static const uint8_t FORMAT_BOOLEAN = 1;
static const uint8_t FORMAT_UINT2 = 2;
static const uint8_t FORMAT_UINT4 = 3;
@ -77,7 +78,6 @@ public:
void setUnit(uint16_t unit);
private:
NimBLE2904(NimBLECharacteristic* pCharacterisitic);
friend class NimBLECharacteristic;
BLE2904_Data m_data;
}; // BLE2904

View File

@ -12,7 +12,7 @@
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include <algorithm>

View File

@ -15,7 +15,7 @@
#ifndef COMPONENTS_NIMBLEADDRESS_H_
#define COMPONENTS_NIMBLEADDRESS_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "nimble/ble.h"
/**** FIX COMPILATION ****/

View File

@ -28,25 +28,14 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
/**
* @brief Constructor
*/
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() :
m_payload(62,0)
{
m_advType = 0;
m_appearance = 0;
m_manufacturerData = "";
m_name = "";
m_rssi = -9999;
m_txPower = 0;
m_payloadLength = 0;
m_payload = nullptr;
m_haveAppearance = false;
m_haveManufacturerData = false;
m_haveName = false;
m_haveRSSI = false;
m_haveServiceData = false;
m_haveServiceUUID = false;
m_haveTXPower = false;
m_callbackSent = false;
m_callbackSent = false;
m_timestamp = 0;
m_advLength = 0;
} // NimBLEAdvertisedDevice
@ -82,25 +71,126 @@ uint8_t NimBLEAdvertisedDevice::getAdvType() {
* @return The appearance of the advertised device.
*/
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
/**
* @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.
* @return The manufacturer data of the advertised device.
*/
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
/**
* @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.
* @return The name of the advertised device.
*/
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
@ -122,17 +212,70 @@ NimBLEScan* NimBLEAdvertisedDevice::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.
* @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.
*/
std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
if(index > m_serviceDataVec.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceData: index out of range");
return "";
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
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
@ -141,51 +284,148 @@ std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
* @param [in] uuid The uuid of the service data requested.
* @return The advertised service data or empty string if no data.
*/
std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) const {
for(auto &it : m_serviceDataVec) {
if(it.first == uuid) {
return it.second;
std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) {
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
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 "";
} //getServiceData
/**
* @brief Get the advertised service UUID.
* @param [in] index The vector index of the service data UUID requested.
* @return The advertised service UUID or an empty UUID if not found.
* @brief Get the UUID of the serice data at the index.
* @param [in] index The index of the service data UUID requested.
* @return The advertised service data UUID or an empty UUID if not found.
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
if(!haveServiceData() || index > m_serviceDataVec.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceDataUUID: index out of range");
return NimBLEUUID("");
ble_hs_adv_field *field = nullptr;
uint8_t bytes;
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
/**
* @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
* @return The number of service data UUIDS in the vector.
*/
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
/**
* @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.
*/
NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
if(!haveServiceUUID() || index > m_serviceUUIDs.size()) {
NIMBLE_LOGW(LOG_TAG, "getServiceUUID: index out of range");
return NimBLEUUID("");
uint8_t count = 0;
uint8_t data_loc = 0;
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
@ -194,18 +434,32 @@ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
* @return The count of services in the advertising packet.
*/
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
/**
* @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
*/
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const {
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
if (m_serviceUUIDs[i].equals(uuid)) return true;
bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) {
size_t count = getServiceUUIDCount();
for(size_t i = 0; i < count; i++) {
if(uuid == getServiceUUID(i)) {
return true;
}
}
return false;
} // isAdvertisingService
@ -215,16 +469,43 @@ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const
* @return The TX Power of the advertised device.
*/
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
/**
* @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?
* @return True if there is an appearance value present.
*/
bool NimBLEAdvertisedDevice::haveAppearance() {
return m_haveAppearance;
return findAdvField(BLE_HS_ADV_TYPE_APPEARANCE) > 0;
} // haveAppearance
@ -233,16 +514,36 @@ bool NimBLEAdvertisedDevice::haveAppearance() {
* @return True if there is manufacturer data present.
*/
bool NimBLEAdvertisedDevice::haveManufacturerData() {
return m_haveManufacturerData;
return findAdvField(BLE_HS_ADV_TYPE_MFG_DATA) > 0;
} // 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?
* @return True if there is a name value present.
*/
bool NimBLEAdvertisedDevice::haveName() {
return m_haveName;
return findAdvField(BLE_HS_ADV_TYPE_COMP_NAME) > 0 ||
findAdvField(BLE_HS_ADV_TYPE_INCOMP_NAME) > 0;
} // haveName
@ -251,7 +552,7 @@ bool NimBLEAdvertisedDevice::haveName() {
* @return True if there is a signal strength value present.
*/
bool NimBLEAdvertisedDevice::haveRSSI() {
return m_haveRSSI;
return m_rssi != -9999;
} // haveRSSI
@ -260,7 +561,7 @@ bool NimBLEAdvertisedDevice::haveRSSI() {
* @return True if there is a service data value present.
*/
bool NimBLEAdvertisedDevice::haveServiceData() {
return m_haveServiceData;
return getServiceDataCount() > 0;
} // haveServiceData
@ -269,7 +570,7 @@ bool NimBLEAdvertisedDevice::haveServiceData() {
* @return True if there is a service UUID value present.
*/
bool NimBLEAdvertisedDevice::haveServiceUUID() {
return m_haveServiceUUID;
return getServiceUUIDCount() > 0;
} // haveServiceUUID
@ -278,143 +579,71 @@ bool NimBLEAdvertisedDevice::haveServiceUUID() {
* @return True if there is a transmission power value present.
*/
bool NimBLEAdvertisedDevice::haveTXPower() {
return m_haveTXPower;
return findAdvField(BLE_HS_ADV_TYPE_TX_PWR_LVL) > 0;
} // haveTXPower
/**
* @brief Parse the advertising pay load.
*
* The pay load is a buffer of bytes that is either 31 bytes long or terminated by
* a 0 length value. Each entry in the buffer has the format:
* [length][type][data...]
*
* The length does not include itself but does include everything after it until the next record. A record
* 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;
uint8_t NimBLEAdvertisedDevice::findAdvField(uint8_t type, uint8_t index, uint8_t *data_loc) {
ble_hs_adv_field *field = nullptr;
uint8_t data = 0;
uint8_t length = m_payload.size();
uint8_t count = 0;
if(length < 2) {
return count;
}
m_payload = payload;
m_payloadLength = length;
while (length > 1) {
field = (ble_hs_adv_field*)&m_payload[data];
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
char* pHex = NimBLEUtils::buildHexData(nullptr, m_payload, m_payloadLength);
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 (field->length >= length) {
return count;
}
}
if (fields.uuids32 != NULL) {
for (int i = 0; i < fields.num_uuids32; i++) {
setServiceUUID(NimBLEUUID(fields.uuids32[i].value));
}
}
if (field->type == type) {
switch(type) {
case BLE_HS_ADV_TYPE_INCOMP_UUIDS16:
case BLE_HS_ADV_TYPE_COMP_UUIDS16:
count += field->length / 2;
break;
if (fields.uuids128 != NULL) {
for (int i = 0; i < fields.num_uuids128; i++) {
setServiceUUID(NimBLEUUID(&fields.uuids128[i]));
}
}
case BLE_HS_ADV_TYPE_INCOMP_UUIDS32:
case BLE_HS_ADV_TYPE_COMP_UUIDS32:
count += field->length / 4;
break;
if (fields.name != NULL) {
setName(std::string(reinterpret_cast<char*>(fields.name), fields.name_len));
}
case BLE_HS_ADV_TYPE_INCOMP_UUIDS128:
case BLE_HS_ADV_TYPE_COMP_UUIDS128:
count += field->length / 16;
break;
if (fields.tx_pwr_lvl_is_present) {
setTXPower(fields.tx_pwr_lvl);
}
case BLE_HS_ADV_TYPE_PUBLIC_TGT_ADDR:
case BLE_HS_ADV_TYPE_RANDOM_TGT_ADDR:
count += field->length / 6;
break;
if (fields.svc_data_uuid16 != NULL ||
fields.svc_data_uuid32 != NULL ||
fields.svc_data_uuid128 != NULL)
{
ble_hs_adv_field *field;
uint8_t *data = payload;
while(length > 1) {
field = (ble_hs_adv_field*)data;
if(field->length > length) {
break;
default:
count++;
break;
}
if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID16) {
if(field->length > 2) {
uint16_t uuid;
memcpy(&uuid, field->value, 2);
setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast<char*>(field->value + 2), field->length - 3));
if(data_loc != nullptr) {
if(index == 0 || count >= index) {
break;
}
}
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) {
setAppearance(fields.appearance);
if(data_loc != nullptr && field != nullptr) {
*data_loc = data;
}
if (fields.mfg_data != NULL) {
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
return count;
}
/**
@ -428,106 +657,22 @@ void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) {
/**
* @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) {
m_advType = advType;
} // 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.
* @param [in] rssi The discovered RSSI.
* @param [in] rssi The RSSI of the discovered device.
*/
void NimBLEAdvertisedDevice::setRSSI(int rssi) {
m_rssi = rssi;
m_haveRSSI = true;
m_rssi = rssi;
} // 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.
* @return A string representation of this device.
@ -579,10 +724,35 @@ std::string NimBLEAdvertisedDevice::toString() {
* @return The advertisement payload.
*/
uint8_t* NimBLEAdvertisedDevice::getPayload() {
return m_payload;
return &m_payload[0];
} // 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(const 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.
* @return The device address type:
@ -610,7 +780,7 @@ time_t NimBLEAdvertisedDevice::getTimestamp() {
* @return The size of the payload in bytes.
*/
size_t NimBLEAdvertisedDevice::getPayloadLength() {
return m_payloadLength;
return m_payload.size();
} // getPayloadLength

View File

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

View File

@ -32,23 +32,29 @@ static const char* LOG_TAG = "NimBLEAdvertising";
/**
* @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_scanData, 0, sizeof m_scanData);
memset(&m_advParams, 0, sizeof m_advParams);
memset(&m_slaveItvl, 0, sizeof m_slaveItvl);
const char *name = ble_svc_gap_device_name();
m_advData.name = (uint8_t *)name;
m_advData.name_len = strlen(name);
m_advData.name_is_complete = 1;
m_advData.tx_pwr_lvl_is_present = 1;
m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
m_advData.appearance = 0;
m_advData.appearance_is_present = 0;
m_advData.mfg_data_len = 0;
m_advData.mfg_data = nullptr;
m_advData.slave_itvl_range = nullptr;
#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
@ -56,17 +62,13 @@ NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() {
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
#endif
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
m_advParams.itvl_min = 0;
m_advParams.itvl_max = 0;
m_customAdvData = false;
m_customScanResponseData = false;
m_scanResp = true;
m_advDataSet = false;
// Set this to non-zero to prevent auto start if host reset before started by app.
m_duration = BLE_HS_FOREVER;
} // NimBLEAdvertising
} // reset
/**
@ -85,6 +87,7 @@ void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) {
*/
void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
addServiceUUID(NimBLEUUID(serviceUUID));
m_advDataSet = false;
} // addServiceUUID
@ -112,9 +115,95 @@ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
void NimBLEAdvertising::setAppearance(uint16_t appearance) {
m_advData.appearance = appearance;
m_advData.appearance_is_present = 1;
m_advDataSet = false;
} // 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.
* @param [in] adv_type:
@ -172,6 +261,8 @@ void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
m_slaveItvl[2] = m_slaveItvl[0];
m_slaveItvl[3] = m_slaveItvl[1];
}
m_advDataSet = false;
} // setMinPreferred
@ -200,6 +291,8 @@ void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
m_slaveItvl[0] = m_slaveItvl[2];
m_slaveItvl[1] = m_slaveItvl[3];
}
m_advDataSet = false;
} // setMaxPreferred
@ -209,6 +302,7 @@ void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
*/
void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set;
m_advDataSet = false;
} // setScanResponse
@ -344,12 +438,29 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data
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)
payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN);
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)
payloadLen += (2 + 4);
payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
@ -360,16 +471,14 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
payloadLen += add;
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16,
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
&it.getNative()->u16.value, 2);
m_advData.uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16;
memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16],
&it.getNative()->u16, sizeof(ble_uuid16_t));
m_advData.uuids16_is_complete = 1;
m_advData.num_uuids16++;
}
@ -381,16 +490,14 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
payloadLen += add;
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32,
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
&it.getNative()->u32.value, 4);
m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32;
memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32],
&it.getNative()->u32, sizeof(ble_uuid32_t));
m_advData.uuids32_is_complete = 1;
m_advData.num_uuids32++;
}
@ -402,16 +509,14 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
payloadLen += add;
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
&it.getNative()->u128.value, 16);
m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128;
memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128],
&it.getNative()->u128, sizeof(ble_uuid128_t));
m_advData.uuids128_is_complete = 1;
m_advData.num_uuids128++;
}
@ -419,7 +524,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
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_len = m_advData.name_len;
if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) {
@ -433,7 +538,6 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advData.name_is_complete = 0;
} else {
if(m_advData.tx_pwr_lvl_is_present) {
m_advData.tx_pwr_lvl = 0;
m_advData.tx_pwr_lvl_is_present = 0;
payloadLen -= (2 + 1);
}
@ -446,7 +550,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);
switch(rc) {
case 0:
@ -489,19 +593,19 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(m_advData.num_uuids128 > 0) {
free(m_advData.uuids128);
free((void*)m_advData.uuids128);
m_advData.uuids128 = nullptr;
m_advData.num_uuids128 = 0;
}
if(m_advData.num_uuids32 > 0) {
free(m_advData.uuids32);
free((void*)m_advData.uuids32);
m_advData.uuids32 = nullptr;
m_advData.num_uuids32 = 0;
}
if(m_advData.num_uuids16 > 0) {
free(m_advData.uuids16);
free((void*)m_advData.uuids16);
m_advData.uuids16 = nullptr;
m_advData.num_uuids16 = 0;
}
@ -581,7 +685,7 @@ void NimBLEAdvertising::advCompleteCB() {
if(m_advCompCB != nullptr) {
m_advCompCB(this);
}
}
} // advCompleteCB
/**
@ -590,7 +694,7 @@ void NimBLEAdvertising::advCompleteCB() {
*/
bool NimBLEAdvertising::isAdvertising() {
return ble_gap_adv_active();
}
} // isAdvertising
/*
@ -608,7 +712,7 @@ void NimBLEAdvertising::onHostSync() {
// Otherwise we should tell the app that advertising stopped.
advCompleteCB();
}
}
} // onHostSync
/**
@ -646,6 +750,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
*/
void NimBLEAdvertisementData::addData(const std::string &data) {
if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceded");
return;
}
m_payload.append(data);
@ -657,11 +762,8 @@ void NimBLEAdvertisementData::addData(const std::string &data) {
* @param [in] data The 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){
if ((m_payload.length() + length) > BLE_HS_ADV_MAX_SZ) {
return;
}
m_payload.append(data,length);
void NimBLEAdvertisementData::addData(char * data, size_t length) {
addData(std::string(data, length));
} // addData
@ -680,43 +782,6 @@ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
} // 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.
* @param [in] flag The flags to be set in the advertisement.
@ -738,64 +803,141 @@ void NimBLEAdvertisementData::setFlags(uint8_t flag) {
* @param [in] data The manufacturer data to advertise.
*/
void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setManufacturerData");
char cdata[2];
cdata[0] = data.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
addData(std::string(cdata, 2) + data);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< 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.
* @param [in] name The name to advertise.
*/
void NimBLEAdvertisementData::setName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str());
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09
addData(std::string(cdata, 2) + name);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< setName");
} // setName
/**
* @brief Set the partial services to advertise.
* @param [in] uuid The single service to advertise.
* @brief Set a single service to advertise as a complete list of services.
* @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) {
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];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x02] [LL] [HH]
cdata[0] = 3;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; // 0x02
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u16.value, 2));
cdata[0] = (size / 8) * v_uuid.size() + 1;
switch(size) {
case 16:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16;
break;
}
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));
case 32:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
break;
}
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));
case 128:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
break;
}
default:
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 +983,42 @@ void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::
* @param [in] name The short name of the device.
*/
void NimBLEAdvertisementData::setShortName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str());
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08
addData(std::string(cdata, 2) + name);
NIMBLE_LOGD("NimBLEAdvertisementData", "<< 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.
* @return The payload that is to be advertised.

View File

@ -50,18 +50,27 @@ class NimBLEAdvertisementData {
public:
void setAppearance(uint16_t appearance);
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 setManufacturerData(const std::string &data);
void setURI(const std::string &uri);
void setName(const std::string &name);
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 setShortName(const std::string &name);
void addData(const std::string &data); // Add data to the payload.
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.
private:
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.
}; // NimBLEAdvertisementData
@ -80,6 +89,10 @@ public:
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop();
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 setMaxInterval(uint16_t maxinterval);
void setMinInterval(uint16_t mininterval);
@ -89,6 +102,8 @@ public:
void setScanResponse(bool);
void setMinPreferred(uint16_t);
void setMaxPreferred(uint16_t);
void addTxPower();
void reset();
void advCompleteCB();
bool isAdvertising();
@ -109,6 +124,12 @@ private:
void (*m_advCompCB)(NimBLEAdvertising *pAdv);
uint8_t m_slaveItvl[4];
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)

View File

@ -12,7 +12,7 @@
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include <string.h>
#include <algorithm>

View File

@ -15,6 +15,7 @@
#ifndef MAIN_NIMBLEBEACON_H_
#define MAIN_NIMBLEBEACON_H_
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "NimBLEUUID.h"
/**
* @brief Representation of a beacon.
@ -48,4 +49,5 @@ public:
void setSignalPower(int8_t signalPower);
}; // NimBLEBeacon
#endif
#endif /* MAIN_NIMBLEBEACON_H_ */

View File

@ -52,7 +52,6 @@ NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t prop
m_pService = pService;
m_value = "";
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_pTaskData = nullptr;
m_timestamp = 0;
} // NimBLECharacteristic
@ -95,15 +94,26 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
}
m_dscVec.push_back(pDescriptor);
addDescriptor(pDescriptor);
return pDescriptor;
} // createCharacteristic
} // createDescriptor
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] uuid The UUID of the descriptor that we wish to retrieve.
* @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
* @brief Add a descriptor to the characteristic.
* @param [in] A pointer to the descriptor to add.
*/
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor *pDescriptor) {
pDescriptor->setCharacteristic(this);
m_dscVec.push_back(pDescriptor);
}
/**
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return getDescriptorByUUID(NimBLEUUID(uuid));
@ -111,9 +121,9 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] uuid The UUID of the descriptor that we wish to retrieve.
* @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
* @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
for (auto &it : m_dscVec) {
@ -124,6 +134,20 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu
return nullptr;
} // 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.
@ -151,6 +175,11 @@ NimBLEService* NimBLECharacteristic::getService() {
} // getService
void NimBLECharacteristic::setService(NimBLEService *pService) {
m_pService = pService;
}
/**
* @brief Get the UUID of the characteristic.
* @return The UUID of the characteristic.
@ -287,16 +316,13 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
subVal |= NIMBLE_SUB_INDICATE;
}
if(m_pTaskData != nullptr) {
m_pTaskData->rc = (subVal & NIMBLE_SUB_INDICATE) ? 0 :
NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED;
xTaskNotifyGive(m_pTaskData->task);
}
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
event->subscribe.conn_handle, subVal);
event->subscribe.conn_handle, subVal);
if(!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
NimBLEDevice::getServer()->clearIndicateWait(event->subscribe.conn_handle);
}
m_pCallbacks->onSubscribe(this, &desc, subVal);
auto it = m_subscribedVec.begin();
for(;it != m_subscribedVec.end(); ++it) {
@ -308,16 +334,14 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
if(subVal > 0) {
if(it == m_subscribedVec.end()) {
m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
return;
} else {
(*it).second = subVal;
}
(*it).second = subVal;
} else if(it != m_subscribedVec.end()) {
m_subscribedVec.erase(it);
m_subscribedVec.shrink_to_fit();
}
m_pCallbacks->onSubscribe(this, &desc, subVal);
}
@ -402,40 +426,20 @@ void NimBLECharacteristic::notify(bool is_notification) {
// We also must create it in each loop iteration because it is consumed with each host call.
os_mbuf *om = ble_hs_mbuf_from_flat((uint8_t*)value.data(), length);
NimBLECharacteristicCallbacks::Status statusRC;
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
ble_task_data_t taskData = {nullptr, xTaskGetCurrentTaskHandle(),0, nullptr};
m_pTaskData = &taskData;
if(!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
NIMBLE_LOGE(LOG_TAG, "prior Indication in progress");
os_mbuf_free_chain(om);
return;
}
rc = ble_gattc_indicate_custom(it.first, m_handle, om);
if(rc != 0){
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
} else {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = m_pTaskData->rc;
}
m_pTaskData = nullptr;
if(rc == BLE_HS_EDONE) {
rc = 0;
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
} else if(rc == BLE_HS_ETIMEOUT) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
} else {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
NimBLEDevice::getServer()->clearIndicateWait(it.first);
}
} else {
rc = ble_gattc_notify_custom(it.first, m_handle, om);
if(rc == 0) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
}
ble_gattc_notify_custom(it.first, m_handle, om);
}
m_pCallbacks->onStatus(this, statusRC, rc);
}
NIMBLE_LOGD(LOG_TAG, "<< notify");
@ -455,6 +459,13 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
}
} // setCallbacks
/**
* @brief Get the callback handlers for this characteristic.
*/
NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() {
return m_pCallbacks;
} //getCallbacks
/**
* @brief Set the value of the characteristic.
@ -462,7 +473,7 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
* @param [in] length The length of the data in bytes.
*/
void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && !defined(DONT_USE_ARDUINO_BULLSHIT) && CORE_DEBUG_LEVEL >= 4)
char* pHex = NimBLEUtils::buildHexData(nullptr, data, length);
NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
free(pHex);

View File

@ -59,6 +59,31 @@ class NimBLECharacteristicCallbacks;
*/
class NimBLECharacteristic {
public:
NimBLECharacteristic(const char* uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
~NimBLECharacteristic();
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
NimBLECharacteristicCallbacks*
getCallbacks();
void indicate();
void notify(bool is_notification = true);
size_t getSubscribedCount();
NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties =
NIMBLE_PROPERTY::READ |
@ -70,11 +95,13 @@ public:
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
void addDescriptor(NimBLEDescriptor *pDescriptor);
NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEUUID getUUID();
std::string getValue(time_t *timestamp = nullptr);
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle);
std::string getValue(time_t *timestamp = nullptr);
size_t getDataLength();
/**
* @brief A template to convert the characteristic data to <type\>.
* @tparam T The type to convert the data to.
@ -92,46 +119,26 @@ public:
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 std::string &value);
/**
* @brief Convenience template to set the characteristic value to <type\>val.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) {
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
std::string toString();
uint16_t getHandle();
size_t getSubscribedCount();
NimBLEService* getService();
uint16_t getProperties();
private:
friend class NimBLEServer;
friend class NimBLEService;
friend class NimBLEServer;
friend class NimBLEService;
NimBLECharacteristic(const char* uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr);
~NimBLECharacteristic();
NimBLEService* getService();
uint16_t getProperties();
void setService(NimBLEService *pService);
void setSubscribe(struct ble_gap_event *event);
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
@ -143,7 +150,6 @@ private:
NimBLEService* m_pService;
std::string m_value;
std::vector<NimBLEDescriptor*> m_dscVec;
ble_task_data_t *m_pTaskData;
portMUX_TYPE m_valMux;
time_t m_timestamp;

View File

@ -436,6 +436,24 @@ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
} // updateConnParams
/**
* @brief Get detailed information about the current peer connection.
*/
NimBLEConnInfo NimBLEClient::getConnInfo() {
NimBLEConnInfo connInfo;
if (!isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Not connected");
} else {
int rc = ble_gap_conn_find(m_conn_id, &connInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Connection info not found");
}
}
return connInfo;
} // getConnInfo
/**
* @brief Set the timeout to wait for connection attempt to complete.
* @param [in] time The number of seconds before timeout.
@ -1016,7 +1034,7 @@ uint16_t NimBLEClient::getMTU() {
NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %d", event->passkey.params.numcmp);
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %lu", event->passkey.params.numcmp);
pkey.action = event->passkey.params.action;
// Compatibility only - Do not use, should be removed the in future
if(NimBLEDevice::m_securityCallbacks != nullptr) {

View File

@ -23,6 +23,7 @@
#include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include "NimBLEUtils.h"
#include "NimBLEConnInfo.h"
#include "NimBLEAdvertisedDevice.h"
#include "NimBLERemoteService.h"
@ -71,6 +72,7 @@ public:
void updateConnParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout);
void discoverAttributes();
NimBLEConnInfo getConnInfo();
private:
NimBLEClient(const NimBLEAddress &peerAddress);

55
src/NimBLEConnInfo.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef NIMBLECONNINFO_H_
#define NIMBLECONNINFO_H_
#include "NimBLEAddress.h"
/**
* @brief Connection information.
*/
class NimBLEConnInfo {
friend class NimBLEServer;
friend class NimBLEClient;
ble_gap_conn_desc m_desc;
NimBLEConnInfo() { m_desc = {}; }
NimBLEConnInfo(ble_gap_conn_desc desc) { m_desc = desc; }
public:
/** @brief Gets the over-the-air address of the connected peer */
NimBLEAddress getAddress() { return NimBLEAddress(m_desc.peer_ota_addr); }
/** @brief Gets the ID address of the connected peer */
NimBLEAddress getIdAddress() { return NimBLEAddress(m_desc.peer_id_addr); }
/** @brief Gets the connection handle of the connected peer */
uint16_t getConnHandle() { return m_desc.conn_handle; }
/** @brief Gets the connection interval for this connection (in 1.25ms units) */
uint16_t getConnInterval() { return m_desc.conn_itvl; }
/** @brief Gets the supervision timeout for this connection (in 10ms units) */
uint16_t getConnTimeout() { return m_desc.supervision_timeout; }
/** @brief Gets the allowable latency for this connection (unit = number of intervals) */
uint16_t getConnLatency() { return m_desc.conn_latency; }
/** @brief Gets the maximum transmission unit size for this connection (in bytes) */
uint16_t getMTU() { return ble_att_mtu(m_desc.conn_handle); }
/** @brief Check if we are in the master role in this connection */
bool isMaster() { return (m_desc.role == BLE_GAP_ROLE_MASTER); }
/** @brief Check if we are in the slave role in this connection */
bool isSlave() { return (m_desc.role == BLE_GAP_ROLE_SLAVE); }
/** @brief Check if we are connected to a bonded peer */
bool isBonded() { return (m_desc.sec_state.bonded == 1); }
/** @brief Check if the connection in encrypted */
bool isEncrypted() { return (m_desc.sec_state.encrypted == 1); }
/** @brief Check if the the connection has been authenticated */
bool isAuthenticated() { return (m_desc.sec_state.authenticated == 1); }
/** @brief Gets the key size used to encrypt the connection */
uint8_t getSecKeySize() { return m_desc.sec_state.key_size; }
};
#endif

View File

@ -37,6 +37,7 @@ NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16
: NimBLEDescriptor(NimBLEUUID(uuid), max_len, properties, pCharacteristic) {
}
/**
* @brief NimBLEDescriptor constructor.
*/
@ -47,7 +48,7 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
m_value.attr_len = 0; // Initial length is 0.
m_value.attr_max_len = max_len; // Maximum length of the data.
m_handle = NULL_HANDLE; // Handle is initially unknown.
m_pCharacteristic = nullptr; // No initial characteristic.
m_pCharacteristic = pCharacteristic;
m_pCallbacks = &defaultCallbacks; // No initial callback.
m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value.
m_valMux = portMUX_INITIALIZER_UNLOCKED;
@ -123,10 +124,26 @@ uint8_t* NimBLEDescriptor::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);
}
/**
* @brief Get the characteristic this descriptor belongs to.
* @return A pointer to the characteristic this descriptor belongs to.
*/
NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() {
return m_pCharacteristic;
} // getCharacteristic
int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
struct ble_gatt_access_ctxt *ctxt, void *arg) {
const ble_uuid_t *uuid;
int rc;
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
@ -163,7 +180,7 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
if((len + next->om_len) > pDescriptor->m_value.attr_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len-1], next->om_data, next->om_len);
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
@ -231,6 +248,14 @@ void NimBLEDescriptor::setValue(const std::string &value) {
setValue((uint8_t*) value.data(), value.length());
} // setValue
/**
* @brief Set the characteristic this descriptor belongs to.
* @param [in] pChar A pointer to the characteristic this descriptior belongs to.
*/
void NimBLEDescriptor::setCharacteristic(NimBLECharacteristic* pChar) {
m_pCharacteristic = pChar;
} // setCharacteristic
/**
* @brief Return a string representation of the descriptor.

View File

@ -43,14 +43,29 @@ class NimBLEDescriptorCallbacks;
*/
class NimBLEDescriptor {
public:
uint16_t getHandle();
size_t getLength();
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();
NimBLEDescriptor(const char* uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr);
NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr);
~NimBLEDescriptor();
uint16_t getHandle();
NimBLEUUID getUUID();
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);
NimBLECharacteristic* getCharacteristic();
/**
* @brief Convenience template to set the descriptor value to <type\>val.
@ -64,22 +79,12 @@ public:
private:
friend class NimBLECharacteristic;
friend class NimBLEService;
friend class NimBLE2902;
friend class NimBLE2904;
NimBLEDescriptor(const char* uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic);
NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties,
uint16_t max_len,
NimBLECharacteristic* pCharacteristic);
~NimBLEDescriptor();
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
void setHandle(uint16_t handle);
void setCharacteristic(NimBLECharacteristic* pChar);
NimBLEUUID m_uuid;
uint16_t m_handle;

View File

@ -12,7 +12,7 @@
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "nimconfig.h"
#include "NimBLEDevice.h"
@ -30,7 +30,7 @@
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#ifdef ARDUINO_ARCH_ESP32
#if defined(CONFIG_ENABLE_ARDUINO_DEPENDS) && !defined(DONT_USE_ARDUINO_BULLSHIT)
#include "esp32-hal-bt.h"
#endif
@ -60,8 +60,11 @@ ble_gap_event_listener NimBLEDevice::m_listener;
std::list <NimBLEClient*> NimBLEDevice::m_cList;
#endif
std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
std::vector<NimBLEAddress> NimBLEDevice::m_whiteList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
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 +306,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:
* * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0
* * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1
@ -342,7 +345,7 @@ void NimBLEDevice::stopAdvertising() {
default:
return BLE_HS_ADV_TX_PWR_LVL_AUTO;
}
} // setPower
} // getPower
/**
@ -400,6 +403,258 @@ 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;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
/**
* @brief Gets the number of bonded peers stored
*/
/*STATIC*/
int NimBLEDevice::getNumBonds() {
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
int num_peers, rc;
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
if (rc !=0) {
return 0;
}
return num_peers;
}
/**
* @brief Deletes all bonding information.
*/
/*STATIC*/
void NimBLEDevice::deleteAllBonds() {
ble_store_clear();
}
/**
* @brief Deletes a peer bond.
* @param [in] address The address of the peer with which to delete bond info.
* @returns true on success.
*/
/*STATIC*/
bool NimBLEDevice::deleteBond(const NimBLEAddress &address) {
ble_addr_t delAddr;
memcpy(&delAddr.val, address.getNative(),6);
delAddr.type = address.getType();
int rc = ble_gap_unpair(&delAddr);
if (rc != 0) {
return false;
}
return true;
}
/**
* @brief Checks if a peer device is bonded.
* @param [in] address The address to check for bonding.
* @returns true if bonded.
*/
/*STATIC*/
bool NimBLEDevice::isBonded(const NimBLEAddress &address) {
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
int num_peers, rc;
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
if (rc != 0) {
return false;
}
for (int i = 0; i < num_peers; i++) {
NimBLEAddress storedAddr(peer_id_addrs[i]);
if(storedAddr == address) {
return true;
}
}
return false;
}
/**
* @brief Get the address of a bonded peer device by index.
* @param [in] index The index to retrieve the peer address of.
* @returns NimBLEAddress of the found bonded peer or nullptr if not found.
*/
/*STATIC*/
NimBLEAddress NimBLEDevice::getBondedAddress(int index) {
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
int num_peers, rc;
rc = ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
if (rc != 0) {
return nullptr;
}
if (index > num_peers || index < 0) {
return nullptr;
}
return NimBLEAddress(peer_id_addrs[index]);
}
#endif
/**
* @brief Checks if a peer device is whitelisted.
* @param [in] address The address to check for in the whitelist.
* @returns true if the address is in the whitelist.
*/
bool NimBLEDevice::onWhiteList(const NimBLEAddress & address) {
for (auto &it : m_whiteList) {
if (it == address) {
return true;
}
}
return false;
}
/**
* @brief Add a peer address to the whitelist.
* @param [in] address The address to add to the whitelist.
* @returns true if successful.
*/
bool NimBLEDevice::whiteListAdd(const NimBLEAddress & address) {
if (NimBLEDevice::onWhiteList(address)) {
return true;
}
m_whiteList.push_back(address);
std::vector<ble_addr_t> wlVec;
wlVec.reserve(m_whiteList.size());
for (auto &it : m_whiteList) {
ble_addr_t wlAddr;
memcpy(&wlAddr.val, it.getNative(), 6);
wlAddr.type = it.getType();
wlVec.push_back(wlAddr);
}
int rc = ble_gap_wl_set(&wlVec[0], wlVec.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed adding to whitelist rc=%d", rc);
return false;
}
return true;
}
/**
* @brief Remove a peer address from the whitelist.
* @param [in] address The address to remove from the whitelist.
* @returns true if successful.
*/
bool NimBLEDevice::whiteListRemove(const NimBLEAddress & address) {
if (!NimBLEDevice::onWhiteList(address)) {
return true;
}
std::vector<ble_addr_t> wlVec;
wlVec.reserve(m_whiteList.size());
for (auto &it : m_whiteList) {
if (it != address) {
ble_addr_t wlAddr;
memcpy(&wlAddr.val, it.getNative(), 6);
wlAddr.type = it.getType();
wlVec.push_back(wlAddr);
}
}
int rc = ble_gap_wl_set(&wlVec[0], wlVec.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed removing from whitelist rc=%d", rc);
return false;
}
// Don't remove from the list unless NimBLE returned success
for (auto it = m_whiteList.begin(); it < m_whiteList.end(); ++it) {
if ((*it) == address) {
m_whiteList.erase(it);
break;
}
}
return true;
}
/**
* @brief Gets the count of addresses in the whitelist.
* @returns The number of addresses in the whitelist.
*/
size_t NimBLEDevice::getWhiteListCount() {
return m_whiteList.size();
}
/**
* @brief Gets the address at the vector index.
* @param [in] index The vector index to retrieve the address from.
* @returns the NimBLEAddress at the whitelist index or nullptr if not found.
*/
NimBLEAddress NimBLEDevice::getWhiteListAddress(size_t index) {
if (index > m_whiteList.size()) {
NIMBLE_LOGE(LOG_TAG, "Invalid index; %u", index);
return nullptr;
}
return m_whiteList[index];
}
/**
* @brief Host reset, we pass the message so we don't make calls until resynced.
* @param [in] reason The reason code for the reset.
@ -485,7 +740,7 @@ void NimBLEDevice::stopAdvertising() {
int rc=0;
esp_err_t errRc = ESP_OK;
#ifdef ARDUINO_ARCH_ESP32
#if defined(CONFIG_ENABLE_ARDUINO_DEPENDS) && !defined(DONT_USE_ARDUINO_BULLSHIT)
// make sure the linker includes esp32-hal-bt.c so ardruino init doesn't release BLE memory.
btStarted();
#endif
@ -499,8 +754,17 @@ void NimBLEDevice::stopAdvertising() {
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();
// Setup callbacks for host events
@ -519,6 +783,8 @@ void NimBLEDevice::stopAdvertising() {
// Set the device name.
rc = ble_svc_gap_device_name_set(deviceName.c_str());
if (rc != 0)
NIMBLE_LOGE(LOG_TAG, "ble_svc_gap_device_name_set() failed %i name_size=%zd", rc, deviceName.size());
assert(rc == 0);
ble_store_config_init();
@ -544,10 +810,12 @@ void NimBLEDevice::stopAdvertising() {
if (ret == 0) {
nimble_port_deinit();
#if 0
ret = esp_nimble_hci_and_controller_deinit();
if (ret != ESP_OK) {
NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret);
}
#endif
initialized = false;
m_synced = false;

View File

@ -15,7 +15,7 @@
#ifndef MAIN_NIMBLEDEVICE_H_
#define MAIN_NIMBLEDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "nimconfig.h"
@ -95,6 +95,11 @@ public:
static bool getInitialized();
static NimBLEAddress getAddress();
static std::string toString();
static bool whiteListAdd(const NimBLEAddress & address);
static bool whiteListRemove(const NimBLEAddress & address);
static bool onWhiteList(const NimBLEAddress & address);
static size_t getWhiteListCount();
static NimBLEAddress getWhiteListAddress(size_t index);
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
static NimBLEScan* getScan();
@ -123,6 +128,8 @@ public:
static bool isIgnored(const NimBLEAddress &address);
static void addIgnored(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)
static NimBLEAdvertising* getAdvertising();
@ -140,6 +147,14 @@ public:
static std::list<NimBLEClient*>* getClientList();
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
static bool deleteBond(const NimBLEAddress &address);
static int getNumBonds();
static bool isBonded(const NimBLEAddress &address);
static void deleteAllBonds();
static NimBLEAddress getBondedAddress(int index);
#endif
private:
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
friend class NimBLEClient;
@ -184,6 +199,9 @@ private:
static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler;
static uint8_t m_own_addr_type;
static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode;
static std::vector<NimBLEAddress> m_whiteList;
};

View File

@ -12,7 +12,7 @@
* Author: pcbreflux
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "NimBLEEddystoneTLM.h"
#include "NimBLELog.h"
@ -124,30 +124,30 @@ std::string NimBLEEddystoneTLM::toString() {
out += " C\n";
out += "Adv. Count ";
snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U32(m_eddystoneData.advCount));
snprintf(val, sizeof(val), "%lu", ENDIAN_CHANGE_U32(m_eddystoneData.advCount));
out += val;
out += "\n";
out += "Time in seconds ";
snprintf(val, sizeof(val), "%d", rawsec/10);
snprintf(val, sizeof(val), "%lu", rawsec/10);
out += val;
out += "\n";
out += "Time ";
snprintf(val, sizeof(val), "%04d", rawsec / 864000);
snprintf(val, sizeof(val), "%04lu", rawsec / 864000);
out += val;
out += ".";
snprintf(val, sizeof(val), "%02d", (rawsec / 36000) % 24);
snprintf(val, sizeof(val), "%02lu", (rawsec / 36000) % 24);
out += val;
out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 600) % 60);
snprintf(val, sizeof(val), "%02lu", (rawsec / 600) % 60);
out += val;
out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 10) % 60);
snprintf(val, sizeof(val), "%02lu", (rawsec / 10) % 60);
out += val;
out += "\n";

View File

@ -14,6 +14,8 @@
#ifndef _NimBLEEddystoneTLM_H_
#define _NimBLEEddystoneTLM_H_
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "NimBLEUUID.h"
#include <string>
@ -57,4 +59,5 @@ private:
}; // NimBLEEddystoneTLM
#endif
#endif /* _NimBLEEddystoneTLM_H_ */

View File

@ -12,7 +12,7 @@
* Author: pcbreflux
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "NimBLEEddystoneURL.h"
#include "NimBLELog.h"

View File

@ -14,6 +14,7 @@
#ifndef _NIMBLEEddystoneURL_H_
#define _NIMBLEEddystoneURL_H_
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "NimBLEUUID.h"
#include <string>
@ -49,4 +50,5 @@ private:
}; // NIMBLEEddystoneURL
#endif
#endif /* _NIMBLEEddystoneURL_H_ */

View File

@ -103,7 +103,7 @@ void NimBLEHIDDevice::manufacturer(std::string name) {
/**
* @brief Sets the Plug n Play characterisc value.
* @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] 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
*/
NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908);
NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ_ENC);
NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC);
uint8_t desc1_val[] = { reportID, 0x01 };
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
@ -144,7 +144,7 @@ NimBLECharacteristic* NimBLEHIDDevice::inputReport(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);
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 };
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
@ -159,7 +159,7 @@ NimBLECharacteristic* NimBLEHIDDevice::outputReport(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);
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 };
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);

View File

@ -7,17 +7,17 @@
*/
#ifndef MAIN_NIMBLELOG_H_
#define MAIN_NIMBLELOG_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "sdkconfig.h"
#if defined(ARDUINO_ARCH_ESP32) && !defined(DONT_USE_ARDUINO_BULLSHIT)
#include "syscfg/syscfg.h"
#include "modlog/modlog.h"
// If Arduino is being used, strip out the colors and ignore log printing below ui setting.
// Note: because CONFIG_LOG_DEFAULT_LEVEL is set at ERROR in Arduino we must use MODLOG_DFLT(ERROR
// otherwise no messages will be printed above that level.
#ifdef ARDUINO_ARCH_ESP32
#ifndef CORE_DEBUG_LEVEL
#define CORE_DEBUG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL
#endif
@ -49,12 +49,15 @@
#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "CRIT %s: "#format"\n",tag,##__VA_ARGS__)
#else
#define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "\033[0;31mE %s: "#format"\033[0m\n",tag,##__VA_ARGS__)
#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(WARN, "\033[0;33mW %s: "#format"\033[0m\n",tag,##__VA_ARGS__)
#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(INFO, "\033[0;32mI %s: "#format"\033[0m\n",tag,##__VA_ARGS__)
#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(DEBUG, "D %s: "#format"\n",tag,##__VA_ARGS__)
#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "\033[1;31mCRIT %s: "#format"\033[0m\n",tag,##__VA_ARGS__)
#include "esp_log.h"
#define NIMBLE_LOGE(tag, format, ...) ESP_LOGE(tag, format, ##__VA_ARGS__)
#define NIMBLE_LOGW(tag, format, ...) ESP_LOGW(tag, format, ##__VA_ARGS__)
#define NIMBLE_LOGI(tag, format, ...) ESP_LOGI(tag, format, ##__VA_ARGS__)
#define NIMBLE_LOGD(tag, format, ...) ESP_LOGD(tag, format, ##__VA_ARGS__)
#define NIMBLE_LOGC(tag, format, ...) ESP_LOGE(tag, format, ##__VA_ARGS__)
#endif /*ARDUINO_ARCH_ESP32*/
#endif /*CONFIG_BT_ENABLED*/
#endif /*MAIN_NIMBLELOG_H_*/
#endif /*MAIN_NIMBLELOG_H_*/

View File

@ -55,6 +55,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_handle = chr->val_handle;
m_defHandle = chr->def_handle;
m_endHandle = 0;
m_charProp = chr->properties;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
@ -146,31 +147,25 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
const struct ble_gatt_dsc *dsc,
void *arg)
{
NIMBLE_LOGD(LOG_TAG,"Descriptor Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? dsc->handle : -1);
int rc = error->status;
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovered >> status: %d handle: %d",
rc, (rc == 0) ? dsc->handle : -1);
desc_filter_t *filter = (desc_filter_t*)arg;
const NimBLEUUID *uuid_filter = filter->uuid;
ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
int rc=0;
if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
if (characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0;
}
switch (error->status) {
switch (rc) {
case 0: {
if(dsc->uuid.u.type == BLE_UUID_TYPE_16 && dsc->uuid.u16.value == uint16_t(0x2803)) {
NIMBLE_LOGD(LOG_TAG,"Descriptor NOT found - end of Characteristic definintion");
rc = BLE_HS_EDONE;
break;
}
if(uuid_filter != nullptr) {
if(ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) {
if (uuid_filter != nullptr) {
if (ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) {
return 0;
} else {
NIMBLE_LOGD(LOG_TAG,"Descriptor Found");
rc = BLE_HS_EDONE;
}
}
@ -180,11 +175,10 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
break;
}
default:
rc = error->status;
break;
}
/** If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process.
/* If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process.
* Else if rc == 0, just return 0 to continue the discovery until we get BLE_HS_EDONE.
* If we get any other error code tell the application to abort by returning non-zero in the rc.
*/
@ -202,6 +196,38 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
}
/**
* @brief callback from NimBLE when the next characteristic of the service is discovered.
*/
int NimBLERemoteCharacteristic::nextCharCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
{
int rc = error->status;
NIMBLE_LOGD(LOG_TAG, "Next Characteristic >> status: %d handle: %d",
rc, (rc == 0) ? chr->val_handle : -1);
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *pChar = (NimBLERemoteCharacteristic*)pTaskData->pATT;
if (pChar->getRemoteService()->getClient()->getConnId() != conn_handle) {
return 0;
}
if (rc == 0) {
pChar->m_endHandle = chr->def_handle - 1;
rc = BLE_HS_EDONE;
} else if (rc == BLE_HS_EDONE) {
pChar->m_endHandle = pChar->getRemoteService()->getEndHandle();
} else {
pTaskData->rc = rc;
}
xTaskNotifyGive(pTaskData->task);
return rc;
}
/**
* @brief Populate the descriptors (if any) for this characteristic.
* @param [in] the end handle of the characteristic, or the service, whichever comes first.
@ -209,35 +235,57 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
uint16_t endHandle = getRemoteService()->getEndHandle(this);
if(m_handle >= endHandle) {
return false;
// If this is the last handle then there are no descriptors
if (m_handle == getRemoteService()->getEndHandle()) {
return true;
}
int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
// If we don't know the end handle of this characteristic retrieve the next one in the service
// The end handle is the next characteristic definition handle -1.
if (m_endHandle == 0) {
rc = ble_gattc_disc_all_chrs(getRemoteService()->getClient()->getConnId(),
m_handle,
getRemoteService()->getEndHandle(),
NimBLERemoteCharacteristic::nextCharCB,
&taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error getting end handle rc=%d", rc);
return false;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Could not retrieve end handle rc=%d", taskData.rc);
return false;
}
}
desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle,
endHandle,
m_endHandle,
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: startHandle:%d endHandle:%d taskData.rc=%d %s", m_handle, endHandle, taskData.rc, NimBLEUtils::returnCodeToString(0x0100+taskData.rc));
return false;
if (taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Failed to retrieve descriptors; startHandle:%d endHandle:%d taskData.rc=%d",
m_handle, m_endHandle, taskData.rc);
}
return true;
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
return (taskData.rc == 0);
} // retrieveDescriptors
@ -664,7 +712,7 @@ std::string NimBLERemoteCharacteristic::toString() {
* @return false if not connected or cant perform write for some reason.
*/
bool NimBLERemoteCharacteristic::writeValue(const std::string &newValue, bool response) {
return writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response);
return writeValue((uint8_t*)newValue.c_str(), newValue.length(), response);
} // writeValue

View File

@ -148,12 +148,15 @@ private:
static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg);
static int nextCharCB(uint16_t conn_handle, const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg);
// Private properties
NimBLEUUID m_uuid;
uint8_t m_charProp;
uint16_t m_handle;
uint16_t m_defHandle;
uint16_t m_endHandle;
NimBLERemoteService* m_pRemoteService;
std::string m_value;
notify_callback m_notifyCallback;

View File

@ -223,6 +223,20 @@ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter)
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc == 0){
if (uuid_filter == nullptr) {
if (m_characteristicVector.size() > 1) {
for (auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it ) {
auto nx = std::next(it, 1);
if (nx == m_characteristicVector.end()) {
break;
}
(*it)->m_endHandle = (*nx)->m_defHandle - 1;
}
}
m_characteristicVector.back()->m_endHandle = getEndHandle();
}
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
return true;
}
@ -249,23 +263,6 @@ uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
/**
* @brief Get the end handle of specified NimBLERemoteCharacteristic.
*/
uint16_t NimBLERemoteService::getEndHandle(NimBLERemoteCharacteristic *pCharacteristic) {
uint16_t endHandle = m_endHandle;
for(auto &it: m_characteristicVector) {
uint16_t defHandle = it->getDefHandle() - 1;
if(defHandle > pCharacteristic->getDefHandle() && endHandle > defHandle) {
endHandle = defHandle;
}
}
return endHandle;
} // getEndHandle
/**
* @brief Get the service start handle.

View File

@ -70,7 +70,6 @@ private:
uint16_t getStartHandle();
uint16_t getEndHandle();
uint16_t getEndHandle(NimBLERemoteCharacteristic *pCharacteristic);
void releaseSemaphores();
// Properties

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_pAdvertisedDeviceCallbacks = nullptr;
m_ignoreResults = false;
m_wantDuplicates = false;
m_pTaskData = nullptr;
m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset
m_maxResults = 0xFF;
}
@ -64,7 +64,7 @@ NimBLEScan::~NimBLEScan() {
case BLE_GAP_EVENT_DISC: {
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;
}
@ -88,34 +88,49 @@ NimBLEScan::~NimBLEScan() {
// 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.
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->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event->disc.event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
NIMBLE_LOGI(LOG_TAG, "NEW DEVICE FOUND: %s", advertisedAddress.toString().c_str());
}
else{
NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str());
}
advertisedDevice->setRSSI(event->disc.rssi);
if(event->disc.length_data > 0) {
advertisedDevice->parseAdvertisement(event->disc.data, event->disc.length_data);
NIMBLE_LOGI(LOG_TAG, "New advertiser: %s", advertisedAddress.toString().c_str());
} else if(advertisedDevice != nullptr) {
NIMBLE_LOGI(LOG_TAG, "Updated advertiser: %s", advertisedAddress.toString().c_str());
} else {
// Scan response from unknown device
return 0;
}
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_wantDuplicates || !advertisedDevice->m_callbackSent) {
// If not active scanning report the result to the listener.
if(pScan->m_scan_params.passive || event->disc.event_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
// If not active scanning or scan response is not available
// report the result to the callback now.
if(pScan->m_scan_params.passive ||
(advertisedDevice->getAdvType() != BLE_HCI_ADV_TYPE_ADV_IND &&
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.
} else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
}
// 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) {
advertisedDevice->m_callbackSent = true;
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: {
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) {
pScan->m_scanCompleteCB(pScan->m_scanResults);
@ -145,15 +174,11 @@ NimBLEScan::~NimBLEScan() {
/**
* @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.
*/
void NimBLEScan::setActiveScan(bool active) {
if (active) {
m_scan_params.passive = 0;
} else {
m_scan_params.passive = 1;
}
m_scan_params.passive = !active;
} // setActiveScan
@ -202,6 +227,16 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
} // 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.
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
@ -209,7 +244,7 @@ void NimBLEScan::setFilterPolicy(uint8_t filter) {
*/
void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
bool wantDuplicates) {
m_wantDuplicates = wantDuplicates;
setDuplicateFilter(!wantDuplicates);
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
} // setAdvertisedDeviceCallbacks
@ -249,7 +284,7 @@ bool NimBLEScan::isScanning() {
* @return True if scan started or false if there was an error.
*/
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
NIMBLE_LOGD(LOG_TAG, ">> start(duration=%lu)", duration);
// Save the callback to be invoked when the scan completes.
m_scanCompleteCB = scanCompleteCB;
@ -281,6 +316,8 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
break;
case BLE_HS_EALREADY:
// Clear the cache if already scanning in case an advertiser was missed.
clearDuplicateCache();
break;
case BLE_HS_EBUSY:
@ -342,10 +379,14 @@ bool NimBLEScan::stop() {
int rc = ble_gap_disc_cancel();
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;
}
if(m_maxResults == 0) {
clearResults();
}
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
@ -359,13 +400,21 @@ bool NimBLEScan::stop() {
} // stop
/**
* @brief Clears the duplicate scan filter cache.
*/
void NimBLEScan::clearDuplicateCache() {
esp_ble_scan_dupilcate_list_flush();
}
/**
* @brief Delete peer device from the scan results vector.
* @param [in] address The address of the device to delete from the results.
* @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) {
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) {
if((*it)->getAddress() == address) {
@ -414,6 +463,7 @@ void NimBLEScan::clearResults() {
delete it;
}
m_scanResults.m_advertisedDevicesVector.clear();
clearDuplicateCache();
}

View File

@ -70,9 +70,11 @@ public:
void setDuplicateFilter(bool enabled);
void setLimitedOnly(bool enabled);
void setFilterPolicy(uint8_t filter);
void clearDuplicateCache();
bool stop();
void clearResults();
NimBLEScanResults getResults();
void setMaxResults(uint8_t maxResults);
void erase(const NimBLEAddress &address);
@ -89,10 +91,10 @@ private:
void (*m_scanCompleteCB)(NimBLEScanResults scanResults);
ble_gap_disc_params m_scan_params;
bool m_ignoreResults;
bool m_wantDuplicates;
NimBLEScanResults m_scanResults;
uint32_t m_duration;
ble_task_data_t *m_pTaskData;
uint8_t m_maxResults;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)

View File

@ -13,7 +13,7 @@
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "NimBLESecurity.h"
#include "NimBLEDevice.h"

View File

@ -15,7 +15,7 @@
#ifndef COMPONENTS_NIMBLESECURITY_H_
#define COMPONENTS_NIMBLESECURITY_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "host/ble_gap.h"
/**** FIX COMPILATION ****/

View File

@ -37,6 +37,7 @@ static NimBLEServerCallbacks defaultCallbacks;
* the NimBLEDevice class.
*/
NimBLEServer::NimBLEServer() {
memset(m_indWait, BLE_HS_CONN_HANDLE_NONE, sizeof(m_indWait));
// m_svcChgChrHdl = 0xffff; // Future Use
m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false;
@ -104,28 +105,47 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
/**
* @brief Get a %BLE Service by its UUID
* @param [in] uuid The UUID of the new service.
* @return A reference to the service object.
* @param [in] uuid The UUID of the service.
* @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) {
return getServiceByUUID(NimBLEUUID(uuid));
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) {
return getServiceByUUID(NimBLEUUID(uuid), instanceId);
} // getServiceByUUID
/**
* @brief Get a %BLE Service by its UUID
* @param [in] uuid The UUID of the new service.
* @return A reference to the service object.
* @param [in] uuid The UUID of the service.
* @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) {
if (it->getUUID() == uuid) {
return it;
if (position == instanceId){
return it;
}
position++;
}
}
return nullptr;
} // 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.
@ -154,7 +174,7 @@ void NimBLEServer::start() {
abort();
}
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && !defined(DONT_USE_ARDUINO_BULLSHIT) && CORE_DEBUG_LEVEL >= 4)
ble_gatts_show_local();
#endif
/*** Future use ***
@ -234,6 +254,63 @@ size_t NimBLEServer::getConnectedCount() {
} // getConnectedCount
/**
* @brief Get the vector of the connected client ID's.
*/
std::vector<uint16_t> NimBLEServer::getPeerDevices() {
return m_connectedPeersVec;
} // getPeerDevices
/**
* @brief Get the connection information of a connected peer by vector index.
* @param [in] index The vector index of the peer.
*/
NimBLEConnInfo NimBLEServer::getPeerInfo(size_t index) {
if (index >= m_connectedPeersVec.size()) {
NIMBLE_LOGE(LOG_TAG, "No peer at index %u", index);
return NimBLEConnInfo();
}
return getPeerIDInfo(m_connectedPeersVec[index]);
} // getPeerInfo
/**
* @brief Get the connection information of a connected peer by address.
* @param [in] address The address of the peer.
*/
NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) {
ble_addr_t peerAddr;
memcpy(&peerAddr.val, address.getNative(),6);
peerAddr.type = address.getType();
NimBLEConnInfo peerInfo;
int rc = ble_gap_conn_find_by_addr(&peerAddr, &peerInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Peer info not found");
}
return peerInfo;
} // getPeerInfo
/**
* @brief Get the connection information of a connected peer by connection ID.
* @param [in] id The connection id of the peer.
*/
NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
NimBLEConnInfo peerInfo;
int rc = ble_gap_conn_find(id, &peerInfo.m_desc);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Peer info not found");
}
return peerInfo;
} // getPeerIDInfo
/**
* @brief Handle a GATT Server Event.
*
@ -261,7 +338,9 @@ size_t NimBLEServer::getConnectedCount() {
server->m_connectedPeersVec.push_back(event->connect.conn_handle);
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
if (rc != 0) {
return 0;
}
server->m_pServerCallbacks->onConnect(server);
server->m_pServerCallbacks->onConnect(server, &desc);
@ -316,7 +395,9 @@ size_t NimBLEServer::getConnectedCount() {
(it->getProperties() & BLE_GATT_CHR_F_READ_ENC))
{
rc = ble_gap_conn_find(event->subscribe.conn_handle, &desc);
assert(rc == 0);
if (rc != 0) {
break;
}
if(!desc.sec_state.encrypted) {
NimBLEDevice::startSecurity(event->subscribe.conn_handle);
@ -339,18 +420,44 @@ size_t NimBLEServer::getConnectedCount() {
} // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX: {
if(event->notify_tx.indication && event->notify_tx.status != 0) {
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->notify_tx.attr_handle) {
if(it->m_pTaskData != nullptr) {
it->m_pTaskData->rc = event->notify_tx.status;
xTaskNotifyGive(it->m_pTaskData->task);
}
break;
}
NimBLECharacteristic *pChar = nullptr;
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->notify_tx.attr_handle) {
pChar = it;
}
}
if(pChar == nullptr) {
return 0;
}
NimBLECharacteristicCallbacks::Status statusRC;
if(event->notify_tx.indication) {
if(event->notify_tx.status != 0) {
if(event->notify_tx.status == BLE_HS_EDONE) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
} else if(rc == BLE_HS_ETIMEOUT) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
} else {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
}
} else {
return 0;
}
server->clearIndicateWait(event->notify_tx.conn_handle);
} else {
if(event->notify_tx.status == 0) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
}
}
pChar->m_pCallbacks->onStatus(pChar, statusRC, event->notify_tx.status);
return 0;
} // BLE_GAP_EVENT_NOTIFY_TX
@ -373,7 +480,10 @@ size_t NimBLEServer::getConnectedCount() {
/* Delete the old bond. */
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
assert(rc == 0);
if (rc != 0){
return BLE_GAP_REPEAT_PAIRING_IGNORE;
}
ble_store_util_delete_peer(&desc.peer_id_addr);
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
@ -414,7 +524,7 @@ size_t NimBLEServer::getConnectedCount() {
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc);
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %d", event->passkey.params.numcmp);
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %lu", event->passkey.params.numcmp);
pkey.action = event->passkey.params.action;
// Compatibility only - Do not use, should be removed the in future
if(NimBLEDevice::m_securityCallbacks != nullptr) {
@ -540,14 +650,23 @@ void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
/**
* @brief Adds a service which was already created, but removed from availability.
* @brief Adds a service which was either already created but removed from availability,\n
* or created and later added to services list.
* @param [in] service The service object to add.
* @note If it is desired to advertise the service it must be added by
* calling NimBLEAdvertising::addServiceUUID.
*/
void NimBLEServer::addService(NimBLEService* service) {
// If adding a service that was not removed just return.
// Check that a service with the supplied UUID does not already exist.
if(getServiceByUUID(service->getUUID()) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
std::string(service->getUUID()).c_str());
}
// If adding a service that was not removed add it and return.
// Else reset GATT and send service changed notification.
if(service->m_removed == 0) {
m_svcVec.push_back(service);
return;
}
@ -646,7 +765,28 @@ void NimBLEServer::updateConnParams(uint16_t conn_handle,
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
}
} // updateConnParams
}// updateConnParams
bool NimBLEServer::setIndicateWait(uint16_t conn_handle) {
for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if(m_indWait[i] == conn_handle) {
return false;
}
}
return true;
}
void NimBLEServer::clearIndicateWait(uint16_t conn_handle) {
for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
if(m_indWait[i] == conn_handle) {
m_indWait[i] = BLE_HS_CONN_HANDLE_NONE;
return;
}
}
}
/** Default callback handlers */

View File

@ -25,6 +25,7 @@
#include "NimBLEAdvertising.h"
#include "NimBLEService.h"
#include "NimBLESecurity.h"
#include "NimBLEConnInfo.h"
class NimBLEService;
@ -49,15 +50,19 @@ public:
void startAdvertising();
void stopAdvertising();
void start();
NimBLEService* getServiceByUUID(const char* uuid);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid);
NimBLEService* getServiceByUUID(const char* uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId = 0);
NimBLEService* getServiceByHandle(uint16_t handle);
int disconnect(uint16_t connID,
uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout);
uint16_t getPeerMTU(uint16_t conn_id);
// std::vector<uint16_t> getPeerDevices();
std::vector<uint16_t> getPeerDevices();
NimBLEConnInfo getPeerInfo(size_t index);
NimBLEConnInfo getPeerInfo(const NimBLEAddress& address);
NimBLEConnInfo getPeerIDInfo(uint16_t id);
void advertiseOnDisconnect(bool);
private:
@ -72,6 +77,7 @@ private:
bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks;
bool m_deleteCallbacks;
uint16_t m_indWait[CONFIG_BT_NIMBLE_MAX_CONNECTIONS];
std::vector<uint16_t> m_connectedPeersVec;
// uint16_t m_svcChgChrHdl; // Future use
@ -81,6 +87,8 @@ private:
static int handleGapEvent(struct ble_gap_event *event, void *arg);
void resetGATT();
bool setIndicateWait(uint16_t conn_handle);
void clearIndicateWait(uint16_t conn_handle);
}; // NimBLEServer

View File

@ -233,9 +233,9 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
*/
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
// Check that we don't add the same characteristic twice.
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());
}
@ -246,31 +246,80 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid
} // createCharacteristic
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @param [in] uuid The UUID of the characteristic.
* @return A pointer to the characteristic object or nullptr if not found.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) {
return getCharacteristic(NimBLEUUID(uuid));
void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) {
pCharacteristic->setService(this);
m_chrVec.push_back(pCharacteristic);
}
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @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.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid, uint16_t instanceId) {
return getCharacteristic(NimBLEUUID(uuid), instanceId);
}
/**
* @brief Get a pointer to the characteristic object with the specified UUID.
* @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.
*/
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) {
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid, uint16_t instanceId) {
uint16_t position = 0;
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
return it;
if (position == instanceId) {
return it;
}
position++;
}
}
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.
@ -295,7 +344,7 @@ std::string NimBLEService::toString() {
*/
NimBLEServer* NimBLEService::getServer() {
return m_pServer;
} // getServer
}// getServer
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED

View File

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

View File

@ -12,7 +12,7 @@
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "NimBLEUtils.h"
#include "NimBLEUUID.h"
@ -65,29 +65,42 @@ static const char* LOG_TAG = "NimBLEUUID";
*this = NimBLEUUID(first, second, third, (uint64_t(fourth) << 48) + fifth);
}
else {
NIMBLE_LOGE(LOG_TAG,"ERROR: UUID value not 2, 4, 16 or 36 bytes");
m_valueSet = false;
}
} // 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] size The size of the data.
* @param [in] msbFirst Is the MSB first in pData memory?
*/
NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
if (size != 16) {
NIMBLE_LOGE(LOG_TAG,"ERROR: UUID length not 16 bytes");
return;
}
m_uuid.u.type = BLE_UUID_TYPE_128;
uint8_t *uuidValue = nullptr;
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) {
std::reverse_copy(pData, pData + 16, m_uuid.u128.value);
std::reverse_copy(pData, pData + size, uuidValue);
} else {
memcpy(m_uuid.u128.value, pData, 16);
memcpy(uuidValue, pData, size);
}
m_valueSet = true;
} // NimBLEUUID
@ -264,10 +277,6 @@ std::string NimBLEUUID::toString() const {
*/
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
NIMBLE_LOGD(LOG_TAG,"Comparing UUIDs; type %u to %u; UUID %s to %s",
m_uuid.u.type, rhs.m_uuid.u.type,
this->toString().c_str(), rhs.toString().c_str());
if(m_uuid.u.type != rhs.m_uuid.u.type) {
uint8_t uuidBase[16] = {
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,

View File

@ -15,7 +15,7 @@
#ifndef COMPONENTS_NIMBLEUUID_H_
#define COMPONENTS_NIMBLEUUID_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "host/ble_uuid.h"
/**** FIX COMPILATION ****/

View File

@ -7,7 +7,7 @@
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "NimBLEUtils.h"
#include "NimBLELog.h"

View File

@ -9,7 +9,7 @@
#ifndef COMPONENTS_NIMBLEUTILS_H_
#define COMPONENTS_NIMBLEUTILS_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_NIMBLE_ENABLED)
#include "host/ble_gap.h"

View File

@ -1,241 +1,93 @@
/** @file
*
/** @file
*
* IGNORE THIS FILE IF USING ESP-IDF, USE MENUCONFIG TO SET NIMBLE OPTIONS.
*
* The config options here are for Arduino use only.
* The config options here are for doxygen documentation only.
*/
#pragma once
#include "sdkconfig.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
**********************************************/
/** @brief Comment out if not using NimBLE Client functions \n
* Reduces flash size by approx. 7kB.
*/
#ifndef CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif
/** @brief Comment out if not using NimBLE Scan functions \n
* Reduces flash size by approx. 26kB.
*/
#ifndef CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
/** @brief Comment out if not using NimBLE Server functions \n
* Reduces flash size by approx. 16kB.
*/
#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif
/** @brief Comment out if not using NimBLE Advertising functions \n
* Reduces flash size by approx. 5kB.
*/
#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.
*/
// #define CONFIG_BT_NIMBLE_DEBUG
/* Uncomment to see NimBLE host return codes as text debug log messages.
* Uses approx. 7kB of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
/* Uncomment to see GAP event codes as text in debug log messages.
* Uses approx. 1kB of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
/* Uncomment to see advertisment types as text while scanning in debug log messages.
* Uses approx. 250 bytes of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
/** @brief Sets the core NimBLE host runs on */
#ifndef CONFIG_BT_NIMBLE_PINNED_TO_CORE
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
#endif
/** @brief Sets the stack size for the NimBLE host task */
#ifndef CONFIG_BT_NIMBLE_TASK_STACK_SIZE
#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
#endif
/**
* @brief Sets the memory pool where NimBLE will be loaded
* @details By default NimBLE is loaded in internal ram.\n
* To use external PSRAM you must change this to `#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1`
*/
#ifndef CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
#endif
/** @brief Sets the number of simultaneous connections (esp controller max is 9) */
#ifndef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
#endif
/** @brief Sets the number of devices allowed to store/bond with */
#ifndef CONFIG_BT_NIMBLE_MAX_BONDS
#define CONFIG_BT_NIMBLE_MAX_BONDS 3
#endif
/** @brief Sets the maximum number of CCCD subscriptions to store */
#ifndef CONFIG_BT_NIMBLE_MAX_CCCDS
#define CONFIG_BT_NIMBLE_MAX_CCCDS 8
#endif
/** @brief Default device name */
#ifndef CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
#endif
/** @brief Set if CCCD's and bond data should be stored in NVS */
#define CONFIG_BT_NIMBLE_NVS_PERSIST 1
/** @brief Allow legacy paring */
#define CONFIG_BT_NIMBLE_SM_LEGACY 1
/** @brief Allow BLE secure connections */
#define CONFIG_BT_NIMBLE_SM_SC 1
/** @brief Max device name length (bytes) */
#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 */
#define CONFIG_BT_NIMBLE_ACL_BUF_COUNT 12
/** @brief ACL Buffer size */
#define CONFIG_BT_NIMBLE_ACL_BUF_SIZE 255
/** @brief HCI Event Buffer size */
#define CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE 70
/** @brief Number of high priority HCI event buffers */
#define CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT 30
/** @brief Number of low priority HCI event buffers */
#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 */
#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_ITVL 1000
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH 2
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT 1
#ifndef CONFIG_BT_ENABLED
#define CONFIG_BT_ENABLED
#endif
#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY
#endif // #if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
/**********************************
End Arduino config
**********************************/
/* Cannot use client without scan */
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
/* Cannot use server without advertise */
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
#include "nimconfig_rename.h"
#ifdef _DOXYGEN_
/** @brief Uncomment to see debug log messages from the NimBLE host \n
/** @brief Un-comment to change the number of simultaneous connections (esp controller max is 9) */
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
/** @brief Un-comment to change the default MTU size */
#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 255
/** @brief Un-comment to change default device name */
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
/** @brief Un-comment to see debug log messages from the NimBLE host
* 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
/** @brief Un-comment to see NimBLE host return codes as text debug log messages.
* 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
/** @brief Un-comment to see GAP event codes as text in debug log messages.
* 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
/** @brief Un-comment to see advertisment types as text while scanning in debug log messages.
* Uses approx. 250 bytes of flash memory.
*/
#define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
/** @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
#endif // _DOXYGEN_

53
src/nimconfig_rename.h Normal file
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