ReactiveArduino implements observable-observer pattern on a processor like Arduino. The purpose is to provide declarative programming approach, within the capacity constraints of a low-power MCU.
ReactiveArduino is heavly based on ReactiveX and ReactiveUI, adapted to the needs and limitations in a MCU.
The general use of ReactiveArduino consists of:
- Define an observable (Timer, Interval, FromArray, FromProperty...)
- Chain with one or more operators (Distinct, Where, Select, First, Last, Sum...)
- Subscribe an observer (Do, DoFinally, ToProperty, ToArray...)
For example:
Reactive::FromRange<int>(10, 20)
>> Reactive::Select<int>([](int x) { return x + 10; })
>> Reactive::DoAndFinally<int>(
[](int x) { Serial.println(x); },
[]() { Serial.println("No more items");
});
More examples in Wiki/Examples
More info about the Observables, Observers, and Operators available in the Wiki
Operators are generally generated through factory methods provided by the Reactive class. For example:
Reactive::FromArray<int>(values, valuesLength)
To chain operators ReactiveArduino uses overload of operator >>
, which allows to combine observable and observers.
For example:
observableInt >> Reactive::ToSerial<bool>();
ReactiveArduino intensively uses templating to define the type of data that an operator receives or emits. Some operators need one template
Reactive::Count<float>()
But other operators need two templates
Reactive::Cast<int, float>()
And other operators need no templates.
ReactiveArduino has two types of observables, hot and cold.
Hot observables emits the sequence when an observer subscribes to it. For example, FromArray(...)
is a Hot Observable.
Reactive::FromArray<int>(values, valuesLength)
Cold observable does not emits any item when a observer suscribes to it. You have to explicitly call the Next()
method whenever you want. For example, FromArrayDefer(...)
Reactive::FromArrayDefer<int>(values, valuesLength)
On many occasions we generate operators directly when we chain them, for example in the Setup()
. However, creating an operator allocates dynamic memory. Therefore, you should avoid creating them in Loop()
, or you coul run out of memory.
If you need to reuse (tipically, call some operator method later in your code) set it as a global variable, and chain as normal.
auto counter = Reactive::Count<int>();
...
//(later in code)
...
obsString >> counter >> Reactive::ToSerial<String>();
This example show how to use Reactive Operator to perform a 3-elements Median filter, then a 4-elements Moving Average Filter, and make some action with the final filtered value.
#include "ReactiveArduinoLib.h"
int values[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int valuesLength = sizeof(values) / sizeof(values[0]);
void setup()
{
Serial.begin(9600);
while (!Serial) delay(1);
Reactive::FromArray<int>(values, valuesLength)
>> Reactive::Cast<int, float>()
>> Reactive::Median3<float>()
>> Reactive::MovingAverage<float>(4)
>> Reactive::DoAndFinally<float>(
[](float x) { Serial.println(x); },
[]() { Serial.println("No more items"); }
);
}
void loop()
{
delay(2000);
}
More examples in Wiki/Examples