Skip to content

Latest commit

 

History

History
118 lines (94 loc) · 4.19 KB

README.md

File metadata and controls

118 lines (94 loc) · 4.19 KB

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.