Suspended task activity

Just because the Arduino libraries does things, doesn’t make it right. The Arduino system hides a number of things from you so you might not actually know what is happening.

There is no way to just use FreeRTOS for “part” of a project. Once you start the FreeRTOS scheduler, it does NOT return to the main routine to run the “loop” function. Arduino might if you turn on a FreeRTOS mode just automatically create a task to put the loop function into, so that may be a task that you don’t know about, and you are just fully using FreeRTOS but don’t realize it.

I will state clearly that suspending one task from another is almost always a source of problem, as you do not know what that suspended task was doing at that exact moment, and thus do not know what resources it might have claimed. Also, trying to resume a task the suspends itself needs great care to make sure you don’t accidentally resume the task before it actually suspended itself, and thus the resume gets lost.

Except in very special cases, it is much better to have the task block on a Semaphore or a Notification or the like, and have the other task signal that.

Often your method “works” most of the time, but then at some point you hit a corner case and things just fall apart.

Another thing to remember, unless you have a multi-core processor, the process will only be doing one thing at a time anyway, so spinning up a task to do compute bound operations inside a compute bound task doesn’t actually help you, but perhaps using a timer to schedule them at the required times might be useful.

I believe the loop() is called by the Arduino main, not by the RTOS idle task but I hae no verified it. Thanks for your input - rick

//**** Here is a task that waits for a semaphore
void Task_2(void *pvParameters)
{
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = 409;
    for (;;)
    {
        xSemaphoreTake(xSemaphoreHandle, portMAX_DELAY);

        // Initialise the xLastWakeTime variable with the current time.
        xLastWakeTime = xTaskGetTickCount();

        //  Wait for the next cycle.
        vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(xFrequency));

        Serial.print("2");
    }
}
//* Here is the arduino loop that Arduino normally calls from their main()
void loop()
{
    // here is where you'd put code that needs to be running all the time.

    // blinking an led
    loopCounter++; // a debug inspired counter
    if (!(loopCounter % 14997))
    {
        if (lightOn)
        {
            lightOn = false;
            digitalWrite(13, HIGH);
        }
        else
        {
            lightOn = true;
            digitalWrite(13, LOW);
        }
        // a keystroke is used to signal the task from a loop
        if (Serial.available())
        {
            uch = Serial.read();
            if (uch == 'a')
            {
                Serial.println("give");
                xSemaphoreGive(xSemaphoreHandle);
            }
        }
    }
}

Richard,
All I can say is that I have no problem running an arduino loop() and within it make calls to resume a task that suspends itself after at the bottom of a forever loop. The arduino loop periodically calls the suspended task and it runs. It looks like I can use FreeRTOS for “part” of a project.
Perhaps I’ll regret it… Perhaps not.
I ran it all last night, Over a billion loops and calling the task every 30,000 times through. (about once every second). I even tried to break it by adding a keystroke to call the same function. So if you resume a task that has already resumed it appears to ignore it.
During this test I also had three other tasks active.
One task just runs every 277ms. Another task runs every 409ms and waits on a semaphore
The task that runs from a background resume gives the semaphore
Another task suspends itself using a global flag that tis set in one of the other running tasks.
Now for my use, I only need to resume one task at a time.
It appears to me that it will work.
If it doesn’t I will have to move everything into the loop and count cycles.
Please remember that I am not suspending a task from another. the task suspends itself.
Again, thanks for your input

Which means that the FreeRTOS interface in the Arduino library makes a task to run loop(), so it being part of FreeRTOS is just hidden from view.

If task suspend themselves, then the issue is that if you send the resume before the task gets to the suspend instruction, that resume will just get thrown away and the task will suspend until you resume it again.

Maybe now, the task always completes before this happens because you have so much slack that it isn’t a problem, but someday it may be. Now, maybe you WANT that if the task doesn’t complete in time you want the next run to be skipped, then what you are doing is ok,

If you want it to run right way if you fall behind, replace the suspend with a wait for a direct-to-task notification, and the resume with sending that direct-to-task notification.

That is incorrect and should be clear if you read my previous response and take a look at this link I shared before - https://github.com/feilipu/Arduino_FreeRTOS_Library/blob/master/src/variantHooks.cpp#L76

This again is misunderstanding and @richard-damon has clarified it multiple times.

