Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
81 views30 pages

MAX30102

The MAX30102 is a pulse oximeter and heart-rate monitor module capable of measuring blood oxygen levels and heart rate, designed for mobile and wearable devices. It features internal LEDs, photodetectors, and a temperature sensor, with adjustable settings for LED current and pulse width to optimize performance. The sensor can be interfaced with Arduino for real-time monitoring of heart rate, blood oxygen levels, and temperature using specific libraries and coding examples provided.

Uploaded by

ramramito98
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
81 views30 pages

MAX30102

The MAX30102 is a pulse oximeter and heart-rate monitor module capable of measuring blood oxygen levels and heart rate, designed for mobile and wearable devices. It features internal LEDs, photodetectors, and a temperature sensor, with adjustable settings for LED current and pulse width to optimize performance. The sensor can be interfaced with Arduino for real-time monitoring of heart rate, blood oxygen levels, and temperature using specific libraries and coding examples provided.

Uploaded by

ramramito98
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

1

MAX30102 Pulse Oximeter Sensor


2

The MAX30102 is an integrated pulse oximetry and heart-rate monitor module.

Not only heart rate, this device can also measure the concentration of oxygen in
blood. This is a sensor designed by Analog Devices. The module is available in
various board configurations designed by different manufacturers.

It includes internal LEDs, photodetectors, optical elements, and low-noise


electronics with ambient light rejection. The MAX30102 provides a complete
system solution to ease the design-in process for mobile and wearable devices.
MAX30102 Functional Diagram with Detailed Description
Lets take a look at the design part of the MAX30102 Pulse Oximeter Sensor
Module. The sensor is designed for the demanding requirements of wearable
devices. The device maintains a very small solution size without sacrificing
optical or electrical performance. Here is its block diagram.
3

The MAX30102 is fully adjustable through software registers, and the digital
output data can be stored in a 32-deep FIFO within the IC. The FIFO allows the
MAX30102 to be connected to a microcontroller or processor on a shared bus,
where the data is not being read continuously from the MAX30102’s registers. It
has a fixed I2C address of 0xAE (for write operation) and 0xAF (for read
operation). You may refer to MAX30102 Datasheet for more information.

SpO2 Subsystem:

The SpO2 subsystem of the MAX30102 contains ambient light cancellation (ALC),
a continuous-time sigma-delta ADC, and a proprietary discrete time filter. The
ALC has an internal Track/Hold circuit to cancel ambient light and increase the
effective dynamic range. The SpO2 ADC has
programmable full-scale ranges from 2µA to 16µA. The ALC can cancel up to
200µA of ambient current. The internal ADC is a continuous time oversampling
sigma-delta converter with 18-bit resolution. The ADC sampling rate is
10.24MHz. The ADC output data rate can be programmed from 50sps (samples
per second) to 3200sps.
Temperature Sensor
The MAX30102 has an on-chip temperature sensor for calibrating the
temperature dependence of the SpO2 subsystem. The temperature sensor has
an inherent resolution of 0.0625°C. The device output data is relatively
4

insensitive to the wavelength of the IR LED, where the Red LED’s wavelength is
critical to correct interpretation of the data. An SpO2 algorithm used with the
MAX30102 output signal can compensate for the associated SpO2 error with
ambient temperature changes.

LED Driver:

The MAX30102 integrates Red and IR LED drivers to modulate LED pulses for
SpO2 and HR measurements. The LED current can be programmed from 0 to
50mA with proper supply voltage. The LED pulse width can be programmed from
69µs to 411µs to allow the algorithm to optimize SpO2 and HR accuracy and
power consumption based on use cases.

Technical Specifications:

Maximum Current Consumption 6mA


Voltage 3.3-5V
Sample Rate 50Hz – 3200Hz
I2C Address 0xAE (Read) & 0xAF (Write)
Temperature Range -40°C to +85°C
Temperature Accuracy ±1˚C
ADC Resolution 18 bits
IR LED peak wavelength 880nm
Red LED peak wavelength 660nm

MAX30102 Power Requirements:

The MAX30102 IC operates on a single 1.8V power supply but a separate 3.3V
power supply is required for the internal LEDs. So the module comes with 3.3V
and 1.8V regulators.
5

