element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      • Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Design For A Cause 2021
  • Challenges & Projects
  • Design Challenges
  • Design For A Cause 2021
  • More
  • Cancel
Design For A Cause 2021
Blog BIBoP 5 - Power efficency and interrupts
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: jduchniewicz
  • Date Created: 24 May 2021 10:48 AM Date Created
  • Views 1554 views
  • Likes 5 likes
  • Comments 9 comments
  • blood pressure
  • bibop
  • health monitoring
  • ppg
  • nano 33 iot
  • samd21
  • power
  • design for a cause design challenge
Related
Recommended

BIBoP 5 - Power efficency and interrupts

jduchniewicz
jduchniewicz
24 May 2021
Previous postsDescription
BIBoP 1 - Introduction, game plan and rationaleIntroduction to the project and overview, creators bios
BIBoP 2 - Writing Arduino code with C++ and C (Makefiles) Using a custom Makefile for development of Arduino code, flashing and debugging code
BIBoP 3 - Blood Pressure Inference - Machine LearningThe process of creating a Machine Learning model for Blood Pressure estimation, data cleaning and training the model
BIBoP 4 - AWS Lambda deployment and MQTT communicationDeploying the trained model on the AWS Lambda and sending secure requests from the Arduino

 

Next posts
Description
Assembly and debuggingAssembly, 3D designing and debugging of the project
Galvanometer creationProcess of creating my own galvanometer
Final product and testingPresentation of finished product and some results

image

  • Introduction
  • Why use less power?
    • Battery and powering
  • Using the RTC for sleeping
  • Do interrupt me!
        • Debouncing hell
  • Troubles along the way!
      • Creating new I2C bus
  • Summary
      • Case sneak-peek(s)

Introduction

 

Hello! In case this is your first time with BIBoP it is a smart armband designed for remote patient monitoring and overall health assessment. It is based on Arduino Nano 33 IoT and some external sensors (PPG, Galvanometer and IMU).

Be sure to regularly check the official repository of this project!

 

In this blog post we will go through the power management of our device, different states, generating and handling interrupts on register level and discuss some issues we had along the way. Let's get started!

 

Why use less power?

Since we want our device to run on battery and want this battery to last longer than an hour, we need to make the power consumption lower than usual. amgalbu in his blog post measured the current consumption of the Arduino under different circumstances, which helped us assess how much power reduction we require in order to have at least 2-3 days on a single charge and we can have even more assuming we use the device less extensively. It all boils down to tradeoffs we are willing to pay and to the voltage and current of our device image. It is no wonder why the medical-grade embedded devices or smartwatches run on microAmp current with voltages as low as under 1V.

 

Basing on Ambrogio's measurements and adding our own, we came up with following estimates for battery expectancy on a single charge:

 

StateCurrent consumedTime (minutes in hour)
SINGLE_SHOT505 min (5s every 60 seconds)
DEEP_SLEEP552 min
ACTIVELY_USING2003 min

 

NOTE: these are only estimates and the accurate measurements will follow once we finish assembling the device.

