使用 Arduino 测量血氧饱和度

 

我们所经历的,并且在某种意义上仍在经历的冠状病毒疫情,已经把间质性肺炎、肺呼吸机、重症监护和饱和度计等词放在了大家的嘴边。; 新闻媒体每天都会带入我们家中的东西,幸运的是,对于那些不是内部人员或直接涉及患者的人来说,这些东西一直是陌生的。它让我们意识到了我们可能永远不想知道的方面,但就像生活中的任何事件一样,COVID-19 可以成为学习和珍惜我们所见所闻的机会。鉴于此事的时事性,我们认为我们会对现在流行的一种临床设备进行正面评价,因为它在监测似乎是 COVID 主要并发症之一的患者的健康状况方面发挥的作用-19,即呼吸衰竭:我们指的是饱和度计,它是一种医疗设备,能够检测血液的氧合水平,从而检测气体交换的质量和呼吸系统的效率;电子的饱和度计的工作原理是分析指尖周围血管的透明度,指尖周围血管由一束光束投射,该光束检测到手指的反射或交叉。到达光检测二极管的辐射质量给出了氧含量的指示,因为血液越黑,含氧量越少,反之亦然,血液越浅(亮红色),氧含量越高.

饱和度以百分比表示,最大值显然是 100%,而高于 92-93% 的值是好的。当然,能检测到的百分比不仅取决于肺部的状态,还取决于吸入空气的质量以及是否存在可能降低空气中氧气供应的因素。

项目

所以让我们看看它是什么,分析我们展示的设备:为了创建我们建议的饱和度计,这显然是电子的,我们使用了血氧计和心率检测器,在分线板上具有光反射,基于集成MAX30100;确切地说,我们谈论的是一种用于“脉搏血氧仪”的传感器,它能够以非侵入性和组合的方式测量血液中与氧气结合的血红蛋白的量,以及通过脉搏血氧饱和度变化的心跳频率。血管的口径。

传感器通过 I²C 总线传输饱和度数据和血液脉搏数据(这将能够绘制图形类型的心电图,以及推断心跳频率),因此我们使用了 Arduino 板获取这些信息,处理它们并将它们转换成适合控制小型 IPS 显示器的格式,我们将在其顶部看到 ECG(心电图),即使不像从心电图仪获得的那样完整,在底部左侧是脉搏率(BPM,或每分钟心跳次数,或者,如果您更喜欢盎格鲁-撒克逊术语,则是每分钟心跳……),右侧是氧饱和度百分比。

氧气和脉搏检测

已安装在分线板上的 Maxim IC 传感器是您在图 1 中看到的传感器。

与大多数应用于指尖的饱和传感器(将光束投射到皮肤上并检测指甲表面的照度)不同,Maxim IC 的传感器通过将光发射到芯片顶部来工作由两个 LED 产生,一个是红色(波长为 660 nm 的红光),另一个是红外线(波长为 880 nm 的红外线发射器):将手指放在上面,一部分穿透到其中的光被反射,并且由在发射组合光的同一表面上的光电二极管捕获的反射光允许根据透明度和透明度随时间的变化来确定血流趋势和其中的氧气百分比(饱和血红蛋白的百分比)氧气)。

图 1

具体来说,光电二极管产生的电信号被发送到低噪声放大器和信号调节器阶段,然后结果进入模拟信号处理块,允许提取血氧饱和度和心率数据。后者是通过分析由心脏心室的搏动和释放引起的外周血管扩张和收缩引起的血液流动模式的变化而获得的,这些变化会导致血压升高和降低。

Maxim 组件还具有接近检测功能,可在用户手指不在传感器上时节省能源并减少可见光输出。

图 2MAX30100器件的操作示意图。

图2

由信号处理模块数字化的信号然后变成数字并在 I²C 总线上发送,我们的 Arduino Uno 从中读取它们,其固件使用特定于传感器的库(称为MAX30100.h),可以计算心率和氧饱和度的百分比值。

处理后的数据允许准备数据包和命令,然后我们用这些数据包和命令来控制将成为我们用户界面的小型 OLED 显示屏,即我们将在其上看到血压趋势图(因此脉搏的影响)的面板心脏)传感器检测到的心率和饱和度;全部实时更新。

