This repository contains an Arduino/C++ pulse oximeter implementation for the MAX30102, based on the MAXIM reference code and the RF algorithm work preserved in README_RF.md.
The current code has been refactored to support:
- interrupt-driven sample acquisition
- rolling updates using a circular sample buffer
- a reusable median filter class (
median_filter.h) - smoothed HR/SpO2 output and OLED display updates
The original RF-focused project history and notes are kept in README_RF.md.
Primary upstream reference:
Compared with the batch-only flow, this fork updates results continuously while still feeding the algorithms a full chronological window:
- Samples are read from MAX30102 using the interrupt pin (
oxiInt). - New samples are written into ring buffers (no shifting of entire arrays).
- A chronological window is assembled from the ring buffer for each algorithm run.
- Results are updated every
UPDATE_STEPsamples instead of waiting for a full refill. - HR/SpO2 smoothing uses a fixed-size median filter that stores invalid sentinels and ignores them when calculating the median.
- Main sketch folder:
max30102_esp32c3_oled_oximeter/ - Main entry sketch:
max30102_esp32c3_oled_oximeter/max30102_esp32c3_oled_oximeter.ino - Supporting code for the sketch is colocated in the same folder:
median_filter.halgorithm_by_RF.cpp/.halgorithm.cpp/.hmax30102.cpp/.h
README_RF.md- archived upstream RF-specific README
- Read
UPDATE_STEPfresh samples (interrupt-gated). - Write into circular red/IR buffers.
- Once the ring has at least
BUFFER_SIZEsamples, build an ordered analysis window. - Run RF (and optional MAXIM) algorithm on that window.
- Push results into median filters (invalid values are preserved as sentinels).
- Output filtered values over Serial and to the display.
You will most likely tune these first:
UPDATE_STEPinmax30102_esp32c3_oled_oximeter.ino: update cadence (smaller = more responsive, noisier)MEDIAN_HISTORYinmax30102_esp32c3_oled_oximeter.ino: smoothing window lengthFS,ST, and quality thresholds inalgorithm_by_RF.h
Important: if you change FS or ST, keep related derived parameters in algorithm_by_RF.h consistent.
- MAX30102 interrupt pin is expected on
oxiInt(currently pin2). - I2C pins are configured in
max30102_esp32c3_oled_oximeter.inoand passed intomaxim_max30102_init(...). - OLED uses U8g2 and is initialized in the sketch; verify pin mapping for your board.
| ESP32-C3 | MAX30102 |
|---|---|
| V5 | VIN |
| GD | GND |
| GPIO2 | INT |
| GPIO5 | SDA |
| GPIO6 | SCL |
If HR/SpO2 is unstable or stuck:
- verify INT wiring and polarity
- confirm red/IR channels are not swapped
- inspect raw PPG waveform quality before tuning algorithm constants
- reduce motion artifacts (finger pressure and movement matter a lot)
- start with smaller
UPDATE_STEPand moderateMEDIAN_HISTORY
For historical debugging/reporting guidance, see README_RF.md.

