开源脉搏血氧仪:Arduino Nano + MAX30102 + OLED 监测心率血氧
项目背景
COVID-19 导致血氧监测需求激增,商用指夹式血氧仪一度短缺。本项目开源一个低成本、可自制的脉搏血氧仪,基于 Arduino Nano、MAX30102 传感器和 128x64 OLED 显示,外壳 3D 打印。声明:本设备仅供学习/参考,不能替代医疗级设备,读数仅供参考,不作为诊断依据。
核心硬件
- MAX30102:Maxim 集成光学心率/血氧传感器,内置绿光/红光/IR LED、光电二极管、环境光抵消、18-bit ADC,I2C 接口 (地址 0x57),工作电压 3.3V/5V 兼容
- DFRobot Gravity I2C OLED-2864:128x64 像素,SSD1306 驱动,I2C 地址 0x3C,4 线即插即用
- Arduino Nano:ATmega328P,SRAM 2KB,勉强存放 50 组 32-bit 采样数据 (代码中 Uno 兼容模式截断为 16-bit)
- 3D 打印外壳:上盖、底座、传感器固定座、OLED 窗口,PLA 材质,70-80% 填充
接线极简 (I2C 总线并联)
| 模块 | VCC | GND | SDA | SCL |
|---|---|---|---|---|
| MAX30102 | 5V | GND | A4 | A5 |
| OLED | 5V | GND | A4 | A5 |
INT 引脚悬空。巧板焊接时注意走线整齐,避免干扰 I2C 通信。
软件库依赖
Wire.h(内置)MAX30105.h(SparkFun MAX3010x 库)spo2_algorithm.h(Maxim 官方算法,计算 HR/SpO2)SSD1306Ascii.h/SSD1306AsciiWire.h(轻量级 OLED 文本库)
库管理器搜索 “MAX30105” “SSD1306Ascii” 安装;spo2_algorithm.h 需手动放入库目录或同目录下。
算法流程
- 初始化传感器:
particleSensor.setup(55, 4, 2, 200, 411, 4096)(LED 电流 55mA, 采样率 400Hz, 脉宽 411μs, ADC 4096) - 采集前 50 组样本建立基线
- 调用
maxim_heart_rate_and_oxygen_saturation()计算初始 HR/SpO2 - 进入循环:滑动窗口丢弃前 25 组,采集 25 组新样本 (约 1 秒)
- 重新计算 HR/SpO2,校验
validHeartRate和validSPO2 - 有效则 OLED 显示,无效显示 “Not valid”
关键代码片段
#include <Wire.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
MAX30105 particleSensor;
SSD1306AsciiWire oled;
#if defined(__AVR_ATmega328P__)
uint16_t irBuffer[50], redBuffer[50]; // Uno/Nano 截断为 16-bit
#else
uint32_t irBuffer[50], redBuffer[50];
#endif
int32_t spo2, heartRate;
int8_t validSPO2, validHeartRate;
void setup() {
Serial.begin(115200);
oled.begin(&Adafruit128x64, 0x3C);
oled.setFont(Arial14);
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
Serial.println("MAX30105 not found");
while (1);
}
particleSensor.setup(55, 4, 2, 200, 411, 4096);
}
void loop() {
// 首次 50 样本校准
for (byte i = 0; i < 50; i++) {
while (!particleSensor.available()) particleSensor.check();
redBuffer[i] = particleSensor.getRed();
irBuffer[i] = particleSensor.getIR();
particleSensor.nextSample();
}
maxim_heart_rate_and_oxygen_saturation(irBuffer, 50, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
while (1) {
// 滑动窗口
for (byte i = 25; i < 50; i++) {
redBuffer[i - 25] = redBuffer[i];
irBuffer[i - 25] = irBuffer[i];
}
// 采集 25 新样本
for (byte i = 25; i < 50; i++) {
while (!particleSensor.available()) particleSensor.check();
redBuffer[i] = particleSensor.getRed();
irBuffer[i] = particleSensor.getIR();
particleSensor.nextSample();
}
maxim_heart_rate_and_oxygen_saturation(irBuffer, 50, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
printToScreen();
}
}
void printToScreen() {
oled.clear(); oled.setCursor(0, 0);
if (validSPO2 && validHeartRate) {
oled.print(F("HR: ")); oled.println(heartRate);
oled.print(F("SPO2: ")); oled.println(spo2);
} else {
oled.print(F("Not valid"));
}
}
使用方法
- 组装硬件,上传代码
- 手指平放于 MAX30102 传感器窗口,轻合上盖 (避免环境光、施压过大影响读数)
- 接入 5V 供电 (USB 移动电源即可)
- 等待约 10 秒稳定,OLED 显示心率 (BPM) 和血氧饱和度 (%)
- 若长期显示 “Not valid”,调整手指位置、压力或环境光
已知局限与改进方向
- 算法对运动伪影敏感,需静止测量
- Arduino Nano SRAM 限制采样精度,升级 ESP32/STM32 可用完整 32-bit 算法
- 增加电池供电管理 (TP4056 + 3.7V 锂电)、充电指示
- 加入数据记录 (MicroSD) 或蓝牙传输至手机 App
- 外壳优化:增加弹簧结构自适应手指粗细,改善贴合度
文件资源
- 3D 打印 STL:项目附件区下载
- 完整代码及库:GitHub (示例链接)
制造工艺
- FirePick Delta 项目日志:基于 OpenPnP 的开源微型工厂构建
- Arduino 数字骰子:LCD 显示随机点数的电子骰子
- DIY 钢铁侠人偶:Arduino 控制超声波触发音效与灯光
- Find Me:基于 Arduino 与蓝牙的智能寻物器制作指南
- 打造功能完整的 Arduino 驱动 3D 打印机械臂:分步指南
- 便携式血氧仪(脉搏血氧监测):Arduino Nano + MAX30100 + OLED,COVID-19 应急开源方案
- DIY Arduino 加湿器控制器:继电器安全控制高压电路
- Arduino Nano 驱动 NeoPixel 矩阵:自制霓虹乒乓游戏
- DIY Arduino 手柄方向盘 - 游戏竞速模拟器自制指南
- Arduino + TEA5767 打造 FM 收音机:Nokia 5110 显频谱/信号强度/立体声
- Health Band 智能健康手环:基于 Infineon DPS310 的老人跌倒/步数/体温监护系统
- 为何开源驱动边缘创新——必读电子书