【开源】ESP8266_MAX30102_OLED屏幕实现血氧心率检测

这篇具有很好参考价值的文章主要介绍了【开源】ESP8266_MAX30102_OLED屏幕实现血氧心率检测。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本系列,是记录自己入门嵌入式领域的学习笔记。

看看能坚持多久。

今天是2023年5月2日

源代码的来源:https://github.com/yangqingyuan-byte/MAX30102-0.96_4pin_oled-esp8266_HR_and_SPO2

项目需求:

ESP8266_MAX30102_OLED屏幕实现血氧心率检测

硬件连接:

* ESP8266 --- OLED

* GND --- GND *

* 5V --- VCC 

* D1 --- SCL 

* D2 --- SDA 

* ESP8266 --- MAX30102

* 5V --- VIN

* G --- GND

* D0 --- INT

* D1 --- SCL

* D2 --- SDA

用8266和max30100设计心率血氧测量系统,嵌入式项目,物联网,嵌入式硬件,开源,学习,单片机

 

程序源码:

引入库

#include <U8g2lib.h>
#include <SPI.h>
#include <Wire.h>
#include "algorithm_by_RF.h"
#include "max30102.h"

这段代码引入了需要用到的库。其中 U8g2lib 是一个用于字体显示的库;SPI、Wire 分别是用于支持 SPI 和 I2C 协议的库;algorithm_by_RF 和 max30102 分别是根据最大值和最小值计算心率和血氧的自定义库。

// uncomment below line if cannot calculate readings
#define REVERSE_LED

这里定义了 REVERSE_LED 宏。如果无法计算读数,则应取消此行代码的注释。

OLED 屏幕初始化

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);

定义了一个 U8G2_SSD1306_128X64_NONAME_F_SW_I2C 类型的对象 u8g2,这个对象代表了 OLED 屏幕,并把屏幕连接到 I2C 总线上。

void setup()
{
  pinMode(oxiInt, INPUT); // pin D0 connects to the interrupt output pin of the MAX30102
  u8g2.begin();
  u8g2.enableUTF8Print();
  u8g2.clearBuffer();
  delay(2000);
  u8g2.setFont(u8g2_font_unifont_tr);
  u8g2.setCursor(0, 14);
  u8g2.print("Initializing...");
  u8g2.sendBuffer();
  Wire.begin();

  Serial.begin(115200);
  Serial.println("Initializing");

  maxim_max30102_reset(); // resets the MAX30102
  maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_dummy); // Reads/clears the interrupt status register
  maxim_max30102_init();                                  // initialize the MAX30102
  old_n_spo2 = 0.0;

  Serial.println(F("Time[s]\tSpO2\tHR\tRatio\tCorr"));
  timeStart = millis();
}

setup() 函数中,首先将 oxiInt 引脚设置为输入模式(用于连接到 MAX30102 的中断输出引脚),然后对 OLED 屏幕进行初始化操作,在开机画面上输出 "Initializing..."。接着使用 Wire 库初始化 I2C 总线,打开串口输出,并分别进行了 MAX30102 的复位、中断状态清除和初始化操作。最后输出表头 "Time[s]\tSpO2\tHR\tRatio\tCorr",并记录开始时间。

显示心率和血氧值

void print_hr_spo2(int val_hr, int val_spo2){
  if(val_hr > 999 || val_hr < 0){
    return;
  }
  if(val_spo2 > 100 || val_spo2 < 0){
    return;
  }
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_fub14_tr); 
  u8g2.setCursor(0,14);
  u8g2.print(" HR");
  u8g2.setCursor(60,14);
  u8g2.print("SPO2");
  
  char str_hr[4];  
  u8g2.setFont(u8g2_font_fur20_tr);
  itoa(val_hr, str_hr, 10);
  if(val_hr > 99){
    u8g2.drawStr(0, 38, str_hr);
  }else if(val_hr > 9){
    u8g2.drawStr(14,38, str_hr);
  }else{
    u8g2.drawStr(28,38, str_hr);
  }
  
  char str_spo2[4];
  u8g2.setFont(u8g2_font_fur30_tr); 
  itoa(val_spo2, str_spo2, 10);  
  if(val_spo2 > 99){
    u8g2.drawStr(55, 48, str_spo2);
  }else if(val_spo2 > 9){
    u8g2.drawStr(70, 48, str_spo2);
  }else{
    u8g2.drawStr(85, 48, str_spo2);    
  }
  
  u8g2.setFont(u8g2_font_fur11_tr); 
  u8g2.setCursor(13,50);
  u8g2.print("bpm");
  u8g2.setCursor(110,64);
  u8g2.print("%");
  u8g2.sendBuffer();   
}

