I had this laying around. Not exactly what you proposed, but feel free to convert it into the example you want. The LED On/Off times are all prime numbers. So, you get some good, seemingly random, blink interleaving.
To see the diagnostic prints, set the Core Debug Level to "Info". Otherwise, set it to "Error".
#include "Arduino.h"
struct Blink {
uint8_t pin;
TickType_t period;
};
void blinkingTask(void *pvParameters);
void setup() {
Serial.begin(115200);
vTaskDelay(2000);
log_i("Starting");
Blink blinkingData[] = {
{ 12, 1154 },
{ 27, 2066 },
{ 33, 4138 },
{ 15, 6002 }
};
const size_t numBlinks = sizeof(blinkingData) / sizeof(blinkingData[0]);
char taskName[25];
for (uint8_t taskNum = 0; taskNum < numBlinks; taskNum++) {
sprintf(taskName, "Blink Task - %d", taskNum);
BaseType_t returnCode = xTaskCreatePinnedToCore(blinkingTask, taskName, 4000, blinkingData + taskNum, 2, NULL, CONFIG_ARDUINO_RUNNING_CORE);
if (returnCode != pdPASS) {
log_e("Failed to create task: %s. Resetting", taskName);
vTaskDelay(1000);
ESP.restart();
}
log_i("Task: %s started", taskName);
}
// Delay long enough for all tasks to start before blinkingData is destroyed
vTaskDelay(1000);
}
void loop() {
}
void blinkingTask(void *pvParameters) {
Blink *blinkInfo = reinterpret_cast<Blink*>(pvParameters);
uint8_t pin = blinkInfo->pin;
TickType_t halfPeriod = blinkInfo->period / 2;
pinMode(pin, OUTPUT);
TickType_t wakeTime = xTaskGetTickCount();
for (;;) {
log_i("Time: %10u, Pin %2d LOW", xTaskGetTickCount(), pin);
digitalWrite(pin, LOW);
xTaskDelayUntil(&wakeTime, halfPeriod);
log_i("Time: %10u, Pin %2d HIGH", xTaskGetTickCount(), pin);
digitalWrite(pin, HIGH);
xTaskDelayUntil(&wakeTime, halfPeriod);
}
}