adafruit / Adafruit_nRF52_Arduino

Adafruit code for the Nordic nRF52 BLE SoC on Arduino
Other
608 stars 493 forks source link

More on Power consumption #165

Open pbecchi opened 6 years ago

pbecchi commented 6 years ago

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 commented 6 years ago

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.

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 commented 6 years ago

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 commented 6 years ago

@hathach Thanks for your replay!

To be more precise on my previous message:

hathach commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

related to #51

Nenik commented 6 years ago

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 commented 6 years ago

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

Nenik commented 6 years ago

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 commented 6 years ago

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

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

Nenik commented 6 years ago

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 commented 6 years ago

@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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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

wb8wka commented 5 years ago

@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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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.

wb8wka commented 5 years ago

@ericlangel

With less sleepy eyes, I'm seeing ~9ua average now. That's as before. Also the scope is less active, much closer to a current spike every 250ms which is what I would expect. I'm thinking perhaps I didn't allow all the caps to fully drain and something was in a higher current mode. I did see you were on 0.9.3 (I was on 0.9.2) but I did verify before updating. Didn't make any difference.

Now to make it do something useful. I hope to read a sensor and update the BLE advertisement.

monsunek commented 5 years ago

I have to say i'm tired with this nrf52:) Guys help, i think i've already tried everything and my current consumption is to big.

#include <bluefruit.h>
#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, 
};

BLEBeacon beacon(beaconUuid, 0x0000, 0x0000, -54);

void setup() 
{
  Bluefruit.begin();

  Bluefruit.autoConnLed(false);
  Bluefruit.setTxPower(0);
  Bluefruit.setName("Bluefruit52");
  beacon.setManufacturer(MANUFACTURER_ID);

  // Setup the advertising packet
  startAdv();

  sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
  sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);

  Serial.end();
  //suspendLoop();
}
void startAdv(void){  
  Bluefruit.Advertising.setBeacon(beacon);

  Bluefruit.ScanResponse.addName();

  //Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_ADV_NONCONN_IND);
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(8000, 8000);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(5);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds  
}
void loop(){
  __set_FPSCR(__get_FPSCR() & ~(0x0000009F)); 
  (void) __get_FPSCR();
  NVIC_ClearPendingIRQ(FPU_IRQn);
  sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
  waitForEvent();
  //delay(1000);
  // loop is already suspended, CPU will not run loop() at all
}

Ive change this in rtos_cmsis_systick.c

`#if 1 // With FreeRTOS sd_app_evt_wait increases power consumption with FreeRTOS compared to _WFE (NRFFOSDK-11174)

ifdef SOFTDEVICE_PRESENT // TODO`

and of course systickrate #define configTICK_RATE_HZ 4//1024

My consumption:

ericlangel commented 5 years ago

@monsunek which Hardware do u use? original Adafruit feather?

monsunek commented 5 years ago

This two

ericlangel commented 5 years ago

The Feather's 3V3 Voltage Regulator has a Quiescent Current Typ. 55µA (max. 80µA) The CP2104 has Input Leakage Current of 25µA to 50µA

So...your Feather is working as expected

monsunek commented 5 years ago

I thought CP2104 in rev G. is supplied from Vbus(usb) only when connected. Or do you mean leakage through TX RX pins? Putting those pins in nRF as input pulledup should help? I'm making thing like that with SD card in sleep period, which give me additional 100uA.

ericlangel commented 5 years ago

i think i found something for better Power consumption:

vPortSuppressTicksAndSleep(250); sd_app_evt_wait();

This stops FreeRTOS Tick Rate for 250ms. I actually get 4 Wakeups in 1 second with a Tick Rate in Config.h of 1024!

of course 250ms can be set to higher or lower values.

in the next days i will do some current measurements. Attention: this is tested with 0.9.1 In the next Days i will test it with 0.10.1

hathach commented 5 years ago