print_hr_spo2() 函数中,首先判断心率和血氧值是否在有效范围内(0~999 和 0~100),然后清空 OLED 屏幕缓存,并设置字体和显示位置。接着分别将心率和血氧值转化为字符串类型,并根据值的长度和位置绘制在屏幕上,最后输出单位并发送缓存。

void print_measuring(){
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_fub14_tr); 
  u8g2.setCursor(0,14);
  u8g2.print(" HR");
  u8g2.setCursor(60,14);
  u8g2.print("SPO2");
  
  char str_hr[4];  
  u8g2.setFont(u8g2_font_fur20_tr);
  u8g2.drawStr(10, 38, "---");
  
  char str_spo2[4];
  u8g2.setFont(u8g2_font_fur30_tr); 
  u8g2.drawStr(65, 48, "---");

  u8g2.setFont(u8g2_font_unifont_tr);
  u8g2.setCursor(26,62);
  u8g2.print("Measuring...");    
  u8g2.sendBuffer();    
}

print_measuring() 函数中,清空 OLED 屏幕缓存,并设置字体和显示位置。接着绘制未测量状态的心率和血氧值,并输出 "Measuring...",最后发送缓存。

void print_press(){
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_fub14_tr); 
  u8g2.setCursor(0,14);
  u8g2.print(" HR");
  u8g2.setCursor(60,14);
  u8g2.print("SPO2");
  
  char str_hr[4];  
  u8g2.setFont(u8g2_font_fur20_tr);
  u8g2.drawStr(10, 38, "---");
  
  char str_spo2[4];
  u8g2.setFont(u8g2_font_fur30_tr); 
  u8g2.drawStr(65, 48, "---");

  u8g2.setFont(u8g2_font_unifont_tr);
  u8g2.setCursor(20,62);
  u8g2.print("Keep pressing");    
  u8g2.sendBuffer();    
}

print_press() 函数中,清空 OLED 屏幕缓存,并设置字体和显示位置。接着绘制要求将指尖放到传感器上。文章来源地址https://www.toymoban.com/news/detail-771366.html

完整代码如下:

#include <U8g2lib.h>
#include <SPI.h>
#include <Wire.h>
#include "algorithm_by_RF.h"
#include "max30102.h"
#define REVERSE_LED


U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);

const byte oxiInt = 16; // pin connected to MAX30102 INT

uint32_t elapsedTime, timeStart;

uint32_t aun_ir, aun_red;
uint32_t aun_ir_buffer[BUFFER_SIZE];  // infrared LED sensor data
uint32_t aun_red_buffer[BUFFER_SIZE]; // red LED sensor data
float old_n_spo2;                     // Previous SPO2 value
uint8_t uch_dummy;
bool showMeasuring = false;

void setup()
{
  pinMode(oxiInt, INPUT); // pin D0 connects to the interrupt output pin of the MAX30102
  u8g2.begin();
  u8g2.enableUTF8Print();
  u8g2.clearBuffer();
  delay(2000);
  u8g2.setFont(u8g2_font_unifont_tr);
  u8g2.setCursor(0, 14);
  u8g2.print("Initializing...");
  u8g2.sendBuffer();
  Wire.begin();

  Serial.begin(115200);
  Serial.println("Initializing");

  maxim_max30102_reset(); // resets the MAX30102
  maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_dummy); // Reads/clears the interrupt status register
  maxim_max30102_init();                                  // initialize the MAX30102
  old_n_spo2 = 0.0;

  Serial.println(F("Time[s]\tSpO2\tHR\tRatio\tCorr"));
  timeStart = millis();
}

void print_hr_spo2(int val_hr, int val_spo2){
  if(val_hr > 999 || val_hr < 0){
    return;
  }
  if(val_spo2 > 100 || val_spo2 < 0){
    return;
  }
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_fub14_tr); 
  u8g2.setCursor(0,14);
  u8g2.print(" HR");
  u8g2.setCursor(60,14);
  u8g2.print("SPO2");
  
  char str_hr[4];  
  u8g2.setFont(u8g2_font_fur20_tr);
  itoa(val_hr, str_hr, 10);
  if(val_hr > 99){
    u8g2.drawStr(0, 38, str_hr);
  }else if(val_hr > 9){
    u8g2.drawStr(14,38, str_hr);
  }else{
    u8g2.drawStr(28,38, str_hr);
  }
  
  char str_spo2[4];
  u8g2.setFont(u8g2_font_fur30_tr); 
  itoa(val_spo2, str_spo2, 10);  
  if(val_spo2 > 99){
    u8g2.drawStr(55, 48, str_spo2);
  }else if(val_spo2 > 9){
    u8g2.drawStr(70, 48, str_spo2);
  }else{
    u8g2.drawStr(85, 48, str_spo2);    
  }
  
  u8g2.setFont(u8g2_font_fur11_tr); 
  u8g2.setCursor(13,50);
  u8g2.print("bpm");
  u8g2.setCursor(110,64);
  u8g2.print("%");
  u8g2.sendBuffer();   
}

