Compare commits

...

168 Commits

Author SHA1 Message Date
ec488aae55 Set a time for scan responses so that callbacks are triggered. 2024-11-18 13:41:49 -07:00
a9a79233bd [BREAKING] - Refactor NimBLEScan
* General code cleanup
* `NimBLEScan::start` will no longer clear cache or results if scanning is already in progress.
* `NimBLEScan::clearResults` will now reset the vector capacity to 0.
* `NimBLEScan::stop` will no longer call the `onScanEnd` callback as the caller should know its been stopped when this is called.
* `NimBLEScan::clearDuplicateCache` has been removed as it was problematic and only for the esp32. Stop and start the scanner for the same effect.
* `NimBLEScan::start` takes a new bool parameter `restart`, default `true`, that will restart an already in progress scan and clear the duplicate filter so all devices will be discovered again.
* Scan response data that is received without advertisement first will now create the device and send a callback.
2024-11-18 13:40:43 -07:00
5f2730de02 Revert #724e1a7 and replace with stack checks.
Replaces `NimBLEDevice::setConnectionInProgress` and `NimBLEDevice::isConnectionInProgress()` with lower level checks to avoid potential incorrect state reporting.
`NimBLEClient::connect` will instead call `NimBLEScan::stop` if it stopped the scan to release any resources waiting, the call the callback if set.
2024-11-18 08:56:38 -07:00
fbbcfadc0c Refactor client connection establishment and client deletion.
* Removes the connection established flag as it should not be necessary.
* Deleting the client instance from the `onDisconnect` callback is now supported.
* `NimBLEDevice::deleteClient` no longer blocks tasks
* Adds a new `Config` struct to `NimBLEClient` to efficiently set single bit config settings.
* Adds `NimBLEClient::setSelfDelete` that takes the bool parameters `deleteOnDisconnect` and `deleteOnConnectFail`
  This will configure the client to delete itself when disconnected or the connection attempt fails.
* Adds `NimBLEClient::setConfig` and `NimBLEClient::getConfig` which takes or returns a `NimBLEClient::Config` object respectively.

* Reword `BLE_HS_EAPP` error string to be more accurate.
2024-11-18 08:56:38 -07:00
a6e75b3537 Ensure events are defined inNimBLEUtils::gapEventToString 2024-11-18 08:11:44 -07:00
7bd7b1dfc2 Workaround for bug in NimBLE stack when connect re-attempt enabled.
Connect reattempt does not pass the arg parameter correctly and the disconnect event will have no client pointer in the argument.
This finds the client from the connection handle to compensate.
2024-11-17 12:20:49 -07:00
a59e8ee9e1 Refactor NimBLEClient::connect and NimBLEClient::secureConnection.
This change ensures that only the function that sets `m_pTaskData` clears it, potentially preventing a segmentation fault.

* Client will no longer disconnect if task timeout occurs when waiting for MTU exchange response.
2024-11-14 10:45:24 -07:00
6a5d6ef5e3 Set task handle in constructor of NimBLETaskData.
* Create destructor for NimBLETaskData to delete semaphore if created.
2024-11-14 10:45:24 -07:00
5aa2fb1443 Refactor NimBLEUtils::taskWait to check notification value before blocking.
Instead of incrementing the notificatin value via `xTaskNotifyGive` this will now set a specific bit in the task notification value
which will be tested before blocking a task. This will prevent a task from blocking indefinitely if the event that calls `taskRelease`
occurs before entering the blocked state.

* Adds a config setting for the bit to set in the task notification value.
2024-11-14 10:45:24 -07:00
7d2ad92ad2 Bit shift length for resize 2024-11-14 10:30:58 -07:00
ab43135f82 Fix NimBLEUtils::dataToHexString returned string, must resize before modifying. 2024-11-14 10:30:58 -07:00
84f4d4f897 Improve NimBLEUtils::dataToHexString efficiency. 2024-11-13 09:40:52 -07:00
beac19cc92 [BREAKING] - Refactor NimBLEUtils
* Add functions `NimBLEUtils::taskWait` and `NimBLEUtils::taskRelease` to simplify task blocking/messaging.
* `NimBLEUtils::buildHexData` replaced with `NimBLEUtils::dataToHexString`
* Add missing GAP event strings.
* Add missing return code strings.
* `NimBLEUtils::dumpGapEvent` function removed.
* Event/error code strings optimized.
2024-11-12 16:56:59 -07:00
65e05e6c57 Add NimBLEClient::cancelConnect
Adds a function to cancel and in-progress connection.
2024-11-12 16:14:02 -07:00
38e764d157 Add asyncronous client connect and MTU exchange.
* Adds parameters `asyncConnect` and `exchangeMTU` to `NimBLEClient::connect`, default values work as the original connect method.
* * `asyncConnect`; if true, will send the connect command and return immediately with a true value for successfully sending the command, else false.
* * `exchangeMTU`; if true will send the exchange MTU command upon connection, otherwise not and the application can choose to do this later via the `exchangeMTU` method.
* Adds `onMTUChange` callback to `NimBLEClientCallbacks`
* Add `NimBLEDevice::getConnectedClients()` which returns a vector of pointers to the currently connected client instances.
* Calling `NimBLEClient::connect` will no longer cancel already in progress connections.
2024-11-10 13:02:11 -07:00
724e1a7083 Fix endless loop when calling scan start from scan end callback.
When attempting to connect and the scanner is running `NimBLEScan::stop` is called to stop it
which then calls the `onScanEnded` callback. If the app then starts the scan again in this
callback an endless loop will be created.

This change prevents the endless loop by setting a flag in `NimBLEDevice` that is checked before
starting a scan while a client is trying to connect.

* Adds `NimBLEDevice::setConnectionInProgress` and `NimBLEDevice::isConnectionInProgress` functions.
2024-11-10 10:36:51 -07:00
7d0636bc91 Fix NimBLEClient::secureConnection incorrectly failing with BLE_HS_EAGAIN. 2024-11-10 07:43:25 -07:00
020c61700d [BREAKING]- Refactor client
* General code cleanup and rename variables to use a consistent style.
* Removes the disconnect timer and will use the BLE_GAP_EVENT_TERM_FAILURE event to handle failed disconnects.
* `NimBLEClient::getConnId` has been renamed to `getConnHandle` to be consistent with bluetooth terminology.
* `NimBLEClient::disconnect` now returns a `bool = true on success` instead of an int to be consistent with the rest of the library.
* `NimBLEClient::setPeerAddress` now returns a bool, true on success.
* `NimBLEClientCallbacks::onConfirmPIN` renamed to `NimBLEClientCallbacks::onConfirmPasskey` to be consistent with bluetooth terminology.
* `NimBLEClient::setDataLen` now returns bool, true if successful.
* `NimBLEClient::updateConnParams` now returns bool, true if successful.
* `NimBLEClient::getServices` now returns a const reference to std::vector<NimBLERemoteService*> instead of a pointer to the internal vector.
2024-11-03 18:12:20 -07:00
68b82f5b85 Add NimBLEDevice::setOwnAddr and NimBLEUtils::generateAddr functions.
Adds a utility to generate random BLE addresses and set the address used.
2024-11-02 18:46:04 -06:00
a2fe5b4780 [BREAKING] Refactor NimBLEDevice
* General code cleanup
* `NimBLEDevice::getInitialized` renamed to `NimBLEDevice::isInitialized`.
* `NimBLEDevice::setPower` no longer takes the `esp_power_level_t` and `esp_ble_power_type_t`, instead only an integer value in dbm units is accepted.
* `NimBLEDevice::setPower` now returns a bool value, true = success.
* `NimBLEDevice::setMTU` now returns a bool value, true = success.
* `NimBLEDevice::injectConfirmPIN` renamed to `NimBLEDevice::injectConfirmPasskey` to use Bluetooth naming.
* Fixes crash if `NimBLEDevice::deinit` is called when the stack has not been initialized.
* Reverts 73f0277 as it would cause a crash when the NimBLEServer instance has a connection.
* `NimBLEDevice::getAddress` will now return the address currently in use.
* `NimBLEDevice::init` now returns a bool with `true` indicating success.
* `NimBLEDevice::deinit` now returns a bool with `true` inidicating success.
* `NimBLEDevice::setDeviceName` now returns a bool with `true` indicating success.
* `NimBLEDevice::setCustomGapHandler` now returns a bool with `true` indicating success.
* `NimBLEDevice::setOwnAddrType` now returns a bool with `true` indicating success.
* `NimBLEDevice::setOwnAddrType` will now correctly apply the provided address type for all devices.
* `NimBLEDevice::setOwnAddrType` no longer takes a `bool nrpa` parameter.
2024-11-02 18:46:04 -06:00
877a29a8b1 Update cmakelists.txt (#213) 2024-11-02 17:58:51 -06:00
a7ff1beacd doc: mention 0 is forever for NimBLESCan::start
Document that setting `duration` to zero means to scan forever.
2024-11-01 15:43:14 -06:00
ed2c59d5de Add initial esp32-P4 support (#208) 2024-10-31 19:06:57 -06:00
8ca58f5a89 implement connection parameters update callback (#210) 2024-10-24 21:21:43 -06:00
3820f57076 fix: #200 Enable use of data()/size() before trying c_str()/length() (#201)
* fix: #200 Use `data()`/`size()` instead of `c_str()`/`length()`

* Reduce duplication, only allow template function in characteristic and remote value attr if the type is not a pointer (otherwise sizeof is useless). add appropriate notes

* clean up AttValue::setValue to remove unnecessary length parameter enabling requirement of non-pointer type
2024-10-14 17:02:21 -06:00
987a69f544 Fix check for arduino component (#204) 2024-10-06 18:16:48 -06:00
b807e6671f Fix Advertised device initialization. 2024-10-06 15:03:11 -06:00
758c4d0471 Remove stray debug message. 2024-09-30 16:08:51 -06:00
5b24c8d681 Fix notifications not being sent.
The connection handle was not initialized correctly when recording the subscibe status causing the call to send notification/indications to fail with BLE_HS_ENOTCONN.
2024-09-30 16:07:00 -06:00
73f0277042 fix(NimBLEDevice): clear all before port_deinit to prevent crash 2024-09-24 16:03:49 -06:00
91210b8610 [BREAKING] Refactor attributes
Refactor attributes to reduce code duplication and improve maintainability.

* Add attribute base classes to provide common code.
* Add const where possible to functions and parameters.
* `NimBLECharacteristic::notify` no longer takes a `bool is_notification` parameter, instead `indicate()` should be called to send indications.
* `NimBLECharacteristic::indicate` now takes the same parameters as `notify`.
* `NimBLECharacteristicCallbacks` and `NimBLEDescriptorCallbacks` methods now take `const NimBLEConnInfo&` instead of non-const.
* `NimBLECharacteristic::onNotify` callback removed as unnecessary, the library does not call notify without app input.
* `NimBLERemoteCharacteristic::getRemoteService` now returns a `const NimBLERemoteService*` instead of non-const.
* Add NimBLEUUID constructor that takes a reference to `ble_uuid_any_t`.
* `NimBLERemoteService::getCharacteristics` now returns a `const std::vector<NimBLERemoteCharacteristic*>&` instead of non-const `std::vector<NimBLERemoteCharacteristic*>*`
* `NimBLERemoteService::getValue` now returns `NimBLEAttValue` instead of `std::string`
* `NimBLEService::getCharacteristics` now returns a `const std::vector<NimBLECharacteristic*>&` instead of a copy of std::vector<NimBLECharacteristic *>.
* Remove const requirement for NimBLEConnInfo parameter in callbacks.
  Const is unnecessary as the data can't be changed by application code.
* Change NimBLERemoteCharacteristic::getRemoteService to return const pointer.
2024-09-24 14:59:54 -06:00
b4b3b0c455 Fix NimBLEExtAdvertisement::setServiceData data length. 2024-09-15 10:28:49 -06:00
6f03b9a6ef fix: data was renamed to adv 2024-09-15 10:26:46 -06:00
cd115f1738 fix(NimBLEAdvertising): Ensure setServiceData() includes length of data 2024-08-29 17:12:21 -06:00
12074d1cc4 Fix crash when creating address from 17 char string. 2024-08-12 07:56:38 -06:00
d22db6ef8c Fix 16 and 32 bit UUID comparison. 2024-08-02 07:18:35 -06:00
209f70a083 Fix 128 bit uuid comparison. 2024-07-25 15:30:45 -06:00
10d589162b [Breaking] Refactor NimBLEUUID.
* msbFirst parameter has been removed from constructor as it was unnecessary,
caller should reverse the data first or call the new `reverseByteOrder` method after.
* `getNative` method replaced with `getBase` which returns a read-only pointer to the UUID size underlying.
* Added `reverseByteOrder` method, this will reverse the bytes of the UUID, which can be useful for advertising/logging.
* Added `getValue` method, which returns a read-only `uint8_t` pointer to the UUID value.
* Removed `m_valueSet` member variable, `bitSize()` can be used as a replacement.
* General code cleanup.
2024-07-23 18:14:09 -06:00
d1d1b49a26 Refactor NimBLEAttValue.
* Add length parameter to `setValue()` templates, with defaults for backward compatibility.
* Changed `setValue(const char*)` to add an optional length parameter so that a NULL character can be included, defaults to strlen.
* Moved non-inline functions to `NimBLEAttValue.cpp` file.
* Corrected includes to permit compilation as a stand-alone utility.
* Removed repeated code in `setValue()` by using `append()` after clearing the value.
* General code cleanup.
2024-07-21 16:36:00 -06:00
8c4832f711 Add clang-format. 2024-07-18 17:17:54 -06:00
efa858c4cc Only trigger build on master when pushing. 2024-07-18 17:17:30 -06:00
c2ab790e1d [BREAKING] Refactor NimBLEAdvertised device.
* Construct the device with the parameters from the advertisement in the initialization list.
* Removed no longer needed methods; setAddress, setAdvType, setRSSI, setSetId, setPrimaryPhy, setSecondaryPhy, setPeriodicInterval.
* Removed `hasRSSI()` method, the RSSI is always reported so this is redundant.
* Replace setPayload with new method; `update` which will update the device info when new advertisement data is received.
* getPayload now returns `const std::vector<uint8_t>` instead of a pointer to internal memory.
* Added `begin` and `end` read-only iterators for convienience and use in range loops.
* Timestamp removed, if needed then the app should track the time in the callback.
* Consolidate some functions to use getPayloadByType.
* Add optional index parameter to getPayloadByType.
* Change payload indexing to use 0 as the first item.
* Code cleanup and apply const correctness.
2024-07-17 20:44:49 -06:00
6279817143 [BREAKING] Refactor NimBLEAddress - use NimBLE core representation.
This simplifies the NimBLEAddress code by directly using the NimBLE core `ble_addr_t` type to hold the address
and allows using NimBLE core functions and macros to replace code in some methods.

* `getNative()` replaced with `getBase()` and now returns a pointer to `const ble_addr_t` instead of a pointer to the address value.
* Adds `isNrpa()` method to test if an address is random non-resolvable.
* Adds `isStatic()` method to test if an address is random static.
* Adds `isPublic()` method to test if an address is a public address.
* Adds `isNull()` methods to test if an address is NULL.
* Adds `getValue()` method which returns a read-only pointer to the address value.
* Adds `reverseByteOrder()` method which will reverse the byte order of the address value.
* `equals()` method and == operator will now also test if the address types are the same.
* Code cleanup.
2024-07-17 19:38:35 -06:00
21e1217e4c [BREAKING] Use an array to manage created client instances.
* Replaces the use of std::list with a fixed array to track and manage created client instances.
* Removes: NimBLEDevice::getClientList
* Replaces: NimBLEDevice::getClientListSize with NimBLEDevice::getCreatedClientCount
2024-07-10 07:05:47 -06:00
a92149ac74 [BREAKING] Change NIMBLE_LOGC macro to use printf.
Replaces all use of NIMBLE_LOGC with NIMBLE_LOGE and redefines NIMBLE_LOGC to use printf.
This allows NIMBLE_CPP_DEBUG_ASSERT messages to print event when log level filtering is set to none.
2024-07-04 19:05:35 -06:00
d9f5794b57 Replace NimBLEAttValue asserts with user defineable action.
* Adds the NIMBLE_CPP_DEBUG_ASSERT macro that calls a weak function to allow user defined action, defaults to critical error message and aborts.
* Adds a config option to disable asserts using the NIMBLE_CPP_DEBUG_ASSERT macro.
2024-07-04 18:47:28 -06:00
44977cdcce Remove NimBLEUtils::checkConnParams.
This function is not required as the stack will return an error code in the case of invalid params.
2024-07-03 15:01:44 -06:00
a5dd84b2f9 Remove descriptor asserts when the connection info is not found.
* Trigger onRead callbacks when the connection handle is NONE (internal).
2024-07-03 15:01:28 -06:00
b8d6e3d87f Remove assert for 0x2902, make a dummy descriptor instead.
Rather than asserting for a harmless situation, this will just create a dummy descriptor
object, marked as removed, and print a warning.
2024-07-03 15:01:14 -06:00
fae53b8d7f Remove asserts from NimBLEDevice. 2024-07-03 15:01:00 -06:00
358170847d Remove aborts in NimBLEAdvertising::start. 2024-07-03 14:27:59 -06:00
a1bd817875 Remove NimBLEServer::start abort. 2024-07-03 14:27:31 -06:00
fd698b0212 [NimBLEServer] Add getPeerName and get peer name on connect.
* Adds a new method, getPeerName to NimBLEServer to read the name from the peers device name characteristic.
* Adds a setting to automatically get the name of the peer when connected and provide it as an additional parameter in the onConnect callback.
* Adds callback with client name after authentication as it may change.
2024-06-30 16:44:49 -06:00
3c5a2fd4a9 add h2 2024-06-18 06:30:35 -06:00
0c797d5b41 add h2 2024-06-18 06:30:35 -06:00
cabd48aef3 Add documentation to NimBLEService::isStarted 2024-06-15 09:55:40 -06:00
a2a50957b1 Create package.json 2024-06-15 09:22:31 -06:00
8acea66c10 Reset advertisment vectors when changing their data.
* Add method to erase all service UUIDS.
2024-06-14 22:02:29 -06:00
bd29e738bd Add service isStarted method, remove abort in NimBLEServer::start. 2024-06-14 18:26:42 -06:00
792359b510 Remove asserts in NimBLECharacteristic read/write. 2024-06-14 10:01:50 -06:00
bc7bfe8278 Add clearData method to NimBLEAdvertisementData. 2024-06-14 10:01:41 -06:00
20349c64a4 Add conditional checks in NimBLEDevice init/deinit. 2024-06-14 10:01:34 -06:00
70c2d83b3b Remove broken links from docs. 2024-06-14 09:57:23 -06:00
63a3301696 Add missing const in NimBLEConnInfo. 2024-06-12 21:12:09 -06:00
c156b0202c Add isRpa method to NimBLEAddress.
This provides and easy way to check if the peer address is a Resolvable Private Address.
2024-06-12 21:12:09 -06:00
ef90a8aa95 Remove address from whitelist if the procedure fails.
This prevents a situation where the whitelist contains the address but the procedure errors and the address cannot be added again.
2024-06-12 21:12:09 -06:00
aeb4334e98 Add server and client onIdentity callback.
Adds a callback that is called when the identity address of the peer is resolved, this is useful for adding it to a whitelist.
2024-06-12 21:12:09 -06:00
44daa3c687 Fix failure to reconnect to device when bonded.
iOS devices specifically expect to receive the ID key of the bonded device even when RPA is not used.
This sets the default value of the key distribution config to provide the ID key as well as the encryption key.
2024-06-12 21:12:09 -06:00
f36929963d Add disconnect overload to take NimBLEConnInfo reference. (#162)
* NimBLEConnInfo - Note that the connection handle is the same as the connection id in the comment for getConnHandle()

* NimBLEServer - int disconnect(const NimBLEConnInfo&) helper method
2024-06-08 10:06:57 -06:00
2447af6a4d Fix Error in converting EddystoneTLM negative temperatures to float | resolves h2zero/NimBLE-Arduino#675 2024-06-08 10:02:47 -06:00
9c1f1adf7a [BREAKING] Asynchronous pin injections for Numeric Comparison and PassKey Input (#165)
* Make NimBLEConnInfo functions const.

* Update callback functions and update client to use new functions.

* Update examples.

* Update migration guide.

---------

Co-authored-by: Casey Smith <csmith@morningstarcorp.com>
2024-06-06 19:36:14 -06:00
6ca61bbd9c feat(NimBLEDevice): deleteAllBonds() Add return value (#158)
Add return value to `deleteAllBonds`.
2024-06-04 16:52:55 -06:00
32c213a8a3 feat(NimBLEClient): allow connection id / established flag to be set via public API. (#156)
* Adds NimBLEClient::setConnection  to allow servers to read the name of connected clients by passing their connection info to the Client class.
* Adds NimBLEClient::clearConnection to be able to reuse the client without deleting and recreating.
2024-06-04 10:37:24 -06:00
51bf1f3c7c feat(NimBLEAdvertising): support std::function for advertising complete callback (#157)
Updates the interface to use a typedef'd std::function for the advertise callback, which is backwards compatible but also allows std::bind and lambda functions.
2024-06-04 07:12:20 -06:00
7c0300c34e Change NimBLEHIDDevice formatting for consistency. 2024-06-02 17:02:02 -06:00
6fa3783206 Bug fix on PNP info
Fixed issue #492
2024-06-02 17:02:00 -06:00
226c67f729 Initialize ble_gatt_chr_def[]. Fixes #148 (#150)
IDF 5.2 introduced a new member, cpfd, to the
ble_gatt_chr_def struct. It needs to be initialized
to nullptr in order to avoid accessing uninitialized
memory. By initializing the whole struct, we get
everything initialized in a backward-compatible way.
2024-04-20 09:14:55 -06:00
bf4b5d4ffa ESP IDF 5.2 include fix (#144) 2024-03-01 12:29:18 -07:00
58f86cb7bd Fix inconsistent use of time units (#143)
Seconds to milliseconds
2024-02-29 17:34:53 -07:00
22fb1ab801 Add generic advertisement 'type' functions (#575) 2023-11-25 08:10:30 -07:00
4e65ce5d32 Support for C2 (#131) 2023-09-01 11:56:52 -06:00
b07cab5858 gptimer is used from driver 2023-09-01 08:22:37 -06:00
2ea0f26de0 Add support for esp32c6 2023-08-13 10:40:00 -06:00
1786d0ede3 Fix compilation when CONFIG_BTDM_BLE_SCAN_DUPL is not enabled.
*  fix warning: variable 'pkey' set but not used [-Wunused-but-set-variable]
---------

Co-authored-by: Franz Höpfinger <f.hoepfinger@hr-agrartechnik.de>
2023-05-29 09:08:14 -06:00
4ff9baf68a Fixed unhandled exception on handleGapEvent (#112)
Somehow, the conn_handle can have the value of `0xffff`. Not sure why or how, but anyway it should be managed before moving forward on NimBLECharacteristic::handleGapEvent. Otherwise, an unhandled exception will occur on `assert(rc == 0);`
2023-05-29 07:56:31 -06:00
c1e7a521b7 Add overloaded setManufacturerData to allow vector of uint8_t 2023-05-28 16:23:34 -06:00
fee3829a89 Fix build with platformio IDF+Arduino (#122) 2023-05-28 10:44:17 -06:00
091c1c014c Update doxygen config 2023-05-25 10:29:53 -06:00
3145319581 Build with idf v5.1 2023-05-25 09:35:11 -06:00
d0eaf6c1e0 Set service handle in getHandle function if not set already.
If a service has been created and started but not yet added to the gatt server then the call to getHandle will result in an invalid handle. This adds a call to set the handle value in the getHandle function.
2023-05-24 15:37:53 -06:00
46e7bb9302 Add onDiscovered scan callback.
This adds a callback that is called when a device is initially discovered.
It is always called wether scanning in passive or active mode. This allows
for active scanning to receive device detection information in the case where
the scan response is not sent/received.

This callback is optional, the application may decide to implement it or not.
2023-05-24 15:37:46 -06:00
559a26b74b Add index parameter for multiple manufacturer data sets.
Adds an index parameter to NimBLEAdvertisedDevice::getManufacturerData to allow for selecting from multiple instances in the advertisements.
Adds NimBLEAdvertisedDevice::getManufacturerDataCount to get the number of manufacturer data sets present in the advertised data.
2023-05-24 15:37:38 -06:00
4423e5fbb6 Expose advertisement flags in NimBLEAdvertisedDevice 2023-05-24 15:37:30 -06:00
0aa7e9510d Add directed peer address parameter to advertising start.
Adds a parameter to NimBLEAdvertising::start to take the peer address for use with directed advertising.
2023-05-24 15:37:22 -06:00
d83cd94d5b Revert 03b22e5 sub/unsub changes, app should specify response. 2023-05-24 15:37:17 -06:00
7d2d73d8fc Fix pairing when already in progress.
If pairing is requested before performing an action on a protected attribute pairing could fail due to already being in progress.
This fix will wait for the result of the pairing process before proceeding with the action.
2023-05-24 15:37:03 -06:00
05ac9deaea Remove deprecated API in case of ESP-IDF V5. (#102)
* Remove deprecated API in case of ESP-IDF V5.

0a93ee1337/docs/en/migration-guides/release-5.x/bluetooth-low-energy.rst (id17)

* Fix initialization in case of ESP-IDF V5
2022-11-03 20:47:04 -06:00
cad022650a Fix getPower return value
Power level -3 returned the wrong value.
2022-09-11 20:34:33 -06:00
e868f37135 Add conn_handle to NimBLECharacteristic::notify to send notification/indication to a specific client (#97)
Adds the ability to send notifications/indications to specific clients individually.
2022-09-11 20:18:55 -06:00
6fb26e3809 Change type of index to int (instead of uint8_t) to fix issue with adding more than 255 Characteristics (or Descriptors) (#101) 2022-09-11 19:54:15 -06:00
6a2f558ea5 [BREAKING] Replace advertised device callbacks with scan callbacks. (#389)
This replaces NimBLEAdvertisedDeviceCallbacks with NimBLEScanCallbacks and adds a onScanEnd callback.

The callback parameter for NimBLEScan::start has been removed and the blocking overload for NimBLEScan::start
has been replaced by an overload of NimBLEScan::getResults with the same parameters.
2022-08-27 14:04:50 -06:00
562a32eda6 Add docs build. 2022-08-27 13:02:29 -06:00
0f4da63fc8 Add CI build check. 2022-08-27 13:02:29 -06:00
ba79a1bf72 [Breaking] Update callbacks to use NimBLEConnInfo.
Change the callback functions that receive a ble_gap_conn_desc pointer to instead receive a NimBLEConnInfo reference.

* Add a reason parameter to the server disconnect callback.

* Remove connect and disconnect callback that do not receive info parameters.

* Remove onRead and onWrite Characteristic callbacks that do not receive info parameters.

* Add info parameter to Descriptor onWrite and onRead callbacks.

* Add details to migration guide.
2022-08-27 13:02:29 -06:00
ca8a7c56ac [Breaking] Remove extra notification status from onStatus callback. (#80)
Removes the unnecessary NimBLECharacteristicCallbacks::Status enum and callback parameter from NimBLECharacteristic::onStatus.

This was maintained for compatibility with the Arduino BLE library and is not necessary as the return code from the stack is also provided.

* Update examples/docs.
2022-08-26 20:40:21 -06:00
03b22e53a0 [Breaking] Remove deprecated functions/response parameter from un/sub (#76)
Removes the response parameter from remote characterisitc subscrible/unsubscribe functions
as a response is always required for this operation as per BLE specs.

Removes the deprecated functions from remote characteristic/desriptor.

* Update examples/docs.
2022-08-26 20:15:25 -06:00
2b758e05c7 Fix client example. 2022-08-26 19:54:43 -06:00
bfe68f4a91 [Breaking] Add disconnect reason to client callback. (#398) (#81)
Adds the reason code as a parameter to the client onDisconnect callback.

* Update examples/docs.
2022-08-26 19:51:19 -06:00
bb3dd5f114 [Breaking] Change all time input parameters to milliseconds. (#78)
Changes all functions that accept a time parameter to use milliseconds instead of seconds.

* Adds duration input to NimBLEDevice::startAdvertising and NimBLEServer::startAdvertising.
2022-08-26 19:32:01 -06:00
0b6337538c Remove NimBLESecurity class. (#75) 2022-08-21 21:33:39 -06:00
32e7059732 add NimBLEHIDDevice::batteryLevel() 2022-08-21 21:19:54 -06:00
cd3185fe43 Added the setDeviceName functionality while BLE is running 2022-08-21 21:19:30 -06:00
efa48c0d57 Release 1.4.0
* Fix typos

* Update docs
2022-07-31 11:34:56 -06:00
70ed6e293f Don't set advertisement flags if not connectable. 2022-07-10 07:15:46 -06:00
93de7ab8ed Bugfix/onRead callback not called for non-long read commands. 2022-06-26 17:03:21 -06:00
9285a9b31f Don't call scan result callback if it was already called. 2022-06-04 14:24:30 -06:00
38a1a2013b Update docs. 2022-05-29 20:08:23 -06:00
a36655c105 Add success/fail return value to disoverAttributes. 2022-04-16 20:32:01 -06:00
c285052f6d Use write with response when writing 0x2902 descriptors.
As per Bluetooth core specification we must write to 0x2902 descriptors with response request.
This change ensures the correct procedure is used.

Todo: Remove the "response" parameter from related functions.
2022-04-16 12:47:23 -06:00
9e5db157f8 Add extended advertising support. (#72)
Adds support for advertising and connections with coded/2M PHY's.

Adds new classes `NimBLEExtAdvertising` and `NimBLEExtAdvertisement`.
When extended advertising is enabled the original advertising classes become unavailable and the new classes must be used.

Changed some return values for advertising methods for consistency with the new classes methods.
2022-04-10 10:21:45 -06:00
58787b516b Prevent crash when characteristic vector is empty. 2022-04-02 14:22:23 -06:00
a1428e63a6 Fixup client logs. 2022-02-20 10:04:13 -07:00
0f4326d3fd Check characteristic handles before fetching descriptors. 2022-02-20 10:02:09 -07:00
f414a5ac73 Release v1.3.3 2022-02-15 20:33:02 -07:00
99a23d3c19 Remove recursive calling when fetching remote attributes. 2022-02-15 20:32:18 -07:00
6cda761f13 Fix memory leak when deleting client instance. 2022-02-15 20:31:26 -07:00
288ee92d39 Added extended advertising scan support (#69) 2022-02-06 16:52:42 -07:00
2e498cef2b Fix IDF version check. 2022-02-06 12:58:26 -07:00
2386a8a68a Retrieve attributes with 16bit uuid if 128bit fails.
When retrieving attribute handles using the 128bit base version of a 16 bit UUID some devices will
report "not found". This will attempt to convert the UUID to the 16bit version and try again.

* Adds `to16()` method to NimBLEUUID.
2022-01-18 14:48:07 -07:00
6b858a0efd Fix memory leak when services changed. 2022-01-18 14:44:47 -07:00
e3ee082dd7 Add NimBLEAttValue class. (#67)
This is a specialized container class to hold BLE attribute values.

- Removes the use of std::string previously used to store the values.
- Allows for setting/getting/notifying values from std::string, std::vector<uint8_t>, Arduino String, const char*, and uint8_t buffers.
- Has operators retrieve the value as std::string, Arduino String, std::vector<uint8_t>, uint8_t* or char pointers.
- Includes iterators and subscript/random access operator.
- Introduces a max length parameter to the creation of server characteristics/descriptors to limit the size of the memory footprint.
- Nearly Seamless integration with existing code.
- Adds a config option to enable/disable timestamp storage when the value is updated.
- Adds a config option to specify the initial size of the value container if not specified in the constructor.
2022-01-16 20:11:18 -07:00
be9cdc1ab6 Update docs 2022-01-15 15:21:41 -07:00
cf482f9112 Cleanup ARM-gcc compiler warnings. 2022-01-15 12:43:17 -07:00
bbed8d1d4b Release v1.3.2 2022-01-15 09:07:08 -07:00
099e7cc326 Remove travis docs build. 2022-01-15 08:57:40 -07:00
830c4cc39e Re-enable data length extension newer IDF versions. 2022-01-15 08:51:49 -07:00
ea6e2101e3 Fix compilation on ESP32-S3 2022-01-14 21:46:30 -07:00
d47cf59ba9 Cleanup compile errors in arm-gcc. 2022-01-14 20:45:24 -07:00
ea02eb9452 Add config option for log verbosity. 2022-01-14 19:47:44 -07:00
798726c05d Update change log. 2022-01-14 10:32:47 -07:00
9824bdfe73 Update log level macro name 2022-01-14 10:02:47 -07:00
fac16f2428 Fix long data notifications.
The notification data when received was using an incorrect length value and would cut off long values, this has now been corrected.
2022-01-13 08:57:50 -07:00
0957d7f6ad The latest versions of IDF include a new esp_timer library that seems to be incrementing the task notification values in unexpected places depending on other tasks in operation.
This causes task blocking to fail in client operations leading to exceptions and crashing.

This is a workaround for this situation and will need to be reworked properly in the future.
2022-01-09 19:04:41 -07:00
5facd89a00 Update NimBLEServer.h (#65) 2022-01-01 18:00:45 -07:00
f2ade345f4 Fix missing data when reading large values.
The wrong length value was being used to set the values read from peer attributes.
This has been corrected to use the proper value size.
2021-12-29 14:01:37 -07:00
7a82067177 Add last error function to client. 2021-12-29 08:12:38 -07:00
d041a089e6 [NimBLEService] Remove unused variables/parameters. 2021-12-29 08:12:07 -07:00
9debfcd226 Use NimBLE calls for critical sections 2021-12-29 08:10:57 -07:00
8620092c90 Cleanup and add support for non-esp devices. 2021-12-29 08:08:25 -07:00
96459073a4 Initialize advertising complete callback in NimBLEAdvertising constructor.
* cleanup advertising start return.
2021-12-29 07:51:57 -07:00
a4403fe6b8 Clear client disconnect timer in constructor before initializing. 2021-12-29 07:50:42 -07:00
f841f030ae Add method to send notifications/indications with custom values (#63) 2021-12-28 20:11:37 -07:00
d793b1251e Remove data length extension in IDF (not released yet) 2021-09-13 20:45:36 -06:00
5925782a65 [Server][Client] Add function to set data length.
Enables the use of BLE data length extension to improve data transfer rates.
2021-09-12 19:11:38 -06:00
ccea428b9e Refactor include directives to use different paths for arduino/idf.
* Sets macros to allow compiling when Central role is enabled and Observer disabled, or Peripheral enabled and Broadcaster disabled.

* Adds a macro definition for CONFIG_NIMBLE_CPP_IDF to enable different include paths/functionality for IDF / Arduino.
2021-09-12 18:43:31 -06:00
e238a18a80 Update Doxygen version 2021-08-04 18:58:12 -06:00
7b40829e77 Release 1.3.1 2021-08-04 18:55:34 -06:00
b24597ac56 Remove definition of btInUse().
This reverts a previous change @2e1d78ff that caused compiling/linking errors due to the redefinition of btInUse() when another linked library uses the Arduino bluetooth code.
2021-08-03 12:43:34 -06:00
9961c5605c Update docs version. 2021-08-02 21:25:17 -06:00
d4e4074f5a Release 1.3.0 2021-08-02 21:14:12 -06:00
e5edc9d59e Add missing return documentation in NimBLEAdvertising. 2021-08-02 19:51:04 -06:00
2e1d78ff78 Code cleanup 2021-08-02 13:29:19 -06:00
6be6a111d0 [Server] Implement remove Characteristics/Descriptors. (#54)
This allows for adding and removing of characteristics and descriptors after initial setup of the server.
When used it will send a service changed notification to all clients.
The changes will not take effect until all clients have disconnected and advertising restarted.
2021-07-30 20:56:52 -06:00
0a2714c169 [Server] Add callback for MTU change. (#55)
onMTUChange callback added that is called when a connection MTU is updated.
2021-07-19 21:47:59 -06:00
7d01fa595d Fix compilation for ESP32C3. (#53) 2021-07-19 21:46:30 -06:00
2decc0682a Remove FreeRTOS from CmakeLists. 2021-07-14 10:30:41 -06:00
6ff1a49dd5 Update documentation. 2021-07-12 13:33:56 -06:00
09adf86036 Remove FreeRTOS files. 2021-07-12 10:25:32 -06:00
116 changed files with 9838 additions and 7419 deletions

42
.clang-format Normal file
View File

@ -0,0 +1,42 @@
BasedOnStyle: Google
Language: Cpp
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: Never
SortIncludes: Never
PPIndentWidth : 1
IndentPPDirectives: AfterHash
ReflowComments: true
SpacesBeforeTrailingComments: 1
AlignTrailingComments: true
AlignAfterOpenBracket: Align
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: true
AcrossComments: true
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignCompound: true
PadOperators: true
AlignConsecutiveDeclarations:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignEscapedNewlines: Left
AccessModifierOffset: -2
AlignArrayOfStructures: Right
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortBlocksOnASingleLine: Empty
BreakBeforeBinaryOperators: None
BinPackArguments: false
BinPackParameters: false
DerivePointerAlignment: false
PenaltyBreakAssignment: 4
PenaltyExcessCharacter: 4
PenaltyBreakBeforeFirstCallParameter: 1
PointerAlignment: Left

70
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,70 @@
name: Build
on:
workflow_dispatch: # Start a workflow
pull_request:
push:
branches:
- master
jobs:
build-esp-idf-component:
name: Build with ESP-IDF ${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
runs-on: ubuntu-latest
strategy:
matrix:
# The version names here correspond to the versions of espressif/idf Docker image.
# See https://hub.docker.com/r/espressif/idf/tags and
# https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html
# for details.
idf_ver: ["release-v4.4", "release-v5.1"]
idf_target: ["esp32", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2"]
example:
- Advanced/NimBLE_Client
- Advanced/NimBLE_Server
- basic/BLE_client
- basic/BLE_notify
- basic/BLE_scan
- basic/BLE_server
- basic/BLE_uart
- Bluetooth_5/NimBLE_extended_client
- Bluetooth_5/NimBLE_extended_server
- Bluetooth_5/NimBLE_multi_advertiser
- NimBLE_server_get_client_name
exclude:
- idf_target: "esp32"
example: Bluetooth_5/NimBLE_extended_client
- idf_target: "esp32"
example: Bluetooth_5/NimBLE_extended_server
- idf_target: "esp32"
example: Bluetooth_5/NimBLE_multi_advertiser
- idf_ver: release-v4.4
idf_target: "esp32c2"
- idf_ver: release-v4.4
idf_target: "esp32c6"
- idf_ver: release-v4.4
idf_target: "esp32h2"
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: components/esp-nimble-cpp
- name: Build examples
env:
IDF_TARGET: ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
cp -r components/esp-nimble-cpp/examples/* .
idf.py -C ${{ matrix.example }} -DEXTRA_COMPONENT_DIRS=$PWD/components build
build_docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Doxygen Action
uses: mattnotmitt/doxygen-action@v1.9.5
with:
working-directory: 'docs/'

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
docs/doxydocs

View File

@ -1,27 +0,0 @@
sudo: false
before_install:
- cd ${TMPDIR-/tmp}
- wget -q http://doxygen.nl/files/doxygen-1.9.0.src.tar.gz
- tar -xzvf doxygen-1.9.0.src.tar.gz
- mkdir doxygen_build
- cd doxygen_build
- cmake ../doxygen-1.9.0/
- make
- export PATH="${TMPDIR-/tmp}/doxygen_build/bin:$PATH"
- cd ${TRAVIS_BUILD_DIR}
branches:
only:
- master
script:
- doxygen ./docs/Doxyfile
deploy:
provider: pages
skip_cleanup: true
local_dir: docs/html
github_token: $GH_REPO_TOKEN
on:
branch: master

View File

@ -1,6 +1,113 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [Unreleased]
### Changed
- NimBLESecurity class removed.
### Added
- `NimBLEDevice::setDeviceName` to change the device name after initialization.
- `NimBLEHIDDevice::batteryLevel` returns the HID device battery level characteristic.
## [1.4.0] - 2022-07-31
### Fixed
- Fixed missing data from long notification values.
- Fixed NimbleCharacteristicCallbacks::onRead not being called when a non-long read command is received.
- Prevent a potential crash when retrieving characteristics from a service if the result was successful but no characteristics found.
- logs/typos.
### Changed
- AD flags are no longer set in the advertisements of non-connectable beacons, freeing up 3 bytes of advertisement room.
- Save resources when retrieving descriptors if the characteristic handle is the same as the end handle (no descriptors).
- Subscribing to characteristic notifications/indications will now always use write with response, as per BLE specifications.
- `NimBLEClient::discoverAttributes` now returns a bool value to indicate success/failure.
- Scan result callbacks are no longer called when the scan response data is updated in order to reduce duplicates.
### Added
- Preliminary support for non-esp devices, NRF51 and NRF52 devices supported with [n-able arduino core](https://github.com/h2zero/n-able-Arduino)
- Alias added for `NimBLEServerCallbacks::onMTUChange` to `onMtuChanged` in order to support porting code from original library.
- `NimBLEAttValue` Class added to reduce and control RAM footprint of characteristic/descriptor values and support conversions from Arduino Strings and many other data types.
- Bluetooth 5 extended advertising support for capable devices. CODED Phy, 2M Phy, extended advertising data, and multi-advertising are supported, periodic advertising will be implemented in the future.
## [1.3.3] - 2022-02-15
### Changed
- If attribute retrieval fails with a "not found" try again with the 16 bit version if a 128 bit base uuid is used.
### Fixed
- Memory leak when deleting client instance.
- IDf version check for data length extension.
- Memory leak when server services changed.
- Compiler warnings for non-esp32 devices.
## [1.3.2] - 2022-01-15
### Fixed
- Initialize advertising complete callback in NimBLEAdvertising constructor.
- Clear client disconnect timer in constructor before initializing.
- Fix missing data when reading large values.
- Fix missing data in notifications when using a large MTU size and more than 270 bytes of data are sent.
- Workaround fix added for cases when the task notification value is not cleared, causing various functions that should block not to block.
### Added
- `NimBLEClient::getLastError` : Gets the error code of the last function call that produces a return code from the stack.
- `NimBLECharacteristic::notify` : Overload method to send notifications/indications with custom values.
- Added conditional checks for ESP32 specific functions/values to support use of the library on non-esp32 devices.
- Added an alias to use the callback name from the original library `onMtuChanged`.
- `NimBLEClient::setDataLen` and `NimBLEServer::setDataLen`: Data length extension support (IDF version >= 4.3.2 only)
- Config option to set logging level for esp-nimble-cpp
### Changed
- Critical section calls now use the NimBLE API instead of FreeRTOS directly. This removes the need for a `portMUX_TYPE` variable in the class definitions.
- Removed unnecessary variables in `NimBLEService` and changed the constructor no no longer accept `numHandles` and `inst_id` parameters.
## [1.3.1] - 2021-08-04
### Fixed
- Corrected a compiler/linker error when an application or a library uses bluetooth classic due to the redefinition of `btInUse`.
## [1.3.0] - 2021-08-02
### Added
- `NimBLECharacteristic::removeDescriptor`: Dynamically remove a descriptor from a characterisic. Takes effect after all connections are closed and sends a service changed indication.
- `NimBLEService::removeCharacteristic`: Dynamically remove a characteristic from a service. Takes effect after all connections are closed and sends a service changed indication
- `NimBLEServerCallbacks::onMTUChange`: This is callback is called when the MTU is updated after connection with a client.
- ESP32C3 support
- Whitelist API:
- `NimBLEDevice::whiteListAdd`: Add a device to the whitelist.
- `NimBLEDevice::whiteListRemove`: Remove a device from the whitelist.
- `NimBLEDevice::onWhiteList`: Check if the device is on the whitelist.
- `NimBLEDevice::getWhiteListCount`: Gets the size of the whitelist
- `NimBLEDevice::getWhiteListAddress`: Get the address of a device on the whitelist by index value.
- Bond management API:
- `NimBLEDevice::getNumBonds`: Gets the number of bonds stored.
- `NimBLEDevice::isBonded`: Checks if the device is bonded.
- `NimBLEDevice::deleteAllBonds`: Deletes all bonds.
- `NimBLEDevice::getBondedAddress`: Gets the address of a bonded device by the index value.
- `NimBLECharacteristic::getCallbacks` to retrieve the current callback handler.
- Connection Information class: `NimBLEConnInfo`.
- `NimBLEScan::clearDuplicateCache`: This can be used to reset the cache of advertised devices so they will be immediately discovered again.
### Changed
- FreeRTOS files have been removed as they are not used by the library.
- Services, characteristics and descriptors can now be created statically and added after.
- Excess logging and some asserts removed.
- Use ESP_LOGx macros to enable using local log level filtering.
### Fixed
- `NimBLECharacteristicCallbacks::onSubscribe` Is now called after the connection is added to the vector.
- Corrected bonding failure when reinitializing the BLE stack.
- Writing to a characterisic with a std::string value now correctly writes values with null characters.
- Retrieving remote descriptors now uses the characterisic end handle correctly.
- Missing data in long writes to remote descriptors.
- Hanging on task notification when sending an indication from the characteristic callback.
- BLE controller memory could be released when using Arduino as a component.
- Complile errors with NimBLE release 1.3.0.
## [1.2.0] - 2021-02-08 ## [1.2.0] - 2021-02-08

View File

@ -2,37 +2,50 @@
# CMakeLists in this exact order for cmake to work correctly # CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
idf_build_get_property(__hack_component_targets __COMPONENT_TARGETS) if(__COMPONENT_TARGETS MATCHES "___idf_esp-nimble-component")
if("esp-nimble-component" IN_LIST BUILD_COMPONENTS OR "__esp-nimble-component" IN_LIST __hack_component_targets)
list(APPEND ESP_NIMBLE_PRIV_REQUIRES list(APPEND ESP_NIMBLE_PRIV_REQUIRES
esp-nimble-component esp-nimble-component
) )
elseif("nimble" IN_LIST BUILD_COMPONENTS OR "__nimble" IN_LIST __hack_component_targets) elseif(__COMPONENT_TARGETS MATCHES "__idf_nimble")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES list(APPEND ESP_NIMBLE_PRIV_REQUIRES
nimble nimble
) )
endif() endif()
if("arduino" IN_LIST BUILD_COMPONENTS OR __hack_component_targets MATCHES "__idf_arduino") # Arduino install using IDF component manager
if(__COMPONENT_TARGETS MATCHES "___idf_espressif__arduino-esp32")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
arduino-esp32
)
# Manual installation of Arduino framework
elseif(__COMPONENT_TARGETS MATCHES "__idf_arduino")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES list(APPEND ESP_NIMBLE_PRIV_REQUIRES
arduino arduino
) )
# PlatformIO
elseif(__COMPONENT_TARGETS MATCHES "___idf_framework-arduinoespressif32")
list(APPEND ESP_NIMBLE_PRIV_REQUIRES
framework-arduinoespressif32
)
endif() endif()
idf_component_register( idf_component_register(
REQUIRED_IDF_TARGETS REQUIRED_IDF_TARGETS
"esp32" "esp32"
"esp32s3" "esp32s3"
"esp32c2"
"esp32c3" "esp32c3"
"esp32c6"
"esp32h2"
"esp32p4"
INCLUDE_DIRS INCLUDE_DIRS
"src" "src"
SRCS SRCS
"src/FreeRTOS.cpp"
"src/NimBLE2904.cpp" "src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp" "src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp" "src/NimBLEAdvertisedDevice.cpp"
"src/NimBLEAdvertising.cpp" "src/NimBLEAdvertising.cpp"
"src/NimBLEAttValue.cpp"
"src/NimBLEBeacon.cpp" "src/NimBLEBeacon.cpp"
"src/NimBLECharacteristic.cpp" "src/NimBLECharacteristic.cpp"
"src/NimBLEClient.cpp" "src/NimBLEClient.cpp"
@ -40,12 +53,13 @@ idf_component_register(
"src/NimBLEDevice.cpp" "src/NimBLEDevice.cpp"
"src/NimBLEEddystoneTLM.cpp" "src/NimBLEEddystoneTLM.cpp"
"src/NimBLEEddystoneURL.cpp" "src/NimBLEEddystoneURL.cpp"
"src/NimBLEExtAdvertising.cpp"
"src/NimBLEHIDDevice.cpp" "src/NimBLEHIDDevice.cpp"
"src/NimBLERemoteCharacteristic.cpp" "src/NimBLERemoteCharacteristic.cpp"
"src/NimBLERemoteDescriptor.cpp" "src/NimBLERemoteDescriptor.cpp"
"src/NimBLERemoteService.cpp" "src/NimBLERemoteService.cpp"
"src/NimBLERemoteValueAttribute.cpp"
"src/NimBLEScan.cpp" "src/NimBLEScan.cpp"
"src/NimBLESecurity.cpp"
"src/NimBLEServer.cpp" "src/NimBLEServer.cpp"
"src/NimBLEService.cpp" "src/NimBLEService.cpp"
"src/NimBLEUtils.cpp" "src/NimBLEUtils.cpp"
@ -53,6 +67,7 @@ idf_component_register(
REQUIRES REQUIRES
bt bt
nvs_flash nvs_flash
driver
PRIV_REQUIRES PRIV_REQUIRES
${ESP_NIMBLE_PRIV_REQUIRES} ${ESP_NIMBLE_PRIV_REQUIRES}
) )

View File

@ -5,7 +5,6 @@ cmake_minimum_required(VERSION 3.5)
set(SUPPORTED_TARGETS esp32) set(SUPPORTED_TARGETS esp32)
set(COMPONENT_SRCS set(COMPONENT_SRCS
"src/FreeRTOS.cpp"
"src/NimBLE2904.cpp" "src/NimBLE2904.cpp"
"src/NimBLEAddress.cpp" "src/NimBLEAddress.cpp"
"src/NimBLEAdvertisedDevice.cpp" "src/NimBLEAdvertisedDevice.cpp"

108
Kconfig
View File

@ -1,5 +1,31 @@
menu "ESP-NimBLE-CPP configuration" menu "ESP-NimBLE-CPP configuration"
choice NIMBLE_CPP_LOG_LEVEL
prompt "NimBLE CPP log verbosity"
default NIMBLE_CPP_LOG_LEVEL_NONE
help
Select NimBLE CPP log verbosity level.
config NIMBLE_CPP_LOG_LEVEL_NONE
bool "No logs"
config NIMBLE_CPP_LOG_LEVEL_ERROR
bool "Error logs"
config NIMBLE_CPP_LOG_LEVEL_WARNING
bool "Warning logs"
config NIMBLE_CPP_LOG_LEVEL_INFO
bool "Info logs"
config NIMBLE_CPP_LOG_LEVEL_DEBUG
bool "Debug logs"
endchoice #NIMBLE_CPP_LOG_LEVEL
config NIMBLE_CPP_LOG_LEVEL
int
default 0 if NIMBLE_CPP_LOG_LEVEL_NONE
default 1 if NIMBLE_CPP_LOG_LEVEL_ERROR
default 2 if NIMBLE_CPP_LOG_LEVEL_WARNING
default 3 if NIMBLE_CPP_LOG_LEVEL_INFO
default 4 if NIMBLE_CPP_LOG_LEVEL_DEBUG
config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT config NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
bool "Show NimBLE return codes as text in debug log." bool "Show NimBLE return codes as text in debug log."
default "n" default "n"
@ -16,7 +42,7 @@ config NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
messages in the debug log. This will use approximately 1kB messages in the debug log. This will use approximately 1kB
of flash memory. of flash memory.
config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT config NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT
bool "Show advertisment types as text in debug log." bool "Show advertisment types as text in debug log."
default "n" default "n"
help help
@ -24,4 +50,84 @@ config NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
while scanning as text messages in the debug log. while scanning as text messages in the debug log.
This will use approximately 250 bytes of flash memory. This will use approximately 250 bytes of flash memory.
config NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
bool "Enable timestamps to be stored with attribute values."
default "n"
help
Enabling this option will store the timestamp when an attribute value is updated.
This allows for checking the last update time using getTimeStamp()
or getValue(time_t*). If disabled, the timestamp returned from these functions will be 0.
Disabling timestamps will reduce the memory used for each value.
config NIMBLE_CPP_ATT_VALUE_INIT_LENGTH
int "Initial attribute value size (bytes) for empty values."
range 1 512
default 20
help
Sets the default allocation size (bytes) for each attribute if not specified
when the constructor is called. This is also the size used when a remote
characteristic or descriptor is constructed before a value is read/notifed.
Increasing this will reduce reallocations but increase memory footprint.
config NIMBLE_CPP_DEBUG_ASSERT_ENABLED
bool "Enable debug asserts."
default "n"
help
Enabling this option will add debug asserts to the NimBLE CPP library.
This will use approximately 1kB of flash memory.
config NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT
int "FreeRTOS task block bit"
default 31
help
Configure the bit to set in the task notification value when a task is blocked waiting for an event.
This should be set to a bit that is not used by other notifications in the system.
#
# BT config
#
config BT_ENABLED
bool "Bluetooth"
default "y"
help
Select this option to enable Bluetooth and show the submenu with Bluetooth configuration choices.
config BT_NIMBLE_ENABLED
bool "NimBLE - BLE only"
default "y"
help
This option is recommended for BLE only usecases to save on memory
if IDF_TARGET_ESP32P4
config BT_NIMBLE_TRANSPORT_UART
bool "Enable Uart Transport"
default "n"
#
# Enable ESP Hosted BT
# Used as VHCI transport between BT Host and Controller
#
config ESP_ENABLE_BT
bool "Enable Hosted Bluetooth support"
default "y"
help
Enable Bluetooth Support via Hosted
choice ESP_WIFI_REMOTE_LIBRARY
prompt "Choose WiFi-remote implementation"
default ESP_WIFI_REMOTE_LIBRARY_HOSTED
help
Select type of WiFi Remote implementation
ESP-HOSTED is the default and most versatile option.
It's also possible to use EPPP, which uses PPPoS link between micros and NAPT, so it's slower
and less universal.
config ESP_WIFI_REMOTE_LIBRARY_HOSTED
bool "ESP-HOSTED"
endchoice
endif
endmenu endmenu

View File

@ -68,9 +68,3 @@ in your project/CMakeLists.txt after the line `include($ENV{IDF_PATH}/tools/cmak
* [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code. * [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code.
<br/> <br/>
# Todo
- Improve host reset handler
- Implement random address handling
- Implement bond management
- Add Bluetooth Mesh
<br/>

View File

@ -0,0 +1,28 @@
# Bluetooth 5.x features
## About extended advertising
Extended advertising allows for much more capability and flexibility.
* Allows for 251 bytes of advertisement data and up to 1650 bytes when chained (configuration dependant) vs 31.
* New PHY's (physical layers) that allow for faster data rate (2M PHY) or long range/slower data rates (CODED PHY) as well as the original 1M PHY.
* New periodic advertising, allowing the scanning device to sync with the advertisements of a beacon. This allows for the scanning device to sleep or perform other tasks before the next expected advertisement is sent, preserving cpu cycles and power (To be implemented).
<br/>
## Enabling extended advertising
Extended advertising is supported when enabled with the config option `CONFIG_BT_NIMBLE_EXT_ADV` set to a value of 1. This is done in menuconfig under `Component config > Bluetooth > NimBLE options > Enable extended advertising`, or set in `nimconfig.h` for Arduino, or in `build_flags` in PlatformIO.
When enabled the following will occur:
* `NimBLEScan::start` method will scan on both the 1M PHY and the coded PHY standards automatically.
* `NimBLEClient::connect` will use the primary PHY the device is listening on, unless specified (see below).
* `NimBLEClient::setConnectPhy` becomes available to specify the PHY's to connect with (default is all).
* `NimBLEAdvertising` is no longer available for use and is replaced by `NimBLEExtAdvertising`. `NimBLEDevice::getAdvertising` will now return an instance of `NimBLEExtAdvertising`.
* `NimBLEAdvertisementData` is no longer available for use and is replaced by `NimBLEExtAdvertisement`. This new class is where everything about the advertisement is configured, including the advertisement intervals and advertisement ended callback.

View File

@ -1,117 +0,0 @@
# Arduino command line and platformio config options
`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
Sets the number of simultaneous connections (esp controller max is 9)
- Default value is 3
<br/>
`CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU`
Sets the default MTU size.
- Default value is 255
<br/>
`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
Set the default device name
- Default value is "nimble"
<br/>
`CONFIG_BT_NIMBLE_DEBUG`
If defined, enables debug log messages from the NimBLE host
- Uses approx. 32kB of flash memory.
<br/>
`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT`
If defined, NimBLE host return codes will be printed as text in debug log messages.
- Uses approx. 7kB of flash memory.
<br/>
`CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT`
If defined, GAP event codes will be printed as text in debug log messages.
- Uses approx. 1kB of flash memory.
<br/>
`CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT`
If defined, advertisment types will be printed as text while scanning in debug log messages.
- Uses approx. 250 bytes of flash memory.
<br/>
`CONFIG_BT_NIMBLE_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
- Options: 0 or 1
<br/>
`CONFIG_BT_NIMBLE_TASK_STACK_SIZE`
Set the task stack size for the NimBLE core.
- Default is 4096
<br/>

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
# Improvements and updates # Improvements and updates
Many improvements have been made to this library vs the original, this is a brief overview of the most significant changes. Many improvements have been made to this library vs the original, this is a brief overview of the most significant changes. Refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) for further information on class specifics.
Refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) for futher information on class specifics.
* [Server](#server) * [Server](#server)
* [Advertising](#advertising) * [Advertising](#advertising)
@ -12,10 +11,13 @@ Refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annot
<a name="server"></a> <a name="server"></a>
# Server # Server
`NimBLEService::NimBLEService::createCharacteristic` takes a 3rd parameter to specify the maximum data size that can be stored by the characteristic. This allows for limiting the RAM use of the characteristic in cases where small amounts of data are expected.
<br/>
`NimBLECharacteristic::setValue(const T &s)` `NimBLECharacteristic::setValue(const T &s)`
`NimBLEDescriptor::setValue(const T &s)` `NimBLEDescriptor::setValue(const T &s)`
Now use a template to accomodate standard and custom types/values. Now use the `NimbleAttValue` class and templates to accommodate standard and custom types/values.
**Example** **Example**
``` ```
@ -34,12 +36,14 @@ struct my_struct{
myStruct.flt = 1234.56; myStruct.flt = 1234.56;
pCharacteristic->setValue(myStruct); pCharacteristic->setValue(myStruct);
```
This will send the struct to the recieving client when read or a notification sent.
`NimBLECharacteristic::getValue` now takes an optional timestamp parameter which will update it's value with // Arduino String support
the time the last value was recieved. In addition an overloaded template has been added to retrieve the value String myString = "Hello";
as a type specified by the user. pCharacteristic->setValue(myString);
```
This will send the struct to the receiving client when read or a notification sent.
`NimBLECharacteristic::getValue` now takes an optional timestamp parameter which will update it's value with the time the last value was received. In addition an overloaded template has been added to retrieve the value as a type specified by the user.
**Example** **Example**
``` ```
@ -53,22 +57,18 @@ as a type specified by the user.
A new method `NimBLEServer::advertiseOnDisconnect(bool)` has been implemented to control this, true(default) = enabled. A new method `NimBLEServer::advertiseOnDisconnect(bool)` has been implemented to control this, true(default) = enabled.
<br/> <br/>
`NimBLEServer::removeService` takes an additional parameter `bool deleteSvc` that if true will delete the service `NimBLEServer::removeService` takes an additional parameter `bool deleteSvc` that if true will delete the service and all characteristics / descriptors belonging to it and invalidating any pointers to them.
and all characteristics / descriptors belonging to it and invalidating any pointers to them.
If false the service is only removed from visibility by clients. The pointers to the service and If false the service is only removed from visibility by clients. The pointers to the service and it's characteristics / descriptors will remain valid and the service can be re-added in the future using `NimBLEServer::addService`.
it's characteristics / descriptors will remain valid and the service can be re-added in the future
using `NimBLEServer::addService`.
<br/> <br/>
<a name="advertising"></a> <a name="advertising"></a>
# Advertising # Advertising
`NimBLEAdvertising::start` `NimBLEAdvertising::start`
Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
This provides an opportunity to update the advertisment data if desired. This provides an opportunity to update the advertisement data if desired.
Also now returns a bool value to indicate if advertising successfully started or not. Also now returns a bool value to indicate if advertising successfully started or not.
<br/> <br/>
@ -97,24 +97,25 @@ struct my_struct{
<br/> <br/>
`NimBLERemoteCharacteristic::registerForNotify` `NimBLERemoteCharacteristic::registerForNotify`
Has been **deprecated** as now the internally stored characteristic value is updated when notification/indication is recieved. Has been removed.
`NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::unsubscribe` have been implemented to replace it. `NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::unsubscribe` have been implemented to replace it.
A callback is no longer requred to get the most recent value unless timing is important. Instead, the application can call `NimBLERemoteCharacteristic::getValue` to
get the last updated value any time. The internally stored characteristic value is now updated when notification/indication is recieved. Making a callback no longer required to get the most recent value unless timing is important. Instead, the application can call `NimBLERemoteCharacteristic::getValue` to get the most recent value any time.
<br/> <br/>
The `notifiy_callback` function is now defined as a `std::function` to take advantage of using `std::bind` to specifiy a class member function for the callback. The `notify_callback` function is now defined as a `std::function` to take advantage of using `std::bind` to specify a class member function for the callback.
Example: Example:
``` ```
using namespace std::placeholders; using namespace std::placeholders;
notify_callback callback = std::bind(&<ClassName>::<memberFunctionCallbackName>, this, _1, _2, _3, _4); notify_callback callback = std::bind(&<ClassName>::<memberFunctionCallbackName>, this, _1, _2, _3, _4);
<remoteCharacteristicInstance>->subscribe(true, callback); <remoteCharacteristicInstance>->subscribe(true, callback);
``` ```
`NimBLERemoteCharacteristic::readValue` and `NimBLERemoteCharacteristic::getValue` take an optional timestamp parameter which will update it's value with `NimBLERemoteCharacteristic::readValue` and `NimBLERemoteCharacteristic::getValue` take an optional timestamp parameter which will update it's value with
the time the last value was recieved. the time the last value was received.
> NimBLEClient::getService > NimBLEClient::getService
> NimBLERemoteService::getCharacteristic > NimBLERemoteService::getCharacteristic
@ -126,8 +127,7 @@ the specified attribute from the peripheral.
These changes allow more control for the user to manage the resources used for the attributes. These changes allow more control for the user to manage the resources used for the attributes.
<br/> <br/>
`NimBLEClient::connect()` can now be called without an address or advertised device parameter. This will connect to the `NimBLEClient::connect()` can now be called without an address or advertised device parameter. This will connect to the device with the address previously set when last connected or set with `NimBLEDevice::setPeerAddress()`.
device with the address previously set when last connected or set with `NimBLEDevice::setPeerAddress()`.
<a name="general"></a> <a name="general"></a>
# General # General
@ -141,8 +141,9 @@ New constructor for `NimBLEUUID(uint32_t, uint16_t, uint16_t, uint64_t)` added t
Security/pairing operations are now handled in the respective `NimBLEClientCallbacks` and `NimBLEServerCallbacks` classes, `NimBLESecurity`(deprecated) remains for backward compatibility. Security/pairing operations are now handled in the respective `NimBLEClientCallbacks` and `NimBLEServerCallbacks` classes, `NimBLESecurity`(deprecated) remains for backward compatibility.
Configuration options have been added to add or remove debugging information, when disabled (default) significatly reduces binary size. Configuration options have been added to add or remove debugging information, when disabled (default) significantly reduces binary size.
In ESP-IDF the options are in menuconfig: `Main menu -> ESP-NimBLE-cpp configuration`. In ESP-IDF the options are in menuconfig: `Main menu -> ESP-NimBLE-cpp configuration`.
For Arduino the options must be commented / uncommented in nimconfig.h. For Arduino the options must be commented / uncommented in nimconfig.h.
<br/>
Characteristics and descriptors now use the `NimBLEAttValue` class to store their data. This is a polymorphic container class capable of converting to/from many different types efficiently. See: [#286](https://github.com/h2zero/NimBLE-Arduino/pull/286)

View File

@ -4,19 +4,23 @@ This guide describes the required changes to existing projects migrating from th
**The changes listed here are only the required changes that must be made**, and a short overview of options for migrating existing applications. **The changes listed here are only the required changes that must be made**, and a short overview of options for migrating existing applications.
For more information on the improvements and additions please refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) and [Improvements and updates](Improvements_and_updates.md) For more information on the improvements and additions please refer to the [class documentation](https://h2zero.github.io/NimBLE-Arduino/annotated.html) and [Improvements and updates](Improvements_and_updates.md)
* [General Changes](#general-information) * [General Changes](#general-information)
* [Server](#server-api) * [Server](#server-api)
* [Services](#services) * [Services](#services)
* [characteristics](#characteristics) * [Characteristics](#characteristics)
* [descriptors](#descriptors) * [Characteristic Callbacks](#characteristic-callbacks)
* [Descriptors](#descriptors)
* [Descriptor Callbacks](#descriptor-callbacks)
* [Security](#server-security) * [Security](#server-security)
* [Advertising](#advertising-api) * [Advertising](#advertising-api)
* [Client](#client-api) * [Client](#client-api)
* [Remote Services](#remote-services) * [Remote Services](#remote-services)
* [Remote characteristics](#remote-characteristics) * [Remote characteristics](#remote-characteristics)
* [Client Callbacks](#client-callbacks)
* [Security](#client-security) * [Security](#client-security)
* [Scanning](#scan-api)
* [General Security](#security-api) * [General Security](#security-api)
* [Configuration](#arduino-configuration) * [Configuration](#arduino-configuration)
<br/> <br/>
@ -27,30 +31,24 @@ For more information on the improvements and additions please refer to the [clas
### Header Files ### Header Files
All classes are accessible by including `NimBLEDevice.h` in your application, no further headers need to be included. All classes are accessible by including `NimBLEDevice.h` in your application, no further headers need to be included.
(Mainly for Arduino) You may choose to include `NimBLELog.h` in your appplication if you want to use the `NIMBLE_LOGx` macros for debugging. (Mainly for Arduino) You may choose to include `NimBLELog.h` in your application if you want to use the `NIMBLE_LOGx` macros for debugging. These macros are used the same way as the `ESP_LOGx` macros.
These macros are used the same way as the `ESP_LOGx` macros.
<br/> <br/>
### Class Names ### Class Names
Class names remain the same as the original with the addition of a "Nim" prefix. Class names remain the same as the original with the addition of a "Nim" prefix.
For example `BLEDevice` is now `NimBLEDevice` and `BLEServer` is now `NimBLEServer` etc. For example `BLEDevice` is now `NimBLEDevice` and `BLEServer` is now `NimBLEServer` etc.
For convienience definitions have been added to allow applications to use either name for all classes For convenience definitions have been added to allow applications to use either name for all classes this means **no class names need to be changed in existing code** and makes migrating easier.
this means **no class names need to be changed in existing code** and makes migrating easier.
<br/> <br/>
### BLE Addresses ### BLE Addresses
`BLEAddress` (`NimBLEAddress`) When constructing an address the constructor now takes an *(optional)* `uint8_t type` paramameter `BLEAddress` (`NimBLEAddress`) When constructing an address the constructor now takes an *(optional)* `uint8_t type` parameter to specify the address type. Default is (0) Public static address.
to specify the address type. Default is (0) Public static address.
For example `BLEAddress addr(11:22:33:44:55:66, 1)` will create the address object with an address type of: 1 (Random). For example `BLEAddress addr(11:22:33:44:55:66, 1)` will create the address object with an address type of: 1 (Random).
As this paramameter is optional no changes to existing code are needed, it is mentioned here for information. As this parameter is optional no changes to existing code are needed, it is mentioned here for information.
<br/>
`BLEAddress::getNative` (`NimBLEAddress::getNative`) returns a uint8_t pointer to the native address byte array. `BLEAddress::getNative` (`NimBLEAddress::getNative`) returns a uint8_t pointer to the native address byte array. In this library the address bytes are stored in reverse order from the original library. This is due to the way the NimBLE stack expects addresses to be presented to it. All other functions such as `toString` are not affected as the endian change is made within them.
In this library the address bytes are stored in reverse order from the original library. This is due to the way
the NimBLE stack expects addresses to be presented to it. All other functions such as `toString` are
not affected as the endian change is made within them.
<br/> <br/>
<a name="server-api"></a> <a name="server-api"></a>
@ -59,6 +57,27 @@ Creating a `BLEServer` instance is the same as original, no changes required.
For example `BLEDevice::createServer()` will work just as it did before. For example `BLEDevice::createServer()` will work just as it did before.
`BLEServerCallbacks` (`NimBLEServerCallbacks`) has new methods for handling security operations. `BLEServerCallbacks` (`NimBLEServerCallbacks`) has new methods for handling security operations.
<br/>
`BLEServerCallbacks::onConnect` (`NimBLEServerCallbacks::onConnect`) only has a single callback declaration which takes an additional (required) parameter `NimBLEConnInfo & connInfo`, which has methods to get information about the connected peer.
```
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo)`
```
<br/>
`BLEServerCallbacks::onDisconnect` (`NimBLEServerCallbacks::onDisconnect`) only has a single callback declaration which takes 2 additional (required) parameters `NimBLEConnInfo & connInfo`, which provides information about the peer and `int reason`, which gives the reason code for disconnection.
```
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason)`
```
<br/>
`BLEServerCallbacks::onMtuChanged` (`NimBLEServerCallbacks::onMtuChanged`) takes the parameter `NimBLEConnInfo & connInfo` instead of `esp_ble_gatts_cb_param_t`, which has methods to get information about the connected peer.
```
onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo)
```
**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable. **Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
<br/> <br/>
@ -66,37 +85,37 @@ For example `BLEDevice::createServer()` will work just as it did before.
### Services ### Services
Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required. Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required.
For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before. For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before.
<br/>
<a name="characteristics"></a> <a name="characteristics"></a>
### Characteristics ### Characteristics
The constructor for `(Nim)BLECharacteristic` is now private, so if you currently subclass it to add logic you should switch to use `NimBLEService::createCharacteristic` instead. Any custom processing logic previously in a `BLECharacteristic` subclass should be moved to a `NimBLECharacteristicCallbacks` subclass instead, and passed into `NimBLECharacteristic::setCallbacks`.
`BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed. `BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed.
When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`. When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`.
#### Originally #### Originally
> BLECharacteristic::PROPERTY_READ | > BLECharacteristic::PROPERTY_READ |
> BLECharacteristic::PROPERTY_WRITE BLECharacteristic::PROPERTY_WRITE
#### Is Now #### Is Now
> NIMBLE_PROPERTY::READ | > NIMBLE_PROPERTY::READ |
> NIMBLE_PROPERTY::WRITE NIMBLE_PROPERTY::WRITE
<br/> <br/>
#### The full list of properties #### The full list of properties
> NIMBLE_PROPERTY::READ > NIMBLE_PROPERTY::READ
> NIMBLE_PROPERTY::READ_ENC NIMBLE_PROPERTY::READ_ENC
> NIMBLE_PROPERTY::READ_AUTHEN NIMBLE_PROPERTY::READ_AUTHEN
> NIMBLE_PROPERTY::READ_AUTHOR NIMBLE_PROPERTY::READ_AUTHOR
> NIMBLE_PROPERTY::WRITE NIMBLE_PROPERTY::WRITE
> NIMBLE_PROPERTY::WRITE_NR NIMBLE_PROPERTY::WRITE_NR
> NIMBLE_PROPERTY::WRITE_ENC NIMBLE_PROPERTY::WRITE_ENC
> NIMBLE_PROPERTY::WRITE_AUTHEN NIMBLE_PROPERTY::WRITE_AUTHEN
> NIMBLE_PROPERTY::WRITE_AUTHOR NIMBLE_PROPERTY::WRITE_AUTHOR
> NIMBLE_PROPERTY::BROADCAST NIMBLE_PROPERTY::BROADCAST
> NIMBLE_PROPERTY::NOTIFY NIMBLE_PROPERTY::NOTIFY
> NIMBLE_PROPERTY::INDICATE NIMBLE_PROPERTY::INDICATE
<br/> <br/>
**Example:** **Example:**
@ -118,8 +137,15 @@ BLECharacteristic *pCharacteristic = pService->createCharacteristic(
``` ```
<br/> <br/>
`BLECharacteristicCallbacks` (`NimBLECharacteristicCallbacks`) has a new method `NimBLECharacteristicCallbacks::onSubscribe` <a name="characteristic-callbacks"></a>
which is called when a client subscribes to notifications/indications. #### Characteristic callbacks
`BLECharacteristicCallbacks` (`NimBLECharacteristicCallbacks`) has a new method `NimBLECharacteristicCallbacks::onSubscribe` which is called when a client subscribes to notifications/indications.
`BLECharacteristicCallbacks::onRead` (`NimBLECharacteristicCallbacks::onRead`) only has a single callback declaration, which takes an additional (required) parameter of `NimBLEConnInfo& connInfo`, which provides connection information about the peer.
`BLECharacteristicCallbacks::onWrite` (`NimBLECharacteristicCallbacks::onWrite`) only has a single callback declaration, which takes an additional (required) parameter of `NimBLEConnInfo& connInfo`, which provides connection information about the peer.
`BLECharacteristicCallbacks::onStatus` (`NimBLECharacteristicCallbacks::onStatus`) has had the status parameter removed as it was unnecessary since the status code from the BLE stack was also provided. The status code for success is 0 for notifications and BLE_HS_EDONE for indications, any other value is an error.
**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable. **Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
<br/> <br/>
@ -144,7 +170,6 @@ my_struct_t myStruct = pChr->getValue<my_struct_t>();
<a name="descriptors"></a> <a name="descriptors"></a>
### Descriptors ### Descriptors
The previous method `BLECharacteristic::addDescriptor()` has been removed.
Descriptors are now created using the `NimBLECharacteristic::createDescriptor` method. Descriptors are now created using the `NimBLECharacteristic::createDescriptor` method.
@ -194,45 +219,44 @@ p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904");
``` ```
<br/> <br/>
<a name="descriptor-callbacks"></a>
#### Descriptor callbacks
> `BLEDescriptorCallbacks::onRead` (`NimBLEDescriptorCallbacks::onRead`)
`BLEDescriptorCallbacks::onwrite` (`NimBLEDescriptorCallbacks::onwrite`)
The above descriptor callbacks take an additional (required) parameter `NimBLEConnInfo& connInfo`, which contains the connection information of the peer.
<br/>
<a name="server-security"></a> <a name="server-security"></a>
### Server Security ### Server Security
Security is set on the characteristic or descriptor properties by applying one of the following: Security is set on the characteristic or descriptor properties by applying one of the following:
> NIMBLE_PROPERTY::READ_ENC > NIMBLE_PROPERTY::READ_ENC
> NIMBLE_PROPERTY::READ_AUTHEN NIMBLE_PROPERTY::READ_AUTHEN
> NIMBLE_PROPERTY::READ_AUTHOR NIMBLE_PROPERTY::READ_AUTHOR
> NIMBLE_PROPERTY::WRITE_ENC NIMBLE_PROPERTY::WRITE_ENC
> NIMBLE_PROPERTY::WRITE_AUTHEN NIMBLE_PROPERTY::WRITE_AUTHEN
> NIMBLE_PROPERTY::WRITE_AUTHOR NIMBLE_PROPERTY::WRITE_AUTHOR
<br/>
When a peer wants to read or write a characteristic or descriptor with any of these properties applied it will trigger the pairing process. By default the "just-works" pairing will be performed automatically.
When a peer wants to read or write a characteristic or descriptor with any of these properties applied
it will trigger the pairing process. By default the "just-works" pairing will be performed automatically.
This can be changed to use passkey authentication or numeric comparison. See [Security API](#security-api) for details. This can be changed to use passkey authentication or numeric comparison. See [Security API](#security-api) for details.
<br/> <br/>
<a name="advertising-api"></a> <a name="advertising-api"></a>
## Advertising API ## Advertising API
Advertising works the same as the original API except: Advertising works the same as the original API except:
> BLEAdvertising::setMinPreferred
> BLEAdvertising::setMaxPreferred
These methods were found to not provide useful functionality and consumed valuable advertising space (6 bytes of 31) if used unknowingly.
If you wish to advertise these parameters you can still do so manually via `BLEAdvertisementData::addData` (`NimBLEAdvertisementData::addData`).
<br/>
Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or
`NimBLEAdvertising::setAppearance` or similar methods. 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.~~
No longer true as of release 1.2.0 and above, custom scan response is now supported without custom advertisement data.
<br/> <br/>
> BLEAdvertising::start (NimBLEAdvertising::start) > BLEAdvertising::start (NimBLEAdvertising::start)
Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback Now takes 2 optional parameters, the first is the duration to advertise for (in milliseconds), the second is a callback that is invoked when advertising ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API). This provides an opportunity to update the advertisement data if desired.
This provides an opportunity to update the advertisment data if desired.
<br/> <br/>
<a name="client-api"></a> <a name="client-api"></a>
@ -240,8 +264,7 @@ This provides an opportunity to update the advertisment data if desired.
Client instances are created just as before with `BLEDevice::createClient` (`NimBLEDevice::createClient`). Client instances are created just as before with `BLEDevice::createClient` (`NimBLEDevice::createClient`).
Multiple client instances can be created, up to the maximum number of connections set in the config file (default: 3). Multiple client instances can be created, up to the maximum number of connections set in the config file (default: 3). To delete a client instance you must use `NimBLEDevice::deleteClient`.
To delete a client instance you must use `NimBLEDevice::deleteClient`.
`BLEClient::connect`(`NimBLEClient::connect`) Has had it's parameters altered. `BLEClient::connect`(`NimBLEClient::connect`) Has had it's parameters altered.
Defined as: Defined as:
@ -249,24 +272,22 @@ Defined as:
> NimBLEClient::connect(NimBLEAdvertisedDevice\* device, bool deleteServices = true); > NimBLEClient::connect(NimBLEAdvertisedDevice\* device, bool deleteServices = true);
> NimBLEClient::connect(NimBLEAddress address, bool deleteServices = true); > NimBLEClient::connect(NimBLEAddress address, bool deleteServices = true);
The type parameter has been removed and a new bool parameter has been added to indicate if the client should The type parameter has been removed and a new bool parameter has been added to indicate if the client should delete the attribute database previously retrieved (if applicable) for the peripheral, default value is true.
delete the attribute database previously retrieved (if applicable) for the peripheral, default value is true.
If set to false the client will use the attribute database it retrieved from the peripheral when previously connected. If set to false the client will use the attribute database it retrieved from the peripheral when previously connected.
This allows for faster connections and power saving if the devices dropped connection and are reconnecting. This allows for faster connections and power saving if the devices dropped connection and are reconnecting.
<br/> <br/>
> `BLEClient::getServices` (`NimBLEClient::getServices`) > `BLEClient::getServices` (`NimBLEClient::getServices`)
This method now takes an optional (bool) parameter to indicate if the services should be retrieved from the server (true) or This method now takes an optional (bool) parameter to indicate if the services should be retrieved from the server (true) or the currently known database returned (false : default).
the currently known database returned (false : default).
Also now returns a pointer to `std::vector` instead of `std::map`. Also now returns a pointer to `std::vector` instead of `std::map`.
<br/> <br/>
**Removed:** the automatic discovery of all peripheral attributes as they consumed time and resources for data **Removed:** the automatic discovery of all peripheral attributes as they consumed time and resources for data the user may not be interested in.
the user may not be interested in.
**Added:** `NimBLEClient::discoverAttributes` for the user to discover all the peripheral attributes **Added:** `NimBLEClient::discoverAttributes` for the user to discover all the peripheral attributes to replace the the removed automatic functionality.
to replace the the removed automatic functionality.
<br/> <br/>
<a name="remote-services"></a> <a name="remote-services"></a>
@ -287,17 +308,18 @@ Also now returns a pointer to `std::vector` instead of `std::map`.
<a name="remote-characteristics"></a> <a name="remote-characteristics"></a>
### Remote Characteristics ### Remote Characteristics
`BLERemoteCharacteristic` (`NimBLERemoteCharacteristic`) There have been a few changes to the methods in this class: `BLERemoteCharacteristic` (`NimBLERemoteCharacteristic`)
There have been a few changes to the methods in this class:
> `BLERemoteCharacteristic::writeValue` (`NimBLERemoteCharacteristic::writeValue`) > `BLERemoteCharacteristic::writeValue` (`NimBLERemoteCharacteristic::writeValue`)
> `BLERemoteCharacteristic::registerForNotify` (`NimBLERemoteCharacteristic::registerForNotify`)
Now return true or false to indicate success or failure so you can choose to disconnect or try again. Now returns true or false to indicate success or failure so you can choose to disconnect or try again.
<br/> <br/>
> `BLERemoteCharacteristic::registerForNotify` (`NimBLERemoteCharacteristic::registerForNotify`) > `BLERemoteCharacteristic::registerForNotify`
Has been removed.
Is now **deprecated**.
> `NimBLERemoteCharacteristic::subscribe` > `NimBLERemoteCharacteristic::subscribe`
> `NimBLERemoteCharacteristic::unsubscribe` > `NimBLERemoteCharacteristic::unsubscribe`
@ -309,7 +331,7 @@ Are the new methods added to replace it.
> `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`) > `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`)
> `BLERemoteCharacteristic::readFloat` (`NimBLERemoteCharacteristic::readFloat`) > `BLERemoteCharacteristic::readFloat` (`NimBLERemoteCharacteristic::readFloat`)
Are **deprecated** a template: NimBLERemoteCharacteristic::readValue<type\>(time_t\*, bool) has been added to replace them. Are **deprecated** a template: `NimBLERemoteCharacteristic::readValue<type\>(time_t\*, bool)` has been added to replace them.
<br/> <br/>
> `BLERemoteCharacteristic::readRawData` > `BLERemoteCharacteristic::readRawData`
@ -317,7 +339,7 @@ Are **deprecated** a template: NimBLERemoteCharacteristic::readValue<type\>(time
**Has been removed from the API** **Has been removed from the API**
Originally it stored an unnecessary copy of the data and was returning a `uint8_t` pointer to volatile internal data. Originally it stored an unnecessary copy of the data and was returning a `uint8_t` pointer to volatile internal data.
The user application should use `NimBLERemoteCharacteristic::readValue` or `NimBLERemoteCharacteristic::getValue`. The user application should use `NimBLERemoteCharacteristic::readValue` or `NimBLERemoteCharacteristic::getValue`.
To obatain a copy of the data, then cast the returned std::string to the type required such as: To obtain a copy of the data, then cast the returned std::string to the type required such as:
``` ```
std::string value = pChr->readValue(); std::string value = pChr->readValue();
uint8_t *data = (uint8_t*)value.data(); uint8_t *data = (uint8_t*)value.data();
@ -335,35 +357,51 @@ the currently known database returned (false : default).
Also now returns a pointer to `std::vector` instead of `std::map`. Also now returns a pointer to `std::vector` instead of `std::map`.
<br/> <br/>
<a name="client-callbacks"></a>
### Client callbacks
> `BLEClientCallbacks::onDisconnect` (`NimBLEClientCallbacks::onDisconnect`)
This now takes a second parameter `int reason` which provides the reason code for disconnection.
<br/>
<a name="client-security"></a> <a name="client-security"></a>
### Client Security ### Client Security
The client will automatically initiate security when the peripheral responds that it's required. The client will automatically initiate security when the peripheral responds that it's required.
The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below. The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below.
<br/> <br/>
<a name="scan-api"></a>
## BLE Scan
The scan API is mostly unchanged from the original except for `NimBLEScan::start`, in which the duration parameter is now in milliseconds instead of seconds.
<br/>
<a name="security-api"></a> <a name="security-api"></a>
## Security API ## Security API
Security operations have been moved to `BLEDevice` (`NimBLEDevice`). Security operations have been moved to `BLEDevice` (`NimBLEDevice`).
The security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes.
Also security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes.
However backward compatibility with the original `BLESecurity` (`NimBLESecurity`) class is retained to minimize application code changes.
The callback methods are: The callback methods are:
> `bool onConfirmPIN(uint32_t pin)` > `bool onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pin)`
Receives the pin when using numeric comparison authentication, `return true;` to accept. Receives the pin when using numeric comparison authentication.
Call `NimBLEDevice::injectConfirmPIN(connInfo, true);` to accept or `NimBLEDevice::injectConfirmPIN(connInfo, false);` to reject.
<br/> <br/>
> `uint32_t onPassKeyRequest()` > `void onPassKeyEntry(NimBLEConnInfo& connInfo)`
For server callback; return the passkey expected from the client. Client callback; client should respond with the passkey (pin) by calling `NimBLEDevice::injectPassKey(connInfo, 123456);`
For client callback; return the passkey to send to the server.
<br/> <br/>
> `void onAuthenticationComplete(ble_gap_conn_desc\* desc)` > `uint32_t onPassKeyDisplay()`
Authentication complete, success or failed information is in `desc`. Server callback; should return the passkey (pin) expected from the client.
<br/>
> `void onAuthenticationComplete(NimBLEConnInfo& connInfo)`
Authentication complete, success or failed information is available from the `NimBLEConnInfo` methods.
<br/> <br/>
Security settings and IO capabilities are now set by the following methods of NimBLEDevice. Security settings and IO capabilities are now set by the following methods of NimBLEDevice.
@ -391,11 +429,9 @@ Sets the keys we are willing to accept from the peer during pairing.
<a name="arduino-configuration"></a> <a name="arduino-configuration"></a>
## Arduino Configuration ## Arduino Configuration
Unlike the original library pre-packaged in the esp32-arduino, this library has all the configuration Unlike the original library pre-packaged in the esp32-arduino, this library has all the configuration options that are normally set in menuconfig available in the *src/nimconfig.h* file.
options that are normally set in menuconfig available in the *src/nimconfig.h* file.
This allows Arduino users to fully customize the build, such as increasing max connections This allows Arduino users to fully customize the build, such as increasing max connections or loading the BLE stack into external PSRAM.
or loading the BLE stack into external PSRAM.
For details on the options, they are fully commented in *nimconfig.h* For details on the options, they are fully commented in *nimconfig.h*
<br/> <br/>

View File

@ -23,7 +23,7 @@ This can be called any time you wish to use BLE functions and does not need to b
<a name="creating-a-server"></a> <a name="creating-a-server"></a>
## Creating a Server ## Creating a Server
BLE servers perform 2 tasks, they advertise their existance for clients to find them and they provide services which contain information for the connecting client. BLE servers perform 2 tasks, they advertise their existence for clients to find them and they provide services which contain information for the connecting client.
After initializing the NimBLE stack we create a server by calling `NimBLEDevice::createServer()`, this will create a server instance and return a pointer to it. After initializing the NimBLE stack we create a server by calling `NimBLEDevice::createServer()`, this will create a server instance and return a pointer to it.
@ -91,7 +91,7 @@ void app_main(void)
} }
``` ```
All that's left to do now is start the sevice, give the characteristic a value and start advertising for clients. All that's left to do now is start the service, give the characteristic a value and start advertising for clients.
Fist we start the service by calling `NimBLEService::start()`. Fist we start the service by calling `NimBLEService::start()`.
@ -146,7 +146,7 @@ After initializing the NimBLE stack we create a scan instance by calling `NimBLE
Once we have created the scan we can start looking for advertising servers. Once we have created the scan we can start looking for advertising servers.
To do this we call `NimBLEScan::start(duration)`, the duration parameter is a uint32_t that specifies the number of seconds to scan for, To do this we call `NimBLEScan::start(duration)`, the duration parameter is a uint32_t that specifies the number of milliseconds to scan for,
passing 0 will scan forever. passing 0 will scan forever.
In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available). In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available).
@ -162,7 +162,7 @@ void app_main(void)
NimBLEDevice::init(""); NimBLEDevice::init("");
NimBLEScan *pScan = NimBLEDevice::getScan(); NimBLEScan *pScan = NimBLEDevice::getScan();
NimBLEScanResults results = pScan->start(10); NimBLEScanResults results = pScan->getResults(10 * 1000);
} }
``` ```
<br/> <br/>
@ -214,7 +214,7 @@ for(int i = 0; i < results.getCount(); i++) {
} }
} }
``` ```
As shown, the call to `NimBLEClient::connect` should have it's eturn value tested to make sure it succeeded before proceeding to get data. As shown, the call to `NimBLEClient::connect` should have it's return value tested to make sure it succeeded before proceeding to get data.
<br/> <br/>
Next we need to access the servers data by asking it for the service and the characteristic we are interested in, then read the characteristic value. Next we need to access the servers data by asking it for the service and the characteristic we are interested in, then read the characteristic value.
@ -222,7 +222,7 @@ Next we need to access the servers data by asking it for the service and the cha
To do this we call `NimBLEClient::getService`, which takes as a parameter the UUID of the service and returns To do this we call `NimBLEClient::getService`, which takes as a parameter the UUID of the service and returns
a pointer an instance to `NimBLERemoteService` or `nullptr` if the service was not found. a pointer an instance to `NimBLERemoteService` or `nullptr` if the service was not found.
Next we will call `NimBLERemoteService::getCharateristic` which takes as a parameter the UUID of the service and returns Next we will call `NimBLERemoteService::getCharacteristic` which takes as a parameter the UUID of the service and returns
a pointer to an instance of `NimBLERemoteCharacteristic` or `nullptr` if not found. a pointer to an instance of `NimBLERemoteCharacteristic` or `nullptr` if not found.
Finally we will read the characteristic value with `NimBLERemoteCharacteristic::readValue()`. Finally we will read the characteristic value with `NimBLERemoteCharacteristic::readValue()`.
@ -302,7 +302,7 @@ void app_main(void)
NimBLEDevice::init(""); NimBLEDevice::init("");
NimBLEScan *pScan = NimBLEDevice::getScan(); NimBLEScan *pScan = NimBLEDevice::getScan();
NimBLEScanResults results = pScan->start(10); NimBLEScanResults results = pScan->start(10 * 1000);
NimBLEUUID serviceUuid("ABCD"); NimBLEUUID serviceUuid("ABCD");

View File

@ -2,7 +2,7 @@
## Put BLE functions in a task running on the NimBLE stack core ## Put BLE functions in a task running on the NimBLE stack core
When commands are sent to the stack from a differnt core they can experience delays in execution. When commands are sent to the stack from a different core they can experience delays in execution.
This library detects this and invokes the esp32 IPC to reroute these commands through the correct core but this also increases overhead. This library detects this and invokes the esp32 IPC to reroute these commands through the correct core but this also increases overhead.
Therefore it is highly recommended to create tasks for BLE to run on the same core, the macro `CONFIG_BT_NIMBLE_PINNED_TO_CORE` can be used to set the core. Therefore it is highly recommended to create tasks for BLE to run on the same core, the macro `CONFIG_BT_NIMBLE_PINNED_TO_CORE` can be used to set the core.
<br/> <br/>
@ -13,12 +13,12 @@ When a client instance has been created and has connected to a peer device and i
If you are periodically connecting to the same devices and you have deleted the client instance or the services when connecting again it will cause a retrieval of that information from the peer again. If you are periodically connecting to the same devices and you have deleted the client instance or the services when connecting again it will cause a retrieval of that information from the peer again.
This results in significant energy drain on the battery of the devices, fragments heap, and reduces connection performance. This results in significant energy drain on the battery of the devices, fragments heap, and reduces connection performance.
Client instances in this library use approximately 20% of the original bluedroid library, deleteing them will provide much less gain than it did before. Client instances in this library use approximately 20% of the original bluedroid library, deleting them will provide much less gain than it did before.
It is recommended to retain the client instance in cases where the time between connecting to the same device is less than 5 minutes. It is recommended to retain the client instance in cases where the time between connecting to the same device is less than 5 minutes.
<br/> <br/>
## Only retrieve the services and characteriscs needed ## Only retrieve the services and characteristics needed
As a client the use of `NimBLEClient::getServices` or `NimBLERemoteService::getCharacteristics` and using `true` for the parameter should be limited to devices that are not known. As a client the use of `NimBLEClient::getServices` or `NimBLERemoteService::getCharacteristics` and using `true` for the parameter should be limited to devices that are not known.
Instead `NimBLEClient::getService(NimBLEUUID)` or `NimBLERemoteService::getCharacteristic(NimBLEUUID)` should be used to access certain attributes that are useful to the application. Instead `NimBLEClient::getService(NimBLEUUID)` or `NimBLERemoteService::getCharacteristic(NimBLEUUID)` should be used to access certain attributes that are useful to the application.

View File

@ -13,18 +13,6 @@ NimBLE is a completely open source Bluetooth Low Energy stack produced by [Apach
It is more suited to resource constrained devices than bluedroid and has now been ported to the ESP32 by Espressif. It is more suited to resource constrained devices than bluedroid and has now been ported to the ESP32 by Espressif.
<br/> <br/>
# Arduino Installation
**Arduino Library manager:** Go to `sketch` -> `Include Library` -> `Manage Libraries` and search for NimBLE and install.
**Alternatively:** Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library.
`#include "NimBLEDevice.h"` at the beginning of your sketch.
Call `NimBLEDevice::init` in `setup`.
Tested and working with esp32-arduino in Arduino IDE and platform IO.
<br/>
# ESP-IDF Installation # ESP-IDF Installation
### v4.0+ ### v4.0+
Download as .zip and extract or clone into the components folder in your esp-idf project. Download as .zip and extract or clone into the components folder in your esp-idf project.
@ -57,21 +45,6 @@ Also see [Improvements and updates](Improvements_and_updates.md) for information
For more advanced usage see [Usage tips](Usage_tips.md) for more performance and optimization. For more advanced usage see [Usage tips](Usage_tips.md) for more performance and optimization.
<br/> <br/>
### Arduino specific
See the Refactored_original_examples in the examples folder for highlights of the differences with the original library.
More advanced examples highlighting many available features are in examples/NimBLE_Server, NimBLE_Client.
Beacon examples provided by [beegee-tokyo](https://github.com/beegee-tokyo) are in examples/BLE_Beacon_Scanner, BLE_EddystoneTLM_Beacon, BLE_EddystoneURL_Beacon.
Change the settings in the nimconfig.h file to customize NimBLE to your project, such as increasing max connections (default is 3).
<br/>
### Arduino command line and platformio
As an alternative to changing the configuration in nimconfig.h, Arduino command line and platformio.ini options are available.
See the command line configuration options available in [Command line config](Command_line_config.md).
<br/>
# Need help? Have a question or suggestion? # Need help? Have a question or suggestion?
Come chat on [gitter](https://gitter.im/NimBLE-Arduino/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link) or open an issue at [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino/issues) or [esp-nimble-cpp](https://github.com/h2zero/esp-nimble-cpp/issues) Come chat on [gitter](https://gitter.im/NimBLE-Arduino/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link) or open an issue at [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino/issues) or [esp-nimble-cpp](https://github.com/h2zero/esp-nimble-cpp/issues)
<br/> <br/>

View File

@ -1,5 +1,5 @@
/** NimBLE_Server Demo: /** NimBLE_Client Demo:
* *
* Demonstrates many of the available features of the NimBLE client library. * Demonstrates many of the available features of the NimBLE client library.
* *
@ -11,12 +11,10 @@
extern "C" {void app_main(void);} extern "C" {void app_main(void);}
void scanEndedCB(NimBLEScanResults results);
static NimBLEAdvertisedDevice* advDevice; static NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false; static bool doConnect = false;
static uint32_t scanTime = 0; /** 0 = scan forever */ static uint32_t scanTime = 0; /** scan time in milliseconds, 0 = scan forever */
/** None of these are required as they will be handled by the library with defaults. ** /** None of these are required as they will be handled by the library with defaults. **
@ -31,42 +29,44 @@ class ClientCallbacks : public NimBLEClientCallbacks {
* Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 45 * 10ms = 450ms timeout * Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 45 * 10ms = 450ms timeout
*/ */
pClient->updateConnParams(120,120,0,45); pClient->updateConnParams(120,120,0,45);
}; }
void onDisconnect(NimBLEClient* pClient) { void onDisconnect(NimBLEClient* pClient, int reason) {
printf("%s Disconnected - Starting scan\n", pClient->getPeerAddress().toString().c_str()); printf("%s Disconnected, reason = %d - Starting scan\n",
NimBLEDevice::getScan()->start(scanTime, scanEndedCB); pClient->getPeerAddress().toString().c_str(), reason);
}; NimBLEDevice::getScan()->start(scanTime);
}
/********************* Security handled here ********************** /********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/ ****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){ void onPassKeyEntry(NimBLEConnInfo& connInfo){
printf("Client Passkey Request\n"); printf("Server Passkey Entry\n");
/** return the passkey to send to the server */ /** This should prompt the user to enter the passkey displayed
return 123456; * on the peer device.
*/
NimBLEDevice::injectPassKey(connInfo, 123456);
}; };
bool onConfirmPIN(uint32_t pass_key){ void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %d\n", pass_key); printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Return false if passkeys don't match. */ /** Inject false if passkeys don't match. */
return true; NimBLEDevice::injectConfirmPasskey(connInfo, true);
}; };
/** Pairing process complete, we can check the results in ble_gap_conn_desc */ /** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(ble_gap_conn_desc* desc){ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
if(!desc->sec_state.encrypted) { if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n"); printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */ /** Find the client with the connection handle provided in desc */
NimBLEDevice::getClientByID(desc->conn_handle)->disconnect(); NimBLEDevice::getClientByHandle(connInfo.getConnHandle())->disconnect();
return; return;
} }
}; }
}; };
/** Define a class to handle the callbacks when advertisments are received */ /** Define a class to handle the callbacks when advertisments are received */
class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { class scanCallbacks: public NimBLEScanCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) { void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str()); printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD")))
@ -79,7 +79,12 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
/** Ready to connect now */ /** Ready to connect now */
doConnect = true; doConnect = true;
} }
}; }
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(NimBLEScanResults results) {
printf("Scan Ended\n");
}
}; };
@ -87,18 +92,13 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
std::string str = (isNotify == true) ? "Notification" : "Indication"; std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from "; str += " from ";
str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString(); str += pRemoteCharacteristic->getClient()->getPeerAddress().toString();
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString(); str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString(); str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
str += ", Value = " + std::string((char*)pData, length); str += ", Value = " + std::string((char*)pData, length);
printf("%s\n", str.c_str()); printf("%s\n", str.c_str());
} }
/** Callback to process the results of the last scan or restart it */
void scanEndedCB(NimBLEScanResults results){
printf("Scan Ended\n");
}
/** Create a single global instance of the callback class to be used by all clients */ /** Create a single global instance of the callback class to be used by all clients */
static ClientCallbacks clientCB; static ClientCallbacks clientCB;
@ -109,7 +109,7 @@ bool connectToServer() {
NimBLEClient* pClient = nullptr; NimBLEClient* pClient = nullptr;
/** Check if we have a client we should reuse first **/ /** Check if we have a client we should reuse first **/
if(NimBLEDevice::getClientListSize()) { if(NimBLEDevice::getCreatedClientCount()) {
/** Special case when we already know this device, we send false as the /** Special case when we already know this device, we send false as the
* second argument in connect() to prevent refreshing the service database. * second argument in connect() to prevent refreshing the service database.
* This saves considerable time and power. * This saves considerable time and power.
@ -132,7 +132,7 @@ bool connectToServer() {
/** No client to reuse? Create a new one. */ /** No client to reuse? Create a new one. */
if(!pClient) { if(!pClient) {
if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) { if(NimBLEDevice::getCreatedClientCount() >= NIMBLE_MAX_CONNECTIONS) {
printf("Max clients reached - no more connections available\n"); printf("Max clients reached - no more connections available\n");
return false; return false;
} }
@ -148,8 +148,8 @@ bool connectToServer() {
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout
*/ */
pClient->setConnectionParams(6,6,0,15); pClient->setConnectionParams(6,6,0,15);
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */ /** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
pClient->setConnectTimeout(5); pClient->setConnectTimeout(5 * 1000);
if (!pClient->connect(advDevice)) { if (!pClient->connect(advDevice)) {
@ -205,9 +205,9 @@ bool connectToServer() {
} }
} }
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe(). /** registerForNotify() has been removed and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=false. * Unsubscribe parameter defaults are: response=true.
*/ */
if(pChr->canNotify()) { if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) { //if(!pChr->registerForNotify(notifyCB)) {
@ -269,8 +269,8 @@ bool connectToServer() {
} }
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe(). /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=false. * Unsubscribe parameter defaults are: response=true.
*/ */
if(pChr->canNotify()) { if(pChr->canNotify()) {
//if(!pChr->registerForNotify(notifyCB)) { //if(!pChr->registerForNotify(notifyCB)) {
@ -311,7 +311,7 @@ void connectTask (void * parameter){
printf("Failed to connect, starting scan\n"); printf("Failed to connect, starting scan\n");
} }
NimBLEDevice::getScan()->start(scanTime,scanEndedCB); NimBLEDevice::getScan()->start(scanTime);
} }
vTaskDelay(10/portTICK_PERIOD_MS); vTaskDelay(10/portTICK_PERIOD_MS);
} }
@ -350,7 +350,7 @@ void app_main (void){
NimBLEScan* pScan = NimBLEDevice::getScan(); NimBLEScan* pScan = NimBLEDevice::getScan();
/** create a callback that gets called when advertisers are found */ /** create a callback that gets called when advertisers are found */
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); pScan->setScanCallbacks (new scanCallbacks());
/** Set scan interval (how often) and window (how long) in milliseconds */ /** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(400); pScan->setInterval(400);
@ -360,10 +360,10 @@ void app_main (void){
* but will use more energy from both devices * but will use more energy from both devices
*/ */
pScan->setActiveScan(true); pScan->setActiveScan(true);
/** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever /** Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops. * Optional callback for when scanning stops.
*/ */
pScan->start(scanTime, scanEndedCB); pScan->start(scanTime);
printf("Scanning for peripherals\n"); printf("Scanning for peripherals\n");

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -19,15 +19,9 @@ static NimBLEServer* pServer;
/** None of these are required as they will be handled by the library with defaults. ** /** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */ ** Remove as you see fit for your needs */
class ServerCallbacks: public NimBLEServerCallbacks { class ServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer) { void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
printf("Client connected\n"); printf("Client address: %s\n", connInfo.getAddress().toString().c_str());
NimBLEDevice::startAdvertising();
};
/** Alternative onConnect() method to extract details of the connection.
* See: src/ble_gap.h for the details of the ble_gap_conn_desc struct.
*/
void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
printf("Client address: %s\n", NimBLEAddress(desc->peer_ota_addr).toString().c_str());
/** We can use the connection handle here to ask for different connection parameters. /** We can use the connection handle here to ask for different connection parameters.
* Args: connection handle, min connection interval, max connection interval * Args: connection handle, min connection interval, max connection interval
* latency, supervision timeout. * latency, supervision timeout.
@ -35,34 +29,39 @@ class ServerCallbacks: public NimBLEServerCallbacks {
* Latency: number of intervals allowed to skip. * Latency: number of intervals allowed to skip.
* Timeout: 10 millisecond increments, try for 3x interval time for best results. * Timeout: 10 millisecond increments, try for 3x interval time for best results.
*/ */
pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 18); pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 18);
}; };
void onDisconnect(NimBLEServer* pServer) {
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
printf("Client disconnected - start advertising\n"); printf("Client disconnected - start advertising\n");
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
}; };
void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) {
printf("MTU updated: %u for connection ID: %u\n", MTU, connInfo.getConnHandle());
pServer->updateConnParams(connInfo.getConnHandle(), 24, 48, 0, 60);
};
/********************* Security handled here ********************** /********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/ ****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){ uint32_t onPassKeyDisplay(){
printf("Server Passkey Request\n"); printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security /** This should return a random 6 digit number for security
* or make your own static passkey as done here. * or make your own static passkey as done here.
*/ */
return 123456; return 123456;
}; };
bool onConfirmPIN(uint32_t pass_key){ void onConfirmasskeyN(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %d\n", pass_key); printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Return false if passkeys don't match. */ /** Inject false if passkeys don't match. */
return true; NimBLEDevice::injectConfirmPasskey(connInfo, true);
}; };
void onAuthenticationComplete(ble_gap_conn_desc* desc){ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
/** Check that encryption was successful, if not we disconnect the client */ /** Check that encryption was successful, if not we disconnect the client */
if(!desc->sec_state.encrypted) { if(!connInfo.isEncrypted()) {
/** NOTE: createServer returns the current server reference unless one is not already created */ NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
NimBLEDevice::createServer()->disconnect(desc->conn_handle);
printf("Encrypt connection failed - disconnecting client\n"); printf("Encrypt connection failed - disconnecting client\n");
return; return;
} }
@ -72,44 +71,61 @@ class ServerCallbacks: public NimBLEServerCallbacks {
/** Handler class for characteristic actions */ /** Handler class for characteristic actions */
class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
void onRead(NimBLECharacteristic* pCharacteristic){ void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
printf("%s : onRead(), value: %s\n", printf("%s : onRead(), value: %s\n",
pCharacteristic->getUUID().toString().c_str(), pCharacteristic->getUUID().toString().c_str(),
pCharacteristic->getValue().c_str()); pCharacteristic->getValue().c_str());
}; }
void onWrite(NimBLECharacteristic* pCharacteristic) { void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
printf("%s : onWrite(), value: %s\n", printf("%s : onWrite(), value: %s\n",
pCharacteristic->getUUID().toString().c_str(), pCharacteristic->getUUID().toString().c_str(),
pCharacteristic->getValue().c_str()); pCharacteristic->getValue().c_str());
}; }
/** Called before notification or indication is sent, /** Called before notification or indication is sent,
* the value can be changed here before sending if desired. * the value can be changed here before sending if desired.
*/ */
void onNotify(NimBLECharacteristic* pCharacteristic) { void onNotify(NimBLECharacteristic* pCharacteristic) {
printf("Sending notification to clients\n"); printf("Sending notification to clients\n");
}; }
/**
/** The status returned in status is defined in NimBLECharacteristic.h.
* The value returned in code is the NimBLE host return code. * The value returned in code is the NimBLE host return code.
*/ */
void onStatus(NimBLECharacteristic* pCharacteristic, Status status, int code) { void onStatus(NimBLECharacteristic* pCharacteristic, int code) {
printf("Notification/Indication status code: %d , return code: %d, %s\n", printf("Notification/Indication return code: %d, %s\n",
status, code, NimBLEUtils::returnCodeToString(code));
code, }
NimBLEUtils::returnCodeToString(code));
}; void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue) {
std::string str = "Client ID: ";
str += connInfo.getConnHandle();
str += " Address: ";
str += connInfo.getAddress().toString();
if(subValue == 0) {
str += " Unsubscribed to ";
}else if(subValue == 1) {
str += " Subscribed to notfications for ";
} else if(subValue == 2) {
str += " Subscribed to indications for ";
} else if(subValue == 3) {
str += " Subscribed to notifications and indications for ";
}
str += std::string(pCharacteristic->getUUID());
printf("%s\n", str.c_str());
}
}; };
/** Handler class for descriptor actions */ /** Handler class for descriptor actions */
class DescriptorCallbacks : public NimBLEDescriptorCallbacks { class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
void onWrite(NimBLEDescriptor* pDescriptor) { void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength()); std::string dscVal = pDescriptor->getValue();
printf("Descriptor witten value: %s\n", dscVal.c_str()); printf("Descriptor witten value: %s\n", dscVal.c_str());
}; };
void onRead(NimBLEDescriptor* pDescriptor) { void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
printf("%s Descriptor read\n", pDescriptor->getUUID().toString().c_str()); printf("%s Descriptor read\n", pDescriptor->getUUID().toString().c_str());
};; };;
}; };
@ -126,7 +142,7 @@ void notifyTask(void * parameter){
if(pSvc) { if(pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D"); NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D");
if(pChr) { if(pChr) {
pChr->notify(true); pChr->notify();
} }
} }
} }

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_client)

View File

@ -0,0 +1,3 @@
PROJECT_NAME := NimBLE_extended_client
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,163 @@
/** NimBLE Extended Client Demo:
*
* Demonstrates the Bluetooth 5.x client capabilities.
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
static NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever
/* Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/
static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */ ;
/* Define a class to handle the callbacks for client connection events */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
printf("Connected\n");
};
void onDisconnect(NimBLEClient* pClient, int reason) {
printf("%s Disconnected, reason = %d - Starting scan\n",
pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTime);
};
};
/* Define a class to handle the callbacks when advertisements are received */
class scanCallbacks: public NimBLEScanCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if(advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD")))
{
printf("Found Our Service\n");
/* Ready to connect now */
doConnect = true;
/* Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/* stop scan before connecting */
NimBLEDevice::getScan()->stop();
}
}
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(NimBLEScanResults results) {
printf("Scan Ended\n");
}
};
/* Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(new ClientCallbacks, false);
/* Set the PHY's to use for this connection. This is a bitmask that represents the PHY's:
* * 0x01 BLE_GAP_LE_PHY_1M_MASK
* * 0x02 BLE_GAP_LE_PHY_2M_MASK
* * 0x04 BLE_GAP_LE_PHY_CODED_MASK
* Combine these with OR ("|"), eg BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK;
*/
pClient->setConnectPhy(connectPhys);
/** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
pClient->setConnectTimeout(10 * 1000);
if (!pClient->connect(advDevice)) {
/* Created a client but failed to connect, don't need to keep it as it has no data */
NimBLEDevice::deleteClient(pClient);
printf("Failed to connect, deleted client\n");
return false;
}
printf("Connected to: %s RSSI: %d\n",
pClient->getPeerAddress().toString().c_str(),
pClient->getRssi());
/* Now we can read/write/subscribe the charateristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
pSvc = pClient->getService(SERVICE_UUID);
if (pSvc) {
pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
if (pChr) {
// Read the value of the characteristic.
if (pChr->canRead()) {
std::string value = pChr->readValue();
printf("Characteristic value: %s\n", value.c_str());
}
}
} else {
printf("ABCD service not found.\n");
}
NimBLEDevice::deleteClient(pClient);
printf("Done with this device!\n");
return true;
}
void connectTask (void * parameter){
/* Loop here until we find a device we want to connect to */
for (;;) {
if (doConnect) {
/* Found a device we want to connect to, do it now */
if (connectToServer()) {
printf("Success!, scanning for more!\n");
} else {
printf("Failed to connect, starting scan\n");
}
doConnect = false;
NimBLEDevice::getScan()->start(scanTime);
}
vTaskDelay(pdMS_TO_TICKS(10));
}
vTaskDelete(NULL);
}
void app_main (void) {
printf("Starting NimBLE Client\n");
/* Create a task to handle connecting to peers */
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
/* Initialize NimBLE, no device name specified as we are not advertising */
NimBLEDevice::init("");
NimBLEScan* pScan = NimBLEDevice::getScan();
/* create a callback that gets called when advertisers are found */
pScan->setScanCallbacks(new scanCallbacks());
/* Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(97);
pScan->setWindow(67);
/* Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/* Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);
printf("Scanning for peripherals\n");
}

View File

@ -0,0 +1,13 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_EXT_ADV=y

View File

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_extended_server)

View File

@ -0,0 +1,3 @@
PROJECT_NAME := NimBLE_extended_server
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,139 @@
/** NimBLE Extended Server Demo:
*
* Demonstrates the Bluetooth 5.x extended advertising capabilities.
*
* This demo will advertise a long data string on the CODED and 1M Phy's and
* starts a server allowing connection over either PHY's. It will advertise for
* 5 seconds then sleep for 20 seconds, if a client connects it will sleep once
* it has disconnected then repeats.
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "esp_sleep.h"
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */
static uint32_t advTime = 5000;
/* Time to sleep between advertisements */
static uint32_t sleepSeconds = 20;
/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
/* Secondary PHY used for advertising and connecting,
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
*/
static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
/* Handler class for server events */
class ServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
printf("Client connected:: %s\n", connInfo.getAddress().toString().c_str());
};
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
printf("Client disconnected - sleeping for %" PRIu32" seconds\n", sleepSeconds);
esp_deep_sleep_start();
};
};
/* Callback class to handle advertising events */
class advertisingCallbacks: public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
/* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id);
switch (reason) {
case 0:
printf("Client connecting\n");
return;
case BLE_HS_ETIMEOUT:
printf("Time expired - sleeping for %" PRIu32" seconds\n", sleepSeconds);
break;
default:
break;
}
esp_deep_sleep_start();
}
};
void app_main (void) {
NimBLEDevice::init("Extended advertiser");
/* Create the server and add the services/characteristics/descriptors */
NimBLEServer *pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks);
NimBLEService *pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World");
/* Start the services */
pService->start();
/*
* Create an extended advertisement with the instance ID 0 and set the PHY's.
* Multiple instances can be added as long as the instance ID is incremented.
*/
NimBLEExtAdvertisement extAdv(primaryPhy, secondaryPhy);
/* Set the advertisement as connectable */
extAdv.setConnectable(true);
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extAdv.setScannable(false); // The default is false, set here for demonstration.
/* Extended advertising allows for 251 bytes (minus header bytes ~20) in a single advertisement or up to 1650 if chained */
extAdv.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Extended Advertising Demo.\r\n"
"Extended advertising allows for "
"251 bytes of data in a single advertisement,\r\n"
"or up to 1650 bytes with chaining.\r\n"
"This example message is 226 bytes long "
"and is using CODED_PHY for long range."));
extAdv.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
/* When extended advertising is enabled `NimBLEDevice::getAdvertising` returns a pointer to `NimBLEExtAdvertising */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks for advertising events */
pAdvertising->setCallbacks(new advertisingCallbacks);
/*
* NimBLEExtAdvertising::setInstanceData takes the instance ID and
* a reference to a `NimBLEExtAdvertisement` object. This sets the data
* that will be advertised for this instance ID, returns true if successful.
*
* Note: It is safe to create the advertisement as a local variable if setInstanceData
* is called before exiting the code block as the data will be copied.
*/
if (pAdvertising->setInstanceData(0, extAdv)) {
/*
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
* and a duration in milliseconds or a max number of advertisements to send (or both).
*/
if (pAdvertising->start(0, advTime)) {
printf("Started advertising\n");
} else {
printf("Failed to start advertising\n");
}
} else {
printf("Failed to register advertisment data\n");
}
esp_sleep_enable_timer_wakeup(sleepSeconds * 1000000);
}

View File

@ -0,0 +1,13 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_EXT_ADV=y

View File

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32c3 esp32s3)
project(NimBLE_multi_advertiser)

View File

@ -0,0 +1,3 @@
PROJECT_NAME := NimBLE_multi_advertiser
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,170 @@
/** NimBLE Multi Advertiser Demo:
*
* Demonstrates the Bluetooth 5.x extended advertising capabilities.
*
* This demo will advertise 2 advertisements, and extended scannable instance
* and a connectable legacy instance. They will advertise for 5 seconds then
* sleep for 20 seconds. The extended scannable instance will use the scan
* request callback to update it's data when a scan response is requested.
*
* Created: on April 9 2022
* Author: H2zero
*
*/
#include "NimBLEDevice.h"
#include "esp_sleep.h"
extern "C" void app_main(void);
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
/* Time in milliseconds to advertise */
static uint32_t advTime = 5000;
/* Time to sleep between advertisements */
static uint32_t sleepTime = 20;
/* Primary PHY used for advertising, can be one of BLE_HCI_LE_PHY_1M or BLE_HCI_LE_PHY_CODED */
static uint8_t primaryPhy = BLE_HCI_LE_PHY_CODED;
/* Secondary PHY used for advertising and connecting,
* can be one of BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_2M or BLE_HCI_LE_PHY_CODED
*/
static uint8_t secondaryPhy = BLE_HCI_LE_PHY_1M;
/* Handler class for server events */
class ServerCallbacks: public NimBLEServerCallbacks {
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
printf("Client connected: %s\n", connInfo.getAddress().toString().c_str());
};
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
printf("Client disconnected\n");
// if still advertising we won't sleep yet.
if (!pServer->getAdvertising()->isAdvertising()) {
printf("Sleeping for %" PRIu32" seconds\n", sleepTime);
esp_deep_sleep_start();
}
};
};
/* Callback class to handle advertising events */
class advCallbacks: public NimBLEExtAdvertisingCallbacks {
void onStopped(NimBLEExtAdvertising* pAdv, int reason, uint8_t inst_id) {
/* Check the reason advertising stopped, don't sleep if client is connecting */
printf("Advertising instance %u stopped\n", inst_id);
switch (reason) {
case 0:
printf(" client connecting\n");
return;
case BLE_HS_ETIMEOUT:
printf("Time expired - sleeping for %" PRIu32" seconds\n", sleepTime);
break;
default:
break;
}
esp_deep_sleep_start();
}
bool m_updatedSR = false;
void onScanRequest(NimBLEExtAdvertising* pAdv, uint8_t inst_id, NimBLEAddress addr) {
printf("Scan request for instance %u\n", inst_id);
// if the data has already been updated we don't need to change it again.
if (!m_updatedSR) {
printf("Updating scan data\n");
NimBLEExtAdvertisement sr;
sr.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Hello from scan response!"));
pAdv->setScanResponseData(inst_id, sr);
m_updatedSR = true;
}
}
};
void app_main (void) {
NimBLEDevice::init("Multi advertiser");
/* Create a server for our legacy advertiser */
NimBLEServer *pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks);
NimBLEService *pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE |
NIMBLE_PROPERTY::NOTIFY);
pCharacteristic->setValue("Hello World");
/* Start the service */
pService->start();
/* Create our multi advertising instances */
// extended scannable instance advertising on coded and 1m PHY's.
NimBLEExtAdvertisement extScannable(primaryPhy, secondaryPhy);
// Legacy advertising as a connectable device.
NimBLEExtAdvertisement legacyConnectable;
// Optional scan response data.
NimBLEExtAdvertisement legacyScanResponse;
/* As per Bluetooth specification, extended advertising cannot be both scannable and connectable */
extScannable.setScannable(true);
extScannable.setConnectable(false);
/* Set the initial data */
extScannable.setServiceData(NimBLEUUID(SERVICE_UUID), std::string("Scan me!"));
/* enable the scan response callback, we will use this to update the data. */
extScannable.enableScanRequestCallback(true);
/* Optional custom address for this advertisment. */
legacyConnectable.setAddress(NimBLEAddress("DE:AD:BE:EF:BA:AD"));
/* Set the advertising data. */
legacyConnectable.setName("Legacy");
legacyConnectable.setCompleteServices16({NimBLEUUID(SERVICE_UUID)});
/* Set the legacy and connectable flags. */
legacyConnectable.setLegacyAdvertising(true);
legacyConnectable.setConnectable(true);
/* Put some data in the scan response if desired. */
legacyScanResponse.setServiceData(NimBLEUUID(SERVICE_UUID), "Legacy SR");
/* Get the advertising ready */
NimBLEExtAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
/* Set the callbacks to handle advertising events */
pAdvertising->setCallbacks(new advCallbacks);
/* Set instance data.
* Up to 5 instances can be used if configured in menuconfig, instance 0 is always available.
*
* We will set the extended scannable data on instance 0 and the legacy data on instance 1.
* Note that the legacy scan response data needs to be set to the same instance (1).
*/
if (pAdvertising->setInstanceData( 0, extScannable ) &&
pAdvertising->setInstanceData( 1, legacyConnectable ) &&
pAdvertising->setScanResponseData( 1, legacyScanResponse )) {
/*
* `NimBLEExtAdvertising::start` takes the advertisement instance ID to start
* and a duration in milliseconds or a max number of advertisements to send (or both).
*/
if (pAdvertising->start(0, advTime) && pAdvertising->start(1, advTime)) {
printf("Started advertising\n");
} else {
printf("Failed to start advertising\n");
}
} else {
printf("Failed to register advertisment data\n");
}
esp_sleep_enable_timer_wakeup(sleepTime * 1000000);
}

View File

@ -0,0 +1,13 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_EXT_ADV=y

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(NimBLE_Async_Client)

View File

@ -0,0 +1,3 @@
PROJECT_NAME := NimBLE_Async_Client
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,83 @@
/**
* NimBLE_Async_client Demo:
*
* Demonstrates asynchronous client operations.
*
* Created: on November 4, 2024
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
static constexpr uint32_t scanTimeMs = 5 * 1000;
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
printf("Connected to: %s\n", pClient->getPeerAddress().toString().c_str());
}
void onDisconnect(NimBLEClient* pClient, int reason) {
printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTimeMs);
}
} clientCB;
class scanCallbacks : public NimBLEScanCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if (advertisedDevice->haveName() && advertisedDevice->getName() == "NimBLE-Server") {
printf("Found Our Device\n");
auto pClient = NimBLEDevice::getDisconnectedClient();
if (!pClient) {
pClient = NimBLEDevice::createClient(advertisedDevice->getAddress());
if (!pClient) {
printf("Failed to create client\n");
return;
}
}
pClient->setClientCallbacks(&clientCB, false);
if (!pClient->connect(true, true, false)) { // delete attributes, async connect, no MTU exchange
NimBLEDevice::deleteClient(pClient);
printf("Failed to connect\n");
return;
}
}
}
void onScanEnd(NimBLEScanResults results) {
printf("Scan Ended\n");
NimBLEDevice::getScan()->start(scanTimeMs);
}
};
extern "C" void app_main(void) {
printf("Starting NimBLE Async Client\n");
NimBLEDevice::init("");
NimBLEDevice::setPower(3); /** +3db */
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setScanCallbacks(new scanCallbacks());
pScan->setInterval(45);
pScan->setWindow(15);
pScan->setActiveScan(true);
pScan->start(scanTimeMs);
for (;;) {
vTaskDelay(pdMS_TO_TICKS(1000));
auto pClients = NimBLEDevice::getConnectedClients();
if (!pClients.size()) {
continue;
}
for (auto& pClient : pClients) {
printf("%s\n", pClient->toString().c_str());
NimBLEDevice::deleteClient(pClient);
}
NimBLEDevice::getScan()->start(scanTimeMs);
}
}

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(NimBLE_server_get_client_name)

View File

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "main.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View File

@ -0,0 +1,83 @@
/** NimBLE_server_get_client_name
*
* Demonstrates 2 ways for the server to read the device name from the connected client.
*
* Created: on June 24 2024
* Author: H2zero
*
*/
#include <NimBLEDevice.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define ENC_CHARACTERISTIC_UUID "9551f35b-8d91-42e4-8f7e-1358dfe272dc"
NimBLEServer* pServer;
class ServerCallbacks : public NimBLEServerCallbacks {
// Same as before but now includes the name parameter
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, std::string& name) override {
printf("Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
// Same as before but now includes the name parameter
void onAuthenticationComplete(NimBLEConnInfo& connInfo, const std::string& name) override {
if (!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
}
printf("Encrypted Client address: %s Name: %s\n", connInfo.getAddress().toString().c_str(), name.c_str());
}
};
extern "C" void app_main(void) {
printf("Starting BLE Server!\n");
NimBLEDevice::init("Connect to me!");
NimBLEDevice::setSecurityAuth(true, false, true); // Enable bonding to see full name on phones.
pServer = NimBLEDevice::createServer();
NimBLEService* pService = pServer->createService(SERVICE_UUID);
NimBLECharacteristic* pCharacteristic =
pService->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE);
pCharacteristic->setValue("Hello World says NimBLE!");
NimBLECharacteristic* pEncCharacteristic = pService->createCharacteristic(
ENC_CHARACTERISTIC_UUID,
(NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC));
pEncCharacteristic->setValue("Hello World says NimBLE Encrypted");
pService->start();
pServer->setCallbacks(new ServerCallbacks());
pServer->getPeerNameOnConnect(true); // Setting this will enable the onConnect callback that provides the name.
BLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->start();
printf("Advertising started, connect with your phone.\n");
while (true) {
auto clientCount = pServer->getConnectedCount();
if (clientCount) {
printf("Connected clients:\n");
for (auto i = 0; i < clientCount; ++i) {
NimBLEConnInfo peerInfo = pServer->getPeerInfo(i);
printf("Client address: %s Name: %s\n", peerInfo.getAddress().toString().c_str(),
// This function blocks until the name is retrieved, so cannot be used in callback functions.
pServer->getPeerName(peerInfo).c_str());
}
}
vTaskDelay(pdMS_TO_TICKS(10000));
}
}

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -43,23 +43,36 @@ class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) { void onConnect(BLEClient* pclient) {
} }
void onDisconnect(BLEClient* pclient) { /** onDisconnect now takes a reason parameter to indicate the reason for disconnection
void onDisconnect(BLEClient* pclient) { */
void onDisconnect(BLEClient* pclient, int reason) {
connected = false; connected = false;
printf("onDisconnect"); printf("onDisconnect");
} }
/***************** New - Security handled here ******************** /***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/ ****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){ void onPassKeyEntry(NimBLEConnInfo& connInfo){
printf("Client PassKeyRequest\n"); printf("Server Passkey Entry\n");
return 123456; /** This should prompt the user to enter the passkey displayed
} * on the peer device.
bool onConfirmPIN(uint32_t pass_key){ */
printf("The passkey YES/NO number: %d\n", pass_key); NimBLEDevice::injectPassKey(connInfo, 123456);
return true; };
}
void onAuthenticationComplete(ble_gap_conn_desc desc){ void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("Starting BLE work!\n"); printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
/** Inject false if passkeys don't match. */
NimBLEDevice::injectConfirmPasskey(connInfo, true);
};
/** Pairing process complete, we can check the results in connInfo */
void onAuthenticationComplete(NimBLEConnInfo& connInfo){
if(!connInfo.isEncrypted()) {
printf("Encrypt connection failed - disconnecting\n");
/** Find the client with the connection handle provided in desc */
NimBLEDevice::getClientByHandle(connInfo.getConnHandle())->disconnect();
return;
}
} }
/*******************************************************************/ /*******************************************************************/
}; };
@ -100,9 +113,10 @@ bool connectToServer() {
std::string value = pRemoteCharacteristic->readValue(); std::string value = pRemoteCharacteristic->readValue();
printf("The characteristic value was: %s\n", value.c_str()); printf("The characteristic value was: %s\n", value.c_str());
} }
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false. /** registerForNotify() has been removed and replaced with subscribe() / unsubscribe().
* Unsubscribe parameter defaults are: response=false. * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=true.
* Unsubscribe parameter defaults are: response=true.
*/ */
if(pRemoteCharacteristic->canNotify()) { if(pRemoteCharacteristic->canNotify()) {
//pRemoteCharacteristic->registerForNotify(notifyCallback); //pRemoteCharacteristic->registerForNotify(notifyCallback);
@ -171,7 +185,7 @@ void connectTask (void * parameter){
/*** Note: write value now returns true if successful, false otherwise - try again or disconnect ***/ /*** Note: write value now returns true if successful, false otherwise - try again or disconnect ***/
pRemoteCharacteristic->writeValue((uint8_t*)buf, strlen(buf), false); pRemoteCharacteristic->writeValue((uint8_t*)buf, strlen(buf), false);
}else if(doScan){ }else if(doScan){
BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it
} }
vTaskDelay(1000/portTICK_PERIOD_MS); // Delay a second between loops. vTaskDelay(1000/portTICK_PERIOD_MS); // Delay a second between loops.
@ -189,12 +203,12 @@ void app_main(void) {
// have detected a new device. Specify that we want active scanning and start the // have detected a new device. Specify that we want active scanning and start the
// scan to run for 5 seconds. // scan to run for 5 seconds.
BLEScan* pBLEScan = BLEDevice::getScan(); BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setScanCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349); pBLEScan->setInterval(1349);
pBLEScan->setWindow(449); pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true); pBLEScan->setActiveScan(true);
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL); xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
pBLEScan->start(5, false); pBLEScan->start(5 * 1000, false);
} // End of setup. } // End of setup.

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -48,28 +48,38 @@ uint32_t value = 0;
/** None of these are required as they will be handled by the library with defaults. ** /** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */ ** Remove as you see fit for your needs */
class MyServerCallbacks: public BLEServerCallbacks { class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) { void onConnect(BLEServer* pServer, BLEConnInfo& connInfo) {
deviceConnected = true; deviceConnected = true;
}; };
void onDisconnect(BLEServer* pServer) { void onDisconnect(BLEServer* pServer, BLEConnInfo& connInfo, int reason) {
deviceConnected = false; deviceConnected = false;
} }
/***************** New - Security handled here ******************** /***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/ ****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){ uint32_t onPassKeyDisplay(){
printf("Server PassKeyRequest\n"); printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456; return 123456;
} };
bool onConfirmPIN(uint32_t pass_key){ void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %d\n", pass_key); printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
return true; /** Inject false if passkeys don't match. */
} NimBLEDevice::injectConfirmPasskey(connInfo, true);
};
void onAuthenticationComplete(ble_gap_conn_desc desc){ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
printf("Starting BLE work!\n"); /** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
} }
printf("Starting BLE work!");
};
/*******************************************************************/ /*******************************************************************/
}; };
@ -128,7 +138,6 @@ void app_main(void) {
NIMBLE_PROPERTY::INDICATE NIMBLE_PROPERTY::INDICATE
); );
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor // Create a BLE Descriptor
/*************************************************** /***************************************************
NOTE: DO NOT create a 2902 descriptor. NOTE: DO NOT create a 2902 descriptor.

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -17,7 +17,7 @@
extern "C"{void app_main(void);} extern "C"{void app_main(void);}
int scanTime = 5; //In seconds int scanTime = 5 * 1000; // In milliseconds, 0 = scan forever
BLEScan* pBLEScan; BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
@ -29,7 +29,7 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void scanTask (void * parameter){ void scanTask (void * parameter){
for(;;) { for(;;) {
// put your main code here, to run repeatedly: // put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false); BLEScanResults foundDevices = pBLEScan->getResults(scanTime, false);
printf("Devices found: %d\n", foundDevices.getCount()); printf("Devices found: %d\n", foundDevices.getCount());
printf("Scan done!\n"); printf("Scan done!\n");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
@ -44,7 +44,7 @@ void app_main(void) {
BLEDevice::init(""); BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setScanCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100); pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value pBLEScan->setWindow(99); // less or equal setInterval value

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -50,33 +50,43 @@ uint8_t txValue = 0;
/** None of these are required as they will be handled by the library with defaults. ** /** None of these are required as they will be handled by the library with defaults. **
** Remove as you see fit for your needs */ ** Remove as you see fit for your needs */
class MyServerCallbacks: public BLEServerCallbacks { class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) { void onConnect(BLEServer* pServer, BLEConnInfo& connInfo) {
deviceConnected = true; deviceConnected = true;
}; };
void onDisconnect(BLEServer* pServer) { void onDisconnect(BLEServer* pServer, BLEConnInfo& connInfo, int reason) {
deviceConnected = false; deviceConnected = false;
} }
/***************** New - Security handled here ******************** /***************** New - Security handled here ********************
****** Note: these are the same return values as defaults ********/ ****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){ uint32_t onPassKeyDisplay(){
printf("Server PassKeyRequest\n"); printf("Server Passkey Display\n");
/** This should return a random 6 digit number for security
* or make your own static passkey as done here.
*/
return 123456; return 123456;
} };
bool onConfirmPIN(uint32_t pass_key){ void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pass_key){
printf("The passkey YES/NO number: %d\n", pass_key); printf("The passkey YES/NO number: %" PRIu32 "\n", pass_key);
return true; /** Inject false if passkeys don't match. */
} NimBLEDevice::injectConfirmPasskey(connInfo, true);
};
void onAuthenticationComplete(ble_gap_conn_desc desc){ void onAuthenticationComplete(NimBLEConnInfo& connInfo){
printf("Starting BLE work!\n"); /** Check that encryption was successful, if not we disconnect the client */
if(!connInfo.isEncrypted()) {
NimBLEDevice::getServer()->disconnect(connInfo.getConnHandle());
printf("Encrypt connection failed - disconnecting client\n");
return;
} }
printf("Starting BLE work!");
};
/*******************************************************************/ /*******************************************************************/
}; };
class MyCallbacks: public BLECharacteristicCallbacks { class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) { void onWrite(BLECharacteristic *pCharacteristic, BLEConnInfo& connInfo) {
std::string rxValue = pCharacteristic->getValue(); std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) { if (rxValue.length() > 0) {

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

16
idf_component.yml Normal file
View File

@ -0,0 +1,16 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_hosted:
version: "*"
rules:
- if: "target in [esp32p4]"
espressif/esp_wifi_remote:
version: "*"
rules:
- if: "target in [esp32p4]"
## Required IDF version
idf:
version: ">=5.3.0"
rules:
- if: "target in [esp32p4]"

17
package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "esp-nimble-cpp",
"version": "1.5.0",
"description": "NimBLE, BLE stack for the Espressif ESP32, ESP32-S and ESP32-C series of SoCs",
"keywords": [
"BLE",
"espidf",
"arduino",
"espressif",
"esp32"
],
"license": "LGPL-2.1-or-later",
"repository": {
"type": "git",
"url": "https://github.com/h2zero/esp-nimble-cpp"
}
}

View File

@ -1,316 +0,0 @@
/*
* FreeRTOS.cpp
*
* Created on: Feb 24, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#include "FreeRTOS.h"
#include "NimBLELog.h"
#include <freertos/FreeRTOS.h> // Include the base FreeRTOS definitions
#include <freertos/task.h> // Include the task definitions
#include <freertos/semphr.h> // Include the semaphore definitions
#include <string>
static const char* LOG_TAG = "FreeRTOS";
/**
* Sleep for the specified number of milliseconds.
* @param[in] ms The period in milliseconds for which to sleep.
*/
void FreeRTOS::sleep(uint32_t ms) {
::vTaskDelay(ms / portTICK_PERIOD_MS);
} // sleep
/**
* Start a new task.
* @param[in] task The function pointer to the function to be run in the task.
* @param[in] taskName A string identifier for the task.
* @param[in] param An optional parameter to be passed to the started task.
* @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task.
*/
void FreeRTOS::startTask(void task(void*), std::string taskName, void* param, uint32_t stackSize) {
::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL);
} // startTask
/**
* Delete the task.
* @param[in] pTask An optional handle to the task to be deleted. If not supplied the calling task will be deleted.
*/
void FreeRTOS::deleteTask(TaskHandle_t pTask) {
::vTaskDelete(pTask);
} // deleteTask
/**
* Get the time in milliseconds since the %FreeRTOS scheduler started.
* @return The time in milliseconds since the %FreeRTOS scheduler started.
*/
uint32_t FreeRTOS::getTimeSinceStart() {
return (uint32_t) (xTaskGetTickCount() * portTICK_PERIOD_MS);
} // getTimeSinceStart
/**
* @brief Wait for a semaphore to be released by trying to take it and
* then releasing it again.
* @param [in] owner A debug tag.
* @return The value associated with the semaphore.
*/
uint32_t FreeRTOS::Semaphore::wait(std::string owner) {
NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str());
if (m_usePthreads) {
pthread_mutex_lock(&m_pthread_mutex);
} else {
xSemaphoreTake(m_semaphore, portMAX_DELAY);
}
if (m_usePthreads) {
pthread_mutex_unlock(&m_pthread_mutex);
} else {
xSemaphoreGive(m_semaphore);
}
NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str());
return m_value;
} // wait
/**
* @brief Wait for a semaphore to be released in a given period of time by trying to take it and
* then releasing it again. The value associated with the semaphore can be taken by value() call after return
* @param [in] owner A debug tag.
* @param [in] timeoutMs timeout to wait in ms.
* @return True if we took the semaphore within timeframe.
*/
bool FreeRTOS::Semaphore::timedWait(std::string owner, uint32_t timeoutMs) {
NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str());
if (m_usePthreads && timeoutMs != portMAX_DELAY) {
assert(false); // We apparently don't have a timed wait for pthreads.
}
auto ret = pdTRUE;
if (m_usePthreads) {
pthread_mutex_lock(&m_pthread_mutex);
} else {
ret = xSemaphoreTake(m_semaphore, timeoutMs);
}
if (m_usePthreads) {
pthread_mutex_unlock(&m_pthread_mutex);
} else {
xSemaphoreGive(m_semaphore);
}
NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore %s released: %d", toString().c_str(), ret);
return ret;
} // wait
/**
* @brief Construct a semaphore, the semaphore is given when created.
* @param [in] name A name string to provide debugging support.
*/
FreeRTOS::Semaphore::Semaphore(std::string name) {
m_usePthreads = false; // Are we using pThreads or FreeRTOS?
if (m_usePthreads) {
pthread_mutex_init(&m_pthread_mutex, nullptr);
} else {
//m_semaphore = xSemaphoreCreateMutex();
m_semaphore = xSemaphoreCreateBinary();
xSemaphoreGive(m_semaphore);
}
m_name = name;
m_owner = std::string("<N/A>");
m_value = 0;
}
FreeRTOS::Semaphore::~Semaphore() {
if (m_usePthreads) {
pthread_mutex_destroy(&m_pthread_mutex);
} else {
vSemaphoreDelete(m_semaphore);
}
}
/**
* @brief Give the semaphore.
*/
void FreeRTOS::Semaphore::give() {
NIMBLE_LOGD(LOG_TAG, "Semaphore giving: %s", toString().c_str());
m_owner = std::string("<N/A>");
if (m_usePthreads) {
pthread_mutex_unlock(&m_pthread_mutex);
} else {
xSemaphoreGive(m_semaphore);
}
// #ifdef ARDUINO_ARCH_ESP32
// FreeRTOS::sleep(10);
// #endif
} // Semaphore::give
/**
* @brief Give a semaphore.
* The Semaphore is given with an associated value.
* @param [in] value The value to associate with the semaphore.
*/
void FreeRTOS::Semaphore::give(uint32_t value) {
m_value = value;
give();
} // give
/**
* @brief Give a semaphore from an ISR.
*/
void FreeRTOS::Semaphore::giveFromISR() {
BaseType_t higherPriorityTaskWoken;
if (m_usePthreads) {
assert(false);
} else {
xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken);
}
} // giveFromISR
/**
* @brief Take a semaphore.
* Take a semaphore and wait indefinitely.
* @param [in] owner The new owner (for debugging)
* @return True if we took the semaphore.
*/
bool FreeRTOS::Semaphore::take(std::string owner) {
NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
bool rc = false;
if (m_usePthreads) {
pthread_mutex_lock(&m_pthread_mutex);
} else {
rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY) == pdTRUE;
}
m_owner = owner;
if (rc) {
NIMBLE_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str());
} else {
NIMBLE_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str());
}
return rc;
} // Semaphore::take
/**
* @brief Take a semaphore.
* Take a semaphore but return if we haven't obtained it in the given period of milliseconds.
* @param [in] timeoutMs Timeout in milliseconds.
* @param [in] owner The new owner (for debugging)
* @return True if we took the semaphore.
*/
bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) {
NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
bool rc = false;
if (m_usePthreads) {
assert(false); // We apparently don't have a timed wait for pthreads.
} else {
rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE;
}
m_owner = owner;
if (rc) {
NIMBLE_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str());
} else {
NIMBLE_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str());
}
return rc;
} // Semaphore::take
/**
* @brief Create a string representation of the semaphore.
* @return A string representation of the semaphore.
*/
std::string FreeRTOS::Semaphore::toString() {
char hex[9];
std::string res = "name: " + m_name + " (0x";
snprintf(hex, sizeof(hex), "%08x", (uint32_t)m_semaphore);
res += hex;
res += "), owner: " + m_owner;
return res;
} // toString
/**
* @brief Set the name of the semaphore.
* @param [in] name The name of the semaphore.
*/
void FreeRTOS::Semaphore::setName(std::string name) {
m_name = name;
} // setName
/**
* @brief Create a ring buffer.
* @param [in] length The amount of storage to allocate for the ring buffer.
* @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
*/
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
#else
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
#else
Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
m_handle = ::xRingbufferCreate(length, type);
} // Ringbuffer
Ringbuffer::~Ringbuffer() {
::vRingbufferDelete(m_handle);
} // ~Ringbuffer
/**
* @brief Receive data from the buffer.
* @param [out] size On return, the size of data returned.
* @param [in] wait How long to wait.
* @return A pointer to the storage retrieved.
*/
void* Ringbuffer::receive(size_t* size, TickType_t wait) {
return ::xRingbufferReceive(m_handle, size, wait);
} // receive
/**
* @brief Return an item.
* @param [in] item The item to be returned/released.
*/
void Ringbuffer::returnItem(void* item) {
::vRingbufferReturnItem(m_handle, item);
} // returnItem
/**
* @brief Send data to the buffer.
* @param [in] data The data to place into the buffer.
* @param [in] length The length of data to place into the buffer.
* @param [in] wait How long to wait before giving up. The default is to wait indefinitely.
* @return
*/
bool Ringbuffer::send(void* data, size_t length, TickType_t wait) {
return ::xRingbufferSend(m_handle, data, length, wait) == pdTRUE;
} // send

View File

@ -1,89 +0,0 @@
/*
* FreeRTOS.h
*
* Created on: Feb 24, 2017
* Author: kolban
*/
#ifndef MAIN_FREERTOS_H_
#define MAIN_FREERTOS_H_
#include <freertos/FreeRTOS.h> // Include the base FreeRTOS definitions.
#include <freertos/task.h> // Include the task definitions.
#include <freertos/semphr.h> // Include the semaphore definitions.
#include <freertos/ringbuf.h> // Include the ringbuffer definitions.
#include <stdint.h>
#include <string>
#include <pthread.h>
/**
* @brief Interface to %FreeRTOS functions.
*/
class FreeRTOS {
public:
static void sleep(uint32_t ms);
static void startTask(void task(void*), std::string taskName, void* param = nullptr, uint32_t stackSize = 2048);
static void deleteTask(TaskHandle_t pTask = nullptr);
static uint32_t getTimeSinceStart();
/**
* @brief A binary semaphore class that operates like a mutex, it is already given when constructed.
*/
class Semaphore {
public:
Semaphore(std::string owner = "<Unknown>");
~Semaphore();
void give();
void give(uint32_t value);
void giveFromISR();
void setName(std::string name);
bool take(std::string owner = "<Unknown>");
bool take(uint32_t timeoutMs, std::string owner = "<Unknown>");
std::string toString();
bool timedWait(std::string owner = "<Unknown>", uint32_t timeoutMs = portMAX_DELAY);
uint32_t wait(std::string owner = "<Unknown>");
/**
* @brief Get the value of the semaphore.
* @return The value stored if the semaphore was given with give(value);
*/
uint32_t value(){ return m_value; };
private:
SemaphoreHandle_t m_semaphore;
pthread_mutex_t m_pthread_mutex;
std::string m_name;
std::string m_owner;
uint32_t m_value;
bool m_usePthreads;
};
};
/**
* @brief A wrapper class for a freeRTOS ringbuffer.
*/
class Ringbuffer {
public:
#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer(size_t length, RingbufferType_t type = RINGBUF_TYPE_NOSPLIT);
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif
~Ringbuffer();
void* receive(size_t* size, TickType_t wait = portMAX_DELAY);
void returnItem(void* item);
bool send(void* data, size_t length, TickType_t wait = portMAX_DELAY);
private:
RingbufHandle_t m_handle;
};
#endif /* MAIN_FREERTOS_H_ */

View File

@ -45,13 +45,8 @@
/* of data as per HID Class standard */ /* of data as per HID Class standard */
/* Main items */ /* Main items */
#ifdef ARDUINO_ARCH_ESP32
#define HIDINPUT(size) (0x80 | size) #define HIDINPUT(size) (0x80 | size)
#define HIDOUTPUT(size) (0x90 | size) #define HIDOUTPUT(size) (0x90 | size)
#else
#define INPUT(size) (0x80 | size)
#define OUTPUT(size) (0x90 | size)
#endif
#define FEATURE(size) (0xb0 | size) #define FEATURE(size) (0xb0 | size)
#define COLLECTION(size) (0xa0 | size) #define COLLECTION(size) (0xa0 | size)
#define END_COLLECTION(size) (0xc0 | size) #define END_COLLECTION(size) (0xc0 | size)

View File

@ -12,24 +12,17 @@
* Author: kolban * Author: kolban
*/ */
/*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLE2904.h" #include "NimBLE2904.h"
NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic) NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacteristic)
: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904), : NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904),
BLE_GATT_CHR_F_READ, BLE_GATT_CHR_F_READ,
sizeof(BLE2904_Data), sizeof(BLE2904_Data),
pCharacterisitic) pCharacteristic)
{ {
m_data.m_format = 0; m_data.m_format = 0;
m_data.m_exponent = 0; m_data.m_exponent = 0;
@ -86,5 +79,4 @@ void NimBLE2904::setUnit(uint16_t unit) {
setValue((uint8_t*) &m_data, sizeof(m_data)); setValue((uint8_t*) &m_data, sizeof(m_data));
} // setUnit } // setUnit
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif

View File

@ -14,11 +14,8 @@
#ifndef MAIN_NIMBLE2904_H_ #ifndef MAIN_NIMBLE2904_H_
#define MAIN_NIMBLE2904_H_ #define MAIN_NIMBLE2904_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEDescriptor.h" #include "NimBLEDescriptor.h"
@ -36,9 +33,6 @@ struct BLE2904_Data {
* @brief Descriptor for Characteristic Presentation Format. * @brief Descriptor for Characteristic Presentation Format.
* *
* This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
*/ */
class NimBLE2904: public NimBLEDescriptor { class NimBLE2904: public NimBLEDescriptor {
public: public:
@ -82,6 +76,5 @@ private:
BLE2904_Data m_data; BLE2904_Data m_data;
}; // BLE2904 }; // BLE2904
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLE2904_H_ */ #endif /* MAIN_NIMBLE2904_H_ */

View File

@ -11,93 +11,68 @@
* Created on: Jul 2, 2017 * Created on: Jul 2, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include <algorithm>
# include "NimBLEAddress.h" # include "NimBLEAddress.h"
#include "NimBLEUtils.h"
# include "NimBLELog.h" # include "NimBLELog.h"
# include <algorithm>
static const char* LOG_TAG = "NimBLEAddress"; static const char* LOG_TAG = "NimBLEAddress";
/************************************************* /*************************************************
* NOTE: NimBLE address bytes are in INVERSE ORDER! * NOTE: NimBLE address bytes are in INVERSE ORDER!
* We will accomodate that fact in these methods. * We will accommodate that fact in these methods.
*************************************************/ *************************************************/
/** /**
* @brief Create an address from the native NimBLE representation. * @brief Create an address from the native NimBLE representation.
* @param [in] address The native NimBLE address. * @param [in] address The native NimBLE address.
*/ */
NimBLEAddress::NimBLEAddress(ble_addr_t address) { NimBLEAddress::NimBLEAddress(ble_addr_t address) : ble_addr_t{address} {}
memcpy(m_address, address.val, 6);
m_addrType = address.type;
} // NimBLEAddress
/** /**
* @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0. * @brief Create an address from a hex string.
*/
NimBLEAddress::NimBLEAddress() {
NimBLEAddress("");
} // NimBLEAddress
/**
* @brief Create an address from a hex string
* *
* A hex string is of the format: * A hex string is of the format:
* ``` * ```
* 00:00:00:00:00:00 * 00:00:00:00:00:00
* ``` * ```
* which is 17 characters in length. * which is 17 characters in length.
* * @param [in] addr The hex string representation of the address.
* @param [in] stringAddress The hex string representation of the address.
* @param [in] type The type of the address. * @param [in] type The type of the address.
*/ */
NimBLEAddress::NimBLEAddress(const std::string &stringAddress, uint8_t type) { NimBLEAddress::NimBLEAddress(const std::string& addr, uint8_t type) {
m_addrType = type; this->type = type;
if (stringAddress.length() == 0) { if (addr.length() == BLE_DEV_ADDR_LEN) {
memset(m_address, 0, 6); std::reverse_copy(addr.data(), addr.data() + BLE_DEV_ADDR_LEN, this->val);
return; return;
} }
if (stringAddress.length() == 6) { if (addr.length() == 17) {
std::reverse_copy(stringAddress.data(), stringAddress.data() + 6, m_address); std::string mac{addr};
mac.erase(std::remove(mac.begin(), mac.end(), ':'), mac.end());
uint64_t address = std::stoull(mac, nullptr, 16);
memcpy(this->val, &address, sizeof this->val);
return; return;
} }
if (stringAddress.length() != 17) { *this = NimBLEAddress{};
memset(m_address, 0, sizeof m_address); // "00:00:00:00:00:00" represents an invalid address NIMBLE_LOGE(LOG_TAG, "Invalid address '%s'", addr.c_str());
NIMBLE_LOGD(LOG_TAG, "Invalid address '%s'", stringAddress.c_str());
return;
}
int data[6];
if(sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[5], &data[4], &data[3], &data[2], &data[1], &data[0]) != 6) {
memset(m_address, 0, sizeof m_address); // "00:00:00:00:00:00" represents an invalid address
NIMBLE_LOGD(LOG_TAG, "Invalid address '%s'", stringAddress.c_str());
}
for(size_t index = 0; index < sizeof m_address; index++) {
m_address[index] = data[index];
}
} // NimBLEAddress } // NimBLEAddress
/** /**
* @brief Constructor for compatibility with bluedroid esp library using native ESP representation. * @brief Constructor for compatibility with bluedroid esp library using native ESP representation.
* @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address. * @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address.
* @param [in] type The type of the address. * @param [in] type The type of the address.
*/ */
NimBLEAddress::NimBLEAddress(uint8_t address[6], uint8_t type) { NimBLEAddress::NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type) {
std::reverse_copy(address, address + sizeof m_address, m_address); std::reverse_copy(address, address + BLE_DEV_ADDR_LEN, this->val);
m_addrType = type; this->type = type;
} // NimBLEAddress } // NimBLEAddress
/** /**
* @brief Constructor for address using a hex value.\n * @brief Constructor for address using a hex value.\n
* Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16" * Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16"
@ -105,11 +80,10 @@ NimBLEAddress::NimBLEAddress(uint8_t address[6], uint8_t type) {
* @param [in] type The type of the address. * @param [in] type The type of the address.
*/ */
NimBLEAddress::NimBLEAddress(const uint64_t& address, uint8_t type) { NimBLEAddress::NimBLEAddress(const uint64_t& address, uint8_t type) {
memcpy(m_address, &address, sizeof m_address); memcpy(this->val, &address, sizeof this->val);
m_addrType = type; this->type = type;
} // NimBLEAddress } // NimBLEAddress
/** /**
* @brief Determine if this address equals another. * @brief Determine if this address equals another.
* @param [in] otherAddress The other address to compare against. * @param [in] otherAddress The other address to compare against.
@ -119,34 +93,72 @@ bool NimBLEAddress::equals(const NimBLEAddress &otherAddress) const {
return *this == otherAddress; return *this == otherAddress;
} // equals } // equals
/** /**
* @brief Get the native representation of the address. * @brief Get the NimBLE base struct of the address.
* @return a pointer to the uint8_t[6] array of the address. * @return A read only reference to the NimBLE base struct of the address.
*/ */
const uint8_t *NimBLEAddress::getNative() const { const ble_addr_t* NimBLEAddress::getBase() const {
return m_address; return reinterpret_cast<const ble_addr_t*>(this);
} // getNative } // getBase
/** /**
* @brief Get the address type. * @brief Get the address type.
* @return The address type. * @return The address type.
*/ */
uint8_t NimBLEAddress::getType() const { uint8_t NimBLEAddress::getType() const {
return m_addrType; return this->type;
} // getType } // getType
/**
* @brief Get the address value.
* @return A read only reference to the address value.
*/
const uint8_t* NimBLEAddress::getVal() const {
return this->val;
} // getVal
/**
* @brief Determine if this address is a Resolvable Private Address.
* @return True if the address is a RPA.
*/
bool NimBLEAddress::isRpa() const {
return BLE_ADDR_IS_RPA(this);
} // isRpa
/**
* @brief Determine if this address is a Non-Resolvable Private Address.
* @return True if the address is a NRPA.
*/
bool NimBLEAddress::isNrpa() const {
return BLE_ADDR_IS_NRPA(this);
} // isNrpa
/**
* @brief Determine if this address is a Static Address.
* @return True if the address is a Static Address.
*/
bool NimBLEAddress::isStatic() const {
return BLE_ADDR_IS_STATIC(this);
} // isStatic
/**
* @brief Determine if this address is a Public Address.
* @return True if the address is a Public Address.
*/
bool NimBLEAddress::isPublic() const {
return this->type == BLE_ADDR_PUBLIC;
} // isPublic
/**
* @brief Determine if this address is a NULL Address.
* @return True if the address is a NULL Address.
*/
bool NimBLEAddress::isNull() const {
return *this == NimBLEAddress{};
} // isNull
/** /**
* @brief Convert a BLE address to a string. * @brief Convert a BLE address to a string.
*
* A string representation of an address is in the format:
*
* ```
* xx:xx:xx:xx:xx:xx
* ```
*
* @return The string representation of the address. * @return The string representation of the address.
* @deprecated Use std::string() operator instead. * @deprecated Use std::string() operator instead.
*/ */
@ -154,43 +166,57 @@ std::string NimBLEAddress::toString() const {
return std::string(*this); return std::string(*this);
} // toString } // toString
/**
* @brief Reverse the byte order of the address.
* @return A reference to this address.
*/
const NimBLEAddress& NimBLEAddress::reverseByteOrder() {
std::reverse(this->val, this->val + BLE_DEV_ADDR_LEN);
return *this;
} // reverseByteOrder
/** /**
* @brief Convienience operator to check if this address is equal to another. * @brief Convenience operator to check if this address is equal to another.
*/ */
bool NimBLEAddress::operator==(const NimBLEAddress& rhs) const { bool NimBLEAddress::operator==(const NimBLEAddress& rhs) const {
return memcmp(rhs.m_address, m_address, sizeof m_address) == 0; if (this->type != rhs.type) {
return false;
}
return memcmp(rhs.val, this->val, sizeof this->val) == 0;
} // operator == } // operator ==
/** /**
* @brief Convienience operator to check if this address is not equal to another. * @brief Convenience operator to check if this address is not equal to another.
*/ */
bool NimBLEAddress::operator!=(const NimBLEAddress& rhs) const { bool NimBLEAddress::operator!=(const NimBLEAddress& rhs) const {
return !this->operator==(rhs); return !this->operator==(rhs);
} // operator != } // operator !=
/** /**
* @brief Convienience operator to convert this address to string representation. * @brief Convenience operator to convert this address to string representation.
* @details This allows passing NimBLEAddress to functions * @details This allows passing NimBLEAddress to functions that accept std::string and/or it's methods as a parameter.
* that accept std::string and/or or it's methods as a parameter.
*/ */
NimBLEAddress::operator std::string() const { NimBLEAddress::operator std::string() const {
char buffer[18]; char buffer[18];
snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x", snprintf(buffer,
m_address[5], m_address[4], m_address[3], sizeof(buffer),
m_address[2], m_address[1], m_address[0]); "%02x:%02x:%02x:%02x:%02x:%02x",
return std::string(buffer); this->val[5],
this->val[4],
this->val[3],
this->val[2],
this->val[1],
this->val[0]);
return std::string{buffer};
} // operator std::string } // operator std::string
/** /**
* @brief Convienience operator to convert the native address representation to uint_64. * @brief Convenience operator to convert the native address representation to uint_64.
*/ */
NimBLEAddress::operator uint64_t() const { NimBLEAddress::operator uint64_t() const {
uint64_t address = 0; uint64_t address = 0;
memcpy(&address, m_address, sizeof m_address); memcpy(&address, this->val, sizeof this->val);
return address; return address;
} // operator uint64_t } // operator uint64_t

View File

@ -12,46 +12,56 @@
* Author: kolban * Author: kolban
*/ */
#ifndef COMPONENTS_NIMBLEADDRESS_H_ #ifndef NIMBLE_CPP_ADDRESS_H_
#define COMPONENTS_NIMBLEADDRESS_H_ #define NIMBLE_CPP_ADDRESS_H_
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "nimble/ble.h" # include "nimble/ble.h"
# else
# include "nimble/nimble/include/nimble/ble.h"
# endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
# undef min # undef min
# undef max # undef max
/**************************/ /**************************/
# include <string> # include <string>
#include <algorithm>
/** /**
* @brief A %BLE device address. * @brief A %BLE device address.
* *
* Every %BLE device has a unique address which can be used to identify it and form connections. * Every %BLE device has a unique address which can be used to identify it and form connections.
*/ */
class NimBLEAddress { class NimBLEAddress : private ble_addr_t {
public: public:
NimBLEAddress(); /**
NimBLEAddress(ble_addr_t address); * @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0.
NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC); */
NimBLEAddress() = default;
NimBLEAddress(const ble_addr_t address);
NimBLEAddress(const uint8_t address[BLE_DEV_ADDR_LEN], uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const std::string& stringAddress, uint8_t type = BLE_ADDR_PUBLIC); NimBLEAddress(const std::string& stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
NimBLEAddress(const uint64_t& address, uint8_t type = BLE_ADDR_PUBLIC); NimBLEAddress(const uint64_t& address, uint8_t type = BLE_ADDR_PUBLIC);
bool isRpa() const;
bool isNrpa() const;
bool isStatic() const;
bool isPublic() const;
bool isNull() const;
bool equals(const NimBLEAddress& otherAddress) const; bool equals(const NimBLEAddress& otherAddress) const;
const uint8_t* getNative() const; const ble_addr_t* getBase() const;
std::string toString() const; std::string toString() const;
uint8_t getType() const; uint8_t getType() const;
const uint8_t* getVal() const;
const NimBLEAddress& reverseByteOrder();
bool operator==(const NimBLEAddress& rhs) const; bool operator==(const NimBLEAddress& rhs) const;
bool operator!=(const NimBLEAddress& rhs) const; bool operator!=(const NimBLEAddress& rhs) const;
operator std::string() const; operator std::string() const;
operator uint64_t() const; operator uint64_t() const;
private:
uint8_t m_address[6];
uint8_t m_addrType;
}; };
#endif /* CONFIG_BT_ENABLED */ #endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEADDRESS_H_ */ #endif /* NIMBLE_CPP_ADDRESS_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -12,24 +12,26 @@
* Author: kolban * Author: kolban
*/ */
#ifndef COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ #ifndef NIMBLE_CPP_ADVERTISED_DEVICE_H_
#define COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ #define NIMBLE_CPP_ADVERTISED_DEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
# include "NimBLEAddress.h" # include "NimBLEAddress.h"
# include "NimBLEScan.h" # include "NimBLEScan.h"
# include "NimBLEUUID.h" # include "NimBLEUUID.h"
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_hs_adv.h" # include "host/ble_hs_adv.h"
# include "host/ble_gap.h"
# else
# include "nimble/nimble/host/include/host/ble_hs_adv.h"
# include "nimble/nimble/host/include/host/ble_gap.h"
# endif
#include <map>
# include <vector> # include <vector>
class NimBLEScan; class NimBLEScan;
/** /**
* @brief A representation of a %BLE advertised device found by a scan. * @brief A representation of a %BLE advertised device found by a scan.
@ -39,16 +41,58 @@ class NimBLEScan;
*/ */
class NimBLEAdvertisedDevice { class NimBLEAdvertisedDevice {
public: public:
NimBLEAdvertisedDevice(); NimBLEAdvertisedDevice() = default;
NimBLEAddress getAddress(); uint8_t getAdvType() const;
uint8_t getAdvType(); uint8_t getAdvFlags() const;
uint16_t getAppearance(); uint16_t getAppearance() const;
uint16_t getAdvInterval(); uint16_t getAdvInterval() const;
uint16_t getMinInterval(); uint16_t getMinInterval() const;
uint16_t getMaxInterval(); uint16_t getMaxInterval() const;
std::string getManufacturerData(); uint8_t getManufacturerDataCount() const;
std::string getURI(); const NimBLEAddress& getAddress() const;
std::string getManufacturerData(uint8_t index = 0) const;
std::string getURI() const;
std::string getPayloadByType(uint16_t type, uint8_t index = 0) const;
std::string getName() const;
int8_t getRSSI() const;
NimBLEScan* getScan() const;
uint8_t getServiceDataCount() const;
std::string getServiceData(uint8_t index = 0) const;
std::string getServiceData(const NimBLEUUID& uuid) const;
NimBLEUUID getServiceDataUUID(uint8_t index = 0) const;
NimBLEUUID getServiceUUID(uint8_t index = 0) const;
uint8_t getServiceUUIDCount() const;
NimBLEAddress getTargetAddress(uint8_t index = 0) const;
uint8_t getTargetAddressCount() const;
int8_t getTXPower() const;
uint8_t getAdvLength() const;
uint8_t getAddressType() const;
bool isAdvertisingService(const NimBLEUUID& uuid) const;
bool haveAppearance() const;
bool haveManufacturerData() const;
bool haveName() const;
bool haveServiceData() const;
bool haveServiceUUID() const;
bool haveTXPower() const;
bool haveConnParams() const;
bool haveAdvInterval() const;
bool haveTargetAddress() const;
bool haveURI() const;
bool haveType(uint16_t type) const;
std::string toString() const;
bool isConnectable() const;
bool isLegacyAdvertisement() const;
# if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t getSetId() const;
uint8_t getPrimaryPhy() const;
uint8_t getSecondaryPhy() const;
uint16_t getPeriodicInterval() const;
# endif
const std::vector<uint8_t>& getPayload() const;
const std::vector<uint8_t>::const_iterator begin() const;
const std::vector<uint8_t>::const_iterator end() const;
/** /**
* @brief A template to convert the service data to <type\>. * @brief A template to convert the service data to <type\>.
@ -59,20 +103,13 @@ public:
* @details <b>Use:</b> <tt>getManufacturerData<type>(skipSizeCheck);</tt> * @details <b>Use:</b> <tt>getManufacturerData<type>(skipSizeCheck);</tt>
*/ */
template <typename T> template <typename T>
T getManufacturerData(bool skipSizeCheck = false) { T getManufacturerData(bool skipSizeCheck = false) const {
std::string data = getManufacturerData(); std::string data = getManufacturerData();
if (!skipSizeCheck && data.size() < sizeof(T)) return T(); if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char* pData = data.data(); const char* pData = data.data();
return *((T*)pData); return *((T*)pData);
} }
std::string getName();
int getRSSI();
NimBLEScan* getScan();
size_t getServiceDataCount();
std::string getServiceData(uint8_t index = 0);
std::string getServiceData(const NimBLEUUID &uuid);
/** /**
* @brief A template to convert the service data to <tt><type\></tt>. * @brief A template to convert the service data to <tt><type\></tt>.
* @tparam T The type to convert the data to. * @tparam T The type to convert the data to.
@ -83,7 +120,7 @@ public:
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt> * @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/ */
template <typename T> template <typename T>
T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) { T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) const {
std::string data = getServiceData(index); std::string data = getServiceData(index);
if (!skipSizeCheck && data.size() < sizeof(T)) return T(); if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char* pData = data.data(); const char* pData = data.data();
@ -100,77 +137,38 @@ public:
* @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt> * @details <b>Use:</b> <tt>getServiceData<type>(skipSizeCheck);</tt>
*/ */
template <typename T> template <typename T>
T getServiceData(const NimBLEUUID &uuid, bool skipSizeCheck = false) { T getServiceData(const NimBLEUUID& uuid, bool skipSizeCheck = false) const {
std::string data = getServiceData(uuid); std::string data = getServiceData(uuid);
if (!skipSizeCheck && data.size() < sizeof(T)) return T(); if (!skipSizeCheck && data.size() < sizeof(T)) return T();
const char* pData = data.data(); const char* pData = data.data();
return *((T*)pData); return *((T*)pData);
} }
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);
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
bool haveRSSI();
bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
bool haveConnParams();
bool haveAdvInterval();
bool haveTargetAddress();
bool haveURI();
std::string toString();
private: private:
friend class NimBLEScan; friend class NimBLEScan;
void setAddress(NimBLEAddress address); NimBLEAdvertisedDevice(const ble_gap_event* event, uint8_t eventType);
void setAdvType(uint8_t advType); void update(const ble_gap_event* event, uint8_t eventType);
void setPayload(const uint8_t *payload, uint8_t length, bool append); uint8_t findAdvField(uint8_t type, uint8_t index = 0, size_t* data_loc = nullptr) const;
void setRSSI(int rssi); size_t findServiceData(uint8_t index, uint8_t* bytes) const;
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(""); NimBLEAddress m_address{};
uint8_t m_advType; uint8_t m_advType{};
int m_rssi; int8_t m_rssi{};
time_t m_timestamp; uint8_t m_callbackSent{};
bool m_callbackSent; uint8_t m_advLength{};
uint8_t m_advLength; uint32_t m_srTimeout{};
# if CONFIG_BT_NIMBLE_EXT_ADV
bool m_isLegacyAdv{};
uint8_t m_sid{};
uint8_t m_primPhy{};
uint8_t m_secPhy{};
uint16_t m_periodicItvl{};
# endif
std::vector<uint8_t> m_payload; std::vector<uint8_t> m_payload;
}; };
/** #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_OBSERVER */
* @brief A callback handler for callbacks associated device scanning. #endif /* NIMBLE_CPP_ADVERTISED_DEVICE_H_ */
*
* When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising
* has been found. This class can be sub-classed and registered such that when a scan is performed and
* a new advertised device has been found, we will be called back to be notified.
*/
class NimBLEAdvertisedDeviceCallbacks {
public:
virtual ~NimBLEAdvertisedDeviceCallbacks() {}
/**
* @brief Called when a new scan result is detected.
*
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
* device that was found. During any individual scan, a device will only be detected one time.
*/
virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0;
};
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEADVERTISEDDEVICE_H_ */

View File

@ -13,13 +13,16 @@
* Author: kolban * Author: kolban
* *
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if (defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
!CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "services/gap/ble_svc_gap.h" #include "services/gap/ble_svc_gap.h"
#else
#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
#endif
#include "NimBLEAdvertising.h" #include "NimBLEAdvertising.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLEServer.h" #include "NimBLEServer.h"
@ -41,7 +44,7 @@ NimBLEAdvertising::NimBLEAdvertising() {
* @brief Stops the current advertising and resets the advertising data to the default values. * @brief Stops the current advertising and resets the advertising data to the default values.
*/ */
void NimBLEAdvertising::reset() { void NimBLEAdvertising::reset() {
if(NimBLEDevice::getInitialized() && isAdvertising()) { if(NimBLEDevice::isInitialized() && isAdvertising()) {
stop(); stop();
} }
memset(&m_advData, 0, sizeof m_advData); memset(&m_advData, 0, sizeof m_advData);
@ -53,7 +56,11 @@ void NimBLEAdvertising::reset() {
m_advData.name = (uint8_t *)name; m_advData.name = (uint8_t *)name;
m_advData.name_len = strlen(name); m_advData.name_len = strlen(name);
m_advData.name_is_complete = 1; m_advData.name_is_complete = 1;
#ifndef CONFIG_IDF_TARGET_ESP32P4
m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
#else
m_advData.tx_pwr_lvl = 0;
#endif
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
@ -68,6 +75,7 @@ void NimBLEAdvertising::reset() {
m_advDataSet = false; m_advDataSet = false;
// Set this to non-zero to prevent auto start if host reset before started by app. // Set this to non-zero to prevent auto start if host reset before started by app.
m_duration = BLE_HS_FOREVER; m_duration = BLE_HS_FOREVER;
m_advCompCB = nullptr;
} // reset } // reset
@ -92,8 +100,8 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
/** /**
* @brief Add a service uuid to exposed list of services. * @brief Remove a service UUID from the advertisment.
* @param [in] serviceUUID The UUID of the service to expose. * @param [in] serviceUUID The UUID of the service to remove.
*/ */
void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) { void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) { for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) {
@ -106,10 +114,17 @@ void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
} // addServiceUUID } // addServiceUUID
/**
* @brief Remove all service UUIDs from the advertisment.
*/
void NimBLEAdvertising::removeServices() {
std::vector<NimBLEUUID>().swap(m_serviceUUIDs);
m_advDataSet = false;
} // removeServices
/** /**
* @brief Set the device appearance in the advertising data. * @brief Set the device appearance in the advertising data.
* The codes for distinct appearances can be found here:\n
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
* @param [in] appearance The appearance of the device in the advertising data. * @param [in] appearance The appearance of the device in the advertising data.
*/ */
void NimBLEAdvertising::setAppearance(uint16_t appearance) { void NimBLEAdvertising::setAppearance(uint16_t appearance) {
@ -133,7 +148,7 @@ void NimBLEAdvertising::addTxPower() {
* @param [in] name The name to advertise. * @param [in] name The name to advertise.
*/ */
void NimBLEAdvertising::setName(const std::string &name) { void NimBLEAdvertising::setName(const std::string &name) {
m_name.assign(name.begin(), name.end()); std::vector<uint8_t>(name.begin(), name.end()).swap(m_name);
m_advData.name = &m_name[0]; m_advData.name = &m_name[0];
m_advData.name_len = m_name.size(); m_advData.name_len = m_name.size();
m_advDataSet = false; m_advDataSet = false;
@ -145,7 +160,19 @@ void NimBLEAdvertising::setName(const std::string &name) {
* @param [in] data The data to advertise. * @param [in] data The data to advertise.
*/ */
void NimBLEAdvertising::setManufacturerData(const std::string &data) { void NimBLEAdvertising::setManufacturerData(const std::string &data) {
m_mfgData.assign(data.begin(), data.end()); std::vector<uint8_t>(data.begin(), data.end()).swap(m_mfgData);
m_advData.mfg_data = &m_mfgData[0];
m_advData.mfg_data_len = m_mfgData.size();
m_advDataSet = false;
} // setManufacturerData
/**
* @brief Set the advertised manufacturer data.
* @param [in] data The data to advertise.
*/
void NimBLEAdvertising::setManufacturerData(const std::vector<uint8_t> &data) {
m_mfgData = data;
m_advData.mfg_data = &m_mfgData[0]; m_advData.mfg_data = &m_mfgData[0];
m_advData.mfg_data_len = m_mfgData.size(); m_advData.mfg_data_len = m_mfgData.size();
m_advDataSet = false; m_advDataSet = false;
@ -157,7 +184,7 @@ void NimBLEAdvertising::setManufacturerData(const std::string &data) {
* @param [in] uri The URI to advertise. * @param [in] uri The URI to advertise.
*/ */
void NimBLEAdvertising::setURI(const std::string &uri) { void NimBLEAdvertising::setURI(const std::string &uri) {
m_uri.assign(uri.begin(), uri.end()); std::vector<uint8_t>(uri.begin(), uri.end()).swap(m_uri);
m_advData.uri = &m_uri[0]; m_advData.uri = &m_uri[0];
m_advData.uri_len = m_uri.size(); m_advData.uri_len = m_uri.size();
m_advDataSet = false; m_advDataSet = false;
@ -173,7 +200,7 @@ void NimBLEAdvertising::setURI(const std::string &uri) {
void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) { void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
switch (uuid.bitSize()) { switch (uuid.bitSize()) {
case 16: { case 16: {
m_svcData16.assign((uint8_t*)&uuid.getNative()->u16.value, (uint8_t*)&uuid.getNative()->u16.value + 2); std::vector<uint8_t>(uuid.getValue(), uuid.getValue() + 2).swap(m_svcData16);
m_svcData16.insert(m_svcData16.end(), data.begin(), data.end()); 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 = (uint8_t*)&m_svcData16[0];
m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0; m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0;
@ -181,7 +208,7 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string
} }
case 32: { case 32: {
m_svcData32.assign((uint8_t*)&uuid.getNative()->u32.value, (uint8_t*)&uuid.getNative()->u32.value + 4); std::vector<uint8_t>(uuid.getValue(), uuid.getValue() + 4).swap(m_svcData32);
m_svcData32.insert(m_svcData32.end(), data.begin(), data.end()); 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 = (uint8_t*)&m_svcData32[0];
m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0; m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0;
@ -189,7 +216,7 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string
} }
case 128: { case 128: {
m_svcData128.assign(uuid.getNative()->u128.value, uuid.getNative()->u128.value + 16); std::vector<uint8_t>(uuid.getValue(), uuid.getValue() + 16).swap(m_svcData128);
m_svcData128.insert(m_svcData128.end(), data.begin(), data.end()); 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 = (uint8_t*)&m_svcData128[0];
m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0; m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0;
@ -381,10 +408,12 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
/** /**
* @brief Start advertising. * @brief Start advertising.
* @param [in] duration The duration, in seconds, to advertise, 0 == advertise forever. * @param [in] duration The duration, in milliseconds, to advertise, 0 == advertise forever.
* @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends. * @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends.
* @param [in] dirAddr The address of a peer to directly advertise to.
* @return True if advertising started successfully.
*/ */
bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) { bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB, NimBLEAddress* dirAddr) {
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d",
m_customAdvData, m_customScanResponseData); m_customAdvData, m_customScanResponseData);
@ -409,7 +438,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
// If already advertising just return // If already advertising just return
if(ble_gap_adv_active()) { if(ble_gap_adv_active()) {
NIMBLE_LOGW(LOG_TAG, "Advertising already active"); NIMBLE_LOGW(LOG_TAG, "Advertising already active");
return false; return true;
} }
// Save the duration incase of host reset so we can restart with the same params // Save the duration incase of host reset so we can restart with the same params
@ -418,9 +447,6 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(duration == 0){ if(duration == 0){
duration = BLE_HS_FOREVER; duration = BLE_HS_FOREVER;
} }
else{
duration = duration*1000; // convert duration to milliseconds
}
m_advCompCB = advCompleteCB; m_advCompCB = advCompleteCB;
@ -429,15 +455,16 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) { if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) {
if(!m_scanResp) { if(!m_scanResp) {
m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON; m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON;
m_advData.flags = BLE_HS_ADV_F_BREDR_UNSUP; // non-connectable advertising does not require AD flags.
m_advData.flags = 0;
} }
} }
int rc = 0; int rc = 0;
if (!m_customAdvData && !m_advDataSet) { if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data //start with 3 bytes for the flags data if required
uint8_t payloadLen = (2 + 1); uint8_t payloadLen = (m_advData.flags > 0) ? (2 + 1) : 0;
if(m_advData.mfg_data_len > 0) if(m_advData.mfg_data_len > 0)
payloadLen += (2 + m_advData.mfg_data_len); payloadLen += (2 + m_advData.mfg_data_len);
@ -463,7 +490,7 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
for(auto &it : m_serviceUUIDs) { for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) { if(it.bitSize() == BLE_UUID_TYPE_16) {
int add = (m_advData.num_uuids16 > 0) ? 2 : 4; int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids16_is_complete = 0; m_advData.uuids16_is_complete = 0;
@ -471,18 +498,17 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
} }
payloadLen += add; payloadLen += add;
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc((void*)m_advData.uuids16, if(nullptr == (m_advData.uuids16 = reinterpret_cast<ble_uuid16_t*>(realloc((void*)m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))) (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))))
{ {
NIMBLE_LOGC(LOG_TAG, "Error, no mem"); NIMBLE_LOGE(LOG_TAG, "Error, no mem");
abort(); return false;
} }
memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16], memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16],
&it.getNative()->u16, sizeof(ble_uuid16_t)); reinterpret_cast<ble_uuid16_t*>(&it), sizeof(ble_uuid16_t));
m_advData.uuids16_is_complete = 1; m_advData.uuids16_is_complete = 1;
m_advData.num_uuids16++; m_advData.num_uuids16++;
} } else if(it.bitSize() == BLE_UUID_TYPE_32) {
if(it.getNative()->u.type == BLE_UUID_TYPE_32) {
int add = (m_advData.num_uuids32 > 0) ? 4 : 6; int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids32_is_complete = 0; m_advData.uuids32_is_complete = 0;
@ -490,18 +516,17 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
} }
payloadLen += add; payloadLen += add;
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc((void*)m_advData.uuids32, if(nullptr == (m_advData.uuids32 = reinterpret_cast<ble_uuid32_t*>(realloc((void*)m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))) (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))))
{ {
NIMBLE_LOGC(LOG_TAG, "Error, no mem"); NIMBLE_LOGE(LOG_TAG, "Error, no mem");
abort(); return false;
} }
memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32], memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32],
&it.getNative()->u32, sizeof(ble_uuid32_t)); reinterpret_cast<ble_uuid32_t*>(&it), sizeof(ble_uuid32_t));
m_advData.uuids32_is_complete = 1; m_advData.uuids32_is_complete = 1;
m_advData.num_uuids32++; m_advData.num_uuids32++;
} } else if(it.bitSize() == BLE_UUID_TYPE_128){
if(it.getNative()->u.type == BLE_UUID_TYPE_128){
int add = (m_advData.num_uuids128 > 0) ? 16 : 18; int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids128_is_complete = 0; m_advData.uuids128_is_complete = 0;
@ -509,14 +534,14 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
} }
payloadLen += add; payloadLen += add;
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc((void*)m_advData.uuids128, if(nullptr == (m_advData.uuids128 = reinterpret_cast<ble_uuid128_t*>(realloc((void*)m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))))
{ {
NIMBLE_LOGC(LOG_TAG, "Error, no mem"); NIMBLE_LOGE(LOG_TAG, "Error, no mem");
abort(); return false;
} }
memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128], memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128],
&it.getNative()->u128, sizeof(ble_uuid128_t)); reinterpret_cast<ble_uuid128_t*>(&it), sizeof(ble_uuid128_t));
m_advData.uuids128_is_complete = 1; m_advData.uuids128_is_complete = 1;
m_advData.num_uuids128++; m_advData.num_uuids128++;
} }
@ -618,19 +643,29 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
} }
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration, rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType,
(dirAddr != nullptr) ? dirAddr->getBase() : NULL,
duration,
&m_advParams, &m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent : (pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEAdvertising::handleGapEvent, NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? (void*)pServer : (void*)this); (void*)this);
#else #else
rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration, rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType,
&m_advParams, NimBLEAdvertising::handleGapEvent, this); (dirAddr != nullptr) ? &peerAddr : NULL,
duration,
&m_advParams,
NimBLEAdvertising::handleGapEvent,
(void*)this);
#endif #endif
switch(rc) { switch(rc) {
case 0: case 0:
break; break;
case BLE_HS_EALREADY:
NIMBLE_LOGI(LOG_TAG, "Advertisement Already active");
break;
case BLE_HS_EINVAL: case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long"); NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
break; break;
@ -652,29 +687,27 @@ bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
break; break;
} }
if(rc != 0) {
return false;
}
NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
return true; return (rc == 0 || rc == BLE_HS_EALREADY);
} // start } // start
/** /**
* @brief Stop advertising. * @brief Stop advertising.
* @return True if advertising stopped successfully.
*/ */
void NimBLEAdvertising::stop() { bool NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop"); NIMBLE_LOGD(LOG_TAG, ">> stop");
int rc = ble_gap_adv_stop(); int rc = ble_gap_adv_stop();
if (rc != 0 && rc != BLE_HS_EALREADY) { if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
rc, NimBLEUtils::returnCodeToString(rc)); rc, NimBLEUtils::returnCodeToString(rc));
return; return false;
} }
NIMBLE_LOGD(LOG_TAG, "<< stop"); NIMBLE_LOGD(LOG_TAG, "<< stop");
return true;
} // stop } // stop
@ -732,7 +765,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
case BLE_HS_EOS: case BLE_HS_EOS:
case BLE_HS_ECONTROLLER: case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED: case BLE_HS_ENOTSYNCED:
NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason); NIMBLE_LOGE(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason); NimBLEDevice::onReset(event->adv_complete.reason);
return 0; return 0;
default: default:
@ -750,7 +783,7 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
*/ */
void NimBLEAdvertisementData::addData(const std::string &data) { void NimBLEAdvertisementData::addData(const std::string &data) {
if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) { if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) {
NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceded"); NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceeded");
return; return;
} }
m_payload.append(data); m_payload.append(data);
@ -770,9 +803,6 @@ void NimBLEAdvertisementData::addData(char * data, size_t length) {
/** /**
* @brief Set the appearance. * @brief Set the appearance.
* @param [in] appearance The appearance code value. * @param [in] appearance The appearance code value.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
*/ */
void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
char cdata[2]; char cdata[2];
@ -810,6 +840,18 @@ void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
} // setManufacturerData } // setManufacturerData
/**
* @brief Set manufacturer specific data.
* @param [in] data The manufacturer data to advertise.
*/
void NimBLEAdvertisementData::setManufacturerData(const std::vector<uint8_t> &data) {
char cdata[2];
cdata[0] = data.size() + 1;
cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
addData(std::string(cdata, 2) + std::string((char*)&data[0], data.size()));
} // setManufacturerData
/** /**
* @brief Set the URI to advertise. * @brief Set the URI to advertise.
* @param [in] uri The uri to advertise. * @param [in] uri The uri to advertise.
@ -918,21 +960,9 @@ void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t siz
for(auto &it : v_uuid){ for(auto &it : v_uuid){
if(it.bitSize() != size) { if(it.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size); NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
return; continue;
} else { } else {
switch(size) { uuids += std::string(reinterpret_cast<const char*>(it.getValue()), size / 8);
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;
}
} }
} }
@ -946,35 +976,30 @@ void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t siz
* @param [in] data The data to be associated with the service data advertised. * @param [in] data The data to be associated with the service data advertised.
*/ */
void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::string &data) { void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
char cdata[2]; uint8_t size = uuid.bitSize() / 8;
switch (uuid.bitSize()) { char cdata[2] = {static_cast<char>(1 + size + data.length()), BLE_HS_ADV_TYPE_SVC_DATA_UUID16};
case 16: { switch (size) {
case 2: {
// [Len] [0x16] [UUID16] data // [Len] [0x16] [UUID16] data
cdata[0] = data.length() + 3;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data);
break; break;
} }
case 16: {
case 32: {
// [Len] [0x20] [UUID32] data
cdata[0] = data.length() + 5;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data);
break;
}
case 128: {
// [Len] [0x21] [UUID128] data // [Len] [0x21] [UUID128] data
cdata[0] = data.length() + 17;
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21 cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data); break;
}
case 4: {
// [Len] [0x20] [UUID32] data
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
break; break;
} }
default: default:
return; return;
} }
addData(std::string(cdata, 2) + std::string(reinterpret_cast<const char*>(uuid.getValue()), size) + data);
} // setServiceData } // setServiceData
@ -997,7 +1022,11 @@ void NimBLEAdvertisementData::addTxPower() {
char cdata[3]; char cdata[3];
cdata[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1; cdata[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1;
cdata[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL; cdata[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL;
#ifndef CONFIG_IDF_TARGET_ESP32P4
cdata[2] = NimBLEDevice::getPower(); cdata[2] = NimBLEDevice::getPower();
#else
cdata[2] = 0;
#endif
addData(cdata, 3); addData(cdata, 3);
} // addTxPower } // addTxPower
@ -1027,5 +1056,12 @@ std::string NimBLEAdvertisementData::getPayload() {
return m_payload; return m_payload;
} // getPayload } // getPayload
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */ /**
* @brief Clear the advertisement data for reuse.
*/
void NimBLEAdvertisementData::clearData() {
m_payload.clear();
}
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */

View File

@ -14,20 +14,26 @@
#ifndef MAIN_BLEADVERTISING_H_ #ifndef MAIN_BLEADVERTISING_H_
#define MAIN_BLEADVERTISING_H_ #define MAIN_BLEADVERTISING_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if (defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
!CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_)
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h" #include "host/ble_gap.h"
#else
#include "nimble/nimble/host/include/host/ble_gap.h"
#endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min #undef min
#undef max #undef max
/**************************/ /**************************/
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include "NimBLEAddress.h"
#include <functional>
#include <vector> #include <vector>
/* COMPATIBILITY - DO NOT USE */ /* COMPATIBILITY - DO NOT USE */
@ -39,6 +45,9 @@
#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) #define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 )
/* ************************* */ /* ************************* */
class NimBLEAdvertising;
typedef std::function<void(NimBLEAdvertising*)> advCompleteCB_t;
/** /**
* @brief Advertisement data set by the programmer to be published by the %BLE server. * @brief Advertisement data set by the programmer to be published by the %BLE server.
@ -54,6 +63,7 @@ public:
void setCompleteServices32(const std::vector<NimBLEUUID> &v_uuid); void setCompleteServices32(const std::vector<NimBLEUUID> &v_uuid);
void setFlags(uint8_t); void setFlags(uint8_t);
void setManufacturerData(const std::string &data); void setManufacturerData(const std::string &data);
void setManufacturerData(const std::vector<uint8_t> &data);
void setURI(const std::string &uri); void setURI(const std::string &uri);
void setName(const std::string &name); void setName(const std::string &name);
void setPartialServices(const NimBLEUUID &uuid); void setPartialServices(const NimBLEUUID &uuid);
@ -66,6 +76,7 @@ public:
void addTxPower(); void addTxPower();
void setPreferredParams(uint16_t min, uint16_t max); void setPreferredParams(uint16_t min, uint16_t max);
std::string getPayload(); // Retrieve the current advert payload. std::string getPayload(); // Retrieve the current advert payload.
void clearData(); // Clear the advertisement data.
private: private:
friend class NimBLEAdvertising; friend class NimBLEAdvertising;
@ -86,11 +97,13 @@ public:
void addServiceUUID(const NimBLEUUID &serviceUUID); void addServiceUUID(const NimBLEUUID &serviceUUID);
void addServiceUUID(const char* serviceUUID); void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID); void removeServiceUUID(const NimBLEUUID &serviceUUID);
bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr); bool start(uint32_t duration = 0, advCompleteCB_t advCompleteCB = nullptr, NimBLEAddress* dirAddr = nullptr);
void stop(); void removeServices();
bool stop();
void setAppearance(uint16_t appearance); void setAppearance(uint16_t appearance);
void setName(const std::string &name); void setName(const std::string &name);
void setManufacturerData(const std::string &data); void setManufacturerData(const std::string &data);
void setManufacturerData(const std::vector<uint8_t> &data);
void setURI(const std::string &uri); void setURI(const std::string &uri);
void setServiceData(const NimBLEUUID &uuid, const std::string &data); void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setAdvertisementType(uint8_t adv_type); void setAdvertisementType(uint8_t adv_type);
@ -109,6 +122,7 @@ public:
private: private:
friend class NimBLEDevice; friend class NimBLEDevice;
friend class NimBLEServer;
void onHostSync(); void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg); static int handleGapEvent(struct ble_gap_event *event, void *arg);
@ -121,7 +135,7 @@ private:
bool m_customScanResponseData; bool m_customScanResponseData;
bool m_scanResp; bool m_scanResp;
bool m_advDataSet; bool m_advDataSet;
void (*m_advCompCB)(NimBLEAdvertising *pAdv); advCompleteCB_t m_advCompCB{nullptr};
uint8_t m_slaveItvl[4]; uint8_t m_slaveItvl[4];
uint32_t m_duration; uint32_t m_duration;
std::vector<uint8_t> m_svcData16; std::vector<uint8_t> m_svcData16;
@ -132,6 +146,5 @@ private:
std::vector<uint8_t> m_uri; std::vector<uint8_t> m_uri;
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_BLEADVERTISING_H_ */ #endif /* MAIN_BLEADVERTISING_H_ */

130
src/NimBLEAttValue.cpp Normal file
View File

@ -0,0 +1,130 @@
/*
* NimBLEAttValue.cpp
*
* Created: on July 17, 2024
* Author H2zero
*
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "nimble/nimble_npl.h"
# else
# include "nimble/nimble/include/nimble/nimble_npl.h"
# endif
# include "NimBLEAttValue.h"
// Default constructor implementation.
NimBLEAttValue::NimBLEAttValue(uint16_t init_len, uint16_t max_len)
: m_attr_value{static_cast<uint8_t*>(calloc(init_len + 1, 1))},
m_attr_max_len{std::min<uint16_t>(BLE_ATT_ATTR_MAX_LEN, max_len)},
m_attr_len{},
m_capacity{init_len}
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
,
m_timestamp{}
# endif
{
NIMBLE_CPP_DEBUG_ASSERT(m_attr_value);
}
// Value constructor implementation.
NimBLEAttValue::NimBLEAttValue(const uint8_t *value, uint16_t len, uint16_t max_len)
: NimBLEAttValue(len, max_len) {
memcpy(m_attr_value, value, len);
m_attr_value[len] = '\0';
m_attr_len = len;
}
// Destructor implementation.
NimBLEAttValue::~NimBLEAttValue() {
if (m_attr_value != nullptr) {
free(m_attr_value);
}
}
// Move assignment operator implementation.
NimBLEAttValue& NimBLEAttValue::operator=(NimBLEAttValue&& source) {
if (this != &source) {
free(m_attr_value);
m_attr_value = source.m_attr_value;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
source.m_attr_value = nullptr;
}
return *this;
}
// Copy assignment implementation.
NimBLEAttValue& NimBLEAttValue::operator=(const NimBLEAttValue& source) {
if (this != &source) {
deepCopy(source);
}
return *this;
}
// Copy all the data from the source object to this object, including allocated space.
void NimBLEAttValue::deepCopy(const NimBLEAttValue& source) {
uint8_t* res = static_cast<uint8_t*>(realloc(m_attr_value, source.m_capacity + 1));
NIMBLE_CPP_DEBUG_ASSERT(res);
ble_npl_hw_enter_critical();
m_attr_value = res;
m_attr_max_len = source.m_attr_max_len;
m_attr_len = source.m_attr_len;
m_capacity = source.m_capacity;
setTimeStamp(source.getTimeStamp());
memcpy(m_attr_value, source.m_attr_value, m_attr_len + 1);
ble_npl_hw_exit_critical(0);
}
// Set the value of the attribute.
bool NimBLEAttValue::setValue(const uint8_t* value, uint16_t len) {
m_attr_len = 0; // Just set the value length to 0 and append instead of repeating code.
append(value, len);
return memcmp(m_attr_value, value, len) == 0 && m_attr_len == len;
}
// Append the new data, allocate as necessary.
NimBLEAttValue& NimBLEAttValue::append(const uint8_t* value, uint16_t len) {
if (len == 0) {
return *this;
}
if ((m_attr_len + len) > m_attr_max_len) {
NIMBLE_LOGE("NimBLEAttValue", "val > max, len=%u, max=%u", len, m_attr_max_len);
return *this;
}
uint8_t* res = m_attr_value;
uint16_t new_len = m_attr_len + len;
if (new_len > m_capacity) {
res = static_cast<uint8_t*>(realloc(m_attr_value, (new_len + 1)));
m_capacity = new_len;
}
NIMBLE_CPP_DEBUG_ASSERT(res);
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t t = time(nullptr);
# else
time_t t = 0;
# endif
ble_npl_hw_enter_critical();
memcpy(res + m_attr_len, value, len);
m_attr_value = res;
m_attr_len = new_len;
m_attr_value[m_attr_len] = '\0';
setTimeStamp(t);
ble_npl_hw_exit_critical(0);
return *this;
}
#endif // CONFIG_BT_ENABLED

314
src/NimBLEAttValue.h Normal file
View File

@ -0,0 +1,314 @@
/*
* NimBLEAttValue.h
*
* Created: on March 18, 2021
* Author H2zero
*
*/
#ifndef NIMBLE_CPP_ATTVALUE_H
#define NIMBLE_CPP_ATTVALUE_H
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
# include <Arduino.h>
# endif
# include "NimBLELog.h"
# include <string>
# include <vector>
# include <ctime>
# include <cstring>
# include <cstdint>
# ifndef CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
# define CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED 0
# endif
# ifndef BLE_ATT_ATTR_MAX_LEN
# define BLE_ATT_ATTR_MAX_LEN 512
# endif
# if !defined(CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
# define CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH 20
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH > BLE_ATT_ATTR_MAX_LEN
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be larger than 512 (BLE_ATT_ATTR_MAX_LEN)
# elif CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH < 1
# error CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH cannot be less than 1; Range = 1 : 512
# endif
/* Used to determine if the type passed to a template has a data() and size() method. */
template <typename T, typename = void, typename = void>
struct Has_data_size : std::false_type {};
template <typename T>
struct Has_data_size<T, decltype(void(std::declval<T&>().data())), decltype(void(std::declval<T&>().size()))>
: std::true_type {};
/* Used to determine if the type passed to a template has a c_str() and length() method. */
template <typename T, typename = void, typename = void>
struct Has_c_str_length : std::false_type {};
template <typename T>
struct Has_c_str_length<T, decltype(void(std::declval<T&>().c_str())), decltype(void(std::declval<T&>().length()))>
: std::true_type {};
/**
* @brief A specialized container class to hold BLE attribute values.
* @details This class is designed to be more memory efficient than using\n
* standard container types for value storage, while being convertible to\n
* many different container classes.
*/
class NimBLEAttValue {
uint8_t* m_attr_value{};
uint16_t m_attr_max_len{};
uint16_t m_attr_len{};
uint16_t m_capacity{};
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
time_t m_timestamp{};
# endif
void deepCopy(const NimBLEAttValue& source);
public:
/**
* @brief Default constructor.
* @param[in] init_len The initial size in bytes.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(uint16_t init_len = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
/**
* @brief Construct with an initial value from a buffer.
* @param value A pointer to the initial value to set.
* @param[in] len The size in bytes of the value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const uint8_t *value, uint16_t len,
uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
/**
* @brief Construct with an initial value from a const char string.
* @param value A pointer to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const char *value, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
:NimBLEAttValue((uint8_t*)value, (uint16_t)strlen(value), max_len){}
/**
* @brief Construct with an initializer list.
* @param list An initializer list containing the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(std::initializer_list<uint8_t> list, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
: NimBLEAttValue(list.begin(), list.size(), max_len) {}
/**
* @brief Construct with an initial value from a std::string.
* @param str A std::string containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const std::string str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
: NimBLEAttValue(reinterpret_cast<const uint8_t*>(&str[0]), str.length(), max_len) {}
/**
* @brief Construct with an initial value from a std::vector<uint8_t>.
* @param vec A std::vector<uint8_t> containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const std::vector<uint8_t> vec, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
: NimBLEAttValue(&vec[0], vec.size(), max_len) {}
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
/**
* @brief Construct with an initial value from an Arduino String.
* @param str An Arduino String containing to the initial value to set.
* @param[in] max_len The max size in bytes that the value can be.
*/
NimBLEAttValue(const String str, uint16_t max_len = BLE_ATT_ATTR_MAX_LEN)
: NimBLEAttValue(reinterpret_cast<const uint8_t*>(str.c_str()), str.length(), max_len) {}
# endif
/** @brief Copy constructor */
NimBLEAttValue(const NimBLEAttValue& source) { deepCopy(source); }
/** @brief Move constructor */
NimBLEAttValue(NimBLEAttValue&& source) { *this = std::move(source); }
/** @brief Destructor */
~NimBLEAttValue();
/** @brief Returns the max size in bytes */
uint16_t max_size() const { return m_attr_max_len; }
/** @brief Returns the currently allocated capacity in bytes */
uint16_t capacity() const { return m_capacity; }
/** @brief Returns the current length of the value in bytes */
uint16_t length() const { return m_attr_len; }
/** @brief Returns the current size of the value in bytes */
uint16_t size() const { return m_attr_len; }
/** @brief Returns a pointer to the internal buffer of the value */
const uint8_t* data() const { return m_attr_value; }
/** @brief Returns a pointer to the internal buffer of the value as a const char* */
const char* c_str() const { return reinterpret_cast<const char*>(m_attr_value); }
/** @brief Iterator begin */
const uint8_t* begin() const { return m_attr_value; }
/** @brief Iterator end */
const uint8_t* end() const { return m_attr_value + m_attr_len; }
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
/** @brief Returns a timestamp of when the value was last updated */
time_t getTimeStamp() const { return m_timestamp; }
/** @brief Set the timestamp to the current time */
void setTimeStamp() { m_timestamp = time(nullptr); }
/**
* @brief Set the timestamp to the specified time
* @param[in] t The timestamp value to set
*/
void setTimeStamp(time_t t) { m_timestamp = t; }
# else
time_t getTimeStamp() const { return 0; }
void setTimeStamp() {}
void setTimeStamp(time_t t) {}
# endif
/**
* @brief Set the value from a buffer
* @param[in] value A pointer to a buffer containing the value.
* @param[in] len The length of the value in bytes.
* @returns True if successful.
*/
bool setValue(const uint8_t* value, uint16_t len);
/**
* @brief Set value to the value of const char*.
* @param [in] s A pointer to a const char value to set.
* @param [in] len The length of the value in bytes, defaults to strlen(s).
*/
bool setValue(const char* s, uint16_t len = 0) {
if (len == 0) {
len = strlen(s);
}
return setValue(reinterpret_cast<const uint8_t*>(s), len);
}
const NimBLEAttValue& getValue(time_t* timestamp = nullptr) const {
if (timestamp != nullptr) {
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
# else
*timestamp = 0;
# endif
}
return *this;
}
/**
* @brief Append data to the value.
* @param[in] value A ponter to a data buffer with the value to append.
* @param[in] len The length of the value to append in bytes.
* @returns A reference to the appended NimBLEAttValue.
*/
NimBLEAttValue& append(const uint8_t* value, uint16_t len);
/*********************** Template Functions ************************/
/**
* @brief Template to set value to the value of <type\>val.
* @param [in] s The <type\>value to set.
* @note This function is only availabe if the type T is not a pointer.
*/
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, bool>::type
setValue(const T& s) {
if constexpr (Has_data_size<T>::value) {
return setValue(reinterpret_cast<const uint8_t*>(s.data()), s.size());
} else if constexpr (Has_c_str_length<T>::value) {
return setValue(reinterpret_cast<const uint8_t*>(s.c_str()), s.length());
} else {
return setValue(reinterpret_cast<const uint8_t*>(&s), sizeof(s));
}
}
/**
* @brief Template to return the value as a <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than\n
* <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is\n
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template <typename T>
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
if (!skipSizeCheck && size() < sizeof(T)) {
return T();
}
if (timestamp != nullptr) {
# if CONFIG_NIMBLE_CPP_ATT_VALUE_TIMESTAMP_ENABLED
*timestamp = m_timestamp;
# else
*timestamp = 0;
# endif
}
return *(reinterpret_cast<const T*>(m_attr_value));
}
/*********************** Operators ************************/
/** @brief Subscript operator */
uint8_t operator[](int pos) const {
NIMBLE_CPP_DEBUG_ASSERT(pos < m_attr_len);
return m_attr_value[pos];
}
/** @brief Operator; Get the value as a std::vector<uint8_t>. */
operator std::vector<uint8_t>() const { return std::vector<uint8_t>(m_attr_value, m_attr_value + m_attr_len); }
/** @brief Operator; Get the value as a std::string. */
operator std::string() const { return std::string(reinterpret_cast<char*>(m_attr_value), m_attr_len); }
/** @brief Operator; Get the value as a const uint8_t*. */
operator const uint8_t*() const { return m_attr_value; }
/** @brief Operator; Append another NimBLEAttValue. */
NimBLEAttValue& operator+=(const NimBLEAttValue& source) { return append(source.data(), source.size()); }
/** @brief Operator; Set the value from a std::string source. */
NimBLEAttValue& operator=(const std::string& source) {
setValue(reinterpret_cast<const uint8_t*>(&source[0]), source.size());
return *this;
}
/** @brief Move assignment operator */
NimBLEAttValue& operator=(NimBLEAttValue&& source);
/** @brief Copy assignment operator */
NimBLEAttValue& operator=(const NimBLEAttValue& source);
/** @brief Equality operator */
bool operator==(const NimBLEAttValue& source) const {
return (m_attr_len == source.size()) ? memcmp(m_attr_value, source.data(), m_attr_len) == 0 : false;
}
/** @brief Inequality operator */
bool operator!=(const NimBLEAttValue& source) const { return !(*this == source); }
# ifdef NIMBLE_CPP_ARDUINO_STRING_AVAILABLE
/** @brief Operator; Get the value as an Arduino String value. */
operator String() const { return String(reinterpret_cast<char*>(m_attr_value)); }
# endif
};
#endif /*(CONFIG_BT_ENABLED) */
#endif /* NIMBLE_CPP_ATTVALUE_H_ */

50
src/NimBLEAttribute.h Normal file
View File

@ -0,0 +1,50 @@
/*
* NimBLEAttribute.h
*
* Created: on July 28 2024
* Author H2zero
*/
#ifndef NIMBLE_CPP_ATTRIBUTE_H_
#define NIMBLE_CPP_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && (defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL))
# include "NimBLEUUID.h"
/**
* @brief A base class for BLE attributes.
*/
class NimBLEAttribute {
public:
/**
* @brief Get the UUID of the attribute.
* @return The UUID.
*/
const NimBLEUUID& getUUID() const { return m_uuid; }
/**
* @brief Get the handle of the attribute.
*/
uint16_t getHandle() const { return m_handle; };
protected:
/**
* @brief Construct a new NimBLEAttribute object.
* @param [in] handle The handle of the attribute.
* @param [in] uuid The UUID of the attribute.
*/
NimBLEAttribute(const NimBLEUUID& uuid, uint16_t handle) : m_uuid{uuid}, m_handle{handle} {}
/**
* @brief Destroy the NimBLEAttribute object.
*/
~NimBLEAttribute() = default;
const NimBLEUUID m_uuid{};
uint16_t m_handle{0};
};
#endif // CONFIG_BT_ENABLED && (CONFIG_BT_NIMBLE_ROLE_PERIPHERAL || CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif // NIMBLE_CPP_ATTRIBUTE_H_

View File

@ -11,7 +11,7 @@
* Created on: Jan 4, 2018 * Created on: Jan 4, 2018
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include <string.h> #include <string.h>
@ -79,7 +79,7 @@ uint16_t NimBLEBeacon::getMinor() {
* @return The UUID advertised. * @return The UUID advertised.
*/ */
NimBLEUUID NimBLEBeacon::getProximityUUID() { NimBLEUUID NimBLEBeacon::getProximityUUID() {
return NimBLEUUID(m_beaconData.proximityUUID, 16, true); return NimBLEUUID(m_beaconData.proximityUUID, 16).reverseByteOrder();
} }
@ -130,7 +130,7 @@ void NimBLEBeacon::setManufacturerId(uint16_t manufacturerId) {
*/ */
void NimBLEBeacon::setMinor(uint16_t minor) { void NimBLEBeacon::setMinor(uint16_t minor) {
m_beaconData.minor = ENDIAN_CHANGE_U16(minor); m_beaconData.minor = ENDIAN_CHANGE_U16(minor);
} // setMinior } // setMinor
/** /**
@ -140,9 +140,7 @@ void NimBLEBeacon::setMinor(uint16_t minor) {
void NimBLEBeacon::setProximityUUID(const NimBLEUUID &uuid) { void NimBLEBeacon::setProximityUUID(const NimBLEUUID &uuid) {
NimBLEUUID temp_uuid = uuid; NimBLEUUID temp_uuid = uuid;
temp_uuid.to128(); temp_uuid.to128();
std::reverse_copy(temp_uuid.getNative()->u128.value, std::reverse_copy(temp_uuid.getValue(), temp_uuid.getValue() + 16, m_beaconData.proximityUUID);
temp_uuid.getNative()->u128.value + 16,
m_beaconData.proximityUUID);
} // setProximityUUID } // setProximityUUID

View File

@ -9,62 +9,52 @@
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLECharacteristic.h" # include "NimBLECharacteristic.h"
# include "NimBLE2904.h" # include "NimBLE2904.h"
# include "NimBLEDevice.h" # include "NimBLEDevice.h"
# include "NimBLELog.h" # include "NimBLELog.h"
#define NULL_HANDLE (0xffff)
# define NIMBLE_SUB_NOTIFY 0x0001 # define NIMBLE_SUB_NOTIFY 0x0001
# define NIMBLE_SUB_INDICATE 0x0002 # define NIMBLE_SUB_INDICATE 0x0002
static NimBLECharacteristicCallbacks defaultCallback; static NimBLECharacteristicCallbacks defaultCallback;
static const char* LOG_TAG = "NimBLECharacteristic"; static const char* LOG_TAG = "NimBLECharacteristic";
/** /**
* @brief Construct a characteristic * @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic. * @param [in] uuid - UUID (const char*) for the characteristic.
* @param [in] properties - Properties for the characteristic. * @param [in] properties - Properties for the characteristic.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to. * @param [in] pService - pointer to the service instance this characteristic belongs to.
*/ */
NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, NimBLEService* pService) NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) { : NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {}
}
/** /**
* @brief Construct a characteristic * @brief Construct a characteristic
* @param [in] uuid - UUID for the characteristic. * @param [in] uuid - UUID for the characteristic.
* @param [in] properties - Properties for the characteristic. * @param [in] properties - Properties for the characteristic.
* @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pService - pointer to the service instance this characteristic belongs to. * @param [in] pService - pointer to the service instance this characteristic belongs to.
*/ */
NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties, NimBLEService* pService) { NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService)
m_uuid = uuid; : NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallback}, m_pService{pService} {
m_handle = NULL_HANDLE; setProperties(properties);
m_properties = properties;
m_pCallbacks = &defaultCallback;
m_pService = pService;
m_value = "";
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_timestamp = 0;
} // NimBLECharacteristic } // NimBLECharacteristic
/** /**
* @brief Destructor. * @brief Destructor.
*/ */
NimBLECharacteristic::~NimBLECharacteristic() { NimBLECharacteristic::~NimBLECharacteristic() {
for(auto &it : m_dscVec) { for (const auto& dsc : m_vDescriptors) {
delete it; delete dsc;
} }
} // ~NimBLECharacteristic } // ~NimBLECharacteristic
/** /**
* @brief Create a new BLE Descriptor associated with this characteristic. * @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor. * @param [in] uuid - The UUID of the descriptor.
@ -76,7 +66,6 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
return createDescriptor(NimBLEUUID(uuid), properties, max_len); return createDescriptor(NimBLEUUID(uuid), properties, max_len);
} }
/** /**
* @brief Create a new BLE Descriptor associated with this characteristic. * @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor. * @param [in] uuid - The UUID of the descriptor.
@ -86,49 +75,92 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
*/ */
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) { NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr; NimBLEDescriptor* pDescriptor = nullptr;
if(uuid == NimBLEUUID(uint16_t(0x2902))) { if (uuid == NimBLEUUID(uint16_t(0x2904))) {
assert(0 && "0x2902 descriptors cannot be manually created");
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this); pDescriptor = new NimBLE2904(this);
} else { } else {
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this); pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
} }
addDescriptor(pDescriptor); addDescriptor(pDescriptor);
return pDescriptor; return pDescriptor;
} // createDescriptor } // createDescriptor
/** /**
* @brief Add a descriptor to the characteristic. * @brief Add a descriptor to the characteristic.
* @param [in] A pointer to the descriptor to add. * @param [in] pDescriptor A pointer to the descriptor to add.
*/ */
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) { void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) {
pDescriptor->setCharacteristic(this); bool foundRemoved = false;
m_dscVec.push_back(pDescriptor); if (pDescriptor->getRemoved() > 0) {
for (const auto& dsc : m_vDescriptors) {
if (dsc == pDescriptor) {
foundRemoved = true;
pDescriptor->setRemoved(0);
}
}
} }
// Check if the descriptor is already in the vector and if so, return.
for (const auto& dsc : m_vDescriptors) {
if (dsc == pDescriptor) {
pDescriptor->setCharacteristic(this); // Update the characteristic pointer in the descriptor.
return;
}
}
if (!foundRemoved) {
m_vDescriptors.push_back(pDescriptor);
}
pDescriptor->setCharacteristic(this);
NimBLEDevice::getServer()->serviceChanged();
}
/**
* @brief Remove a descriptor from the characteristic.
* @param[in] pDescriptor A pointer to the descriptor instance to remove from the characteristic.
* @param[in] deleteDsc If true it will delete the descriptor instance and free it's resources.
*/
void NimBLECharacteristic::removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc) {
// Check if the descriptor was already removed and if so, check if this
// is being called to delete the object and do so if requested.
// Otherwise, ignore the call and return.
if (pDescriptor->getRemoved() > 0) {
if (deleteDsc) {
for (auto it = m_vDescriptors.begin(); it != m_vDescriptors.end(); ++it) {
if ((*it) == pDescriptor) {
delete (*it);
m_vDescriptors.erase(it);
break;
}
}
}
return;
}
pDescriptor->setRemoved(deleteDsc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE);
NimBLEDevice::getServer()->serviceChanged();
} // removeDescriptor
/** /**
* @brief Return the BLE Descriptor for the given UUID. * @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor. * @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found. * @return A pointer to the descriptor object or nullptr if not found.
*/ */
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) const {
return getDescriptorByUUID(NimBLEUUID(uuid)); return getDescriptorByUUID(NimBLEUUID(uuid));
} // getDescriptorByUUID } // getDescriptorByUUID
/** /**
* @brief Return the BLE Descriptor for the given UUID. * @brief Return the BLE Descriptor for the given UUID.
* @param [in] uuid The UUID of the descriptor. * @param [in] uuid The UUID of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found. * @return A pointer to the descriptor object or nullptr if not found.
*/ */
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID& uuid) const {
for (auto &it : m_dscVec) { for (const auto& dsc : m_vDescriptors) {
if (it->getUUID() == uuid) { if (dsc->getUUID() == uuid) {
return it; return dsc;
} }
} }
return nullptr; return nullptr;
@ -139,175 +171,47 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uu
* @param [in] handle The handle of the descriptor. * @param [in] handle The handle of the descriptor.
* @return A pointer to the descriptor object or nullptr if not found. * @return A pointer to the descriptor object or nullptr if not found.
*/ */
NimBLEDescriptor *NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByHandle(uint16_t handle) const {
for (auto &it : m_dscVec) { for (const auto& dsc : m_vDescriptors) {
if (it->getHandle() == handle) { if (dsc->getHandle() == handle) {
return it; return dsc;
} }
} }
return nullptr; return nullptr;
} } // getDescriptorByHandle
/**
* @brief Get the handle of the characteristic.
* @return The handle of the characteristic.
*/
uint16_t NimBLECharacteristic::getHandle() {
return m_handle;
} // getHandle
/** /**
* @brief Get the properties of the characteristic. * @brief Get the properties of the characteristic.
* @return The properties of the characteristic. * @return The properties of the characteristic.
*/ */
uint16_t NimBLECharacteristic::getProperties() { uint16_t NimBLECharacteristic::getProperties() const {
return m_properties; return m_properties;
} // getProperties } // getProperties
/** /**
* @brief Get the service associated with this characteristic. * @brief Get the service that owns this characteristic.
*/ */
NimBLEService* NimBLECharacteristic::getService() { NimBLEService* NimBLECharacteristic::getService() const {
return m_pService; return m_pService;
} // getService } // getService
void NimBLECharacteristic::setService(NimBLEService* pService) { void NimBLECharacteristic::setService(NimBLEService* pService) {
m_pService = pService; m_pService = pService;
} } // setService
/**
* @brief Get the UUID of the characteristic.
* @return The UUID of the characteristic.
*/
NimBLEUUID NimBLECharacteristic::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Retrieve the current value of the characteristic.
* @return A std::string containing the current characteristic value.
*/
std::string NimBLECharacteristic::getValue(time_t *timestamp) {
portENTER_CRITICAL(&m_valMux);
std::string retVal = m_value;
if(timestamp != nullptr) {
*timestamp = m_timestamp;
}
portEXIT_CRITICAL(&m_valMux);
return retVal;
} // getValue
/**
* @brief Retrieve the the current data length of the characteristic.
* @return The length of the current characteristic data.
*/
size_t NimBLECharacteristic::getDataLength() {
portENTER_CRITICAL(&m_valMux);
size_t len = m_value.length();
portEXIT_CRITICAL(&m_valMux);
return len;
}
/**
* @brief STATIC callback to handle events from the NimBLE stack.
*/
int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
const ble_uuid_t *uuid;
int rc;
struct ble_gap_conn_desc desc;
NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg;
NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(),
ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write");
uuid = ctxt->chr->uuid;
if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR: {
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8) {
rc = ble_gap_conn_find(conn_handle, &desc);
assert(rc == 0);
pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc);
}
portENTER_CRITICAL(&pCharacteristic->m_valMux);
rc = os_mbuf_append(ctxt->om, (uint8_t*)pCharacteristic->m_value.data(),
pCharacteristic->m_value.length());
portEXIT_CRITICAL(&pCharacteristic->m_valMux);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[BLE_ATT_ATTR_MAX_LEN];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
rc = ble_gap_conn_find(conn_handle, &desc);
assert(rc == 0);
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc);
return 0;
}
default:
break;
}
}
return BLE_ATT_ERR_UNLIKELY;
}
/** /**
* @brief Get the number of clients subscribed to the characteristic. * @brief Get the number of clients subscribed to the characteristic.
* @returns Number of clients subscribed to notifications / indications. * @returns Number of clients subscribed to notifications / indications.
*/ */
size_t NimBLECharacteristic::getSubscribedCount() { size_t NimBLECharacteristic::getSubscribedCount() const {
return m_subscribedVec.size(); return m_subscribedVec.size();
} }
/** /**
* @brief Set the subscribe status for this characteristic.\n * @brief Set the subscribe status for this characteristic.\n
* This will maintain a vector of subscribed clients and their indicate/notify status. * This will maintain a vector of subscribed clients and their indicate/notify status.
*/ */
void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { void NimBLECharacteristic::setSubscribe(const ble_gap_event* event, NimBLEConnInfo& connInfo) {
ble_gap_conn_desc desc;
if(ble_gap_conn_find(event->subscribe.conn_handle, &desc) != 0) {
return;
}
uint16_t subVal = 0; uint16_t subVal = 0;
if (event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) { if (event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
subVal |= NIMBLE_SUB_NOTIFY; subVal |= NIMBLE_SUB_NOTIFY;
@ -316,24 +220,22 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
subVal |= NIMBLE_SUB_INDICATE; subVal |= NIMBLE_SUB_INDICATE;
} }
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", connInfo.getConnHandle(), subVal);
event->subscribe.conn_handle, subVal);
if (!event->subscribe.cur_indicate && event->subscribe.prev_indicate) { if (!event->subscribe.cur_indicate && event->subscribe.prev_indicate) {
NimBLEDevice::getServer()->clearIndicateWait(event->subscribe.conn_handle); NimBLEDevice::getServer()->clearIndicateWait(connInfo.getConnHandle());
} }
auto it = m_subscribedVec.begin(); auto it = m_subscribedVec.begin();
for (; it != m_subscribedVec.end(); ++it) { for (; it != m_subscribedVec.end(); ++it) {
if((*it).first == event->subscribe.conn_handle) { if ((*it).first == connInfo.getConnHandle()) {
break; break;
} }
} }
if (subVal > 0) { if (subVal > 0) {
if (it == m_subscribedVec.end()) { if (it == m_subscribedVec.end()) {
m_subscribedVec.push_back({event->subscribe.conn_handle, subVal}); m_subscribedVec.push_back({connInfo.getConnHandle(), subVal});
} else { } else {
(*it).second = subVal; (*it).second = subVal;
} }
@ -341,110 +243,138 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
m_subscribedVec.erase(it); m_subscribedVec.erase(it);
} }
m_pCallbacks->onSubscribe(this, &desc, subVal); m_pCallbacks->onSubscribe(this, connInfo, subVal);
} }
/** /**
* @brief Send an indication.\n * @brief Send an indication.
* An indication is a transmission of up to the first 20 bytes of the characteristic value.\n * @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send
* An indication will block waiting for a positive confirmation from the client. * the indication to all subscribed clients.
*/ */
void NimBLECharacteristic::indicate() { void NimBLECharacteristic::indicate(uint16_t conn_handle) const {
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength()); sendValue(m_value.data(), m_value.size(), false, conn_handle);
notify(false);
NIMBLE_LOGD(LOG_TAG, "<< indicate");
} // indicate } // indicate
/** /**
* @brief Send a notification.\n * @brief Send an indication.
* A notification is a transmission of up to the first 20 bytes of the characteristic value.\n * @param[in] value A pointer to the data to send.
* A notification will not block; it is a fire and forget. * @param[in] length The length of the data to send.
* @param[in] is_notification if true sends a notification, false sends an indication. * @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send
* the indication to all subscribed clients.
*/ */
void NimBLECharacteristic::notify(bool is_notification) { void NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_t conn_handle) const {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", getDataLength()); sendValue(value, length, false, conn_handle);
} // indicate
/**
* @brief Send a notification.
* @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
* the notification to all subscribed clients.
*/
void NimBLECharacteristic::notify(uint16_t conn_handle) const {
sendValue(m_value.data(), m_value.size(), true, conn_handle);
} // notify
if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) && /**
!(m_properties & NIMBLE_PROPERTY::INDICATE)) * @brief Send a notification.
{ * @param[in] value A pointer to the data to send.
NIMBLE_LOGE(LOG_TAG, * @param[in] length The length of the data to send.
"<< notify-Error; Notify/indicate not enabled for characterisitc: %s", * @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send
std::string(getUUID()).c_str()); * the notification to all subscribed clients.
} */
void NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t conn_handle) const {
sendValue(value, length, true, conn_handle);
} // indicate
if (m_subscribedVec.size() == 0) { /**
NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed."); * @brief Sends a notification or indication.
* @param[in] value A pointer to the data to send.
* @param[in] length The length of the data to send.
* @param[in] is_notification if true sends a notification, false sends an indication.
* @param[in] conn_handle Connection handle to send to a specific peer, or BLE_HS_CONN_HANDLE_NONE to send
* to all subscribed clients.
*/
void NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool is_notification, uint16_t conn_handle) const {
NIMBLE_LOGD(LOG_TAG, ">> sendValue");
if (is_notification && !(getProperties() & NIMBLE_PROPERTY::NOTIFY)) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: notification not enabled for characteristic");
return; return;
} }
m_pCallbacks->onNotify(this); if (!is_notification && !(getProperties() & NIMBLE_PROPERTY::INDICATE)) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: indication not enabled for characteristic");
return;
}
std::string value = getValue(); if (!m_subscribedVec.size()) {
size_t length = value.length(); NIMBLE_LOGD(LOG_TAG, "<< sendValue: No clients subscribed.");
bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) || return;
(m_properties & BLE_GATT_CHR_F_READ_AUTHOR) || }
(m_properties & BLE_GATT_CHR_F_READ_ENC);
int rc = 0;
for (auto &it : m_subscribedVec) {
uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first);
for (const auto& it : m_subscribedVec) {
// check if connected and subscribed // check if connected and subscribed
if(_mtu == 0 || it.second == 0) { if (!it.second) {
continue;
}
// sending to a specific client?
if ((conn_handle <= BLE_HCI_LE_CONN_HANDLE_MAX) && (it.first != conn_handle)) {
continue;
}
if (is_notification && !(it.second & NIMBLE_SUB_NOTIFY)) {
continue;
}
if (!is_notification && !(it.second & NIMBLE_SUB_INDICATE)) {
continue; continue;
} }
// check if security requirements are satisfied // check if security requirements are satisfied
if(reqSec) { if ((getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) || (getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) ||
struct ble_gap_conn_desc desc; (getProperties() & BLE_GATT_CHR_F_READ_ENC)) {
rc = ble_gap_conn_find(it.first, &desc); ble_gap_conn_desc desc;
if(rc != 0 || !desc.sec_state.encrypted) { if (ble_gap_conn_find(it.first, &desc) != 0 || !desc.sec_state.encrypted) {
continue; continue;
} }
} }
if (length > _mtu - 3) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
}
if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) {
NIMBLE_LOGW(LOG_TAG,
"Sending notification to client subscribed to indications, sending indication instead");
is_notification = false;
}
if(!is_notification && (!(it.second & NIMBLE_SUB_INDICATE))) {
NIMBLE_LOGW(LOG_TAG,
"Sending indication to client subscribed to notification, sending notification instead");
is_notification = true;
}
// don't create the m_buf until we are sure to send the data or else // don't create the m_buf until we are sure to send the data or else
// we could be allocating a buffer that doesn't get released. // we could be allocating a buffer that doesn't get released.
// We also must create it in each loop iteration because it is consumed with each host call. // 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); os_mbuf* om = ble_hs_mbuf_from_flat(value, length);
if (!om) {
NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf");
return;
}
if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) { if (is_notification) {
ble_gattc_notify_custom(it.first, getHandle(), om);
} else {
if (!NimBLEDevice::getServer()->setIndicateWait(it.first)) { if (!NimBLEDevice::getServer()->setIndicateWait(it.first)) {
NIMBLE_LOGE(LOG_TAG, "prior Indication in progress"); NIMBLE_LOGE(LOG_TAG, "<< sendValue: waiting for previous indicate");
os_mbuf_free_chain(om); os_mbuf_free_chain(om);
return; return;
} }
rc = ble_gattc_indicate_custom(it.first, m_handle, om); if (ble_gattc_indicate_custom(it.first, getHandle(), om) != 0) {
if(rc != 0){
NimBLEDevice::getServer()->clearIndicateWait(it.first); NimBLEDevice::getServer()->clearIndicateWait(it.first);
} }
} else {
ble_gattc_notify_custom(it.first, m_handle, om);
} }
} }
NIMBLE_LOGD(LOG_TAG, "<< notify"); NIMBLE_LOGD(LOG_TAG, "<< sendValue");
} // Notify } // sendValue
void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) {
m_pCallbacks->onRead(this, connInfo);
}
void NimBLECharacteristic::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) {
setValue(val, len);
m_pCallbacks->onWrite(this, connInfo);
}
/** /**
* @brief Set the callback handlers for this characteristic. * @brief Set the callback handlers for this characteristic.
@ -462,56 +392,18 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
/** /**
* @brief Get the callback handlers for this characteristic. * @brief Get the callback handlers for this characteristic.
*/ */
NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() { NimBLECharacteristicCallbacks* NimBLECharacteristic::getCallbacks() const {
return m_pCallbacks; return m_pCallbacks;
} // getCallbacks } // getCallbacks
/**
* @brief Set the value of the characteristic.
* @param [in] data The data to set for the characteristic.
* @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)
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);
#endif
if (length > BLE_ATT_ATTR_MAX_LEN) {
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN);
return;
}
time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
m_timestamp = t;
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< setValue");
} // setValue
/**
* @brief Set the value of the characteristic from string data.\n
* We set the value of the characteristic from the bytes contained in the string.
* @param [in] value the std::string value of the characteristic.
*/
void NimBLECharacteristic::setValue(const std::string &value) {
setValue((uint8_t*)(value.data()), value.length());
} // setValue
/** /**
* @brief Return a string representation of the characteristic. * @brief Return a string representation of the characteristic.
* @return A string representation of the characteristic. * @return A string representation of the characteristic.
*/ */
std::string NimBLECharacteristic::toString() { std::string NimBLECharacteristic::toString() const {
std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x"; std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x";
char hex[5]; char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle); snprintf(hex, sizeof(hex), "%04x", getHandle());
res += hex; res += hex;
res += " "; res += " ";
if (m_properties & BLE_GATT_CHR_PROP_READ) res += "Read "; if (m_properties & BLE_GATT_CHR_PROP_READ) res += "Read ";
@ -523,68 +415,39 @@ std::string NimBLECharacteristic::toString() {
return res; return res;
} // toString } // toString
NimBLECharacteristicCallbacks::~NimBLECharacteristicCallbacks() {}
/** /**
* @brief Callback function to support a read request. * @brief Callback function to support a read request.
* @param [in] pCharacteristic The characteristic that is the source of the event. * @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/ */
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic) { void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
} // onRead
/**
* @brief Callback function to support a read request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] desc The connection description struct that is associated with the peer that performed the read.
*/
void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default"); NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
} // onRead } // onRead
/** /**
* @brief Callback function to support a write request. * @brief Callback function to support a write request.
* @param [in] pCharacteristic The characteristic that is the source of the event. * @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/ */
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) { void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default"); NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
} // onWrite } // onWrite
/**
* @brief Callback function to support a write request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] desc The connection description struct that is associated with the peer that performed the write.
*/
void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
} // onWrite
/**
* @brief Callback function to support a Notify request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacteristic) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onNotify: default");
} // onNotify
/** /**
* @brief Callback function to support a Notify/Indicate Status report. * @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event. * @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] s Status of the notification/indication. * @param [in] code Status return code from the NimBLE stack.
* @param [in] code Additional return code from the NimBLE stack. * @details The status code for success is 0 for notifications and BLE_HS_EDONE for indications,
* any other value is an error.
*/ */
void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) { void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, int code) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default"); NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
} // onStatus } // onStatus
/** /**
* @brief Callback function called when a client changes subscription status. * @brief Callback function called when a client changes subscription status.
* @param [in] pCharacteristic The characteristic that is the source of the event. * @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] desc The connection description struct that is associated with the client. * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @param [in] subValue The subscription status: * @param [in] subValue The subscription status:
* * 0 = Un-Subscribed * * 0 = Un-Subscribed
* * 1 = Notifications * * 1 = Notifications
@ -592,12 +455,9 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist
* * 3 = Notifications and Indications * * 3 = Notifications and Indications
*/ */
void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic, void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
ble_gap_conn_desc* desc, NimBLEConnInfo& connInfo,
uint16_t subValue) uint16_t subValue) {
{
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default"); NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
} }
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -11,152 +11,132 @@
* Author: kolban * Author: kolban
*/ */
#ifndef MAIN_NIMBLECHARACTERISTIC_H_ #ifndef NIMBLE_CPP_CHARACTERISTIC_H_
#define MAIN_NIMBLECHARACTERISTIC_H_ #define NIMBLE_CPP_CHARACTERISTIC_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "host/ble_hs.h" class NimBLECharacteristicCallbacks;
/**** FIX COMPILATION ****/ class NimBLECharacteristic;
#undef min
#undef max
/**************************/
typedef enum {
READ = BLE_GATT_CHR_F_READ,
READ_ENC = BLE_GATT_CHR_F_READ_ENC,
READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN,
READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR,
WRITE = BLE_GATT_CHR_F_WRITE,
WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP,
WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC,
WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN,
WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR,
BROADCAST = BLE_GATT_CHR_F_BROADCAST,
NOTIFY = BLE_GATT_CHR_F_NOTIFY,
INDICATE = BLE_GATT_CHR_F_INDICATE
} NIMBLE_PROPERTY;
# include "NimBLELocalValueAttribute.h"
# include "NimBLEServer.h"
# include "NimBLEService.h" # include "NimBLEService.h"
# include "NimBLEDescriptor.h" # include "NimBLEDescriptor.h"
# include "NimBLEAttValue.h"
# include "NimBLEConnInfo.h"
# include <string> # include <string>
# include <vector> # include <vector>
class NimBLEService;
class NimBLEDescriptor;
class NimBLECharacteristicCallbacks;
/** /**
* @brief The model of a %BLE Characteristic. * @brief The model of a BLE Characteristic.
* *
* A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE service and
* can be read and written to by a %BLE client. * can be read and written to by a BLE client.
*/ */
class NimBLECharacteristic { class NimBLECharacteristic : public NimBLELocalValueAttribute {
public: public:
NimBLECharacteristic(const char* uuid, NimBLECharacteristic(const char* uuid,
uint16_t properties = uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
NIMBLE_PROPERTY::READ | uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr); NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID& uuid, NimBLECharacteristic(const NimBLEUUID& uuid,
uint16_t properties = uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
NIMBLE_PROPERTY::READ | uint16_t max_len = BLE_ATT_ATTR_MAX_LEN,
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr); NimBLEService* pService = nullptr);
~NimBLECharacteristic(); ~NimBLECharacteristic();
uint16_t getHandle(); std::string toString() const;
NimBLEUUID getUUID(); size_t getSubscribedCount() const;
std::string toString(); void addDescriptor(NimBLEDescriptor* pDescriptor);
void removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc = false);
uint16_t getProperties() const;
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
NimBLECharacteristicCallbacks* void indicate(uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
getCallbacks(); void indicate(const uint8_t* value, size_t length, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void notify(uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void indicate(); void notify(const uint8_t* value, size_t length, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
void notify(bool is_notification = true);
size_t getSubscribedCount();
NimBLEDescriptor* createDescriptor(const char* uuid, NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties = uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
NIMBLE_PROPERTY::READ | uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100);
NimBLEDescriptor* createDescriptor(const NimBLEUUID& uuid, NimBLEDescriptor* createDescriptor(const NimBLEUUID& uuid,
uint32_t properties = uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE,
NIMBLE_PROPERTY::READ | uint16_t max_len = BLE_ATT_ATTR_MAX_LEN);
NIMBLE_PROPERTY::WRITE, NimBLEDescriptor* getDescriptorByUUID(const char* uuid) const;
uint16_t max_len = 100); NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID& uuid) const;
NimBLEDescriptor* getDescriptorByHandle(uint16_t handle) const;
NimBLEService* getService() const;
void addDescriptor(NimBLEDescriptor *pDescriptor); NimBLECharacteristicCallbacks* getCallbacks() const;
NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid); /*********************** Template Functions ************************/
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\>. * @brief Template to send a notification for classes which may have
* @tparam T The type to convert the data to. * data()/size() or c_str()/length() methods. Falls back to sending
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read. * the data by casting the first element of the array to a uint8_t
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>. * pointer and getting the length of the array using sizeof.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is * @tparam T The a reference to a class containing the data to send.
* less than <tt>sizeof(<type\>)</tt>. * @param[in] value The <type\>value to set.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt> * @param[in] conn_handle The connection handle to send the notification to.
* @note This function is only available if the type T is not a pointer.
*/ */
template <typename T> template <typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) { typename std::enable_if<!std::is_pointer<T>::value, void>::type
std::string value = getValue(); notify(const T& value, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const {
if(!skipSizeCheck && value.size() < sizeof(T)) return T(); if constexpr (Has_data_size<T>::value) {
const char *pData = value.data(); notify(reinterpret_cast<const uint8_t*>(value.data()), value.size(), conn_handle);
return *((T *)pData); } else if constexpr (Has_c_str_length<T>::value) {
notify(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), conn_handle);
} else {
notify(reinterpret_cast<const uint8_t*>(&value), sizeof(value), conn_handle);
}
} }
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. * @brief Template to send an indication for classes which may have
* @param [in] s The value to set. * data()/size() or c_str()/length() methods. Falls back to sending
* the data by casting the first element of the array to a uint8_t
* pointer and getting the length of the array using sizeof.
* @tparam T The a reference to a class containing the data to send.
* @param[in] value The <type\>value to set.
* @param[in] conn_handle The connection handle to send the indication to.
* @note This function is only available if the type T is not a pointer.
*/ */
template <typename T> template <typename T>
void setValue(const T &s) { typename std::enable_if<!std::is_pointer<T>::value, void>::type
setValue((uint8_t*)&s, sizeof(T)); indicate(const T& value, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const {
if constexpr (Has_data_size<T>::value) {
indicate(reinterpret_cast<const uint8_t*>(value.data()), value.size(), conn_handle);
} else if constexpr (Has_c_str_length<T>::value) {
indicate(reinterpret_cast<const uint8_t*>(value.c_str()), value.length(), conn_handle);
} else {
indicate(reinterpret_cast<const uint8_t*>(&value), sizeof(value), conn_handle);
}
} }
NimBLEService* getService();
uint16_t getProperties();
private: private:
friend class NimBLEServer; friend class NimBLEServer;
friend class NimBLEService; friend class NimBLEService;
void setService(NimBLEService* pService); void setService(NimBLEService* pService);
void setSubscribe(struct ble_gap_event *event); void setSubscribe(const ble_gap_event* event, NimBLEConnInfo& connInfo);
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, void readEvent(NimBLEConnInfo& connInfo) override;
struct ble_gatt_access_ctxt *ctxt, void *arg); void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) override;
void sendValue(const uint8_t* value,
size_t length,
bool is_notification = true,
uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const;
NimBLEUUID m_uuid; NimBLECharacteristicCallbacks* m_pCallbacks{nullptr};
uint16_t m_handle; NimBLEService* m_pService{nullptr};
uint16_t m_properties; std::vector<NimBLEDescriptor*> m_vDescriptors{};
NimBLECharacteristicCallbacks* m_pCallbacks; std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec{};
NimBLEService* m_pService;
std::string m_value;
std::vector<NimBLEDescriptor*> m_dscVec;
portMUX_TYPE m_valMux;
time_t m_timestamp;
std::vector<std::pair<uint16_t, uint16_t>> m_subscribedVec;
}; // NimBLECharacteristic }; // NimBLECharacteristic
/** /**
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events. * @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
* *
@ -166,33 +146,12 @@ private:
*/ */
class NimBLECharacteristicCallbacks { class NimBLECharacteristicCallbacks {
public: public:
virtual ~NimBLECharacteristicCallbacks() {}
/** virtual void onRead(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
* @brief An enum to provide the callback the status of the virtual void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
* notification/indication, implemented for backward compatibility. virtual void onStatus(NimBLECharacteristic* pCharacteristic, int code);
* @deprecated To be removed in the future as the NimBLE stack return code is also provided. virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo, uint16_t subValue);
*/
typedef enum {
SUCCESS_INDICATE,
SUCCESS_NOTIFY,
ERROR_INDICATE_DISABLED,
ERROR_NOTIFY_DISABLED,
ERROR_GATT,
ERROR_NO_CLIENT,
ERROR_INDICATE_TIMEOUT,
ERROR_INDICATE_FAILURE
}Status;
virtual ~NimBLECharacteristicCallbacks();
virtual void onRead(NimBLECharacteristic* pCharacteristic);
virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onWrite(NimBLECharacteristic* pCharacteristic);
virtual void onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onNotify(NimBLECharacteristic* pCharacteristic);
virtual void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code);
virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue);
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* CONFIG_BT_ENABLED */ #endif /*NIMBLE_CPP_CHARACTERISTIC_H_*/
#endif /*MAIN_NIMBLECHARACTERISTIC_H_*/

File diff suppressed because it is too large Load Diff

View File

@ -11,102 +11,134 @@
* Author: kolban * Author: kolban
*/ */
#ifndef MAIN_NIMBLECLIENT_H_ #ifndef NIMBLE_CPP_CLIENT_H_
#define MAIN_NIMBLECLIENT_H_ #define NIMBLE_CPP_CLIENT_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
# else
# include "nimble/nimble/host/include/host/ble_gap.h"
# endif
# include "NimBLEAddress.h" # include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include "NimBLEUtils.h"
#include "NimBLEConnInfo.h"
#include "NimBLEAdvertisedDevice.h"
#include "NimBLERemoteService.h"
# include <stdint.h>
# include <vector> # include <vector>
# include <string> # include <string>
class NimBLEAddress;
class NimBLEUUID;
class NimBLERemoteService; class NimBLERemoteService;
class NimBLERemoteCharacteristic; class NimBLERemoteCharacteristic;
class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice; class NimBLEAdvertisedDevice;
class NimBLEAttValue;
class NimBLEClientCallbacks;
class NimBLEConnInfo;
struct NimBLETaskData;
/** /**
* @brief A model of a %BLE client. * @brief A model of a BLE client.
*/ */
class NimBLEClient { class NimBLEClient {
public: public:
bool connect(NimBLEAdvertisedDevice* device, bool deleteAttibutes = true); bool connect(NimBLEAdvertisedDevice* device,
bool connect(const NimBLEAddress &address, bool deleteAttibutes = true); bool deleteAttributes = true,
bool connect(bool deleteAttibutes = true); bool asyncConnect = false,
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); bool exchangeMTU = true);
NimBLEAddress getPeerAddress(); bool connect(const NimBLEAddress& address, bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
void setPeerAddress(const NimBLEAddress &address); bool connect(bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
int getRssi(); bool disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
std::vector<NimBLERemoteService*>* getServices(bool refresh = false); bool cancelConnect() const;
void setSelfDelete(bool deleteOnDisconnect, bool deleteOnConnectFail);
NimBLEAddress getPeerAddress() const;
bool setPeerAddress(const NimBLEAddress& address);
int getRssi() const;
bool isConnected() const;
void setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks = true);
std::string toString() const;
uint16_t getConnHandle() const;
void clearConnection();
bool setConnection(const NimBLEConnInfo& connInfo);
bool setConnection(uint16_t connHandle);
uint16_t getMTU() const;
bool exchangeMTU();
bool secureConnection() const;
void setConnectTimeout(uint32_t timeout);
bool setDataLen(uint16_t txOctets);
bool discoverAttributes();
NimBLEConnInfo getConnInfo() const;
int getLastError() const;
bool updateConnParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout);
void setConnectionParams(uint16_t minInterval,
uint16_t maxInterval,
uint16_t latency,
uint16_t timeout,
uint16_t scanInterval = 16,
uint16_t scanWindow = 16);
const std::vector<NimBLERemoteService*>& getServices(bool refresh = false);
std::vector<NimBLERemoteService*>::iterator begin(); std::vector<NimBLERemoteService*>::iterator begin();
std::vector<NimBLERemoteService*>::iterator end(); std::vector<NimBLERemoteService*>::iterator end();
NimBLERemoteCharacteristic* getCharacteristic(uint16_t handle);
NimBLERemoteService* getService(const char* uuid); NimBLERemoteService* getService(const char* uuid);
NimBLERemoteService* getService(const NimBLEUUID& uuid); NimBLERemoteService* getService(const NimBLEUUID& uuid);
void deleteServices(); void deleteServices();
size_t deleteService(const NimBLEUUID& uuid); size_t deleteService(const NimBLEUUID& uuid);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID); NimBLEAttValue getValue(const NimBLEUUID& serviceUUID, const NimBLEUUID& characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, bool setValue(const NimBLEUUID& serviceUUID,
const std::string &value, bool response = false); const NimBLEUUID& characteristicUUID,
NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle); const NimBLEAttValue& value,
bool isConnected(); bool response = false);
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
bool deleteCallbacks = true); # if CONFIG_BT_NIMBLE_EXT_ADV
std::string toString(); void setConnectPhy(uint8_t mask);
uint16_t getConnId(); # endif
uint16_t getMTU();
bool secureConnection(); struct Config {
void setConnectTimeout(uint8_t timeout); uint8_t deleteCallbacks : 1; // Delete the callback object when the client is deleted.
void setConnectionParams(uint16_t minInterval, uint16_t maxInterval, uint8_t deleteOnDisconnect : 1; // Delete the client when disconnected.
uint16_t latency, uint16_t timeout, uint8_t deleteOnConnectFail : 1; // Delete the client when a connection attempt fails.
uint16_t scanInterval=16, uint16_t scanWindow=16); uint8_t asyncConnect : 1; // Connect asynchronously.
void updateConnParams(uint16_t minInterval, uint16_t maxInterval, uint8_t exchangeMTU : 1; // Exchange MTU after connection.
uint16_t latency, uint16_t timeout); };
void discoverAttributes();
NimBLEConnInfo getConnInfo(); Config getConfig() const;
void setConfig(Config config);
private: private:
NimBLEClient(const NimBLEAddress& peerAddress); NimBLEClient(const NimBLEAddress& peerAddress);
~NimBLEClient(); ~NimBLEClient();
NimBLEClient(const NimBLEClient&) = delete;
NimBLEClient& operator=(const NimBLEClient&) = delete;
friend class NimBLEDevice; bool retrieveServices(const NimBLEUUID* uuidFilter = nullptr);
friend class NimBLERemoteService;
static int handleGapEvent(struct ble_gap_event* event, void* arg); static int handleGapEvent(struct ble_gap_event* event, void* arg);
static int serviceDiscoveredCB(uint16_t conn_handle, static int exchangeMTUCb(uint16_t conn_handle, const ble_gatt_error* error, uint16_t mtu, void* arg);
static int serviceDiscoveredCB(uint16_t connHandle,
const struct ble_gatt_error* error, const struct ble_gatt_error* error,
const struct ble_gatt_svc* service, const struct ble_gatt_svc* service,
void* arg); void* arg);
static void dcTimerCb(ble_npl_event *event);
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress; NimBLEAddress m_peerAddress;
uint16_t m_conn_id; mutable int m_lastErr;
bool m_connEstablished;
bool m_deleteCallbacks;
int32_t m_connectTimeout; int32_t m_connectTimeout;
mutable NimBLETaskData* m_pTaskData;
std::vector<NimBLERemoteService*> m_svcVec;
NimBLEClientCallbacks* m_pClientCallbacks; NimBLEClientCallbacks* m_pClientCallbacks;
ble_task_data_t* m_pTaskData; uint16_t m_connHandle;
ble_npl_callout m_dcTimer; uint8_t m_terminateFailCount;
Config m_config;
std::vector<NimBLERemoteService*> m_servicesVector; # if CONFIG_BT_NIMBLE_EXT_ADV
uint8_t m_phyMask;
private: # endif
friend class NimBLEClientCallbacks; ble_gap_conn_params m_connParams;
ble_gap_conn_params m_pConnParams;
friend class NimBLEDevice;
}; // class NimBLEClient }; // class NimBLEClient
/** /**
* @brief Callbacks associated with a %BLE client. * @brief Callbacks associated with a %BLE client.
*/ */
@ -123,41 +155,52 @@ public:
/** /**
* @brief Called when disconnected from the server. * @brief Called when disconnected from the server.
* @param [in] pClient A pointer to the calling client object. * @param [in] pClient A pointer to the calling client object.
* @param [in] reason Contains the reason code for the disconnection.
*/ */
virtual void onDisconnect(NimBLEClient* pClient); virtual void onDisconnect(NimBLEClient* pClient, int reason);
/** /**
* @brief Called when server requests to update the connection parameters. * @brief Called when server requests to update the connection parameters.
* @param [in] pClient A pointer to the calling client object. * @param [in] pClient A pointer to the calling client object.
* @param [in] params A pointer to the struct containing the connection parameters requested. * @param [in] params A pointer to the struct containing the connection parameters requested.
* @return True to accept the parmeters. * @return True to accept the parameters.
*/ */
virtual bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params); virtual bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params);
/** /**
* @brief Called when server requests a passkey for pairing. * @brief Called when server requests a passkey for pairing.
* @return The passkey to be sent to the server. * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/ */
virtual uint32_t onPassKeyRequest(); virtual void onPassKeyEntry(NimBLEConnInfo& connInfo);
/*virtual void onPassKeyNotify(uint32_t pass_key);
virtual bool onSecurityRequest();*/
/** /**
* @brief Called when the pairing procedure is complete. * @brief Called when the pairing procedure is complete.
* @param [in] desc A pointer to the struct containing the connection information.\n * @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.\n
* This can be used to check the status of the connection encryption/pairing. * This can be used to check the status of the connection encryption/pairing.
*/ */
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc); virtual void onAuthenticationComplete(NimBLEConnInfo& connInfo);
/** /**
* @brief Called when using numeric comparision for pairing. * @brief Called when using numeric comparision for pairing.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @param [in] pin The pin to compare with the server. * @param [in] pin The pin to compare with the server.
* @return True to accept the pin.
*/ */
virtual bool onConfirmPIN(uint32_t pin); virtual void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pin);
/**
* @brief Called when the peer identity address is resolved.
* @param [in] connInfo A reference to a NimBLEConnInfo instance with information
*/
virtual void onIdentity(NimBLEConnInfo& connInfo);
/**
* @brief Called when the connection MTU changes.
* @param [in] pClient A pointer to the client that the MTU change is associated with.
* @param [in] MTU The new MTU value.
* about the peer connection parameters.
*/
virtual void onMTUChange(NimBLEClient* pClient, uint16_t MTU);
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // CONFIG_BT_ENABLED #endif /* NIMBLE_CPP_CLIENT_H_ */
#endif /* MAIN_NIMBLECLIENT_H_ */

View File

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

View File

@ -11,11 +11,9 @@
* Created on: Jun 22, 2017 * Created on: Jun 22, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLEService.h" # include "NimBLEService.h"
# include "NimBLEDescriptor.h" # include "NimBLEDescriptor.h"
@ -23,180 +21,72 @@
# include <string> # include <string>
#define NULL_HANDLE (0xffff)
static const char* LOG_TAG = "NimBLEDescriptor"; static const char* LOG_TAG = "NimBLEDescriptor";
static NimBLEDescriptorCallbacks defaultCallbacks; static NimBLEDescriptorCallbacks defaultCallbacks;
/**
* @brief Construct a descriptor
* @param [in] uuid - UUID (const char*) for the descriptor.
* @param [in] properties - Properties for the descriptor.
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
*/
NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic)
: NimBLEDescriptor(NimBLEUUID(uuid), properties, max_len, pCharacteristic) {}
/** /**
* @brief NimBLEDescriptor constructor. * @brief Construct a descriptor
* @param [in] uuid - UUID (const char*) for the descriptor.
* @param [in] properties - Properties for the descriptor.
* @param [in] max_len - The maximum length in bytes that the descriptor value can hold. (Default: 512 bytes for esp32, 20 for all others).
* @param [in] pCharacteristic - pointer to the characteristic instance this descriptor belongs to.
*/ */
NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, NimBLEDescriptor::NimBLEDescriptor(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic)
NimBLECharacteristic* pCharacteristic) : NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallbacks}, m_pCharacteristic{pCharacteristic} {
: NimBLEDescriptor(NimBLEUUID(uuid), max_len, properties, pCharacteristic) { // Check if this is the client configuration descriptor and set to removed if true.
if (uuid == NimBLEUUID((uint16_t)0x2902)) {
NIMBLE_LOGW(LOG_TAG, "Manually created 2902 descriptor has no functionality; please remove.");
setRemoved(NIMBLE_ATT_REMOVE_HIDE);
} }
// convert uint16_t properties to uint8_t for descriptor properties
/** uint8_t descProperties = 0;
* @brief NimBLEDescriptor constructor. if (properties & NIMBLE_PROPERTY::READ) {
*/ descProperties |= BLE_ATT_F_READ;
NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len,
NimBLECharacteristic* pCharacteristic)
{
m_uuid = uuid;
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 = 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;
m_properties = 0;
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
m_properties |= BLE_ATT_F_READ;
} }
if (properties & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) { if (properties & (NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::WRITE)) {
m_properties |= BLE_ATT_F_WRITE; descProperties |= BLE_ATT_F_WRITE;
} }
if (properties & BLE_GATT_CHR_F_READ_ENC) { if (properties & NIMBLE_PROPERTY::READ_ENC) {
m_properties |= BLE_ATT_F_READ_ENC; descProperties |= BLE_ATT_F_READ_ENC;
} }
if (properties & BLE_GATT_CHR_F_READ_AUTHEN) { if (properties & NIMBLE_PROPERTY::READ_AUTHEN) {
m_properties |= BLE_ATT_F_READ_AUTHEN; descProperties |= BLE_ATT_F_READ_AUTHEN;
} }
if (properties & BLE_GATT_CHR_F_READ_AUTHOR) { if (properties & NIMBLE_PROPERTY::READ_AUTHOR) {
m_properties |= BLE_ATT_F_READ_AUTHOR; descProperties |= BLE_ATT_F_READ_AUTHOR;
} }
if (properties & BLE_GATT_CHR_F_WRITE_ENC) { if (properties & NIMBLE_PROPERTY::WRITE_ENC) {
m_properties |= BLE_ATT_F_WRITE_ENC; descProperties |= BLE_ATT_F_WRITE_ENC;
} }
if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) { if (properties & NIMBLE_PROPERTY::WRITE_AUTHEN) {
m_properties |= BLE_ATT_F_WRITE_AUTHEN; descProperties |= BLE_ATT_F_WRITE_AUTHEN;
} }
if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) { if (properties & NIMBLE_PROPERTY::WRITE_AUTHOR) {
m_properties |= BLE_ATT_F_WRITE_AUTHOR; descProperties |= BLE_ATT_F_WRITE_AUTHOR;
} }
setProperties(descProperties);
} // NimBLEDescriptor } // NimBLEDescriptor
/**
* @brief NimBLEDescriptor destructor.
*/
NimBLEDescriptor::~NimBLEDescriptor() {
free(m_value.attr_value); // Release the storage we created in the constructor.
} // ~NimBLEDescriptor
/**
* @brief Get the BLE handle for this descriptor.
* @return The handle for this descriptor.
*/
uint16_t NimBLEDescriptor::getHandle() {
return m_handle;
} // getHandle
/**
* @brief Get the length of the value of this descriptor.
* @return The length (in bytes) of the value of this descriptor.
*/
size_t NimBLEDescriptor::getLength() {
return m_value.attr_len;
} // getLength
/**
* @brief Get the UUID of the descriptor.
*/
NimBLEUUID NimBLEDescriptor::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Get the value of this descriptor.
* @return A pointer to the value of this descriptor.
*/
uint8_t* NimBLEDescriptor::getValue() {
return m_value.attr_value;
} // 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. * @brief Get the characteristic this descriptor belongs to.
* @return A pointer to the characteristic this descriptor belongs to. * @return A pointer to the characteristic this descriptor belongs to.
*/ */
NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() { NimBLECharacteristic* NimBLEDescriptor::getCharacteristic() const {
return m_pCharacteristic; return m_pCharacteristic;
} // getCharacteristic } // getCharacteristic
int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) {
const ble_uuid_t *uuid;
int rc;
NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg;
NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(),
ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC ? "Read" : "Write");
uuid = ctxt->chr->uuid;
if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){
switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC: {
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8) {
pDescriptor->m_pCallbacks->onRead(pDescriptor);
}
portENTER_CRITICAL(&pDescriptor->m_valMux);
rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength());
portEXIT_CRITICAL(&pDescriptor->m_valMux);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_DSC: {
if (ctxt->om->om_len > pDescriptor->m_value.attr_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
uint8_t buf[pDescriptor->m_value.attr_max_len];
size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){
if((len + next->om_len) > pDescriptor->m_value.attr_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
pDescriptor->setValue(buf, len);
pDescriptor->m_pCallbacks->onWrite(pDescriptor);
return 0;
}
default:
break;
}
}
return BLE_ATT_ERR_UNLIKELY;
}
/** /**
* @brief Set the callback handlers for this descriptor. * @brief Set the callback handlers for this descriptor.
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
@ -209,84 +99,50 @@ void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) {
} }
} // setCallbacks } // setCallbacks
/**
* @brief Set the handle of this descriptor.
* Set the handle of this descriptor to be the supplied value.
* @param [in] handle The handle to be associated with this descriptor.
* @return N/A.
*/
void NimBLEDescriptor::setHandle(uint16_t handle) {
NIMBLE_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle);
m_handle = handle;
NIMBLE_LOGD(LOG_TAG, "<< setHandle()");
} // setHandle
/**
* @brief Set the value of the descriptor.
* @param [in] data The data to set for the descriptor.
* @param [in] length The length of the data in bytes.
*/
void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
if (length > m_value.attr_max_len) {
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, m_value.attr_max_len);
return;
}
portENTER_CRITICAL(&m_valMux);
m_value.attr_len = length;
memcpy(m_value.attr_value, data, length);
portEXIT_CRITICAL(&m_valMux);
} // setValue
/**
* @brief Set the value of the descriptor.
* @param [in] value The value of the descriptor in string form.
*/
void NimBLEDescriptor::setValue(const std::string &value) {
setValue((uint8_t*) value.data(), value.length());
} // setValue
/** /**
* @brief Set the characteristic this descriptor belongs to. * @brief Set the characteristic this descriptor belongs to.
* @param [in] pChar A pointer to the characteristic this descriptior belongs to. * @param [in] pChar A pointer to the characteristic this descriptor belongs to.
*/ */
void NimBLEDescriptor::setCharacteristic(NimBLECharacteristic* pChar) { void NimBLEDescriptor::setCharacteristic(NimBLECharacteristic* pChar) {
m_pCharacteristic = pChar; m_pCharacteristic = pChar;
} // setCharacteristic } // setCharacteristic
/** /**
* @brief Return a string representation of the descriptor. * @brief Return a string representation of the descriptor.
* @return A string representation of the descriptor. * @return A string representation of the descriptor.
*/ */
std::string NimBLEDescriptor::toString() { std::string NimBLEDescriptor::toString() const {
char hex[5]; char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle); snprintf(hex, sizeof(hex), "%04x", getHandle());
std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex; std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex;
return res; return res;
} // toString } // toString
void NimBLEDescriptor::readEvent(NimBLEConnInfo& connInfo) {
m_pCallbacks->onRead(this, connInfo);
} // readEvent
NimBLEDescriptorCallbacks::~NimBLEDescriptorCallbacks() {} void NimBLEDescriptor::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) {
setValue(val, len);
m_pCallbacks->onWrite(this, connInfo);
} // writeEvent
/** /**
* @brief Callback function to support a read request. * @brief Callback function to support a read request.
* @param [in] pDescriptor The descriptor that is the source of the event. * @param [in] pDescriptor The descriptor that is the source of the event.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/ */
void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) { void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default"); NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default");
} // onRead } // onRead
/** /**
* @brief Callback function to support a write request. * @brief Callback function to support a write request.
* @param [in] pDescriptor The descriptor that is the source of the event. * @param [in] pDescriptor The descriptor that is the source of the event.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
*/ */
void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor) { void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo) {
NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default"); NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default");
} // onWrite } // onWrite
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* CONFIG_BT_ENABLED */

View File

@ -12,90 +12,52 @@
* Author: kolban * Author: kolban
*/ */
#ifndef MAIN_NIMBLEDESCRIPTOR_H_ #ifndef NIMBLE_CPP_DESCRIPTOR_H_
#define MAIN_NIMBLEDESCRIPTOR_H_ #define NIMBLE_CPP_DESCRIPTOR_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
class NimBLEDescriptor;
class NimBLEDescriptorCallbacks;
# include "NimBLELocalValueAttribute.h"
# include "NimBLECharacteristic.h" # include "NimBLECharacteristic.h"
# include "NimBLEUUID.h" # include "NimBLEUUID.h"
# include "NimBLEAttValue.h"
# include "NimBLEConnInfo.h"
# include <string> # include <string>
typedef struct
{
uint16_t attr_max_len; /*!< attribute max value length */
uint16_t attr_len; /*!< attribute current value length */
uint8_t *attr_value; /*!< the pointer to attribute value */
} attr_value_t;
class NimBLEService;
class NimBLECharacteristic;
class NimBLEDescriptorCallbacks;
/** /**
* @brief A model of a %BLE descriptor. * @brief A model of a BLE descriptor.
*/ */
class NimBLEDescriptor { class NimBLEDescriptor : public NimBLELocalValueAttribute {
public: public:
NimBLEDescriptor(const char* uuid, uint16_t properties, NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic = nullptr);
NimBLEDescriptor(const NimBLEUUID& uuid,
uint16_t properties,
uint16_t max_len, uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr); NimBLECharacteristic* pCharacteristic = nullptr);
~NimBLEDescriptor() = default;
NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, std::string toString() const;
uint16_t max_len,
NimBLECharacteristic* pCharacteristic = nullptr);
~NimBLEDescriptor();
uint16_t getHandle();
NimBLEUUID getUUID();
std::string toString();
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
NimBLECharacteristic* getCharacteristic() const;
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.
* @param [in] s The value to set.
*/
template<typename T>
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
private: private:
friend class NimBLECharacteristic; friend class NimBLECharacteristic;
friend class NimBLEService; friend class NimBLEService;
friend class NimBLE2904;
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); void setCharacteristic(NimBLECharacteristic* pChar);
void readEvent(NimBLEConnInfo& connInfo) override;
void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) override;
NimBLEUUID m_uuid; NimBLEDescriptorCallbacks* m_pCallbacks{nullptr};
uint16_t m_handle; NimBLECharacteristic* m_pCharacteristic{nullptr};
NimBLEDescriptorCallbacks* m_pCallbacks;
NimBLECharacteristic* m_pCharacteristic;
uint8_t m_properties;
attr_value_t m_value;
portMUX_TYPE m_valMux;
}; // NimBLEDescriptor }; // NimBLEDescriptor
/** /**
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events. * @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
* *
@ -105,13 +67,12 @@ private:
*/ */
class NimBLEDescriptorCallbacks { class NimBLEDescriptorCallbacks {
public: public:
virtual ~NimBLEDescriptorCallbacks(); virtual ~NimBLEDescriptorCallbacks() = default;
virtual void onRead(NimBLEDescriptor* pDescriptor); virtual void onRead(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
virtual void onWrite(NimBLEDescriptor* pDescriptor); virtual void onWrite(NimBLEDescriptor* pDescriptor, NimBLEConnInfo& connInfo);
}; };
# include "NimBLE2904.h" # include "NimBLE2904.h"
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif /* CONFIG_BT_ENABLED */ #endif /* NIMBLE_CPP_DESCRIPTOR_H_ */
#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -12,38 +12,55 @@
* Author: kolban * Author: kolban
*/ */
#ifndef MAIN_NIMBLEDEVICE_H_ #ifndef NIMBLE_CPP_DEVICE_H_
#define MAIN_NIMBLEDEVICE_H_ #define NIMBLE_CPP_DEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED)
# ifdef ESP_PLATFORM
# include <esp_bt.h>
# endif
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include <host/ble_gap.h>
# else
# include <nimble/nimble/host/include/host/ble_gap.h>
# endif
/**** FIX COMPILATION ****/
# undef min
# undef max
/**************************/
# include <string>
# include <vector>
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include <array>
class NimBLEClient;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) # if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEScan.h" class NimBLEScan;
# endif # endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) # if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLEAdvertising.h" # if CONFIG_BT_NIMBLE_EXT_ADV
class NimBLEExtAdvertising;
# else
class NimBLEAdvertising;
# endif # endif
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
# endif # endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) # if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h" class NimBLEServer;
# endif # endif
#include "NimBLEUtils.h" # if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLESecurity.h" class NimBLEConnInfo;
#include "NimBLEAddress.h" # endif
#include "esp_bt.h" class NimBLEAddress;
#include <map>
#include <string>
#include <list>
# define BLEDevice NimBLEDevice # define BLEDevice NimBLEDevice
# define BLEClient NimBLEClient # define BLEClient NimBLEClient
@ -53,12 +70,10 @@
# define BLEAdvertisedDevice NimBLEAdvertisedDevice # define BLEAdvertisedDevice NimBLEAdvertisedDevice
# define BLEScan NimBLEScan # define BLEScan NimBLEScan
# define BLEUUID NimBLEUUID # define BLEUUID NimBLEUUID
#define BLESecurity NimBLESecurity
#define BLESecurityCallbacks NimBLESecurityCallbacks
# define BLEAddress NimBLEAddress # define BLEAddress NimBLEAddress
# define BLEUtils NimBLEUtils # define BLEUtils NimBLEUtils
# define BLEClientCallbacks NimBLEClientCallbacks # define BLEClientCallbacks NimBLEClientCallbacks
#define BLEAdvertisedDeviceCallbacks NimBLEAdvertisedDeviceCallbacks # define BLEAdvertisedDeviceCallbacks NimBLEScanCallbacks
# define BLEScanResults NimBLEScanResults # define BLEScanResults NimBLEScanResults
# define BLEServer NimBLEServer # define BLEServer NimBLEServer
# define BLEService NimBLEService # define BLEService NimBLEService
@ -68,12 +83,12 @@
# define BLECharacteristicCallbacks NimBLECharacteristicCallbacks # define BLECharacteristicCallbacks NimBLECharacteristicCallbacks
# define BLEAdvertisementData NimBLEAdvertisementData # define BLEAdvertisementData NimBLEAdvertisementData
# define BLEDescriptor NimBLEDescriptor # define BLEDescriptor NimBLEDescriptor
#define BLE2902 NimBLE2902
# define BLE2904 NimBLE2904 # define BLE2904 NimBLE2904
# define BLEDescriptorCallbacks NimBLEDescriptorCallbacks # define BLEDescriptorCallbacks NimBLEDescriptorCallbacks
# define BLEBeacon NimBLEBeacon # define BLEBeacon NimBLEBeacon
# define BLEEddystoneTLM NimBLEEddystoneTLM # define BLEEddystoneTLM NimBLEEddystoneTLM
# define BLEEddystoneURL NimBLEEddystoneURL # define BLEEddystoneURL NimBLEEddystoneURL
# define BLEConnInfo NimBLEConnInfo
# ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS # ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
# define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS # define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS
@ -83,16 +98,15 @@
typedef int (*gap_event_handler)(ble_gap_event* event, void* arg); typedef int (*gap_event_handler)(ble_gap_event* event, void* arg);
extern "C" void ble_store_config_init(void);
/** /**
* @brief A model of a %BLE Device from which all the BLE roles are created. * @brief A model of a BLE Device from which all the BLE roles are created.
*/ */
class NimBLEDevice { class NimBLEDevice {
public: public:
static void init(const std::string &deviceName); static bool init(const std::string& deviceName);
static void deinit(bool clearAll = false); static bool deinit(bool clearAll = false);
static bool getInitialized(); static bool setDeviceName(const std::string& deviceName);
static bool isInitialized();
static NimBLEAddress getAddress(); static NimBLEAddress getAddress();
static std::string toString(); static std::string toString();
static bool whiteListAdd(const NimBLEAddress& address); static bool whiteListAdd(const NimBLEAddress& address);
@ -100,6 +114,30 @@ public:
static bool onWhiteList(const NimBLEAddress& address); static bool onWhiteList(const NimBLEAddress& address);
static size_t getWhiteListCount(); static size_t getWhiteListCount();
static NimBLEAddress getWhiteListAddress(size_t index); static NimBLEAddress getWhiteListAddress(size_t index);
static bool setPower(int8_t dbm);
static int getPower();
static bool setOwnAddrType(uint8_t type);
static bool setOwnAddr(const NimBLEAddress& addr);
static bool setOwnAddr(const uint8_t* addr);
static void setScanDuplicateCacheSize(uint16_t cacheSize);
static void setScanFilterMode(uint8_t type);
static bool setCustomGapHandler(gap_event_handler handler);
static void setSecurityAuth(bool bonding, bool mitm, bool sc);
static void setSecurityAuth(uint8_t auth);
static void setSecurityIOCap(uint8_t iocap);
static void setSecurityInitKey(uint8_t initKey);
static void setSecurityRespKey(uint8_t respKey);
static void setSecurityPasskey(uint32_t passKey);
static uint32_t getSecurityPasskey();
static bool startSecurity(uint16_t connHandle);
static bool setMTU(uint16_t mtu);
static uint16_t getMTU();
static bool isIgnored(const NimBLEAddress& address);
static void addIgnored(const NimBLEAddress& address);
static void removeIgnored(const NimBLEAddress& address);
static void onReset(int reason);
static void onSync(void);
static void host_task(void* param);
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) # if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
static NimBLEScan* getScan(); static NimBLEScan* getScan();
@ -110,52 +148,80 @@ public:
static NimBLEServer* getServer(); static NimBLEServer* getServer();
# endif # endif
static void setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); # if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static int getPower(esp_ble_power_type_t powerType=ESP_BLE_PWR_TYPE_DEFAULT); static bool injectConfirmPasskey(const NimBLEConnInfo& peerInfo, bool accept);
static void setCustomGapHandler(gap_event_handler handler); static bool injectPassKey(const NimBLEConnInfo& peerInfo, uint32_t pin);
static void setSecurityAuth(bool bonding, bool mitm, bool sc); # endif
static void setSecurityAuth(uint8_t auth_req);
static void setSecurityIOCap(uint8_t iocap);
static void setSecurityInitKey(uint8_t init_key);
static void setSecurityRespKey(uint8_t init_key);
static void setSecurityPasskey(uint32_t pin);
static uint32_t getSecurityPasskey();
static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks);
static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false);
static int startSecurity(uint16_t conn_id);
static int setMTU(uint16_t mtu);
static uint16_t getMTU();
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) # if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
static NimBLEExtAdvertising* getAdvertising();
static bool startAdvertising(uint8_t instId, int duration = 0, int maxEvents = 0);
static bool stopAdvertising(uint8_t instId);
static bool stopAdvertising();
# endif
# if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
static NimBLEAdvertising* getAdvertising(); static NimBLEAdvertising* getAdvertising();
static void startAdvertising(); static bool startAdvertising(uint32_t duration = 0);
static void stopAdvertising(); static bool stopAdvertising();
# endif
# endif # endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) # if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static NimBLEClient* createClient(NimBLEAddress peerAddress = NimBLEAddress("")); static NimBLEClient* createClient();
static NimBLEClient* createClient(const NimBLEAddress& peerAddress);
static bool deleteClient(NimBLEClient* pClient); static bool deleteClient(NimBLEClient* pClient);
static NimBLEClient* getClientByID(uint16_t conn_id); static NimBLEClient* getClientByHandle(uint16_t connHandle);
static NimBLEClient* getClientByPeerAddress(const NimBLEAddress &peer_addr); static NimBLEClient* getClientByPeerAddress(const NimBLEAddress& peerAddress);
static NimBLEClient* getDisconnectedClient(); static NimBLEClient* getDisconnectedClient();
static size_t getClientListSize(); static size_t getCreatedClientCount();
static std::list<NimBLEClient*>* getClientList(); static std::vector<NimBLEClient*> getConnectedClients();
# endif # endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) # if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) || defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
static bool deleteBond(const NimBLEAddress& address); static bool deleteBond(const NimBLEAddress& address);
static int getNumBonds(); static int getNumBonds();
static bool isBonded(const NimBLEAddress& address); static bool isBonded(const NimBLEAddress& address);
static void deleteAllBonds(); static bool deleteAllBonds();
static NimBLEAddress getBondedAddress(int index); static NimBLEAddress getBondedAddress(int index);
# endif # endif
private: private:
static bool m_synced;
static bool m_initialized;
static std::vector<NimBLEAddress> m_ignoreList;
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
static uint8_t m_ownAddrType;
static std::vector<NimBLEAddress> m_whiteList;
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
static NimBLEScan* m_pScan;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
static NimBLEServer* m_pServer;
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
# if CONFIG_BT_NIMBLE_EXT_ADV
static NimBLEExtAdvertising* m_bleAdvertising;
# else
static NimBLEAdvertising* m_bleAdvertising;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static std::array<NimBLEClient*, NIMBLE_MAX_CONNECTIONS> m_pClients;
# endif
# ifdef ESP_PLATFORM
# ifdef CONFIG_BTDM_BLE_SCAN_DUPL
static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode;
# endif
# endif
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) # if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
friend class NimBLEClient; friend class NimBLEClient;
# endif # endif
@ -171,39 +237,38 @@ private:
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) # if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
friend class NimBLEAdvertising; friend class NimBLEAdvertising;
# if CONFIG_BT_NIMBLE_EXT_ADV
friend class NimBLEExtAdvertising;
friend class NimBLEExtAdvertisement;
# endif
# endif
};
# if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include "NimBLEClient.h"
# include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLERemoteDescriptor.h"
# endif # endif
static void onReset(int reason);
static void onSync(void);
static void host_task(void *param);
static bool m_synced;
# if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) # if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
static NimBLEScan* m_pScan; # include "NimBLEScan.h"
# endif # endif
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) # if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
static NimBLEServer* m_pServer; # include "NimBLEServer.h"
# include "NimBLEService.h"
# include "NimBLECharacteristic.h"
# include "NimBLEDescriptor.h"
# endif # endif
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) # if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
static NimBLEAdvertising* m_bleAdvertising; # if CONFIG_BT_NIMBLE_EXT_ADV
# include "NimBLEExtAdvertising.h"
# else
# include "NimBLEAdvertising.h"
# endif # endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
static std::list <NimBLEClient*> m_cList;
# endif # endif
static std::list <NimBLEAddress> m_ignoreList;
static NimBLESecurityCallbacks* m_securityCallbacks;
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler;
static uint8_t m_own_addr_type;
static uint16_t m_scanDuplicateSize;
static uint8_t m_scanFilterMode;
static std::vector<NimBLEAddress> m_whiteList;
};
#endif // CONFIG_BT_ENABLED #endif // CONFIG_BT_ENABLED
#endif // MAIN_NIMBLEDEVICE_H_ #endif // NIMBLE_CPP_DEVICE_H_

View File

@ -11,12 +11,14 @@
* Created on: Mar 12, 2018 * Created on: Mar 12, 2018
* Author: pcbreflux * Author: pcbreflux
*/ */
#include "sdkconfig.h"
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include "NimBLEEddystoneTLM.h" #include "NimBLEEddystoneTLM.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <stdio.h>
#include <cstring> #include <cstring>
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
@ -79,12 +81,12 @@ uint16_t NimBLEEddystoneTLM::getVolt() {
* @return The temperature value. * @return The temperature value.
*/ */
float NimBLEEddystoneTLM::getTemp() { float NimBLEEddystoneTLM::getTemp() {
return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f; return (int16_t)ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f;
} // getTemp } // getTemp
/** /**
* @brief Get the count of advertisments sent. * @brief Get the count of advertisements sent.
* @return The number of advertisments. * @return The number of advertisements.
*/ */
uint32_t NimBLEEddystoneTLM::getCount() { uint32_t NimBLEEddystoneTLM::getCount() {
return ENDIAN_CHANGE_U32(m_eddystoneData.advCount); return ENDIAN_CHANGE_U32(m_eddystoneData.advCount);
@ -92,8 +94,8 @@ uint32_t NimBLEEddystoneTLM::getCount() {
/** /**
* @brief Get the advertisment time. * @brief Get the advertisement time.
* @return The advertisment time. * @return The advertisement time.
*/ */
uint32_t NimBLEEddystoneTLM::getTime() { uint32_t NimBLEEddystoneTLM::getTime() {
return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10; return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10;
@ -124,30 +126,30 @@ std::string NimBLEEddystoneTLM::toString() {
out += " C\n"; out += " C\n";
out += "Adv. Count "; out += "Adv. Count ";
snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U32(m_eddystoneData.advCount)); snprintf(val, sizeof(val), "%" PRIu32, ENDIAN_CHANGE_U32(m_eddystoneData.advCount));
out += val; out += val;
out += "\n"; out += "\n";
out += "Time in seconds "; out += "Time in seconds ";
snprintf(val, sizeof(val), "%d", rawsec/10); snprintf(val, sizeof(val), "%" PRIu32, rawsec/10);
out += val; out += val;
out += "\n"; out += "\n";
out += "Time "; out += "Time ";
snprintf(val, sizeof(val), "%04d", rawsec / 864000); snprintf(val, sizeof(val), "%04" PRIu32, rawsec / 864000);
out += val; out += val;
out += "."; out += ".";
snprintf(val, sizeof(val), "%02d", (rawsec / 36000) % 24); snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 36000) % 24);
out += val; out += val;
out += ":"; out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 600) % 60); snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 600) % 60);
out += val; out += val;
out += ":"; out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 10) % 60); snprintf(val, sizeof(val), "%02" PRIu32, (rawsec / 10) % 60);
out += val; out += val;
out += "\n"; out += "\n";
@ -156,7 +158,7 @@ std::string NimBLEEddystoneTLM::toString() {
/** /**
* @brief Set the raw data for the beacon advertisment. * @brief Set the raw data for the beacon advertisement.
* @param [in] data The raw data to advertise. * @param [in] data The raw data to advertise.
*/ */
void NimBLEEddystoneTLM::setData(const std::string &data) { void NimBLEEddystoneTLM::setData(const std::string &data) {
@ -174,7 +176,11 @@ void NimBLEEddystoneTLM::setData(const std::string &data) {
* @param [in] l_uuid The UUID. * @param [in] l_uuid The UUID.
*/ */
void NimBLEEddystoneTLM::setUUID(const NimBLEUUID &l_uuid) { void NimBLEEddystoneTLM::setUUID(const NimBLEUUID &l_uuid) {
beaconUUID = l_uuid.getNative()->u16.value; if (l_uuid.bitSize() != 16) {
NIMBLE_LOGE(LOG_TAG, "UUID must be 16 bits");
return;
}
beaconUUID = *reinterpret_cast<const uint16_t*>(l_uuid.getValue());
} // setUUID } // setUUID
@ -201,13 +207,13 @@ void NimBLEEddystoneTLM::setVolt(uint16_t volt) {
* @param [in] temp The temperature value. * @param [in] temp The temperature value.
*/ */
void NimBLEEddystoneTLM::setTemp(float temp) { void NimBLEEddystoneTLM::setTemp(float temp) {
m_eddystoneData.temp = (uint16_t)temp; m_eddystoneData.temp = ENDIAN_CHANGE_U16((int16_t)(temp * 256.0f));
} // setTemp } // setTemp
/** /**
* @brief Set the advertisment count. * @brief Set the advertisement count.
* @param [in] advCount The advertisment number. * @param [in] advCount The advertisement number.
*/ */
void NimBLEEddystoneTLM::setCount(uint32_t advCount) { void NimBLEEddystoneTLM::setCount(uint32_t advCount) {
m_eddystoneData.advCount = advCount; m_eddystoneData.advCount = advCount;
@ -215,8 +221,8 @@ void NimBLEEddystoneTLM::setCount(uint32_t advCount) {
/** /**
* @brief Set the advertisment time. * @brief Set the advertisement time.
* @param [in] tmil The advertisment time in milliseconds. * @param [in] tmil The advertisement time in milliseconds.
*/ */
void NimBLEEddystoneTLM::setTime(uint32_t tmil) { void NimBLEEddystoneTLM::setTime(uint32_t tmil) {
m_eddystoneData.tmil = tmil; m_eddystoneData.tmil = tmil;

View File

@ -14,6 +14,7 @@
#ifndef _NimBLEEddystoneTLM_H_ #ifndef _NimBLEEddystoneTLM_H_
#define _NimBLEEddystoneTLM_H_ #define _NimBLEEddystoneTLM_H_
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include <string> #include <string>

View File

@ -11,7 +11,7 @@
* Created on: Mar 12, 2018 * Created on: Mar 12, 2018
* Author: pcbreflux * Author: pcbreflux
*/ */
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include "NimBLEEddystoneURL.h" #include "NimBLEEddystoneURL.h"
@ -152,7 +152,7 @@ std::string NimBLEEddystoneURL::getDecodedURL() {
/** /**
* @brief Set the raw data for the beacon advertisment. * @brief Set the raw data for the beacon advertisement.
* @param [in] data The raw data to advertise. * @param [in] data The raw data to advertise.
*/ */
void NimBLEEddystoneURL::setData(const std::string &data) { void NimBLEEddystoneURL::setData(const std::string &data) {
@ -172,7 +172,11 @@ void NimBLEEddystoneURL::setData(const std::string &data) {
* @param [in] l_uuid The UUID. * @param [in] l_uuid The UUID.
*/ */
void NimBLEEddystoneURL::setUUID(const NimBLEUUID &l_uuid) { void NimBLEEddystoneURL::setUUID(const NimBLEUUID &l_uuid) {
beaconUUID = l_uuid.getNative()->u16.value; if (l_uuid.bitSize() != 16) {
NIMBLE_LOGE(LOG_TAG, "UUID must be 16 bits");
return;
}
beaconUUID = *reinterpret_cast<const uint16_t*>(l_uuid.getValue());
} // setUUID } // setUUID

View File

@ -0,0 +1,843 @@
/*
* NimBLEExtAdvertising.cpp
*
* Created: on February 6, 2022
* Author H2zero
*/
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
CONFIG_BT_NIMBLE_EXT_ADV
#if defined(CONFIG_NIMBLE_CPP_IDF)
#include "services/gap/ble_svc_gap.h"
#else
#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
#endif
#include "NimBLEExtAdvertising.h"
#include "NimBLEDevice.h"
#include "NimBLEServer.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
static NimBLEExtAdvertisingCallbacks defaultCallbacks;
static const char* LOG_TAG = "NimBLEExtAdvertising";
/**
* @brief Destructor: deletes callback instances if requested.
*/
NimBLEExtAdvertising::~NimBLEExtAdvertising() {
if(m_deleteCallbacks && m_pCallbacks != &defaultCallbacks) {
delete m_pCallbacks;
}
}
/**
* @brief Register the extended advertisement data.
* @param [in] inst_id The extended advertisement instance ID to assign to this data.
* @param [in] adv The extended advertisement instance with the data to set.
* @return True if advertising started successfully.
*/
bool NimBLEExtAdvertising::setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv) {
adv.m_params.sid = inst_id;
// Legacy advertising as connectable requires the scannable flag also.
if (adv.m_params.legacy_pdu && adv.m_params.connectable) {
adv.m_params.scannable = true;
}
// If connectable or not scannable disable the callback for scan response requests
if (adv.m_params.connectable || !adv.m_params.scannable) {
adv.m_params.scan_req_notif = false;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
NimBLEServer* pServer = NimBLEDevice::getServer();
if (pServer != nullptr) {
if (!pServer->m_gattsStarted) {
pServer->start();
}
}
int rc = ble_gap_ext_adv_configure(inst_id,
&adv.m_params,
NULL,
(pServer != nullptr) ? NimBLEServer::handleGapEvent :
NimBLEExtAdvertising::handleGapEvent,
NULL);
#else
int rc = ble_gap_ext_adv_configure(inst_id,
&adv.m_params,
NULL,
NimBLEExtAdvertising::handleGapEvent,
NULL);
#endif
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Advertising config error: rc = %d", rc);
} else {
os_mbuf *buf;
buf = os_msys_get_pkthdr(adv.m_payload.size(), 0);
if (!buf) {
NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed");
return false;
}
rc = os_mbuf_append(buf, &adv.m_payload[0], adv.m_payload.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Unable to copy data: rc = %d", rc);
return false;
} else {
if (adv.m_params.scannable && !adv.m_params.legacy_pdu) {
rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf);
} else {
rc = ble_gap_ext_adv_set_data(inst_id, buf);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Invalid advertisement data: rc = %d", rc);
} else {
if (!adv.m_advAddress.isNull()) {
rc = ble_gap_ext_adv_set_addr(inst_id, adv.m_advAddress.getBase());
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error setting advertisement address: rc = %d", rc);
return false;
}
}
}
}
return (rc == 0);
}
/**
* @brief Set the scan response data for a legacy advertisement.
* @param [in] inst_id The extended advertisement instance ID to assign to this data.
* @param [in] lsr A reference to a NimBLEExtAdvertisement that contains the data.
*/
bool NimBLEExtAdvertising::setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & lsr) {
os_mbuf *buf = os_msys_get_pkthdr(lsr.m_payload.size(), 0);
if (!buf) {
NIMBLE_LOGE(LOG_TAG, "Data buffer allocation failed");
return false;
}
int rc = os_mbuf_append(buf, &lsr.m_payload[0], lsr.m_payload.size());
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Unable to copy scan data: rc = %d", rc);
return false;
} else {
rc = ble_gap_ext_adv_rsp_set_data(inst_id, buf);
}
return (rc == 0);
}
/**
* @brief Start extended advertising.
* @param [in] inst_id The extended advertisement instance ID to start.
* @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
* @return True if advertising started successfully.
*/
bool NimBLEExtAdvertising::start(uint8_t inst_id, int duration, int max_events) {
NIMBLE_LOGD(LOG_TAG, ">> Extended Advertising start");
// If Host is not synced we cannot start advertising.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
return false;
}
int rc = ble_gap_ext_adv_start(inst_id, duration / 10, max_events);
switch (rc) {
case 0:
m_advStatus[inst_id] = true;
break;
case BLE_HS_EINVAL:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Value Error");
break;
case BLE_HS_EALREADY:
NIMBLE_LOGI(LOG_TAG, "Advertisement Already active");
break;
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
break;
default:
NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
break;
}
NIMBLE_LOGD(LOG_TAG, "<< Extended Advertising start");
return (rc == 0 || rc == BLE_HS_EALREADY);
} // start
/**
* @brief Stop and remove this instance data from the advertisement set.
* @param [in] inst_id The extended advertisement instance to stop advertising.
* @return True if successful.
*/
bool NimBLEExtAdvertising::removeInstance(uint8_t inst_id) {
if (stop(inst_id)) {
int rc = ble_gap_ext_adv_remove(inst_id);
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_remove rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
return true;
}
return false;
} // removeInstance
/**
* @brief Stop and remove all advertising instance data.
* @return True if successful.
*/
bool NimBLEExtAdvertising::removeAll() {
if (stop()) {
int rc = ble_gap_ext_adv_clear();
if (rc == 0 || rc == BLE_HS_EALREADY) {
return true;
} else {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_clear rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
}
return false;
} // removeAll
/**
* @brief Stop advertising this instance data.
* @param [in] inst_id The extended advertisement instance to stop advertising.
* @return True if successful.
*/
bool NimBLEExtAdvertising::stop(uint8_t inst_id) {
int rc = ble_gap_ext_adv_stop(inst_id);
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
m_advStatus[inst_id] = false;
return true;
} // stop
/**
* @brief Stop all advertisements.
* @return True if successful.
*/
bool NimBLEExtAdvertising::stop() {
int rc = ble_gap_ext_adv_clear();
if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "ble_gap_ext_adv_stop rc = %d %s",
rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
for(auto it : m_advStatus) {
it = false;
}
return true;
} // stop
/**
* @brief Set a callback to call when the advertisement stops.
* @param [in] pCallbacks A pointer to a callback to be invoked when an advertisement stops.
* @param [in] deleteCallbacks if true callback class will be deleted when advertising is destructed.
*/
void NimBLEExtAdvertising::setCallbacks(NimBLEExtAdvertisingCallbacks* pCallbacks,
bool deleteCallbacks) {
if (pCallbacks != nullptr){
m_pCallbacks = pCallbacks;
m_deleteCallbacks = deleteCallbacks;
} else {
m_pCallbacks = &defaultCallbacks;
}
} // setCallbacks
/**
* @brief Check if currently advertising.
* @param [in] inst_id The instance ID of the advertised data to get the status of.
* @return True if advertising is active.
*/
bool NimBLEExtAdvertising::isActive(uint8_t inst_id) {
return m_advStatus[inst_id];
} // isAdvertising
/**
* @brief Check if any instances are currently advertising.
* @return True if any instance is active.
*/
bool NimBLEExtAdvertising::isAdvertising() {
for (auto it : m_advStatus) {
if (it) {
return true;
}
}
return false;
} // isAdvertising
/*
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
void NimBLEExtAdvertising::onHostSync() {
NIMBLE_LOGD(LOG_TAG, "Host re-synced");
for(auto it : m_advStatus) {
it = false;
}
} // onHostSync
/**
* @brief Handler for gap events when not using peripheral role.
* @param [in] event the event data.
* @param [in] arg pointer to the advertising instance.
*/
/*STATIC*/
int NimBLEExtAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
(void)arg;
NimBLEExtAdvertising* pAdv = NimBLEDevice::getAdvertising();
switch (event->type) {
case BLE_GAP_EVENT_ADV_COMPLETE: {
switch (event->adv_complete.reason) {
// Don't call the callback if host reset, we want to
// preserve the active flag until re-sync to restart advertising.
case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_EOS:
case BLE_HS_ECONTROLLER:
case BLE_HS_ENOTSYNCED:
NIMBLE_LOGE(LOG_TAG, "host reset, rc = %d", event->adv_complete.reason);
NimBLEDevice::onReset(event->adv_complete.reason);
return 0;
default:
break;
}
pAdv->m_advStatus[event->adv_complete.instance] = false;
pAdv->m_pCallbacks->onStopped(pAdv, event->adv_complete.reason,
event->adv_complete.instance);
break;
}
case BLE_GAP_EVENT_SCAN_REQ_RCVD: {
pAdv->m_pCallbacks->onScanRequest(pAdv, event->scan_req_rcvd.instance,
NimBLEAddress(event->scan_req_rcvd.scan_addr));
break;
}
}
return 0;
} // handleGapEvent
/** Default callback handlers */
void NimBLEExtAdvertisingCallbacks::onStopped(NimBLEExtAdvertising *pAdv,
int reason, uint8_t inst_id) {
NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onStopped: Default");
} // onStopped
void NimBLEExtAdvertisingCallbacks::onScanRequest(NimBLEExtAdvertising *pAdv,
uint8_t inst_id, NimBLEAddress addr) {
NIMBLE_LOGD("NimBLEExtAdvertisingCallbacks", "onScanRequest: Default");
} // onScanRequest
/**
* @brief Construct a BLE extended advertisement.
* @param [in] priPhy The primary Phy to advertise on, can be one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_CODED
* @param [in] secPhy The secondary Phy to advertise on, can be one of:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_2M
* * BLE_HCI_LE_PHY_CODED
*/
NimBLEExtAdvertisement::NimBLEExtAdvertisement(uint8_t priPhy, uint8_t secPhy)
: m_advAddress{}
{
memset (&m_params, 0, sizeof(m_params));
m_params.own_addr_type = NimBLEDevice::m_ownAddrType;
m_params.primary_phy = priPhy;
m_params.secondary_phy = secPhy;
m_params.tx_power = 127;
} // NimBLEExtAdvertisement
/**
* @brief Sets wether the advertisement should use legacy (BLE 4.0, 31 bytes max) advertising.
* @param [in] val true = using legacy advertising.
*/
void NimBLEExtAdvertisement::setLegacyAdvertising(bool val) {
m_params.legacy_pdu = val;
} // setLegacyAdvertising
/**
* @brief Sets wether the advertisement has scan response data available.
* @param [in] val true = scan response is available.
*/
void NimBLEExtAdvertisement::setScannable(bool val) {
m_params.scannable = val;
} // setScannable
/**
* @brief Sets the transmission power level for this advertisement.
* @param [in] dbm the transmission power to use in dbm.
* @details The allowable value range depends on device hardware. \n
* The ESP32C3 and ESP32S3 have a range of -27 to +18.
*/
void NimBLEExtAdvertisement::setTxPower(int8_t dbm) {
m_params.tx_power = dbm;
}
/**
* @brief Sets wether this advertisement should advertise as a connectable device.
* @param [in] val True = connectable.
*/
void NimBLEExtAdvertisement::setConnectable(bool val) {
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
m_params.connectable = val;
#endif
} // setConnectable
/**
* @brief Set the address to use for this advertisement.
* @param [in] addr The address to use.
*/
void NimBLEExtAdvertisement::setAddress(const NimBLEAddress & addr) {
m_advAddress = addr;
// Must use random address type.
m_params.own_addr_type = BLE_OWN_ADDR_RANDOM;
}
/**
* @brief Sets The primary channels to advertise on.
* @param [in] ch37 Advertise on channel 37.
* @param [in] ch38 Advertise on channel 38.
* @param [in] ch39 Advertise on channel 39.
* @details This will set a bitmask using the input parameters to allow different \n
* combinations. If all inputs are false then all 3 channels will be used.
*/
void NimBLEExtAdvertisement::setPrimaryChannels(bool ch37, bool ch38, bool ch39) {
m_params.channel_map = (ch37 | (ch38 << 1) | (ch39 << 2));
} // setPrimaryChannels
/**
* @brief Set the filtering for the scan filter.
* @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list.
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
*/
void NimBLEExtAdvertisement::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_NONE;
return;
}
if (scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_SCAN;
return;
}
if (!scanRequestWhitelistOnly && connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_CONN;
return;
}
if (scanRequestWhitelistOnly && connectWhitelistOnly) {
m_params.filter_policy = BLE_HCI_ADV_FILT_BOTH;
return;
}
} // setScanFilter
/**
* @brief Sets the peer to directly advertise to.
* @param [in] addr The address of the peer to direct the advertisements.
*/
void NimBLEExtAdvertisement::setDirectedPeer(const NimBLEAddress & addr) {
m_params.peer = *addr.getBase();
} // setDirectedPeer
/**
* @brief Enable or disable direct advertisements to the peer set with `NimBLEExtAdvertisement::setDirectedPeer`
* @param [in] val true = send directed advertisements to peer.
* @param [in] high_duty true = use fast advertising rate, default - true.
*/
void NimBLEExtAdvertisement::setDirected(bool val, bool high_duty) {
m_params.directed = val;
m_params.high_duty_directed = high_duty;
} // setDirected
/**
* @brief Set the minimum advertising interval.
* @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default.
*/
void NimBLEExtAdvertisement::setMinInterval(uint32_t mininterval) {
m_params.itvl_min = mininterval;
} // setMinInterval
/**
* @brief Set the maximum advertising interval.
* @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default.
*/
void NimBLEExtAdvertisement::setMaxInterval(uint32_t maxinterval) {
m_params.itvl_max = maxinterval;
} // setMaxInterval
/**
* @brief Set the primary advertising PHY to use
* @param [in] phy Can be one of following constants:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_CODED
*/
void NimBLEExtAdvertisement::setPrimaryPhy(uint8_t phy) {
m_params.primary_phy = phy;
} // setPrimaryPhy
/**
* @brief Set the secondary advertising PHY to use
* @param [in] phy Can be one of following constants:
* * BLE_HCI_LE_PHY_1M
* * BLE_HCI_LE_PHY_2M
* * BLE_HCI_LE_PHY_CODED
*/
void NimBLEExtAdvertisement::setSecondaryPhy(uint8_t phy) {
m_params.secondary_phy = phy;
} // setSecondaryPhy
/**
* @brief Sets whether the advertisement should be anonymous.
* @param [in] val Set to true to enable anonymous advertising.
*
* @details Anonymous advertising omits the device's address from the advertisement.
*/
void NimBLEExtAdvertisement::setAnonymous(bool val) {
m_params.anonymous = val;
} // setAnonymous
/**
* @brief Sets whether the scan response request callback should be called.
* @param [in] enable If true the scan response request callback will be called for this advertisement.
*/
void NimBLEExtAdvertisement::enableScanRequestCallback(bool enable) {
m_params.scan_req_notif = enable;
} // enableScanRequestCallback
/**
* @brief Clears the data stored in this instance, does not change settings.
* @details This will clear all data but preserves advertising parameter settings.
*/
void NimBLEExtAdvertisement::clearData() {
std::vector<uint8_t> swap;
std::swap(m_payload, swap);
}
/**
* @brief Get the size of the current data.
*/
size_t NimBLEExtAdvertisement::getDataSize() {
return m_payload.size();
} // getDataSize
/**
* @brief Set the advertisement data.
* @param [in] data The data to be set as the payload.
* @param [in] length The size of data.
* @details This will completely replace any data that was previously set.
*/
void NimBLEExtAdvertisement::setData(const uint8_t * data, size_t length) {
m_payload.assign(data, data + length);
} // setData
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
*/
void NimBLEExtAdvertisement::addData(const std::string &data) {
addData((uint8_t*)data.data(), data.length());
} // addData
/**
* @brief Add data to the payload to be advertised.
* @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 NimBLEExtAdvertisement::addData(const uint8_t * data, size_t length) {
m_payload.insert(m_payload.end(), data, data + length);
} // addData
/**
* @brief Set the appearance.
* @param [in] appearance The appearance code value.
*/
void NimBLEExtAdvertisement::setAppearance(uint16_t appearance) {
char cdata[2];
cdata[0] = 3;
cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19
addData(std::string(cdata, 2) + std::string((char*) &appearance, 2));
} // setAppearance
/**
* @brief Set the advertisement flags.
* @param [in] flag The flags to be set in the advertisement.
* * BLE_HS_ADV_F_DISC_LTD
* * BLE_HS_ADV_F_DISC_GEN
* * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE
*/
void NimBLEExtAdvertisement::setFlags(uint8_t flag) {
char cdata[3];
cdata[0] = 2;
cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01
cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP;
addData(std::string(cdata, 3));
} // setFlags
/**
* @brief Set manufacturer specific data.
* @param [in] data The manufacturer data to advertise.
*/
void NimBLEExtAdvertisement::setManufacturerData(const std::string &data) {
char cdata[2];
cdata[0] = data.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff
addData(std::string(cdata, 2) + data);
} // setManufacturerData
/**
* @brief Set the URI to advertise.
* @param [in] uri The uri to advertise.
*/
void NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::setName(const std::string &name) {
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09
addData(std::string(cdata, 2) + name);
} // setName
/**
* @brief Set a single service to advertise as a complete list of services.
* @param [in] uuid The service to advertise.
*/
void NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::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 NimBLEExtAdvertisement::setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid)
{
char cdata[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:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32;
break;
case 128:
cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128;
break;
default:
return;
}
std::string uuids;
for(auto &it : v_uuid){
if(it.bitSize() != size) {
NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size);
continue;
} else {
uuids += std::string(reinterpret_cast<const char*>(it.getValue()), size / 8);
}
}
addData(std::string(cdata, 2) + uuids);
} // setServices
/**
* @brief Set the service data (UUID + data)
* @param [in] uuid The UUID to set with the service data.
* @param [in] data The data to be associated with the service data advertised.
*/
void NimBLEExtAdvertisement::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
uint8_t size = uuid.bitSize() / 8;
char cdata[2] = {static_cast<char>(1 + size + data.length()), BLE_HS_ADV_TYPE_SVC_DATA_UUID16};
switch (size) {
case 2: {
// [Len] [0x16] [UUID16] data
break;
}
case 16: {
// [Len] [0x21] [UUID128] data
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21
break;
}
case 4: {
// [Len] [0x20] [UUID32] data
cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20
break;
}
default:
return;
}
addData(std::string(cdata, 2) + std::string(reinterpret_cast<const char*>(uuid.getValue()), size) + data);
} // setServiceData
/**
* @brief Set the short name.
* @param [in] name The short name of the device.
*/
void NimBLEExtAdvertisement::setShortName(const std::string &name) {
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08
addData(std::string(cdata, 2) + name);
} // setShortName
/**
* @brief Adds Tx power level to the advertisement data.
*/
void NimBLEExtAdvertisement::addTxPower() {
m_params.include_tx_power = 1;
} // addTxPower
/**
* @brief Set the preferred connection interval parameters.
* @param [in] min The minimum interval desired.
* @param [in] max The maximum interval desired.
*/
void NimBLEExtAdvertisement::setPreferredParams(uint16_t min, uint16_t max) {
uint8_t data[6];
data[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1;
data[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE;
data[2] = min;
data[3] = min >> 8;
data[4] = max;
data[5] = max >> 8;
addData(data, 6);
} // setPreferredParams
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */

152
src/NimBLEExtAdvertising.h Normal file
View File

@ -0,0 +1,152 @@
/*
* NimBLEExtAdvertising.h
*
* Created: on February 6, 2022
* Author H2zero
*/
#ifndef MAIN_BLEEXTADVERTISING_H_
#define MAIN_BLEEXTADVERTISING_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && \
defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \
CONFIG_BT_NIMBLE_EXT_ADV
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_gap.h"
# else
# include "nimble/nimble/host/include/host/ble_gap.h"
# endif
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
#include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include <vector>
class NimBLEExtAdvertisingCallbacks;
/**
* @brief Extended advertisement data
*/
class NimBLEExtAdvertisement {
public:
NimBLEExtAdvertisement(uint8_t priPhy = BLE_HCI_LE_PHY_1M,
uint8_t secPhy = BLE_HCI_LE_PHY_1M);
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 flag);
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 setData(const uint8_t * data, size_t length);
void addData(const std::string &data);
void addData(const uint8_t * data, size_t length);
void addTxPower();
void setPreferredParams(uint16_t min, uint16_t max);
void setLegacyAdvertising(bool val);
void setConnectable(bool val);
void setScannable(bool val);
void setMinInterval(uint32_t mininterval);
void setMaxInterval(uint32_t maxinterval);
void setPrimaryPhy(uint8_t phy);
void setSecondaryPhy(uint8_t phy);
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setDirectedPeer(const NimBLEAddress & addr);
void setDirected(bool val, bool high_duty = true);
void setAnonymous(bool val);
void setPrimaryChannels(bool ch37, bool ch38, bool ch39);
void setTxPower(int8_t dbm);
void setAddress(const NimBLEAddress & addr);
void enableScanRequestCallback(bool enable);
void clearData();
size_t getDataSize();
private:
friend class NimBLEExtAdvertising;
void setServices(const bool complete, const uint8_t size,
const std::vector<NimBLEUUID> &v_uuid);
std::vector<uint8_t> m_payload;
ble_gap_ext_adv_params m_params;
NimBLEAddress m_advAddress;
}; // NimBLEExtAdvertisement
/**
* @brief Extended advertising class.
*/
class NimBLEExtAdvertising {
public:
/**
* @brief Construct an extended advertising object.
*/
NimBLEExtAdvertising() :m_advStatus(CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES + 1, false) {}
~NimBLEExtAdvertising();
bool start(uint8_t inst_id, int duration = 0, int max_events = 0);
bool setInstanceData(uint8_t inst_id, NimBLEExtAdvertisement& adv);
bool setScanResponseData(uint8_t inst_id, NimBLEExtAdvertisement & data);
bool removeInstance(uint8_t inst_id);
bool removeAll();
bool stop(uint8_t inst_id);
bool stop();
bool isActive(uint8_t inst_id);
bool isAdvertising();
void setCallbacks(NimBLEExtAdvertisingCallbacks* callbacks,
bool deleteCallbacks = true);
private:
friend class NimBLEDevice;
friend class NimBLEServer;
void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
bool m_scanResp;
bool m_deleteCallbacks;
NimBLEExtAdvertisingCallbacks* m_pCallbacks;
ble_gap_ext_adv_params m_advParams;
std::vector<bool> m_advStatus;
};
/**
* @brief Callbacks associated with NimBLEExtAdvertising class.
*/
class NimBLEExtAdvertisingCallbacks {
public:
virtual ~NimBLEExtAdvertisingCallbacks() {};
/**
* @brief Handle an advertising stop event.
* @param [in] pAdv A convenience pointer to the extended advertising interface.
* @param [in] reason The reason code for stopping the advertising.
* @param [in] inst_id The instance ID of the advertisement that was stopped.
*/
virtual void onStopped(NimBLEExtAdvertising *pAdv, int reason, uint8_t inst_id);
/**
* @brief Handle a scan response request.
* This is called when a scanning device requests a scan response.
* @param [in] pAdv A convenience pointer to the extended advertising interface.
* @param [in] inst_id The instance ID of the advertisement that the scan response request was made.
* @param [in] addr The address of the device making the request.
*/
virtual void onScanRequest(NimBLEExtAdvertising *pAdv, uint8_t inst_id, NimBLEAddress addr);
}; // NimBLEExtAdvertisingCallbacks
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && CONFIG_BT_NIMBLE_EXT_ADV */
#endif /* MAIN_BLEADVERTISING_H_ */

View File

@ -11,11 +11,9 @@
* Created on: Jan 03, 2018 * Created on: Jan 03, 2018
* Author: chegewara * Author: chegewara
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEHIDDevice.h" #include "NimBLEHIDDevice.h"
#include "NimBLE2904.h" #include "NimBLE2904.h"
@ -29,7 +27,7 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
* Here we create mandatory services described in bluetooth specification * Here we create mandatory services described in bluetooth specification
*/ */
m_deviceInfoService = server->createService(NimBLEUUID((uint16_t)0x180a)); m_deviceInfoService = server->createService(NimBLEUUID((uint16_t)0x180a));
m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812), 40); m_hidService = server->createService(NimBLEUUID((uint16_t)0x1812));
m_batteryService = server->createService(NimBLEUUID((uint16_t)0x180f)); m_batteryService = server->createService(NimBLEUUID((uint16_t)0x180f));
/* /*
@ -37,6 +35,12 @@ NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
*/ */
m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, NIMBLE_PROPERTY::READ); m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, NIMBLE_PROPERTY::READ);
/*
* Non-mandatory characteristics for device info service
* Will be created on demand
*/
m_manufacturerCharacteristic = nullptr;
/* /*
* Mandatory characteristics for HID service * Mandatory characteristics for HID service
*/ */
@ -88,7 +92,10 @@ void NimBLEHIDDevice::startServices() {
* @brief Create a manufacturer characteristic (this characteristic is optional). * @brief Create a manufacturer characteristic (this characteristic is optional).
*/ */
NimBLECharacteristic* NimBLEHIDDevice::manufacturer() { NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
if (m_manufacturerCharacteristic == nullptr) {
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, NIMBLE_PROPERTY::READ); m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, NIMBLE_PROPERTY::READ);
}
return m_manufacturerCharacteristic; return m_manufacturerCharacteristic;
} }
@ -97,18 +104,26 @@ NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
* @param [in] name The manufacturer name of this HID device. * @param [in] name The manufacturer name of this HID device.
*/ */
void NimBLEHIDDevice::manufacturer(std::string name) { void NimBLEHIDDevice::manufacturer(std::string name) {
m_manufacturerCharacteristic->setValue(name); manufacturer()->setValue(name);
} }
/** /**
* @brief Sets the Plug n Play characterisc value. * @brief Sets the Plug n Play characteristic value.
* @param [in] sig The vendor ID source number. * @param [in] sig The vendor ID source number.
* @param [in] vid The vendor ID number. * @param [in] vid The vendor ID number.
* @param [in] pid The product ID number. * @param [in] pid The product ID number.
* @param [in] version The produce version number. * @param [in] version The produce version number.
*/ */
void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) { void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version }; uint8_t pnp[] = {
sig,
((uint8_t*)&vid)[0],
((uint8_t*)&vid)[1],
((uint8_t*)&pid)[0],
((uint8_t*)&pid)[1],
((uint8_t*)&version)[0],
((uint8_t*)&version)[1]
};
m_pnpCharacteristic->setValue(pnp, sizeof(pnp)); m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
} }
@ -205,17 +220,17 @@ void NimBLEHIDDevice::setBatteryLevel(uint8_t level) {
/* /*
* @brief Returns battery level characteristic * @brief Returns battery level characteristic
* @ return battery level characteristic * @ return battery level characteristic
*//* */
BLECharacteristic* BLEHIDDevice::batteryLevel() { NimBLECharacteristic* NimBLEHIDDevice::batteryLevel() {
return m_batteryLevelCharacteristic; return m_batteryLevelCharacteristic;
} }
NimBLECharacteristic* NimBLEHIDDevice::reportMap() {
BLECharacteristic* BLEHIDDevice::reportMap() {
return m_reportMapCharacteristic; return m_reportMapCharacteristic;
} }
/*
BLECharacteristic* BLEHIDDevice::pnp() { BLECharacteristic* BLEHIDDevice::pnp() {
return m_pnpCharacteristic; return m_pnpCharacteristic;
} }
@ -247,5 +262,4 @@ NimBLEService* NimBLEHIDDevice::batteryService() {
return m_batteryService; return m_batteryService;
} }
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
#endif // #if defined(CONFIG_BT_ENABLED)

View File

@ -15,11 +15,8 @@
#ifndef _BLEHIDDEVICE_H_ #ifndef _BLEHIDDEVICE_H_
#define _BLEHIDDEVICE_H_ #define _BLEHIDDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#include "NimBLECharacteristic.h" #include "NimBLECharacteristic.h"
#include "NimBLEService.h" #include "NimBLEService.h"
@ -36,6 +33,7 @@
#define HID_DIGITAL_PEN 0x03C7 #define HID_DIGITAL_PEN 0x03C7
#define HID_BARCODE 0x03C8 #define HID_BARCODE 0x03C8
#define PNPVersionField(MajorVersion, MinorVersion, PatchVersion) ((MajorVersion << 16) & 0xFF00) | ((MinorVersion << 8) & 0x00F0) | (PatchVersion & 0x000F)
/** /**
* @brief A model of a %BLE Human Interface Device. * @brief A model of a %BLE Human Interface Device.
@ -58,11 +56,11 @@ public:
void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version); void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
//NimBLECharacteristic* hidInfo(); //NimBLECharacteristic* hidInfo();
void hidInfo(uint8_t country, uint8_t flags); void hidInfo(uint8_t country, uint8_t flags);
//NimBLECharacteristic* batteryLevel(); NimBLECharacteristic* batteryLevel();
void setBatteryLevel(uint8_t level); void setBatteryLevel(uint8_t level);
//NimBLECharacteristic* reportMap(); NimBLECharacteristic* reportMap();
NimBLECharacteristic* hidControl(); NimBLECharacteristic* hidControl();
NimBLECharacteristic* inputReport(uint8_t reportID); NimBLECharacteristic* inputReport(uint8_t reportID);
NimBLECharacteristic* outputReport(uint8_t reportID); NimBLECharacteristic* outputReport(uint8_t reportID);
@ -84,6 +82,6 @@ private:
NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19 NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
}; };
#endif // CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif // CONFIG_BT_ENABLED #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER */
#endif /* _BLEHIDDEVICE_H_ */ #endif /* _BLEHIDDEVICE_H_ */

View File

@ -0,0 +1,48 @@
/*
* NimBLELocalAttribute.cpp
*
* Created: on July 28 2024
* Author H2zero
*/
#ifndef NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
#define NIMBLE_CPP_LOCAL_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# include "NimBLEAttribute.h"
/**
* @brief A base class for local BLE attributes.
*/
class NimBLELocalAttribute : public NimBLEAttribute {
public:
/**
* @brief Get the removed flag.
* @return The removed flag.
*/
uint8_t getRemoved() const { return m_removed; }
protected:
/**
* @brief Construct a local attribute.
*/
NimBLELocalAttribute(const NimBLEUUID& uuid, uint16_t handle) : NimBLEAttribute{uuid, handle}, m_removed{0} {}
/**
* @brief Destroy the local attribute.
*/
~NimBLELocalAttribute() = default;
/**
* @brief Set the removed flag.
* @param [in] removed The removed flag.
*/
void setRemoved(uint8_t removed) { m_removed = removed; }
uint8_t m_removed{0};
};
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif // NIMBLE_CPP_LOCAL_ATTRIBUTE_H_

View File

@ -0,0 +1,156 @@
/*
* NimBLELocalValueAttribute.cpp
*
* Created: on July 28 2024
* Author H2zero
*/
#ifndef NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
#define NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_
#include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
# if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_hs.h"
# else
# include "nimble/nimble/host/include/host/ble_hs.h"
# endif
typedef enum {
READ = BLE_GATT_CHR_F_READ,
READ_ENC = BLE_GATT_CHR_F_READ_ENC,
READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN,
READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR,
WRITE = BLE_GATT_CHR_F_WRITE,
WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP,
WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC,
WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN,
WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR,
BROADCAST = BLE_GATT_CHR_F_BROADCAST,
NOTIFY = BLE_GATT_CHR_F_NOTIFY,
INDICATE = BLE_GATT_CHR_F_INDICATE
} NIMBLE_PROPERTY;
# include "NimBLELocalAttribute.h"
# include "NimBLEAttValue.h"
# include <vector>
class NimBLEConnInfo;
class NimBLELocalValueAttribute : public NimBLELocalAttribute {
public:
/**
* @brief Get the properties of the attribute.
*/
uint16_t getProperties() const { return m_properties; }
/**
* @brief Get the length of the attribute value.
* @return The length of the attribute value.
*/
size_t getLength() const { return m_value.size(); }
/**
* @brief Get a copy of the value of the attribute value.
* @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set.
* @return A copy of the attribute value.
*/
NimBLEAttValue getValue(time_t* timestamp = nullptr) const { return m_value; }
/**
* @brief Set the value of the attribute value.
* @param [in] data The data to set the value to.
* @param [in] size The size of the data.
*/
void setValue(const uint8_t* data, size_t size) { m_value.setValue(data, size); }
/**
* @brief Set the value of the attribute value.
* @param [in] str The string to set the value to.
*/
void setValue(const char* str) { m_value.setValue(str); }
/**
* @brief Set the value of the attribute value.
* @param [in] vec The vector to set the value to.
*/
void setValue(const std::vector<uint8_t>& vec) { m_value.setValue(vec); }
/**
* @brief Template to set the value to <type\>val.
* @param [in] val The value to set.
*/
template <typename T>
void setValue(const T& val) {
m_value.setValue<T>(val);
}
/**
* @brief Template to convert the data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp (Optional) A pointer to a time_t struct to get the time the value set.
* @param [in] skipSizeCheck (Optional) If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template <typename T>
T getValue(time_t* timestamp = nullptr, bool skipSizeCheck = false) const {
return m_value.getValue<T>(timestamp, skipSizeCheck);
}
protected:
friend class NimBLEServer;
/**
* @brief Construct a new NimBLELocalValueAttribute object.
* @param [in] uuid The UUID of the attribute.
* @param [in] handle The handle of the attribute.
* @param [in] maxLen The maximum length of the attribute value.
* @param [in] initLen The initial length of the attribute value.
*/
NimBLELocalValueAttribute(const NimBLEUUID& uuid,
uint16_t handle,
uint16_t maxLen,
uint16_t initLen = CONFIG_NIMBLE_CPP_ATT_VALUE_INIT_LENGTH)
: NimBLELocalAttribute(uuid, handle), m_value(initLen, maxLen) {}
/**
* @brief Destroy the NimBLELocalValueAttribute object.
*/
virtual ~NimBLELocalValueAttribute() = default;
/**
* @brief Callback function to support a read request.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @details This function is called by NimBLEServer when a read request is received.
*/
virtual void readEvent(NimBLEConnInfo& connInfo) = 0;
/**
* @brief Callback function to support a write request.
* @param [in] val The value to write.
* @param [in] len The length of the value.
* @param [in] connInfo A reference to a NimBLEConnInfo instance containing the peer info.
* @details This function is called by NimBLEServer when a write request is received.
*/
virtual void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) = 0;
/**
* @brief Get a pointer to value of the attribute.
* @return A pointer to the value of the attribute.
* @details This function is used by NimBLEServer when handling read/write requests.
*/
const NimBLEAttValue& getAttVal() const { return m_value; }
/**
* @brief Set the properties of the attribute.
* @param [in] properties The properties of the attribute.
*/
void setProperties(uint16_t properties) { m_properties = properties; }
NimBLEAttValue m_value{};
uint16_t m_properties{0};
};
#endif // CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif // NIMBLE_LOCAL_VALUE_ATTRIBUTE_H_

View File

@ -8,59 +8,72 @@
#ifndef MAIN_NIMBLELOG_H_ #ifndef MAIN_NIMBLELOG_H_
#define MAIN_NIMBLELOG_H_ #define MAIN_NIMBLELOG_H_
#include "sdkconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#ifdef ARDUINO_ARCH_ESP32 #if defined(CONFIG_NIMBLE_CPP_IDF) // using esp-idf
#include "syscfg/syscfg.h" # include "esp_log.h"
#include "modlog/modlog.h" # include "console/console.h"
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
// If Arduino is being used, strip out the colors and ignore log printing below ui setting. # define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
// 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.
#ifndef CORE_DEBUG_LEVEL
#define CORE_DEBUG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL
# endif # endif
#if CORE_DEBUG_LEVEL >= 4 # define NIMBLE_CPP_LOG_PRINT(level, tag, format, ...) do { \
#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(ERROR, "D %s: "#format"\n",tag,##__VA_ARGS__) if (CONFIG_NIMBLE_CPP_LOG_LEVEL >= level) \
ESP_LOG_LEVEL_LOCAL(level, tag, format, ##__VA_ARGS__); \
} while(0)
# define NIMBLE_LOGD(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_DEBUG, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGI(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_INFO, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGW(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_WARN, tag, format, ##__VA_ARGS__)
# define NIMBLE_LOGE(tag, format, ...) \
NIMBLE_CPP_LOG_PRINT(ESP_LOG_ERROR, tag, format, ##__VA_ARGS__)
#else // using Arduino
# include "nimble/porting/nimble/include/syscfg/syscfg.h"
# include "nimble/console/console.h"
# ifndef CONFIG_NIMBLE_CPP_LOG_LEVEL
# if defined(ARDUINO_ARCH_ESP32) && defined(CORE_DEBUG_LEVEL)
# define CONFIG_NIMBLE_CPP_LOG_LEVEL CORE_DEBUG_LEVEL
# else
# define CONFIG_NIMBLE_CPP_LOG_LEVEL 0
# endif
# endif
# if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
# define NIMBLE_LOGD( tag, format, ... ) console_printf("D %s: " format "\n", tag, ##__VA_ARGS__)
# else # else
# define NIMBLE_LOGD( tag, format, ... ) (void)tag # define NIMBLE_LOGD( tag, format, ... ) (void)tag
# endif # endif
#if CORE_DEBUG_LEVEL >= 3 # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 3
#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(ERROR, "I %s: "#format"\n",tag,##__VA_ARGS__) # define NIMBLE_LOGI( tag, format, ... ) console_printf("I %s: " format "\n", tag, ##__VA_ARGS__)
# else # else
# define NIMBLE_LOGI( tag, format, ... ) (void)tag # define NIMBLE_LOGI( tag, format, ... ) (void)tag
# endif # endif
#if CORE_DEBUG_LEVEL >= 2 # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 2
#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(ERROR, "W %s: "#format"\n",tag,##__VA_ARGS__) # define NIMBLE_LOGW( tag, format, ... ) console_printf("W %s: " format "\n", tag, ##__VA_ARGS__)
# else # else
# define NIMBLE_LOGW( tag, format, ... ) (void)tag # define NIMBLE_LOGW( tag, format, ... ) (void)tag
# endif # endif
#if CORE_DEBUG_LEVEL >= 1 # if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 1
#define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "E %s: "#format"\n",tag,##__VA_ARGS__) # define NIMBLE_LOGE( tag, format, ... ) console_printf("E %s: " format "\n", tag, ##__VA_ARGS__)
# else # else
# define NIMBLE_LOGE( tag, format, ... ) (void)tag # define NIMBLE_LOGE( tag, format, ... ) (void)tag
# endif # endif
#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "CRIT %s: "#format"\n",tag,##__VA_ARGS__) #endif /* CONFIG_NIMBLE_CPP_IDF */
#else #define NIMBLE_LOGC( tag, format, ... ) console_printf("CRIT %s: " format "\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 /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLELOG_H_ */ #endif /* MAIN_NIMBLELOG_H_ */

View File

@ -12,21 +12,28 @@
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include "NimBLERemoteCharacteristic.h" # include "NimBLERemoteCharacteristic.h"
# include "NimBLERemoteDescriptor.h"
# include "NimBLERemoteService.h"
# include "NimBLEClient.h"
# include "NimBLEUtils.h" # include "NimBLEUtils.h"
# include "NimBLELog.h" # include "NimBLELog.h"
# include <climits>
typedef struct {
const NimBLEUUID* uuid;
void* task_data;
} desc_filter_t;
static const char* LOG_TAG = "NimBLERemoteCharacteristic"; static const char* LOG_TAG = "NimBLERemoteCharacteristic";
/** /**
* @brief Constructor. * @brief Constructor.
* @param [in] reference to the service this characteristic belongs to. * @param [in] svc A pointer to the service this characteristic belongs to.
* @param [in] ble_gatt_chr struct defined as: * @param [in] ble_gatt_chr struct defined as:
* struct ble_gatt_chr { * struct ble_gatt_chr {
* uint16_t def_handle; * uint16_t def_handle;
@ -35,36 +42,12 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
* ble_uuid_any_t uuid; * ble_uuid_any_t uuid;
* }; * };
*/ */
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService, NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(const NimBLERemoteService* svc, const ble_gatt_chr* chr)
const struct ble_gatt_chr *chr) : NimBLERemoteValueAttribute{chr->uuid, chr->val_handle},
{ m_pRemoteService{svc},
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()"); m_properties{chr->properties},
switch (chr->uuid.u.type) { m_notifyCallback{},
case BLE_UUID_TYPE_16: m_vDescriptors{} {} // NimBLERemoteCharacteristic
m_uuid = NimBLEUUID(chr->uuid.u16.value);
break;
case BLE_UUID_TYPE_32:
m_uuid = NimBLEUUID(chr->uuid.u32.value);
break;
case BLE_UUID_TYPE_128:
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&chr->uuid.u128));
break;
default:
break;
}
m_handle = chr->val_handle;
m_defHandle = chr->def_handle;
m_endHandle = 0;
m_charProp = chr->properties;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
m_timestamp = 0;
m_valMux = portMUX_INITIALIZER_UNLOCKED;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
} // NimBLERemoteCharacteristic
/** /**
*@brief Destructor. *@brief Destructor.
@ -73,259 +56,131 @@ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() {
deleteDescriptors(); deleteDescriptors();
} // ~NimBLERemoteCharacteristic } // ~NimBLERemoteCharacteristic
/*
#define BLE_GATT_CHR_PROP_BROADCAST 0x01
#define BLE_GATT_CHR_PROP_READ 0x02
#define BLE_GATT_CHR_PROP_WRITE_NO_RSP 0x04
#define BLE_GATT_CHR_PROP_WRITE 0x08
#define BLE_GATT_CHR_PROP_NOTIFY 0x10
#define BLE_GATT_CHR_PROP_INDICATE 0x20
#define BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE 0x40
#define BLE_GATT_CHR_PROP_EXTENDED 0x80
*/
/**
* @brief Does the characteristic support broadcasting?
* @return True if the characteristic supports broadcasting.
*/
bool NimBLERemoteCharacteristic::canBroadcast() {
return (m_charProp & BLE_GATT_CHR_PROP_BROADCAST) != 0;
} // canBroadcast
/**
* @brief Does the characteristic support indications?
* @return True if the characteristic supports indications.
*/
bool NimBLERemoteCharacteristic::canIndicate() {
return (m_charProp & BLE_GATT_CHR_PROP_INDICATE) != 0;
} // canIndicate
/**
* @brief Does the characteristic support notifications?
* @return True if the characteristic supports notifications.
*/
bool NimBLERemoteCharacteristic::canNotify() {
return (m_charProp & BLE_GATT_CHR_PROP_NOTIFY) != 0;
} // canNotify
/**
* @brief Does the characteristic support reading?
* @return True if the characteristic supports reading.
*/
bool NimBLERemoteCharacteristic::canRead() {
return (m_charProp & BLE_GATT_CHR_PROP_READ) != 0;
} // canRead
/**
* @brief Does the characteristic support writing?
* @return True if the characteristic supports writing.
*/
bool NimBLERemoteCharacteristic::canWrite() {
return (m_charProp & BLE_GATT_CHR_PROP_WRITE) != 0;
} // canWrite
/**
* @brief Does the characteristic support writing with no response?
* @return True if the characteristic supports writing with no response.
*/
bool NimBLERemoteCharacteristic::canWriteNoResponse() {
return (m_charProp & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0;
} // canWriteNoResponse
/** /**
* @brief Callback used by the API when a descriptor is discovered or search complete. * @brief Callback used by the API when a descriptor is discovered or search complete.
*/ */
int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle, int NimBLERemoteCharacteristic::descriptorDiscCB(
const struct ble_gatt_error *error, uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg) {
uint16_t chr_val_handle,
const struct ble_gatt_dsc *dsc,
void *arg)
{
int rc = error->status; int rc = error->status;
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovered >> status: %d handle: %d", NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1);
rc, (rc == 0) ? dsc->handle : -1);
desc_filter_t *filter = (desc_filter_t*)arg; auto filter = (desc_filter_t*)arg;
const NimBLEUUID *uuid_filter = filter->uuid; auto pTaskData = (NimBLETaskData*)filter->task_data;
ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data; const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT; const NimBLEUUID* uuidFilter = filter->uuid;
if (characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ if (error->status == BLE_HS_ENOTCONN) {
return 0; NIMBLE_LOGE(LOG_TAG, "<< Descriptor Discovery; Not connected");
NimBLEUtils::taskRelease(*pTaskData, error->status);
return error->status;
} }
switch (rc) { if (pChr->getHandle() != chr_val_handle) {
case 0: { rc = BLE_HS_EDONE; // descriptor not for this characteristic
if (uuid_filter != nullptr) {
if (ble_uuid_cmp(&uuid_filter->getNative()->u, &dsc->uuid.u) != 0) {
return 0;
} else {
rc = BLE_HS_EDONE;
}
}
NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc);
characteristic->m_descriptorVector.push_back(pNewRemoteDescriptor);
break;
}
default:
break;
}
/* 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.
*/
if (rc == BLE_HS_EDONE) {
pTaskData->rc = 0;
xTaskNotifyGive(pTaskData->task);
} else if(rc != 0) {
// Error; abort discovery.
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
}
NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", pTaskData->rc);
return rc;
}
/**
* @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) { if (rc == 0) {
pChar->m_endHandle = chr->def_handle - 1; if (uuidFilter != nullptr) {
rc = BLE_HS_EDONE; if (ble_uuid_cmp(uuidFilter->getBase(), &dsc->uuid.u) == 0) {
} else if (rc == BLE_HS_EDONE) { rc = BLE_HS_EDONE; // Found the descriptor, stop the search
pChar->m_endHandle = pChar->getRemoteService()->getEndHandle();
} else { } else {
pTaskData->rc = rc; return 0; // Not the descriptor we are looking for
}
} }
xTaskNotifyGive(pTaskData->task); pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc));
}
NimBLEUtils::taskRelease(*pTaskData, rc);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery");
return rc; return rc;
} }
/** /**
* @brief Populate the descriptors (if any) for this characteristic. * @brief Populate the descriptors (if any) for this characteristic.
* @param [in] the end handle of the characteristic, or the service, whichever comes first. * @param [in] the end handle of the characteristic, or the service, whichever comes first.
*/ */
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) { bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilter) const {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
// If this is the last handle then there are no descriptors NimBLETaskData taskData(const_cast<NimBLERemoteCharacteristic*>(this));
if (m_handle == getRemoteService()->getEndHandle()) { desc_filter_t filter = {uuidFilter, &taskData};
return true;
}
int rc = 0; int rc = ble_gattc_disc_all_dscs(getClient()->getConnHandle(),
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; getHandle(),
// 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(), 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,
m_endHandle,
NimBLERemoteCharacteristic::descriptorDiscCB, NimBLERemoteCharacteristic::descriptorDiscCB,
&filter); &filter);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_dscs: 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; return false;
} }
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
rc = taskData.m_flags;
if (taskData.rc != 0) { if (rc == 0 || rc == BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "Failed to retrieve descriptors; startHandle:%d endHandle:%d taskData.rc=%d", NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size());
m_handle, m_endHandle, taskData.rc); return true;
} }
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size()); NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return (taskData.rc == 0); return false;
} // retrieveDescriptors } // retrieveDescriptors
/** /**
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic. * @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
* @param [in] uuid The UUID of the descriptor to find. * @param [in] uuid The UUID of the descriptor to find.
* @return The Remote descriptor (if present) or null if not present. * @return The Remote descriptor (if present) or nullptr if not present.
*/ */
NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID &uuid) { NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
NimBLERemoteDescriptor* pDsc = nullptr;
size_t prev_size = m_vDescriptors.size();
for(auto &it: m_descriptorVector) { for (const auto& it : m_vDescriptors) {
if (it->getUUID() == uuid) { if (it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str()); pDsc = it;
return it; goto Done;
} }
} }
size_t prev_size = m_descriptorVector.size();
if (retrieveDescriptors(&uuid)) { if (retrieveDescriptors(&uuid)) {
if(m_descriptorVector.size() > prev_size) { if (m_vDescriptors.size() > prev_size) {
return m_descriptorVector.back(); pDsc = m_vDescriptors.back();
goto Done;
} }
// If the request was successful but 16/32 bit descriptor not found // If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid. // try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 || if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
uuid.bitSize() == BLE_UUID_TYPE_32)
{
NimBLEUUID uuid128(uuid); NimBLEUUID uuid128(uuid);
uuid128.to128(); uuid128.to128();
return getDescriptor(uuid128); if (retrieveDescriptors(&uuid128)) {
if (m_vDescriptors.size() > prev_size) {
pDsc = m_vDescriptors.back();
}
}
} else {
// If the request was successful but the 128 bit uuid not found
// try again with the 16 bit uuid.
NimBLEUUID uuid16(uuid);
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
if (retrieveDescriptors(&uuid16)) {
if (m_vDescriptors.size() > prev_size) {
pDsc = m_vDescriptors.back();
}
}
}
} }
} }
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found"); Done:
return nullptr; NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: %sfound", pDsc ? "" : "not ");
return pDsc;
} // getDescriptor } // getDescriptor
/** /**
* @brief Get a pointer to the vector of found descriptors. * @brief Get a pointer to the vector of found descriptors.
* @param [in] refresh If true the current descriptor vector will be cleared and\n * @param [in] refresh If true the current descriptor vector will be cleared and\n
@ -334,240 +189,39 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
* of this characteristic. * of this characteristic.
* @return A pointer to the vector of descriptors for this characteristic. * @return A pointer to the vector of descriptors for this characteristic.
*/ */
std::vector<NimBLERemoteDescriptor*>* NimBLERemoteCharacteristic::getDescriptors(bool refresh) { const std::vector<NimBLERemoteDescriptor*>& NimBLERemoteCharacteristic::getDescriptors(bool refresh) const {
if (refresh) { if (refresh) {
deleteDescriptors(); deleteDescriptors();
retrieveDescriptors();
}
if (!retrieveDescriptors()) { return m_vDescriptors;
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors");
}
else{
NIMBLE_LOGI(LOG_TAG, "Found %d descriptor(s)", m_descriptorVector.size());
}
}
return &m_descriptorVector;
} // getDescriptors } // getDescriptors
/** /**
* @brief Get iterator to the beginning of the vector of remote descriptor pointers. * @brief Get iterator to the beginning of the vector of remote descriptor pointers.
* @return An iterator to the beginning of the vector of remote descriptor pointers. * @return An iterator to the beginning of the vector of remote descriptor pointers.
*/ */
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::begin() { std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::begin() const {
return m_descriptorVector.begin(); return m_vDescriptors.begin();
} }
/** /**
* @brief Get iterator to the end of the vector of remote descriptor pointers. * @brief Get iterator to the end of the vector of remote descriptor pointers.
* @return An iterator to the end of the vector of remote descriptor pointers. * @return An iterator to the end of the vector of remote descriptor pointers.
*/ */
std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::end() { std::vector<NimBLERemoteDescriptor*>::iterator NimBLERemoteCharacteristic::end() const {
return m_descriptorVector.end(); return m_vDescriptors.end();
} }
/**
* @brief Get the handle for this characteristic.
* @return The handle for this characteristic.
*/
uint16_t NimBLERemoteCharacteristic::getHandle() {
return m_handle;
} // getHandle
/**
* @brief Get the handle for this characteristics definition.
* @return The handle for this characteristic definition.
*/
uint16_t NimBLERemoteCharacteristic::getDefHandle() {
return m_defHandle;
} // getDefHandle
/** /**
* @brief Get the remote service associated with this characteristic. * @brief Get the remote service associated with this characteristic.
* @return The remote service associated with this characteristic. * @return The remote service associated with this characteristic.
*/ */
NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() { const NimBLERemoteService* NimBLERemoteCharacteristic::getRemoteService() const {
return m_pRemoteService; return m_pRemoteService;
} // getRemoteService } // getRemoteService
/**
* @brief Get the UUID for this characteristic.
* @return The UUID for this characteristic.
*/
NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Get the value of the remote characteristic.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
std::string NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
portENTER_CRITICAL(&m_valMux);
std::string value = m_value;
if(timestamp != nullptr) {
*timestamp = m_timestamp;
}
portEXIT_CRITICAL(&m_valMux);
return value;
}
/**
* @brief Read an unsigned 16 bit value
* @return The unsigned 16 bit value.
* @deprecated Use readValue<uint16_t>().
*/
uint16_t NimBLERemoteCharacteristic::readUInt16() {
return readValue<uint16_t>();
} // readUInt16
/**
* @brief Read an unsigned 32 bit value.
* @return the unsigned 32 bit value.
* @deprecated Use readValue<uint32_t>().
*/
uint32_t NimBLERemoteCharacteristic::readUInt32() {
return readValue<uint32_t>();
} // readUInt32
/**
* @brief Read a byte value
* @return The value as a byte
* @deprecated Use readValue<uint8_t>().
*/
uint8_t NimBLERemoteCharacteristic::readUInt8() {
return readValue<uint8_t>();
} // readUInt8
/**
* @brief Read a float value.
* @return the float value.
*/
float NimBLERemoteCharacteristic::readFloat() {
return readValue<float>();
} // readFloat
/**
* @brief Read the value of the remote characteristic.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x",
getUUID().toString().c_str(), getHandle(), getHandle());
NimBLEClient* pClient = getRemoteService()->getClient();
std::string value;
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return value;
}
int rc = 0;
int retryCount = 1;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(),0, &value};
do {
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
NimBLERemoteCharacteristic::onReadCB,
&taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
return value;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc){
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
// Characteristic is not long-readable, return with what we have.
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGI(LOG_TAG, "Attribute not long");
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection())
break;
/* Else falls through. */
default:
NIMBLE_LOGE(LOG_TAG, "<< readValue rc=%d", rc);
return value;
}
} while(rc != 0 && retryCount--);
time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = value;
m_timestamp = t;
if(timestamp != nullptr) {
*timestamp = m_timestamp;
}
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< readValue length: %d rc=%d", value.length(), rc);
return value;
} // readValue
/**
* @brief Callback for characteristic read operation.
* @return success == 0 or error code.
*/
int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
uint16_t conn_id = characteristic->getRemoteService()->getClient()->getConnId();
if(conn_id != conn_handle) {
return 0;
}
NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
std::string *strBuf = (std::string*)pTaskData->buf;
int rc = error->status;
if(rc == 0) {
if(attr) {
if(((*strBuf).length() + attr->om->om_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else {
NIMBLE_LOGD(LOG_TAG, "Got %d bytes", attr->om->om_len);
(*strBuf) += std::string((char*) attr->om->om_data, attr->om->om_len);
return 0;
}
}
}
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
return rc;
}
/** /**
* @brief Subscribe or unsubscribe for notifications or indications. * @brief Subscribe or unsubscribe for notifications or indications.
* @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications. * @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
@ -576,11 +230,10 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
* If NULL is provided then no callback is performed. * If NULL is provided then no callback is performed.
* @return false if writing to the descriptor failed. * @return false if writing to the descriptor failed.
*/ */
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) { bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) const {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val); NIMBLE_LOGD(LOG_TAG, ">> setNotify()");
m_notifyCallback = notifyCallback; m_notifyCallback = notifyCallback;
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902)); NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
if (desc == nullptr) { if (desc == nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found"); NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
@ -588,11 +241,9 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
} }
NIMBLE_LOGD(LOG_TAG, "<< setNotify()"); NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
return desc->writeValue(reinterpret_cast<uint8_t*>(&val), 2, response);
return desc->writeValue((uint8_t *)&val, 2, response);
} // setNotify } // setNotify
/** /**
* @brief Subscribe for notifications or indications. * @brief Subscribe for notifications or indications.
* @param [in] notifications If true, subscribe for notifications, false subscribe for indications. * @param [in] notifications If true, subscribe for notifications, false subscribe for indications.
@ -601,89 +252,125 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
* If NULL is provided then no callback is performed. * If NULL is provided then no callback is performed.
* @return false if writing to the descriptor failed. * @return false if writing to the descriptor failed.
*/ */
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) { bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) const {
if(notifications) { return setNotify(notifications ? 0x01 : 0x02, notifyCallback, response);
return setNotify(0x01, notifyCallback, response);
} else {
return setNotify(0x02, notifyCallback, response);
}
} // subscribe } // subscribe
/** /**
* @brief Unsubscribe for notifications or indications. * @brief Unsubscribe for notifications or indications.
* @param [in] response bool if true, require a write response from the descriptor write operation. * @param [in] response bool if true, require a write response from the descriptor write operation.
* @return false if writing to the descriptor failed. * @return false if writing to the descriptor failed.
*/ */
bool NimBLERemoteCharacteristic::unsubscribe(bool response) { bool NimBLERemoteCharacteristic::unsubscribe(bool response) const {
return setNotify(0x00, nullptr, response); return setNotify(0x00, nullptr, response);
} // unsubscribe } // unsubscribe
/**
* @brief backward-compatibility method for subscribe/unsubscribe notifications/indications
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we
* will unregister for notifications.
* @param [in] notifications If true, register for notifications, false register for indications.
* @param [in] response If true, require a write response from the descriptor write operation.
* @return true if successful.
* @deprecated Use subscribe() / unsubscribe() instead.
*/
bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications, bool response) {
bool success;
if(notifyCallback != nullptr) {
success = subscribe(notifications, notifyCallback, response);
} else {
success = unsubscribe(response);
}
return success;
} // registerForNotify
/** /**
* @brief Delete the descriptors in the descriptor vector. * @brief Delete the descriptors in the descriptor vector.
* @details We maintain a vector called m_descriptorVector that contains pointers to NimBLERemoteDescriptors * @details We maintain a vector called m_vDescriptors that contains pointers to NimBLERemoteDescriptors
* object references. Since we allocated these in this class, we are also responsible for deleting * object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that. * them. This method does just that.
*/ */
void NimBLERemoteCharacteristic::deleteDescriptors() { void NimBLERemoteCharacteristic::deleteDescriptors() const {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors"); NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors");
for(auto &it: m_descriptorVector) { for (const auto& it : m_vDescriptors) {
delete it; delete it;
} }
m_descriptorVector.clear(); std::vector<NimBLERemoteDescriptor*>().swap(m_vDescriptors);
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors"); NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors");
} // deleteDescriptors } // deleteDescriptors
/** /**
* @brief Delete descriptor by UUID * @brief Delete descriptor by UUID
* @param [in] uuid The UUID of the descriptor to be deleted. * @param [in] uuid The UUID of the descriptor to be deleted.
* @return Number of descriptors left in the vector. * @return Number of descriptors left in the vector.
*/ */
size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) { size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor"); NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor");
for(auto it = m_descriptorVector.begin(); it != m_descriptorVector.end(); ++it) { for (auto it = m_vDescriptors.begin(); it != m_vDescriptors.end(); ++it) {
if ((*it)->getUUID() == uuid) { if ((*it)->getUUID() == uuid) {
delete *it; delete (*it);
m_descriptorVector.erase(it); m_vDescriptors.erase(it);
break; break;
} }
} }
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor"); NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor");
return m_vDescriptors.size();
return m_descriptorVector.size();
} // deleteDescriptor } // deleteDescriptor
/**
* @brief Does the characteristic support value broadcasting?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canBroadcast() const {
return (m_properties & BLE_GATT_CHR_PROP_BROADCAST) != 0;
};
/**
* @brief Does the characteristic support reading?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canRead() const {
return (m_properties & BLE_GATT_CHR_PROP_READ);
};
/**
* @brief Does the characteristic support writing without a response?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canWriteNoResponse() const {
return (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP);
};
/**
* @brief Does the characteristic support writing?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canWrite() const {
return (m_properties & BLE_GATT_CHR_PROP_WRITE);
};
/**
* @brief Does the characteristic support reading with encryption?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canNotify() const {
return (m_properties & BLE_GATT_CHR_PROP_NOTIFY);
};
/**
* @brief Does the characteristic support indication?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canIndicate() const {
return (m_properties & BLE_GATT_CHR_PROP_INDICATE);
};
/**
* @brief Does the characteristic support signed writing?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::canWriteSigned() const {
return (m_properties & BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE);
};
/**
* @brief Does the characteristic support extended properties?
* @return True if supported.
*/
bool NimBLERemoteCharacteristic::hasExtendedProps() const {
return (m_properties & BLE_GATT_CHR_PROP_EXTENDED);
};
/** /**
* @brief Convert a NimBLERemoteCharacteristic to a string representation; * @brief Convert a NimBLERemoteCharacteristic to a string representation;
* @return a String representation. * @return a String representation.
*/ */
std::string NimBLERemoteCharacteristic::toString() { std::string NimBLERemoteCharacteristic::toString() const {
std::string res = "Characteristic: uuid: " + m_uuid.toString(); std::string res = "Characteristic: uuid: " + m_uuid.toString();
char val[6]; char val[6];
res += ", handle: "; res += ", handle: ";
@ -694,131 +381,18 @@ std::string NimBLERemoteCharacteristic::toString() {
res += val; res += val;
res += ", props: "; res += ", props: ";
res += " 0x"; res += " 0x";
snprintf(val, sizeof(val), "%02x", m_charProp); snprintf(val, sizeof(val), "%02x", m_properties);
res += val; res += val;
for(auto &it: m_descriptorVector) { for (const auto& it : m_vDescriptors) {
res += "\n" + it->toString(); res += "\n" + it->toString();
} }
return res; return res;
} // toString } // toString
NimBLEClient* NimBLERemoteCharacteristic::getClient() const {
return getRemoteService()->getClient();
} // getClient
/** #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
* @brief Write the new value for the characteristic.
* @param [in] newValue The new value to write.
* @param [in] response Do we expect a response?
* @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(), newValue.length(), response);
} // writeValue
/**
* @brief Write the new value for the characteristic from a data buffer.
* @param [in] data A pointer to a data buffer.
* @param [in] length The length of the data in the data buffer.
* @param [in] response Whether we require a response from the write.
* @return false if not connected or cant perform write for some reason.
*/
bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> writeValue(), length: %d", length);
NimBLEClient* pClient = getRemoteService()->getClient();
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return false;
}
int rc = 0;
int retryCount = 1;
uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3;
// Check if the data length is longer than we can write in one connection event.
// If so we must do a long write which requires a response.
if(length <= mtu && !response) {
rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
return (rc==0);
}
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
do {
if(length > mtu) {
NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
NimBLERemoteCharacteristic::onWriteCB,
&taskData);
} else {
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
data, length,
NimBLERemoteCharacteristic::onWriteCB,
&taskData);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc);
return false;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc){
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
retryCount++;
length = mtu;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection())
break;
/* Else falls through. */
default:
NIMBLE_LOGE(LOG_TAG, "<< writeValue, rc: %d", rc);
return false;
}
} while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d", rc);
return (rc == 0);
} // writeValue
/**
* @brief Callback for characteristic write operation.
* @return success == 0 or error code.
*/
int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0;
}
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
pTaskData->rc = error->status;
xTaskNotifyGive(pTaskData->task);
return 0;
}
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -12,161 +12,67 @@
* Author: kolban * Author: kolban
*/ */
#ifndef COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ #ifndef NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
#define COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ #define NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteService.h"
#include "NimBLERemoteDescriptor.h"
# include "NimBLERemoteValueAttribute.h"
# include <vector> # include <vector>
# include <functional> # include <functional>
class NimBLERemoteService; class NimBLERemoteService;
class NimBLERemoteDescriptor; class NimBLERemoteDescriptor;
typedef std::function<void (NimBLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify)> notify_callback;
typedef struct {
const NimBLEUUID *uuid;
void *task_data;
} desc_filter_t;
/** /**
* @brief A model of a remote %BLE characteristic. * @brief A model of a remote BLE characteristic.
*/ */
class NimBLERemoteCharacteristic { class NimBLERemoteCharacteristic : public NimBLERemoteValueAttribute {
public: public:
~NimBLERemoteCharacteristic(); std::string toString() const;
const NimBLERemoteService* getRemoteService() const;
void deleteDescriptors() const;
size_t deleteDescriptor(const NimBLEUUID& uuid) const;
bool canBroadcast() const;
bool canRead() const;
bool canWriteNoResponse() const;
bool canWrite() const;
bool canNotify() const;
bool canIndicate() const;
bool canWriteSigned() const;
bool hasExtendedProps() const;
NimBLEClient* getClient() const override;
// Public member functions typedef std::function<void(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)> notify_callback;
bool canBroadcast();
bool canIndicate();
bool canNotify();
bool canRead();
bool canWrite();
bool canWriteNoResponse();
std::vector<NimBLERemoteDescriptor*>::iterator begin();
std::vector<NimBLERemoteDescriptor*>::iterator end();
NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid);
std::vector<NimBLERemoteDescriptor*>* getDescriptors(bool refresh = false);
void deleteDescriptors();
size_t deleteDescriptor(const NimBLEUUID &uuid);
uint16_t getHandle();
uint16_t getDefHandle();
NimBLEUUID getUUID();
std::string readValue(time_t *timestamp = nullptr);
/** bool subscribe(bool notifications = true, const notify_callback notifyCallback = nullptr, bool response = true) const;
* @brief A template to convert the remote characteristic data to <type\>. bool unsubscribe(bool response = true) const;
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = readValue(timestamp);
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()"))); std::vector<NimBLERemoteDescriptor*>::iterator begin() const;
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()"))); std::vector<NimBLERemoteDescriptor*>::iterator end() const;
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()"))); NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID& uuid) const;
float readFloat() __attribute__ ((deprecated("Use template readValue<float>()"))); const std::vector<NimBLERemoteDescriptor*>& getDescriptors(bool refresh = false) const;
std::string getValue(time_t *timestamp = nullptr);
/**
* @brief A template to convert the remote characteristic data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>getValue<type>(&timestamp, skipSizeCheck);</tt>
*/
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue(timestamp);
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
bool subscribe(bool notifications = true,
notify_callback notifyCallback = nullptr,
bool response = false);
bool unsubscribe(bool response = false);
bool registerForNotify(notify_callback notifyCallback,
bool notifications = true,
bool response = true)
__attribute__ ((deprecated("Use subscribe()/unsubscribe()")));
bool writeValue(const uint8_t* data,
size_t length,
bool response = false);
bool writeValue(const std::string &newValue,
bool response = false);
/**
* @brief Convenience template to set the remote characteristic value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
*/
template<typename T>
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
std::string toString();
NimBLERemoteService* getRemoteService();
private: private:
NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteservice, const struct ble_gatt_chr *chr);
friend class NimBLEClient; friend class NimBLEClient;
friend class NimBLERemoteService; friend class NimBLERemoteService;
friend class NimBLERemoteDescriptor;
// Private member functions NimBLERemoteCharacteristic(const NimBLERemoteService* pRemoteService, const ble_gatt_chr* chr);
bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true); ~NimBLERemoteCharacteristic();
bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
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 bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true) const;
NimBLEUUID m_uuid; bool retrieveDescriptors(const NimBLEUUID* uuidFilter = nullptr) const;
uint8_t m_charProp;
uint16_t m_handle; static int descriptorDiscCB(
uint16_t m_defHandle; uint16_t conn_handle, const ble_gatt_error* error, uint16_t chr_val_handle, const ble_gatt_dsc* dsc, void* arg);
uint16_t m_endHandle;
NimBLERemoteService* m_pRemoteService; const NimBLERemoteService* m_pRemoteService{nullptr};
std::string m_value; uint8_t m_properties{0};
notify_callback m_notifyCallback; mutable notify_callback m_notifyCallback{nullptr};
time_t m_timestamp; mutable std::vector<NimBLERemoteDescriptor*> m_vDescriptors{};
portMUX_TYPE m_valMux;
// We maintain a vector of descriptors owned by this characteristic.
std::vector<NimBLERemoteDescriptor*> m_descriptorVector;
}; // NimBLERemoteCharacteristic }; // NimBLERemoteCharacteristic
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* CONFIG_BT_ENABLED */ #endif /* NIMBLE_CPP_REMOTE_CHARACTERISTIC_H_ */
#endif /* COMPONENTS_NIMBLEREMOTECHARACTERISTIC_H_ */

View File

@ -11,220 +11,35 @@
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include "NimBLERemoteDescriptor.h" # include "NimBLERemoteDescriptor.h"
#include "NimBLEUtils.h" # include "NimBLERemoteCharacteristic.h"
#include "NimBLELog.h"
static const char* LOG_TAG = "NimBLERemoteDescriptor";
/** /**
* @brief Remote descriptor constructor. * @brief Remote descriptor constructor.
* @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to. * @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to.
* @param [in] dsc A pointer to the struct that contains the descriptor information. * @param [in] dsc A pointer to the struct that contains the descriptor information.
*/ */
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic, NimBLERemoteDescriptor::NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc) const ble_gatt_dsc* dsc)
{ : NimBLERemoteValueAttribute{dsc->uuid, dsc->handle}, m_pRemoteCharacteristic{pRemoteCharacteristic} {} // NimBLERemoteDescriptor
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()");
switch (dsc->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(dsc->uuid.u16.value);
break;
case BLE_UUID_TYPE_32:
m_uuid = NimBLEUUID(dsc->uuid.u32.value);
break;
case BLE_UUID_TYPE_128:
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&dsc->uuid.u128));
break;
default:
break;
}
m_handle = dsc->handle;
m_pRemoteCharacteristic = pRemoteCharacteristic;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str());
}
/**
* @brief Retrieve the handle associated with this remote descriptor.
* @return The handle associated with this remote descriptor.
*/
uint16_t NimBLERemoteDescriptor::getHandle() {
return m_handle;
} // getHandle
/** /**
* @brief Get the characteristic that owns this descriptor. * @brief Get the characteristic that owns this descriptor.
* @return The characteristic that owns this descriptor. * @return The characteristic that owns this descriptor.
*/ */
NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() { NimBLERemoteCharacteristic* NimBLERemoteDescriptor::getRemoteCharacteristic() const {
return m_pRemoteCharacteristic; return const_cast<NimBLERemoteCharacteristic*>(m_pRemoteCharacteristic);
} // getRemoteCharacteristic } // getRemoteCharacteristic
/**
* @brief Retrieve the UUID associated this remote descriptor.
* @return The UUID associated this remote descriptor.
*/
NimBLEUUID NimBLERemoteDescriptor::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Read a byte value
* @return The value as a byte
* @deprecated Use readValue<uint8_t>().
*/
uint8_t NimBLERemoteDescriptor::readUInt8() {
std::string value = readValue();
if (value.length() >= 1) {
return (uint8_t) value[0];
}
return 0;
} // readUInt8
/**
* @brief Read an unsigned 16 bit value
* @return The unsigned 16 bit value.
* @deprecated Use readValue<uint16_t>().
*/
uint16_t NimBLERemoteDescriptor::readUInt16() {
std::string value = readValue();
if (value.length() >= 2) {
return *(uint16_t*) value.data();
}
return 0;
} // readUInt16
/**
* @brief Read an unsigned 32 bit value.
* @return the unsigned 32 bit value.
* @deprecated Use readValue<uint32_t>().
*/
uint32_t NimBLERemoteDescriptor::readUInt32() {
std::string value = readValue();
if (value.length() >= 4) {
return *(uint32_t*) value.data();
}
return 0;
} // readUInt32
/**
* @brief Read the value of the remote descriptor.
* @return The value of the remote descriptor.
*/
std::string NimBLERemoteDescriptor::readValue() {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
std::string value;
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return value;
}
int rc = 0;
int retryCount = 1;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(),0, &value};
do {
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
NimBLERemoteDescriptor::onReadCB,
&taskData);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to read descriptor; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
return value;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc){
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
// Descriptor is not long-readable, return with what we have.
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGI(LOG_TAG, "Attribute not long");
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection())
break;
/* Else falls through. */
default:
return value;
}
} while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %d rc=%d", value.length(), rc);
return value;
} // readValue
/**
* @brief Callback for Descriptor read operation.
* @return success == 0 or error code.
*/
int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)pTaskData->pATT;
uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId();
if(conn_id != conn_handle){
return 0;
}
NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
std::string *strBuf = (std::string*)pTaskData->buf;
int rc = error->status;
if(rc == 0) {
if(attr) {
if(((*strBuf).length() + attr->om->om_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} else {
NIMBLE_LOGD(LOG_TAG, "Got %d bytes", attr->om->om_len);
(*strBuf) += std::string((char*) attr->om->om_data, attr->om->om_len);
return 0;
}
}
}
pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
return rc;
}
/** /**
* @brief Return a string representation of this Remote Descriptor. * @brief Return a string representation of this Remote Descriptor.
* @return A string representation of this Remote Descriptor. * @return A string representation of this Remote Descriptor.
*/ */
std::string NimBLERemoteDescriptor::toString() { std::string NimBLERemoteDescriptor::toString() const {
std::string res = "Descriptor: uuid: " + getUUID().toString(); std::string res = "Descriptor: uuid: " + getUUID().toString();
char val[6]; char val[6];
res += ", handle: "; res += ", handle: ";
@ -234,121 +49,8 @@ std::string NimBLERemoteDescriptor::toString() {
return res; return res;
} // toString } // toString
NimBLEClient* NimBLERemoteDescriptor::getClient() const {
/** return m_pRemoteCharacteristic->getClient();
* @brief Callback for descriptor write operation.
* @return success == 0 or error code.
*/
int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)pTaskData->pATT;
if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0;
} }
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
pTaskData->rc = error->status;
xTaskNotifyGive(pTaskData->task);
return 0;
}
/**
* @brief Write data to the BLE Remote Descriptor.
* @param [in] data The data to send to the remote descriptor.
* @param [in] length The length of the data to send.
* @param [in] response True if we expect a write response.
* @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor writeValue: %s", toString().c_str());
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
// Check to see that we are connected.
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return false;
}
int rc = 0;
int retryCount = 1;
uint16_t mtu = ble_att_mtu(pClient->getConnId()) - 3;
// Check if the data length is longer than we can write in 1 connection event.
// If so we must do a long write which requires a response.
if(length <= mtu && !response) {
rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
return (rc == 0);
}
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
do {
if(length > mtu) {
NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
NimBLERemoteDescriptor::onWriteCB,
&taskData);
} else {
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
data, length,
NimBLERemoteDescriptor::onWriteCB,
&taskData);
}
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc);
return false;
}
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc) {
case 0:
case BLE_HS_EDONE:
rc = 0;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_ATTR_NOT_LONG):
NIMBLE_LOGE(LOG_TAG, "Long write not supported by peer; Truncating length to %d", mtu);
retryCount++;
length = mtu;
break;
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC):
if (retryCount && pClient->secureConnection())
break;
/* Else falls through. */
default:
return false;
}
} while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor writeValue, rc: %d",rc);
return (rc == 0);
} // writeValue
/**
* @brief Write data represented as a string to the BLE Remote Descriptor.
* @param [in] newValue The data to send to the remote descriptor.
* @param [in] response True if we expect a response.
* @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const std::string &newValue, bool response) {
return writeValue((uint8_t*) newValue.data(), newValue.length(), response);
} // writeValue
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -12,75 +12,34 @@
* Author: kolban * Author: kolban
*/ */
#ifndef COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ #ifndef NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
#define COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ #define NIMBLE_CPP_REMOTE_DESCRIPTOR_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLERemoteCharacteristic.h" # include "NimBLERemoteValueAttribute.h"
class NimBLERemoteCharacteristic; class NimBLERemoteCharacteristic;
class NimBLEClient;
/** /**
* @brief A model of remote %BLE descriptor. * @brief A model of remote BLE descriptor.
*/ */
class NimBLERemoteDescriptor { class NimBLERemoteDescriptor : public NimBLERemoteValueAttribute {
public: public:
uint16_t getHandle(); NimBLERemoteCharacteristic* getRemoteCharacteristic() const;
NimBLERemoteCharacteristic* getRemoteCharacteristic(); std::string toString(void) const;
NimBLEUUID getUUID(); NimBLEClient* getClient() const override;
std::string readValue();
/**
* @brief A template to convert the remote descriptor data to <type\>.
* @tparam T The type to convert the data to.
* @param [in] skipSizeCheck If true it will skip checking if the data size is less than <tt>sizeof(<type\>)</tt>.
* @return The data converted to <type\> or NULL if skipSizeCheck is false and the data is
* less than <tt>sizeof(<type\>)</tt>.
* @details <b>Use:</b> <tt>readValue<type>(skipSizeCheck);</tt>
*/
template<typename T>
T readValue(bool skipSizeCheck = false) {
std::string value = readValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue<uint8_t>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false);
/**
* @brief Convenience template to set the remote descriptor value to <type\>val.
* @param [in] s The value to write.
* @param [in] response True == request write response.
*/
template<typename T>
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
private: private:
friend class NimBLERemoteCharacteristic; friend class NimBLERemoteCharacteristic;
NimBLERemoteDescriptor (NimBLERemoteCharacteristic* pRemoteCharacteristic, NimBLERemoteDescriptor(const NimBLERemoteCharacteristic* pRemoteCharacteristic, const ble_gatt_dsc* dsc);
const struct ble_gatt_dsc *dsc); ~NimBLERemoteDescriptor() = default;
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
uint16_t m_handle; const NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
NimBLEUUID m_uuid;
NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
}; };
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* CONFIG_BT_ENABLED */ #endif /* NIMBLE_CPP_REMOTE_DESCRIPTOR_H_ */
#endif /* COMPONENTS_NIMBLEREMOTEDESCRIPTOR_H_ */

View File

@ -11,17 +11,19 @@
* Created on: Jul 8, 2017 * Created on: Jul 8, 2017
* Author: kolban * Author: kolban
*/ */
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
# include "NimBLERemoteService.h" # include "NimBLERemoteService.h"
# include "NimBLERemoteCharacteristic.h"
# include "NimBLEClient.h"
# include "NimBLEAttValue.h"
# include "NimBLEUtils.h" # include "NimBLEUtils.h"
#include "NimBLEDevice.h"
# include "NimBLELog.h" # include "NimBLELog.h"
# include <climits>
static const char* LOG_TAG = "NimBLERemoteService"; static const char* LOG_TAG = "NimBLERemoteService";
/** /**
@ -29,28 +31,8 @@ static const char* LOG_TAG = "NimBLERemoteService";
* @param [in] pClient A pointer to the client this belongs to. * @param [in] pClient A pointer to the client this belongs to.
* @param [in] service A pointer to the structure with the service information. * @param [in] service A pointer to the structure with the service information.
*/ */
NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) { NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const ble_gatt_svc* service)
: NimBLEAttribute{service->uuid, service->start_handle}, m_pClient{pClient}, m_endHandle{service->end_handle} {}
NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteService()");
m_pClient = pClient;
switch (service->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(service->uuid.u16.value);
break;
case BLE_UUID_TYPE_32:
m_uuid = NimBLEUUID(service->uuid.u32.value);
break;
case BLE_UUID_TYPE_128:
m_uuid = NimBLEUUID(const_cast<ble_uuid128_t*>(&service->uuid.u128));
break;
default:
break;
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str());
}
/** /**
* @brief When deleting the service make sure we delete all characteristics and descriptors. * @brief When deleting the service make sure we delete all characteristics and descriptors.
@ -59,333 +41,261 @@ NimBLERemoteService::~NimBLERemoteService() {
deleteCharacteristics(); deleteCharacteristics();
} }
/** /**
* @brief Get iterator to the beginning of the vector of remote characteristic pointers. * @brief Get iterator to the beginning of the vector of remote characteristic pointers.
* @return An iterator to the beginning of the vector of remote characteristic pointers. * @return An iterator to the beginning of the vector of remote characteristic pointers.
*/ */
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::begin() { std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::begin() const {
return m_characteristicVector.begin(); return m_vChars.begin();
} }
/** /**
* @brief Get iterator to the end of the vector of remote characteristic pointers. * @brief Get iterator to the end of the vector of remote characteristic pointers.
* @return An iterator to the end of the vector of remote characteristic pointers. * @return An iterator to the end of the vector of remote characteristic pointers.
*/ */
std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() { std::vector<NimBLERemoteCharacteristic*>::iterator NimBLERemoteService::end() const {
return m_characteristicVector.end(); return m_vChars.end();
} }
/** /**
* @brief Get the remote characteristic object for the characteristic UUID. * @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid. * @param [in] uuid Remote characteristic uuid.
* @return A pointer to the remote characteristic object. * @return A pointer to the remote characteristic object.
*/ */
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) { NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) const {
return getCharacteristic(NimBLEUUID(uuid)); return getCharacteristic(NimBLEUUID(uuid));
} // getCharacteristic } // getCharacteristic
/** /**
* @brief Get the characteristic object for the UUID. * @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid. * @param [in] uuid Characteristic uuid.
* @return A pointer to the characteristic object, or nullptr if not found. * @return A pointer to the characteristic object, or nullptr if not found.
*/ */
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) { NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str());
NimBLERemoteCharacteristic* pChar = nullptr;
size_t prev_size = m_vChars.size();
for(auto &it: m_characteristicVector) { for (const auto& it : m_vChars) {
if (it->getUUID() == uuid) { if (it->getUUID() == uuid) {
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str()); pChar = it;
return it; goto Done;
} }
} }
size_t prev_size = m_characteristicVector.size();
if (retrieveCharacteristics(&uuid)) { if (retrieveCharacteristics(&uuid)) {
if(m_characteristicVector.size() > prev_size) { if (m_vChars.size() > prev_size) {
return m_characteristicVector.back(); pChar = m_vChars.back();
goto Done;
} }
// If the request was successful but 16/32 bit characteristic not found // If the request was successful but 16/32 bit uuid not found
// try again with the 128 bit uuid. // try again with the 128 bit uuid.
if(uuid.bitSize() == BLE_UUID_TYPE_16 || if (uuid.bitSize() == BLE_UUID_TYPE_16 || uuid.bitSize() == BLE_UUID_TYPE_32) {
uuid.bitSize() == BLE_UUID_TYPE_32)
{
NimBLEUUID uuid128(uuid); NimBLEUUID uuid128(uuid);
uuid128.to128(); uuid128.to128();
return getCharacteristic(uuid128); if (retrieveCharacteristics(&uuid128)) {
if (m_vChars.size() > prev_size) {
pChar = m_vChars.back();
}
}
} else {
// If the request was successful but the 128 bit uuid not found
// try again with the 16 bit uuid.
NimBLEUUID uuid16(uuid);
uuid16.to16();
// if the uuid was 128 bit but not of the BLE base type this check will fail
if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
if (retrieveCharacteristics(&uuid16)) {
if (m_vChars.size() > prev_size) {
pChar = m_vChars.back();
}
}
}
} }
} }
NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found"); Done:
return nullptr; NIMBLE_LOGD(LOG_TAG, "<< Characteristic %sfound", pChar ? "" : "not ");
return pChar;
} // getCharacteristic } // getCharacteristic
/** /**
* @brief Get a pointer to the vector of found characteristics. * @brief Get a pointer to the vector of found characteristics.
* @param [in] refresh If true the current characteristics vector will cleared and * @param [in] refresh If true the current characteristics vector will cleared and
* all characteristics for this service retrieved from the peripheral. * all characteristics for this service retrieved from the peripheral.
* If false the vector will be returned with the currently stored characteristics of this service. * If false the vector will be returned with the currently stored characteristics of this service.
* @return A pointer to the vector of descriptors for this characteristic. * @return A read-only reference to the vector of characteristics retrieved for this service.
*/ */
std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics(bool refresh) { const std::vector<NimBLERemoteCharacteristic*>& NimBLERemoteService::getCharacteristics(bool refresh) const {
if (refresh) { if (refresh) {
deleteCharacteristics(); deleteCharacteristics();
retrieveCharacteristics();
}
if (!retrieveCharacteristics()) { return m_vChars;
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics");
}
else{
NIMBLE_LOGI(LOG_TAG, "Found %d characteristics", m_characteristicVector.size());
}
}
return &m_characteristicVector;
} // getCharacteristics } // getCharacteristics
/** /**
* @brief Callback for Characterisic discovery. * @brief Callback for Characteristic discovery.
* @return success == 0 or error code. * @return success == 0 or error code.
*/ */
int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error *error, const ble_gatt_error* error,
const struct ble_gatt_chr *chr, void *arg) const ble_gatt_chr* chr,
{ void* arg) {
NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d", NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >>");
error->status, (error->status == 0) ? chr->val_handle : -1); auto pTaskData = (NimBLETaskData*)arg;
const auto pSvc = (NimBLERemoteService*)pTaskData->m_pInstance;
ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteService *service = (NimBLERemoteService*)pTaskData->pATT; if (error->status == BLE_HS_ENOTCONN) {
NIMBLE_LOGE(LOG_TAG, "<< Characteristic Discovery; Not connected");
NimBLEUtils::taskRelease(*pTaskData, error->status);
return error->status;
}
// Make sure the discovery is for this device // Make sure the discovery is for this device
if(service->getClient()->getConnId() != conn_handle){ if (pSvc->getClient()->getConnHandle() != conn_handle) {
return 0; return 0;
} }
if (error->status == 0) { if (error->status == 0) {
// Found a service - add it to the vector pSvc->m_vChars.push_back(new NimBLERemoteCharacteristic(pSvc, chr));
NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr);
service->m_characteristicVector.push_back(pRemoteCharacteristic);
return 0; return 0;
} }
if(error->status == BLE_HS_EDONE) { NimBLEUtils::taskRelease(*pTaskData, error->status);
pTaskData->rc = 0; NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery");
} else {
NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s",
error->status,
NimBLEUtils::returnCodeToString(error->status));
pTaskData->rc = error->status;
}
xTaskNotifyGive(pTaskData->task);
NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered");
return error->status; return error->status;
} }
/** /**
* @brief Retrieve all the characteristics for this service. * @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics. * This function will not return until we have all the characteristics.
* @return True if successful. * @return True if successful.
*/ */
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) { bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter) const {
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()");
int rc = 0; int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr}; NimBLETaskData taskData(const_cast<NimBLERemoteService*>(this));
if(uuid_filter == nullptr) { if (uuidFilter == nullptr) {
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(), rc = ble_gattc_disc_all_chrs(m_pClient->getConnHandle(),
m_startHandle, getHandle(),
m_endHandle, getEndHandle(),
NimBLERemoteService::characteristicDiscCB, NimBLERemoteService::characteristicDiscCB,
&taskData); &taskData);
} else { } else {
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(), rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnHandle(),
m_startHandle, getHandle(),
m_endHandle, getEndHandle(),
&uuid_filter->getNative()->u, uuidFilter->getBase(),
NimBLERemoteService::characteristicDiscCB, NimBLERemoteService::characteristicDiscCB,
&taskData); &taskData);
} }
if (rc != 0) { 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_chrs rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false; return false;
} }
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
rc = taskData.m_flags;
if(taskData.rc == 0){ if (rc == 0 || rc == BLE_HS_EDONE) {
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()"); NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
return true; return true;
} }
NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics"); NIMBLE_LOGE(LOG_TAG, "<< retrieveCharacteristics() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false; return false;
} // retrieveCharacteristics } // retrieveCharacteristics
/** /**
* @brief Get the client associated with this service. * @brief Get the client associated with this service.
* @return A reference to the client associated with this service. * @return A reference to the client associated with this service.
*/ */
NimBLEClient* NimBLERemoteService::getClient() { NimBLEClient* NimBLERemoteService::getClient() const {
return m_pClient; return m_pClient;
} // getClient } // getClient
/**
* @brief Get the service end handle.
*/
uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
/**
* @brief Get the service start handle.
*/
uint16_t NimBLERemoteService::getStartHandle() {
return m_startHandle;
} // getStartHandle
/**
* @brief Get the service UUID.
*/
NimBLEUUID NimBLERemoteService::getUUID() {
return m_uuid;
}
/** /**
* @brief Read the value of a characteristic associated with this service. * @brief Read the value of a characteristic associated with this service.
* @param [in] characteristicUuid The characteristic to read. * @param [in] uuid The characteristic to read.
* @returns a string containing the value or an empty string if not found or error. * @returns a string containing the value or an empty string if not found or error.
*/ */
std::string NimBLERemoteService::getValue(const NimBLEUUID &characteristicUuid) { NimBLEAttValue NimBLERemoteService::getValue(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str()); const auto pChar = getCharacteristic(uuid);
if (pChar) {
std::string ret = ""; return pChar->readValue();
NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
if(pChar != nullptr) {
ret = pChar->readValue();
} }
NIMBLE_LOGD(LOG_TAG, "<< readValue"); return NimBLEAttValue{};
return ret;
} // readValue } // readValue
/** /**
* @brief Set the value of a characteristic. * @brief Set the value of a characteristic.
* @param [in] characteristicUuid The characteristic to set. * @param [in] uuid The characteristic UUID to set.
* @param [in] value The value to set. * @param [in] value The value to set.
* @returns true on success, false if not found or error * @returns true on success, false if not found or error
*/ */
bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const std::string &value) { bool NimBLERemoteService::setValue(const NimBLEUUID& uuid, const NimBLEAttValue& value) const {
NIMBLE_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str()); const auto pChar = getCharacteristic(uuid);
if (pChar) {
bool ret = false; return pChar->writeValue(value);
NimBLERemoteCharacteristic* pChar = getCharacteristic(characteristicUuid);
if(pChar != nullptr) {
ret = pChar->writeValue(value);
} }
NIMBLE_LOGD(LOG_TAG, "<< setValue"); return false;
return ret;
} // setValue } // setValue
/** /**
* @brief Delete the characteristics in the characteristics vector. * @brief Delete the characteristics in the characteristics vector.
* @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic * @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
* object references. Since we allocated these in this class, we are also responsible for deleting * object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that. * them. This method does just that.
*/ */
void NimBLERemoteService::deleteCharacteristics() { void NimBLERemoteService::deleteCharacteristics() const {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics"); for (const auto& it : m_vChars) {
for(auto &it: m_characteristicVector) {
delete it; delete it;
} }
m_characteristicVector.clear(); std::vector<NimBLERemoteCharacteristic*>{}.swap(m_vChars);
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristics");
} // deleteCharacteristics } // deleteCharacteristics
/** /**
* @brief Delete characteristic by UUID * @brief Delete characteristic by UUID
* @param [in] uuid The UUID of the characteristic to be removed from the local database. * @param [in] uuid The UUID of the characteristic to be removed from the local database.
* @return Number of characteristics left. * @return Number of characteristics left.
*/ */
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) { size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID& uuid) const {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristic"); for (auto it = m_vChars.begin(); it != m_vChars.end(); ++it) {
for(auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it) {
if ((*it)->getUUID() == uuid) { if ((*it)->getUUID() == uuid) {
delete *it; delete (*it);
m_characteristicVector.erase(it); m_vChars.erase(it);
break; break;
} }
} }
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristic"); return m_vChars.size();
return m_characteristicVector.size();
} // deleteCharacteristic } // deleteCharacteristic
/** /**
* @brief Create a string representation of this remote service. * @brief Create a string representation of this remote service.
* @return A string representation of this remote service. * @return A string representation of this remote service.
*/ */
std::string NimBLERemoteService::toString() { std::string NimBLERemoteService::toString() const {
std::string res = "Service: uuid: " + m_uuid.toString(); std::string res = "Service: uuid: " + m_uuid.toString() + ", start_handle: 0x";
char val[6]; char val[5];
res += ", start_handle: "; snprintf(val, sizeof(val), "%04x", getHandle());
snprintf(val, sizeof(val), "%d", m_startHandle);
res += val; res += val;
snprintf(val, sizeof(val), "%04x", m_startHandle); res += ", end_handle: 0x";
res += " 0x"; snprintf(val, sizeof(val), "%04x", getEndHandle());
res += val;
res += ", end_handle: ";
snprintf(val, sizeof(val), "%d", m_endHandle);
res += val;
snprintf(val, sizeof(val), "%04x", m_endHandle);
res += " 0x";
res += val; res += val;
for (auto &it: m_characteristicVector) { for (const auto& chr : m_vChars) {
res += "\n" + it->toString(); res += "\n" + chr->toString();
} }
return res; return res;
} // toString } // toString
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -12,77 +12,54 @@
* Author: kolban * Author: kolban
*/ */
#ifndef COMPONENTS_NIMBLEREMOTESERVICE_H_ #ifndef NIMBLE_CPP_REMOTE_SERVICE_H_
#define COMPONENTS_NIMBLEREMOTESERVICE_H_ #define NIMBLE_CPP_REMOTE_SERVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h" #include "nimconfig.h"
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h"
#include "NimBLEUUID.h"
#include "NimBLERemoteCharacteristic.h"
# include "NimBLEAttribute.h"
# include <vector> # include <vector>
class NimBLEClient;
class NimBLERemoteCharacteristic; class NimBLERemoteCharacteristic;
class NimBLEClient;
class NimBLEAttValue;
/** /**
* @brief A model of a remote %BLE service. * @brief A model of a remote BLE service.
*/ */
class NimBLERemoteService { class NimBLERemoteService : public NimBLEAttribute {
public: public:
virtual ~NimBLERemoteService(); NimBLERemoteCharacteristic* getCharacteristic(const char* uuid) const;
NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID& uuid) const;
void deleteCharacteristics() const;
size_t deleteCharacteristic(const NimBLEUUID& uuid) const;
NimBLEClient* getClient(void) const;
NimBLEAttValue getValue(const NimBLEUUID& characteristicUuid) const;
bool setValue(const NimBLEUUID& characteristicUuid, const NimBLEAttValue& value) const;
std::string toString(void) const;
uint16_t getStartHandle() const { return getHandle(); }
uint16_t getEndHandle() const { return m_endHandle; }
// Public methods const std::vector<NimBLERemoteCharacteristic*>& getCharacteristics(bool refresh = false) const;
std::vector<NimBLERemoteCharacteristic*>::iterator begin(); std::vector<NimBLERemoteCharacteristic*>::iterator begin() const;
std::vector<NimBLERemoteCharacteristic*>::iterator end(); std::vector<NimBLERemoteCharacteristic*>::iterator end() const;
NimBLERemoteCharacteristic* getCharacteristic(const char* uuid);
NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid);
void deleteCharacteristics();
size_t deleteCharacteristic(const NimBLEUUID &uuid);
NimBLEClient* getClient(void);
//uint16_t getHandle();
NimBLEUUID getUUID(void);
std::string getValue(const NimBLEUUID &characteristicUuid);
bool setValue(const NimBLEUUID &characteristicUuid,
const std::string &value);
std::string toString(void);
std::vector<NimBLERemoteCharacteristic*>* getCharacteristics(bool refresh = false);
private: private:
// Private constructor ... never meant to be created by a user application.
NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc *service);
// Friends
friend class NimBLEClient; friend class NimBLEClient;
friend class NimBLERemoteCharacteristic;
// Private methods NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service);
bool retrieveCharacteristics(const NimBLEUUID *uuid_filter = nullptr); ~NimBLERemoteService();
bool retrieveCharacteristics(const NimBLEUUID* uuidFilter = nullptr) const;
static int characteristicDiscCB(uint16_t conn_handle, static int characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error* error, const struct ble_gatt_error* error,
const struct ble_gatt_chr* chr, const struct ble_gatt_chr* chr,
void* arg); void* arg);
uint16_t getStartHandle(); mutable std::vector<NimBLERemoteCharacteristic*> m_vChars{};
uint16_t getEndHandle(); NimBLEClient* m_pClient{nullptr};
void releaseSemaphores(); uint16_t m_endHandle{0};
// Properties
// We maintain a vector of characteristics owned by this service.
std::vector<NimBLERemoteCharacteristic*> m_characteristicVector;
NimBLEClient* m_pClient;
NimBLEUUID m_uuid;
uint16_t m_startHandle;
uint16_t m_endHandle;
}; // NimBLERemoteService }; // NimBLERemoteService
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_CENTRAL */
#endif /* CONFIG_BT_ENABLED */ #endif /* NIMBLE_CPP_REMOTE_SERVICE_H_*/
#endif /* COMPONENTS_NIMBLEREMOTESERVICE_H_ */

Some files were not shown because too many files have changed in this diff Show More