MAX30100 允许用户设置,通过I²C总线和初始化,采样速率,然后样品在时间,所述模拟/数字转换器放置在模拟信号处理器的下游必须执行单元的数目时:更高的频率的结果在更准确的分析和显示中,但会增加功耗,而采样率的降低(因此测量的准确性)会导致功耗的降低;这是可观的,并且代表在您想要创建便携式饱和度计的应用中的妥协。

完整的电路

看接线图我们看到安装Maxim传感器的分线板通过三根线加地和5V电源(分别取自GND和Arduino Uno的5V引脚)连接到Arduino Uno;三条线分别是I²C总线和INT的SCL和SDA,即开漏中断输出低电平有效,表示有数据可供读取。SDA 和 SCL 线也是漏极开路的,因此建议提供上拉电阻,在分线板中为 4.7 kohm,但端接在内部电源线上。

三条线路每条都需要一个 4.7 kohm 的上拉电阻到 5 V 正极,因为分线板工作在 5 V,而MAX30100工作在 1.8 和 3.3V 之间;这是因为在小板上有用于组件的调节器。

出于这个原因,由于 Arduino Uno 的内部供电电压为 5 伏,并且 I/O 在此电压下运行(但仍然能够读取较低的逻辑 1 电平),因此移除内部上拉电阻并应用外部连接到 Arduino Uno 5V 线上,如这些页面中建议的接线图所示。

为了显示心率、饱和度和心率轨迹的参数,我们使用了一个微型显示器 TFT/IPS 1.44“对角线 128×128 像素(图形区域为 25.5 x 26.5 毫米宽,所以实际上是正方形)具有串行接口到 SPI 类型巴士。

该显示器从MAX30100传感器接收的数据中接收由 Arduino Uno 草图准备的要显示的数据并显示它们。

为此,固件将 Arduino Uno I/O 初始化为四线 SPI 接口加复位。

该显示器采用全彩 IPS LCD 技术,提供出色的对比度、出色的色深和 ±80° 的宽视角。

其控制电子设备基于久经考验的 ST7735 控制器,配备 SPI 数据接口;Adafruit_ST7735.h 库简化了固件管理。

模块的供电电压在3-5.5V之间,通过底部的8个焊盘进行连接,由于它们的间距为2.54mm,因此可以焊接到普通引脚条上以方便连接。

图3

显示器连接焊盘的含义如下:

GND = 公共地;

VCC = 正电源;

SCL = SPI 时钟;

SDA = SPI 数据线;

RES = 复位;

DC = 数据/命令选择;

CS = 片选信号,逻辑 0 有效;

BLK = 背光控制。

最后一行允许您打开或关闭背光,如果您不想要背光,可以保持打开状态,而要打开背光,它必须设置为逻辑零。

我们的显示器由 Open Electronics 销售,部件号为 3085-144ST7735。

固件

好吧,现在让我们在草图上花几行来加载 Arduino Uno 以运行我们的饱和度计;为此,我们从 Xtronical (www.xtronical.com/ColourHeartMonitor) 的一个想法中汲取灵感,除了这些页面中解释的内容外,它还允许驱动带有电子设备的压电蜂鸣器,使其每次发出蜂鸣声检测到心跳,就像您正在执行心电图一样。例如,如果我们将仪器用作体育活动的监视器,则蜂鸣声可能很有用。

您可以在清单 1 中找到完整的草图;其中我们注意到包含用于管理分线板传感器的 MAX30100.h 库、用于设置传感器范围的 MAX30100_PulseOximeter.h、用于在显示屏上绘制心率图的图形库 Adafruit_GFX.h 以及Adafruit_ST7735.h 用于通过与 ST7735 控制器的接口与显示器本身进行通信。

清单 1

