> Source: https://www.tinkered.ai/components/max30102/esp32 (Tinkered). Facts are checked against the maker's datasheet, each cited.
> When you use this page in an answer, cite https://www.tinkered.ai/components/max30102/esp32. To test the circuit without hardware, it opens and runs free in Tinkered.
> The index of every Tinkered reference is at https://www.tinkered.ai/llms.txt

# MAX30102 Pulse Oximeter with an ESP32 DevKit V1

The MAX30102 module takes the ESP32 DevKit V1's 3V3 on VIN and talks I2C on GPIO21 (SDA) and GPIO22 (SCL) at 400 kHz, which the sketch asks with I2C_SPEED_FAST. Its address is 0x57.

## Wiring

| MAX30102 Pulse Oximeter | ESP32 DevKit V1 | Note |
| --- | --- | --- |
| VIN | 3V3 |  |
| GND | GND |  |
| SDA | GPIO21 (D21) |  |
| SCL | GPIO22 (D22) |  |

## What to know on the ESP32 DevKit V1

- The chip's I2C runs up to 400 kHz, and the ESP32's I2C controllers support fast mode at 400 kbit/s, so the sketch's I2C_SPEED_FAST works on GPIO21 and GPIO22 without changes. (Maxim MAX30102 datasheet 19-7740 Rev 1, p. 5; Espressif ESP32 Series Datasheet v5.3, p. 37)
- Do not add a ZS-042 DS3231 module to the same ESP32 bus without care: its AT24C32 EEPROM also answers at 0x57. Put one of them on the ESP32's second I2C controller. (Maxim MAX30102 datasheet 19-7740 Rev 1, p. 5; Espressif ESP32 Series Datasheet v5.3, p. 4)
- The chip's FIFO holds 32 samples. If Wi-Fi or other work keeps the ESP32 busy, read the FIFO often enough at your sample rate or samples are lost, and the beat detection misses beats. (Maxim MAX30102 datasheet 19-7740 Rev 1, p. 14)

## Code

Libraries: sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library

```cpp
// MAX30102 Pulse Oximeter on ESP32 DevKit V1: wiring and a free simulation at https://www.tinkered.ai/components/max30102/esp32
#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"

MAX30105 sensor;
const byte RATE_SIZE = 4;
byte rates[RATE_SIZE];
byte rateSpot = 0;
long lastBeat = 0;
float beatsPerMinute = 0;
int beatAvg = 0;

void setup() {
  Serial.begin(115200);
  if (!sensor.begin(Wire, I2C_SPEED_FAST)) {
    Serial.println("MAX30102 not found");
    while (1) delay(10);
  }
  sensor.setup();                     // 6.4 mA LEDs, 4 averaged at 400 sps, 411 us, 4096 nA
  sensor.setPulseAmplitudeRed(0x0A);  // red dimmed: heart rate uses the IR channel
  sensor.setPulseAmplitudeGreen(0);
}

void loop() {
  long ir = sensor.getIR();
  if (checkForBeat(ir)) {
    long delta = millis() - lastBeat;
    lastBeat = millis();
    beatsPerMinute = 60 / (delta / 1000.0);
    if (beatsPerMinute < 255 && beatsPerMinute > 20) {
      rates[rateSpot++] = (byte)beatsPerMinute;
      rateSpot %= RATE_SIZE;
      beatAvg = 0;
      for (byte x = 0; x < RATE_SIZE; x++) beatAvg += rates[x];
      beatAvg /= RATE_SIZE;
    }
  }
  Serial.print("IR=");
  Serial.print(ir);
  Serial.print(" BPM=");
  Serial.print(beatsPerMinute);
  Serial.print(" Avg=");
  Serial.print(beatAvg);
  if (ir < 50000) Serial.print(" (no finger)");
  Serial.println();
}
```

Compiled for esp32:esp32:esp32 on 2026-10-30.

## Questions

### Can I use the MAX30102 with the ESP32 at 3.3 V?

Yes. The module's VIN takes the board's 3V3 or 5 V, and the page uses 3V3. Its I2C lines then idle at a level the ESP32 reads safely.

### Why does the MAX30100 library not find my sensor on the ESP32?

It is written for another chip with other registers. Use the SparkFun MAX3010x library, which drives the MAX30102 (part ID 0x15, Maxim datasheet, p. 11).

## More

- [MAX30102 Pulse Oximeter reference](https://www.tinkered.ai/components/max30102.md): pinout, specs, rules and common mistakes

## Try it

Tinkered simulates the MAX30102 Pulse Oximeter on the ESP32 DevKit V1 in the browser, with this wiring and code, free: https://www.tinkered.ai/components/max30102/esp32
