restructure
[phd-thesis.git] / top / green.tex
1 \documentclass[../thesis.tex]{subfiles}
2
3 \input{subfilepreamble}
4
5 \begin{document}
6 \input{subfileprefix}
7
8 \chapter{Green computing with \texorpdfstring{\gls{MTASK}}{mTask}}%
9 \label{chp:green_computing_mtask}
10 \begin{chapterabstract}
11 This chapter demonstrate the energy saving features of \gls{MTASK}.
12 First it gives an overview of general green computing measures for edge devices.
13 Then \gls{MTASK}'s task scheduling is explained and it is shown how to customise it so suit the applications and energy needs.
14 Finally it shows how to use interrupts in \gls{MTASK} to reduce the need for polling.
15 \end{chapterabstract}
16
17 The edge layer of the \gls{IOT} contains small devices that sense and interact with the world and it is crucial to lower their energy consumption.
18 While individual devices consume little energy, the sheer number of devices in total amounts to a lot.
19 Furthermore, many \gls{IOT} devices operate on batteries and higher energy consumption increases the amount of e-waste as \gls{IOT} edge devices are often hard to reach and consequently hard to replace \citep{nizetic_internet_2020}.
20
21 To reduce the power consumption of an \gls{IOT} device, the specialized low-power sleep modes of the microprocessors can be leveraged.
22 Different sleep modes achieve different power reductions because of their different run time characteristics.
23 These specifics range from disabling or suspending WiFi; stopping powering (parts) of the \gls{RAM}; disabling peripherals; or even turning off the processor completely, requiring an external signal to wake up again.
24 Determining when exactly and for how long it is possible to sleep is expensive in the general case and often requires annotations in the source code, a real-time operating system or a handcrafted scheduler.
25
26 \begin{table}
27 \centering
28 \caption{Current use in \unit{\milli\ampere} of two microprocessor boards in various sleep modes.}%
29 \label{tbl:top_sleep}
30 \small
31 \begin{tabular}{ccccccccc}
32 \toprule
33 & \multicolumn{4}{c}{Wemos D1 mini} & \multicolumn{4}{c}{Adafruit Feather M0 Wifi} \\
34 \midrule
35 & active & modem & light & deep & active & modem & light & deep \\
36 & & sleep & sleep & sleep & & sleep & sleep & sleep \\
37 \midrule
38 WiFi & on & off & off & off & on & off & off & off \\
39 CPU & on & on & pending & off & on & on & idle & idle \\
40 \gls{RAM} & on & on & on & off & on & on & on & on\\%low power \\
41 \midrule
42 current & 100--240 & 15 & 0.5 & 0.002 & 90--300 & 5 & 2 & 0.005\\
43 \bottomrule
44 \end{tabular}
45 \end{table}
46
47 \Cref{tbl:top_sleep} shows the properties and current consumption of two commonly used microcontrollers.
48 It shows that switching the WiFi radio off yields the biggest energy savings.
49 In most \gls{IOT} applications, we need WiFi for communications.
50 It is fine to switch it off, but after switching it on, the WiFi protocol needs to transmit a number of messages to re-establish the connection.
51 This implies that it is only worthwhile to switch the radio off when this can be done for some time.
52 The details vary per system and situation.
53 As a rule of thumb, it is only worthwhile to switch the WiFi off when it is not needed for at least some tens of seconds.
54
55 \section{Green \texorpdfstring{\glsxtrshort{IOT}}{IoT} computing}
56 The data in \cref{tbl:top_sleep} shows that it is worthwhile to put the system in some sleep mode when there is temporarily no work to be done.
57 A deeper sleep mode saves more energy, but also requires more work to restore the software to its working state.
58 A processor like the ESP8266 driving the Wemos D1 mini loses the content of its \gls{RAM} in deep sleep mode.
59 As a result, after waking up, the program itself is preserved, since it is stored in flash memory, but the program state is lost.
60 When there is a program state to be preserved, we must either store it elsewhere, limit us to light sleep, or use a microcontroller that keeps the \gls{RAM} intact during deep sleep.
61
62 For \gls{IOT} nodes executing a single task, explicit sleeping to save energy can be achieved without too much hassle.
63 This becomes much more challenging as soon as multiple independent tasks run on the same node.
64 Sleeping of the entire node induced by one task prevents progress of all tasks.
65 This is especially annoying when the other tasks are executing time critical parts, like communication protocols.
66 Such protocols control the communication with sensors and actuators.
67 Without the help of an \gls{OS}, the programmer is forced to combine all subtasks into one big system that decides if it is safe to sleep for all subtasks.
68
69 \Gls{MTASK} offers abstractions for edge layer-specific details such as the heterogeneity of architectures, platforms and frameworks; peripheral access; and multitasking but also for energy consumption and scheduling.
70 In \gls{MTASK}, tasks are implemented as a rewrite system, where the work is automatically segmented in small atomic bits and stored as a task tree.
71 Each cycle, a single rewrite step is performed on all task trees, during rewriting, tasks do a bit of their work and progress steadily, allowing interleaved and seemingly parallel operation.
72 After a loop, the \gls{RTS} knows which task is waiting on which triggers and is thus able to determine the next execution time for each task automatically.
73 Utilising this information, the \gls{RTS} can determine when it is possible and safe to sleep and choose the optimal sleep mode according to the sleeping time.
74 For example, the \gls{RTS} never attempts to sleep during an \gls{I2C} communication because \gls{IO} is always contained \emph{within} a rewrite step.
75
76 An \gls{MTASK} program is dynamically transformed to byte code.
77 This byte code and the initial \gls{MTASK} expression are shipped to \gls{MTASK} \gls{IOT} node.
78 The \gls{MTASK} rewrite engine rewrites the current expression just a single rewrite step at a time.
79 When subtasks are composed in parallel, all subtasks are rewritten unless the result of the first rewrite step makes the result of the other tasks superfluous.
80 The task design ensures such that all time critical communication with peripherals is within a single rewrite step.
81 This is very convenient, since the system can inspect the current state of all \gls{MTASK} expressions after a rewrite and decide if sleeping and how long is possible.
82 %As a consequence, we cannot have fair multitasking.
83 %When a single rewrite step would take forever due to an infinite sequence of function calls, this would block the entire IoT node.
84 Even infinite sequences rewrite steps, as in the \cleaninline{blink} example, are perfectly fine.
85 The \gls{MTASK} system does proper tail-call optimizations to facilitate this.
86
87 \section{Task scheduling in the \gls{MTASK} language}
88 Some \gls{MTASK} examples contain one or more explicit \cleaninline{delay} primitives, offering a natural place for the node executing it to pause.
89 However, there are many \gls{MTASK} programs that just specify a repeated set of primitives.
90 A typical example is the program that reads the temperature for a sensor and sets the system \gls{LED} if the reading is below some given \cleaninline{goal}.
91
92 \begin{lstClean}[caption={A basic thermostat task.},label={lst:thermostat}]
93 thermostat :: Main (MTask v Bool) | mtask v
94 thermostat = DHT I2Caddr \dht->
95 {main = rpeat ( temperature dht >>~. \temp.
96 writeD builtInLED (goal <. temp))}
97 \end{lstClean}
98
99 This program repeatedly reads the \gls{DHT} sensor and sets the on-board \gls{LED} based on the comparison with the \cleaninline{goal} as fast as possible on the \gls{MTASK} node.
100 This is a perfect solution as long as we ignore the power consumption.
101 The \gls{MTASK} machinery ensures that if there are other tasks running on the node, they will make progress.
102 However, this solution is far from perfect when we take power consumption into account.
103 In most applications, it is very unlikely that the temperature will change significantly within one minute, let alone within some milliseconds.
104 Hence, it is sufficient to repeat the measurement with an appropriate interval.
105
106 There are various ways to improve this program.
107 The simplest solution is to add an explicit delay to the body of the repeat loop.
108 A slightly more sophisticated option is to add a repetition period to the \cleaninline{rpeat} combinator.
109 The combinator implementing this is called \cleaninline{rpeatEvery}.
110 Both solutions rely on an explicit action of the programmer.
111
112 Fortunately, \gls{MTASK} also contains machinery to do this automatically.
113 The key of this solution is to associate dynamically an evaluation interval with each task.
114 The interval $\refreshrate{low}{high}$ indicates that the evaluation can be safely delayed by any number of milliseconds in that range.
115 Such an interval is just a hint for the \gls{RTS}.
116 It is not a guarantee that the evaluation takes place in the given interval.
117 Other parts of the task expression can force an earlier evaluation of this part of the task.
118 When the system is very busy with other work, the task might even be executed after the upper bound of the interval.
119 The system calculates the refresh rates from the current task expression.
120 This has the advantage that the programmer does not have to deal with them and that they are available in each and every \gls{MTASK} program.
121
122 \subsection{Basic tasks}
123
124 We start by assigning default refresh rates to basic tasks.
125 These refresh rates reflect the expected change rates of sensors and other inputs.
126 Writing to basic \gls{GPIO} pins and actuators has refresh rate $\refreshrate{0}{0}$, this is never delayed.
127
128 \begin{table}
129 \centering
130 \caption{Default refresh rates of basic tasks.}%
131 \label{tbl:refresh}
132 \begin{tabular}{ll}
133 \toprule
134 task & default interval \\
135 \midrule
136 reading \pgls{SDS} & $\refreshrate{0}{2000}$ \\
137 slow sensor, like temperature & $\refreshrate{0}{2000}$ \\
138 gesture sensor & $\refreshrate{0}{1000}$ \\
139 fast sensor, like sound or light & $\refreshrate{0}{100}$ \\
140 reading GPIO pins & $\refreshrate{0}{100}$ \\
141 \bottomrule
142 \end{tabular}
143 \end{table}
144
145 \subsection{Deriving refresh rates}\label{sec:deriving_refresh_rates}
146 Based on these refresh rates, the system can automatically derive refresh rates for composed \gls{MTASK} expressions using $\mathcal{R}$.
147 We use the operator $\cap_{\textit{safe}}$ to compose refresh ranges.
148 When the ranges overlap the result is the overlapping range.
149 Otherwise, the result is the range with the lowest numbers.
150 The rationale is that subtasks should not be delayed longer than their refresh range.
151 Evaluating a task earlier should not change its result but can consume more energy.
152
153 \begin{align}
154 \cap_{\textit{safe}} :: \refreshrate{\mathit{Int}}{\mathit{Int}} \; \refreshrate{\mathit{Int}}{\mathit{Int}} & \shortrightarrow \refreshrate{\mathit{Int}}{\mathit{Int}} & \notag \\
155 R_1 \cap_{\textit{safe}} R_2 & = R_1 \cap R_2 & \text{if } R_1 \cap R_2 \neq \emptyset \\
156 \refreshrate{l_1}{h_1} \cap_{\textit{safe}} \refreshrate{l_2}{h_2} & = \refreshrate{l_2}{h_2} & \text{if } h_2 < l_1 \\
157 R_1 \cap_{\textit{safe}} R_2 & = R_1 & \text{otherwise}
158 \end{align}
159
160 \begin{align}
161 \mathcal{R} :: (\mathit{MTask}~v~a) & \shortrightarrow \refreshrate{\mathit{Int}}{\mathit{Int}} \notag \\
162 \mathcal{R} (t_1~{.||.}~t_2) & = \mathcal{R}(t_1) \cap_{\textit{safe}} \mathcal{R}(t_2) \label{R:or} \\
163 \mathcal{R}(t_1~{.\&\&.}~t_2) & = \mathcal{R}(t_1) \cap_{\textit{safe}} \mathcal{R}(t_2) \label{R:and}\\
164 \mathcal{R}(t_1~{>\!\!>\!|.}~t_2) & = \mathcal{R}(t_1) \label{R:seq} \\
165 \mathcal{R}(t~{>\!\!>\!=.}~f) & = \mathcal{R}(t) \label{R:bind} \\
166 \mathcal{R}(t~{>\!\!>\!\!*.}~[a_1 \ldots a_n]) & = \mathcal{R}(t) \label{R:step} \\
167 \mathcal{R}(\mathit{rpeat}~t) & = \refreshrate{0}{0} \label{R:rpeat} \\
168 \mathcal{R}(\mathit{rpeatEvery}~d~t) & = \refreshrate{0}{0} \label{R:rpeatevery} \\
169 \mathcal{R}(delay~d) & = \refreshrate{d}{d} \label{R:delay} \\
170 \mathcal{R}(t) & =
171 \left\{%
172 \begin{array}{ll}
173 \refreshrate{\infty}{\infty}~& \text{if}~t~\text{is Stable} \\
174 \refreshrate{r_l}{r_u} & \text{otherwise}
175 \end{array}
176 \right.\label{R:other}
177 \end{align}
178
179 We will briefly discuss the various cases of deriving refresh rates together with the task semantics of the different combinators
180
181 \subsubsection{Parallel combinators} For the parallel composition of tasks we compute the intersection of the refresh intervals of the components as outlined in the definition of $\cap_{\textit{safe}}$.
182 The operator \cleaninline{.\|\|.} in \cref{R:or} is the \emph{or}-combinator; the first subtask that produces a stable value determines the result of the composition.
183 The operator \cleaninline{.&&.} in \cref{R:and} is the \emph{and}-operator. The result is the tuple containing both results when both subtasks have a stable value.
184 The refresh rates of the parallel combinators have no direct relation with their task result.
185
186 \subsubsection{Sequential combinators}
187 For the sequential composition of tasks we only have to look at the refresh rate of the current task on the left.
188 The sequential composition operator \cleaninline{>>\|.} in \cref{R:seq} is similar to the monadic sequence operator \cleaninline{>>\|}.
189 The operator \cleaninline{>>=.} in \cref{R:bind} provides the stable task result to the function on the right-hand side, similar to the monadic bind.
190 The operator \cleaninline{>>~.} steps on an unstable value and is otherwise equal to \cleaninline{>>=.}.
191 The step combinator \cleaninline{>>*.} in \cref{R:step} has a list of conditional actions that specify a new task.
192
193 \subsubsection{Repeat combinators}
194 The repeat combinators repeats their argument indefinitely.
195 The combinator \cleaninline{rpeatEvery} guarantees the given delay between repetitions.
196 The refresh rate is equal to the refresh rate of the current argument task.
197 Only when \cleaninline{rpeatEvery} waits between the iterations of the argument the refresh interval is equal to the remaining delay time.
198
199 \subsubsection{Other combinators}
200 The refresh rate of the \cleaninline{delay} in \cref{R:delay} is equal to the remaining delay.
201 Refreshing stable tasks can be delayed indefinitely, their value never changes.
202 For other basic tasks, the values from \cref{tbl:refresh} apply.
203 The values $r_l$ and $r_u$ in \cref{R:other} are the lower and upper bound of the rate.
204
205 The refresh intervals associated with various steps of the thermostat program from \cref{lst:thermostat} are given in \cref{tbl:intervals}.
206 Those rewrite steps and intervals are circular, after step 2 we continue with step 0 again.
207 Only the actual reading of the sensor with \cleaninline{temperature dht} offers the possibility for a non-zero delay.
208
209 %%\begin{table}[tb]
210 \begin{table}
211 \centering
212 \caption{Rewrite steps of the thermostat from \cref{lst:thermostat} and associated intervals.}%
213 \label{tbl:intervals}
214 \begin{tabular}{cp{20em}c}
215 \toprule
216 Step & Expression & Interval \\
217 \midrule
218 0 &
219 \begin{lstClean}[aboveskip=-2ex,belowskip=-2ex,frame=]
220 rpeat ( temperature dht >>~. \temp.
221 writeD builtInLED (goal <. temp)
222 )\end{lstClean}
223 &
224 $\refreshrate{0}{0}$ \\
225 %\hline
226 1 &
227 \begin{lstClean}[aboveskip=-2ex,belowskip=-2ex,frame=]
228 temperature dht >>~. \temp.
229 writeD builtInLED (goal <. temp) >>|.
230 rpeat ( temperature dht >>~. \temp.
231 writeD builtInLED (goal <. temp)
232 )\end{lstClean}
233 & $\refreshrate{0}{2000}$ \\
234 %\hline
235 2 &
236 \begin{lstClean}[aboveskip=-2ex,belowskip=-2ex,frame=]
237 writeD builtInLED false >>|.
238 rpeat ( temperature dht >>~. \temp.
239 writeD builtInLED (goal <. temp)
240 )\end{lstClean}
241 & $\refreshrate{0}{0}$ \\
242 \bottomrule
243 \end{tabular}
244 \end{table}
245
246 \subsection{Tweaking refresh rates}
247 A tailor-made \gls{ADT} (see \cref{lst:interval}) determines the timing intervals for which the value is determined at runtime but the constructor is known at compile time.
248 During compilation, the constructor of the \gls{ADT} is checked and code is generated accordingly.
249 If it is \cleaninline{Default}, no extra code is generated.
250 In the other cases, code is generated to wrap the task tree node in a \emph{tune rate} node.
251 In the case that there is a lower bound, i.e.\ the task must not be executed before this lower bound, an extra \emph{rate limit} task tree node is generated that performs a no-op rewrite if the lower bound has not passed but caches the task value.
252
253 \begin{lstClean}[caption={The \gls{ADT} for timing intervals in \gls{MTASK}.},label={lst:interval}]
254 :: TimingInterval v = Default
255 | BeforeMs (v Int) // yields [+$\refreshrate{0}{x}$+]
256 | BeforeS (v Int) // yields [+$\refreshrate{0}{x \times 1000}$+]
257 | ExactMs (v Int) // yields [+$\refreshrate{x}{x}$+]
258 | ExactS (v Int) // yields [+$\refreshrate{0}{x \times 1000}$+]
259 | RangeMs (v Int) (v Int) // yields [+$\refreshrate{x}{y}$+]
260 | RangeS (v Int) (v Int) // yields [+$\refreshrate{x \times 1000}{y \times 1000}$+]
261 \end{lstClean}
262
263 \subsubsection{Sensors and \texorpdfstring{\glspl{SDS}}{shared data sources}}
264 In some applications, it is necessary to read sensors or \glspl{SDS} at a different rate than the default rate given in \cref{tbl:refresh}, i.e.\ to customise the refresh rate.
265 This is achieved by calling the access functions with a custom refresh rate as an additional argument (suffixed with the backtick (\cleaninline{`}))
266 The adaptions to other classes are similar and omitted for brevity.
267 \Cref{lst:dht_ext} shows the extended \cleaninline{dht} and \cleaninline{dio} class definition with functions for custom refresh rates.
268
269 \begin{lstClean}[caption={Auxiliary definitions to \cref{lst:gpio,lst:dht} for \gls{DHT} sensors and digital \gls{GPIO} with custom timing intervals.},label={lst:dht_ext}]
270 class dht v where
271 ...
272 temperature` :: (TimingInterval v) (v DHT) -> MTask v Real
273 temperature :: (v DHT) -> MTask v Real
274 humidity` :: (TimingInterval v) (v DHT) -> MTask v Real
275 humidity :: (v DHT) -> MTask v Real
276
277 class dio p v | pin p where
278 ...
279 readD` :: (TimingInterval v) (v p) -> MTask v Bool | pin p
280 readD :: (v p) -> MTask v Bool | pin p
281 \end{lstClean}
282
283 As example, we define an \gls{MTASK} that updates the \gls{SDS} \cleaninline{tempSds} in \gls{ITASK} in a tight loop.
284 The \cleaninline{temperature`} reading requires that this happens at least once per minute.
285 Without other tasks on the \gls{IOT} node, the temperature \gls{SDS} is updated once per minute.
286 Other tasks can cause a slightly more frequent update.
287
288 \begin{lstClean}[caption={Updating \pgls{SDS} in \gls{ITASK} at least once per minute.},label={lst:updatesds2}]
289 delayTime :: TimingInterval v | mtask v
290 delayTime = BeforeS (lit 60) // 1 minute in seconds
291
292 devTask :: Main (MTask v Real) | mtask, dht, liftsds v
293 devTask =
294 DHT (DHT_DHT pin DHT11) \dht =
295 liftsds \localSds = tempSds
296 In {main = rpeat (temperature` delayTime dht >>~. setSds localSds)}
297 \end{lstClean}
298
299 \subsubsection{Repeating tasks}
300 The task combinator \cleaninline{rpeat} restarts the child task in the evaluation if the previous produced a stable result.
301 However, in some cases it is desirable to postpone the restart of the child.
302 For this, the \cleaninline{rpeatEvery} task is introduced which receives an extra argument, the refresh rate, as shown in \cref{lst:rpeatevery}.
303 Instead of immediately restarting the child once it yields a stable value, it checks whether the lower bound of the provided timing interval has passed since the start of the task.\footnotemark{}
304 \footnotetext{In reality, it also compensates for time drift by taking into account the upper bound of the timing interval.
305 If the task takes longer to stabilise than the upper bound of the timing interval, this upper bound is taken as the start of the task instead of the actual start.}
306
307 \begin{lstClean}[caption={Repeat task combinator with a timing interval.},label={lst:rpeatevery}]
308 class rpeat v where
309 rpeat :: (MTask v t) -> MTask v t
310 rpeatEvery v :: (TimingInterval v) (MTask v t) -> MTask v t
311 \end{lstClean}
312
313 \Cref{lst:rpeateveryex} shows an example of an \gls{MTASK} task utilising the \cleaninline{rpeatEvery} combinator that would be impossible to create with the regular \cleaninline{rpeat}.
314 The \cleaninline{timedPulse} function creates a task that sends a \qty{50}{\ms} pulse to the \gls{GPIO} pin 0 every second.
315 The task created by the \cleaninline{timedPulseNaive} functions emulates the behaviour by using \cleaninline{rpeat} and \cleaninline{delay}.
316 However, this results in a time drift because rewriting tasks trees takes some time and the time it takes can not always be reliably predicted due to external factors.
317 E.g.\ writing to \gls{GPIO} pins takes some time, interrupts may slow down the program (see \cref{lst:interrupts}), or communication may occur in between task evaluations.
318
319 \begin{lstClean}[caption={Example program for the repeat task combinator with a timing interval.},label={lst:rpeateveryex}]
320 timedPulse :: Main (MTask v Bool) | mtask v
321 timedPulse = declarePin D0 PMOutput \d0->
322 in {main = rpeatEvery (ExactSec (lit 1)) (
323 writeD d0 true
324 >>|. delay (lit 50)
325 >>|. writeD d0 false
326 )
327 }
328
329 timedPulseNaive :: Main (MTask v Bool) | mtask v
330 timedPulseNaive = declarePin D0 PMOutput \d0->
331 {main = rpeat (
332 writeD d0 true
333 >>|. delay (lit 50)
334 >>|. writeD d0 false
335 >>|. delay (lit 950))
336 }
337 \end{lstClean}
338
339 \section{Task scheduling in the \texorpdfstring{\gls{MTASK}}{mTask} engine}
340 The refresh rates from the previous section only tell us how much the next evaluation of the task can be delayed.
341 An \gls{IOT} edge devices executes multiple tasks may run interleaved.
342 In addition, it has to communicate with a server to collect new tasks and updates of \glspl{SDS}.
343 Hence, the refresh intervals cannot be used directly to let the microcontroller sleep.
344 Our scheduler has the following objectives.
345 \begin{itemize}
346 \item
347 Meet the deadline whenever possible, i.e.\ the system tries to execute every task before the end of its refresh interval.
348 Only too much work on the device might cause an overflow of the deadline.
349 \item
350 Achieve long sleep times. Waking up from sleep consumes some energy and takes some time.
351 Hence, we prefer a single long sleep over splitting this interval into several smaller pieces.
352 \item
353 The scheduler tries to avoid unnecessary evaluations of tasks as much as possible.
354 A task should not be evaluated now when its execution can also be delayed until the next time that the device is active.
355 That is, a task should preferably not be executed before the start of its refresh interval.
356 Whenever possible, task execution should even be delayed when we are inside the refresh interval as long as we can execute the task before the end of the interval.
357 \item
358 The optimal power state should be selected.
359 Although a system uses less power in a deep sleep mode, it also takes more time and energy to wake up from deep sleep.
360 When the system knows that it can sleep only a short time it is better to go to light sleep mode since waking up from light sleep is faster and consumes less energy.
361 \end{itemize}
362
363 The algorithm $\mathcal{R}$ from \cref{sec:deriving_refresh_rates} computes the evaluation rate of the current tasks.
364 For the scheduler, we transform this interval to an absolute evaluation interval; the lower and upper bound of the start time of that task measured in the time of the \gls{IOT} edge device.
365 We obtain those bounds by adding the current system time to the bounds of the computed refresh interval by algorithm $\mathcal{R}$.
366
367 For the implementation, it is important to note that the evaluation of a task takes time.
368 Some tasks are extremely fast, but other tasks require long computations and time-consuming communication with peripherals as well as with the server.
369 These execution times can yield a considerable and noticeable time drift in \gls{MTASK} programs.
370 For instance, a task like \cleaninline{rpeatEvery (ExactMs 1) t} should repeat \cleaninline{t} every millisecond.
371 The programmer might expect that \cleaninline{t} will be executed for the ${(N+1)}^{th}$ time after $N$ milliseconds.
372 Uncompensated time drift might make this considerably later.
373 \Gls{MTASK} does not pretend to be a hard real-time \gls{OS}, and cannot give firm guarantees with respect to evaluation time.
374 Nevertheless, we try to make time handling as reliable as possible.
375 This is achieved by adding the start time of this round of task evaluations rather than the current time to compute absolute execution intervals.
376
377 \subsection{Scheduling Tasks}
378 Apart from the task to execute, the \gls{IOT} device has to maintain the connection with the server and check there for new tasks and updates of \gls{SDS}.
379 When the microcontroller is active, it checks the connection and updates from the server and executes the task if it is in its execution window.
380 Next, the microcontroller goes to light sleep for the minimum of a predefined interval and the task delay.
381
382 In general, the microcontroller node executes multiple \gls{MTASK} tasks at the same time.
383 \Gls{MTASK} nodes repeatedly check for inputs from servers and execute all tasks that cannot be delayed to the next evaluation round one step.
384 The tasks are stored in a priority queue to check efficiently which of them need to be stepped.
385 The \gls{MTASK} tasks are ordered at their absolute latest start time in this queue; the earliest deadline first.
386 We use the earliest deadline to order tasks with equal latest deadline.
387
388 It is very complicated to make an optimal scheduling algorithm for tasks to minimize the energy consumption.
389 We use a simple heuristic to evaluate tasks and determine sleep time rather than wasting energy on a fancy evaluation algorithm.
390 \Cref{lst:evalutionRound} gives this algorithm in pseudo code.
391 First the \gls{MTASK} node checks for new tasks and updates of \glspl{SDS}.
392 This communication adds any task to the queue.
393 The \cleaninline{stepped} set contains all tasks evaluated in this evaluation round.
394 Next, we evaluate tasks from the queue until we encounter a task that has an evaluation interval that is not started.
395 This might evaluate tasks earlier than required, but maximizes the opportunities to sleep after this evaluation round.
396 %Using the \prog{stepped} set ensures that we evaluate each task at most once during an evaluation round.
397 Executed tasks are temporarily stored in the \cleaninline{stepped} set instead of inserted directly into the queue to ensure that they are evaluated at most once in a evaluation round to ensure that there is frequent communication with the server.
398 A task that produces a stable value is completed and is not queued again.
399
400 \begin{algorithm}
401 \DontPrintSemicolon
402 \SetKwProg{Repeatt}{repeat}{}{end}
403 \KwData{queue = []\;}
404 \Begin{
405 \Repeatt{}{
406 queue += communicateWithServer\;
407 stepped = []\tcp*{tasks stepped in this round}
408 \While{notEmpty(queue) $\wedge$ earliestDeadline(top(queue)) $\leq$ currentTime}{
409 (task, queue) = pop(queue)\;
410 task2 = step(task)\tcp*{computes new execution interval}
411 \If{$\neg$ isStable(task2)\tcp*{not finished after step}}{
412 stepped += task2\;
413 }
414 }
415 queue = merge(queue, stepped)\;
416 sleep(queue)\;
417 }
418 }
419 \caption{Pseudo code for the evaluation round of tasks in the queue.}
420 \label{lst:evalutionRound}
421 \end{algorithm}
422
423 The \cleaninline{sleep} function determines the maximum sleep time based on the top of the queue.
424 The computed sleep time and the characteristics of the microprocessor determine the length and depth of the sleep.
425 For very short sleep times it might not be worthwhile to sleep.
426 In the current \gls{MTASK} \gls{RTS}, the thresholds are determined by experimentation but can be tuned by the programmer.
427 On systems that lose the content of their \gls{RAM} it is not possible to go to deep sleep mode.
428
429 \section{Interrupts}\label{lst:interrupts}
430 Most microcontrollers have built-in support for processor interrupts.
431 These interrupts are hard-wired signals that can interrupt the normal flow of the program to execute a small piece of code, the \gls{ISR}.
432 While the \glspl{ISR} look like regular functions, they do come with some limitations.
433 For example, they must be very short, in order not to miss future interrupts; can only do very limited \gls{IO}; cannot reliably check the clock; and they operate in their own stack, and thus communication must happen via global variables.
434 After the execution of the \gls{ISR}, the normal program flow is resumed.
435 Interrupts are heavily used internally in the \gls{RTS} of the microcontrollers to perform timing critical operations such as WiFi, \gls{I2C}, or \gls{SPI} communication; completed \gls{ADC} conversions, software timers; exception handling; \etc.
436
437 Interrupts offer two substantial benefits: fewer missed events and better energy usage.
438 Sometimes an external event such as a button press only occurs for a very small duration, making it possible to miss it due to it happening right between two polls.
439 Using interrupts is not a fool-proof way of never missing an event.
440 Events may still be missed if they occur during the execution of an \gls{ISR} or while the microcontroller is still in the process of waking up from a triggered interrupt.
441 There are also some sensors, such as the CCS811 air quality sensor, with support for triggering interrupts when a value exceeds a critical limit.
442
443 There are several different types of interrupts possible.
444 \begin{table}
445 \centering
446 \caption{Overview of \gls{GPIO} interrupt types.}%
447 \label{tbl:gpio_interrupts}
448 \begin{tabular}{ll}
449 \toprule
450 type & triggers\\
451 \midrule
452 change & input changes\\
453 falling & input becomes low\\
454 rising & input becomes high\\
455 low & input is low\\
456 high & input is high\\
457 \bottomrule
458 \end{tabular}
459 \end{table}
460
461 \subsection{\Gls{ARDUINO} platform}
462 \Cref{lst:arduino_interrupt} shows an exemplatory program utilising interrupts written in \gls{ARDUINO}'s \gls{CPP} dialect.
463 The example shows a debounced light switch for the built-in \gls{LED} connected to \gls{GPIO} pin 13.
464 When the user presses the button connected to \gls{GPIO} pin 11, the state of the \gls{LED} changes.
465 As buttons sometimes induce noise shortly after pressing, events within \qty{30}{\ms} after pressing are ignored.
466 In between the button presses, the device goes into deep sleep using the \arduinoinline{LowPower} library.
467
468 \Crefrange{lst:arduino_interrupt:defs_fro}{lst:arduino_interrupt:defs_to} defines the pin and debounce constants.
469 \Cref{lst:arduino_interrupt:state} defines the current state of the \gls{LED}, it is declared \arduinoinline{volatile} to exempt it from compiler optimisations because it is accessed in the interrupt handler.
470 \Cref{lst:arduino_interrupt:cooldown} flags whether the program is in debounce state, i.e.\ events should be ignored for a short period of time.
471
472 In the \arduinoinline{setup} function (\crefrange{lst:arduino_interrupt:setup_fro}{lst:arduino_interrupt:setup_to}), the pinmode of the \gls{LED} and interrupt pins are set.
473 Furthermore, the microcontroller is instructed to wake up from sleep mode when a \emph{rising} interrupt occurs on the interrupt pin and to call the \gls{ISR} at \crefrange{lst:arduino_interrupt:isr_fro}{lst:arduino_interrupt:isr_to}.
474 This \gls{ISR} checks if the program is in cooldown state.
475 If this is not the case, the state of the \gls{LED} is toggled.
476 In any case, the program goes into cooldown state afterwards.
477
478 In the \arduinoinline{loop} function, the microcontroller goes to low-power sleep immediately and indefinitely.
479 Only when an interrupt triggers, the program continues, writes the state to the \gls{LED}, waits for the debounce time, and finally disables the \arduinoinline{cooldown} state.
480
481 \begin{lstArduino}[numbers=left,label={lst:arduino_interrupt},caption={Light switch using interrupts.}]
482 #define LEDPIN 13[+\label{lst:arduino_interrupt:defs_fro}+]
483 #define INTERRUPTPIN 11
484 #define DEBOUNCE 30[+\label{lst:arduino_interrupt:defs_to}+]
485
486 volatile byte state = LOW;[+\label{lst:arduino_interrupt:state}+]
487 volatile bool cooldown = true;[+\label{lst:arduino_interrupt:cooldown}+]
488
489 void setup() {[+\label{lst:arduino_interrupt:setup_fro}+]
490 pinMode(LEDPIN, OUTPUT);
491 pinMode(INTERRUPTPIN, INPUT);
492 LowPower.attachInterruptWakeup(INTERRUPTPIN, buttonPressed, RISING);
493 }[+\label{lst:arduino_interrupt:setup_to}+]
494
495 void loop() {[+\label{lst:arduino_interrupt:loop_fro}+]
496 LowPower.sleep();
497 digitalWrite(LEDPIN, state);
498 delay(DEBOUNCE);
499 cooldown = false;
500 }[+\label{lst:arduino_interrupt:loop_to}+]
501
502 void buttonPressed() {[+\label{lst:arduino_interrupt:isr_fro}+]
503 if (!cooldown)
504 state = !state;
505 cooldown = true;
506 }[+\label{lst:arduino_interrupt:isr_to}+]
507 \end{lstArduino}
508
509 \subsection{\texorpdfstring{\Gls{MTASK}}{MTask} language}
510 \Cref{lst:mtask_interrupts} shows the interrupt interface in \gls{MTASK}.
511 The \cleaninline{interrupt} class contains a single function that, given an interrupt mode and a \gls{GPIO} pin, produces a task that represents this interrupt.
512 Lowercase variants of the various interrupt modes such as \cleaninline{change :== lit Change} are available as convenience macros (see \cref{sec:expressions}).
513
514 \begin{lstClean}[label={lst:mtask_interrupts},caption={The interrupt interface in \gls{MTASK}.}]
515 class interrupt v where
516 interrupt :: (v InterruptMode) (v p) -> MTask v Bool | pin p
517
518 :: InterruptMode = Change | Rising | Falling | Low | High
519 \end{lstClean}
520
521 When the \gls{MTASK} device executes this task, it installs an \gls{ISR} and sets the refresh rate of the task to infinity, $\refreshrate{\infty}{\infty}$.
522 The interrupt handler is set up in such a way that the refresh rate is changed to $\refreshrate{0}{0}$ once the interrupt triggers.
523 As a consequence, the task is executed on the next execution cycle.
524
525 The \cleaninline{pirSwitch} function in \cref{lst:pirSwitch} creates, given an interval in \unit{\ms}, a task that reacts to motion detection by a \gls{PIR} sensor (connected to \gls{GPIO} pin 0) by lighting the \gls{LED} connected to \gls{GPIO} pin 13 for the given interval.
526 The system lightens the \gls{LED} again when there is still motion detected after this interval.
527 By changing the interrupt mode in this program text from \cleaninline{High} to \cleaninline{Rising} the system lights the \gls{LED} only one interval when it detects motion no matter how long this signal is present at the \gls{PIR} pin.
528
529 \begin{lstClean}[caption={Example of a toggle light switch using interrupts.},label={lst:pirSwitch}]
530 pirSwitch :: Int -> Main (MTask v Bool) | mtask v
531 pirSwitch =
532 declarePin D13 PMOutput \ledpin->
533 declarePin D0 PMInput \pirpin->
534 {main = rpeat ( interrupt high pirpin
535 >>|. writeD ledpin false
536 >>|. delay (lit interval)
537 >>|. writeD ledpin true) }
538 \end{lstClean}
539
540 \subsection{\texorpdfstring{\Gls{MTASK}}{MTask} engine}
541
542 While interrupt tasks have their own node type in the task tree, they differ slightly from other node types because they require a more elaborate setup and teardown.
543 Enabling and disabling interrupts is done in a general way in which tasks register themselves after creation and deregister after deletion.
544 Interrupts should be disabled when there are no tasks waiting for that kind of interrupt because unused interrupts can lead to unwanted wake ups, and only one kind of interrupt can be attached to a pin.
545
546 \subsubsection{Event registration}
547 The \gls{MTASK} \gls{RTS} contains an event administration to register which task is waiting on which event.
548 During the setup of an interrupt task, the event administration in the \gls{MTASK} \gls{RTS} is checked to determine whether a new \gls{ISR} for the particular pin needs to be registered.
549 Furthermore, this registration allows for a quick lookup in the \gls{ISR} to find the tasks listening to the events.
550 Conversely, during the teardown, the \gls{ISR} is disabled again when the last interrupt task of that kind is deleted.
551 The registration is light-weight and consists only of an event identifier and task identifier.
552 This event registration is stored as a linked list of task tree nodes so that the garbage collector can clean them up when they become unused.
553
554 Registering and deregistering interrupts is a device specific procedure, although most supported devices use the \gls{ARDUINO} \gls{API} for this.
555 Which pins support which interrupt differs greatly from device to device but this information is known at compile time.
556 At the time of registration, the \gls{RTS} checks whether the interrupt is valid and throws an \gls{MTASK} exception if it is not.
557 Moreover, an exception is thrown if multiple types of interrupts are registered on the same pin.
558
559 \subsubsection{Triggering interrupts}
560 Once an interrupt fires, tasks registered to that interrupt are not immediately evaluated because it is usually not safe to do.
561 For example, the interrupt could fire in the middle of a garbage collection process, resulting in incorrect pointers.
562 Furthermore, as the \gls{ISR} is supposed to be be very short, just a flag in the event administration is set.
563 Interrupt event flags are processed at the beginning of the event loop, before tasks are executed.
564 For each subscribed task, the task tree is searched for nodes listening for the particular interrupt.
565 When found, the node is flagged and the pin status is written.
566 Afterwards, the evaluation interval of the task is set to $\refreshrate{0}{0}$ and the task is reinsterted at the front of the scheduling queue to ensure rapid evaluation of the task.
567 Finally, the event is removed from the registration and the interrupt is disabled.
568 The interrupt can be disabled as all tasks waiting for the interrupt become stable after firing.
569 More occurrences of the interrupts do not change the value of the task as stable tasks keep the same value forever.
570 Therefore, it is no longer necessary to keep the interrupt enabled, and it is relatively cheap to enable it again if needed in the future.
571 Evaluating interrupt task node in the task tree is trivial because all of the work was already done when the interrupt was triggered.
572 The task emits the status of the pin as a stable value if the information in the task shows that it was triggered.
573 Otherwise, no value is emitted.
574
575 \input{subfilepostamble}
576 \end{document}