DEV Community

Jared Wolff
Jared Wolff

Posted on • Originally published at on

Better Battery Life for Delightful Particle Deployments

Post image

IoT devices (almost) always need batteries. With those batteries some important things to think about:

  • How long your device will last.
  • How your device will perform in the environment.
  • How you'll re-charge or replace the batteries when they die

In this post, you'll learn how I optimized every microamp used by a Particle Xenon based motion sensor. You'll learn about the the roadblocks I encountered, the fixes and how get your designs low power too!


The Noodling Begins

After noodling around on the project's direction, I decided to keep it simple. Only one sensor. The sensor? PIR.

PIR stands for Passive Infrared Sensor. Ever walk into a building to see a blinking box in the upper corner? That, my friend, is a PIR sensor. They're used for motion detection for security systems and light control.

motion sensor

What if we can evolve this sensor and give it some more capabilities. Say, connect it to the internet? That opens a few more doors.

If you've read any of my other recent articles, you know that i'm a big fan of Particle. Especially what they're doing with their mesh platform. So much so that I'm writing a guide about it. In this project, we'll use the Particle Xenon to make our motion sensor smart. That way you can have instantaneous motion alerts anywhere.

The final design of the motion sensor board uses a TI TLV8544PWR. It's an ultra-ultra low-power rail-to-rail op amp. Its primary purpose is signal processing of the PIR sensor. In a typical case, it draws about 1.2µA (!!) when all the op-amps are active.

The design also sports a PCF85063ATL/1 which is a standalone low-power real time clock. This device, along with the PIR, can wake up the Particle Mesh device from sleep. For the purposes of this project, it's used to limit the message count when a new PIR event occurs. i.e. a PIR interrupt is only allowed every 2 minutes.

Motion Sensor Block Diagram

There are other various parts in the mix but the above block diagram is this board at it's essence.


The main problem came when I had to actually assemble the device. I was abroad and had zero tools. Luckily there was a FabLab nearby that allowed me to use their tools. (And boy, was I grateful)

Here's a few pictures from the assembly process.

Unpopulated circuit board ready to go.

I also had no jig to hold the boards. So, a quick sketch in Fusion360 and some time on the laser cutter yielded some fantastic results!

Laser cut jig taped down + Unpopulated circuit board

This board also happened to be the first that I used a metal stencil on. Oh man was it a dream. It's much more consistent compared to Kapton based stencils.

Post solder paste application

Here's what the boards look like after assembly. I had some issues with bridging around the op-amp but that was easily addressed with some flux and a hot iron.

Back Side of Motion Board

Here's the front of the design. Nothing but a PIR!

Motion Board Top Side

If you do ever find yourself assembling a board like this one here are a few tips:

  • Most PIR sensors are extremely sensitive to heat. That means you must be careful when applying your soldering iron. The data sheet states that the temperature must be 350°C and you shouldn't apply the tip for more than 3 seconds.
  • In this design there are some small parts. Normally you can use a microscope to assemble. If you're out of town, like I was when I assembled these boards, you can use your cell phone. Use the camera's pinch-zoom feature to check part orientations and shorts between pins.
  • In this design, there's a ton of bulk capacitance on the input to the PIR sensor. This takes a while to charge especially if you're using a large 100µf capacitor. In the next design, the motion board will enable monitoring of this voltage. That way the Particle Mesh device will know when the PIR is stable.

Now that we have some hardware, it's time to figure out how to get it all working and as low power as possible. We'll talk about some of the ways we can do that in the next section.

Sleepy Time

Sleep mode allows you to place your device in a low power state. It is often used when your Particle Mesh is idle. The primary goal is to conserve as much battery power as possible.

There are two sleep modes with a few distinct differences. We'll discuss them below:

Stop mode sleep

"Stop mode" is the standard sleep for any Particle Mesh device. It shuts down the network and, when configured, can wake up from an interrupt. Once the device wakes up, it will continue execution where it left off.

