Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

MAX30102 pulse sensor with ESP32-S3-DevKitC-1 — heart rate

Read heart rate from a MAX30102 on an ESP32-S3-DevKitC-1 over I2C (GPIO8/9, address 0x57) with the SparkFun MAX3010x library. Not a medical device.

Arduino C++Verified — core esp32 3.3.12, SparkFun MAX3010x Pulse and Proximity Sensor Library 1.1.2, 3 Nov 2026Compile-only

Community draft — report a mistake

The MAX30102 shines red and infrared light into a fingertip and measures what comes back; the IR signal pulses with each heartbeat. The SparkFun library’s beat detector turns that into BPM.

On the S3-DevKitC-1 wire it to 3V3 and the default I2C pins GPIO8 (SDA) and GPIO9 (SCL). Open the Serial Monitor on the UART port and rest a fingertip on the sensor.

Wiring

ESP32-S3-DevKitC-1Only the pins used are shown · 3.3 V logicMAX30102 pulse oximeter module3V38 · 89 · 9GNDVINSDASCLINT (not connected)IRD (not connected)RD (not connected)GND
Every connection
ESP32-S3-DevKitC-1MAX30102 pulse oximeter moduleWireNotes
3V3VINred
GNDGNDblack
8SDAblue
9SCLyellow

3.3–5 V (module regulators) part, 3.3 V board. Many purple modules pull SDA/SCL up to 1.8 V. That usually still reads on 3.3 V boards, but some don't — if the scanner can't find 0x57, use a module whose pull-ups go to 3.3 V.

Board quirks

  • Many purple MAX30102 boards pull SDA/SCL up to 1.8 V. The S3 usually still reads them, but if the sketch can’t find 0x57, use a module whose pull-ups go to 3.3 V.
  • GPIO8/9 are the S3’s default I2C pins. The sketch uses 400 kHz (I2C_SPEED_FAST) to keep up with the sensor’s sample rate.
  • Rest the fingertip lightly and keep still: pressing hard or moving ruins the reading. Bright room light leaking in also adds noise.
  • This shows how optical pulse sensing works. It is not a medical device — don’t use it for health decisions.

Code

max30102-esp32s3.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// MAX30102 pulse sensor on an ESP32-S3-DevKitC-1 — heart rate (BPM) from the IR signal.
// VIN → 3V3, GND → GND, SDA → GPIO8, SCL → GPIO9 (S3 default I2C). INT unused. Address 0x57.
// For learning only — not a medical device.
#include <Wire.h>
#include <MAX30105.h>
#include <heartRate.h>

MAX30105 sensor;
constexpr uint8_t RATE_SIZE = 4;
uint8_t rates[RATE_SIZE];
uint8_t rateSpot = 0;
uint32_t lastBeat = 0;
float bpm = 0;
int avgBpm = 0;

void setup() {
  Serial.begin(115200);
  Wire.begin(8, 9);
  if (!sensor.begin(Wire, I2C_SPEED_FAST)) {
    Serial.println("MAX30102 not found at 0x57 - check wiring (some modules pull I2C up to 1.8 V)");
    while (true) delay(1000);
  }
  sensor.setup();                    // library defaults: 400 Hz sample rate, 411 us pulse width
  sensor.setPulseAmplitudeRed(0x0A); // red LED low: we only use IR here
  sensor.setPulseAmplitudeGreen(0);
  Serial.println("Place your fingertip on the sensor and keep still");
}

void loop() {
  long ir = sensor.getIR();
  if (checkForBeat(ir)) {
    uint32_t delta = millis() - lastBeat;
    lastBeat = millis();
    bpm = 60.0f / (delta / 1000.0f);
    if (bpm > 20 && bpm < 255) {
      rates[rateSpot++] = (uint8_t)bpm;
      rateSpot %= RATE_SIZE;
      int sum = 0;
      for (uint8_t i = 0; i < RATE_SIZE; i++) sum += rates[i];
      avgBpm = sum / RATE_SIZE;
    }
  }
  static uint32_t lastPrint = 0;
  if (millis() - lastPrint > 1000) {
    lastPrint = millis();
    if (ir < 50000) Serial.println("No finger?");
    else Serial.printf("IR %ld  BPM %.0f  Avg BPM %d\n", ir, bpm, avgBpm);
  }
}

Library: SparkFun MAX3010x Pulse and Proximity Sensor Library (SparkFun Electronics) — compiled with SparkFun MAX3010x Pulse and Proximity Sensor Library 1.1.2.

Expected serial output

Serial Monitor · 115200Output
Place your fingertip on the sensor and keep still
No finger?
IR 102344  BPM 74  Avg BPM 71
IR 102877  BPM 69  Avg BPM 72

Common errors

“MAX30102 not found at 0x57”

Wiring, or the 1.8 V pull-up issue above. Run an I2C scan.

BPM jumps between 40 and 200

Movement or pressure. Rest the finger with tape, and wait for the 4-beat average to settle.

↑↓ to move · Enter to open