Decision making : how many tasks , response times?

I’ve been observing various FreeRTOS projects and I am interested in understanding how the decision-making process for the number of tasks and their guaranteed response times decide.

To clarify, I’m interested in this context:

imagine a simple example of a toy car project controlled by a remote. The car receives commands from the remote and adjusts its position and speed accordingly.

I’ve divided it into two tasks and assumed their response times based on my knowledge.

Task 1 (Receiving Commands from the Remote):
Acceptable Response Time: Less than 100 milliseconds.

Not Acceptable Response Time: Exceeding 100 milliseconds can lead to delayed recognition of user commands.

Task 2 (Controlling Car’s Position and Speed):
Acceptable Response Time: Ideally less than 100 milliseconds for real-time control.

Not Acceptable Response Time: Response times exceeding 100 milliseconds may hinder precision and user satisfaction.

My goal is to understand decision making on how many tasks , response times, and task priorities in FreeRTOS projects. I’m starting with a simple toy car project controlled by a remote and will expand to more complex features as I understand simple first.

What is your target platform, in particular, processor speed? What is the communication protocol over the remote connection? Will those factors allow your deadlines in the first place?

My first comment is you will need to better define what you actually want to do. Tasks really can be an implementation detail, so you need to know what you are implementing. “Controlling Car’s Position and Speed” is an abstract operation (and a complex on at that) so can’t really be allocated to a “task”. It also is likely “complex” and deals with multiple things being done.