void print_measuring(){
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_fub14_tr); 
  u8g2.setCursor(0,14);
  u8g2.print(" HR");
  u8g2.setCursor(60,14);
  u8g2.print("SPO2");
  
  char str_hr[4];  
  u8g2.setFont(u8g2_font_fur20_tr);
  u8g2.drawStr(10, 38, "---");
  
  char str_spo2[4];
  u8g2.setFont(u8g2_font_fur30_tr); 
  u8g2.drawStr(65, 48, "---");

  u8g2.setFont(u8g2_font_unifont_tr);
  u8g2.setCursor(26,62);
  u8g2.print("Measuring...");    
  u8g2.sendBuffer();    
}

void print_press(){
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_fub14_tr); 
  u8g2.setCursor(0,14);
  u8g2.print(" HR");
  u8g2.setCursor(60,14);
  u8g2.print("SPO2");
  
  char str_hr[4];  
  u8g2.setFont(u8g2_font_fur20_tr);
  u8g2.drawStr(10, 38, "---");
  
  char str_spo2[4];
  u8g2.setFont(u8g2_font_fur30_tr); 
  u8g2.drawStr(65, 48, "---");

  u8g2.setFont(u8g2_font_unifont_tr);
  u8g2.setCursor(20,62);
  u8g2.print("Keep pressing");    
  u8g2.sendBuffer();    
}

void loop()
{
  // Serial.println("looping");
  float n_spo2, ratio, correl; // SPO2 value
  int8_t ch_spo2_valid;        // indicator to show if the SPO2 calculation is valid
  int32_t n_heartrate;         // heart rate value
  int8_t ch_hr_valid;          // indicator to show if the heart rate calculation is valid
  int32_t i;

  // buffer length of BUFFER_SIZE stores ST seconds of samples running at FS sps
  // read BUFFER_SIZE samples, and determine the signal range
  for (i = 0; i < BUFFER_SIZE; i++)
  {
    while (digitalRead(oxiInt) == 1)
      ; // wait until the interrupt pin asserts
    delay(1);
    // wdt_reset();
#ifdef REVERSE_LED
    maxim_max30102_read_fifo(&aun_ir, &aun_red); // read from MAX30102 FIFO
#else
    maxim_max30102_read_fifo(&aun_red, &aun_ir); // read from MAX30102 FIFO
#endif
    if (aun_ir < 5000)
    {
      break;
    }
    if (i == 0)
    {
      if(showMeasuring){
        print_measuring();
        showMeasuring = false;
      }
      Serial.print("Measuring...");
    }
    *(aun_ir_buffer + i) = aun_ir;
    *(aun_red_buffer + i) = aun_red;
  }

  if (aun_ir < 5000)
  {
    print_press();
    showMeasuring = true;
    Serial.print("Put On Finger");
  }
  else
  {
    // calculate heart rate and SpO2 after BUFFER_SIZE samples (ST seconds of samples) using Robert's method
    rf_heart_rate_and_oxygen_saturation(aun_ir_buffer, BUFFER_SIZE, aun_red_buffer, &n_spo2, &ch_spo2_valid, &n_heartrate, &ch_hr_valid, &ratio, &correl);
    // Serial.println("rf_heart_rate_and_oxygen_saturation");
    elapsedTime = millis() - timeStart;
    elapsedTime /= 1000; // Time in seconds

    if (ch_hr_valid && ch_spo2_valid)
    {
      Serial.print(elapsedTime);
      Serial.print("\t");
      Serial.print(n_spo2);
      Serial.print("\t");
      Serial.print(n_heartrate, DEC);
      Serial.print("\t");
      Serial.print(ratio);
      Serial.print("\t");
      Serial.print(correl);
      Serial.println("");

      print_hr_spo2(n_heartrate, (int)(n_spo2+0.5));
      old_n_spo2 = n_spo2;
    }
  }
}