You can trigger sleep mode in several different ways. The first way is by setting up a pin interrupt:

    System.sleep(D2, RISING);

This will put the device into stop mode and wait for a rising interrupt on D2. You can connect a sensor IC that toggles D2 when it's done with a sensor reading. Not only will that alert the device a reading is available, but it will also wake the device from sleep!

The Particle Mesh device can use a wakeup timeout when you specify a third argument. Here's an example:

    System.sleep(D2, RISING, 60);

The third argument represents the time, in seconds, the device will remain in sleep. If no pin interrupt occurs, the device will wake back up after that interval. In this case, the device is set to wake up after 60 seconds if no event occurs on D2.

Deep sleep

There is another lower power option:

deep sleep.

Like stop mode, deep sleep mode will disconnect your device, and not retain the RAM. When the device wakes up, start execution from the beginning of your application. Any variables in memory will get reset. Data in non-volatile memory, like EEPROM, is not affected.

It also has a 'wakeup pin' but unlike stop mode, you can't choose the pin. (The default is D8)

You can enter deep sleep by running executing the following function.



Measuring current on Xenon

So, what's the actual difference between all these modes? Here's a table of all the Particle Mesh devices and the two modes discussed above.

Li @ 3.4V (mA) Deep Sleep (uA) Stop Mode (uA)
Xenon 834 846
Boron 1387 1978
Argon 846 1333

Side note: these measurements are from Agilent 6611C power supply set to 3.4V. The 6611C has a low current measurement mode. In other words, it can measure the current without an ammeter in series.

The results? Not particularly impressive. 0.834mA in deep sleep mode will last about 11.24 days on a 225mAh battery cell. There had to be a better way.

Knowing that the NRF52 Series sips current in sleep mode, the solution was somewhere else. So after perusing the schematic, the culprit became clear. It was all the power management circuitry (highlighted below)! 😬

Particle Mesh Xenon Schematic with power circuitry highlighted

There is a way around this though. The solution?

Bypass it all.

Bypass Bypass Bypass

The main risk of bypassing was the potential for back powering those circuits. This could lead to unintended operation or even high quiescent currents. In my case though, there was none of that. I saw a noticeable improvement.

When bypassing, the Xenon went from 846µA to 46µA. Thats a factor of 17x! With the motion sense board, it was about 49µA. That means with a 225mAh battery this setup would last about 0.52 years.

Bypassing though came with some extra concerns. Like, "will the board support the voltage of a coin cell which varies from 3.3V down to 2.6V?" It was time to do some research.


So, we're bypassing all the regulators. That means that 3.3V can, over time, turn into 2.6V. Can all the circuitry on the Particle Mesh board handle 3.3V down to 2.6V?

Let's look at the schematic to see what the devices of concern are.

Particle Mesh Xenon Schematic with circles

The circled devices are the ones we care about.

This gives us a list of

  • NRF52840
  • SKY13351-378LF
  • MX25L1606E

Now, we can then go and research each of them to understand the power range they can operate in. In this case every device must work at 3.3V and down to 2.6V.

So, where's the best source of how these parts operate? The data sheets.

Here's the one for the flash part:

MX25L1606E Flash Chip Spec

For Nordic's NRF52840,

NRF52840 Product Spec

And for the antenna switch:

Skyworks RF Switch Datasheet

The search took a few minutes and 3 round trips to DuckDuckGo but we've found the information we've needed. All devices can operate in the range we're looking for. (With a slight exception of the flash chip. In this example, it's not used so we can safely ignore it)

So, with that hurdle out of the way, it was time to write some firmware.

Make it Smart

Alets on iPhone 6

The last step in the process was to get the firmware as optimized as possible. i.e. to keep the device in sleep mode most of the time.

