/* * NimBLEAdvertisedDevice.cpp * * Created: on Jan 24 2020 * Author H2zero * * Originally: * * BLEAdvertisedDevice.cpp * * Created on: Jul 3, 2017 * Author: kolban */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) #include "nimconfig.h" #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER) #include "NimBLEAdvertisedDevice.h" #include "NimBLEUtils.h" #include "NimBLELog.h" static const char* LOG_TAG = "NimBLEAdvertisedDevice"; /** * @brief Constructor */ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() { m_advType = 0; m_appearance = 0; m_deviceType = 0; m_manufacturerData = ""; m_name = ""; m_rssi = -9999; m_serviceData = ""; m_txPower = 0; m_payloadLength = 0; m_pScan = nullptr; m_payloadLength = 0; m_payload = nullptr; m_haveAppearance = false; m_haveManufacturerData = false; m_haveName = false; m_haveRSSI = false; m_haveServiceData = false; m_haveServiceUUID = false; m_haveTXPower = false; m_callbackSent = false; } // NimBLEAdvertisedDevice /** * @brief Get the address of the advertising device. * @return The address of the advertised device. */ NimBLEAddress NimBLEAdvertisedDevice::getAddress() { return m_address; } // getAddress /** * @brief Get the advertisement type. * @return The advertising type the device is reporting: * * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle * * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response * * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle */ uint8_t NimBLEAdvertisedDevice::getAdvType() { return m_advType; } // getAddress /** * @brief Get the appearance. * * A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user * typcially in the form of an icon. * * @return The appearance of the advertised device. */ uint16_t NimBLEAdvertisedDevice::getAppearance() { return m_appearance; } // getAppearance /** * @brief Get the manufacturer data. * @return The manufacturer data of the advertised device. */ std::string NimBLEAdvertisedDevice::getManufacturerData() { return m_manufacturerData; } // getManufacturerData /** * @brief Get the advertised name. * @return The name of the advertised device. */ std::string NimBLEAdvertisedDevice::getName() { return m_name; } // getName /** * @brief Get the RSSI. * @return The RSSI of the advertised device. */ int NimBLEAdvertisedDevice::getRSSI() { return m_rssi; } // getRSSI /** * @brief Get the scan object that created this advertised device. * @return The scan object. */ NimBLEScan* NimBLEAdvertisedDevice::getScan() { return m_pScan; } // getScan /** * @brief Get the service data. * @return The ServiceData of the advertised device. */ std::string NimBLEAdvertisedDevice::getServiceData() { return m_serviceData; } //getServiceData /** * @brief Get the advertised service UUID. * @return The advertise service UUID. */ NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID() { return m_serviceDataUUID; } // getServiceDataUUID /** * @brief Get the Service UUID. * @return The Service UUID of the advertised device. */ NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful return m_serviceUUIDs[0]; } // getServiceUUID /** * @brief Check advertised services for existance of the required UUID * @return Return true if service is advertised */ bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid){ for (int i = 0; i < m_serviceUUIDs.size(); i++) { NIMBLE_LOGI(LOG_TAG, "Comparing UUIDS: %s %s", m_serviceUUIDs[i].toString().c_str(), uuid.toString().c_str()); if (m_serviceUUIDs[i].equals(uuid)) return true; } return false; } // isAdvertisingService /** * @brief Get the TX Power. * @return The TX Power of the advertised device. */ int8_t NimBLEAdvertisedDevice::getTXPower() { return m_txPower; } // getTXPower /** * @brief Does this advertisement have an appearance value? * @return True if there is an appearance value present. */ bool NimBLEAdvertisedDevice::haveAppearance() { return m_haveAppearance; } // haveAppearance /** * @brief Does this advertisement have manufacturer data? * @return True if there is manufacturer data present. */ bool NimBLEAdvertisedDevice::haveManufacturerData() { return m_haveManufacturerData; } // haveManufacturerData /** * @brief Does this advertisement have a name value? * @return True if there is a name value present. */ bool NimBLEAdvertisedDevice::haveName() { return m_haveName; } // haveName /** * @brief Does this advertisement have a signal strength value? * @return True if there is a signal strength value present. */ bool NimBLEAdvertisedDevice::haveRSSI() { return m_haveRSSI; } // haveRSSI /** * @brief Does this advertisement have a service data value? * @return True if there is a service data value present. */ bool NimBLEAdvertisedDevice::haveServiceData() { return m_haveServiceData; } // haveServiceData /** * @brief Does this advertisement have a service UUID value? * @return True if there is a service UUID value present. */ bool NimBLEAdvertisedDevice::haveServiceUUID() { return m_haveServiceUUID; } // haveServiceUUID /** * @brief Does this advertisement have a transmission power value? * @return True if there is a transmission power value present. */ bool NimBLEAdvertisedDevice::haveTXPower() { return m_haveTXPower; } // haveTXPower /** * @brief Parse the advertising pay load. * * The pay load is a buffer of bytes that is either 31 bytes long or terminated by * a 0 length value. Each entry in the buffer has the format: * [length][type][data...] * * The length does not include itself but does include everything after it until the next record. A record * with a length value of 0 indicates a terminator. * * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile */ void NimBLEAdvertisedDevice::parseAdvertisement(ble_hs_adv_fields *fields) { //char s[BLE_HS_ADV_MAX_SZ]; uint8_t *u8p; uint8_t length; int i; if (fields->uuids16 != NULL) { for (i = 0; i < fields->num_uuids16; i++) { setServiceUUID(NimBLEUUID(fields->uuids16[i].value)); } } if (fields->uuids32 != NULL) { for (i = 0; i < fields->num_uuids32; i++) { setServiceUUID(NimBLEUUID(fields->uuids32[i].value)); } } if (fields->uuids128 != NULL) { for (i = 0; i < fields->num_uuids128; i++) { setServiceUUID(NimBLEUUID(&fields->uuids128[i])); } } if (fields->name != NULL) { setName(std::string(reinterpret_cast(fields->name), fields->name_len)); } if (fields->tx_pwr_lvl_is_present) { setTXPower(fields->tx_pwr_lvl); } if (fields->svc_data_uuid16 != NULL) { u8p = fields->svc_data_uuid16; length = fields->svc_data_uuid16_len; if (length < 2) { NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); } else{ uint16_t uuid = *(uint16_t*)u8p; setServiceDataUUID(NimBLEUUID(uuid)); if (length > 2) { setServiceData(std::string(reinterpret_cast(u8p + 2), length - 2)); } } } if (fields->svc_data_uuid32 != NULL) { u8p = fields->svc_data_uuid16; length = fields->svc_data_uuid16_len; if (length < 4) { NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); } uint32_t uuid = *(uint32_t*) u8p; setServiceDataUUID(NimBLEUUID(uuid)); if (length > 4) { setServiceData(std::string(reinterpret_cast(u8p + 4), length - 4)); } } if (fields->svc_data_uuid128 != NULL) { u8p = fields->svc_data_uuid16; length = fields->svc_data_uuid16_len; if (length < 16) { NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); } setServiceDataUUID(NimBLEUUID(u8p, (size_t)16, false)); if (length > 16) { setServiceData(std::string(reinterpret_cast(u8p + 16), length - 16)); } } if (fields->appearance_is_present) { NIMBLE_LOGD(LOG_TAG, " appearance=0x%04x", fields->appearance); setAppearance(fields->appearance); } /* TODO: create storage and fucntions for these parameters if (fields->public_tgt_addr != NULL) { NIMBLE_LOGD(LOG_TAG, " public_tgt_addr="); u8p = fields->public_tgt_addr; for (i = 0; i < fields->num_public_tgt_addrs; i++) { NIMBLE_LOGD(LOG_TAG, "public_tgt_addr=%s ", addr_str(u8p)); u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN; } NIMBLE_LOGD(LOG_TAG, "\n"); } if (fields->slave_itvl_range != NULL) { NIMBLE_LOGD(LOG_TAG, " slave_itvl_range="); print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN); NIMBLE_LOGD(LOG_TAG, "\n"); } if (fields->adv_itvl_is_present) { NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields->adv_itvl); } if (fields->uri != NULL) { NIMBLE_LOGD(LOG_TAG, " uri="); print_bytes(fields->uri, fields->uri_len); NIMBLE_LOGD(LOG_TAG, "\n"); } */ if (fields->mfg_data != NULL) { setManufacturerData(std::string(reinterpret_cast(fields->mfg_data), fields->mfg_data_len)); } } //parseAdvertisement /** * @brief Set the address of the advertised device. * @param [in] address The address of the advertised device. */ void NimBLEAdvertisedDevice::setAddress(NimBLEAddress address) { m_address = address; } // setAddress /** * @brief Set the adFlag for this device. * @param [in] The discovered adFlag. */ void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) { m_advType = advType; } // setAdvType /** * @brief Set the appearance for this device. * @param [in] The discovered appearance. */ void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) { m_appearance = appearance; m_haveAppearance = true; NIMBLE_LOGD(LOG_TAG,"- appearance: %d", m_appearance); } // setAppearance /** * @brief Set the manufacturer data for this device. * @param [in] The discovered manufacturer data. */ void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) { m_manufacturerData = manufacturerData; m_haveManufacturerData = true; char* pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length()); NIMBLE_LOGD(LOG_TAG,"- manufacturer data: %s", pHex); free(pHex); } // setManufacturerData /** * @brief Set the name for this device. * @param [in] name The discovered name. */ void NimBLEAdvertisedDevice::setName(std::string name) { m_name = name; m_haveName = true; NIMBLE_LOGD(LOG_TAG,"- setName(): name: %s", m_name.c_str()); } // setName /** * @brief Set the RSSI for this device. * @param [in] rssi The discovered RSSI. */ void NimBLEAdvertisedDevice::setRSSI(int rssi) { m_rssi = rssi; m_haveRSSI = true; NIMBLE_LOGD(LOG_TAG,"- setRSSI(): rssi: %d", m_rssi); } // setRSSI /** * @brief Set the Scan that created this advertised device. * @param [in] pScan The Scan that created this advertised device. */ void NimBLEAdvertisedDevice::setScan(NimBLEScan* pScan) { m_pScan = pScan; } // setScan /** * @brief Set the Service UUID for this device. * @param [in] serviceUUID The discovered serviceUUID */ void NimBLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) { return setServiceUUID(NimBLEUUID(serviceUUID)); } // setServiceUUID /** * @brief Set the Service UUID for this device. * @param [in] serviceUUID The discovered serviceUUID */ void NimBLEAdvertisedDevice::setServiceUUID(NimBLEUUID serviceUUID) { // Don't add duplicates for (int i = 0; i < m_serviceUUIDs.size(); i++) { if (m_serviceUUIDs[i] == serviceUUID) { return; } } m_serviceUUIDs.push_back(serviceUUID); m_haveServiceUUID = true; NIMBLE_LOGD(LOG_TAG,"- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str()); } // setServiceUUID /** * @brief Set the ServiceData value. * @param [in] data ServiceData value. */ void NimBLEAdvertisedDevice::setServiceData(std::string serviceData) { m_haveServiceData = true; // Set the flag that indicates we have service data. m_serviceData = serviceData; // Save the service data that we received. } //setServiceData /** * @brief Set the ServiceDataUUID value. * @param [in] data ServiceDataUUID value. */ void NimBLEAdvertisedDevice::setServiceDataUUID(NimBLEUUID uuid) { m_haveServiceData = true; // Set the flag that indicates we have service data. m_serviceDataUUID = uuid; } // setServiceDataUUID /** * @brief Set the power level for this device. * @param [in] txPower The discovered power level. */ void NimBLEAdvertisedDevice::setTXPower(int8_t txPower) { m_txPower = txPower; m_haveTXPower = true; NIMBLE_LOGD(LOG_TAG,"- txPower: %d", m_txPower); } // setTXPower /** * @brief Create a string representation of this device. * @return A string representation of this device. */ std::string NimBLEAdvertisedDevice::toString() { std::string res = "Name: " + getName() + ", Address: " + getAddress().toString(); if (haveAppearance()) { char val[6]; snprintf(val, sizeof(val), "%d", getAppearance()); res += ", appearance: "; res += val; } if (haveManufacturerData()) { char *pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length()); res += ", manufacturer data: "; res += pHex; free(pHex); } if (haveServiceUUID()) { res += ", serviceUUID: " + getServiceUUID().toString(); } if (haveTXPower()) { char val[5]; snprintf(val, sizeof(val), "%d", getTXPower()); res += ", txPower: "; res += val; } res += ", advType: " + std::string(NimBLEUtils::advTypeToString(m_advType)); return res; } // toString /** * @brief Get the payload advertised by the device. * @return The advertisement payload. */ uint8_t* NimBLEAdvertisedDevice::getPayload() { return m_payload; } // getPayload /** * @brief Get the advertised device address type. * @return The device address type: * * BLE_ADDR_PUBLIC (0x00) * * BLE_ADDR_RANDOM (0x01) * * BLE_ADDR_PUBLIC_ID (0x02) * * BLE_ADDR_RANDOM_ID (0x03) */ uint8_t NimBLEAdvertisedDevice::getAddressType() { return m_addressType; } // getAddressType /** * @brief Get the timeStamp of when the device last advertised. * @return The timeStamp of when the device was last seen. */ time_t NimBLEAdvertisedDevice::getTimestamp() { return m_timestamp; } // getTimestamp /** * @brief Set the advertised device address type. * @param [in] type The address type of the device: * * BLE_ADDR_PUBLIC (0x00) * * BLE_ADDR_RANDOM (0x01) * * BLE_ADDR_PUBLIC_ID (0x02) * * BLE_ADDR_RANDOM_ID (0x03) */ void NimBLEAdvertisedDevice::setAddressType(uint8_t type) { m_addressType = type; } // setAddressType /** * @brief Get the length of the payload advertised by the device. * @return The size of the payload in bytes. */ size_t NimBLEAdvertisedDevice::getPayloadLength() { return m_payloadLength; } // getPayloadLength /** * @brief Set the advertisment payload data. * @param [in] payload A pointer to the device payload data. * @param [in] length The length of the payload data in bytes. */ void NimBLEAdvertisedDevice::setAdvertisementResult(uint8_t* payload, uint8_t length){ m_payload = payload; m_payloadLength = length; } // setAdvertisementResult #endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL) #endif /* CONFIG_BT_ENABLED */