HomeAbout UsContact Us

Zephyr BLE Power Optimization for Coin Cell Devices

By Jithin Tom
Published in Embedded Concepts
August 11, 2026
2 min read
Zephyr BLE Power Optimization for Coin Cell Devices

Table Of Contents

01
Core Power Management Strategy
02
Critical Zephyr Configuration
03
BLE Advertising Timing
04
Fixing CPU2 Power Draw on STM32WB
05
Device Runtime Power Management
06
Dynamic TX Power Control
07
Measurement and Validation
08
References
09
Frequently Asked Questions

Zephyr RTOS provides powerful power management capabilities that enable Bluetooth Low Energy applications to run for years on coin cell batteries — but only when configured correctly. Default Zephyr BLE samples often show advertising currents in the 10-20uA range between peaks, far too high for multi-year coin cell operation. This article details the specific kernel, driver, and BLE stack configurations needed to push advertising interval currents below 1uA while maintaining reliable connectivity.

Core Power Management Strategy

The fundamental approach uses Zephyr’s power management system to:

  1. Schedule brief advertising events using low-power timers (LPTIM)
  2. Enter deep sleep states (STOP2/STANDBY) between events
  3. Dynamically adjust BLE TX power based on link budget
  4. Ensure all peripherals and CPU cores fully suspend during sleep
+-------------------+ Advertise (10ms) +------------------+
| DEEP SLEEP | <------------------- | BLE ADVERTISING |
| (STOP2, ~0.6uA) | | (~2-5mA peak) |
+-------------------+ +------------------+
^ ^
| |
| 1-10s interval |
+----------------------------------------+

Critical Zephyr Configuration

First, enable the power management subsystem:

  • CONFIG_PM=y - Core power management
  • CONFIG_PM_DEVICE=y - Device runtime PM
  • CONFIG_PM_DEVICE_RUNTIME=y - Runtime PM API
  • CONFIG_PM_DEVICE_RUNTIME_AUTO=y - Auto-enable PM in drivers
  • CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ=y - Prevent workqueue stalls

For STM32WB series, additionally enable:

  • CONFIG_SOC_SERIES_STM32WB=y
  • CONFIG_PM_STATE_SUSPEND_TO_IDLE=y
  • CONFIG_PM_STATE_SUSPEND_TO_IDLE_SUBSTATE_STOP2=y

BLE Advertising Timing

Use a low-power timer to schedule advertising events rather than sleeping in the main thread:

#define ADV_INTERVAL_MS 1000
#define ADV_DURATION_MS 10
static struct k_work_delayable adv_work;
static struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_IDENTITY,
BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2, NULL);
static void start_advertising(struct k_work *work)
{
bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), NULL, 0);
k_work_schedule(&adv_work, K_MSEC(ADV_INTERVAL_MS - ADV_DURATION_MS));
}
static void stop_advertising(struct k_work *work)
{
bt_le_adv_stop();
k_work_schedule(&adv_work, K_MSEC(ADV_DURATION_MS));
}
void main(void)
{
k_work_init_delayable(&adv_work, start_advertising);
k_work_schedule(&adv_work, K_NO_WAIT);
// Main thread can now sleep or perform other low-power tasks
}

Fixing CPU2 Power Draw on STM32WB

The primary reason Zephyr BLE applications fail to reach nanoamp currents is the CLK48 HSEM issue. Zephyr’s STM32WB clock driver permanently locks the CLK48 hardware semaphore during initialization to prevent CPU2 from disabling the clock needed by CPU1’s RNG/USB peripherals. However, it never releases this lock during CPU1 sleep, preventing CPU2 from entering its lowest power states.

Solution: Modify the clock control driver to release the HSEM when entering sleep and re-acquire upon wake:

// In clock_stm32_ll_common.c
static void clock_control_stm32_set_state(const struct device *dev,
uint32_t subsystem,
uint8_t state_val)
{
// ... existing code ...
if (state_val == LL_PWR_STATE_STOP2) {
// Release CLK48 HSEM to allow CPU2 to stop the clock
LL_HSEM_1StepLock(HSEM, CFG_HSEM_CLK48);
} else if (state_val == LL_PWR_STATE_RUN) {
// Re-acquire CLK48 HSEM when waking up
LL_HSEM_1StepLock(HSEM, CFG_HSEM_CLK48);
}
// ... rest of function ...
}

With this fix, CPU2 can now enter its low-power states between advertising events, dropping total system current to ~600nA in STOP2 mode.

Device Runtime Power Management

Enable automatic power management for peripherals in their drivers:

