
A plain advertising beacon only answers one question: “where is this tag?” Add a sensor — temperature, humidity, acceleration, light — and the same hardware becomes a distributed measurement node answering “what is the condition here, and did anything change?” This article covers the engineering decisions behind sensor beacons: how to interface sensors, how to pack readings into the 31-byte advertising payload, how sampling cadence drives battery life, and how on-device threshold logic turns a dumb broadcaster into an edge detector.
1. Why Sensor Beacons Are Different
A location-only beacon transmits a fixed payload at a fixed interval. Its power draw is deterministic and its firmware is trivial. A sensor beacon must:
1. Wake the sensor from sleep (or keep it in a low-power sampling mode)
2. Acquire a reading over I2C/SPI, which takes milliseconds and draws sensor quiescent current
3. Process the value (filtering, calibration, threshold check)
4. Decide what to send — the raw value, a delta, or nothing
5. Encode and advertise the result
Each step adds latency and current. The design problem is balancing measurement fidelity against battery life, because the sensor subsystem — not the radio — usually dominates the budget once you sample more than a few times per hour.
2. Sensor Interfaces and Current Draw
Most beacon sensors hang off a digital bus. The two common choices:
| Interface | Speed | Typical sensor current | Wake latency | Notes |
|---|---|---|---|---|
| I2C | 100-400 kHz | 1-15 µA idle, 100-500 µA active | 1-5 ms | 2 wires, address-limited, easy to share |
| SPI | 1-8 MHz | 2-20 µA idle, 200-800 µA active | 0.1-1 ms | 4 wires, faster, lower latency |
For a coin-cell beacon, the dominant cost is not the active current but the energy per sample = (wake_latency + conversion_time) × average_current. A humidity sensor that takes 10 ms at 300 µA costs 3 µC per reading. At 1 sample/hour that is 3 µC/h ≈ 0.83 µA average — negligible. At 1 sample/minute it is 50 µA average — now competing with the radio.
2.1 Common sensors and their footprints
| Sensor | Typical part | Bus | Active current | Conversion time | Output range |
|---|---|---|---|---|---|
| Temperature | TMP117 | I2C | 3.5 µA | 15.5 ms (avg) | -0.1 to 0.2 °C |
| Humidity | HDC2080 | I2C | 300 µA | 1.2 ms | ±2 % RH |
| 3-axis accel | LIS2DH12 | I2C/SPI | 4 µA (low-power) | 1 ms | ±2/4/8/16 g |
| Ambient light | OPT3001 | I2C | 1.8 µA | 100-800 ms | 0.01-83k lux |
| Pressure | LPS22HB | I2C/SPI | 25 µA | 13 µs (ODR) | ±0.025 hPa |
| CO2 (NDIR) | SCD40 | I2C | 18 mA (pulse) | 0.5-5 s | 0-40000 ppm |
The CO2 sensor stands out: at 18 mA it is 1000× the radio’s sleep current and cannot run on a coin cell continuously. It is the canonical example of why sensor choice constrains the power architecture — a CO2 beacon needs a larger cell or wired power.
3. Encoding Sensor Data in 31 Bytes
A BLE advertising packet carries a 31-byte payload (legacy) split into AD structures: a 1-byte length, 1-byte type, then data. iBeacon uses 25 bytes of manufacturer data; Eddystone-URL uses ~20. That leaves little room for sensors.
3.1 The “append, don’t replace” rule
Keep the identity frame (iBeacon / Eddystone-UID) intact for location systems, and add a secondary advertising frame carrying sensor data. BLE lets a beacon cycle between multiple advertising frames (alternating on each advertising event). A gateway scanning all three channels over a few seconds captures both.
3.2 Eddystone-TLM: the built-in sensor channel
Eddystone-TLM is the standards-based way to carry telemetry:
| Field | Bytes | Content |
|---|---|---|
| Version | 1 | 0x00 |
| Battery voltage | 2 | 1 mV units, 0-65535 |
| Beacon temperature | 2 | 8.8 fixed point, °C |
| PDU count | 4 | Lifetime packet count |
| Uptime | 4 | 0.1 s units |
TLM is clean but limited: one temperature value, battery only, no humidity/accel. For richer data you need a custom manufacturer-specific data (MSD) frame.
3.3 Custom MSD encoding example
A compact packed layout for a multi-sensor beacon (little-endian):
Byte 0: Company ID LSB (0x4C for Apple, or your assigned CID)
Byte 1: Company ID MSB
Byte 2: Frame type = 0x50 (vendor sensor frame)
Byte 3: Sensor bitmap (bit0 temp, bit1 hum, bit2 accel, bit3 light, bit4 batt)
Byte 4-5: Temperature, 8.8 fixed point, signed (×256)
Byte 6-7: Humidity, 8.8 fixed point (×256)
Byte 8-9: Light, 10-bit log scale (0-1023 → 0.01-100k lux log)
Byte 10-11:Battery mV (0-65535)
Byte 12-13:Accel magnitude, 8.8 fixed (g × 256)
Byte 14: Status / flags (motion detected, threshold tripped)
Byte 15: Sequence number (wraps 0-255)
This 16-byte frame fits alongside an iBeacon identity frame with room to spare. The bitmap lets a beacon omit absent sensors, shrinking the payload. A temp-only beacon sends bytes 0-7 (8 bytes); a full sensor node sends all 16.
3.4 Compression tricks
- Delta encoding: advertise the change since last reading, not the absolute. A temperature drifting 0.1 °C/hour fits in a signed 8-bit delta (±127 × 0.01 °C) instead of a 16-bit absolute.
- Fixed-point over float: 8.8 fixed point costs 2 bytes for ±127.99 with 0.004 °C resolution — plenty for environment monitoring.
- Log-scale light: `encoded = round(1023 × log10(lux/0.01) / log10(100000/0.01))` packs 5 decades into 10 bits.
- Run-length skip: if nothing changed beyond noise floor, send a heartbeat frame every N cycles instead of every cycle.
4. Sampling Strategy and the Power Budget
The single biggest lever on battery life is how often you sample. Three strategies:
4.1 Fixed periodic sampling
Simplest: sample every T seconds, advertise every reading.
Average current ≈ I_radio_adv × duty_adv + I_sensor_avg × (t_sample / T)
For T = 60 s, t_sample = 5 ms, I_sensor_avg = 200 µA: sensor contribution = 200 µA × 5ms/60s = 0.017 µA — negligible. The radio (e.g. 1 µA sleep, 15 mA for 3 ms every 1 s) dominates at ~45 µA. So at 1/min sampling, the sensor is free; push to 1/s and sensor cost rises to ~1 µA — still small but no longer negligible.
4.2 Adaptive / on-demand sampling
Sample slow (e.g. every 5 min) for the identity frame, but let the accelerometer’s interrupt wake the MCU on motion. The accel in wake-on-motion mode draws ~1-4 µA continuously but eliminates radio activity until something happens. For asset tracking where motion is rare, this cuts average current by 10-100×.
4.3 Threshold-triggered burst
The beacon sits silent (deep sleep, <1 µA) until a value crosses a limit — freezer above 0 °C, vibration above 2 g, door opened. On trigger it:
1. Wakes fully
2. Captures a burst of N samples at high rate (to characterize the event)
3. Advertises an alert frame with the peak value + timestamp
4. Returns to deep sleep
This is the edge intelligence pattern: the beacon decides *whether* to speak, not just *what* to say. Average current collapses to the sleep floor plus rare burst energy.
4.4 Power budget worked example
Coin cell CR2032 = 225 mAh = 810 C. Target life 2 years = 17.5 k hours.
| Component | Current | Duty | Average |
|---|---|---|---|
| nRF52 sleep | 0.6 µA | 99.9% | 0.6 µA |
| Radio adv (3 ms @ 15 mA, 1/2 s) | 15 mA | 0.6% | 90 µA* |
| Sensor sampling (5 ms @ 200 µA, 1/min) | 200 µA | 0.0008% | 0.017 µA |
| Accel wake-on-motion | 2 µA | 100% | 2 µA |
| Total (periodic) | — | — | ~92.6 µA |
| Total (threshold, silent 99%) | — | — | ~2.6 µA |
*The radio dominates periodic mode. Switching to threshold-triggered drops total current 35×, extending life from ~2.5 years to decades (cell self-discharge becomes the limit).
5. On-Device Filtering and Trigger Logic
Sending every raw reading wastes bandwidth and battery. The beacon should filter:
- Deadband: only advertise if |value − last_sent| > Δ (e.g. 0.3 °C). Suppresses noise-driven chatter.
- Moving average: average 4-8 samples to kill single-sample spikes before comparing to deadband.
- Rate-of-change: trigger if d(value)/dt exceeds a slope (e.g. temp dropping 5 °C/min = failing compressor).
- Hysteresis: use separate enter/exit thresholds (e.g. alert at >0 °C, clear at <−0.5 °C) to avoid alert flapping at the boundary.
A minimal state machine:
state: IDLE
on timer tick:
v = read_sensor()
v_f = lowpass(v)
if |v_f - last_sent| > deadband:
advertise(v_f); last_sent = v_f
if v_f > high_thr and state == IDLE:
state = ALERT; advertise_alert(v_f, reason=HIGH)
if state == ALERT and v_f < low_thr:
state = IDLE; advertise_clear()
This logic runs in <1 ms on the nRF52 and costs negligible current versus the radio.
6. Real Deployment Patterns
6.1 Cold chain monitoring
A temp+humidity beacon in a vaccine cooler. Sample every 5 min, advertise on deadband (0.2 °C) or threshold (>8 °C alert, <-0.5 °C clear with hysteresis). The gateway logs the time series; a breach triggers a push notification. Battery: CR2477 (1000 mAh) lasting 3+ years at 5-min sampling.
6.2 Predictive maintenance vibration
An accel beacon bolted to a motor. In wake-on-motion, it captures 1 kHz vibration bursts only when the motor runs. RMS acceleration over a window indicates bearing wear. Comparing week-over-week RMS trends (computed at the gateway) predicts failure before breakdown. Power: the accel’s 2 µA idle dominates; a CR2032 lasts 1-2 years.
6.3 Smart building occupancy
A light+PIR (or mmWave) beacon in each room. Light level + passive IR gives presence without cameras. The beacon advertises occupancy state changes only (enter/leave), not continuous lux. Average current dominated by the PIR’s ~5 µA quiescent — a AA pack lasts years.
7. Gateway-Side Considerations
The gateway must understand the custom MSD frame. Practical points:
- Scan all 3 channels: sensor frames alternate with identity frames; catching both needs multi-channel dwell of ≥2-3 advertising intervals.
- Parse the bitmap: decode only the sensors present; ignore unknown frame types gracefully.
- Handle sequence numbers: detect dropped frames (gap in seq) to flag unreliable links.
- Time-series store: write to InfluxDB/TimescaleDB; the beacon itself keeps no history.
- Calibration offset: apply per-device calibration constants server-side (each sensor has a bias).
8. Pitfalls
- I2C bus lockup: a sensor that misses a clock can hold SDA low and freeze the bus. Add a recovery sequence (toggle SCL 9×, send STOP) on every wake.
- Cold-start offset: TMP117 needs 15.5 ms average conversion; sampling too fast returns stale data. Respect t_conv.
- Battery voltage as temperature proxy: many beacons report V_batt, but under load the voltage sags — measure battery only after a rest period for accuracy.
- RH condensation: HDC2080 reads 100 % RH if condensation forms; the gateway should flag sustained 100 % RH as a fault, not a valid reading.
- Advertising collision: adding a sensor frame doubles airtime; at high beacon density this raises collision probability (see ad collision article). Keep the sensor frame short and on a separate advertising interval if possible.
9. Design Checklist
- [ ] Pick sensors whose current fits the cell (avoid 18 mA CO2 on coin cell)
- [ ] Use a secondary advertising frame; keep the identity frame for location
- [ ] Pack with fixed-point + bitmap; delta-encode slow drift
- [ ] Set deadband + hysteresis to suppress chatter
- [ ] Prefer threshold/wake-on-motion over fixed fast sampling
- [ ] Add I2C bus recovery on every wake
- [ ] Document the MSD layout so the gateway can parse it
- [ ] Verify battery model against measured sleep current, not datasheet typ
10. Conclusion
A sensor beacon is an edge device, not just a transmitter. The engineering value is in the decisions made on-device: when to sample, when to stay silent, and what minimal bytes to send. With fixed-point packing, a bitmap-driven payload, deadband filtering, and threshold-triggered bursts, a coin-cell Beacon can report environmental condition for years while speaking only when it matters.
The radio was never the bottleneck — the sensor current and the sampling cadence are. Design the wake logic first; the advertising encoder is the easy part.
