IoTSimulator

Display Heartbeat BPM on an Arduino LCD

Build an Arduino heartbeat monitor with a pulse-style heart beat sensor, an LCD1602 display, and a buzzer. The Arduino watches the analog pulse signal, detects clean beat peaks, estimates BPM from the time between beats, and shows the live result on the LCD.

Io
IoTSim Editor
May 8, 2026
Display Heartbeat BPM on an Arduino LCD

Live project track

Interactive hardware & logic preview

Have you ever felt your pulse on your wrist and counted the beats for a few seconds? You are doing two things at once: noticing each beat and measuring the time between them. A heartbeat monitor does the same basic job, but it lets the Arduino do the counting and timing.

In this project, a heart beat sensor gives the Arduino a changing analog signal. The Arduino watches for clean peaks in that signal, estimates beats per minute, shows the result on an LCD1602 display, and makes a short buzzer beep when a beat is detected. The one main concept is: BPM comes from the time between repeated beat peaks, not from one single sensor reading.

This is a learning project, not a medical device. Use it to understand sensors, signals, and timing, not to make health decisions.

Blood Flow Changes Become a Pulse Waveform

Think of the sensor like a tiny flashlight and observer looking at your fingertip. With each heartbeat, blood flow in the finger changes slightly. That small change affects how much light is reflected or absorbed. The sensor turns that changing light level into an electrical signal.

The Arduino does not see a neat number called heartbeat. It sees an analog value that rises and falls. Analog means the value can move through a range, like 512, 548, 601, and back down again, instead of only being on or off. The peak of that rise is the clue we use to detect one beat.

Blood flow change becoming a pulse waveform
Blood flow change becoming a pulse waveform
What happens physicallyWhat the Arduino receivesWhat the code does
Blood flow changes with a beatThe sensor value risesWatch for the value crossing a threshold
The beat passesThe sensor value falls againWait until the signal is ready for another beat
Beats repeat over timePeaks appear again and againMeasure the time between peaks

This is why heartbeat projects can feel a little more delicate than a button project. A button usually gives a clear HIGH or LOW. A pulse sensor gives a shape. The code has to decide which part of that shape counts as a real beat and which part is just noise.

Two Thresholds Stop One Beat From Being Counted Twice

If the code counted a beat every time the signal was above one threshold, one real heartbeat could be counted many times. The Arduino loop runs very fast, so the signal might stay above the threshold for several loop cycles. That would make the BPM jump unrealistically high.

To prevent that, this project uses two thresholds. The higher beatThreshold detects the beat peak. The lower releaseThreshold tells the Arduino when the waveform has fallen enough to be ready for the next beat. This gap is called hysteresis. In plain words, the signal must rise high enough to count, then fall low enough before it can count again.

Beat threshold and release threshold on a pulse waveform
Beat threshold and release threshold on a pulse waveform
  • Beat threshold is the upper line that marks a likely heartbeat peak.
  • Release threshold is the lower line that re-arms the detector after the signal falls.
  • Ready for beat is the code's memory that prevents one long peak from being counted repeatedly.
If the buzzer chatters rapidly, the threshold is probably too low or the signal is noisy. Raise beatThreshold or steady the sensor contact before changing the rest of the code.

The Gap Between Peaks Becomes BPM

BPM means beats per minute. The Arduino does not need to wait one full minute to estimate it. Instead, it measures the time between two neighboring beats and scales that interval up to one minute.

If two beats are 1000 milliseconds apart, that is one beat per second. One beat per second becomes 60 beats per minute. If the interval is 500 milliseconds, that is two beats per second, or 120 BPM. The formula in the code is 60000 / interval because there are 60,000 milliseconds in one minute.

Time between heartbeat peaks converted into BPM
Time between heartbeat peaks converted into BPM
Beat intervalEstimated BPMMeaning in the project
1000 ms60 BPMOne beat every second
750 ms80 BPMFaster than one beat per second
500 ms120 BPMTwo beats every second

