使用MAX30100和ESP32构建了一个脉搏血氧仪

2023-08-12 11:26:52 |人围观 | 评论:

  脉搏血氧仪是一种广泛使用的医疗测量仪器,它是一种非侵入性和无痛的测试,可以测量我们血液中的氧饱和度水平,可以很容易地检测到氧气的微小变化。在当前的 Covid-19 情况下,在不与患者接触的情况下远程同时跟踪多名患者的氧气水平变得很重要。

  因此,在这个项目中,我们使用MAX30100 脉搏血氧仪和ESP32构建了一个脉搏血氧仪,它将跟踪血氧水平并通过连接到Wi-Fi网络通过互联网发送数据。这样,我们可以通过与患者保持社交距离来远程监控多个患者。获得的数据将显示为图表,便于跟踪和分析患者的状况。

  除 Covid-19 应用外,该项目还可广泛用于慢性阻塞性肺病 (COPD)、哮喘、肺炎、肺癌、贫血、心脏病发作或心力衰竭,或先天性心脏缺陷。

  请注意,该项目中使用的传感器未经过医学评估,并且该项目未针对防故障应用进行测试。始终使用医疗级脉搏血氧仪来确定患者的脉搏和氧气水平,并与医生讨论。这里讨论的项目仅用于教育目的。

  MAX30100 传感器

  MAX30100传感器是集成脉搏血氧仪和心率监测模块。它与I2C数据线通信并向主机微控制器单元提供SpO2 和脉搏信息。它使用光电探测器光学元件,其中红色、绿色 IRLED调制 LED 脉冲。LED电流可配置为 0 至 50mA。下图显示了 MAX30100 传感器。

pYYBAGLyK6qAdd4zAAF45dq7Ldk349.png

  上述传感器模块在 1.8V 至 5.5V 范围内工作。I2C 引脚的上拉电阻包含在模块中。

  所需组件

  WiFi 连接

  ESP32

  MAX30100 传感器

Adafruit IO 用户 ID 和自定义创建的仪表板(将进一步说明)

  5V足够的电源单元,额定电流至少1A

USB数据线 Micro USB 转 USBA

  带有ArduinoIDE 和 ESP32编程环境的 PC。

  连接 MAX30100 血氧计与 ESP32

  下面给出了带有 ESP32 的 MAX30100的完整电路图。

poYBAGLyK6aANSI8AACdrP9Op9o090.png

  这是一个非常简单的示意图。ESP32 devkit C 的引脚 21 和 22 通过SDA 和 SCL 引脚与脉搏血氧计传感器 MAX30100 连接。血氧仪也由 ESP32 开发板上的 5V 引脚供电。我使用面包板和连接线进行连接,我的测试设置如下所示 -

poYBAGLyK6KAB_GVAAUuu1M44SQ245.png

  带有 ESP32 的 Adafruit IO 用于心率监测

  我们之前已经为不同的物联网应用构建了许多 Adafruit IO 项目。Adafruit IO 是一个出色的平台,可以在其中创建自定义仪表板。要为基于IoT的脉搏血氧计传感器创建自定义仪表板,请使用以下步骤 -

  第 1 步:首先在 adafruit IO 中注册,然后提供 Fist 姓名、姓氏、电子邮件地址、用户名和密码。

pYYBAGLyK52AYN6uAAEgOqj316Q899.png

  第 2 步:登录过程完成后,将打开空白仪表板窗口。在此部分中,我们将需要创建一个仪表板以各种方式显示数据。因此,是时候创建新仪表板并提供仪表板的名称和描述了。

poYBAGLyK5qARvrRAAGRLNVSkAQ757.png

  第 3 步:填写完上述表格后,就可以为传感器创建图形和控制部分了。

pYYBAGLyK5aAFIpNAAPK0YlKkrw010.png

  选择开关块。打开或关闭脉搏血氧计传感器需要它。

poYBAGLyK5KAEikKAAFGqPYAGWo599.png

  第 4 步:写下块名称。正如我们在上图中看到的,切换功能将提供两种状态,开和关。在同一过程中,选择图形块。

poYBAGLyK4-AF6R1AAOyUT-K754628.png

  此图表部分需要选择两次,因为将显示两个图表,Heart bit 和 SpO2。两个部分都已创建。如我们所见,我们选择了所有输入和输出功能。

pYYBAGLyK4uAa45hAAFwquarA-A660.png

  第 5 步:下一步也是最后一步是拥有 adafruit 密钥。如我们所见,我们得到了 adafruit 密钥,这需要添加到代码中。

