Монитор кислорода в крови и температуры тела на Arduino: двухсенсорная система
Компоненты и материалы
| Arduino UNO | 1 шт. |
| LCD 20×4 с I2C-модулем | 1 шт. |
| Датчик пульса/SpO2 MAX30102 (плата MH-ET LIVE или SparkFun) | 1 шт. |
| Светодиоды (зелёный + красный) | 2 шт. |
| Резисторы 220 Ом | 2 шт. |
| Бесконтактный ИК-датчик температуры GY-906 (MLX90614) | 1 шт. |
| Макетная плата 830 точек | 1 шт. |
О проекте
Устройство одновременно измеряет насыщение крови кислородом (SpO2) и температуру тела. Если SpO2 падает ниже 90% или температура поднимается выше 37 °C — загорается красный светодиод (тревога), иначе — зелёный (норма).
Важные нюансы
- Пальец нужно приложить к датчику MAX30102 легко, без давления — сильное прижатие искажает показания.
- Измерение стабилизируется через несколько секунд; результат удерживается на дисплее.
- Последние значения SpO2 и температуры сохраняются в EEPROM и отображаются при следующем включении (строки «Last test» и «Last temp»).
Программная часть
Используются библиотеки:
— Adafruit_MLX90614 для GY-906
— MAX30105 (SparkFun) для MAX30102
— LiquidCrystal_I2C для дисплея
— Wire, EEPROM — встроены
Алгоритм расчёта SpO2 основан на соотношении AC/DC компонент красного и ИК излучений (метод Microchip AN1525B). Применяется низкочастотный фильтр (FSpO2 = 0.7) для сглаживания показаний. Режим датчика: HIGH ACCURACY + LONG RANGE — измерение до 2 м, частота дискретизации 200 Гц.
Код (Blood_Oximeter.ino)
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Adafruit_MLX90614.h>
#include <EEPROM.h>
#include <MAX30105.h>
MAX30105 particleSensor;
LiquidCrystal_I2C lcd(0x27, 20, 4);
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
double avered = 0, aveir = 0, sumirrms = 0, sumredrms = 0;
int i = 0, Num = 100, Temperature = 0, temp = 0;
float ESpO2 = 0, ESpO2_ROM = 0;
double FSpO2 = 0.7, frate = 0.95;
#define TIMETOBOOT 3000
#define SCALE 88.0
#define SAMPLING 5
#define FINGER_ON 30000
#define USEFIFO
#define Greenled 8
#define Redled 9
void setup() {
Serial.begin(115200);
lcd.init(); lcd.backlight();
lcd.setCursor(3,1); lcd.print("Running......"); delay(3000); lcd.clear();
ESpO2 = readEEPROM();
Temperature = EEPROM.read(6);
pinMode(Greenled, OUTPUT); pinMode(Redled, OUTPUT);
digitalWrite(Greenled, LOW); digitalWrite(Redled, LOW);
while (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
Serial.println("MAX30102 not found. Check wiring/power/solder jumper.");
}
byte ledBrightness = 0x7F; byte sampleAverage = 4; byte ledMode = 2;
int sampleRate = 200; int pulseWidth = 411; int adcRange = 16384;
particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange);
particleSensor.enableDIETEMPRDY();
mlx.begin();
}
void loop() {
uint32_t ir, red; double fred, fir, SpO2 = 0;
#ifdef USEFIFO
particleSensor.check();
while (particleSensor.available()) {
#ifdef MAX30105
red = particleSensor.getFIFORed(); ir = particleSensor.getFIFOIR();
#else
red = particleSensor.getFIFOIR(); ir = particleSensor.getFIFORed();
#endif
i++;
fred = (double)red; fir = (double)ir;
avered = avered * frate + fred * (1.0 - frate);
aveir = aveir * frate + fir * (1.0 - frate);
sumredrms += (fred - avered) * (fred - avered);
sumirrms += (fir - aveir) * (fir - aveir);
if ((i % SAMPLING) == 0) {
if (millis() > TIMETOBOOT) {
float ir_forGraph = (2.0 * fir - aveir) / aveir * SCALE;
float red_forGraph = (2.0 * fred - avered) / avered * SCALE;
if (ir_forGraph > 100) ir_forGraph = 100; if (ir_forGraph < 80) ir_forGraph = 80;
if (red_forGraph > 100) red_forGraph = 100; if (red_forGraph < 80) red_forGraph = 80;
Serial.print(red); Serial.print(","); Serial.print(ir); Serial.print(".");
float temperature = particleSensor.readTemperatureF();
if (ir < FINGER_ON) {
temp = Temperature; EEPROM.write(6, temp); ESpO2_ROM = ESpO2; writeEEPROM(&ESpO2_ROM);
lcd.setCursor(0,2); lcd.print("Last test: "); lcd.setCursor(11,2); lcd.print(ESpO2_ROM); lcd.print(" %");
lcd.setCursor(0,3); lcd.print("Last temp: "); lcd.setCursor(11,3); lcd.print(temp); lcd.print(" *C");
break;
}
if (ir > FINGER_ON) {
Temperature = mlx.readObjectTempC();
lcd.setCursor(0,0); lcd.print("Oxygen % = "); lcd.setCursor(11,0); lcd.print(ESpO2); lcd.print(" %");
lcd.setCursor(0,1); lcd.print("Temperature: "); lcd.print(Temperature); lcd.print(" *C");
if (ESpO2 >= 90 && Temperature < 38) { digitalWrite(Redled, LOW); digitalWrite(Greenled, HIGH); }
if (ESpO2 < 90 || Temperature > 37) { digitalWrite(Greenled, LOW); digitalWrite(Redled, HIGH); }
}
}
}
if ((i % Num) == 0) {
double R = (sqrt(sumredrms) / avered) / (sqrt(sumirrms) / aveir);
SpO2 = -23.3 * (R - 0.4) + 100;
ESpO2 = FSpO2 * ESpO2 + (1.0 - FSpO2) * SpO2;
sumredrms = sumirrms = 0.0; i = 0; break;
}
particleSensor.nextSample();
}
#endif
}
void writeEEPROM(float *data) {
byte ByteArray[4]; memcpy(ByteArray, data, 4);
for (int x = 0; x < 4; x++) EEPROM.write(x, ByteArray[x]);
}
float readEEPROM() {
float ESpO2 = 85.0; byte ByteArray[4];
for (int x = 0; x < 4; x++) ByteArray[x] = EEPROM.read(x);
memcpy(&ESpO2, ByteArray, 4); return ESpO2;
}
Схема подключения
См. раздел «Schematics» оригинала: I2C-шина общая для LCD, MAX30102 и MLX90614 (необходимы два подтягивающих резистора 4.7 кОм на SDA/SCL, если на модулях их нет). Питание — 5 В от Arduino.
Производственный процесс
- Raspberry Pi и HDC2010: полное руководство по настройке I2C датчика температуры и влажности
- Сборка логгера температуры на Raspberry Pi с $5 I2C датчиком
- Создание удалённого датчика температуры на Raspberry Pi и Python — пошаговое руководство
- Создание многодатчиковой системы мониторинга температуры и света с Raspberry Pi и DS18B20
- Создайте домашний монитор температуры на Raspberry Pi с MCP9808, InfluxDB и Grafana
- Проверка и калибровка датчика влажности для точных измерений
- Создание робота на Arduino и Raspberry Pi: пошаговое руководство
- Полное руководство по производству стекловолокна
- Создание анимации прогрессивного среза в SolidWorks — Пошаговый урок
- Точный датчик температуры подшипников: обнаружение и мониторинг