A pulse oximeter is a vital medical device that measures oxygen saturation percentage using red and infrared light. This device operates on the fundamental principle that oxygenated blood absorbs more infrared rays compared to deoxygenated blood. By precisely quantifying the light absorbed, a pulse oximeter calculates the oxygen saturation level, often referred to as the oxygen saturation index or SpO2. Explore this informative guide to gain a deeper understanding of how pulse oximeters work and their critical role in healthcare monitoring.
Working Process
Pulse oximeters, essential healthcare devices, employ two light-emitting diodes (LEDs) to shine light through the skin - one red and one infrared. These LEDs are thoughtfully placed on a probe, which can be comfortably positioned on the finger, earlobe, or toe of the individual. As this light traverses through the skin, a portion of it is absorbed by the blood. The degree of light absorption hinges on the blood's oxygen saturation. Additionally, the device incorporates a photodetector, situated on the same probe as the LEDs, to gauge the quantity of light that successfully penetrates the skin.
By leveraging the data from the photodetector, the device meticulously calculates the oxygen saturation of the blood. This calculation involves a comparison between the light absorbed by the blood and the light transmitted through the skin. The resulting oxygen saturation level is then presented as a percentage on the device's screen. Typically, a healthy individual exhibits an average oxygen saturation level ranging from 95% to 100%. Should the readings fall below this range, it may signal insufficient oxygen intake, possibly indicating underlying medical conditions like 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 Libraries
1#include <Wire.h>2#include <LiquidCrystal_I2C.h>This code includes two libraries:
Wire
for I2C communication andLiquidCrystal_I2C
for controlling a 2x16 character LCD display.Define Constants
1#define maxperiod_siz 80 // number of samples in a period2#define measures 10 // periods stored3#define samp_siz 4 // samples for average4#define rise_threshold 3 // rising measures to determine a peakThese are constant values used for configuring the program. They determine the number of samples, periods, and thresholds used for calculations.
Initialize LCD Display
1LiquidCrystal_I2C lcd(0x3F, 16, 2);This initializes the LCD display with the I2C address
0x3F
and specifies the display size as 16x2.Define Pin Configuration
1int T = 20;2int sensorPin = A1;3int REDLed = 3;4int IRLed = 4;These variables define various pins used in the circuit.
sensorPin
is connected to a sensor, andREDLed
andIRLed
control LEDs emitting red and infrared light, respectively.Define Custom LCD Characters
1byte sym[3][8] = { ... };This defines three custom characters for the LCD display. These characters are later used to display special symbols on the LCD.
Setup Function
1void setup() { ... }This function runs once when the Arduino is powered up or reset. It initializes serial communication, sets pin modes, initializes the LCD, and creates custom characters for the display.
Main Loop
1void loop() { ... }This is the main loop of the program that runs continuously. It contains the core logic for reading sensor data, calculating heart rate and SpO2 levels, and displaying the results on the LCD.
Now, let's break down the main loop into several key sections:
Sensor Data Acquisition- The loop begins by reading sensor data from both the infrared and red sensors. It calculates an average value for each sensor over a short period of time.
Finger Status Check- It checks if a finger is placed on the sensor. If there's no finger detected, it displays "No finger?" on the LCD.
Peak Detection- It looks for peaks in the sensor data to detect heartbeats. When a rising peak is detected, it calculates heart rate (BPM) and SpO2 levels based on the ratio of red and infrared sensor values. It also averages these values over a short time frame.
Displaying Results- The program then displays heart rate, SpO2, and the quality of the signal (R) on the LCD. If a valid signal is detected (based on
c
), it calculates and displays the BPM and SpO2 levels. Otherwise, it displays placeholders.Plotting and Array Handling- Sensor data and calculated values are sent to the serial monitor for debugging purposes. Arrays are also managed to store historical data.
Loop Continues- The loop continues to run indefinitely, repeating the above steps.
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 hemoglobin 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.
For the reference, the following is the source code used.
1#include <Wire.h> 2#include <LiquidCrystal_I2C.h> 3 4#define maxperiod_siz 80 // number of samples in a period 5#define measures 10 // periods stored 6#define samp_siz 4 // samples for average 7#define rise_threshold 3 // rising measures to determine a peak 8 9// a liquid crystal displays BPM 10LiquidCrystal_I2C lcd(0x3F, 16, 2); 11 12int T = 20; 13int sensorPin = A1; 14int REDLed = 3; 15int IRLed = 4; 16 17byte sym[3][8] = { 18 { 19 B00000, 20 B01010, 21 B11111, 22 B11111, 23 B01110, 24 B00100, 25 B00000, 26 B00000 27},{ 28 B00000, 29 B00000, 30 B00000, 31 B11000, 32 B00100, 33 B01000, 34 B10000, 35 B11100 36},{ 37 B00000, 38 B00000, 39 B00000, 40 B01000, 41 B10101, 42 B00010, 43 B00000, 44 B00000 45} 46}; 47 48void setup() { 49 Serial.begin(9600); 50 Serial.flush(); 51 pinMode(sensorPin,INPUT); 52 pinMode(REDLed,OUTPUT); 53 pinMode(IRLed,OUTPUT); 54 55 lcd.init(); 56 lcd.backlight(); 57 58 digitalWrite(REDLed,LOW); 59 digitalWrite(IRLed,LOW); 60 61 for(int i=0;i<8;i++) lcd.createChar(i, sym[i]); 62} 63 64void loop () 65{ 66 67 bool finger_status = true; 68 float readsIR[samp_siz], sumIR,lastIR, reader, start; 69 float readsRED[samp_siz], sumRED,lastRED; 70 int period, samples; 71 period=0; samples=0; 72 int samplesCounter = 0; 73 float readsIRMM[maxperiod_siz],readsREDMM[maxperiod_siz]; 74 int ptrMM =0; 75 for (int i = 0; i < maxperiod_siz; i++) { readsIRMM[i] = 0;readsREDMM[i]=0;} 76 float IRmax=0; 77 float IRmin=0; 78 float REDmax=0; 79 float REDmin=0; 80 double R=0; 81 float measuresR[measures]; 82 int measuresPeriods[measures]; 83 int m = 0; 84 85 for (int i = 0; i < measures; i++) { measuresPeriods[i]=0; measuresR[i]=0; } 86 87 int ptr; 88 float beforeIR; 89 bool rising; 90 int rise_count; 91 int n; 92 long int last_beat; 93 for (int i = 0; i < samp_siz; i++) { readsIR[i] = 0; readsRED[i]=0; } 94 sumIR = 0; sumRED=0; 95 ptr = 0; 96 97 int cm=0; 98 99 Serial.flush();100 101 while(1)102 {103 // turn on IR104 digitalWrite(REDLed,LOW);105 digitalWrite(IRLed,HIGH);106 107 // calculate an average of the sensor108 n = 0;109 start = millis();110 reader = 0.;111 do112 {113 reader += analogRead (sensorPin);114 n++;115 }116 while (millis() < start + T); 117 reader /= n; 118 sumIR -= readsIR[ptr];119 sumIR += reader;120 readsIR[ptr] = reader;121 lastIR = sumIR / samp_siz;122 123 digitalWrite(REDLed,HIGH);124 digitalWrite(IRLed,LOW);125 126 n = 0;127 start = millis();128 reader = 0.;129 do130 {131 reader += analogRead (sensorPin);132 n++;133 }134 while (millis() < start + T); 135 reader /= n; 136 sumRED -= readsRED[ptr];137 sumRED += reader;138 readsRED[ptr] = reader;139 lastRED = sumRED / samp_siz;140 readsIRMM[ptrMM]=lastIR;141 readsREDMM[ptrMM]=lastRED;142 ptrMM++;143 ptrMM %= maxperiod_siz;144 samplesCounter++;145 146 if(samplesCounter>=samples){147 samplesCounter =0;148 IRmax = 0; IRmin=1023; REDmax = 0; REDmin=1023;149 for(int i=0;i<maxperiod_siz;i++) {150 if( readsIRMM[i]> IRmax) IRmax = readsIRMM[i];151 if( readsIRMM[i]>0 && readsIRMM[i]< IRmin ) IRmin = readsIRMM[i];152 readsIRMM[i] =0;153 if( readsREDMM[i]> REDmax) REDmax = readsREDMM[i];154 if( readsREDMM[i]>0 && readsREDMM[i]< REDmin ) REDmin = readsREDMM[i];155 readsREDMM[i] =0;156 }157 R = ( (REDmax-REDmin) / REDmin) / ( (IRmax-IRmin) / IRmin ) ;158 }159 160 // check that the finger is placed inside the sensor161 if (lastRED < lastIR) {162 if(finger_status==true) {163 finger_status = false;164 lcd.clear();165 lcd.setCursor(0,0);166 lcd.print("No finger?"); 167 }168 } else {169 if(finger_status==false) {170 lcd.clear();171 finger_status = true;172 }173 }174 175 float avR = 0;176 int avBPM=0;177 178 if (finger_status==true){179 180 if (lastIR > beforeIR)181 {182 rise_count++; 183 if (!rising && rise_count > rise_threshold)184 {185 lcd.setCursor(15,1); 186 lcd.write( 0 ); 187 188 rising = true;189 190 measuresR[m] = R;191 measuresPeriods[m] = millis() - last_beat;192 last_beat = millis();193 int period = 0;194 for(int i =0; i<measures; i++) period += measuresPeriods[i];195 196 period = period / measures;197 samples = period / (2*T);198 199 int c = 0;200 int avPeriod = 0;201 202 for(int i =1; i<measures; i++) {203 if ( (measuresPeriods[i] < measuresPeriods[i-1] * 1.1) && 204 (measuresPeriods[i] > measuresPeriods[i-1] / 1.1) ) {205 206 c++;207 avPeriod += measuresPeriods[i];208 avR += measuresR[i];209 210 }211 }212 213 m++;214 m %= measures;215 216 lcd.setCursor(0,1);217 lcd.print("c " + String(c)+" ");218 219 avBPM = 60000 / ( avPeriod / c) ;220 avR = avR / c ;221 222 lcd.setCursor(0,0);223 lcd.print("bpm ");224 lcd.setCursor(8,0);225 lcd.print("SpO2 ");226 lcd.setCursor(11,0); lcd.write(1); 227 lcd.setCursor(7,1);228 lcd.print("R ");229 230 if(c > 4) {231 232 int SpO2 = -64 * R + 158;233 234 lcd.setCursor(4,0);235 lcd.print(String(avBPM));236 lcd.setCursor(12,0); 237 lcd.print( String(SpO2) +"% ");238 lcd.setCursor(9,1);239 lcd.print(String(avR) + " ");240 241 } else {242 lcd.setCursor(4,0);243 lcd.print("---");244 lcd.setCursor(12,0);245 lcd.print("--% "); 246 lcd.setCursor(9,1);247 lcd.print("----");248 }249 }250 }251 else252 {253 rising = false;254 rise_count = 0;255 lcd.setCursor(15,1); lcd.print(" ");256 }257 beforeIR = lastIR;258 }259 260 // Plotting261 Serial.print(lastIR);262 Serial.print(",");263 Serial.print(lastRED);264 Serial.print(",");265 Serial.print(R); 266 Serial.print(","); 267 Serial.print(IRmax);268 Serial.print(",");269 Serial.print(IRmin);270 Serial.print(",");271 Serial.print(REDmax);272 Serial.print(",");273 Serial.print(REDmin);274 Serial.print(",");275 Serial.print(avR);276 Serial.print(",");277 Serial.print(avBPM);278 Serial.println();279 280 // Array handling 281 ptr++;282 ptr %= samp_siz;283 284 } 285}