怕快樂缺氧?用 Arduino 開發板與 MAX30100 打造自己的 DIY 簡易脈搏血氧儀

Alan Wang
7 min readMay 30, 2021
Photo by Syed Ali on Unsplash

這是一篇很短的文,其實也不算創新,但最近剛好能派上用場。

簡而言之,在台灣於 2021 年 5 月爆發大規模社區感染後,有許多在家等待就醫的人因「快樂缺氧」(happy hypoxia)猝逝,也就是因肺部受感染後血氧濃度降低、但患者本身沒有明顯感覺,導致情況惡化就來不及了。也因為如此,人們開始搶購血氧機,甚至有醫生傳授若搶不到血氧機時該怎麼檢查是否隱性缺氧。

剛好我手上有個別人給的 MAX30100 脈搏血氧感測模組,趕快做了一個 DIY 血氧機,裝在一個名片盒裡面給我爸媽用。

由於 MAX30100 感測器(右下紅點)前面沒有東西,所以顯示的數據會怪怪的。

MAX30100 是加州 Maxim Integrated 公司製造的感測器晶片,藉由測量可見紅光和紅外線光在手指血管的吸收程度來測量脈搏(心律)及血氧濃度。你很容易在網路上找到以它為基礎製作的模組:

就算它的精確度可能不及正式的血氧儀,我們也不需要知道 1% 或 2% 的差別。若測出來數字掉到 90% 以下,自己卻覺得無異狀的話,這就能當成很有用的警訊。

材料

以下我假設你已經懂怎麼使用 Arduino 開發板製作創客裝置。這個簡易血氧儀只需要以下設備:

  • 一個 Arduino AVR 家族開發版:Uno、Nano、Micro、Leonardo 等皆可。
  • 一個 LCD 16x2 I2C 顯示模組(背後有 I2C 轉接板的 HD44780 螢幕,請參考這篇說明)
  • 一個 MAX30100 脈搏與血氧感測模組
  • 麵包板和杜邦線(除非你打算用線直接焊在板子腳位上)
  • (選擇性)一個 LED 和 220Ω 電阻

根據產品規格書,MAX30100、MAX30102 和 MAX30105 的通訊方式相同,因此理論上也可以用在這裡,但我手邊沒有材料來驗證。

較老的 Arduino 系列開發板在台灣仍然容易找到,而且另外兩個原件剛好都需要 5V 電源(MAX30100 理論上用 3.3V 也能跑,但測試發現在 5V 電源下運作才正常 — — 雖然正確來說 Arduino 接 USB 的實際輸出是 4.7V 左右),直接用 Arduino 就直接、便宜又方便。

我個人用的是以前在電子材料行買的 Arduino Pro micro,其實就是縮小型的 Leonardo。但使用不同 Arduino 板子時要注意 I2C 腳位會有不同:

  • Uno/Nano/Pro mini:SCL → A5,SDA → A4
  • Leonardo/Micro/Pro micro:SCL → 3,SDA → 2
  • Mega2560:SCL → 21,SDA → 20

LCD 和 MAX30100 電源(Vin/Vcc)都接 5V,GND 接 GND。此外本文的程式會用內建 LED(腳位 13)的閃動代表脈搏指示器,但我用的 Pro micro 剛好沒有,所以是外接了一個藍色 LED,這樣從盒子裡看也比較顯眼。

下圖以 Arduino Nano 為基礎示範接線(不含外接 LED):

程式

這裡使用的函式庫分別是:

在 Arduino IDE 的函式庫畫面搜尋它們和下載之,或者用上面的連結下載 .zip 和匯入。

這支程式其實就是以這兩個函式庫的範例程式為基礎組合而成。程式會根據你選擇的板子使用預設的 I2C 腳位,這部分就不必再設定,但注意有些 LCD 可能會有 I2C 位址 0x3f 而不是 0x27。而若你有外接 LED 的話,也記得更改常數 BEAT_LED 的腳位。

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#include <LiquidCrystal_I2C.h>
#define REPORTING_PERIOD_MS 1000 // 數據顯示間隔 (毫秒)
#define BEAT_LED 13 // 脈搏指示 LED 的腳位
PulseOximeter pox;
uint32_t tsLastReport = 0;
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD I2C 位址和長寬void setup() {
pinMode(BEAT_LED, OUTPUT);
digitalWrite(BEAT_LED, LOW);
// 初始化 LCD
lcd.init();
lcd.backlight();
lcd.clear();
lcd.print("Arduino Oximeter");
delay(1000);
// 初始化 MAX30100, 若失敗就用無窮迴圈卡住
if (!pox.begin()) {
lcd.setCursor(0, 1);
lcd.print("Failed to init!");
while (true) {}
}
// 設定測到脈搏時要回呼的函式 (必須放在 setup 最後一行)
pox.setOnBeatDetectedCallback(onBeatDetected);
}
void loop() {
pox.update(); // 更新 MAX30100 讀數
// 每隔指定的時間讀一次讀數, 並顯示在 LCD 上
if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
float bpm = pox.getHeartRate();
uint8_t spo2 = pox.getSpO2();
lcd.clear();
lcd.home();
lcd.print(" Bpm : " + String(bpm)); // 顯示脈搏 bpm
lcd.setCursor(0, 1);
lcd.print(" SpO2: " + String(spo2) + "%"); // 顯示血氧濃度
tsLastReport = millis();
}
}
void onBeatDetected() { // 點亮 LED 50 毫秒代表一次脈搏
pinMode(BEAT_LED, OUTPUT);
digitalWrite(BEAT_LED, HIGH);
delay(50);
digitalWrite(BEAT_LED, LOW);
}

我的版本裝在名片盒中,蓋住所有裸露的電子接點,只在 MAX30100 的讀取頭上挖個洞,並盡量固定杜邦線免得鬆脫。既然電源來自 USB 線,就能沿用手機充電器。

通電後,把食指按在紅燈上,等一段時間就能看到測出結果。理論上我也應該找個東西罩住手指上方,像市面上的血氧儀那樣,不過只要手沒有被強光照到就似乎沒明顯影響。

--

--

Alan Wang

Technical writer, former translator and IT editor.