BTHome Component
The BTHome component is the core of BTHome for ESPHome, providing BLE broadcasting capabilities for ESP32 and nRF52 platforms.
Platform Support
Section titled “Platform Support”| Platform | BLE Stack | Status |
|---|---|---|
| ESP32, ESP32-S3, ESP32-C3 | Bluedroid (default) | Supported |
| ESP32, ESP32-S3, ESP32-C3 | NimBLE | Supported |
| nRF52840 | Zephyr BT | Supported |
BLE Stack Selection (ESP32 Only)
Section titled “BLE Stack Selection (ESP32 Only)”ESP32 platforms support two different BLE stacks: Bluedroid (default) and NimBLE. The choice of stack affects memory usage, features, and compatibility.
Bluedroid Stack
Section titled “Bluedroid Stack”The default BLE stack for ESP32. Use this when:
- You need compatibility with other ESPHome BLE components (
esp32_ble_tracker,bluetooth_proxy, etc.) - You’re already using the
esp32_blecomponent - You need full BLE functionality (scanning, connections, etc.)
bthome: ble_stack: bluedroid # Default, can be omittedNimBLE Stack
Section titled “NimBLE Stack”A lightweight, standalone BLE stack optimized for broadcast-only scenarios. Choose NimBLE when:
- Memory is limited - Saves approximately 170KB flash and 100KB RAM
- Broadcasting only - Your device only needs to send BTHome advertisements
- No other BLE features needed - You don’t need BLE scanning or connections
- Battery-powered devices - Smaller footprint means less power consumption
bthome: ble_stack: nimbleStack Comparison
Section titled “Stack Comparison”Actual measurements from CPU temperature sensor example on ESP32-S3:
| Feature | Bluedroid | NimBLE | Savings |
|---|---|---|---|
| Flash Usage | 1,101KB (60%) | 886KB (48%) | ~215KB |
| RAM Usage | 47KB (14.3%) | 40KB (12.2%) | ~7KB |
| Broadcasting | Yes | Yes | - |
| Scanning | Yes | No | - |
| Connections | Yes | No | - |
| Encryption Library | mbedtls | tinycrypt | ~7KB |
| Compatible with esp32_ble | Yes | No | - |
| Compatible with bluetooth_proxy | Yes | No | - |
| Best For | Multi-function BLE | Broadcast-only sensors | - |
Supported Devices
Section titled “Supported Devices”NimBLE is available on:
- ESP32 (original)
- ESP32-S3
- ESP32-C3
Other ESP32 variants (ESP32-S2, ESP32-C6) may have varying support depending on ESP-IDF version.
Measurement Rotation (Multi-Packet Support)
Section titled “Measurement Rotation (Multi-Packet Support)”BLE advertisements have a maximum payload of 31 bytes. For devices with many sensors (like weather stations), not all measurements may fit in a single packet. The BTHome component automatically handles this with measurement rotation.
How It Works
Section titled “How It Works”When sensors don’t all fit in one packet, the component:
- Fills the packet with as many measurements as will fit
- Rotates to the next set of measurements on the next advertisement
- Cycles through all sensors over multiple advertisements
For example, with 8 sensors where only 4 fit per packet:
- First advertisement: Sensors 0, 1, 2, 3
- Second advertisement: Sensors 4, 5, 6, 7
- Third advertisement: Sensors 0, 1, 2, 3 (cycle repeats)
Receiver Compatibility
Section titled “Receiver Compatibility”The BTHome mobile app and other receivers automatically merge measurements from multiple packets by sensor type. This means:
- All sensor values are visible in the app
- Values update as each packet arrives
- No configuration needed on the receiver side
Weather Station Example
Section titled “Weather Station Example”esphome: name: weather-station friendly_name: Weather Station
esp32: board: esp32dev framework: type: esp-idf
external_components: - source: type: git url: https://github.com/dz0ny/esphome-bthome ref: main components: [bthome]
i2c: sda: GPIO21 scl: GPIO22
sensor: # BME680 - 4 measurements - platform: bme680 temperature: id: temperature name: "Temperature" humidity: id: humidity name: "Humidity" pressure: id: pressure name: "Pressure" gas_resistance: id: gas name: "Gas Resistance" update_interval: 60s
# Additional sensors - platform: adc pin: GPIO34 id: uv_index name: "UV Index"
- platform: pulse_counter pin: GPIO25 id: wind_speed name: "Wind Speed"
# BTHome broadcasts all sensors with automatic rotationbthome: ble_stack: nimble min_interval: 5s max_interval: 10s sensors: - type: temperature id: temperature - type: humidity id: humidity - type: pressure id: pressure - type: tvoc id: gas - type: uv_index id: uv_index - type: speed id: wind_speedDebug Logging
Section titled “Debug Logging”Enable debug logging to see rotation in action:
[D][bthome:426]: Built advertisement data (27 bytes)[D][bthome:429]: Sensor rotation: starting from index 0/6[D][bthome:438]: ADV: 02 01 06 14 16 D2 FC 40 02 E8 03 03 C2 1E 04 ...The log shows which sensor index the current packet starts from and how many total sensors exist.
Complete Configuration Example
Section titled “Complete Configuration Example”Basic BTHome with NimBLE
Section titled “Basic BTHome with NimBLE”esphome: name: bthome-sensor friendly_name: BTHome Sensor
esp32: board: esp32dev framework: type: esp-idf
logger:
wifi: ap: ssid: "BTHome-Sensor"
captive_portal:
external_components: - source: type: git url: https://github.com/dz0ny/esphome-bthome ref: main components: [bthome]
# I2C bus for BME280i2c: sda: GPIO21 scl: GPIO22
# Temperature and humidity sensorsensor: - platform: bme280_i2c address: 0x76 temperature: id: temperature name: "Temperature" humidity: id: humidity name: "Humidity" update_interval: 30s
# BTHome configuration with NimBLEbthome: ble_stack: nimble # Use lightweight NimBLE stack min_interval: 10s max_interval: 30s tx_power: 3 sensors: - type: temperature id: temperature - type: humidity id: humidityLow-Power Battery Sensor with NimBLE
Section titled “Low-Power Battery Sensor with NimBLE”Maximize battery life using NimBLE’s smaller footprint and deep sleep:
esphome: name: battery-sensor friendly_name: Battery Sensor
esp32: board: esp32dev framework: type: esp-idf
logger: level: WARN # Reduce logging overhead
external_components: - source: type: git url: https://github.com/dz0ny/esphome-bthome ref: main components: [bthome]
# Deep sleep for maximum battery savingsdeep_sleep: run_duration: 10s sleep_duration: 5min
# ADC for battery voltagesensor: - platform: adc pin: GPIO34 id: battery_voltage attenuation: 11db filters: - multiply: 2.0 # Voltage divider compensation
- platform: template id: battery_percent name: "Battery" unit_of_measurement: "%" accuracy_decimals: 0 lambda: |- // Convert voltage to percentage (3.0V = 0%, 4.2V = 100%) float voltage = id(battery_voltage).state; return ((voltage - 3.0) / 1.2) * 100.0;
# BTHome with NimBLE - minimal memory footprintbthome: ble_stack: nimble # Save ~170KB flash, ~100KB RAM min_interval: 1s max_interval: 1s tx_power: -3 # Low power for battery savings sensors: - type: battery id: battery_percentMotion Sensor with Encryption
Section titled “Motion Sensor with Encryption”esphome: name: motion-sensor friendly_name: Motion Sensor
esp32: board: esp32dev framework: type: esp-idf
logger:
external_components: - source: type: git url: https://github.com/dz0ny/esphome-bthome ref: main components: [bthome]
# PIR motion sensorbinary_sensor: - platform: gpio pin: number: GPIO4 mode: INPUT_PULLDOWN id: motion name: "Motion" device_class: motion filters: - delayed_off: 30s
# BTHome with NimBLE and encryptionbthome: ble_stack: nimble # Lightweight stack encryption_key: "231d39c1d7cc1ab1aee224cd096db932" # 32 hex chars min_interval: 30s max_interval: 60s tx_power: 0 binary_sensors: - type: motion id: motion advertise_immediately: true # Instant updates on motionMigration Guide
Section titled “Migration Guide”From Bluedroid to NimBLE
Section titled “From Bluedroid to NimBLE”If you have an existing BTHome configuration using Bluedroid and want to switch to NimBLE:
-
Check for conflicts - Remove any incompatible components:
esp32_bleoresp32_ble_trackerbluetooth_proxy- Any other components that use BLE scanning or connections
-
Add ble_stack option:
bthome:ble_stack: nimble # Add this line# ... rest of your config -
Recompile and flash - The firmware will be smaller and use less RAM
From NimBLE to Bluedroid
Section titled “From NimBLE to Bluedroid”If you need to add BLE scanning or other features:
-
Change or remove ble_stack:
bthome:ble_stack: bluedroid # Or omit this line entirely# ... rest of your config -
Add required components - Now you can use
esp32_ble,bluetooth_proxy, etc. -
Recompile and flash
Troubleshooting
Section titled “Troubleshooting”Build fails with NimBLE
Section titled “Build fails with NimBLE”Symptom: Compilation errors related to NimBLE headers or configuration.
Solution: Ensure you’re using ESP-IDF framework (not Arduino):
esp32: framework: type: esp-idf # RequiredNimBLE conflicts with other components
Section titled “NimBLE conflicts with other components”Symptom: Build errors about conflicting BLE configurations.
Solution: Remove incompatible BLE components (esp32_ble, bluetooth_proxy, etc.) or switch to Bluedroid stack.
High memory usage despite using NimBLE
Section titled “High memory usage despite using NimBLE”Symptom: Still running out of memory even with NimBLE.
Solution:
- Reduce logging level:
logger: level: WARN - Disable unnecessary features
- Use deep sleep to reduce runtime memory usage
- Check for other memory-intensive components
NimBLE scan response limitations
Section titled “NimBLE scan response limitations”Symptom: Device name or other scan response data not visible to receivers.
Explanation: NimBLE uses non-connectable advertising mode optimized for broadcast-only scenarios. Scan response data (which contains device name and ESPHome version) is only transmitted when a scanner actively requests it. Some receivers may not request scan response data from non-connectable advertisers.
What’s included in scan response:
- Device name (Complete Local Name)
- ESPHome version (in Manufacturer Specific Data)
What’s NOT included (to keep within size limits):
- Service UUID (already in main advertisement)
- TX Power Level
- Appearance
Solution: This is a protocol limitation, not a bug. The core BTHome sensor data is always in the main advertisement packet and will be received correctly. If device identification is important, ensure your receiver supports active scanning.
NimBLE host task crashes
Section titled “NimBLE host task crashes”Symptom: “Guru Meditation Error” with stack trace showing nimble_on_sync_ or ble_hs_sync.
Solution: Increase NimBLE host task stack size in your configuration:
esp32: framework: type: esp-idf sdkconfig_options: CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE: "8192"See Also
Section titled “See Also”- ESP32 Platform - ESP32-specific configuration
- Basic Setup - General BTHome configuration
- Encryption - Securing your sensor data
- Power Saving - Battery optimization techniques