Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1bcf8ae

Browse files
PilnyTomasme-no-dev
authored andcommittedFeb 8, 2023
Multi threading examples (tasks, queues, semaphores, mutexes) (#7660)
* Moved and renamed example ESP32/FreeRTOS to MultiThreading/BasicMultiThreading * Added dummy files * Modified original example * Fixed BasicMultiThreading.ino * Added Example demonstrating use of queues * Extended info in BasicMultiThreading * Renamed Queues to singular Queue * Added Mutex example * Added Semaphore example * Moved info from example to README * Moved doc from Mutex to README * Added Queue README * Removed unecesary text * Fixed grammar * Increased stack size for Sempahore example * Added headers into .ino files * Added word Example at the end of title in README * removed unused line * Added forgotten README * Modified BasicMultiThreading example * Added missing S3 entry in README * moved location
1 parent bcbf73b commit 1bcf8ae

File tree

9 files changed

+756
-102
lines changed

9 files changed

+756
-102
lines changed
 
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/* Basic Multi Threading Arduino Example
2+
This example code is in the Public Domain (or CC0 licensed, at your option.)
3+
Unless required by applicable law or agreed to in writing, this
4+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
5+
CONDITIONS OF ANY KIND, either express or implied.
6+
*/
7+
// Please read file README.md in the folder containing this example.
8+
9+
#if CONFIG_FREERTOS_UNICORE
10+
#define ARDUINO_RUNNING_CORE 0
11+
#else
12+
#define ARDUINO_RUNNING_CORE 1
13+
#endif
14+
15+
#define ANALOG_INPUT_PIN A0
16+
17+
#ifndef LED_BUILTIN
18+
#define LED_BUILTIN 13 // Specify the on which is your LED
19+
#endif
20+
21+
// Define two tasks for Blink & AnalogRead.
22+
void TaskBlink( void *pvParameters );
23+
void TaskAnalogRead( void *pvParameters );
24+
TaskHandle_t analog_read_task_handle; // You can (don't have to) use this to be able to manipulate a task from somewhere else.
25+
26+
// The setup function runs once when you press reset or power on the board.
27+
void setup() {
28+
// Initialize serial communication at 115200 bits per second:
29+
Serial.begin(115200);
30+
// Set up two tasks to run independently.
31+
uint32_t blink_delay = 1000; // Delay between changing state on LED pin
32+
xTaskCreate(
33+
TaskBlink
34+
, "Task Blink" // A name just for humans
35+
, 2048 // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
36+
, (void*) &blink_delay // Task parameter which can modify the task behavior. This must be passed as pointer to void.
37+
, 2 // Priority
38+
, NULL // Task handle is not used here - simply pass NULL
39+
);
40+
41+
// This variant of task creation can also specify on which core it will be run (only relevant for multi-core ESPs)
42+
xTaskCreatePinnedToCore(
43+
TaskAnalogRead
44+
, "Analog Read"
45+
, 2048 // Stack size
46+
, NULL // When no parameter is used, simply pass NULL
47+
, 1 // Priority
48+
, &analog_read_task_handle // With task handle we will be able to manipulate with this task.
49+
, ARDUINO_RUNNING_CORE // Core on which the task will run
50+
);
51+
52+
Serial.printf("Basic Multi Threading Arduino Example\n");
53+
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
54+
}
55+
56+
void loop(){
57+
if(analog_read_task_handle != NULL){ // Make sure that the task actually exists
58+
delay(10000);
59+
vTaskDelete(analog_read_task_handle); // Delete task
60+
analog_read_task_handle = NULL; // prevent calling vTaskDelete on non-existing task
61+
}
62+
}
63+
64+
/*--------------------------------------------------*/
65+
/*---------------------- Tasks ---------------------*/
66+
/*--------------------------------------------------*/
67+
68+
void TaskBlink(void *pvParameters){ // This is a task.
69+
uint32_t blink_delay = *((uint32_t*)pvParameters);
70+
71+
/*
72+
Blink
73+
Turns on an LED on for one second, then off for one second, repeatedly.
74+
75+
If you want to know what pin the on-board LED is connected to on your ESP32 model, check
76+
the Technical Specs of your board.
77+
*/
78+
79+
// initialize digital LED_BUILTIN on pin 13 as an output.
80+
pinMode(LED_BUILTIN, OUTPUT);
81+
82+
for (;;){ // A Task shall never return or exit.
83+
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
84+
// arduino-esp32 has FreeRTOS configured to have a tick-rate of 1000Hz and portTICK_PERIOD_MS
85+
// refers to how many milliseconds the period between each ticks is, ie. 1ms.
86+
delay(blink_delay);
87+
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
88+
delay(blink_delay);
89+
}
90+
}
91+
92+
void TaskAnalogRead(void *pvParameters){ // This is a task.
93+
(void) pvParameters;
94+
// Check if the given analog pin is usable - if not - delete this task
95+
if(!adcAttachPin(ANALOG_INPUT_PIN)){
96+
Serial.printf("TaskAnalogRead cannot work because the given pin %d cannot be used for ADC - the task will delete itself.\n", ANALOG_INPUT_PIN);
97+
analog_read_task_handle = NULL; // Prevent calling vTaskDelete on non-existing task
98+
vTaskDelete(NULL); // Delete this task
99+
}
100+
101+
/*
102+
AnalogReadSerial
103+
Reads an analog input on pin A3, prints the result to the serial monitor.
104+
Graphical representation is available using serial plotter (Tools > Serial Plotter menu)
105+
Attach the center pin of a potentiometer to pin A3, and the outside pins to +5V and ground.
106+
107+
This example code is in the public domain.
108+
*/
109+
110+
for (;;){
111+
// read the input on analog pin:
112+
int sensorValue = analogRead(ANALOG_INPUT_PIN);
113+
// print out the value you read:
114+
Serial.println(sensorValue);
115+
delay(100); // 100ms delay
116+
}
117+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Basic Multi Threading Example
2+
3+
This example demonstrates the basic usage of FreeRTOS Tasks for multi threading.
4+
5+
Please refer to other examples in this folder to better utilize their full potential and safeguard potential problems.
6+
It is also advised to read the documentation on FreeRTOS web pages:
7+
[https://www.freertos.org/a00106.html](https://www.freertos.org/a00106.html)
8+
9+
This example will blink the built-in LED and read analog data.
10+
Additionally, this example demonstrates the usage of the task handle, simply by deleting the analog
11+
read task after 10 seconds from the main loop by calling the function `vTaskDelete`.
12+
13+
### Theory:
14+
A task is simply a function that runs when the operating system (FreeeRTOS) sees fit.
15+
This task can have an infinite loop inside if you want to do some work periodically for the entirety of the program run.
16+
This, however, can create a problem - no other task will ever run and also the Watch Dog will trigger and your program will restart.
17+
A nice behaving tasks know when it is useless to keep the processor for itself and give it away for other tasks to be used.
18+
This can be achieved in many ways, but the simplest is called `delay(`milliseconds)`.
19+
During that delay, any other task may run and do its job.
20+
When the delay runs out the Operating System gives the processor the task which can continue.
21+
For other ways to yield the CPU in a task please see other examples in this folder.
22+
It is also worth mentioning that two or more tasks running the same function will run them with separate stacks, so if you want to run the same code (which could be differentiated by the argument) there is no need to have multiple copies of the same function.
23+
24+
**Task creation has a few parameters you should understand:**
25+
```
26+
xTaskCreate(TaskFunction_t pxTaskCode,
27+
const char * const pcName,
28+
const uint16_t usStackDepth,
29+
void * const pvParameters,
30+
UBaseType_t uxPriority,
31+
TaskHandle_t * const pxCreatedTask )
32+
```
33+
- **pxTaskCode** is the name of your function which will run as a task
34+
- **pcName** is a string of human-readable descriptions for your task
35+
- **usStackDepth** is the number of words (word = 4B) available to the task. If you see an error similar to this "Debug exception reason: Stack canary watchpoint triggered (Task Blink)" you should increase it
36+
- **pvParameters** is a parameter that will be passed to the task function - it must be explicitly converted to (void*) and in your function explicitly converted back to the intended data type.
37+
- **uxPriority** is a number from 0 to configMAX_PRIORITIES which determines how the FreeRTOS will allow the tasks to run. 0 is the lowest priority.
38+
- **pxCreatedTask** task handle is a pointer to the task which allows you to manipulate the task - delete it, suspend and resume.
39+
If you don't need to do anything special with your task, simply pass NULL for this parameter.
40+
You can read more about task control here: https://www.freertos.org/a00112.html
41+
42+
# Supported Targets
43+
44+
This example supports all SoCs.
45+
46+
### Hardware Connection
47+
48+
If your board does not have a built-in LED, please connect one to the pin specified by the `LED_BUILTIN` in the code (you can also change the number and connect it to the pin you desire).
49+
50+
Optionally you can connect the analog element to the pin. such as a variable resistor, analog input such as an audio signal, or any signal generator. However, if the pin is left unconnected it will receive background noise and you will also see a change in the signal when the pin is touched by a finger.
51+
Please refer to the ESP-IDF ADC documentation for specific SoC for info on which pins are available:
52+
[ESP32](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32/api-reference/peripherals/adc.html),
53+
[ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32s2/api-reference/peripherals/adc.html),
54+
[ESP32-S3](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32s3/api-reference/peripherals/adc.html),
55+
[ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/v4.4/esp32c3/api-reference/peripherals/adc.html)
56+
57+
58+
#### Using Arduino IDE
59+
60+
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
61+
62+
* Before Compile/Verify, select the correct board: `Tools -> Board`.
63+
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
64+
65+
#### Using Platform IO
66+
67+
* Select the COM port: `Devices` or set the `upload_port` option on the `platformio.ini` file.
68+
69+
## Troubleshooting
70+
71+
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
72+
73+
## Contribute
74+
75+
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
76+
77+
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
78+
79+
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
80+
81+
## Resources
82+
83+
* Official ESP32 Forum: [Link](https://esp32.com)
84+
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
85+
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
86+
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
87+
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
88+
* ESP32-S3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
89+
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

‎libraries/ESP32/examples/FreeRTOS/FreeRTOS.ino

Lines changed: 0 additions & 102 deletions
This file was deleted.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* Basic Multi Threading Arduino Example
2+
This example code is in the Public Domain (or CC0 licensed, at your option.)
3+
Unless required by applicable law or agreed to in writing, this
4+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
5+
CONDITIONS OF ANY KIND, either express or implied.
6+
*/
7+
// Please read file README.md in the folder containing this example.
8+
9+
#define USE_MUTEX
10+
int shared_variable = 0;
11+
SemaphoreHandle_t shared_var_mutex = NULL;
12+
13+
// Define a task function
14+
void Task( void *pvParameters );
15+
16+
// The setup function runs once when you press reset or power on the board.
17+
void setup() {
18+
// Initialize serial communication at 115200 bits per second:
19+
Serial.begin(115200);
20+
while(!Serial) delay(100);
21+
Serial.printf(" Task 0 | Task 1\n");
22+
23+
#ifdef USE_MUTEX
24+
shared_var_mutex = xSemaphoreCreateMutex(); // Create the mutex
25+
#endif
26+
27+
// Set up two tasks to run the same function independently.
28+
static int task_number0 = 0;
29+
xTaskCreate(
30+
Task
31+
, "Task 0" // A name just for humans
32+
, 2048 // The stack size
33+
, (void*)&task_number0 // Pass reference to a variable describing the task number
34+
//, 5 // High priority
35+
, 1 // priority
36+
, NULL // Task handle is not used here - simply pass NULL
37+
);
38+
39+
static int task_number1 = 1;
40+
xTaskCreate(
41+
Task
42+
, "Task 1"
43+
, 2048 // Stack size
44+
, (void*)&task_number1 // Pass reference to a variable describing the task number
45+
, 1 // Low priority
46+
, NULL // Task handle is not used here - simply pass NULL
47+
);
48+
49+
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
50+
}
51+
52+
void loop(){
53+
}
54+
55+
/*--------------------------------------------------*/
56+
/*---------------------- Tasks ---------------------*/
57+
/*--------------------------------------------------*/
58+
59+
void Task(void *pvParameters){ // This is a task.
60+
int task_num = *((int*)pvParameters);
61+
Serial.printf("%s\n", task_num ? " Starting |" : " | Starting");
62+
for (;;){ // A Task shall never return or exit.
63+
#ifdef USE_MUTEX
64+
if(shared_var_mutex != NULL){ // Sanity check if the mutex exists
65+
// Try to take the mutex and wait indefintly if needed
66+
if(xSemaphoreTake(shared_var_mutex, portMAX_DELAY) == pdTRUE){
67+
// Mutex successfully taken
68+
#endif
69+
int new_value = random(1000);
70+
71+
char str0[32]; sprintf(str0, " %d <- %d |", shared_variable, new_value);
72+
char str1[32]; sprintf(str1, " | %d <- %d", shared_variable, new_value);
73+
Serial.printf("%s\n", task_num ? str0 : str1);
74+
75+
shared_variable = new_value;
76+
delay(random(100)); // wait random time of max 100 ms - simulating some computation
77+
78+
sprintf(str0, " R: %d |", shared_variable);
79+
sprintf(str1, " | R: %d", shared_variable);
80+
Serial.printf("%s\n", task_num ? str0 : str1);
81+
//Serial.printf("Task %d after write: reading %d\n", task_num, shared_variable);
82+
83+
if(shared_variable != new_value){
84+
Serial.printf("%s\n", task_num ? " Mismatch! |" : " | Mismatch!");
85+
//Serial.printf("Task %d: detected race condition - the value changed!\n", task_num);
86+
}
87+
88+
#ifdef USE_MUTEX
89+
xSemaphoreGive(shared_var_mutex); // After accessing the shared resource give the mutex and allow other processes to access it
90+
}else{
91+
// We could not obtain the semaphore and can therefore not access the shared resource safely.
92+
} // mutex take
93+
} // sanity check
94+
#endif
95+
delay(10); // Allow other task to be scheduled
96+
} // Infinite loop
97+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Mutex Example
2+
3+
This example demonstrates the basic usage of FreeRTOS Mutually Exclusive Locks (Mutex) for securing access to shared resources in multi-threading.
4+
Please refer to other examples in this folder to better understand the usage of tasks.
5+
It is also advised to read the documentation on FreeRTOS web pages:
6+
https://www.freertos.org/a00106.html
7+
8+
This example creates 2 tasks with the same implementation - they write into a shared variable and then read it and check if it is the same as what they have written.
9+
In single-thread programming like on Arduino this is of no concern and will be always ok, however when multi-threading is used the execution of the task is switched by the FreeRTOS and the value can be rewritten from another task before reading again.
10+
The tasks print write and read operation - each in their column for better reading. Task 0 is on the left and Task 1 is on the right.
11+
Watch the writes and read in secure mode when using the mutex (default) as the results are as you would expect them.
12+
Then try to comment the USE_MUTEX and watch again - there will be a lot of mismatches!
13+
14+
### Theory:
15+
Mutex is a specialized version of Semaphore (please see the Semaphore example for more info).
16+
In essence, the mutex is a variable whose value determines if the mute is taken (locked) or given (unlocked).
17+
When two or more processes access the same resource (variable, peripheral, etc) it might happen, for example, that when one task starts to read a variable and the operating system (FreeRTOS) will schedule the execution of another task
18+
which will write to this variable and when the previous task runs again it will read something different.
19+
20+
Mutexes and binary semaphores are very similar but have some subtle differences:
21+
Mutexes include a priority inheritance mechanism, whereas binary semaphores do not.
22+
This makes binary semaphores the better choice for implementing synchronization (between tasks or between tasks and an interrupt), and mutexes the better
23+
choice for implementing simple mutual exclusion.
24+
What is priority inheritance?
25+
If a low-priority task holds the Mutex but gets interrupted by a Higher priority task, which
26+
then tries to take the Mutex, the low-priority task will temporarily ‘inherit’ the high priority so a middle-priority task can't block the low-priority task, and thus also block the high priority task.
27+
Semaphores don't have the logic to handle this, in part because Semaphores aren't 'owned' by the task that takes them.
28+
29+
A mutex can also be recursive - if a task that holds the mutex takes it again, it will succeed, and the mutex will be released
30+
for other tasks only when it is given the same number of times that it was taken.
31+
32+
You can check the danger by commenting on the definition of USE_MUTEX which will disable the mutex and present the danger of concurrent access.
33+
34+
35+
# Supported Targets
36+
37+
This example supports all ESP32 SoCs.
38+
39+
## How to Use Example
40+
41+
Flash and observe the serial output.
42+
43+
Comment the `USE_MUTEX` definition, save and flash again and observe the behavior of unprotected access to the shared variable.
44+
45+
* How to install the Arduino IDE: [Install Arduino IDE](https://github.com/espressif/arduino-esp32/tree/master/docs/arduino-ide).
46+
47+
#### Using Arduino IDE
48+
49+
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
50+
51+
* Before Compile/Verify, select the correct board: `Tools -> Board`.
52+
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
53+
54+
#### Using Platform IO
55+
56+
* Select the COM port: `Devices` or set the `upload_port` option on the `platformio.ini` file.
57+
58+
## Example Log Output
59+
60+
The expected output of shared variables protected by mutex demonstrates mutually exclusive access from tasks - they do not interrupt each other and do not rewrite the value before the other task has read it back.
61+
62+
```
63+
Task 0 | Task 1
64+
| Starting
65+
| 0 <- 227
66+
Starting |
67+
| R: 227
68+
227 <- 737 |
69+
R: 737 |
70+
| 737 <- 282
71+
| R: 282
72+
282 <- 267 |
73+
```
74+
75+
The output of unprotected access to shared variable - it happens often that a task is interrupted after writing and before reading the other task write a different value - a corruption occurred!
76+
77+
```
78+
Task 0 | Task 1
79+
| Starting
80+
| 0 <- 333
81+
Starting |
82+
333 <- 620 |
83+
R: 620 |
84+
620 <- 244 |
85+
| R: 244
86+
| Mismatch!
87+
| 244 <- 131
88+
R: 131 |
89+
Mismatch! |
90+
131 <- 584 |
91+
| R: 584
92+
| Mismatch!
93+
| 584 <- 134
94+
| R: 134
95+
| 134 <- 554
96+
R: 554 |
97+
Mismatch! |
98+
554 <- 313 |
99+
```
100+
101+
## Troubleshooting
102+
103+
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
104+
105+
## Contribute
106+
107+
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
108+
109+
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
110+
111+
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
112+
113+
## Resources
114+
115+
* Official ESP32 Forum: [Link](https://esp32.com)
116+
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
117+
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
118+
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
119+
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
120+
* ESP32-S3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
121+
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/* Basic Multi Threading Arduino Example
2+
This example code is in the Public Domain (or CC0 licensed, at your option.)
3+
Unless required by applicable law or agreed to in writing, this
4+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
5+
CONDITIONS OF ANY KIND, either express or implied.
6+
*/
7+
// Please read file README.md in the folder containing this example./*
8+
9+
#define MAX_LINE_LENGTH (64)
10+
11+
// Define two tasks for reading and writing from and to the serial port.
12+
void TaskWriteToSerial(void *pvParameters);
13+
void TaskReadFromSerial(void *pvParameters);
14+
15+
// Define Queue handle
16+
QueueHandle_t QueueHandle;
17+
const int QueueElementSize = 10;
18+
typedef struct{
19+
char line[MAX_LINE_LENGTH];
20+
uint8_t line_length;
21+
} message_t;
22+
23+
// The setup function runs once when you press reset or power on the board.
24+
void setup() {
25+
// Initialize serial communication at 115200 bits per second:
26+
Serial.begin(115200);
27+
while(!Serial){delay(10);}
28+
29+
// Create the queue which will have <QueueElementSize> number of elements, each of size `message_t` and pass the address to <QueueHandle>.
30+
QueueHandle = xQueueCreate(QueueElementSize, sizeof(message_t));
31+
32+
// Check if the queue was successfully created
33+
if(QueueHandle == NULL){
34+
Serial.println("Queue could not be created. Halt.");
35+
while(1) delay(1000); // Halt at this point as is not possible to continue
36+
}
37+
38+
// Set up two tasks to run independently.
39+
xTaskCreate(
40+
TaskWriteToSerial
41+
, "Task Write To Serial" // A name just for humans
42+
, 2048 // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
43+
, NULL // No parameter is used
44+
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
45+
, NULL // Task handle is not used here
46+
);
47+
48+
xTaskCreate(
49+
TaskReadFromSerial
50+
, "Task Read From Serial"
51+
, 2048 // Stack size
52+
, NULL // No parameter is used
53+
, 1 // Priority
54+
, NULL // Task handle is not used here
55+
);
56+
57+
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
58+
Serial.printf("\nAnything you write will return as echo.\nMaximum line length is %d characters (+ terminating '0').\nAnything longer will be sent as a separate line.\n\n", MAX_LINE_LENGTH-1);
59+
}
60+
61+
void loop(){
62+
// Loop is free to do any other work
63+
64+
delay(1000); // While not being used yield the CPU to other tasks
65+
}
66+
67+
/*--------------------------------------------------*/
68+
/*---------------------- Tasks ---------------------*/
69+
/*--------------------------------------------------*/
70+
71+
void TaskWriteToSerial(void *pvParameters){ // This is a task.
72+
message_t message;
73+
for (;;){ // A Task shall never return or exit.
74+
// One approach would be to poll the function (uxQueueMessagesWaiting(QueueHandle) and call delay if nothing is waiting.
75+
// The other approach is to use infinite time to wait defined by constant `portMAX_DELAY`:
76+
if(QueueHandle != NULL){ // Sanity check just to make sure the queue actually exists
77+
int ret = xQueueReceive(QueueHandle, &message, portMAX_DELAY);
78+
if(ret == pdPASS){
79+
// The message was successfully received - send it back to Serial port and "Echo: "
80+
Serial.printf("Echo line of size %d: \"%s\"\n", message.line_length, message.line);
81+
// The item is queued by copy, not by reference, so lets free the buffer after use.
82+
}else if(ret == pdFALSE){
83+
Serial.println("The `TaskWriteToSerial` was unable to receive data from the Queue");
84+
}
85+
} // Sanity check
86+
} // Infinite loop
87+
}
88+
89+
void TaskReadFromSerial(void *pvParameters){ // This is a task.
90+
message_t message;
91+
for (;;){
92+
// Check if any data are waiting in the Serial buffer
93+
message.line_length = Serial.available();
94+
if(message.line_length > 0){
95+
// Check if the queue exists AND if there is any free space in the queue
96+
if(QueueHandle != NULL && uxQueueSpacesAvailable(QueueHandle) > 0){
97+
int max_length = message.line_length < MAX_LINE_LENGTH ? message.line_length : MAX_LINE_LENGTH-1;
98+
for(int i = 0; i < max_length; ++i){
99+
message.line[i] = Serial.read();
100+
}
101+
message.line_length = max_length;
102+
message.line[message.line_length] = 0; // Add the terminating nul char
103+
104+
// The line needs to be passed as pointer to void.
105+
// The last parameter states how many milliseconds should wait (keep trying to send) if is not possible to send right away.
106+
// When the wait parameter is 0 it will not wait and if the send is not possible the function will return errQUEUE_FULL
107+
int ret = xQueueSend(QueueHandle, (void*) &message, 0);
108+
if(ret == pdTRUE){
109+
// The message was successfully sent.
110+
}else if(ret == errQUEUE_FULL){
111+
// Since we are checking uxQueueSpacesAvailable this should not occur, however if more than one task should
112+
// write into the same queue it can fill-up between the test and actual send attempt
113+
Serial.println("The `TaskReadFromSerial` was unable to send data into the Queue");
114+
} // Queue send check
115+
} // Queue sanity check
116+
}else{
117+
delay(100); // Allow other tasks to run when there is nothing to read
118+
} // Serial buffer check
119+
} // Infinite loop
120+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Queue Example
2+
3+
This example demonstrates the basic usage of FreeRTOS Queues which enables tasks to pass data between each other in a secure asynchronous way.
4+
Please refer to other examples in this folder to better understand the usage of tasks.
5+
It is also advised to read the documentation on FreeRTOS web pages:
6+
[https://www.freertos.org/a00106.html](https://www.freertos.org/a00106.html)
7+
8+
This example reads data received on the serial port (sent by the user) pass it via queue to another task which will send it back on Serial Output.
9+
10+
### Theory:
11+
A queue is a simple-to-use data structure (in the most basic way) controlled by `xQueueSend` and `xQueueReceive` functions.
12+
Usually, one task writes into the queue and the other task reads from it.
13+
Usage of queues enables the reading task to yield the CPU until there are data in the queue and therefore not waste precious computation time.
14+
15+
# Supported Targets
16+
17+
This example supports all ESP32 SoCs.
18+
19+
## How to Use Example
20+
21+
Flash and write anything to serial input.
22+
23+
* How to install the Arduino IDE: [Install Arduino IDE](https://github.com/espressif/arduino-esp32/tree/master/docs/arduino-ide).
24+
25+
#### Using Arduino IDE
26+
27+
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
28+
29+
* Before Compile/Verify, select the correct board: `Tools -> Board`.
30+
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
31+
32+
#### Using Platform IO
33+
34+
* Select the COM port: `Devices` or set the `upload_port` option on the `platformio.ini` file.
35+
36+
## Example Log Output
37+
38+
```
39+
Anything you write will return as echo.
40+
Maximum line length is 63 characters (+ terminating '0').
41+
Anything longer will be sent as a separate line.
42+
43+
```
44+
< Input text "Short input"
45+
46+
``Echo line of size 11: "Short input"``
47+
48+
< Input text "An example of very long input which is longer than default 63 characters will be split."
49+
50+
```
51+
Echo line of size 63: "An example of very long input which is longer than default 63 c"
52+
Echo line of size 24: "haracters will be split."
53+
```
54+
55+
## Troubleshooting
56+
57+
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
58+
59+
## Contribute
60+
61+
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
62+
63+
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
64+
65+
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
66+
67+
## Resources
68+
69+
* Official ESP32 Forum: [Link](https://esp32.com)
70+
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
71+
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
72+
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
73+
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
74+
* ESP32-S3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
75+
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Semaphore Example
2+
3+
This example demonstrates the basic usage of FreeRTOS Semaphores and queue sets for coordination between tasks for multi-threading.
4+
Please refer to other examples in this folder to better understand the usage of tasks.
5+
It is also advised to read the documentation on FreeRTOS web pages:
6+
[https://www.freertos.org/a00106.html](https://www.freertos.org/a00106.html)
7+
8+
### Theory:
9+
Semaphore is in essence a variable. Tasks can set the value, wait until one or more
10+
semaphores are set and thus communicate between each other their state.
11+
A binary semaphore is a semaphore that has a maximum count of 1, hence the 'binary' name.
12+
A task can only 'take' the semaphore if it is available, and the semaphore is only available if its count is 1.
13+
14+
Semaphores can be controlled by any number of tasks. If you use semaphore as a one-way
15+
signalization with only one task giving and only one task taking there is a much faster option
16+
called Task Notifications - please see FreeRTOS documentation and read more about them: [https://www.freertos.org/RTOS-task-notifications.html](https://www.freertos.org/RTOS-task-notifications.html)
17+
18+
This example uses a semaphore to signal when a package is delivered to a warehouse by multiple
19+
delivery trucks, and multiple workers are waiting to receive the package.
20+
21+
# Supported Targets
22+
23+
This example supports all ESP32 SoCs.
24+
25+
## How to Use Example
26+
27+
Read the code and try to understand it, then flash and observe the Serial output.
28+
29+
* How to install the Arduino IDE: [Install Arduino IDE](https://github.com/espressif/arduino-esp32/tree/master/docs/arduino-ide).
30+
31+
#### Using Arduino IDE
32+
33+
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
34+
35+
* Before Compile/Verify, select the correct board: `Tools -> Board`.
36+
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
37+
38+
#### Using Platform IO
39+
40+
* Select the COM port: `Devices` or set the `upload_port` option on the `platformio.ini` file.
41+
42+
## Example Log Output
43+
44+
```
45+
Anything you write will return as echo.
46+
Maximum line length is 63 characters (+ terminating '0').
47+
Anything longer will be sent as a separate line.
48+
49+
```
50+
< Input text "Short input"
51+
52+
``Echo line of size 11: "Short input"``
53+
54+
< Input text "An example of very long input which is longer than default 63 characters will be split."
55+
56+
```
57+
Echo line of size 63: "An example of very long input which is longer than default 63 c"
58+
Echo line of size 24: "haracters will be split."
59+
```
60+
61+
## Troubleshooting
62+
63+
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
64+
65+
## Contribute
66+
67+
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
68+
69+
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
70+
71+
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
72+
73+
## Resources
74+
75+
* Official ESP32 Forum: [Link](https://esp32.com)
76+
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
77+
* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
78+
* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
79+
* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf)
80+
* ESP32-S3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
81+
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/* Basic Multi Threading Arduino Example
2+
This example code is in the Public Domain (or CC0 licensed, at your option.)
3+
Unless required by applicable law or agreed to in writing, this
4+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
5+
CONDITIONS OF ANY KIND, either express or implied.
6+
*/
7+
// Please read file README.md in the folder containing this example.
8+
9+
#include <Arduino.h>
10+
11+
SemaphoreHandle_t package_delivered_semaphore;
12+
13+
void delivery_truck_task(void *pvParameters) {
14+
int truck_number = (int) pvParameters;
15+
while(1) {
16+
// Wait for a package to be delivered
17+
// ...
18+
// Notify the warehouse that a package has been delivered
19+
xSemaphoreGive(package_delivered_semaphore);
20+
Serial.printf("Package delivered by truck: %d\n", truck_number);
21+
//wait for some time
22+
vTaskDelay(1000 / portTICK_PERIOD_MS);
23+
}
24+
}
25+
26+
void warehouse_worker_task(void *pvParameters) {
27+
int worker_number = (int) pvParameters;
28+
while(1) {
29+
// Wait for a package to be delivered
30+
xSemaphoreTake(package_delivered_semaphore, portMAX_DELAY);
31+
Serial.printf("Package received by worker: %d\n", worker_number);
32+
// Receive the package
33+
// ...
34+
}
35+
}
36+
37+
void setup() {
38+
Serial.begin(115200);
39+
while(!Serial){ delay(100); }
40+
// Create the semaphore
41+
package_delivered_semaphore = xSemaphoreCreateCounting(10, 0);
42+
43+
// Create multiple delivery truck tasks
44+
for (int i = 0; i < 5; i++) {
45+
xTaskCreate(delivery_truck_task, "Delivery Truck", 2048, (void *)i, tskIDLE_PRIORITY, NULL);
46+
}
47+
48+
// Create multiple warehouse worker tasks
49+
for (int i = 0; i < 3; i++) {
50+
xTaskCreate(warehouse_worker_task, "Warehouse Worker", 2048, (void *)i, tskIDLE_PRIORITY, NULL);
51+
}
52+
}
53+
54+
void loop() {
55+
// Empty loop
56+
}

0 commit comments

Comments
 (0)
Please sign in to comment.