В основе модуля лежит MAX30102 — небольшой, но мощный датчик, предназначенный для измерения частоты сердечных сокращений (ЧСС) и уровня насыщения крови кислородом (SpO2). Этот сенсор представляет собой усовершенствованную версию более старой модели MAX30100.
Датчик включает в себя:
- Два специальных светодиода — один красный, другой инфракрасный (ИК), направляют свет на вашу кожу.
- Фотодетектор — для измерения отраженного света.
- Оптическую систему и обработку сигналов — для обеспечения точности измерений.

Этот датчик часто используется в устройствах для мониторинга здоровья, таких как фитнес-трекеры и умные часы, благодаря своей компактности, надежности и энергоэффективности.
Питание
MAX30102 требует два разных напряжения для работы:
- 1.8 В – для основной микросхемы;
- 3.3 В – для красного и ИК-светодиодов.
Модуль оснащён встроенными стабилизаторами напряжения, поэтому не нужно подавать оба напряжения отдельно.

На обратной стороне платы находится перемычка (джампер), позволяющая выбрать:
- 3.3 В (по умолчанию) – совместимость с Arduino, ESP32 и другими популярными контроллерами;
- 1.8 В – для устройств с низковольтной логикой.
Если Ваш контроллер работает от 1.8 В, переставьте перемычку в соответствующее положение.

Одно из ключевых преимуществ MAX30102 – крайне низкое энергопотребление:
- Рабочий режим: < 600 мкА
- Режим ожидания: всего 0.7 мкА
Встроенный датчик температуры
MAX30102 оснащён встроенным температурным сенсором, который повышает точность измерений:
- Компенсирует влияние температуры на показания пульса и SpO2.
- Контролирует нагрев микросхемы для стабильной работы.
Характеристики датчика:
- Диапазон измерений: от -40°C до +85°C
- Точность: ±1°C
- Измеряет температуру кристалла (die temperature)
Практическое применение:
- Учёт температурных помех при контакте с кожей.
- Предотвращение перегрева в закрытых корпусах.
- Калибровка оптических измерений в реальном времени.
Хотя сенсор точный, он измеряет температуру чипа, а не окружающей среды. Для внешних температурных поправок может потребоваться дополнительный датчик.
Буфер FIFO
MAX30102 собирает данные быстрее, чем микроконтроллер может их обработать. Это может привести к проблемам, например к потере важных данных, если микроконтроллер не успеет их считать. Чтобы этого не произошло, в MAX30102 есть специальное хранилище данных под названием буфер FIFO (First In, First Out — «первым пришёл — первым вышел»). Этот буфер работает как временное хранилище. Он позволяет датчику продолжать собирать данные о частоте сердечных сокращений и уровне кислорода в крови, даже если микроконтроллер занят чем-то другим. Позже, когда микроконтроллер будет готов, он сможет считать сохранённые измерения из буфера.
FIFO в MAX30102 может хранить до 32 наборов измерений. Это даёт микроконтроллеру достаточно времени для выполнения других задач, прежде чем он вернётся к сбору данных.
Функция прерываний (Interrupts)
MAX30102 оснащен функцией прерываний, которая позволяет датчику отправлять оповещения на микроконтроллер при возникновении определенных событий. Существует пять различных типов оповещений:
- Power Ready: сигнал о том, что датчик включился или восстановился после отключения питания (просадки напряжения).
- New Data Ready: сигнал каждый раз при сборе новых данных о частоте сердечных сокращений или уровне кислорода в крови.
- Ambient Light Cancellation: предупреждает, когда фоновое освещение слишком яркое и мешает считыванию данных.
- Temperature Ready: сигнал о завершении измерения внутренней температуры кристалла.
- FIFO Almost Full: предупреждение о том, что FIFO почти заполнен и будущие данные могут быть потеряны.

Эти оповещения отправляются через специальный контакт под названием INT, который обычно находится в состоянии высокого уровня сигнала. При возникновении прерывания контакт переходит в состояние низкого уровня сигнала и остаётся в таком состоянии до тех пор, пока микроконтроллер не проверит и не сбросит прерывание.
Принцип работы для измерения пульса и уровня кислорода в крови
MAX30102 — это оптический датчик, который измеряет частоту сердечных сокращений (ЧСС) и сатурацию кислорода (SpO2) с помощью света. В его основе лежат:
- Два светодиода: красный (660 нм) и инфракрасный (880 нм)
- Фотодетектор (приёмник отражённого света)
Красный и ИК-светодиоды имеют разную длину волны, что позволяет:
Красный свет (660 нм):
- Сильнее поглощается оксигемоглобином (HbO2, кислородсвязанным гемоглобином).
- Используется для расчёта SpO2 (насыщения крови кислородом).
Инфракрасный свет (880 нм):
- Лучше проходит через ткани и поглощается дезоксигемоглобином (Hb).
- Помогает точнее определять пульс и фоновые шумы.

