Skip to content

Commit 257dad0

Browse files
author
dave
committed
#24 scheduling using lambda functions is now supported on 32 bit boards. Specifically all ESP, SAMD and mbed hardware.
1 parent 82ec77f commit 257dad0

File tree

6 files changed

+57
-14
lines changed

6 files changed

+57
-14
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ In the setup method, add an function callback that gets fired once in the future
3838
}, TIME_SECONDS);
3939
```
4040

41+
From 1.2 onwards: On ESP8266, ESP32, all mbed boards, and most 32 bit Arduino boards you can also capture values. An example of this usage follows:
42+
43+
```
44+
int capturedValue = 42;
45+
taskManager.scheduleFixedRate(2, [capturedValue]() {
46+
log("Execution with captured value = ", capturedValue);
47+
}, TIME_SECONDS);
48+
49+
```
50+
4151
You can also create a class that extends from `Executable` and schedule that instead. For example:
4252

4353
```

examples/mbedRtos/mbedExample.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ void setupTasks() {
151151
// here we create an event that will be triggered on another thread and then notify task manager when it is
152152
// triggered. We will allocate using new and let task manager delete it when done.
153153
taskManager.registerEvent(&diceEvent);
154+
155+
int capturedValue = 42;
156+
taskManager.scheduleFixedRate(2, [capturedValue]() {
157+
log("Execution with captured value = ", capturedValue);
158+
}, TIME_SECONDS);
154159
}
155160

156161
bool exitThreads = false;

examples/taskManagement/taskManagement.ino

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ shows all the uses of the scheduler, for a simpler example, see the timedBlink e
1010
To test the interrupt support, wire a switch to pin 2 with pull up/down. Each change of state
1111
will cause an interrupt.
1212
13-
Written by Dave Cherry of thecoderscorner.com in 2017
13+
Written by Dave Cherry of TheCodersCorner.com in 2017
1414
*/
1515

1616
#include <Arduino.h>
@@ -22,8 +22,6 @@ Written by Dave Cherry of thecoderscorner.com in 2017
2222
// a task, so it's safe to call anything you wish during it's execution.
2323
const int interruptPin = 2;
2424

25-
int taskId = -1;
26-
2725
// When we are not using IoAbstraction with task manager, then if we want to use interrupts, this class
2826
// provides the absolute bare minimum interrupt abstraction. Normally, we'd use IoAbstraction's device
2927
// abstraction capabilities instead.
@@ -40,13 +38,13 @@ void log(const char* logLine) {
4038
}
4139

