Arduino 血氧心率模块传感器数据采集

开发环境:

Arduino-1.8.5-windows.exe、HXDZ-30102-ACC


硬件设备:

MAX30102
  • MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。
  • 它集成了一个红光LED和一个红外光LED、光电检测器、光器件,以及带环境光抑制的低噪声电子电路。
  • 采用一个1.8V电源和一个独立的5.0V用于内部LED的电源,应用于可穿戴设备进行心率和血氧采集检测,佩戴于手指、耳垂和手腕等处。
  • 标准的I2C兼容的通信接口可以将采集到的数值传输给Arduino、KL25Z等单片机进行心率和血氧计算。
  • 该芯片还可通过软件关断模块,待机电流接近为零,实现电源始终维持供电状态。
  • 集成了玻璃盖可以有效排除外界和内部光干扰,拥有最优可靠的性能。
心率血氧传感器模块(HXDZ-30102-ACC)

集成了LIS2DH12(ST的三轴加速传感器,用于记录运动数据)和MAX30102(血氧和心率检测记录)。

传感器参数:

电路板尺寸:38*16mm
电路板厚度:2.5mm
LED峰值波长:660nm/880nm
LED供电电压:3.3v~5v
检测信号类型:光反射信号(PPG)
输出信号接口:I2C接口(数字接口)
通信接口电压:3~5v
工作电路:1.5mA(3.3v 输入)
心率精确度:+/- 5bpm,+/- 10bpm(动态)
分辨率: 1bpm
采样率:100Hz(STM32程序)/ 25Hz(arduino程序)

接口说明

VCC:LED电源输入端,也是I2C总线上拉电平,可以接3.3v或者5v
GND:地线
SCL:I2C总线的时钟引脚
SDA:I2C总线的数据引脚
I_L:LIS2DH12芯片的中断引脚
I_M:MAX30102芯片的中断引脚


工作原理

传统的脉搏测量方法主要有三种:
1、从心电信号中提取
2、从测量血压时压力传感器测到的波动来计算脉率
3、光电容积法

前两种会限制病人的活动,长时间使用会加重病患的心理和生理负担,而光电容积法在实际中时普遍使用的一种有效方法,其特点:方法简单、佩戴方便、可靠性高。

光电容积法基本原理:

利用人体组织在血管搏动时造成透光率不同来进行脉搏和血氧饱和度测量的。其使用的传感器由光源和光电变换器两部分组成,通过绑带或夹子固定在病患的手指、手腕或耳垂上。光源一般采用对动脉血中氧合血红蛋白(Hb02)和血红蛋白(Hb)有选择性的特定波长的发光二极管(一般使用660nm附近的红光和900nm附近的红外光)。当光束透过人体外周血管,由于动脉搏动充血容积变化导致这束光的透光率发生改变,此时由光电变换器接收经人体组织反射的光线,转变为电信号并将其放大和输出。

由于脉搏是随心脏的搏动而周期性变化的信号,动脉血管容积也周期性变化,因此光电变换器的电信号变化周期就是脉搏率。同时根据血氧饱和度的定义,其表示为:


计算公式.png
image.png

注意:
1、SaO2 :广义上的氧饱和度,常指血液样品中的氧含量对该样品血液最大氧含量的百分比(SpO2是经皮血氧饱和度, 而SaO2是动脉血氧饱和度,二者不同,但是相关性好,绝对值十分接近)。
2、HbO2:氧合血红蛋白
3、Hb:还原血红蛋白

MAX30102本身集成了完整的发光LED及其驱动部分,光感应和AD转换部分,环境光干扰消除及数字滤波部分,只将数字接口留给用户,极大地减轻了用户的设计负担。用户只需要使用单片机通过硬件I2C或者模拟I2C接口来读取MAX30102本身的FIFO,就可以得到转换后的光强度数值,通过编写相应的算法就可以得到心率值和血氧饱和度。

工作原理.png


实现算法

心率和血氧饱和度算法流程图:


算法流程图.png

工作流程

