LOGBOOK

HELP

Quiz Entry - updated: 2026.10.13

How does a single print_task function serve as two different FreeRTOS tasks, and what must be true of the argument passed to it?

xTaskCreate takes a void * parameter that FreeRTOS hands to the task function on start; the function casts it back (here to char *) and prints it, so two tasks created with different strings print different messages. The data it points to must outlive the task, so it has to be global, static or heap memory, never a local variable of the creating function.

void print_task(void *args) {
    char *task_name = (char *)args;      /* recover the argument */
    while (true) {
        printf("Hello from %s\n", task_name);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

char *task_a_param = "Task A";            /* global: lives forever */

void app_main(void) {
    const uint16_t stack_depth = 4096;    /* bytes on ESP-IDF */
    xTaskCreate(&print_task, "taska", stack_depth, task_a_param, 10, NULL);
    xTaskCreate(&print_task, "taskb", stack_depth, "Task B", 10, NULL);
}

A task is the function plus its own stack and its own argument, so one function can run as many tasks at once, each with separate local variables. void * is C's way of passing "a pointer to anything": the kernel doesn't need to know the type, and the task function knows what to cast it to.

The lifetime rule is the classic bug. xTaskCreate returns immediately, and on ESP-IDF app_main may then return too, which frees its stack. If you had passed &some_local_variable, the task would later read memory that now belongs to something else. A string literal or a global lives for the whole program, so it is safe.

Go deeper:

From Quiz: SIOT / ESP32 Development with ESP-IDF and FreeRTOS Tasks | Updated: Oct 13, 2026