The module can be shut down through software with zero standby current,
allowing the power rails to remain powered at all times.
MAX30102 Module Pinout

Most of the MAX30102 Module has 5 Pins while some have 7 pins which are
explained below:

Pin Description
This pin is used to supply power to the sensor. This sensor is powered on
VIN at 3.3-5V.
SCL This is the I2C serial clock pin.
SDA This is the I2C serial data pin.
This is the active low interrupt pin. It is pulled HIGH by the onboard
resistor but when an interrupt occurs it goes LOW until the interrupt
INT clears.
IRD IR LED Cathode and LED Driver Connection Point
RD Red LED Cathode and LED Driver Connection Point
GND Ground Pin

How MAX30102 Pulse Oximeter Sensor Works?


Let us understand the working of MAX30102 Pulse Oximeter Sensor Principle

The MAX30102 works by shining both lights onto the finger or earlobe (or
6

essentially anywhere where the skin isn’t too thick, so both lights can easily
penetrate the tissue) and measuring the amount of reflected light using
a photodetector. This method of pulse detection through light is
called Photoplethysmogram.
The working of MAX30102 can be divided into two parts: Heart Rate
Measurement and Pulse Oximetry (measuring the oxygen level of the blood).

Heart Rate Measurement:

The oxygenated hemoglobin (HbO2) in the arterial blood has the characteristic
of absorbing IR light. The redder the blood (the higher the hemoglobin), the
more IR light is absorbed.

As the blood is pumped through the finger with each heartbeat, the amount
of reflected light changes, creating a changing waveform at the output of the
photodetector. As you continue to shine light and take photodetector readings,
you quickly start to get a heart-beat (HR) pulse reading.

Pulse Oximetry:

Pulse oximetry is based on the principle that the amount of RED and IR
light absorbed varies depending on the amount of oxygen in your blood.
7

Interfacing MAX30102 Pulse Oximeter Sensor with Arduino


Let us interface the MAX30102 Pulse Oximeter Sensor with Arduino & find out
how we can use it for Heart Rate Blood Oxygen measurement.

Wiring up a MAX30102 Module to an Arduino

Connecting a MAX30102 module to an Arduino board allows you to experiment


with monitoring heart rate and blood oxygen levels. Here is the connection
diagram.
8

Connect the VIN pin & GND Pin of the MAX30102 to the 3.3V & GND pin on your
Arduino. Similarly Connect the SCL & SDA pin of the MAX30102 to the A5 & A4
pin on the Arduino Uno respectively.

You can use a breadboard and jumper wires for assembly.

Measuring Heart Rate, Blood Oxygen & Temperature with MAX30102 & Arduino
In the coding part of this project, we can use the MAX30102 Arduino library to
measure the value of Heart Rate in BPM, Blood Oxygen in percentage and
Temperature in degree Celsius. Before that, we need to install the MAX30102
library.

MAX30102 Arduino Library Installation:

There are multiple libraries available for MAX30102 Sensor to measure the
heart beat, blood oxygen and temperature. Out of all these libraries, the library
from Sparkfun Electronics is the most popular one.
9

To install the library, search by typing MAX3010x. Look for SparkFun MAX3010x
Pulse and Proximity Sensor Library. Click on install to install the library.

Example 1: Measuring Heart-Rate (BPM) with MAX30102 & Arduino

This code initializes a MAX30105 sensor on an Arduino to measure heart rate in


beats per minute, averages the readings for accuracy, and outputs the data
along with the infrared sensor value to the serial monitor, indicating whether a
finger is placed on the sensor.

1 // Include necessary libraries for communication and for the MAX30105


2 sensor
3 #include <Wire.h>
4 #include "MAX30105.h"
5 #include "heartRate.h"
6
7 // Create an instance of the MAX30105 class to interact with the sensor
8 MAX30105 particleSensor;
9
10 // Define the size of the rates array for averaging BPM; can be adjusted for
11 smoother results
12 const byte RATE_SIZE = 4; // Increase this for more averaging. 4 is a good
13 starting point.
14 byte rates[RATE_SIZE]; // Array to store heart rate readings for averaging
10

