You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This library makes it easy to use the multi-threading capability of [Arduino](https://www.arduino.cc/) boards that use an [Mbed OS](https://os.mbed.com/docs/mbed-os/latest/introduction/index.html)-based core library. Additionally this library provides thread-safe access to `Wire`, `SPI` and `Serial` which is relevant when creating multi-threaded sketches in order to avoid common pitfalls such as race-conditions and invalid state.
11
12
13
+
Preeliminary **documentation** and download links for **required tooling** are available within the [`/docs`](docs/README.md) subfolder.
14
+
12
15
## :star: Features
13
16
### :thread: Multi-threaded sketch execution
14
17
Instead of one big state-machine-of-doom you can split your application into multiple independent threads, each with it's own `setup()` and `loop()` function. Instead of implementing your application in a single `.ino` file, each independent thread is implemented in a dedicated `.inot` file (t suffix stands for **t**hread) representing a clear separation of concerns on a file level.
@@ -43,17 +46,14 @@ As a result this interruption by the scheduler will break Wire I/O access for bo
43
46
`Arduino_Threads` solves this problem by encapsulating the complete I/O access (e.g. reading from a `Wire` client device) within a single function call which generates an I/O request to be asynchronously executed by a high-priority I/O thread. The high-priority I/O thread is the **only** instance which directly communicates with physical hardware.
44
47
45
48
### :runner: Asynchronous
46
-
The mechanisms implemented in this library allow any thread to dispatch an I/O request asynchronously and either continue its operation or [yield](https://en.wikipedia.org/wiki/Yield_(multithreading)) control to the next scheduled thread. All I/O requests are stored in a queue and are executed within a high-priority I/O thread after a [context-switch](https://en.wikipedia.org/wiki/Context_switch). An example of this can be seen [here](examples/Threadsafe_IO/Threadsafe_SPI/Threadsafe_SPI.ino)).
49
+
The mechanisms implemented in this library allow any thread to dispatch an I/O request asynchronously and either continue its operation or [yield](https://en.wikipedia.org/wiki/Yield_(multithreading)) control to the next scheduled thread. All I/O requests are stored in a queue and are executed within a high-priority I/O thread after a [context-switch](https://en.wikipedia.org/wiki/Context_switch). An example of this can be seen [here](examples/Threadsafe_IO/Threadsafe_SPI/Threadsafe_SPI.ino).
47
50
48
51
### :relieved: Convenient API
49
-
Although you are free to directly manipulate I/O requests and responses (e.g. [Threadsafe_Wire](examples/Threadsafe_IO/Threadsafe_Wire/Threadsafe_Wire.ino)) there are convenient `read`/`write`/`write_then_read` abstractions inspired by the [Adafruit_BusIO](https://github.com/adafruit/Adafruit_BusIO) library (e.g. [Threadsafe_Wire_BusIO](examples/Threadsafe_IO/Threadsafe_Wire_BusIO/Threadsafe_Wire_BusIO.ino)).
50
-
51
-
52
+
Although you are free to directly manipulate I/O requests and responses (e.g. [Threadsafe_Wire](examples/Threadsafe_IO/Threadsafe_Wire/Threadsafe_Wire.ino)) there are convenient `read`/`write`/`writeThenRead` abstractions inspired by the [Adafruit_BusIO](https://github.com/adafruit/Adafruit_BusIO) library (e.g. [Threadsafe_Wire_BusIO](examples/Threadsafe_IO/Threadsafe_Wire_BusIO/Threadsafe_Wire_BusIO.ino)).
52
53
53
54
## :zap: Caveats
54
55
55
-
56
-
56
+
This library is currently in **BETA** phase. This means that neither the API nor the usage patterns are set in stone and are likely to change. We are publishing this library in the full knowledge that we can't foresee every possible use-case and edge-case. Therefore we would like to treat this library, while it's in beta phase, as an experiment and ask for your input for shaping this library. Please help us by providing feedback in the [issues section](https://github.com/bcmi-labs/Arduino_Threads/issues) or participating in our [discussions](https://github.com/arduino/language/discussions).
Copy file name to clipboardExpand all lines: docs/01-threading-basics.md
+11-3
Original file line number
Diff line number
Diff line change
@@ -3,7 +3,11 @@
3
3
Threading Basics
4
4
================
5
5
## Introduction
6
-
Previously Arduino sketches didn't support the concept of multitasking, unless you took specific measures to support it. With this so called single-threaded approach instructions in your Arduino sketch are executed one after another. If an instruction or a function call respectively makes the runtime environment wait for its execution to complete, it's called a "blocking" function. You may have encountered the limitations of this when interacting with multiple sensors and actuators at once. For example if you let a servo motor react to the data read from a distance sensor. While the servo motor is moving to its target position no further reading of the distance sensor can be done because the program waits until the servo is done moving. To solve this issue multitasking can be used which allows to "simultaneously" execute multiple task such as reading from a sensor and controlling a motor. In the context of multitasking a thread is basically a mechanism (provided usually by the operating system) to encapsulate a task to be run concurrently with others.
6
+
Threading is a concept that is used on many operating systems to run tasks in parallel. An Arduino example of two such tasks could be to read the position of a potentiometer knob while controlling a servo motor to follow that position. Running such tasks in parallel is also called multitasking.
7
+
8
+
Previously Arduino sketches didn't support the concept of multitasking, unless you took specific measures to support it. With this so called single-threaded approach instructions in your Arduino sketch are executed sequentially one by one. If an instruction or a function call respectively makes the runtime environment wait for its execution to complete, it's called a "blocking" function. You may have encountered the limitations of this when interacting with multiple sensors and actuators at once. For example if you let a servo motor react to the data read from a potentiometer as mentioned above. While the servo motor is moving to its target position no further reading of the potentiometer can be done because the program waits until the servo is done moving. To solve this issue multitasking can be used which allows to "simultaneously" execute multiple task such as reading from a sensor and controlling a motor. In the context of multitasking a thread is basically a mechanism (provided usually by the operating system) to encapsulate a task to be run concurrently with others.
9
+
10
+

7
11
8
12
In the historic single-threaded execution of Arduino sketches the complete program logic is contained within the `*.ino` file. It contains both a `setup()` function, which is executed only once at program start, and a `loop()` function, which is executed indefinitely. A single-threaded Arduino project can theoretically contain multiple `*.ino` files but you can define `setup()` or `loop()` only once.
9
13
In order to support multi-threaded (or parallel) sketch execution a new file type called the `*.inot` file is introduced.
@@ -13,6 +17,8 @@ The advantage of this approach is that a complex and lengthy `loop()` function (
13
17
#### Example (Single-Threaded):
14
18
This sketch demonstrates how one would implement a program which requires the execution of three different actions on three different periodic intervals. In this example we blink three different LEDs at three different intervals.
15
19
20
+

21
+
16
22
**Blink_Three_LEDs.ino**:
17
23
18
24
```C++
@@ -55,7 +61,9 @@ You can imagine that with increasing complexity of a sketch it gets quite diffic
55
61
56
62
#### Example (Multi-Threaded):
57
63
58
-
The same functionality can be provided via multi-threaded execution in a much cleaner way.
64
+
The same functionality can be provided via multi-threaded execution in a much cleaner way by splitting up the tasks into separate files / threads.
65
+
66
+

59
67
60
68
**Blink_Three_LEDs.ino**
61
69
@@ -116,4 +124,4 @@ As you can see from the example the name of the `*.inot`-file is used to generat
116
124
* cannot contain spaces or special characters.
117
125
* cannot be a C++ keyword (i.e. `register`, `volatile`, `while`, etc.).
118
126
119
-
To be consistent with the Arduino programming style we recommend using [camel case](https://en.wikipedia.org/wiki/Camel_case) for the file names.
127
+
To be consistent with the Arduino programming style we recommend using [camel case](https://en.wikipedia.org/wiki/Camel_case) for the file names.
Copy file name to clipboardExpand all lines: docs/02-data-exchange.md
+9-7
Original file line number
Diff line number
Diff line change
@@ -12,20 +12,22 @@ A `Shared` variable is a global variable accessible to all threads. It can be de
12
12
```C++
13
13
/* SharedVariables.h */
14
14
SHARED(counter, int); /* A globally available, threadsafe, shared variable of type 'int'. */
15
+
/* ... or ... */
16
+
SHARED(counter, int, 8); /* Same as before, but now the internal queue size is defined as 8. */
15
17
```
16
18
Writing to and reading from the shared variable may not always happen concurrently. I.e. a thread reading sensor data may update the shared variable faster than a slower reader thread would extract those values. Therefore the shared variable is modeled as a queue which can store (buffer) a certain number of entries. That way the slower reader thread can access all the values in the same order as they have been written.
17
-
New values can be inserted naturally by using the assignment operator `=` as if it was just any ordinary variable type, i.e. `int`, `char`, ...
19
+
New values can be inserted by using the `push` function that you may know from other data structures.
18
20
19
21
```C++
20
22
/* Thread_1.inot */
21
-
counter = 10; /* Store a value into the shared variable in a threadsafe manner. */
23
+
counter.push(10); /* Store a value into the shared variable in a threadsafe manner. */
22
24
```
23
25
If the internal queue is full the oldest element is discarded and the latest element is inserted into the queue.
24
26
25
-
Retrieving stored data works also very naturally like it would for any POD data type:
27
+
Stored data can be retrieved by using the `pop` function:
26
28
```C++
27
29
/* Thread_2.inot */
28
-
Serial.println(counter); /* Retrieves a value from the shared variable in a threadsafe manner. */
30
+
Serial.println(counter.pop()); /* Retrieves a value from the shared variable in a threadsafe manner. */
29
31
```
30
32
31
33
Should the internal queue be empty when trying to read the latest available value then the thread reading the shared variable will be suspended and the next available thread will be scheduled. Once a new value is stored inside the shared variable the suspended thread resumes operation and consumes the value which has been stored in the internal queue.
Whenever a new value is assigned to a data source, i.e.
56
58
```C++
57
59
/* DataProducerThread.inot */
58
-
counter = 10;
60
+
counter.push(10);
59
61
```
60
62
data is automatically copied and stored within the internal queues of all connected data sinks, from where it can be retrieved, i.e.
61
63
```C++
62
64
/* DataConsumerThread_1.inot */
63
-
Serial.println(counter);
65
+
Serial.println(counter.pop());
64
66
```
65
67
```C++
66
68
/* DataConsumerThread_2.inot */
67
-
Serial.println(counter);
69
+
Serial.println(counter.pop());
68
70
```
69
71
If a thread tries to read from an empty `Sink` the thread is suspended and the next ready thread is scheduled. When a new value is written to a `Source` and consequently copied to a `Sink` the suspended thread is resumed and continuous execution (i.e. read the data and act upon it).
As the use of the `transfer` API might be difficult to grasp there's also a synchronous API call combining the request of the transfer and waiting for its result using `transfer_and_wait`.
58
+
As the use of the `transfer` API might be difficult to grasp there's also a synchronous API call combining the request of the transfer and waiting for its result using `transferAndWait`.
59
59
```C++
60
60
byte lsm6dsox_read_reg(byte const reg_addr)
61
61
{
62
62
byte write_buffer = reg_addr;
63
63
byte read_buffer = 0;
64
64
65
65
IoRequest request(write_buffer, read_buffer);
66
-
IoResponse response = transfer_and_wait(lsm6dsox, request); /* Transmit IO request for execution and wait for completion of request. */
66
+
IoResponse response = transferAndWait(lsm6dsox, request); /* Transmit IO request for execution and wait for completion of request. */
67
67
68
68
return read_buffer;
69
69
}
@@ -78,7 +78,7 @@ For further simplification [Adafruit_BusIO](https://github.com/adafruit/Adafruit
As the use of the `transfer` API might be difficult to grasp there's also a synchronous API call combining the request of the transfer and waiting for its result using `transfer_and_wait`.
50
+
As the use of the `transfer` API might be difficult to grasp there's also a synchronous API call combining the request of the transfer and waiting for its result using `transferAndWait`.
*`mbed_nicla`: [Nicla Sense ME](https://store.arduino.cc/products/nicla-sense-me), [Nicla Vision](http://store.arduino.cc/products/nicla-vision)
12
12
13
+
### Tooling
13
14
14
15
Threading with the above mentioned Arduino cores can be achieved by leveraging the [Arduino_Threads](https://github.com/bcmi-labs/Arduino_Threads) library in combination with the [Arduino Command Line Interface](https://github.com/facchinm/arduino-cli/commits/arduino_threads_rebased) (arduino-cli).
15
16
16
-
Download a preliminary, pre-built `arduino-cli` binary for your operating system that supports threading:
Download a preliminary, pre-built `arduino-cli` binary (experimental) or patched `Arduino IDE` (very experimental) for your operating system that supports threading:
0 commit comments