Battery Saving JavaScript Tips for Nordic nRF52

With the introduction of Moddable Four, industry-standard modern JavaScript can now power the ultra-low power Nordic nRF52 microcontroller. Operating for a year (or more) on a coin-cell battery in a compact form-factor opens a world of new possibilities for products and projects. Achieving the longest battery life requires some attention. The good news is that managing the energy use of your code is straightforward using the JavaScript energy management APIs in Moddable SDK 4.0.

Moddable Four

The key to maximizing battery life in a microcontroller is simple and obvious: turn it off. Of course, the device has work to do, so it can't always be off. Fortunately, the nRF52 allows individual parts of the chip to be turned off. This is managed by two special operating modes on the nRF52: light and deep sleep.

Light Sleep

Light sleep pauses execution of all code on the nRF52. While paused, energy consumption is reduced significantly. The Moddable runtime configures the nRF52 to wake automatically from light sleep when needed for timers, interrupts, and BLE. Even better, the runtime automatically enters light sleep whenever scripts are idle. Light sleep can as short as the interval between frames of an animation. Every bit of sleep saves a little energy.

Sounds great? It is. For many projects, light sleep may be most of what's needed to get reasonable battery life. Deep sleep may extend it. Before diving into that, let's look at how to get the most out of light sleep.

For light sleep to activate, your scripts need to be idle. If you use the usual JavaScript tools for asynchronous operation like callbacks, timers, promises, and async/await you are off to a great start. Your script is returning control to the runtime while waiting for events to happen. Avoid using loops that block or poll constantly. If you need a brief delay in a script, for example because of a sensor's timing requirements, use Timer.delay() which allows light sleep to activate.

It can sometimes be difficult to know exactly what your script is doing. You tried to be efficient, but how can you be sure? The Moddable runtime has extensive instrumentation that reports the use of key system resources. This is displayed as real-time graphs in the xsbug debugger.

The two instrumentation graphs that matter most to deep sleep are "CPU" and "Event loop." CPU is easy to understand -- it is how busy the CPU is. There's no harm in using lots of CPU time when your project has lots of work to do. It should be at 0% when there's nothing going on. If your CPU use looks high, you can use the profiler to see which functions are taking the most time to guide your optimization.

The idea of an "Event loop" will be familiar to many developers. It represents one pass through the event loop, during which callbacks and promises are invoked. If there's nothing going on, it should be zero. If you are using a timer to poll a sensor, each time that timer fires is one turn through the event loop. There's nothing wrong with having lots of events, if your project is busy. You want to watch to see if the number of passes through the event loop matches your expectation of the product's activity.

This video shows the First Run app on Moddable Four along with the CPU and Event Loop instrumentation. Notice how they both change depending on the activity of the device.

Note: Nordic documents call Light Sleep "System ON".

Deep Sleep

Other than turning the microcontroller off, deep sleep is the lowest possible power mode, drawing as little as about 1 µA, roughly 80% less energy than light sleep. To achieve that, deep sleep stops execution completely and turns off RAM, losing all your program's state. Execution resumes by restarting the nRF52. Restarting Moddable-powered projects on nRF52 can take as little as 1 ms, less than the blink of an eye.

Because waking from deep-sleep takes some time, it also uses some energy. If your project typically stays in deep sleep for an extended time (30 seconds or more), deep sleep is a good choice. If it is waking more often, light sleep may actually be more energy efficient because it resumes execution immediately.

Because waking from deep sleep restarts your code, your code needs to manage deep sleep. There are three aspects to that: setting conditions to wake-up, going to sleep, and waking up.

Setting Conditions to Wake-up

Before entering deep sleep, you need to tell the nRF52 what should wake you up. There are APIs to set-up these wake triggers:

  • Timer - Wake after a period of time elapses. Used for occasional polling.
  • Digital - Wake when a digital input changes. Used to wake on button presses and interrupts triggered by peripherals like a sensor.
  • Analog - Wake when an analog input crosses a specified threshold. Used to wake when an analog sensor changes.

Your can set more than one wake trigger. Just keep in mind that each trigger uses a little bit of energy, so fewer triggers means longer battery life.

Going to Deep Sleep

To enter deep sleep, your project calls Sleep.deep(). The call doesn't return because the nRF52 immediately halts execution.

Before you activate deep sleep, you might need to save your program's current state so it can resume at the same place on wake. The best way to do this is using the pool of 128 bytes of "retention RAM." The Moddable runtime sets up this RAM to be retained during deep sleep. Reading and writing retention RAM is easy with Sleep.getRetainedValue() and Sleep.setRetainedValue(). Retention RAM is faster than Preferences, which must write to flash for permanent storage.

Waking Up

When one of the wake-up conditions is met, the nRF52 reboots and starts running your project. Your project can use the values it stored in retention RAM to resume in the state it was in prior to deep sleep. If you have several wake-up conditions, use Sleep.resetReason to determine which caused one triggered wake.

When using deep sleep, you want the nRF52 to reboot as quickly as possible. Preloading of JavaScript modules is a critical technique for booting as fast as possible. Preload is a unique feature of the Moddable SDK that allows modules to be loaded at build time instead of runtime. Preloaded modules take literally no time to load on the device. In a project with many modules, preloading makes a measurable impact on start-up performance. For most modules, enabling preload is very simple -- see the preload documentation for details.

Note: Nordic documents call Deep Sleep "System OFF".

Keeping Display On During Deep Sleep

For interactive products that make use of a screen, like those using Moddable Four, you can often use deep sleep without the user noticing. The reboot time is so fast, a wake-up button press can be handled without any perceived delay. While the nRF52 is in deep sleep, your project has the option of leaving the screen on or turning it off. Turning it off uses less energy, of course, but leaving it on consumes just 0.1 µA. For example, a watch app can update the screen once a minute and leave the display on while in deep sleep for 60 seconds. Updating the screen takes a few milliseconds, so the effective duty cycle of the nRF52 is just 0.0083%. The user doesn't notice and the battery lasts a very, very long time.

Resources to Learn More

Now that you know all about managing energy use on the nRF52 using JavaScript, you are ready to try it out. There are many resources to help.

First-Run Moddable Four app

The app that comes pre-installed on Moddable Four makes extensive use of energy management features:

  • Optimized to spend as much time in light sleep as much as possible
  • Automatically enters deep sleep after 60 seconds of inactivity
  • "Shake to wake" uses the accelerometer to wake on motion

It is a great place to see how all the pieces come together. The source code is available in the Moddable SDK.

Examples

There's a suite of sleep examples that show how to use the different sleep features, individually and in combination. These examples are very small, so they are a great place to explore and learn.

Documentation

The extensive "nRF52 Low Power Notes" document is a reference for all the energy management capabilities of the Moddable SDK on nRF52. It also has links to the Nordic nRF52 documentation describing the low-level implementation of these features.

Discussions

If you have a question or you've made an interesting discovery about how to optimize energy use, stop by the Discussions page of our repository on GitHub. The Moddable team and the rest of the community are happy to help with your questions and learn from your discoveries.