15 byte rateSpot = 0; // Index for inserting the next heart rate reading into the
16 array
17 long lastBeat = 0; // Timestamp of the last detected beat, used to calculate
18 BPM
19
20 float beatsPerMinute; // Calculated heart rate in beats per minute
21 int beatAvg; // Average heart rate after processing multiple readings
22
23 void setup() {
24 Serial.begin(115200); // Start serial communication at 115200 baud rate
25 Serial.println("Initializing...");
26
27 // Attempt to initialize the MAX30105 sensor. Check for a successful
28 connection and report.
29 if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { // Start communication
30 using fast I2C speed
31 Serial.println("MAX30102 was not found. Please check wiring/power. ");
32 while (1); // Infinite loop to halt further execution if sensor is not found
33 }
34 Serial.println("Place your index finger on the sensor with steady pressure.");
35
36 particleSensor.setup(); // Configure sensor with default settings for heart
37 rate monitoring
38 particleSensor.setPulseAmplitudeRed(0x0A); // Set the red LED pulse
39 amplitude (intensity) to a low value as an indicator
40 particleSensor.setPulseAmplitudeGreen(0); // Turn off the green LED as it's
41 not used here
42 }
43
44 void loop() {
45 long irValue = particleSensor.getIR(); // Read the infrared value from the
46 sensor
47
48 if (checkForBeat(irValue) == true) { // Check if a heart beat is detected
49 long delta = millis() - lastBeat; // Calculate the time between the current
and last beat
50
lastBeat = millis(); // Update lastBeat to the current time
51
52
beatsPerMinute = 60 / (delta / 1000.0); // Calculate BPM
53
54
// Ensure BPM is within a reasonable range before updating the rates array
55
if (beatsPerMinute < 255 && beatsPerMinute > 20) {
56
rates[rateSpot++] = (byte)beatsPerMinute; // Store this reading in the
11

57 rates array
58 rateSpot %= RATE_SIZE; // Wrap the rateSpot index to keep it within the
59 bounds of the rates array
60
61 // Compute the average of stored heart rates to smooth out the BPM
62 beatAvg = 0;
63 for (byte x = 0 ; x < RATE_SIZE ; x++)
64 beatAvg += rates[x];
65 beatAvg /= RATE_SIZE;
66 }
67 }
68
69 // Output the current IR value, BPM, and averaged BPM to the serial monitor
Serial.print("IR=");
Serial.print(irValue);
Serial.print(", BPM=");
Serial.print(beatsPerMinute);
Serial.print(", Avg BPM=");
Serial.print(beatAvg);

// Check if the sensor reading suggests that no finger is placed on the sensor
if (irValue < 50000)
Serial.print(" No finger?");

Serial.println();
}
From the Tools Menu, Select Arduino UNO Board and the COM Port. Click on
upload button to upload the code. Once code uploading is done, the MAX30102
Pulse Oximeter Sensor is ready to test.

Now keep your finger on the sensor as steady as possible and wait a few seconds
for the readings to appear. The Serial monitor will display this reading.
12

For consistent blood flow and reliable sensor data, maintain constant pressure.
Applying the optimal pressure on the pulse sensor — not too hard to avoid
squeezing out blood and not too light to prevent noise from movement and
ambient light.

Example 2: Measuring Blood Oxygen (SpO2) with MAX30102 & Arduino

This code reads heart rate and oxygen levels from a finger using the MAX30105
sensor, then sends this information to a Serial Screen. It dynamically adjusts the
data buffer based on the Arduino model to accommodate memory constraints.

1 #include <Wire.h>
2 #include "MAX30105.h" // Include MAX30105 sensor library
3 #include "spo2_algorithm.h" // Include SpO2 calculation algorithm
4
5 MAX30105 particleSensor; // Create an instance of the MAX30105 class
6
7 #define MAX_BRIGHTNESS 255 // Define maximum LED brightness
8
9 // Adjust buffer size based on the microcontroller's memory capacity
10 #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
11 uint16_t irBuffer[100]; // Buffer for IR data (16-bit for memory-limited
12 devices)
13 uint16_t redBuffer[100]; // Buffer for red LED data
14 #else
15 uint32_t irBuffer[100]; // Buffer for IR data (32-bit for devices with more
16 memory)
13