When do you create Task_2 and xSemaphoreHandle? What is the priority of Task_2? Can you share that code snippet too?

Can you try simplifying your definition of the loop function to the following -

void loop()
{
    // here is where you'd put code that needs to be running all the time.

    // blinking an led
    loopCounter++; // a debug inspired counter
    if (!(loopCounter % 14997))
    {
        if (lightOn)
        {
            lightOn = false;
            digitalWrite(13, HIGH);
        }
        else
        {
            lightOn = true;
            digitalWrite(13, LOW);
        }
        
        if( xSemaphoreHandle != NULL )
        {
            xSemaphoreGive(xSemaphoreHandle);
        }
    }
}

This is just to narrow down the problem and confirm that you are really calling xSemaphoreGive.

Task 1 and 2 are created in Arduino Setup. I will eventually make these static creates

xTaskCreate(Task_1, "Task1 ", 128, NULL, 3, &Task_1Handler); //
xTaskCreate(Task_2, "Task2 ", 128, NULL, 3, &Task_2Handler); //

I create the semaphore in task1

void Task_1(void *pvParameters)
{
TickType_t xLastWakeTime;
TickType_t xFrequency = 277;

/* Create a binary semaphore without using any dynamic memory allocation. */
xSemaphoreHandle = xSemaphoreCreateBinaryStatic(&xSemaphoreBuffer);
xSemaphoreTake(xSemaphoreHandle, 0);

for (;;)
{
// Initialise the xLastWakeTime variable with the current time.
xLastWakeTime = xTaskGetTickCount();

// Wait for the next cycle.
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(xFrequency));

Serial.print("1");
}
}

Here is task 2 which successfully gets called from a third task but not from the loop()

void Task_2(void *pvParameters)
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = 409;

for (;;)
{
xSemaphoreTake(xSemaphoreHandle, portMAX_DELAY);

// Initialise the xLastWakeTime variable with the current time.
xLastWakeTime = xTaskGetTickCount();

// Wait for the next cycle.
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(xFrequency));

Serial.print("2");
}
}

I’m going to set up a test although this failed for me before. I sure hope it works. I have no problem admitting that I’m wrong. I mean we’ve all been there.

  1. I’ll run task one with a q to initialize the semaphore output 111111
    I2. I’ll stop task 1 by issuing a ‘w’ 111 stop
    idles should continue
  2. I’ll issue a ‘z’ which starts a task that gives the semaphore
    Here’s the output
    T

As you can see task 2 only ran after I issued a ‘z’ . That task not listed here but runs in a while loop giving up the semaphore

I checked out your link and it appears that sure enough the RTOS calls the Arduino loop. I was told above by someone that this couldn’t be true but it is. I would like the Arduino loop to be able to issue the semaphore since for release I do not plan on using more than one task at a time. Since this is the better way, how come it does not work for me.

Perhaps you now have more information on what I am seeing.
thanks for your input

Rick

Do you mind sharing the complete code with everything including key handing and all the tasks? May be push it to a repo and share link?

In addition the tasks stack size of just 128 could be too small since you’re using Serial.print and print(f) functions usually required much more stack.
Did you enable stack checking and properly defined configASSERT?

To be honest, I really do not know how to push the code to a repo. Also, I’m testing out several devices like heaters, valves, pumps, stepper motors all of which have been commented out for this demo. All are a work in progress.
I can put together all my task related initializations and all four tasks but I don’t think that would add to much. I appreciate you interest. If there is something specific you want to see let me know.

Rick

I’ve been using the highwatermark and printing out the stack usage. I’ve used different stack sizes as well. The highwater mark returns values in the 40’s 50’s and higher depending on the task. I’ve have added Serial.println s to the tsk to see the difference. Ther will be no prints in the release. One task which printed out the high water mark ran all niight a few days back and changed the output from 52 to 48.
Still, I don’s see how this could effect the ability to give a semaphore from the Arduino loop() to a task
I’ll look into the stack checking that you gave me the link to for the future - thank you

It is my understanding that a stack size of 128 on an 8 bit processor gives 128 bytes while on a 32 bit processor would amount to 128x4 since the word size is 32bits and not 8. Is this assumption correct?

Thanks

Rick

To be precise there is a portSTACK_TYPE / StackType_t in portmacro.h which is contained in the port used.

Can you write one complete file showing the issue which I can use to repro it?