Skip to content

Integrate Arduino_ThreadsafeIO #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 119 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
6b4cd90
Backup-Point #1: This SPI access works.
aentinger Jun 30, 2021
c0c6644
Backup-Point #2: This async access works but god knows its hacky as f…
aentinger Jun 30, 2021
c67b924
Turn SpiDispatcher into a threadsafe singleon, eliminating the need t…
aentinger Jul 1, 2021
06f0961
Limit public API of SpiDispatcher.
aentinger Jul 1, 2021
46deed6
Eliminate global variable rtos::Queues.
aentinger Jul 1, 2021
84d1664
Extract SPI processing code for better readability.
aentinger Jul 1, 2021
de5819c
Lock all public APIs of SpiDispatcher to avoid race conditions.
aentinger Jul 1, 2021
813b062
Slightly expanding IoRequest API and cleaning up a bit.
aentinger Jul 1, 2021
d73db48
Rename TxBuf to WriteBuf, RxBuf to ReadBuf
aentinger Jul 1, 2021
e588f48
IoRequest is now again properly hiding data behind accessor().
aentinger Jul 1, 2021
2517bed
First call to SpiDispatcher::instance(). ... starts everything, no de…
aentinger Jul 1, 2021
3ff0d44
Extract SpiBusDeviceConfig.
aentinger Jul 1, 2021
90514b2
Extracting SpiIoRequest.
aentinger Jul 1, 2021
eb3fb60
Minor refactorings.
aentinger Jul 1, 2021
3d67409
Move everything into subfolder spi
aentinger Jul 1, 2021
2eaab0a
Extracting SpiIoRequest
aentinger Jul 2, 2021
920ba3f
Async waiting step 1 ...
aentinger Jul 2, 2021
ddcb183
Using SharedPtr eliminates the issue of memory leaks.
aentinger Jul 5, 2021
ab763ea
Renaming 'request' to 'dispatch'
aentinger Jul 5, 2021
3c963d3
Simplifying Input and Output buffer (IoRequest/IoResponse)
aentinger Jul 5, 2021
14ff754
Pass IoTransactions instead of IoRequest/IoResponses.
aentinger Jul 5, 2021
c0b9db6
Also transport information on how many bytes have been read/how many …
aentinger Jul 5, 2021
b0d61ea
Encapsulate the waiting in a function.
aentinger Jul 5, 2021
30b0542
Consolating IoRequest and IoResponse within IoTransaction.
aentinger Jul 5, 2021
22f55a9
Replacing the Queue with a Mailbox, therefore eliminating the last me…
aentinger Jul 5, 2021
5b82cc7
Extending example code to whip up 20 threads and do parallel SPI access.
aentinger Jul 5, 2021
b54f584
A bit of refactoring for better readability.
aentinger Jul 5, 2021
042cfc3
Cleverly restructuring the way we are using IoTransactions we can rem…
aentinger Jul 5, 2021
ffadee7
Deleting SpiIoRequest
aentinger Jul 5, 2021
a1d0feb
Minimal keywords.txt for syntax highlighting.
aentinger Jul 5, 2021
17488b8
Replacing TSharedIoResponse type with name IoResponse (still a Shared…
aentinger Jul 5, 2021
3beb38e
Single-threaded example for querying WHO_AM_I register of LMS6 sensor…
aentinger Jul 6, 2021
d25b307
Providing threadsafe Wire implementation.
aentinger Jul 6, 2021
f3e5b79
Adding some linebreaks for better readability.
aentinger Jul 6, 2021
733ae32
Fixing WireDispatcher and SpiDispatcher.
aentinger Jul 6, 2021
8ef1d71
Reducing public API to prevent mis-usage.
aentinger Jul 6, 2021
b11b1be
Eliminate pointer to read buffer from IO response, therefore making t…
aentinger Jul 6, 2021
24623b0
Provide overload of IoRequest constructor to deal with single-byte 'b…
aentinger Jul 7, 2021
5fde088
Replacing uint8_t with byte for better consistency with the Arduino way.
aentinger Jul 7, 2021
552c940
Replace deprecated mailbox.get() with mailbox.try_get_for(forever)
aentinger Jul 7, 2021
5fe1520
Replacing deprecated rtos::ThisThread::sleep_for with the version usi…
aentinger Jul 7, 2021
218383e
Minor cosmetic operations
aentinger Jul 12, 2021
c210f28
Moving all library files in the repositories root folder.
aentinger Jul 12, 2021
4eca7fb
Renaming library to Arduino_ThreadsafeIO and ensure compileability fo…
aentinger Jul 12, 2021
af14e49
Providing a minimal README
aentinger Jul 12, 2021
f4db4a8
Configure Dependabot to check for outdated actions used in workflows
per1234 Jul 12, 2021
df126d7
Adding license text in header.
aentinger Jul 12, 2021
679b94f
Adding LGPL license deed.
aentinger Jul 12, 2021
631e149
Filling license deed.
aentinger Jul 12, 2021
20ba41b
Release v0.1.0
aentinger Jul 12, 2021
9d0961a
Add CI workflow to check for commonly misspelled words
per1234 Jul 12, 2021
7ec7ad6
Correct typos in comments
per1234 Jul 12, 2021
eb791f4
Add CI workflow to do Arduino project-specific linting
per1234 Jul 12, 2021
c9ecc44
Correct field name in library.properties
per1234 Jul 12, 2021
0dd94af
Add "smoke test" examples compilation CI workflow
per1234 Jul 12, 2021
ce281e6
Merge pull request #1 from per1234/ci
aentinger Jul 12, 2021
6d9459c
Overload SpiBusDeviceConfig in order to support the simple use case o…
aentinger Jul 19, 2021
30ef095
Fix: Filling empty url field within library.properties. (#3)
aentinger Jul 19, 2021
b2c2b36
Creating SpiBusDevice objects via new class BusDeviceCreator.
aentinger Jul 19, 2021
2727e41
Creating WireBusDevice objects via new class BusDeviceCreator.
aentinger Jul 19, 2021
010c71f
SpiBusDevice: Replace string for object name with actual SPI object.
aentinger Jul 19, 2021
a0ac870
Removing no longer used functions bmp388_select/bmp388_deselect.
aentinger Jul 19, 2021
062bb0a
WireBusDevice: Replace string for object name with actual Wire object.
aentinger Jul 19, 2021
a9330ec
Reorder WireBusDevice create functions to form a natural progression …
aentinger Jul 19, 2021
d041968
Streamline SPI API.
aentinger Jul 19, 2021
f811c2d
Merge pull request #4 from bcmi-labs/hide-config-objects
aentinger Jul 19, 2021
93a70bf
Release v0.2.0
aentinger Jul 22, 2021
77bc88b
Replace class BusDeviceCreator with static member function BusDeviceB…
aentinger Jul 27, 2021
36edb44
Applying patch by @facchinm allowing for Ctor style object creation.
aentinger Jul 28, 2021
368e6a1
Beautify up and also test for SPI.
aentinger Jul 28, 2021
fdb24c0
Merge pull request #5 from bcmi-labs/use-ctor-for-object-creation
aentinger Aug 19, 2021
e20752a
Release v0.3.0.
aentinger Aug 19, 2021
a2f4e6d
Initial draft of SerialDispatched based on @facchinm's implementation…
aentinger Aug 31, 2021
8a86745
Fixing spelling mistake.
aentinger Aug 31, 2021
850046b
Fix CI compilation for Portenta H7/M4 - no SerialUSB available.
aentinger Sep 1, 2021
94c6c0f
block/unblock allows for safe aggregration of serial messages in a th…
aentinger Sep 3, 2021
2b00b68
Merge pull request #7 from bcmi-labs/serial
aentinger Sep 3, 2021
8edd5b9
Release v0.4.0.
aentinger Sep 3, 2021
8861451
Removing code duplication in SerialDispatcher. (#13)
aentinger Sep 3, 2021
a04ae1f
begin(baudrate,config) needs to be locked against multi-threaded acce…
aentinger Sep 3, 2021
0faf9ab
Bugfix: Prevent access to iterator when block/unblock is called by an…
aentinger Sep 6, 2021
5000a58
Refactor lambda function to make the control flow more visible. (#16)
aentinger Sep 6, 2021
1d9bdc3
Implementation of threadsafe reading from the serial.
aentinger Sep 9, 2021
d2a4fa4
Eliminate dedicated status variable is_reader because checking whethe…
aentinger Sep 9, 2021
5ee1bb2
Enable the injection of a prefined message before after a message. (#19)
aentinger Sep 17, 2021
c505217
Merge pull request #18 from bcmi-labs/serial-read
aentinger Sep 17, 2021
b4e6462
ts_spi -> Threadsafe_SPI
aentinger Sep 17, 2021
93ca11c
ts_wire -> Threadsafe_Wire
aentinger Sep 17, 2021
39cdcea
ts_serial -> Threadsafe_Serial_Writer
aentinger Sep 17, 2021
992f315
ts_serial_reader -> Threadsafe_Serial_Reader
aentinger Sep 17, 2021
3893f15
ts_serial_prefix_suffix -> Threadsafe_Serial_Prefix_Suffix
aentinger Sep 17, 2021
5ac6fa7
Release v0.5.0
aentinger Sep 17, 2021
ad17cde
Merge pull request #25 from bcmi-labs/rename-examples
aentinger Sep 17, 2021
aa64ae2
Provide message to be written to prefix/suffix callbacks:
aentinger Sep 17, 2021
760e4b8
Rewrite suffix/prefix example to use for NMEA encoding of a message.
aentinger Sep 21, 2021
4b02a9d
We need a bigger serial transmit ringbuffer, if we are going to store…
aentinger Sep 21, 2021
d164c26
Replace inline-lamda-implementation with regular function pointers.
aentinger Sep 21, 2021
e55cdb9
Rename example for better clarity about use-case.
aentinger Sep 21, 2021
ec08d12
Merge pull request #26 from bcmi-labs/data-in-callback
aentinger Sep 21, 2021
1b152ac
Allow registration of global prefix/suffix callbacks which take 2nd p…
aentinger Sep 22, 2021
206aa05
Example Threadsafe_Serial_GlobalPrefixSuffix demonstrates how to util…
aentinger Sep 22, 2021
b3b39d5
Adding global_prefix/suffix as keywords for accurate syntax highlight…
aentinger Sep 22, 2021
10348ab
Fix spelling of incorrectly spelled word 'precedence'
aentinger Sep 22, 2021
e3e793f
Bugfix: Only perform i2c write if the write buffer is > 0 (#28)
aentinger Sep 28, 2021
26b562b
Implement write_then_read style API for threadsafe Wire.
aentinger Sep 28, 2021
2d1a689
Move implementation from WireBusDevice.h to WireBusDevice.cpp
aentinger Sep 28, 2021
d492b23
Implement Adafruit_BusIO-style read/write methods for I2C.
aentinger Sep 28, 2021
ec2eb39
Move implementation from SpiBusDevice.h to SpiBusDevice.cpp
aentinger Sep 28, 2021
58b6fb9
Implement Adafruit_BusIO-style write_then_read method for SPI.
aentinger Sep 28, 2021
98bb122
Implement Adafruit_BusIO-style write/read method for SPI.
aentinger Sep 28, 2021
414908d
Prevent out-of-bound buffer access.
aentinger Sep 28, 2021
1fae13f
Redesiging SpiDispatcher to conform the write_then_read API of Adafru…
aentinger Sep 30, 2021
fd2bfa1
Merge pull request #29 from bcmi-labs/busio-style-interface
aentinger Oct 1, 2021
31840d7
Merge branch 'main' into global-local-callbacks
aentinger Oct 1, 2021
28522d6
Merge pull request #27 from bcmi-labs/global-local-callbacks
aentinger Oct 1, 2021
0142844
Release v0.6.0
aentinger Oct 1, 2021
40e2855
Initial draft on a documentation within README. (#31)
aentinger Oct 5, 2021
5a21e7f
Merge branch 'main' of /home/alex/projects/arduino/libraries/Arduino_…
aentinger Oct 13, 2021
d7e9dfe
Delete superfluous compile-examples-private.yml.
aentinger Oct 13, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[codespell]
# In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here:
ignore-words-list = inot
skip = ./.git
builtin = clear,informal,en-GB_to_en-US
check-filenames =
check-hidden =
skip = ./.git
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# See: https://docs.github.com/en/code-security/supply-chain-security/configuration-options-for-dependency-updates#about-the-dependabotyml-file
version: 2

updates:
# Configure check for outdated GitHub Actions actions in workflows.
# See: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-actions-up-to-date-with-dependabot
- package-ecosystem: github-actions
directory: / # Check the repository's workflows under /.github/workflows/
schedule:
interval: daily
labels:
- "topic: infrastructure"
2 changes: 1 addition & 1 deletion .github/workflows/check-arduino.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Arduino Lint
uses: arduino/arduino-lint-action@v1
with:
compliance: strict
compliance: specification
# Change this to "update" once the library is added to the index.
library-manager: submit
# Always use this setting for official repositories. Remove for 3rd party projects.
Expand Down
46 changes: 43 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,59 @@
<img src="https://content.arduino.cc/website/Arduino_logo_teal.svg" height="100" align="right" />

`Arduino_Threads`
=================

[![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)
[![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)
[![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)

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

## :zap: Features
### :thread: Multi-threaded sketch execution
### :thread: Multi-Threading
#### :thread: Multi-threaded sketch execution
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` representing a clear separation of concerns on a file level.

### :calling: Easy communication between multiple threads
#### :calling: Easy communication between multiple threads
Easy inter-thread-communication is facilitated via a `Shared` abstraction providing thread-safe sink/source semantics allowing to safely exchange data of any type between threads.

### :thread: Threadsafe IO
#### :thread: Threadsafe
A key problem of multi-tasking is the **prevention of erroneous state when multiple threads share a single resource**. The following example borrowed from a typical application demonstrates the problems resulting from multiple threads sharing a single resource:

Imagine a embedded system where multiple `Wire` client devices are physically connected to a single `Wire` server. Each `Wire` client device is managed by a separate software thread. Each thread polls its `Wire` client device periodically. Access to the I2C bus is managed via the `Wire` library and typically follows this pattern:

```C++
/* Wire Write Access */
Wire.beginTransmission(addr);
Wire.write(val);
Wire.endTransmission();

/* Wire Read Access */
Wire.beginTransmission(addr);
Wire.write(val);
Wire.endTransmission();
Wire.requestFrom(addr, bytes)
while(Wire.available()) {
int val = Wire.read();
}
```

Since we are using [ARM Mbed OS](https://os.mbed.com/mbed-os/) which is a [preemptive](https://en.wikipedia.org/wiki/Preemption_(computing)) [RTOS](https://en.wikipedia.org/wiki/Real-time_operating_system) for achieving multi-tasking capability and under the assumption that all threads share the same priority (which leads to a [round-robin](https://en.wikipedia.org/wiki/Round-robin_scheduling) scheduling) it can easily happen that one thread is half-way through its Wire IO access when the scheduler interrupts it and schedules the next thread which in turn starts/continues/ends its own Wire IO access.

As a result this interruption by the scheduler will break Wire IO access for both devices and leave the Wire IO controller in an undefined state. :fire:.

`Arduino_ThreadsafeIO` solves this problem by encapsulating a complete IO access (e.g. reading from a `Wire` client device) within a single function call which generates an IO request to be asynchronously executed by a high-priority IO thread. The high-priority IO thread is the **only** instance which actually directly communicates with physical hardware.

#### :zzz: Asynchronous

The mechanisms implemented in this library allow any thread to dispatch an IO request asynchronously and either continue operation or [yield](https://en.wikipedia.org/wiki/Yield_(multithreading))-ing control to the next scheduled thread. All IO requests are stored in a queue and are executed within a high-priority IO thread after a context-switch. An example of this can be seen [here](examples/Threadsafe_SPI/Threadsafe_SPI.ino)).

#### :sparkling_heart: Convenient API

Although you are free to directly manipulate IO requests and responses (e.g. [Threadsafe_Wire](examples/Threadsafe_Wire/Threadsafe_Wire.ino)) there do exist 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_Wire_BusIO/Threadsafe_Wire_BusIO.ino)).


## :mag_right: Resources

* [How to install a library](https://www.arduino.cc/en/guide/libraries)
Expand Down
89 changes: 89 additions & 0 deletions examples/Threadsafe_SPI/Threadsafe_SPI.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**************************************************************************************
* INCLUDE
**************************************************************************************/

#include <Arduino_ThreadsafeIO.h>

/**************************************************************************************
* CONSTANTS
**************************************************************************************/

static int const BMP388_CS_PIN = 2;
static int const BMP388_INT_PIN = 6;
static byte const BMP388_CHIP_ID_REG_ADDR = 0x00;

static size_t constexpr NUM_THREADS = 20;

/**************************************************************************************
* FUNCTION DECLARATION
**************************************************************************************/

byte bmp388_read_reg(byte const reg_addr);
void bmp388_thread_func();

/**************************************************************************************
* GLOBAL VARIABLES
**************************************************************************************/

BusDevice bmp388(SPI, BMP388_CS_PIN, 1000000, MSBFIRST, SPI_MODE0);

static char thread_name[NUM_THREADS][32];

/**************************************************************************************
* SETUP/LOOP
**************************************************************************************/

void setup()
{
Serial.begin(9600);
while (!Serial) { }

pinMode(BMP388_CS_PIN, OUTPUT);
digitalWrite(BMP388_CS_PIN, HIGH);

for(size_t i = 0; i < NUM_THREADS; i++)
{
snprintf(thread_name[i], sizeof(thread_name[i]), "Thread #%02d", i);
rtos::Thread * t = new rtos::Thread(osPriorityNormal, OS_STACK_SIZE, nullptr, thread_name[i]);
t->start(bmp388_thread_func);
}
}

void loop()
{

}

/**************************************************************************************
* FUNCTION DEFINITION
**************************************************************************************/

byte bmp388_read_reg(byte const reg_addr)
{
/* REG_ADDR | DUMMY_BYTE | REG_VAL is on SDO */
byte read_write_buf[] = {static_cast<byte>(0x80 | reg_addr), 0, 0};

IoRequest req(read_write_buf, sizeof(read_write_buf), nullptr, 0);
IoResponse rsp = bmp388.transfer(req);

/* Do other stuff */

rsp->wait();

return read_write_buf[2];
}

void bmp388_thread_func()
{
for(;;)
{
/* Sleep between 5 and 500 ms */
rtos::ThisThread::sleep_for(rtos::Kernel::Clock::duration_u32(random(5,500)));
/* Try to read some data from the BMP3888. */
byte const chip_id = bmp388_read_reg(BMP388_CHIP_ID_REG_ADDR);
/* Print thread id and chip id value to serial. */
char msg[64] = {0};
snprintf(msg, sizeof(msg), "%s: Chip ID = 0x%X", rtos::ThisThread::get_name(), chip_id);
Serial.println(msg);
}
}
84 changes: 84 additions & 0 deletions examples/Threadsafe_SPI_BusIO/Threadsafe_SPI_BusIO.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**************************************************************************************
* INCLUDE
**************************************************************************************/

#include <Arduino_ThreadsafeIO.h>

/**************************************************************************************
* CONSTANTS
**************************************************************************************/

static int const BMP388_CS_PIN = 2;
static int const BMP388_INT_PIN = 6;
static byte const BMP388_CHIP_ID_REG_ADDR = 0x00;

static size_t constexpr NUM_THREADS = 20;

/**************************************************************************************
* FUNCTION DECLARATION
**************************************************************************************/

byte bmp388_read_reg(byte const reg_addr);
void bmp388_thread_func();

/**************************************************************************************
* GLOBAL VARIABLES
**************************************************************************************/

BusDevice bmp388(SPI, BMP388_CS_PIN, 1000000, MSBFIRST, SPI_MODE0);

static char thread_name[NUM_THREADS][32];

/**************************************************************************************
* SETUP/LOOP
**************************************************************************************/

void setup()
{
Serial.begin(9600);
while (!Serial) { }

pinMode(BMP388_CS_PIN, OUTPUT);
digitalWrite(BMP388_CS_PIN, HIGH);

for(size_t i = 0; i < NUM_THREADS; i++)
{
snprintf(thread_name[i], sizeof(thread_name[i]), "Thread #%02d", i);
rtos::Thread * t = new rtos::Thread(osPriorityNormal, OS_STACK_SIZE, nullptr, thread_name[i]);
t->start(bmp388_thread_func);
}
}

void loop()
{

}

/**************************************************************************************
* FUNCTION DEFINITION
**************************************************************************************/

byte bmp388_read_reg(byte const reg_addr)
{
/* REG_ADDR | DUMMY_BYTE | REG_VAL is on SDO */
byte write_buf[2] = {static_cast<byte>(0x80 | reg_addr), 0};
byte read_buf = 0;

bmp388.spi().write_then_read(write_buf, sizeof(write_buf), &read_buf, sizeof(read_buf));
return read_buf;
}

void bmp388_thread_func()
{
for(;;)
{
/* Sleep between 5 and 500 ms */
rtos::ThisThread::sleep_for(rtos::Kernel::Clock::duration_u32(random(5,500)));
/* Try to read some data from the BMP3888. */
byte const chip_id = bmp388_read_reg(BMP388_CHIP_ID_REG_ADDR);
/* Print thread id and chip id value to serial. */
char msg[64] = {0};
snprintf(msg, sizeof(msg), "%s: Chip ID = 0x%X", rtos::ThisThread::get_name(), chip_id);
Serial.println(msg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**************************************************************************************
* INCLUDE
**************************************************************************************/

#include <Arduino_ThreadsafeIO.h>

/**************************************************************************************
* CONSTANTS
**************************************************************************************/

static size_t constexpr NUM_THREADS = 5;

/**************************************************************************************
* FUNCTION DECLARATION
**************************************************************************************/

String serial_log_message_prefix(String const & /* msg */);
String serial_log_message_suffix(String const & prefix, String const & msg);
void serial_thread_func();

/**************************************************************************************
* GLOBAL VARIABLES
**************************************************************************************/

static char thread_name[NUM_THREADS][32];
#undef Serial
#ifdef ARDUINO_PORTENTA_H7_M4
SerialDispatcher Serial(Serial1); /* No SerialUSB for Portenta H7 / M4 Core */
#else
SerialDispatcher Serial(SerialUSB);
#endif

/**************************************************************************************
* SETUP/LOOP
**************************************************************************************/

void setup()
{
Serial.global_prefix(serial_log_message_prefix);
Serial.global_suffix(serial_log_message_suffix);

/* Fire up some threads all accessing the LSM6DSOX */
for(size_t i = 0; i < NUM_THREADS; i++)
{
snprintf(thread_name[i], sizeof(thread_name[i]), "Thread #%02d", i);
rtos::Thread * t = new rtos::Thread(osPriorityNormal, OS_STACK_SIZE, nullptr, thread_name[i]);
t->start(serial_thread_func);
}
}

void loop()
{

}

/**************************************************************************************
* FUNCTION DEFINITION
**************************************************************************************/

String serial_log_message_prefix(String const & /* msg */)
{
char msg[32] = {0};
snprintf(msg, sizeof(msg), "[%05lu] ", millis());
return String(msg);
}

String serial_log_message_suffix(String const & prefix, String const & msg)
{
return String("\r\n");
}

void serial_thread_func()
{
Serial.begin(9600);

for(;;)
{
/* Sleep between 5 and 500 ms */
rtos::ThisThread::sleep_for(rtos::Kernel::Clock::duration_u32(random(5,500)));
/* Print a unbroken log message including thread name and timestamp as a prefix. */
Serial.block();
Serial.print(rtos::ThisThread::get_name());
Serial.print(": ");
Serial.print("Lorem ipsum ...");
Serial.unblock();
}
}
Loading