DIY Pulse Oximeter

Malith Dulsara
6 min readDec 28, 2022

--

A pulse oximeter calculates the oxygen saturation percentage using red light and infrared light. The basic principle is oxygenated blood absorbs more infrared rays than deoxygenated blood. The device calculates the oxygen saturation level (also known as the oxygen saturation index or SpO2) based on the amount of light absorbed.

Working Process

The device has two light-emitting diodes (LEDs) that shine light through the skin, one red and one infrared. The LEDs are mounted on a probe placed on the person's finger, earlobe, or toe. As the light passes through the skin, the blood absorbs some of it. The amount of light absorbed depends on the oxygen saturation of the blood. The device also has a photodetector that measures the amount of light transmitted through the skin. The photodetector is mounted on the same probe as the LEDs. The device uses the photodetector information to calculate the blood's oxygen saturation. It does this by comparing the amount of light absorbed by the blood to the amount of light transmitted through the skin. The device displays the oxygen saturation as a percentage on its screen. An average oxygen saturation level is typically between 95% and 100%. Lower levels may indicate that the person is not getting enough oxygen, which could mean a medical condition such as COPD or asthma.

Project

Usually, pulse oximeters are made using a MAX 30100 sensor which will cost more than the components used in my project. Therefore I have reduced the production cost by using some simple electronic components and Arduino.

Components

Resistors (10K, 4.7K*2), Photodiode, IR LED, Red LED, LM 7805, BC547 transistors, OLED display, Arduino pro mini, 9V Battery

Circuit diagrams

Schematic diagram

Diagram of the components

Code


#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define maxperiod_siz 80 // number of samples in a period
#define measures 10 // periods stored
#define samp_siz 4 // samples for average
#define rise_threshold 3 // rising measures to determine a peak

// a liquid crystal displays BPM
LiquidCrystal_I2C lcd(0x3F, 16, 2);

int T = 20;
int sensorPin = A1;
int REDLed = 3;
int IRLed = 4;

byte sym[3][8] = {
{
B00000,
B01010,
B11111,
B11111,
B01110,
B00100,
B00000,
B00000
},{
B00000,
B00000,
B00000,
B11000,
B00100,
B01000,
B10000,
B11100
},{
B00000,
B00000,
B00000,
B01000,
B10101,
B00010,
B00000,
B00000
}
};

void setup() {
Serial.begin(9600);
Serial.flush();
pinMode(sensorPin,INPUT);
pinMode(REDLed,OUTPUT);
pinMode(IRLed,OUTPUT);

lcd.init();
lcd.backlight();

digitalWrite(REDLed,LOW);
digitalWrite(IRLed,LOW);

for(int i=0;i<8;i++) lcd.createChar(i, sym[i]);
}