pYYBAGLyK4eAOndUAANI9vXY018122.png

  Adafruit IO 现在已配置。是时候为这个项目准备硬件和创建固件了。

代码说明

这段代码使用了很多库,而且都很重要。这些库是 MAX30100 脉搏血氧计传感器库、用于 I2C 的Wire.h 、用于ESP32中的 WiFi 相关支持的 WiFi.h、Adafruit MQTT和MQTT 客户端库。完整的程序可以在本页底部找到。

上面提到的那些库包含在代码的开头。

#include
       
        #include
        
         #include
         
          #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "MAX30100_PulseOxime
          ter.h" //使用 arduino 内置 MAX30100 lib ( https://github.com/oxullo/Arduino-MAX30100 )
         
        
       

接下来的两个定义是 WLAN SSID 和 WLAN 密码。这必须准确,ESP32 将使用它来连接 WiFi 网络。

#define WLAN_SSID "xxxxxxxxx" #define WLAN_PASS "2581xxxxxx2"

接下来,我们定义了 Adafruit io 定义。

#defineAIO_UPDATE_RATE_SEC 5 #define AIO_SERVER "io.adafruit.com" #define AIO_SERVERPORT 1883 #define AIO_USERNAME "xxxxxxxxxxxxx" #define AIO_KEY "abcdefgh"

更新速率将每 5 秒更新一次数据,服务器将是io.adafruit.com,服务器端口为 1883。用户名和密码将是从 adafruit IO 仪表板生成的用户名和密码。它对所有人都不同,需要按照 adafruit 设置部分中的说明生成。

之后定义 I2C 端口,如原理图所示。

#define I2C_SDA 21 #define I2C_SCL 22

接下来,使用三个变量来存储最后的报告以及 bpm 和 spo2 值。

uint32_t tsLastReport = 0; 浮动 bpm_dt=0; 浮动 spo2_dt = 0;

MQTT 使用发布-订阅模型(发布和订阅)。在此工作模型中,向 Adafruit 服务器提交数据的设备保持在 Adafruit IO 服务器订阅相同数据点的发布模式。在这种效果中,每当设备发布任何新数据时,订阅该数据的服务器都会接收数据并提供必要的操作。

当服务器发布数据并且设备订阅它时,也会发生同样的事情。在我们的应用程序中,设备将 SPO2 和 BPM 的数据发送到服务器,因此它发布相同的数据并从服务器接收 ON-OFF 状态,从而订阅这个。这个东西是在下面描述的代码片段中配置的——

WiFiClient客户端; Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); Adafruit_MQTT_Subscribe sw_sub = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/switch"); // 注意 AIO 的 MQTT 路径遵循以下格式:
       
        /feeds/
        
         Adafruit_MQTT_Publish bpm_pub = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/bpm"); Adafruit_MQTT_Publish spo2_pub = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/SpO2");
        
       

在设置功能中,我们正在启动 I2C,使用预定义的 SSID 和密码连接 WiFi,并启动开关状态的 MQTT 订阅过程(在 Adafruit IO 仪表板中创建的开关按钮)。