Как происходит измерение?
- Светодиоды поочерёдно излучают свет через тонкий участок тела (палец, мочку уха).
- Свет частично поглощается кровью, а остаток отражается обратно.
- Фотодетектор фиксирует интенсивность отражённого света.
- Алгоритмы анализируют изменения в поглощении света из-за пульсации крови.
Этот метод называется Фотоплетизмография (PPG) — неинвазивное измерение кровотока с помощью света.
Измерение ЧСС
Кислород в крови переносится с помощью белка гемоглобина. Когда гемоглобин насыщен кислородом (оксигемоглобин, HbO2), он поглощает больше инфракрасного света.

Как это работает:
- Во время удара сердца в палец поступает свежая, богатая кислородом кровь.
- Поскольку в ней много HbO2, она сильнее поглощает ИК-свет → фотодетектор получает меньше отражённого света.
- Между ударами сердца кислорода в крови немного меньше.
- Поглощение ИК-света снижается → фотодетектор фиксирует больше отражённого света.
- Датчик постоянно излучает ИК-свет и анализирует изменения в отражённом сигнале.
- Эти колебания создают пульсовую волну, соответствующую сердцебиению.
- Подсчитывая «пики» изменения интенсивности света, датчик определяет количество ударов сердца в минуту — вашу ЧСС.
Измерение уровня кислорода в крови
Для определения уровня кислорода датчик MAX30102 использует оба светодиода — красный и инфракрасный. Это основано на разнице поглощения света:
- Оксигемоглобин (HbO2) сильнее поглощает инфракрасный свет (880 нм)
- Дезоксигемоглобин (Hb) сильнее поглощает красный свет (660 нм)
На приведённом ниже графике спектра поглощения показано, как оксигенированный и дезоксигенированный гемоглобин поглощают свет разной длины с разной скоростью.

Сравнивая количество поглощённого красного и инфракрасного света, MAX30102 может рассчитать, какой процент вашего гемоглобина переносит кислород. Этот процент — ваш уровень SpO2, который показывает, насколько хорошо насыщена кислородом кровь.
Распиновка

- VIN — это вход питания. Вы можете подключить его к источнику напряжением 3,3 В или 5 В, в зависимости от того, на какой логике основан ваш микроконтроллер.
- SCL — это тактовый контакт, используемый для связи по протоколу I2C.
- SDA — это контакт для передачи данных по протоколу I2C.
- INT — это вывод прерывания. MAX30102 можно настроить на генерацию прерывания при возникновении определенных событий, как описано выше. Обычно на этом выводе высокий уровень сигнала, но при возникновении прерывания он становится низким и остается таким до тех пор, пока микроконтроллер не прочитает и не сбросит прерывание.
- IRD — это заземляющий контакт для инфракрасного светодиода внутри модуля. К этому контакту ничего подключать не нужно — он обрабатывается микросхемой.
- RD — как и в случае с IRD, это заземление для красного светодиода внутри модуля. Этот контакт также не подключается.
- GND — контакт заземления.

Пример 1. Чтение значений красного и ИК света
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <Wire.h> #include "MAX30105.h" MAX30105 particleSensor; void setup() { Serial.begin(9600); // Initialize sensor if (particleSensor.begin() == false) { Serial.println("MAX30102 was not found. Please check wiring/power."); while (1) ; } particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive } void loop() { Serial.print(" R["); Serial.print(particleSensor.getRed()); Serial.print("] IR["); Serial.print(particleSensor.getIR()); Serial.println("]"); } |
После загрузки кода в Arduino откройте последовательный монитор. Направьте датчик вверх и проведите над ним рукой. Вы заметите, что цифры меняются в зависимости от того, сколько света отражается от вашей руки и попадает на датчик.

