Replies: 16 comments 35 replies
-
Have a look at https://github.com/muwerk/muwerk |
Beta Was this translation helpful? Give feedback.
-
Another existing library of note is AceRoutine which can run coroutines all the way down on ATmega328P processors (Arduino Uno/Nano). It uses a variety of tricks to run entirely in software while being statically allocated. I'd find it helpful if the common threading language implementation could have a "minimal" base that could be implemented by a library like AceRoutine to provide support for older and cheaper processors. That said, I understand that might not be possible given the design challenges behind implementing pseudo-threading on microcontrollers. |
Beta Was this translation helpful? Give feedback.
-
Oh boy, I’ve been doing multitasking in arduino for years already! I use freertos and it works super well. Here is an example: I’m sure it is possible to generalize free rtos so that it works on every MCU, and, if we want it to be user friendly, we can put wrapper functions around the API that are easier to understand by newcomers. FreeRTOS just needs a systick, which arduino already has. When you call vTaskDelay() in a thread, the scheduler checks to see if any other threads should run and then goes and runs them. |
Beta Was this translation helpful? Give feedback.
-
Would by super useful and love this approach with clear description and soliciting community input! |
Beta Was this translation helpful? Give feedback.
-
interesting stuff. Imagine also an Arduino profiling library that adds hooks to those threads and then uses something like processing to display how your program is executing. Heck you could integrate it into the IDE that would be cool. While most RTOSs have their profiling features you usually need third-party complicated tools to view the output. |
Beta Was this translation helpful? Give feedback.
-
The muwerk cooperative scheduler (which can run unmodified Arduino code and libs) has such a tool mutop. It can even be cascaded to connect a simple Arduino-Uno via serial and get multiple, simple concurrent loops on the uno with process monitoring: Arduino bridge monitoring. Source length: 70 lines for both ESP and Arduino. |
Beta Was this translation helpful? Give feedback.
-
A meta / brainstorming comment. Concurrency is hard, is there any way to help the user not to shoot themselves in the foot, both within multitasking per se, and when considering multitasking + interrupts which makes things even worse? I was reading quite a bit around these questions in both the 'Embedded Rust book' and the 'RTIC' book. These, together with Rust in itself, go through great care to make sure that the user cannot shoot themselves in the foot too easily. It was an eye opener for me to see how much care must be taken to avoid UB. I guess in cpp there will be needs to be extra careful as soon as concurrency is taking place. Also, as soon as there is multitasking, arises the question around for example priority of tasks and preemption; any thoughts on this (for example, the Arduino article below was not saying anything about these questions)? Given the API examples provided at https://docs.arduino.cc/tutorials/generic/multiple-blinks?_gl=1*16o0z1b*_ga*OTQyNzM3MTcuMTY1OTYzNjcwNg..*_ga_NEXN8H46L5*MTY1OTcxMzk0NS4zLjEuMTY1OTcxNTcyMC42MA.. , if multitasking takes this kind of route in Arduino, I guess this would mean providing only quite primitive multitasking with quite coarse control, but also helping the user not shooting themselves in the foot by doing so? |
Beta Was this translation helpful? Give feedback.
-
Three questions:
|
Beta Was this translation helpful? Give feedback.
-
IMO the best way would be to implement a (quite original) std::thread or even a pthread-like API syntax. Most of the Arduino-users targeting preemptive multithreading are most probably already used to pthread a/o std::thread on other platforms (e.g., Raspberry Pi), and to benefit from the experience and from standard C/C++ examples in the web the very intuitive pthread/std::thread syntax is useful, reasonable, and sensible also for common (not extremely high skilled) Arduino users. simple example for an ESP32 std::thread MT pattern:
|
Beta Was this translation helpful? Give feedback.
-
another issue can be solved by std lib syntax easily too: thread-safe r/w communication when using std::mutex.
(edit, simplified) |
Beta Was this translation helpful? Give feedback.
-
to summarize, IMO https://github.com/arduino-libraries/Arduino_Threads/blob/main/docs/threading-basics.md is the wrong approach. Instead, as I have shown by my examples above, std::thread is absolutely fine by design and performance and also absolutely suitable also for beginners. |
Beta Was this translation helpful? Give feedback.
-
I think there are two very different audiences for multithreading on Arduino. The difference between them is encapsulated in the Arduino API Style Guide with:
It's very true that the std::thread is an OK design and that it is battle proven. However I think as soon as you need something like I think that anyone comfortable with the standard library's multithreading APIs and who needs a mutex is probably writing C++ for their application too. From this point of view, I really like a lot of Arduino_Threads. |
Beta Was this translation helpful? Give feedback.
-
I’m thinking about the cognitive load on someone who really doesn’t need to understand what std::thread does. Ask a non tech friend to read this :-)
The separation into files might seem pointless to you but - I believe - you’re not the audience for this API.
Whatever, this feels like opinion: my argument certainly is so I’ll bow out now.
…On 16 Nov 2022 at 14:51 +0000, dsyleixa ***@***.***>, wrote:
std::thread is fine, common, handsome, and proven both for e.g. Raspberry Pi (LINUX, C++14, C++18,...) and even ESP32-arduino (freRTOS), and as to ESP32 it's already kind of actual standard which should also be adopted for common Arduino.
As to @rogernolan:
your argument is useless, even for only 2 simple threads std::thread is dead simple, no extra files, just 2 simple declarations for the already existing functions to make them run in independant threads:
thread thread1(broadcast_IMU_data); // starting 1st std::thread
thread thread2(send_temperature_to_ArduinoCloud); // starting 2nd std::thread
what could actually be more simple?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
Hi, I would like to revive this thread regarding what the API could look like. I agree that fun quickly turns into 'state-machine of doom' and in my version of multitasking for AVR/SAMD I implemented a templated API to allow for strongly typed argument for the thread function. The API also supports nonstatic class methods as thread functions.
Can use it like so (more info at https://github.com/glutio/Taskfun)
|
Beta Was this translation helpful? Give feedback.
-
The API should be simple for beginners to use with two threads that don't need much synchronization. It should also support sophisticated users and future multi-core processors. std::thread is probably not a great choice. C++ is too general, C++ must run in environments that do not have a stack so it will need to be extended in non-standard ways. Also the API probably should be callable from C. POSIX Threads have the advantage of maturity. For example thread create allows expansion to multi-core processors.
This API and others hide expandability in the type of arguments. You cant find the definition of The Often there is even a pthread interface for a RTOS. See this for FreeRTOS. pthread is an example of how to extend an API without breaking code. In addition to using
Functions like So a program does not need to be modified unless an attribute you use is changed. There have been attempts at a standard API for RTOSs but none have as much popularity as pthread. It's probably too late since Arduino is inventing yet another threading API. |
Beta Was this translation helpful? Give feedback.
-
I agree that POSIX pthread would be a good choice too, finally C++std::thread is more or less just a wrapper to pthread (and still uses pthread for e.g. thread prio etc...). |
Beta Was this translation helpful? Give feedback.
-
👋 We would like to extend the Arduino API to support a long-wanted feature: multitasking.
🔍 The problem
Timing logic is one of the most common parts of every Arduino sketch: when you blink a LED or regulate the speed of a motor you’ll need pauses between signals; likewise, to detect button presses or read rotary encoders you’ll need multiple signal readings separated by pauses.
We all started with the well-known delay() function seen in the Blink example, being the simplest way to pause sketch execution between one command and another. However, as soon as you need to run two different things at the same time, with different timing needs, you’ll find out that this approach doesn’t work. Also, blocking the execution of your
loop()
will have side effects such as slow performance in user interaction and network operations.After seeing the limitations of delay() we all learned about state machines, i.e. how to turn a sketch from blocking to non-blocking by using the
millis()
function (as seen in the BlinkWithoutDelay example).Then, in order to make code more readable and support multiple state machines, they are usually moved into classes exposing an
.update()
method to be called in the loop. This approach is solid but if you’re a novice user and need multiple state machines things get suddenly quite complicated as you’re forced to learn about classes a bit too early in your learning journey.State machines also come with drawbacks:
millis()
function, do math and change the state variables.loop()
run quickly, they all run in the main thread. This means you can’t leverage the capabilities of modern multi-core boards (such as Nano RP2040 Connect) which can truly run code in parallel.💡 The solution!
The limitations of state machines can be overcome implementing multitasking, aka threads: you write a block of code that needs to be run in parallel, and let the operating system schedule it in the proper way so that it does not interfere with other tasks.
How does multitasking compare to state machines?
delay(100)
calls between one reading and the other, with no perceptible effect on user experience but significant power saving. Interrupts can be used too.Now, we don’t have any official Arduino API for this. Multiple libraries exist, such as:
Also, some cores expose native functionality:
setup1()
andloop1()
functionsrtos::Thread()
is available toovTaskDelay()
API from FreeRTOSNone of these approaches are portable. Standardizing a higher level API would make life easier to users. Also, the lack of a portable API also limits the creation of libraries which might want to use threading.
📝 Requirements
How can we design a nice, simple, Arduinish API for multitasking?
Let’s try to lay down some general requirements:
What more? Let’s discuss:
setup()
/loop()
combo inside each additional thread?Serial.print()
andSerial.read()
in a concurrent way?🚀 Now what?
As a conversation starter, we developed a possible implementation, which is hosted in this repository:
👉 https://github.com/arduino-libraries/Arduino_Threads
It satisfies many of the requirements above, but not all of them. Please help us testing it! Comments are very welcome.
But this is just the beginning as there could be more ideas from the community so let’s get the discussion started! Please, possibly structure your comments according to the above requirement list, and feel free to post alternative design proposals.
Beta Was this translation helpful? Give feedback.
All reactions