Overview: Easy Pulse Sensor & Arduino
In this tutorial, we will make our own Heart Rate/BPM Meter using HRM-2511-E Easy Pulse Sensor & Arduino & also check Photoplethysmography wave. There are many low-cost pulse sensor available in the market which can be used to make the Pulse BPM Monitoring System. We already discussed few Pulse Sensors like Simple pulse Sensor & MAX30100 Pulse Oximeter in previous Articles. But when it comes to accuracy and stability, the sensors are not reliable. The sensor value fluctuates so much and doesn’t give proper reading if you move or misplace the fingers.
This is the reason why we need to use some good Pulse Sensor. In my opinion the most stable & better pulse sensor is HRM-2511-E Based Pulse Sensor as it comes with a shell or cover. The Easy Pulse Sensor HRM-2511-E is a better version of Pulse Sensor which gives more accurate and better results compared to World Famous Pulse Sensor & MAX30100.
In this project we will interface Easy Pulse Sensor with Arduino & OLED Display. We will observe the PPG waveform on Serial Plotter using some Arduino example code. We will then display the Pulse Rate or BPM Value on 0.96″ OLED Display. The sensor is very good if you want to learn about the Photoplethysmography or PPG wave using Arduino.
Bill of Materials
The electronics components that you need for making this project are as follows. You can purcahse all the components online from Amazon.
| S.N. | Components | Quantity | Purchase Links |
|---|---|---|---|
| 1 | Arduino Nano Board | 1 | Amazon | AliExpress |
| 2 | Easy Pulse Sensor HRM-2511-E | 1 | Graylogix |
| 3 | 0.96" I2C OLED Display | 1 | Amazon | AliExpress |
| 4 | Power Supply 9V | 1 | Amazon | AliExpress |
| 5 | Connecting Wires | 10 | Amazon | AliExpress |
| 6 | Breadboard | 1 | Amazon | AliExpress |
Easy Pulse Sensor
The Easy Pulse Sensor is a DIY pulse sensor that is designed for hobbyists and educational applications. It is used to illustrate the principle of photoplethysmography (PPG). PPG is a non-invasive technique for detecting the cardio-vascular pulse wave from a fingertip. The Easy Pulse Sensor uses a transmission mode PPG probe (HRM-2511E) sensor.
The Sensor uses an infrared light source to illuminate the finger on one side. On the other side of the sensor, there is a photodetector that measures small variations in the transmitted light intensity due to changes in blood volume inside the tissue. The onboard components & instrumentation provide a clean and filtered analog PPG waveform. The on-board LED also indicates the digital pulse output. The analog and digital signals are both synchronous with the heartbeat.
Features of Easy Pulse Sensor
- Stable PPG signal output (Generate Photoplethysmography PPG with Arduino)
- MCP6004 Opamp based instrumentation with rail-to-rail output capability for maximum output signal swing
- Separate analog & digital outputs
- Potentiometer gain control for the analog output
- Pulse width control for the digital output
- Additional test points on board for analyzing signals at different stages of instrumentation
- Output as +3.3V & +5V
- Simple to interface Easy Pulse Sensor with Arduino & any other Microcontroller
Interfacing Easy Pulse Sensor with Arduino
Now let us interface the Easy Pulse Sensor with the Arduino Board. The connection is fairly simple. You can follow the following circuit diagram.
Connect the Easy Pulse Sensor 5V Pin to Arduino 5V Pin and GND to GND. Connect the Sensor output pin to Arduino A0 Pin. The output value is analog type. You can either use 3.3V output or you can go with 5V Output. I prefer 5V output as the code is written for a 5V signal.
Similarly connect the OLED Display I2C Pins, i.e. SDA & SCL to Arduino A4 & A5 Pins. You can power the OLED Display either with 3.3V or with 5V. It is better to power it using the 3.3V Supply.
Instead of assembling the circuit on a breadboard, you can use your custom-designed PCB. I used EasyEDA to draw the schematic and then converted it into a PCB. The PCB looks something like this.


