Tasks and LED Strips

LanManMike
Posts: 7
Joined: Fri Jan 11, 2019 4:21 am

Tasks and LED Strips

Postby LanManMike » Fri Jan 11, 2019 4:40 am

Hi All,
I have been playing around with Strip LEDs (AM103 style) on the ESP32 DEVKITC.
To do this I have been using PtformIO (based on Visual Studio Code). This has worked well and I have has some good fun with the LED strips.However, I have found some issues when running tasks to update the LED Strip. Specifically, I would like to know:
1. Am I doing this the right way, or should I be using some sort of synchronous method?
2. Why cant I update the LED strip on another task (thread)?

// This is included for use in PIO
#include <Arduino.h>

// Inclue the Dostar Library
#include <Adafruit_DotStar.h>
// Set the number of pixels
#define NUMPIXELS 144
// Set the Data and Clock Pins
#define DATAPIN 22
#define CLOCKPIN 23
// Create a new Adafruit_DotStar object called strip
Adafruit_DotStar strip = Adafruit_DotStar(NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_BGR);

// Structure to hold info for the traffic light
struct TrafficLight
{
int RedColour;
int OrangeColour;
int GreenColour;
int RedTime;
int OrangeTime;
int GreenTime;
int RedLED;
int OrangeLED;
int GreenLED;
} TrafficLight1, TrafficLight2, TrafficLight3, TrafficLight4;

void TrafficLightTask1(void *parameter)
{
TrafficLight thisTrafficLight = *(TrafficLight *)parameter;
// This traffipc lights simply cycles red, orange , green, orange for ever
while (true)
{
// Red on
strip.setPixelColor(thisTrafficLight.RedLED, thisTrafficLight.RedColour);
delay(thisTrafficLight.RedTime);
// Red off, Orange on
strip.setPixelColor(thisTrafficLight.RedLED, 0, 0, 0);
strip.setPixelColor(thisTrafficLight.OrangeLED, thisTrafficLight.OrangeColour);
delay(thisTrafficLight.OrangeTime);
// Orange off , Green on
strip.setPixelColor(thisTrafficLight.OrangeLED, 0, 0, 0);
strip.setPixelColor(thisTrafficLight.GreenLED, thisTrafficLight.GreenColour);
delay(thisTrafficLight.GreenTime);
// Green off, Orange on
strip.setPixelColor(thisTrafficLight.GreenLED, 0, 0, 0);
strip.setPixelColor(thisTrafficLight.OrangeLED, thisTrafficLight.OrangeColour);
delay(thisTrafficLight.OrangeTime);
// Orange off
strip.setPixelColor(thisTrafficLight.OrangeLED, 0, 0, 0);
}
}

void stripShow() {
strip.show();
delay(10);
}

void TrafficLightShowTask(void *paramater)
{
stripShow();
}



