아두이노 혈중 산소 & 체온 모니터: 듀얼 센서 시스템 제작
부품 및 소모품
| 부품 | 수량 |
| 아두이노 우노 (Arduino UNO) | 1 |
| 20x4 I2C LCD | 1 |
| MAX30102 심박/산소포화도 센서 | 1 |
| LED 2개 (적색, 녹색 등) | 1세트 |
| 220Ω 저항 2개 | 1세트 |
| GY-906-BCC 비접촉 적외선 온도 센서 (MLX90614) | 1 |
| 브레드보드 830 포인트 | 1 |
프로젝트 소개
혈중 산소 포화도(SpO2)와 체온을 동시에 측정하여 설정값 미만 시 적색 LED 경고, 정상 시 녹색 LED 점등하는 건강 모니터링 장치입니다.
주요 특징
- MAX30102: 손가락을 자연스럽게 올려놓기만 하면 측정 (과도한 압력 시 측정값 오류)
- MLX90614 (GY-906): 비접촉 적외선 체온 측정
- I2C LCD에 실시간 수치 표시
- 임계값: SpO2 90% 미만 또는 체온 37℃ 초과 시 적색 LED 경고
- 마지막 측정값 EEPROM 저장 후 재부팅 시 표시
깃허브 저장소
코드 및 회로도: https://github.com/DKARDU/bloodoxygen
소스 코드 (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;
int Temperature, temp;
float ESpO2, ESpO2_ROM;
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.");
}
byte ledBrightness = 0x7F, sampleAverage = 4, ledMode = 2;
int sampleRate = 200, pulseWidth = 411, adcRange = 16384;
particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange);
particleSensor.enableDIETEMPRDY();
mlx.begin();
}
void loop() {
uint32_t ir, red, green;
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) {
// 시리얼 플로터용 그래프 데이터 출력
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; // SpO2 계산 공식
ESpO2 = FSpO2 * ESpO2 + (1.0 - FSpO2) * SpO2; // 로우패스 필터
sumredrms = sumirrms = 0.0; i = 0; break;
}
particleSensor.nextSample();
}
#endif
}
void writeEEPROM(float *data) { ... }
float readEEPROM() { ... }
회로도