Chapters

18 Python BLE: RSSI Smoothing and Zones

bluetooth-ble
bt
impl
python
networking
wireless
bluetooth
ble
esp32
lab

18.1 Start With the Decision

This section: Start With the Decision — One weak Bluetooth Low Energy (BLE) packet can make a nearby beacon, a device that broadcasts short radio messages, look far away.

One weak Bluetooth Low Energy (BLE) packet can make a nearby beacon, a device that broadcasts short radio messages, look far away. RSSI is a rough measure of received signal strength, not a distance reading. Smooth a trace of several RSSI readings before a zone, a broad area rather than an exact point, can trigger an action.

18.2 Route Overview

This section: Route Overview — This is part 2 of 3.

This is part 2 of 3. Review Python BLE: Scanning and Connections for the preceding evidence.

18.3 Learning Objectives

This section: Learning Objectives

  • Smooth RSSI samples before classifying proximity zones.
  • Test zone thresholds against path-loss and radio noise.

18.4 Chapter Roadmap

This section: Chapter Roadmap

  • Phoebe’s Field Notes: Why RSSI Distance Moves in Multipliers
  • Try It: RSSI Smoothing and Zone Classification
  • Putting Numbers to It
  • RSSI Limitations
  • Checkpoint: RSSI Smoothing and Zones
  • Try It: ESP32 BLE Advertiser (Pairs with Python Scanner)
  • BLE Power Optimization Decision Flow
  • Visual Reference Gallery
  • Visual: BLE Module Architecture
  • Visual: BLE GATT Profile Implementation
  • Visual: BLE Connection Flow
  • Visual: BLE Stack Architecture
  • Visual: Bluetooth Serial Port Profile
  • Knowledge Check
  • Deployment Pattern: BLE Proximity System for Retail Analytics
  • Checkpoint: Deployment Trade-Offs
  • Concept Relationships:
  • See Also
  • Practice Activities
  • Interactive Quiz: Match Python BLE Concepts
  • Interactive Quiz: Sequence the Python BLE Implementation
  • Label the Diagram
  • Code Challenge
  • Start With the Story

The mathematical gist. With a calibrated −59 dBm at 1 m and path exponent 2.5, a −67.8 dBm sample estimates 2.25 m. A ±6 dB swing is multiplicative, not ±0.8 m: it expands the same estimate to roughly 1.29–3.91 m, so RSSI supports zones rather than centimetre claims.

Math Bridge · guided foundationsWhy can −67.8 dBm mean anywhere from 1.29 to 3.91 m?Let Radio Remi undo the logarithm and carry the ±6 dB uncertainty through it.
Try It: RSSI Smoothing and Zone Classification

Experiment with EMA smoothing parameters and see how they affect proximity zone detection. Adjust the alpha value and watch the smoothed RSSI converge, then observe zone classification in real time.

The EMA filter’s effective window length and response time are:

Neffective=2α−1andtresponse=−ln⁡(0.05)α×fsampleN_{effective} = \frac{2}{\alpha} - 1 \quad \text{and} \quad t_{response} = \frac{-\ln(0.05)}{\alpha \times f_{sample}}

where α\alpha is the smoothing factor and fsamplef_{sample} is the sampling rate (Hz).

Example: RSSI sampling at 1 Hz (once per second) with α=0.3\alpha = 0.3:

  • Effective window: Neffective=20.3−1=5.67≈6N_{effective} = \frac{2}{0.3} - 1 = 5.67 \approx 6 samples
  • Time to reach 95% of new value: tresponse=−ln⁡(0.05)0.3×1=3.00.3=10t_{response} = \frac{-\ln(0.05)}{0.3 \times 1} = \frac{3.0}{0.3} = 10 seconds

Compare with α=0.1\alpha = 0.1 (more smoothing):

  • Effective window: 20.1−1=19\frac{2}{0.1} - 1 = 19 samples
  • Response time: 3.00.1=30\frac{3.0}{0.1} = 30 seconds

Lower α\alpha smooths more aggressively but reacts slower to real movement. For proximity detection, α=0.2-0.3\alpha = 0.2\text{-}0.3 balances noise reduction with reasonable tracking speed.

This section:

RSSI Limitations

RSSI-based distance estimation has inherent limitations:

  • Multipath fading: Reflections cause +/-6 dBm variance
  • Body shadowing: Human body attenuates 5-15 dBm
  • Antenna orientation: Different orientations vary +/-10 dBm
  • Environmental factors: Walls, furniture, humidity affect signal

Recommendation: Use zone-based classification (immediate/near/far) rather than precise distance calculations. For sub-meter accuracy, consider UWB technology instead.

