asyncio.sleep_ms(1) takes 10 msec to finish on esp32 #15594
-
Board : Esp-wroom-32 (tested on 2 boards) The following code should print a message every second but it takes about 10 seconds on the esp32. import asyncio
import time
async def test():
while True:
for i in range(1000):
await asyncio.sleep_ms(1)
print("Ok", time.time())
asyncio.run(test()) How is this possible? |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 9 replies
-
I first thought "don't be ridiculous". Changed the code slightly to get rid of any floating point influence and to see the result more clearly: import asyncio
import time
async def test():
while True:
start = time.ticks_ms()
for i in range(1000):
await asyncio.sleep_ms(1)
print(time.ticks_diff(time.ticks_ms(), start), 'msecs')
asyncio.run(test()) Ran it on linux:
which is about what you would expect. Ran it on my ESP32 and ESP32-S3 and got:
So certainly does appear ridiculous! Tried MP 1.23, 1.22, and 1.21 with same result. Sorry I can't explain this (without spending time going down a rabbit hole) but hopefully somebody else can. |
Beta Was this translation helpful? Give feedback.
-
With a bit of testing it appears that any value for asyncio.sleep_ms < 10 is taken as 10. So, for example, with a range of 500 and a sleep_ms(2) a delay of 5000 ms occurs, similar results being obtained with other pairs of values in single figures that should deliver a 1 second delay. With a delay of 10ms / range 100 it works as expected. All tested on an ESP32 S3 running v1.24 preview. I'm sure someone will drill down into the code and tell us what's going on. |
Beta Was this translation helpful? Give feedback.
-
This is caused / limited by Espressif IDF default freertos tick rate. Any time the async loop sleeps it release the current thread in freertos so other background tasks can be handled. It turns out the default freertos tick rate is 100hz / 10ms so that's why it takes (at least) that long to get back to the the application thread. If your particular application actually needs more responsiveness than that it's generally safe to speed that tick rate up in a custom build to 1000hz / 1ms though I expect your power usage will go up too. |
Beta Was this translation helpful? Give feedback.
-
I think there is a misunderstanding here. When you issue await asyncio.sleep_ms(1) on any microcontroller you will only get anything approaching a 1ms delay in the most trivial of applications. In practical code with multiple tasks the Please see the tutorial for further clarification. asyncio on ESP32 is far from useless. There are a number of substantial applications and libraries using it. |
Beta Was this translation helpful? Give feedback.
-
Luckily I found a solution to overcome the behaviour I described in this topic. It can be used if people need better resolution with the stock Micropython for the Esp32. await asyncio.sleep_ms(0) in async def do_nothing(), will do the trick. import asyncio
import time
async def do_nothing():
while True:
await asyncio.sleep_ms(0)
async def test():
for _ in range(100):
for i in range(1, 55):
start = time.ticks_us()
await asyncio.sleep_ms(i)
t = time.ticks_diff(time.ticks_us(), start)
print(f"i={i:2} {t/1000:.1f} ms")
asyncio.create_task(do_nothing())
asyncio.run(test()) Some of the output: i=48 48.3 ms
i=49 49.3 ms
i=50 50.5 ms
i=51 51.4 ms
i=52 52.3 ms
i=53 53.2 ms
i=54 54.4 ms
i= 1 1.3 ms
i= 2 2.5 ms
i= 3 3.4 ms
i= 4 4.3 ms
i= 5 5.2 ms
i= 6 6.4 ms
i= 7 7.3 ms
i= 8 8.2 ms If you change the await asyncio.sleep_ms(0) in async def do_nothing() to asyncio.sleep_ms(1) or only use task test() the problem will appear again. i=20 29.0 ms
i=21 29.3 ms
i=22 29.6 ms
i=23 29.3 ms
i=24 29.3 ms
i=25 29.3 ms
i=26 29.3 ms
i=27 29.3 ms
i=28 29.3 ms
i=29 29.3 ms
i=30 39.0 ms
i=31 39.3 ms
i=32 39.6 ms
i=33 39.3 ms
i=34 39.3 ms
i=35 39.3 ms
i=36 39.3 ms
i=37 39.3 ms
i=38 39.3 ms
i=39 39.3 ms |
Beta Was this translation helpful? Give feedback.
This is caused / limited by Espressif IDF default freertos tick rate.
Any time the async loop sleeps it release the current thread in freertos so other background tasks can be handled.
It turns out the default freertos tick rate is 100hz / 10ms so that's why it takes (at least) that long to get back to the the application thread.
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#config-freertos-hz
If your particular application actually needs more responsiveness than that it's generally safe to speed that tick rate up in a custom build to 1000hz / 1ms though I expect your power usage will go up too.