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

Sleep and delay() #35

Open
skorokithakis opened this issue Sep 15, 2020 · 13 comments
Open

Sleep and delay() #35

skorokithakis opened this issue Sep 15, 2020 · 13 comments
Assignees

Comments

@skorokithakis
Copy link

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 20ms delay() 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?

@Makuna
Copy link
Owner

Makuna commented Sep 15, 2020

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.

@skorokithakis
Copy link
Author

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.

@Makuna
Copy link
Owner

Makuna commented Sep 15, 2020

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().

@skorokithakis
Copy link
Author

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.

@Makuna
Copy link
Owner

Makuna commented Sep 16, 2020

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.

@skorokithakis
Copy link
Author

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.

@skorokithakis
Copy link
Author

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

@Makuna
Copy link
Owner

Makuna commented Sep 18, 2020

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
}

@skorokithakis
Copy link
Author

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?

@Makuna
Copy link
Owner

Makuna commented Sep 18, 2020

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.
  • The user maybe running other things in the loop that are not in a task, like other libraries "loop" calls.
  • 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.

@skorokithakis
Copy link
Author

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).

@Makuna
Copy link
Owner

Makuna commented Sep 18, 2020

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.

@Makuna
Copy link
Owner

Makuna commented Sep 18, 2020

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.

@Makuna Makuna self-assigned this Sep 18, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants