Skip to content

Commit ed9e23c

Browse files
committed
Merge branch 'main' into experiment-threading
2 parents cb6cb1a + 082d78c commit ed9e23c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1198
-298
lines changed

.github/workflows/check-arduino.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ jobs:
2323
with:
2424
compliance: specification
2525
# Change this to "update" once the library is added to the index.
26-
library-manager: submit
26+
library-manager: update
2727
# Always use this setting for official repositories. Remove for 3rd party projects.
2828
official: true

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.vscode/

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
`Arduino_Threads`
44
=================
5+
*Note: This library is currently in [beta](#zap-caveats).*
56

67
[![Compile Examples status](https://github.com/arduino-libraries/Arduino_Threads/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_Threads/actions/workflows/compile-examples.yml)
78
[![Check Arduino status](https://github.com/arduino-libraries/Arduino_Threads/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_Threads/actions/workflows/check-arduino.yml)
89
[![Spell Check status](https://github.com/arduino-libraries/Arduino_Threads/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_Threads/actions/workflows/spell-check.yml)
910

1011
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. ​
1112

13+
Preeliminary **documentation** and download links for **required tooling** are available within the [`/docs`](docs/README.md) subfolder.
14+
1215
## :star: Features
1316
### :thread: Multi-threaded sketch execution
1417
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
4346
`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.
4447

4548
### :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).
4750

4851
### :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)).
5253

5354
## :zap: Caveats
5455

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).
5757

5858
## :mag_right: Resources
5959

docs/01-threading-basics.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
Threading Basics
44
================
55
## 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+
![Example of two tasks that ideally run in parallel.](assets/Arduino-Threads-Tasks-Example.svg)
711

812
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.
913
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 (
1317
#### Example (Single-Threaded):
1418
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.
1519

20+
![Diagram showing the sequential execution of the tasks](assets/Arduino-Threads-Sequential.svg)
21+
1622
**Blink_Three_LEDs.ino**:
1723

1824
```C++
@@ -55,7 +61,9 @@ You can imagine that with increasing complexity of a sketch it gets quite diffic
5561

5662
#### Example (Multi-Threaded):
5763

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+
![Diagram showing the parallel execution of the tasks](assets/Arduino-Threads-Parallel.svg)
5967

6068
**Blink_Three_LEDs.ino**
6169

@@ -116,4 +124,4 @@ As you can see from the example the name of the `*.inot`-file is used to generat
116124
* cannot contain spaces or special characters.
117125
* cannot be a C++ keyword (i.e. `register`, `volatile`, `while`, etc.).
118126

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.

docs/02-data-exchange.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,22 @@ A `Shared` variable is a global variable accessible to all threads. It can be de
1212
```C++
1313
/* SharedVariables.h */
1414
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. */
1517
```
1618
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.
1820
1921
```C++
2022
/* 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. */
2224
```
2325
If the internal queue is full the oldest element is discarded and the latest element is inserted into the queue.
2426

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:
2628
```C++
2729
/* 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. */
2931
```
3032

3133
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.
@@ -55,16 +57,16 @@ DataProducerThread.counter.connectTo(DataConsumerThread_2.counter);
5557
Whenever a new value is assigned to a data source, i.e.
5658
```C++
5759
/* DataProducerThread.inot */
58-
counter = 10;
60+
counter.push(10);
5961
```
6062
data is automatically copied and stored within the internal queues of all connected data sinks, from where it can be retrieved, i.e.
6163
```C++
6264
/* DataConsumerThread_1.inot */
63-
Serial.println(counter);
65+
Serial.println(counter.pop());
6466
```
6567
```C++
6668
/* DataConsumerThread_2.inot */
67-
Serial.println(counter);
69+
Serial.println(counter.pop());
6870
```
6971
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).
7072

docs/04-threadsafe-wire.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,18 @@ byte lsm6dsox_read_reg(byte const reg_addr)
5252
}
5353
```
5454

55-
### Synchronous thread-safe `Wire` access with `transfer_and_wait`
55+
### Synchronous thread-safe `Wire` access with `transferAndWait`
5656
([`examples/Threadsafe_IO/Wire`](../examples/Threadsafe_IO/Wire))
5757

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 `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`.
5959
```C++
6060
byte lsm6dsox_read_reg(byte const reg_addr)
6161
{
6262
byte write_buffer = reg_addr;
6363
byte read_buffer = 0;
6464

6565
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. */
6767

6868
return read_buffer;
6969
}
@@ -78,7 +78,7 @@ For further simplification [Adafruit_BusIO](https://github.com/adafruit/Adafruit
7878
byte lsm6dsox_read_reg(byte reg_addr)
7979
{
8080
byte read_buffer = 0;
81-
lsm6dsox.wire().write_then_read(&reg_addr, 1, &read_buffer, 1);
81+
lsm6dsox.wire().writeThenRead(&reg_addr, 1, &read_buffer, 1);
8282
return read_buffer;
8383
}
8484
```

docs/05-threadsafe-spi.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,18 @@ byte bmp388_read_reg(byte const reg_addr)
4444
}
4545
```
4646
47-
### Synchronous thread-safe `SPI` access with `transfer_and_wait`
47+
### Synchronous thread-safe `SPI` access with `transferAndWait`
4848
([`examples/Threadsafe_IO/SPI`](../examples/Threadsafe_IO/SPI))
4949
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 `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`.
5151
```C++
5252
byte bmp388_read_reg(byte const reg_addr)
5353
{
5454
/* REG_ADDR | DUMMY_BYTE | REG_VAL is on SDO */
5555
byte read_write_buffer[] = {0x80 | reg_addr, 0, 0};
5656
5757
IoRequest request(read_write_buffer, sizeof(read_write_buffer), nullptr, 0);
58-
IoResponse response = transfer_and_wait(bmp388, request);
58+
IoResponse response = transferAndWait(bmp388, request);
5959
6060
auto value = read_write_buffer[2];
6161
return value;
@@ -74,7 +74,7 @@ byte bmp388_read_reg(byte const reg_addr)
7474
byte write_buffer[2] = {0x80 | reg_addr, 0};
7575
byte read_buffer = 0;
7676

77-
bmp388.spi().write_then_read(write_buffer, sizeof(write_buffer), &read_buffer, sizeof(read_buffer));
77+
bmp388.spi().writeThenRead(write_buffer, sizeof(write_buffer), &read_buffer, sizeof(read_buffer));
7878
return read_buffer;
7979
}
8080
```

docs/README.md

+11-8
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,20 @@ The following Arduino architectures and boards are currently supported:
1010
* `mbed_edge`: [Edge Control](https://store.arduino.cc/products/arduino-edge-control)
1111
* `mbed_nicla`: [Nicla Sense ME](https://store.arduino.cc/products/nicla-sense-me), [Nicla Vision](http://store.arduino.cc/products/nicla-vision)
1212

13+
### Tooling
1314

1415
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).
1516

16-
Download a preliminary, pre-built `arduino-cli` binary for your operating system that supports threading:
17-
* [MacOS_64Bit](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_macOS_64bit.tar.gz)
18-
* [Linux_32Bit](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Linux_32bit.tar.gz)
19-
* [Linux_64Bit](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Linux_64bit.tar.gz)
20-
* [Linux_ARMv6](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Linux_ARMv6.tar.gz)
21-
* [Linux_ARMv7](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Linux_ARMv7.tar.gz)
22-
* [Windows_32Bit](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Windows_32bit.zip)
23-
* [Windows_64Bit](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Windows_64bit.zip)
17+
Download a preliminary, pre-built `arduino-cli` binary (experimental) or patched `Arduino IDE` (very experimental) for your operating system that supports threading:
18+
| Platform | Download CLI | Download IDE |
19+
|:-:|:-:|:-:|
20+
| MacOS_64Bit | [`arduino-cli`](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_macOS_64bit.tar.gz) | [`Arduino IDE`](https://downloads.arduino.cc/ide_staging/arduino_threads/arduino-PR-ae92bd4498e867b07581d1d4191be14b9ef0f69a-BUILD-65-macosx.zip) |
21+
| Linux_32Bit | [`arduino-cli`](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Linux_32bit.tar.gz) | [`Arduino IDE`](https://downloads.arduino.cc/ide_staging/arduino_threads/arduino-PR-ae92bd4498e867b07581d1d4191be14b9ef0f69a-BUILD-65-linux32.tar.xz) |
22+
| Linux_64Bit | [`arduino-cli`](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Linux_64bit.tar.gz) | [`Arduino IDE`](https://downloads.arduino.cc/ide_staging/arduino_threads/arduino-PR-ae92bd4498e867b07581d1d4191be14b9ef0f69a-BUILD-65-linux64.tar.xz) |
23+
| Linux_ARMv6 | [`arduino-cli`](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Linux_ARMv6.tar.gz) | |
24+
| Linux_ARMv7 | [`arduino-cli`](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Linux_ARMv7.tar.gz) | [`Arduino IDE`](https://downloads.arduino.cc/ide_staging/arduino_threads/arduino-PR-ae92bd4498e867b07581d1d4191be14b9ef0f69a-BUILD-65-linuxarm.tar.xz) |
25+
| Windows_32Bit | [`arduino-cli`](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Windows_32bit.zip) | |
26+
| Windows_64Bit | [`arduino-cli`](https://downloads.arduino.cc/tools/arduino-cli/inot_support/arduino-cli_git-snapshot_Windows_64bit.zip) | [`Arduino IDE`](https://downloads.arduino.cc/ide_staging/arduino_threads/arduino-PR-ae92bd4498e867b07581d1d4191be14b9ef0f69a-BUILD-65-windows.zip) |
2427

2528
### Table of Contents
2629

0 commit comments

Comments
 (0)