Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More on Power consumption #165

Open
pbecchi opened this issue Jul 23, 2018 · 101 comments
Open

More on Power consumption #165

pbecchi opened this issue Jul 23, 2018 · 101 comments

Comments

@pbecchi
Copy link

pbecchi commented Jul 23, 2018

Since long time I am fighting to lower power consumption using this library to the levels reachable with Sdk examples!
I have run several examples, testing the uA level in different type of code.
If we don't use BLE libraries uA are very near to the one expected , normally few uA when we are in low power mode.
But if we use BLE consumption get well above 1mA so about 100 times more that expected!
I have used the dual role BLE uart example and these are my findings:
-to enter low power mode you have to add delay() in the main loop , the longest the milliseconds the lowest the power drain.
-all Ble central related functions are taking quite a bit of mA
-keeping only BLE peripheral functions, with delay(100) will give about 600uA that is about what is expected (100 uA+500uA for Serial uart)
-taking out all Serial.begin and Serial.print from the example INCREASE the power drain to about 2mA while should be reduced by 500uA

I think this strange behaviour may only be due to some Serial debug statement present on the BLE libraries.
In theory Serial debug should be fully controlled by the Tools menu setting and with debug=0 slould not be executed!

@hathach
Copy link
Member

hathach commented Jul 24, 2018

Thanks @pbecchi for raising this issue and share your experience with the power consumption. You are right with your observation, I will throw a bit more detail where I could.

  • When ble radio is active (prph and central), it will consume much more power. I haven't looked at the nordic specs on those number. So it is a TODO for us.
  • delay() in your library is more than delay as you figure out, more like sleep() now. Since we implement freeRTOS tickless https://www.freertos.org/low-power-tickless-rtos.html Everytime delay() is invoke it will put mcu into decent sleep mode (waitForEvent() ) an only wake up a few ticks before the time of scheduled action. It is all handled under the hood, man we should call it sleep() :D . It is a feature point in our upcoming "coding guideline for low power" tutorial. However the longer you delay/sleep the lower responsiveness of the device.
  • For serial draining, are you testing with the later revision of the power, the previous version has an hw issue that make cp2104 constantly draw power even not enabled. Too bad, I don't even have the later revision myself to test with for now :( . However with the debug = 0, the serial shouldn't ever be used. I could double check it later.

I have also wanted to test more with power consumption and write an tutorial for it. But we are too busy for now with the upcoming nrf52840, which we will need do low power, so we wait until it comes out to test with. This will serve as notes to ourself to improve it later.

@hathach
Copy link
Member

hathach commented Jul 24, 2018

Another way to reduce power is to go full event response aka callback and abandon the use of loop() with suspendLoop() call. ( I will take note myself for the tutorial, before I start to forget things)

https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/Bluefruit52Lib/examples/Peripheral/beacon/beacon.ino#L59

Also you may want to tweak the ble radio timing/parameter such adv, connection interval etc ... the full list and its affect (current save) will come later when I tested them with the bench ( we may go as far as low power profile with pre-configured parameter for user). There may be more trick, but I kind of forget, will get back to this when we are done with the nrf52840 :D

@pbecchi
Copy link
Author

pbecchi commented Jul 24, 2018

@hathach Thanks for your replay!

To be more precise on my previous message:

  • I use your libraries on nrf52 DK and on Fanstel BT832 modules ....so the Cp2104 cannot be the reason for the abnormal UART drain.

  • The high mA start in the setup() function just after startAdv() call. I have added a 1 minute delay() after that call . When I have a Serial.begin call the consumption is 1.5 mA and after 30s 0.6 mA.
    Without Serial.begin my consumption is floating from 1.5 to 3.5 mA. This behaviour dont shows up with the nrf52 DK board.

  • Is suspendLoop() a way to not start the loop task? I cannot do this since my code is a sprinkler controller and I need to spend few millisecond per minute to do GPIO controls. I can manage to use a delay(500) in my loop() that hopefully can reduce consumption to around 100uA

  • I need anyhow to solve the Serial related power drain......can you help me on that?

@hathach
Copy link
Member

hathach commented Jul 24, 2018

Yes, suspendLoop() will put loop() task in suspend and prevent it to execute. If you need it, so just use it if you need it.

