+devTask =
+ DHT (DHT_DHT pin DHT11) \dht =
+ liftsds \localSds = tempSds
+ In {main = rpeat (temperature` delayTime dht >>~. setSds localSds)}
+\end{lstClean}
+
+\subsubsection{Repeating tasks}
+The task combinator \cleaninline{rpeat} restarts the child task in the evaluation if the previous produced a stable result.
+However, in some cases it is desirable to postpone the restart of the child.
+For this, the \cleaninline{rpeatEvery} task is introduced which receives an extra argument, the refresh rate, as shown in \cref{lst:rpeatevery}.
+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{}
+\footnotetext{In reality, it also compensates for time drift by taking into account the upper bound of the timing interval.
+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.}
+
+\begin{lstClean}[caption={Repeat task combinator with a timing interval.},label={lst:rpeatevery}]
+class rpeat v where
+ rpeatEvery v :: (TimingInterval v) (MTask v t) -> MTask v t
+ rpeat :: (MTask v t) -> MTask v t
+\end{lstClean}
+
+\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}.
+The \cleaninline{timedPulse} function creates a task that sends a \qty{50}{\ms} pulse to the \gls{GPIO} pin 0 every second.
+The task created by the \cleaninline{timedPulseNaive} functions emulates the behaviour by using \cleaninline{rpeat} and \cleaninline{delay}.
+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.
+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.
+
+\begin{lstClean}[caption={Example program for the repeat task combinator with a timing interval.},label={lst:rpeateveryex}]
+timedPulse :: Main (MTask v Bool) | mtask v
+timedPulse = declarePin D0 PMOutput \d0->
+ in {main = rpeatEvery (ExactSec (lit 1)) (
+ writeD d0 true
+ >>|. delay (lit 50)
+ >>|. writeD d0 false
+ )
+ }
+
+timedPulseNaive :: Main (MTask v Bool) | mtask v
+timedPulseNaive = declarePin D0 PMOutput \d0->
+ {main = rpeat (
+ writeD d0 true
+ >>|. delay (lit 50)
+ >>|. writeD d0 false
+ >>|. delay (lit 950))
+ }
+\end{lstClean}
+
+\section{Task scheduling in the \texorpdfstring{\gls{MTASK}}{mTask} engine}
+The refresh rates from the previous section only tell us how much the next evaluation of the task can be delayed.
+An \gls{IOT} edge devices executes multiple tasks may run interleaved.
+In addition, it has to communicate with a server to collect new tasks and updates of \glspl{SDS}.
+Hence, the refresh intervals cannot be used directly to let the microcontroller sleep.
+Our scheduler has the following objectives.
+\begin{itemize}
+ \item
+ Meet the deadline whenever possible, i.e.\ the system tries to execute every task before the end of its refresh interval.
+ Only too much work on the device might cause an overflow of the deadline.
+ \item
+ Achieve long sleep times. Waking up from sleep consumes some energy and takes some time.
+ Hence, we prefer a single long sleep over splitting this interval into several smaller pieces.
+ \item
+ The scheduler tries to avoid unnecessary evaluations of tasks as much as possible.
+ A task should not be evaluated now when its execution can also be delayed until the next time that the device is active.
+ That is, a task should preferably not be executed before the start of its refresh interval.
+ 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.
+ \item
+ The optimal power state should be selected.
+ Although a system uses less power in a deep sleep mode, it also takes more time and energy to wake up from deep sleep.
+ 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.
+\end{itemize}
+
+The algorithm $\mathcal{R}$ from \cref{sec:deriving_refresh_rates} computes the evaluation rate of the current tasks.
+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.
+We obtain those bounds by adding the current system time to the bounds of the computed refresh interval by algorithm $\mathcal{R}$.
+
+For the implementation, it is important to note that the evaluation of a task takes time.
+Some tasks are extremely fast, but other tasks require long computations and time-consuming communication with peripherals as well as with the server.
+These execution times can yield a considerable and noticeable time drift in \gls{MTASK} programs.
+For instance, a task like \cleaninline{rpeatEvery (ExactMs 1) t} should repeat \cleaninline{t} every millisecond.
+The programmer might expect that \cleaninline{t} will be executed for the ${(N+1)}^{th}$ time after $N$ milliseconds.
+Uncompensated time drift might make this considerably later.
+\Gls{MTASK} does not pretend to be a hard real-time \gls{OS}, and cannot give firm guarantees with respect to evaluation time.
+Nevertheless, we try to make time handling as reliable as possible.
+This is achieved by adding the start time of this round of task evaluations rather than the current time to compute absolute execution intervals.
+
+\subsection{Scheduling Tasks}
+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}.
+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.
+Next, the microcontroller goes to light sleep for the minimum of a predefined interval and the task delay.
+
+In general, the microcontroller node executes multiple \gls{MTASK} tasks at the same time.
+\Gls{MTASK} nodes repeatedly check for inputs from servers and execute all tasks that cannot be delayed to the next evaluation round one step.
+The tasks are stored in a priority queue to check efficiently which of them need to be stepped.
+The \gls{MTASK} tasks are ordered at their absolute latest start time in this queue; the earliest deadline first.
+We use the earliest deadline to order tasks with equal latest deadline.
+
+It is very complicated to make an optimal scheduling algorithm for tasks to minimize the energy consumption.
+We use a simple heuristic to evaluate tasks and determine sleep time rather than wasting energy on a fancy evaluation algorithm.
+\Cref{lst:evalutionRound} gives this algorithm in pseudo code.
+First the \gls{MTASK} node checks for new tasks and updates of \glspl{SDS}.
+This communication adds any task to the queue.
+The \cleaninline{stepped} set contains all tasks evaluated in this evaluation round.
+Next, we evaluate tasks from the queue until we encounter a task that has an evaluation interval that is not started.
+This might evaluate tasks earlier than required, but maximizes the opportunities to sleep after this evaluation round.
+%Using the \prog{stepped} set ensures that we evaluate each task at most once during an evaluation round.
+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.
+A task that produces a stable value is completed and is not queued again.
+
+\begin{algorithm}
+\DontPrintSemicolon
+\SetKwProg{Repeatt}{repeat}{}{end}
+\KwData{queue = []\;}
+\Begin{
+ \Repeatt{}{
+ queue += communicateWithServer\;
+ stepped = []\tcp*{tasks stepped in this round}
+ \While{notEmpty(queue) $\wedge$ earliestDeadline(top(queue)) $\leq$ currentTime}{
+ (task, queue) = pop(queue)\;
+ task2 = step(task)\tcp*{computes new execution interval}
+ \If{$\neg$ isStable(task2)\tcp*{not finished after step}}{
+ stepped += task2\;
+ }
+ }
+ queue = merge(queue, stepped)\;
+ sleep(queue)\;
+ }
+}
+\caption{Pseudo code for the evaluation round of tasks in the queue.}
+\label{lst:evalutionRound}
+\end{algorithm}
+
+The \cleaninline{sleep} function determines the maximum sleep time based on the top of the queue.
+The computed sleep time and the characteristics of the microprocessor determine the length and depth of the sleep.
+For very short sleep times it might not be worthwhile to sleep.
+In the current \gls{MTASK} \gls{RTS}, the thresholds are determined by experimentation but can be tuned by the programmer.
+On systems that lose the content of their \gls{RAM} it is not possible to go to deep sleep mode.
+
+\section{Interrupts}\label{lst:interrupts}
+Most microcontrollers have built-in support for processor interrupts.
+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}.
+While the \glspl{ISR} look like regular functions, they do come with some limitations.
+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.
+After the execution of the \gls{ISR}, the normal program flow is resumed.
+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.
+
+Interrupts offer two substantial benefits: fewer missed events and better energy usage.
+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.
+Using interrupts is not a fool-proof way of never missing an event.
+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.
+There are also some sensors, such as the CCS811 air quality sensor, with support for triggering interrupts when a value exceeds a critical limit.
+
+There are several different types of interrupts possible.
+\begin{table}
+ \centering
+ \caption{Overview of \gls{GPIO} interrupt types.}%
+ \label{tbl:gpio_interrupts}
+ \begin{tabular}{ll}
+ \toprule
+ type & triggers\\
+ \midrule
+ change & input changes\\
+ falling & input becomes low\\
+ rising & input becomes high\\
+ low & input is low\\
+ high & input is high\\
+ \bottomrule
+ \end{tabular}
+\end{table}
+
+\subsection{\Gls{ARDUINO} platform}
+\Cref{lst:arduino_interrupt} shows an exemplatory program utilising interrupts written in \gls{ARDUINO}'s \gls{CPP} dialect.
+The example shows a debounced light switch for the built-in \gls{LED} connected to \gls{GPIO} pin 13.
+When the user presses the button connected to \gls{GPIO} pin 11, the state of the \gls{LED} changes.
+As buttons sometimes induce noise shortly after pressing, events within \qty{30}{\ms} after pressing are ignored.
+In between the button presses, the device goes into deep sleep using the \arduinoinline{LowPower} library.
+
+\Crefrange{lst:arduino_interrupt:defs_fro}{lst:arduino_interrupt:defs_to} defines the pin and debounce constants.
+\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.
+\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.
+
+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.
+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}.
+This \gls{ISR} checks if the program is in cooldown state.
+If this is not the case, the state of the \gls{LED} is toggled.
+In any case, the program goes into cooldown state afterwards.
+
+In the \arduinoinline{loop} function, the microcontroller goes to low-power sleep immediately and indefinitely.
+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.
+
+\begin{lstArduino}[numbers=left,label={lst:arduino_interrupt},caption={Light switch using interrupts.}]
+#define LEDPIN 13[+\label{lst:arduino_interrupt:defs_fro}+]
+#define INTERRUPTPIN 11
+#define DEBOUNCE 30[+\label{lst:arduino_interrupt:defs_to}+]
+
+volatile byte state = LOW;[+\label{lst:arduino_interrupt:state}+]
+volatile bool cooldown = true;[+\label{lst:arduino_interrupt:cooldown}+]
+
+void setup() {[+\label{lst:arduino_interrupt:setup_fro}+]
+ pinMode(LEDPIN, OUTPUT);
+ pinMode(INTERRUPTPIN, INPUT);
+ LowPower.attachInterruptWakeup(INTERRUPTPIN, buttonPressed, RISING);
+}[+\label{lst:arduino_interrupt:setup_to}+]
+
+void loop() {[+\label{lst:arduino_interrupt:loop_fro}+]
+ LowPower.sleep();
+ digitalWrite(LEDPIN, state);
+ delay(DEBOUNCE);
+ cooldown = false;
+}[+\label{lst:arduino_interrupt:loop_to}+]
+
+void buttonPressed() {[+\label{lst:arduino_interrupt:isr_fro}+]
+ if (!cooldown)
+ state = !state;
+ cooldown = true;
+}[+\label{lst:arduino_interrupt:isr_to}+]
+\end{lstArduino}
+
+\subsection{\texorpdfstring{\Gls{MTASK}}{MTask} language}
+\Cref{lst:mtask_interrupts} shows the interrupt interface in \gls{MTASK}.
+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.
+Lowercase variants of the various interrupt modes such as \cleaninline{change :== lit Change} are available as convenience macros (see \cref{sec:expressions}).
+
+\begin{lstClean}[label={lst:mtask_interrupts},caption={The interrupt interface in \gls{MTASK}.}]
+class interrupt v where
+ interrupt :: (v InterruptMode) (v p) -> MTask v Bool | pin p
+
+:: InterruptMode = Change | Rising | Falling | Low | High