Create multiple instances of the same periodic task and period parameter

Hi All,
I am using an ESP32-wroom devkitc
I am trying to prove if changing the task name will create a separate instance of a vPeriodicTask. I have passed parameters to the task in xTaskCreate() and changed the name. the parameter sets the delay period. It always seems to only run the shortest period task regardless of which task was created first/last. Code follows :

TickType_t period1, period2;

static int i = 0;
void app_main(void)
{
     period1 = pdMS_TO_TICKS( 5000UL );		// 5 seconds
    period2 = pdMS_TO_TICKS( 3000UL );		// 3 seconds

   xTaskCreate( vPeriodicTask, "Periodic2", 1000, (void *)period2, 1, NULL);
    xTaskCreate( vPeriodicTask, "Periodic1", 1000, (void *)period1, 1, NULL);	
// Scheduler already started by the rtos system, no need to do it again.
//    vTaskStartScheduler();

	while (true) {
		// Write data to UART.
    	i++;
        printf("Hello from gt32 app_main! %u\n", i);
        vTaskDelay( pdMS_TO_TICKS( 1000UL ));
        usleep(100000);
	}
}

/*-----------------------------------------------------------*/

static void vPeriodicTask( void * pvParameters )
{
const TickType_t xDelayms = (TickType_t)(pvParameters);		
char Task_Name[20];
getTaskName(Task_Name);

// As per most tasks, this task is implemented within an infinite loop. 
    for( ; ; )
    {
    vTaskDelay( xDelayms );
    printf( "Periodic task %s.\r\n", Task_Name);
     }
}

Output is as follows :

Periodic task Periodic2.
Hello from gt32 app_main! 4
Hello from gt32 app_main! 5
Hello from gt32 app_main! 6
Periodic task Periodic2.
Hello from gt32 app_main! 7
Hello from gt32 app_main! 8

I am very new to freertos so any help is much appreciated.
Regards
Bill

Check the return value of xTaskCreate to ensure that the task creation is successful. Is usleep busy waiting? If so, try removing that.

Hello Bill,

I’m also new to FreeRTOS and my first steps have been similar to yours.

As suggested by Gaurav you should check the return value, if it’s not pdPASS it indicates that there was not enough heap memory. Since you’re creating the task “Periodic2” first, chances are high that it’s a heap problem.
You can do a quick check by exchanging the two lines creating the tasks - the text printed out should change and the also the period.

FreeRTOS provides some debugging mechanisms for this, like the malloc-failed hooks, etc and being a beginner myself I would recommend to enable them.
The FreeRTOS book and the reference manual are also very helpful, there are code examples which shows how the return values, etc are used to check if a task (or other object) has been created successfully.

Regards

Hi, Thanks for the reply. I added this code to both task create functions

	if (xTaskCreate(vPeriodicTask, "Periodic1", 1000, (void*) period1, 1,
			NULL) != pdPASS) {
		printf("Periodic1 not created successfully");
	}

And tried swapping the order of task create, but both still only print out Periodic2 every 3 seconds. Periodic1 does not get printed out every 5 seconds as I was hoping. I removed usleep();
What I really want to understand is : Does this code generate two instances of the same task or only one. If there is only one instance then I can’t see how the periodic timer can be set with two different timeout periods at the same time. If there are two instances of the timer task running then the system should wake each one up at it’s allocated time. It seems that only the task with the shortest timeout is being woken up.

I read through the Mastering freertos book fairly quickly before I started, so I have a reasonable overview of the system.
Thanks again for your replies.
Bill

Hi Guarav,
I removed usleep(); and added code to check if tasks were created successfully and it appears that both tasks were created successfully.
Thanks
Bill

1 Like

Can you tell me how to “enable them”?

You can enable the malloc failed hooks by defining configUSE_MALLOC_FAILED_HOOK.

Whats getTaskName doing, can you share the code?
Also, is the printf that’s been used here thread-safe?

I looked into the hook function stuff, all pretty straight forward, thenks.
getTaskName is a function I added to task.c to return the task name ie: “Periodic1” etc into the given buffer.

        void getTaskName(char* name)
                {
                  strcpy(name, pxCurrentTCBs[0]->pcTaskName);
                }

I assume that pxCurrentTCB[0] points to the TCB structure of the currently running task, but I am not certain of this. I guess it’s possible that it may point to the most recently created task, but the printout always shows the name of the task with the shortest timeout, regardless of whether it was created first or last, and the time between name printing is always the period of the shortest timeout. This indicated that I am getting the name of the currently running task, but never seem to run the longer task.

Also, is the printf that’s been used here thread-safe?

Not sure what this means? I am using ESP-IDF under Espressif IDE which includes Espressif’s freertos standard installation.
Thanks again for your replies.
Bill

Hi Bill,

am I correct in reading that your problem is solved now?

Just to wrap it up, then: A task instance consists of two parts: The task code (static) and the task context (dynamic). With the technique you are using, there is only one instance of the code, but two different contexts. A context roughly consists of the two parts runtime stack and TCB.

Using a single parametrized task function for two tasks doing similar things is perfectly sound and good practice. Many beginners end up copying and pasting identical code, thus keeping separate instances of the code AND the dynamic components. That approach generally leads into a dead end as all changes and bug fixes in one function need to be copied to all ther functions, also, code footprint bloats unneccessarily.