首先连接开发板串口,波特率需要进行必要设置,奇偶校验位无,上电后,复位MAX30102,并开始对MAX30102进行功能初始化,此时Red LED和 IR LED交替点亮来检测人体皮肤下血液的搏动和血氧含量(此时可以看到MAX30102有红光亮起,说明初始化成功)。开发板将一段时间内MAX30102采集的LED反射数据存储在内部RAM中,然后分别计算Red LED和 IR LED的直流成分(DC)和交流成分(AC),最后算出数值R并通过预先存储在两波峰之间的时间差T来确定,每分钟心跳数BPM=60/T。(具体算法原理可以参考AN6409芯片手册中29~31页说明)

red和ir是红色LED,红外LED的原始数据,HR表示心率值,HRvalid是心率是否有效标识,SPO2是血氧数值,SPO2valid是血氧首付有效标识

血氧模块与Arduino连接说明:

接口连接说明:


Arduino和HXDZ-30102-ACC接口说明.png

实际连接图:


实物连接图.png

原理图管脚说明:


Arduino UNO原理图.png

实现代码

/*
引脚连接关系
Arduino UNO  <--->  HXDZ-30102/HXDZ-30102-ACC
SDA          <--->   SDA
SCL          <--->   SCL
PIN 10       <--->   INT/I_M
5V           <--->   VCC
GND          <--->   GND
*/
#include <Arduino.h>
#include "algorithm.h"
#include "max30102.h"

//if Adafruit Flora development board is chosen, include NeoPixel library and define an NeoPixel object
#if defined(ARDUINO_AVR_FLORA8)
#include "adafruit_neopixel.h"

//to lower the max brightness of the neopixel LED
#define BRIGHTNESS_DIVISOR 8  
Adafruit_NeoPixel LED = Adafruit_NeoPixel(1, 8, NEO_GRB + NEO_KHZ800);
#endif

#define MAX_BRIGHTNESS 255
#define blinkPin 13

#if defined(ARDUINO_AVR_UNO)
//Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format
//To solve this problem, 16-bit MSB of the sampled data will be truncated.  Samples become 16-bit data.
uint16_t aun_ir_buffer[100]; //infrared LED sensor data
uint16_t aun_red_buffer[100];  //red LED sensor data
#else
uint32_t aun_ir_buffer[100]; //infrared LED sensor data
uint32_t aun_red_buffer[100];  //red LED sensor data
#endif
int32_t n_ir_buffer_length; //data length
int32_t n_spo2;  //SPO2 value
int8_t ch_spo2_valid;  //indicator to show if the SPO2 calculation is valid
int32_t n_heart_rate; //heart rate value
int8_t  ch_hr_valid;  //indicator to show if the heart rate calculation is valid
uint8_t uch_dummy;

// the setup routine runs once when you press reset:
void setup() {

#if defined(ARDUINO_AVR_LILYPAD_USB)    
  pinMode(13, OUTPUT);  //LED output pin on Lilypad
#endif

#if defined(ARDUINO_AVR_FLORA8)
  //Initialize the LED
  LED.begin();
  LED.show();
#endif

  pinMode(blinkPin, OUTPUT);  //LED output pin on UNO

  digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
  delay(1000);
  digitalWrite(blinkPin,LOW); // turn on pin 13 LED
  delay(1000);
  digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
  delay(1000);
  digitalWrite(blinkPin,LOW); // turn on pin 13 LED
  delay(1000);
  digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
  delay(1000);
  digitalWrite(blinkPin,LOW); // turn on pin 13 LED
  delay(1000);
  
  maxim_max30102_reset(); //resets the MAX30102
  // initialize serial communication at 115200 bits per second:
  Serial.begin(115200);
  Serial.print(F("MAX30102 begins to start working"));
  pinMode(10, INPUT);  //pin D10 connects to the interrupt output pin of the MAX30102
  delay(1000);
  maxim_max30102_read_reg(REG_INTR_STATUS_1,&uch_dummy);  //Reads/clears the interrupt status register
  maxim_max30102_init();  //initialize the MAX30102
}

