Sensor Power Management
The application from the previous page reads the BME280 every 10 seconds. In between, the sensor draws roughly 3.4 µA in normal mode and 0.1 µA in sleep. On a 225 mAh CR2032 coin cell, this difference is the gap between weeks and years of runtime.
Zephyr's runtime power management (PM) lets you suspend a device between uses with a single function call — if the driver implements it.
Enabling runtime PM
Add to prj.conf:
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE=y enables the per-device suspend/resume interface.
Suspending and resuming the BME280
Wrap each sensor_sample_fetch from the previous page with PM calls:
#include <zephyr/pm/device.h>
static const struct device *bme280 = DEVICE_DT_GET(DT_NODELABEL(bme280));
int main(void)
{
while (1) {
/* Wake the sensor */
pm_device_action_run(bme280, PM_DEVICE_ACTION_RESUME);
/* BME280 needs ~2 ms to leave sleep mode */
k_sleep(K_MSEC(2));
sensor_sample_fetch(bme280);
struct sensor_value temp, press, hum;
sensor_channel_get(bme280, SENSOR_CHAN_AMBIENT_TEMP, &temp);
sensor_channel_get(bme280, SENSOR_CHAN_PRESS, &press);
sensor_channel_get(bme280, SENSOR_CHAN_HUMIDITY, &hum);
LOG_INF("T: %d.%06d C P: %d.%06d kPa",
temp.val1, temp.val2, press.val1, press.val2);
/* Suspend the sensor again */
pm_device_action_run(bme280, PM_DEVICE_ACTION_SUSPEND);
/* Sleep for 10 seconds */
k_sleep(K_SECONDS(10));
}
}
That's the whole change. The driver decides what RESUME and SUSPEND actually mean for the BME280 — typically writing the ctrl_meas register to switch between normal and sleep mode.
Does the driver support PM?
Not all Zephyr drivers implement PM. Check:
grep -n "pm_device_action\|PM_DEVICE_DT_INST_DEFINE" \
zephyr/drivers/sensor/bosch/bme280/bme280.c
If PM_DEVICE_DT_INST_DEFINE appears, the driver supports runtime PM.
If you call pm_device_action_run() on a driver that doesn't implement PM, it returns -ENOTSUP silently. Always check the return value during development:
int ret = pm_device_action_run(bme280, PM_DEVICE_ACTION_SUSPEND);
if (ret && ret != -ENOTSUP) {
LOG_ERR("PM suspend failed: %d", ret);
}
What PM actually does to the BME280
When you call PM_DEVICE_ACTION_SUSPEND on the BME280:
- The Zephyr driver writes
mode = 00to thectrl_measregister (sleep mode) - The chip drops to ~0.1 µA
When you call PM_DEVICE_ACTION_RESUME:
- The driver writes
mode = 11(normal mode) toctrl_meas - The chip restarts continuous measurement after the configured oversampling delay
This is the consumer side of PM — you call into the driver. The next page (Writing Drivers) shows the other side: how the driver implements PM_DEVICE_ACTION_SUSPEND and _RESUME for itself.
Runtime PM — let Zephyr ref-count for you
The example above does manual suspend/resume. For multi-threaded code, Zephyr's runtime PM is safer — Zephyr reference-counts the requests so the chip stays awake as long as any caller needs it:
CONFIG_PM_DEVICE_RUNTIME=y
pm_device_runtime_get(bme280); /* +1 ref, wakes if needed */
sensor_sample_fetch(bme280);
pm_device_runtime_put(bme280); /* -1 ref, suspends at 0 */
If two threads call _get and only one calls _put, the chip stays on. Both must _put to suspend.
BLE + I2C conflict during PM
On ESP32, the radio and I2C share the clock tree. With CONFIG_PM=y, there are occasional I2C NACK errors when a sensor fetch happens during a BLE connection event.
Fix: delay the sensor fetch by 5–10 ms after a BLE connection interval boundary:
/* Wait until we're not in a BLE connection event */
k_sleep(K_MSEC(5));
sensor_sample_fetch(bme280);
This was observed on a real ESP32 + BME280 build.
This is a known issue with ESP32 when CONFIG_BT=y and CONFIG_PM=y are both active. It's not a bug — it's a hardware constraint. The 5 ms workaround is reliable in practice.
Next: write a driver that implements these PM hooks
You now know how to use a PM-capable driver. The next page — Writing Drivers — shows the other side. You've described the BME280 in devicetree, validated it with a binding, enabled it through Kconfig, read from it via the sensor API, and suspended it with PM. The final step is to write the driver beneath all of that yourself.