This section:

Knowledge Check: EMA Smoothing Parameters

This section:

Radio Remi, Wireless and radio domain expertCheckpoint: RSSI Smoothing and Zones

You now know:

  • The chapter’s zone boundaries are immediate above -55 dBm, near from about -55 dBm to -70 dBm, and far below -70 dBm.
  • EMA smoothing uses smoothed_rssi = alpha * new_rssi + (1 - alpha) * prev_smoothed, so lowering alpha from 0.3 to 0.1 smooths more but slows response from about 10 seconds to about 30 seconds at 1 Hz.
  • RSSI can vary by multipath, body shadowing, antenna orientation, and environment, so BLE proximity evidence should defend broad zones rather than exact meters.

This section:

Objective: Run an ESP32 as a BLE peripheral that advertises a custom service. In a real setup, you would connect to this device using the Python bleak scanner code above.

Open the simulator directly: Wokwi ESP32 starter project.

Code to Try:

#include <BLEDevice.h>
#include <BLEServer.h>

#define SERVICE_UUID       "181A0000-0000-1000-8000-00805f9b34fb"
#define TEMP_CHAR_UUID     "2A6E0000-0000-1000-8000-00805f9b34fb"
#define HUMIDITY_CHAR_UUID "2A6F0000-0000-1000-8000-00805f9b34fb"

BLECharacteristic *pTempChar, *pHumChar;
bool deviceConnected = false;

class CB : public BLEServerCallbacks {
  void onConnect(BLEServer* s)    { deviceConnected = true; }
  void onDisconnect(BLEServer* s) {
    deviceConnected = false;
    BLEDevice::startAdvertising();
  }
};

void setup() {
  Serial.begin(115200);
  BLEDevice::init("ESP32-EnvSensor");
  BLEServer* srv = BLEDevice::createServer();
  srv->setCallbacks(new CB());

  BLEService* svc = srv->createService(SERVICE_UUID);
  pTempChar = svc->createCharacteristic(TEMP_CHAR_UUID,
      BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
  pHumChar  = svc->createCharacteristic(HUMIDITY_CHAR_UUID,
      BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
  svc->start();

  BLEDevice::getAdvertising()->addServiceUUID(SERVICE_UUID);
  BLEDevice::startAdvertising();
}

void loop() {
  float temp = 22.0 + random(-30, 30) / 10.0;
  float hum  = 55.0 + random(-100, 100) / 10.0;
  int16_t  tBLE = (int16_t)(temp * 100);  // 0.01 C units
  uint16_t hBLE = (uint16_t)(hum * 100);
  pTempChar->setValue((uint8_t*)&tBLE, 2);
  pHumChar->setValue((uint8_t*)&hBLE, 2);
  if (deviceConnected) { pTempChar->notify(); pHumChar->notify(); }
  delay(2000);
}

What to Observe:

  1. The ESP32 advertises as “ESP32-EnvSensor” with a custom Environmental Sensing service
  2. Temperature and humidity values are encoded as BLE-standard int16 in 0.01-degree units
  3. When a client connects, notifications push data automatically every 2 seconds
  4. Try changing the device name in BLEDevice::init() and observe how it affects discovery

18.5 BLE Power Optimization Decision Flow

This section: BLE Power Optimization Decision Flow — When building battery-powered BLE devices, power optimization is critical.

When building battery-powered BLE devices, power optimization is critical. Inspect Figure 18.1 to turn that broad goal into an ordered design review: begin with the application’s actual data-rate and latency need, then choose whether the device needs a connection at all before tuning radio parameters.

BLE power decision flow: start with application data-rate need, decide whether a continuous client connection is needed, choose connection interval or advertising interval, configure sleep mode, tune TX power, and record the responsiveness, maintenance, range, and failure-behavior tradeoff.
Figure 18.1: BLE power optimization decision flow from data-rate need through connection interval, advertising interval, sleep mode, and TX power tuning

Read Figure 18.1 from the data-rate question into its connection and beacon branches. A connected product tunes connection interval; a broadcaster tunes advertising interval. Both paths then converge on sleep mode and transmit power. The sequence matters because reducing TX power cannot compensate for a radio that wakes too often, and a long sleep schedule is unacceptable if it breaks the promised response time. Record those trade-offs alongside the measured current trace.

18.6 Visual Reference Gallery

This section: Visual Reference Gallery

Visual: BLE Module Architecture

Before treating a BLE module as one opaque part, inspect Figure 18.2 to link code-visible behaviour to the controller, radio path, antenna network, and power blocks that produce it.

Modern diagram of BLE module architecture showing radio transceiver, baseband processor, host controller interface, antenna matching, and power management for embedded IoT applications
Figure 18.2: BLE module hardware components

Read Figure 18.2 from the host link to the antenna. The host controller interface carries commands and data into the baseband block. The radio then turns those bits into an RF signal. Antenna matching guides that signal to the antenna. Check the power block last, since a weak supply or poor sleep control can upset every other part. An all-in-one module still needs sound host wiring, local power decoupling, good board placement, and clear space around its antenna.

Visual: BLE GATT Profile Implementation

Inspect Figure 18.3 to ask what a client may do, how both sides read each value, and how they agree that notices are active.

BLE GATT client-server contract separating characteristic properties from value schema and version, permissions and security, descriptors and CCCD subscription state, and the evidence required to prove interoperable reads, writes, and notifications.
Figure 18.3: BLE GATT client-server contract covering properties, schema, security, descriptors, subscription state, and evidence

Read Figure 18.3 from characteristic properties to value schema and version. Properties say what a client may attempt; the schema says how both sides interpret bytes, units, and versions. Check permissions and security before following descriptors to the CCCD subscription state, because a notify property alone does not prove that notifications were enabled. The worked service makes the distinction concrete: its temperature value is a two-byte signed integer scaled by 0.01 °C. A real client must decode that value and prove the promised read, write, or notification behaviour. A connection-only test never reaches these application-contract checks.

Visual: BLE Connection Flow

Inspect Figure 18.4 to ask which step failed when radio discovery succeeds but the app session still cannot exchange a value.

BLE connection evidence path separating discovery, link establishment, security, ATT and GATT readiness, and application exchange, with observable checks and recovery paths. A connected link alone does not mean the GATT application is ready.
Figure 18.4: BLE connection evidence path from discovery through link, security, GATT readiness, application exchange, and recovery

Read Figure 18.4 from the scan to the app exchange. Pause after the radio link forms. That link alone does not mean GATT is ready. The peers may still need to set up trust, find a service, and subscribe to a value. Each step is a separate test point. If one fails, follow the return arrow to the first safe step to try again. Keep the granted interval, peer delay, link timeout, MTU, notice rate, and drop reason in one record. A fast scan has little value if the later steps or the return path cannot be seen and tested.

Visual: BLE Stack Architecture

Inspect Figure 18.5 to locate which layer owns a symptom before changing application code. The controller/host split is especially important when a module exposes HCI rather than running the complete application stack internally.

Read upward from PHY and Link Layer through HCI to L2CAP, ATT, GATT, GAP and Python. Radio, attribute and discovery symptoms belong to different layers; link success is not application-contract success.
Figure 18.5: BLE protocol stack layers

Read Figure 18.5 upward from PHY and Link Layer through L2CAP, ATT, GATT, and GAP, noting where HCI separates controller duties from host protocols. RF loss belongs near the bottom; attribute permissions and values belong near ATT/GATT; discovery roles and advertising behavior belong to GAP. That mapping connects the Python examples to interoperable interfaces and prevents a link-layer success from being mistaken for an application-contract success.

Visual: Bluetooth Serial Port Profile

When a product already speaks UART, inspect Figure 18.6 to see which serial behaviour SPP keeps and which new timing or link faults it adds.

Modern diagram of Bluetooth Serial Port Profile showing virtual COM port emulation for wireless UART communication between microcontrollers and host computers
Figure 18.6: Bluetooth SPP for serial communication

Follow Figure 18.6 from the UART to the host app. RFCOMM and the Bluetooth radio carry the byte stream across the air. At the other end, a virtual COM port lets old serial software use that stream. The simple app view hides more work in the link: the peers must pair, connect, buffer data, and recover after a drop. SPP is handy for setup and old debug tools, but it still needs timeouts and a clear retry plan. Test short writes, link loss, and repeat connections as well as a steady stream.

18.7 Knowledge Check

This section: Knowledge Check

Knowledge Check: BLE Scanning and Filtering
Knowledge Check: GATT Service Exploration
Knowledge Check: BLE Proximity Detection

18.8 Deployment Pattern: BLE Proximity System for Retail Analytics

This section: Deployment Pattern: BLE Proximity System for Retail Analytics — Retail analytics systems often use BLE proximity detection to estimate customer dwell time and foot-traffic patterns.

Retail analytics systems often use BLE proximity detection to estimate customer dwell time and foot-traffic patterns. A typical design places Python gateways on small Linux computers near entrances and key departments, then classifies nearby beacon traffic into immediate, near, and far zones.

System Specifications:

  • Scan interval: 2 seconds, balancing detection speed against gateway CPU load.
  • RSSI threshold: -75 dBm, filtering devices beyond the useful local radius.
  • EMA alpha: 0.2, prioritizing stability over responsiveness for dwell-time estimates.
  • Zone boundaries: -55 dBm and -70 dBm, mapping readings to immediate, near, and far zones.
  • Minimum samples: 3, requiring consecutive readings before assigning a zone.
  • Gateway density: 4 to 6 gateways for a medium retail floor, adjusted after site survey testing.

Why EMA Alpha = 0.2 (Not 0.3)?

An alpha of 0.3 responds faster than 0.2; neither is a standard setting. For an illustrative step from -60 to -80 dBm with a -70 dBm far threshold and one sample every 2 seconds, alpha 0.3 crosses the threshold after 2 new readings (4 seconds), while alpha 0.2 crosses after 4 (8 seconds). A slower filter can suppress a brief body-shadowing dip, but it also delays a real departure; tune alpha and hysteresis from site traces.

Battery Impact on Beacons:

Assume a BLE beacon with a 1000 mAh battery advertising at 1 Hz:

  • Advertising current per event: 8 mA.
  • Event duration, including radio ramp-up: 3 ms.
  • Events per day at 1 Hz: 86,400.
  • Daily energy at 1 Hz: 8 mA x 0.003 s x 86,400 = 2,073.6 mA-s, and dividing by 3,600 converts to 0.576 mAh/day.
  • Estimated battery life at 1 Hz: 1000 mAh / 0.576 mAh = 1,736 days from advertising alone — sleep current and sensor reads shorten this in practice.
  • Estimated battery life at 10 Hz: about 174 days, because daily energy rises to 5.76 mAh/day.

This is why many deployments prefer 1 Hz advertising with gateway-side EMA smoothing. Raising the advertising rate by 10x can make detection feel faster, but it also turns a maintenance interval measured in years into one measured in months.

This section:

Radio Remi, Wireless and radio domain expertCheckpoint: Deployment Trade-Offs

You now know:

  • The retail pattern combines a 2 seconds scan interval, -75 dBm filter, alpha 0.2, -55 dBm and -70 dBm zone boundaries, and 3 consecutive samples before assigning a zone.
  • For a 1000 mAh beacon advertising at 1 Hz, the chapter’s arithmetic gives 0.576 mAh/day and about 1,736 days from advertising alone.
  • Raising the rate to 10 Hz improves responsiveness but cuts the advertising-only estimate to about 174 days, so gateway-side smoothing is often the better maintenance choice.

This section: — At this point the script has enough design context to be reviewed.

At this point the script has enough design context to be reviewed. The remaining sections test whether the same choices survive quizzes, runtime limits, disconnections, and common implementation mistakes.

This section:

Concept Relationships:
  • RSSI filtering and zone-based detection: Threshold filtering reduces noise from distant devices before zone classification runs.
  • EMA smoothing and proximity detection: Exponential moving average stabilizes noisy RSSI readings so zones do not flicker.
  • GATT explorer and service discovery: Enumerating UUIDs maps device capabilities before data access.
  • Beacon protocols and indoor positioning: iBeacon and Eddystone formats provide repeatable advertisement structures for location services.
  • bleak and cross-platform support: One async Python API can target Windows, macOS, and Linux backends.

This section:

18.9 See Also

This section: See Also

18.10 Practice Activities

This section: Practice Activities

18.11 Start With the Story

This section: Start With the Story — A laptop running Python can turn BLE from a black box into a repeatable inspection tool.

A laptop running Python can turn BLE from a black box into a repeatable inspection tool. Scans, filters, UUIDs, GATT reads, notifications, RSSI samples, and exceptions become evidence a team can rerun.

Use this chapter to make automation serve the design review. Write the smallest Bleak script that observes the claim, log the result, and connect the script output back to the device behavior it is meant to prove.

18.12 Continue to the Next Part

This section: Continue to the Next Part — Carry this evidence into Python BLE: Runtime Boundaries and Positioning, which begins with Deep Dive: Bleak Role Boundaries and Runtime Limits.

Carry this evidence into Python BLE: Runtime Boundaries and Positioning, which begins with Deep Dive: Bleak Role Boundaries and Runtime Limits.

Find help

3 reviewed answers.

Where should I start in a module?

Open the module page from the sidebar to see its chapters in order. Use the module map to return to a topic you need.

Modules and paths

Reviewed

What can I try when a project does not work?

Check the symptom, then work through the diagnostic steps. Keep the error message and the smallest example that reproduces it.

When you are stuck

Reviewed

How do interactive examples work?

Use the interactive guide to check how examples and simulations work before changing your project.

Interactive examples guide

Reviewed