Skip to content

Commit ef2b545

Browse files
authored
Fix issue #3833, data parsing of Eddystone TLM data frame (#3836)
* Fix issue #3833, data parsing of Eddystone TLM data frame Add Beacon scanner example to show usage of BLEEddystoneTLM class and BLEEddystoneURL class Add EddystoneTLM beacon example Add EddystoneURL beacon example * Fix buffer size for .toString()
1 parent 9856f0c commit ef2b545

File tree

7 files changed

+522
-10
lines changed

7 files changed

+522
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
3+
Ported to Arduino ESP32 by Evandro Copercini
4+
Changed to a beacon scanner to report iBeacon, EddystoneURL and EddystoneTLM beacons by beegee-tokyo
5+
*/
6+
7+
#include <Arduino.h>
8+
9+
#include <BLEDevice.h>
10+
#include <BLEUtils.h>
11+
#include <BLEScan.h>
12+
#include <BLEAdvertisedDevice.h>
13+
#include <BLEEddystoneURL.h>
14+
#include <BLEEddystoneTLM.h>
15+
#include <BLEBeacon.h>
16+
17+
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8))
18+
19+
int scanTime = 5; //In seconds
20+
BLEScan *pBLEScan;
21+
22+
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
23+
{
24+
void onResult(BLEAdvertisedDevice advertisedDevice)
25+
{
26+
if (advertisedDevice.haveName())
27+
{
28+
Serial.print("Device name: ");
29+
Serial.println(advertisedDevice.getName().c_str());
30+
Serial.println("");
31+
}
32+
33+
if (advertisedDevice.haveServiceUUID())
34+
{
35+
BLEUUID devUUID = advertisedDevice.getServiceUUID();
36+
Serial.print("Found ServiceUUID: ");
37+
Serial.println(devUUID.toString().c_str());
38+
Serial.println("");
39+
}
40+
else
41+
{
42+
if (advertisedDevice.haveManufacturerData() == true)
43+
{
44+
std::string strManufacturerData = advertisedDevice.getManufacturerData();
45+
46+
uint8_t cManufacturerData[100];
47+
strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0);
48+
49+
if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00)
50+
{
51+
Serial.println("Found an iBeacon!");
52+
BLEBeacon oBeacon = BLEBeacon();
53+
oBeacon.setData(strManufacturerData);
54+
Serial.printf("iBeacon Frame\n");
55+
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());
56+
}
57+
else
58+
{
59+
Serial.println("Found another manufacturers beacon!");
60+
Serial.printf("strManufacturerData: %d ", strManufacturerData.length());
61+
for (int i = 0; i < strManufacturerData.length(); i++)
62+
{
63+
Serial.printf("[%X]", cManufacturerData[i]);
64+
}
65+
Serial.printf("\n");
66+
}
67+
}
68+
return;
69+
}
70+
71+
uint8_t *payLoad = advertisedDevice.getPayload();
72+
73+
BLEUUID checkUrlUUID = (uint16_t)0xfeaa;
74+
75+
if (advertisedDevice.getServiceUUID().equals(checkUrlUUID))
76+
{
77+
if (payLoad[11] == 0x10)
78+
{
79+
Serial.println("Found an EddystoneURL beacon!");
80+
BLEEddystoneURL foundEddyURL = BLEEddystoneURL();
81+
std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct!
82+
83+
foundEddyURL.setData(eddyContent);
84+
std::string bareURL = foundEddyURL.getURL();
85+
if (bareURL[0] == 0x00)
86+
{
87+
size_t payLoadLen = advertisedDevice.getPayloadLength();
88+
Serial.println("DATA-->");
89+
for (int idx = 0; idx < payLoadLen; idx++)
90+
{
91+
Serial.printf("0x%08X ", payLoad[idx]);
92+
}
93+
Serial.println("\nInvalid Data");
94+
return;
95+
}
96+
97+
Serial.printf("Found URL: %s\n", foundEddyURL.getURL().c_str());
98+
Serial.printf("Decoded URL: %s\n", foundEddyURL.getDecodedURL().c_str());
99+
Serial.printf("TX power %d\n", foundEddyURL.getPower());
100+
Serial.println("\n");
101+
}
102+
else if (payLoad[11] == 0x20)
103+
{
104+
Serial.println("Found an EddystoneTLM beacon!");
105+
BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM();
106+
std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct!
107+
108+
eddyContent = "01234567890123";
109+
110+
for (int idx = 0; idx < 14; idx++)
111+
{
112+
eddyContent[idx] = payLoad[idx + 11];
113+
}
114+
115+
foundEddyURL.setData(eddyContent);
116+
Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt());
117+
Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp());
118+
int temp = (int)payLoad[16] + (int)(payLoad[15] << 8);
119+
float calcTemp = temp / 256.0f;
120+
Serial.printf("Reported temperature from data: %.2fC\n", calcTemp);
121+
Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount());
122+
Serial.printf("Reported time since last reboot: %ds\n", foundEddyURL.getTime());
123+
Serial.println("\n");
124+
Serial.print(foundEddyURL.toString().c_str());
125+
Serial.println("\n");
126+
}
127+
}
128+
}
129+
};
130+
131+
void setup()
132+
{
133+
Serial.begin(115200);
134+
Serial.println("Scanning...");
135+
136+
BLEDevice::init("");
137+
pBLEScan = BLEDevice::getScan(); //create new scan
138+
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
139+
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
140+
pBLEScan->setInterval(100);
141+
pBLEScan->setWindow(99); // less or equal setInterval value
142+
}
143+
144+
void loop()
145+
{
146+
// put your main code here, to run repeatedly:
147+
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
148+
Serial.print("Devices found: ");
149+
Serial.println(foundDevices.getCount());
150+
Serial.println("Scan done!");
151+
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
152+
delay(2000);
153+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## BLE Beacon Scanner
2+
3+
Initiates a BLE device scan.
4+
Checks if the discovered devices are
5+
- an iBeacon
6+
- an Eddystone TLM beacon
7+
- an Eddystone URL beacon
8+
9+
and sends the decoded beacon information over Serial log
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
EddystoneTLM beacon by BeeGee based on https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino
3+
EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md
4+
*/
5+
6+
/*
7+
Create a BLE server that will send periodic Eddystone URL frames.
8+
The design of creating the BLE server is:
9+
1. Create a BLE Server
10+
2. Create advertising data
11+
3. Start advertising.
12+
4. wait
13+
5. Stop advertising.
14+
6. deep sleep
15+
16+
*/
17+
#include "sys/time.h"
18+
19+
#include <Arduino.h>
20+
21+
#include "BLEDevice.h"
22+
#include "BLEUtils.h"
23+
#include "BLEBeacon.h"
24+
#include "BLEAdvertising.h"
25+
#include "BLEEddystoneURL.h"
26+
27+
#include "esp_sleep.h"
28+
29+
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
30+
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
31+
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
32+
33+
// See the following for generating UUIDs:
34+
// https://www.uuidgenerator.net/
35+
BLEAdvertising *pAdvertising;
36+
struct timeval nowTimeStruct;
37+
38+
time_t lastTenth;
39+
40+
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)
41+
42+
// Check
43+
// https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md
44+
// and http://www.hugi.scene.org/online/coding/hugi%2015%20-%20cmtadfix.htm
45+
// for the temperature value. It is a 8.8 fixed-point notation
46+
void setBeacon()
47+
{
48+
char beacon_data[25];
49+
uint16_t beconUUID = 0xFEAA;
50+
uint16_t volt = random(2800, 3700); // 3300mV = 3.3V
51+
float tempFloat = random(2000, 3100) / 100.0f;
52+
Serial.printf("Random temperature is %.2fC\n", tempFloat);
53+
int temp = (int)(tempFloat * 256); //(uint16_t)((float)23.00);
54+
Serial.printf("Converted to 8.8 format %0X%0X\n", (temp >> 8), (temp & 0xFF));
55+
56+
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
57+
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
58+
59+
oScanResponseData.setFlags(0x06); // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04
60+
oScanResponseData.setCompleteServices(BLEUUID(beconUUID));
61+
62+
beacon_data[0] = 0x20; // Eddystone Frame Type (Unencrypted Eddystone-TLM)
63+
beacon_data[1] = 0x00; // TLM version
64+
beacon_data[2] = (volt >> 8); // Battery voltage, 1 mV/bit i.e. 0xCE4 = 3300mV = 3.3V
65+
beacon_data[3] = (volt & 0xFF); //
66+
beacon_data[4] = (temp >> 8); // Beacon temperature
67+
beacon_data[5] = (temp & 0xFF); //
68+
beacon_data[6] = ((bootcount & 0xFF000000) >> 24); // Advertising PDU count
69+
beacon_data[7] = ((bootcount & 0xFF0000) >> 16); //
70+
beacon_data[8] = ((bootcount & 0xFF00) >> 8); //
71+
beacon_data[9] = (bootcount & 0xFF); //
72+
beacon_data[10] = ((lastTenth & 0xFF000000) >> 24); // Time since power-on or reboot as 0.1 second resolution counter
73+
beacon_data[11] = ((lastTenth & 0xFF0000) >> 16); //
74+
beacon_data[12] = ((lastTenth & 0xFF00) >> 8); //
75+
beacon_data[13] = (lastTenth & 0xFF); //
76+
77+
oScanResponseData.setServiceData(BLEUUID(beconUUID), std::string(beacon_data, 14));
78+
oAdvertisementData.setName("TLMBeacon");
79+
pAdvertising->setAdvertisementData(oAdvertisementData);
80+
pAdvertising->setScanResponseData(oScanResponseData);
81+
}
82+
83+
void setup()
84+
{
85+
86+
Serial.begin(115200);
87+
gettimeofday(&nowTimeStruct, NULL);
88+
89+
Serial.printf("start ESP32 %d\n", bootcount++);
90+
91+
Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", nowTimeStruct.tv_sec, nowTimeStruct.tv_sec - last);
92+
93+
last = nowTimeStruct.tv_sec;
94+
lastTenth = nowTimeStruct.tv_sec * 10; // Time since last reset as 0.1 second resolution counter
95+
96+
// Create the BLE Device
97+
BLEDevice::init("TLMBeacon");
98+
99+
BLEDevice::setPower(ESP_PWR_LVL_N12);
100+
101+
pAdvertising = BLEDevice::getAdvertising();
102+
103+
setBeacon();
104+
// Start advertising
105+
pAdvertising->start();
106+
Serial.println("Advertizing started for 10s ...");
107+
delay(10000);
108+
pAdvertising->stop();
109+
Serial.printf("enter deep sleep for 10s\n");
110+
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
111+
Serial.printf("in deep sleep\n");
112+
}
113+
114+
void loop()
115+
{
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## Eddystone TLM beacon
2+
EddystoneTLM beacon by BeeGee based on
3+
[pcbreflux ESP32 Eddystone TLM deepsleep](https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino)
4+
5+
[EddystoneTLM frame specification](https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md)
6+
7+
Create a BLE server that will send periodic Eddystone TLM frames.
8+
The design of creating the BLE server is:
9+
1. Create a BLE Server
10+
2. Create advertising data
11+
3. Start advertising.
12+
4. wait
13+
5. Stop advertising.
14+
6. deep sleep

0 commit comments

Comments
 (0)