Open skorokithakis opened 4 years ago
esp32 or Esp8266?
#if defined(ARDUINO_ARCH_ESP8266)
// must have GPIO16 tied to RST
void EnterSleep(uint32_t microSeconds = 0,
void* state = NULL,
uint16_t sizeofState = 0,
WakeMode mode = WAKE_RF_DEFAULT);
bool RestartedFromSleep(void* state = NULL,
uint16_t sizeofState = 0 );
#elif defined(ARDUINO_ARCH_ESP32)
// ESP32 support for sleep not implemented yet
Back when I implemented this the ESP32 sleep features were not stabile enough to use them. I don't know how much the esp8266 sleep as changed and needs some love.
taskmanager.cpp line 110, (for esp8266) adding a delay(n) where n = 0,1,2 would be fine for the idle sleep (light sleep). Something similar for ESP32 should be added.
But, this general area is where an automatic idle level sleep would be triggered. Esp8266 there is the EnterSleep method also, for the real sleep modes.
Sorry, ESP8266. My use case is one where I update an OLED screen every 50ms, so I delay(20)
to take advantage of light sleep, but since Task can know exactly when the next task is going to trigger, it could sleep for longer.
This is assuming that delaying for longer has power advantages, otherwise I guess just adding a delay of 1 ms in my code would be equivalent.
I don't believe calling delay causes any power advantage on esp8266. You have to enter sleep to get any advantages; which is an explicit call. But we can ask on the esp8266 Arduino gitter channel and get a quick answer for that.
Note that they request calling delay() or yield() when in tight loops. But the way you are supposed to call the taskmanager.Loop() within the sketches loop() means that it quickly enters and leaves the sketches loop() and thus there is no need to call delay() or yield().
If you're on light sleep, calling delay does save power, it took me from 80 mA down to 60ish. In another program I wrote that used the Task library, I noticed that some interrupts wouldn't get processed sometimes unless I called delay(1)
in the loop, for some reason, it was very odd.
What about calling delay(0) or just yield()?
If either of these will cause the same ma drop, then I would be all in for putting that into the location I mentioned above.
I will measure various delays and report back, though I think it only benefits the CPU for the amount of time it's sleeping. Will check, though.
Alright, so:
delay() ms | amp draw |
---|---|
None | 77mA |
0 | 77 mA |
1 | 26 mA |
5 | 22-43 mA (very spiky) |
20 | 22 mA |
50 | 22 mA |
Unfortunately I don't have very reliable equipment, these tests were done with a Ruideng UM25C USB meter, so the refresh frequency is pretty low. Anything under 20 was pretty spiky, though, so I ended up setting the delay to 20ms for my needs.
If you want to look at the code, it's here: https://gitlab.com/stavros/do-not-be-alarmed
Ok, with this knowledge, then I would not include it in my library.
BUT it does warrant a FAQ entry in the wiki and maybe an update to the samples to include a delay(1) with a comment in the main loop() after calling taskmanager.Loop(). like
void loop() {
...
taskManager.Loop();
delay(1); // (1-100) used to allow the processor to idle, depending on sketch this value can be changed
}
That works, I was mainly wondering because you advise against calling delay()
in the user code.
My other question would be if, since you know when the next task is going to run, you could delay()
for that amount in the scheduler, so the CPU yields. So, for example, if I had two tasks, one every 50ms and the other every 90ms, you'd usually delay(50)
inside your loop, except for cases where you knew the next task was less than 50ms away. Would that be easy/valuable, or would it just be too much work because you don't exactly know when to wake up?
It does know "the time interval to the next task".
The issue are:
The time interval could be in microseconds or it could be in milliseconds; depending on the TASK_MICRO_RESOLUTION define.
Ah okay, I don't know why this is a problem but maybe you could just skip the delay if it's less than a few ms, where it wouldn't be effective anyway?
The time interval could be in microseconds or it could be in milliseconds; depending on the TASK_MICRO_RESOLUTION define.
Yes, I do this in other applications and this would definitely need to be an option when instantiating.
I am positive the results you are seeing are Esp8266 specific, most architectures will not like having the delay; but there is a spot for esp8266 specific code where it would go.
This is very likely (although I think even Arduino recommends adding a delay(0)
in the loop function so interrupts can be processed, but I'm not sure), but it could possibly be an ESP-specific thing, as you say.
Either way, this isn't a big deal, I can just delay()
outside the code, but you could do it with more precision (since you know when the next task will run). The larger issue I had was with clarifying in the README whether "don't call delay()
in your code!" was something we should absolutely never do or if we'd just miss events, as one would expect (and it seems to be the latter).
I really think you should put together a minimal sketch (without task) and check an empty loop versus a loop with a delay(1) and then go to the esp8266 team and ask why this is so. Since they are calling loop, they already service all the normal things behind the scenes.
Feature request: Expose a user provided feature to inject action when next task is greater than wait period and would possibly enter an "idle mode" for N milliseconds. Today AVR will enter a sleep mode but other architectures it maybe more sketch specific as outlined above.
I'm using this excellent library to run tasks, but I want the ESP to sleep otherwise. I have implemented light sleep, but that only runs on a
delay()
call. If my fastest task is 50ms and I don't care about accuracy (I'm fine being 20ms over), can I add a 20msdelay()
call in the loop? The only other thing that runs is the Task scheduler.Would it be possible to have Task optionally call
delay()
in its downtime, so I don't have to do it?