ESP32 Eddystone TLM Beacon Example (Deep Sleep Telemetry)


This example shows how to turn an ESP32 into a BLE beacon that broadcasts Eddystone TLM (Telemetry) frames. It periodically advertises battery voltage and temperature, then enters deep sleep to conserve power — making it ideal for low-power IoT beacon applications.

1. Introduction

This example demonstrates a very different BLE use case compared to clients and servers:

👉 broadcast-only BLE (beacon mode)

Instead of:

  • connecting
  • exchanging data

The ESP32:

  • advertises telemetry data
  • does not accept connections
  • operates in ultra-low-power cycles

It uses Eddystone TLM, a Google-defined BLE beacon format designed specifically for telemetry data.

2. What is Eddystone TLM

Eddystone TLM (Telemetry) is a BLE advertisement format that broadcasts:

  • battery voltage
  • temperature
  • uptime
  • packet count

Unlike iBeacon or Eddystone-URL:

👉 TLM is designed for device health monitoring, not user interaction.

Specification:
https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md

3. What this example does

The ESP32 runs this cycle:

  1. Boot
  2. Generate telemetry data (voltage + temperature)
  3. Broadcast BLE advertisement (Eddystone TLM)
  4. Wait 10 seconds
  5. Stop advertising
  6. Enter deep sleep
  7. Wake up and repeat

This creates a low-power periodic beacon


4. Key design concept: Deep sleep beacon

This is the most important part:

#define GPIO_DEEP_SLEEP_DURATION 10

And later:

esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);

👉 The ESP32 is not continuously running

Instead:

  • wakes up
  • transmits
  • sleeps

This drastically reduces power consumption.

5. RTC memory usage

RTC_DATA_ATTR static time_t last;
RTC_DATA_ATTR static uint32_t bootcount;

These variables survive deep sleep.

They are used to:

  • track number of wake cycles
  • measure time between boots

👉 This is critical for low-power designs

6. Setting up the beacon

BLE initialization

BLEDevice::init("TLMBeacon");
BLEDevice::setPower(BEACON_POWER);
  • Initializes BLE stack
  • Sets transmission power
#define BEACON_POWER ESP_PWR_LVL_N12

This is low transmit power → saves energy

Advertising object

pAdvertising = BLEDevice::getAdvertising();

This controls BLE broadcast behavior.

7. Creating telemetry data

The core logic is inside:

void setBeacon()

Battery voltage

EddystoneTLM.setVolt((uint16_t)random(2800, 3700));
  • Simulates voltage between 2.8V and 3.7V
  • Value stored in millivolts

Temperature

EddystoneTLM.setTemp(random(-3000, 3000) / 100.0f);
  • Generates temperature between -30°C and +30°C
  • Converted to float

Fixed-point format (important)

EddystoneTLM.getRawTemp()

Eddystone uses:

👉 8.8 fixed-point format

Meaning:

  • 8 bits integer
  • 8 bits fractional

This is why conversion is needed.

8. Advertising payload structure

oScanResponseData.setServiceData(
BLEUUID((uint16_t)0xFEAA),
...
);

Key points:

  • 0xFEAA = Eddystone UUID
  • Telemetry data is placed in scan response

Advertisement vs Scan Response

oAdvertisementData.setName("ESP32 TLM Beacon");
  • Advertisement → device name
  • Scan response → telemetry data

👉 This is standard BLE practice

9. Starting and stopping advertising

pAdvertising->start();
delay(10000);
pAdvertising->stop();

The beacon:

  • advertises for 10 seconds
  • then stops

10. Deep sleep cycle

esp_deep_sleep(...)

This is where power savings happen.

Instead of idle looping:

👉 ESP32 shuts down almost completely

Only RTC memory remains active.

11. Boot diagnostics

Serial.printf("Starting ESP32. Bootcount = %lu\n", bootcount++);

And:

Serial.printf("Deep sleep (%llds since last reset, %llds since last boot)\n", ...)

These help track:

  • how often the device wakes
  • how long it has been running

12. Complete runtime flow

  1. ESP32 wakes from deep sleep
  2. Initializes BLE
  3. Generates telemetry data
  4. Starts advertising
  5. Broadcasts for 10 seconds
  6. Stops advertising
  7. Enters deep sleep
  8. Repeats

13. How to read this beacon

You need a scanner.

Options:

  • ESP32 with BLE_Beacon_Scanner example
  • nRF Connect app (Android/iOS)
  • Any BLE scanner supporting Eddystone

👉 Look for:

  • Service UUID: 0xFEAA
  • TLM frame

14. Important limitations

1. No connections

This is a pure beacon:

  • cannot be connected to
  • cannot receive data

2. Random data

Voltage and temperature are simulated:

random(...)

Replace with real sensors for production.

3. No uptime/frame counters

TLM supports:

  • uptime
  • packet count

But this example does not fully implement them.

15. Real-world applications

This pattern is used in:

  • battery-powered IoT sensors
  • environmental monitoring
  • asset tracking beacons
  • industrial telemetry
  • smart building systems

16. How to improve this example

1. Use real sensors

Replace random values with:

  • ADC battery measurement
  • temperature sensor

2. Adjust sleep interval

Balance:

  • update frequency
  • battery life

3. Increase transmit power if needed

Trade-off:

  • range vs battery

4. Add uptime counter

Use RTC memory to track:

  • total uptime
  • beacon frame count

5. Optimize advertising duration

10 seconds is arbitrary — tune for your use case.

17. Honest take

This example is:

  • excellent for low-power design concepts
  • underused compared to BLE client/server

Big takeaway:

👉 BLE does not require connections to be useful

For many IoT systems:

  • broadcasting is enough
  • and much more efficient

Conclusion

This example shows how to build a true low-power BLE telemetry device using ESP32.

Instead of constant communication, it uses:

  • short advertising bursts
  • deep sleep cycles

This is exactly how modern IoT beacons are designed.

If you combine this with a scanner or gateway, you get:

👉 a complete low-power wireless telemetry system

Share your love

Leave a Reply

Your email address will not be published. Required fields are marked *