From b8ea455f45c13992884ad0c6c87c27c7eee20510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Piln=C3=BD?= <34927466+PilnyTomas@users.noreply.github.com> Date: Mon, 20 Feb 2023 12:30:04 +0100 Subject: [PATCH] Fix for negative temp in Eddystone TLM; solving #7618 (#7791) * Changed data type of temperature * Changed data type in EddystoneTLM class and example * Revert "Changed data type in EddystoneTLM class and example" This reverts commit 1f3a9414ee13341f80bd6d51cdbd57254aadfe97. * Draft of Eddystone TLM example * Adds MACROs to convert beacon temperature 2 Macros EDDYSTONE_TEMP_U16_TO_FLOAT(tempU16) - takes the TLM BigEndian 8.8 fixed point representation and returns its float value EDDYSTONE_TEMP_FLOAT_TO_U16(tempFloat) - takes a float (temperature) and returns its BigEndian 8.8 fixed point representation * Fixed temp * Changed to conform with PR comments * Fixed comment on closing bracket * Prints negative TEMP big endian as just 2 bytes * Extacts correct Eddyston Service Data * Fixes BLEEddystoneTLM::toString() negative temp * Fixes URL field length * Fixes Eddystone URL decoding * Fixes MSB for iBeacon UUID iBeacons use big endian BLE fields. * Fix to detect iBeacon that also has Service UUID This fix makes the BLE_iBeacon.ino to work correctly with the BLE_Beacon_Scanner.ino example --------- Co-authored-by: Rodrigo Garcia --- .../BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino | 105 +++++++++--------- .../BLE_EddystoneTLM_Beacon.ino | 22 ++-- libraries/BLE/src/BLEBeacon.cpp | 2 +- libraries/BLE/src/BLEEddystoneTLM.cpp | 27 ++++- libraries/BLE/src/BLEEddystoneTLM.h | 55 ++++----- libraries/BLE/src/BLEEddystoneURL.h | 2 +- 6 files changed, 114 insertions(+), 99 deletions(-) diff --git a/libraries/BLE/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino b/libraries/BLE/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino index b46baa7c8..26d0e880f 100644 --- a/libraries/BLE/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino +++ b/libraries/BLE/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino @@ -14,8 +14,6 @@ #include #include -#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8)) - int scanTime = 5; //In seconds BLEScan *pBLEScan; @@ -37,58 +35,66 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks Serial.println(devUUID.toString().c_str()); Serial.println(""); } - else + + if (advertisedDevice.haveManufacturerData() == true) { - if (advertisedDevice.haveManufacturerData() == true) - { - std::string strManufacturerData = advertisedDevice.getManufacturerData(); + std::string strManufacturerData = advertisedDevice.getManufacturerData(); - uint8_t cManufacturerData[100]; - strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0); + uint8_t cManufacturerData[100]; + strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0); - if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) - { - Serial.println("Found an iBeacon!"); - BLEBeacon oBeacon = BLEBeacon(); - oBeacon.setData(strManufacturerData); - Serial.printf("iBeacon Frame\n"); - Serial.printf("ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower()); - } - else + if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) + { + Serial.println("Found an iBeacon!"); + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setData(strManufacturerData); + Serial.printf("iBeacon Frame\n"); + Serial.printf("ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower()); + } + else + { + Serial.println("Found another manufacturers beacon!"); + Serial.printf("strManufacturerData: %d ", strManufacturerData.length()); + for (int i = 0; i < strManufacturerData.length(); i++) { - Serial.println("Found another manufacturers beacon!"); - Serial.printf("strManufacturerData: %d ", strManufacturerData.length()); - for (int i = 0; i < strManufacturerData.length(); i++) - { - Serial.printf("[%X]", cManufacturerData[i]); - } - Serial.printf("\n"); + Serial.printf("[%X]", cManufacturerData[i]); } + Serial.printf("\n"); } - return; } uint8_t *payLoad = advertisedDevice.getPayload(); + // search for Eddystone Service Data in the advertising payload + // *payload shall point to eddystone data or to its end when not found + const uint8_t serviceDataEddystone[3] = {0x16, 0xAA, 0xFE}; // it has Eddystone BLE UUID + const size_t payLoadLen = advertisedDevice.getPayloadLength(); + uint8_t *payLoadEnd = payLoad + payLoadLen - 1; // address of the end of payLoad space + while (payLoad < payLoadEnd) { + if (payLoad[1] == serviceDataEddystone[0] && payLoad[2] == serviceDataEddystone[1] && payLoad[3] == serviceDataEddystone[2]) { + // found! + payLoad += 4; + break; + } + payLoad += *payLoad + 1; // payLoad[0] has the field Length + } - BLEUUID checkUrlUUID = (uint16_t)0xfeaa; - - if (advertisedDevice.getServiceUUID().equals(checkUrlUUID)) + if (payLoad < payLoadEnd) // Eddystone Service Data and respective BLE UUID were found { - if (payLoad[11] == 0x10) + if (*payLoad == 0x10) { Serial.println("Found an EddystoneURL beacon!"); BLEEddystoneURL foundEddyURL = BLEEddystoneURL(); - std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! - - foundEddyURL.setData(eddyContent); + uint8_t URLLen = *(payLoad - 4) - 3; // Get Field Length less 3 bytes (type and UUID) + foundEddyURL.setData(std::string((char*)payLoad, URLLen)); std::string bareURL = foundEddyURL.getURL(); if (bareURL[0] == 0x00) { - size_t payLoadLen = advertisedDevice.getPayloadLength(); + // dumps all bytes in advertising payload Serial.println("DATA-->"); + uint8_t *payLoad = advertisedDevice.getPayload(); for (int idx = 0; idx < payLoadLen; idx++) { - Serial.printf("0x%08X ", payLoad[idx]); + Serial.printf("0x%02X ", payLoad[idx]); } Serial.println("\nInvalid Data"); return; @@ -98,30 +104,19 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks Serial.printf("Decoded URL: %s\n", foundEddyURL.getDecodedURL().c_str()); Serial.printf("TX power %d\n", foundEddyURL.getPower()); Serial.println("\n"); - } - else if (payLoad[11] == 0x20) + } + else if (*payLoad == 0x20) { Serial.println("Found an EddystoneTLM beacon!"); - BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM(); - std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! - - eddyContent = "01234567890123"; - - for (int idx = 0; idx < 14; idx++) - { - eddyContent[idx] = payLoad[idx + 11]; - } - - foundEddyURL.setData(eddyContent); - Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt()); - Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp()); - int temp = (int)payLoad[16] + (int)(payLoad[15] << 8); - float calcTemp = temp / 256.0f; - Serial.printf("Reported temperature from data: %.2fC\n", calcTemp); - Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount()); - Serial.printf("Reported time since last reboot: %ds\n", foundEddyURL.getTime()); + + BLEEddystoneTLM eddystoneTLM; + eddystoneTLM.setData(std::string((char*)payLoad, 14)); + Serial.printf("Reported battery voltage: %dmV\n", eddystoneTLM.getVolt()); + Serial.printf("Reported temperature: %.2f°C (raw data=0x%04X)\n", eddystoneTLM.getTemp(), eddystoneTLM.getRawTemp()); + Serial.printf("Reported advertise count: %d\n", eddystoneTLM.getCount()); + Serial.printf("Reported time since last reboot: %ds\n", eddystoneTLM.getTime()); Serial.println("\n"); - Serial.print(foundEddyURL.toString().c_str()); + Serial.print(eddystoneTLM.toString().c_str()); Serial.println("\n"); } } diff --git a/libraries/BLE/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino b/libraries/BLE/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino index 96be28cd5..ddb1d8593 100644 --- a/libraries/BLE/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino +++ b/libraries/BLE/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino @@ -13,6 +13,7 @@ 5. Stop advertising. 6. deep sleep + To read data advertised by this beacon use second ESP with example sketch BLE_Beacon_Scanner */ #include "sys/time.h" @@ -22,7 +23,7 @@ #include "BLEUtils.h" #include "BLEBeacon.h" #include "BLEAdvertising.h" -#include "BLEEddystoneURL.h" +#include "BLEEddystoneTLM.h" #include "esp_sleep.h" @@ -48,10 +49,10 @@ void setBeacon() char beacon_data[25]; uint16_t beconUUID = 0xFEAA; uint16_t volt = random(2800, 3700); // 3300mV = 3.3V - float tempFloat = random(2000, 3100) / 100.0f; - Serial.printf("Random temperature is %.2fC\n", tempFloat); - int temp = (int)(tempFloat * 256); //(uint16_t)((float)23.00); - Serial.printf("Converted to 8.8 format %0X%0X\n", (temp >> 8), (temp & 0xFF)); + float tempFloat = random(-3000, 3000) / 100.0f; + Serial.printf("Random temperature is %.2f°C\n", tempFloat); + int temp = (int)(tempFloat * 256); + Serial.printf("Converted to 8.8 format %0X%0X\n", (temp >> 8) & 0xFF, (temp & 0xFF)); BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); @@ -82,13 +83,11 @@ void setBeacon() void setup() { - Serial.begin(115200); gettimeofday(&nowTimeStruct, NULL); - Serial.printf("start ESP32 %d\n", bootcount++); - - Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", nowTimeStruct.tv_sec, nowTimeStruct.tv_sec - last); + Serial.printf("Starting ESP32. Bootcount = %d\n", bootcount++); + Serial.printf("Deep sleep (%lds since last reset, %lds since last boot)\n", nowTimeStruct.tv_sec, nowTimeStruct.tv_sec - last); last = nowTimeStruct.tv_sec; lastTenth = nowTimeStruct.tv_sec * 10; // Time since last reset as 0.1 second resolution counter @@ -103,12 +102,11 @@ void setup() setBeacon(); // Start advertising pAdvertising->start(); - Serial.println("Advertizing started for 10s ..."); + Serial.println("Advertising started for 10s ..."); delay(10000); pAdvertising->stop(); - Serial.printf("enter deep sleep for 10s\n"); + Serial.printf("Enter deep sleep for 10s\n"); esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); - Serial.printf("in deep sleep\n"); } void loop() diff --git a/libraries/BLE/src/BLEBeacon.cpp b/libraries/BLE/src/BLEBeacon.cpp index 9f3519b8a..177926d9e 100644 --- a/libraries/BLE/src/BLEBeacon.cpp +++ b/libraries/BLE/src/BLEBeacon.cpp @@ -40,7 +40,7 @@ uint16_t BLEBeacon::getMinor() { } BLEUUID BLEBeacon::getProximityUUID() { - return BLEUUID(m_beaconData.proximityUUID, 16, false); + return BLEUUID(m_beaconData.proximityUUID, 16, true); } int8_t BLEBeacon::getSignalPower() { diff --git a/libraries/BLE/src/BLEEddystoneTLM.cpp b/libraries/BLE/src/BLEEddystoneTLM.cpp index 10cc657a2..a902209fa 100644 --- a/libraries/BLE/src/BLEEddystoneTLM.cpp +++ b/libraries/BLE/src/BLEEddystoneTLM.cpp @@ -17,8 +17,6 @@ #include "BLEEddystoneTLM.h" static const char LOG_TAG[] = "BLEEddystoneTLM"; -#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) -#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) BLEEddystoneTLM::BLEEddystoneTLM() { beaconUUID = 0xFEAA; @@ -47,9 +45,13 @@ uint16_t BLEEddystoneTLM::getVolt() { } // getVolt float BLEEddystoneTLM::getTemp() { - return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f; + return EDDYSTONE_TEMP_U16_TO_FLOAT(m_eddystoneData.temp); } // getTemp +uint16_t BLEEddystoneTLM::getRawTemp() { + return ENDIAN_CHANGE_U16(m_eddystoneData.temp); +} // getRawTemp + uint32_t BLEEddystoneTLM::getCount() { return ENDIAN_CHANGE_U32(m_eddystoneData.advCount); } // getCount @@ -73,7 +75,7 @@ std::string BLEEddystoneTLM::toString() { out += " mV\n"; out += "Temperature "; - snprintf(val, sizeof(val), "%.2f", ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f); + snprintf(val, sizeof(val), "%.2f", ((int16_t)ENDIAN_CHANGE_U16(m_eddystoneData.temp)) / 256.0f); out += val; out += " C\n"; @@ -110,6 +112,21 @@ std::string BLEEddystoneTLM::toString() { /** * Set the raw data for the beacon record. + * Example: + * uint8_t *payLoad = advertisedDevice.getPayload(); + * eddystoneTLM.setData(std::string((char*)payLoad+22, advertisedDevice.getPayloadLength() - 22)); + * Note: the offset 22 works for current implementation of example BLE_EddystoneTLM Beacon.ino, however it is not static and will be reimplemented + * Data frame: + * | Field || Len | Type | UUID | EddyStone TLM | + * | Offset || 0 | 1 | 2 | 4 | + * | Len || 1 B | 1 B | 2 B | 14 B | + * | Data || ?? | ?? | 0xAA | 0xFE | ??? | + * + * EddyStone TLM frame: + * | Field || Type | Version | Batt mV | Beacon temp | Cnt since boot | Time since boot | + * | Offset || 0 | 1 | 2 | 4 | 6 | 10 | + * | Len || 1 B | 1 B | 2 B | 2 B | 4 B | 4 B | + * | Data || 0x20 | ?? | ?? | ?? | ?? | ?? | | | | | | | | | */ void BLEEddystoneTLM::setData(std::string data) { if (data.length() != sizeof(m_eddystoneData)) { @@ -132,7 +149,7 @@ void BLEEddystoneTLM::setVolt(uint16_t volt) { } // setVolt void BLEEddystoneTLM::setTemp(float temp) { - m_eddystoneData.temp = (uint16_t)temp; + m_eddystoneData.temp = EDDYSTONE_TEMP_FLOAT_TO_U16(temp); } // setTemp void BLEEddystoneTLM::setCount(uint32_t advCount) { diff --git a/libraries/BLE/src/BLEEddystoneTLM.h b/libraries/BLE/src/BLEEddystoneTLM.h index a93e224fd..72dec3180 100644 --- a/libraries/BLE/src/BLEEddystoneTLM.h +++ b/libraries/BLE/src/BLEEddystoneTLM.h @@ -10,6 +10,10 @@ #include "BLEUUID.h" #define EDDYSTONE_TLM_FRAME_TYPE 0x20 +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) +#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) +#define EDDYSTONE_TEMP_U16_TO_FLOAT(tempU16) (((int16_t)ENDIAN_CHANGE_U16(tempU16)) / 256.0f) +#define EDDYSTONE_TEMP_FLOAT_TO_U16(tempFloat) (ENDIAN_CHANGE_U16(((int)((tempFloat) * 256)))) /** * @brief Representation of a beacon. @@ -18,33 +22,34 @@ */ class BLEEddystoneTLM { public: - BLEEddystoneTLM(); - std::string getData(); - BLEUUID getUUID(); - uint8_t getVersion(); - uint16_t getVolt(); - float getTemp(); - uint32_t getCount(); - uint32_t getTime(); - std::string toString(); - void setData(std::string data); - void setUUID(BLEUUID l_uuid); - void setVersion(uint8_t version); - void setVolt(uint16_t volt); - void setTemp(float temp); - void setCount(uint32_t advCount); - void setTime(uint32_t tmil); + BLEEddystoneTLM(); + std::string getData(); + BLEUUID getUUID(); + uint8_t getVersion(); + uint16_t getVolt(); + float getTemp(); + uint16_t getRawTemp(); + uint32_t getCount(); + uint32_t getTime(); + std::string toString(); + void setData(std::string data); + void setUUID(BLEUUID l_uuid); + void setVersion(uint8_t version); + void setVolt(uint16_t volt); + void setTemp(float temp); + void setCount(uint32_t advCount); + void setTime(uint32_t tmil); private: - uint16_t beaconUUID; - struct { - uint8_t frameType; - uint8_t version; - uint16_t volt; - uint16_t temp; - uint32_t advCount; - uint32_t tmil; - } __attribute__((packed)) m_eddystoneData; + uint16_t beaconUUID; + struct { + uint8_t frameType; + uint8_t version; + uint16_t volt; + uint16_t temp; + uint32_t advCount; + uint32_t tmil; + } __attribute__((packed)) m_eddystoneData; }; // BLEEddystoneTLM diff --git a/libraries/BLE/src/BLEEddystoneURL.h b/libraries/BLE/src/BLEEddystoneURL.h index 0b538c07d..adb1335e6 100644 --- a/libraries/BLE/src/BLEEddystoneURL.h +++ b/libraries/BLE/src/BLEEddystoneURL.h @@ -35,7 +35,7 @@ private: struct { uint8_t frameType; int8_t advertisedTxPower; - uint8_t url[16]; + uint8_t url[18]; // 18 bytes: 1 byte for URL scheme + up to 17 bytes of URL } __attribute__((packed)) m_eddystoneData; }; // BLEEddystoneURL -- GitLab