Пример 2. Измерение ЧСС
Одна из самых интересных функций MAX30102 — измерение частоты сердечных сокращений в ударах в минуту (уд/мин).
Этот датчик использует свет для определения пульса. На показания могут влиять многие факторы, например движение или положение пальца. Поэтому он отлично подходит для обучения и экспериментов, но не подходит для медицинского использования.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
#include <Wire.h> #include "MAX30105.h" #include "heartRate.h" MAX30105 particleSensor; const byte RATE_SIZE = 4; //Increase this for more averaging. 4 is good. byte rates[RATE_SIZE]; //Array of heart rates byte rateSpot = 0; long lastBeat = 0; //Time at which the last beat occurred float beatsPerMinute; int beatAvg; void setup() { Serial.begin(9600); Serial.println("Initializing..."); // Initialize sensor if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { Serial.println("MAX30102 was not found. Please check wiring/power. "); while (1) ; } Serial.println("Place your index finger on the sensor with steady pressure."); particleSensor.setup(); //Configure sensor with default settings particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED } void loop() { long irValue = particleSensor.getIR(); if (checkForBeat(irValue) == true) { //We sensed a beat! long delta = millis() - lastBeat; lastBeat = millis(); beatsPerMinute = 60 / (delta / 1000.0); if (beatsPerMinute < 255 && beatsPerMinute > 20) { rates[rateSpot++] = (byte)beatsPerMinute; //Store this reading in the array rateSpot %= RATE_SIZE; //Wrap variable //Take average of readings beatAvg = 0; for (byte x = 0; x < RATE_SIZE; x++) beatAvg += rates[x]; beatAvg /= RATE_SIZE; } } Serial.print("IR="); Serial.print(irValue); Serial.print(", BPM="); Serial.print(beatsPerMinute); Serial.print(", Avg BPM="); Serial.print(beatAvg); if (irValue < 50000) Serial.print(" No finger?"); Serial.println(); } |
После загрузки скетча в Arduino аккуратно приложите палец к датчику и постарайтесь не двигаться. Подождите несколько секунд, пока датчик соберет данные. Вскоре вы увидите свой пульс на экране последовательного монитора.

Если ваш датчик неправильно определяет частоту сердечных сокращений, не волнуйтесь! Вот несколько простых способов исправить ситуацию:
- Не нажимайте на датчик слишком сильно. Если вы слишком сильно надавите пальцем, это нарушит нормальный приток крови.
- Не нажимайте слишком слабо. Это может привести к попаданию лишнего света и появлению помех в сигнале.
- Попробуйте найти «идеальное» давление — как будто вы аккуратно держите монету между пальцами.
- Попробуйте использовать резинку или скотч, чтобы зафиксировать палец на датчике.
- Проверьте другие части тела с тонкой кожей и интенсивным кровотоком, например мочку уха или нижнюю губу.
Пример 3. Измерение SpO2
В этом примере используется MAX30102 для измерения уровня кислорода в крови (SpO2). Это позволяет узнать, сколько кислорода содержится в крови, что у здоровых людей обычно составляет от 95 % до 100 %. Попробуйте запустить скетч.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
#include <Wire.h> #include "MAX30105.h" #include "spo2_algorithm.h" MAX30105 particleSensor; #define MAX_BRIGHTNESS 255 #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) //Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format //To solve this problem, 16-bit MSB of the sampled data will be truncated. Samples become 16-bit data. uint16_t irBuffer[100]; //infrared LED sensor data uint16_t redBuffer[100]; //red LED sensor data #else uint32_t irBuffer[100]; //infrared LED sensor data uint32_t redBuffer[100]; //red LED sensor data #endif int32_t bufferLength; //data length int32_t spo2; //SPO2 value int8_t validSPO2; //indicator to show if the SPO2 calculation is valid int32_t heartRate; //heart rate value int8_t validHeartRate; //indicator to show if the heart rate calculation is valid byte pulseLED = 11; //Must be on PWM pin byte readLED = 13; //Blinks with each data read void setup() { Serial.begin(9600); // initialize serial communication at 9600 bits per second: pinMode(pulseLED, OUTPUT); pinMode(readLED, OUTPUT); // Initialize sensor if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed { Serial.println(F("MAX30105 was not found. Please check wiring/power.")); while (1) ; } Serial.println(F("Attach sensor to finger with rubber band. Press any key to start conversion")); while (Serial.available() == 0) ; //wait until user presses a key Serial.read(); byte ledBrightness = 60; //Options: 0=Off to 255=50mA byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32 byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200 int pulseWidth = 411; //Options: 69, 118, 215, 411 int adcRange = 4096; //Options: 2048, 4096, 8192, 16384 particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings } void loop() { bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps //read the first 100 samples, and determine the signal range for (byte i = 0; i < bufferLength; i++) { while (particleSensor.available() == false) //do we have new data? particleSensor.check(); //Check the sensor for new data redBuffer[i] = particleSensor.getRed(); irBuffer[i] = particleSensor.getIR(); particleSensor.nextSample(); //We're finished with this sample so move to next sample Serial.print(F("red=")); Serial.print(redBuffer[i], DEC); Serial.print(F(", ir=")); Serial.println(irBuffer[i], DEC); } //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples) maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate); //Continuously taking samples from MAX30102. Heart rate and SpO2 are calculated every 1 second while (1) { //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top for (byte i = 25; i < 100; i++) { redBuffer[i - 25] = redBuffer[i]; irBuffer[i - 25] = irBuffer[i]; } //take 25 sets of samples before calculating the heart rate. for (byte i = 75; i < 100; i++) { while (particleSensor.available() == false) //do we have new data? particleSensor.check(); //Check the sensor for new data digitalWrite(readLED, !digitalRead(readLED)); //Blink onboard LED with every data read redBuffer[i] = particleSensor.getRed(); irBuffer[i] = particleSensor.getIR(); particleSensor.nextSample(); //We're finished with this sample so move to next sample //send samples and calculation result to terminal program through UART Serial.print(F("red=")); Serial.print(redBuffer[i], DEC); Serial.print(F(", ir=")); Serial.print(irBuffer[i], DEC); Serial.print(F(", HR=")); Serial.print(heartRate, DEC); Serial.print(F(", HRvalid=")); Serial.print(validHeartRate, DEC); Serial.print(F(", SPO2=")); Serial.print(spo2, DEC); Serial.print(F(", SPO2Valid=")); Serial.println(validSPO2, DEC); } //After gathering 25 new samples recalculate HR and SP02 maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate); } } |
После загрузки скетча в Arduino приложите палец к датчику. Не двигайтесь и подождите несколько секунд, пока датчик соберет данные. Показания SpO2 появятся в последовательном мониторе, как только датчик соберет достаточно данных для расчета SpO2.

