🌈 ESP32-S3 Rainbow: ZX Spectrum Emulator Board! Get it on Crowd Supply →
View All Posts
read
Want to keep up to date with the latest posts and videos? Subscribe to the newsletter
HELP SUPPORT MY WORK: If you're feeling flush then please stop by Patreon Or you can make a one off donation via ko-fi

Building a Heart Rate Monitor with an ESP32-C3 and MAX30102

You know how it is. You buy one cute little module from AliExpress and then think ā€œit looks lonely, I’ll get anotherā€. Before you know it, you’ve got a drawer full of cute little modules. So it’s time to put one of them to work.

The ESP32-C3 Board

I’ve got a bunch of these tiny ESP32-C3 boards kicking around. Let’s take a look at one under the microscope.

The brains of the operation is an ESP32-C3 with built-in flash — four megabytes of it, which is pretty decent.

ESP32-C3 board under the microscope

There’s a 3.3 volt regulator on board, and the soldering looks nicely done. It’s a pretty clean, well-designed little board. The antenna is a ceramic one on the side — though I’ve read a few people saying that on these tiny compact boards you don’t get much Wi-Fi signal due to the antenna placement being a bit too close to the buttons and other components. So we’ll avoid Wi-Fi for this project.

ESP32-C3 board under the microscope

The MAX30102 Heart Rate and Blood Oxygen Sensor

For the actual sensor, I’m using a MAX30102 module — a heart rate and blood oxygen monitoring board that I picked up in a recent AliExpress order.

MAX30102 sensor module

The board has a 3.3 volt boost regulator and a 1.8 volt regulator. Some people have reported finding a 2.8 volt regulator instead of 1.8 on some boards — it’s worth checking yours is correct. Mine was fine.

How the Sensor Works

The really interesting part is the sensor itself. Under the microscope you can see:

Close-up of the MAX30102 sensor

  • Two LEDs behind a window — a red LED and an infrared LED
  • A photodetector with a bunch of DSP processing built in

The outputs from this device are just the value of the red channel and the value of the infrared channel. You stick your finger on top and it detects pulse rate using the red LED and blood oxygen levels using the infrared LED.

When you power it up, you can easily see the red LED. If you cover it up and move the lights away, you can even see a slight glow from the infrared LED — the camera on the microscope picks it up nicely.

MAX30102 LEDs

MAX30102 Infra Red LED

Wiring It Up

The wiring is pretty simple — it’s just I2C plus power:

const byte oxiInt = 2; // pin connected to MAX30102 INT
const int SENSOR_I2C_SDA_PIN = 5;
const int SENSOR_I2C_SCL_PIN = 6;
  • Pin 6 → SCL
  • Pin 5 → SDA
  • Pin 2 → Interrupt (tells us when data is ready)
  • 5V → Vin (the board has its own 3.3V regulator)
  • GND → GND

Wiring diagram

A quick I2C scan confirms everything is connected properly — two devices show up: the I2C display and the heart rate monitor.

The Code

The main loop reads samples from the MAX30102 into a ring buffer, then runs the heart rate and SpO2 algorithms once the buffer is full:

void loop() {
  float n_spo2, ratio, correl;
  int8_t ch_spo2_valid;
  int32_t n_heart_rate;
  int8_t ch_hr_valid;

  // Read UPDATE_STEP new samples into the ring buffer
  for(int32_t n = 0; n < UPDATE_STEP; ++n) {
    readNextSampleIntoRing(data_ready_timeout_us);
  }

  // Need a full buffer before we can calculate
  if(sample_count < BUFFER_SIZE) return;
  buildOrderedWindow();

  // Calculate heart rate and SpO2
  rf_heart_rate_and_oxygen_saturation(
    ordered_ir_buffer, BUFFER_SIZE, ordered_red_buffer,
    &n_spo2, &ch_spo2_valid,
    &n_heart_rate, &ch_hr_valid,
    &ratio, &correl);

  // Median filter smooths out noisy readings
  hr_filter.push(ch_hr_valid ? n_heart_rate : INVALID_HR);
  spo2_filter.push(ch_spo2_valid ? n_spo2 : INVALID_SPO2);

  updateDisplay(filtered_heart_rate, (int32_t)filtered_spo2);
}