Just want to warn you 0.10.1 added lots of API changes. There will be more coming in the future releated to usb prph.

MacGyverNL commented 5 years ago

vPortSuppressTicksAndSleep(250); sd_app_evt_wait();

vPortSuppressTicksAndSleep internally calls __WFE() so I think that sd_app_evt_wait will only be executed after the system returns from its sleep period; I might be wrong there. Still, I don't think you should do this, see below.

This stops FreeRTOS Tick Rate for 250ms. I actually get 4 Wakeups in 1 second with a Tick Rate in Config.h of 1024!

of course 250ms can be set to higher or lower values.

After deep-diving in freeRTOS's documentation and the port's code, I'm going to say "don't do this". Yes, it does precisely what you want: It suspends the system tick for 250ms, and sleeps for that time. However, that function is literally what tickless-configured freeRTOS does by default, but properly, if only the idle task can be run and (in the case of this library) 2 tick periods (so 2ms here) are expected to pass before it has to wake up again. Note that this also means that lowering the tick rate to 4Hz will effectively mean for all intents and purposes your device never goes into this mode.

freeRTOS will calculate the time it should sleep, also setting up a timer to wake the system based on when it knows it will need to wake; or sleeping indefinitely if no timers are active, and no tasks are due to become runnable at a fixed time in the future.

What you should do instead, and has been hinted at a few times, is make proper use of interrupts, timers, tasks, and callbacks. Stop thinking about loop() as this special thing that always runs on the bare metal -- that's not how this system works. Instead, consider that you are running on top of a full-fledged operating system that has proper task management. loop() on this system is just another task. suspendLoop() shows this: it simply calls vTaskSuspend(_loopHandle);. What's currently missing, if you insist on using loop(), is a function resumeLoop() to be called from an ISR or timer callback. However, there's absolutely no reason you could not just set this up as a timer callback in the first place. Just write your application code in a simple function, as though it was loop(), but don't try anything fancy with delay() or __WFE() or whatever. Define a SoftwareTimer with that function as callback and a period of 250ms in Setup(). Start that timer, and end Setup() with a call to suspendLoop().

And if you don't even need that, and only need to respond on inputs changing, as I do in my project, consider using interrupts. The only working example of this I currently have is https://github.com/MacGyverNL/bluepedal/blob/02213ee9dcc495ccdc47f572892f7388aac09a55/bluepedal/bluepedal.ino It's not a minimal example, but it should get the point across. Note I'm firing off timers in those interrupts, because these are bouncy buttons and I actually want to wait 3ms after the last interrupt fires before considering it as "finished". This is one way to do it if you have a critical part of your interrupt (e.g. the incrementing of a counter for every interrupt fired) and a non-critical part (e.g. sending stuff over bluetooth). Another way is using vTaskNotifyGiveFromISR if you just want the handler of the non-critical part to fire as soon as it can. And if you have no critical part, consider using ISR_DEFERRED | <mode> when calling attachInterrupt, that will queue up a task for the entire handling of the interrupt; as though you'd only called vTaskNotifyGiveFromISR in a normal ISR.

Granted, this library is currently missing quite a bit of glue code, as well as examples (e.g. I'm at a loss w.r.t whether I could use ada_callback instead of the freeRTOS code I'm currently using) and you may find yourself calling directly into freeRTOS's API instead, but I'm hoping that with more people figuring out what they actually need here, that glue code and documentation will appear.

ericlangel commented 5 years ago

@MacGyverNL

thank you for your thoughts. I will do some tests on that.

But the Last time i played around with suspendLoop(), i had a current of 1000µA! But it should be around 5µA. So vPortSuppressTicksAndSleep(250) or 4Hz Tick rate was a fast an easy code to get down to 5µA

MacGyverNL commented 5 years ago

But the Last time i played around with suspendLoop(), i had a current of 1000µA! But it should be around 5µA. So vPortSuppressTicksAndSleep(250) or 4Hz Tick rate was a fast an easy code to get down to 5µA

