Configuring BLE Telemetry for ESPectre CSI Streaming: A Complete Guide
Enable BLE telemetry in ESPectre by setting ble_channel_enabled: true, defining three BLE characteristics (telemetry, sysinfo, control), and binding them to the ESPectre component to stream 8-byte movement-threshold payloads at configurable intervals.
ESPectre is an ESPHome component that captures Wi-Fi Channel State Information (CSI) frames to detect motion and publish telemetry. While it traditionally integrates with Home Assistant via MQTT, the francescopace/espectre repository also supports low-latency Bluetooth Low Energy (BLE) streaming for direct device-to-device communication. Configuring BLE telemetry for ESPectre CSI streaming allows external clients like smartphones or custom controllers to receive real-time movement data without Wi-Fi dependency.
Prerequisites for BLE Telemetry
The BLE telemetry path requires an ESP32-S3 or ESP32 board with Bluetooth support. The implementation relies on the esp32_ble_server library, which is compiled with USE_ESP32_BLE_SERVER by default on ESP32-S3 targets. You must define three distinct BLE characteristics to handle notifications, system information, and control commands.
Step-by-Step Configuration Guide
To enable BLE telemetry, you must declare the BLE server infrastructure in ESPHome YAML and bind the specific characteristics to the ESPectre component using the setter methods defined in components/espectre/espectre.h.
Declare the BLE Server and Characteristics
First, instantiate the esp32_ble_server component and declare three characteristics with the required UUIDs. The telemetry characteristic must support notify, the sysinfo characteristic read, and the control characteristic write properties.
esp32_ble_server:
id: espectre_ble_server
name: "ESPectre S3"
ble_characteristic:
- id: espectre_ble_telemetry
uuid: "0000c001-0000-1000-8000-00805f9b34fb"
properties: [ notify ]
- id: espectre_ble_sysinfo
uuid: "0000c002-0000-1000-8000-00805f9b34fb"
properties: [ read ]
- id: espectre_ble_control
uuid: "0000c003-0000-1000-8000-00805f9b34fb"
properties: [ write ]
Source: This configuration mirrors the reference implementation in examples/espectre-s3.yaml (lines 70–87).
Wire ESPectre to BLE Characteristics
In the ESPectre component configuration, enable the BLE channel and reference the IDs of the server and characteristics you declared above. The set_ble_channel_enabled, set_ble_telemetry_interval_ms, and characteristic setter methods (declared in espectre.h lines 10–21) handle the runtime wiring.
espectre:
id: espectre
ble_channel_enabled: true
ble_telemetry_interval_ms: 40
ble_server: espectre_ble_server
ble_telemetry_char: espectre_ble_telemetry
ble_sysinfo_char: espectre_ble_sysinfo
ble_control_char: espectre_ble_control
Adjust Notification Intervals
The ble_telemetry_interval_ms parameter controls the throttling interval between BLE notifications. The default behavior targets approximately 25 Hz (40 ms), but you can adjust this value to balance update latency against radio congestion. The component stores this value in ble_telemetry_interval_ms_ and compares it against last_ble_telemetry_ms_ before emitting data.
How BLE Telemetry Works Under the Hood
The ESPectre component registers BLE callbacks during setup and transmits data from the CSI manager’s game-mode callback.
Setup and Callback Registration
In ESpectreComponent::setup() (located in components/espectre/espectre.cpp lines 86–100), the code checks ble_channel_enabled_. If the server or any characteristic is missing, it logs a warning and disables the channel (lines 88–92). When valid, it registers connect and disconnect callbacks (lines 93–95) and a write handler for the control characteristic (lines 95–97).
The connect/disconnect handlers set the ble_client_connected_ flag, which acts as a guard clause in the telemetry pipeline.
Game-Mode Callback and Payload Generation
The CSI manager invokes the lambda registered at csi_manager_.set_game_mode_callback (lines 18–34). This callback performs the following steps:
- Connection Check: Exits early if
ble_client_connected_is false (lines 19–21). - Throttling: Validates the interval since
last_ble_telemetry_ms_againstble_telemetry_interval_ms_(lines 22–27). - Payload Packing: Constructs an 8-byte payload containing two little-endian floats (
movementandthreshold) usingmemcpy(lines 28–31). - Transmission: Calls
ble_telemetry_char_->set_value()followed bynotify()(line 33).
The payload structure is always 8 bytes (2 × 4 bytes), representing the current motion value and detection threshold.
Control Command Processing
Data written to ble_control_char is converted to a std::string and passed to handle_ble_control_command_ (lines 66–68). Valid ASCII commands include:
reset– Restarts the ESPectre component and re-runs calibration.gain_lock:on– Forces gain-lock mode.gain_lock:off– Disables gain-lock mode.
Client Implementation Example
You can consume the telemetry stream using Python and the bleak library. The following script connects to the ESP32 and unpacks the binary payload:
import asyncio
import struct
from bleak import BleakClient
BLE_ADDR = "XX:XX:XX:XX:XX:XX"
TELEMETRY_UUID = "0000c001-0000-1000-8000-00805f9b34fb"
def notification_handler(_, data):
if len(data) == 8:
movement, threshold = struct.unpack('<ff', data)
print(f"Movement: {movement:.4f}, Threshold: {threshold:.4f}")
else:
print(f"Received unexpected payload: {data}")
async def main():
async with BleakClient(BLE_ADDR) as client:
await client.start_notify(TELEMETRY_UUID, notification_handler)
await asyncio.sleep(30)
asyncio.run(main())
The script subscribes to notifications and decodes the 8-byte payload into two floating-point values representing the current CSI movement metric and the configured detection threshold.
Summary
- Enable the BLE channel by setting
ble_channel_enabled: truein the ESPectre component configuration. - Declare three BLE characteristics (telemetry for notify, sysinfo for read, control for write) using the standard 16-bit UUIDs provided in the repository examples.
- Bind the BLE server and characteristics to the ESPectre component to activate the telemetry stream.
- Tune the update rate with
ble_telemetry_interval_msto balance latency and throughput. - Parse the 8-byte binary payload on the client side to extract real-time movement and threshold values.
This configuration provides a low-latency, Wi-Fi-independent telemetry path that operates even when the device is isolated from the network, making it ideal for portable motion-based controllers or on-board diagnostics.
Frequently Asked Questions
What is the binary payload format for BLE telemetry?
The payload is exactly 8 bytes containing two 32-bit IEEE 754 floats in little-endian order. The first float represents the current movement value calculated from CSI frames, and the second float represents the detection threshold. The component packs these values using memcpy in components/espectre/espectre.cpp (lines 28–31) and transmits them via the notify() method.
How do I change the BLE update rate?
Set the ble_telemetry_interval_ms parameter in your ESPHome YAML. The default is 40 milliseconds (25 Hz). The component enforces this interval in the game-mode callback by comparing the current timestamp against last_ble_telemetry_ms_ (lines 22–27), ensuring the BLE radio is not overwhelmed by high-frequency CSI events.
Can I use BLE telemetry without Wi-Fi connectivity?
Yes. The BLE transport operates independently of Wi-Fi. The ble_client_connected_ flag (managed by connect/disconnect callbacks in espectre.cpp lines 93–95) gates all telemetry transmission. As long as a BLE client is paired, you will receive CSI data even if the ESP32 is not connected to a Wi-Fi network or MQTT broker.
How do I send control commands to the ESP32 via BLE?
Write ASCII strings to the control characteristic (UUID 0000c003-0000-1000-8000-00805f9b34fb). The component parses these commands in handle_ble_control_command_ (lines 66–68). Supported commands include reset to recalibrate the sensor, and gain_lock:on or gain_lock:off to toggle the automatic gain control lock.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →