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
