Is there a way I can have multiple parts of the program running together without doing multiple things in the same code block?
One thread waiting for an external device while also blinking a LED in another thread.
Is there a way I can have multiple parts of the program running together without doing multiple things in the same code block?
One thread waiting for an external device while also blinking a LED in another thread.
There is no multi-process, nor multi-threading, support on the Arduino. You can do something close to multiple threads with some software though.
You want to look at Protothreads:
Protothreads are extremely lightweight stackless threads designed for severely memory constrained systems, such as small embedded systems or wireless sensor network nodes. Protothreads provide linear code execution for event-driven systems implemented in C. Protothreads can be used with or without an underlying operating system to provide blocking event-handlers. Protothreads provide sequential flow of control without complex state machines or full multi-threading.
Of course, there is an Arduino example here with example code. This SO question might be useful, too.
ArduinoThread is a good one too.
AVR based Arduino's do not support (hardware) threading, I am unfamiliar with the ARM based Arduino's. One way around this limitation is the use of interrupts, especially timed interrupts. You can program a timer to interrupt the main routine every so many microseconds, to run a specific other routine.
https://www.arduino.cc/reference/en/language/functions/interrupts/interrupts/
It is possible to do software side multi-threading on the Uno. Hardware level threading is not supported.
To achieve multithreading, it will require the implementation of a basic scheduler and maintaining a process or task list to track the different tasks that need to be run.
The structure of a very simple non-preemptive scheduler would be like:
//Pseudocode
void loop()
{
for(i=o; i<n; i++)
run(tasklist[i] for timelimit):
}
Here, tasklist
can be an array of function pointers.
tasklist [] = {function1, function2, function3, ...}
With each function of the form:
int function1(long time_available)
{
top:
//Do short task
if (run_time<time_available)
goto top;
}
Each function can perform a separate task such as function1
performing LED manipulations, and function2
doing float calculations. It will be the responsibility of each task(function) to adhere to the time allocated to it.
Hopefully, this should be enough to get you started.
As per the description of your requirements:
It seems you could use one Arduino interrupt for the first "thread" (I would rather call it "task" in fact).
Arduino interrupts can call one function (your code) based on an external event (voltage level or level change on a digital input pin), that will trigger your function immediately.
However, one important point to keep in mind with interrupts is that the called function should be as fast as possible (typically, there should be no delay()
call or any other API that would depend on delay()
).
If you have a long task to activate upon external event trigger, then you could potentially use a cooperative scheduler and add a new task to it from your interrupt function.
A second important point about interrupts is that their number is limited (e.g. only 2 on UNO). So if you start tohave more external events, you would need to implement some kind of multiplexing all inputs into one, and have your interrupt function determine what multiplexed inut was the actual trigger.
A simple solution is to use a Scheduler. There are several implementations. This describes shortly one that is available for AVR and SAM based boards. Basically a single call will start a task; "sketch within a sketch".
#include <Scheduler.h>
....
void setup()
{
...
Scheduler.start(taskSetup, taskLoop);
}
Scheduler.start() will add a new task that will run the taskSetup once and then repeatedly call taskLoop just as the Arduino sketch works. The task has its own stack. The size of the stack is an optional parameter. Default stack size is 128 bytes.
To allow context switching the tasks need to call yield() or delay(). There is also a support macro for waiting for a condition.
await(Serial.available());
The macro is syntactic sugar for the following:
while (!(Serial.available())) yield();
Await can also be used to synchronize tasks. Below is an example snippet:
volatile int taskEvent = 0;
#define signal(evt) do { await(taskEvent == 0); taskEvent = evt; } while (0)
...
void taskLoop()
{
await(taskEvent);
switch (taskEvent) {
case 1:
...
}
taskEvent = 0;
}
...
void loop()
{
...
signal(1);
}
For further details see the examples. There are examples from multiple LED blink to debounce button and a simple shell with non-blocking command line read. Templates and namespaces can be used to help structure and reduce the source code. Below sketch shows how to use template functions for multi-blink. It is sufficient with 64 bytes for the stack.
#include <Scheduler.h>
template<int pin> void setupBlink()
{
pinMode(pin, OUTPUT);
}
template<int pin, unsigned int ms> void loopBlink()
{
digitalWrite(pin, HIGH);
delay(ms);
digitalWrite(pin, LOW);
delay(ms);
}
void setup()
{
Scheduler.start(setupBlink<11>, loopBlink<11,500>, 64);
Scheduler.start(setupBlink<12>, loopBlink<12,250>, 64);
Scheduler.start(setupBlink<13>, loopBlink<13,1000>, 64);
}
void loop()
{
yield();
}
There is also a benchmark to give some idea of the performance, i.e. time to start task, context switch, etc.
Last, there are a few support classes for task level synchronization and communication; Queue and Semaphore.
I also came to this topic while implementing a matrix LED display.
In one word, you may build a polling scheduler by using millis() function and timer interrupt in Arduino.
I suggest the following articles from Bill Earl:
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-2/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
From a previous incantation of this forum, the following question/answer was moved to Electrical Engineering. It has sample arduino code to blink an LED using a timer interrupt while using the main loop to do serial IO.
Repost:
Interrupts are a common way to get things done while something else is going on. In the example below, the LED is blinking without using delay()
. Whenever Timer1
fires, the interrupt service routine (ISR) isrBlinker()
is called. It switches the LED on/off.
To show that other things can simultaneously happen, loop()
repeatedly writes foo/bar to the serial port independent of the LED blinking.
#include "TimerOne.h"
int led = 13;
void isrBlinker()
{
static bool on = false;
digitalWrite( led, on ? HIGH : LOW );
on = !on;
}
void setup() {
Serial.begin(9600);
Serial.flush();
Serial.println("Serial initialized");
pinMode(led, OUTPUT);
// initialize the ISR blinker
Timer1.initialize(1000000);
Timer1.attachInterrupt( isrBlinker );
}
void loop() {
Serial.println("foo");
delay(1000);
Serial.println("bar");
delay(1000);
}
This is a very simple demo. ISRs can be much more complex and can be triggered by timers and external events (pins). Many of the common libraries are implemented using ISRs.
You could also give my ThreadHandler library a try
https://bitbucket.org/adamb3_14/threadhandler/src/master/
It uses an interrupting scheduler to allow context switching without relaying on yield() or delay().
I created the library because I needed three threads and I needed two of them to run at a precise time no matter what the others were doing. The first thread handled serial communication. The second was running a Kalman filter using float matrix multiplication with the Eigen library. And the third was a fast current control loop thread which had to be able to interrupt the matrix calculations.
Each cyclic thread has a priority and a period. If a thread, with higher priority than the current executing thread, reaches its next execution time the scheduler will pause the current thread and switch to the higher priority one. Once the high priority thread completes its execution the scheduler switches back to the previous thread.
The scheduling scheme of the ThreadHandler library is as follows:
Threads can be created via c++ inheritance
class MyThread : public Thread
{
public:
MyThread() : Thread(priority, period, offset){}
virtual ~MyThread(){}
virtual void run()
{
//code to run
}
};
MyThread* threadObj = new MyThread();
Or via createThread and a lambda function
Thread* myThread = createThread(priority, period, offset,
[]()
{
//code to run
});
Thread objects automatically connect to the ThreadHandler when they are created.
To start execution of created thread objects call:
ThreadHandler::getInstance()->enableThreadExecution();
And here is yet another microprocessor cooperative multitasking library – PQRST: a Priority Queue for Running Simple Tasks.
In this model, a thread is implemented as a subclass of a Task
, which is scheduled for some future time (and possibly rescheduled at regular intervals, if, as is common, it subclasses LoopTask
instead). The run()
method of the object is called when the task becomes due. The run()
method does some due work, and then returns (this is the cooperative bit); it'll typically maintain some sort of state machine to manage its actions on successive invocations (a trivial example is the light_on_p_
variable in the example below). It requires a slight rethinking of how you organise your code, but has proven very flexible and robust in fairly intensive use.
It's agnostic about the time units, so it is as happy running in units of millis()
as micros()
, or any other tick that is convenient.
Here is the ‘blink’ program implemented using this library. This shows only a single task running: other tasks would typically be created, and started within setup()
.
#include "pqrst.h"
class BlinkTask : public LoopTask {
private:
int my_pin_;
bool light_on_p_;
public:
BlinkTask(int pin, ms_t cadence);
void run(ms_t) override;
};
BlinkTask::BlinkTask(int pin, ms_t cadence)
: LoopTask(cadence),
my_pin_(pin),
light_on_p_(false)
{
// empty
}
void BlinkTask::run(ms_t t)
{
// toggle the LED state every time we are called
light_on_p_ = !light_on_p_;
digitalWrite(my_pin_, light_on_p_);
}
// flash the built-in LED at a 500ms cadence
BlinkTask flasher(LED_BUILTIN, 500);
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
flasher.start(2000); // start after 2000ms (=2s)
}
void loop()
{
Queue.run_ready(millis());
}
run()
method is called, it is not interrupted, so it has the responsibility to finish reasonably promptly. Typically, however, it'll do its work then reschedule itself (possibly automatically, in the case of a subclass of LoopTask
) for some future time. A common pattern is for the task to maintain some internal state machine (a trivial example is the light_on_p_
state above) so that it behaves suitably when it is next due.
Commented
Jul 8, 2019 at 15:58
run()
. This is in contrast with cooperative threads, that can yield the CPU by, e.g., calling yield()
or delay()
. Or preemptive threads, that can be scheduled-out at any time. I feel the distinction is important, as I have seen that many people who come around here searching for threads do so because they prefer writing blocking code rather than state machines. Blocking real threads that yield the CPU is fine. Blocking RtC tasks is not.
Commented
Jul 8, 2019 at 18:09