17 uint32_t redBuffer[100]; // Buffer for red LED data


18 #endif
19
20 int32_t bufferLength = 100; // Length of data buffer
21 int32_t spo2; // Variable to store calculated SpO2 value
22 int8_t validSPO2; // Flag indicating if SpO2 calculation is valid
23 int32_t heartRate; // Variable to store calculated heart rate
24 int8_t validHeartRate; // Flag indicating if heart rate calculation is valid
25
26 byte pulseLED = 11; // LED pin for pulse indication (must support PWM)
27 byte readLED = 13; // LED pin to indicate data read operation
28
29 void setup() {
30 Serial.begin(115200); // Initialize serial communication
31 pinMode(pulseLED, OUTPUT); // Set pulseLED as output
32 pinMode(readLED, OUTPUT); // Set readLED as output
33
34 // Initialize MAX30105 sensor
35 if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
36 Serial.println(F("MAX30105 not found. Check wiring/power."));
37 while (1); // Halt execution if sensor not found
38 }
39
40 Serial.println(F("Attach sensor to finger. Press key to start."));
41 while (Serial.available() == 0); // Wait for user input to proceed
42 Serial.read(); // Clear the serial buffer
43
44 // Sensor configuration settings
45 byte ledBrightness = 60; // LED brightness (0-255)
46 byte sampleAverage = 4; // Averaging (1, 2, 4, 8, 16, 32)
47 byte ledMode = 2; // LED mode (1=Red, 2=Red+IR, 3=Red+IR+Green)
48 byte sampleRate = 100; // Sampling rate (50, 100, 200, 400, 800, 1000,
49 1600, 3200)
50 int pulseWidth = 411; // Pulse width (69, 118, 215, 411)
51 int adcRange = 4096; // ADC range (2048, 4096, 8192, 16384)
52
53 // Apply configuration settings to the sensor
54 particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate,
55 pulseWidth, adcRange);
56 }
57
58 void loop() {
14

59 // Collect 100 samples and output raw red and IR data


60 for (byte i = 0; i < bufferLength; i++) {
61 while (!particleSensor.available()) particleSensor.check(); // Wait for new
62 data
63
64 redBuffer[i] = particleSensor.getRed(); // Store red LED data
65 irBuffer[i] = particleSensor.getIR(); // Store IR data
66 particleSensor.nextSample(); // Move to next sample
67
68 Serial.print(F("red="));
69 Serial.print(redBuffer[i], DEC);
70 Serial.print(F(", ir="));
71 Serial.println(irBuffer[i], DEC);
72 }
73
74 // Calculate heart rate and SpO2 from the first 100 samples
75 maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength,
76 redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
77
78 // Continuously update heart rate and SpO2 values with new samples
79 while (1) {
80 // Shift the last 75 samples to the beginning and fill the remaining with
81 new data
82 for (byte i = 25; i < 100; i++) {
83 redBuffer[i - 25] = redBuffer[i];
84 irBuffer[i - 25] = irBuffer[i];
85 }
86
87 // Collect new samples to refill the buffer
88 for (byte i = 75; i < 100; i++) {
89 while (!particleSensor.available()) particleSensor.check(); // Wait for
90 new data
91
92 digitalWrite(readLED, !digitalRead(readLED)); // Blink LED with each
93 data read
94
95 redBuffer[i] = particleSensor.getRed(); // Store new red data
96 irBuffer[i] = particleSensor.getIR(); // Store new IR data
97 particleSensor.nextSample(); // Move to next sample
98
99 // Output raw data and calculated heart rate/SpO2 values
100 Serial.print(F("red="));
15

101 Serial.print(redBuffer[i], DEC);


102 Serial.print(F(", ir="));
103 Serial.print(irBuffer[i], DEC);
104 Serial.print(F(", HR="));
105 Serial.print(heartRate, DEC);
106 Serial.print(F(", HRvalid="));
107 Serial.print(validHeartRate, DEC);
108 Serial.print(F(", SPO2="));
Serial.print(spo2, DEC);
Serial.print(F(", SPO2Valid="));
Serial.println(validSPO2, DEC);
}

// Recalculate heart rate and SpO2 with the updated buffer


maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength,
redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
}
}