4240
/**
43-
* This is called by taskManager when the interrupt is raised. TaskManager marshalls the
41+
* This is called by taskManager when the interrupt is raised. TaskManager marshals the
4442
* interrupt into a task, so it is safe to call Serial etc here. Be aware that interrupts
4543
* handled by taskManager are not completely real time, so only use when some slight delay
4644
* can be accepted.
4745
*
4846
* - Safe usage: change in rotary encoder, button pressed.
49-
* - Unsafe usage: over temprature shutdown, safety circuit.
47+
* - Unsafe usage: over temperature shutdown, safety circuit.
5048
*/
5149
void onInterrupt(pintype_t pin) {
5250
log("Interrupt triggered");
@@ -114,7 +112,7 @@ void setup() {
114112
pinMode(interruptPin, INPUT);
115113

116114
//
117-
// Now we register some taks, note that on AVR by default there are 6 slots, all others have 10 slots.
115+
// Now we register some tasks, note that on AVR by default there are 6 slots, all others have 10 slots.
118116
// this can be changed in TaskManager.h to your preferred setting.
119117
//
120118

@@ -123,7 +121,7 @@ void setup() {
123121

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

128126
//
129127
// now we do a yield operation, which is similar to delayMicroseconds but allows other
@@ -133,13 +131,16 @@ void setup() {
133131
taskManager.yieldForMicros(32000);
134132
log("Waited 32 milli second with yield in setup");
135133

136-
// now schedule a task to run once in 30 seconds
137-
taskManager.scheduleOnce(30000, [] {
134+
#ifdef TM_ALLOW_CAPTURED_LAMBDA
135+
// now schedule a task to run once in 30 seconds, we capture the taskId using a locally captured value. Notice that
136+
// this only works on 32 bit boards such as ESP*, ARM, mbed etc.
137+
taskManager.scheduleOnce(30000, [taskId]() {
138138
log("30 seconds up, stopping 1 second job");
139139

140140
// now cancel the one second job we scheduled earlier
141141
taskManager.cancelTask(taskId);
142142
});
143+
#endif
143144

144145
// and another to run repeatedly at 5 second intervals, shows the task slot status
145146
taskManager.scheduleFixedRate(5, [] {

src/TaskPlatformDeps.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ class TimerTask;
1111

1212
#if defined(__MBED__) || defined(ARDUINO_ARDUINO_NANO33BLE)
1313

14+
#define TM_ALLOW_CAPTURED_LAMBDA
15+
1416
// check if this is Arduino mbed or regular mbed.
1517
#if defined(ARDUINO_ARDUINO_NANO33BLE)
1618
# define IOA_USE_ARDUINO
@@ -79,6 +81,7 @@ namespace tm_internal {
7981
#include "Arduino.h"
8082
typedef uint8_t pintype_t;
8183
# define IOA_USE_ARDUINO
84+
# define TM_ALLOW_CAPTURED_LAMBDA
8285

8386

8487
#if defined(ESP8266)
@@ -256,6 +259,12 @@ inline void atomicWritePtr(TimerTaskAtomicPtr* pPtr, TimerTask* newValue) {
256259
}
257260
#endif // All platform checks
258261

262+
// for all mbed and ESP boards we already enable lambda captures, SAMD is a known extra case that works.
263+
// we can only enable on larger boards with enough memory to take the extra size of the structures.
264+
#if !defined(TM_DONT_USE_LAMBDA) && defined(ARDUINO_ARCH_SAMD)
265+
# define TM_ALLOW_CAPTURED_LAMBDA
266+
#endif
267+
259268
//
260269
// Scheduling size. On all boards by default task manager uses 32 bit schedule data to make it more general purpose.
261270
// Note that even on 8 bit boards, all the math still needs to be 32 bit to deal with times, so there is very little

src/TaskTypes.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ class RunningState {
2525
}
2626
};
2727

28-
TimerTask::TimerTask() {
28+
TimerTask::TimerTask() : callback() {
2929
// set everything to not in use.
3030
timingInformation = TIME_MILLIS;
3131
myTimingSchedule = 0;
32-
callback = nullptr;
32+
scheduledAt = 0;
33+
next = nullptr;
34+
taskRef = nullptr;
3335
executeMode = EXECTYPE_FUNCTION;
3436
tm_internal::atomicWritePtr(&next, nullptr);
3537
tm_internal::atomicWriteBool(&taskInUse, false);
@@ -94,8 +96,6 @@ unsigned long TimerTask::microsFromNow() {
9496
}
9597

9698
void TimerTask::execute() {
97-
if (callback == nullptr) return;
98-
9999
RunningState runningState(this);
100100

101101
auto execType = (ExecutionType) (executeMode & EXECTYPE_MASK);
@@ -122,7 +122,10 @@ void TimerTask::clear() {
122122
if((executeMode & EXECTYPE_DELETE_ON_DONE) != 0 && taskRef != nullptr) {
123123
delete taskRef;
124124
}
125-
callback = nullptr;
125+
taskRef = nullptr;
126+
#ifdef TM_ALLOW_CAPTURED_LAMBDA
127+
callback = std::function<void()>();
128+
#endif
126129

127130
// clear timing info
128131
scheduledAt = 0;

src/TaskTypes.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,12 @@ class BaseEvent : public Executable {
116116
/**
117117
* Definition of a function to be called back when a scheduled event is required. Takes no parameters, returns nothing.
118118
*/
119+
#ifdef TM_ALLOW_CAPTURED_LAMBDA
120+
#include <functional>
121+
typedef std::function<void()> TimerFn;
122+
#else
119123
typedef void (*TimerFn)();
124+
#endif
120125

121126
/**
122127
* The time units that can be used with the schedule calls.
@@ -161,12 +166,22 @@ enum ExecutionType : uint8_t {
161166
*/
162167
class TimerTask {
163168
private:
169+
#ifdef TM_ALLOW_CAPTURED_LAMBDA
170+
/** the thing that needs to be executed when the time is reached or event is triggered */
171+
volatile union {
172+
Executable *taskRef;
173+
BaseEvent *eventRef;
174+
};
175+
TimerFn callback;
176+
#else
164177
/** the thing that needs to be executed when the time is reached or event is triggered */
165178
volatile union {
166179
TimerFn callback;
167180
Executable *taskRef;
168181
BaseEvent *eventRef;
169182
};
183+
#endif
184+
170185
/** TimerTask is essentially stored in a linked list by time in TaskManager, this represents the next item */
171186
tm_internal::TimerTaskAtomicPtr next;
172187

0 commit comments

Comments
 (0)