Your approach is absolutely correct. Thumbs up!

1 Like

Can you try updating your task code to the following -

static void vPeriodicTask( void * pvParameters )
{
const TickType_t xDelayMs = ( TickType_t )( pvParameters );		

    for( ; ; )
    {
        vTaskDelay( xDelayMs );
        printf( "Periodic task %s.\r\n", pcTaskGetName( NULL ) );
    }
}

@tony-josi-aws Please correct me if I’m telling nonsense :slight_smile: In short it means that calling the same function from different tasks doesn’t disturb each function call or anything else.
You wrote that you’re using a ‘FreeRTOS standard installation’ provided by Espressif. The documentation about it should tell if the underlying C library is already thread-safe or which steps must be done to get it thread-safe.

I had a similar problem with my PSoC setup using GCC & newlib and it needed to provide some lock functions and system calls (it depends on the used library). As far as I can tell using printf() works from different tasks (note that this is usually not a good approach, I did it just for testing the locks).

Regards

Hi Rac,
Thanks for the confirmation.

am I correct in reading that your problem is solved now?

In short, No, but since Friday I have got it working. Had a number of issues, I think the main one was that the getTaskName function was being called in the initial part of the vPeriodicTask() and not within the for( ; ; ) loop. I haven’t yet fully understood when the initial part is executed. Is it correct to assume that this part is executed only once as the task is created, and then it stays within the for( ; ; ) loop?
Other issues were not enough stack allocation in the vCreateTask functions. I would have thought 1000 bytes would be plenty. I don’t like using printf within the periodic task so I sent the task name via a queue to a print task name function, but if I enable the vQueueSend() function it seems to completely upset things again and clobber one of the periodic tasks. Code now is as follows :

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
//#include "esp_hw_support/include/rtc_wdt.h"
#include "sio_esp32.h"
//#include "semphr.h"

static const char *TAG = "gt32";

static void vPeriodicTask(void *pvParameters);
static void vPrintTaskName(void *pvParameters);

extern void init_uart1(void);
extern void init_uart2(void);

TickType_t period1, period2;
QueueHandle_t Q_periodic = NULL;

static int i = 0;

void app_main(void) {
	esp_log_level_set(TAG, ESP_LOG_INFO);
	//   rtc_wdt_protect_off();
	//   rtc_wdt_disable();
	period1 = pdMS_TO_TICKS(5000UL);		// 5 seconds
	period2 = pdMS_TO_TICKS(3000UL);		// 3 seconds
//	init_uart1();
//	init_uart2();
	// Create the task that will run periodically 
	if (xTaskCreate(vPeriodicTask, "Periodic2", 2000, (void*) period2, 1, NULL) != pdPASS) {
		printf("Periodic2 not created successfully");
	}
	else{
		printf("Periodic2 task created successfully\r\n");
	}
	if (xTaskCreate(vPeriodicTask, "Periodic1", 2000, (void*) period1, 1, NULL) != pdPASS) {
		printf("Periodic1 not created successfully");
	}
	else{
		printf("Periodic1 task created successfully\r\n");
	}
	if (xTaskCreate(vPrintTaskName, "PrintTaskName", 1000, NULL, 1,
			NULL) != pdPASS) {
		printf("PritTaskName not created successfully\r\n");
	}
	else{
		printf("PritTaskName task created successfully\r\n");
	}
// Start the scheduler so the created tasks start executing.
// Scheduler seems to be already started by the rtos system, no need to do it again.
//    vTaskStartScheduler();

	while (true) {
		rtc_wdt_feed();
		i++;
		printf("Hello from gt32 app_main! %u\n", i);
		vTaskDelay(pdMS_TO_TICKS(1000UL)); // This delay seems to be pretty accurate
	}

}

/*-----------------------------------------------------------*/

// 1mS granularity, called every second if xDelayms = 1000
static void vPeriodicTask(void *pvParameters) {
	const TickType_t xDelayms = (TickType_t) (pvParameters);//pdMS_TO_TICKS( 5000UL );		// 5 seconds
	char Task_Name[20];
	Q_periodic = xQueueCreate(5, sizeof(Task_Name));
	if(Q_periodic == NULL){
		printf("Failed to create Q_periodic\r\n");
	}

//getTaskName(Task_Name);
//xQueueSend(Q_periodic, (void *)Task_Name, (TickType_t)0);

	/* As per most tasks, this task is implemented within an infinite loop. */
	for (;;) {
//		strcpy(Task_Name, pcTaskGetName(NULL));
	getTaskName(Task_Name);
//	xQueueSend(Q_periodic, (void *)Task_Name, (TickType_t)0);
	
		printf("Periodic task %s.\r\n", Task_Name);
		vTaskDelay(xDelayms);
	}

}

Thanks to all for the replies. Any suggestions as to why the vQueueSend() seems to stop the task running would be much appreciated.
Regards
Bill

The pcTaskGetName(NULL) seems to work well, thanks for that hint.

strcpy(Task_Name, pcTaskGetName(NULL));

Regards Bill

Glad that you made it work. Thank you for reporting back.