For the serial issue with power, currently I don't have any ideas why it behaves as such. I am quite sure to not print anything when debug=0. Although, I am too busy by now for upcoming nrf52840 to pull out nrf52dk to test this out. I am afraid you have to do it by yourself a little bit more until we release the nrf52840 board. Sorry since I only have 2 hands and 10 fingers :))

@pbecchi
Copy link
Author

pbecchi commented Jul 24, 2018

Thanks, I understand your problems.....
I am also very interested to the nrf52840 , since I am getting my first bt840 end of this week! :-)

So I will try to solve this power drain problem by myself and ...I will let you know!!

@hathach
Copy link
Member

hathach commented Jul 25, 2018

thanks, please update here if you could find out anything. Low power is mostly software thing ( hw is often too obvious and spotted out soon enough). It needs a guideline and discipline to follow. Unfortunately, we haven't got the guideline yet ( just some above tips )

@hathach
Copy link
Member

hathach commented Aug 9, 2018

related to #51

@Nenik
Copy link
Contributor

Nenik commented Aug 28, 2018

I'd have one more data point on unexpected power consumption - FPU.
On a custom design (but still using nRF52 Feather framework, since that's what I prototype on), I have attained great idle power consumption (<0.5mA). But only till the first float multiplication.
I initially blamed ADC, since it was in the battery measurement code, but it really turned out to the the float multiplication, after which my draw was constantly over 6.5mA.
Then I have found https://devzone.nordicsemi.com/f/nordic-q-a/23242/single-float-division-causing-7x-higher-current-draw and the reference to the errata 87, which includes a workaround.
Perhaps this workaround (clearing the FPU pending IRQ) could be easily applied to the Feather codebase (I have since avoided float math, to a great effect on idle power).

@hathach
Copy link
Member

hathach commented Aug 28, 2018

superb !!! That is brilliant finding, we will note this and make sure including this in our soon-coming testing and tutorial.

@Nenik
Copy link
Contributor

Nenik commented Aug 28, 2018

Interestingly enough, there seems to be (ifdef-outed) stub of the workaround in the waitForEvent implementation, as I have just found (looking into why my waitForEvent doesn't really sleep either, while delay() does pretty well)
0.8.3, wiring.c

@hathach
Copy link
Member

hathach commented Aug 28, 2018

use delay for the sleep(), it is mentioned in my above comment. waitForEvent will probably be removed
the next time we work on systematizing power stuff. Here is sum up when you call delay

  • freeRTOS check scheduler for the next time a task will run
  • if there is long-enough, it will skip tick, auto call wait for event, and wake up just a few tick before the runtime.

For details: https://www.freertos.org/low-power-tickless-rtos.html

@Nenik
Copy link
Contributor

Nenik commented Aug 29, 2018

I am moving towards tickless, though I wonder about threading model of the platform.
Posted question on that topic at the forum (though at the end of the day, I might go with a central worker thread driving the UX that might get all the wakeup/event sources re-posted into...).

Thanks.

@hathach
Copy link
Member

hathach commented Aug 29, 2018

@Nenik it is rather simple, ble and soc run on their on thread, most of callback run on a worker thread ( some needs signature changes to be called in worker). Give me a couple of days, I will answer it more details in the forum topic.

@Nenik
Copy link
Contributor

Nenik commented Aug 29, 2018

Looking forward to that.
But I've just discovered the beauty of xTimerPendFunctionCallFromISR() :-)
That gets me covered for most of my needs, since most of the other stuff will come on the timer thread too, thus allowing simple serialization of the events...
Thanks!

@Nenik
Copy link
Contributor

Nenik commented Aug 30, 2018

But I've just discovered the beauty of xTimerPendFunctionCallFromISR()

Except that the timer stack is only 100B deep :-(
That's like 2 stack frames on Cortex and when I tried something nontrivial (code wise, not execution-time wise, I know to keep things short in such a context) on the timer thread, I've got a stack overflow.

I know how to modify the FreeRTOS config underneath, which gets me back into the game, but a reasonable extension of the timer thread stack sounds cheaper (memory-wise) than rolling out another handler thread to forward timed things into. nRF52 has plenty of memory anyways, by Arduino standards...

@hathach
Copy link
Member

hathach commented Aug 30, 2018

try our ada_callback() and ada_callback_fromISR()

https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/develop/cores/nRF5/utility/AdaCallback.h#L116

it is meant to use internally for deferred callbacks. But can be served as generic worker thread, I may rename it to make that clearer and easier to use for Arduino sketch later.

@hathach
Copy link
Member

hathach commented Aug 30, 2018

Though you need to be aware of which thread your code is running, shouldn't wait for an semaphore or event that triggered within callback thread, while you are there. Some callback (not all, we do that later) has an option to be invoked in callback thread or in the main ble thread. You can make use of that for adv threading.
e.g
https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/develop/libraries/Bluefruit52Lib/src/BLEClientCharacteristic.h#L103

PS: for continue with threading, please post on the forum, or if you found any bugs/improvement, post it in separated issue. This one is for low power, Although they are somewhat related.

@pbecchi
Copy link
Author

pbecchi commented Aug 31, 2018

HAve a look to this post :https://devzone.nordicsemi.com/f/nordic-q-a/36649/custom-board-using-bt832-and-bt832x-power-drain-issue/146816#146816
It show very strange behaviour of delay(500) on fanstell BT 832 module, while it work as expected on BT832X.

Nordik PPK is a very useful tool to understand power drains!

@hathach
Copy link
Member

hathach commented Sep 1, 2018

@pbecchi that is interesting, indeed Nordic PPK is very handy for analyzing the current. I have one here as well :D

@wb8wka
Copy link

wb8wka commented Jan 12, 2019

@hathach
You mentioned last summer:

  • delay() in your library is more than delay as you figure out, more like sleep() now. Since we implement freeRTOS tickless https://www.freertos.org/low-power-tickless-rtos.html Everytime delay() is invoke it will put mcu into decent sleep mode (waitForEvent() ) an only wake up a few ticks before the time of scheduled action. It is all handled under the hood, man we should call it sleep() :D . It is a feature point in our upcoming "coding guideline for low power" tutorial. However the longer you delay/sleep the lower responsiveness of the device.

But when I got between waitForEvent vs. a delay, I'm seeing my current go from about 70ua to over 400ua... my scope trace shows waitForEvent is waking up every ~1ms where as with delay it's about every 20us. Perhaps I misunderstood this. Any thoughts welcome. My code sample is below, with relevant stuff at the very bottom

Thanks for any feedback

`/*********************************************************************
 This is an example for our nRF52 based Bluefruit LE modules

 Pick one up today in the adafruit shop!

 Adafruit invests time and resources providing this open source code,
 please support Adafruit and open-source hardware by purchasing
 products from Adafruit!

 MIT license, check LICENSE for more information
 All text above, and the splash screen below must be included in
 any redistribution
*********************************************************************/
#include <bluefruit.h>

// Beacon uses the Manufacturer Specific Data field in the advertising
// packet, which means you must provide a valid Manufacturer ID. Update
// the field below to an appropriate value. For a list of valid IDs see:
// https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
// 0x004C is Apple (for example)
#define MANUFACTURER_ID   0x004C 

// AirLocate UUID: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
uint8_t beaconUuid[16] = 
{ 
  0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 
  0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 
};

// A valid Beacon packet consists of the following information:
// UUID, Major, Minor, RSSI @ 1M
BLEBeacon beacon(beaconUuid, 0x0000, 0x0000, -54);

void setup() 
{
//  Serial.begin(115200);
//  while ( !Serial ) delay(10);   // for nrf52840 with native usb
//
//  Serial.println("Bluefruit52 Beacon Example");
//  Serial.println("--------------------------\n");

  Bluefruit.begin();

  // off Blue LED for lowest power consumption
  Bluefruit.autoConnLed(false);
  
  // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4
  Bluefruit.setTxPower(0);
  Bluefruit.setName("Current Test");

  // Manufacturer ID is required for Manufacturer Specific Data
  beacon.setManufacturer(MANUFACTURER_ID);

  // Setup the advertising packet
  startAdv();

  //Serial.println("Broadcasting beacon, open your beacon app to test");

  // Suspend Loop() to save power, since we didn't have any code there
  //suspendLoop();

  Serial.end();
  sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
  sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
}

void startAdv(void)
{  
  // Advertising packet
  // Set the beacon payload using the BLEBeacon class populated
  // earlier in this example
  Bluefruit.Advertising.setBeacon(beacon);

  // Secondary Scan Response packet (optional)
  // Since there is no room for 'Name' in Advertising packet
  Bluefruit.ScanResponse.addName();
  
  /* Start Advertising
   * - Enable auto advertising if disconnected
   * - Timeout for fast mode is 30 seconds
   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
   * 
   * Apple Beacon specs
   * - Type: Non connectable, undirected
   * - Fixed interval: 100 ms -> fast = slow = 100 ms
   */
//  Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_ADV_NONCONN_IND);
  Bluefruit.Advertising.restartOnDisconnect(true);
  //Bluefruit.Advertising.setInterval(160, 160);    // in unit of 0.625 ms     100ms
  //Bluefruit.Advertising.setInterval(200, 400);    // in unit of 0.625 ms     125ms
  Bluefruit.Advertising.setInterval(1600, 1600);    // in unit of 0.625 ms   1 second
  //Bluefruit.Advertising.setInterval(16000, 16000);    // in unit of 0.625 ms   10 second
  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds    
}

void loop() 
{
waitForEvent();    // current about 70ua, using a uCurrent gold into a Fluke 179 in averaging mode
//delay(100);      // current about 410ua, with above,  FreeRTOSConfig.h #define configUSE_TICKLESS_IDLE 1 is set
}`

@ogatatsu
Copy link
Collaborator

Hello!

With the latest firmware, it seems that power consumption increases as softdevice is turned on.

https://devzone.nordicsemi.com/f/nordic-q-a/35073/high-power-consumption-of-softdevice/136048#136048

In my environment power consumption has been reduced by rewriting port_cmsis_systick.c as written on this link.

@wb8wka
Copy link

wb8wka commented Jan 15, 2019

Hello!

With the latest firmware, it seems that power consumption increases as softdevice is turned on.

https://devzone.nordicsemi.com/f/nordic-q-a/35073/high-power-consumption-of-softdevice/136048#136048

In my environment power consumption has been reduced by rewriting port_cmsis_systick.c as written on this link.

I looked at that and it wasn't entirely clear how it would impact the delay function. Is that what you observed?

@hathach could you follow up on my earlier question directed to you?

@ericlangel
Copy link

some Measurements with my NRF52832 module:

void loop()
{
// some other stuff to do...
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
waitForEvent();
}

i get arround 7µA current consumption (BLE advertising). This is the same value when i used RedBear Lib.
Settings:
2 sec advertise interval
4HZ RTOS Tick Rate (cant get lower)
no LED, no Serial, No I2C, No GPIOs

with delay(1000) in loop() i get arround 2000µA!
suspendLoop() i get arround 1000µA!

@wb8wka
Copy link

wb8wka commented Jan 16, 2019

4HZ RTOS Tick Rate (cant get lower)
no LED, no Serial, No I2C, No GPIOs

with delay(1000) in loop() i get arround 2000µA!
suspendLoop() i get arround 1000µA!

@ericlangel

Thank you. Can you post how you are setting RTOS tick rate? I know from scope measurement with waitForevent() it is 1khz so I think this is real key here. Or better yet post your example please.

I was able to reduce delay current in loop some by adding the idle task but as you say it is still quite high. I think your method may be the best. Hope you can reply. Thanks

/**
 * RTOS Idle callback is automatically invoked by FreeRTOS
 * when there are no active threads. E.g when loop() calls delay() and
 * there is no bluetooth or hw event. This is the ideal place to handle
 * background data.
 * 
 * NOTE: FreeRTOS is configured as tickless idle mode. After this callback
 * is executed, if there is time, freeRTOS kernel will go into low power mode.
 * Therefore waitForEvent() should not be called in this callback.
 * http://www.freertos.org/low-power-tickless-rtos.html
 * 
 * WARNING: This function MUST NOT call any blocking FreeRTOS API 
 * such as delay(), xSemaphoreTake() etc ... for more information
 * http://www.freertos.org/a00016.html
 */
void rtos_idle_callback(void)
{
  // Don't call any other FreeRTOS blocking API()
  // Perform background task(s) here
}

@ericlangel
Copy link

Hello wb8wka

you need to modify the RTOS Config Header

... adafruit\hardware\nrf52\0.9.3\cores\nRF5\freertos\config\FreeRTOSConfig.h

#define configTICK_RATE_HZ 4 // default = 1024

A Rate between 4 Hz and 10Hz uses arround 7µA on my Hardware (just the NRF52Module and some pull resistors @ 3,7V)
If i set the Rate <4Hz the MCU seem to get stuck somewhere.

please post a reply with your current consumption with Lower RTOS TickRate

@wb8wka
Copy link

wb8wka commented Jan 16, 2019

please post a reply with your current consumption with Lower RTOS TickRate

@ericlangel

average is now 36ua with your mods and the beacon rate at 2000ms. However, I only have the serial port off and still have I2C on. I need to read I2C sensor about every 30 seconds (not doing that yet) BLE beacon working correctly.

On my scope trace I am still seeing wakups ~1ms but they are not as "dense" as when tick was at 1024.

Thank you this is very good find, it is late so I will exam more later in the day.

@nicertwin
Copy link

It's the 832.
With the original current, I had a my local repo was on 8cba1aa, which i think is part of 0.10.

When i first tried updating, it was to 0.17, which is when I realized the current jump, but since my local was working, I just left it alone. More recently, I've also tried 0.20 and 0.21.

I tried just copying my old version of the repo and then giving it to others to use so that I can work around this, but they get the following error that I couldn't figure out, since the file does seem to exist in the bin folder. I can't find anything about that file anywhere online.
image

@selman-nus
Copy link

Another way to reduce power is to go full event response aka callback and abandon the use of loop() with suspendLoop() call. ( I will take note myself for the tutorial, before I start to forget things)

https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/libraries/Bluefruit52Lib/examples/Peripheral/beacon/beacon.ino#L59

Also you may want to tweak the ble radio timing/parameter such adv, connection interval etc ... the full list and its affect (current save) will come later when I tested them with the bench ( we may go as far as low power profile with pre-configured parameter for user). There may be more trick, but I kind of forget, will get back to this when we are done with the nrf52840 :D

Hi @hathach , Is this aforementioned "coding guideline for low power" tutorial published and already available? If it is not available can you please suggest a list of things we can try to reduce the power consumption?

@selman-nus
Copy link

so, i just tested the Beacon Example from Version 10.1
the only change: i stopped Serial with Serial.end()

with Suspendloop() at the end of Setup() i get 800-900µA
with waitforevent() in the loop() i get around 300µA
with waitforevent() and vPortSuppressTicksAndSleep(250) in the loop() i get also 800-900µA
with waitforevent() in the loop() and Tickrate = 4 i get around 130µA
with waitforevent() in the loop() and Tickrate = 4 and advertiseinterval = 2sek i get around 5-7µA

finaly.....suspendloop() doesn't save current
vPortSuppressTicksAndSleep(250) doesn't save current

MCU: NRF52832
Board: Custom ( Taiyo Yuden NRF52832 Module with external 32khz and external LC for DCDC)

Hi @ericlangel , I have the same board from Taiyo Yuden and it seems like it is using more current (0.5ma) than nrf52DK. I understand that some optimizations you list here don't help reducing consumption anymore because of the updates on the library.

Can you please advise me on what current optimizations/tweaks I can work on so I can reduce the power consumption on this board?

@ericlangel
Copy link

@selman-nus
which board do you mean? i have never used a taiyo board. i only use their NRF52 Modules with custom made boards.

i think i cant advise you, because i stay on Version 0.9.3.
Never tested actual versions. Had no time for that.

@selman-nus
Copy link

@ericlangel
I thought you were using the EBSHSNZWZ board which has EYSHSNZWZ on it. However, I have already disconnected the module from the rest of the board. So I am also using the same module with you.

I can test on the current version and update here with my findings. But I don't know what should I test, I am new to this library.

@ericlangel
Copy link

@selman-nus
Watch out: The EYSHSNZWZ is different to Adafruit default.

It has no LF 32khz Osc.

So you need to Enable the RC Osc

//#define USE_LFXO // Board uses 32khz crystal for LF
#define USE_LFRC // Board uses RC for LF

its defined in variant.h of your Arduino selected Board

C:\Users\Username\AppData\Local\Arduino15\packages\adafruit\hardware\nrf52\0.9.3\variants\feather_nrf52832

@selman-nus
Copy link

Thanks for the tip, I will try that when I am designing a custom board with this module. But actually the current board I am using (EBSHSNZWZ) already has the 32khz crystal and the DCDC converter: (see image below)

I have a question tho, is that RC Osc increasing power consumption?

image

@ericlangel
Copy link

its a long time ago that i worked with....

but using rc when 32khz osc is connected was a bad idea (dont know why actual)
and using 32khz osc without connected 32khz osc was also a bad idea (dont know why actual)

sorry, i think i cant help you at the moment

@ajs123
Copy link
Contributor

ajs123 commented Jul 11, 2021

Update The nice power reduction I'm seeing may be attributable to this change in v0.23 of the core library rather than to the difference between suspendloop() and a scheduled task vs. the traditional loop(). Apologies for that - I've left the post in case the summary is useful to others but removed the part attributing the 6 mA reduction in supply current to the scheduled task approach.

Since I've consulted this tread, and things it points to, repeatedly, I thought I'd weigh in with what worked for me.

In short, replacing

void loop()
{
  //do all the stuff you need to do
  delay(LOOP_INTERVAL);
}

with

void setup()
{
  // Your normal initialization code goes here
  SuspendLoop();          // Don't use the traditional Arduino loop() function
  SoftwareTimer myLoopTimer;         // Set up a timer for the routine that replaces loop()
  myLoopTImer.begin(LOOP_INTERVAL, myLoop);      // Set it to run every LOOP_INTERVAL ms
  myLoopTimer.start();       // Start it
}

void loop     // Empty function
{
};

void myLoop(TimerHandle_t xTimerID)
{
// Things you used to do in loop()
// Do not end with delay()
}

Elsewhere (typically a callback), you can pause with
myLoopTimer.stop();
and resume with
myLoopTImer.start();

@jpconstantineau
Copy link
Contributor

@ajs123 You might want to note that some things cannot be called from timers. Take for example file system access. I did try to save data to a file from a timer (update settings for future reloading in setup) and never managed to make it work. As soon as I put it in the main loop, it worked.

A constant 6mA is an indication that the CPU never sleeps. If you use BLE, actual current consumption will be very "spiky" as you can see in #600

@ajs123
Copy link
Contributor

ajs123 commented Jul 12, 2021

Yes re the 6mA - see my update to the post. I was looking and looking for the FPU interrupt issue perhaps still lingering and most likely I was falling victim to the crypto interrupt issue that's fixed with v0.23 of the core.

Re things that cannot be called from timers, the issue I'm aware of is with things that take longer than the recurring timer interval (or, in the case of deferred callbacks from interrupts, things that take longer than the possible time between interrupts). See here for use of direct-to-task notification as a solution. The "mainline" task runs at lowest priority and blocks unless it has a notification waiting. There can be multiple notifications and it will only unblock once.

With that said - and I hope it's useful - if performance is no different with delay() then in many cases it will be simpler, and still effective, to use the regular Arduino loop(). In my application, I want to be able to go into a very low power mode. I was unable (at least with the 0.21 and 0.22 cores) to get resumeloop() to work but I was successful (with the 0.23 core) with stopping and restarting a timer. Power usage with the timer stopped is barely different from what I get using systemOff().

@ericlangel
Copy link

In the last Days i tried the V1.2 release.

And i could get very good results for Current consumption.
i did something similar to @ajs123

suspendLoo() after Setup and in Loop itself.
And with a timer i call resumeLoop(), to process what i want.

So i got around 4-6µA current consumption (with Advertising Interval Peaks).
I dont changed anything in RTOS or Core.

So for me the V1.2 is a great improvement against V0.9.3

@joseangeljimenez
Copy link

Thanks @ericlangel for your nice report!
For the sake of completeness, can you indicate,

  • Which hardware are you using (nRF52832 or nRF52840)? Is it a modified commercial board? Or custom?
  • Which board definition?

@ericlangel
Copy link

oh sorry

its NRF52832 on a custom Board (without LDO, supplied by 3,6V Li-Battery)
Selected Board is Adafruit Feather nRF52832
Advertising Interval = 2000ms

@Siroyan
Copy link

Siroyan commented Jan 9, 2022

Hello!

Currently, I am having trouble with the high power consumption of Adafruit Feather nRF52840 Board.
According to RAYTAC's datasheet, it should only consume a few micro-A in sleep mode, but in my environment, it was at least 700uA. Do you have any good ideas?
By the way, I'm thinking of eventually making a product that transmits sensor values via BLE once every few minutes.

  • Board: Adafruit Feather nRF52840
  • Version : v1.2
  • Dev env : PlatformIO

Following are the results of my experiment.

  • 700 uA (Minimum)
#include <Arduino.h>

void setup() {
  suspendLoop();
}

void loop() {}
  • 7.65 mA
#include <Arduino.h>

void setup() {
  sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
}

void loop() {
  waitForEvent();
}

@ajs123
Copy link
Contributor

ajs123 commented Jan 9, 2022

The NeoPixel on the Feather board consumes about 500uA when not lit. It’s not too hard to carefully pop it off (all at once or kind of layer by layer) with a pair of small side cutters. That should get you down to 200 uA. Recalling past forum discussion, the interface chip for the USB may be a significant contributor as well.

@Siroyan
Copy link

Siroyan commented Jan 10, 2022

@ajs123
Thanks for the reply !
I could understand that the power consumption of the LED is the problem. I will try to remove it.
As for the USB chip, I don't think it is installed on the board I am using.
I am supplying power directly from the 3V pin.
Here is the schematic.
https://learn.adafruit.com/assets/68545

P.S.
After removing the LED, the current consumption dropped to 500uA.

@jpconstantineau
Copy link
Contributor

@Siroyan Have you tried running the "Blink_sleep" example?
It blinks for 30 seconds, then it goes to deep sleep.
Before going in deep sleep, it sets up 2 wake-up pins that the chip will wake up and reboot if it detects the correct transition on.

During that sleep time, you should be able to measure a truely low current consumption from the chip and anything above a couple of uA should be from any additional hardware on your board (neopixel being one hungry thing...)

That being said, are you quoting average currents? This will likely be very spiky in fact and highly dependent on the code you have.

For my BlueMicro_BLE keyboard firmware, I was able to bring the current consumption to a low level in between radio transmissions, however, you have to keep in mind that when the CPU is running, it can consume about 6mA and when the radio is running, it can generate spikes upwards of 20mA. As such, having a device that measures current consumption very rapidly is very useful to tweak and verify that your code is indeed minimizing power consumption. Have a look at my video here that's done with the PPK2 to get a hint about what that looks like.

Getting the CPU to low power mode when doing nothing is very important; otherwise, you will get that 6 mA I was mentioning above.

I haven't checked whether my code is still "low power" with the latest (1.2.0) but I use these 2 methods:
1 - add a delay at the bottom of the loop. This ensures that the loop doesn't run as soon as it's completed. Without a delay, it will restart the loop as soon as possible, making the CPU run in circles very fast (~6mA).
2 - add a vApplicationIdleHook(void) function with sd_power_mode_set and sd_app_evt_wait. I don't know if these are really needed or already done somewhere else but for my code, it seems to help. This task is the one that the RTOS uses when there are no other tasks left to do and it goes idle. Have a look here for the code I use...

@Siroyan
Copy link

Siroyan commented Jan 10, 2022

@jpconstantineau
Thanks for your reply !

When I tried the sample program, the current consumption after the LED blinks was about 500uA.
I would eventually like to get the current down to a few uA during sleep, and I don't think this is quite enough.

I am using Keysight's b2900 series Source mesurement unit. It should have enough resolution and I don't think it is "average current".
I tried to use sd_power_system_off() and sd_power_mode_set(NRF_POWER_MODE_LOWPWR), but they all consumed about 6~7mA, and the lowest current was when I used suspendLoop().

@ericlangel
Copy link

@Siroyan
Your suspendLoop() Code is the right example with the V1.2 (my experience)
But i have no experience with the NRF52840. Maybe the USB consumes some power?

But as the Neopixel show....dealing with µA is not just code. Every 10k Resistor matters -> 3V3/10k = 330µA
So you should also have a look at the schematic.

@Siroyan
Copy link

Siroyan commented Jan 10, 2022

@ericlangel
Thank you for pointing this out.
After reviewing the schematic, I thought it might be related to the power consumption of the QSPI Flash and the leakage current from the OUT side of the LDO.
However, I don't think both of them are enough to reach several hundred uA, I would appreciate your opinion.

@ericlangel
Copy link

i dont know when QSPI is used.
In idle(slepp) it should consume 25µA.

But you said you suppling the Board with the 3V Pin?

Did you checked (measured) reverse Current of the LDO?

@Siroyan
Copy link

Siroyan commented Jan 10, 2022

@ericlangel
Thanks.

But you said you suppling the Board with the 3V Pin?

Yes, and I measured current with LDO removed now, so It still consumed about 500uA.

With the nRF52840 running suspendLoop(), if there are no factors on the board that consume current, the total consumption should be only a few uA, right?

@ericlangel
Copy link

I have never used NRF52840 (never tested current consumption).

I just used NRF52832. And yes, with the example above you get around 2-5µA. (without BLE advertise!)

@Siroyan
Copy link

Siroyan commented Jan 10, 2022

Thank you very much for people, for helping me !

I said "LDO removed now", but actually I removed LiPo charger chip.

After removing the LDO, the current dropped to a few µA !!!
The waveform of the current is very noisy though.

@ericlangel
Copy link

ericlangel commented Jan 18, 2022

i have a strange Bug

void clock_handle(void)
{
  Status.RTC_Flag = true;
}

attachInterrupt(PIN, clock_handle, FALLING);

causing a Current of around 410µA

without this line i have 5-6µA

@jgartrel
Copy link
Contributor

jgartrel commented Sep 10, 2022

I seem to have a similar issue that @ericlangel described above when testing with the nrf52832. When using BOTH the Wire Library (I2C) and attachInterrupt, power consumption increases dramatically from <11uA to 472uA.

I ran 4 tests and measured the idle current during the delay(10000) in loop:

  1. Baseline (No I2C, no attachInterrupt) - 2.525uA
  2. attachInterrupt only - 10.25uA
  3. I2C only - 2.525uA
  4. Both I2C and attachInterrupt - 472uA

The code snippit below seems to be the simplest way to express the issue:

#include <bluefruit.h>
#include <Wire.h> //I2C library

#define INT_PIN 15

volatile unsigned long ms = 25;

void irqHandler()
{
  #if CFG_SYSVIEW
  SEGGER_SYSVIEW_RecordEnterISR();
  #endif

  ms = 2000;

  #if CFG_SYSVIEW
  SEGGER_SYSVIEW_RecordExitISR();
  #endif
}

void setup() {
  Bluefruit.begin();
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, !LED_STATE_ON);

  Wire.begin();
  delay(100);

  Wire.beginTransmission(0x0E);
  Wire.write(0x0F);
  Wire.endTransmission();

  Wire.end();
  delay(100);

  pinMode(INT_PIN, INPUT_PULLUP);
  attachInterrupt(INT_PIN, irqHandler, FALLING);
}

void loop() 
{
  digitalWrite(LED_BUILTIN, LED_STATE_ON);
  delay(ms);
  digitalWrite(LED_BUILTIN, !LED_STATE_ON);
  delay(10000);
}

Test 1 - (2.525uA):

//  Wire.beginTransmission(0x0E);
//  Wire.write(0x0F);
//  Wire.endTransmission();
...
//  attachInterrupt(INT_PIN, irqHandler, FALLING);

Test 2 - (10.25uA):

//  Wire.beginTransmission(0x0E);
//  Wire.write(0x0F);
//  Wire.endTransmission();
...
  attachInterrupt(INT_PIN, irqHandler, FALLING);

Test 3 - (2.525uA):

  Wire.beginTransmission(0x0E);
  Wire.write(0x0F);
  Wire.endTransmission();
...
//  attachInterrupt(INT_PIN, irqHandler, FALLING);

Test 4 - (472uA):

  Wire.beginTransmission(0x0E);
  Wire.write(0x0F);
  Wire.endTransmission();
...
  attachInterrupt(INT_PIN, irqHandler, FALLING);

Should I open this as a separate issue, or should we just use this thread to discuss it?

@celiafb
Copy link

celiafb commented Mar 1, 2024

Hi everyone,
I am using a custom board with the Fanstel BC832 module (using nRF52832). I'm trying to use PlatformIO to program through the Arduino libraries using VS Code. With the provided Arduino BLE sample projects, I'm only able to get a minimum current draw of about 6.5 mA. I need it to be no more than 2.5 mA. Does anyone have a sample script that enables BLE (read/write) and sets the module idle in between for minimal power consumption?

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Next Release
  
In Progress
Development

No branches or pull requests