arkhipenko / TaskScheduler

Cooperative multitasking for Arduino, ESPx, STM32, nRF and other microcontrollers
http://playground.arduino.cc/Code/TaskScheduler
BSD 3-Clause "New" or "Revised" License
1.21k stars 221 forks source link

SleepCallback(aDuration) seems not to be usable for practical purposes #141

Closed poveden closed 2 years ago

poveden commented 2 years ago

The value of the aDuration parameter on calls to the SleepCallback method currently informs the total number of microseconds that the last idle run lasted:

https://github.com/arkhipenko/TaskScheduler/blob/776cb42397c6e059612cbb47709820c2d35386d4/src/TaskScheduler.h#L1228 https://github.com/arkhipenko/TaskScheduler/blob/776cb42397c6e059612cbb47709820c2d35386d4/src/TaskScheduler.h#L1393 https://github.com/arkhipenko/TaskScheduler/blob/776cb42397c6e059612cbb47709820c2d35386d4/src/TaskScheduler.h#L1403

I would have expected that aDuration provided the duration of the sleep period instead.

Am I mistaken? Am I missing something?

arkhipenko commented 2 years ago

Hi,

SleepCallback is responsible for deciding if and for how long it should put the MCU to sleep. The implementation is MCU-specific, and is configurable dynamically (meaning you can decide how long you want the scheduler to sleep by implementing this method). Typically a scheduler runs every 1 ms. This time interval is chosen because most MCUs wake up to update their millis() timer anyway. The aDuration tells you how much time MCU already spent going through the execute pass, so you can decide how long to sleep to keep the scheduling run at constant intervals. E.g., if idle run took 200 uS, you should sleep for additional 800uS to make it up to a full ms. On Arduino you don't need to do anything since it will wake up every 1 ms anyways, but on esp32 for instance, you need to tell MCU how long you want it to light sleep.

poveden commented 2 years ago

That was a really detailed explanation. Thanks!

However, it's not clear to me then how could I combine SleepCallback with a power-saving library like ArduinoLowPower. I want to run a few tasks that go idle for hundreds of milliseconds, and SleepCallback seemed to me like the ideal place to handle this. Perhaps I should do it somewhere else (e.g. in loop())?

arkhipenko commented 2 years ago

This is how I implemented a longer sleep on Arduino Uno board some time ago.

https://github.com/arkhipenko/pumpkin/blob/master/Pumpkin_Firmware.ino

Depending on what board you are running on you may not need to do anything actually.

If you are running on STM32 boards or Arduino Uno/Nano, the TS already puts them to sleep after an idle pass. Considering that an idle pass on Uno takes about 20uS, your MCU will be asleep for the rest of 980uS, or 98% of time? That's pretty good already, doesn't require additional programming and does not mess up TS clock. Now if you do put MCU to real sleep without millis() updates, TS will have no idea how long it slept upon wake up, so you will need to either restart the clock as if you just booted (by forcefully calling enable() on all tasks that should be running), or implement external_millis() and compensate for the sleep time with some external clock or alike

This is the compile parameter for external millis():

define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods