|
| 1 | +/* |
| 2 | + This example demonstrates basic usage of FreeRTOS Queues which enable tasks to pass data between each other in a secure asynchronous way. |
| 3 | + Please refer to other examples in this folder to better understand usage of tasks. |
| 4 | + It is also advised to read documentation on FreeRTOS web pages: |
| 5 | + https://www.freertos.org/a00106.html |
| 6 | +
|
| 7 | + This example read data received on serial port (sent by user) pass it vie queue to another task which will send it back on Serial Output. |
| 8 | +
|
| 9 | + Theory: |
| 10 | + A queue is a simple to use data structure (in the most basic way) controlled by `xQueueSend` and `xQueueReceive` functions. |
| 11 | + Usually one task writes into the queue and the other task reads from it. |
| 12 | + 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. |
| 13 | +*/ |
| 14 | + |
| 15 | +#define MAX_LINE_LENGTH (64) |
| 16 | + |
| 17 | +// Define two tasks for reading and writing from and to the serial port. |
| 18 | +void TaskWriteToSerial(void *pvParameters); |
| 19 | +void TaskReadFromSerial(void *pvParameters); |
| 20 | + |
| 21 | +// Define Queue handle |
| 22 | +QueueHandle_t QueueHandle; |
| 23 | +const int QueueElementSize = 10; |
| 24 | +typedef struct{ |
| 25 | + char line[MAX_LINE_LENGTH]; |
| 26 | + uint8_t line_length; |
| 27 | +} message_t; |
| 28 | + |
| 29 | +// The setup function runs once when you press reset or power on the board. |
| 30 | +void setup() { |
| 31 | + // Initialize serial communication at 115200 bits per second: |
| 32 | + Serial.begin(115200); |
| 33 | + while(!Serial){delay(10);} |
| 34 | + |
| 35 | + // Create the queue which will have <QueueElementSize> number of elements, each of size `message_t` and pass the address to <QueueHandle>. |
| 36 | + QueueHandle = xQueueCreate(QueueElementSize, sizeof(message_t)); |
| 37 | + |
| 38 | + // Check if the queue was successfully created |
| 39 | + if(QueueHandle == NULL){ |
| 40 | + Serial.println("Queue could not be created. Halt."); |
| 41 | + while(1) delay(1000); // Halt at this point as is not possible to continue |
| 42 | + } |
| 43 | + |
| 44 | + // Set up two tasks to run independently. |
| 45 | + xTaskCreate( |
| 46 | + TaskWriteToSerial |
| 47 | + , "Task Write To Serial" // A name just for humans |
| 48 | + , 2048 // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);` |
| 49 | + , NULL // No parameter is used |
| 50 | + , 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest. |
| 51 | + , NULL // Task handle is not used here |
| 52 | + ); |
| 53 | + |
| 54 | + xTaskCreate( |
| 55 | + TaskReadFromSerial |
| 56 | + , "Task Read From Serial" |
| 57 | + , 2048 // Stack size |
| 58 | + , NULL // No parameter is used |
| 59 | + , 1 // Priority |
| 60 | + , NULL // Task handle is not used here |
| 61 | + ); |
| 62 | + |
| 63 | + // Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started. |
| 64 | + Serial.printf("\nAnything you write will return as echo.\nMaximum line length is %d characters (+ terminating '0').\nAnything longer will sent as separate line.\n\n", MAX_LINE_LENGTH-1); |
| 65 | +} |
| 66 | + |
| 67 | +void loop(){ |
| 68 | + // Loop is free to do any other work |
| 69 | + |
| 70 | + delay(1000); // While not being used yield the CPU to other tasks |
| 71 | +} |
| 72 | + |
| 73 | +/*--------------------------------------------------*/ |
| 74 | +/*---------------------- Tasks ---------------------*/ |
| 75 | +/*--------------------------------------------------*/ |
| 76 | + |
| 77 | +void TaskWriteToSerial(void *pvParameters){ // This is a task. |
| 78 | + message_t message; |
| 79 | + for (;;){ // A Task shall never return or exit. |
| 80 | + // One approach would be to poll the function (uxQueueMessagesWaiting(QueueHandle) and call delay if nothing is waiting. |
| 81 | + // The other approach is to use infinite time to wait defined by constant `portMAX_DELAY`: |
| 82 | + if(QueueHandle != NULL){ // Sanity check just to make sure the queue actually exists |
| 83 | + int ret = xQueueReceive(QueueHandle, &message, portMAX_DELAY); |
| 84 | + if(ret == pdPASS){ |
| 85 | + // The message was successfully received - send it back to Serial port and "Echo: " |
| 86 | + //Serial.printf("Echo: \"%s\"\n", line); |
| 87 | + Serial.printf("Echo line of size %d: \"%s\"\n", message.line_length, message.line); |
| 88 | + // The item is queued by copy, not by reference, so lets free the buffer after use. |
| 89 | + }else if(ret == pdFALSE){ |
| 90 | + Serial.println("The `TaskWriteToSerial` was unable to receive data from the Queue"); |
| 91 | + } |
| 92 | + } // Sanity check |
| 93 | + } // Infinite loop |
| 94 | +} |
| 95 | + |
| 96 | +void TaskReadFromSerial(void *pvParameters){ // This is a task. |
| 97 | + message_t message; |
| 98 | + for (;;){ |
| 99 | + // Check if any data are waiting in the Serial buffer |
| 100 | + message.line_length = Serial.available(); |
| 101 | + if(message.line_length > 0){ |
| 102 | + // Check if the queue exists AND if there is any free space in the queue |
| 103 | + if(QueueHandle != NULL && uxQueueSpacesAvailable(QueueHandle) > 0){ |
| 104 | + int max_length = message.line_length < MAX_LINE_LENGTH ? message.line_length : MAX_LINE_LENGTH-1; |
| 105 | + for(int i = 0; i < max_length; ++i){ |
| 106 | + message.line[i] = Serial.read(); |
| 107 | + } |
| 108 | + message.line_length = max_length; |
| 109 | + message.line[message.line_length] = 0; // Add the terminating nul char |
| 110 | + |
| 111 | + // The line needs to be passed as pointer to void. |
| 112 | + // The last parameter states how many milliseconds should wait (keep trying to send) if is not possible to send right away. |
| 113 | + // When the wait parameter is 0 it will not wait and if the send is not possible the function will return errQUEUE_FULL |
| 114 | + int ret = xQueueSend(QueueHandle, (void*) &message, 0); |
| 115 | + if(ret == pdTRUE){ |
| 116 | + // The message was successfully sent. |
| 117 | + }else if(ret == errQUEUE_FULL){ |
| 118 | + // Since we are checking uxQueueSpacesAvailable this should not occur, however if more than one task should |
| 119 | + // write into the same queue it can fill-up between the test and actual send attempt |
| 120 | + Serial.println("The `TaskReadFromSerial` was unable to send data into the Queue"); |
| 121 | + } // Queue send check |
| 122 | + } // Queue sanity check |
| 123 | + }else{ |
| 124 | + delay(100); // Allow other tasks to run when there is nothing to read |
| 125 | + } // Serial buffer check |
| 126 | + } // Infinite loop |
| 127 | +} |
0 commit comments