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

氣體感測器模組電路圖
Raspberry Pi Pico W
Raspberry Pi Pico W 擴充板
MAX30102 心率 / 脈搏 / 血氧感測器
母 – 母 杜邦線

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?