As I get closer to finishing v0.1 of my Zigbee F.A.R.T. sensor, I continue to look at power consumption. This is my first post of 2025, so Happy New Year!

I thought I was doing okay!

My initial testing had the sensor sipping an average 400µA. Some more tinkering got that down to around 300µA. I felt that was pretty good as it meant a battery life of ≃ 280 days. Well over my six months target, but it was higher than I expected.

After a chat with Peter Eastern on BlueSky, he suggested that my voltage divider arrangement would be consuming power. A quick calculation showed that the pair of them would use 330µA.

When you apply a voltage over the divider, current will flow. My divider is made up of a 10kΩ resistor and a variable NTC thermistor. For simplicity, let’s assume the NTC is at 10kΩ too. That gives a total of 20kΩ resistance and a potential of 3.3v. Using Ohm’s law, this gives a current of 165µA. We have two dividers, yielding the 330µA.

Would this explain the high consumption figure when the nRF52840 was supposed to be asleep?

The importance of correct testing

I decided to check the consumption of the dividers. When I sat down at my desk to check, I had a horrible realisation! I was measuring the consumption of the Seeed XIAO nRF52840 board by itself.

When testing the code I was using a simple potentiometer divider. But when testing power consumption, I was only testing the chip on its own!

Running the consumption test again, with the simple divider circuit attached, more than *doubled* the consumption! It rose from an average of around 300µA to well over 600µA!

Here you can see the before and after. Not good!!

This confirmed Peter’s assessment that the voltage dividers were indeed using power. It also meant that my device’s battery life was now only 140 days. Short of my target six months 😦

First things first

I needed to remove the consumption from the voltage dividers to keep the six months of battery life.

Peter suggested I should turn off the power to the dividers when they weren’t being used. This would eliminate the constant current flowing across the dividers, lowering their average consumption.

I needed to turn on the power to the dividers, take some measurements using the ADC (analog-to-digital converter) and then turn it off. This seemed like a job for one of the GPIOs.

Based on the layout of my PCB, one of the pins on the left would be perfect.

I was using P0.03 (A1) and P0.04 (A4) to take ADC readings already. This made P0.28 and P0.29 ideal candidates. To clean up the PCB layout, I moved the 2nd ADC to P0.29 and configured P0.28 as power.

The reading thread would then toggle the power on and off.

// Switch on the power pin.
//
int err = gpio_pin_set_dt(&divider_power, 1);
// Let the voltage stabalise.
//
k_sleep(K_MSEC(100));
uint16_t temperature_1 = read(0);
uint16_t temperature_2 = read(1);
// Switch off the power pin.
//
gpio_pin_set_dt(&divider_power, 0);

This worked well and since the GPIO output was nicely regulated, I got a nice resistance reading!

This now meant the 300µA drawn from the voltage divider was short lived.

I decided to see what else I could trim off the remaining 300µA average consumption.

Finding that extra power usage!

I began this investigation I decided to see what the power consumption was like when the chip was doing nothing. If an idle chip was still drawing 300µA, I’d have no hope!

From what I had read, the nRF52840 had two sleep modes, “System On” and “System Off”. The former being a light sleep and the latter being a deep sleep.

“System Off” is when the chip is completely switched off. I knew this wasn’t for me as the Zigbee threads would be waking up to ping. This would mean the chip rebooting a lot.

“System On” puts the chip to sleep, but it’s still awake. From Nordics documentation, even “System On” requires only a few µA. It’s also a mode the chip enters automatically when it senses it’s doing nothing.

I replaced the main() method with this basic block of code. When the main thread when to sleep, the chip should put itself into “System On” as the thread is idle.

int main(void)
{
    LOG_INF("Starting Zigbee FART Sensor");
	
    while (1)
    {
	k_sleep(K_FOREVER);
    }
}

Running this simple code still yielded an average of 261µA!!

From Nordic’s own power guide, if the CPU is idle and you’re seeing 100s of µA, then it’s a peripheral. I wasn’t even sure at this point that the CPU was inactive! Was k_sleep(K_FOREVER) enough to put the chip to sleep??

I decided I need to start at the bottom and try and build up an understanding of what is happening. Thankfully, as ever, somebody else had done it!

https://forum.seeedstudio.com/t/low-power-with-xiao-nrf52840-on-zephyr-rtos/270491

I pulled down the code and tried to compile it, but the method pm_state_force caused the compilation to fail. I resolved that by replacing it with a call to sys_poweroff.

Running it gave me very surprising results! When I put the unit into SYSTEM OFF, the consumption was tiny at 2.3µA!

System Off consumption is tiny!!

Pressing the “do work” button (which lights the blue LED) saw consumption rise to around 300µA. When I toggled off the LED the power consumption dropped to 3.9µA!

Toggled the LED to the off state.

The “system off” and “idle” consumption figures are higher than the blog post, but we’re talking 1 or 2µA. That’s probably manufacturing tolerance at this stage.

I now knew the XIAO nRF52840 *could* run down in the single micro amps. Armed with that, I was sure I reduce my Zigbee consumption further!

One of the comments I spotted whilst reading the sample was this:

Prevent deep sleep (system off) from being entered on long timeouts or `K_FOREVER` due to the default residency policy.

I wondered if this explained why my Zigbee sensors dropped offline after indeterminate period of time? My code does use K_FOREVER. Something to return to 🙂

Fixing the power consumption

I started with the overlay. I hadn’t turned off all the SPI peripherals, so I added those.

I took out my ADC configuration too.

This made no real difference

Consumption remains high after disabling some of the peripherals