The sketch also rejects intervals that are too short or too long. A beat interval under 300 milliseconds would imply more than 200 BPM, which is usually a false trigger in this learning setup. A gap longer than 2000 milliseconds is treated as too old to produce a useful live BPM estimate.

Steady Contact Makes the BPM Reading Less Jumpy

Pulse sensors are sensitive to movement. If the fingertip slides, presses too hard, or barely touches the sensor, the waveform can become jagged. The Arduino may then see fake peaks and count them as beats. This is why heartbeat projects often need more patience than light or temperature projects.

Noisy moving contact compared with steady pulse sensor contact
Noisy moving contact compared with steady pulse sensor contact

For the cleanest result, hold the sensor still and give the reading a few seconds to settle. In the simulator, you can use the pulse control to test different signal levels. On a real sensor, the first debugging step is usually not code. It is better contact, less movement, and less strong light hitting the sensor.

Each Heartbeat Monitor Wire Has One Simple Role

The circuit has three groups: the pulse sensor reads the body signal, the LCD shows the BPM and raw signal, and the buzzer gives a short beep for each detected beat. The Arduino sits in the middle and makes the timing decision.

5V is the positive power supply from the Arduino. GND is the return path for electricity, like the negative terminal of a battery. The sensor, LCD, and buzzer all need a shared GND so their signals use the same zero-voltage reference.

Pin Connection Map
Heart Beat Sensor
VCC
Arduino Uno
5V
Explanation

Powers the light-sensing circuit inside the pulse module.

Heart Beat Sensor
GND
Arduino Uno
GND
Explanation

Shares the same return path and voltage reference as the Arduino.

Heart Beat Sensor
OUT
Arduino Uno
A0
Explanation

Sends the changing analog pulse signal into the Arduino.

Pin Connection Map
Buzzer
Positive
Arduino Uno
D8
Explanation

Receives the short tone signal when a beat is detected.

Buzzer
Negative
Arduino Uno
GND
Explanation

Completes the buzzer circuit back to ground.

Pin Connection Map
LCD1602
VSS
Arduino Uno
GND
Explanation

Ground for the LCD logic.

LCD1602
VDD
Arduino Uno
5V
Explanation

Power for the LCD logic.

LCD1602
RW
Arduino Uno
GND
Explanation

Keeps the LCD in write mode so Arduino sends text to it.

LCD1602
RS
Arduino Uno
D12
Explanation

Chooses whether the LCD receives a command or display text.

LCD1602
E
Arduino Uno
D11
Explanation

Tells the LCD to accept the current data.

LCD1602
D4
Arduino Uno
D5
Explanation

First data line used by the display in 4-bit mode.

LCD1602
D5
Arduino Uno
D4
Explanation

Second data line used by the display in 4-bit mode.

LCD1602
D6
Arduino Uno
D3
Explanation

Third data line used by the display in 4-bit mode.

LCD1602
D7
Arduino Uno
D2
Explanation

Fourth data line used by the display in 4-bit mode.

LCD1602
A
Arduino Uno
5V
Explanation

Power for the LCD backlight.

LCD1602
K
Arduino Uno
GND
Explanation

Ground for the LCD backlight.

If the LCD works but BPM stays as --, check the raw signal number on row 2. If it never rises above the beat threshold, the sensor signal is too low or the threshold is too high.

The Complete Heartbeat BPM Monitor Sketch

This sketch uses timing logic, so it is more advanced than a basic LED project. The key idea is still understandable: sample the pulse sensor, count only clean peaks, measure the time between peaks, and update the LCD.

The code uses millis() instead of only delay() because the buzzer beep should end while the sensor keeps being checked. millis() returns how many milliseconds have passed since the Arduino started running, which makes it useful for timing events without stopping the whole sketch.

C++ Source
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

How the Heartbeat BPM Code Works, Part By Part

