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

<figure><img src="/files/JCc7w6AnEJmHK80bCkFk" alt="" width="137"><figcaption></figcaption></figure>

{% hint style="danger" %} <mark style="color:red;">**注意： MAX30102感測器針腳外漏，所以測量時手指需保持乾燥，太潮濕有可能造成感測器短路。**</mark>
{% endhint %}

### **氣體感測器模組電路圖**

* Raspberry Pi Pico W
* Raspberry Pi Pico W 擴充板
* MAX30102 心率 / 脈搏 / 血氧感測器
* 母 – 母 杜邦線

{% hint style="info" %}
*<mark style="color:$warning;">**MAX30102 心率 / 脈搏 / 血氧感測器是I2C訊號輸入。本範例之模組SDA腳位需接至Raspberry Pi Pico擴充板D4腳位，模組SCL腳位需接至Raspberry Pi Pico擴充板D5腳位。**</mark>*
{% endhint %}

<figure><img src="/files/dx2SNAkpqO0qlxJ5PFV4" alt="" width="497"><figcaption></figcaption></figure>

### **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!!" 。

<figure><img src="/files/zMcDtzzU1fk53QNKJ0I3" alt="" width="341"><figcaption></figcaption></figure>

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

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

<figure><img src="/files/V7SYEStrdRaigcwVOnXj" alt="" width="350"><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cavedu.gitbook.io/cavedu/sheng-wu-yi-xue-cai-liao-bao/max30102-xinl-mai-bo-xue-yang-gan-ce-qi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