That implies that, for some reason, freeRTOS isn't sleeping by itself on a 1024Hz tick rate and suspendLoop in that situation. Would be interesting to figure out why that might be happening. I don't have the power analysis tooling for precise measurements, but afaict the idle task callback gets called at least once before the system sleeps; which has allowed some rudimentary sleep behaviour analysis by toggling an LED pin in it.

wb8wka commented 5 years ago

I had the same experience with suspendloop although my knowledge at the time was minimal.

On Mon, May 6, 2019 at 8:48 AM Pol Van Aubel notifications@github.com wrote:

But the Last time i played around with suspendLoop(), i had a current of 1000µA! But it should be around 5µA. So vPortSuppressTicksAndSleep(250) or 4Hz Tick rate was a fast an easy code to get down to 5µA

That implies that, for some reason, freeRTOS isn't sleeping by itself on a 1024Hz tick rate and suspendLoop in that situation. Would be interesting to figure out why that might be happening. I don't have the power analysis tooling for precise measurements, but afaict the idle task callback gets called at least once before the system sleeps; which has allowed some rudimentary sleep behaviour analysis by toggling an LED pin in it.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/adafruit/Adafruit_nRF52_Arduino/issues/165#issuecomment-489608049, or mute the thread https://github.com/notifications/unsubscribe-auth/ABQKNA5BNSCSUDT2XTTDORLPUASLLANCNFSM4FLLTOQA .

ericlangel commented 5 years ago

So i just did the Test with suspendLoop()

its called as the last command in Setup() and in loop()

without suspendLoop() -> around 350µA with suspendLoop() -> around 1000µA

i just added suspendLoop()...nothing else is changed. and....loop() is not running!

So suspendLoop() doesn't save current.

MacGyverNL commented 5 years ago

That makes no sense. Could you post the entire sketch, along with any changes you've made to config (e.g. lowering tick rate)?

wb8wka commented 5 years ago

@MacGyverNL

This is good low cost tool for ua current measurements;

https://lowpowerlab.com/guide/currentranger/

I use it with meter in averaging mode. To use with scope you will have to float it (use a battery to power NRF52).

jpconstantineau commented 5 years ago

The current ranger does appear to be more useful than the uCurrent. I have a ucurrent and it's not trivial to use because it's not auto-ranging.

I also have a power profiler from nordic. Seeing the actual current over time is very insightful; especially with recurring tasks. Very often, I have observed how making small changes in the code impacts the "shape" of power consumption. There are "High Current" periods and "Low Current" periods. Mapping these periods to code helps in understand what really helps and what doesn't. It's the combination of time spend in each period as well as the average current drawn during each period that really impacts the total average current as seen on a multimeter.

Looking at the time element helps a lot. The nordic power profiler also needs a nrf52832 dk. The combination isn't the cheapest but if you don't have a scope, and really want to understand your power consumption, it's a worthwhile investment.

On Tue, 7 May 2019 at 10:08, wb8wka notifications@github.com wrote:

@MacGyverNL https://github.com/MacGyverNL

This is good low cost tool for ua current measurements;

https://lowpowerlab.com/guide/currentranger/

I use it with meter in averaging mode. To use with scope you will have to float it (use a battery to power NRF52).

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/adafruit/Adafruit_nRF52_Arduino/issues/165#issuecomment-490145410, or mute the thread https://github.com/notifications/unsubscribe-auth/AAIYW6EZJM7S5GTMWR7SNLLPUGSRBANCNFSM4FLLTOQA .

-- Pierre

wb8wka commented 5 years ago

@jpconstantineau

Good comments. I've got a power profiler from Nordic although I use the integrate math function on my scope more since I'm working with custom boards.

However for the gross differences being discussed here even a simple meter would quickly show the differences with suspendLoop()