Speed is likely controlled by the speed of the motor that drives the drive wheels, and depending on the type of motor may have very different timing requirements (and 100 milliseconds is likely too long for most of them.

If you are really thinking of controlling “Position” that is a complicated task. If you mean just steering, then it becomes something similar to the speed control and be a simple loop, again the timing requirements become a function of how the loop is actually built.

For these loops, the simplest case would be a DC Motor for speed and a DC servo for steering, where you just apply a voltage for the desired command, and the motor/controller does all the work, then the system could be just a simple get a command from the remote as to what level to put on each, and set the voltage to that, and you only really need one task (and maybe don’t even need a real time system at all).

If you need to be generating the actual signals to a more complicated motor, then you may need a much faster loop.

I will also point out that if the user is part of the “control loop” for the car, 100 millisecond response time will be too slow for many users. That sort of timing is normally good enough to run a GUI-like UI, but not a feedback loop. It might “work”, but many people will notice the lag.

I didn’t went too deeply into it, but from what I’ve read in books and documents, it’s advisable to assign high priority to tasks that are more important in your task list. Also, setting deadlines for each task helps the scheduler determine when and which task should run on the processor

I created hypothetical scenarios to understand how we make decisions about how many tasks, deadlines, and priorities for a project. I thought that using the toy car project as an example was a useful way to explore decision-making processes.

I thought it’s always good start by thinking the fundamental aspects: the number of tasks involved, the deadlines for each task, and the order of their priorities.

It seems like I might have asked my question incorrectly and didn’t express what I intended to ask.

Schedulers can use a number of different methods. FreeRTOS uses a strict priority scheduler, always running the highest priority ready task, and (optionally) rotating between ready tasks of equal priority. As such, “deadlines” mean nothing to it, and there is no way to actually tell FreeRTOS what deadlines you have. Some other scheduling algorithms might use deadlines, and give effective priority to tasks with approaching deadlines. With FreeRTOS, you might use your analysis of relative deadlines, and the work needed to be done, to figure out what priorities to assign.

In my view, you can’t start to think about the division into tasks until you have a clear definition of what actually needs to get done. Division into tasks is answering a How, but until you know the What, you can’t really work on How. (Knowing possible Hows can help shape deciding on your Whats, but you need to keep the horse before the cart, and decide what you need to do before spending a lot of time on How to do it.)

My point is that the details about tasks and their priorities is a LATER decision than working out a description of what needs to be done, because it is shaped by those details.

Does the project description below address the points you were trying to explain previously?

Project Objective:

The primary objective of this project is to design a system that allows precise control of an LED’s blink speed based on commands received over UART.

The core objectives include:

LED Blink Speed Control:

Slow Blink: Upon receiving the command ‘A’ over UART, the system will adjust the LED blink speed to a slow rate, specifically set at 1000 milliseconds (1000 ms) per blink.

Medium Blink: In its default state, when no commands or unrecognized commands are received, the system will maintain the LED blink speed at a rate of 500 milliseconds (500 ms) per blink.

Fast Blink: Upon receiving the command ‘B,’ the system will decrease the LED blink speed to a fast rate, precisely 300 milliseconds (300 ms) per blink.

Yes, that is a detailed description of “What” needs to be done. There will be some finer details about exactly how you turn on and off the LED, presumably a GPIO port. And from that, you can start to work on dividing the program into tasks.

I see that we need to create at least two tasks, one for UART to send commands and the other for the LED to receive commands and control blink speed based on those commands.

Now, I want to understand what happens if I want to turn off the LED when a user presses an emergency button.

Do we need to create a third task, or should we set up an interrupt?

Where was “Send Commands” listed as an operation to be performed? The only operations listed were “Receive a command over a UART” and “Blink LED” (and then in your addedum, “Detect Button Press”).

I call these “Operations” and not “Tasks”, as we haven’t yet allocated the operations to tasks. Sometimes you can put multiple operations in a single task, and sometimes a single operation breaks down into multiple tasks, so we haven’t defined “Tasks” yet (in the FreeRTOS sense).

Because things are simple enough, this project COULD be done with the simple “Big ol’ Loop” of code that checks for characters coming in on the UART, and if so processes the command, and then checks if it is time to change the LED and if so do so, and then just continue looping doing steps as they appear.

A second approach might see that the serial commands, being very simple, could all just be programmed in the UART Rx ISR, and perhaps the LED blinking could be done in a timer ISR, either fixed frequence interrupts with a counter, or adjusting the timer rate.

Finally (although there are still more options past this) we could adopt a classical RTOS structure where we have one task that handles the UART command processing, reading from a message queue of some sort filled by a UART ISR. Then we have a second task to handle the LED blinking.

For this second task, FreeRTOS includes a built in “Timer Task” that can do much of the work, needing just a timer callback function that turns the LED on and off as the timer expires.

When you add your extra option, the stop button, the best choice is to make it generate an interrupt, otherwise you need to have sometime periodically poll the button, which is wasteful of resorces. That polling could be a third task, or it could be built using another “Timer” and Timer call back function. With an interrupt, the ISR could do the processing, and make the “Emergency” stop happen quicker. “Emergency” tends to imply an ISR, if not handled directly in hardware.

1 Like

Apologies for the confusion. system can indeed ‘Receive a command over a UART’ and ‘Blink LED.’

Yes, I am agree with you that both operations, ‘Receiving a command over a UART’ and ‘Blinking LED,’ can be combined into a single task.

However, I’m thinking about the decision-making process for task separation.

Let’s consider a scenario where receiving commands within 10 ms is more importance due to a strict deadline. In such cases, I believe it would be necessary to create two separate tasks—one for receiving commands and another for blinking LEDs.

If we attempt to handle both in a single task, we might risk missing commands. Please correct me if I’m going in the wrong direction regarding the number of tasks

The “Big ol’ Loop” method (which is non-rtos) can be performant if none of the steps take long. Note, none of your steps take any significant amount of time, so it can surely easily make a 10 ms response time. “Big ol’ Loop” does blinking by marking time stamps for when the next LED change would occur, and doing an action when that happens, so this will only cost microseconds of time. The problem with the “Big ol’ Loop” is that it doesn’t scale well, and starts to break once you need to add a task that will take significant time to perform.

Now, once you decide that an RTOS-style design is to be used, that will make you want to separate the actions, so one task is waiting on the UART, and the second is waiting on Timer Ticks to do its operations.

The key point is that rarely is their only a single way to do something, but you normally have options. Different options may be better in different ways (and some options might not be better in any way, except maybe what you are used to doing). The “Big ol’ Loop” option typically generates a smaller program for simple sharing (not needing the code for the RTOS) but doesn’t scale well if adding new features that might get more complicated. The RTOS-Style code may have less “User” code, and is easier to modify. With FreeRTOS, using the Timer task can reduce your code complexity, but does mean learning more of the API. It may be simpler (at first) to just code your own task for the LED flashing, but then you will need to debug the various “races” you accidently program into your code. (The FreeRTOS Timers may become very useful in future additions).

Indeed, there can be various solutions to the same problem, whether using a Real-Time Operating System (RTOS) or a non-RTOS approach. I’m interested towards adopting an RTOS-style coding approach for this particular case.

I considered this specific scenario

: If ‘received == ‘A’’ triggers actions like turning the LED ON, delaying for 1000 ms, turning the LED OFF, and delaying for 1000 ms, all within a loop,

it doesn’t leave sufficient time for a fast UART polling mechanism. This setup may lead to UART data loss, especially when there’s a 10 ms deadline for receiving UART data.

Right, so blinking the LED by a delay loop is just the wrong answer. And in fact, is totally broken as to blink the LED that way, you need to STAY in the loop to keep it braking.

The “Big ol’ Loop” method would be polling in the loop with test like:

Is there a character in the UART, if so, receive it and process it.
Is it time to change the LED, if so, do that
repeat the loop.

The steps for processing blink rates and blinking the LED set variables so that the test of “Is it time to change the LED” will activate at the right time.

The RTOS method, using Timers, would be something like a single user task that BLOCKS waiting for a character received by the UART ISR, then getting that character and setting the Timer to the appropriate blink rate.

The Timer Callback would then check the current state of the LED and invert it.

In an RTOS style, you NEVER “Poll”, but should always be blocking waiting for an interrupt. The only exceptions would be very short “micro-waits” were the response will be ready in time comparable to ISR entry and rescheduling timing, or “polling” that actually needs to do blocking I/O, or is done with a moderate length block in the loop to run other tasks.

As you agree, achieving the goal can be done in multiple ways, such as using a single task with UART ISR (Interrupt Service Routine) and a timer or using two tasks. I’m particularly interested in exploring the two-task approach because I believe it would provide me with a better understanding and help clarify many doubts.

consider we have two tasks: one UART task to receive commands every 10 ms, and another LED speed control task that adjusts the speed based on received commands. I would assign high priority to the UART task because we can’t afford to miss the deadline for receiving commands. I would assign low priority to the LED speed control task.

In this scenario, the UART task runs first and then goes into a waiting state for 10 ms until the next command is expected. During this 10 ms period, the second task, the LED speed control task, runs. However, it’s a low-priority task, which means it’s allowed to run for only 10 ms before being interrupted by the high-priority UART task.

I understand when task one goes from the running state to the blocked state, as well as when task two goes into the running state. However, what I’m having trouble understanding is when will task two transitions from the running state to the blocked state?. It should block for a specific duration to give another task a chance to execute

Blocking only happens when a task calls a FreeRTOS function that causes the task to block. One function you can use is vTaskDelay, which will block for a specific time period. Another option is to block on a semaphore or a Queue to wait for it to be signaled.

A Task waiting for data on a UART should NOT be checking the UART every x ms, but should block on either a queue which the UART ISR puts received characters into, or waits on a semaphore that is signaled by the ISR when it has but a message into a buffer.

“Polling”, by periodically checking for data is NOT a normal design method in a RTOS style program. This is because tasks should be written so the BLOCK until they have something to do. A loop with a delay then a check is a last resort to be done for things that can’t generate an interrupt.

1 Like

Why did you suggest either semaphore or a queue? In situations where we need both resource protection and the ability to send and receive data between two tasks.

Mutex and semaphore are tools used to protect shared resources when multiple tasks need access to them. On the other hand, a queue is not used to protect resources but rather for inter-task communication. Message queues are primarily used for inter-task communication, allowing tasks to send and receive data

Mutex is for shared resource protection. Semaphore is a signaling (events) tool.
A queue is handy when serving e.g. UART HW interface as fast as possible (far better and much more reliable than polling) in a ISR to avoid dropped data/overflows.
Just putting the data from the HW into a queue a post processing task is receiving from resp. is blocked on waiting for data to arrive allows short ISR code (good) and helps to decouple serving the HW in time from more relaxed and comfortable processing the received and enqueued data by a task.

1 Like

What “Two tasks”? The Queue or Semaphore (with a buffer) was the technique for the task to communicate with a serial port.

There was NO resource protection requirement inherent in your problem, you had a UART bringing in commands, and a blinking LED going out (and later a push button).

While Queue can send data between tasks, they also work well between a task and an ISR.

Task 1: Receive UART Data and Store
Task 2: Parse Data and Adjust LED Speed

I am sorry but I think I may have lost track due to multiple solutions being suggested.
To clarify, are you referring to both UART reading commands and LED speed control as two separate tasks, or are they both part of one task?

I was talking in general, in general ALL tasks should be blocking when they don’t have something to do until they do have something to do. This blocking can be done with vTaskDelay for a fixed time period, or by waiting on a primitive, like a Semaphore, Queue, or Stream Buffer.

The UART Command reception task will likely be using a Queue to receive data from UART ISR, which is getting the data from the UART device. Your description of “Wait 10 ms then check if a command is waiting” just telegraphs a misunderstanding of proper program structure or misunderstanding of how Real Time programming works.

Why wait a fixed time and check if something is there, when you can just wait for that thing to happen?

When I said:
Blocking only happens when a task calls a FreeRTOS function that causes the task to block. One function you can use is vTaskDelay, which will block for a specific time period. Another option is to block on a semaphore or a Queue to wait for it to be signaled.

That was a GENERIC statement about ALL tasks, that, in general, tasks should be designed around blocking, and that blocking could be on a number of things. Until you understand WHY we want to be blocking, and not just polling, deciding on HOW to block (or WHAT to block on) for a given case should be the focus.

“Response Time” specs do NOT imply polling periods, because polling is the last resort, and if you DO need to poll, the polling period needs to be FASTER than the response time, or you may have no time to actually respond.

1 Like