From 1102bb71aa1a7ac9b44f5669e8c3e27ba467f784 Mon Sep 17 00:00:00 2001 From: h2zero Date: Fri, 12 Jul 2024 13:04:43 -0600 Subject: [PATCH] [BREAKING] - Refactor NimBLEAdvertising * General code cleanup. * Added new method, `NimBLEAdvertising::setPreferredConnectionInterval` that takes the min and max preferred connection parameters as an alternative for `setMinPreferred` and `setMaxPreferred`. * Added new method, `NimBLEAdvertising::setAdvertisingInterval` Sets the advertisement interval for min and max to the same value instead of calling `setMinInterval` and `setMaxInterval` separately if there is not value difference. * The advertisement payload is now stored in `std::vector` instead of `std::string`. * `NimBLEAdvertisementData::getPayload` now returns `std::vector` instead of `std::string`. * `NimBLEAdvertisementData::addData` now takes either a `std::vector` or `uint8_t* + length` instead of `std::string` or `char + length`. * `NimBLEAdvertisementData::setName` now takes an optional `bool` parameter to indicate if the name is complete or incomplete, default = complete. * `NimBLEAdvertising::start` No longer takes a callback pointer parameter, instead the new method `NimBLEAdvertising::setAdvertisingCompleteCallback` should be used. * `NimBLEAdvertising::setAdvertisementType` has been renamed to `NimBLEAdvertising::setConnectableMode` to better reflect it's function. * Added new method, `NimBLEAdvertising::setDiscoverableMode` to allow applications to control the discoverability of the advertiser. * Advertising the name and TX power of the device will no longer happen by default and should be set manually by the application. --- src/NimBLEAdvertising.cpp | 946 +++++++++++++++++--------------------- src/NimBLEAdvertising.h | 140 +++--- 2 files changed, 490 insertions(+), 596 deletions(-) diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index 150fb6d..e91da0d 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -14,81 +14,74 @@ * */ #include "nimconfig.h" -#if (defined(CONFIG_BT_ENABLED) && \ - defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \ - !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) +#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \ + defined(_DOXYGEN_) -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "services/gap/ble_svc_gap.h" -#else -#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" -#endif -#include "NimBLEAdvertising.h" -#include "NimBLEDevice.h" -#include "NimBLEServer.h" -#include "NimBLEUtils.h" -#include "NimBLELog.h" +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "services/gap/ble_svc_gap.h" +# else +# include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h" +# endif +# include "NimBLEAdvertising.h" +# include "NimBLEDevice.h" +# include "NimBLEServer.h" +# include "NimBLEUtils.h" +# include "NimBLELog.h" static const char* LOG_TAG = "NimBLEAdvertising"; - /** * @brief Construct a default advertising object. */ -NimBLEAdvertising::NimBLEAdvertising() { - reset(); +NimBLEAdvertising::NimBLEAdvertising() + : m_advData{}, + m_scanData{}, + m_advParams{}, + m_serviceUUIDs{}, + m_customAdvData{false}, + m_customScanResponseData{false}, + m_scanResp{true}, + m_advDataSet{false}, + m_advCompCb{nullptr}, + m_slaveItvl{0}, + m_duration{BLE_HS_FOREVER}, + m_svcData16{}, + m_svcData32{}, + m_svcData128{}, + m_name{}, + m_mfgData{}, + m_uri{} { +# if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON; +# else + m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; + m_advData.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; +# endif + m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; } // NimBLEAdvertising - /** * @brief Stops the current advertising and resets the advertising data to the default values. + * @return True if successful. */ -void NimBLEAdvertising::reset() { - if(NimBLEDevice::isInitialized() && isAdvertising()) { - stop(); +bool NimBLEAdvertising::reset() { + if (!stop()) { + return false; } - memset(&m_advData, 0, sizeof m_advData); - memset(&m_scanData, 0, sizeof m_scanData); - memset(&m_advParams, 0, sizeof m_advParams); - memset(&m_slaveItvl, 0, sizeof m_slaveItvl); - const char *name = ble_svc_gap_device_name(); - m_advData.name = (uint8_t *)name; - m_advData.name_len = strlen(name); - m_advData.name_is_complete = 1; -#ifndef CONFIG_IDF_TARGET_ESP32P4 - m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); -#else - m_advData.tx_pwr_lvl = 0; -#endif - m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); - -#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON; -#else - m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; -#endif - m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; - m_customAdvData = false; - m_customScanResponseData = false; - m_scanResp = true; - m_advDataSet = false; - // Set this to non-zero to prevent auto start if host reset before started by app. - m_duration = BLE_HS_FOREVER; - m_advCompCB = nullptr; + *this = NimBLEAdvertising(); + return true; } // reset - /** * @brief Add a service uuid to exposed list of services. * @param [in] serviceUUID The UUID of the service to expose. */ -void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) { +void NimBLEAdvertising::addServiceUUID(const NimBLEUUID& serviceUUID) { m_serviceUUIDs.push_back(serviceUUID); m_advDataSet = false; } // addServiceUUID - /** * @brief Add a service uuid to exposed list of services. * @param [in] serviceUUID The string representation of the service to expose. @@ -98,111 +91,108 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) { m_advDataSet = false; } // addServiceUUID - /** - * @brief Remove a service UUID from the advertisment. + * @brief Remove a service UUID from the advertisement. * @param [in] serviceUUID The UUID of the service to remove. */ -void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) { - for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) { - if((*it) == serviceUUID) { - m_serviceUUIDs.erase(it); - break; +void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID& serviceUUID) { + std::vector sVec{}; + for (auto& uuid : m_serviceUUIDs) { + if (uuid == serviceUUID) { + continue; } + sVec.push_back(uuid); } - m_advDataSet = false; -} // addServiceUUID + sVec.swap(m_serviceUUIDs); + m_advDataSet = false; +} // removeServiceUUID /** - * @brief Remove all service UUIDs from the advertisment. + * @brief Remove all service UUIDs from the advertisement. */ void NimBLEAdvertising::removeServices() { std::vector().swap(m_serviceUUIDs); m_advDataSet = false; } // removeServices - /** * @brief Set the device appearance in the advertising data. * @param [in] appearance The appearance of the device in the advertising data. + * If the appearance value is 0 then the appearance will not be in the advertisement. */ void NimBLEAdvertising::setAppearance(uint16_t appearance) { - m_advData.appearance = appearance; - m_advData.appearance_is_present = 1; - m_advDataSet = false; + m_advData.appearance = appearance; + m_advData.appearance_is_present = appearance > 0; + m_advDataSet = false; } // setAppearance - /** * @brief Add the transmission power level to the advertisement packet. */ void NimBLEAdvertising::addTxPower() { - m_advData.tx_pwr_lvl_is_present = 1; - m_advDataSet = false; + m_advData.tx_pwr_lvl_is_present = true; + m_advData.tx_pwr_lvl = NimBLEDevice::getPower(); + m_advDataSet = false; } // addTxPower - /** * @brief Set the advertised name of the device. * @param [in] name The name to advertise. */ -void NimBLEAdvertising::setName(const std::string &name) { +void NimBLEAdvertising::setName(const std::string& name) { std::vector(name.begin(), name.end()).swap(m_name); - m_advData.name = &m_name[0]; - m_advData.name_len = m_name.size(); - m_advDataSet = false; + m_advData.name = &m_name[0]; + m_advData.name_len = m_name.size(); + m_advData.name_is_complete = true; + m_advDataSet = false; } // setName - /** * @brief Set the advertised manufacturer data. * @param [in] data The data to advertise. */ -void NimBLEAdvertising::setManufacturerData(const std::string &data) { +void NimBLEAdvertising::setManufacturerData(const std::string& data) { std::vector(data.begin(), data.end()).swap(m_mfgData); - m_advData.mfg_data = &m_mfgData[0]; + m_advData.mfg_data = &m_mfgData[0]; m_advData.mfg_data_len = m_mfgData.size(); - m_advDataSet = false; + m_advDataSet = false; } // setManufacturerData - /** * @brief Set the advertised manufacturer data. * @param [in] data The data to advertise. */ -void NimBLEAdvertising::setManufacturerData(const std::vector &data) { - m_mfgData = data; - m_advData.mfg_data = &m_mfgData[0]; +void NimBLEAdvertising::setManufacturerData(const std::vector& data) { + std::vector(data.begin(), data.end()).swap(m_mfgData); + m_advData.mfg_data = &m_mfgData[0]; m_advData.mfg_data_len = m_mfgData.size(); - m_advDataSet = false; + m_advDataSet = false; } // setManufacturerData - /** * @brief Set the advertised URI. * @param [in] uri The URI to advertise. */ -void NimBLEAdvertising::setURI(const std::string &uri) { +void NimBLEAdvertising::setURI(const std::string& uri) { std::vector(uri.begin(), uri.end()).swap(m_uri); - m_advData.uri = &m_uri[0]; + m_advData.uri = &m_uri[0]; m_advData.uri_len = m_uri.size(); - m_advDataSet = false; + m_advDataSet = false; } // setURI - /** * @brief Set the service data advertised for the UUID. * @param [in] uuid The UUID the service data belongs to. * @param [in] data The data to advertise. * @note If data length is 0 the service data will not be advertised. */ -void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string &data) { +void NimBLEAdvertising::setServiceData(const NimBLEUUID& uuid, const std::string& data) { switch (uuid.bitSize()) { case 16: { std::vector(uuid.getValue(), uuid.getValue() + 2).swap(m_svcData16); m_svcData16.insert(m_svcData16.end(), data.begin(), data.end()); - m_advData.svc_data_uuid16 = (uint8_t*)&m_svcData16[0]; + m_advData.svc_data_uuid16 = &m_svcData16[0]; m_advData.svc_data_uuid16_len = (data.length() > 0) ? m_svcData16.size() : 0; break; } @@ -210,7 +200,7 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string case 32: { std::vector(uuid.getValue(), uuid.getValue() + 4).swap(m_svcData32); m_svcData32.insert(m_svcData32.end(), data.begin(), data.end()); - m_advData.svc_data_uuid32 = (uint8_t*)&m_svcData32[0]; + m_advData.svc_data_uuid32 = &m_svcData32[0]; m_advData.svc_data_uuid32_len = (data.length() > 0) ? m_svcData32.size() : 0; break; } @@ -218,7 +208,7 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string case 128: { std::vector(uuid.getValue(), uuid.getValue() + 16).swap(m_svcData128); m_svcData128.insert(m_svcData128.end(), data.begin(), data.end()); - m_advData.svc_data_uuid128 = (uint8_t*)&m_svcData128[0]; + m_advData.svc_data_uuid128 = &m_svcData128[0]; m_advData.svc_data_uuid128_len = (data.length() > 0) ? m_svcData128.size() : 0; break; } @@ -230,535 +220,459 @@ void NimBLEAdvertising::setServiceData(const NimBLEUUID &uuid, const std::string m_advDataSet = false; } // setServiceData - /** - * @brief Set the type of advertisment to use. - * @param [in] adv_type: + * @brief Set the type of connectable mode to advertise. + * @param [in] mode The connectable mode: * * BLE_GAP_CONN_MODE_NON (0) - not connectable advertising * * BLE_GAP_CONN_MODE_DIR (1) - directed connectable advertising * * BLE_GAP_CONN_MODE_UND (2) - undirected connectable advertising */ -void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){ - m_advParams.conn_mode = adv_type; +void NimBLEAdvertising::setConnectableMode(uint8_t mode) { + if (mode > BLE_GAP_CONN_MODE_UND) { + NIMBLE_LOGE(LOG_TAG, "Invalid connectable mode: %u", mode); + return; + } + + m_advParams.conn_mode = mode; } // setAdvertisementType +/** + * @brief Set the discoverable mode to use. + * @param [in] mode The discoverable mode: + * * BLE_GAP_DISC_MODE_NON (0) - non-discoverable + * * BLE_GAP_DISC_MODE_LTD (1) - limited discoverable + * * BLE_GAP_DISC_MODE_GEN (2) - general discoverable + */ +void NimBLEAdvertising::setDiscoverableMode(uint8_t mode) { + switch (mode) { + case BLE_GAP_DISC_MODE_NON: + m_advData.flags = 0; + break; + case BLE_GAP_DISC_MODE_LTD: + m_advData.flags = (BLE_HS_ADV_F_DISC_LTD | BLE_HS_ADV_F_BREDR_UNSUP); + break; + case BLE_GAP_DISC_MODE_GEN: + m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); + break; + default: + NIMBLE_LOGE(LOG_TAG, "Invalid discoverable mode: %u", mode); + return; + } + + m_advParams.disc_mode = mode; +} // setDiscoverableMode + +/** + * @brief Set the advertising interval. + * @param [in] interval The advertising interval in 0.625ms units, 0 = use default. + */ +void NimBLEAdvertising::setAdvertisingInterval(uint16_t interval) { + m_advParams.itvl_min = interval; + m_advParams.itvl_max = interval; +} // setAdvertisingInterval /** * @brief Set the minimum advertising interval. - * @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default. + * @param [in] minInterval Minimum value for advertising interval in 0.625ms units, 0 = use default. */ -void NimBLEAdvertising::setMinInterval(uint16_t mininterval) { - m_advParams.itvl_min = mininterval; +void NimBLEAdvertising::setMinInterval(uint16_t minInterval) { + m_advParams.itvl_min = minInterval; } // setMinInterval - /** * @brief Set the maximum advertising interval. - * @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default. + * @param [in] maxInterval Maximum value for advertising interval in 0.625ms units, 0 = use default. */ -void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) { - m_advParams.itvl_max = maxinterval; +void NimBLEAdvertising::setMaxInterval(uint16_t maxInterval) { + m_advParams.itvl_max = maxInterval; } // setMaxInterval +/** + * @brief Set the preferred min and max connection intervals to advertise. + * @param [in] minInterval The minimum preferred connection interval. + * @param [in] maxInterval The Maximum preferred connection interval. + * @details Range = 0x0006(7.5ms) to 0x0C80(4000ms), values not within the range will be limited to this range. + */ +void NimBLEAdvertising::setPreferredConnectionInterval(uint16_t minInterval, uint16_t maxInterval) { + if (!minInterval) { + minInterval = *reinterpret_cast(m_advData.slave_itvl_range); + } + + if (!maxInterval) { + maxInterval = *reinterpret_cast(m_advData.slave_itvl_range + 2); + } + + minInterval = std::max(minInterval, 0x6); + minInterval = std::min(minInterval, 0xC80); + maxInterval = std::max(maxInterval, 0x6); + maxInterval = std::min(maxInterval, 0xC80); + maxInterval = std::max(maxInterval, minInterval); // Max must be greater than or equal to min. + + m_slaveItvl[0] = minInterval; + m_slaveItvl[1] = minInterval >> 8; + m_slaveItvl[2] = maxInterval; + m_slaveItvl[3] = maxInterval >> 8; + + m_advDataSet = false; +} // setPreferredConnectionInterval /** * @brief Set the advertised min connection interval preferred by this device. - * @param [in] mininterval the max interval value. Range = 0x0006 to 0x0C80. - * @details Values not within the range will cancel advertising of this data.\n - * Consumes 6 bytes of advertising space (combined with max interval). + * @param [in] minInterval the max interval value. + * @details Range = 0x0006 to 0x0C80, values not within the range will be limited to this range. */ -void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) { - // invalid paramters, set the slave interval to null - if(mininterval < 0x0006 || mininterval > 0x0C80) { - m_advData.slave_itvl_range = nullptr; - return; - } - - if(m_advData.slave_itvl_range == nullptr) { - m_advData.slave_itvl_range = m_slaveItvl; - } - - m_slaveItvl[0] = mininterval; - m_slaveItvl[1] = mininterval >> 8; - - uint16_t maxinterval = *(uint16_t*)(m_advData.slave_itvl_range+2); - - // If mininterval is higher than the maxinterval make them the same - if(mininterval > maxinterval) { - m_slaveItvl[2] = m_slaveItvl[0]; - m_slaveItvl[3] = m_slaveItvl[1]; - } - - m_advDataSet = false; +void NimBLEAdvertising::setMinPreferred(uint16_t minInterval) { + setPreferredConnectionInterval(minInterval, 0); } // setMinPreferred - /** * @brief Set the advertised max connection interval preferred by this device. - * @param [in] maxinterval the max interval value. Range = 0x0006 to 0x0C80. - * @details Values not within the range will cancel advertising of this data.\n - * Consumes 6 bytes of advertising space (combined with min interval). + * @param [in] maxInterval the max interval value. Range = 0x0006 to 0x0C80. + * @details Range = 0x0006 to 0x0C80, values not within the range will be limited to this range. */ -void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) { - // invalid paramters, set the slave interval to null - if(maxinterval < 0x0006 || maxinterval > 0x0C80) { - m_advData.slave_itvl_range = nullptr; - return; - } - if(m_advData.slave_itvl_range == nullptr) { - m_advData.slave_itvl_range = m_slaveItvl; - } - m_slaveItvl[2] = maxinterval; - m_slaveItvl[3] = maxinterval >> 8; - - uint16_t mininterval = *(uint16_t*)(m_advData.slave_itvl_range); - - // If mininterval is higher than the maxinterval make them the same - if(mininterval > maxinterval) { - m_slaveItvl[0] = m_slaveItvl[2]; - m_slaveItvl[1] = m_slaveItvl[3]; - } - - m_advDataSet = false; +void NimBLEAdvertising::setMaxPreferred(uint16_t maxInterval) { + setPreferredConnectionInterval(0, maxInterval); } // setMaxPreferred - /** * @brief Set if scan response is available. - * @param [in] set true = scan response available. + * @param [in] enable true if scan response should be available. */ -void NimBLEAdvertising::setScanResponse(bool set) { - m_scanResp = set; +void NimBLEAdvertising::setScanResponse(bool enable) { + m_scanResp = enable; m_advDataSet = false; } // setScanResponse - /** * @brief Set the filtering for the scan filter. * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. */ void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { - NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", - scanRequestWhitelistOnly, connectWhitelistOnly); if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE; - NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (scanRequestWhitelistOnly && !connectWhitelistOnly) { m_advParams.filter_policy = BLE_HCI_ADV_FILT_SCAN; - NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (!scanRequestWhitelistOnly && connectWhitelistOnly) { m_advParams.filter_policy = BLE_HCI_ADV_FILT_CONN; - NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (scanRequestWhitelistOnly && connectWhitelistOnly) { m_advParams.filter_policy = BLE_HCI_ADV_FILT_BOTH; - NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } } // setScanFilter - /** - * @brief Set the advertisement data that is to be published in a regular advertisement. - * @param [in] advertisementData The data to be advertised. + * @brief Set the advertisement data that is to be broadcast in a regular advertisement. + * @param [in] data The data to be broadcast. * @details The use of this function will replace any data set with addServiceUUID\n * or setAppearance. If you wish for these to be advertised you must include them\n * in the advertisementData parameter sent. */ - -void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisementData) { - NIMBLE_LOGD(LOG_TAG, ">> setAdvertisementData"); - int rc = ble_gap_adv_set_data( - (uint8_t*)advertisementData.getPayload().data(), - advertisementData.getPayload().length()); +void NimBLEAdvertising::setAdvertisementData(const NimBLEAdvertisementData& data) { + int rc = ble_gap_adv_set_data(&data.getPayload()[0], data.getPayload().size()); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", - rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); } - m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. - NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData"); + + m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. } // setAdvertisementData - /** - * @brief Set the advertisement data that is to be published in a scan response. - * @param [in] advertisementData The data to be advertised. + * @brief Set the data that is to be provided in a scan response. + * @param [in] data The data to be provided in the scan response * @details Calling this without also using setAdvertisementData will have no effect.\n * When using custom scan response data you must also use custom advertisement data. */ -void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertisementData) { - NIMBLE_LOGD(LOG_TAG, ">> setScanResponseData"); - int rc = ble_gap_adv_rsp_set_data( - (uint8_t*)advertisementData.getPayload().data(), - advertisementData.getPayload().length()); +void NimBLEAdvertising::setScanResponseData(const NimBLEAdvertisementData& data) { + int rc = ble_gap_adv_rsp_set_data(&data.getPayload()[0], data.getPayload().size()); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", - rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); } - m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. - NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData"); -} // setScanResponseData + m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. +} // setScanResponseData /** * @brief Start advertising. * @param [in] duration The duration, in milliseconds, to advertise, 0 == advertise forever. - * @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends. * @param [in] dirAddr The address of a peer to directly advertise to. * @return True if advertising started successfully. */ -bool NimBLEAdvertising::start(uint32_t duration, advCompleteCB_t advCompleteCB, NimBLEAddress* dirAddr) { - NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", - m_customAdvData, m_customScanResponseData); +bool NimBLEAdvertising::start(uint32_t duration, const NimBLEAddress* dirAddr) { + NIMBLE_LOGD(LOG_TAG, + ">> Advertising start: customAdvData: %d, customScanResponseData: %d", + m_customAdvData, + m_customScanResponseData); - // If Host is not synced we cannot start advertising. - if(!NimBLEDevice::m_synced) { - NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); + if (!NimBLEDevice::m_synced) { + NIMBLE_LOGE(LOG_TAG, "Host not synced!"); return false; } -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) - NimBLEServer* pServer = NimBLEDevice::getServer(); - if(pServer != nullptr) { - if(!pServer->m_gattsStarted){ - pServer->start(); - } else if(pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) { - NIMBLE_LOGE(LOG_TAG, "Max connections reached - not advertising"); - return false; - } - } -#endif - - // If already advertising just return - if(ble_gap_adv_active()) { + if (ble_gap_adv_active()) { NIMBLE_LOGW(LOG_TAG, "Advertising already active"); return true; } +# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) + NimBLEServer* pServer = NimBLEDevice::getServer(); + if (pServer != nullptr) { + if (!pServer->m_gattsStarted) { + pServer->start(); + } else if (pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) { + NIMBLE_LOGE(LOG_TAG, "Unable to advertise; Max connections reached"); + return false; + } + } +# endif + + int rc = 0; // Save the duration incase of host reset so we can restart with the same params m_duration = duration; - - if(duration == 0){ + if (duration == 0) { duration = BLE_HS_FOREVER; } - m_advCompCB = advCompleteCB; - - m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; - m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); - if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) { - if(!m_scanResp) { - m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON; - // non-connectable advertising does not require AD flags. - m_advData.flags = 0; - } + if (m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) { + m_advData.flags = 0; // non-connectable advertising does not require AD flags. } - int rc = 0; - if (!m_customAdvData && !m_advDataSet) { - //start with 3 bytes for the flags data if required + // start with 3 bytes for the flags data if required uint8_t payloadLen = (m_advData.flags > 0) ? (2 + 1) : 0; - if(m_advData.mfg_data_len > 0) + if (m_advData.mfg_data_len > 0) { payloadLen += (2 + m_advData.mfg_data_len); + } - if(m_advData.svc_data_uuid16_len > 0) + if (m_advData.svc_data_uuid16_len > 0) { payloadLen += (2 + m_advData.svc_data_uuid16_len); + } - if(m_advData.svc_data_uuid32_len > 0) + if (m_advData.svc_data_uuid32_len > 0) { payloadLen += (2 + m_advData.svc_data_uuid32_len); + } - if(m_advData.svc_data_uuid128_len > 0) + if (m_advData.svc_data_uuid128_len > 0) { payloadLen += (2 + m_advData.svc_data_uuid128_len); + } - if(m_advData.uri_len > 0) + if (m_advData.uri_len > 0) { payloadLen += (2 + m_advData.uri_len); + } - if(m_advData.appearance_is_present) + if (m_advData.appearance_is_present) { payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN); + } - if(m_advData.tx_pwr_lvl_is_present) + if (m_advData.tx_pwr_lvl_is_present) { payloadLen += (2 + BLE_HS_ADV_TX_PWR_LVL_LEN); + } - if(m_advData.slave_itvl_range != nullptr) + if (m_advData.slave_itvl_range != nullptr) { payloadLen += (2 + BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); + } - for(auto &it : m_serviceUUIDs) { - if(it.bitSize() == BLE_UUID_TYPE_16) { - int add = (m_advData.num_uuids16 > 0) ? 2 : 4; - if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ + for (const auto& uuid : m_serviceUUIDs) { + int payloadAdd = 0; + if (uuid.bitSize() == BLE_UUID_TYPE_16) { + payloadAdd = (m_advData.num_uuids16 > 0) ? 2 : 4; // 2 bytes more if this is the first of the type + if ((payloadLen + payloadAdd) > BLE_HS_ADV_MAX_SZ) { m_advData.uuids16_is_complete = 0; continue; } - payloadLen += add; - if(nullptr == (m_advData.uuids16 = reinterpret_cast(realloc((void*)m_advData.uuids16, - (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))) - { + m_advData.uuids16 = reinterpret_cast( + realloc((void*)m_advData.uuids16, (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))); + if (!m_advData.uuids16) { NIMBLE_LOGE(LOG_TAG, "Error, no mem"); return false; } - memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16], - reinterpret_cast(&it), sizeof(ble_uuid16_t)); + + memcpy((void*)&m_advData.uuids16[m_advData.num_uuids16], uuid.getBase(), sizeof(ble_uuid16_t)); m_advData.uuids16_is_complete = 1; m_advData.num_uuids16++; - } else if(it.bitSize() == BLE_UUID_TYPE_32) { - int add = (m_advData.num_uuids32 > 0) ? 4 : 6; - if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ + } else if (uuid.bitSize() == BLE_UUID_TYPE_32) { + payloadAdd = (m_advData.num_uuids32 > 0) ? 4 : 6; // 2 bytes more if this is the first of the type + if ((payloadLen + payloadAdd) > BLE_HS_ADV_MAX_SZ) { m_advData.uuids32_is_complete = 0; continue; } - payloadLen += add; - if(nullptr == (m_advData.uuids32 = reinterpret_cast(realloc((void*)m_advData.uuids32, - (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))) - { + m_advData.uuids32 = reinterpret_cast( + realloc((void*)m_advData.uuids32, (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))); + if (!m_advData.uuids32) { NIMBLE_LOGE(LOG_TAG, "Error, no mem"); return false; } - memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32], - reinterpret_cast(&it), sizeof(ble_uuid32_t)); + + memcpy((void*)&m_advData.uuids32[m_advData.num_uuids32], uuid.getBase(), sizeof(ble_uuid32_t)); m_advData.uuids32_is_complete = 1; m_advData.num_uuids32++; - } else if(it.bitSize() == BLE_UUID_TYPE_128){ - int add = (m_advData.num_uuids128 > 0) ? 16 : 18; - if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){ + } else if (uuid.bitSize() == BLE_UUID_TYPE_128) { + payloadAdd = (m_advData.num_uuids128 > 0) ? 16 : 18; // 2 bytes more if this is the first of the type + if ((payloadLen + payloadAdd) > BLE_HS_ADV_MAX_SZ) { m_advData.uuids128_is_complete = 0; continue; } - payloadLen += add; - if(nullptr == (m_advData.uuids128 = reinterpret_cast(realloc((void*)m_advData.uuids128, - (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))) - { + m_advData.uuids128 = reinterpret_cast( + realloc((void*)m_advData.uuids128, (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))); + if (!m_advData.uuids128) { NIMBLE_LOGE(LOG_TAG, "Error, no mem"); return false; } - memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128], - reinterpret_cast(&it), sizeof(ble_uuid128_t)); + + memcpy((void*)&m_advData.uuids128[m_advData.num_uuids128], uuid.getBase(), sizeof(ble_uuid128_t)); m_advData.uuids128_is_complete = 1; m_advData.num_uuids128++; } + + payloadLen += payloadAdd; } // check if there is room for the name, if not put it in scan data - if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) { - if(m_scanResp && !m_customScanResponseData){ - m_scanData.name = m_advData.name; + if (m_advData.name_len && (payloadLen + 2 + m_advData.name_len) > BLE_HS_ADV_MAX_SZ) { + if (m_scanResp && !m_customScanResponseData) { + m_scanData.name = m_advData.name; m_scanData.name_len = m_advData.name_len; - if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) { - m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2; + if (m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) { + m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2; m_scanData.name_is_complete = 0; } else { m_scanData.name_is_complete = 1; } - m_advData.name = nullptr; - m_advData.name_len = 0; + m_advData.name = nullptr; + m_advData.name_len = 0; m_advData.name_is_complete = 0; } else { - if(m_advData.tx_pwr_lvl_is_present) { - m_advData.tx_pwr_lvl_is_present = 0; - payloadLen -= (2 + 1); - } - // if not using scan response just cut the name down - // leaving 2 bytes for the data specifier. - if(m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) { - m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2); + // if not using scan response truncate the name + if (m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) { + m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2); m_advData.name_is_complete = 0; } } } - if(m_scanResp && !m_customScanResponseData) { + rc = ble_gap_adv_set_fields(&m_advData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + } + + if (m_scanResp && !m_customScanResponseData) { rc = ble_gap_adv_rsp_set_fields(&m_scanData); - switch(rc) { - case 0: - break; - - case BLE_HS_EBUSY: - NIMBLE_LOGE(LOG_TAG, "Already advertising"); - break; - - case BLE_HS_EMSGSIZE: - NIMBLE_LOGE(LOG_TAG, "Scan data too long"); - break; - - default: - NIMBLE_LOGE(LOG_TAG, "Error setting scan response data; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - break; + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Error setting scan response data rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); } } - if(rc == 0) { - rc = ble_gap_adv_set_fields(&m_advData); - switch(rc) { - case 0: - break; - - case BLE_HS_EBUSY: - NIMBLE_LOGE(LOG_TAG, "Already advertising"); - break; - - case BLE_HS_EMSGSIZE: - NIMBLE_LOGE(LOG_TAG, "Advertisement data too long"); - break; - - default: - NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - break; - } - } - - if(m_advData.num_uuids128 > 0) { + // free the memory used for the UUIDs as they are now copied into the advertisement data. + if (m_advData.num_uuids128 > 0) { free((void*)m_advData.uuids128); - m_advData.uuids128 = nullptr; + m_advData.uuids128 = nullptr; m_advData.num_uuids128 = 0; } - if(m_advData.num_uuids32 > 0) { + if (m_advData.num_uuids32 > 0) { free((void*)m_advData.uuids32); - m_advData.uuids32 = nullptr; + m_advData.uuids32 = nullptr; m_advData.num_uuids32 = 0; } - if(m_advData.num_uuids16 > 0) { + if (m_advData.num_uuids16 > 0) { free((void*)m_advData.uuids16); - m_advData.uuids16 = nullptr; + m_advData.uuids16 = nullptr; m_advData.num_uuids16 = 0; } - if(rc !=0) { + if (rc != 0) { return false; } m_advDataSet = true; } -#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) +# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType, (dirAddr != nullptr) ? dirAddr->getBase() : NULL, duration, &m_advParams, - (pServer != nullptr) ? NimBLEServer::handleGapEvent : - NimBLEAdvertising::handleGapEvent, - (void*)this); -#else - rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType, - (dirAddr != nullptr) ? &peerAddr : NULL, - duration, - &m_advParams, - NimBLEAdvertising::handleGapEvent, - (void*)this); -#endif - switch(rc) { - case 0: - break; - - case BLE_HS_EALREADY: - NIMBLE_LOGI(LOG_TAG, "Advertisement Already active"); - break; - - case BLE_HS_EINVAL: - NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long"); - break; - - case BLE_HS_EPREEMPTED: - NIMBLE_LOGE(LOG_TAG, "Unable to advertise - busy"); - break; - - case BLE_HS_ETIMEOUT_HCI: - case BLE_HS_EOS: - case BLE_HS_ECONTROLLER: - case BLE_HS_ENOTSYNCED: - NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset"); - break; - - default: - NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s", - rc, NimBLEUtils::returnCodeToString(rc)); - break; + (pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent, + this); +# else + rc = ble_gap_adv_start(NimBLEDevice::m_ownAddrType, NULL, duration, &m_advParams, NimBLEAdvertising::handleGapEvent, this); +# endif + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + return false; } NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); - return (rc == 0 || rc == BLE_HS_EALREADY); + return true; } // start - /** * @brief Stop advertising. * @return True if advertising stopped successfully. */ bool NimBLEAdvertising::stop() { - NIMBLE_LOGD(LOG_TAG, ">> stop"); - int rc = ble_gap_adv_stop(); if (rc != 0 && rc != BLE_HS_EALREADY) { - NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", - rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; } - NIMBLE_LOGD(LOG_TAG, "<< stop"); return true; } // stop - /** - * @brief Handles the callback when advertising stops. + * @brief Set the callback to be invoked when advertising stops. + * @param [in] callback The callback to be invoked when advertising stops. */ -void NimBLEAdvertising::advCompleteCB() { - if(m_advCompCB != nullptr) { - m_advCompCB(this); - } -} // advCompleteCB - +void NimBLEAdvertising::setAdvertisingCompleteCallback(advCompleteCB_t callback) { + m_advCompCb = callback; +} // setAdvertisingCompleteCallback /** * @brief Check if currently advertising. - * @return true if advertising is active. + * @return True if advertising is active. */ bool NimBLEAdvertising::isAdvertising() { return ble_gap_adv_active(); } // isAdvertising - /* * Host reset seems to clear advertising data, * we need clear the flag so it reloads it. */ void NimBLEAdvertising::onHostSync() { - NIMBLE_LOGD(LOG_TAG, "Host re-synced"); - m_advDataSet = false; // If we were advertising forever, restart it now - if(m_duration == 0) { - start(m_duration, m_advCompCB); + if (m_duration == 0) { + start(m_duration); } else { - // Otherwise we should tell the app that advertising stopped. - advCompleteCB(); + // Otherwise we should tell the app that advertising stopped. + if (m_advCompCb != nullptr) { + m_advCompCb(this); + } } } // onHostSync - /** * @brief Handler for gap events when not using peripheral role. * @param [in] event the event data. * @param [in] arg pointer to the advertising instance. */ -/*STATIC*/ -int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) { - NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg; +int NimBLEAdvertising::handleGapEvent(struct ble_gap_event* event, void* arg) { + NimBLEAdvertising* pAdv = (NimBLEAdvertising*)arg; - if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) { - switch(event->adv_complete.reason) { + if (event->type == BLE_GAP_EVENT_ADV_COMPLETE) { + switch (event->adv_complete.reason) { // Don't call the callback if host reset, we want to // preserve the active flag until re-sync to restart advertising. case BLE_HS_ETIMEOUT_HCI: @@ -771,47 +685,53 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) { default: break; } - pAdv->advCompleteCB(); + + if (pAdv->m_advCompCb != nullptr) { + pAdv->m_advCompCb(pAdv); + } } return 0; -} +} // handleGapEvent +/* -------------------------------------------------------------------------- */ +/* Advertisement Data */ +/* -------------------------------------------------------------------------- */ /** * @brief Add data to the payload to be advertised. * @param [in] data The data to be added to the payload. */ -void NimBLEAdvertisementData::addData(const std::string &data) { - if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) { +void NimBLEAdvertisementData::addData(const std::vector& data) { + if ((m_payload.size() + data.size()) > BLE_HS_ADV_MAX_SZ) { NIMBLE_LOGE(LOG_TAG, "Advertisement data length exceeded"); return; } - m_payload.append(data); -} // addData + m_payload.insert(m_payload.end(), data.begin(), data.end()); +} // addData /** * @brief Add data to the payload to be advertised. * @param [in] data The data to be added to the payload. * @param [in] length The size of data to be added to the payload. */ -void NimBLEAdvertisementData::addData(char * data, size_t length) { - addData(std::string(data, length)); +void NimBLEAdvertisementData::addData(uint8_t* data, size_t length) { + addData(std::vector(data, data + length)); } // addData - /** * @brief Set the appearance. * @param [in] appearance The appearance code value. */ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { - char cdata[2]; - cdata[0] = 3; - cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19 - addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); + uint8_t data[4]; + data[0] = 3; + data[1] = BLE_HS_ADV_TYPE_APPEARANCE; + data[2] = appearance; + data[3] = (appearance >> 8) & 0xFF; + addData(data, 4); } // setAppearance - /** * @brief Set the advertisement flags. * @param [in] flag The flags to be set in the advertisement. @@ -820,248 +740,228 @@ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { * * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE */ void NimBLEAdvertisementData::setFlags(uint8_t flag) { - char cdata[3]; - cdata[0] = 2; - cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01 - cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; - addData(std::string(cdata, 3)); + uint8_t data[3]; + data[0] = 2; + data[1] = BLE_HS_ADV_TYPE_FLAGS; + data[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; + addData(data, 3); } // setFlag +/** + * @brief Set manufacturer specific data. + * @param [in] data The manufacturer data to advertise. + */ +void NimBLEAdvertisementData::setManufacturerData(const std::string& data) { + std::vector mdata; + mdata.push_back(data.length() + 1); + mdata.push_back(BLE_HS_ADV_TYPE_MFG_DATA); + mdata.insert(mdata.end(), data.begin(), data.end()); + addData(mdata); +} // setManufacturerData /** * @brief Set manufacturer specific data. * @param [in] data The manufacturer data to advertise. */ -void NimBLEAdvertisementData::setManufacturerData(const std::string &data) { - char cdata[2]; - cdata[0] = data.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff - addData(std::string(cdata, 2) + data); +void NimBLEAdvertisementData::setManufacturerData(const std::vector& data) { + std::vector mdata; + mdata.push_back(data.size() + 1); + mdata.push_back(BLE_HS_ADV_TYPE_MFG_DATA); + mdata.insert(mdata.end(), data.begin(), data.end()); + addData(mdata); } // setManufacturerData - -/** - * @brief Set manufacturer specific data. - * @param [in] data The manufacturer data to advertise. - */ -void NimBLEAdvertisementData::setManufacturerData(const std::vector &data) { - char cdata[2]; - cdata[0] = data.size() + 1; - cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff - addData(std::string(cdata, 2) + std::string((char*)&data[0], data.size())); -} // setManufacturerData - - /** * @brief Set the URI to advertise. * @param [in] uri The uri to advertise. */ -void NimBLEAdvertisementData::setURI(const std::string &uri) { - char cdata[2]; - cdata[0] = uri.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_URI; - addData(std::string(cdata, 2) + uri); +void NimBLEAdvertisementData::setURI(const std::string& uri) { + std::vector data; + data.push_back(uri.length() + 1); + data.push_back(BLE_HS_ADV_TYPE_URI); + data.insert(data.end(), uri.begin(), uri.end()); + addData(data); } // setURI - /** * @brief Set the complete name of this device. * @param [in] name The name to advertise. + * @param [in] isComplete If true the name is complete, otherwise it is shortened. */ -void NimBLEAdvertisementData::setName(const std::string &name) { - char cdata[2]; - cdata[0] = name.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09 - addData(std::string(cdata, 2) + name); +void NimBLEAdvertisementData::setName(const std::string& name, bool isComplete) { + std::vector data; + data.push_back(name.length() + 1); + data.push_back(isComplete ? BLE_HS_ADV_TYPE_COMP_NAME : BLE_HS_ADV_TYPE_INCOMP_NAME); + data.insert(data.end(), name.begin(), name.end()); + addData(data); } // setName +/** + * @brief Set the short name. + * @param [in] name The short name of the device. + */ +void NimBLEAdvertisementData::setShortName(const std::string& name) { + setName(name, false); +} // setShortName /** * @brief Set a single service to advertise as a complete list of services. * @param [in] uuid The service to advertise. */ -void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) { +void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID& uuid) { setServices(true, uuid.bitSize(), {uuid}); } // setCompleteServices - /** * @brief Set the complete list of 16 bit services to advertise. - * @param [in] v_uuid A vector of 16 bit UUID's to advertise. + * @param [in] uuids A vector of 16 bit UUID's to advertise. */ -void NimBLEAdvertisementData::setCompleteServices16(const std::vector& v_uuid) { - setServices(true, 16, v_uuid); +void NimBLEAdvertisementData::setCompleteServices16(const std::vector& uuids) { + setServices(true, 16, uuids); } // setCompleteServices16 - /** * @brief Set the complete list of 32 bit services to advertise. - * @param [in] v_uuid A vector of 32 bit UUID's to advertise. + * @param [in] uuids A vector of 32 bit UUID's to advertise. */ -void NimBLEAdvertisementData::setCompleteServices32(const std::vector& v_uuid) { - setServices(true, 32, v_uuid); +void NimBLEAdvertisementData::setCompleteServices32(const std::vector& uuids) { + setServices(true, 32, uuids); } // setCompleteServices32 - /** * @brief Set a single service to advertise as a partial list of services. * @param [in] uuid The service to advertise. */ -void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) { +void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID& uuid) { setServices(false, uuid.bitSize(), {uuid}); } // setPartialServices - /** * @brief Set the partial list of services to advertise. - * @param [in] v_uuid A vector of 16 bit UUID's to advertise. + * @param [in] uuids A vector of 16 bit UUID's to advertise. */ -void NimBLEAdvertisementData::setPartialServices16(const std::vector& v_uuid) { - setServices(false, 16, v_uuid); +void NimBLEAdvertisementData::setPartialServices16(const std::vector& uuids) { + setServices(false, 16, uuids); } // setPartialServices16 - /** * @brief Set the partial list of services to advertise. - * @param [in] v_uuid A vector of 32 bit UUID's to advertise. + * @param [in] uuids A vector of 32 bit UUID's to advertise. */ -void NimBLEAdvertisementData::setPartialServices32(const std::vector& v_uuid) { - setServices(false, 32, v_uuid); +void NimBLEAdvertisementData::setPartialServices32(const std::vector& uuids) { + setServices(false, 32, uuids); } // setPartialServices32 - /** * @brief Utility function to create the list of service UUID's from a vector. * @param [in] complete If true the vector is the complete set of services. * @param [in] size The bit size of the UUID's in the vector. (16, 32, or 128). - * @param [in] v_uuid The vector of service UUID's to advertise. + * @param [in] uuids The vector of service UUID's to advertise. */ -void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t size, - const std::vector &v_uuid) -{ - char cdata[2]; - cdata[0] = (size / 8) * v_uuid.size() + 1; - switch(size) { +void NimBLEAdvertisementData::setServices(const bool complete, const uint8_t size, const std::vector& uuids) { + std::vector uuidData; + uuidData.push_back((size / 8) * uuids.size() + 1); + switch (size) { case 16: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16; + uuidData.push_back(complete ? BLE_HS_ADV_TYPE_COMP_UUIDS16 : BLE_HS_ADV_TYPE_INCOMP_UUIDS16); break; case 32: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32; + uuidData.push_back(complete ? BLE_HS_ADV_TYPE_COMP_UUIDS32 : BLE_HS_ADV_TYPE_INCOMP_UUIDS32); break; case 128: - cdata[1] = complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128; + uuidData.push_back(complete ? BLE_HS_ADV_TYPE_COMP_UUIDS128 : BLE_HS_ADV_TYPE_INCOMP_UUIDS128); break; default: return; } - std::string uuids; - - for(auto &it : v_uuid){ - if(it.bitSize() != size) { + for (const auto& uuid : uuids) { + if (uuid.bitSize() != size) { NIMBLE_LOGE(LOG_TAG, "Service UUID(%d) invalid", size); continue; } else { - uuids += std::string(reinterpret_cast(it.getValue()), size / 8); + uuidData.insert(uuidData.end(), uuid.getValue(), uuid.getValue() + (size / 8)); } } - addData(std::string(cdata, 2) + uuids); + addData(uuidData); } // setServices - /** * @brief Set the service data (UUID + data) * @param [in] uuid The UUID to set with the service data. * @param [in] data The data to be associated with the service data advertised. */ -void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::string &data) { - uint8_t size = uuid.bitSize() / 8; - char cdata[2] = {static_cast(1 + size + data.length()), BLE_HS_ADV_TYPE_SVC_DATA_UUID16}; +void NimBLEAdvertisementData::setServiceData(const NimBLEUUID& uuid, const std::string& data) { + uint8_t size = uuid.bitSize() / 8; + std::vector serviceData; + serviceData.push_back(1 + size + data.length()); switch (size) { case 2: { - // [Len] [0x16] [UUID16] data + serviceData.push_back(BLE_HS_ADV_TYPE_SVC_DATA_UUID16); break; } case 16: { - // [Len] [0x21] [UUID128] data - cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21 + serviceData.push_back(BLE_HS_ADV_TYPE_SVC_DATA_UUID128); break; } - case 4: { - // [Len] [0x20] [UUID32] data - cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20 + serviceData.push_back(BLE_HS_ADV_TYPE_SVC_DATA_UUID32); break; } - default: return; } - addData(std::string(cdata, 2) + std::string(reinterpret_cast(uuid.getValue()), size) + data); + serviceData.insert(serviceData.end(), uuid.getValue(), uuid.getValue() + size); + serviceData.insert(serviceData.end(), data.begin(), data.end()); + addData(serviceData); } // setServiceData - -/** - * @brief Set the short name. - * @param [in] name The short name of the device. - */ -void NimBLEAdvertisementData::setShortName(const std::string &name) { - char cdata[2]; - cdata[0] = name.length() + 1; - cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08 - addData(std::string(cdata, 2) + name); -} // setShortName - - /** * @brief Adds Tx power level to the advertisement data. */ void NimBLEAdvertisementData::addTxPower() { - char cdata[3]; - cdata[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1; - cdata[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL; -#ifndef CONFIG_IDF_TARGET_ESP32P4 - cdata[2] = NimBLEDevice::getPower(); -#else - cdata[2] = 0; -#endif - addData(cdata, 3); + uint8_t data[3]; + data[0] = BLE_HS_ADV_TX_PWR_LVL_LEN + 1; + data[1] = BLE_HS_ADV_TYPE_TX_PWR_LVL; +# ifndef CONFIG_IDF_TARGET_ESP32P4 + data[2] = NimBLEDevice::getPower(); +# else + data[2] = 0; +# endif + addData(data, 3); } // addTxPower - /** * @brief Set the preferred connection interval parameters. * @param [in] min The minimum interval desired. * @param [in] max The maximum interval desired. */ void NimBLEAdvertisementData::setPreferredParams(uint16_t min, uint16_t max) { - char cdata[6]; - cdata[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1; - cdata[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE; - cdata[2] = min; - cdata[3] = min >> 8; - cdata[4] = max; - cdata[5] = max >> 8; - addData(cdata, 6); + uint8_t data[6]; + data[0] = BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN + 1; + data[1] = BLE_HS_ADV_TYPE_SLAVE_ITVL_RANGE; + data[2] = min; + data[3] = min >> 8; + data[4] = max; + data[5] = max >> 8; + addData(data, 6); } // setPreferredParams - /** * @brief Retrieve the payload that is to be advertised. * @return The payload that is to be advertised. */ -std::string NimBLEAdvertisementData::getPayload() { +std::vector NimBLEAdvertisementData::getPayload() const { return m_payload; } // getPayload - /** * @brief Clear the advertisement data for reuse. */ void NimBLEAdvertisementData::clearData() { - m_payload.clear(); -} + std::vector().swap(m_payload); +} // clearData #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */ diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index dc36d07..8b54cfb 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -12,38 +12,30 @@ * Author: kolban */ -#ifndef MAIN_BLEADVERTISING_H_ -#define MAIN_BLEADVERTISING_H_ -#include "nimconfig.h" -#if (defined(CONFIG_BT_ENABLED) && \ - defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && \ - !CONFIG_BT_NIMBLE_EXT_ADV) || defined(_DOXYGEN_) +#ifndef NIMBLE_CPP_ADVERTISING_H_ +#define NIMBLE_CPP_ADVERTISING_H_ -#if defined(CONFIG_NIMBLE_CPP_IDF) -#include "host/ble_gap.h" -#else -#include "nimble/nimble/host/include/host/ble_gap.h" -#endif +#include "nimconfig.h" +#if (defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER) && !CONFIG_BT_NIMBLE_EXT_ADV) || \ + defined(_DOXYGEN_) + +# if defined(CONFIG_NIMBLE_CPP_IDF) +# include "host/ble_gap.h" +# else +# include "nimble/nimble/host/include/host/ble_gap.h" +# endif /**** FIX COMPILATION ****/ -#undef min -#undef max +# undef min +# undef max /**************************/ -#include "NimBLEUUID.h" -#include "NimBLEAddress.h" +# include "NimBLEUUID.h" +# include "NimBLEAddress.h" -#include -#include - -/* COMPATIBILITY - DO NOT USE */ -#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0) -#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1) -#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2) -#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) -#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4) -#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) - /* ************************* */ +# include +# include +# include class NimBLEAdvertising; @@ -56,35 +48,34 @@ class NimBLEAdvertisementData { // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will // be exposed on demand/request or as time permits. // -public: + public: void setAppearance(uint16_t appearance); - void setCompleteServices(const NimBLEUUID &uuid); - void setCompleteServices16(const std::vector &v_uuid); - void setCompleteServices32(const std::vector &v_uuid); + void setCompleteServices(const NimBLEUUID& uuid); + void setCompleteServices16(const std::vector& uuids); + void setCompleteServices32(const std::vector& uuids); void setFlags(uint8_t); - void setManufacturerData(const std::string &data); - void setManufacturerData(const std::vector &data); - void setURI(const std::string &uri); - void setName(const std::string &name); - void setPartialServices(const NimBLEUUID &uuid); - void setPartialServices16(const std::vector &v_uuid); - void setPartialServices32(const std::vector &v_uuid); - void setServiceData(const NimBLEUUID &uuid, const std::string &data); - void setShortName(const std::string &name); - void addData(const std::string &data); // Add data to the payload. - void addData(char * data, size_t length); + void setManufacturerData(const std::string& data); + void setManufacturerData(const std::vector& data); + void setURI(const std::string& uri); + void setName(const std::string& name, bool isComplete = true); + void setPartialServices(const NimBLEUUID& uuid); + void setPartialServices16(const std::vector& uuids); + void setPartialServices32(const std::vector& uuids); + void setServiceData(const NimBLEUUID& uuid, const std::string& data); + void setShortName(const std::string& name); + void addData(const std::vector& data); + void addData(uint8_t* data, size_t length); void addTxPower(); void setPreferredParams(uint16_t min, uint16_t max); - std::string getPayload(); // Retrieve the current advert payload. - void clearData(); // Clear the advertisement data. + void clearData(); -private: + std::vector getPayload() const; + + private: friend class NimBLEAdvertising; - void setServices(const bool complete, const uint8_t size, - const std::vector &v_uuid); - std::string m_payload; // The payload of the advertisement. -}; // NimBLEAdvertisementData - + void setServices(const bool complete, const uint8_t size, const std::vector& v_uuid); + std::vector m_payload; // The payload of the advertisement. +}; // NimBLEAdvertisementData /** * @brief Perform and manage %BLE advertising. @@ -92,40 +83,43 @@ private: * A %BLE server will want to perform advertising in order to make itself known to %BLE clients. */ class NimBLEAdvertising { -public: + public: NimBLEAdvertising(); - void addServiceUUID(const NimBLEUUID &serviceUUID); + void addServiceUUID(const NimBLEUUID& serviceUUID); void addServiceUUID(const char* serviceUUID); - void removeServiceUUID(const NimBLEUUID &serviceUUID); - bool start(uint32_t duration = 0, advCompleteCB_t advCompleteCB = nullptr, NimBLEAddress* dirAddr = nullptr); + void removeServiceUUID(const NimBLEUUID& serviceUUID); + bool start(uint32_t duration = 0, const NimBLEAddress* dirAddr = nullptr); + void setAdvertisingCompleteCallback(advCompleteCB_t callback); void removeServices(); bool stop(); void setAppearance(uint16_t appearance); - void setName(const std::string &name); - void setManufacturerData(const std::string &data); - void setManufacturerData(const std::vector &data); - void setURI(const std::string &uri); - void setServiceData(const NimBLEUUID &uuid, const std::string &data); - void setAdvertisementType(uint8_t adv_type); - void setMaxInterval(uint16_t maxinterval); - void setMinInterval(uint16_t mininterval); - void setAdvertisementData(NimBLEAdvertisementData& advertisementData); + void setName(const std::string& name); + void setManufacturerData(const std::string& data); + void setManufacturerData(const std::vector& data); + void setURI(const std::string& uri); + void setServiceData(const NimBLEUUID& uuid, const std::string& data); + void setConnectableMode(uint8_t mode); + void setDiscoverableMode(uint8_t mode); + void setAdvertisingInterval(uint16_t interval); + void setMaxInterval(uint16_t maxInterval); + void setMinInterval(uint16_t minInterval); + void setAdvertisementData(const NimBLEAdvertisementData& advertisementData); void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly); - void setScanResponseData(NimBLEAdvertisementData& advertisementData); - void setScanResponse(bool); - void setMinPreferred(uint16_t); - void setMaxPreferred(uint16_t); + void setScanResponseData(const NimBLEAdvertisementData& advertisementData); + void setScanResponse(bool enable); + void setPreferredConnectionInterval(uint16_t minInterval, uint16_t maxInterval); + void setMinPreferred(uint16_t min); + void setMaxPreferred(uint16_t max); void addTxPower(); - void reset(); - void advCompleteCB(); + bool reset(); bool isAdvertising(); -private: + private: friend class NimBLEDevice; friend class NimBLEServer; - void onHostSync(); - static int handleGapEvent(struct ble_gap_event *event, void *arg); + void onHostSync(); + static int handleGapEvent(ble_gap_event* event, void* arg); ble_hs_adv_fields m_advData; ble_hs_adv_fields m_scanData; @@ -135,7 +129,7 @@ private: bool m_customScanResponseData; bool m_scanResp; bool m_advDataSet; - advCompleteCB_t m_advCompCB{nullptr}; + advCompleteCB_t m_advCompCb; uint8_t m_slaveItvl[4]; uint32_t m_duration; std::vector m_svcData16; @@ -147,4 +141,4 @@ private: }; #endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_BROADCASTER && !CONFIG_BT_NIMBLE_EXT_ADV */ -#endif /* MAIN_BLEADVERTISING_H_ */ +#endif /* NIMBLE_CPP_ADVERTISING_H_ */