Sunteți pe pagina 1din 10

/*

Gonzalo Martínez Pérez UAM

January 2019

Poximeter 2.2. Pulse Oximeter. Measures heart rate and blood oxygen concentration.

Two leds are turned on and off intermittently: the red led is turned on while the ir led is off;
then both of them are left off; then the IR led is turned on; then it is turned off again,

completing the cycle. The light from the led that's on rebounds off the finger (or other part of
the body) and hits a photodiode which produces a current. The current is then amplified,

converted into voltage and filtered, separating a DC and an AC component. We measure both
components for each of the three situations (once for each situation and cycle): red led on, ir
led

off; both leds off; and red led off, ir led on. This way we get values for the red and infrarred dc
and ac components, which we save in different vectors. This is repeated until we have a full

vector with the data from a few seconds.

To get the period, we only use the AC red signal. We calculate the time distance between
minima. In one sample, we get a few values for the period; we average to get a more accurate
value.

Heart rate values outside normal ranges are automatically excluded.

To get oxygen concentration, we need to calculate the ratio [redAC/iredAC][redDC/iredDC].


The ratio of the DC components is easily obtained by dividing each term of the redDC vector

by each corresponding term of the iredDC vector, and taking the average value of all these
ratios. To get the ratio of the AC components, we substract first the moving average of each
point

and then divide each new redAC point by the corresponding new iredAC point; we take the
average of all these ratios as the proper value, and from it we calculate the oxygen
concentration.

The heart rate and blood oxygen concentration are both displayed on the serial monitor and
the LCD screen.

WARNING: This code couldn't be properly verified because of inability to reduce noise in the
ired signal
*/

// INCLUDE FUNCTIONS FOR LCD

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F,16,2);

// DECLARE GLOBAL VARIABLES

// Variables required to turn the LEDs on and off

int rled= 4;// digital pin for rled source voltage

int irled= 5;// digital pin for irled source voltage

// Variables required to get the average values of each sample vector

int count=0;// counter to get The average values

int maxcount=40;// number of values measured per averaged value

long int rac=0;

long int rdc=0;

long int irac=0;

long int irdc=0;

long int ac0=0;

long int dc0=0;

// Variables required to define the sample vectors

int n=0;// Index for each data point in the sample vectors

long tprev=0;// Reference time; time when each sampling starts (each sample starts at the
time of the last minimum)

// Variables required to calculate minima and period (and quotient of AC amplitudes)


int l=0;// Counter for the number of minima in each sample

int mk;

double HR; // Heart rate

double vector[15];// Vector to store the minima; the number of minima depends on the time
length of the sample and the heart rate, but it should be < 15

double long t0=0;// Time reference for the time of the first minimum

int const x=20;// Number of points for the moving average

int const dim=200;// Number of points in each sample vector

int y=5;// Required to calculate the minima

double fracac [dim-2*x];// Vector with the AC ratios

// Variables required to calculate oxygen levels

double CHbOir=0.6;// Coeficiente de absorción de la hemoglobina oxigenada en el infrarrojo

double CHbOr=0.2;// Coeficiente de absorción de la hemoglobina oxigenada en el rojo

double CHbir=0.35;// Coeficiente de absorción de la hemoglobina desoxigenada en el


infrarrojo

double CHbr=1.8;// Coeficiente de absorción de la hemoglobina desoxigenada en el rojo

double SpO2;// Concentración de oxígeno

// ACTIONS THAT ARE EXECUTED ONLY ONCE

void setup(){

Serial.begin(9600);// Begin serial communication at 9600 bpm

pinMode (rled, OUTPUT);//Set pins rled and irled in output mode

pinMode (irled, OUTPUT);

Serial.println("Please wait");// Print "Please wait" on serial monitor

lcd.init(); // Start LCD

lcd.backlight(); // Turn LCD backlight on

lcd.print("Please wait"); // Print on LCD

digitalWrite(rled,LOW);//Start with both leds off

digitalWrite(irled,LOW);
}

// THE LOOP IS REPEATED ONCE FOR EACH SAMPLE VECTOR (the sample vector values are
reinitialized and the process is repeated)

