Skip to content

Commit 52be010

Browse files
committed
#47 improve scheduling with value type and single schedule function (fully backward compatible)
1 parent f864830 commit 52be010

7 files changed

Lines changed: 162 additions & 29 deletions

File tree

examples/halloween/halloween.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public:
3939

4040
analogWrite(pin, levelThisTime);
4141

42-
taskManager.scheduleOnce(delayThisTime, this);
42+
taskManager.schedule(onceMillis(delayThisTime), this);
4343
}
4444
};
4545

examples/mbedRtos/mbedExample.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,15 @@ class DiceEvent : public BaseEvent {
119119
// that extends Executable. In this case the job creates a repeating task and logs to the console.
120120
void tenSecondJob() {
121121
log("30 seconds up, restart a new repeating job");
122-
taskManager.scheduleFixedRate(500, [] {
122+
taskManager.schedule(repeatMillis(500), [] {
123123
log("Half second job, micros = ", microsTask->getCurrentTicks());
124124
});
125125
}
126126

127127
// Again another task manager function, we pass this as the timerFn argument later on
128128
void twentySecondJob() {
129129
log("20 seconds up, delete 1 second job, schedule 10 second job");
130-
taskManager.scheduleOnce(10, tenSecondJob, TIME_SECONDS);
130+
taskManager.schedule(repeatSeconds(10), tenSecondJob);
131131
taskManager.cancelTask(oneSecondTask);
132132
}
133133

@@ -137,28 +137,32 @@ void twentySecondJob() {
137137
void setupTasks() {
138138
// Here we create a new task using milliseconds; which is the default time unit for scheduling. We use a lambda
139139
// function as the means of scheduling.
140-
oneSecondTask = taskManager.scheduleFixedRate(1000, [] {
140+
oneSecondTask = taskManager.schedule(repeatSeconds(1), [] {
141141
log("One second job, micro count = ", microsTask->getCurrentTicks());
142142
});
143143

144144
// Here we create a new task based on the twentySecondJob function, that will be called at the appropriate time
145145
// We schedule this with a unit of seconds.
146-
taskManager.scheduleOnce(20, twentySecondJob, TIME_SECONDS);
146+
taskManager.schedule(onceSeconds(20), twentySecondJob);
147147

148148
// here we create a new task based on Executable and pass it to taskManager for scheduling. We provide the
149149
// time unit as microseconds, and with the last parameter we tell task manager to delete it when the task
150150
// is done, IE for one shot tasks that is as soon as it's done, for repeating tasks when it's cancelled.
151151
microsTask = new MicrosecondTask(0);
152-
taskManager.scheduleFixedRate(100, microsTask, TIME_MICROS, true);
152+
taskManager.schedule(repeatMicros(100), microsTask, true);
153153

154154
// here we create an event that will be triggered on another thread and then notify task manager when it is
155155
// triggered. We will allocate using new and let task manager delete it when done.
156156
taskManager.registerEvent(&diceEvent);
157157

158+
// If you enable lambda support, then you can capture parameters, this is somewhat contentious and therefore needs to
159+
// be enabled for those who want to use it by defining the below flag.
160+
#ifdef TM_ALLOW_CAPTURED_LAMBDA
158161
int capturedValue = 42;
159-
taskManager.scheduleFixedRate(2, [capturedValue]() {
162+
taskManager.schedule(repeatSeconds(2), [capturedValue]() {
160163
log("Execution with captured value = ", capturedValue);
161-
}, TIME_SECONDS);
164+
});
165+
#endif
162166

163167
// this shows how to create a long schedule event using the new operator, make sure the second parameter is true
164168
// as this will delete the event when it completes.

examples/simpleTasks/simpleTasks.ino

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,24 @@
1212
#include <Arduino.h>
1313
#include "TaskManagerIO.h"
1414

15+
// A counter that will be increased in the microsecond task.
16+
int counter = 0;
17+
18+
// here we globally store the task ID of our repeating task, we need this to cancel it later.
19+
// ADVANCED: On supported 32 bit boards you could enable the lambda support and pass this to the other task.
20+
int taskId;
21+
1522
//
1623
// A simple logging function that logs the time and the log line.
1724
//
1825
void logIt(const char* toLog) {
1926
Serial.print(millis());
2027
Serial.print(':');
28+
Serial.print(counter);
29+
Serial.print(':');
2130
Serial.println(toLog);
2231
}
2332

24-
//
25-
// here we globally store the task ID of our repeating task, we need this to cancel it later.
26-
//
27-
int taskId;
28-
2933
//
3034
// A task can either be a function that takes no parameters and returns void, a class that extends Executable or
3135
// if you want to call with parameters either ExecWithParameter or ExecWith2Parameters
@@ -34,26 +38,31 @@ void twentySecondJob() {
3438
logIt("20 seconds one off task");
3539
logIt("stop 1 second task");
3640
taskManager.cancelTask(taskId);
37-
taskManager.scheduleOnce(10, [] {
41+
taskManager.schedule(onceSeconds(10), [] {
3842
logIt("Ten more seconds done finished.");
39-
}, TIME_SECONDS);
43+
});
4044
}
4145

46+
4247
//
4348
// In setup we prepare our tasks, this is what a usual task manager sketch looks like
4449
//
4550
void setup() {
4651
Serial.begin(115200);
4752

53+
taskManager.schedule(repeatMicros(100), []{
54+
counter++;
55+
});
56+
4857
// schedule a task to run at a fixed rate, every 1000 milliseconds.
49-
taskId = taskManager.scheduleFixedRate(1000, [] {
58+
taskId = taskManager.schedule(repeatMillis(1000), [] {
5059
logIt("Fixed rate, every second");
5160
});
5261

5362
// schedule a task to run once in 20 seconds.
54-
taskManager.scheduleOnce(20, twentySecondJob, TIME_SECONDS);
63+
taskManager.schedule(onceSeconds(20), twentySecondJob);
5564

56-
// schedule a task to be executed immediately as a taskManager task.
65+
// schedule a task to be executed as soon as possible as a taskManager task.
5766
taskManager.execute([] {
5867
logIt("To do as soon as possible");
5968
});

examples/taskManagement/taskManagement.ino

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Written by Dave Cherry of TheCodersCorner.com in 2017
1616
#include <Arduino.h>
1717
#include <TaskManagerIO.h>
1818
#include <BasicInterruptAbstraction.h>
19+
#include "Wire.h"
1920

2021
// here we define an interrupt capable pin that will be varied in value during execution causing the
2122
// task manager interrupt handler to be executed. Task manager will marshal the interrupt back into
@@ -117,11 +118,11 @@ void setup() {
117118
//
118119

119120
// We schedule the function tenSecondsUp() to be called in 10,000 milliseconds.
120-
taskManager.scheduleOnce(10000, tenSecondsUp);
121+
taskManager.schedule(onceSeconds(10), tenSecondsUp);
121122

122123
// Now we schedule oneSecondPulse() to be called every second.
123124
// keep hold of the ID as we will later cancel it from running.
124-
taskid_t taskId = taskManager.scheduleFixedRate(1, oneSecondPulse, TIME_SECONDS);
125+
taskid_t taskId = taskManager.schedule(repeatSeconds(1), oneSecondPulse);
125126

126127
//
127128
// now we do a yield operation, which is similar to delayMicroseconds but allows other
@@ -131,6 +132,8 @@ void setup() {
131132
taskManager.yieldForMicros(32000);
132133
log("Waited 32 milli second with yield in setup");
133134

135+
// If you enable lambda support, then you can capture parameters, this is somewhat contentious and therefore needs to
136+
// be enabled for those who want to use it by defining the below flag.
134137
#ifdef TM_ALLOW_CAPTURED_LAMBDA
135138
// now schedule a task to run once in 30 seconds, we capture the taskId using a locally captured value. Notice that
136139
// this only works on 32 bit boards such as ESP*, ARM, mbed etc.
@@ -143,13 +146,13 @@ void setup() {
143146
#endif
144147

145148
// and another to run repeatedly at 5 second intervals, shows the task slot status
146-
taskManager.scheduleFixedRate(5, [] {
149+
taskManager.schedule(repeatSeconds(5), [] {
147150
char slotString[32];
148151
log(taskManager.checkAvailableSlots(slotString, sizeof(slotString)));
149-
}, TIME_SECONDS);
152+
});
150153

151154
// and now schedule onMicrosJob() to be called every 100 micros
152-
taskManager.scheduleFixedRate(100, onMicrosJob, TIME_MICROS);
155+
taskManager.schedule(repeatMicros(100), onMicrosJob);
153156

154157
// register a port 2 interrupt.
155158
taskManager.setInterruptCallback(onInterrupt);

examples/tasksUsingExecutable/tasksUsingExecutable.ino

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,32 +101,32 @@ void setup() {
101101
state.setData("Started");
102102

103103
// create a task that calls the lambda function every 100 millis, it sets the state
104-
taskManager.scheduleFixedRate(100, [] {
104+
taskManager.schedule(repeatMillis(100), [] {
105105
state.setState(millis() % 10000);
106106
moreState.amount = millis() % 10000;
107107
});
108108

109109
// create a task that runs the function provided once in 10 seconds
110-
taskManager.scheduleOnce(10, [] {state.setData("Warmup"); }, TIME_SECONDS);
110+
taskManager.schedule(onceSeconds(10), [] {state.setData("Warmup"); });
111111

112112
// create a task that calls the function provided once in 30 seconds.
113-
taskManager.scheduleOnce(30, [] {state.setData("Running"); }, TIME_SECONDS);
113+
taskManager.schedule(onceSeconds(30), [] {state.setData("Running"); });
114114

115115
// create a task that calls the exec method on our SharedState object every 250 millis.
116-
taskManager.scheduleFixedRate(250, &state);
116+
taskManager.schedule(repeatMillis(250), &state);
117117

118118
// Create a task function that will be called with the moreState parameter. Because it's allocated with new we must
119119
// tell task manager to delete it when it's done with it, that's the last true parameter.
120120
// Note that if you allocate the thing to be scheduled using new, you must pass true as the deleteWhenDone (last)
121121
// parameter. This instructs task manager that it has been passed ownership of the object.
122-
taskManager.scheduleFixedRate(1000, new ExecWithParameter<MoreState*>(parameterFunction, &moreState), TIME_MILLIS, true);
122+
taskManager.schedule(repeatMillis(1000), new ExecWithParameter<MoreState*>(parameterFunction, &moreState), true);
123123

124124
// Here we creaate another task to run every 3 seconds that takes two parameters. It will call a function with two
125125
// parameters that match the parameters in the template.
126126
// Note that if you allocate the thing to be scheduled using new, you must pass true as the deleteWhenDone (last)
127127
// parameter. This instructs task manager that it has been passed ownership of the object.
128128
auto paramFunction = new ExecWith2Parameters<MoreState*, SharedState*>(twoParameterFunction, &moreState, &state);
129-
taskManager.scheduleFixedRate(3, paramFunction, TIME_SECONDS, true);
129+
taskManager.schedule(repeatSeconds(3), paramFunction, true);
130130
}
131131

132132
void loop() {

src/TaskManagerIO.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,22 @@ TimerTask *TaskManager::getTask(taskid_t taskId) {
450450
return nullptr;
451451
}
452452

453+
taskid_t TaskManager::schedule(const TimePeriod &when, TimerFn timerFunction) {
454+
if(when.getRepeating()) {
455+
return scheduleFixedRate(when.getAmount(), timerFunction, when.getUnit());
456+
} else {
457+
return scheduleOnce(when.getAmount(), timerFunction, when.getUnit());
458+
}
459+
}
460+
461+
taskid_t TaskManager::schedule(const TimePeriod &when, Executable *execRef, bool deleteWhenDone) {
462+
if(when.getRepeating()) {
463+
return scheduleFixedRate(when.getAmount(), execRef, when.getUnit(), deleteWhenDone);
464+
} else {
465+
return scheduleOnce(when.getAmount(), execRef, when.getUnit(), deleteWhenDone);
466+
}
467+
}
468+
453469
#ifdef IOA_USE_MBED
454470

455471
volatile bool timingStarted = false;
@@ -481,3 +497,11 @@ unsigned long micros() {
481497
}
482498

483499
#endif
500+
501+
TimePeriod::TimePeriod() {
502+
amount = 0;
503+
unit = TIME_MILLIS;
504+
repeating = 0;
505+
}
506+
507+
TimePeriod::TimePeriod(uint32_t amount, TimerUnit unit, bool repeat) : amount(amount), unit(unit), repeating(repeat != false) {}

src/TaskManagerIO.h

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,78 @@ class InterruptAbstraction {
6666

6767
class TaskExecutionRecorder;
6868

69+
/**
70+
* A scheduling object that makes it easier to represent schedules in taskManager, they can be applied in a neater
71+
* written form that makes for quicker code understanding. The are used along with the helper function to provide a
72+
* single schedule method. For example: `taskManager.schedule(onceMicros(100), [] { workToDo() });`.
73+
*/
74+
class TimePeriod {
75+
private:
76+
uint32_t amount: 24;
77+
uint32_t unit: 7;
78+
uint32_t repeating: 1;
79+
public:
80+
TimePeriod();
81+
TimePeriod(uint32_t amount, TimerUnit unit, bool repeat);
82+
83+
TimePeriod(const TimePeriod& other) =default;
84+
TimePeriod& operator= (const TimePeriod& other) =default;
85+
86+
uint32_t getAmount() const {
87+
return amount;
88+
}
89+
90+
TimerUnit getUnit() const {
91+
return (TimerUnit)unit;
92+
}
93+
94+
bool getRepeating() const {
95+
return repeating != 0;
96+
}
97+
};
98+
99+
/**
100+
* Create a repeating time period for scheduling in seconds
101+
* @param seconds the number of seconds to schedule in
102+
* @return the time period for scheduling.
103+
*/
104+
inline TimePeriod repeatSeconds(uint32_t seconds) { return {seconds, TIME_SECONDS, true}; }
105+
106+
/**
107+
* Create a repeating time period for scheduling in millis
108+
* @param millis the number of milliseconds to schedule in
109+
* @return the time period for scheduling.
110+
*/
111+
inline TimePeriod repeatMillis(uint32_t millis) { return {millis, TIME_MILLIS, true}; }
112+
113+
/**
114+
* Create a repeating time period for scheduling in microseconds
115+
* @param micros the number of microseconds to schedule in
116+
* @return the time period for scheduling.
117+
*/
118+
inline TimePeriod repeatMicros(uint32_t micros) { return {micros, TIME_MICROS, true}; }
119+
120+
/**
121+
* Create a time period for scheduling once in seconds
122+
* @param seconds the number of seconds to schedule in
123+
* @return the time period for scheduling.
124+
*/
125+
inline TimePeriod onceSeconds(uint32_t seconds) { return {seconds, TIME_SECONDS, false}; }
126+
127+
/**
128+
* Create a time period for scheduling once in milliseconds
129+
* @param millis the number of milliseconds to schedule in
130+
* @return the time period for scheduling.
131+
*/
132+
inline TimePeriod onceMillis(uint32_t millis) { return {millis, TIME_MILLIS, false}; }
133+
134+
/**
135+
* Create a time period for scheduling once in microseconds
136+
* @param millis the number of microseconds to schedule in
137+
* @return the time period for scheduling.
138+
*/
139+
inline TimePeriod onceMicros(uint32_t micros) { return {micros, TIME_MICROS, false}; }
140+
69141
/**
70142
* TaskManager is a lightweight cooperative co-routine implementation for Arduino, it works by scheduling tasks to be
71143
* done either immediately, or at a future point in time. It is quite efficient at scheduling tasks as internally tasks
@@ -134,6 +206,27 @@ class TaskManager {
134206
return scheduleOnce(2, execToDo, TIME_MICROS, deleteWhenDone);
135207
}
136208

209+
/**
210+
* Schedule using a time period, normally using the helper functions to quickly create the period, underneath this
211+
* calls one of the existing schedule... methods. This schedules a no parameter function to be called. On larger
212+
* boards with lambda support enabled, it actually schedules a std::function.
213+
* @param when the time period providing the schedule details
214+
* @param timerFunction the function to call
215+
* @return the task ID that can be queried and cancelled.
216+
*/
217+
taskid_t schedule(const TimePeriod& when, TimerFn timerFunction);
218+
219+
/**
220+
* Schedule using a time period, normally using the helper functions to quickly create the period, underneath this
221+
* calls one of the existing schedule... methods. This schedules an executable to be called back.
222+
* @param when the time period providing the schedule details
223+
* @param execRef the function to call
224+
* @param deleteWhenDone if true, once the task ends (cancelled or finished in once mode) it is deleted.
225+
* @return the task ID that can be queried and cancelled.
226+
*/
227+
taskid_t schedule(const TimePeriod& when, Executable* execRef, bool deleteWhenDone = false);
228+
229+
137230
/**
138231
* Schedules a task for one shot execution in the timeframe provided.
139232
* @param millis the time frame in which to schedule the task

0 commit comments

Comments
 (0)