The display code is pretty straightforward — just show the heart rate and oxygen percentage on the tiny OLED, or dashes if we don’t have a valid reading:

static void updateDisplay(int32_t hr, int32_t spo2) {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_10x20_mr);
  u8g2.setCursor(0, 19);
  if (hr >= 30 && hr <= 250) {
    u8g2.printf("HR: %d", (int)hr);
  } else {
    u8g2.print("HR: --");
  }
  u8g2.setCursor(0, 39);
  if (spo2 >= 0 && spo2 <= 100) {
    u8g2.printf("O2: %d%%", (int)spo2);
  } else {
    u8g2.print("O2: --");
  }
  u8g2.sendBuffer();
}

There’s also a nice touch — the on-board LED flashes in time with your heartbeat.

Getting Readings

With the sketch uploaded and the OLED display showing results, it’s time to test.

Heart rate readings on the display

It can be a bit finicky about finger placement — you need to keep it very still. But once it settles, it starts producing results: heart rate and SpO2 percentage.

Comparing to a Commercial Device

The real test — how does it compare to a proper pulse oximeter?

With both running side by side:

  • Heart rate: around 70 BPM on the ESP32-C3, matching the commercial monitor
  • Blood oxygen: 99% on both devices

Side-by-side comparison with commercial pulse oximeter

Not bad at all. The main challenge is keeping your finger positioned accurately without squishing it on — it’s quite easy to lose the reading. But when you get a good contact, the results match up well.

Wrapping Up

The full code is on GitHub. Grab yourself one of these ESP32-C3 modules, a MAX30102 sensor, and a little OLED display, and you can build something equivalent to a commercial pulse oximeter for very little money. It’s a fun project — why not try it out?

Related Posts

ESP32-S3 Dev Board Assembly - I finally assembled our ESP32-S3 dev boards—used a stencil for easy SMD work, fixed a few tiny USB solder bridges with flux, and even hand-built one for fun. The EPAD isn’t required (per the datasheet), power LEDs look good, and on macOS you can spot it under /dev before flashing. A quick boot-button dance and the blink sketch runs great—full build and walkthrough in the video.
Vibing a PCB - surprisingly good - In my latest adventure, I challenged AI to design a working ESP32-S3 development board from scratch using Atopile and Claude. The idea was as simple as vibe-coding actual hardware without diving into the code. It was a chaotic yet fascinating journey, with some misses like unwired components and a forgotten capacitor. After a few prompts, the AI delivered a surprisingly functional board featuring USB-C, an AMS1117 regulator, and status LEDs. While not yet perfect, vibe-coding feels like a glimpse into the future of hardware design.
Crowdfunding Success - Was it worth it? - I embarked on a thrilling adventure of turning a nifty idea into a tangible product — the ESP32 Rainbow, a Sinclair Spectrum-inspired gadget that merges retro vibes with modern tech. With the brains of an ESP32-S3, capacitive touch, a built-in display, and some snazzy UV silk screen printing, it's a little marvel that got crowdfunded successfully. But, oh boy, was the journey enlightening — from grappling with pricing conundrums and the bureaucracy of certifications to the harrowing odyssey of international shipping. While it didn't line my pockets with gold, seeing people enjoy something I created is worth all the sweat and jitters. Would I do it again? Well, maybe if lightning strikes twice with another great idea.
ESP32-C3 0.42 OLED - Picked up a stack of ESP32-C3 + 0.42" SSD1306 modules and followed an existing guide, but I wasn’t keen on the 128x64-with-offset bodge. I dug into U8g2 and created a proper 72x40 SSD1306 constructor so drawing uses native coordinates. Cleaner code, same tiny display, job done.
Look at my shiny crystal balls - Just upgraded my basic AliExpress crystal balls with some tech wizardry - I've thrown in an ESP32-S3-MINI, a mic, and made them battery powered. Thanks to WLED software, they're now smart and responsive! Shared the KiCAD project for fellow tinkerers. Check out my video to see these balls in action!

