Merge pull request #8867 from Staars/mi32

MI_ESP32: update NimBLE-Arduino
This commit is contained in:
Theo Arends 2020-07-06 21:53:25 +02:00 committed by GitHub
commit dcab142b98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 1551 additions and 3724 deletions

View File

@ -122,21 +122,20 @@ Has been removed from the API as it is no longer maintained in the library.
The last two above changes reduce the heap usage significantly with minimal application code adjustments. The last two above changes reduce the heap usage significantly with minimal application code adjustments.
**NEW** on May 23, 2020 **UPDATED** on June 21, 2020
> ``` > ```
> NimBLEClient::getServices(bool refresh = false) > NimBLEClient::getServices(bool refresh = false)
> NimBLERemoteService::getCharacteristics(bool refresh = false) > NimBLERemoteService::getCharacteristics(bool refresh = false)
> NimBLERemoteCharacteristic::getDecriptors(bool refresh = false) > NimBLERemoteCharacteristic::getDecriptors(bool refresh = false)
>``` >```
> These methods now take an optional (bool) parameter. These methods now take an optional (bool) parameter.
If true it will clear the respective vector and retrieve all the respective attributes from the peripheral. If true it will clear the respective vector and retrieve all the respective attributes from the peripheral.
If false it will retrieve the attributes only if the vector is empty, otherwise the vector is returned If false(default) it will return the respective vector empty or otherwise with the currently stored attributes.
with the currently stored attributes.
> 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::discoverAtrributes()` for the user to discover all the peripheral attributes **Added:** `NimBLEClient::discoverAtrributes()` for the user to discover all the peripheral attributes
to replace the the former functionality. to replace the the former functionality.
@ -145,12 +144,11 @@ to replace the the former functionality.
>getCharacteristic(NimBLEUUID) >getCharacteristic(NimBLEUUID)
>getDescriptor(NimBLEUUID) >getDescriptor(NimBLEUUID)
>``` >```
>These methods will now check the respective vectors for the attribute object and, if not found, will retrieve (only) These methods will now check the respective vectors for the attribute object and, if not found, will retrieve (only)
the specified attribute from the peripheral. 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.
***
#### 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.

View File

@ -1,5 +1,17 @@
# *** UPDATE *** # *** UPDATES ***
**Breaking change:** Client and scan now use `std::vector` instead of `std::map` for storing the remote attribute database. **Breaking changes:**
**NEW** on June 21, 2020
> ```
> NimBLEClient::getServices(bool refresh = false)
> NimBLERemoteService::getCharacteristics(bool refresh = false)
> NimBLERemoteCharacteristic::getDecriptors(bool refresh = false)
>```
These methods now take an optional (bool) parameter.
If true it will clear the respective vector and retrieve all the respective attributes from the peripheral.
If false(default) it will return the respective vector empty or otherwise with the currently stored attributes.
**NEW** on May 23, 2020
Client and scan now use `std::vector` instead of `std::map` for storing the remote attribute database.
This change will affect your application code if you use `NimBLEClient::getServices()` or `NimBLERemoteService::getCharacteristics()` This change will affect your application code if you use `NimBLEClient::getServices()` or `NimBLERemoteService::getCharacteristics()`
in your application as they now return a pointer to `std::vector` of the respective attributes. in your application as they now return a pointer to `std::vector` of the respective attributes.
@ -13,29 +25,32 @@ It is expected that there will be minimal impact on most applications, if you ne
# NimBLE-Arduino # NimBLE-Arduino
A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32. A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32.
Why? Because the Bluedroid library is too bulky. This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared
with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original
library but refactored to use the NimBLE stack. In addition, this library will be more actively developed and maintained
to provide improved capabilites and stability over the original.
Initial client code testing has resulted in code size reduction of ~115k and reduced ram consumption of ~37k. ## Resource use improvement:
Server code testing results from @beegee-toyo [from the project here](https://github.com/beegee-tokyo/ESP32WiFiBLE-NimBLE): ### (Original) BLE_client example comparison (Debug):
#### Arduino BLE Library
Sketch uses **1216377** bytes (58%) of program storage space.
Memory after connection: Free Heap: **171548**
### Memory usage (compilation output)
#### Arduino BLE library
```log
RAM: [== ] 17.7% (used 58156 bytes from 327680 bytes)
Flash: [======== ] 76.0% (used 1345630 bytes from 1769472 bytes)
```
#### NimBLE-Arduino library #### NimBLE-Arduino library
```log Sketch uses **617256** bytes (29%) of program storage space.
RAM: [= ] 14.5% (used 47476 bytes from 327680 bytes) Memory after connection: Free Heap: **270336**
Flash: [======= ] 69.5% (used 911378 bytes from 1310720 bytes) ***
``` ### (Original) BLE_notify example comparison (Debug):
### Memory usage after **`setup()`** function #### Arduino BLE Library
#### Arduino BLE library Sketch uses **1208409** bytes (57%) of program storage space.
**`Internal Total heap 259104, internal Free Heap 91660`** Memory after connection: Free Heap: **173300**
#### NimBLE-Arduino library #### NimBLE-Arduino library
**`Internal Total heap 290288, internal Free Heap 182344`** Sketch uses **603432** bytes (28%) of program storage space.
Memory after connection: Free Heap: **269792**
**As shown: there is nearly a 50% reduction in flash use and approx. 100kB less ram consumed!**
# Installation: # Installation:
@ -62,20 +77,21 @@ Change the settings in the `nimconfig.h` file to customize NimBLE to your projec
# Continuing development: # Continuing development:
This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@fead24e.](https://github.com/espressif/esp-nimble) This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@46c1d9f.](https://github.com/espressif/esp-nimble)
Also tracking the NimBLE related changes in esp-idf, master branch, currently [@2bc28bb.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble) Also tracking the NimBLE related changes in esp-idf, master branch, currently [@2ef4890.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
# Acknowledgments: # Acknowledgments:
* @nkolban and @chegewara for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets) this project was derived from. * @nkolban and @chegewara for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets) this project was derived from.
* @beegee-tokyo for contributing your time to test/debug and contributing the beacon examples. * @beegee-tokyo for contributing your time to test/debug and contributing the beacon examples.
* @Jeroen88 for the amazing help debugging and improving the client code.
# Todo: # Todo:
1. Code cleanup. 1. Create documentation.
2. Create documentation. 2. Add BLE Mesh code.
3. Expose more NimBLE features. 3. Expose more NimBLE features.
4. Add BLE Mesh code.

View File

@ -22,11 +22,16 @@
#include "NimBLEDescriptor.h" #include "NimBLEDescriptor.h"
#include <map> #include <vector>
#define NIMBLE_DESC_FLAG_NOTIFY 0x0001 #define NIMBLE_DESC_FLAG_NOTIFY 0x0001
#define NIMBLE_DESC_FLAG_INDICATE 0x0002 #define NIMBLE_DESC_FLAG_INDICATE 0x0002
typedef struct {
uint16_t conn_id;
uint16_t sub_val;
} chr_sub_status_t;
/** /**
* @brief Descriptor for Client Characteristic Configuration. * @brief Descriptor for Client Characteristic Configuration.
@ -45,7 +50,7 @@ public:
private: private:
NimBLE2902(NimBLECharacteristic* pCharacterisitic); NimBLE2902(NimBLECharacteristic* pCharacterisitic);
friend class NimBLECharacteristic; friend class NimBLECharacteristic;
std::map<uint16_t, uint16_t> m_subscribedMap; std::vector<chr_sub_status_t> m_subscribedVec;
}; // NimBLE2902 }; // NimBLE2902

View File

@ -31,6 +31,7 @@ struct BLE2904_Data {
} __attribute__((packed)); } __attribute__((packed));
/** /**
* @brief Descriptor for Characteristic Presentation Format. * @brief Descriptor for Characteristic Presentation Format.
* *

View File

@ -142,4 +142,10 @@ NimBLEAddress::operator std::string() const {
return std::string(buffer); return std::string(buffer);
} }
NimBLEAddress::operator uint64_t() const {
uint64_t address = 0;
memcpy(&address, m_address, sizeof m_address);
return address;
}
#endif #endif

View File

@ -44,6 +44,7 @@ public:
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;
private: private:
uint8_t m_address[6]; uint8_t m_address[6];

View File

@ -37,6 +37,8 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
m_serviceData = ""; m_serviceData = "";
m_txPower = 0; m_txPower = 0;
m_pScan = nullptr; m_pScan = nullptr;
m_payloadLength = 0;
m_payload = nullptr;
m_haveAppearance = false; m_haveAppearance = false;
m_haveManufacturerData = false; m_haveManufacturerData = false;
@ -45,6 +47,7 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
m_haveServiceData = false; m_haveServiceData = false;
m_haveServiceUUID = false; m_haveServiceUUID = false;
m_haveTXPower = false; m_haveTXPower = false;
m_callbackSent = false;
} // NimBLEAdvertisedDevice } // NimBLEAdvertisedDevice
@ -62,6 +65,16 @@ NimBLEAddress NimBLEAdvertisedDevice::getAddress() {
} // getAddress } // getAddress
/**
* @brief Get the advertised type.
*
* @return The advertised type of the advertised device.
*/
uint8_t NimBLEAdvertisedDevice::getAdvType() {
return m_advType;
} // getAddress
/** /**
* @brief Get the appearance. * @brief Get the appearance.
* *
@ -536,6 +549,11 @@ uint8_t NimBLEAdvertisedDevice::getAddressType() {
} }
time_t NimBLEAdvertisedDevice::getTimestamp() {
return m_timestamp;
}
void NimBLEAdvertisedDevice::setAddressType(uint8_t type) { void NimBLEAdvertisedDevice::setAddressType(uint8_t type) {
m_addressType = type; m_addressType = type;
} }

View File

@ -42,18 +42,38 @@ public:
NimBLEAdvertisedDevice(); NimBLEAdvertisedDevice();
NimBLEAddress getAddress(); NimBLEAddress getAddress();
uint8_t getAdvType();
uint16_t getAppearance(); uint16_t getAppearance();
std::string getManufacturerData(); std::string getManufacturerData();
template<typename T>
T getManufacturerData(bool skipSizeCheck = false) {
std::string data = getManufacturerData();
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
}
std::string getName(); std::string getName();
int getRSSI(); int getRSSI();
NimBLEScan* getScan(); NimBLEScan* getScan();
std::string getServiceData(); std::string getServiceData();
template<typename T>
T getServiceData(bool skipSizeCheck = false) {
std::string data = getServiceData();
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
}
NimBLEUUID getServiceDataUUID(); NimBLEUUID getServiceDataUUID();
NimBLEUUID getServiceUUID(); NimBLEUUID getServiceUUID();
int8_t getTXPower(); int8_t getTXPower();
uint8_t* getPayload(); uint8_t* getPayload();
size_t getPayloadLength(); size_t getPayloadLength();
uint8_t getAddressType(); uint8_t getAddressType();
time_t getTimestamp();
void setAddressType(uint8_t type); void setAddressType(uint8_t type);
@ -108,8 +128,10 @@ private:
std::string m_serviceData; std::string m_serviceData;
NimBLEUUID m_serviceDataUUID; NimBLEUUID m_serviceDataUUID;
uint8_t* m_payload; uint8_t* m_payload;
size_t m_payloadLength = 0; size_t m_payloadLength;
uint8_t m_addressType; uint8_t m_addressType;
time_t m_timestamp;
bool m_callbackSent;
}; };
/** /**

View File

@ -100,7 +100,8 @@ void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
m_advParams.itvl_max = maxinterval; m_advParams.itvl_max = maxinterval;
} // setMaxInterval } // setMaxInterval
// These are dummy functions for now for compatibility
/* These are dummy functions for now for compatibility */
void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) { void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
//m_advData.min_interval = mininterval; //m_advData.min_interval = mininterval;
} // } //
@ -108,7 +109,8 @@ void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) { void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
//m_advData.max_interval = maxinterval; //m_advData.max_interval = maxinterval;
} // } //
////////////////////////////////////////////////////////// /*******************************************************/
void NimBLEAdvertising::setScanResponse(bool set) { void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set; m_scanResp = set;
@ -204,19 +206,19 @@ void NimBLEAdvertising::start() {
} }
#endif #endif
int numServices = m_serviceUUIDs.size();
int rc = 0;
uint8_t addressType;
uint8_t payloadLen = 3; //start with 3 bytes for the flags data
// If already advertising just return // If already advertising just return
if(ble_gap_adv_active()) { if(ble_gap_adv_active()) {
return; return;
} }
if (!m_customAdvData && !m_advSvcsSet && numServices > 0) { int rc = 0;
for (int i = 0; i < numServices; i++) {
if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16) { if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data
uint8_t payloadLen = 3;
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == 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) > 31){ if((payloadLen + add) > 31){
m_advData.uuids16_is_complete = 0; m_advData.uuids16_is_complete = 0;
@ -231,13 +233,13 @@ void NimBLEAdvertising::start() {
abort(); abort();
} }
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value, memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
&m_serviceUUIDs[i].getNative()->u16.value, sizeof(uint16_t)); &it.getNative()->u16.value, 2);
m_advData.uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16; m_advData.uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16;
m_advData.uuids16_is_complete = 1; m_advData.uuids16_is_complete = 1;
m_advData.num_uuids16++; m_advData.num_uuids16++;
} }
if(m_serviceUUIDs[i].getNative()->u.type == 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) > 31){ if((payloadLen + add) > 31){
m_advData.uuids32_is_complete = 0; m_advData.uuids32_is_complete = 0;
@ -252,13 +254,13 @@ void NimBLEAdvertising::start() {
abort(); abort();
} }
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value, memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
&m_serviceUUIDs[i].getNative()->u32.value, sizeof(uint32_t)); &it.getNative()->u32.value, 4);
m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32; m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32;
m_advData.uuids32_is_complete = 1; m_advData.uuids32_is_complete = 1;
m_advData.num_uuids32++; m_advData.num_uuids32++;
} }
if(m_serviceUUIDs[i].getNative()->u.type == 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) > 31){ if((payloadLen + add) > 31){
m_advData.uuids128_is_complete = 0; m_advData.uuids128_is_complete = 0;
@ -267,12 +269,13 @@ void NimBLEAdvertising::start() {
payloadLen += add; payloadLen += add;
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128, if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) { (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
NIMBLE_LOGE(LOG_TAG, "Error, no mem"); NIMBLE_LOGE(LOG_TAG, "Error, no mem");
abort(); abort();
} }
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value, memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
&m_serviceUUIDs[i].getNative()->u128.value, 16); &it.getNative()->u128.value, 16);
m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128; m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128;
m_advData.uuids128_is_complete = 1; m_advData.uuids128_is_complete = 1;
@ -315,11 +318,11 @@ void NimBLEAdvertising::start() {
rc = ble_gap_adv_rsp_set_fields(&m_scanData); rc = ble_gap_adv_rsp_set_fields(&m_scanData);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGC(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort(); abort();
} }
// if not using scan response and there is room, // if not using scan response and there is room,
// throw the tx power data into the advertisment // put the tx power data into the advertisment
} else if (payloadLen < 29) { } else if (payloadLen < 29) {
m_advData.tx_pwr_lvl_is_present = 1; m_advData.tx_pwr_lvl_is_present = 1;
m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
@ -327,7 +330,7 @@ void NimBLEAdvertising::start() {
rc = ble_gap_adv_set_fields(&m_advData); rc = ble_gap_adv_set_fields(&m_advData);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGC(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort(); abort();
} }
@ -349,22 +352,16 @@ void NimBLEAdvertising::start() {
m_advData.num_uuids16 = 0; m_advData.num_uuids16 = 0;
} }
m_advSvcsSet = true; m_advDataSet = true;
}
rc = ble_hs_id_infer_auto(0, &addressType);
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "Error determining address type; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
abort();
} }
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER, rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER,
&m_advParams, &m_advParams,
(pServer != nullptr) ? NimBLEServer::handleGapEvent : NULL, (pServer != nullptr) ? NimBLEServer::handleGapEvent : NULL,
pServer); pServer);
#else #else
rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER, rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER,
&m_advParams, NULL,NULL); &m_advParams, NULL,NULL);
#endif #endif
if (rc != 0) { if (rc != 0) {
@ -398,7 +395,7 @@ void NimBLEAdvertising::stop() {
* we need clear the flag so it reloads it. * we need clear the flag so it reloads it.
*/ */
void NimBLEAdvertising::onHostReset() { void NimBLEAdvertising::onHostReset() {
m_advSvcsSet = false; m_advDataSet = false;
} }

View File

@ -27,7 +27,6 @@
/**************************/ /**************************/
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include "FreeRTOS.h"
#include <vector> #include <vector>
@ -103,7 +102,7 @@ private:
bool m_customAdvData = false; // Are we using custom advertising data? bool m_customAdvData = false; // Are we using custom advertising data?
bool m_customScanResponseData = false; // Are we using custom scan response data? bool m_customScanResponseData = false; // Are we using custom scan response data?
bool m_scanResp = true; bool m_scanResp = true;
bool m_advSvcsSet = false; bool m_advDataSet = false;
}; };

View File

@ -19,21 +19,18 @@
#include "NimBLE2902.h" #include "NimBLE2902.h"
#include "NimBLE2904.h" #include "NimBLE2904.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h" #include "NimBLELog.h"
#include <string>
#define NULL_HANDLE (0xffff) #define NULL_HANDLE (0xffff)
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] 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, NimBLEService* pService)
: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) { : NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) {
@ -43,6 +40,7 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties
* @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] 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, NimBLEService* pService) {
m_uuid = uuid; m_uuid = uuid;
@ -50,15 +48,10 @@ NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t prop
m_properties = properties; m_properties = properties;
m_pCallbacks = &defaultCallback; m_pCallbacks = &defaultCallback;
m_pService = pService; m_pService = pService;
// Backward Compatibility - to be removed m_value = "";
/* setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); m_valMux = portMUX_INITIALIZER_UNLOCKED;
setReadProperty((properties & PROPERTY_READ) != 0); m_pTaskData = nullptr;
setWriteProperty((properties & PROPERTY_WRITE) != 0); m_timestamp = 0;
setNotifyProperty((properties & PROPERTY_NOTIFY) != 0);
setIndicateProperty((properties & PROPERTY_INDICATE) != 0);
setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0);
*/
///////////////////////////////////////////
} // NimBLECharacteristic } // NimBLECharacteristic
/** /**
@ -68,23 +61,6 @@ NimBLECharacteristic::~NimBLECharacteristic() {
} // ~NimBLECharacteristic } // ~NimBLECharacteristic
/**
* @brief Associate a descriptor with this characteristic.
* @param [in] pDescriptor
* @return N/A.
*/
void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) {
NIMBLE_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str());
// Check that we don't add the same descriptor twice.
if (m_descriptorMap.getByUUID(pDescriptor->getUUID()) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Adding a new descriptor with the same UUID as a previous one");
//return;
}
m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor);
NIMBLE_LOGD(LOG_TAG, "<< addDescriptor()");
} // addDescriptor
/** /**
* @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.
@ -104,25 +80,26 @@ 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.equals(NimBLEUUID((uint16_t)0x2902))) { if(uuid == NimBLEUUID(uint16_t(0x2902))) {
if(!(m_properties & BLE_GATT_CHR_F_NOTIFY) && !(m_properties & BLE_GATT_CHR_F_INDICATE)) { if(!(m_properties & BLE_GATT_CHR_F_NOTIFY) && !(m_properties & BLE_GATT_CHR_F_INDICATE)) {
assert(0 && "Cannot create 2902 descriptior without characteristic notification or indication property set"); assert(0 && "Cannot create 2902 descriptior without characteristic notification or indication property set");
} }
// We cannot have more than one 2902 descriptor, if it's already been created just return a pointer to it. // We cannot have more than one 2902 descriptor, if it's already been created just return a pointer to it.
pDescriptor = m_descriptorMap.getByUUID(uuid); pDescriptor = getDescriptorByUUID(uuid);
if(pDescriptor == nullptr) { if(pDescriptor == nullptr) {
pDescriptor = new NimBLE2902(this); pDescriptor = new NimBLE2902(this);
} else { } else {
return pDescriptor; return pDescriptor;
} }
} else if (uuid.equals(NimBLEUUID((uint16_t)0x2904))) { } 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);
m_dscVec.push_back(pDescriptor);
return pDescriptor; return pDescriptor;
} // createCharacteristic } // createCharacteristic
@ -132,8 +109,8 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/ */
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return m_descriptorMap.getByUUID(NimBLEUUID(descriptorUUID)); return getDescriptorByUUID(NimBLEUUID(uuid));
} // getDescriptorByUUID } // getDescriptorByUUID
@ -142,8 +119,13 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* descript
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/ */
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &descriptorUUID) { NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
return m_descriptorMap.getByUUID(descriptorUUID); for (auto &it : m_dscVec) {
if (it->getUUID() == uuid) {
return it;
}
}
return nullptr;
} // getDescriptorByUUID } // getDescriptorByUUID
@ -155,13 +137,8 @@ uint16_t NimBLECharacteristic::getHandle() {
return m_handle; return m_handle;
} // getHandle } // getHandle
/*
void NimBLECharacteristic::setAccessPermissions(uint16_t perm) {
m_permissions = perm;
}
*/
uint8_t NimBLECharacteristic::getProperties() { uint16_t NimBLECharacteristic::getProperties() {
return m_properties; return m_properties;
} // getProperties } // getProperties
@ -187,26 +164,28 @@ NimBLEUUID NimBLECharacteristic::getUUID() {
* @brief Retrieve the current value of the characteristic. * @brief Retrieve the current value of the characteristic.
* @return A pointer to storage containing the current characteristic value. * @return A pointer to storage containing the current characteristic value.
*/ */
std::string NimBLECharacteristic::getValue() { std::string NimBLECharacteristic::getValue(time_t *timestamp) {
return m_value.getValue(); portENTER_CRITICAL(&m_valMux);
std::string retVal = m_value;
if(timestamp != nullptr) {
*timestamp = m_timestamp;
}
portEXIT_CRITICAL(&m_valMux);
return retVal;
} // getValue } // getValue
/**
* @brief Retrieve the current raw data of the characteristic.
* @return A pointer to storage containing the current characteristic data.
*/
uint8_t* NimBLECharacteristic::getData() {
return m_value.getData();
} // getData
/** /**
* @brief Retrieve the the current data length of the characteristic. * @brief Retrieve the the current data length of the characteristic.
* @return The length of the current characteristic data. * @return The length of the current characteristic data.
*/ */
size_t NimBLECharacteristic::getDataLength() { size_t NimBLECharacteristic::getDataLength() {
return m_value.getLength(); portENTER_CRITICAL(&m_valMux);
size_t len = m_value.length();
portEXIT_CRITICAL(&m_valMux);
return len;
} }
@ -225,32 +204,41 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){ if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){
switch(ctxt->op) { switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR: { case BLE_GATT_ACCESS_OP_READ_CHR: {
//NIMBLE_LOGD(LOG_TAG, "read char pkthdr len:%d flags:%d", ctxt->om->om_pkthdr_len, ctxt->om->om_flags);
// If the packet header is only 8 bytes this is a follow up of a long read // 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. // so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8) { if(ctxt->om->om_pkthdr_len > 8) {
pCharacteristic->m_pCallbacks->onRead(pCharacteristic); pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
} }
rc = os_mbuf_append(ctxt->om, pCharacteristic->getData(), pCharacteristic->m_value.getLength());
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; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
case BLE_GATT_ACCESS_OP_WRITE_CHR: { case BLE_GATT_ACCESS_OP_WRITE_CHR: {
//NIMBLE_LOGD(LOG_TAG, "write char pkthdr len:%d datalen:%d", ctxt->om->om_pkthdr_len, ctxt->om->om_len);
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
//pCharacteristic->setValue(ctxt->om->om_data, ctxt->om->om_len); uint8_t buf[BLE_ATT_ATTR_MAX_LEN];
pCharacteristic->m_value.addPart(ctxt->om->om_data, ctxt->om->om_len); size_t len = ctxt->om->om_len;
memcpy(buf, ctxt->om->om_data,len);
os_mbuf *next; os_mbuf *next;
next = SLIST_NEXT(ctxt->om, om_next); next = SLIST_NEXT(ctxt->om, om_next);
while(next != NULL){ while(next != NULL){
//NIMBLE_LOGD(LOG_TAG, "Found long write data, len:%d", next->om_len); if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
pCharacteristic->m_value.addPart(next->om_data, next->om_len); return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
memcpy(&buf[len-1], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next); next = SLIST_NEXT(next, om_next);
} }
pCharacteristic->m_value.commit();
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
return 0; return 0;
@ -278,14 +266,19 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
subVal |= NIMBLE_DESC_FLAG_INDICATE; subVal |= NIMBLE_DESC_FLAG_INDICATE;
} }
m_semaphoreConfEvt.give((subVal | NIMBLE_DESC_FLAG_INDICATE) ? 0 : if(m_pTaskData != nullptr) {
NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED); m_pTaskData->rc = (subVal & NIMBLE_DESC_FLAG_INDICATE) ? 0 :
NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED;
xTaskNotifyGive(m_pTaskData->task);
}
NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", event->subscribe.conn_handle, subVal); NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
event->subscribe.conn_handle, subVal);
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902); NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
if(p2902 == nullptr){ if(p2902 == nullptr){
ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s", getUUID().toString().c_str()); ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s",
std::string(getUUID()).c_str());
return; return;
} }
@ -294,21 +287,28 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
p2902->m_pCallbacks->onWrite(p2902); p2902->m_pCallbacks->onWrite(p2902);
auto it = p2902->m_subscribedMap.find(event->subscribe.conn_handle); auto it = p2902->m_subscribedVec.begin();
if(subVal > 0 && it == p2902->m_subscribedMap.cend()) { for(;it != p2902->m_subscribedVec.end(); ++it) {
p2902->m_subscribedMap.insert(std::pair<uint16_t, uint16_t>(event->subscribe.conn_handle, subVal)); if((*it).conn_id == event->subscribe.conn_handle) {
return; break;
} else if(it != p2902->m_subscribedMap.cend()) { }
p2902->m_subscribedMap.erase(event->subscribe.conn_handle); }
if(subVal > 0) {
if(it == p2902->m_subscribedVec.end()) {
chr_sub_status_t client_sub;
client_sub.conn_id = event->subscribe.conn_handle;
client_sub.sub_val = subVal;
p2902->m_subscribedVec.push_back(client_sub);
return; return;
} }
/*
if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { (*it).sub_val = subVal;
p2902->m_subscribedMap.erase(event->subscribe.conn_handle);
return; } else if(it != p2902->m_subscribedVec.end()) {
p2902->m_subscribedVec.erase(it);
p2902->m_subscribedVec.shrink_to_fit();
} }
*/
(*it).second = subVal;
} }
@ -319,7 +319,7 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
* @return N/A * @return N/A
*/ */
void NimBLECharacteristic::indicate() { void NimBLECharacteristic::indicate() {
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
notify(false); notify(false);
NIMBLE_LOGD(LOG_TAG, "<< indicate"); NIMBLE_LOGD(LOG_TAG, "<< indicate");
} // indicate } // indicate
@ -331,91 +331,102 @@ void NimBLECharacteristic::indicate() {
* @return N/A. * @return N/A.
*/ */
void NimBLECharacteristic::notify(bool is_notification) { void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length()); NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", getDataLength());
assert(getService() != nullptr); NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
assert(getService()->getServer() != nullptr);
if(p2902 == nullptr) {
NIMBLE_LOGE(LOG_TAG,
"<< notify-Error; Notify/indicate not enabled for characterisitc: %s",
std::string(getUUID()).c_str());
}
if (getService()->getServer()->getConnectedCount() == 0) { if (p2902->m_subscribedVec.size() == 0) {
NIMBLE_LOGD(LOG_TAG, "<< notify: No connected clients."); NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
return; return;
} }
m_pCallbacks->onNotify(this); m_pCallbacks->onNotify(this);
std::string value = getValue();
size_t length = value.length();
bool reqSec = (m_properties & BLE_GATT_CHR_F_READ_AUTHEN) ||
(m_properties & BLE_GATT_CHR_F_READ_AUTHOR) ||
(m_properties & BLE_GATT_CHR_F_READ_ENC);
int rc = 0; int rc = 0;
NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902);
for (auto it = p2902->m_subscribedMap.cbegin(); it != p2902->m_subscribedMap.cend(); ++it) { for (auto &it : p2902->m_subscribedVec) {
uint16_t _mtu = getService()->getServer()->getPeerMTU((*it).first); uint16_t _mtu = getService()->getServer()->getPeerMTU(it.conn_id);
// Must rebuild the data on each loop iteration as NimBLE will release it.
size_t length = m_value.getValue().length();
uint8_t* data = (uint8_t*)m_value.getValue().data();
os_mbuf *om;
if(_mtu == 0) { // check if connected and subscribed
//NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map"); if(_mtu == 0 || it.sub_val == 0) {
p2902->m_subscribedMap.erase((*it).first);
it = p2902->m_subscribedMap.cbegin();
if(it == p2902->m_subscribedMap.cend()) {
return;
}
continue; continue;
} }
// check if security requirements are satisfied
if(reqSec) {
struct ble_gap_conn_desc desc;
rc = ble_gap_conn_find(it.conn_id, &desc);
if(rc != 0 || !desc.sec_state.encrypted) {
continue;
}
}
if (length > _mtu - 3) { if (length > _mtu - 3) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
} }
if((*it).second == 0) { if(is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_NOTIFY))) {
//NIMBLE_LOGI(LOG_TAG, "Skipping unsubscribed client");
continue;
}
if(is_notification && (!((*it).second & NIMBLE_DESC_FLAG_NOTIFY))) {
NIMBLE_LOGW(LOG_TAG, NIMBLE_LOGW(LOG_TAG,
"Sending notification to client subscribed to indications, sending indication instead"); "Sending notification to client subscribed to indications, sending indication instead");
is_notification = false; is_notification = false;
} }
if(!is_notification && (!((*it).second & NIMBLE_DESC_FLAG_INDICATE))) { if(!is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_INDICATE))) {
NIMBLE_LOGW(LOG_TAG, NIMBLE_LOGW(LOG_TAG,
"Sending indication to client subscribed to notifications, sending notifications instead"); "Sending indication to client subscribed to notification, sending notification instead");
is_notification = true; 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.
om = ble_hs_mbuf_from_flat(data, length); os_mbuf *om = ble_hs_mbuf_from_flat((uint8_t*)value.data(), length);
if(!is_notification) { NimBLECharacteristicCallbacks::Status statusRC;
m_semaphoreConfEvt.take("indicate");
rc = ble_gattc_indicate_custom((*it).first, m_handle, om); if(!is_notification && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
ble_task_data_t taskData = {nullptr, xTaskGetCurrentTaskHandle(),0, nullptr};
m_pTaskData = &taskData;
rc = ble_gattc_indicate_custom(it.conn_id, m_handle, om);
if(rc != 0){ if(rc != 0){
m_semaphoreConfEvt.give(); statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc); } else {
return; ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = m_pTaskData->rc;
} }
rc = m_semaphoreConfEvt.wait(); m_pTaskData = nullptr;
if(rc == BLE_HS_ETIMEOUT) { if(rc == BLE_HS_EDONE) {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, rc); rc = 0;
} else if(rc == BLE_HS_EDONE) { statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE, rc); } else if(rc == BLE_HS_ETIMEOUT) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
} else { } else {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, rc); statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
} }
} else { } else {
rc = ble_gattc_notify_custom((*it).first, m_handle, om); rc = ble_gattc_notify_custom(it.conn_id, m_handle, om);
if(rc == 0) { if(rc == 0) {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0); statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else { } else {
m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc); statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
} }
} }
m_pCallbacks->onStatus(this, statusRC, rc);
} }
NIMBLE_LOGD(LOG_TAG, "<< notify"); NIMBLE_LOGD(LOG_TAG, "<< notify");
@ -434,87 +445,6 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback
} }
} // setCallbacks } // setCallbacks
// Backward compatibility - to be removed ////////////////////////////////
/**
* @brief Set the permission to broadcast.
* A characteristics has properties associated with it which define what it is capable of doing.
* One of these is the broadcast flag.
* @param [in] value The flag value of the property.
* @return N/A
*/
void NimBLECharacteristic::setBroadcastProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_BROADCAST);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_BROADCAST);
}
} // setBroadcastProperty
/**
* @brief Set the Indicate property value.
* @param [in] value Set to true if we are to allow indicate messages.
*/
void NimBLECharacteristic::setIndicateProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_INDICATE);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_INDICATE);
}
} // setIndicateProperty
/**
* @brief Set the Notify property value.
* @param [in] value Set to true if we are to allow notification messages.
*/
void NimBLECharacteristic::setNotifyProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_NOTIFY);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_NOTIFY);
}
} // setNotifyProperty
/**
* @brief Set the Read property value.
* @param [in] value Set to true if we are to allow reads.
*/
void NimBLECharacteristic::setReadProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_READ);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_READ);
}
} // setReadProperty
/**
* @brief Set the Write No Response property value.
* @param [in] value Set to true if we are to allow writes with no response.
*/
void NimBLECharacteristic::setWriteNoResponseProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_WRITE_NO_RSP);
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE_NO_RSP);
}
} // setWriteNoResponseProperty
/**
* @brief Set the Write property value.
* @param [in] value Set to true if we are to allow writes.
*/
void NimBLECharacteristic::setWriteProperty(bool value) {
if (value) {
m_properties = (m_properties | BLE_GATT_CHR_F_WRITE );
} else {
m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE );
}
} // setWriteProperty
//////////////////////////////////////////////////////////////////////////////////
/** /**
* @brief Set the value of the characteristic. * @brief Set the value of the characteristic.
@ -522,21 +452,21 @@ void NimBLECharacteristic::setWriteProperty(bool value) {
* @param [in] length The length of the data in bytes. * @param [in] length The length of the data in bytes.
*/ */
void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) { 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); 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()); NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
free(pHex); free(pHex);
#endif
if (length > BLE_ATT_ATTR_MAX_LEN) { 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); NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN);
return; return;
} }
m_value.setValue(data, length); portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
// if(m_handle != NULL_HANDLE) { m_timestamp = time(nullptr);
//ble_gatts_chr_updated(m_handle); portEXIT_CRITICAL(&m_valMux);
// ble_gattc_notify(getService()->getServer()->m_connId, m_handle);
// }
NIMBLE_LOGD(LOG_TAG, "<< setValue"); NIMBLE_LOGD(LOG_TAG, "<< setValue");
} // setValue } // setValue
@ -553,41 +483,6 @@ void NimBLECharacteristic::setValue(const std::string &value) {
setValue((uint8_t*)(value.data()), value.length()); setValue((uint8_t*)(value.data()), value.length());
} // setValue } // setValue
void NimBLECharacteristic::setValue(uint16_t& data16) {
uint8_t temp[2];
temp[0] = data16;
temp[1] = data16 >> 8;
setValue(temp, 2);
} // setValue
void NimBLECharacteristic::setValue(uint32_t& data32) {
uint8_t temp[4];
temp[0] = data32;
temp[1] = data32 >> 8;
temp[2] = data32 >> 16;
temp[3] = data32 >> 24;
setValue(temp, 4);
} // setValue
void NimBLECharacteristic::setValue(int& data32) {
uint8_t temp[4];
temp[0] = data32;
temp[1] = data32 >> 8;
temp[2] = data32 >> 16;
temp[3] = data32 >> 24;
setValue(temp, 4);
} // setValue
void NimBLECharacteristic::setValue(float& data32) {
float temp = data32;
setValue((uint8_t*)&temp, 4);
} // setValue
void NimBLECharacteristic::setValue(double& data64) {
double temp = data64;
setValue((uint8_t*)&temp, 8);
} // setValue
/** /**
* @brief Return a string representation of the characteristic. * @brief Return a string representation of the characteristic.

View File

@ -42,42 +42,15 @@ typedef enum {
#include "NimBLEService.h" #include "NimBLEService.h"
#include "NimBLEDescriptor.h" #include "NimBLEDescriptor.h"
#include "NimBLEUUID.h"
#include "NimBLEValue.h"
#include "FreeRTOS.h"
#include <string> #include <string>
#include <map> #include <vector>
class NimBLEService; class NimBLEService;
class NimBLEDescriptor; class NimBLEDescriptor;
class NimBLECharacteristicCallbacks; class NimBLECharacteristicCallbacks;
/**
* @brief A management structure for %BLE descriptors.
*/
class NimBLEDescriptorMap {
public:
void setByUUID(const char* uuid, NimBLEDescriptor* pDescriptor);
void setByUUID(const NimBLEUUID &uuid, NimBLEDescriptor* pDescriptor);
// void setByHandle(uint16_t handle, NimBLEDescriptor* pDescriptor);
NimBLEDescriptor* getByUUID(const char* uuid);
NimBLEDescriptor* getByUUID(const NimBLEUUID &uuid);
// NimBLEDescriptor* getByHandle(uint16_t handle);
std::string toString();
NimBLEDescriptor* getFirst();
NimBLEDescriptor* getNext();
uint8_t getSize();
private:
std::map<NimBLEDescriptor*, std::string> m_uuidMap;
// std::map<uint16_t, BLEDescriptor*> m_handleMap;
std::map<NimBLEDescriptor*, std::string>::iterator m_iterator;
};
/** /**
* @brief The model of a %BLE Characteristic. * @brief The model of a %BLE Characteristic.
* *
@ -87,84 +60,78 @@ private:
class NimBLECharacteristic { class NimBLECharacteristic {
public: public:
NimBLEDescriptor* createDescriptor(const char* uuid, NimBLEDescriptor* createDescriptor(const char* uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE, NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100); uint16_t max_len = 100);
NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid, NimBLEDescriptor* createDescriptor(const NimBLEUUID &uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE, NIMBLE_PROPERTY::WRITE,
uint16_t max_len = 100); uint16_t max_len = 100);
NimBLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); NimBLEDescriptor* getDescriptorByUUID(const char* uuid);
NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &descriptorUUID); NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID &uuid);
NimBLEUUID getUUID(); NimBLEUUID getUUID();
std::string getValue(); std::string getValue(time_t *timestamp = nullptr);
uint8_t* getData();
size_t getDataLength();
template<typename T>
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue();
if(!skipSizeCheck && value.size() < sizeof(T)) return T();
const char *pData = value.data();
return *((T *)pData);
}
size_t getDataLength();
void indicate(); void indicate();
void notify(bool is_notification = true); void notify(bool is_notification = true);
void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks);
// Backward Compatibility - to be removed
void setBroadcastProperty(bool value);
void setIndicateProperty(bool value);
void setNotifyProperty(bool value);
void setReadProperty(bool value);
void setWriteProperty(bool value);
void setWriteNoResponseProperty(bool value);
//////////////////////////////////////////////////////
void setValue(const uint8_t* data, size_t size); void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value); void setValue(const std::string &value);
void setValue(uint16_t& data16);
void setValue(uint32_t& data32); template<typename T>
void setValue(int& data32); void setValue(const T &s) {
void setValue(float& data32); setValue((uint8_t*)&s, sizeof(T));
void setValue(double& data64); }
std::string toString(); std::string toString();
uint16_t getHandle(); uint16_t getHandle();
// void setAccessPermissions(uint16_t perm);
// Backward Compatibility - to be removed
/* static const uint32_t PROPERTY_READ = 1<<0;
static const uint32_t PROPERTY_WRITE = 1<<1;
static const uint32_t PROPERTY_NOTIFY = 1<<2;
static const uint32_t PROPERTY_BROADCAST = 1<<3;
static const uint32_t PROPERTY_INDICATE = 1<<4;
static const uint32_t PROPERTY_WRITE_NR = 1<<5;
*/
//////////////////////////////////////////////////////
private: private:
friend class NimBLEServer; friend class NimBLEServer;
friend class NimBLEService; friend class NimBLEService;
// friend class NimBLEDescriptor;
// friend class NimBLECharacteristicMap;
NimBLECharacteristic(const char* uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, NimBLECharacteristic(const char* uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr); NimBLEService* pService = nullptr);
NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, NimBLECharacteristic(const NimBLEUUID &uuid,
uint16_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE,
NimBLEService* pService = nullptr); NimBLEService* pService = nullptr);
virtual ~NimBLECharacteristic();
NimBLEUUID m_uuid; ~NimBLECharacteristic();
NimBLEDescriptorMap m_descriptorMap;
uint16_t m_handle;
uint16_t m_properties;
NimBLECharacteristicCallbacks* m_pCallbacks;
NimBLEService* m_pService;
NimBLEValue m_value;
// uint16_t m_permissions;
void addDescriptor(NimBLEDescriptor* pDescriptor);
NimBLEService* getService(); NimBLEService* getService();
uint8_t getProperties(); uint16_t getProperties();
void setSubscribe(struct ble_gap_event *event); void setSubscribe(struct ble_gap_event *event);
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg); struct ble_gatt_access_ctxt *ctxt, void *arg);
FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); NimBLEUUID m_uuid;
uint16_t m_handle;
uint16_t m_properties;
NimBLECharacteristicCallbacks* m_pCallbacks;
NimBLEService* m_pService;
std::string m_value;
std::vector<NimBLEDescriptor*> m_dscVec;
ble_task_data_t *m_pTaskData;
portMUX_TYPE m_valMux;
time_t m_timestamp;
}; // NimBLECharacteristic }; // NimBLECharacteristic

View File

@ -1,132 +0,0 @@
/*
* NimBLECharacteristicMap.cpp
*
* Created: on March 3, 2020
* Author H2zero
*
* BLECharacteristicMap.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEService.h"
#include "NimBLELog.h"
/**
* @brief Return the characteristic by handle.
* @param [in] handle The handle to look up the characteristic.
* @return The characteristic.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getByHandle(uint16_t handle) {
return m_handleMap.at(handle);
} // getByHandle
/**
* @brief Return the characteristic by UUID.
* @param [in] UUID The UUID to look up the characteristic.
* @return The characteristic.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(const char* uuid) {
return getByUUID(NimBLEUUID(uuid));
}
/**
* @brief Return the characteristic by UUID.
* @param [in] UUID The UUID to look up the characteristic.
* @return The characteristic.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(const NimBLEUUID &uuid) {
for (auto &myPair : m_uuidMap) {
if (myPair.first->getUUID().equals(uuid)) {
return myPair.first;
}
}
return nullptr;
} // getByUUID
/**
* @brief Get the number of characteristics in the map.
*/
uint8_t NimBLECharacteristicMap::getSize() {
return (uint8_t)m_uuidMap.size();
} // getSize
/**
* @brief Get the first characteristic in the map.
* @return The first characteristic in the map.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getFirst() {
m_iterator = m_uuidMap.begin();
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLECharacteristic* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getFirst
/**
* @brief Get the next characteristic in the map.
* @return The next characteristic in the map.
*/
NimBLECharacteristic* NimBLECharacteristicMap::getNext() {
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLECharacteristic* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getNext
/**
* @brief Set the characteristic by handle.
* @param [in] handle The handle of the characteristic.
* @param [in] characteristic The characteristic to cache.
* @return N/A.
*/
void NimBLECharacteristicMap::setByHandle(uint16_t handle, NimBLECharacteristic* characteristic) {
m_handleMap.insert(std::pair<uint16_t, NimBLECharacteristic*>(handle, characteristic));
} // setByHandle
/**
* @brief Set the characteristic by UUID.
* @param [in] uuid The uuid of the characteristic.
* @param [in] characteristic The characteristic to cache.
* @return N/A.
*/
void NimBLECharacteristicMap::setByUUID(NimBLECharacteristic* pCharacteristic, const NimBLEUUID &uuid) {
m_uuidMap.insert(std::pair<NimBLECharacteristic*, std::string>(pCharacteristic, uuid.toString()));
} // setByUUID
/**
* @brief Return a string representation of the characteristic map.
* @return A string representation of the characteristic map.
*/
std::string NimBLECharacteristicMap::toString() {
std::string res;
int count = 0;
char hex[5];
for (auto &myPair: m_uuidMap) {
if (count > 0) {res += "\n";}
snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + myPair.first->getUUID().toString();
}
return res;
} // toString
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -18,7 +18,6 @@
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEClient.h" #include "NimBLEClient.h"
#include "NimBLEUtils.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLELog.h" #include "NimBLELog.h"
@ -54,7 +53,10 @@ NimBLEClient::NimBLEClient()
m_pClientCallbacks = &defaultCallbacks; m_pClientCallbacks = &defaultCallbacks;
m_conn_id = BLE_HS_CONN_HANDLE_NONE; m_conn_id = BLE_HS_CONN_HANDLE_NONE;
m_isConnected = false; m_isConnected = false;
m_waitingToConnect = false;
m_connectTimeout = 30000; m_connectTimeout = 30000;
m_deleteCallbacks = false;
m_pTaskData = nullptr;
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default) m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
@ -74,7 +76,7 @@ NimBLEClient::NimBLEClient()
NimBLEClient::~NimBLEClient() { NimBLEClient::~NimBLEClient() {
// We may have allocated service references associated with this client. // We may have allocated service references associated with this client.
// Before we are finished with the client, we must release resources. // Before we are finished with the client, we must release resources.
clearServices(); deleteServices();
if(m_deleteCallbacks && m_pClientCallbacks != &defaultCallbacks) { if(m_deleteCallbacks && m_pClientCallbacks != &defaultCallbacks) {
delete m_pClientCallbacks; delete m_pClientCallbacks;
@ -84,18 +86,40 @@ NimBLEClient::~NimBLEClient() {
/** /**
* @brief Clear any existing services. * @brief Delete any existing services.
*/ */
void NimBLEClient::clearServices() { void NimBLEClient::deleteServices() {
NIMBLE_LOGD(LOG_TAG, ">> clearServices"); NIMBLE_LOGD(LOG_TAG, ">> deleteServices");
// Delete all the services. // Delete all the services.
for(auto &it: m_servicesVector) { for(auto &it: m_servicesVector) {
delete it; delete it;
} }
m_servicesVector.clear(); m_servicesVector.clear();
NIMBLE_LOGD(LOG_TAG, "<< clearServices"); NIMBLE_LOGD(LOG_TAG, "<< deleteServices");
} // clearServices } // deleteServices
/**
* @brief Delete service by UUID
* @param [in] uuid The UUID of the service to be deleted from the local database.
* @return Number of services left.
*/
size_t NimBLEClient::deleteService(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> deleteService");
// Delete the requested service.
for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) {
if((*it)->getUUID() == uuid) {
delete *it;
m_servicesVector.erase(it);
break;
}
}
NIMBLE_LOGD(LOG_TAG, "<< deleteService");
return m_servicesVector.size();
} // deleteServices
/** /**
@ -135,6 +159,10 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
return false; return false;
} }
if(!NimBLEDevice::getScan()->stop()) {
return false;
}
int rc = 0; int rc = 0;
m_peerAddress = address; m_peerAddress = address;
@ -142,7 +170,8 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
memcpy(&peerAddrt.val, address.getNative(),6); memcpy(&peerAddrt.val, address.getNative(),6);
peerAddrt.type = type; peerAddrt.type = type;
m_semaphoreOpenEvt.take("connect"); ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_pTaskData = &taskData;
/** Try to connect the the advertiser. Allow 30 seconds (30000 ms) for /** Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout (default value of m_connectTimeout). * timeout (default value of m_connectTimeout).
@ -152,7 +181,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams, rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams,
NimBLEClient::handleGapEvent, this); NimBLEClient::handleGapEvent, this);
if(rc == BLE_HS_EBUSY) { if(rc == BLE_HS_EBUSY) {
vTaskDelay(1); vTaskDelay(1 / portTICK_PERIOD_MS);
} }
}while(rc == BLE_HS_EBUSY); }while(rc == BLE_HS_EBUSY);
@ -162,23 +191,23 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
type, type,
m_peerAddress.toString().c_str(), m_peerAddress.toString().c_str(),
rc, NimBLEUtils::returnCodeToString(rc)); rc, NimBLEUtils::returnCodeToString(rc));
m_pTaskData = nullptr;
m_semaphoreOpenEvt.give();
m_waitingToConnect = false; m_waitingToConnect = false;
return false; return false;
} }
m_waitingToConnect = true; m_waitingToConnect = true;
rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. // Wait for the connection to complete.
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(rc != 0){ if(taskData.rc != 0){
return false; return false;
} }
if(refreshServices) { if(refreshServices) {
NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str()); NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str());
clearServices(); deleteServices();
} }
m_pClientCallbacks->onConnect(this); m_pClientCallbacks->onConnect(this);
@ -194,17 +223,18 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
* @return True on success. * @return True on success.
*/ */
bool NimBLEClient::secureConnection() { bool NimBLEClient::secureConnection() {
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_semeaphoreSecEvt.take("secureConnection"); m_pTaskData = &taskData;
int rc = NimBLEDevice::startSecurity(m_conn_id); int rc = NimBLEDevice::startSecurity(m_conn_id);
if(rc != 0){ if(rc != 0){
m_semeaphoreSecEvt.give(); m_pTaskData = nullptr;
return false; return false;
} }
rc = m_semeaphoreSecEvt.wait("secureConnection"); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(rc != 0){
if(taskData.rc != 0){
return false; return false;
} }
@ -220,15 +250,11 @@ int NimBLEClient::disconnect(uint8_t reason) {
NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
int rc = 0; int rc = 0;
if(m_isConnected){ if(m_isConnected){
m_isConnected = false; // flag the disconnect now so no calls are performed after
rc = ble_gap_terminate(m_conn_id, reason); rc = ble_gap_terminate(m_conn_id, reason);
if(rc != 0){ if(rc != 0){
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc,
NimBLEUtils::returnCodeToString(rc)); NimBLEUtils::returnCodeToString(rc));
} }
// Sometimes a disconnect event is not sent so we need to make sure
// the device can be found again.
NimBLEDevice::removeIgnored(m_peerAddress);
} }
NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
@ -394,15 +420,13 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
* @param [in] bool value to indicate if the current vector should be cleared and * @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all services retrieved from the peripheral. * subsequently all services retrieved from the peripheral.
* If false the vector will be returned with the currently stored services, * If false the vector will be returned with the currently stored services,
* if vector is empty it will retrieve all services from the peripheral. * If true it will retrieve all services from the peripheral and return the vector with all services
* @return a pointer to the vector of available services. * @return a pointer to the vector of available services.
*/ */
std::vector<NimBLERemoteService*>* NimBLEClient::getServices(bool refresh) { std::vector<NimBLERemoteService*>* NimBLEClient::getServices(bool refresh) {
if(refresh) { if(refresh) {
clearServices(); deleteServices();
}
if(m_servicesVector.empty()) {
if (!retrieveServices()) { if (!retrieveServices()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get services"); NIMBLE_LOGE(LOG_TAG, "Error: Failed to get services");
} }
@ -442,30 +466,31 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
*/ */
NIMBLE_LOGD(LOG_TAG, ">> retrieveServices"); NIMBLE_LOGD(LOG_TAG, ">> retrieveServices");
int rc = 0;
if(!m_isConnected){ if(!m_isConnected){
NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting"); NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting");
return false; return false;
} }
m_semaphoreSearchCmplEvt.take("retrieveServices"); int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
if(uuid_filter == nullptr) { if(uuid_filter == nullptr) {
rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, this); rc = ble_gattc_disc_all_svcs(m_conn_id, NimBLEClient::serviceDiscoveredCB, &taskData);
} else { } else {
rc = ble_gattc_disc_svc_by_uuid(m_conn_id, &uuid_filter->getNative()->u, rc = ble_gattc_disc_svc_by_uuid(m_conn_id, &uuid_filter->getNative()->u,
NimBLEClient::serviceDiscoveredCB, this); NimBLEClient::serviceDiscoveredCB, &taskData);
} }
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_svcs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
m_semaphoreSearchCmplEvt.give();
return false; return false;
} }
// wait until we have all the services // wait until we have all the services
if(m_semaphoreSearchCmplEvt.wait("retrieveServices") == 0){ ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc == 0){
NIMBLE_LOGD(LOG_TAG, "<< retrieveServices"); NIMBLE_LOGD(LOG_TAG, "<< retrieveServices");
return true; return true;
} }
@ -489,41 +514,34 @@ int NimBLEClient::serviceDiscoveredCB(
NIMBLE_LOGD(LOG_TAG,"Service Discovered >> status: %d handle: %d", NIMBLE_LOGD(LOG_TAG,"Service Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? service->start_handle : -1); error->status, (error->status == 0) ? service->start_handle : -1);
NimBLEClient *peer = (NimBLEClient*)arg; ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
int rc=0; NimBLEClient *client = (NimBLEClient*)pTaskData->pATT;
// Make sure the service discovery is for this device // Make sure the service discovery is for this device
if(peer->getConnId() != conn_handle){ if(client->getConnId() != conn_handle){
return 0; return 0;
} }
switch (error->status) { if(error->status == 0) {
case 0: {
// Found a service - add it to the vector // Found a service - add it to the vector
NimBLERemoteService* pRemoteService = new NimBLERemoteService(peer, service); NimBLERemoteService* pRemoteService = new NimBLERemoteService(client, service);
peer->m_servicesVector.push_back(pRemoteService); client->m_servicesVector.push_back(pRemoteService);
break; return 0;
}
case BLE_HS_EDONE:{
// All services discovered; start discovering characteristics.
//NIMBLE_LOGD(LOG_TAG,"Giving search semaphore - completed");
peer->m_semaphoreSearchCmplEvt.give(0);
rc = 0;
break;
}
default:
// Error; abort discovery.
rc = error->status;
break;
} }
if (rc != 0) { if(error->status == BLE_HS_EDONE) {
// pass non-zero to semaphore on error to indicate an error finding services pTaskData->rc = 0;
peer->m_semaphoreSearchCmplEvt.give(1); } else {
NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s",
error->status,
NimBLEUtils::returnCodeToString(error->status));
pTaskData->rc = error->status;
} }
NIMBLE_LOGD(LOG_TAG,"<< Service Discovered. status: %d", rc);
return rc; xTaskNotifyGive(pTaskData->task);
NIMBLE_LOGD(LOG_TAG,"<< << Service Discovered");
return error->status;
} }
@ -595,15 +613,11 @@ uint16_t NimBLEClient::getMTU() {
* @param [in] arg = pointer to the client instance * @param [in] arg = pointer to the client instance
*/ */
/*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) { /*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg; NimBLEClient* client = (NimBLEClient*)arg;
//struct ble_gap_conn_desc desc;
//struct ble_hs_adv_fields fields;
int rc; int rc;
NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type)); NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type));
// Execute handler code based on the type of event received.
switch(event->type) { switch(event->type) {
case BLE_GAP_EVENT_DISCONNECT: { case BLE_GAP_EVENT_DISCONNECT: {
@ -620,9 +634,6 @@ uint16_t NimBLEClient::getMTU() {
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason, NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason,
NimBLEUtils::returnCodeToString(event->disconnect.reason)); NimBLEUtils::returnCodeToString(event->disconnect.reason));
//print_conn_desc(&event->disconnect.conn);
//MODLOG_DFLT(INFO, "\n");
// If Host reset tell the device now before returning to prevent // If Host reset tell the device now before returning to prevent
// any errors caused by calling host functions before resyncing. // any errors caused by calling host functions before resyncing.
@ -639,15 +650,9 @@ uint16_t NimBLEClient::getMTU() {
} }
//client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
// Indicate a non-success return value to any semaphores waiting
client->m_semaphoreOpenEvt.give(1);
client->m_semaphoreSearchCmplEvt.give(1);
client->m_semeaphoreSecEvt.give(1);
client->m_pClientCallbacks->onDisconnect(client); client->m_pClientCallbacks->onDisconnect(client);
rc = event->disconnect.reason;
return 0; break;
} // BLE_GAP_EVENT_DISCONNECT } // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_CONNECT: { case BLE_GAP_EVENT_CONNECT: {
@ -667,30 +672,25 @@ uint16_t NimBLEClient::getMTU() {
client->m_conn_id = event->connect.conn_handle; client->m_conn_id = event->connect.conn_handle;
// rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
// assert(rc == 0);
// print_conn_desc(&desc);
// MODLOG_DFLT(INFO, "\n");
// In the case of a multiconnecting device we ignore this device when
// scanning since we are already connected to it
NimBLEDevice::addIgnored(client->m_peerAddress);
rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL); rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL);
if(rc != 0) { if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc, NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc,
NimBLEUtils::returnCodeToString(rc)); NimBLEUtils::returnCodeToString(rc));
// if error getting mtu indicate a connection error. break;
client->m_semaphoreOpenEvt.give(rc);
} }
// In the case of a multiconnecting device we ignore this device when
// scanning since we are already connected to it
NimBLEDevice::addIgnored(client->m_peerAddress);
} else { } else {
// Connection attempt failed
NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s", NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s",
event->connect.status, event->connect.status,
NimBLEUtils::returnCodeToString(event->connect.status)); NimBLEUtils::returnCodeToString(event->connect.status));
client->m_isConnected = false;
rc = event->connect.status;
break;
} }
client->m_semaphoreOpenEvt.give(event->connect.status);
return 0; return 0;
} // BLE_GAP_EVENT_CONNECT } // BLE_GAP_EVENT_CONNECT
@ -701,7 +701,7 @@ uint16_t NimBLEClient::getMTU() {
NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle); NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle);
for(auto &it: client->m_servicesVector) { for(auto &it: client->m_servicesVector) {
// Dont waste cycles searching services without this handle in their range // Dont waste cycles searching services without this handle in its range
if(it->getEndHandle() < event->notify_rx.attr_handle) { if(it->getEndHandle() < event->notify_rx.attr_handle) {
continue; continue;
} }
@ -720,6 +720,11 @@ uint16_t NimBLEClient::getMTU() {
if(characteristic != cVector->cend()) { if(characteristic != cVector->cend()) {
NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", (*characteristic)->toString().c_str()); NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", (*characteristic)->toString().c_str());
portENTER_CRITICAL(&(*characteristic)->m_valMux);
(*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data, event->notify_rx.om->om_len);
(*characteristic)->m_timestamp = time(nullptr);
portEXIT_CRITICAL(&(*characteristic)->m_valMux);
if ((*characteristic)->m_notifyCallback != nullptr) { if ((*characteristic)->m_notifyCallback != nullptr) {
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
(*characteristic)->toString().c_str()); (*characteristic)->toString().c_str());
@ -727,7 +732,6 @@ uint16_t NimBLEClient::getMTU() {
event->notify_rx.om->om_len, event->notify_rx.om->om_len,
!event->notify_rx.indication); !event->notify_rx.indication);
} }
break; break;
} }
} }
@ -738,7 +742,7 @@ uint16_t NimBLEClient::getMTU() {
case BLE_GAP_EVENT_CONN_UPDATE_REQ: case BLE_GAP_EVENT_CONN_UPDATE_REQ:
case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: {
if(client->m_conn_id != event->conn_update_req.conn_handle){ if(client->m_conn_id != event->conn_update_req.conn_handle){
return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE return 0;
} }
NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters");
NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d", NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d",
@ -764,7 +768,7 @@ uint16_t NimBLEClient::getMTU() {
case BLE_GAP_EVENT_CONN_UPDATE: { case BLE_GAP_EVENT_CONN_UPDATE: {
if(client->m_conn_id != event->conn_update.conn_handle){ if(client->m_conn_id != event->conn_update.conn_handle){
return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE return 0;
} }
if(event->conn_update.status == 0) { if(event->conn_update.status == 0) {
NIMBLE_LOGI(LOG_TAG, "Connection parameters updated."); NIMBLE_LOGI(LOG_TAG, "Connection parameters updated.");
@ -776,7 +780,7 @@ uint16_t NimBLEClient::getMTU() {
case BLE_GAP_EVENT_ENC_CHANGE: { case BLE_GAP_EVENT_ENC_CHANGE: {
if(client->m_conn_id != event->enc_change.conn_handle){ if(client->m_conn_id != event->enc_change.conn_handle){
return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE return 0;
} }
if(event->enc_change.status == 0) { if(event->enc_change.status == 0) {
@ -791,23 +795,23 @@ uint16_t NimBLEClient::getMTU() {
} }
} }
client->m_semeaphoreSecEvt.give(event->enc_change.status); rc = event->enc_change.status;
return 0; break;
} //BLE_GAP_EVENT_ENC_CHANGE } //BLE_GAP_EVENT_ENC_CHANGE
case BLE_GAP_EVENT_MTU: { case BLE_GAP_EVENT_MTU: {
if(client->m_conn_id != event->mtu.conn_handle){ if(client->m_conn_id != event->mtu.conn_handle){
return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE return 0;
} }
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d",
event->mtu.conn_handle, event->mtu.conn_handle,
event->mtu.value); event->mtu.value);
client->m_semaphoreOpenEvt.give(0); rc = 0;
return 0; break;
} // BLE_GAP_EVENT_MTU } // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_PASSKEY_ACTION: { case BLE_GAP_EVENT_PASSKEY_ACTION: {
struct ble_sm_io pkey = {0}; struct ble_sm_io pkey = {0,0};
if(client->m_conn_id != event->passkey.conn_handle) if(client->m_conn_id != event->passkey.conn_handle)
return 0; return 0;
@ -868,6 +872,14 @@ uint16_t NimBLEClient::getMTU() {
return 0; return 0;
} }
} // Switch } // Switch
if(client->m_pTaskData != nullptr) {
client->m_pTaskData->rc = rc;
xTaskNotifyGive(client->m_pTaskData->task);
client->m_pTaskData = nullptr;
}
return 0;
} // handleGapEvent } // handleGapEvent

View File

@ -21,17 +21,14 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#include "NimBLEAddress.h" #include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include "NimBLEUtils.h"
#include "NimBLEAdvertisedDevice.h" #include "NimBLEAdvertisedDevice.h"
#include "NimBLERemoteService.h" #include "NimBLERemoteService.h"
#include <vector> #include <vector>
#include <string> #include <string>
typedef struct {
const NimBLEUUID *uuid;
const void *attribute;
} disc_filter_t;
class NimBLERemoteService; class NimBLERemoteService;
class NimBLEClientCallbacks; class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice; class NimBLEAdvertisedDevice;
@ -52,6 +49,8 @@ public:
std::vector<NimBLERemoteService*>::iterator end(); std::vector<NimBLERemoteService*>::iterator end();
NimBLERemoteService* getService(const char* uuid); NimBLERemoteService* getService(const char* uuid);
NimBLERemoteService* getService(const NimBLEUUID &uuid); NimBLERemoteService* getService(const NimBLEUUID &uuid);
void deleteServices();
size_t deleteService(const NimBLEUUID &uuid);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID); std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID, bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
const std::string &value); const std::string &value);
@ -82,19 +81,16 @@ private:
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);
void clearServices();
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr); bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress = NimBLEAddress(""); NimBLEAddress m_peerAddress = NimBLEAddress("");
uint16_t m_conn_id; uint16_t m_conn_id;
bool m_isConnected = false; bool m_isConnected;
bool m_waitingToConnect =false; bool m_waitingToConnect;
bool m_deleteCallbacks = true; bool m_deleteCallbacks;
int32_t m_connectTimeout; int32_t m_connectTimeout;
NimBLEClientCallbacks* m_pClientCallbacks = nullptr; NimBLEClientCallbacks* m_pClientCallbacks;
FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); ble_task_data_t *m_pTaskData;
FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt");
FreeRTOS::Semaphore m_semeaphoreSecEvt = FreeRTOS::Semaphore("Security");
std::vector<NimBLERemoteService*> m_servicesVector; std::vector<NimBLERemoteService*> m_servicesVector;

View File

@ -50,6 +50,7 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_
m_pCharacteristic = nullptr; // No initial characteristic. m_pCharacteristic = nullptr; // No initial characteristic.
m_pCallbacks = &defaultCallbacks; // No initial callback. m_pCallbacks = &defaultCallbacks; // No initial callback.
m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value. m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value.
m_valMux = portMUX_INITIALIZER_UNLOCKED;
m_properties = 0; m_properties = 0;
if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t
@ -137,17 +138,37 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){ if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){
switch(ctxt->op) { switch(ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_DSC: { 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); pDescriptor->m_pCallbacks->onRead(pDescriptor);
}
portENTER_CRITICAL(&pDescriptor->m_valMux);
rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength()); rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength());
portEXIT_CRITICAL(&pDescriptor->m_valMux);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
case BLE_GATT_ACCESS_OP_WRITE_DSC: { case BLE_GATT_ACCESS_OP_WRITE_DSC: {
if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { if (ctxt->om->om_len > pDescriptor->m_value.attr_max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
} }
pDescriptor->setValue(ctxt->om->om_data, ctxt->om->om_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-1], 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); pDescriptor->m_pCallbacks->onWrite(pDescriptor);
return 0; return 0;
} }
@ -191,12 +212,14 @@ void NimBLEDescriptor::setHandle(uint16_t handle) {
* @param [in] length The length of the data in bytes. * @param [in] length The length of the data in bytes.
*/ */
void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) { void NimBLEDescriptor::setValue(const uint8_t* data, size_t length) {
if (length > BLE_ATT_ATTR_MAX_LEN) { if (length > m_value.attr_max_len) {
NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN); NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, m_value.attr_max_len);
return; return;
} }
portENTER_CRITICAL(&m_valMux);
m_value.attr_len = length; m_value.attr_len = length;
memcpy(m_value.attr_value, data, length); memcpy(m_value.attr_value, data, length);
portEXIT_CRITICAL(&m_valMux);
} // setValue } // setValue
@ -209,13 +232,6 @@ void NimBLEDescriptor::setValue(const std::string &value) {
} // setValue } // setValue
/*
void NimBLEDescriptor::setAccessPermissions(uint8_t perm) {
m_permissions = perm;
}
*/
/** /**
* @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.

View File

@ -22,7 +22,6 @@
#include "NimBLECharacteristic.h" #include "NimBLECharacteristic.h"
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include "FreeRTOS.h"
#include <string> #include <string>
@ -34,8 +33,6 @@ typedef struct
uint8_t *attr_value; /*!< the pointer to attribute value */ uint8_t *attr_value; /*!< the pointer to attribute value */
} attr_value_t; } attr_value_t;
typedef attr_value_t esp_attr_value_t; /*!< compatibility for esp32 */
class NimBLEService; class NimBLEService;
class NimBLECharacteristic; class NimBLECharacteristic;
class NimBLEDescriptorCallbacks; class NimBLEDescriptorCallbacks;
@ -46,20 +43,21 @@ class NimBLEDescriptorCallbacks;
*/ */
class NimBLEDescriptor { class NimBLEDescriptor {
public: public:
virtual ~NimBLEDescriptor(); uint16_t getHandle();
uint16_t getHandle(); // Get the handle of the descriptor. size_t getLength();
size_t getLength(); // Get the length of the value of the descriptor. NimBLEUUID getUUID();
NimBLEUUID getUUID(); // Get the UUID of the descriptor. uint8_t* getValue();
uint8_t* getValue(); // Get a pointer to the value of the descriptor. void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks);
// void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. void setValue(const uint8_t* data, size_t size);
void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. void setValue(const std::string &value);
void setValue(const uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. std::string toString();
void setValue(const std::string &value); // Set the value of the descriptor as a data buffer.
std::string toString(); // Convert the descriptor to a string representation. template<typename T>
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
}
private: private:
friend class NimBLEDescriptorMap;
friend class NimBLECharacteristic; friend class NimBLECharacteristic;
friend class NimBLEService; friend class NimBLEService;
friend class NimBLE2902; friend class NimBLE2902;
@ -73,18 +71,20 @@ private:
uint16_t max_len, uint16_t max_len,
NimBLECharacteristic* pCharacteristic); NimBLECharacteristic* pCharacteristic);
~NimBLEDescriptor();
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
void setHandle(uint16_t handle);
NimBLEUUID m_uuid; NimBLEUUID m_uuid;
uint16_t m_handle; uint16_t m_handle;
NimBLEDescriptorCallbacks* m_pCallbacks; NimBLEDescriptorCallbacks* m_pCallbacks;
NimBLECharacteristic* m_pCharacteristic; NimBLECharacteristic* m_pCharacteristic;
uint8_t m_properties; uint8_t m_properties;
attr_value_t m_value; attr_value_t m_value;
portMUX_TYPE m_valMux;
static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, }; // NimBLEDescriptor
struct ble_gatt_access_ctxt *ctxt, void *arg);
void setHandle(uint16_t handle);
}; // BLEDescriptor
/** /**

View File

@ -1,149 +0,0 @@
/*
* NimBLEDescriptorMap.cpp
*
* Created: on March 10, 2020
* Author H2zero
*
* Originally:
*
* BLEDescriptorMap.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
#include "NimBLEDescriptor.h"
/**
* @brief Return the descriptor by UUID.
* @param [in] UUID The UUID to look up the descriptor.
* @return The descriptor. If not present, then nullptr is returned.
*/
NimBLEDescriptor* NimBLEDescriptorMap::getByUUID(const char* uuid) {
return getByUUID(NimBLEUUID(uuid));
}
/**
* @brief Return the descriptor by UUID.
* @param [in] UUID The UUID to look up the descriptor.
* @return The descriptor. If not present, then nullptr is returned.
*/
NimBLEDescriptor* NimBLEDescriptorMap::getByUUID(const NimBLEUUID &uuid) {
for (auto &myPair : m_uuidMap) {
if (myPair.first->getUUID().equals(uuid)) {
return myPair.first;
}
}
return nullptr;
} // getByUUID
/**
* @brief Return the descriptor by handle.
* @param [in] handle The handle to look up the descriptor.
* @return The descriptor.
*/
/*
NimBLEDescriptor* NimBLEDescriptorMap::getByHandle(uint16_t handle) {
return m_handleMap.at(handle);
} // getByHandle
*/
/**
* @brief Set the descriptor by UUID.
* @param [in] uuid The uuid of the descriptor.
* @param [in] characteristic The descriptor to cache.
* @return N/A.
*/
void NimBLEDescriptorMap::setByUUID(const char* uuid, NimBLEDescriptor* pDescriptor){
m_uuidMap.insert(std::pair<NimBLEDescriptor*, std::string>(pDescriptor, uuid));
} // setByUUID
/**
* @brief Set the descriptor by UUID.
* @param [in] uuid The uuid of the descriptor.
* @param [in] characteristic The descriptor to cache.
* @return N/A.
*/
void NimBLEDescriptorMap::setByUUID(const NimBLEUUID &uuid, NimBLEDescriptor* pDescriptor) {
m_uuidMap.insert(std::pair<NimBLEDescriptor*, std::string>(pDescriptor, uuid.toString()));
} // setByUUID
/**
* @brief Set the descriptor by handle.
* @param [in] handle The handle of the descriptor.
* @param [in] descriptor The descriptor to cache.
* @return N/A.
*/
/*
void NimBLEDescriptorMap::setByHandle(uint16_t handle, NimBLEDescriptor* pDescriptor) {
m_handleMap.insert(std::pair<uint16_t, NimBLEDescriptor*>(handle, pDescriptor));
} // setByHandle
*/
/**
* @brief Get the number of descriptors in the map.
*/
uint8_t NimBLEDescriptorMap::getSize() {
return (uint8_t)m_uuidMap.size();
} // getSize
/**
* @brief Return a string representation of the descriptor map.
* @return A string representation of the descriptor map.
*/
std::string NimBLEDescriptorMap::toString() {
std::string res;
char hex[5];
int count = 0;
for (auto &myPair : m_uuidMap) {
if (count > 0) {res += "\n";}
snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + myPair.first->getUUID().toString();
}
return res;
} // toString
/**
* @brief Get the first descriptor in the map.
* @return The first descriptor in the map.
*/
NimBLEDescriptor* NimBLEDescriptorMap::getFirst() {
m_iterator = m_uuidMap.begin();
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLEDescriptor* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getFirst
/**
* @brief Get the next descriptor in the map.
* @return The next descriptor in the map.
*/
NimBLEDescriptor* NimBLEDescriptorMap::getNext() {
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLEDescriptor* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getNext
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -152,21 +152,26 @@ void NimBLEDevice::stopAdvertising() {
return false; return false;
} }
int rc =0;
if(pClient->m_isConnected) { if(pClient->m_isConnected) {
if (pClient->disconnect() != 0) { rc = pClient->disconnect();
if (rc != 0 && rc != BLE_HS_EALREADY && rc != BLE_HS_ENOTCONN) {
return false; return false;
} }
while(pClient->m_isConnected) { while(pClient->m_isConnected) {
vTaskDelay(1); vTaskDelay(10);
} }
} }
if(pClient->m_waitingToConnect) { if(pClient->m_waitingToConnect) {
if(ble_gap_conn_cancel() != 0){ rc = ble_gap_conn_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) {
return false; return false;
} }
while(pClient->m_waitingToConnect) { while(pClient->m_waitingToConnect) {
vTaskDelay(1); vTaskDelay(10);
} }
} }

View File

@ -116,6 +116,7 @@ public:
static void setSecurityPasskey(uint32_t pin); static void setSecurityPasskey(uint32_t pin);
static uint32_t getSecurityPasskey(); static uint32_t getSecurityPasskey();
static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks); static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks);
static int startSecurity(uint16_t conn_id);
static int setMTU(uint16_t mtu); static int setMTU(uint16_t mtu);
static uint16_t getMTU(); static uint16_t getMTU();
static bool isIgnored(const NimBLEAddress &address); static bool isIgnored(const NimBLEAddress &address);
@ -159,7 +160,6 @@ private:
static void onReset(int reason); static void onReset(int reason);
static void onSync(void); static void onSync(void);
static void host_task(void *param); static void host_task(void *param);
static int startSecurity(uint16_t conn_id);
static bool m_synced; static bool m_synced;
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)

View File

@ -25,25 +25,25 @@
#if CORE_DEBUG_LEVEL >= 4 #if CORE_DEBUG_LEVEL >= 4
#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(ERROR, "D %s: "#format"\n",tag,##__VA_ARGS__) #define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(ERROR, "D %s: "#format"\n",tag,##__VA_ARGS__)
#else #else
#define NIMBLE_LOGD( tag, format, ... ) #define NIMBLE_LOGD( tag, format, ... ) (void)tag
#endif #endif
#if CORE_DEBUG_LEVEL >= 3 #if CORE_DEBUG_LEVEL >= 3
#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(ERROR, "I %s: "#format"\n",tag,##__VA_ARGS__) #define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(ERROR, "I %s: "#format"\n",tag,##__VA_ARGS__)
#else #else
#define NIMBLE_LOGI( tag, format, ... ) #define NIMBLE_LOGI( tag, format, ... ) (void)tag
#endif #endif
#if CORE_DEBUG_LEVEL >= 2 #if CORE_DEBUG_LEVEL >= 2
#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(ERROR, "W %s: "#format"\n",tag,##__VA_ARGS__) #define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(ERROR, "W %s: "#format"\n",tag,##__VA_ARGS__)
#else #else
#define NIMBLE_LOGW( tag, format, ... ) #define NIMBLE_LOGW( tag, format, ... ) (void)tag
#endif #endif
#if CORE_DEBUG_LEVEL >= 1 #if CORE_DEBUG_LEVEL >= 1
#define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "E %s: "#format"\n",tag,##__VA_ARGS__) #define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "E %s: "#format"\n",tag,##__VA_ARGS__)
#else #else
#define NIMBLE_LOGE( tag, format, ... ) #define NIMBLE_LOGE( tag, format, ... ) (void)tag
#endif #endif
#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "CRIT %s: "#format"\n",tag,##__VA_ARGS__) #define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "CRIT %s: "#format"\n",tag,##__VA_ARGS__)

View File

@ -53,13 +53,14 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_uuid = nullptr; m_uuid = nullptr;
break; break;
} }
m_handle = chr->val_handle; m_handle = chr->val_handle;
m_defHandle = chr->def_handle; m_defHandle = chr->def_handle;
m_charProp = chr->properties; m_charProp = chr->properties;
m_pRemoteService = pRemoteService; m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr; m_notifyCallback = nullptr;
m_rawData = nullptr; m_timestamp = 0;
m_dataLen = 0; m_valMux = portMUX_INITIALIZER_UNLOCKED;
} // NimBLERemoteCharacteristic } // NimBLERemoteCharacteristic
@ -67,10 +68,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
*@brief Destructor. *@brief Destructor.
*/ */
NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() { NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() {
removeDescriptors(); // Release resources for any descriptor information we may have allocated. deleteDescriptors();
if(m_rawData != nullptr) {
free(m_rawData);
}
} // ~NimBLERemoteCharacteristic } // ~NimBLERemoteCharacteristic
/* /*
@ -150,12 +148,12 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
NIMBLE_LOGD(LOG_TAG,"Descriptor Discovered >> status: %d handle: %d", NIMBLE_LOGD(LOG_TAG,"Descriptor Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? dsc->handle : -1); error->status, (error->status == 0) ? dsc->handle : -1);
disc_filter_t *filter = (disc_filter_t*)arg; desc_filter_t *filter = (desc_filter_t*)arg;
NimBLEUUID *uuid_filter = (NimBLEUUID*)filter->uuid; const NimBLEUUID *uuid_filter = filter->uuid;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)filter->attribute; ble_task_data_t *pTaskData = (ble_task_data_t*)filter->task_data;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
int rc=0; int rc=0;
// Make sure the discovery is for this device
if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0; return 0;
} }
@ -175,7 +173,7 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
rc = BLE_HS_EDONE; rc = BLE_HS_EDONE;
} }
} }
// Found a descriptor - add it to the vector
NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc); NimBLERemoteDescriptor* pNewRemoteDescriptor = new NimBLERemoteDescriptor(characteristic, dsc);
characteristic->m_descriptorVector.push_back(pNewRemoteDescriptor); characteristic->m_descriptorVector.push_back(pNewRemoteDescriptor);
break; break;
@ -185,18 +183,20 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
break; break;
} }
/** If rc == BLE_HS_EDONE, release the semaphore with a success error code and stop the discovery process. /** If rc == BLE_HS_EDONE, resume the task with a success error code and stop the discovery process.
* Else if rc == 0, just return 0 to continue the discovery until we get BLE_HS_EDONE. * 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 semaphore rc. * If we get any other error code tell the application to abort by returning non-zero in the rc.
*/ */
if (rc == BLE_HS_EDONE) { if (rc == BLE_HS_EDONE) {
characteristic->m_semaphoreGetDescEvt.give(0); pTaskData->rc = 0;
xTaskNotifyGive(pTaskData->task);
} else if(rc != 0) { } else if(rc != 0) {
/* Error; abort discovery. */ // Error; abort discovery.
// pass error code to semaphore waiting pTaskData->rc = rc;
characteristic->m_semaphoreGetDescEvt.give(rc); xTaskNotifyGive(pTaskData->task);
} }
NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", rc);
NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", pTaskData->rc);
return rc; return rc;
} }
@ -209,11 +209,8 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
int rc = 0; int rc = 0;
disc_filter_t filter; ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
filter.uuid = uuid_filter; desc_filter_t filter = {uuid_filter, &taskData};
filter.attribute = this;
m_semaphoreGetDescEvt.take("retrieveDescriptors");
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(), rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle, m_handle,
@ -222,11 +219,12 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
&filter); &filter);
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_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
m_semaphoreGetDescEvt.give();
return false; return false;
} }
if(m_semaphoreGetDescEvt.wait("retrieveDescriptors") != 0) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0) {
return false; return false;
} }
@ -272,10 +270,8 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
*/ */
std::vector<NimBLERemoteDescriptor*>* NimBLERemoteCharacteristic::getDescriptors(bool refresh) { std::vector<NimBLERemoteDescriptor*>* NimBLERemoteCharacteristic::getDescriptors(bool refresh) {
if(refresh) { if(refresh) {
removeDescriptors(); deleteDescriptors();
}
if(m_descriptorVector.empty()) {
if (!retrieveDescriptors()) { if (!retrieveDescriptors()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors"); NIMBLE_LOGE(LOG_TAG, "Error: Failed to get descriptors");
} }
@ -340,16 +336,28 @@ NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
} // getUUID } // getUUID
/**
* @brief Get the value of the remote characteristic.
* @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 * @brief Read an unsigned 16 bit value
* @return The unsigned 16 bit value. * @return The unsigned 16 bit value.
*/ */
uint16_t NimBLERemoteCharacteristic::readUInt16() { uint16_t NimBLERemoteCharacteristic::readUInt16() {
std::string value = readValue(); return readValue<uint16_t>();
if (value.length() >= 2) {
return *(uint16_t*)(value.data());
}
return 0;
} // readUInt16 } // readUInt16
@ -358,11 +366,7 @@ uint16_t NimBLERemoteCharacteristic::readUInt16() {
* @return the unsigned 32 bit value. * @return the unsigned 32 bit value.
*/ */
uint32_t NimBLERemoteCharacteristic::readUInt32() { uint32_t NimBLERemoteCharacteristic::readUInt32() {
std::string value = readValue(); return readValue<uint32_t>();
if (value.length() >= 4) {
return *(uint32_t*)(value.data());
}
return 0;
} // readUInt32 } // readUInt32
@ -371,49 +375,52 @@ uint32_t NimBLERemoteCharacteristic::readUInt32() {
* @return The value as a byte * @return The value as a byte
*/ */
uint8_t NimBLERemoteCharacteristic::readUInt8() { uint8_t NimBLERemoteCharacteristic::readUInt8() {
std::string value = readValue(); return readValue<uint8_t>();
if (value.length() >= 1) {
return (uint8_t)value[0];
}
return 0;
} // readUInt8 } // 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. * @brief Read the value of the remote characteristic.
* @return The value of the remote characteristic. * @return The value of the remote characteristic.
*/ */
std::string NimBLERemoteCharacteristic::readValue() { std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", NIMBLE_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x",
getUUID().toString().c_str(), getHandle(), getHandle()); getUUID().toString().c_str(), getHandle(), getHandle());
int rc = 0;
int retryCount = 1;
// Clear the value before reading.
m_value = "";
NimBLEClient* pClient = getRemoteService()->getClient(); NimBLEClient* pClient = getRemoteService()->getClient();
std::string value;
// Check to see that we are connected.
if (!pClient->isConnected()) { if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected"); NIMBLE_LOGE(LOG_TAG, "Disconnected");
return ""; return value;
} }
do { int rc = 0;
m_semaphoreReadCharEvt.take("readValue"); int retryCount = 1;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(),0, &value};
do {
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0, rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
NimBLERemoteCharacteristic::onReadCB, NimBLERemoteCharacteristic::onReadCB,
this); &taskData);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s", NIMBLE_LOGE(LOG_TAG, "Error: Failed to read characteristic; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc)); rc, NimBLEUtils::returnCodeToString(rc));
m_semaphoreReadCharEvt.give(0); return value;
return "";
} }
rc = m_semaphoreReadCharEvt.wait("readValue"); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc){ switch(rc){
case 0: case 0:
case BLE_HS_EDONE: case BLE_HS_EDONE:
@ -431,12 +438,21 @@ std::string NimBLERemoteCharacteristic::readValue() {
break; break;
/* Else falls through. */ /* Else falls through. */
default: default:
return ""; NIMBLE_LOGE(LOG_TAG, "<< readValue rc=%d", rc);
return value;
} }
} while(rc != 0 && retryCount--); } while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length()); portENTER_CRITICAL(&m_valMux);
return m_value; m_value = value;
m_timestamp = time(nullptr);
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 } // readValue
@ -448,31 +464,91 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg) struct ble_gatt_attr *attr, void *arg)
{ {
NimBLERemoteCharacteristic* characteristic = (NimBLERemoteCharacteristic*)arg; ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
uint16_t conn_id = characteristic->getRemoteService()->getClient()->getConnId(); uint16_t conn_id = characteristic->getRemoteService()->getClient()->getConnId();
// Make sure the read is for this client
if(conn_id != conn_handle) { if(conn_id != conn_handle) {
return 0; return 0;
} }
NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
if(error->status == 0) { std::string *strBuf = (std::string*)pTaskData->buf;
if(attr) { int rc = error->status;
NIMBLE_LOGD(LOG_TAG, "Got %d bytes", attr->om->om_len);
characteristic->m_value += std::string((char*) attr->om->om_data, attr->om->om_len); 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; return 0;
} }
} }
// Read complete release semaphore and let the app can continue. }
characteristic->m_semaphoreReadCharEvt.give(error->status);
return 0; pTaskData->rc = rc;
xTaskNotifyGive(pTaskData->task);
return rc;
} }
/** /**
* @brief Register for notifications. * @brief Subscribe or unsubscribe for notifications or indications.
* @param [in] uint16_t val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then no callback
* is performed for notifications.
* @return true if successful.
*/
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, bool response, notify_callback notifyCallback) {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
if(desc == nullptr) {
NIMBLE_LOGE(LOG_TAG, "<< setNotify(): Could not get descriptor");
return false;
}
m_notifyCallback = notifyCallback;
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
return desc->writeValue((uint8_t *)&val, 2, response);
} // setNotify
/**
* @brief Subscribe for notifications or indications.
* @param [in] bool if true, subscribe for notifications, false subscribe for indications.
* @param [in] bool if true, require a write response from the descriptor write operation.
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then no callback
* is performed for notifications.
* @return true if successful.
*/
bool NimBLERemoteCharacteristic::subscribe(bool notifications, bool response, notify_callback notifyCallback) {
if(notifications) {
return setNotify(0x01, response, notifyCallback);
} else {
return setNotify(0x02, response, notifyCallback);
}
} // subscribe
/**
* @brief Unsubscribe for notifications or indications.
* @param [in] bool if true, require a write response from the descriptor write operation.
* @return true if successful.
*/
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
return setNotify(0x00, response);
} // 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 are * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are
* unregistering for notifications. * unregistering for notifications.
* @param [in] bool if true, register for notifications, false register for indications. * @param [in] bool if true, register for notifications, false register for indications.
@ -480,46 +556,54 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
* @return true if successful. * @return true if successful.
*/ */
bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications, bool response) { bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str()); bool success;
m_notifyCallback = notifyCallback; // Save the notification callback.
uint8_t val[] = {0x01, 0x00};
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
if(desc == nullptr)
return false;
if(notifyCallback != nullptr) { if(notifyCallback != nullptr) {
if(!notifications){ success = subscribe(notifications, response, notifyCallback);
val[0] = 0x02; } else {
success = unsubscribe(response);
} }
} return success;
else if (notifyCallback == nullptr){
val[0] = 0x00;
}
NIMBLE_LOGD(LOG_TAG, "<< registerForNotify()");
return desc->writeValue(val, 2, response);
} // registerForNotify } // registerForNotify
/** /**
* @brief Delete the descriptors in the descriptor vector. * @brief Delete the descriptors in the descriptor vector.
* We maintain a vector called m_descriptorVector that contains pointers to BLERemoteDescriptors * We maintain a vector called m_descriptorVector that contains pointers to BLERemoteDescriptors
* object references. Since we allocated these in this class, we are also responsible for deleteing * 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.
* @return N/A. * @return N/A.
*/ */
void NimBLERemoteCharacteristic::removeDescriptors() { void NimBLERemoteCharacteristic::deleteDescriptors() {
// Iterate through all the descriptors releasing their storage and erasing them from the vector. NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors");
for(auto &it: m_descriptorVector) { for(auto &it: m_descriptorVector) {
delete it; delete it;
} }
m_descriptorVector.clear(); m_descriptorVector.clear();
} // removeCharacteristics NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptors");
} // deleteDescriptors
/**
* @brief Delete descriptor by UUID
* @param [in] uuid The UUID of the descriptor to be deleted.
* @return Number of services left.
*/
size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor");
for(auto it = m_descriptorVector.begin(); it != m_descriptorVector.end(); ++it) {
if((*it)->getUUID() == uuid) {
delete *it;
m_descriptorVector.erase(it);
break;
}
}
NIMBLE_LOGD(LOG_TAG, "<< deleteDescriptor");
return m_descriptorVector.size();
} // deleteDescriptor
/** /**
@ -559,19 +643,6 @@ bool NimBLERemoteCharacteristic::writeValue(const std::string &newValue, bool re
} // writeValue } // writeValue
/**
* @brief Write the new value for the characteristic.
*
* This is a convenience function. Many BLE characteristics are a single byte of data.
* @param [in] newValue The new byte value to write.
* @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(uint8_t newValue, bool response) {
return writeValue(&newValue, 1, response);
} // writeValue
/** /**
* @brief Write the new value for the characteristic from a data buffer. * @brief Write the new value for the characteristic from a data buffer.
* @param [in] data A pointer to a data buffer. * @param [in] data A pointer to a data buffer.
@ -584,47 +655,45 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length,
NIMBLE_LOGD(LOG_TAG, ">> writeValue(), length: %d", length); NIMBLE_LOGD(LOG_TAG, ">> writeValue(), length: %d", length);
NimBLEClient* pClient = getRemoteService()->getClient(); NimBLEClient* pClient = getRemoteService()->getClient();
int rc = 0;
int retryCount = 1;
uint16_t mtu;
// Check to see that we are connected.
if (!pClient->isConnected()) { if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected"); NIMBLE_LOGE(LOG_TAG, "Disconnected");
return false; return false;
} }
mtu = ble_att_mtu(pClient->getConnId()) - 3; 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. // 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 so we must do a long write which requires a response.
if(length <= mtu && !response) { if(length <= mtu && !response) {
rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length); rc = ble_gattc_write_no_rsp_flat(pClient->getConnId(), m_handle, data, length);
return (rc==0); return (rc==0);
} }
do { ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_semaphoreWriteCharEvt.take("writeValue");
do {
if(length > mtu) { if(length > mtu) {
NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length); NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
os_mbuf *om = ble_hs_mbuf_from_flat(data, length); os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
NimBLERemoteCharacteristic::onWriteCB, NimBLERemoteCharacteristic::onWriteCB,
this); &taskData);
} else { } else {
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
data, length, data, length,
NimBLERemoteCharacteristic::onWriteCB, NimBLERemoteCharacteristic::onWriteCB,
this); &taskData);
} }
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc); NIMBLE_LOGE(LOG_TAG, "Error: Failed to write characteristic; rc=%d", rc);
m_semaphoreWriteCharEvt.give();
return false; return false;
} }
rc = m_semaphoreWriteCharEvt.wait("writeValue"); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc){ switch(rc){
case 0: case 0:
@ -644,6 +713,7 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length,
break; break;
/* Else falls through. */ /* Else falls through. */
default: default:
NIMBLE_LOGE(LOG_TAG, "<< writeValue, rc: %d", rc);
return false; return false;
} }
} while(rc != 0 && retryCount--); } while(rc != 0 && retryCount--);
@ -661,59 +731,21 @@ int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg) struct ble_gatt_attr *attr, void *arg)
{ {
NimBLERemoteCharacteristic* characteristic = (NimBLERemoteCharacteristic*)arg; ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteCharacteristic *characteristic = (NimBLERemoteCharacteristic*)pTaskData->pATT;
// Make sure the discovery is for this device
if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){ if(characteristic->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0; return 0;
} }
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
characteristic->m_semaphoreWriteCharEvt.give(error->status); pTaskData->rc = error->status;
xTaskNotifyGive(pTaskData->task);
return 0; return 0;
} }
/**
* @brief Read raw data from remote characteristic as hex bytes
* @return uint8_t pointer to the data read.
*/
uint8_t* NimBLERemoteCharacteristic::readRawData() {
if(m_rawData != nullptr) {
free(m_rawData);
m_rawData = nullptr;
}
m_dataLen = m_value.length();
// If we have data copy it to rawData
if(m_dataLen) {
m_rawData = (uint8_t*) calloc(m_dataLen, sizeof(uint8_t));
memcpy(m_rawData, m_value.data(), m_dataLen);
}
return m_rawData;
}
/**
* @brief Get the length of the data read from the remote characteristic.
* @return size_t length of the data in bytes.
*/
size_t NimBLERemoteCharacteristic::getDataLength() {
return m_value.length();
}
void NimBLERemoteCharacteristic::releaseSemaphores() {
for (auto &it: m_descriptorVector) {
it->releaseSemaphores();
}
m_semaphoreWriteCharEvt.give(1);
m_semaphoreGetDescEvt.give(1);
m_semaphoreReadCharEvt.give(1);
}
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */ #endif /* CONFIG_BT_ENABLED */

View File

@ -32,6 +32,12 @@ class NimBLERemoteDescriptor;
typedef void (*notify_callback)(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, typedef void (*notify_callback)(NimBLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData, size_t length, bool isNotify); uint8_t* pData, size_t length, bool isNotify);
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.
*/ */
@ -50,26 +56,54 @@ public:
std::vector<NimBLERemoteDescriptor*>::iterator end(); std::vector<NimBLERemoteDescriptor*>::iterator end();
NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid); NimBLERemoteDescriptor* getDescriptor(const NimBLEUUID &uuid);
std::vector<NimBLERemoteDescriptor*>* getDescriptors(bool refresh = false); std::vector<NimBLERemoteDescriptor*>* getDescriptors(bool refresh = false);
void deleteDescriptors();
size_t deleteDescriptor(const NimBLEUUID &uuid);
uint16_t getHandle(); uint16_t getHandle();
uint16_t getDefHandle(); uint16_t getDefHandle();
NimBLEUUID getUUID(); NimBLEUUID getUUID();
std::string readValue(); std::string readValue(time_t *timestamp = nullptr);
uint8_t readUInt8();
uint16_t readUInt16(); template<typename T>
uint32_t readUInt32(); T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
bool registerForNotify(notify_callback _callback, 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>()")));
uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue<uint16_t>()")));
uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue<uint32_t>()")));
float readFloat() __attribute__ ((deprecated("Use template readValue<float>()")));
std::string getValue(time_t *timestamp = nullptr);
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,
bool response = true,
notify_callback notifyCallback = nullptr);
bool unsubscribe(bool response = true);
bool registerForNotify(notify_callback notifyCallback,
bool notifications = true, bool notifications = true,
bool response = true); bool response = true)
__attribute__ ((deprecated("Use subscribe()/unsubscribe()")));
bool writeValue(const uint8_t* data, bool writeValue(const uint8_t* data,
size_t length, size_t length,
bool response = false); bool response = false);
bool writeValue(const std::string &newValue, bool writeValue(const std::string &newValue,
bool response = false); bool response = false);
bool writeValue(uint8_t newValue, template<typename T>
bool response = false); bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
}
std::string toString(); std::string toString();
uint8_t* readRawData();
size_t getDataLength();
NimBLERemoteService* getRemoteService(); NimBLERemoteService* getRemoteService();
private: private:
@ -81,13 +115,12 @@ private:
friend class NimBLERemoteDescriptor; friend class NimBLERemoteDescriptor;
// Private member functions // Private member functions
void removeDescriptors(); bool setNotify(uint16_t val, bool response = true, notify_callback notifyCallback = nullptr);
bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr); bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg); struct ble_gatt_attr *attr, void *arg);
static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error, static int onWriteCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg); struct ble_gatt_attr *attr, void *arg);
void releaseSemaphores();
static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error, static int descriptorDiscCB(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc, uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg); void *arg);
@ -98,13 +131,10 @@ private:
uint16_t m_handle; uint16_t m_handle;
uint16_t m_defHandle; uint16_t m_defHandle;
NimBLERemoteService* m_pRemoteService; NimBLERemoteService* m_pRemoteService;
FreeRTOS::Semaphore m_semaphoreGetDescEvt = FreeRTOS::Semaphore("GetDescEvt");
FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt");
FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt");
std::string m_value; std::string m_value;
uint8_t* m_rawData;
size_t m_dataLen;
notify_callback m_notifyCallback; notify_callback m_notifyCallback;
time_t m_timestamp;
portMUX_TYPE m_valMux;
// We maintain a vector of descriptors owned by this characteristic. // We maintain a vector of descriptors owned by this characteristic.
std::vector<NimBLERemoteDescriptor*> m_descriptorVector; std::vector<NimBLERemoteDescriptor*> m_descriptorVector;

View File

@ -45,9 +45,9 @@ NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemo
m_uuid = nullptr; m_uuid = nullptr;
break; break;
} }
m_handle = dsc->handle; m_handle = dsc->handle;
m_pRemoteCharacteristic = pRemoteCharacteristic; m_pRemoteCharacteristic = pRemoteCharacteristic;
} }
@ -78,96 +78,6 @@ NimBLEUUID NimBLERemoteDescriptor::getUUID() {
} // getUUID } // getUUID
/**
* @brief Callback for Descriptor read operation.
* @return 0 or error code.
*/
int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg)
{
NimBLERemoteDescriptor* desc = (NimBLERemoteDescriptor*)arg;
uint16_t conn_id = desc->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId();
// Make sure the discovery is for this device
if(conn_id != conn_handle){
return 0;
}
NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle);
if(error->status == 0){
if(attr){
NIMBLE_LOGD(LOG_TAG, "Got %d bytes", attr->om->om_len);
desc->m_value += std::string((char*) attr->om->om_data, attr->om->om_len);
return 0;
}
}
// Read complete release semaphore and let the app can continue.
desc->m_semaphoreReadDescrEvt.give(error->status);
return 0;
}
std::string NimBLERemoteDescriptor::readValue() {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
int rc = 0;
int retryCount = 1;
// Clear the value before reading.
m_value = "";
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
// Check to see that we are connected.
if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected");
return "";
}
do {
m_semaphoreReadDescrEvt.take("ReadDescriptor");
rc = ble_gattc_read_long(pClient->getConnId(), m_handle, 0,
NimBLERemoteDescriptor::onReadCB,
this);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to read descriptor; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
m_semaphoreReadDescrEvt.give(0);
return "";
}
rc = m_semaphoreReadDescrEvt.wait("ReadDescriptor");
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 "";
}
} while(rc != 0 && retryCount--);
NIMBLE_LOGD(LOG_TAG, "<< Descriptor readValue(): length: %d", m_value.length());
return m_value;
} // readValue
uint8_t NimBLERemoteDescriptor::readUInt8() { uint8_t NimBLERemoteDescriptor::readUInt8() {
std::string value = readValue(); std::string value = readValue();
if (value.length() >= 1) { if (value.length() >= 1) {
@ -195,6 +105,100 @@ uint32_t NimBLERemoteDescriptor::readUInt32() {
} // readUInt32 } // readUInt32
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 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 BLE Remote Descriptor. * @brief Return a string representation of this BLE Remote Descriptor.
* @retun A string representation of this BLE Remote Descriptor. * @retun A string representation of this BLE Remote Descriptor.
@ -218,16 +222,17 @@ int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg) struct ble_gatt_attr *attr, void *arg)
{ {
NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)arg; ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
NimBLERemoteDescriptor* descriptor = (NimBLERemoteDescriptor*)pTaskData->pATT;
// Make sure the discovery is for this device
if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){ if(descriptor->getRemoteCharacteristic()->getRemoteService()->getClient()->getConnId() != conn_handle){
return 0; return 0;
} }
NIMBLE_LOGD(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle);
descriptor->m_semaphoreDescWrite.give(error->status); pTaskData->rc = error->status;
xTaskNotifyGive(pTaskData->task);
return 0; return 0;
} }
@ -245,17 +250,15 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient(); NimBLEClient* pClient = getRemoteCharacteristic()->getRemoteService()->getClient();
int rc = 0;
int retryCount = 1;
uint16_t mtu;
// Check to see that we are connected. // Check to see that we are connected.
if (!pClient->isConnected()) { if (!pClient->isConnected()) {
NIMBLE_LOGE(LOG_TAG, "Disconnected"); NIMBLE_LOGE(LOG_TAG, "Disconnected");
return false; return false;
} }
mtu = ble_att_mtu(pClient->getConnId()) - 3; 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. // 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 so we must do a long write which requires a response.
@ -264,29 +267,29 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
return (rc == 0); return (rc == 0);
} }
do { ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_semaphoreDescWrite.take("WriteDescriptor");
do {
if(length > mtu) { if(length > mtu) {
NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length); NIMBLE_LOGI(LOG_TAG,"long write %d bytes", length);
os_mbuf *om = ble_hs_mbuf_from_flat(data, length); os_mbuf *om = ble_hs_mbuf_from_flat(data, length);
rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om, rc = ble_gattc_write_long(pClient->getConnId(), m_handle, 0, om,
NimBLERemoteDescriptor::onWriteCB, NimBLERemoteDescriptor::onWriteCB,
this); &taskData);
} else { } else {
rc = ble_gattc_write_flat(pClient->getConnId(), m_handle, rc = ble_gattc_write_flat(pClient->getConnId(), m_handle,
data, length, data, length,
NimBLERemoteDescriptor::onWriteCB, NimBLERemoteDescriptor::onWriteCB,
this); &taskData);
} }
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc); NIMBLE_LOGE(LOG_TAG, "Error: Failed to write descriptor; rc=%d", rc);
m_semaphoreDescWrite.give();
return false; return false;
} }
rc = m_semaphoreDescWrite.wait("WriteDescriptor"); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch(rc) { switch(rc) {
case 0: case 0:
@ -325,23 +328,5 @@ bool NimBLERemoteDescriptor::writeValue(const std::string &newValue, bool respon
} // writeValue } // writeValue
/**
* @brief Write a byte value to the Descriptor.
* @param [in] The single byte to write.
* @param [in] True if we expect a response.
*/
bool NimBLERemoteDescriptor::writeValue(uint8_t newValue, bool response) {
return writeValue(&newValue, 1, response);
} // writeValue
/**
* @brief In the event of an error this is called to make sure we don't block.
*/
void NimBLERemoteDescriptor::releaseSemaphores() {
m_semaphoreDescWrite.give(1);
m_semaphoreReadDescrEvt.give(1);
}
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */ #endif /* CONFIG_BT_ENABLED */

View File

@ -31,15 +31,26 @@ public:
uint16_t getHandle(); uint16_t getHandle();
NimBLERemoteCharacteristic* getRemoteCharacteristic(); NimBLERemoteCharacteristic* getRemoteCharacteristic();
NimBLEUUID getUUID(); NimBLEUUID getUUID();
std::string readValue(void); std::string readValue();
uint8_t readUInt8(void);
uint16_t readUInt16(void); template<typename T>
uint32_t readUInt32(void); 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));
uint16_t readUInt16() __attribute__ ((deprecated));
uint32_t readUInt32() __attribute__ ((deprecated));
std::string toString(void); std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false); bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false); bool writeValue(const std::string &newValue, bool response = false);
bool writeValue(uint8_t newValue, bool response = false); 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;
@ -50,16 +61,10 @@ private:
struct ble_gatt_attr *attr, void *arg); struct ble_gatt_attr *attr, void *arg);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error, static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg); struct ble_gatt_attr *attr, void *arg);
void releaseSemaphores();
uint16_t m_handle; uint16_t m_handle;
NimBLEUUID m_uuid; NimBLEUUID m_uuid;
std::string m_value;
NimBLERemoteCharacteristic* m_pRemoteCharacteristic; NimBLERemoteCharacteristic* m_pRemoteCharacteristic;
FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt");
FreeRTOS::Semaphore m_semaphoreDescWrite = FreeRTOS::Semaphore("WriteDescEvt");
}; };
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)

View File

@ -58,7 +58,7 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
* Also release any semaphores they may be holding. * Also release any semaphores they may be holding.
*/ */
NimBLERemoteService::~NimBLERemoteService() { NimBLERemoteService::~NimBLERemoteService() {
removeCharacteristics(); deleteCharacteristics();
} }
@ -118,17 +118,15 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
* @param [in] bool value to indicate if the current vector should be cleared and * @param [in] bool value to indicate if the current vector should be cleared and
* subsequently all characteristics for this service retrieved from the peripheral. * subsequently all characteristics for this service retrieved from the peripheral.
* If false the vector will be returned with the currently stored characteristics, * If false the vector will be returned with the currently stored characteristics,
* if the vector is empty it will retrieve all characteristics of this service * If true it will retrieve all characteristics of this service from the peripheral
* from the peripheral. * and return the vector with all characteristics for this service.
* @return a pointer to the vector of descriptors for this characteristic. * @return a pointer to the vector of descriptors for this characteristic.
*/ */
std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics(bool refresh) { std::vector<NimBLERemoteCharacteristic*>* NimBLERemoteService::getCharacteristics(bool refresh) {
if(refresh) { if(refresh) {
removeCharacteristics(); deleteCharacteristics();
}
if(m_characteristicVector.empty()) {
if (!retrieveCharacteristics()) { if (!retrieveCharacteristics()) {
NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics"); NIMBLE_LOGE(LOG_TAG, "Error: Failed to get characteristics");
} }
@ -150,43 +148,34 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d", NIMBLE_LOGD(LOG_TAG,"Characteristic Discovered >> status: %d handle: %d",
error->status, (error->status == 0) ? chr->val_handle : -1); error->status, (error->status == 0) ? chr->val_handle : -1);
NimBLERemoteService *service = (NimBLERemoteService*)arg; ble_task_data_t *pTaskData = (ble_task_data_t*)arg;
int rc=0; NimBLERemoteService *service = (NimBLERemoteService*)pTaskData->pATT;
// Make sure the discovery is for this device // Make sure the discovery is for this device
if(service->getClient()->getConnId() != conn_handle){ if(service->getClient()->getConnId() != conn_handle){
return 0; return 0;
} }
switch (error->status) { if(error->status == 0) {
case 0: {
// Found a service - add it to the vector // Found a service - add it to the vector
NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr); NimBLERemoteCharacteristic* pRemoteCharacteristic = new NimBLERemoteCharacteristic(service, chr);
service->m_characteristicVector.push_back(pRemoteCharacteristic); service->m_characteristicVector.push_back(pRemoteCharacteristic);
break; return 0;
} }
case BLE_HS_EDONE:{
/** All characteristics in this service discovered; start discovering if(error->status == BLE_HS_EDONE) {
* characteristics in the next service. pTaskData->rc = 0;
*/ } else {
service->m_semaphoreGetCharEvt.give(0); NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s",
rc = 0; error->status,
break; NimBLEUtils::returnCodeToString(error->status));
pTaskData->rc = error->status;
} }
default:
rc = error->status; xTaskNotifyGive(pTaskData->task);
break;
} NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered");
if (rc != 0) { return error->status;
/* Error; abort discovery. */
// pass non-zero to semaphore on error to indicate an error finding characteristics
// release memory from any characteristics we created
//service->removeCharacteristics(); --this will now be done when we clear services on returning with error
NIMBLE_LOGE(LOG_TAG, "characteristicDiscCB() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
service->m_semaphoreGetCharEvt.give(1);
}
NIMBLE_LOGD(LOG_TAG,"<< Characteristic Discovered. status: %d", rc);
return rc;
} }
@ -199,32 +188,31 @@ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter)
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
int rc = 0; int rc = 0;
//removeCharacteristics(); // Forget any previous characteristics. ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_semaphoreGetCharEvt.take("retrieveCharacteristics");
if(uuid_filter == nullptr) { if(uuid_filter == nullptr) {
rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(), rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
m_startHandle, m_startHandle,
m_endHandle, m_endHandle,
NimBLERemoteService::characteristicDiscCB, NimBLERemoteService::characteristicDiscCB,
this); &taskData);
} else { } else {
rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(), rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
m_startHandle, m_startHandle,
m_endHandle, m_endHandle,
&uuid_filter->getNative()->u, &uuid_filter->getNative()->u,
NimBLERemoteService::characteristicDiscCB, NimBLERemoteService::characteristicDiscCB,
this); &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_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
m_semaphoreGetCharEvt.give();
return false; return false;
} }
if(m_semaphoreGetCharEvt.wait("retrieveCharacteristics") == 0){ ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc == 0){
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
return true; return true;
} }
@ -316,12 +304,36 @@ bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const s
* them. This method does just that. * them. This method does just that.
* @return N/A. * @return N/A.
*/ */
void NimBLERemoteService::removeCharacteristics() { void NimBLERemoteService::deleteCharacteristics() {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics");
for(auto &it: m_characteristicVector) { for(auto &it: m_characteristicVector) {
delete it; delete it;
} }
m_characteristicVector.clear(); // Clear the vector m_characteristicVector.clear();
} // removeCharacteristics NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristics");
} // deleteCharacteristics
/**
* @brief Delete characteristic by UUID
* @param [in] uuid The UUID of the characteristic to be cleared.
* @return Number of characteristics left.
*/
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristic");
for(auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it) {
if((*it)->getUUID() == uuid) {
delete *it;
m_characteristicVector.erase(it);
break;
}
}
NIMBLE_LOGD(LOG_TAG, "<< deleteCharacteristic");
return m_characteristicVector.size();
} // deleteCharacteristic
/** /**
@ -352,16 +364,5 @@ std::string NimBLERemoteService::toString() {
} // toString } // toString
/**
* @brief called when an error occurrs and we need to release the semaphores to resume operations.
* Will release all characteristic and subsequently all descriptor semaphores for this service.
*/
void NimBLERemoteService::releaseSemaphores() {
for(auto &it: m_characteristicVector) {
it->releaseSemaphores();
}
m_semaphoreGetCharEvt.give(1);
}
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */ #endif /* CONFIG_BT_ENABLED */

View File

@ -22,7 +22,6 @@
#include "NimBLEClient.h" #include "NimBLEClient.h"
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include "FreeRTOS.h"
#include "NimBLERemoteCharacteristic.h" #include "NimBLERemoteCharacteristic.h"
#include <vector> #include <vector>
@ -43,6 +42,8 @@ public:
std::vector<NimBLERemoteCharacteristic*>::iterator end(); std::vector<NimBLERemoteCharacteristic*>::iterator end();
NimBLERemoteCharacteristic* getCharacteristic(const char* uuid); NimBLERemoteCharacteristic* getCharacteristic(const char* uuid);
NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid); NimBLERemoteCharacteristic* getCharacteristic(const NimBLEUUID &uuid);
void deleteCharacteristics();
size_t deleteCharacteristic(const NimBLEUUID &uuid);
NimBLEClient* getClient(void); NimBLEClient* getClient(void);
uint16_t getHandle(); uint16_t getHandle();
NimBLEUUID getUUID(void); NimBLEUUID getUUID(void);
@ -70,7 +71,6 @@ private:
uint16_t getStartHandle(); uint16_t getStartHandle();
uint16_t getEndHandle(); uint16_t getEndHandle();
void releaseSemaphores(); void releaseSemaphores();
void removeCharacteristics();
// Properties // Properties
@ -78,7 +78,6 @@ private:
std::vector<NimBLERemoteCharacteristic*> m_characteristicVector; std::vector<NimBLERemoteCharacteristic*> m_characteristicVector;
NimBLEClient* m_pClient; NimBLEClient* m_pClient;
FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt");
NimBLEUUID m_uuid; NimBLEUUID m_uuid;
uint16_t m_startHandle; uint16_t m_startHandle;
uint16_t m_endHandle; uint16_t m_endHandle;

View File

@ -18,7 +18,6 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEScan.h" #include "NimBLEScan.h"
#include "NimBLEUtils.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLELog.h" #include "NimBLELog.h"
@ -26,41 +25,12 @@
static const char* LOG_TAG = "NimBLEScan"; static const char* LOG_TAG = "NimBLEScan";
/*
* Scanning filter policy
* NO_WL:
* Scanner processes all advertising packets (white list not used) except
* directed, connectable advertising packets not sent to the scanner.
* USE_WL:
* Scanner processes advertisements from white list only. A connectable,
* directed advertisment is ignored unless it contains scanners address.
* NO_WL_INITA:
* Scanner process all advertising packets (white list not used). A
* connectable, directed advertisement shall not be ignored if the InitA
* is a resolvable private address.
* USE_WL_INITA:
* Scanner process advertisements from white list only. A connectable,
* directed advertisement shall not be ignored if the InitA is a
* resolvable private address.
*/
//#define BLE_HCI_SCAN_FILT_NO_WL (0)
//#define BLE_HCI_SCAN_FILT_USE_WL (1)
//#define BLE_HCI_SCAN_FILT_NO_WL_INITA (2)
//#define BLE_HCI_SCAN_FILT_USE_WL_INITA (3)
//#define BLE_HCI_SCAN_FILT_MAX (3)
/** /**
* @brief Scan constuctor. * @brief Scan constuctor.
*/ */
NimBLEScan::NimBLEScan() { NimBLEScan::NimBLEScan() {
uint8_t own_addr_type; m_own_addr_type = 0;
if(ble_hs_id_infer_auto(0, &own_addr_type) !=0){
NIMBLE_LOGE(LOG_TAG, "error determining address type\n");
return;
}
m_own_addr_type = own_addr_type;
m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL; m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL;
m_scan_params.passive = 1; // If set, dont send scan requests to advertisers (i.e., dont request additional advertising data). m_scan_params.passive = 1; // If set, dont send scan requests to advertisers (i.e., dont request additional advertising data).
m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec) m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec)
@ -70,6 +40,7 @@ NimBLEScan::NimBLEScan() {
m_pAdvertisedDeviceCallbacks = nullptr; m_pAdvertisedDeviceCallbacks = nullptr;
m_stopped = true; m_stopped = true;
m_wantDuplicates = false; m_wantDuplicates = false;
m_pTaskData = nullptr;
} }
@ -101,14 +72,6 @@ NimBLEScan::NimBLEScan() {
NimBLEAddress advertisedAddress(event->disc.addr); NimBLEAddress advertisedAddress(event->disc.addr);
// Print advertisement data
// print_adv_fields(&fields);
// If we are not scanning, nothing to do with the extra results.
if (pScan->m_stopped) {
return 0;
}
// Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected // Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected
if(NimBLEDevice::isIgnored(advertisedAddress)) { if(NimBLEDevice::isIgnored(advertisedAddress)) {
NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str()); NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str());
@ -131,7 +94,6 @@ NimBLEScan::NimBLEScan() {
advertisedDevice = new NimBLEAdvertisedDevice(); advertisedDevice = new NimBLEAdvertisedDevice();
advertisedDevice->setAddressType(event->disc.addr.type); advertisedDevice->setAddressType(event->disc.addr.type);
advertisedDevice->setAddress(advertisedAddress); advertisedDevice->setAddress(advertisedAddress);
//NIMBLE_LOGE(LOG_TAG, "advertisement type: %d, %s",event->disc.event_type, NimBLEUtils::advTypeToString(event->disc.event_type));
advertisedDevice->setAdvType(event->disc.event_type); advertisedDevice->setAdvType(event->disc.event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice); pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
NIMBLE_LOGI(LOG_TAG, "NEW DEVICE FOUND: %s", advertisedAddress.toString().c_str()); NIMBLE_LOGI(LOG_TAG, "NEW DEVICE FOUND: %s", advertisedAddress.toString().c_str());
@ -143,16 +105,21 @@ NimBLEScan::NimBLEScan() {
advertisedDevice->parseAdvertisement(&fields); advertisedDevice->parseAdvertisement(&fields);
advertisedDevice->setScan(pScan); advertisedDevice->setScan(pScan);
advertisedDevice->setAdvertisementResult(event->disc.data, event->disc.length_data); advertisedDevice->setAdvertisementResult(event->disc.data, event->disc.length_data);
advertisedDevice->m_timestamp = time(nullptr);
if (pScan->m_pAdvertisedDeviceCallbacks) { if (pScan->m_pAdvertisedDeviceCallbacks) {
if(pScan->m_wantDuplicates || !advertisedDevice->m_callbackSent) {
// If not active scanning report the result to the listener. // If not active scanning report the result to the listener.
if(pScan->m_scan_params.passive || event->disc.event_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) { if(pScan->m_scan_params.passive || event->disc.event_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
// Otherwise wait for the scan response so we can report all of the data at once. // Otherwise wait for the scan response so we can report all of the data at once.
} else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
advertisedDevice->m_callbackSent = true;
pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
} }
//m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); }
} }
return 0; return 0;
@ -166,7 +133,11 @@ NimBLEScan::NimBLEScan() {
} }
pScan->m_stopped = true; pScan->m_stopped = true;
pScan->m_semaphoreScanEnd.give(); if(pScan->m_pTaskData != nullptr) {
pScan->m_pTaskData->rc = event->disc_complete.reason;
xTaskNotifyGive(pScan->m_pTaskData->task);
}
return 0; return 0;
} }
@ -191,13 +162,59 @@ void NimBLEScan::setActiveScan(bool active) {
} // setActiveScan } // setActiveScan
/**
* @brief Set whether or not the BLE controller should only report results
* from devices it has not already seen.
* @param [in] active If true, scanned devices will only be reported once.
* @details The controller has a limited buffer and will start reporting
* dupicate devices once the limit is reached.
*/
void NimBLEScan::setDuplicateFilter(bool active) {
m_scan_params.filter_duplicates = active;
} // setDuplicateFilter
/**
* @brief Set whether or not the BLE controller only report scan results
* from devices advertising in limited discovery mode, i.e. directed advertising.
* @param [in] active If true, only limited discovery devices will be in scan results.
*/
void NimBLEScan::setLimitedOnly(bool active) {
m_scan_params.limited = active;
} // setLimited
/**
* @brief Sets the scan filter policy.
* @param [in] filter Can be one of:
* BLE_HCI_SCAN_FILT_NO_WL (0)
* Scanner processes all advertising packets (white list not used) except
* directed, connectable advertising packets not sent to the scanner.
* BLE_HCI_SCAN_FILT_USE_WL (1)
* Scanner processes advertisements from white list only. A connectable,
* directed advertisment is ignored unless it contains scanners address.
* BLE_HCI_SCAN_FILT_NO_WL_INITA (2)
* Scanner process all advertising packets (white list not used). A
* connectable, directed advertisement shall not be ignored if the InitA
* is a resolvable private address.
* BLE_HCI_SCAN_FILT_USE_WL_INITA (3)
* Scanner process advertisements from white list only. A connectable,
* directed advertisement shall not be ignored if the InitA is a
* resolvable private address.
*/
void NimBLEScan::setFilterPolicy(uint8_t filter) {
m_scan_params.filter_policy = filter;
} // setFilterPolicy
/** /**
* @brief Set the call backs to be invoked. * @brief Set the call backs to be invoked.
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked. * @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
* @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false. * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false.
*/ */
void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks/*, bool wantDuplicates*/) { void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
//m_wantDuplicates = wantDuplicates; bool wantDuplicates) {
m_wantDuplicates = wantDuplicates;
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks; m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
} // setAdvertisedDeviceCallbacks } // setAdvertisedDeviceCallbacks
@ -248,7 +265,6 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
} }
m_stopped = false; m_stopped = false;
m_semaphoreScanEnd.take("start");
// Save the callback to be invoked when the scan completes. // Save the callback to be invoked when the scan completes.
m_scanCompleteCB = scanCompleteCB; m_scanCompleteCB = scanCompleteCB;
@ -274,7 +290,7 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params, rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params,
NimBLEScan::handleGapEvent, this); NimBLEScan::handleGapEvent, this);
if(rc == BLE_HS_EBUSY) { if(rc == BLE_HS_EBUSY) {
vTaskDelay(2); vTaskDelay(1 / portTICK_PERIOD_MS);
} }
} while(rc == BLE_HS_EBUSY); } while(rc == BLE_HS_EBUSY);
@ -282,7 +298,6 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
rc, NimBLEUtils::returnCodeToString(rc)); rc, NimBLEUtils::returnCodeToString(rc));
m_stopped = true; m_stopped = true;
m_semaphoreScanEnd.give();
return false; return false;
} }
@ -297,9 +312,18 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
* @return The BLEScanResults. * @return The BLEScanResults.
*/ */
NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) { NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) {
if(start(duration, nullptr, is_continue)) { if(duration == 0) {
m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release. NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever");
} }
ble_task_data_t taskData = {nullptr, xTaskGetCurrentTaskHandle(),0, nullptr};
m_pTaskData = &taskData;
if(start(duration, nullptr, is_continue)) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
m_pTaskData = nullptr;
return m_scanResults; return m_scanResults;
} // start } // start
@ -308,13 +332,13 @@ NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) {
* @brief Stop an in progress scan. * @brief Stop an in progress scan.
* @return N/A. * @return N/A.
*/ */
void NimBLEScan::stop() { bool NimBLEScan::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop()"); NIMBLE_LOGD(LOG_TAG, ">> stop()");
int rc = ble_gap_disc_cancel(); int rc = ble_gap_disc_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) { if (rc != 0 && rc != BLE_HS_EALREADY) {
NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d\n", rc); NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d\n", rc);
return; return false;
} }
m_stopped = true; m_stopped = true;
@ -323,9 +347,12 @@ void NimBLEScan::stop() {
m_scanCompleteCB(m_scanResults); m_scanCompleteCB(m_scanResults);
} }
m_semaphoreScanEnd.give(); if(m_pTaskData != nullptr) {
xTaskNotifyGive(m_pTaskData->task);
}
NIMBLE_LOGD(LOG_TAG, "<< stop()"); NIMBLE_LOGD(LOG_TAG, "<< stop()");
return true;
} // stop } // stop
@ -349,7 +376,6 @@ void NimBLEScan::erase(const NimBLEAddress &address) {
*/ */
void NimBLEScan::onHostReset() { void NimBLEScan::onHostReset() {
m_stopped = true; m_stopped = true;
m_semaphoreScanEnd.give();
} }

View File

@ -20,7 +20,7 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#include "NimBLEAdvertisedDevice.h" #include "NimBLEAdvertisedDevice.h"
#include "FreeRTOS.h" #include "NimBLEUtils.h"
#include "host/ble_gap.h" #include "host/ble_gap.h"
@ -62,11 +62,14 @@ class NimBLEScan {
public: public:
bool start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue = false); bool start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue = false);
NimBLEScanResults start(uint32_t duration, bool is_continue = false); NimBLEScanResults start(uint32_t duration, bool is_continue = false);
void setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks/*, bool wantDuplicates = false*/); void setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates = false);
void setActiveScan(bool active); void setActiveScan(bool active);
void setInterval(uint16_t intervalMSecs); void setInterval(uint16_t intervalMSecs);
void setWindow(uint16_t windowMSecs); void setWindow(uint16_t windowMSecs);
void stop(); void setDuplicateFilter(bool active);
void setLimitedOnly(bool active);
void setFilterPolicy(uint8_t filter);
bool stop();
void clearResults(); void clearResults();
NimBLEScanResults getResults(); NimBLEScanResults getResults();
void erase(const NimBLEAddress &address); void erase(const NimBLEAddress &address);
@ -85,8 +88,8 @@ private:
bool m_stopped; bool m_stopped;
bool m_wantDuplicates; bool m_wantDuplicates;
NimBLEScanResults m_scanResults; NimBLEScanResults m_scanResults;
FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd");
uint32_t m_duration; uint32_t m_duration;
ble_task_data_t *m_pTaskData;
}; };
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)

View File

@ -19,8 +19,6 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEServer.h" #include "NimBLEServer.h"
#include "NimBLE2902.h"
#include "NimBLEUtils.h"
#include "NimBLEDevice.h" #include "NimBLEDevice.h"
#include "NimBLELog.h" #include "NimBLELog.h"
@ -32,51 +30,46 @@ static NimBLEServerCallbacks defaultCallbacks;
* @brief Construct a %BLE Server * @brief Construct a %BLE Server
* *
* This class is not designed to be individually instantiated. Instead one should create a server by asking * This class is not designed to be individually instantiated. Instead one should create a server by asking
* the BLEDevice class. * the NimBLEDevice class.
*/ */
NimBLEServer::NimBLEServer() { NimBLEServer::NimBLEServer() {
m_connId = BLE_HS_CONN_HANDLE_NONE; // m_svcChgChrHdl = 0xffff; // Future Use
m_svcChgChrHdl = 0xffff;
m_pServerCallbacks = &defaultCallbacks; m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false; m_gattsStarted = false;
} // BLEServer m_advertiseOnDisconnect = true;
} // NimBLEServer
/** /**
* @brief Create a %BLE Service. * @brief Create a %BLE Service.
*
* With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
* of a new service. Every service must have a unique UUID.
* @param [in] uuid The UUID of the new service. * @param [in] uuid The UUID of the new service.
* @return A reference to the new service object. * @return A reference to the new service object.
*/ */
NimBLEService* NimBLEServer::createService(const char* uuid) { NimBLEService* NimBLEServer::createService(const char* uuid) {
return createService(NimBLEUUID(uuid)); return createService(NimBLEUUID(uuid));
} } // createService
/** /**
* @brief Create a %BLE Service. * @brief Create a %BLE Service.
*
* With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
* of a new service. Every service must have a unique UUID.
* @param [in] uuid The UUID of the new service. * @param [in] uuid The UUID of the new service.
* @param [in] numHandles The maximum number of handles associated with this service. * @param [in] numHandles The maximum number of handles associated with this service.
* @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. * @param [in] inst_id if we have multiple services with the same UUID we need
* to provide inst_id value different for each service.
* @return A reference to the new service object. * @return A reference to the new service object.
*/ */
NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numHandles, uint8_t inst_id) { NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numHandles, uint8_t inst_id) {
NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str());
// TODO: add functionality to use inst_id for multiple services with same uuid
(void)inst_id;
// Check that a service with the supplied UUID does not already exist. // Check that a service with the supplied UUID does not already exist.
if (m_serviceMap.getByUUID(uuid) != nullptr) { if(getServiceByUUID(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
uuid.toString().c_str()); std::string(uuid).c_str());
} }
NimBLEService* pService = new NimBLEService(uuid, numHandles, this); NimBLEService* pService = new NimBLEService(uuid, numHandles, this);
pService->m_instId = inst_id; m_svcVec.push_back(pService); // Save a reference to this service being on this server.
m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server.
NIMBLE_LOGD(LOG_TAG, "<< createService"); NIMBLE_LOGD(LOG_TAG, "<< createService");
return pService; return pService;
@ -89,8 +82,8 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
* @return A reference to the service object. * @return A reference to the service object.
*/ */
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) { NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) {
return m_serviceMap.getByUUID(uuid); return getServiceByUUID(NimBLEUUID(uuid));
} } // getServiceByUUID
/** /**
@ -99,8 +92,13 @@ NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) {
* @return A reference to the service object. * @return A reference to the service object.
*/ */
NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) { NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) {
return m_serviceMap.getByUUID(uuid); for (auto &it : m_svcVec) {
if (it->getUUID() == uuid) {
return it;
} }
}
return nullptr;
} // getServiceByUUID
/** /**
@ -109,18 +107,8 @@ NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid) {
* @return An advertising object. * @return An advertising object.
*/ */
NimBLEAdvertising* NimBLEServer::getAdvertising() { NimBLEAdvertising* NimBLEServer::getAdvertising() {
return BLEDevice::getAdvertising(); return NimBLEDevice::getAdvertising();
} } // getAdvertising
/**
* @brief Retrieve the connection id of the last connected client.
* @todo Not very useful, should refactor or remove.
* @return Client connection id.
*/
uint16_t NimBLEServer::getConnId() {
return m_connId;
}
/** /**
@ -143,6 +131,8 @@ void NimBLEServer::start() {
#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4) #if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
ble_gatts_show_local(); ble_gatts_show_local();
#endif #endif
/*** Future use ***
* TODO: implement service changed handling
ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801}; ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801};
ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05}; ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05};
@ -155,36 +145,25 @@ void NimBLEServer::start() {
} }
NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl); NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl);
*/
// Build a map of characteristics with Notify / Indicate capabilities for event handling // Build a vector of characteristics with Notify / Indicate capabilities for event handling
uint8_t numSvcs = m_serviceMap.getRegisteredServiceCount(); for(auto &svc : m_svcVec) {
NimBLEService* pService = m_serviceMap.getFirst(); for(auto &chr : svc->m_chrVec) {
for(int i = 0; i < numSvcs; i++) {
uint8_t numChrs = pService->m_characteristicMap.getSize();
NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst();
if(pChr != nullptr) {
for( int d = 0; d < numChrs; d++) {
// if Notify / Indicate is enabled but we didn't create the descriptor // if Notify / Indicate is enabled but we didn't create the descriptor
// we do it now. // we do it now.
if((pChr->m_properties & BLE_GATT_CHR_F_INDICATE) || if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(pChr->m_properties & BLE_GATT_CHR_F_NOTIFY)) { (chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
if(nullptr == pChr->getDescriptorByUUID("2902")) { if(nullptr == chr->getDescriptorByUUID(uint16_t(0x2902))) {
pChr->createDescriptor("2902"); chr->createDescriptor(uint16_t(0x2902));
} }
m_notifyChrMap.insert(std::pair<uint16_t, NimBLECharacteristic*> m_notifyChrVec.push_back(chr);
(pChr->getHandle(), pChr));
}
pChr = pService->m_characteristicMap.getNext();
} }
} }
pService = m_serviceMap.getNext();
} }
m_gattsStarted = true; m_gattsStarted = true;
} } // start
/** /**
@ -202,17 +181,26 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
NimBLEUtils::returnCodeToString(rc)); NimBLEUtils::returnCodeToString(rc));
} }
return rc;
NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
} return rc;
} // disconnect
/**
* @brief Set the server to automatically start advertising when a client disconnects.
* @param [in] bool true == advertise, false == don't advertise.
*/
void NimBLEServer::advertiseOnDisconnect(bool aod) {
m_advertiseOnDisconnect = aod;
} // advertiseOnDisconnect
/** /**
* @brief Return the number of connected clients. * @brief Return the number of connected clients.
* @return The number of connected clients. * @return The number of connected clients.
*/ */
uint32_t NimBLEServer::getConnectedCount() { size_t NimBLEServer::getConnectedCount() {
return m_connectedServersMap.size(); return m_connectedPeersVec.size();
} // getConnectedCount } // getConnectedCount
@ -236,15 +224,12 @@ uint32_t NimBLEServer::getConnectedCount() {
case BLE_GAP_EVENT_CONNECT: { case BLE_GAP_EVENT_CONNECT: {
if (event->connect.status != 0) { if (event->connect.status != 0) {
/* Connection failed; resume advertising */ /* Connection failed; resume advertising */
NIMBLE_LOGC(LOG_TAG, "Connection failed"); NIMBLE_LOGE(LOG_TAG, "Connection failed");
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
server->m_connId = BLE_HS_CONN_HANDLE_NONE;
} }
else { else {
server->m_connId = event->connect.conn_handle; server->m_connectedPeersVec.push_back(event->connect.conn_handle);
server->addPeerDevice((void*)server, false, server->m_connId);
ble_gap_conn_desc desc;
rc = ble_gap_conn_find(event->connect.conn_handle, &desc); rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0); assert(rc == 0);
@ -271,10 +256,15 @@ uint32_t NimBLEServer::getConnectedCount() {
break; break;
} }
server->removePeerDevice(event->disconnect.conn.conn_handle, false); server->m_connectedPeersVec.erase(std::remove(server->m_connectedPeersVec.begin(),
server->m_connId = BLE_HS_CONN_HANDLE_NONE; server->m_connectedPeersVec.end(),
event->disconnect.conn.conn_handle),
server->m_connectedPeersVec.end());
server->m_pServerCallbacks->onDisconnect(server); server->m_pServerCallbacks->onDisconnect(server);
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
}
return 0; return 0;
} // BLE_GAP_EVENT_DISCONNECT } // BLE_GAP_EVENT_DISCONNECT
@ -283,9 +273,23 @@ uint32_t NimBLEServer::getConnectedCount() {
"val_handle=%d\n", "val_handle=%d\n",
event->subscribe.cur_notify, event->subscribe.attr_handle); event->subscribe.cur_notify, event->subscribe.attr_handle);
auto it = server->m_notifyChrMap.find(event->subscribe.attr_handle); for(auto &it : server->m_notifyChrVec) {
if(it != server->m_notifyChrMap.cend()) { if(it->getHandle() == event->subscribe.attr_handle) {
(*it).second->setSubscribe(event); if((it->getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) ||
(it->getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) ||
(it->getProperties() & BLE_GATT_CHR_F_READ_ENC))
{
rc = ble_gap_conn_find(event->subscribe.conn_handle, &desc);
assert(rc == 0);
if(!desc.sec_state.encrypted) {
NimBLEDevice::startSecurity(event->subscribe.conn_handle);
}
}
it->setSubscribe(event);
break;
}
} }
return 0; return 0;
@ -295,15 +299,19 @@ uint32_t NimBLEServer::getConnectedCount() {
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d",
event->mtu.conn_handle, event->mtu.conn_handle,
event->mtu.value); event->mtu.value);
server->updatePeerMTU(event->mtu.conn_handle, event->mtu.value);
return 0; return 0;
} // BLE_GAP_EVENT_MTU } // BLE_GAP_EVENT_MTU
case BLE_GAP_EVENT_NOTIFY_TX: { case BLE_GAP_EVENT_NOTIFY_TX: {
if(event->notify_tx.indication && event->notify_tx.status != 0) { if(event->notify_tx.indication && event->notify_tx.status != 0) {
auto it = server->m_notifyChrMap.find(event->notify_tx.attr_handle); for(auto &it : server->m_notifyChrVec) {
if(it != server->m_notifyChrMap.cend()) { if(it->getHandle() == event->notify_tx.attr_handle) {
(*it).second->m_semaphoreConfEvt.give(event->notify_tx.status); if(it->m_pTaskData != nullptr) {
it->m_pTaskData->rc = event->notify_tx.status;
xTaskNotifyGive(it->m_pTaskData->task);
}
break;
}
} }
} }
@ -333,7 +341,7 @@ uint32_t NimBLEServer::getConnectedCount() {
} // BLE_GAP_EVENT_REPEAT_PAIRING } // BLE_GAP_EVENT_REPEAT_PAIRING
case BLE_GAP_EVENT_ENC_CHANGE: { case BLE_GAP_EVENT_ENC_CHANGE: {
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
if(rc != 0) { if(rc != 0) {
return BLE_ATT_ERR_INVALID_HANDLE; return BLE_ATT_ERR_INVALID_HANDLE;
} }
@ -349,7 +357,7 @@ uint32_t NimBLEServer::getConnectedCount() {
} // BLE_GAP_EVENT_ENC_CHANGE } // BLE_GAP_EVENT_ENC_CHANGE
case BLE_GAP_EVENT_PASSKEY_ACTION: { case BLE_GAP_EVENT_PASSKEY_ACTION: {
struct ble_sm_io pkey = {0}; struct ble_sm_io pkey = {0,0};
if (event->passkey.params.action == BLE_SM_IOACT_DISP) { if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
pkey.action = event->passkey.params.action; pkey.action = event->passkey.params.action;
@ -416,7 +424,7 @@ uint32_t NimBLEServer::getConnectedCount() {
NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent");
return 0; return 0;
} // handleGATTServerEvent } // handleGapEvent
/** /**
@ -437,18 +445,6 @@ void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) {
} // setCallbacks } // setCallbacks
/*
* Remove service
*/
/*
void BLEServer::removeService(BLEService* service) {
service->stop();
service->executeDelete();
m_serviceMap.removeService(service);
}
*/
/** /**
* @brief Start advertising. * @brief Start advertising.
* *
@ -456,9 +452,7 @@ void BLEServer::removeService(BLEService* service) {
* retrieving the advertising object and invoking start upon it. * retrieving the advertising object and invoking start upon it.
*/ */
void NimBLEServer::startAdvertising() { void NimBLEServer::startAdvertising() {
NIMBLE_LOGD(LOG_TAG, ">> startAdvertising");
NimBLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
NIMBLE_LOGD(LOG_TAG, "<< startAdvertising");
} // startAdvertising } // startAdvertising
@ -466,38 +460,43 @@ void NimBLEServer::startAdvertising() {
* @brief Stop advertising. * @brief Stop advertising.
*/ */
void NimBLEServer::stopAdvertising() { void NimBLEServer::stopAdvertising() {
NIMBLE_LOGD(LOG_TAG, ">> stopAdvertising");
NimBLEDevice::stopAdvertising(); NimBLEDevice::stopAdvertising();
NIMBLE_LOGD(LOG_TAG, "<< stopAdvertising");
} // startAdvertising } // startAdvertising
/** /**
* Allow to connect GATT server to peer device * @brief Get the MTU of the client.
* Probably can be used in ANCS for iPhone * @returns The client MTU or 0 if not found/connected.
*/ */
/* uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
bool BLEServer::connect(BLEAddress address) { return ble_att_mtu(conn_id);
esp_bd_addr_t addr; } //getPeerMTU
memcpy(&addr, address.getNative(), 6);
// Perform the open connection request against the target BLE Server.
m_semaphoreOpenEvt.take("connect"); /**
esp_err_t errRc = ::esp_ble_gatts_open( * Update connection parameters can be called only after connection has been established
getGattsIf(), */
addr, // address void NimBLEServer::updateConnParams(uint16_t conn_handle,
1 // direct connection uint16_t minInterval, uint16_t maxInterval,
); uint16_t latency, uint16_t timeout)
if (errRc != ESP_OK) { {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); ble_gap_upd_params params;
return false;
params.latency = latency;
params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
int rc = ble_gap_update_params(conn_handle, &params);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
} }
} // updateConnParams
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
*/
/** Default callback handlers */
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) { void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
@ -534,80 +533,6 @@ bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){
return true; return true;
} }
/* multi connect support */
void NimBLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) {
const std::map<uint16_t, conn_status_t>::iterator it = m_connectedServersMap.find(conn_id);
if (it != m_connectedServersMap.end()) {
it->second.mtu = mtu;
}
}
std::map<uint16_t, conn_status_t> NimBLEServer::getPeerDevices() {
return m_connectedServersMap;
}
/**
* @brief Get the MTU of the client.
* @returns The client MTU or 0 if not found/connected.
*/
uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
auto it = m_connectedServersMap.find(conn_id);
if(it != m_connectedServersMap.cend()) {
return (*it).second.mtu;
} else {
return 0;
}
}
void NimBLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) {
conn_status_t status = {
.peer_device = peer,
.connected = true,
.mtu = 23
};
m_connectedServersMap.insert(std::pair<uint16_t, conn_status_t>(conn_id, status));
}
void NimBLEServer::removePeerDevice(uint16_t conn_id, bool _client) {
m_connectedServersMap.erase(conn_id);
}
/* multi connect support */
/**
* Update connection parameters can be called only after connection has been established
*/
void NimBLEServer::updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
uint16_t minConnTime, uint16_t maxConnTime)
{
ble_gap_upd_params params;
params.latency = latency;
params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
params.min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units
params.max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units
int rc = ble_gap_update_params(conn_handle, &params);
if(rc != 0) {
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
}
}
/* Don't think this is needed
void NimBLEServer::onHostReset() {
for(auto it = m_notifyChrMap.cbegin(); it != m_notifyChrMap.cend(); ++it) {
(*it).second->m_semaphoreConfEvt.give(0);
}
}
*/
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED #endif // CONFIG_BT_ENABLED

View File

@ -20,101 +20,58 @@
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEUtils.h"
#include "NimBLEAddress.h" #include "NimBLEAddress.h"
#include "NimBLEUUID.h"
#include "NimBLEAdvertising.h" #include "NimBLEAdvertising.h"
#include "NimBLEService.h" #include "NimBLEService.h"
#include "NimBLESecurity.h" #include "NimBLESecurity.h"
#include "FreeRTOS.h"
#include <map>
class NimBLEService; class NimBLEService;
class NimBLECharacteristic; class NimBLECharacteristic;
class NimBLEServerCallbacks; class NimBLEServerCallbacks;
/* TODO possibly refactor this struct */
typedef struct {
void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here
bool connected; // do we need it?
uint16_t mtu; // every peer device negotiate own mtu
} conn_status_t;
/**
* @brief A data structure that manages the %BLE servers owned by a BLE server.
*/
class NimBLEServiceMap {
public:
// NimBLEService* getByHandle(uint16_t handle);
NimBLEService* getByUUID(const char* uuid);
NimBLEService* getByUUID(const NimBLEUUID &uuid, uint8_t inst_id = 0);
// void setByHandle(uint16_t handle, NimBLEService* service);
void setByUUID(const char* uuid, NimBLEService* service);
void setByUUID(const NimBLEUUID &uuid, NimBLEService* service);
std::string toString();
NimBLEService* getFirst();
NimBLEService* getNext();
void removeService(NimBLEService *service);
int getRegisteredServiceCount();
private:
// std::map<uint16_t, NimBLEService*> m_handleMap;
std::map<NimBLEService*, std::string> m_uuidMap;
std::map<NimBLEService*, std::string>::iterator m_iterator;
};
/** /**
* @brief The model of a %BLE server. * @brief The model of a %BLE server.
*/ */
class NimBLEServer { class NimBLEServer {
public: public:
uint32_t getConnectedCount(); size_t getConnectedCount();
NimBLEService* createService(const char* uuid); NimBLEService* createService(const char* uuid);
NimBLEService* createService(const NimBLEUUID &uuid, uint32_t numHandles=15, uint8_t inst_id=0); NimBLEService* createService(const NimBLEUUID &uuid, uint32_t numHandles=15,
uint8_t inst_id=0);
NimBLEAdvertising* getAdvertising(); NimBLEAdvertising* getAdvertising();
void setCallbacks(NimBLEServerCallbacks* pCallbacks); void setCallbacks(NimBLEServerCallbacks* pCallbacks);
void startAdvertising(); void startAdvertising();
void stopAdvertising(); void stopAdvertising();
void start(); void start();
// void removeService(BLEService* service);
NimBLEService* getServiceByUUID(const char* uuid); NimBLEService* getServiceByUUID(const char* uuid);
NimBLEService* getServiceByUUID(const NimBLEUUID &uuid); NimBLEService* getServiceByUUID(const NimBLEUUID &uuid);
int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); int disconnect(uint16_t connID,
// bool connect(BLEAddress address); uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
void updateConnParams(uint16_t conn_handle, void updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval, uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout, uint16_t latency, uint16_t timeout);
uint16_t minConnTime=0, uint16_t maxConnTime=0);
/* multi connection support */
std::map<uint16_t, conn_status_t> getPeerDevices();
void addPeerDevice(void* peer, bool is_client, uint16_t conn_id);
void removePeerDevice(uint16_t conn_id, bool client);
NimBLEServer* getServerByConnId(uint16_t conn_id);
void updatePeerMTU(uint16_t connId, uint16_t mtu);
uint16_t getPeerMTU(uint16_t conn_id); uint16_t getPeerMTU(uint16_t conn_id);
uint16_t getConnId(); std::vector<uint16_t> getPeerDevices();
void advertiseOnDisconnect(bool);
private: private:
NimBLEServer(); NimBLEServer();
//friend class BLEService;
friend class NimBLECharacteristic; friend class NimBLECharacteristic;
friend class NimBLEDevice; friend class NimBLEDevice;
friend class NimBLEAdvertising; friend class NimBLEAdvertising;
// void onHostReset();
// BLEAdvertising m_bleAdvertising;
uint16_t m_connId;
uint16_t m_svcChgChrHdl;
bool m_gattsStarted; bool m_gattsStarted;
bool m_advertiseOnDisconnect;
std::map<uint16_t, conn_status_t> m_connectedServersMap;
std::map<uint16_t, NimBLECharacteristic*> m_notifyChrMap;
NimBLEServiceMap m_serviceMap;
NimBLEServerCallbacks* m_pServerCallbacks; NimBLEServerCallbacks* m_pServerCallbacks;
std::vector<uint16_t> m_connectedPeersVec;
// uint16_t m_svcChgChrHdl; // Future use
std::vector<NimBLEService*> m_svcVec;
std::vector<NimBLECharacteristic*> m_notifyChrVec;
static int handleGapEvent(struct ble_gap_event *event, void *arg); static int handleGapEvent(struct ble_gap_event *event, void *arg);
}; // NimBLEServer }; // NimBLEServer
@ -149,7 +106,7 @@ public:
virtual bool onSecurityRequest(); //{return true;} virtual bool onSecurityRequest(); //{return true;}
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{}; virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{};
virtual bool onConfirmPIN(uint32_t pin);//{return true;} virtual bool onConfirmPIN(uint32_t pin);//{return true;}
}; // BLEServerCallbacks }; // NimBLEServerCallbacks
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View File

@ -32,9 +32,10 @@ static const char* LOG_TAG = "NimBLEService"; // Tag for logging.
/** /**
* @brief Construct an instance of the BLEService * @brief Construct an instance of the NimBLEService
* @param [in] uuid The UUID of the service. * @param [in] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with the service. * @param [in] numHandles The maximum number of handles associated with the service.
* @param [in] a pointer to the server instance that this service belongs to.
*/ */
NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer) NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer)
: NimBLEService(NimBLEUUID(uuid), numHandles, pServer) { : NimBLEService(NimBLEUUID(uuid), numHandles, pServer) {
@ -45,6 +46,7 @@ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer
* @brief Construct an instance of the BLEService * @brief Construct an instance of the BLEService
* @param [in] uuid The UUID of the service. * @param [in] uuid The UUID of the service.
* @param [in] numHandles The maximum number of handles associated with the service. * @param [in] numHandles The maximum number of handles associated with the service.
* @param [in] a pointer to the server instance that this service belongs to.
*/ */
NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) { NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) {
m_uuid = uuid; m_uuid = uuid;
@ -59,10 +61,22 @@ NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLE
* @return N/A. * @return N/A.
*/ */
void NimBLEService::dump() { void NimBLEService::dump() {
NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%2x",
m_uuid.toString().c_str(), m_uuid.toString().c_str(),
m_handle); m_handle);
NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str());
std::string res;
int count = 0;
char hex[5];
for (auto &it: m_chrVec) {
if (count > 0) {res += "\n";}
snprintf(hex, sizeof(hex), "%04x", it->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + std::string(it->getUUID());
}
NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", res.c_str());
} // dump } // dump
@ -76,10 +90,9 @@ NimBLEUUID NimBLEService::getUUID() {
/** /**
* @brief Start the service. * @brief Builds the database of characteristics/descriptors for the service
* Here we wish to start the service which means that we will respond to partner requests about it. * and registers it with the NimBLE stack.
* Starting a service also means that we can create the corresponding characteristics. * @return bool success/failure .
* @return Start the service.
*/ */
bool NimBLEService::start() { bool NimBLEService::start() {
@ -96,7 +109,7 @@ bool NimBLEService::start() {
svc[0].uuid = &m_uuid.getNative()->u; svc[0].uuid = &m_uuid.getNative()->u;
svc[0].includes = NULL; svc[0].includes = NULL;
uint8_t numChrs = m_characteristicMap.getSize(); size_t numChrs = m_chrVec.size();
NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str()); NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
@ -107,16 +120,17 @@ bool NimBLEService::start() {
// of the characteristics for the service. We create 1 extra and set it to null // of the characteristics for the service. We create 1 extra and set it to null
// for this purpose. // for this purpose.
pChr_a = new ble_gatt_chr_def[numChrs+1]; pChr_a = new ble_gatt_chr_def[numChrs+1];
NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); NimBLECharacteristic* pCharacteristic = *m_chrVec.begin();
for(uint8_t i=0; i < numChrs; i++) { for(uint8_t i=0; i < numChrs;) {
uint8_t numDscs = pCharacteristic->m_descriptorMap.getSize(); uint8_t numDscs = pCharacteristic->m_dscVec.size();
if(numDscs) { if(numDscs) {
// skip 2902 as it's automatically created by NimBLE // skip 2902 as it's automatically created by NimBLE
// if Indicate or Notify flags are set // if Indicate or Notify flags are set
if(((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) || if(((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) && (pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) &&
pCharacteristic->getDescriptorByUUID("2902") != nullptr) { pCharacteristic->getDescriptorByUUID("2902") != nullptr)
{
numDscs--; numDscs--;
} }
} }
@ -127,12 +141,12 @@ bool NimBLEService::start() {
// Must have last descriptor uuid = 0 so we have to create 1 extra // Must have last descriptor uuid = 0 so we have to create 1 extra
//NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs); //NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs);
pDsc_a = new ble_gatt_dsc_def[numDscs+1]; pDsc_a = new ble_gatt_dsc_def[numDscs+1];
NimBLEDescriptor* pDescriptor = pCharacteristic->m_descriptorMap.getFirst(); NimBLEDescriptor* pDescriptor = *pCharacteristic->m_dscVec.begin();
for(uint8_t d=0; d < numDscs;) { for(uint8_t d=0; d < numDscs;) {
// skip 2902 // skip 2902
if(pDescriptor->m_uuid.equals(NimBLEUUID((uint16_t)0x2902))) { if(pDescriptor->m_uuid == NimBLEUUID(uint16_t(0x2902))) {
//NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902"); //NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902");
pDescriptor = pCharacteristic->m_descriptorMap.getNext(); pDescriptor = *(pCharacteristic->m_dscVec.begin()+d+1);
continue; continue;
} }
pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u; pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u;
@ -140,8 +154,8 @@ bool NimBLEService::start() {
pDsc_a[d].min_key_size = 0; pDsc_a[d].min_key_size = 0;
pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent; pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
pDsc_a[d].arg = pDescriptor; pDsc_a[d].arg = pDescriptor;
pDescriptor = pCharacteristic->m_descriptorMap.getNext();
d++; d++;
pDescriptor = *(pCharacteristic->m_dscVec.begin() + d);
} }
pDsc_a[numDscs].uuid = NULL; pDsc_a[numDscs].uuid = NULL;
@ -154,7 +168,8 @@ bool NimBLEService::start() {
pChr_a[i].flags = pCharacteristic->m_properties; pChr_a[i].flags = pCharacteristic->m_properties;
pChr_a[i].min_key_size = 0; pChr_a[i].min_key_size = 0;
pChr_a[i].val_handle = &pCharacteristic->m_handle; pChr_a[i].val_handle = &pCharacteristic->m_handle;
pCharacteristic = m_characteristicMap.getNext(); i++;
pCharacteristic = *(m_chrVec.begin() + i);
} }
pChr_a[numChrs].uuid = NULL; pChr_a[numChrs].uuid = NULL;
@ -182,21 +197,6 @@ bool NimBLEService::start() {
} // start } // start
/**
* @brief Set the handle associated with this service.
* @param [in] handle The handle associated with the service.
*/
void NimBLEService::setHandle(uint16_t handle) {
NIMBLE_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str());
if (m_handle != NULL_HANDLE) {
NIMBLE_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle);
return;
}
m_handle = handle;
NIMBLE_LOGD(LOG_TAG, "<< setHandle");
} // setHandle
/** /**
* @brief Get the handle associated with this service. * @brief Get the handle associated with this service.
* @return The handle associated with this service. * @return The handle associated with this service.
@ -206,34 +206,6 @@ uint16_t NimBLEService::getHandle() {
} // getHandle } // getHandle
/**
* @brief Add a characteristic to the service.
* @param [in] pCharacteristic A pointer to the characteristic to be added.
*/
void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) {
// We maintain a mapping of characteristics owned by this service. These are managed by the
// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic
// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF).
NIMBLE_LOGD(LOG_TAG, ">> addCharacteristic()");
NIMBLE_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s",
pCharacteristic->getUUID().toString().c_str(),
toString().c_str());
// Check that we don't add the same characteristic twice.
if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one");
//return;
}
// Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID
// but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT.
m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID());
NIMBLE_LOGD(LOG_TAG, "<< addCharacteristic()");
} // addCharacteristic
/** /**
* @brief Create a new BLE Characteristic associated with this service. * @brief Create a new BLE Characteristic associated with this service.
* @param [in] uuid - The UUID of the characteristic. * @param [in] uuid - The UUID of the characteristic.
@ -253,8 +225,15 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint
*/ */
NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) { NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid, uint32_t properties) {
NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this); NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this);
addCharacteristic(pCharacteristic); // Check that we don't add the same characteristic twice.
//pCharacteristic->executeCreate(this); if (getCharacteristic(uuid) != nullptr) {
NIMBLE_LOGW(LOG_TAG, "<< Adding a duplicate characteristic with UUID: %s",
std::string(uuid).c_str());
}
// Remember this characteristic in our vector of characteristics.
m_chrVec.push_back(pCharacteristic);
return pCharacteristic; return pCharacteristic;
} // createCharacteristic } // createCharacteristic
@ -265,7 +244,13 @@ NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) {
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) { NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) {
return m_characteristicMap.getByUUID(uuid); for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
return it;
}
}
return nullptr;
} }

View File

@ -20,37 +20,14 @@
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
#include "NimBLEServer.h" #include "NimBLEServer.h"
#include "NimBLECharacteristic.h"
#include "NimBLEUUID.h" #include "NimBLEUUID.h"
#include "FreeRTOS.h"
class NimBLEServer; class NimBLEServer;
class NimBLECharacteristic; class NimBLECharacteristic;
/**
* @brief A data mapping used to manage the set of %BLE characteristics known to the server.
*/
class NimBLECharacteristicMap {
public:
void setByUUID(NimBLECharacteristic* pCharacteristic, const char* uuid);
void setByUUID(NimBLECharacteristic* pCharacteristic, const NimBLEUUID &uuid);
void setByHandle(uint16_t handle, NimBLECharacteristic* pCharacteristic);
NimBLECharacteristic* getByUUID(const char* uuid);
NimBLECharacteristic* getByUUID(const NimBLEUUID &uuid);
NimBLECharacteristic* getByHandle(uint16_t handle);
NimBLECharacteristic* getFirst();
NimBLECharacteristic* getNext();
uint8_t getSize();
std::string toString();
private:
std::map<NimBLECharacteristic*, std::string> m_uuidMap;
std::map<uint16_t, NimBLECharacteristic*> m_handleMap;
std::map<NimBLECharacteristic*, std::string>::iterator m_iterator;
};
/** /**
* @brief The model of a %BLE service. * @brief The model of a %BLE service.
@ -59,11 +36,13 @@ private:
class NimBLEService { class NimBLEService {
public: public:
NimBLECharacteristic* createCharacteristic(const char* uuid, NimBLECharacteristic* createCharacteristic(const char* uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE); NIMBLE_PROPERTY::WRITE);
NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid, NimBLECharacteristic* createCharacteristic(const NimBLEUUID &uuid,
uint32_t properties = NIMBLE_PROPERTY::READ | uint32_t properties =
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE); NIMBLE_PROPERTY::WRITE);
void dump(); void dump();
@ -72,27 +51,24 @@ public:
NimBLEUUID getUUID(); NimBLEUUID getUUID();
NimBLEServer* getServer(); NimBLEServer* getServer();
bool start(); bool start();
// void stop();
std::string toString(); std::string toString();
uint16_t getHandle(); uint16_t getHandle();
uint8_t m_instId = 0;
private: private:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer); NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);
NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer); NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer);
friend class NimBLEServer; friend class NimBLEServer;
friend class NimBLEDevice; friend class NimBLEDevice;
void addCharacteristic(NimBLECharacteristic* pCharacteristic);
NimBLECharacteristicMap m_characteristicMap;
uint16_t m_handle; uint16_t m_handle;
NimBLEServer* m_pServer = nullptr; NimBLEServer* m_pServer;
NimBLEUUID m_uuid; NimBLEUUID m_uuid;
uint16_t m_numHandles; uint16_t m_numHandles;
void setHandle(uint16_t handle);
}; // BLEService std::vector<NimBLECharacteristic*> m_chrVec;
}; // NimBLEService
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) #endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)

View File

@ -1,145 +0,0 @@
/*
* NimBLEService.cpp
*
* Created: on March 7, 2020
* Author H2zero
*
* Originally:
*
* BLEServiceMap.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEService.h"
/**
* @brief Return the service by UUID.
* @param [in] UUID The UUID to look up the service.
* @return The characteristic.
*/
NimBLEService* NimBLEServiceMap::getByUUID(const char* uuid) {
return getByUUID(NimBLEUUID(uuid));
}
/**
* @brief Return the service by UUID.
* @param [in] UUID The UUID to look up the service.
* @return The characteristic.
*/
NimBLEService* NimBLEServiceMap::getByUUID(const NimBLEUUID &uuid, uint8_t inst_id) {
for (auto &myPair : m_uuidMap) {
if (myPair.first->getUUID().equals(uuid)) {
return myPair.first;
}
}
//return m_uuidMap.at(uuid.toString());
return nullptr;
} // getByUUID
/**
* @brief Return the service by handle.
* @param [in] handle The handle to look up the service.
* @return The service.
*/
/*
NimBLEService* NimBLEServiceMap::getByHandle(uint16_t handle) {
return m_handleMap.at(handle);
} // getByHandle
*/
/**
* @brief Set the service by UUID.
* @param [in] uuid The uuid of the service.
* @param [in] characteristic The service to cache.
* @return N/A.
*/
void NimBLEServiceMap::setByUUID(const NimBLEUUID &uuid, NimBLEService* service) {
m_uuidMap.insert(std::pair<NimBLEService*, std::string>(service, uuid.toString()));
} // setByUUID
/**
* @brief Set the service by handle.
* @param [in] handle The handle of the service.
* @param [in] service The service to cache.
* @return N/A.
*/
/*
void NimBLEServiceMap::setByHandle(uint16_t handle, NimBLEService* service) {
m_handleMap.insert(std::pair<uint16_t, NimBLEService*>(handle, service));
} // setByHandle
*/
/**
* @brief Return a string representation of the service map.
* @return A string representation of the service map.
*/
std::string NimBLEServiceMap::toString() {
std::string res;
//char hex[5];
for (auto &myPair: m_uuidMap) {
// res += "handle: 0x";
// snprintf(hex, sizeof(hex), "%04x", myPair.first);
// res += hex;
res += ", uuid: " + myPair.second + "\n";
}
return res;
} // toString
/**
* @brief Get the first service in the map.
* @return The first service in the map.
*/
NimBLEService* NimBLEServiceMap::getFirst() {
m_iterator = m_uuidMap.begin();
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLEService* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getFirst
/**
* @brief Get the next service in the map.
* @return The next service in the map.
*/
NimBLEService* NimBLEServiceMap::getNext() {
if (m_iterator == m_uuidMap.end()) return nullptr;
NimBLEService* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getNext
/**
* @brief Removes service from maps.
* @return N/A.
*/
void NimBLEServiceMap::removeService(NimBLEService* service) {
//m_handleMap.erase(service->getHandle());
m_uuidMap.erase(service);
} // removeService
/**
* @brief Returns the amount of registered services
* @return amount of registered services
*/
int NimBLEServiceMap::getRegisteredServiceCount(){
//return m_handleMap.size();
return m_uuidMap.size();
}
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */

View File

@ -502,10 +502,15 @@ void print_bytes(const uint8_t *bytes, int len)
int i; int i;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); MODLOG_DFLT(ERROR, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
if(i % 30 == 0){
MODLOG_DFLT(ERROR, "\n");
} }
} }
MODLOG_DFLT(ERROR, "\n");
}
void print_mbuf(const struct os_mbuf *om) void print_mbuf(const struct os_mbuf *om)
{ {
int colon; int colon;

View File

@ -13,6 +13,20 @@
#include "host/ble_gap.h" #include "host/ble_gap.h"
/**** FIX COMPILATION ****/
#undef min
#undef max
/**************************/
#include <string>
typedef struct {
void *pATT;
TaskHandle_t task;
int rc;
std::string *buf;
} ble_task_data_t;
extern "C"{ extern "C"{
char *addr_str(const void *addr); char *addr_str(const void *addr);
void print_conn_desc(const struct ble_gap_conn_desc *desc); void print_conn_desc(const struct ble_gap_conn_desc *desc);

View File

@ -1,143 +0,0 @@
/*
* NimNimBLEValue.cpp
*
* Created: on March 6, 2020
* Author H2zero
*
* Originally:
*
* BLEValue.cpp
*
* Created on: Jul 17, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLEValue.h"
#include "NimBLELog.h"
static const char* LOG_TAG="NimBLEValue";
NimBLEValue::NimBLEValue() {
m_accumulation = "";
m_value = "";
m_readOffset = 0;
} // NimBLEValue
/**
* @brief Add a message part to the accumulation.
* The accumulation is a growing set of data that is added to until a commit or cancel.
* @param [in] part A message part being added.
*/
void NimBLEValue::addPart(const std::string &part) {
NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", part.length());
m_accumulation += part;
} // addPart
/**
* @brief Add a message part to the accumulation.
* The accumulation is a growing set of data that is added to until a commit or cancel.
* @param [in] pData A message part being added.
* @param [in] length The number of bytes being added.
*/
void NimBLEValue::addPart(const uint8_t* pData, size_t length) {
NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", length);
m_accumulation += std::string((char*) pData, length);
} // addPart
/**
* @brief Cancel the current accumulation.
*/
void NimBLEValue::cancel() {
NIMBLE_LOGD(LOG_TAG, ">> cancel");
m_accumulation = "";
m_readOffset = 0;
} // cancel
/**
* @brief Commit the current accumulation.
* When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces
* of the overall message. After the last part has been received, we may perform a commit which means that
* we now have the complete message and commit the change as a unit.
*/
void NimBLEValue::commit() {
NIMBLE_LOGD(LOG_TAG, ">> commit");
// If there is nothing to commit, do nothing.
if (m_accumulation.length() == 0) return;
setValue(m_accumulation);
m_accumulation = "";
m_readOffset = 0;
} // commit
/**
* @brief Get a pointer to the data.
* @return A pointer to the data.
*/
uint8_t* NimBLEValue::getData() {
return (uint8_t*) m_value.data();
}
/**
* @brief Get the length of the data in bytes.
* @return The length of the data in bytes.
*/
size_t NimBLEValue::getLength() {
return m_value.length();
} // getLength
/**
* @brief Get the read offset.
* @return The read offset into the read.
*/
uint16_t NimBLEValue::getReadOffset() {
return m_readOffset;
} // getReadOffset
/**
* @brief Get the current value.
*/
std::string NimBLEValue::getValue() {
return m_value;
} // getValue
/**
* @brief Set the read offset
* @param [in] readOffset The offset into the read.
*/
void NimBLEValue::setReadOffset(uint16_t readOffset) {
m_readOffset = readOffset;
} // setReadOffset
/**
* @brief Set the current value.
*/
void NimBLEValue::setValue(const std::string &value) {
m_value = value;
} // setValue
/**
* @brief Set the current value.
* @param [in] pData The data for the current value.
* @param [in] The length of the new current value.
*/
void NimBLEValue::setValue(const uint8_t* pData, size_t length) {
m_value = std::string((char*) pData, length);
} // setValue
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED

View File

@ -1,52 +0,0 @@
/*
* NimBLEValue.h
*
* Created: on March 6, 2020
* Author H2zero
*
* Originally:
*
* BLEValue.h
*
* Created on: Jul 17, 2017
* Author: kolban
*/
#ifndef MAIN_BLEVALUE_H_
#define MAIN_BLEVALUE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include <string>
/**
* @brief The model of a %BLE value.
*/
class NimBLEValue {
public:
NimBLEValue();
void addPart(const std::string &part);
void addPart(const uint8_t* pData, size_t length);
void cancel();
void commit();
uint8_t* getData();
size_t getLength();
uint16_t getReadOffset();
std::string getValue();
void setReadOffset(uint16_t readOffset);
void setValue(const std::string &value);
void setValue(const uint8_t* pData, size_t length);
private:
std::string m_accumulation;
uint16_t m_readOffset;
std::string m_value;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif // CONFIG_BT_ENABLED
#endif /* MAIN_BLEVALUE_H_ */

View File

@ -19,6 +19,9 @@
* under the License. * under the License.
*/ */
#ifndef H_BLE_HS_PVCY_
#define H_BLE_HS_PVCY_
#include "host/ble_hs.h" #include "host/ble_hs.h"
#ifdef __cplusplus #ifdef __cplusplus
@ -26,15 +29,45 @@ extern "C" {
#endif #endif
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) #if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
/* Called to configure local(own) privacy (RPA) when using host based privacy. In
* Host based privacy as controller is not aware of RPA, we do it via #define NIMBLE_HOST_DISABLE_PRIVACY 0x00
* 'BLE_ADDR_RANDOM' addr_type route. #define NIMBLE_HOST_ENABLE_RPA 0x01
#define NIMBLE_HOST_ENABLE_NRPA 0x02
/* Called to configure local(own) privacy (RPA/NRPA) when using Host based privacy.
* In Host based privacy, as controller is not aware of RPA/NRPA address is in use,
* we do it through 'BLE_ADDR_RANDOM (0x01)' addr_type route. This is necessary
* so as to set the private address as random address in controller.
* Remember to configure `BLE_SM_PAIR_KEY_DIST_ID` in our & their
* key distributions for using RPA. For NRPA part of privacy it is not
* necessary to configure key distributions in host, as anyway NRPA is non-resolvable.
* Please call this API once host-controller are synced as we set the private
* (RPA/NRPA) address using host-controller HCI commands.
* *
* @param enable RPA when enable is not 0 * To give brief information on how to use this feature,
* disable RPA otherwise * please refer to following steps while using RPA feature:
*
* 1. Include "host/ble_hs_pvcy.h".
* 2. Set own_addr_type to `BLE_OWN_ADDR_RANDOM`.
* 3. Add `BLE_SM_PAIR_KEY_DIST_ID` to key distribution in
* `ble_hs_cfg.sm_our_key_dist` & `ble_hs_cfg.sm_their_key_dist`.
* 4. Call `ble_hs_pvcy_rpa_config(1)` in Host-Controller sync callback.
*
* In case of NRPA, steps 1, 2 and calling ble_hs_pvcy_rpa_config(2) will
* suffice.
*
* @param enable RPA when param = 1 (NIMBLE_HOST_ENABLE_RPA)
* enable NRPA when param = 2 (NIMBLE_HOST_ENABLE_NRPA)
* disable privacy when param = 0 (NIMBLE_HOST_DISABLE_PRIVACY)
* *
* @return return 0 when successful. * @return return 0 when successful.
* return appropriate error code otherwise * return appropriate error code otherwise
*/ */
int ble_hs_pvcy_rpa_config(uint8_t enable); int ble_hs_pvcy_rpa_config(uint8_t enable);
#endif #endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -332,6 +332,8 @@ static inline void net_buf_simple_restore(struct os_mbuf *buf,
static inline void sys_memcpy_swap(void *dst, const void *src, size_t length) static inline void sys_memcpy_swap(void *dst, const void *src, size_t length)
{ {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-arith"
__ASSERT(((src < dst && (src + length) <= dst) || __ASSERT(((src < dst && (src + length) <= dst) ||
(src > dst && (dst + length) <= src)), (src > dst && (dst + length) <= src)),
"Source and destination buffers must not overlap"); "Source and destination buffers must not overlap");
@ -341,6 +343,7 @@ static inline void sys_memcpy_swap(void *dst, const void *src, size_t length)
for (; length > 0; length--) { for (; length > 0; length--) {
*((u8_t *)dst++) = *((u8_t *)src--); *((u8_t *)dst++) = *((u8_t *)src--);
} }
#pragma GCC diagnostic pop
} }
#define popcount(x) __builtin_popcount(x) #define popcount(x) __builtin_popcount(x)

View File

@ -1,49 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/mesh
pkg.description: Bluetooth Mesh
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- mesh
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/util/mem"
- "@apache-mynewt-core/crypto/tinycrypt"
- nimble
- nimble/host
pkg.deps.BLE_MESH_SHELL:
- "@apache-mynewt-core/sys/shell"
pkg.deps.BLE_MESH_SETTINGS:
- "@apache-mynewt-core/encoding/base64"
- "@apache-mynewt-core/sys/config"
pkg.req_apis:
- log
- stats
pkg.init:
bt_mesh_register_gatt: 'MYNEWT_VAL(BLE_MESH_SYSINIT_STAGE)'
ble_mesh_shell_init: 'MYNEWT_VAL(BLE_MESH_SYSINIT_STAGE_SHELL)'

View File

@ -99,7 +99,7 @@ int ble_gap_rx_l2cap_update_req(uint16_t conn_handle,
struct ble_gap_upd_params *params); struct ble_gap_upd_params *params);
void ble_gap_rx_phy_update_complete(struct hci_le_phy_upd_complete *evt); void ble_gap_rx_phy_update_complete(struct hci_le_phy_upd_complete *evt);
void ble_gap_enc_event(uint16_t conn_handle, int status, void ble_gap_enc_event(uint16_t conn_handle, int status,
int security_restored); int security_restored, int bonded);
void ble_gap_passkey_event(uint16_t conn_handle, void ble_gap_passkey_event(uint16_t conn_handle,
struct ble_gap_passkey_params *passkey_params); struct ble_gap_passkey_params *passkey_params);
void ble_gap_notify_rx_event(uint16_t conn_handle, uint16_t attr_handle, void ble_gap_notify_rx_event(uint16_t conn_handle, uint16_t attr_handle,

View File

@ -36,6 +36,7 @@ void ble_hs_id_rnd_reset(void);
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) #if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
bool ble_hs_is_rpa(uint8_t *addr, uint8_t addr_type); bool ble_hs_is_rpa(uint8_t *addr, uint8_t addr_type);
int ble_hs_id_set_pseudo_rnd(const uint8_t *); int ble_hs_id_set_pseudo_rnd(const uint8_t *);
int ble_hs_id_set_nrpa_rnd(void);
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -115,7 +115,7 @@ int ble_hs_hci_evt_acl_process(struct os_mbuf *om);
int ble_hs_misc_conn_chan_find(uint16_t conn_handle, uint16_t cid, int ble_hs_misc_conn_chan_find(uint16_t conn_handle, uint16_t cid,
struct ble_hs_conn **out_conn, struct ble_hs_conn **out_conn,
struct ble_l2cap_chan **out_chan); struct ble_l2cap_chan **out_chan);
void ble_hs_misc_conn_chan_find_reqd(uint16_t conn_handle, uint16_t cid, int ble_hs_misc_conn_chan_find_reqd(uint16_t conn_handle, uint16_t cid,
struct ble_hs_conn **out_conn, struct ble_hs_conn **out_conn,
struct ble_l2cap_chan **out_chan); struct ble_l2cap_chan **out_chan);
uint8_t ble_hs_misc_addr_type_to_id(uint8_t addr_type); uint8_t ble_hs_misc_addr_type_to_id(uint8_t addr_type);

View File

@ -61,7 +61,7 @@ struct ble_hs_dev_records {
/* Add a device to the resolving list */ /* Add a device to the resolving list */
int ble_hs_resolv_list_add(uint8_t *cmdbuf); int ble_hs_resolv_list_add(uint8_t *cmdbuf);
int ble_hs_gen_own_rpa_random(void); int ble_hs_gen_own_private_rnd(void);
uint8_t *ble_hs_get_rpa_local(void); uint8_t *ble_hs_get_rpa_local(void);
/* Remove a device from the resolving list */ /* Remove a device from the resolving list */
@ -71,6 +71,8 @@ void ble_hs_resolv_list_clear_all(void);
/* Address resolution enable command */ /* Address resolution enable command */
void ble_hs_resolv_enable(bool); void ble_hs_resolv_enable(bool);
void ble_hs_resolv_nrpa_enable(void);
void ble_hs_resolv_nrpa_disable(void);
/* Finds 'addr' in resolving list. Doesnt check if address resolution enabled */ /* Finds 'addr' in resolving list. Doesnt check if address resolution enabled */
struct ble_hs_resolv_entry * struct ble_hs_resolv_entry *

View File

@ -279,7 +279,7 @@ struct ble_sm_result {
void *state_arg; void *state_arg;
unsigned execute : 1; unsigned execute : 1;
unsigned enc_cb : 1; unsigned enc_cb : 1;
unsigned persist_keys:1; unsigned bonded : 1;
unsigned restore : 1; unsigned restore : 1;
}; };

View File

@ -1,540 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
syscfg.defs:
BLE_MESH_PROV:
description: >
Enable provisioning. It is automatically enabled whenever
BLE_MESH_PB_ADV or BLE_MESH_PB_GATT is set.
value: 1
BLE_MESH_PB_ADV:
description: >
Enable this option to allow the device to be provisioned over
the advertising bearer.
value: 1
BLE_MESH_PROXY:
description: >
Enable proxy. This is automatically set whenever BLE_MESH_PB_GATT or
BLE_MESH_GATT_PROXY is set.
value: 0
BLE_MESH_PB_GATT:
description: >
Enable this option to allow the device to be provisioned over
the GATT bearer.
value: 1
BLE_MESH_GATT_PROXY:
description: >
This option enables support for the Mesh GATT Proxy Service,
i.e. the ability to act as a proxy between a Mesh GATT Client
and a Mesh network.
value: 1
BLE_MESH_NODE_ID_TIMEOUT:
description: >
This option determines for how long the local node advertises
using Node Identity. The given value is in seconds. The
specification limits this to 60 seconds, and implies that to
be the appropriate value as well, so just leaving this as the
default is the safest option.
value: 60
BLE_MESH_PROXY_FILTER_SIZE:
descryption: >
This option specifies how many Proxy Filter entries the local
node supports.
value: 1
BLE_MESH_SUBNET_COUNT:
description: >
This option specifies how many subnets a Mesh network can
participate in at the same time.
value: 1
BLE_MESH_APP_KEY_COUNT:
description: >
This option specifies how many application keys the device can
store per network.
value: 1
BLE_MESH_MODEL_KEY_COUNT:
description: >
This option specifies how many application keys each model can
at most be bound to.
value: 1
BLE_MESH_MODEL_GROUP_COUNT:
description: >
This option specifies how many group addresses each model can
at most be subscribed to.
value: 1
BLE_MESH_LABEL_COUNT:
description: >
This option specifies how many Label UUIDs can be stored.
value: 1
BLE_MESH_CRPL:
description: >
This options specifies the maximum capacity of the replay
protection list. This option is similar to the network message
cache size, but has a different purpose.
value: 10
BLE_MESH_ADV_TASK_PRIO:
description: >
Advertising task prio (FIXME)
type: task_priority
value: 9
BLE_MESH_MSG_CACHE_SIZE:
description: >
Number of messages that are cached for the network. This description
prevent unnecessary decryption operations and unnecessary
relays. This option is similar to the replay protection list,
but has a different purpose.
value: 10
BLE_MESH_ADV_BUF_COUNT:
description: >
Number of advertising buffers available. This should be chosen
based on what kind of features the local node shoule have. E.g.
a relay will perform better the more buffers it has. Another
thing to consider is outgoing segmented messages. There must
be at least three more advertising buffers than the maximum
supported outgoing segment count (BT_MESH_TX_SEG_MAX).
value: 6
BLE_MESH_IVU_DIVIDER:
description: >
When the IV Update state enters Normal operation or IV Update
in Progress, we need to keep track of how many hours has passed
in the state, since the specification requires us to remain in
the state at least for 96 hours (Update in Progress has an
additional upper limit of 144 hours).
In order to fulfil the above requirement, even if the node might
be powered off once in a while, we need to store persistently
how many hours the node has been in the state. This doesn't
necessarily need to happen every hour (thanks to the flexible
duration range). The exact cadence will depend a lot on the
ways that the node will be used and what kind of power source it
has.
Since there is no single optimal answer, this configuration
option allows specifying a divider, i.e. how many intervals
the 96 hour minimum gets split into. After each interval the
duration that the node has been in the current state gets
stored to flash. E.g. the default value of 4 means that the
state is saved every 24 hours (96 / 4).
value: 4
BLE_MESH_TX_SEG_MSG_COUNT:
description: >
Maximum number of simultaneous outgoing multi-segment and/or
reliable messages.
value: 4
BLE_MESH_RX_SEG_MSG_COUNT:
description: >
Maximum number of simultaneous incoming multi-segment and/or
reliable messages.
value: 2
BLE_MESH_RX_SDU_MAX:
description: >
Maximum incoming Upper Transport Access PDU length. This
determines also how many segments incoming segmented messages
can have. Each segment can contain 12 bytes, so this value should
be set to a multiple of 12 to avoid wasted memory. The minimum
requirement is 2 segments (24 bytes) whereas the maximum supported
by the Mesh specification is 32 segments (384 bytes).
value: 72
BLE_MESH_TX_SEG_MAX:
description: >
Maximum number of segments supported for outgoing messages.
This value should typically be fine-tuned based on what
models the local node supports, i.e. what's the largest
message payload that the node needs to be able to send.
This value affects memory and call stack consumption, which
is why the default is lower than the maximum that the
specification would allow (32 segments).
The maximum outgoing SDU size is 12 times this number (out of
which 4 or 8 bytes is used for the Transport Layer MIC). For
example, 5 segments means the maximum SDU size is 60 bytes,
which leaves 56 bytes for application layer data using a
4-byte MIC and 52 bytes using an 8-byte MIC.
Be sure to specify a sufficient number of advertising buffers
when setting this option to a higher value. There must be at
least three more advertising buffers (BT_MESH_ADV_BUF_COUNT)
as there are outgoing segments.
value: 3
BLE_MESH_RELAY:
description: >
Support for acting as a Mesh Relay Node.
value: 0
BLE_MESH_LOW_POWER:
description: >
Enable this option to be able to act as a Low Power Node.
value: 0
BLE_MESH_LPN_ESTABLISHMENT:
description: >
Perform the Friendship establishment using low power, with
the help of a reduced scan duty cycle. The downside of this
is that the node may miss out on messages intended for it
until it has successfully set up Friendship with a Friend
node.
value: 1
BLE_MESH_LPN_AUTO:
description: >
Automatically enable LPN functionality once provisioned and start
looking for Friend nodes. If this option is disabled LPN mode
needs to be manually enabled by calling bt_mesh_lpn_set(true).
node.
value: 1
BLE_MESH_LPN_AUTO_TIMEOUT:
description: >
Time in seconds from the last received message, that the node
will wait before starting to look for Friend nodes.
value: 15
BLE_MESH_LPN_RETRY_TIMEOUT:
description: >
Time in seconds between Friend Requests, if a previous Friend
Request did not receive any acceptable Friend Offers.
value: 8
BLE_MESH_LPN_RSSI_FACTOR:
description: >
The contribution of the RSSI measured by the Friend node used
in Friend Offer Delay calculations. 0 = 1, 1 = 1.5, 2 = 2, 3 = 2.5.
value: 0
BLE_MESH_LPN_RECV_WIN_FACTOR:
description: >
The contribution of the supported Receive Window used in
Friend Offer Delay calculations. 0 = 1, 1 = 1.5, 2 = 2, 3 = 2.5.
value: 0
BLE_MESH_LPN_MIN_QUEUE_SIZE:
description: >
The MinQueueSizeLog field is defined as log_2(N), where N is
the minimum number of maximum size Lower Transport PDUs that
the Friend node can store in its Friend Queue. As an example,
MinQueueSizeLog value 1 gives N = 2, and value 7 gives N = 128.
value: 1
BLE_MESH_LPN_RECV_DELAY:
description: >
The ReceiveDelay is the time between the Low Power node
sending a request and listening for a response. This delay
allows the Friend node time to prepare the response. The value
is in units of milliseconds.
value: 100
BLE_MESH_LPN_POLL_TIMEOUT:
description: >
PollTimeout timer is used to measure time between two
consecutive requests sent by the Low Power node. If no
requests are received by the Friend node before the
PollTimeout timer expires, then the friendship is considered
terminated. The value is in units of 100 milliseconds, so e.g.
a value of 300 means 30 seconds.
value: 300
BLE_MESH_LPN_INIT_POLL_TIMEOUT:
description: >
The initial value of the PollTimeout timer when Friendship
gets established for the first time. After this the timeout
will gradually grow toward the actual PollTimeout, doubling
in value for each iteration. The value is in units of 100
milliseconds, so e.g. a value of 300 means 3 seconds.
value: MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT
BLE_MESH_LPN_SCAN_LATENCY:
description: >
Latency in milliseconds that it takes to enable scanning. This
is in practice how much time in advance before the Receive Window
that scanning is requested to be enabled.
value: 10
BLE_MESH_LPN_GROUPS:
description: >
Maximum number of groups that the LPN can subscribe to.
value: 10
BLE_MESH_FRIEND:
description: >
Enable this option to be able to act as a Friend Node.
value: 0
BLE_MESH_FRIEND_RECV_WIN:
description: >
Receive Window in milliseconds supported by the Friend node.
value: 255
BLE_MESH_FRIEND_QUEUE_SIZE:
description: >
Minimum number of buffers available to be stored for each
local Friend Queue.
value: 16
BLE_MESH_FRIEND_SUB_LIST_SIZE:
description: >
Size of the Subscription List that can be supported by a
Friend node for a Low Power node.
value: 3
BLE_MESH_FRIEND_LPN_COUNT:
description: >
Number of Low Power Nodes the Friend can have a Friendship
with simultaneously.
value: 2
BLE_MESH_FRIEND_SEG_RX:
description: >
Number of incomplete segment lists that we track for each LPN
that we are Friends for. In other words, this determines how
many elements we can simultaneously be receiving segmented
messages from when the messages are going into the Friend queue.
value: 1
BLE_MESH_CFG_CLI:
description: >
Enable support for the configuration client model.
value: 0
BLE_MESH_HEALTH_CLI:
description: >
Enable support for the health client model.
value: 0
BLE_MESH_SHELL:
description: >
Activate shell module that provides Bluetooth Mesh commands to
the console.
value: 0
BLE_MESH_DEBUG:
description: >
Use this option to enable debug logs for the Bluetooth
Mesh functionality.
value: 0
BLE_MESH_DEBUG_NET:
description: >
Use this option to enable Network layer debug logs for the
Bluetooth Mesh functionality.
value: 0
BLE_MESH_DEBUG_TRANS:
description: >
Use this option to enable Transport layer debug logs for the
Bluetooth Mesh functionality.
value: 0
BLE_MESH_DEBUG_BEACON:
description: >
Use this option to enable Beacon-related debug logs for the
Bluetooth Mesh functionality.
value: 0
BLE_MESH_DEBUG_CRYPTO:
description: >
Use this option to enable cryptographic debug logs for the
Bluetooth Mesh functionality.
value: 0
BLE_MESH_DEBUG_PROV:
description: >
Use this option to enable Provisioning debug logs for the
Bluetooth Mesh functionality.
value: 0
BLE_MESH_DEBUG_ACCESS:
description: >
Use this option to enable Access layer and device composition
related debug logs for Bluetooth Mesh.
value: 0
BLE_MESH_DEBUG_MODEL:
description: >
Use this option to enable debug logs for the Foundation
Models.
value: 0
BLE_MESH_DEBUG_ADV:
description: >
Use this option to enable advertising debug logs for
the Bluetooth Mesh functionality.
value: 0
BLE_MESH_DEBUG_LOW_POWER:
description: >
Use this option to enable Low Power debug logs for the
Bluetooth Mesh functionality.
value: 0
BLE_MESH_DEBUG_FRIEND:
description: >
Use this option to enable Friend debug logs for the
Bluetooth Mesh functionality.
value: 0
BLE_MESH_DEBUG_PROXY:
description: >
Use this option to enable Proxy protocol debug logs.
value: 0
BLE_MESH_DEBUG_SETTINGS:
description: >
Use this option to enable persistent settings debug logs.
value: 1
BLE_MESH_IV_UPDATE_TEST:
description: >
This option removes the 96 hour limit of the IV Update
Procedure and lets the state be changed at any time.
value: 0
BLE_MESH_TESTING:
description: >
This option enables testing API.
value: 0
BLE_MESH_DEV_UUID:
description: >
Device UUID
value: ((uint8_t[16]){0x11, 0x22, 0})
BLE_MESH_SHELL_MODELS:
description: >
Include implementation of some demo models.
value: 0
BLE_MESH_OOB_OUTPUT_ACTIONS:
description: >
Supported Output OOB Actions
BT_MESH_NO_OUTPUT = 0,
BT_MESH_BLINK = BIT(0)
BT_MESH_BEEP = BIT(1)
BT_MESH_VIBRATE = BIT(2)
BT_MESH_DISPLAY_NUMBER = BIT(3)
BT_MESH_DISPLAY_STRING = BIT(4)
value: ((BT_MESH_DISPLAY_NUMBER))
BLE_MESH_OOB_OUTPUT_SIZE:
description: >
Output OOB size
value: 4
BLE_MESH_OOB_INPUT_ACTIONS:
description: >
Supported Input OOB Actions
BT_MESH_NO_INPUT = 0,
BT_MESH_PUSH = BIT(0)
BT_MESH_TWIST = BIT(1)
BT_MESH_ENTER_NUMBER = BIT(2)
BT_MESH_ENTER_STRING = BIT(3)
value: ((BT_MESH_NO_INPUT))
BLE_MESH_OOB_INPUT_SIZE:
description: >
Input OOB size
value: 4
BLE_MESH_SETTINGS:
description: >
This option enables Mesh settings storage.
value: 1
BLE_MESH_STORE_TIMEOUT:
description: >
This value defines in seconds how soon any pending changes
are actually written into persistent storage (flash) after
a change occurs.
value: 2
BLE_MESH_SEQ_STORE_RATE:
description: >
This value defines how often the local sequence number gets
updated in persistent storage (i.e. flash). E.g. a value of 100
means that the sequence number will be stored to flash on every
100th increment. If the node sends messages very frequently a
higher value makes more sense, whereas if the node sends
infrequently a value as low as 0 (update storage for every
increment) can make sense. When the stack gets initialized it
will add this number to the last stored one, so that it starts
off with a value that's guaranteed to be larger than the last
one used before power off.
value: 128
BLE_MESH_RPL_STORE_TIMEOUT:
description: >
This value defines in seconds how soon the RPL gets written to
persistent storage after a change occurs. If the node receives
messages frequently it may make sense to have this set to a
large value, whereas if the RPL gets updated infrequently a
value as low as 0 (write immediately) may make sense. Note that
if the node operates a security sensitive use case, and there's
a risk of sudden power loss, it may be a security vulnerability
to set this value to anything else than 0 (a power loss before
writing to storage exposes the node to potential message
replay attacks).
value: 5
BLE_MESH_DEVICE_NAME:
description: >
This value defines BLE Mesh device/node name.
value: '"nimble-mesh-node"'
BLE_MESH_SYSINIT_STAGE:
description: >
Primary sysinit stage for BLE mesh functionality.
value: 500
BLE_MESH_SYSINIT_STAGE_SHELL:
description: >
Secondary sysinit stage for BLE mesh functionality.
value: 1000
syscfg.vals.BLE_MESH_SHELL:
BLE_MESH_CFG_CLI: 1
BLE_MESH_HEALTH_CLI: 1
BLE_MESH_IV_UPDATE_TEST: 1
syscfg.vals.BLE_MESH_GATT_PROXY:
BLE_MESH_PROXY: 1
syscfg.vals.BLE_MESH_PB_GATT:
BLE_MESH_PROXY: 1
BLE_MESH_PROV: 1
syscfg.vals.BLE_MESH_PB_ADV:
BLE_MESH_PROV: 1

View File

@ -1,34 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/services/ans
pkg.description: Alert Notification Service Server.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- ans
- nimble
pkg.deps:
- nimble/host
pkg.init:
ble_svc_ans_init: 'MYNEWT_VAL(BLE_SVC_ANS_SYSINIT_STAGE)'

View File

@ -1,30 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
syscfg.defs:
BLE_SVC_ANS_NEW_ALERT_CAT:
description: "Initial supported new alert category bitmask."
value: 0
BLE_SVC_ANS_UNR_ALERT_CAT:
description: "Initial supported unread alert category bitmask."
value: 0
BLE_SVC_ANS_SYSINIT_STAGE:
description: >
Sysinit stage for the alert notification service.
value: 303

View File

@ -1,34 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/services/bas
pkg.description: Battery Service
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- bas
- nimble
pkg.deps:
- nimble/host
pkg.init:
ble_svc_bas_init: 'MYNEWT_VAL(BLE_SVC_BAS_SYSINIT_STAGE)'

View File

@ -1,34 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
syscfg.defs:
BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM:
description: >
Defines permissions for reading "Battery Level" characteristics. Can
be zero to allow read without extra permissions or combination of:
BLE_GATT_CHR_F_READ_ENC
BLE_GATT_CHR_F_READ_AUTHEN
BLE_GATT_CHR_F_READ_AUTHOR
value: 0
BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE:
description: >
Set to 1 to support notification or 0 to disable it.
value: 1
BLE_SVC_BAS_SYSINIT_STAGE:
description: >
Sysinit stage for the battery level service.
value: 303

View File

@ -1,34 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/services/gap
pkg.description: Implements the GAP Service.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- nimble
- gap
pkg.deps:
- nimble/host
pkg.init:
ble_svc_gap_init: 'MYNEWT_VAL(BLE_SVC_GAP_SYSINIT_STAGE)'

View File

@ -1,83 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
syscfg.defs:
BLE_SVC_GAP_DEVICE_NAME:
description: >
Default value for "Device Name" characteristics, unless overwritten
by application.
value: '"nimble"'
BLE_SVC_GAP_DEVICE_NAME_WRITE_PERM:
description: >
Defines permissions for writing "Device Name" characteristics. Can
be zero to allow write without extra permissions or combination of:
BLE_GATT_CHR_F_WRITE_ENC
BLE_GATT_CHR_F_WRITE_AUTHEN
BLE_GATT_CHR_F_WRITE_AUTHOR
Set to '-1' to make characteristic read only.
value: -1
BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH:
description: Maximum length for "Device Name" characteristics
value: 31
BLE_SVC_GAP_APPEARANCE:
description: 'Device appearance'
value: 0
BLE_SVC_GAP_APPEARANCE_WRITE_PERM:
description: >
Defines permissions for writing "Appearance" characteristics. Can
be zero to allow write without extra permissions or combination of:
BLE_GATT_CHR_F_WRITE_ENC
BLE_GATT_CHR_F_WRITE_AUTHEN
BLE_GATT_CHR_F_WRITE_AUTHOR
Set to '-1' to make characteristic read only.
value: -1
# Setting all values for PPCP to '0' will disable characteristic!
BLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL:
description: >
Value of "minimum connection interval" of PPCP characteristic as
defined by Core specification 5.0, Vol 3, Part C, section 12.3.
value: 0
BLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL:
description: >
Value of "maximum connection interval" of PPCP characteristic as
defined by Core specification 5.0, Vol 3, Part C, section 12.3.
value: 0
BLE_SVC_GAP_PPCP_SLAVE_LATENCY:
description: >
Value of "slave latency" of PPCP characteristic as defined by Core
specification 5.0, Vol 3, Part C, section 12.3.
value: 0
BLE_SVC_GAP_PPCP_SUPERVISION_TMO:
description: >
Value of "connection supervision timeout multiplier" of PPCP
characteristic as defined by Core specification 5.0, Vol 3, Part C,
section 12.3.
value: 0
BLE_SVC_GAP_CENTRAL_ADDRESS_RESOLUTION:
description: >
Value of "Central Address Resolution" characteristics, as defined
by Core specification 5.0, Vol 3, Part C, section 12.
Set to '-1' to disable.
value: -1
BLE_SVC_GAP_SYSINIT_STAGE:
description: >
Sysinit stage for the GAP BLE service.
value: 301

View File

@ -1,34 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/services/gatt
pkg.description: Implements the GATT service.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- nimble
- gatt
pkg.deps:
- nimble/host
pkg.init:
ble_svc_gatt_init: 'MYNEWT_VAL(BLE_SVC_GATT_SYSINIT_STAGE)'

View File

@ -1,24 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
syscfg.defs:
BLE_SVC_GATT_SYSINIT_STAGE:
description: >
Sysinit stage for the GATT BLE service
value: 302

View File

@ -1,34 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/services/ias
pkg.description: Immediate Alert Service Implementation.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- ias
- nimble
pkg.deps:
- nimble/host
pkg.init:
ble_svc_ias_init: 'MYNEWT_VAL(BLE_SVC_IAS_SYSINIT_STAGE)'

View File

@ -1,23 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
syscfg.defs:
BLE_SVC_IAS_SYSINIT_STAGE:
description: >
Sysinit stage for the immediate alert BLE service.
value: 303

View File

@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include "sysinit/sysinit.h"
#include "host/ble_hs.h"
#include "services/ipss/ble_svc_ipss.h"
static const struct ble_gatt_svc_def ble_svc_ipss_defs[] = {
{
/*** Service: GATT */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(BLE_SVC_IPSS_UUID16),
.characteristics = NULL,
},
{
0, /* No more services. */
},
};
void
ble_svc_ipss_init(void)
{
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
rc = ble_gatts_count_cfg(ble_svc_ipss_defs);
SYSINIT_PANIC_ASSERT(rc == 0);
rc = ble_gatts_add_svcs(ble_svc_ipss_defs);
SYSINIT_PANIC_ASSERT(rc == 0);
}

View File

@ -1,34 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/services/lls
pkg.description: Link Loss Service Implementation.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- lls
- nimble
pkg.deps:
- nimble/host
pkg.init:
ble_svc_lls_init: 'MYNEWT_VAL(BLE_SVC_LLS_SYSINIT_STAGE)'

View File

@ -1,22 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
syscfg.defs:
BLE_SVC_LLS_SYSINIT_STAGE:
description: >
Sysinit stage for the link loss BLE service.
value: 303

View File

@ -1,34 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/services/tps
pkg.description: Tx Power Service adopted specification.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- tps
- nimble
pkg.deps:
- nimble/host
pkg.init:
ble_svc_tps_init: 'MYNEWT_VAL(BLE_SVC_TPS_SYSINIT_STAGE)'

View File

@ -1,23 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
syscfg.defs:
BLE_SVC_TPS_SYSINIT_STAGE:
description: >
Sysinit stage for the transmit power BLE service.
value: 303

View File

@ -67,6 +67,37 @@ ble_hs_id_gen_rnd(int nrpa, ble_addr_t *out_addr)
} }
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) #if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
/**
* Sets the device's pseudo Non Resolvable Private Address when 'Host based
* privacy' is in use.
*
* @return 0 on success;
* Appropriate error code if failure.
*/
int
ble_hs_id_set_nrpa_rnd()
{
ble_addr_t nrpa_addr;
int rc;
ble_hs_id_gen_rnd(1, &nrpa_addr);
ble_hs_lock();
/* set the NRPA address as pseudo random address in controller */
rc = ble_hs_hci_util_set_random_addr(nrpa_addr.val);
if (rc != 0) {
goto done;
}
memcpy(ble_hs_id_rnd, nrpa_addr.val, BLE_DEV_ADDR_LEN);
done:
ble_hs_unlock();
return rc;
}
/** /**
* Sets the device's pseudo RPA address when 'Host based privacy' is in use. * Sets the device's pseudo RPA address when 'Host based privacy' is in use.
* The address type (RPA) is inferred from the most-significant bits. The * The address type (RPA) is inferred from the most-significant bits. The

View File

@ -36,6 +36,7 @@ void ble_hs_id_rnd_reset(void);
#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) #if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
bool ble_hs_is_rpa(uint8_t *addr, uint8_t addr_type); bool ble_hs_is_rpa(uint8_t *addr, uint8_t addr_type);
int ble_hs_id_set_pseudo_rnd(const uint8_t *); int ble_hs_id_set_pseudo_rnd(const uint8_t *);
int ble_hs_id_set_nrpa_rnd(void);
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -22,6 +22,7 @@
#include "stats/stats.h" #include "stats/stats.h"
#include "ble_hs_priv.h" #include "ble_hs_priv.h"
#include "ble_hs_resolv_priv.h" #include "ble_hs_resolv_priv.h"
#include "host/ble_hs_pvcy.h"
static uint8_t ble_hs_pvcy_started; static uint8_t ble_hs_pvcy_started;
static uint8_t ble_hs_pvcy_irk[16]; static uint8_t ble_hs_pvcy_irk[16];
@ -329,7 +330,7 @@ ble_hs_pvcy_rpa_config(uint8_t enable)
{ {
int rc = 0; int rc = 0;
if (enable != 0) { if (enable != NIMBLE_HOST_DISABLE_PRIVACY) {
rc = ble_hs_pvcy_ensure_started(); rc = ble_hs_pvcy_ensure_started();
if (rc != 0) { if (rc != 0) {
return rc; return rc;
@ -337,8 +338,15 @@ ble_hs_pvcy_rpa_config(uint8_t enable)
ble_hs_resolv_enable(true); ble_hs_resolv_enable(true);
/* Configure NRPA address related flags according to input parameter */
if (enable == NIMBLE_HOST_ENABLE_NRPA) {
ble_hs_resolv_nrpa_enable();
} else {
ble_hs_resolv_nrpa_disable();
}
/* Generate local RPA address and set it in controller */ /* Generate local RPA address and set it in controller */
rc = ble_hs_gen_own_rpa_random(); rc = ble_hs_gen_own_private_rnd();
} else { } else {
ble_hs_resolv_enable(false); ble_hs_resolv_enable(false);
} }

View File

@ -50,6 +50,9 @@ struct ble_hs_resolv_data {
struct ble_npl_callout rpa_timer; struct ble_npl_callout rpa_timer;
}; };
/* NRPA bit: Enables NRPA as private address. */
static bool nrpa_pvcy;
/*** APIs for Peer Device Records. /*** APIs for Peer Device Records.
* *
* These Peer records are necessary to take care of Peers with RPA address when * These Peer records are necessary to take care of Peers with RPA address when
@ -305,6 +308,10 @@ is_ble_hs_resolv_enabled(void)
bool bool
ble_host_rpa_enabled(void) ble_host_rpa_enabled(void)
{ {
if (nrpa_pvcy) {
return false;
}
if (is_ble_hs_resolv_enabled() && ble_hs_pvcy_enabled()) { if (is_ble_hs_resolv_enabled() && ble_hs_pvcy_enabled()) {
return true; return true;
} }
@ -385,12 +392,16 @@ ble_hs_resolv_gen_priv_addr(struct ble_hs_resolv_entry *rl, int local)
addr[2] = ecb.cipher_text[13]; addr[2] = ecb.cipher_text[13];
} }
/* Called to generate RPA address and this address is set in controller as /* Called to generate private (RPA/NRPA) address and this address is set in controller as
* Random address. This is necessary in Host based privacy because controller is unaware of RPA * Random address. This is necessary in Host based privacy because controller
* address is being used */ * is unaware of private address is being used */
int int
ble_hs_gen_own_rpa_random(void) ble_hs_gen_own_private_rnd(void)
{ {
if (nrpa_pvcy) {
return ble_hs_id_set_nrpa_rnd();
}
struct ble_hs_resolv_entry *rl = &g_ble_hs_resolv_list[0]; struct ble_hs_resolv_entry *rl = &g_ble_hs_resolv_list[0];
ble_hs_resolv_gen_priv_addr(rl, 1); ble_hs_resolv_gen_priv_addr(rl, 1);
@ -412,12 +423,11 @@ ble_hs_get_rpa_local(void)
static void static void
ble_hs_resolv_rpa_timer_cb(struct ble_npl_event *ev) ble_hs_resolv_rpa_timer_cb(struct ble_npl_event *ev)
{ {
if (ble_host_rpa_enabled()) { if (ble_host_rpa_enabled() || (nrpa_pvcy)) {
BLE_HS_LOG(DEBUG, "RPA Timeout; start active adv & scan with new RPA\n"); BLE_HS_LOG(DEBUG, "RPA/NRPA Timeout; start active adv & scan with new Private address \n");
ble_gap_preempt(); ble_gap_preempt();
/* Generate local RPA */ /* Generate local private address */
ble_hs_gen_own_rpa_random(); ble_hs_gen_own_private_rnd();
ble_npl_callout_reset(&g_ble_hs_resolv_data.rpa_timer, ble_npl_callout_reset(&g_ble_hs_resolv_data.rpa_timer,
(int32_t)g_ble_hs_resolv_data.rpa_tmo); (int32_t)g_ble_hs_resolv_data.rpa_tmo);
ble_gap_preempt_done(); ble_gap_preempt_done();
@ -595,6 +605,23 @@ ble_hs_resolv_list_clear_all(void)
return; return;
} }
/**
* Called by host stack to enable NRPA privacy flag for future reference
*/
void
ble_hs_resolv_nrpa_enable(void)
{
nrpa_pvcy = true;
}
/**
* Called by host stack to disable NRPA privacy flag
*/
void
ble_hs_resolv_nrpa_disable(void)
{
nrpa_pvcy = false;
}
/** /**
* Called to enable or disable address resolution in the host * Called to enable or disable address resolution in the host
* *

View File

@ -61,7 +61,7 @@ struct ble_hs_dev_records {
/* Add a device to the resolving list */ /* Add a device to the resolving list */
int ble_hs_resolv_list_add(uint8_t *cmdbuf); int ble_hs_resolv_list_add(uint8_t *cmdbuf);
int ble_hs_gen_own_rpa_random(void); int ble_hs_gen_own_private_rnd(void);
uint8_t *ble_hs_get_rpa_local(void); uint8_t *ble_hs_get_rpa_local(void);
/* Remove a device from the resolving list */ /* Remove a device from the resolving list */
@ -71,6 +71,8 @@ void ble_hs_resolv_list_clear_all(void);
/* Address resolution enable command */ /* Address resolution enable command */
void ble_hs_resolv_enable(bool); void ble_hs_resolv_enable(bool);
void ble_hs_resolv_nrpa_enable(void);
void ble_hs_resolv_nrpa_disable(void);
/* Finds 'addr' in resolving list. Doesnt check if address resolution enabled */ /* Finds 'addr' in resolving list. Doesnt check if address resolution enabled */
struct ble_hs_resolv_entry * struct ble_hs_resolv_entry *

View File

@ -239,7 +239,10 @@ ble_uuid_flat(const ble_uuid_t *uuid, void *dst)
break; break;
case BLE_UUID_TYPE_32: case BLE_UUID_TYPE_32:
memcpy(dst, ble_uuid_base, 16); memcpy(dst, ble_uuid_base, 16);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-arith"
put_le32(dst + 12, BLE_UUID32(uuid)->value); put_le32(dst + 12, BLE_UUID32(uuid)->value);
#pragma GCC diagnostic pop
break; break;
case BLE_UUID_TYPE_128: case BLE_UUID_TYPE_128:
memcpy(dst, BLE_UUID128(uuid)->value, 16); memcpy(dst, BLE_UUID128(uuid)->value, 16);

View File

@ -1,38 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/store/config
pkg.description: sys/config-based persistence layer for the NimBLE host.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- nimble
- persistence
pkg.deps:
- "@apache-mynewt-core/encoding/base64"
- nimble/host
pkg.deps.BLE_STORE_CONFIG_PERSIST:
- "@apache-mynewt-core/sys/config"
pkg.init:
ble_store_config_init: 'MYNEWT_VAL(BLE_STORE_SYSINIT_STAGE)'

View File

@ -1,231 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST)
#include <inttypes.h>
#include <string.h>
#include "sysinit/sysinit.h"
#include "host/ble_hs.h"
#include "config/config.h"
#include "base64/base64.h"
#include "store/config/ble_store_config.h"
#include "ble_store_config_priv.h"
static int
ble_store_config_conf_set(int argc, char **argv, char *val);
static int
ble_store_config_conf_export(void (*func)(char *name, char *val),
enum conf_export_tgt tgt);
static struct conf_handler ble_store_config_conf_handler = {
.ch_name = "ble_hs",
.ch_get = NULL,
.ch_set = ble_store_config_conf_set,
.ch_commit = NULL,
.ch_export = ble_store_config_conf_export
};
#define BLE_STORE_CONFIG_SEC_ENCODE_SZ \
BASE64_ENCODE_SIZE(sizeof (struct ble_store_value_sec))
#define BLE_STORE_CONFIG_SEC_SET_ENCODE_SZ \
(MYNEWT_VAL(BLE_STORE_MAX_BONDS) * BLE_STORE_CONFIG_SEC_ENCODE_SZ + 1)
#define BLE_STORE_CONFIG_CCCD_ENCODE_SZ \
BASE64_ENCODE_SIZE(sizeof (struct ble_store_value_cccd))
#define BLE_STORE_CONFIG_CCCD_SET_ENCODE_SZ \
(MYNEWT_VAL(BLE_STORE_MAX_CCCDS) * BLE_STORE_CONFIG_CCCD_ENCODE_SZ + 1)
static void
ble_store_config_serialize_arr(const void *arr, int obj_sz, int num_objs,
char *out_buf, int buf_sz)
{
int arr_size;
arr_size = obj_sz * num_objs;
assert(arr_size <= buf_sz);
base64_encode(arr, arr_size, out_buf, 1);
}
static int
ble_store_config_deserialize_arr(const char *enc,
void *out_arr,
int obj_sz,
int *out_num_objs)
{
int len;
len = base64_decode(enc, out_arr);
if (len < 0) {
return OS_EINVAL;
}
*out_num_objs = len / obj_sz;
return 0;
}
static int
ble_store_config_conf_set(int argc, char **argv, char *val)
{
int rc;
if (argc == 1) {
if (strcmp(argv[0], "our_sec") == 0) {
rc = ble_store_config_deserialize_arr(
val,
ble_store_config_our_secs,
sizeof *ble_store_config_our_secs,
&ble_store_config_num_our_secs);
return rc;
} else if (strcmp(argv[0], "peer_sec") == 0) {
rc = ble_store_config_deserialize_arr(
val,
ble_store_config_peer_secs,
sizeof *ble_store_config_peer_secs,
&ble_store_config_num_peer_secs);
return rc;
} else if (strcmp(argv[0], "cccd") == 0) {
rc = ble_store_config_deserialize_arr(
val,
ble_store_config_cccds,
sizeof *ble_store_config_cccds,
&ble_store_config_num_cccds);
return rc;
}
}
return OS_ENOENT;
}
static int
ble_store_config_conf_export(void (*func)(char *name, char *val),
enum conf_export_tgt tgt)
{
union {
char sec[BLE_STORE_CONFIG_SEC_SET_ENCODE_SZ];
char cccd[BLE_STORE_CONFIG_CCCD_SET_ENCODE_SZ];
} buf;
ble_store_config_serialize_arr(ble_store_config_our_secs,
sizeof *ble_store_config_our_secs,
ble_store_config_num_our_secs,
buf.sec,
sizeof buf.sec);
func("ble_hs/our_sec", buf.sec);
ble_store_config_serialize_arr(ble_store_config_peer_secs,
sizeof *ble_store_config_peer_secs,
ble_store_config_num_peer_secs,
buf.sec,
sizeof buf.sec);
func("ble_hs/peer_sec", buf.sec);
ble_store_config_serialize_arr(ble_store_config_cccds,
sizeof *ble_store_config_cccds,
ble_store_config_num_cccds,
buf.cccd,
sizeof buf.cccd);
func("ble_hs/cccd", buf.cccd);
return 0;
}
static int
ble_store_config_persist_sec_set(const char *setting_name,
const struct ble_store_value_sec *secs,
int num_secs)
{
char buf[BLE_STORE_CONFIG_SEC_SET_ENCODE_SZ];
int rc;
ble_store_config_serialize_arr(secs, sizeof *secs, num_secs,
buf, sizeof buf);
rc = conf_save_one(setting_name, buf);
if (rc != 0) {
return BLE_HS_ESTORE_FAIL;
}
return 0;
}
int
ble_store_config_persist_our_secs(void)
{
int rc;
rc = ble_store_config_persist_sec_set("ble_hs/our_sec",
ble_store_config_our_secs,
ble_store_config_num_our_secs);
if (rc != 0) {
return rc;
}
return 0;
}
int
ble_store_config_persist_peer_secs(void)
{
int rc;
rc = ble_store_config_persist_sec_set("ble_hs/peer_sec",
ble_store_config_peer_secs,
ble_store_config_num_peer_secs);
if (rc != 0) {
return rc;
}
return 0;
}
int
ble_store_config_persist_cccds(void)
{
char buf[BLE_STORE_CONFIG_CCCD_SET_ENCODE_SZ];
int rc;
ble_store_config_serialize_arr(ble_store_config_cccds,
sizeof *ble_store_config_cccds,
ble_store_config_num_cccds,
buf,
sizeof buf);
rc = conf_save_one("ble_hs/cccd", buf);
if (rc != 0) {
return BLE_HS_ESTORE_FAIL;
}
return 0;
}
void
ble_store_config_conf_init(void)
{
int rc;
rc = conf_register(&ble_store_config_conf_handler);
SYSINIT_PANIC_ASSERT_MSG(rc == 0,
"Failed to register ble_store_config conf");
}
#endif /* MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) */

View File

@ -1,27 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
syscfg.defs:
BLE_STORE_CONFIG_PERSIST:
description: >
Whether to save data to sys/config, or just keep it in RAM.
value: 1
BLE_STORE_SYSINIT_STAGE:
description: >
Sysinit stage for BLE host store.
value: 500

View File

@ -1,37 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/store/ram
pkg.description: >
DEPRECATED; for a RAM-only BLE store, use store/config and set
BLE_STORE_CONFIG_PERSIST to 0. RAM-based persistence layer for the NimBLE
host.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- nimble
- persistence
pkg.deps:
- nimble/host
pkg.init:
ble_store_ram_init: 'MYNEWT_VAL(BLE_STORE_RAM_SYSINIT_STAGE)'

View File

@ -1,23 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
syscfg.defs:
BLE_STORE_RAM_SYSINIT_STAGE:
description: >
Sysinit stage for the RAM BLE store.
value: 500

View File

@ -1,29 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/host/util
pkg.description: Supplementary utilities for the NimBLE host
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
pkg.deps:
- nimble/host

View File

@ -1,19 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
syscfg.defs:

View File

@ -9,27 +9,27 @@
/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig)*/ /* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig)*/
#if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE) #if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
#if defined(CONFIG_NIMBLE_ENABLED) #if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
#define CONFIG_BT_NIMBLE_ENABLED #define CONFIG_BT_NIMBLE_ENABLED
#endif #endif
#if defined(CONFIG_NIMBLE_ROLE_OBSERVER) #if defined(CONFIG_NIMBLE_ROLE_OBSERVER) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER #define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif #endif
#if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) #if defined(CONFIG_NIMBLE_ROLE_BROADCASTER) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif #endif
#if defined(CONFIG_NIMBLE_ROLE_CENTRAL) #if defined(CONFIG_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL #define CONFIG_BT_NIMBLE_ROLE_CENTRAL
#endif #endif
#if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) #if defined(CONFIG_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
#endif #endif
#if defined(CONFIG_NIMBLE_DEBUG) #if defined(CONFIG_NIMBLE_DEBUG) && !defined(CONFIG_BT_NIMBLE_DEBUG)
#define CONFIG_BT_NIMBLE_DEBUG #define CONFIG_BT_NIMBLE_DEBUG
#endif #endif
@ -52,12 +52,12 @@
/** Comment out if not using NimBLE Server functions /** Comment out if not using NimBLE Server functions
* Reduces flash size by approx. 16kB. * Reduces flash size by approx. 16kB.
*/ */
// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL // Tasmota // #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
/** Comment out if not using NimBLE Advertising functions /** Comment out if not using NimBLE Advertising functions
* Reduces flash size by approx. 5kB. * Reduces flash size by approx. 5kB.
*/ */
// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER // Tasmota // #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
/** Uncomment to see debug log messages from the NimBLE host /** Uncomment to see debug log messages from the NimBLE host
* Uses approx. 32kB of flash memory. * Uses approx. 32kB of flash memory.
@ -89,12 +89,12 @@
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3 #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
/** Sets the number of devices allowed to store/bond with */ /** Sets the number of devices allowed to store/bond with */
#define CONFIG_BT_NIMBLE_MAX_BONDS 3 // Tasmota #define CONFIG_BT_NIMBLE_MAX_BONDS 3
/** Sets the number of CCCD's to store per bonded device */ /** Sets the number of CCCD's to store per bonded device */
#define CONFIG_BT_NIMBLE_MAX_CCCDS 3 // Tasmota #define CONFIG_BT_NIMBLE_MAX_CCCDS 8
#define CONFIG_BT_NIMBLE_NVS_PERSIST 0 // Tasmota #define CONFIG_BT_NIMBLE_NVS_PERSIST 0
#define CONFIG_BT_NIMBLE_SM_LEGACY 1 #define CONFIG_BT_NIMBLE_SM_LEGACY 1
#define CONFIG_BT_NIMBLE_SM_SC 1 #define CONFIG_BT_NIMBLE_SM_SC 1
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble" #define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"

View File

@ -1,32 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
# * http://www.apache.org/licenses/LICENSE-2.0
# * Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
NIMBLE_CFLAGS += \
-DNIMBLE_CFG_CONTROLLER=1 \
NIMBLE_INCLUDE += \
$(NIMBLE_ROOT)/nimble/transport/ram/include \
$(NIMBLE_ROOT)/nimble/controller/include \
$(NIMBLE_ROOT)/nimble/drivers/nrf52/include \
$(NULL)
NIMBLE_SRC += \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/transport/ram/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/controller/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/drivers/nrf52/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/porting/nimble/controller/src/*.c)) \
$(NULL)

View File

@ -1,68 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
# * http://www.apache.org/licenses/LICENSE-2.0
# * Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
ifeq (,$(NIMBLE_ROOT))
$(error NIMBLE_ROOT shall be defined)
endif
NIMBLE_CFLAGS :=
NIMBLE_INCLUDE := \
$(NIMBLE_ROOT)/nimble/include \
$(NIMBLE_ROOT)/nimble/host/include \
$(NIMBLE_ROOT)/nimble/host/services/ans/include \
$(NIMBLE_ROOT)/nimble/host/services/bas/include \
$(NIMBLE_ROOT)/nimble/host/services/bleuart/include \
$(NIMBLE_ROOT)/nimble/host/services/gap/include \
$(NIMBLE_ROOT)/nimble/host/services/gatt/include \
$(NIMBLE_ROOT)/nimble/host/services/ias/include \
$(NIMBLE_ROOT)/nimble/host/services/lls/include \
$(NIMBLE_ROOT)/nimble/host/services/tps/include \
$(NIMBLE_ROOT)/nimble/host/store/ram/include \
$(NIMBLE_ROOT)/nimble/host/util/include \
$(NIMBLE_ROOT)/porting/nimble/include \
$(NULL)
NIMBLE_SRC := \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/porting/nimble/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/util/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/ans/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/bas/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/gap/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/gatt/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/ias/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/lls/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/services/tps/src/*.c)) \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/store/ram/src/*.c)) \
$(NULL)
ifneq (,$(NIMBLE_CFG_CONTROLLER))
include $(NIMBLE_ROOT)/porting/nimble/Makefile.controller
endif
# TinyCrypt (for SM)
ifneq (,$(NIMBLE_CFG_TINYCRYPT))
include $(NIMBLE_ROOT)/porting/nimble/Makefile.tinycrypt
endif
ifneq (,$(NIMBLE_CFG_MESH))
include $(NIMBLE_ROOT)/porting/nimble/Makefile.mesh
endif
NIMBLE_OBJ := $(NIMBLE_SRC:.c=.o)

View File

@ -1,24 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
# * http://www.apache.org/licenses/LICENSE-2.0
# * Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
NIMBLE_INCLUDE += \
$(NIMBLE_ROOT)/nimble/host/mesh/include \
$(NULL)
NIMBLE_SRC += \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/nimble/host/mesh/src/*.c)) \
$(NULL)

View File

@ -1,26 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
# * http://www.apache.org/licenses/LICENSE-2.0
# * Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
TINYCRYPT_CFLAGS := -std=c99
TINYCRYPT_INCLUDE := \
$(NIMBLE_ROOT)/ext/tinycrypt/include \
$(NULL)
TINYCRYPT_SRC := \
$(filter-out $(NIMBLE_IGNORE), $(wildcard $(NIMBLE_ROOT)/ext/tinycrypt/src/*.c)) \
$(NULL)

View File

@ -1,43 +0,0 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: porting/nimble
pkg.type: app
pkg.description: Stub for NimBLE porting
pkg.author: "Apache Mynewt <dev@mynewt.incubator.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- nimble/host
- nimble/host/services/ans
- nimble/host/services/bas
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/services/ias
- nimble/host/services/lls
- nimble/host/services/tps
- nimble/transport
- "@apache-mynewt-core/sys/console/stub"
- "@apache-mynewt-core/sys/log/stub"
- "@apache-mynewt-core/sys/stats/stub"
# No need to build files from this package
pkg.ign_files:
- ".*\\.c"

View File

@ -385,7 +385,10 @@ os_mbuf_append(struct os_mbuf *om, const void *data, uint16_t len)
memcpy(OS_MBUF_DATA(last, uint8_t *) + last->om_len , data, space); memcpy(OS_MBUF_DATA(last, uint8_t *) + last->om_len , data, space);
last->om_len += space; last->om_len += space;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-arith"
data += space; data += space;
#pragma GCC diagnostic pop
remainder -= space; remainder -= space;
} }
@ -400,7 +403,10 @@ os_mbuf_append(struct os_mbuf *om, const void *data, uint16_t len)
new->om_len = min(omp->omp_databuf_len, remainder); new->om_len = min(omp->omp_databuf_len, remainder);
memcpy(OS_MBUF_DATA(new, void *), data, new->om_len); memcpy(OS_MBUF_DATA(new, void *), data, new->om_len);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-arith"
data += new->om_len; data += new->om_len;
#pragma GCC diagnostic pop
remainder -= new->om_len; remainder -= new->om_len;
SLIST_NEXT(last, om_next) = new; SLIST_NEXT(last, om_next) = new;
last = new; last = new;
@ -651,7 +657,10 @@ os_mbuf_cmpf(const struct os_mbuf *om, int off, const void *data, int len)
chunk_sz = min(om->om_len - om_off, len - data_off); chunk_sz = min(om->om_len - om_off, len - data_off);
if (chunk_sz > 0) { if (chunk_sz > 0) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-arith"
rc = memcmp(om->om_data + om_off, data + data_off, chunk_sz); rc = memcmp(om->om_data + om_off, data + data_off, chunk_sz);
#pragma GCC diagnostic pop
if (rc != 0) { if (rc != 0) {
return rc; return rc;
} }

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef H_BLE_SVC_IPSS_
#define H_BLE_SVC_IPSS_
#ifdef __cplusplus
extern "C" {
#endif
#define BLE_SVC_IPSS_UUID16 0x1820
/**
* @brief Initialize the IPSS service
*/
void ble_svc_ipss_init(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -279,7 +279,7 @@ struct ble_sm_result {
void *state_arg; void *state_arg;
unsigned execute : 1; unsigned execute : 1;
unsigned enc_cb : 1; unsigned enc_cb : 1;
unsigned persist_keys:1; unsigned bonded : 1;
unsigned restore : 1; unsigned restore : 1;
}; };
@ -399,6 +399,7 @@ int ble_sm_slave_initiate(uint16_t conn_handle);
int ble_sm_enc_initiate(uint16_t conn_handle, uint8_t key_size, int ble_sm_enc_initiate(uint16_t conn_handle, uint8_t key_size,
const uint8_t *ltk, uint16_t ediv, const uint8_t *ltk, uint16_t ediv,
uint64_t rand_val, int auth); uint64_t rand_val, int auth);
int ble_sm_alg_encrypt(uint8_t *key, uint8_t *plaintext, uint8_t *enc_data);
int ble_sm_init(void); int ble_sm_init(void);
#define BLE_SM_LOG_CMD(is_tx, cmd_name, conn_handle, log_cb, cmd) \ #define BLE_SM_LOG_CMD(is_tx, cmd_name, conn_handle, log_cb, cmd) \
@ -419,6 +420,9 @@ int ble_sm_init(void);
#define ble_sm_init() 0 #define ble_sm_init() 0
#define ble_sm_alg_encrypt(key, plaintext, enc_data) \
BLE_HS_ENOTSUP
#endif #endif
struct ble_l2cap_chan *ble_sm_create_chan(uint16_t handle); struct ble_l2cap_chan *ble_sm_create_chan(uint16_t handle);

View File

@ -20,6 +20,7 @@
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
Version yyyymmdd Action Description Version yyyymmdd Action Description
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
0.9.0.1 20200706 changed - adapt to new NimBLE-API, tweak scan process
0.9.0.0 20200413 started - initial development by Christian Baars 0.9.0.0 20200413 started - initial development by Christian Baars
forked - from arendst/tasmota - https://github.com/arendst/Tasmota forked - from arendst/tasmota - https://github.com/arendst/Tasmota
@ -232,7 +233,6 @@ class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks {
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length()); // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length());
if (advertisedDevice->getServiceData().length() == 0) { if (advertisedDevice->getServiceData().length() == 0) {
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length()); // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length());
MI32Scan->erase(advertisedDevice->getAddress());
return; return;
} }
uint16_t uuid = advertisedDevice->getServiceDataUUID().getNative()->u16.value; uint16_t uuid = advertisedDevice->getServiceDataUUID().getNative()->u16.value;
@ -242,13 +242,14 @@ class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks {
MI32_ReverseMAC(addr); MI32_ReverseMAC(addr);
if(uuid==0xfe95) { if(uuid==0xfe95) {
MI32ParseResponse((char*)advertisedDevice->getServiceData().data(),advertisedDevice->getServiceData().length(), addr); MI32ParseResponse((char*)advertisedDevice->getServiceData().data(),advertisedDevice->getServiceData().length(), addr);
MI32Scan->erase(advertisedDevice->getAddress());
} }
else if(uuid==0xfdcd) { else if(uuid==0xfdcd) {
MI32parseCGD1Packet((char*)advertisedDevice->getServiceData().data(),advertisedDevice->getServiceData().length(), addr); MI32parseCGD1Packet((char*)advertisedDevice->getServiceData().data(),advertisedDevice->getServiceData().length(), addr);
MI32Scan->erase(advertisedDevice->getAddress());
} }
else { else {
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length()); // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length());
MI32Scan->erase(advertisedDevice->getAddress());
} }
}; };
}; };
@ -551,7 +552,7 @@ bool MI32connectLYWSD03forNotification(){
} }
if (pChr){ if (pChr){
if(pChr->canNotify()) { if(pChr->canNotify()) {
if(pChr->registerForNotify(MI32notifyCB)) { if(pChr->subscribe(true,false,MI32notifyCB)) {
return true; return true;
} }
} }