void loop(){

// DECLARE SAMPLE VECTORS

int racvalues[dim];// red light ac values

int rdcvalues[dim];// red light dc values

int iracvalues[dim];// ired light ac values

int irdcvalues[dim];// ired light dc values

int times[dim];// times for each measured value

// FILL SAMPLE VECTORS WITH VALUES

while(n<dim){//While the number of obtained values is less than or equal to the size of the
sample vectors, it repeats the while instructions

while(count<maxcount){//One average point for each magnitude is measured each times it


enters the while; when it leaves, an average value is obtained

count++;// count=1 for the first measured value

// Turn the red led ON (ired led is OFF) and measure values of red(ac) and red(dc) signals

digitalWrite(rled,HIGH);

delayMicroseconds(200);// A delay of 200 microseconds is introduced in case there is a lag


between the emission of the pulse and its detection in A0 and A1

rac=rac+analogRead(A0);// analogRead takes about 500 microseconds to be executed

rdc=rdc+analogRead(A1);
// Turn the red led OFF (ired led is OFF) and measure values of ac and dc when both leds are
off

digitalWrite(rled,LOW);

delayMicroseconds(200);

//ac0=ac0+analogRead(A0);// There is the possibility of substracting ambient noise (signal


when both leds are off)

//dc0=ac0+analogRead(A1);

// Turn the irled ON (red led is OFF) and measure values of ac and dc for ir

digitalWrite(irled,HIGH);

delayMicroseconds(200);

irac=irac+analogRead(A0);

irdc=irdc+analogRead(A1);

// Turn the irled back OFF

digitalWrite(irled,LOW);

delayMicroseconds(200);

// After maxcount loops (about 60 milliseconds), we divide the sum of all the values obtained
so far by the number of measures (or counts) to get the average value and store it

double racav=(rac-ac0)/maxcount;

double rdcav=(rdc-dc0)/maxcount; // The AC and DC values when both leds are off can
be substracted

double iracav= (irac-ac0)/maxcount;

double irdcav= (irdc-dc0)/maxcount;


racvalues[n]=racav; // The values are stored in the sample vectors

rdcvalues[n]=rdcav;

iracvalues[n]=iracav;

irdcvalues[n]=irdcav;

times[n]=millis()-tprev; // The time for each sample vector starts at the time of the
last minimum (tprev)

count=0; // The counter is reinitialized to calculate the next averaged


value

rac=0;

rdc=0;

irac=0;

irdc=0;

ac0=0;

dc0=0;

n++; // The index for the number of data in the sample vector is
updated (n=0 for the first value)

}//It leaves the while loop when n=dim

// CALCULATE PERIOD AND AC AMPLITUDES RATE

// When our data sample is full (n=dim), we analyse the data to get the heart rate and AC
amplitudes rate

for(int m=y;m<dim-y;m++){// We go through al m in the sample vectors except the "y" first
ones and the "y" last ones

//For each m, take the y closest neighbour points to the left and right and find the minimum
value

int minim=1023;

for(int k=m-y;k<=m+y;k++){

if(racvalues[k]<minim){

minim=racvalues[k];

mk=k;
}

// If m itself is the minimum value, then we calculate the period as the time difference with
the previous minimum; otherwise, repeat the process with the next m

if (mk==m){

double T=times[m]-t0;

// If it is the first minimum, there is no previous minimum to compare with, but t0=0, so it
skips this loop. Otherwise, it enters the loop (t0>0) and the heart rate and amplitude quotient

// are calculated

if(t0>0){

HR=60.0/T*1000;// Heart rate in bpm

if(HR>50&&HR<150){ // If the heart rate is lower than 50 or higher than 150, we assume the
value is wrong and don't store it

l++;//Number of calculated heart rates

vector[l-1]=HR;// Heart rates are stored in a vector of lenght l-1

t0=times[m]; // t0 is updated as the time of the last minimum recorded

//We substract the moving average of the AC signals: for every m in each ac vector, we
calculate its average with the x values to the left and x values to the right; we then substract it

//from the point and do the fraction of red ac and ired ac for that value and store it in the
fracac vector

if(m>x-1&&m<dim-x){// We go through all m values except the "x" first ones and the "x" last
ones

double long RACmovav=0;

double long IRACmovav=0;

for(int k=m-x;k<=m+x;k++){

RACmovav=RACmovav+racvalues[k];

IRACmovav=IRACmovav+iracvalues[k];

fracac[m-x]=(racvalues[m]-RACmovav/(2*x+1))/(iracvalues[m]-IRACmovav/(2*x+1));
}

// CALCULATE BLOOD OXYGEN RATIO

// We calculate the fraction of the DC components

double fracdc=0;

for(int k=x; k<dim-x;k++){

fracdc=rdcvalues[k]/irdcvalues[k]+fracdc;

double fracdcav=fracdc/(dim-2*x);

// We calculate the R constant and the SpO2

double R=0;

for (int k=0;k<dim-2*x;k++){

R=fracac[k]/fracdcav+R;

R=R/(dim-2*x);

SpO2=1/(1-(R*CHbOir-CHbOr)/(R*CHbir-CHbr))*100;

// CALCULATION OF PERIOD

int g=l;

while(g>2){

// Calculate the average

double long valor=0;

for(int k=0;k<g;k++){

valor=valor+vector[k];

}
double mean=valor/g;

// Find the value farthest from the average

double disp=0;

for(int k=0;k<g;k++){

if(abs(vector[k]-mean)>=disp){

disp=abs(vector[k]-mean);

mk=k;

// Place the value farthest from the average at the end of the vector

double disc=vector[g-1];

vector[g-1]=vector[mk];

vector[mk]=disc;

// Decrease g so that in the next loop the average is calculated for all the values except the
one/s we found was/were farthest from the average

g=g-1;

}// It leaves the loop when g=2

//Do the average of the two remaining values

double long valor=0;

for(int k=0;k<g;k++){

valor=valor+vector[k];

double mean=valor/g;

// Get the average heart rate in multiples of 2

double avHR=int(mean/2)*2;

// PRINT RESULTS

Serial.println("Your heartbeat: ");

Serial.println(avHR);
lcd.clear();

lcd.setCursor(0,0);

lcd.print("Heart rate:"+String(avHR));

Serial.println("Your SpO2: ");

Serial.println(SpO2);

lcd.setCursor(1,0);

lcd.print("SpO2: "+String(SpO2));

//REINITIALIZE VALUES

n=0; // Reinitialize n

tprev=millis(); // Update starting time for the next sample

t0=0; // Reinitialize reference time for the first


minimum of the next sample

l=0; // Reinitialize number of minima detected in the


next sample

S-ar putea să vă placă și