Related Videos

$3 AliExpress Heart Rate and Oxygen Sensor Module - You know how it is—you grab one cute little AliExpress module, then another… so I finally put an ESP32-C3 to work. Under the microscope it looks solid (nice 3.3V reg, ceramic antenna I’m not trusting for Wi‑Fi), and I paired it with a MAX30102 heart-rate/SpO2 sensor. Wired up I2C (SCL pin 6, SDA pin 5, INT to pin 2, 5V in), ran a scan, and boom—two devices found. After some finger-fiddling, the readings matched a commercial oximeter. Code’s on GitHub—cheap, fun, and it works.
Drones and Lasers? - It’s been a while since we’ve done an unbagging, and I’ve clearly been on an AliExpress bender. Big thanks to PCBWay for fueling the chaos. Today’s haul: a pair of tiny ESP32‑C3 OLED modules (no idea why I ordered two, but here we are), a finger pulse/SpO2 sensor, a USB‑C PD trigger with DIP switches that happily serves up 5/9/12/15/20V (1+3 gives 20V—very handy), an INMP441‑compatible MS8625 mic, a surprisingly nice transparent clock with temp/humidity, a couple of rechargeable nightlights that only charge on dumb USB because there are no CC resistors, a laser and matching detector (pew pew!), and, bizarrely, a ā€œsmartā€ insulated tea mug that lights up and says 20°. Lots of little projects to spin out from this lot.
Live GPIO Viewer - In my latest discovery, I found an incredibly efficient visualization tool from the last outpost workshop that upgrades how I check the status of the GPIO pins on my ESP32. It's super simple to use and allows me to monitor pin activities directly. I set up a couple of LEDs and a button on various GPIO pins to demonstrate its effectiveness and the results were fantastic. Although it currently supports only digital input and output and PWM, I'm optimistic it will expand with time. Thanks to the open-source community, you can find the setup instructions in a GitHub repo. This tool is a revelation and I highly recommend it.
I Built My Own ESP32-S3 Board… And It Actually Works! - I finally assembled my super simple ESP32‑S3 dev board—voltage regulator, reset button, three status LEDs (5V, 3.3V, and a GPIO blinker), and all pins broken out. I showed two build methods: stencil + hot-plate reflow (quick, with a few USB bridges to clean up) and full hand-solder under the microscope, complete with the rigorous ā€˜solid’ test. Soldered the ESP32‑S3 module (skipping the center thermal pad unless you need it), plugged in, got power LEDs, confirmed USB enumeration, flashed a blink sketch, and we’ve got a blinking LED. Next up: turning this basic dev board into something more professional for production.
Arduino Nano ESP32 - It's nice - But probably not for me. - In this video, I took a deep dive into the Arduino Nano ESP32 and compared it with other available boards in the market. Despite its seemingly high cost as compared to the options on AliExpress, its overall quality, fantastic documentation, and the fact that it fits neatly into the Arduino ecosystem makes it a good buy, especially if you've already invested in the Arduino Nano and its shields. The board has a few interesting peculiarity, including the use of a NORA-W106 module, a switch mode step-down converter for power, and an RGB LED. However, I was disappointed to find out it doesn't come with built-in battery charging. The pin labelling and remapping could definitely cause some confusion, especially if you're transitioning from other ESP32 boards. It's a fairly decent ESP32-S3 board if that's what you're after. But given a choice, I'm keener on the Unexpected Maker boards or the cheap boards on AliExpress.
HELP SUPPORT MY WORK: If you're feeling flush then please stop by Patreon Or you can make a one off donation via ko-fi
Want to keep up to date with the latest posts and videos? Subscribe to the newsletter
Blog Logo

Chris Greening


Published

> Image

atomic14

A collection of slightly mad projects, instructive/educational videos, and generally interesting stuff. Building projects around the Arduino and ESP32 platforms - we'll be exploring AI, Computer Vision, Audio, 3D Printing - it may get a bit eclectic...

View All Posts