The Gerber File for the PCB is given below. You can simply download the Gerber File and order the PCB.
You can solder all the necessary active and passive components to the PCB Board. My PCB Assembly looks something like this.
Source Code/Program (PPG Signal)
This is a simple code from Analog example code. This example code will read the Analog output value after an interval of 1 milliseconds. You can modify the code to make it 10 milli seconds. The waveform is similar to Photoplethysmography wave generated from Arduino Code & Pulse Sensor output.
Copy the Code and upload it to the Arduino Nano Board.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void setup() { // initialize serial communication at 9600 bits per second: Serial.begin(9600); } // the loop routine runs over and over again forever: void loop() { // read the input on analog pin 0: int sensorValue = analogRead(A0); // print out the value you read: Serial.println(sensorValue); delay(1); // delay in between reads for stability } |
After uploading the code, open the Serial Plotter from the Tools Menu. The Serial Plotter will plot the Photoplethysmography (PPG) Waveform on Screen. This is how you can perform simple Photoplethysmography using Arduino.
Source Code/Program (BPM Value)
The above code will read analog values and plot the graph. So from the above code, we can calculate the BPM Values depending upon the Pulse Counting.
To count the pulse we have to determine which pulse we should count as beat. For that, we need to determine the threshold. So in this example, I will use 600 ADC Value as a upper threshold value. So when the ADC reaches more than 600 the pulse will counts as a beat. I will also assign 500 as a lower Threshold. This means when ADC Values drops to 0 the beat status will be zero. This will help to count the beat again.
Then it is easy to count the number of beats. I printed every beat on Serial Monitor as a number up to 15 seconds. In 60 seconds the beat will be 4 times, so it is necessary to multiply the beat value by 4. So the Pulse Rate in BPM can be derived now.
Here is a complete code. Copy the code and upload it to the Arduino Board.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
boolean countStatus; int beat, bpm; unsigned long millisBefore; // the setup routine runs once when you press reset: void setup() { // initialize serial communication at 9600 bits per second: Serial.begin(9600); } // the loop routine runs over and over again forever: void loop() { // read the input on analog pin 0: int sensorValue = analogRead(A0); // print out the value you read: //Serial.println(sensorValue); if (countStatus == 0) { if (sensorValue > 600) { countStatus = 1; beat++; Serial.println("Beat Detected!"); Serial.print("Beat : "); Serial.println(beat); } } else { if (sensorValue < 500) { countStatus = 0; } } if (millis() - millisBefore > 15000) { bpm = beat * 4; beat = 0; Serial.print("BPM : "); Serial.println(bpm); millisBefore = millis(); } delay(1); // delay in between reads for stability } |
After uploading the code, open your Serial Monitor. The Serial Monitor will display the beat count as well as BPM Value. In conclusion, you can say this Heart Rate or BPM Meter is ready.
Source Code/Program (BPM Value on OLED Display)
To make this Arduino Easy Pulse Sensor Project more interesting, you can Display the BPM Value on OLED Screen. You can animate a heart picture on every beat detection. So here’s the complete easy pulse sensor with an Arduino OLED sketch. The original author of this code is Miliohm Website. You can follow the orgianl article there.
This code requires SSD1306 Driver Library & Adafruit GFX Library. Download the libraries and add to the Arduino Library Folder.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
#include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Fonts/FreeMonoBold18pt7b.h > #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); #define NUMFLAKES 10 // Number of snowflakes in the animation example #define bitmap1_height 128 #define bitmap1_width 32 static const unsigned char PROGMEM bitmap1[] = { 0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x9F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char PROGMEM bitmap2[] = { 0x07, 0xF0, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; boolean countStatus; int beat, bpm; unsigned long millisBefore; #define XPOS 0 // Indexes into the 'icons' array in function below #define YPOS 1 #define DELTAY 2 // the setup routine runs once when you press reset: void setup() { // initialize serial communication at 9600 bits per second: Serial.begin(9600); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64 Serial.println(F("SSD1306 allocation failed")); for (;;); // Don't proceed, loop forever } Serial.println("Starting..."); } // the loop routine runs over and over again forever: void loop() { // read the input on analog pin 0: int sensorValue = analogRead(A0); // print out the value you read: //Serial.println(sensorValue); if (countStatus == 0) { if (sensorValue > 600) { countStatus = 1; beat++; Serial.println("Beat Detected!"); Serial.print("Beat : "); Serial.println(beat); showBitmap2(); printText(); } } else { if (sensorValue < 500) { showBitmap(); printText(); countStatus = 0; } } if (millis() - millisBefore > 15000) { bpm = beat * 4; beat = 0; Serial.print("BPM : "); Serial.println(bpm); printBPM(); delay(2000); millisBefore = millis(); } delay(1); // delay in between reads for stability //showBitmap(); } void printText() { display.setFont(&FreeMonoBold18pt7b); display.setTextColor(WHITE); // Draw white text display.setCursor(50, 30); // Start at top-left corner display.print(beat); display.display(); } void printBPM() { display.clearDisplay(); display.setFont(&FreeMonoBold18pt7b); display.setTextColor(WHITE); // Draw white text display.setCursor(0, 40); // Start at top-left corner display.print("BPM:"+(String)bpm); display.display(); } void showBitmap(void) { display.clearDisplay(); display.drawBitmap(5, 12, bitmap1, bitmap1_height, bitmap1_width, WHITE); display.display(); //delay(1000); } void showBitmap2(void) { display.clearDisplay(); display.drawBitmap(0, 5, bitmap2, bitmap1_height, bitmap1_width, WHITE); display.display(); //delay(1000); } |
The OLED Display will start showing some values after the code is uploaded to Arduino Board. In other words, the BPM value will be zero when no finger is attached to the Sensor. Take a look at the practical example below.
The pulse Rate or the BPM value is zero now. This is because the pulse sensor is not detecting any transitions due to the absence of a finger & further blood pumping mechanism.
When you insert the finger in the shell/grip, the pulse sensor will start reading the Beat. In this process, the OLED will display the Heart Animation along with the Pulse Count. However, some reading might be incorrect due to improper placement of the finger.
After counting the Beat for 15 Seconds, it will calculate the BPM Value. Finally, the final BPM will be shown on OLED Screen. In my case, the BPM Value ranged from 84 to 92.
This is how you can use the Easy Pulse Sensor with Arduino to read the BPM Value & also the PPG Value.


















2 Comments
Hey Alex hope you are doing well..
In the project lm35 temp control and fan.
Arduino giving random variables any help?
Hey Alex is this sensor available out of India? Or is there any alternative