After uploading the code, put your finger on the MAX30102 Pulse Oximeter
Sensor. The Serial Monitor will display the Blood Oxygen reading in percentage.
16

Example 3: Measuring Temperature with MAX30102 & Arduino

The following code initializes a MAX30105 sensor to measure temperature, then


continuously reads and displays the temperature in both Celsius and Fahrenheit
on the serial monitor.

1 #include <Wire.h>
2 #include "MAX30105.h" // Include library for MAX30105 sensor
3
4 MAX30105 particleSensor; // Create an instance of the MAX30105 class
5
6 void setup() {
7 Serial.begin(115200); // Start serial communication at 9600 baud rate
8 Serial.println("Initializing...");
9
10 // Check if MAX30105 sensor is properly connected
11 if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { // Initialize sensor with
12 fast I2C speed
13 Serial.println("MAX30102 was not found. Please check wiring/power. ");
14 while (1); // Infinite loop if sensor is not found
15 }
16
17 particleSensor.setup(0); // Initialize sensor with LEDs turned off to prevent
18 local heating
19 particleSensor.enableDIETEMPRDY(); // Enable temperature ready interrupt
20 for reading temperature
21 }
22
23 void loop() {
24 float temperature = particleSensor.readTemperature(); // Read temperature
25 in Celsius
26 Serial.print("temperatureC="); // Print temperature in Celsius
27 Serial.print(temperature, 4); // Print with 4 decimal places
28
29 float temperatureF = particleSensor.readTemperatureF(); // Convert
30 temperature to Fahrenheit
Serial.print(" temperatureF="); // Print temperature in Fahrenheit
Serial.print(temperatureF, 4); // Print with 4 decimal places

Serial.println(); // New line for readability


}
17

Open the Serial Monitor after uploading the code. The Serial Monitor will show
the value of Temperature in both Celcius and Fahrenheit Scale.

Making a Portable Pulse Oximeter with OLED Display

Let us make a portable Pulse Oximeter by interfacing OLED Display & MAX30102
with Arduino Nano. The OLED will display the real-time Heart Rate and Oxygen
Saturation value.

Circuit Diagram & Connection:


In the hardware part, we will use 0.96″ I2C OLED Display with SSD1306 Driver.
We will also use a push button switch here to control the display part. Here is
the simple connection diagram.
18

Connect the SSD1306 OLED Display to I2C Pins of Arduino Nano. Also connect
the Push button switch to digital pin 3 of the Arduino and its other terminal to
the GND.

While making connection on a breadboard, you need a lot of wiring. Therefore


if you can make a custom PCB to avoid wiring, it would be better.
Source Code/Program
Let us see the programming part of the portable pulse oximeter. The following
19

code integrates an OLED display and a MAX30102 sensor with an Arduino to


measure and display heart rate and blood oxygen saturation (SpO2) levels.
It includes functionality to filter signal noise for more accurate readings,
dynamically adjust the display based on sensor data, and enter a low-power
mode when the sensor is not in use. The program also offers user interaction
through a button to change display modes and uses EEPROM for saving user
preferences.

The code requires a custom made library. The library contains all the header
files for OLED Display and MAX30102 Sensor. First download the MAX30102 &
OLED Library and add it to your Arduino Library folder.

