The Moving Average Trend Spotter
Press the pushbutton to test the interactive circuit in real time.
- Arduino Uno microcontroller board
- LED indicator (green)
- 220 ohm current-limiting resistor
- Tactile pushbutton switch
- Jumper wires (male-male)
🔧 Build Your Moving Average Filter
const int SENSOR_PIN = A0;
const int WINDOW_SIZE = 10;
int readings[WINDOW_SIZE];
int readIndex = 0;
long runningSum = 0;
int smoothedValue = 0;
void setup() {
Serial.begin(115200);
for (int i = 0; i < WINDOW_SIZE; i++) {
readings[i] = analogRead(SENSOR_PIN);
runningSum += readings[i];
}
Serial.println("Raw,Smoothed");
}
void loop() {
int rawReading = analogRead(SENSOR_PIN);
// Constant-time O(1) circular buffer update
runningSum -= readings[readIndex];
readings[readIndex] = rawReading;
runningSum += readings[readIndex];
readIndex = (readIndex + 1) % WINDOW_SIZE;
smoothedValue = runningSum / WINDOW_SIZE;
Serial.print(rawReading);
Serial.print(",");
Serial.println(smoothedValue);
delay(50);
}
Maintaining runningSum enables O(1) updates without expensive iterative array looping in embedded memory.
⚗️ Test your model
| Test Condition / Action | Expected Result |
|---|---|
| Thermal ADC Noise (±15 raw counts) | Smoothed output variance drops to < ±1 count |
| Sudden Step Input (+200 counts) | Smoothed output cleanly ramps to new level over exactly 10 samples |
| Sustained DC Input | Zero steady-state error; smoothed output matches mean raw input |
Calculate and output the real-time sample variance in the loop to identify when the sensor has entered a turbulent state!
📖 Our Story & Concepts
Analog-to-Digital Converters (ADCs) in microcontrollers convert continuous physical voltages into discrete integer counts. In real circuits, power supply ripple, electromagnetic interference (EMI), and thermal agitation induce high-frequency white noise.
In medical electrocardiograms (ECGs) and industrial thermal kilns, calculating an N-point Simple Moving Average (SMA) acts as an optimal finite impulse response (FIR) low-pass filter. Implementing this with a circular ring buffer ensures execution time remains independent of window length.
</> Full Annotated Code Walkthrough
// Complete Circular Buffer with Real-Time Statistical Variance
const int SENSOR_PIN = A0;
const int N = 10;
int buf[N];
int idx = 0;
long sum = 0;
void setup() {
Serial.begin(115200);
for (int i = 0; i < N; i++) {
buf[i] = analogRead(SENSOR_PIN);
sum += buf[i];
}
}
void loop() {
int raw = analogRead(SENSOR_PIN);
sum -= buf[idx];
buf[idx] = raw;
sum += buf[idx];
idx = (idx + 1) % N;
float mean = (float)sum / N;
// Compute Variance
float varSum = 0;
for (int i = 0; i < N; i++) {
float diff = buf[i] - mean;
varSum += diff * diff;
}
float variance = varSum / N;
Serial.print("Raw:"); Serial.print(raw);
Serial.print(",Mean:"); Serial.print(mean);
Serial.print(",Variance:"); Serial.println(variance);
delay(100);
}
Maintaining runningSum enables O(1) updates without expensive iterative array looping in embedded memory.
▷ Expected Results & Troubleshooting
| Test Condition / Action | Expected Result |
|---|---|
| Thermal ADC Noise (±15 raw counts) | Smoothed output variance drops to < ±1 count |
| Sudden Step Input (+200 counts) | Smoothed output cleanly ramps to new level over exactly 10 samples |
| Sustained DC Input | Zero steady-state error; smoothed output matches mean raw input |
⚑ Extension Challenges & Blueprints
- Level 1: Implement an Exponential Moving Average (EMA) with configurable smoothing factor alpha (0.1 to 0.9).
- Level 2: Benchmark CPU execution cycles between circular buffer O(1) and array recalculation O(N).
- Level 3: Apply the filter to an ultrasonic rangefinder to eliminate acoustic reflection spikes.