无效设置() { 序列号.开始(115200); Wire.begin(I2C_SDA,I2C_SCL); WiFi.开始(WLAN_SSID,WLAN_PASS); 而(WiFi.status()!= WL_CONNECTED){ 延迟(500); Serial.print("."); } 序列号.println(); Serial.println("WiFi 连接"); Serial.println("IP地址:"); Serial.println(WiFi.localIP()); mqtt.subscribe(&sw_sub); Serial.print("正在初始化脉搏血氧仪.."); // 初始化 PulseOximeter 实例 // 故障一般是由于I2C接线不当,缺少电源 //或错误的目标芯片如果(!pox.begin()){ Serial.println("失败"); 为了(;;); } 别的 { Serial.println("成功"); } // IR LED 的默认电流为 50mA,可以更改 // 通过取消注释以下行。检查 MAX30100_Registers.h 中的所有 // 可用选项。 pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA); // 为节拍检测注册回调 pox.setOnBeatDetectedCallback(onBeatDetected); stopReadPOX(); }

在所有这些之后,max30100 以 LED 电流设置启动。MAX30100 头文件中还提供不同的电流设置,用于不同的配置。心跳检测回调函数也被启动。完成所有这些设置后,血氧计传感器将停止。

在循环函数中,每 5000 毫秒启动 MQTT 连接并检查订阅模型。在这种情况下,如果打开开关,它就会开始读取血氧计传感器并发布Heartbeat 和 SPO2 值的数据。如果开关关闭,它将暂停与脉搏血氧计传感器相关的所有任务。

无效循环(){ MQTT_connect(); Adafruit_MQTT_Subscribe *订阅; 而 ((订阅 = mqtt.readSubscription(5000))) { 如果(订阅 == &sw_sub) { Serial.print(F("得到:")); Serial.println((char *)sw_sub.lastread); if (!strcmp((char*) sw_sub.lastread, "ON")) { Serial.print(("正在启动 POX...")); startReadPOX(); BaseType_t xReturned; 如果(poxReadTaskHld == NULL){ xReturned = xTaskCreate( poxReadTask, /* 实现任务的函数。*/ "pox_read", /* 任务的文本名称。*/ 1024*3, /* 以字为单位的堆栈大小,而不是字节。*/ NULL, /* 传递给任务的参数。*/ 2,/* 创建任务的优先级。*/ &poxReadTaskHld ); /* 用于传递创建任务的句柄。*/ } 延迟(100); 如果(mqttPubTaskHld == NULL){ xReturned = xTaskCreate( mqttPubTask, /* 实现任务的函数。*/ "mqttPub", /* 任务的文本名称。*/ 1024*3, /* 以字为单位的堆栈大小,而不是字节。*/ NULL, /* 传递给任务的参数。*/ 2,/* 创建任务的优先级。*/ &mqttPubTaskHld ); /* 用于传递创建任务的句柄。*/ } } 别的 { Serial.print(("正在停止 POX...")); // 删除 POX 读取任务 如果(poxReadTaskHld != NULL) vTaskDelete(poxReadTaskHld); poxReadTaskHld = NULL; } // 删除 MQTT 发布任务 如果(mqttPubTaskHld != NULL){ vTaskDelete(mqttPubTaskHld); mqttPubTaskHld = NULL; } stopReadPOX(); } } } }

  基于物联网的脉搏血氧仪演示

  电路在面包板上正确连接,下面给出的程序被上传到 ESP32。确保在您的代码中相应地更改 Wi-Fi 和 Adafruit 凭据以使其适合您。

poYBAGLyK36AWM3lAAPr4B4UKrc203.png

  与 WiFi 和 Adafruit IO 服务器连接后,它开始按预期工作。

  正如我们所见,SPO2 水平显示为 96%,心跳显示为每分钟 78 到 81 位。它还提供了捕获数据的时间。

pYYBAGLyK3mAOyahAADohAQIs48721.png

  如上图所示,开关关闭,数据为0。该项目的完整工作视频也可以在本页底部找到。

#include

#include

#include

#include "Adafruit_MQTT.h"

#include "Adafruit_MQTT_Client.h"

#include "MAX30100_PulseOximeter.h" //使用 arduino 内置 MAX30100 lib (https://github.com/oxullo/Arduino-MAX30100)

#define WLAN_SSID "xxxxxxxxx"

#define WLAN_PASS "2581xxxxxx2"

#define AIO_UPDATE_RATE_SEC 5

#define AIO_SERVER "io.adafruit.com"

#define AIO_SERVERPORT 1883

#define AIO_USERNAME "xxxxxx"

#define AIO_KEY "abcdefgh"

#define I2C_SDA 21

#define I2C_SCL 22

TaskHandle_t poxReadTaskHld = NULL;

TaskHandle_t mqttPubTaskHld = NULL;

// PulseOximeter 是传感器的高级接口

脉搏血氧仪痘;

uint32_t tsLastReport = 0;

浮动 bpm_dt=0;

浮动 spo2_dt = 0;

WiFiClient客户端;

Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

Adafruit_MQTT_Subscribe sw_sub = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/switch");

// 注意 AIO 的 MQTT 路径遵循以下格式: /feeds/

Adafruit_MQTT_Publish bpm_pub = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/bpm");

Adafruit_MQTT_Publish spo2_pub = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/SpO2");

// 检测到脉冲时触发回调(在下面注册)

无效 onBeatDetected()

{

Serial.println("Beat!")

}

/******************************************* MAX30100 读取暂停函数 * *************************************************/

无效 stopReadPOX(){

pox.shutdown();

}

/******************************************* 启动 MAX30100 读取函数 * *************************************************/

无效 startReadPOX(){

pox.resume();

}

/******************************************* MAX30100 读取任务 *** ***********************************************/

无效 poxReadTask(无效 * 参数)

{

而(1){

// 确保尽快调用更新

pox.update();

vTaskDelay(1/portTICK_PERIOD_MS);

}

poxReadTaskHld = NULL;

vTaskDelete(NULL); //杀死自己

}

/******************************************* MQTT 发布任务 *** ***********************************************/

无效 mqttPubTask( 无效 * 参数 )

{

uint8_t sec_count=0;

而(1){

Serial.print("心率:");

浮动 bpm_dt = pox.getHeartRate();

串行打印(bpm_dt);

Serial.print("bpm / SpO2:");

浮动 spo2_dt = pox.getSpO2();

序列号.print(spo2_dt);

序列号.println("%");

如果(sec_count >= AIO_UPDATE_RATE_SEC){

如果(!bpm_pub.publish(bpm_dt)){

Serial.println(F("无法发布 bmp.."));

} 别的 {

Serial.println(F("bmp 发布成功!"));

}

如果(!spo2_pub.publish(spo2_dt)){

Serial.println(F("未能发布 SpO2.."));

} 别的 {

Serial.println(F("SpO2 发布成功!"));

}

sec_count=0;

}

vTaskDelay(1000 / portTICK_PERIOD_MS);

秒计数++;

}

mqttPubTaskHld = NULL;

vTaskDelete(NULL); //杀死自己

}

/************************************************ MQTT 连接函数 ** ****************************************************** ***/

// 根据需要连接和重新连接到 MQTT 服务器的函数。

无效 MQTT_connect() {

int8_t ret;

如果 (mqtt.connected()) {

返回;

}

Serial.print("正在连接 MQTT...");

uint8_t 重试次数 = 3;

而 ((ret = mqtt.connect()) != 0) {

Serial.println(mqtt.connectErrorString(ret));

Serial.println("5 秒后重试 MQTT 连接...");

mqtt.disconnect();

延迟(5000);

重试——;

如果(重试 == 0){

而(1);

}

}

Serial.println("MQTT 已连接!");

}

/************************************************* *************************************************/

无效设置()

{

序列号.开始(115200);

Wire.begin(I2C_SDA,I2C_SCL);

WiFi.开始(WLAN_SSID,WLAN_PASS);

而(WiFi.status()!= WL_CONNECTED){

延迟(500);

Serial.print(".");

}

序列号.println();

Serial.println("WiFi 连接");

Serial.println("IP地址:"); Serial.println(WiFi.localIP());

mqtt.subscribe(&sw_sub);

Serial.print("正在初始化脉搏血氧仪..");

// 初始化 PulseOximeter 实例

// 故障一般是由于I2C接线不当,缺少电源

//或错误的目标芯片

如果(!pox.begin()){

Serial.println("失败");

为了(;;);

} 别的 {

Serial.println("成功");

}

// IR LED 的默认电流为 50mA,可以更改

// 通过取消注释以下行。检查 MAX30100_Registers.h 中的所有

// 可用选项。

pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);

// 为节拍检测注册回调

pox.setOnBeatDetectedCallback(onBeatDetected);

stopReadPOX();

}

无效循环(){

MQTT_connect();

Adafruit_MQTT_Subscribe *订阅;

而 ((订阅 = mqtt.readSubscription(5000)))

{

如果(订阅 == &sw_sub)

{

Serial.print(F("得到:"));

Serial.println((char *)sw_sub.lastread);

if (!strcmp((char*) sw_sub.lastread, "ON"))

{

Serial.print(("正在启动 POX..."));

startReadPOX();

BaseType_t xReturned;

如果(poxReadTaskHld == NULL){

xReturned = xTaskCreate(

poxReadTask, /* 实现任务的函数。*/

"pox_read", /* 任务的文本名称。*/

1024*3, /* 以字为单位的堆栈大小,而不是字节。*/

NULL, /* 传递给任务的参数。*/

2,/* 创建任务的优先级。*/

&poxReadTaskHld ); /* 用于传递创建任务的句柄。*/

}

延迟(100);

如果(mqttPubTaskHld == NULL){

xReturned = xTaskCreate(

mqttPubTask, /* 实现任务的函数。*/

"mqttPub", /* 任务的文本名称。*/

1024*3, /* 以字为单位的堆栈大小,而不是字节。*/

NULL, /* 传递给任务的参数。*/

2,/* 创建任务的优先级。*/

&mqttPubTaskHld ); /* 用于传递创建任务的句柄。*/

}

}

别的

{

Serial.print(("正在停止 POX..."));

// 删除 POX 读取任务

如果(poxReadTaskHld != NULL){

vTaskDelete(poxReadTaskHld);

poxReadTaskHld = NULL;

}

// 删除 MQTT 发布任务

如果(mqttPubTaskHld != NULL){

vTaskDelete(mqttPubTaskHld);

mqttPubTaskHld = NULL;

}

stopReadPOX();

}

}

}

}





标签: [db:tags]