Skip to content

Commit

Permalink
feat: added LeanTask and new methods for tasks (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
Laxilef authored Jun 30, 2022
1 parent 42557fd commit cae2d28
Show file tree
Hide file tree
Showing 14 changed files with 608 additions and 137 deletions.
160 changes: 123 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Include the library in your sketch

```cpp
#include <Scheduler.h>
#include <Task.h>
#include <LeanTask.h>
```

In your setup function start the scheduler
Expand All @@ -26,69 +28,153 @@ Scheduler.start(&task);
Scheduler.begin();
```

The scheduler blocks once begun, so the loop function is never called. You should instead create tasks.
The scheduler blocks once begun, so the loop function is called by the scheduler BEFORE the first task is runned each time. But it is recommended to avoid using a loop.

# Creating a Task
## Creating a Task

Tasks are classes that should inherit the ```Task``` class. A task can define a ```loop()``` and ```setup()``` function
much as the normal Arduino standard.

```cpp
class BlinkTask : public Task {
protected:
void setup() {
state = HIGH;
protected:
void setup() {
state = HIGH;

pinMode(2, OUTPUT);
digitalWrite(2, state);
}
pinMode(2, OUTPUT);
digitalWrite(2, state);
}

void loop() {
state = state == HIGH ? LOW : HIGH;
digitalWrite(2, state);
void loop() {
state = state == HIGH ? LOW : HIGH;
digitalWrite(2, state);

delay(1000);
}
delay(1000);
}

private:
uint8_t state;
uint8_t state;
} blink_task;
```
**IMPORTANT: Tasks must be declared globally on the stack (not a pointer). Failure to do so will crash your device.**
**IMPORTANT:** Tasks must be declared globally on the stack (not a pointer). Failure to do so will crash your device.
Tasks can run ```yield``` and ```delay``` like they normally would. These functions yield control to the scheduler
Tasks can run ```yield()``` and ```delay()``` like they normally would. These functions yield control to the scheduler
rather than the ESP8266.
### Advanced Task Functions
## Creating a LeanTask
The ```Task``` also exposes a ```bool shouldRun()``` method that is used determine if the task loop
should be be resumed. This can be inherited to add your own logic to determine if your code should be resumed.
Lean tasks are classes that should inherit the ```LeanTask``` class. A lean task can define a ```loop()``` and ```setup()``` function
much as the normal Arduino standard.
LeanTask doesn't use ```cont.h``` (runs in the global context), so ```yield()``` works exactly as it would without using the library. This can be useful for saving ram (4k) if the task does not use ```yield()``` to interrupt the task. You can use ```delay()``` at the end of a task to set the interval.
**Good example:**
```cpp
bool shouldRun() {
bool run = Task::shouldRun();
class MemTask : public LeanTask {
public:
void loop() {
Serial.print("Free Heap: ");
Serial.print(ESP.getFreeHeap());
Serial.println(" bytes");
delay(10000);
}
} mem_task;
```

// Your code here
**Bad example. The first ```delay()``` does nothing:**
<details><summary>Source. DO NOT DO THIS!</summary>
<p>

```cpp
class PrintTask : public LeanTask {
protected:
void loop() {
Serial.println("Print Loop Start");

return run;
}
delay(5000);

Serial.println("Print Loop End");

delay(5000);
}
} print_task;
```
**This function handles the ```delay()``` logic. The parent method should be called.**
</p>
</details>
# Documentation
Tasks can run ```yield()``` and ```delay()``` like they normally would. The ```yield()``` function transfers control to the ESP8266, NOT the scheduler. The ```delay()``` function will tell the scheduler that a delay is needed before the next run. If you REALLY need a delay, use ```::delay()```, but this will block the task and the scheduler.
## Methods
## Tests using Task vs LeanTask
All examples have the same logic. To optimize RAM, use ```LeanTask``` (if possible), because each instance of ```Task``` requires 4 kb of RAM.
| File | Description | Free heap (more is better) |
| --- | --- | --- |
| [simple.ino](examples/simple/simple.ino) | 3 Task | 39896 bytes |
| [lean_simple.ino](examples/lean_simple/lean_simple.ino) | 1 Task, 2 LeanTask | 48168 bytes |
| [subfile_simple.ino](examples/subfile_simple/subfile_simple.ino) | 1 Task, 2 LeanTask + main loop | 48136 bytes |
### start
```
static void start(Task *task)
```
> Adds a task to the multitasking queue.
[heap_test.ino](examples/heap_test/heap_test.ino):
| TASK_TYPE | Description | Free heap (more is better) |
| --- | --- | --- |
| Task | 12 Task + main loop | 2280 bytes |
| LeanTask | 12 LeanTask + main loop | 51912 bytes |
### begin
```
static void begin()
```
> Starts the scheduler. This function is "blocking". It should be the last call the ```setup``` function.
## Task methods
#### bool AbstractTask::isEnabled();
Method return the status of the task: enabled (true) or disabled (false).
---
#### AbstractTask(bool _enabled = true, unsigned long _interval = 0);
Constructor.
---
#### bool AbstractTask::isEnabled();
Method return the status of the task: enabled (true) or disabled (false).
---
#### void AbstractTask::enable();
The method enable the task.
---
#### void AbstractTask::disable();
The method disable the task.
---
#### void AbstractTask::setInterval(unsigned long val);
The method sets the task run interval.
---
#### unsigned long AbstractTask::getInterval();
The method returns the task run interval.
## Scheduler methods
#### static void SchedulerClass::start(AbstractTask *task);
Adds a task to the multitasking queue.
---
#### static void SchedulerClass::begin();
Starts the scheduler. This function is "blocking". It should be the last call the ```setup``` function.
---
#### static void SchedulerClass::delay(unsigned long ms);
Calls ```AbstractTask::delay()``` on the runned task from outside
**IMPORTANT:** You should not call this method outside for LeanTask tasks. It does not make sense.
---
#### static void SchedulerClass::yield();
Calls ```AbstractTask::yield()``` on the runned task from outside
**IMPORTANT:** You should not call this method outside for LeanTask tasks. It does not make sense.
138 changes: 138 additions & 0 deletions examples/heap_test/heap_test.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include <Arduino.h>
#include <Scheduler.h>
#include <Task.h>
#include <LeanTask.h>

#define TASK_TYPE Task
//#define TASK_TYPE LeanTask


class Blank1Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 1");
delay(1010);
}
} blank1_task;

class Blank2Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 2");
delay(1020);
}
} blank2_task;

class Blank3Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 3");
delay(1030);
}
} blank3_task;

class Blank4Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 4");
delay(1040);
}
} blank4_task;

class Blank5Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 5");
delay(1050);
}
} blank5_task;

class Blank6Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 6");
delay(1060);
}
} blank6_task;


class Blank7Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 7");
delay(1070);
}
} blank7_task;

class Blank8Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 8");
delay(1080);
}
} blank8_task;

class Blank9Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 9");
delay(1090);
}
} blank9_task;

class Blank10Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 10");
delay(1100);
}
} blank10_task;

class Blank11Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 11");
delay(1110);
}
} blank11_task;

class Blank12Task : public TASK_TYPE {
protected:
void loop() {
Serial.println("Blank 12");
delay(1120);
}
} blank12_task;


void setup() {
Serial.begin(115200);
Serial.println("");
delay(1000);

Scheduler.start(&blank1_task);
Scheduler.start(&blank2_task);
Scheduler.start(&blank3_task);
Scheduler.start(&blank4_task);
Scheduler.start(&blank5_task);
Scheduler.start(&blank6_task);
Scheduler.start(&blank7_task);
Scheduler.start(&blank8_task);
Scheduler.start(&blank9_task);
Scheduler.start(&blank10_task);
Scheduler.start(&blank11_task);
Scheduler.start(&blank12_task);

Scheduler.begin();
}

void loop() {
static unsigned long ts = 0;
if (millis() - ts > 10000) {
Serial.print("Free Heap: ");
Serial.print(ESP.getFreeHeap());
Serial.println(" bytes");

ts = millis();
}
}
Loading

0 comments on commit cae2d28

Please sign in to comment.