Skip to content

Portable C++ library for cooperative multitasking like Arduino Scheduler on ESP8266/ESP32, AVR, Linux, Windows

License

Notifications You must be signed in to change notification settings

dok-net/CoopTask

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CoopTask

An all-C++ implementation of a cooperative multitasking layer for ESP8266/ESP32, Arduino boards, Linux, and Windows x86 and x86_64

During regular development it's built and tested on the ESP MCUs and Arduino Pro/Pro Mini.

Tasks in this scheduler are stackful coroutines. They act almost the same as the main setup()/loop() code in Arduino sketches, but there can be many of them simultaneously on the same device. It's even powerful enough to run the ESP8266 and ESP32 WebServer in a CoopTask.

Use the normal global delay() function to suspend execution of a task for the given number of milliseconds, use yield() to give up the CPU, both return after other cooperative tasks have run awhile.

A simple blink task can be written just like this:

#include <CoopTask.h>

int loopBlink()
{
	// like setup():
    pinMode(LED_BUILTIN, OUTPUT);
    
    // like loop():
    for (;;)
    {
        digitalWrite(LED_BUILTIN, LOW);
        delay(2000);
        digitalWrite(LED_BUILTIN, HIGH);
        delay(3000);
    }

    // tasks can return or exit() and leave an exit code
    return 0;
}

CoopTask<>* taskBlink;

void setup()
{
    Serial.begin(115200);
    delay(500);

#if defined(ESP8266) || defined(ESP32)
    taskBlink = createCoopTask(F("Blink"), loopBlink, 0x240);
#else
    taskBlink = createCoopTask(F("Blink"), loopBlink, 0x40);
#endif
    if (!taskBlink) Serial.println("CoopTask Blink not created");
}

void loop()
{
    runCoopTasks();
}

The runCoopTasks() scheduling helper has two optional callback arguments.

The first, reaper, gets called each time a task exits. Retrieving the exit code or deleting the CoopTask object would typically be performed in a task reaper function.

The second callback, onDelay, is called after each scheduling rountrip with the total minimum delay (can be zero) of all managed tasks. A use scenario for this is to put the MCU into a power saving sleep mode for the given duration.

Using Arduino or Linux default loop stack space for CoopTask

Given that CoopTasks are scheduled from the Arduino default loop() or the main() function on Linux, any code in these functions is non-cooperative. This is great for incompatible sketches or libraries, but otherwise puts the memory of that main stack to waste. It is therefore good practice to allocate the local stack for a single, infinitely running, CoopTask on the main stack. Reserve enough stack to remain for loop() internals. In its most simple form, borrowing from the example above, where taskBlink meets the requirement of never returning, a CoopTask that uses the default stack space is created like so:

#if defined(ESP8266) || defined(ESP32)
    taskBlink = createCoopTask<int, CoopTaskStackAllocatorFromLoop<>>(
        F("Blink"), loopBlink, 0x240);
#else
    taskBlink = createCoopTask<int, CoopTaskStackAllocatorFromLoop<>>(
        F("Blink"), loopBlink, 0x40);
#endif

ESP8266 Core For Arduino specifics

ESP8266 Core For Arduino release 2.6.0 and later include all support for this release of CoopTask.

Arduino-ESP32 specifics

The ESP32 runs the Arduino API on top of the FreeRTOS real-time operating system. This OS has all the capabilities for real-time programming and offers prioritized, preemptive multitasking. The purpose of CoopTask on the other hand is to take the complexity out of multi-threaded/tasked programming, and offers a cooperative multi-tasking scheme instead.

Arduino-ESP32 has the necessary support for CoopTask beginning with commit-ish c2b3f2d, dated Oct 4 2019, in Github master branch post release 1.4.0.

For Arduino sketches, and the libraries used in these, that never use the global Arduino delay(), don't make use of FreeRTOS vTaskDelay(), and implement delays only ever using the CoopTask metaphor CoopTaskBase::delay(), CoopTask would not require anything specific for the ESP32.

If the convenient Arduino delay() does get used, or there is any chance that the FreeRTOS vTaskDelay() gets used, though, on the ESP32 it is necessary to prevent unsolicited preemptive concurrency and control the CPU time for the idle task.

This is being taken care of by CoopTask when using the runCoopTasks() scheduling helper in the Sketch loop() function.

About

Portable C++ library for cooperative multitasking like Arduino Scheduler on ESP8266/ESP32, AVR, Linux, Windows

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages