Is it just me, or does it seem like the Analog Read function on micro:bit is staggeringly slow, maybe with a 10Hz update speed?
Just as a follow-up, with some testing, it seems that a simple “forever” loop with an analog read and a few IF statements might be running at around 20Hz! (ya, 20 iterations a second!)
This seems unbelievably slow, but is this expected behaviour?
Hi @MCbubba77 ,
Since you mention a
forever loop, I’m assuming you are working in MakeCode. MicroPython is probably different.
forever loop has a 20ms pause built in on each iteration. There may be two reasons for this: 1) to allow other fibers to run to handle events, etc. and 2) to ensure the loop is slow enough to be noticeable to novice users (maybe). See here to see the actual loop. The period is at least 20ms, so the maximum on anything in a
forever loop is 50Hz.
Here are some interesting examples/data from a V2:
- Analog Read in a
foreverloop: This was able to do about 50 cycles through the loop per second (50Hz), so the read itself is negligible in comparison to the 20mS sleep in the
- Analog Read in Tight Loop: You can use a different style of infinite loop inTypeScript and omit the
pauseon each iteration. It was able to do about 29300 reads per second (and other logic). So a bit over 29kHz and maybe a little under 34.1 uS between reads. Of course, this may starve out other tasks.
- You can include a
pause(0)in a tight loop to ensure other events are handled. That appears to significantly impact the period. It was able to do about 250Hz.
It looks like the
analog read itself isn’t really slow. The
forever is just throttled.
It’s a shame that it’s implemented in a such a ham-fisted way! Anything that uses movement and analog inputs (like line-following) can easily be tripped-up by such a slow refresh rate.
My students were working on a routine to sense different “colours” of tape and it works by sensing the reflection gradient and lowest returned level, but the robot has to move incredibly slowly for it to work and it’s flaky at best! Same with line-following, if you go too “fast” (maybe half the speed of a Move:Motor) the latency of the system allows it to simply drive-off the tape!
I think there’s a need for some sort of yield to avoid having other tasks starve, but I’m not sure about the choice of 20ms. Again, I wouldn’t be surprised if it was related to user experience for the most common uses.
One of the nice features of MakeCode is the ability to make extensions. I made an extension that provides “faster” loops: On the V2 one loop is about 250Hz (max) and the other can be 120,000Hz-150,000Hz (120-150kHz) . The URL is: https://github.com/bsiever/pxt-fastloops .
... more menu has the block that can go 120-150kHz, but it will still occasionally
pause to allow other tasks to run. I included the periodic
pause to make starvation of other tasks a little less likely. I think misuse could be a problem. I’m not sure if the loop was the cause, but in one test I had trouble reprogramming the micro:bit over USB and had to use the file drag-and-drop approach.
The 250Hz extension sounds awesome, but how do you implement it into a vanilla install? (I typically use the offline version)
This extension does not use C++, so you should be able to follow the process described at: https://makecode.microbit.org/offline I haven’t tried it and it’s a little clunky…But once you have a template project to use it doesn’t seem too bad.
If I’m online in the app, do I search for it in Extensions (doesn’t appear to be there), or do I need to download something from the Github page and use “import” on the Extensions page?
Offline-wise, as you say, if you make a project with an extension, that extension appears to travel with the .hex file, so other offline apps can use the same extension(s). That said, we’ve ran into trouble where the app gets confused and refuses to load the .hex file with an extension (some kind of ambiguous error message).
Only official extensions are searchable by keyword. I’m not planning to submit this to be an official extension (approval can take a bit of time anyway). Add the extension by entering its URL in the search box rather than a keyword: https://github.com/bsiever/pxt-fastloops, like:
I’m not sure about the problems you’ve encountered before. There can certainly be problems if the extension requires compilation of C++ code, which isn’t available offline.
Super, got it to work!
Is fast forever a fixed iterative time, or does it adapt to the complexity of the loop itself? For example, would it iterate a simple variable increment faster than something more complex, like I/O operations, etc (assuming you don’t bump into absolute overhead)?
You can look at the code yourself: https://github.com/bsiever/pxt-fastloops/blob/master/fastloops.ts .
It’s not dynamic in any way. It’s just a background fiber. It executes the body and then does a
pause(0) to yield to other tasks. Timing depends on both the duration of content in the loop and other tasks in the queue(s)…and scheduling.
pause() is really a call to the CODAL
fiber_sleep. It looks like it will just deschedule the current fiber, put it in the sleep queue, and call the scheduler (here).
I haven’t delved into it, but it seems like it’s actually idling/sleeping following a
pause() (4ms Scheduler Tick here, which is why it’s only hitting 250 Hz). A C++ based extension that created a new fiber could probably overcome this or maybe a little more careful use of tasks (scheduler details are here).
Scheduling is interesting stuff… I’ll try to look at it again in a few weeks if/when I get time.