void loop ()
{

bool finger_status = true;
float readsIR[samp_siz], sumIR,lastIR, reader, start;
float readsRED[samp_siz], sumRED,lastRED;
int period, samples;
period=0; samples=0;
int samplesCounter = 0;
float readsIRMM[maxperiod_siz],readsREDMM[maxperiod_siz];
int ptrMM =0;
for (int i = 0; i < maxperiod_siz; i++) { readsIRMM[i] = 0;readsREDMM[i]=0;}
float IRmax=0;
float IRmin=0;
float REDmax=0;
float REDmin=0;
double R=0;
float measuresR[measures];
int measuresPeriods[measures];
int m = 0;

for (int i = 0; i < measures; i++) { measuresPeriods[i]=0; measuresR[i]=0; }

int ptr;
float beforeIR;
bool rising;
int rise_count;
int n;
long int last_beat;
for (int i = 0; i < samp_siz; i++) { readsIR[i] = 0; readsRED[i]=0; }
sumIR = 0; sumRED=0;
ptr = 0;

int cm=0;

Serial.flush();

while(1)
{
// turn on IR
digitalWrite(REDLed,LOW);
digitalWrite(IRLed,HIGH);

// calculate an average of the sensor
n = 0;
start = millis();
reader = 0.;
do
{
reader += analogRead (sensorPin);
n++;
}
while (millis() < start + T);
reader /= n;
sumIR -= readsIR[ptr];
sumIR += reader;
readsIR[ptr] = reader;
lastIR = sumIR / samp_siz;


digitalWrite(REDLed,HIGH);
digitalWrite(IRLed,LOW);

n = 0;
start = millis();
reader = 0.;
do
{
reader += analogRead (sensorPin);
n++;
}
while (millis() < start + T);
reader /= n;
sumRED -= readsRED[ptr];
sumRED += reader;
readsRED[ptr] = reader;
lastRED = sumRED / samp_siz;
readsIRMM[ptrMM]=lastIR;
readsREDMM[ptrMM]=lastRED;
ptrMM++;
ptrMM %= maxperiod_siz;
samplesCounter++;

if(samplesCounter>=samples){
samplesCounter =0;
IRmax = 0; IRmin=1023; REDmax = 0; REDmin=1023;
for(int i=0;i<maxperiod_siz;i++) {
if( readsIRMM[i]> IRmax) IRmax = readsIRMM[i];
if( readsIRMM[i]>0 && readsIRMM[i]< IRmin ) IRmin = readsIRMM[i];
readsIRMM[i] =0;
if( readsREDMM[i]> REDmax) REDmax = readsREDMM[i];
if( readsREDMM[i]>0 && readsREDMM[i]< REDmin ) REDmin = readsREDMM[i];
readsREDMM[i] =0;
}
R = ( (REDmax-REDmin) / REDmin) / ( (IRmax-IRmin) / IRmin ) ;
}

// check that the finger is placed inside the sensor
if (lastRED < lastIR) {
if(finger_status==true) {
finger_status = false;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("No finger?");
}
} else {
if(finger_status==false) {
lcd.clear();
finger_status = true;
}
}

float avR = 0;
int avBPM=0;

if (finger_status==true){

if (lastIR > beforeIR)
{
rise_count++;
if (!rising && rise_count > rise_threshold)
{
lcd.setCursor(15,1);
lcd.write( 0 );

rising = true;

measuresR[m] = R;
measuresPeriods[m] = millis() - last_beat;
last_beat = millis();
int period = 0;
for(int i =0; i<measures; i++) period += measuresPeriods[i];

period = period / measures;
samples = period / (2*T);

int c = 0;
int avPeriod = 0;

for(int i =1; i<measures; i++) {
if ( (measuresPeriods[i] < measuresPeriods[i-1] * 1.1) &&
(measuresPeriods[i] > measuresPeriods[i-1] / 1.1) ) {

c++;
avPeriod += measuresPeriods[i];
avR += measuresR[i];

}
}

m++;
m %= measures;

lcd.setCursor(0,1);
lcd.print("c " + String(c)+" ");

avBPM = 60000 / ( avPeriod / c) ;
avR = avR / c ;

lcd.setCursor(0,0);
lcd.print("bpm ");
lcd.setCursor(8,0);
lcd.print("SpO2 ");
lcd.setCursor(11,0); lcd.write(1);
lcd.setCursor(7,1);
lcd.print("R ");

if(c > 4) {

int SpO2 = -64 * R + 158;

lcd.setCursor(4,0);
lcd.print(String(avBPM));
lcd.setCursor(12,0);
lcd.print( String(SpO2) +"% ");
lcd.setCursor(9,1);
lcd.print(String(avR) + " ");

} else {
lcd.setCursor(4,0);
lcd.print("---");
lcd.setCursor(12,0);
lcd.print("--% ");
lcd.setCursor(9,1);
lcd.print("----");
}
}
}
else
{
rising = false;
rise_count = 0;
lcd.setCursor(15,1); lcd.print(" ");
}
beforeIR = lastIR;
}

// Plotting
Serial.print(lastIR);
Serial.print(",");
Serial.print(lastRED);
Serial.print(",");
Serial.print(R);
Serial.print(",");
Serial.print(IRmax);
Serial.print(",");
Serial.print(IRmin);
Serial.print(",");
Serial.print(REDmax);
Serial.print(",");
Serial.print(REDmin);
Serial.print(",");
Serial.print(avR);
Serial.print(",");
Serial.print(avBPM);
Serial.println();

// Array handling
ptr++;
ptr %= samp_siz;

}
}

Details of the Components

  • Resistors — 10K and 4.7K

There are two types of resistors used in the pulse oximeter. One is a 10kilo ohm resistor, and the other is a 4.7kilo ohm resistor. Either way, a resistor is usually employed in dropping a voltage or producing a potential divider action. It might also be used to limit current flow.

  • Photodiode

A photodiode is a p — n junction that consumes light energy to generate an electric current. When we consider the working principle of a photodiode, a photodiode is subjected to photons in the form of light, which affects the generation of electron-hole pairs. As a result of the increase in the number of electrons on the n — side and holes on the p-side, a rise in the electromotive forces are observed.

  • IR LED

An infrared light-emitting diode (LED) is used for non-visual applications and nighttime illumination. The application of an infrared LED depends on its wavelength, with the common usage being between 808nm and 940nm.

  • Red LED

The pulse rate is measured by using the wavelength of the IR LED and the red LED light. The body scatters and absorbs visible and near-infrared wavelengths to have a measurable signal. The average number of oxygen atoms attached to a haemoglobin molecule will be observed according to the wavelength.

  • BC 547 transistor

BC547 is a general-purpose BJT NPN transistor with an output current of 100mA. It is ideal to use in amplifications and as a switch due to its DC gain and saturation voltage of 90mV. Especially this transistor is used in applications like sensor circuits, amplifier circuits and many other electronic projects.

  • LM 7805

The LM7805 is a voltage regulator that outputs +5 volts. This voltage regulator can deliver up to 1.5A of current with current limiting and thermal shutdown features.

  • Battery

The purpose of the battery in this circuit is to supply voltage for the operation of the circuit. Here we use a 9V battery to give a relatively wide operating voltage range during its life without being overly high voltage.

  • OLED display

It provides fast oxygen saturation readings and pulse measurements and displays it conveniently on a large digital bright display. The advanced dual OLED display technology helps the user get a brighter & sharper vision of the information from a pulse oximeter.

  • Arduino pro mini

Arduino Pro Mini is a small-sized microcontroller board, and it comes with an Atmega328 microcontroller incorporated into the board. This board comes with 14 Digital I/O Pins and 8 Analog Pins. Because of the small size, there are many applications of Arduino pro mini.

--

--

Malith Dulsara
0 Followers

Undergraduate | Electrical and Electronic Engineering