// the loop routine runs over and over again forever:
void loop() {
  uint32_t un_min, un_max, un_prev_data, un_brightness;  //variables to calculate the on-board LED brightness that reflects the heartbeats
  int32_t i;
  float f_temp;
  
  un_brightness=0;
  un_min=0x3FFFF;
  un_max=0;
  
  n_ir_buffer_length=100;  //buffer length of 100 stores 4 seconds of samples running at 25sps

  //read the first 100 samples, and determine the signal range
  for(i=0;i<n_ir_buffer_length;i++)
  {
    while(digitalRead(10)==1);  //wait until the interrupt pin asserts
    maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));  //read from MAX30102 FIFO
    
    if(un_min>aun_red_buffer[i])
      un_min=aun_red_buffer[i];  //update signal min
    if(un_max<aun_red_buffer[i])
      un_max=aun_red_buffer[i];  //update signal max
  }
  un_prev_data=aun_red_buffer[i];
  //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
  maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_spo2, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); 

  //Continuously taking samples from MAX30102.  Heart rate and SpO2 are calculated every 1 second
  while(1)
  {
    i=0;
    un_min=0x3FFFF;
    un_max=0;

    //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
    for(i=25;i<100;i++)
    {
      aun_red_buffer[i-25]=aun_red_buffer[i];
      aun_ir_buffer[i-25]=aun_ir_buffer[i];

      //update the signal min and max
      if(un_min>aun_red_buffer[i])
        un_min=aun_red_buffer[i];
      if(un_max<aun_red_buffer[i])
        un_max=aun_red_buffer[i];
    }

    //take 25 sets of samples before calculating the heart rate.
    for(i=75;i<100;i++)
    {
      un_prev_data=aun_red_buffer[i-1];
      while(digitalRead(10)==1);
      digitalWrite(9, !digitalRead(9));
      maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));

      //calculate the brightness of the LED
      if(aun_red_buffer[i]>un_prev_data)
      {
        f_temp=aun_red_buffer[i]-un_prev_data;
        f_temp/=(un_max-un_min);
        f_temp*=MAX_BRIGHTNESS;
        f_temp=un_brightness-f_temp;
        if(f_temp<0)
          un_brightness=0;
        else
          un_brightness=(int)f_temp;
      }
      else
      {
        f_temp=un_prev_data-aun_red_buffer[i];
        f_temp/=(un_max-un_min);
        f_temp*=MAX_BRIGHTNESS;
        un_brightness+=(int)f_temp;
        if(un_brightness>MAX_BRIGHTNESS)
          un_brightness=MAX_BRIGHTNESS;
      }
#if defined(ARDUINO_AVR_LILYPAD_USB)  
      analogWrite(13, un_brightness);
#endif

#if defined(ARDUINO_AVR_FLORA8)
      LED.setPixelColor(0, un_brightness/BRIGHTNESS_DIVISOR, 0, 0);
      LED.show();
#endif

      //send samples and calculation result to terminal program through UART
      Serial.print(F("red="));
      Serial.print(aun_red_buffer[i], DEC);
      Serial.print(F(", ir="));
      Serial.print(aun_ir_buffer[i], DEC);
      
      Serial.print(F(", HR="));
      Serial.print(n_heart_rate, DEC);
      
      Serial.print(F(", HRvalid="));
      Serial.print(ch_hr_valid, DEC);
      
      Serial.print(F(", SPO2="));
      Serial.print(n_spo2, DEC);

      Serial.print(F(", SPO2Valid="));
      Serial.println(ch_spo2_valid, DEC);
    }
    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_spo2, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); 
  }
}
 

运行效果

初始化:


MAX30102初始化.png

无采集状态:


无采集数据.png

image.png

数据采集状态:


数据开始采集.png

ps:这里由于整体软件和硬件的标准没有像医疗设备的标准一样,所以只是验证性测试。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 一、前言 这几年智能穿戴设备大火,尤其是手环类,从Apple Watch到荣耀手环,再到不知名的某些品牌,智能穿戴...
    借东西阅读 675评论 1 2
  • 假期最后一天,选择与父母在一起,陪他们到紫竹苑踏青赏花。经历了前天的暴雪,昨天的狂风,今天天气晴朗,万里无云,阳光...
    水星2018阅读 198评论 0 0
  • 感恩,快递送来的快件 感恩,妈妈给我打电话,妈妈辛苦啦 感恩,老公辛苦赚钱 感恩,两儿子唱歌给我听 感恩,邻居对两宝好
    梁琼华阅读 49评论 0 0
  • 亲爱的,晚安!
    玉润阅读 197评论 0 0
  • 皇皇Heddy阅读 59评论 0 0