Hence, the estimate working time for 600mAh battery is around 32 hours, which is non-ideal but good enough for a prototype (we wouldn't be working in 3.3V but with much less ideally).

 

The power states of the system look as follows:

image

 

We can have the main loop running more or less frequent depending on the clients desire.

 

Battery and powering

For powering the system we are using raw Arduino pins (we could also use a limiting circuit so we don't accidentally fry our Arduino image - it is always good to have some kind of protection - boards are expensive, passive components not so much!

Initially we planned to use a Li-Po battery cell akin to those used in RC cars and drones, but it had too big voltages and we found out that there exists a type of battery providing us with the just right voltage most of the time - LiFePO4! It boasts recharge-ability and better efficiency than Li-ion ones. Also the voltage is mostly flat when the battery discharges, only decaying rapidly when the battery is almost empty which is desired for our application. The graph below shows exemplary battery charge curves over time:

image

Credit: quora

 

As you can see, the graphs are very flat and this is what we need! Most of the devices onboard the Arduino need up to 3.6V and the LiPo battery would provide 3.7V when full. Maybe the only disadvantages are that charging these batteries is quite difficult and they have lower capacities (or I could not find bigger for less money).

 

I also found a simple DIY battery charger on hackaday.com which I will have to construct once the battery dies out!

The charger is making use of a popular element from TI - a programmable shunt regulator. The only problem with this charger is that you have to remember to switch it off to not overcharge the battery. It could be alleviated but as mentioned this is mostly a hack!

 

Using the RTC for sleeping

In his post amgalbu used the RTC onboard the SAMD21 for sleeping arbitrary time values in increments of 30.5 us (the tick value of the MCU). We tried the code and observed that somehow it sleeps 10 times less than asked for? So for now we are just inputting 10 times the value I need to sleep. In the blog post Ambrogio explains details behind the creation of the Generic Clock Generator (GCLK), however after spending considerable time reading the datasheet we think more explanation is needed!

 

This research is motivated with a need for an external interrupt (button press) which will wake up the MCU even if it is in the DEEPSLEEP mode and the CPU is not running. In order to do so we need to dig deeper into several components of the system (GCLK, EXTINT) which are visible below:

First we need to create the GCLK and connect it to the External Interrupt Controller (EIC).

image

Onboard our MCU we have 9 GCLKs we can enable and then connect to peripherals. The code below creates one of them and connects it to the EIC:

 

    // create a generic clock generator for the EXTINT peripheral system
    GCLK->GENDIV.reg = GCLK_GENDIV_ID(3) |
                       GCLK_GENDIV_DIV(0);
    while (GCLK->STATUS.bit.SYNCBUSY);

    GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
                        GCLK_GENCTRL_GENEN |
                        GCLK_GENCTRL_SRC_OSCULP32K;
    while (GCLK->STATUS.bit.SYNCBUSY);

    GCLK->CLKCTRL.reg = (uint32_t) (GCLK_CLKCTRL_CLKEN |
                        GCLK_CLKCTRL_GEN_GCLK3 |
                        (EIC_GCLK_ID << GCLK_CLKCTRL_ID_Pos));
    while (GCLK->STATUS.bit.SYNCBUSY);

 

As you can see there are three registers we need to configure: GENCTRL, GENDIV and CLKCTRL.

GENCTRL - Controls the generator and clock source

GENDIV - Controls the prescaler of the clock

CLKCTRL - Controls the Generic Clock Multiplexer

 

If you seek even more information about GCLK, be sure to check out this blog post and the official datasheet!

 

This is exactly how Arduino implements the interrupts and in the end I decided to use their wrapper functions. Nevertheless research I made is useful if you want to do all of this in a bare-metal environment (without Arduino libraries).

Knowing your MCU is useful if you want to assume control over it image Oh and you don't have to go through the datasheet to figure everything by yourself!

 

Do interrupt me!

So now we know what the GCLK is and how to create them and register them for different peripherals in the MCU. Next we can use this knowledge to our benefit and enable the External Interrupt peripheral, which allows for setting IO pins to serve as interrupt lines. It even allows for supplying the NMI (non-maskable interrupt) to the MCU which is a big advantage - this interrupt cannot be masked (ignored by the MCU). We will not need it in this case, but it is good to have various options.

 

Basing on the graph above, we can start activating and configuring the necessary peripherals basing on EXTINT's requirements:

image

The first precondition is by default satisified, the second one we just configured, so now we will focus on the third one!

 

We are using a GPIO as an external interrupt so we have to configure it beforehand (This was probably the most difficult thing to set up for me!)

We are using pin Digital 9 - which corresponds to the PA20 in the GPIO peripheral.

 

    PORT->Group[PORTA].DIRCLR.reg |= PORT_PA20; // input
    PORT->Group[PORTA].OUTSET.reg |= PORT_PA20;
    PORT->Group[PORTA].PINCFG[20].reg = PORT_PINCFG_INEN | // enable input
                                       PORT_PINCFG_PMUXEN | // enable port muxing
                                       PORT_PINCFG_PULLEN;
    PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXE_A;

 

We first have to declare our pin as input by clearing a flag - writing 1 to DIRCLR for PA20. Then we enable internal pull-up to prevent the pin from floating (alternating between 0 and 1) by writing to OUTSET.

We then set up the PINCFG which sets up the settings for a given PIN, enabling PIN muxing which is responsible for setting alternative functions for the PIN, such as external interrupt.

 

With this done, we can setup EIC!

    // set up EXTINT
    // Pin Digital 9 = PA20 - connected to EXTINT4
    EIC->INTENSET.reg |= EIC_INTENSET_EXTINT;
     
    EIC->CONFIG[0].reg |= //EIC_CONFIG_FILTEN0 |
                          EIC_CONFIG_SENSE2_FALL_Val;

    EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN4;

 

We can check which functions our PIN has by examining the variant.cpp file which shows our configuration for Arduino Nano 33 IoT.

 

We enabled external interrupt 4 which is connected to PA20 and set up the FALL detection meaning it triggers upon driving the pin LOW. We also enable it as a WAKE UP PIN.

 

Nested Vectored Interrupt Controller

The last part we need to set up is NVIC which enables the interrupts to the CPU from other peripherals in our MCU. The EIC line is tied also as NMI but we won't be using this functionality and just enabling the IRQs from EIC.

// set up NVIC
    NVIC_DisableIRQ(EIC_IRQn);
    NVIC_ClearPendingIRQ(EIC_IRQn);

    NVIC_SetPriority(EIC_IRQn, 0);
    NVIC_EnableIRQ(EIC_IRQn);

 

We have nice functions which abstract details of setting it from us and we can happily call them!

We also have to declare a handler for this interrupt like this:

void EIC_Handler(void)
{
     // handle it!
}

 

NVIC abstracts all the details from us, but in short, when an interrupt line (hardware connection to a peripheral like EIC) is driven high, it calls a function it has registered in the appropriate slot in its interrupt vector.

More details about it can be found here - you are in for a ride about weak functions and startup of SAMD21

 

Debouncing hell

It is also a good place to talk about debouncing, as even though we have prevented floating we still get intermittent states of the pin. There are various techniques on how to do this, be it hardware or software, but since we don't really care about whether our interrupt is acknowledged exactly when it came we can use software for that:

void buttonIrq()
{
    wakeUpMillis = millis();

    // needs debouncing in the button case
    if (wakeUpMillis - debounceMillis > BUTTON_PRESS_DELAY)
    {
        pressAcknowledged = true; // needs handling for OLED display changes
    }
    debounceMillis = wakeUpMillis; //millis don't advance in IRQ
}

Nevertheless, it is worth looking at various techniques of doing it: Part 1, Part 2.

 

Troubles along the way!

 

With the theory away, we could try implementing the actual handling in our code! Interrupts worked just fine, interrupting the device whether it was in the DEEPSLEEP or powered on.

However, as it turned out we soon stumbled upon big problem: when communicating with the MQTT broker, we could not gather data from our sensor and communicate with the OLED on the I2C bus. After several hours of debugging, it turned out that the ECCX08 is hogging our I2C bus with the amount of traffic it creates.

Without the regular hardware I2C bus we had to find a way around this problem.

Enter SAMD21 capabilities: creating new busses!

 

Creating new I2C bus

In order to create a new  bus in SAMD21, some research has to be done beforehand about the pins we want to utilize and their capabilities. The ultimate guide to Arduino Nano 33 IoT mentions creating a hardware serial and the Sparkfun article explains how to create any bus we want.

We used pins Digital 5 and 6 for that and all we had to do was create a new TwoWire object tied to a free SERCOM instance:

// add a new serial because original one (pins A4 and A5) is hogged by the ECC
// D5 - SCL - pad 1
// D6 - SDA - pad 0
TwoWire sensorI2c(&sercom0, 5, 6);

 

And setup the pins:

// setup the second I2C bus

sensorI2c.begin();

pinPeripheral(5, PIO_SERCOM_ALT);

pinPeripheral(6, PIO_SERCOM_ALT);

 

We also have to tie devices to this bus and surprisingly some drivers do not have such capabilities.

 

One tweak I had to do to external libraries is also worth mentioning:

ArduinoMqttLibrary has hardcoded message callback format and when the MqttClient object is encapsulated, it is very hard to create this callback. I had to modify the library as follows:

And then tweak my NetworkManager class to handle the callback accordingly (I had to make some dancing around to make the function call properly). C++ classes make callback game much more difficult, so instead of just passing the member function I had to create a static function which in turn calls the member function.

If you are curious about how to do it differently, have a look here.

 

Summary

In this post we went through the power management system and different power states in our device. We also described some issues we had along the way and necesary modifications to some external libraries. In the penultimate post (next one image) we will describe a small addition to the device (Galvanometer) which was soldered on a small experimental board by us. Ideally it would be an SMD component but we lack SMD design capabilities at the time of writing this post (hopefully soon we will learn these arcanas!) Also we will show how the casing of BIBoP was designed and assembled and show our testing results.

Case sneak-peek(s)

Below is a sneak-peek of the casing for BIBoP, designed in fusion 360 by us!

{tabbedtable} Tab LabelTab Content
Rendered 3D modelimage
assembled 3D print

image

 

Jakub & Szymon & Michał

 

Next post
Galvanometer creation
  • Sign in to reply

Top Comments

  • javagoza
    javagoza over 4 years ago +1
    You can use connection callbacks to wait for connect event. I'm using that technique, see code in last blog VenTTracker #14 (aka #11 bis) - Environmental Monitor Revisited - Version 2 https://github.com…
  • javagoza
    javagoza over 4 years ago in reply to jduchniewicz +1
    I also think you can use the attachInterrupt () function // detect falling edges the switch changes from high to low attachInterrupt(digitalPinToInterrupt(PORT), buttonIrq , FALLING); You can see an…
  • javagoza
    javagoza over 4 years ago +1
    I had not seen the box in 3D printing before. Nice job! That is something I want to learn one day. Any advice on 3D printers?
  • javagoza
    javagoza over 4 years ago in reply to jduchniewicz

    Thanks for the reference of the Prusa MK3 assembly kit. It seems to me very funny and instructive to assembly one.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • javagoza
    javagoza over 4 years ago in reply to jduchniewicz

    I'm glad you found it useful. By using standard Arduino libraries, you ensure compatibility with more Arduino boards.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jduchniewicz
    jduchniewicz over 4 years ago in reply to javagoza

    Yes, the Arduino wrapper functions were somewhat better as they abstracted all the details. I was initially afraid they won't wake up the device from DEEPSLEEP but it turned out to work just fine, so in the current version I just use these function calls image

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jduchniewicz
    jduchniewicz over 4 years ago in reply to javagoza

    I will try to publish one more post on 3D printing. Szymon does most of designing and we use Prusa MK3 for printing. We chose the DIY version which is not assembled so we learned how it works along while constructing it!

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • ilvyanyatka
    ilvyanyatka over 4 years ago

    Very useful explanation on powering the device. Thank you!

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube