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

One weak BLE packet can make a nearby beacon look far away. Smooth the RSSI trace before a zone change can trigger an action.

18.2 Route Overview

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

18.3 Learning Objectives

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

18.4 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.31=5.676N_{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.11=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.

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.

Knowledge Check: EMA Smoothing Parameters

Radio RemiCheckpoint: 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.

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

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

Before treating a BLE module as one opaque part, inspect Figure 18.2 to locate the boundaries that can affect integration, radio performance, and power. The aim is to connect firmware-visible behavior to the controller, RF path, antenna network, and supply circuitry 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 controller interface toward the baseband processor and radio transceiver, then follow the RF path through antenna matching to the antenna. Finally inspect power management, because supply stability and sleep control affect every block. This view explains why a convenient integrated module still needs correct host signalling, board placement, power decoupling, and antenna clearance in the finished device.

Inspect Figure 18.3 to review a GATT design as an application contract, not merely a list of UUIDs. Look for the choices that determine what a client may do, how it interprets a value, and how both sides establish notification state.

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, then check permissions and security before following descriptors to CCCD subscription state. The evidence path at the end asks whether real clients can read, write, and receive notifications with the agreed encoding. That ordered review connects the Python implementation to interoperable behavior and exposes contracts that a connection-only test would miss.

Inspect Figure 18.4 to separate radio discovery from a usable application session. The path matters because each boundary can succeed while a later one still fails, and each failure needs different evidence.

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 discovery into link establishment, then through security, ATT/GATT readiness, and application exchange. Follow each recovery arrow back to the earliest state that can be retried safely. This order connects latency and power tuning to correctness: shortening discovery or connection time is useful only when security, service discovery, subscriptions, data exchange, and reconnect behavior remain observable and reliable.

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.

When an existing product already speaks UART, Figure 18.6 shows what Bluetooth SPP preserves and what it inserts between the endpoints. Inspect it before assuming that a wireless serial link has the same timing and failure behavior as a physical cable.

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 microcontroller UART into RFCOMM and the Bluetooth radio, then out through the host’s virtual COM port. The application can retain a serial-stream interface, but pairing, link establishment, buffering, disconnection, and reconnection now sit in the path. That makes SPP useful for legacy debugging and configuration while requiring explicit timeout and recovery handling.

18.7 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

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)?

The standard alpha of 0.3 works well for single-device tracking, but in a crowded retail environment with 50-200 simultaneous BLE advertisers, lower alpha reduces false zone transitions caused by body shadowing. A customer stepping behind a display rack causes a sudden 10-15 dBm drop. With alpha 0.3, the smoothed RSSI reacts in 2 readings (4 seconds), potentially triggering a false “far” classification. With alpha 0.2, it takes 4 readings (8 seconds) — long enough for the customer to move again, preventing a spurious zone change.

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.

Radio RemiCheckpoint: 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.

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.

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.

18.9 See Also

18.10 Practice Activities

18.11 Start With the Story

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

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