Подключение цифрового датчика пульса MAX30102 к Arduino
Сегодня расскажу о модуле MAX30102 с помощью которого можно измерить уровень насыщенности кислорода в крови и частоту сердечных сокращений, то есть пульс. Так же в датчик MAX30102 встроен датчик температуры, с помощью которого можно получить температуру окружающей среды.
Технические параметры
► Напряжение питания: 3.3 … 5 В;
► Номинальный ток: 1.2мА в режиме считывания, 10мкА в спящем режиме;
► Время считывания: 400 мкс;
► Частота внутреннего АЦП: 10 МГц;
► Длина волны светодиодов: 660нм, 880нм;
► Погрешность температурного датчика: ±1 °C;
► Интерфейс: I2C;
► Габариты: 14 х 14 х 4 мм;
► Вес: 1 грам.
Как работает пульсоксиметр.
Как мы знаем, кислород поступает в легкие, а затем попадает в кровь. Кровь доставляет кислород к различным органам нашего тела. Основным способом переноса кислорода в нашей крови является гемоглобин. Измерение кислорода в крови осуществляется с помощью небольшими устройствами (пульсоксиметр), похожее на зажим, которые одевается на палец, мочку уха или палец ноги.
Пульсоксиметр посылает небольшие пучки света, которые проходят через кровь в пальце и частично поглощается в насыщенном кислороде или дезоксигенированной крови. Насыщенная кислородом кровь поглощает больше инфракрасного света и пропускает больше красного света, в то время как деоксигенированная кровь поглощает красный свет и пропускает больше инфракрасного света. Излучаемый свет поглощается насыщенной кислородом кровью, а остальной свет отражается от пальца и попадает на детектор, выходные данные которого затем обрабатываются и считываются через микроконтроллер.
Описание датчика MAX30102
Основная микросхема модуля, это датчик MAX30102 которая способна измерять уровень кислорода в крови (SpO2) и частоту сердечного ритм (HR). Измерения осуществляется с помощью оптического датчика, который измеряет поглощение пульсаций крови через фотоприемник после излучения двух светодиодов — красного и инфракрасного цвета. Данные о частоте сердечных сокращений и содержании кислорода в крови передаются на микроконтроллеры через связь I2C.
Так же MAX30102 оснащен встроенным датчиком температуры для калибровки температурной зависимости SpO2. Разрешение датчика температуры составляет 0,0625°C.
Конфигурация контактов MAX30102
► VIN — питание модуля 2,7 до 5,5 вольт;
► SCL — последовательные данные шины I2C (serial data);
► SDA — последовательное тактирование шины данных I2C (serial clock);
► INT — вывод прерывания;
► GND — минус от источника.
Подключение модуля MAX30102 к Arduino
В примере приведу схему подключения датчика MAX30102 к плате Arduino UNO и напишем три скетча, в которых прочитаем показания кислорода в крови, частоту сердечных сокращений и температуру окружающий (все примеры взяты с библиотеки MAX30105). Все показания выведем на последовательной порт.
Необходимые компоненты.
► Arduino UNO R3 x 1 шт.
► Датчик пульса и содержания кислорода в крови MAX30102 x 1 шт.
► Провод DuPont, 2,54 мм, 20 см, F-M (Female — Male) x 1 шт.
► Кабель USB 2.0 A-B x 1 шт
Подключение.
Для подключения модуля MAX30102 к плате Arduino необходимо задействовать в общей сложности 4 провода. Подключаем контакты VIN и GND с MAX30102 к контактам 5 В и GND на Arduino UNO, а контакты SCL и SDA с MAX30102 к контактам A5 и A4 на Arduino UNO.
Установка библиотеки.
Для работы, будем использовать библиотеку «SparkFun MAX30105«, скачать ее можно с github или в конце статьи.
Для точных измерений, необходимо чтобы палец был в постоянном зафиксированном и давил на сенсор с постоянным давлением. Лучше всего воспользоваться канцелярской резинкой, изолентой и зафиксировать датчик с пальцем.
Программа №1 — Определения частоты сердечных сокращений
Открываем среду разработки Arduino IDE и загружаем первую программу в Arduino UNO.
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 |
#include "Wire.h" // Подключаем библиотеку для работы с шиной I2C #include "MAX30105.h" // Подключаем библиотеку для работы с модулем #include "heartRate.h" // Подключаем блок для работы с ЧСС (пульс) MAX30105 particleSensor; // Создаём объект для работы с библиотекой const byte RATE_SIZE = 4; // Коэффициент усреднения byte rates[RATE_SIZE]; // Массив со значениями ЧСС byte rateSpot = 0; // Переменная с порядковым номером значения в массиве long lastBeat = 0; // Время последнего зафиксированного удара float beatsPerMinute; // Создаём переменную для хранения значения ЧСС int beatAvg; // Создаём переменную для хранения усреднённого значения ЧСС void setup() { Serial.begin(115200); // Инициируем работу с монитором последовательного порта на скорости 115200 бод Serial.println("Initializing..."); // Выводим собощение if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) // Инициируем работу с модулем. Если инициализация не прошла, то { Serial.println("MAX30105 was not found."); // Выводим сообщение об ошибки while (1); // Зацикливаем } Serial.println("Place your finger on the sensor"); // Выводим сообщение particleSensor.setup(); // Устанавливаем настройки для сенсора по умолчанию particleSensor.setPulseAmplitudeRed(0x0A); // Выключаем КРАСНЫЙ светодиод для того, чтобы модуль начал работу particleSensor.setPulseAmplitudeGreen(0); // Выключаем ЗЕЛЁНЫЙ светодиод } void loop() { long irValue = particleSensor.getIR(); // Считываем значение отражённого ИК-светодиода if (checkForBeat(irValue) == true) { // Пульс был зафиксирован? long delta = millis() - lastBeat; // Находим дельту по времени между ударами lastBeat = millis(); // Обновляем счётчик beatsPerMinute = 60 / (delta / 1000.0); // Вычисляем количество ударов в минуту if (beatsPerMinute < 255 && beatsPerMinute > 20) { // Если количество ударов в минуту находится в промежутке между 20 и 255 rates[rateSpot++] = (byte)beatsPerMinute; // Записываем значение rateSpot %= RATE_SIZE; // Задаём порядковый номер значения в массиве, возвращая остаток от деления и присваивая его переменной rateSpot 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(); // Выводим в монитор последовательного порта переход на новую строку } |
Прикладываем датчик к пальцу с помощью канцелярской резинкой и открываем окно «Последовательного порта», где видим частоту сердечных сокращений.
Программа №2 — Определение температуры окружающей среды.
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 |
#include "Wire.h" // Подключаем библиотеку для работы с шиной I2C #include "MAX30105.h" // Подключаем библиотеку для работы с модулем MAX30105 PARTICLE_SENSOR; // Создаём объект для работы с библиотекой void setup() { Serial.begin(9600); // Инициируем работу с монитором последовательного порта на скорости 9600 бод if (!PARTICLE_SENSOR.begin()) { // Инициируем работу с модулем. Если инициализация не прошла, то Serial.println("MAX30105 was not found."); // выводим сообщение об этом в монитор последовательного порта while (1); // и останавливаем дальнейшее выполнение скетча } PARTICLE_SENSOR.setup(0); // Настраиваем сенсор на работу с выключенными светодиодами PARTICLE_SENSOR.enableDIETEMPRDY(); // Включаем поддержку прерываний для датчика температуры. Это ОБЯЗАТЕЛЬНЫЙ параметр } void loop() { float temperature = PARTICLE_SENSOR.readTemperature(); // Создаём переменную с плавающей точкой и записываем в неё полученное от датчика значение (в Цельсиях) Serial.print("temperature C = "); // Выводим в монитор последовательного порта текст Serial.print(temperature, 4); // Выводим в монитор последовательного порта значение температуры в Цельсиях float temperatureF = PARTICLE_SENSOR.readTemperatureF(); // Создаём переменную с плавающей точкой и записываем в неё полученное от датчика значение (в Фаренгейтах) Serial.print(" | temperature F = "); // Выводим в монитор последовательного порта текст Serial.print(temperatureF, 4); // Выводим в монитор последовательного порта значение температуры в Фаренгейтах Serial.println(); // Переходим на новую строку } |
Открываем окно «Последовательного порта», где видим температуру в цельсиях и в фаренгейтах.
Программа №3 — Определение насыщенности кислорода в крови.
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 |
#define MAX_BRIGHTNESS 255 // Задаём переменную максимальной яркости свечения светодиода #include "Wire.h" // Подключаем библиотеку для работы с шиной I2C #include "MAX30105.h" // Подключаем библиотеку для работы с модулем #include "spo2_algorithm.h" // Подключаем блок работы с насыщением крови кислородом MAX30105 PARTICLE_SENSOR; // Создаём объект для работы с библиотекой #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) uint16_t irBuffer[100]; // 16-битный массив данных от сенсора со значениями от ИК-светодиода uint16_t redBuffer[100]; // 16-битный массив данных от сенсора со значениями от красного светодиода #else uint32_t irBuffer[100]; // 32-битный массив данных от сенсора со значениями от ИК-светодиода uint32_t redBuffer[100]; // 32-битный массив данных от сенсора со значениями от красного светодиода #endif int32_t bufferLength; // длина буфера данных int32_t spo2; // значение SpO2 (насыщенности крови кислородом) int8_t validSPO2; // флаг валидности значений сенсора по SpO2 int32_t heartRate; // значение ЧСС int8_t validHeartRate; // флаг валидности значений сенсора по ЧСС void setup() { Serial.begin(115200); // инициируем работу с монитором последовательного порта на скорости 115200 бод if (!PARTICLE_SENSOR.begin()) { // инициируем работу с сенсором. Если этого не произошло, то Serial.println(F("MAX30105 was not found.")); // Выводим сообщением об этом и while (1); // останавливаем дальнейшее выполнение скетча } Serial.println(F("Press any key!")); // Прежде, чем переходить к считыванию, выводим в монитор порта сообщение и while (Serial.available() == 0); // ждём отправки любого символа в монитор порта (нажмите Enter, находясь в строке отправки) Serial.read(); // Если символ получен, то скетч будет выполнен дальше byte ledBrightness = 60; // Задаём яркость работы светодиода, при этом потребление тока будет следующим: 0 - 0мА, 255 - 50 мА byte sampleAverage = 4; // Устанавливаем коэффициент усреднения. Возможные варианты значений: 1, 2, 4, 8, 16, 32 byte ledMode = 2; // Устанавливаем режим работы светодиодов на сенсоре: 1 - только красный (Red only), 2 - красный и ИК (Red + IR), 3 - красный, ИК и зелёный (Red + IR + Green) byte sampleRate = 100; // Устанавливаем частоту дискретизации (сглаживания сигнала). Варианты: 50, 100, 200, 400, 800, 1000, 1600, 3200 int pulseWidth = 411; // Устанавливаем ширину импульса. Варианты: 69, 118, 215, 411 int adcRange = 4096; // Устанавливаем диапазон значений с АЦП. Варианты: 2048(11 бит), 4096(12 бит), 8192(13 бит), 16384(14 бит) PARTICLE_SENSOR.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); } void loop() { bufferLength = 100; // Устанавливаем длину буфера равным 100 (куда будут записаны пакеты по 25 значений в течении 4 секунд) for (byte i = 0 ; i < bufferLength ; i++) { // проходим в цикле по буферу и while (PARTICLE_SENSOR.available() == false) // отправляем сенсору запрос на получение новых данных PARTICLE_SENSOR.check(); redBuffer[i] = PARTICLE_SENSOR.getRed(); // Записываем в массив значения сенсора, полученные при работе с КРАСНЫМ светодиодом irBuffer[i] = PARTICLE_SENSOR.getIR(); // Записываем в массив значения сенсора, полученные при работе с ИК светодиодом PARTICLE_SENSOR.nextSample(); // Как только в буфер было записано 100 значений - отправляем сенсору команду начать вычислять значения ЧСС и SpO2 Serial.print(F("red=")); // Выводим текст в монитор последовательного порта Serial.print(redBuffer[i], DEC); // Выводим значение переменной redBuffer[i] в монитор последовательного порта Serial.print(F(", ir=")); // Выводим текст в монитор последовательного порта Serial.println(irBuffer[i], DEC); // Выводим значение переменной irBuffer[i] в монитор последовательного порта } maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate); while (1) { // Сбрасываем первые полученные 25 значений из буфера, а оставшиеся 75 сдвигаем влево в массиве for (byte i = 25; i < 100; i++) { redBuffer[i - 25] = redBuffer[i]; irBuffer[i - 25] = irBuffer[i]; } // Получаем новые 25 значений прежде чем переходить к вычислению ЧСС for (byte i = 75; i < 100; i++) { while (PARTICLE_SENSOR.available() == false) { // Опрашиваем сенсор на предмет наличия новых значений PARTICLE_SENSOR.check(); } redBuffer[i] = PARTICLE_SENSOR.getRed(); // Записываем в массив значения сенсора, полученные при работе с КРАСНЫМ светодиодом irBuffer[i] = PARTICLE_SENSOR.getIR(); // Записываем в массив значения сенсора, полученные при работе с ИК светодиодом PARTICLE_SENSOR.nextSample(); // Как только в буфер было записано 100 значений - отправляем сенсору команду начать вычислять значения ЧСС и SpO2 Serial.print(F("red=")); // Выводим текст в монитор последовательного порта Serial.print(redBuffer[i], DEC); // Выводим значение переменной redBuffer[i] в монитор последовательного порта Serial.print(F(", ir=")); // Выводим текст в монитор последовательного порта Serial.print(irBuffer[i], DEC); // Выводим значение переменной irBuffer[i] в монитор последовательного порта Serial.print(F(", HR=")); // Выводим текст в монитор последовательного порта Serial.print(heartRate, DEC); // Выводим значение переменной heartRate в монитор последовательного порта Serial.print(F(", HRvalid=")); // Выводим текст в монитор последовательного порта Serial.print(validHeartRate, DEC); // Выводим значение переменной validHeartRate в монитор последовательного порта Serial.print(F(", SPO2=")); // Выводим текст в монитор последовательного порта Serial.print(spo2, DEC); // Выводим значение переменной spo2 в монитор последовательного порта Serial.print(F(", SPO2Valid=")); // Выводим текст в монитор последовательного порта Serial.println(validSPO2, DEC); // Выводим значение переменной validSPO2 в монитор последовательного порта } // После получения очередного пакета из 25 значений повторно считаем значения ЧСС и SpO2 maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate); } } |
Прикладываем датчик к пальцу с помощью канцелярской резинкой, открываем окно «Последовательного порта» и отправляем любую команду, через время начнем получать показания.
Ссылки
Библиотека SparkFun MAX3010x v.1.1.2
Купить на Aliexpress
Контроллер Arduino UNO R3 на CH340G
Контроллер Arduino UNO R3 на Atmega16U2
Провода DuPont, 2,54 мм, 20 см
Датчик пульса и содержания кислорода в крови MAX30102
Купить в Самаре и области
Контроллер Arduino UNO R3 на CH340G
Контроллер Arduino UNO R3 на Atmega16U2
Провода DuPont, 2,54 мм, 20 см
Датчик пульса и содержания кислорода в крови MAX30102