Copy the following code and paste it on your Arduino IDE. Then finally you can
upload the code.
1 #include "ssd1306h.h"
2 #include "MAX30102.h"
3 #include "Pulse.h"
4 #include <avr/pgmspace.h>
5 #include <EEPROM.h>
6 #include <avr/sleep.h>
7
8 #ifndef cbi
9 #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
10 #endif
11 #ifndef sbi
12 #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
13 #endif
14
15
16 SSD1306 oled;
17 MAX30102 sensor;
18 Pulse pulseIR;
19 Pulse pulseRed;
20 MAFilter bpm;
21
22 #define LED LED_BUILTIN
23 #define BUTTON 3
24 #define OPTIONS 7
25
26 static const uint8_t heart_bits[] PROGMEM = { 0x00, 0x00, 0x38, 0x38,
27 0x7c, 0x7c, 0xfe, 0xfe, 0xfe, 0xff,
20

28 0xfe, 0xff, 0xfc, 0x7f, 0xf8, 0x3f, 0xf0, 0x1f,


29 0xe0, 0x0f,
30 0xc0, 0x07, 0x80, 0x03, 0x00, 0x01, 0x00,
31 0x00, 0x00, 0x00,
32 0x00, 0x00
33 };
34
35 //spo2_table is approximated as -45.060*ratioAverage* ratioAverage +
36 30.354 *ratioAverage + 94.845 ;
37 const uint8_t spo2_table[184] PROGMEM =
38 { 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99,
39 99,
40 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
41 100, 100, 100, 100, 100,
42 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98,
43 97, 97,
44 97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91,
45 91,
46 90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81,
47 81,
48 80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68,
67,
49
66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51,
50
50,
51
49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30,
52
29,
53
28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5,
54
3, 2, 1
55
};
56
57
58
int getVCC() {
59
//reads internal 1V1 reference against VCC
60
#if defined(__AVR_ATmega1284P__)
61
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) |
62 _BV(MUX1); // For ATmega1284
63 #else
64 ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // For
65 ATmega328
66 #endif
67 delay(2); // Wait for Vref to settle
68 ADCSRA |= _BV(ADSC); // Convert
69 while (bit_is_set(ADCSRA, ADSC));
21

70 uint8_t low = ADCL;


71 unsigned int val = (ADCH << 8) | low;
72 //discard previous result
73 ADCSRA |= _BV(ADSC); // Convert
74 while (bit_is_set(ADCSRA, ADSC));
75 low = ADCL;
76 val = (ADCH << 8) | low;
77
78 return (((long)1024 * 1100) / val) / 100;
79 }
80
81 void print_digit(int x, int y, long val, char c = ' ', uint8_t field = 3, const int
82 BIG = 2)
83 {
84 uint8_t ff = field;
85 do {
86 char ch = (val != 0) ? val % 10 + '0' : c;
87 oled.drawChar( x + BIG * (ff - 1) * 6, y, ch, BIG);
88 val = val / 10;
89 --ff;
90 } while (ff > 0);
91 }
92
93
94 /*
95 Record, scale and display PPG Wavefoem
96 */
97 const uint8_t MAXWAVE = 72;
98
99 class Waveform {
100 public:
101 Waveform(void) {
102 wavep = 0;
103 }
104
105 void record(int waveval) {
106 waveval = waveval / 8; // scale to fit in byte 缩放以适合字节
107
waveval += 128; //shift so entired waveform is +ve
108
waveval = waveval < 0 ? 0 : waveval;
109
waveform[wavep] = (uint8_t) (waveval > 255) ? 255 : waveval;
110
wavep = (wavep + 1) % MAXWAVE;
111
}
22

112
113 void scale() {
114 uint8_t maxw = 0;
115 uint8_t minw = 255;
116 for (int i = 0; i < MAXWAVE; i++) {
117 maxw = waveform[i] > maxw ? waveform[i] : maxw;
118 minw = waveform[i] < minw ? waveform[i] : minw;
119 }
120 uint8_t scale8 = (maxw - minw) / 4 + 1; //scale * 8 to preserve precision
121 uint8_t index = wavep;
122 for (int i = 0; i < MAXWAVE; i++) {
123 disp_wave[i] = 31 - ((uint16_t)(waveform[index] - minw) * 8) / scale8;
124 index = (index + 1) % MAXWAVE;
125 }
126 }
127
128 void draw(uint8_t X) {
129 for (int i = 0; i < MAXWAVE; i++) {
130 uint8_t y = disp_wave[i];
131 oled.drawPixel(X + i, y);
132 if (i < MAXWAVE - 1) {
133 uint8_t nexty = disp_wave[i + 1];
134 if (nexty > y) {
135 for (uint8_t iy = y + 1; iy < nexty; ++iy)
136 oled.drawPixel(X + i, iy);
137 }
138 else if (nexty < y) {
139 for (uint8_t iy = nexty + 1; iy < y; ++iy)
140 oled.drawPixel(X + i, iy);
141 }
142 }
143 }
144 }
145
146 private:
147 uint8_t waveform[MAXWAVE];
148 uint8_t disp_wave[MAXWAVE];
149 uint8_t wavep = 0;
150
151 } wave;
152
153 int beatAvg;
23

