A high-performance, real-time digital signal processing library implementing a 4th-order Infinite Impulse Response (IIR) Butterworth Bandpass Filter. Designed specifically for embedded microcontrollers like the Arduino Uno R4 WiFi (ARM Cortex-M4) to clean up ECG/biopotential waveforms from analog frontends like the AD8232.
This filter completely attenuates low-frequency baseline wander (< 0.5 Hz) caused by respiration and electrode movement, as well as high-frequency muscle artifacts / powerline harmonics (> 40 Hz), yielding a crisp, diagnostic-grade trace ready for QRS feature extraction and BPM calculation.
- 4th-Order Precision: Aggressive roll-off slope to separate true cardiac vectors from physiological noise.
- Cascaded Biquad Structure: Implemented using Direct Form I Cascaded Second-Order Sections (Biquads) to prevent numerical rounding errors and maintain absolute floating-point stability.
- Hardware Accelerated Ready: Perfectly optimized for the Renesas RA4M1 48MHz FPU on the Arduino Uno R4.
- Deterministic Execution: Ultra-lightweight per-step time complexity (O(1)) makes it highly safe for hardware interrupts and strict real-time loops.
| Parameter | Value | Description |
|---|---|---|
| Sampling Frequency (Fs) | 125 Hz | Fixed temporal step (8000 μs interval) |
| Low Cutoff (Fc1) | 0.5 Hz | Eliminates deep breathing & baseline sway |
| High Cutoff (Fc2) | 40.0 Hz | Crushes high-frequency electromyogram (EMG) noise |
| Passband Ripple | 0 dB | Flat passband response (Butterworth characteristic) |
The repository matches the standard PlatformIO Library Manager specification:
ECGFilter/
├── docs/ # Extended design documentation & transfer functions
├── examples/ # Out-of-the-box working application examples
│ └── basic_filtering/
│ └── basic_filtering.ino
├── src/ # Embedded source code
│ ├── ECGFilter.cpp
│ └── ECGFilter.h
├── library.json # PlatformIO metadata configurations
└── README.md # Library documentation
---
## 📦 Installation
### PlatformIO (Recommended)
Add the library dependency directly to your `platformio.ini` environment configuration:
```ini
lib_deps =
anuragpanda/ECGFilter @ ^1.0.1
- Download the latest source package release as a
.zip. - Navigate to Sketch → Include Library → Add .ZIP Library... inside the IDE.
To achieve accurate digital filtering, the analog frontend must be read at a highly consistent, uniform sampling rate. The example below configures a hardware independent micros-based loop to run precisely at 125 Hz (8000 μs interval).
#include <Arduino.h>
#include <EEGFilter.h>
// Hardware Pin Configuration
const int PIN_EEG_OUT = A0; // Analog input reading the raw conditioned EEG waveform
// Instantiate the EEGFilter core engine instance
EEGFilter filter;
// Fixed temporal constraint configuration:
// 1,000,000 microseconds / 256 Hz sampling rate = 3,906.25 microseconds per sample step
// We use 3906 microseconds to maintain alignment close to the target interval
const unsigned long TIMESTEP_US = 3906;
unsigned long scheduledTime = 0;
void setup() {
// Open high-speed serial pipeline for stable telemetry visualization
Serial.begin(115200);
while (!Serial) {
; // Pause execution until hardware serial stream synchronizes
}
// Initialize filter internal delay registers and state structures
filter.begin();
// Seed initial clock tracking variable
scheduledTime = micros();
}
void loop() {
// Strict, jitter-free real-time execution checker via rollover-safe subtraction
if (micros() - scheduledTime >= TIMESTEP_US) {
// Increment next step checkpoint execution marker dynamically
scheduledTime += TIMESTEP_US;
// Sample the raw instantaneous ADC voltage channel
int rawADC = analogRead(PIN_EEG_OUT);
// Execute the 4th-order biquad DSP progression step (0.5 Hz - 29.5 Hz)
float cleanEEG = filter.step(static_cast<float>(rawADC));
// Output space-delimited trace logs directly optimized for Telemetry/Serial Plotters
Serial.print(rawADC);
Serial.print(" ");
Serial.println(cleanEEG);
}
}When plotting the raw stream against the filtered stream in your IDE's telemetry view:
-
Raw Trace (
$A_0$ input): Susceptible to sudden diagonal shifting or shifting vertically across the grid window as the patient takes deep breaths. -
Filtered Trace (
filter.step()output): Stays strictly anchored to a steady horizontal line. The P-waves, QRS complexes, and T-waves emerge perfectly sharpened, with zero phase-shift jitter or noise ripples.
This project is licensed under the terms of the MIT License. Check out library.json for full distribution details. Developed for reliable biopotential signal conditioning on modern embedded architectures.