MacGyverNL commented 5 years ago

Sure, but looking at what suspendLoop does, it just suspends that task. To figure out why that leads to a 650µA current draw, it would be very helpful to be able to monitor the shape of power consumption.

For example: Maybe it sleeps more often, but it actually draws more current than desirable when going to sleep / resuming from sleep -- in which case, figuring out how many idle ticks would need to pass before a net gain is had would be ideal.

monsunek commented 5 years ago

It would be convenient if everybody would include information about CPU and Hardware type on which you are making tests.

ericlangel commented 5 years ago

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)

trungdn commented 5 years ago

Hi. This's so interesting around power consumption. I tried it on Board version 0.11.0 with all you guys suggestion. Looks like waitforevent() doesn't help any more. With delay() Tickrate =4 advertiseinterval=2s. I get around 500-700uF. What should be the best board version should I use? 10.1 9.3 9.1 Also is bootloader version matter? Mine is 0.2.11 SoftDrive s132 6.1.1

Thanks

jpconstantineau commented 5 years ago

For the keyboard code I am maintaining, low power consumption is about 1-1.5mA average. When power consumption is "broken" it usually increases the average to 7.5mA. I do have code to go in "deep sleep" with a wake-up on keypress. In deep sleep, current is on the order of 1uA.

I was able to re-gain my low power consumption with BSP 0.11.0 by:

1- suspending the main loop in setup (last line of setup) suspendLoop();

2 - create software timers for specific tasks (in setup) batterytimer.begin(30*1000, batterytimer_callback); //update battery service every 30 seconds. batterytimer.start();

3 - Add the following code at the end of each timer callbacks: sd_power_mode_set(NRF_POWER_MODE_LOWPWR); sd_app_evt_wait();

On Tue, 25 Jun 2019 at 15:26, trungdn notifications@github.com wrote:

Hi. This's so interesting around power consumption. I tried it on Board version 0.11.0 with all you guys suggestion. Looks like waitforevent() doesn't help any more. With delay() Tickrate =4 advertiseinterval=2s. I get around 500-700uF. What should be the best board version should I use? 10.1 9.3 9.1 Also is bootloader version matter? Mine is 0.2.11 SoftDrive s132 6.1.1

Thanks

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/adafruit/Adafruit_nRF52_Arduino/issues/165?email_source=notifications&email_token=AAIYW6GE2EIEKVQ4FOUNXH3P4KEQHA5CNFSM4FLLTOQKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODYRUTFA#issuecomment-505629076, or mute the thread https://github.com/notifications/unsubscribe-auth/AAIYW6HYVI4RN5URETBJZHLP4KEQHANCNFSM4FLLTOQA .

-- Pierre

orhanyor commented 4 years ago

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.

i would like to ask a question slightly out of topic but still related to some degree :) at the end you mentioned about lower responsiveness of the nrf which i really want to avoid in my RC car remote project(snappy and responsive connection is what im after). To people who dont care about power consumption and want max responsiveness is it enough to avoid delays in the main loop? thanks @hathach

billyjoker commented 4 years ago

Hi, i have read some issues related with the power consumption and i have no clear how to do a well setup to use the Adafruit with a battery. I did some tests with "standard" code but i see that the lipo batt is drained too fast, so my questions are below:

Should i send a command code from my controller device (Android phone) to set the Adafruit as some as a "standby mode"? I mean if exists something like power off and power on by BT commands. I do not if it is the called as "sleep mode" and "wake up"

Should i add delays in the loop code in order to have less consumption? I've read this thread but i do not see clear if these are good practices reccommeded by Adafruit

Exists any convenction to proceed when the lipo batt is almost empty? i mean, advice to the user when batt is under 5%

When i put the charger, the lipo batt will be recharging automatically? Or maybe i should to manage something programatically.

I am newby using this Adafruit BT device and i need some patterns to start working well with it. Thanks in advance