void setup()
{
//Setup the serial port
Serial.begin(9600);

// Start the strip object
strip.begin();
// Show all black on the strip
strip.show();

// Setup Traffic Light show task
// ***** this will cause a corea core dump
// xTaskCreate(
// TrafficLightShowTask, /* Task function. */
// "Traffic Light Show Task", /* String with name of task. */
// 10000, /* Stack size in bytes. */
// NULL, /* Parameter passed as input of the task */
// 2, /* Priority of the task. */
// NULL); /* Task handle. */

// Setup Traffic Light 1
TrafficLight1.GreenColour = 0x0000FF00;
TrafficLight1.GreenTime = 1000;
TrafficLight1.GreenLED = 2;
TrafficLight1.RedColour = 0x00FF0000;
TrafficLight1.RedTime = 1000;
TrafficLight1.RedLED = 0;
TrafficLight1.OrangeColour = 0x00FFFF00;
TrafficLight1.OrangeTime = 1000;
TrafficLight1.OrangeLED = 1;

//Create a task for the traffic Light
xTaskCreate(
TrafficLightTask1, /* Task function. */
"Traffic Light 1", /* String with name of task. */
10000, /* Stack size in bytes. */
&TrafficLight1, /* Parameter passed as input of the task */
1, /* Priority of the task. */
NULL); /* Task handle. */

TrafficLight2.GreenColour = 0x0000FF00;
TrafficLight2.GreenTime = 500;
TrafficLight2.GreenLED = 5;
TrafficLight2.RedColour = 0x00FF0000;
TrafficLight2.RedTime = 500;
TrafficLight2.RedLED = 3;
TrafficLight2.OrangeColour = 0x00FFFF00;
TrafficLight2.OrangeTime = 500;
TrafficLight2.OrangeLED = 4;

//Create a task for the traffic Light
xTaskCreate(
TrafficLightTask1, /* Task function. */
"Traffic Light 2", /* String with name of task. */
10000, /* Stack size in bytes. */
&TrafficLight2, /* Parameter passed as input of the task */
1, /* Priority of the task. */
NULL); /* Task handle. */

TrafficLight3.GreenColour = 0x0000FF00;
TrafficLight3.GreenTime = 250;
TrafficLight3.GreenLED = 8;
TrafficLight3.RedColour = 0x00FF0000;
TrafficLight3.RedTime = 250;
TrafficLight3.RedLED = 6;
TrafficLight3.OrangeColour = 0x00FFFF00;
TrafficLight3.OrangeTime = 250;
TrafficLight3.OrangeLED = 7;

//Create a task for the traffic Light
xTaskCreate(
TrafficLightTask1, /* Task function. */
"Traffic Light 3", /* String with name of task. */
10000, /* Stack size in bytes. */
&TrafficLight3, /* Parameter passed as input of the task */
1, /* Priority of the task. */
NULL); /* Task handle. */

TrafficLight4.GreenColour = 0x0000FF00;
TrafficLight4.GreenTime = 125;
TrafficLight4.GreenLED = 11;
TrafficLight4.RedColour = 0x00FF0000;
TrafficLight4.RedTime = 125;
TrafficLight4.RedLED = 9;
TrafficLight4.OrangeColour = 0x00FFFF00;
TrafficLight4.OrangeTime = 125;
TrafficLight4.OrangeLED = 10;

//Create a task for the traffic Light
xTaskCreate(
TrafficLightTask1, /* Task function. */
"Traffic Light 4", /* String with name of task. */
10000, /* Stack size in bytes. */
&TrafficLight4, /* Parameter passed as input of the task */
1, /* Priority of the task. */
NULL); /* Task handle. */
}

void loop()
{
// I have to do this here as doing it in a task causess a core dump
strip.show();
delay(10);
}



Thanks to all that reply.
Regards
Mike

FreddyVictor
Posts: 8
Joined: Sat Dec 01, 2018 11:34 am

Re: Tasks and LED Strips

Postby FreddyVictor » Fri Jan 11, 2019 2:15 pm

What are you trying to achieve exactly ?
You presumably have one set of LED's but have 3 tasks trying to each send them a different sequence of patterns all at the same time?

I'm no expert on FreeRTOS, but I don't believe you should be using delay() function in a task as it will lock-out the scheduler.
You should use vTaskDelay() function instead. This returns control to the scheduler so it can give processing time to other tasks.

Sorry, no idea why you have core dump, I'm new too ...

just to add: vTaskDelay() may not use milliseconds like delay() does

ESP_Sprite
Posts: 8921
Joined: Thu Nov 26, 2015 4:08 am

Re: Tasks and LED Strips

Postby ESP_Sprite » Sat Jan 12, 2019 5:47 am

Not too sure about the Dotstar library, but I'd bet it's not re-entrant, that is, it does not expect multiple tasks calling its functions at the same time. Suggest you either rewrite your code to use only one task (and e.g. some state machines to decide what to do next), put mutex lock/unlock functions before and after each (set of) Dotstar function, or e.g. have one task that does all the Dotstar handling, and e.g. an event queue to send it commands from the other tasks.

LanManMike
Posts: 7
Joined: Fri Jan 11, 2019 4:21 am

Re: Tasks and LED Strips

Postby LanManMike » Sat Jan 12, 2019 11:54 am

Thanks heaps for the replies. Sorry that my initial post didn't include enough details.

The program sets up 4 tasks, each of which will change between a Red, Orange and Green LED on the Dotstar Strip in the same way a traffic light works.

