[HN Gopher] Introducing multitasking to Arduino
       ___________________________________________________________________
        
       Introducing multitasking to Arduino
        
       Author : pyprism
       Score  : 122 points
       Date   : 2022-08-02 16:22 UTC (6 hours ago)
        
 (HTM) web link (blog.arduino.cc)
 (TXT) w3m dump (blog.arduino.cc)
        
       | dariosalvi78 wrote:
       | I like the simplicity of setTimeout and setIntetval of
       | JavaScript. Maybe something similar plus some mutex primitive
       | (which JS doesn't need)?
        
       | oconnor663 wrote:
       | I wonder if async Rust would be a good fit for this use case? It
       | doesn't solve the accidental blocking problem (although async-std
       | did experiment with blocking detection at one point), but you can
       | get transparent support for multiple cores and a lot of thread
       | safety.
        
       | xkcd-sucks wrote:
       | I'm crappy at embedded development but isn't it pretty normal to
       | handle "multitasking" with interrupts instead of using an event
       | loop?
        
         | gmiller123456 wrote:
         | Not really, the processor has to be designed to support it that
         | way to make it practical. With interrupts, execution can be
         | interrupted at any point, and you have to be able to restore
         | the registers to the correct state before letting the process
         | continue from that point. It's technically doable, but with a
         | chip that has only 128 bytes of RAM, you'll be pretty limited
         | in how many processes you can run. Even Windows, prior to
         | Win95, used the event loop style for multitasking.
        
           | AnonymousPlanet wrote:
           | > Even Windows, prior to Win95, used the event loop style for
           | multitasking.
           | 
           | Even? That's nearly the lowest bar regarding multitasking,
           | compared to other OSes of the time.
        
         | roland35 wrote:
         | Interrupts work up to a point, but the correct way to do it is
         | with an RTOS. Looking at the GitHub discussion it seems they
         | are looking at mbed, FreeRTOS, or some other varieties too.
        
         | sophacles wrote:
         | Embedded can be a tiny little msp430 with RAM measured in
         | bytes, or it can be the latest and greatest intel flagship -
         | it's a description of how the chip is used more than the chip
         | type.
         | 
         | As CPUs get more powerful in general, so too do the little
         | development boards. I can spend $10 on a board with multiple
         | cores and megabytes of ram to drive my blinky lights and do
         | wifi - that's enough power to run a full unix.
        
           | baremetal wrote:
           | where? where can i get one?
        
             | dvh wrote:
             | Esp32 boards has 2 cores and cost $5, works fine in
             | Arduino. Try AliExpress.
        
               | dvh wrote:
               | I forgot to mention, blinky demo compilation take 60s on
               | my PC, that's the only downside but maybe there are some
               | workarounds.
        
               | ge96 wrote:
               | Showing some Teensy love, crazy the 4.0 has 600MHz base
               | speed.
        
               | Teknoman117 wrote:
               | Is that a Cortex-M7 or something?
        
               | ge96 wrote:
               | Yeah that's what it says. I like it because it has so
               | many connection pins eg. i2c/can be lazy.
               | 
               | I have STM32 and Beaglebone too but have not used them
               | yet.
        
               | fermuch wrote:
               | And can use up to 8mb of psram and 16mb of flash
        
             | sophacles wrote:
             | Well, there's a "hardware" link at the top of the linked
             | article for starters.
             | 
             | Or google "esp-32" to find store selling a very popular
             | one.
             | 
             | Or check out what's available on sparkfun or adafruit.
        
         | Zardoz84 wrote:
         | And/or using a RTOS. I remember using a RTOS on a PIC16/18
         | family chip like ten years ago.
         | 
         | But the most usual thing was knowing the precise timings that
         | takes instructions and the peripherals to allow do multiple
         | things at same time.
        
         | qbasic_forever wrote:
         | They're looking at this from a higher level for defining the
         | API and programming model people will use to write event-based
         | code. An implementation might run using interrupts and a timer
         | to drive an event loop on a single core, or it might scale
         | itself out to run on multiple cores where available like they
         | mention.
        
         | petsfed wrote:
         | That's exactly what's described.
         | 
         | Arduino's framework divides main() into two functions: Setup()
         | and loop(), but they're essentially the same as whatever comes
         | before the while loop, and the while loop itself, in a standard
         | embedded c main function.
         | 
         | Then you just have interrupts setting states, and the loop
         | responds to the change in state.
        
           | skybrian wrote:
           | It's not quite the same as a while loop, because when you
           | return from the loop() function, how long it takes before it
           | gets called again isn't defined. Using arduino-pico, for
           | example, it does some USB I/O handling (when using TinyUSB).
           | If you write an actual while loop and don't return from the
           | loop function often enough, you might starve the USB port
           | implementation.
           | 
           | This adds enough of a delay that I moved some code to the
           | other core on the Pico to get precise timing.
        
             | petsfed wrote:
             | That's a fair point. I admit, I moved away from the Arduino
             | framework pretty rapidly after embedded development became
             | my job, well before I had to contend with everything else
             | under the hood.
             | 
             | I never ran into an issue of quasi-hidden behaviors like
             | that, because I never pushed the thing that hard. It feels
             | a little bit like the Arduino toolset (that is pre IDE 2.0)
             | was designed to discourage people from pushing things that
             | hard.
        
               | skybrian wrote:
               | Well, it's behavior specific to one board, and perhaps to
               | TinyUSB. But it looks like Arduino has a yield function
               | [1] that you can use to run periodic tasks if you write
               | your own loop. And Teensy calls yield() between calls to
               | loop(). [2]
               | 
               | Since switching to VSCode and PlatformIO, I read the
               | source more. (Arduino 1.x discouraged this due to not
               | having easy ways to navigate between callers and
               | callees.)
               | 
               | [1] https://www.arduino.cc/reference/en/libraries/schedul
               | er/yiel... [2] https://github.com/PaulStoffregen/cores/bl
               | ob/master/teensy4/...
        
       | OlaCh wrote:
       | Energia IDE was a good example of multitasking for MCU. Here is
       | an example:
       | https://energia.nu/guide/foundations/programming_technique/m...
        
       | ramary wrote:
       | We've done quite a bit of experimentation with adding preemptive
       | multitasking support to non-hard real time software running on
       | MCUs at my current company.
       | 
       | After a lot of head banging and dead ends, I've come to the
       | conclusion personally that the embedded community could really
       | use an implementation of the POSIX threading APIs or some
       | meaningful subset thereof for various platforms. They're already
       | standardized, well understood/used, and aren't that hard to
       | implement on an MCU.
       | 
       | Of course, there would probably be some semantics that wouldn't
       | make sense to or may not be possible to implement on MCUs, and
       | there would be work required to support different cores, but
       | these tradeoffs seem better to me than reinventing the wheel and
       | probably needing to make the same tradeoffs at some point down
       | the line with a ground up new API.
       | 
       | For Arduino, exposing POSIX APIs wouldn't be very user-friendly.
       | But wrapping something more user-friendly around them seems like
       | a maintainable and extensible path for the project and community.
        
         | qbasic_forever wrote:
         | This is the direction a lot of RTOSs go, for example Zephyr (ht
         | tps://docs.zephyrproject.org/3.0.0/guides/portability/posi...)
         | and FreeRTOS (https://www.freertos.org/FreeRTOS-
         | Plus/FreeRTOS_Plus_POSIX/i...).
        
           | ramary wrote:
           | Thanks for sharing! I hope the Arduino teams heads in this
           | direction as well.
        
         | Tajnymag wrote:
         | Threads are already supported by esp-idf on esp32
         | 
         | source: https://docs.espressif.com/projects/esp-
         | idf/en/latest/esp32/...
        
           | Teknoman117 wrote:
           | Do you know how the core scheduling works? Does it migrate
           | tasks between cores? Is there any notable core to core
           | migration penalty?
        
           | ramary wrote:
           | That's great, thanks for sharing!
        
       | ge96 wrote:
       | Sounds good. I was working on a project where I could not receive
       | websocket info until I was done running servo commands.
       | 
       | I know you can have multiple boards but yeah.
        
       | andrewstuart wrote:
       | async/await does not result in bloated code that is hard to
       | maintain and debug.
       | 
       | It's a strange thing to say - async/await is now built in to most
       | major programming languages - strange that Arduino would dismiss
       | it so easily.
       | 
       | They are correct in that async/await does not solve multicore
       | utilisation, but it's the most important and easiest way to
       | implement parallelism on a single core.
        
       | primitivesuave wrote:
       | A couple years ago, I managed a summer/afterschool program that
       | introduced thousands of kids (age 8-18) to the Arduino. When we
       | would set up a software abstraction to simplify multitasking
       | (something that came up often with more advanced students), we
       | set up something like this:                 int time = 0;
       | int max_period = 10000;            void loop() {           if
       | (time % 100 == 0) {               everyTenthSecond();           }
       | if (time % 1000 == 0) {               everySecond();           }
       | // ...                  time = (time + 1) % max_period;        }
       | 
       | This `loop` assumes functions are perfectly non-blocking, which
       | obviously is not always true, but works well for 99% of actual
       | use cases. If keeping accurate time is required, the student
       | would compare `time` with the elapsed millisecond time since the
       | last loop invocation, adjust accordingly, and check for any
       | periodic functions that weren't executed in that period. It gets
       | tedious quite fast.
       | 
       | If I had to imagine a solution, it would be to simply have an
       | easy way to enter parameters to the JS-equivalent of `setTimeout`
       | and `setInterval`.
        
         | _whiteCaps_ wrote:
         | http://www.calgaryfieros.com/OSGdocs/ECM-hacking.html
         | 
         | The GM engineers used a similar approach:
         | 
         | > The ECM hardware generates a periodic interrupt at 160 Hz.
         | (Where have we seen that number before?) The entire ECM program
         | operates off of this one interrupt. Except for some
         | initialization code, there is no non-interrupt level code. In
         | fact, the interrupt service routine never returns, it simply
         | cleans the return address off the stack and waits for the next
         | interrupt. There are no other interrupts in the ECM. This means
         | that fully autonomous hardware generates all the high speed
         | signals like the fuel injector and ignition pulses.
         | 
         | > At each tick of the 160 Hz, various tasks are performed. Some
         | are done every tick. Others only on just odd or just even
         | ticks. There are also 16 tasks, one of which is performed each
         | tick. Every tenth of a second, all 16 tasks will have been
         | completed. In general, this means that the ECM cannot adjust
         | engine performance faster than 10 times a second. Perhaps this
         | explains the engine surging at low rpm that many list members
         | have complained about.
        
         | joezydeco wrote:
         | That is spiritually what their Scheduler library is doing, as
         | mentioned in the article. Which works for a lot of things just
         | fine until one task begins to overrun its time slice or just
         | hangs. And then you're looking into preemption.
         | 
         | But if you're overruning an AVR micro maybe it's time to take
         | the training wheels off and move up to a larger system.
        
           | HeyLaughingBoy wrote:
           | > move up to a larger system
           | 
           | I'll take this one step further and suggest that beginners
           | not start with the AVR, but instead with a Blue/Black pill
           | (STM32) or ESP32. Leave the AVR for the more advanced people
           | who are trying to squeeze every penny out of a project (even
           | there, in many cases STM32 will cost less!!!).
           | 
           | I visit the arduino.cc forums at least once every day and the
           | things that beginners want to do these days is far more
           | advanced than what they would have attempted even 10 years
           | ago. It gets very difficult trying to explain how to do
           | things on an AVR that would be much easier on a processor
           | with far more resources. String vs char* is one of those.
           | 
           | Hell, I don't know why we're even telling beginners to code
           | in C++ in 2022.
           | 
           | My downvoted comment that boils down to "just use an ESP32
           | and get FreeRTOS along for the ride" is in this vein. The
           | reality is that beginners have a _lot_ of trouble wrapping
           | their heads around writing nonblocking code using timers and
           | something like a FreeRTOS thread is a much simpler concept to
           | explain.
           | 
           | Rant over :-)
        
             | botdan wrote:
             | I'm a casual embedded-electronics user at best, most of my
             | projects haven't advanced much past turn a simple servo,
             | light some LEDs, maybe read a sensor and POST to an
             | endpoint. For these tasks, Arduino has been pretty good for
             | me and removed a lot of the complexity. The `setup()` and
             | `loop()` model makes a lot of intuitive sense. That being
             | said, I've spent no small amount of time on `String` vs
             | `char*` (as an inexperienced C/C++ developer) and found the
             | guardrails of the Arduino ecosystem to be very frustrating.
             | 
             | Do you have any recommended resources or additional search
             | terms to explore to learn more about hobbyist-level
             | embedded electronics outside of the Arduino ecosystem?
             | FreeRTOS looks interesting but it seems to add a lot of
             | overhead versus something simple like Arduino. Similarly,
             | I've looked at STM32 programming before but my searches
             | were very generic and the STM ecosystem is massive.
             | Specifically, I was trying to figure out if I could
             | reprogram some old drone flight controllers (equipped an
             | STM32F103CBT6 with a bunch of useful embedded sensors,
             | running old versions of "betaflight") for personal projects
             | but the entrypoint to STM programming (STM32Cube?) and the
             | setup code was considerable.
        
             | joezydeco wrote:
             | I hear ya, I think we're just in a different era now. Users
             | can wrap their head around a precooked framework that's
             | ready to go and only needs a few API calls to make
             | something happen. Beyond that? That's homework and homework
             | _sucks_.
             | 
             | FreeRTOS really isn't that large of a leap forward, in fact
             | when it's done right on your target platform it's just a
             | few API calls as well. But it means wrapping your head
             | around a lot of detailed concepts (stack size? semaphores?)
             | when all you want to do is light up that string of RGB
             | LEDs.
        
               | RealityVoid wrote:
               | Stack size is a thing on Arduino as well.
        
             | Teknoman117 wrote:
             | It's funny - I stepped away from electronics when I started
             | college and in the first few years out of it just due to
             | not having enough free time. About a 10 year period.
             | 
             | I come back and now AVR is old and slow and ARM is hot
             | stuff. All it took was the tooling becoming nearly free. No
             | more $500 ISP programmers, just USB DFU boot.
        
               | sitzkrieg wrote:
               | yea, usb dfu, better tooling and the price is nearly
               | interchangeable. heck, small PDIPs are sometimes more
               | expensive and less stocked
        
               | Teknoman117 wrote:
               | I remember the first ARM board I got back in like 2008
               | required some Keil IDE which was >$1k for hobbyists and
               | only had a 30 day evaluation included in the kit. Made by
               | a company called Luminary Micro iirc. Builtin programmer
               | was locked to only work with that specific chip. Never
               | seriously considered ARM for a long while after that.
        
             | dekhn wrote:
             | I agree- I think just about everybody should start with an
             | ESP32 instead of an Arduino. I still use the Arduino API
             | when programming the ESP32 (due to the large amount of
             | compatible drivers) but I've also installed FreeRTOS and
             | ported zork. I am embarassed to say I also got C++ STL
             | working on an AVR with 8k of ram- enough to allocate a
             | std::vector<int> of length 2.
        
         | gnramires wrote:
         | I wonder if it's possible to just interleave statements from k
         | different (loop) functions? Then it would seem mostly
         | equivalent to an arduino at 1/k Clock speed.
         | 
         | I think it's not possible to interleave instructions per se
         | (because of registers?), but a compiler should be able to
         | figure out the correct instructions of statement interleaving.
         | 
         | This should eliminate problems with blocking.
        
           | Teknoman117 wrote:
           | Anyone know if a barrel-processor microcontroller exists?
           | Propeller is sort of that way at least regarding main memory
           | access.
        
       | mikewarot wrote:
       | I see an opening for a very good set of tutorials on how to avoid
       | all the pitfalls.
       | 
       | [edit] - Actually... they have some good points, I just worry
       | about someone thinking they can just "sprinkle threading" into
       | their code, and failing to understand that in doing so they
       | _change the laws of physics_ of the code.
        
         | nyanpasu64 wrote:
         | I find that Rust-like rules (&T across threads and &mut T
         | within a thread, restricting cross-thread mutation to &Mutex<T>
         | and &AtomicT, global variables are treated as shared) are the
         | best approach I've seen so far to general multithreading (as
         | opposed to structured subsets like message passing or
         | structured concurrency, which I haven't explored as much).
         | However I'm unsure if mutexes are incompatible or unnecessary
         | with the majority of single-core Arduinos with cooperative
         | multitasking, and treating global data as shared is a pain on
         | embedded and binding them to a specific thread is not a problem
         | Rust has solved yet (you'll have to find your own solution).
        
       | HeyLaughingBoy wrote:
       | Nice, but pointless addition to an old processor. ESP32's are
       | cheap and readily accessible and they come with FreeRTOS running
       | already.
       | 
       | If I absolutely need to multitask on a Mega328 Arduino, I use the
       | protothreads library. But really, for anything heavy, I'll turn
       | to an ESP32 or an STM32. Use the right tool for the job!
        
         | sophacles wrote:
         | In 2022 Arduino has several baords, many of which are NOT using
         | atmega 328. Some of them have multi-core Arm chips, some of
         | them have risc-v (not sure if they have any of those as multi-
         | core though).
         | 
         | There are already people running FreeRTOS on those.
         | 
         | Not sure why you would dismiss multitasking on those as
         | "useless".
        
           | kevin_thibedeau wrote:
           | Even on an AVR, you can implement lightweight cooperative
           | scheduling with low resource consumption. That gives you many
           | of the benefits of an RTOS with less risk of creating
           | heisenbugs. Arduino should have broken free of its main-loop
           | architecture long ago.
        
         | francisduvivier wrote:
         | Don't forget that a lot of people are using Arduino framework
         | code on ESP32, it works pretty well.
         | 
         | So this development is also relevant for those other boards,
         | maybe even more so.
        
           | HeyLaughingBoy wrote:
           | Yup, and it's running FreeRTOS out of the box.
        
         | atoav wrote:
         | Yeah, the art students with miniscule coding experience who
         | just want to get stuff done are probably going to be _thrilled_
         | to work with FreeRTOS.
         | 
         | The truth is, that Arduino always has put in a lot of work _to
         | do things that could already be done_ only to make them more
         | accessible, easier to use and thus more widely available.
         | 
         | If you think this is pointless you should consider this might
         | say more about how far you have come in your own journey than
         | about the usefulness of that new feature.
        
       | fmakunbound wrote:
       | Forths such as FlashForth https://www.flashforth.com/index.html
       | on the Arduino support multiple tasks .. in addition to including
       | a compiler and interactive REPL
        
       ___________________________________________________________________
       (page generated 2022-08-02 23:00 UTC)