到了这里,关于【开源】ESP8266_MAX30102_OLED屏幕实现血氧心率检测的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MAX30102脉搏血氧仪和心率传感器(三)心率计算——时域法

    本章介绍PPG信号的心率计算方法——时域法。基本思想是计算动态阈值曲线,利用波形与曲线相交来确定PPG信号的周期。 如下图,当PPG波形在相同的位置两次经过动态阈值曲线的交点时, 这段时间的间隔就能认为是PPG的一个周期 ,根据此周期即可求出 心率 。 动态阈值曲线

    2024年02月03日
    浏览(51)
  • STM32+ MAX30102通过指尖测量心率+血氧饱和度

            重要的事情放在最前面:max30102只适用于指尖手指测量,不适用与手腕手指测量,如需做成可穿戴样式选择传感器的小伙伴请pass掉他,因为他只有红光和红外2种光,不够充足的数据源去运算。         由于一些原因,本篇文章所有下载资源不收取任何积分,让你不

    2024年02月03日
    浏览(53)
  • STM32F103标准库函数驱动max30102心率血氧模块

    实际接线图, 1.VIN 3v-5v都可以 2.SDA SCL 是两根依据IIC传输的线(具体看你想用哪两个IO口) 代码里面iicStart.c有解释 3.GND接地 4.其余的端口,我没接,最后是可以接受到数据的。 (想更详细了解模块的朋友,可以看该模块手册)手册放下面了 ----------------------------------------------

    2023年04月15日
    浏览(47)
  • 基于STM32F030、MAX30102血氧心率监测仪的设计(一)

            搞这个设计用时大约一周,中途遇到好多问题,查找资料乱七八糟,始终没有解决问题,只能自己慢慢的啃资料,本文章主要记录设计过程及记录遇到的问题,做个记录同时帮助有需要的朋友。         目前该设计已成功读取计算出血氧及心率数据,算法还需

    2023年04月23日
    浏览(48)
  • 基于STM32F030、MAX30102血氧心率监测仪的设计(二)

            上篇主要讲解了MAX30102寄存器相关知识,这篇主要看下程序配置。 MAX30102寄存器配置         在一般的配置中我们让设备开机直接开始进入SpO2/HR 模式(PROX_INT_EN 置 0),设置两个LED的电流都为0x40,然后开启 RDY 中断使能。这样每次数据采集ok就可以中断一次去

    2024年02月06日
    浏览(48)
  • STM32--基于STM32F103的MAX30102心率血氧测量

    本文介绍基于STM32F103ZET6+MAX30102心率血氧测量+0.96寸OLED(7针)显示(完整程序代码见文末链接) 一、简介 MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了一个红光LED和一个红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。 MA

    2024年01月16日
    浏览(51)
  • STM32传感器外设集--心率模块(MAX30102)

    目录 ​​​​​​​ 一、模块介绍 二、资料获取连接  欢迎关注微信公众号--星之援工作室 发送(MAX30102) 三、接线方式 四、代码编写 main.c max30102.c max30102.h myiic.c myiic.h algorithm.c algorithm.h 五 、参考 MAX30102模块是一种集成了光学传感器和信号处理器的模块,广泛应用

    2024年01月22日
    浏览(53)
  • LuatOS-SOC接口文档(air780E)--max30102 - 心率模块

    初始化MAX30102传感器 参数 传入值类型 解释 int 传感器所在的i2c总线id,默认为0 int int引脚 返回值 返回值类型 解释 bool 成功返回true, 否则返回nil或者false 例子 获取心率血氧(大概需要10s时间测量) 参数 无 返回值 返回值类型 解释 bool 成功返回true, 否则返回nil或者false number 心率

    2024年02月07日
    浏览(46)
  • stm32+MAX30102+OLED

    stm32 + 1个心率血氧模块(MAX30102)+ 1个WiFi模块(ESP8266-01S) + 蜂鸣器 + OLED显示+ 本地数据保存(内置Flash ROM或附加SD卡) 背景:对医院住院危重患者的心率和血氧进行实时监测并报警。 主要功能与要求: 1)了解外置心率血氧模块(MAX30102)检测心率和血氧浓度的原理;模块的接

    2024年01月21日
    浏览(47)
  • 84、基于STM32单片机的心率MAX102血氧浓度脉搏检测系统设计

    毕设帮助、开题指导、技术解答(有偿)见文末。 目录 摘要 一、硬件方案 二、设计功能 三、实物图 四、原理图 五、PCB图 六、硬件框图 七、流程图  八、程序源码  九、 资料包括 血氧饱和度是人体生理参数检测的一个重要指标。但是传统的血氧监测仪造价成本太高,只

    2024年02月07日
    浏览(75)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包