154 int SPO2, SPO2f;


155 int voltage;
156 bool filter_for_graph = false;
157 bool draw_Red = false;
158 uint8_t pcflag = 0;
159 uint8_t istate = 0;
160 uint8_t sleep_counter = 0;
161
162 void button(void) {
163 pcflag = 1;
164 }
165
166 void checkbutton() {
167 if (pcflag && !digitalRead(BUTTON)) {
168 istate = (istate + 1) % 4;
169 filter_for_graph = istate & 0x01;
170 draw_Red = istate & 0x02;
171 EEPROM.write(OPTIONS, filter_for_graph);
172 EEPROM.write(OPTIONS + 1, draw_Red);
173 }
174 pcflag = 0;
175 }
176
177
178
179 void Display_5() {
180 if (pcflag && !digitalRead(BUTTON)) {
181 draw_oled(5);
182 delay(1100);
183 }
184 pcflag = 0;
185
186
187 }
188
189 void go_sleep() {
190 oled.fill(0);
191 oled.off();
192 delay(10);
193 sensor.off();
194 delay(10);
195 cbi(ADCSRA, ADEN); // disable adc
24

196 delay(10);
197 pinMode(0, INPUT);
198 pinMode(2, INPUT);
199 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
200 sleep_mode(); // sleep until button press
201 // cause reset
202 setup();
203 }
204
205 void draw_oled(int msg) {
206 oled.firstPage();
207 do {
208 switch (msg) {
209 case 0: oled.drawStr(10, 0, F("Device error"), 1);
210 break;
211 case 1: oled.drawStr(0, 0, F("PLACE YOUR"), 2);
212 oled.drawStr(25, 18, F("FINGER"), 2);
213
214
215 break;
216 case 2: print_digit(86, 0, beatAvg);
217 oled.drawStr(0, 3, F("PULSE RATE"), 1);
218 oled.drawStr(11, 17, F("OXYGEN"), 1);
219 oled.drawStr(0, 25, F("SATURATION"), 1);
220 print_digit(73, 16, SPO2f, ' ', 3, 2);
221 oled.drawChar(116, 16, '%', 2);
222
223 break;
224 case 3: oled.drawStr(33, 0, F("Pulse"), 2);
225 oled.drawStr(17, 15, F("Oximeter"), 2);
226
227 //oled.drawXBMP(6,8,16,16,heart_bits);
228
229 break;
230 case 4: oled.drawStr(28, 12, F("OFF IN"), 1);
231 oled.drawChar(76, 12, 10 - sleep_counter / 10 + '0');
232 oled.drawChar(82, 12, 's');
233 break;
234 case 5: oled.drawStr(0, 0, F("Avg Pulse"), 1);
235 print_digit(75, 0, beatAvg);
236 oled.drawStr(0, 15, F("AVG OXYGEN"), 1);
237 oled.drawStr(0, 22, F("saturation"), 1);
25

238 print_digit(75, 15, SPO2);


239
240 break;
241 }
242 } while (oled.nextPage());
243 }
244
245 void setup(void) {
246 pinMode(LED, OUTPUT);
247 pinMode(BUTTON, INPUT_PULLUP);
248 filter_for_graph = EEPROM.read(OPTIONS);
249 draw_Red = EEPROM.read(OPTIONS + 1);
250 oled.init();
251 oled.fill(0x00);
252 draw_oled(3);
253 delay(3000);
254 if (!sensor.begin()) {
255 draw_oled(0);
256 while (1);
257 }
258 sensor.setup();
259 attachInterrupt(digitalPinToInterrupt(BUTTON), button, CHANGE);
260 }
261
262 long lastBeat = 0; //Time of the last beat
263 long displaytime = 0; //Time of the last display update
264 bool led_on = false;
265
266
267 void loop() {
268 sensor.check();
269 long now = millis(); //start time of this cycle
270 if (!sensor.available()) return;
271 uint32_t irValue = sensor.getIR();
272 uint32_t redValue = sensor.getRed();
273 sensor.nextSample();
274 if (irValue < 5000) {
275 voltage = getVCC();
276 checkbutton();
277 draw_oled(sleep_counter <= 50 ? 1 : 4); // finger not down message
278 //? : 是三元运算符,整个表达式根据条件返回不同的值,如果x>y为真则返回x
279
26

280 ,如果为假则返回y,之后=赋值给z。相当于:if(x>y)z=x;elsez=y
281
delay(200);
282
++sleep_counter;
283
if (sleep_counter > 100) {
284
go_sleep();
285
sleep_counter = 0;
286
}
287
} else {
288
sleep_counter = 0;
289
// remove DC element移除直流元件
290
int16_t IR_signal, Red_signal;
291
bool beatRed, beatIR;
292
293 if (!filter_for_graph) {//图形过滤器
294 IR_signal = pulseIR.dc_filter(irValue) ;
295 Red_signal = pulseRed.dc_filter(redValue);
296 beatRed = pulseRed.isBeat(pulseRed.ma_filter(Red_signal));
297 beatIR = pulseIR.isBeat(pulseIR.ma_filter(IR_signal));
298 } else {
299 IR_signal = pulseIR.ma_filter(pulseIR.dc_filter(irValue)) ;
300 Red_signal = pulseRed.ma_filter(pulseRed.dc_filter(redValue));
301 beatRed = pulseRed.isBeat(Red_signal);
302 beatIR = pulseIR.isBeat(IR_signal);
303 }
304 // invert waveform to get classical BP waveshape
305 wave.record(draw_Red ? -Red_signal : -IR_signal );
306 // check IR or Red for heartbeat
307 if (draw_Red ? beatRed : beatIR) {
308 long btpm = 60000 / (now - lastBeat);
309 if (btpm > 0 && btpm < 200) beatAvg = bpm.filter((int16_t)btpm);
310 lastBeat = now;
311 digitalWrite(LED, HIGH);
312 led_on = true;
313 // compute SpO2 ratio
314 long numerator = (pulseRed.avgAC() * pulseIR.avgDC()) / 256;
315 long denominator = (pulseRed.avgDC() * pulseIR.avgAC()) / 256;
316 int RX100 = (denominator > 0) ? (numerator * 100) / denominator : 999;
317 // using formula
318 SPO2f = (10400 - RX100 * 17 + 50) / 100;
319 // from table
320 if ((RX100 >= 0) && (RX100 < 184))
321 SPO2 = pgm_read_byte_near(&spo2_table[RX100]);
27

322 }
// update display every 50 ms if fingerdown
if (now - displaytime > 50) {
displaytime = now;
wave.scale();
draw_oled(2);

}
Display_5();

}
// flash led for 25 ms
if (led_on && (now - lastBeat) > 25) {
digitalWrite(LED, LOW);
led_on = false;
}
}
Testing the Portable Pulse Oximeter Device:

After uploading the code, the device is ready for testing. Immediately the OLED
will display the “Pulse Oximeter” text.
28

Then it will display the message to place your finger.

When you place the finger, it will start measuring the Pulse Rate in BPM and
Oxygen Saturation in percentage.

If you press the push button while placing the finger on MAX30102, the OLED
will display the Average Pulse and Average Oxygen Saturation.
29

When no finger is placed on MAX30102 Sensor, the device goes to sleep mode
and OLED will turn off in 5 seconds. The OLED will display the countdown timer
as well.

You can bring the OLED to working mode after pressing the push button again.
This will wake up the device from sleep mode.

Thus, this is how you can make a portable pulse Oximeter using the Arduino,
MAX30102 Sensor & display the Blood Oxygen and Heart Rate in OLED Display.
30

Tecknow Store
Computers & More

Tel: 70997001
Instagram: tecknow_lb
Website: www.tecknow.store

You might also like