Now let us break the sketch into smaller jobs. Each block supports one part of the monitor: sensor input, peak detection, beat timing, buzzer feedback, and LCD display.

▸ Heartbeat Monitor Pins and Thresholds Define the Signal Rules

The first constants name the sensor pin, buzzer pin, LCD pins, and detection values. A constant is a named value that does not change while the program runs. Using names keeps the project readable because beatThreshold explains itself better than a mystery number like 560.

C++ Source
1
2
3
4
5
6
7
8

The threshold pair is the most important setting to tune. If real beats are missed, lower beatThreshold a little. If fake beats are counted, raise it or increase the gap between the beat and release thresholds.

▸ Monitor Memory Prevents Repeat Counting

The variables remember the state of the monitor between loop cycles. readyForBeat tells the sketch whether the next high signal is allowed to count as a new beat. The time variables remember when the last beat, beep, and display update happened.

C++ Source
1
2
3
4
5

▸ Setup Starts the LCD Before Reading

The setup section prepares the buzzer as an OUTPUT, which means the Arduino sends a signal from that pin. Then it starts the LCD and prints a short message so the reader sees that the display is responding before the BPM value appears.

C++ Source
1
2
3
4
5
6
7
8
9

▸ Loop Samples the Pulse Waveform

Each loop reads the analog sensor signal from A0. analogRead() returns a number from 0 to 1023, where higher numbers mean a higher input voltage. The sketch also stores the current time from millis() so every timing decision uses the same time reference.

C++ Source
1
2

▸ Threshold Crossing Counts One Beat

The beat detector only runs when two things are true: the monitor is ready for a beat, and the signal is above the beat threshold. After counting that peak, the code sets readyForBeat to false so the same high pulse cannot be counted again.

C++ Source
1
2
3
4
5
6
7
8

The second if statement re-arms the detector only after the signal falls below the release threshold. That fall is what tells the Arduino that the previous beat has passed.

▸ Interval Math Updates the BPM

handleBeat() measures the gap between this beat and the previous beat. If the interval looks realistic, the code divides 60,000 by that interval to estimate BPM. The UL in 60000UL tells Arduino to treat the number as an unsigned long value, which is safer for time math.

C++ Source
1
2
3
4
5
6
7
8
9

▸ Short Beep Confirms the Detected Peak

The buzzer starts when a beat is detected, but it should not stay on forever. The sketch stores lastBeepStart, then later turns the tone off when beepDurationMs has passed. This gives each beat a quick audible tick without blocking sensor reading for too long.

C++ Source
1
2
3
4
5
6
7

▸ LCD Shows BPM and Raw Signal

The LCD shows the estimated BPM on the first row and the raw sensor signal on the second row. The raw signal is included on purpose. It helps learners tune the threshold because they can see whether the sensor value is rising above the detection line.

C++ Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Adjusting the Heartbeat Threshold For Your Sensor

If the BPM never appears, watch the raw signal on the LCD. If the signal peaks around 535, the default threshold of 560 is too high for that setup. Lower it in small steps, such as 560 to 545, then test again.

If the BPM appears but jumps wildly, the threshold may be too low or the sensor contact may be moving. Raise beatThreshold, keep releaseThreshold lower than it, and hold the sensor more steadily. The best result usually comes from both better placement and a threshold that matches the observed signal.

Taking It Further

Once the BPM display works, you can add an LED that flashes with the buzzer so each detected beat has both visual and audio feedback. You could also store the last few BPM values and display an average to make the reading less jumpy.

For a more polished version, try showing a warning when the signal stays too low for several seconds. That would teach the user to adjust their finger instead of trusting a blank or unstable BPM value.

Keywords
#Arduino #Heartbeat #BPM #Pulse Sensor #LCD #Buzzer #Analog #Intermediate
Total word count: 1787 words

Project discussion

Questions, feedback, and community insights
No discussions yet. Be the first to start!