Blinking multiple error-code LEDs at different rates within single task without locking

Hi,
I have many LEDs indicating various status and errors. They all blink at different frequencies, different number of blinks, different on states and different off state. And for each error I have a flag.

I need to implement all that functionality in a single task and without locking.

Any tips on how to implement it efficiently and elegantly?

Thank you :slight_smile:

When confronted with similar behaviors in the past I have implemented a set of simple state machine functions to manage the blink state of each led. These functions would not block and would check the current system tick counter and the led state to determine the next required state.

there are certainly many ways to do this but here is one idea:

typedef enum {NEEDS_OFF, NEEDS_ON, NEEDS_BLINK} led_need_t;

#define BLINK_ON_TIME 500 // ticks
#define BLINK_OFF_TIME 250 // ticks

void led_FSM_on_off_blinker(led_need_t theNeed)
{
    static enum{OFF, ON, BLINK_ON, BLINK_OFF} ledState;
    static lastBlink = 0;

    switch(ledState)
    {
        case OFF:
            gpio_led_off();
            if(theNeed == NEEDS_ON)
            {
                ledState = ON;
            }
            if(theNeed == NEEDS_BLINK)
            {
                ledState = BLINK_OFF;
            }
            break;
        case ON:
            gpio_led_on();
            if(theNeed == NEEDS_OFF)
            {
                ledState = OFF;
            }
            if(theNeed == NEEDS_BLINK)
            {
                ledState = BLINK_ON;
            }
            break;
        case BLINK_OFF:
            gpio_led_off();
            if(xTaskGetTickCount() > lastBlink + BLINK_OFF_TIME)
            {
                ledState = BLINK_ON;
            }
            if(theNeed == NEEDS_OFF)
            {
                ledState = OFF;
            }
            if(theNeed == NEEDS_ON)
            {
                ledState = ON;
            }
            break;
        case BLINK_ON:
            gpio_led_on();
            if(xTaskGetTickCount() > lastBlink + BLINK_ON_TIME)
            {
                ledState = BLINK_OFF;
            }
            if(theNeed == NEEDS_OFF)
            {
                ledState = OFF;
            }
            if(theNeed == NEEDS_ON)
            {
                ledState = ON;
            }
            break;
    }
}

void ledTask(void *parameters)
{
  led_need_t led_needs = NEED_BLINK;
   while(1)
   {
      led_FSM_on_off(led_needs);
   }
}

Thank you Joseph! :slight_smile:

I might do all that with Timers, so you only have the Timer task handling everything.

That is potentially much simpler, because you can schedule the timer callback to a reasonable rate for updating the LED state. You would still use the LED state machine functions and just make each one a timer callback running at a suitable rate. Take a look here for a timer example:

Iā€™ll second Richardā€™s suggestion, the only potential drawback being that your LED logic in that scenario also ā€œcompetesā€ with all other FreeRTOS software timers. If all behave well (ie donā€™t starve the timer chain), it wouldnā€™t be a problem. but if you really need a dedicated task for only your LED timers, you could clone and strip down FreeRTOSā€™s timer task to act ask your LED control task. After all, FreeRTOS software timers pretty much are a generic solution for exactly your problem.

Thank you all,
because every LED has also a sequence of error codes depending on how many errors have been assigned to that LED, I assume going with the software timers I would then:

  1. create one software timer for each LED
  2. create a state machine within each software timer callback which then takes care of sequencing through all the error codes one after the other

Is that the correct way of doing it?

Thank you as usual! :slight_smile:

That would work perfectly. It would also be easy to develop because you can do each LED one at a time.

Yes, I would have each LED on a separate software timer that has a simple state machine (maybe even just a sequence list) that defines its blinking. Software times are ā€˜cheapā€™ enough that there is no need to add the complexity to decide which LED needs the next servicing to combine them.

Thank you both.

I created the timers, stated them but it gives the ā€œundefined referenceā€ error further below.

I have included the timers.s and other freeRTOS .h files but still gives undefined referenceā€¦

#include "freeRTOS.h"
#include "queue.h"
#include "task.h"
#include "timers.h"

TimerHandle_t xLEDTimer_ErrorLED;
TimerHandle_t xLEDTimer_HostLED;
TimerHandle_t xLEDTimer_TestLED;

void CreateLEDsSoftwareTimers()
{
    xLEDTimer_ErrorLED = xTimerCreate(  "xSoftTmr_ErrorLED",
                                        100,
                                        pdTRUE,
                                        ( void * ) 0,
                                        CBK_LEDProcessing_ErrorLED);

    xLEDTimer_HostLED = xTimerCreate(   "xSoftTmr_HostLED",
                                        100,
                                        pdTRUE,
                                        ( void * ) 0,
                                        CBK_LEDProcessing_HostLED);
                                        
    xLEDTimer_TestLED = xTimerCreate(   "xSoftTmr_TestLED",
                                        100,
                                        pdTRUE,
                                        ( void * ) 0,
                                        CBK_LEDProcessing_TestLED);
}

void StartLEDsSoftwareTimers()
{
    xTimerStart(xLEDTimer_ErrorLED, 0);
    xTimerStart(xLEDTimer_HostLED, 0);
    xTimerStart(xLEDTimer_TestLED, 0);
    
}

I am getting these errors for each xTimerStart() inside the in the StartLEDsSoftwareTimers():

build/default/production/_ext/1360937237/panel_UI_control.o: In function `StartLEDsSoftwareTimers':
d:/dropbox (tdl)/tdl design/rational/firmware_2021_op60_rtos/firmware/src/panel_ui_control.c:113: undefined reference to `xTimerGenericCommand'
d:/dropbox (tdl)/tdl design/rational/firmware_2021_op60_rtos/firmware/src/panel_ui_control.c:114: undefined reference to `xTimerGenericCommand'
d:/dropbox (tdl)/tdl design/rational/firmware_2021_op60_rtos/firmware/src/panel_ui_control.c:115: undefined reference to `xTimerGenericCommand'

What is causing it?

Thank you

Is timers.c being compiled for the project?

do you have
#define configUSE_TIMERS 1
in FreeRTOSConfig.h?

It was the configUSE_TIMERS 1 that was set to 0.

But now the following error came up but cannot find any documentation on vApplicationDaemonTaskStartupHook() or where it is located.

Do I need to include some other .h or some other variable/#define?

Also generally, do I need to either create or start the timers AFTER some freeRTOS initialization of some sort?

Error:

build/default/production/_ext/404212886/timers.o: In function `prvTimerTask':
d:/dropbox (tdl)/tdl design/rational/firmware_2021_op60_rtos/firmware/src/third_party/rtos/freertos/source/timers.c:564: undefined reference to `vApplicationDaemonTaskStartupHook'

Thank you

That says you have #define configUSE_DAEMON_TASK_STARTUP_HOOK 1 which means you need to define the function yourself (or change the option to 0). All function that begin like vApplicationā€¦ are user defined functions, which when enabled by the appropriate define in FreeRTOSConfig.h are called at the appropriate point by FreeRTOS. That hook is called at the start of the Timer Task to allow your application to do some initial initializations after FreeRTOS starts if needed rather than needing you to define your own startup task. (I rarely use that option myself).

After you create a timer, you need to start it.

Thank you Richard, after setting it to 0 it works.