#include “MAX30100.h” // Libraries required for the
#include “MAX30100_PulseOximeter.h” // MAX30100 range sensors
#include <Adafruit_GFX.h> // Graphics library for drawin on screen
#include <Adafruit_ST7735.h> // Hardware-specific library
// Recommended settings for the MAX30100, DO NOT CHANGE!!!!, refer to the datasheet for further info
#define SAMPLING_RATE MAX30100_SAMPRATE_100HZ // Max sample rate
#define IR_LED_CURRENT MAX30100_LED_CURR_50MA // The LEDs currents must be set to a level that 
#define RED_LED_CURRENT MAX30100_LED_CURR_27_1MA // avoids clipping and maximises the dynamic range
#define PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS // The pulse width of the LEDs driving determines
#define HIGHRES_MODE true // the resolution of the ADC
// Create objects for the raw data from the sensor (used to make the trace) and the pulse and oxygen levels
MAX30100 sensor; // Raw Data
PulseOximeter pox; // Pulse and Oxygen
// The following settings adjust various factors of the display
#define SCALING 12 // Scale height of trace, reduce value to make trace height
 // bigger, increase to make smaller
#define TRACE_SPEED 0.5 // Speed of trace across screen, higher=faster 
#define TRACE_MIDDLE_Y_POSITION 41 // y pos on screen of approx middle of trace
#define TRACE_HEIGHT 64 // Max height of trace in pixels 
#define HALF_TRACE_HEIGHT TRACE_HEIGHT/2 // half Max height of trace in pixels (the trace amplitude) 
#define TRACE_MIN_Y TRACE_MIDDLE_Y_POSITION-HALF_TRACE_HEIGHT+1 // Min Y pos of trace, calculated from above values
#define TRACE_MAX_Y TRACE_MIDDLE_Y_POSITION+HALF_TRACE_HEIGHT-1 // Max Y pos of trace, calculated from above values
// Pins to use with the 7735 display
#define TFT_CS 10 // Chop select
#define TFT_RST 9 // Reset
#define TFT_RS 8 // Register select
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_RS, TFT_RST);
 void onBeatDetected()
{
 // beep the beeper
}
void setup()
{
 tft.initR(INITR_144GREENTAB); // initialize a ST7735S chip, for 128x128 display
 tft.fillScreen(ST7735_BLACK);
 tft.setTextSize(1);
 tft.setTextColor(ST7735_WHITE);
 pox.setOnBeatDetectedCallback(onBeatDetected);
 // Initialize the sensor. Failures are generally due to an improper I2C wiring, missing power supply
 // or wrong target chip. Occasionally fails on startup (very rare), just press reset on Arduino
 if (!sensor.begin()) {
 tft.print(“Could not initialise MAX30100”);
 for(;;); // End program in permanent loop
 }
 tft.setCursor(0,0);
 if (!pox.begin()) {
 tft.println(“Could not initialise MAX30100”);
 for(;;); // End program in permanent loop
 }
 // Set up the parameters for the raw data object
 sensor.setMode(MAX30100_MODE_SPO2_HR);
 sensor.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT);
 sensor.setLedsPulseWidth(PULSE_WIDTH);
 sensor.setSamplingRate(SAMPLING_RATE);
 sensor.setHighresModeEnabled(HIGHRES_MODE);
 // Display BPM and O2 titles, these remain on screen, we only erase the trace and the 
 // BPM/O2 results, otherwise we can get some flicker 
 tft.setTextSize(2);
 tft.setCursor(0,86);
 tft.print(“BPM O”);
 tft.setCursor(92,86);
 tft.print(“%”);
 tft.setTextSize(1);
 tft.setCursor(84,94);
 tft.print(“2”); // The small subscriper 2 of O2
 tft.setCursor(1,0); 
 tft.print(“XTronical Health Care”);
 tft.setTextSize(2);
 tft.drawRect(0,TRACE_MIN_Y-1,128,TRACE_HEIGHT+2,ST7735_BLUE); // The border box for the trace 
}
void loop()
{
 int16_t Diff=0; // The difference between the Infra Red (IR) and Red LED raw results
 uint16_t ir, red; // raw results returned in these
 static float lastx=1; // Last x position of trace
 static int lasty=TRACE_MIDDLE_Y_POSITION; // Last y position of trace, default to middle
 static float x=1; // current x position of trace
 int32_t y; // current y position of trace
 uint8_t BPM,O2; // BPM and O2 values
 static uint32_t tsLastReport = 0; // Last time BMP/O2 were checked
 static int32_t SensorOffset=10000; // Offset to lowest point that raw data does not go below, default 10000
 // Note that as sensors may be slightly different the code adjusts this
 // on the fly if the trace is off screen. The default was determined
 // By analysis of the raw data returned 
 pox.update(); // Request pulse and o2 data from sensor
 sensor.update(); // request raw data from sensor
 if(sensor.getRawValues(&ir, &red)) // If raw data available for IR and Red 
 {
 if(red<1000) // No pulse
 y=TRACE_MIDDLE_Y_POSITION; // Set Y to default flat line in middle
 else
 {
 // Plot our new point
 Diff=(ir-red); // Get raw difference between the 2 LEDS
 Diff=Diff-SensorOffset; // Adjust the baseline of raw values by removing the offset (moves into a good range 
for scaling)
 Diff=Diff/SCALING; // Scale the difference so that it appears at a good height on screen
 // If the Min or max are off screen then we need to alter the SensorOffset, this should bring it nicely on screen
 if(Diff<-HALF_TRACE_HEIGHT)
 SensorOffset+=(SCALING*(abs(Diff)-32));
 if(Diff>HALF_TRACE_HEIGHT)
 SensorOffset+=(SCALING*(abs(Diff)-32));
 
 y=Diff+(TRACE_MIDDLE_Y_POSITION-HALF_TRACE_HEIGHT); // These two lines move Y pos of trace to approx middle of display 
area
 y+=TRACE_HEIGHT/4; 
 } 
 if(y>TRACE_MAX_Y) y=TRACE_MAX_Y; // If going beyond trace box area then crop the trace
 if(y<TRACE_MIN_Y) y=TRACE_MIN_Y; // so it stays within
 tft.drawLine(lastx,lasty,x,y,ST7735_YELLOW); // Plot the next part of the trace
 lasty=y; // Save where the last Y pos was
 lastx=x; // Save where the last X pos was
 x+=TRACE_SPEED; // Move trace along the display
 if(x>126) // If reached end of display then reset to statt
 {
 tft.fillRect(1,TRACE_MIN_Y,126,TRACE_HEIGHT,ST7735_BLACK); // Blank trace display area
 x=1; // Back to start
 lastx=x; 
 }
 if (millis() - tsLastReport > 1000) // If more than 1 second (1000milliseconds) has past
 { // since getting heart rate and O2 then get some bew values
 tft.fillRect(0,104,128,16,ST7735_BLACK); // Clear the old values
 BPM=round(pox.getHeartRate()); // Get BPM
 if((BPM<60)|(BPM>110)) // If too low or high for a resting heart rate then display in red
 tft.setTextColor(ST7735_RED);
 else
 tft.setTextColor(ST7735_GREEN); // else display in green
 tft.setCursor(0,104); // Put BPM at this position
 tft.print(BPM); // print BPM to screen
 O2=pox.getSpO2(); // Get the O2
 if(O2<94) // If too low then display in red
 tft.setTextColor(ST7735_RED);
 else
 tft.setTextColor(ST7735_GREEN); // else green
 tft.setCursor(72,104); // Set print position for the O2 value
 tft.print(O2); // print it to screen
 tsLastReport = millis(); // Set the last time values got to current time
 }
 }
}

结论

好吧,我们相信我们已经解释了正确制作和使用饱和度计所需的一切;以及在家里,作为一种固定的诊断工具,您可以将其安装在橡胶顶针上,在户外运动中使用它,将 Arduino Uno 替换为要放置的 Arduino Micro 或 Nano,例如,放在一个盒子上夹克或应用于衬衫(用细多线电缆连接所有东西)以创建带有集成饱和度计的真实心率监测器,也许能够存储在训练课程中进行的身体活动的轨迹。

用到的模块:

Arduino UNO R3

1.44寸彩色TFT模组ST7735

脉搏血氧仪和心率模块 MAX30100

欢迎关注飞多学堂微博:https://weibo.com/u/7586574130

 欢迎关注我的微信公众号,学习更多电子和示波器使用技巧、知识:

 

  • 2
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞多学堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值