Пример 4. Измерение температуры
Как упоминалось ранее, MAX30102 оснащён встроенным датчиком температуры, который измеряет внутреннюю температуру чипа (температуру кристалла). Хотя этот датчик в основном помогает корректировать (или калибровать) показания частоты сердечных сокращений и SpO2 с учётом изменений температуры, он также может служить быстрым термометром.
В этом примере температура отображается как в градусах Цельсия, так и в градусах Фаренгейта.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <Wire.h> #include "MAX30105.h" MAX30105 particleSensor; void setup() { Serial.begin(9600); Serial.println("Initializing..."); // Initialize sensor if (particleSensor.begin(Wire, I2C_SPEED_FAST) == false) { //Use default I2C port, 400kHz speed Serial.println("MAX30102 was not found. Please check wiring/power. "); while (1) ; } //The LEDs are very low power and won't affect the temp reading much but //you may want to turn off the LEDs to avoid any local heating particleSensor.setup(0); //Configure sensor. Turn off LEDs particleSensor.enableDIETEMPRDY(); //Enable the temp ready interrupt. This is required. } void loop() { float temperature = particleSensor.readTemperature(); Serial.print("temperatureC="); Serial.print(temperature, 4); float temperatureF = particleSensor.readTemperatureF(); Serial.print(" temperatureF="); Serial.print(temperatureF, 4); Serial.println(); } |
После запуска скетча попробуйте приложить палец к датчику или слегка подуть на него. Вы должны увидеть, что температура немного повысится.

Пример 5. Обнаружение присутствия
В этом последнем примере вы узнаете, как использовать MAX30102 в качестве датчика приближения или движения.
Вот как это работает: в начале (во время настройки) скетч считывает несколько показаний и усредняет их, чтобы установить базовый уровень. После этого он постоянно отслеживает любые резкие отклонения от этого базового уровня. Когда что-то проходит над датчиком, например ваша рука, он выводит сообщение: «Something is there!»
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#include <Wire.h> #include "MAX30105.h" MAX30105 particleSensor; long samplesTaken = 0; //Counter for calculating the Hz or read rate long unblockedValue; //Average IR at power up long startTime; //Used to calculate measurement rate void setup() { Serial.begin(9600); // Initialize sensor if (particleSensor.begin(Wire, I2C_SPEED_FAST) == false) { //Use default I2C port, 400kHz speed Serial.println("MAX30102 was not found. Please check wiring/power. "); while (1) ; } //Setup to sense up to 18 inches, max LED brightness byte ledBrightness = 0xFF; //Options: 0=Off to 255=50mA byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32 byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green int sampleRate = 400; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200 int pulseWidth = 411; //Options: 69, 118, 215, 411 int adcRange = 2048; //Options: 2048, 4096, 8192, 16384 //Configure sensor with these settings particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); particleSensor.setPulseAmplitudeRed(0); //Turn off Red LED particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED //Take an average of IR readings at power up unblockedValue = 0; for (byte x = 0; x < 32; x++) { unblockedValue += particleSensor.getIR(); //Read the IR value } unblockedValue /= 32; startTime = millis(); } void loop() { samplesTaken++; Serial.print("IR["); Serial.print(particleSensor.getIR()); Serial.print("] Hz["); Serial.print((float)samplesTaken / ((millis() - startTime) / 1000.0), 2); Serial.print("]"); long currentDelta = particleSensor.getIR() - unblockedValue; Serial.print(" delta["); Serial.print(currentDelta); Serial.print("]"); if (currentDelta > (long)100) { Serial.print(" Something is there!"); } Serial.println(); } |
Попробуйте загрузить этот скетч и провести рукой над датчиком. Следите за появлением сообщения в мониторе. Вы также можете проверить, на каком расстоянии от датчика будет определяться ваша рука.
