Reactive Arduino implements the observer-observable pattern on a processor like Arduino. The purpose is to provide an approach to declarative programming within the constraints of a low-power MCU.
Reactive Arduino is based on ReactiveX and ReactiveUI, adapted to the needs and limitations of an MCU.
More documentation on the Wiki of the project on GitHub.
Usage Instructions
The general use of Reactive Arduino consists of:
- Defining an observable (Timer, Interval, FromArray, FromProperty…)
- Chaining with one or more operators (Distinct, Where, Select, First, Last, Sum…)
- Subscribing an observer (Do, DoFinally, ToProperty, ToArray…)
For example:
Reactive::FromRange<int>(10, 20)
>> Reactive::Select<int>([](int x) { return x + 10; })
>> Reactive::ToSerial<int>();
>> Reactive::DoAndFinally<int>(
[](int x) { Serial.println(x); },
[]() { Serial.println("No more items");
});
Creating Operators
Operators are generally generated through a factory method, provided by the Reactive class. For example:
Reactive::FromArray<int>(values, valuesLength)
Chaining Operators
To chain operators Reactive Arduino uses the ’>>’ operator overload, which allows combining observables and observers.
observableInt >> Reactive::ToSerial<bool>();
Templating
Reactive Arduino uses Templates intensively to define the data type that an operator sends or receives.
Some operators require a single Template, when they emit the same type of data they receive.
Reactive::Count<float>()
While other operators require two Templates, one for the data received and another for the data emitted.
Reactive::Cast<int, float>()
Finally, some operators do not require Templates.
Hot/Cold Observers
Reactive Arduino has two types of observables. ‘Hot’ observables trigger actions when another operator subscribes to it. For example, ‘FromArray(…)’ is a hot observable.
Reactive::FromArray<int>(values, valuesLength)
On the contrary, a cold observer does not generate any action when subscribing to it. To execute it, we must explicitly call the ‘Next()’ method. For example, ‘FromArrayDefer(…)‘.
Reactive::FromArrayDefer<int>(values, valuesLength)
Considerations on Dynamic Memory
In many cases, we will generate the operators directly by chaining the operator, usually during the ‘Setup()‘. However, keep in mind that creating an operator requires the use of dynamic memory. Therefore, we must avoid creating them in repetitive functions such as in the ‘Loop()’ function.
If you need to reuse or access an operator, declare it as a global function and chain it normally.
auto counter = Reactive::Count<int>();
...
//(later in code)
...
obsString >> counter >> Reactive::ToSerial<String>();
Examples
#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::MovingAverage<float>(4)
>> Reactive::DoAndFinally<float>(
[](float x) { Serial.println(x); },
[]() { Serial.println("No more items"); }
);
}
void loop()
{
delay(2000);
}
Code
All the code is available on Github at https://github.com/luisllamasbinaburo/Arduino-ReactiveArduino.