When to create a task?

Hi, what criteria do you follow to create tasks?, I would not want to create a system too complex, My idea is an application were a microcontroller gets GPS data and tx/rx MQTT messages using a cellular/gnss module.

GPS: data is read every second (task)

cellular module:
MQTT tx: every 10 minutes (task)
MQTT rx: random (task ?)

the MQTT rx is user dependent, when the user wants he sends data from a server, so is not clear for me if I should create a task for this action that is not periodic, used only a few times in the day and could waste system resources and have a task blocked most of the time seems like an useless task, so these are my options:

task_read_gps()
{
  //do something
  vTaskDelay(pdMS_TO_TICKS(1000));//blocked for 1 sec
}

task_check_rx_mqtt()
{
  //do something
  vTaskDelay(pdMS_TO_TICKS(1000));//blocked for 1 sec
}

task_tx_mqtt()
{
  //do something
  vTaskDelay(pdMS_TO_TICKS(600000));//blocked for 10 min
}

or

task_read_gps()
{
  //do something
  vTaskDelay(pdMS_TO_TICKS(1000));//blocked for 1 sec
}

task_cellular()
{
    if(check_rx_mqtt())
    {
    //do something
    }
    
    else if ((HalGetTick() - timestamp) >= 600000)//waits for 10 min
	{
		timestamp = HalGetTick();
		tx_mqtt();
	}

    vTaskDelay(pdMS_TO_TICKS(1000));//blocked for 1 sec
}

what would be the best choice?

First comment, an idle task that is blocked doesn’t consume any resources other than a bit of memory, and if you are running MQTT, you likely are not short on that unless you are pushing the processor to run it.

Second, building a program based on blocking for an interval, and then seeing if there is data to do something with is generally not the best use of resources, as it will add a lot of latency to the requests.

Most GPSes will send a message every time they get a data update, so if you set a task to block on getting that message, processes it, then block again, is much better than waiting a second, then seeing if you got a message, and if so, processes it. You might as well process the data right when it comes in.

The same for the mqtt reception. rather than blocking for a second, and then seeing if something is there, use a blocking read that makes the task wait until there is something to do. That way, you respond to the request quickly.

The mqtt transmittion is perhaps another question. The first point is that whatever is the source of the data being transmitted, is best to be in control of that transmittion, rather than just hard coding a fixed 10 minute delay period. It might make sense to make an early update if something major changes, which can’t be don’t with a fixed update frequency.

If the updata can be done quickly, in the interval between data aquisitions, then the collecting task can just do the update in its loop. If the update might take a while for some reason, then make it a seperate task so it doesn’t block the data collection, but make it triggered by an event from the data source.

Hi,

Sorry for the delay, and thanks for reply, I will start with the easy part

this is exactly how is done, each sensor updates the variable were its data is saved after every read and the task_tx_mqtt() only takes those variables to create the message to be sent to the server

Now, about the task_read_gps() and task_check_rx_mqtt():

But, use a blocking read for each of those tasks is valid only if they have the same priority right?, because for example if task_check_rx_mqtt() has lower priority it must wait until task_read_gps() gets the data to run

Why do you think they need the same priority? They are blocking on different things. The GPS task blocking for data on the serial port with the GPS, and the mqtt on its source. While the GPS is blocked on its serial port, it won’t stop the mqtt from reading its data. That is the advantage of BLOCKING rather than spin waiting for data. If your serial port driver is just spin waiting for data, THAT needs to be fixed, It should be using an interrupt, and the task using a blocking primitive (like a queue or a semaphore if your ISR gathers a whole message) waits without using processor for the data. The two tasks aren’t sharing the same serial port for some reason are they?

Hi,

correct, each device is using its own serial port

Sorry, I think I have a “terminology issue” as mentioned in this post:

I associated blocking with a function that is in a loop waiting for data and only breaks the loop after get it, thanks

1 Like

No, that is polling, which you should not do. Blocking happens when a task operates on a FreeRTOS object that isn’t ready, and the scheduler “blocks” the task (just like it did a delay) for the amount of time specified, or until the object becomes ready, which ever comes firsts. Tasks that are blocked use to CPU time.

Ideally, tasks should NEVER “loop waiting for data” as that is just wasting resources, but the I/O device supplying data should generate an interrupt, and the ISR can then somehow notify the waiting task. For serial ports, I have the ISR gather the data. Generic interface puts it into a queue. A higher speed interface may fill a buffer and signal the task when the end of message arrives to save activations after each character.