Skip to content

Commit 98fa99b

Browse files
committed
Add examples and core functionalities for ReactiveArduinoLib
- Implement PID Temperature Control example with analog temperature sensor and heater control. - Create Rotary Encoder example demonstrating menu navigation and button press detection. - Introduce Throttle example to demonstrate throttling of fast timer events. - Add Ultrasonic sensor example with distance measurement and alerts for proximity. - Develop FilterHysteresis, FilterKalman, and FilterPID classes for advanced signal processing. - Implement Observable classes for Accelerometer, Rotary Encoder, and Ultrasonic sensor. - Add various operators including Debounce, DistinctUntilChanged, Scan, StartWith, Throttle, and Interpolate for reactive programming.
1 parent 3c9a527 commit 98fa99b

27 files changed

+1967
-6
lines changed

EXTENSIONS.md

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# ReactiveArduino Extensions
2+
3+
This document describes the new reactive methods and operators added to the ReactiveArduino library.
4+
5+
## New Operators
6+
7+
### OperatorThrottle
8+
Limits the rate at which values are emitted from an observable.
9+
10+
```cpp
11+
timer.Throttle(500) // Only emit once every 500ms
12+
```
13+
14+
**Use cases:**
15+
- Rate limiting sensor readings
16+
- Preventing spam in serial output
17+
- Reducing computational load
18+
19+
### OperatorScan
20+
Applies an accumulator function over the observable sequence and emits each intermediate result.
21+
22+
```cpp
23+
sensor.Scan<int>(0, [](int acc, float value) {
24+
return acc + (value > threshold ? 1 : 0);
25+
}) // Count values above threshold
26+
```
27+
28+
**Use cases:**
29+
- Running totals and counters
30+
- State machines
31+
- Progressive calculations
32+
33+
### OperatorStartWith
34+
Emits specified values before beginning to emit values from the source observable.
35+
36+
```cpp
37+
sensor.StartWith(0) // Start with initial value of 0
38+
```
39+
40+
**Use cases:**
41+
- Providing default values
42+
- Initializing UI components
43+
- Setting baseline measurements
44+
45+
### OperatorDebounce
46+
Suppresses values from an observable until a specified time period has passed without another value.
47+
48+
```cpp
49+
button.Debounce(100) // Wait 100ms after last change
50+
```
51+
52+
**Use cases:**
53+
- Button debouncing
54+
- Noise filtering
55+
- Preventing rapid-fire events
56+
57+
### OperatorDistinct
58+
Filters out all duplicate values from the observable sequence (not just consecutive ones).
59+
60+
```cpp
61+
sensor.Distinct() // Only emit unique values
62+
```
63+
64+
**Use cases:**
65+
- Removing duplicate sensor readings
66+
- Event deduplication
67+
- Unique value collection
68+
- Memory-conscious duplicate filtering (limited to 32 unique values by default)
69+
70+
## New Filters
71+
72+
### FilterHysteresis
73+
Implements hysteresis filtering to prevent oscillation around threshold values.
74+
75+
```cpp
76+
sensor.Hysteresis(10.0, 20.0) // Low threshold 10, high threshold 20
77+
```
78+
79+
**Use cases:**
80+
- Thermostat control
81+
- Motion detection
82+
- Level sensing with noise immunity
83+
84+
### FilterKalman
85+
Applies Kalman filtering for optimal estimation in the presence of noise.
86+
87+
```cpp
88+
sensor.Kalman(0.1, 4.0) // Process variance 0.1, measurement variance 4.0
89+
```
90+
91+
**Use cases:**
92+
- Sensor fusion
93+
- Position tracking
94+
- Noise reduction in measurements
95+
96+
### FilterPID
97+
Implements a complete PID (Proportional-Integral-Derivative) controller for closed-loop control systems.
98+
99+
```cpp
100+
auto pidController = FilterPID<float>(25.0, 2.0, 0.1, 0.5, 0, 255);
101+
sensor.PID(25.0, 2.0, 0.1, 0.5, 0, 255) // Setpoint, Kp, Ki, Kd, output min, max
102+
```
103+
104+
**Features:**
105+
- Configurable P, I, D gains
106+
- Integral windup protection
107+
- Derivative kick prevention
108+
- Adjustable output limits
109+
- Real-time tuning methods
110+
111+
**Use cases:**
112+
- Temperature control
113+
- Motor speed control
114+
- Position control
115+
- Level control
116+
- Process automation
117+
118+
## New Observables
119+
120+
### ObservableAccelerometer
121+
Monitors 3-axis accelerometer data from analog pins.
122+
123+
```cpp
124+
auto accel = ObservableAccelerometer<AccelerometerData>(A0, A1, A2, 100);
125+
accel.SetSensitivity(3.3);
126+
accel.SetZeroOffset(0.0, 0.0, 0.0);
127+
```
128+
129+
**Features:**
130+
- Configurable sensitivity and zero offset
131+
- Magnitude calculation
132+
- Adjustable sampling rate
133+
134+
### ObservableUltrasonic
135+
Measures distance using HC-SR04 ultrasonic sensors.
136+
137+
```cpp
138+
auto ultrasonic = ObservableUltrasonic<float>(7, 8, 250);
139+
ultrasonic.SetMaxDistance(200.0);
140+
ultrasonic.SetTemperature(25.0); // Temperature compensation
141+
```
142+
143+
**Features:**
144+
- Temperature compensation for accuracy
145+
- Configurable maximum range
146+
- Built-in timeout handling
147+
148+
### ObservableRotaryEncoder
149+
Monitors rotary encoder rotation and button presses.
150+
151+
```cpp
152+
auto encoder = ObservableRotaryEncoder<RotaryEncoderData>(2, 3, 4);
153+
```
154+
155+
**Features:**
156+
- Direction detection
157+
- Position tracking
158+
- Button state monitoring
159+
- Built-in debouncing
160+
161+
## New Transformations
162+
163+
### TransformationInterpolate
164+
Maps input values from one range to another with optional constraining.
165+
166+
```cpp
167+
sensor.Interpolate(0, 1023, 0.0, 5.0) // Map ADC to voltage
168+
```
169+
170+
**Features:**
171+
- Linear interpolation
172+
- Optional value constraining
173+
- Supports any numeric type
174+
175+
## Usage Examples
176+
177+
### Smart Thermostat
178+
```cpp
179+
auto thermistor = AnalogInput(A0, 1000);
180+
181+
thermistor
182+
.AdcToVoltage()
183+
.Select([](float voltage) { return voltageToTemperature(voltage); })
184+
.Kalman(0.1, 1.0) // Noise filtering
185+
.Hysteresis(20.0, 22.0) // Prevent oscillation
186+
.Do([](float temp) {
187+
digitalWrite(HEATER_PIN, temp < 21.0);
188+
});
189+
```
190+
191+
### Motion-Activated Light
192+
```cpp
193+
auto motion = ObservableAccelerometer<AccelerometerData>(A0, A1, A2);
194+
195+
motion
196+
.Select([](AccelerometerData data) { return data.magnitude; })
197+
.Where([](float mag) { return mag > 1.5; }) // Motion threshold
198+
.Throttle(5000) // Stay on for 5 seconds minimum
199+
.Do([](float mag) {
200+
digitalWrite(LED_PIN, HIGH);
201+
// Set timer to turn off later
202+
});
203+
```
204+
205+
### Distance Warning System
206+
```cpp
207+
auto distance = ObservableUltrasonic<float>(7, 8);
208+
209+
distance
210+
.Kalman(0.1, 2.0) // Smooth readings
211+
.Where([](float d) { return d < 20.0; }) // Danger zone
212+
.Scan<int>(0, [](int count, float d) {
213+
return d < 10.0 ? count + 1 : 0; // Count close readings
214+
})
215+
.Where([](int count) { return count > 3; }) // Sustained proximity
216+
.Do([](int count) {
217+
digitalWrite(BUZZER_PIN, HIGH);
218+
});
219+
```
220+
221+
### PID Temperature Controller
222+
```cpp
223+
auto tempSensor = AnalogInput(A0, 200);
224+
auto pidController = FilterPID<float>(25.0, 2.0, 0.1, 0.5, 0, 255);
225+
226+
tempSensor
227+
.Select([](int adc) { return adcToTemperature(adc); })
228+
.Kalman(0.1, 1.0) // Smooth readings
229+
.PID(25.0, 2.0, 0.1, 0.5, 0, 255) // PID control
230+
.Do([](float output) {
231+
analogWrite(HEATER_PIN, (int)output);
232+
});
233+
234+
// Dynamic setpoint adjustment
235+
pidController.SetSetpoint(newTarget);
236+
pidController.SetTunings(kp, ki, kd);
237+
```
238+
239+
### PID Motor Speed Control
240+
```cpp
241+
auto encoder = ObservableRotaryEncoder<RotaryEncoderData>(2, 3);
242+
auto speedTimer = IntervalMillis(250);
243+
244+
speedTimer
245+
.Select([](unsigned long t) { return calculateRPM(); })
246+
.PID(targetRPM, 1.5, 0.2, 0.1, -255, 255)
247+
.Do([](float output) {
248+
setMotorSpeed(output); // Apply to motor driver
249+
});
250+
```
251+
252+
### Sensor Event Detection
253+
```cpp
254+
auto motionSensor = AnalogInput(A0, 100);
255+
256+
motionSensor
257+
.Where([](int value) { return value > 512; }) // Motion threshold
258+
.Distinct() // Only report unique motion events
259+
.Do([](int value) {
260+
Serial.println("Motion detected!");
261+
triggerAlarm();
262+
});
263+
```
264+
265+
## Performance Considerations
266+
267+
- **Memory Usage**: New operators allocate minimal additional memory
268+
- **CPU Impact**: Kalman filter requires floating-point arithmetic
269+
- **Update Frequency**: Call `Update()` methods in your main loop
270+
- **Debouncing**: Built-in debouncing reduces CPU load from rapid changes
271+
272+
## Best Practices
273+
274+
1. **Chain Wisely**: Order operators for efficiency (filters before transformations)
275+
2. **Throttle Output**: Use throttling for serial output and actuator control
276+
3. **Calibrate Sensors**: Always calibrate accelerometers and other analog sensors
277+
4. **Temperature Compensation**: Use temperature correction for precise measurements
278+
5. **Error Handling**: Check sensor validity before processing data
279+
6. **PID Tuning**:
280+
- Start with P-only control, then add I and D
281+
- Use Ziegler-Nichols or other systematic tuning methods
282+
- Monitor for integral windup and oscillation
283+
- Adjust sample time based on system response speed
284+
- Set appropriate output limits to prevent actuator saturation
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/***************************************************
2+
Copyright (c) 2019 Luis Llamas
3+
(www.luisllamas.es)
4+
5+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License
8+
****************************************************/
9+
10+
#include "ReactiveArduinoLib.h"
11+
using namespace Reactive;
12+
13+
// Example demonstrating accelerometer observable and scan operator
14+
auto accelerometer = ObservableAccelerometer<AccelerometerData>(A0, A1, A2, 100); // X, Y, Z pins, 100ms interval
15+
int stepCount = 0;
16+
17+
void setup()
18+
{
19+
Serial.begin(115200);
20+
21+
// Configure accelerometer
22+
accelerometer.SetSensitivity(3.3); // 3.3V sensitivity
23+
accelerometer.SetZeroOffset(0.0, 0.0, 0.0);
24+
25+
// Step counter using scan operator
26+
accelerometer
27+
.Select([](AccelerometerData data) { return data.magnitude; })
28+
.Where([](float magnitude) { return magnitude > 1.2; }) // Motion threshold
29+
.DistinctUntilChanged() // Only count distinct movements
30+
.Scan<int>(0, [](int count, float magnitude) { return count + 1; }) // Accumulate steps
31+
.Do([](int steps) {
32+
Serial.print("Steps counted: ");
33+
Serial.println(steps);
34+
});
35+
36+
// Tilt detection
37+
accelerometer
38+
.Select([](AccelerometerData data) {
39+
return atan2(data.y, data.x) * 180.0 / PI; // Calculate tilt angle
40+
})
41+
.Interpolate(-90.0, 90.0, 0, 255) // Map angle to 0-255 range
42+
.Do([](int mappedAngle) {
43+
Serial.print("Tilt mapped to LED brightness: ");
44+
Serial.println(mappedAngle);
45+
analogWrite(9, mappedAngle); // Control LED brightness with tilt
46+
});
47+
}
48+
49+
void loop()
50+
{
51+
accelerometer.Update();
52+
}

0 commit comments

Comments
 (0)