The program I have listed in the original post works quite well - I was surprised it was so easy. The problem occurs when I create another task and use that to update the LED strip every 10 mS. This will cause a stack dump (repeatedly).

To get around the issue I have put the update task in the main loop() function. For some reason this seems to work fine, but I am unsure why (or why putting it in its own task causes issues).

The info on vTaskDelay() is very useful and I will be looking into that shortly. Interestingly, the Delay() function doesn't appear to be blocking as I can run the four traffic light tasks (each at a different speed) and it all seems to work OK. However, it was my memory that Delay() is actually blocking - Ill look it up and find out - I wonder if the ESP32 IDF has implemented its own non-blocking Delay() function.

I was also wondering about using a queue to update the Dotstar as this would avoid sync problems across multiple tasks. I have had a brief look at this already, but thought I would try this method first. I'll probably create a version to use a queue just to see how it works.

Thanks again,
Regards
LanManMike

LanManMike
Posts: 7
Joined: Fri Jan 11, 2019 4:21 am

Re: Tasks and LED Strips

Postby LanManMike » Sun Jan 13, 2019 2:46 am

Hi All,
I have just been looking into the delay() function. The code I have found in the Arduino Core is:

void delay(unsigned long ms)
{
uint32_t start = micros();

while (ms > 0) {
yield();
while ( ms > 0 && (micros() - start) >= 1000) {
ms--;
start += 1000;
}
}
}

As you can see, whilst the function is "waiting" it runs yield() which allows the task scheduler to run other tasks.

Is this your understanding, or is there a special reason (for ESP32s) to use vTaskDelay() or one of its relatives?

Thanks
Regards
LanManMike

ESP_Sprite
Posts: 8921
Joined: Thu Nov 26, 2015 4:08 am

Re: Tasks and LED Strips

Postby ESP_Sprite » Sun Jan 13, 2019 12:13 pm

I think delay uses yield() which in turn maps to vTaskYield... which, from memory, will try to yield to the task with the highest priority, which may be the task that does the yielding itself. I think this will work if the priority of all tasks are the same, but may land you in trouble if they differ. Also, it uses some power, as vTaskYield will never let the idle task run, and as such the CPU never can halt.

Downside of vTaskDelay is that it's not exactly precise and can be off by a tick or so (the actual effect is dependent on the tick frequency of FreeRTOS; not sure exactly what that is in the Arduino SDK but likely either 10mS or 1mS). If that's an issue, you can use the ESP-IDF esp_timer functions instead, they are extremely precise.

boarchuz
Posts: 559
Joined: Tue Aug 21, 2018 5:28 am

Re: Tasks and LED Strips

Postby boarchuz » Mon Jan 14, 2019 6:48 am

Wouldn't it simply use this delay function?

Code: Select all

void delay(uint32_t ms)
{
    vTaskDelay(ms / portTICK_PERIOD_MS);
}
https://github.com/espressif/arduino-es ... isc.c#L121

FreddyVictor
Posts: 8
Joined: Sat Dec 01, 2018 11:34 am

Re: Tasks and LED Strips

Postby FreddyVictor » Mon Jan 14, 2019 8:25 am

^^^
yes, as boarchuzsays

yield() is a FreeRTOS function so abides by the rules: it gives control back to the scheduler

LanManMike
Posts: 7
Joined: Fri Jan 11, 2019 4:21 am

Re: Tasks and LED Strips

Postby LanManMike » Tue Jan 15, 2019 10:29 am

Yes, I think boarchuz is absolutely correct.
The first definition of the delay() function (above) I found by Internet searching.
I have just tried highlighting delay() in Visual Studio Code (Platform IO) and I could go to the definition of the delay function, which is:

void delay(uint32_t ms)
{
vTaskDelay(ms / portTICK_PERIOD_MS);
}

This function appears in esp32-hal-misc.c. As you can see this calls vTaskDelay as stated by boarchuz.

In conclusion, it appears that you can use delay() to delay a task without blocking other tasks when using ESP32-Arduino libraries (tested in Visual Studio Code with Platform IO). There is a caveat - timing may not be highly accurate, however there are other functions which can produce highly accurate timing.

Regards
Mike

Who is online

Users browsing this forum: No registered users and 53 guests