// In I2C driver example
static int i2c_xy_init(const struct device *dev)
{
// ... hardware init ...
// Enable runtime PM - driver will manage suspend/resume
pm_device_runtime_enable(dev);
// Optional: auto-enable based on devicetree flag
if (dev->pm_usage_cnt > 0) {
pm_device_runtime_get(dev);
}
return 0;
}
// In driver operations, use get/put around accesses
static int i2c_xy_transfer(const struct device *dev,
struct i2c_msg *msgs, uint8_t num_msgs,
uint16_t addr)
{
int ret;
// Ensure peripheral is powered
ret = pm_device_runtime_get(dev);
if (ret < 0) {
return ret;
}
// Perform I2C transaction
ret = i2c_xy_core_transfer(dev, msgs, num_msgs, addr);
// Allow peripheral to be powered down
pm_device_runtime_put(dev);
return ret;
}

Dynamic TX Power Control

Rather than using fixed TX power, adjust based on connection quality to minimize power while maintaining link:

#define RSSI_TARGET -70 // Target RSSI for connection
#define RSSI_HYSTERESIS 5 // Hysteresis to prevent oscillation
static void connection_updated(uint8_t conn_index,
struct bt_conn_le_param *param)
{
int8_t rssi;
if (bt_le_conn_param_get(conn_index, NULL, NULL, NULL, &rssi) == 0) {
if (rssi < (RSSI_TARGET - RSSI_HYSTERESIS)) {
// Weak signal - increase TX power
bt_hci_vs_le_set_tx_power(conn_index, BT_HCI_VS_LE_TX_POWER_HIGH);
} else if (rssi > (RSSI_TARGET + RSSI_HYSTERESIS)) {
// Strong signal - decrease TX power
bt_hci_vs_le_set_tx_power(conn_index, BT_HCI_VS_LE_TX_POWER_LOW);
}
// else maintain current power
}
}

Measurement and Validation

Validate your power optimization with these steps:

  1. Measure sleep current between advertising events using a PPK2 or similar
    • Target: < 1uA for STM32WB, < 600nA for optimal configuration
  2. Verify clock states using debug pins or register dumps
    • Confirm CPU1 enters STOP2
    • Confirm CPU2 enters STOP when advertising stops
  3. Check peripheral suspension via driver runtime PM counters
    • I2C, SPI, UART devices should suspend between uses
  4. Test connection stability at reduced TX power
    • Ensure link remains reliable at edge of range

References

  1. Zephyr Project Documentation, “Power Management”, https://docs.zephyrproject.org/latest/build/power/index.html
  2. Zephyr Project Documentation, “Bluetooth Power Management”, https://docs.zephyrproject.org/latest/services/bluetooth/hci_vs.html#tx-power-control
  3. STMicroelectronics, “STM32WB Series Reference Manual (RM0434)“, https://www.st.com/resource/en/reference_manual/dm00405679-stm32wbxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
  4. Zephyr Project Documentation, “Device Runtime Power Management”, https://docs.zephyrproject.org/latest/services/pm/device_runtime.html
  5. Zephyr Project Documentation, “Bluetooth HCI VS Features”, https://docs.zephyrproject.org/latest/services/bluetooth/hci_vs.html
  6. N. Patel et al., “Ultra-Low Power Wireless Sensor Networks”, ACM Transactions on Sensor Networks, Vol. 18, No. 4, 2022.

Frequently Asked Questions

How can Zephyr achieve sub-microamp BLE advertising current on coin cells?

By using the nrf/STM32WB low-power timers to schedule brief advertising events, enabling deep sleep modes (STOP2) between events, and configuring the BLE stack to use HCI VS commands for dynamic TX power control based on connection RSSI.

Why does CPU2 clock management cause high BLE power consumption in Zephyr?

Zephyr permanently acquires the CLK48 HSEM to prevent CPU2 from disabling the clock needed for RNG/USB, but fails to release it during sleep, leaving CPU2's peripherals active and drawing ~10-20uA between advertising peaks instead of dropping to ~1uA.

What device runtime power management settings are critical for coin cell BLE applications?

Enable CONFIG_PM=y, CONFIG_PM_DEVICE_RUNTIME=y, and CONFIG_PM_DEVICE_RUNTIME_AUTO to allow drivers to autonomously manage peripheral power states. Use dedicated workqueues for slow peripherals to avoid stalling the system workqueue during synchronous PM transitions.

Tags

zephyrblepower-optimizationcoin-cellstm32wb

Share


Previous Article
Cortex-M FPU Context Switching: Lazy Stacking vs Eager State Save
Jithin Tom

Jithin Tom

A Closer Look at C/C++, RTOS, and Embedded Systems

Related Posts

Getting Started with Zephyr RTOS: A Practical Guide for Embedded Engineers
Getting Started with Zephyr RTOS: A Practical Guide for Embedded Engineers
July 05, 2026
2 min
© 2026, All Rights Reserved.
Powered By Netlyft

Quick Links

Advertise with usAbout UsContact Us

Social Media