MAX30102 心率 / 脈搏 / 血氧感測器

MAX30102 以光電體積描記法(Photoplethysmography , PPG)工作,利用紅光與紅外光(IR)照射皮膚,並量測反射光強度來推算心率與血氧。不同血紅蛋白對兩種光的吸收率不同,透過分析其反射光比例可計算 SpO₂;而血液脈動造成的反射光週期變化則可用來取得心率。可用於健康監測、穿戴裝置開發、智慧醫療教學等場域。

注意: MAX30102感測器針腳外漏,所以測量時手指需保持乾燥,太潮濕有可能造成感測器短路。

氣體感測器模組電路圖

  • Raspberry Pi Pico W

  • Raspberry Pi Pico W 擴充板

  • MAX30102 心率 / 脈搏 / 血氧感測器

  • 母 – 母 杜邦線

MAX30102 心率 / 脈搏 / 血氧感測器是I2C訊號輸入。本範例之模組SDA腳位需接至Raspberry Pi Pico擴充板D4腳位,模組SCL腳位需接至Raspberry Pi Pico擴充板D5腳位。

Arduino 程式如下

#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"

// -------------- 參數設定 --------------
#define FINGER_ON 7000               // 手指是否放上的 IR 門檻
#define MINIMUM_SPO2 90.0            // 最低血氧值(避免顯示過低)
boolean max3010xReady = false;       // 感測器初始化狀態

// 心跳與血氧計算用的平均與平滑變數
double avgRed = 0, avgIR = 0, ESpO2 = MINIMUM_SPO2;
const double FSpO2 = 0.7, frate = 0.95;  // SpO2 與 DC 分量平滑係數
byte validMin = 20, validMax = 250;      // 合理的心跳範圍(20 ~ 250 BPM)

MAX30105 max3010xSensor;  // 建立 MAX30105 感測器物件

// -------------- 心跳偵測函式 --------------
int getHeartBeat(byte avgTimes) {
  long lastBeat = 0, myIRvalue = 0, delta = 0;
  byte myCount = 0;
  int beatSum = 0;
  float myBPM = 0.0;

  // 收集指定次數的心跳數據並平均
  while (myCount < avgTimes) {
    myIRvalue = max3010xSensor.getIR();
    if (checkForBeat(myIRvalue)) {
      delta = millis() - lastBeat;
      lastBeat = millis();
      myBPM = 60 / (delta / 1000.0);  // 換算成 BPM

      if (myBPM < validMax && myBPM > validMin) {
        beatSum += (byte)myBPM;
        myCount++;
      }
    }
    // 偵測手指離開則中止
    if (myIRvalue < FINGER_ON)
      break;
  }

  if (myCount == 0) myCount = 1;  // 防止除以 0
  return (beatSum / myCount);     // 傳回平均心跳
}

// -------------- 血氧計算函式 --------------
double getSPO2(byte avgTimes) {
  uint32_t ir, red;
  double df_red, df_ir;
  byte myCount = 0;
  double sumIR = 0, sumRed = 0, SpO2 = 0;

  // 讀取指定次數的 Red 與 IR 數據,計算 AC 成分變異
  while (myCount < avgTimes) {
    max3010xSensor.check();
    if (max3010xSensor.available()) {
      myCount++;
      red = max3010xSensor.getFIFOIR();   // 取得 IR 值
      ir = max3010xSensor.getFIFORed();   // 取得紅光值
      max3010xSensor.nextSample();

      df_red = (double)red;
      df_ir = (double)ir;

      // 平滑 DC 成分
      avgRed = avgRed * frate + df_red * (1.0 - frate);
      avgIR = avgIR * frate + df_ir * (1.0 - frate);

      // 累積 AC 成分變異(平方)
      sumRed += (df_red - avgRed) * (df_red - avgRed);
      sumIR += (df_ir - avgIR) * (df_ir - avgIR);
    }
  }

  // 計算 R 比例並推估 SpO2
  if (myCount == avgTimes) {
    double R = (sqrt(sumRed) / avgRed) / (sqrt(sumIR) / avgIR);

    // 用較穩定的 SpO2 經驗公式
    SpO2 = 110.0 - 25.0 * R;

    // 平滑顯示數值
    ESpO2 = FSpO2 * ESpO2 + (1.0 - FSpO2) * SpO2;

    // 限制範圍
    if (ESpO2 <= MINIMUM_SPO2) ESpO2 = MINIMUM_SPO2;
    if (ESpO2 > 100)           ESpO2 = 99.9;
  } else {
    ESpO2 = MINIMUM_SPO2;
  }

  return ESpO2;  // 傳回平滑後 SpO2
}

// -------------- 初始化設定 --------------
void setup() {
  max3010xReady = max3010xSensor.begin(Wire, I2C_SPEED_FAST);

  // 感測器參數設定(亮度、平均數、取樣率、脈衝寬度、解析度)
  max3010xSensor.setup(127, 4, 2, 800, 215, 16384);
  max3010xSensor.enableDIETEMPRDY();  // 啟用內建溫度
  max3010xSensor.setPulseAmplitudeRed(0x0A);  // 紅光亮度
  max3010xSensor.setPulseAmplitudeGreen(0);   // 不使用綠光

  // 心跳有效範圍(可以依年齡或應用調整)
  validMin = 20;
  validMax = 250;

  Serial.begin(9600);  // 設定序列埠傳輸速度
}

// -------------- 主迴圈(持續讀取與輸出) --------------
void loop() {
  // 判斷是否有手指放上去(根據 IR 值)
  if ((max3010xSensor.getIR() > FINGER_ON)) {
    Serial.println((String("Heart Beat:") + String(getHeartBeat(10))));
    Serial.println((String("SpO2 :") + String(getSPO2(30))));
  } else {
    // 沒偵測到手指,清除狀態並顯示提示
    Serial.println("NO finger detected!!");
    avgRed = 0;
    avgIR = 0;
    ESpO2 = MINIMUM_SPO2;
  }
}

程式執行結果

MAX30102 模組是由紅外線來偵測是否有手指,若無手指按壓則會顯示"No finger detected!!" 。

將手指放置於MAX30102 模組後,盡量不要移動手指,也避免身體做過大的動作,以免讓數值有誤差。

若偵測到手指,則會顯示心跳值及血氧值。

Last updated

Was this helpful?