For this first round of testing stop sleep mode was used*.*

    // Sleep differently depending on the situtation
    if( !pirReady ) {
      System.sleep(D2, FALLING, 2000);
    } else if( rtcReadyForMotion ) {
      System.sleep({A5, D2}, {CHANGE, FALLING});
    } else {
      System.sleep(D2, FALLING);

Furthermore, the state of the device, dictated how sleep mode worked. For instance, the device would ignore PIR input if it wasn't ready for motion interrupts. This would prevent the device from waking up until it was time to.

Additionally, the idea was to use Particle.publish() to the cloud. Unfortunately, early complications made that tough. Sometimes the device would connect immediately. Sometimes it would sit there and do nothing. In the end, I changed the Particle.publish() to Mesh.publish().

This, in turn, required another device in the mesh network to subscribe to the message and forward it on to the cloud. An Argon running as an edge router is great for this purpose.

Interestingly, I did notice that when Mesh.connect() is called, Mesh.publish() would work some of the time. I added some delay after Mesh.connect() and the setup has been rock solid ever since. The code looks something like this now:

    // If A5 is high then we have an event.
    if (pirEventOccurred && pirReady) {

      // Reset variable
      pirEventOccurred = false;
      publishInProgress = true;

      // Start connection
      // TODO: fix this. This is a hack. I'm getting unreliable messages *without a delay*. :|

      // Blink RED led

      Log.trace("event occurred");

Side note: System.sleep causes the Particle Board to disconnect. So, you need to reconnect to the mesh network when you're ready to publish.

Also, in the sprit of designing low power firmware, using the RGB LED was out. Especially since you can't see when it's inside the enclosure! You can disable it by turning on manual control:

    // Turn off RGB Led

Once the firmware seemed to be in a good place, it was time to put everything together. Unfortunately the sensor + Particle Mesh combination wasn't ready to cooperate just yet.

Mysterious Restarts

After some testing, I noticed that the device stopped working. I removed it from the plastics and found the RGB LED was blinking white. My best guess? The processor was continuously restarting.

So after some head scratching, I attached the battery to the oscilloscope. I watched as the board started up and attempted to connect to the mesh network. As it did, the battery voltage would promptly drop. This is typical for any type of battery based design.The unexpected part? The Xenon would do a full hardware reset whenever the battery voltage would reach below 2.8V.

What caused these resets? Was it another part? (Like the flash part unable to communicate?) Was it the NRF52?

After searching the answer in data sheets and schematics, it turned out to be the NRF52. More specifically, the power supply supervisor.

Power Supply Supervisor

In the DeviceOS code, the power supply supervisor is set to restart the NRF52 if it goes below 2.8V. The exact call inside hw_config.c under platform/mcu/nRF52840/src.

Screenshot of code location

And Bingo was his name-o.

The solution was to use the STARTUP macro and run sd_power_pof_threshold_set. Then using one of the many options to set the new threshold:

    /** @brief Power failure comparator thresholds. */
    typedef enum
        NRF_POWER_POFTHR_V21 = POWER_POFCON_THRESHOLD_V21, /**< Set threshold to 2.1&nbsp;V */
        NRF_POWER_POFTHR_V23 = POWER_POFCON_THRESHOLD_V23, /**< Set threshold to 2.3&nbsp;V */
        NRF_POWER_POFTHR_V25 = POWER_POFCON_THRESHOLD_V25, /**< Set threshold to 2.5&nbsp;V */
        NRF_POWER_POFTHR_V27 = POWER_POFCON_THRESHOLD_V27, /**< Set threshold to 2.7&nbsp;V */
    #if defined(POWER_POFCON_THRESHOLD_V17) || defined(__NRFX_DOXYGEN__)
        NRF_POWER_POFTHR_V17 = POWER_POFCON_THRESHOLD_V17, /**< Set threshold to 1.7&nbsp;V */
        NRF_POWER_POFTHR_V18 = POWER_POFCON_THRESHOLD_V18, /**< Set threshold to 1.8&nbsp;V */
        NRF_POWER_POFTHR_V19 = POWER_POFCON_THRESHOLD_V19, /**< Set threshold to 1.9&nbsp;V */
        NRF_POWER_POFTHR_V20 = POWER_POFCON_THRESHOLD_V20, /**< Set threshold to 2.0&nbsp;V */
        NRF_POWER_POFTHR_V22 = POWER_POFCON_THRESHOLD_V22, /**< Set threshold to 2.2&nbsp;V */
        NRF_POWER_POFTHR_V24 = POWER_POFCON_THRESHOLD_V24, /**< Set threshold to 2.4&nbsp;V */
        NRF_POWER_POFTHR_V26 = POWER_POFCON_THRESHOLD_V26, /**< Set threshold to 2.6&nbsp;V */
        NRF_POWER_POFTHR_V28 = POWER_POFCON_THRESHOLD_V28, /**< Set threshold to 2.8&nbsp;V */
    #endif // defined(POWER_POFCON_THRESHOLD_V17) || defined(__NRFX_DOXYGEN__)
    } nrf_power_pof_thr_t;

In this case I set it to NRF_POWER_THRESHOLD_V20


It was only after implementing this fix did the full device operate longer than a couple minutes! Now that it's working it's time to pack everything away into a polished enclosure. We'll talk about that step in the next section.

The Enclosure

Instead of sticking raw circuit boards to the wall, the first step was to find or create an enclosure. Unfortunately, I'm no product designer. Designing an enclosure takes a chunk of time that I didn't quite have. So what's the next best bet?

Using a pre-existing enclosure.

Digikey is full of enclosures of all shapes and sizes. Luckily I had a few extra one in my box-of-stuff that seemed to fit the bill.

The box is a Bud Industries CU-1941. It's 83.01mm x 54.00mm with a height of 30.51mm. Which is the perfect size to hold a circuit board plus a Particle Mesh board mushed together.

3D Render of the Project Box

It also happens to be the same enclosure used to house a modified version of the uCurrent Gold. Here's a picture of the one I built in the not to distant past:

uCurrent Gold

I contacted Bud and asked them for the STEP files. They obliged and while developing the electronics, I was able to mock up the design in Fusion360.

Digital mockup

Pro tip: Whenever you're using an off-the-shelf design, you need to do your due diligence. That means checking for fit in all directions. My main concern was making sure that the mated height of the boards was small enough. That way they could fit into the height of the enclosure.

Here's a sectional analysis of the highest part of the motion sense board:

Section Analysis of the Assembly

And it fit like a glove! (with some extra space to boot) I could even stick a much much larger battery in there and still have room. Here's what it looks like when everything is mated:


This process takes some time but it's well worth it. It's especially gratifying when you have assembled circuitboards that slide into place. Despite how well everything came together there's always improvements for the next version.

The Finished Product?

Motion Board in Plastics

Despite some miscellaneous mechanical tweaks (drilling out holes a little big larger, etc), the project came together. Plus, it looks like something you'd by from some spy shop on the internet to boot!

Despite how polished it looks there's one glaring factor.


The device draws 49µA. Replacing a battery every half year though is rough on the wallet and the landfill.

How do we get that current even lower?

As you know by now, the Xenon board takes up 94% of the sleep power. So, why not take it completely out of the picture?

Removing the processor from the mix is as simple as removing the power from the device while in sleep mode. We can do that by gating the power to the Particle Mesh board through the 3.3V pin.

Flow of energy

This turns our 49µA of sleep current into 3µA. Thats a difference of 0.5 year and 8 years of standby current!


Creating low power hardware to save battery life can be frustrating at times. As long as you stay creative, you can eke every hour of battery life out of your deployed devices.

Want to learn more? I have more content like this coming out soon. Plus, if you haven't already, you can sign up for updates on my upcoming Ultimate Guide to Particle Mesh. I'm sharing some insider content and list only exclusives you don't want to miss out on!

Additionally, learn how to send push notifications to your phone using this previous tutorial.

Want one of these motion sensor boards?

Join the waiting list here and you'll be the first to know when they're available on my site. This particular version will only work with Xenons.

Finally, thanks for reading this post. It's always great to hear from you guys about what projects you're building. It's and inspiration and keeps me going.

Until next time!


Top comments (0)