Next, I looked to the QSPI flash code. I’m not 100% sure what this is all about, but the code in the sample was turning it off. I copied in the code and bingo! Power consumption plummeted!!

Turning off the QSPI flash dropped consumption to just 3µA!

With the empty block using no power, I turned back on my ADC code. This takes a reading from two ADC pins every thirty seconds. Amazingly, the consumption averaged 3.26µA over two minutes and the ADC spikes can be seen every 30 seconds!

Four ADC readings taken in two minutes

Very promising!

Of course, taking ADC readings is no use with the Zigbee code to send them……

With the Zigbee code running, average consumption is only 25.13µA

Speechless! Only 25µA! The Zigbee radio is working as I can see the massive 15mA spikes. Home Assistant also reported the sensor checking in!

Final results

I let the sensor run over the course of a minute, and the consumption was at 22µA, which is amazing.

But when I came to connecting the device to Home Assistant, the interview just wouldn’t complete 😦

When this happens, the advice is always “check for interference”, meaning the problem is radio based.

I thought of this line I copied from the power sample

nrf_802154_tx_power_set(8);

This lowers the power of the radio to reduce consumption. Too low it seems to allow HA to configure it. I have Zigbee light bulbs in my office light, so the coverage should be very good. But the power of 8 is obviously just too low. I commented out that line and sure enough, I paired the device successfully.

As expected, this raised the average consumption over one minute to 40µA.

With the TX power at full, consumption rises to 40µA

Don’t get me wrong, I’m really happy with 40µA, as it’s a far cry from the 600µA I started with. The 40µA would be 240 days on a coin battery, but a whopping 5 years on a 2000mAh one!

I’m sure there is room to reduce the power consumption by tinkering with the TX power. This is software based, so I’m not too concerned about it now. When I did this testing the MCU was in a nest of wires on a breadboard, which probably didn’t help.

One observation is that the measured temperature is only 24.0°C. It should be much closer to 25 as I’ve balanced the Potentiometer. Feels like the value was being rounded. Another item for the glitch list 🙂

Next Steps

I’ve paused my PCB order with Aisler.net to and I’ll refine the track layout to facilitate the change in pins. Once I get the new boards, I’ll experiment a little more with the TX power.

This is a good start to 2025 🙂

Update!

I came back up to record a quick short/TikTok and spun up the PPK recorder again.

The sensor’s average consumption is actually being recorded as 15µA!

The number I look at in the bottom left is the *total* average since recording began. My recordings always included the initial Zigbee bootup and network negotiation.

Whilst low at an average of 7mA, that’s *massive* compared to the 15µA or 20µA. If take an average *after* the Zigbee code has settled down, we’re actually only using 16µA.

This is even better news!!!!

At that consumption, we’re talking 1.5 years on a coin battery or an eye water 15 years on a 2000mAh battery.

This is *with* the unchanged TX power too! Amazing.

I’m not going to look at this again, save it goes back up 🙂

Be sure to check out my YouTube channel.

6 responses

  1. WADE J WEPPLER Avatar
    WADE J WEPPLER

    Very helpful series on the Xiao NRF52840! I’ve been working with this module for a bit and trying to get power consumption down to what you’re seeing. I’m having limited success. Using your code, average consumption is around 350uA after the device is paired.

    I’ve changed all the prj.conf options to “n” where you’ve indicated they should be for production. I’m not sure what else I’m missing.

    Is the code in your github the latest?

    Would you be willing to share the compiled production merged.hex file to see if I can get at least the same results on my module?

    Thanks again!

  2. almosthologramce1351fe76 Avatar
    almosthologramce1351fe76

    Very helpful series on the Xiao NRF52840! I’ve been working with this module for a bit and trying to get power consumption down to what you’re seeing. I’m having limited success. Using your code, average consumption is around 350uA after the device is paired.

    I’ve changed all the prj.conf options to “n” where you’ve indicated they should be for production. I’m not sure what else I’m missing.

    Is the code in your github the latest?

    Would you be willing to share the compiled production merged.hex file to see if I can get at least the same results on my module?

    Thanks again!

    1. Yeah, Github is up-to-date.

      Are you using the battery terminals on the rear? What is the voltage of the battery?

      I had a similar experience on an ESP32-C6 where it was actually the voltage regulator drawing that much current (https://tomasmcguinness.com/2025/01/06/lowering-power-consumption-in-esp32-c6/).

      I’d be happy to share my hex file.

      Tom

  3. almosthologramce1351fe76 Avatar
    almosthologramce1351fe76

    Thanks Tomas!

    I’m powering using the PPK @ 3.3V to the 3.3V pins on the Xiao. I hadn’t noticed, but the blue led was solid on the Xiao, running your code after pairing. I re-paired, and the led went out. After that, current was down to very, very low. So, I’m glad I was able to mimic what you have and I at least know my setup should be able to give me the same output.My project is using the external flash for a littlefs filesystem and is also using Bluetooth, not Zigbee. I’m pretty sure it’s the flash not doing the proper DPD stuff causing this issue, but not sure. I’m going to test bit-by-bit (bluetooth first) and see where my problems lie.

    Thanks again!

  4. […] has shown me that insanely low power consumption is possible. I got my nRF52840 based sensor using an average of 16µA. This gives a potential battery life on a coin battery of 1.5 years! Well past my six-month […]

  5. […] even have any additional components to blame, since I was powering the module directly! My experience with the nRF52840 taught me a lot about how to reduce power for Zigbee. However, this project was a different “Matter”. Because I’m using the Matter […]

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.