Biomedical Patient Monitoring System using Arduino and IoT

A biomedical patient monitoring system is an IoT-based project that records and transmits a patient's vital signs. It helps doctors monitor patients remotely in hospitals or at home. This guide builds a system that tracks three parameters: body temperature (LM35 sensor), heart rate and oxygen saturation SpO2 (MAX30102 sensor), and shows them on an LCD while uploading them to ThingSpeak over Wi-Fi. Blood pressure and ECG are optional extras — they need extra hardware and are not part of this build.

Important: this is a student model for learning, NOT a medical device. Its readings are estimates for demonstration. Never use it to diagnose or treat anyone — always compare with a clinical device, and see a doctor for real health decisions.

Biomedical patient monitoring system circuit diagram — Arduino UNO, MAX30102, LM35, LCD and NodeMCU.

How the System Works

  1. The LM35 measures body temperature and the MAX30102 measures heart rate and SpO2 from a fingertip.
  2. The Arduino UNO reads both sensors, shows the values on the 16×2 LCD, and sends one line of readings over serial to the NodeMCU.
  3. The NodeMCU (an ESP8266 board with Wi-Fi built in) uploads the readings to ThingSpeak, where they appear on a live chart anyone with the link can watch.
  4. A doctor or family member can then check the patient's temperature, heart rate and SpO2 from a phone, without being in the same room.

Main Components Required

ComponentPrice / link
Arduino UNO R3 DIPArduino UNO R3 DIP ATmega328P with USB Cable — Rs 1,200
MAX30102 pulse oximeter sensorNo QKZEE listing — available at the Hall Road shop on request
LM35 temperature sensorNo QKZEE listing — available at the Hall Road shop on request
Pulse Sensor (optional — see note below)No QKZEE listing — available at the Hall Road shop on request
LCD1602 16×2 display with I2C moduleLCD1602 16x2 Display with I2C Module — Rs 660
NodeMCU ESP8266 CH340 WiFi boardNodeMCU ESP8266 CH340 WiFi Board — Rs 590
MB102 breadboard (for testing on the bench)MB102 Breadboard 830 Points — Rs 250
40-pin jumper wires40-Pin Jumper Wires 30cm — Rs 180

About the Pulse Sensor: the parts list includes both a Pulse Sensor and a MAX30102, but both measure heart rate — you only need one. This guide uses the MAX30102 (it also gives SpO2). The Pulse Sensor is listed as an optional alternative: it connects to A1, and its heart-rate number comes from the PulseSensor Playground library. Use the Arduino UNO as the sensor board — the NodeMCU has only one analog pin (A0) and this build needs two analog inputs.

Wiring

LM35 Temperature Sensor → Arduino

LM35 pinArduino pin
OUTA0
VCC5V
GNDGND

Pulse Sensor (Optional) → Arduino

Pulse Sensor pinArduino pin
OUT (signal)A1
VCC5V
GNDGND

MAX30102 → Arduino

MAX30102 pinArduino pin
SDAA4
SCLA5
VCC3.3V
GNDGND

LCD Display → Arduino

LCD pinArduino pin
SDAA4
SCLA5
VCC5V
GNDGND

The MAX30102 and the LCD share the same I2C bus (A4/A5) — that is normal for I2C; both devices work together on the same two wires.

Arduino → NodeMCU (Wi-Fi Link)

NodeMCU pinArduino pin
RX (GPIO3)UNO TX (D1) — through a 1k + 2k voltage divider
GNDUNO GND

The UNO's TX pin is 5V, but the NodeMCU RX pin is 3.3V logic — the divider is required: UNO TX → 1k resistor → NodeMCU RX, and NodeMCU RX → 2k resistor → GND. Join the grounds: without a common GND the serial link will not work. The NodeMCU is powered by its own USB 5V cable.

Safety: The ESP8266 Is 3.3V — Never 5V

The old version of this post said "Use 5V regulated supply to power ESP8266". That is wrong and can damage the board. The ESP8266 chip (and the NodeMCU board's logic pins) run on 3.3V. Power the NodeMCU through its own USB port (which has a 3.3V regulator built in), and never connect the UNO's 5V TX pin straight to the NodeMCU's RX pin — always use the divider above. The MAX30102 also takes 3.3V on its VCC pin, not 5V.

Required Libraries

Install these two from the Arduino IDE Library Manager (Sketch → Include Library → Manage Libraries):

  • LiquidCrystal I2C by Frank de Brabander
  • SparkFun MAX3010x Pulse and Proximity Sensor Library (gives you MAX30105.h and the SpO2 helper)

Wire is built into the Arduino IDE — no install needed. On the NodeMCU side, ESP8266WiFi is built into the ESP8266 board package.

Complete Arduino Code (Sensors + LCD)

/*
  Biomedical Patient Monitoring — ARDUINO UNO side
  Reads LM35 temperature (A0) and MAX30102 heart rate + SpO2 (I2C),
  shows them on the 16x2 LCD, and sends one line to the NodeMCU:
  "T:36.5 H:72 S:98". The Pulse Sensor is optional (A1) — see the note
  in the parts list; this sketch uses the MAX30102 for heart rate.
  Student model — NOT a medical device. Readings are estimates.
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"

LiquidCrystal_I2C lcd(0x27, 16, 2);   // try 0x3F if the display stays blank
MAX30105 particleSensor;

const int lm35Pin = A0;

uint32_t irBuffer[100];
uint32_t redBuffer[100];
int32_t spo2;
int8_t validSPO2;
int32_t heartRate;
int8_t validHeartRate;

void setup() {
  Serial.begin(9600);   // to the NodeMCU (through the 1k + 2k divider)
  lcd.init();
  lcd.backlight();
  lcd.print("Patient Monitor");
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
    lcd.setCursor(0, 1);
    lcd.print("MAX30102 missing");
    while (1);
  }
  particleSensor.setup();
  particleSensor.setPulseAmplitudeRed(0x0A);
  particleSensor.setPulseAmplitudeGreen(0);
}

void loop() {
  // Collect 100 samples from the MAX30102
  for (int i = 0; i < 100; i++) {
    while (!particleSensor.available()) particleSensor.check();
    redBuffer[i] = particleSensor.getRed();
    irBuffer[i] = particleSensor.getIR();
    particleSensor.nextSample();
  }
  maxim_heart_rate_and_oxygen_saturation(irBuffer, 100, redBuffer,
                                         &spo2, &validSPO2, &heartRate, &validHeartRate);

  float tempC = analogRead(lm35Pin) * 5.0 / 1024.0 * 100.0;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("T:");
  lcd.print(tempC, 1);
  lcd.print("C HR:");
  lcd.print(validHeartRate ? heartRate : 0);
  lcd.setCursor(0, 1);
  lcd.print("SpO2:");
  lcd.print(validSPO2 ? spo2 : 0);
  lcd.print("%");

  Serial.print("T:");
  Serial.print(tempC, 1);
  Serial.print(" H:");
  Serial.print(validHeartRate ? heartRate : 0);
  Serial.print(" S:");
  Serial.println(validSPO2 ? spo2 : 0);
}

Complete NodeMCU Code (ThingSpeak Upload)

/*
  Biomedical Patient Monitoring — NodeMCU (ESP8266) side
  Receives "T:36.5 H:72 S:98" lines from the Arduino on Serial (RX = GPIO3)
  and uploads temperature, heart rate and SpO2 to ThingSpeak fields 1-3.
  Arduino TX (5V) -> 1k + 2k divider -> NodeMCU RX (3.3V). Common GND.
  The NodeMCU is powered by its own USB 5V cable.
*/

#include <ESP8266WiFi.h>

const char* ssid = "Your_SSID";
const char* password = "Your_PASS";
const char* host = "api.thingspeak.com";
String apiKey = "YOUR_API_KEY";

void setup() {
  Serial.begin(9600);   // USB monitor + the Arduino link share this port
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void loop() {
  if (Serial.available()) {
    String line = Serial.readStringUntil('\n');
    line.trim();
    if (line.startsWith("T:")) {
      float tempC = line.substring(2, line.indexOf(' ', 2)).toFloat();
      int hr = valueAfter(line, "H:");
      int spo2 = valueAfter(line, "S:");
      sendToThingSpeak(tempC, hr, spo2);
    }
  }
}

int valueAfter(String line, String key) {
  int i = line.indexOf(key);
  if (i < 0) return 0;
  return line.substring(i + key.length()).toInt();
}

void sendToThingSpeak(float tempC, int hr, int spo2) {
  if (WiFi.status() != WL_CONNECTED) return;
  WiFiClient client;
  if (!client.connect(host, 80)) return;
  String url = "/update?api_key=" + apiKey
             + "&field1=" + String(tempC, 1)
             + "&field2=" + String(hr)
             + "&field3=" + String(spo2);
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
  client.stop();
}

What the Code Does

  • The Arduino sketch collects 100 samples from the MAX30102 (red + infrared light), then runs the SparkFun SpO2 algorithm, which finds the heart rate from the pulse waveform and estimates SpO2 from the red/infrared ratio. If the finger is not on the sensor, the "valid" flags are false and the display shows 0.
  • The LM35 gives 10 mV per °C, so the temperature is analogRead(A0) × 5.0 / 1024 × 100.
  • The LCD shows temperature and heart rate on the first line, SpO2 on the second. After every reading the Arduino sends one line — T:36.5 H:72 S:98 — over serial.
  • The NodeMCU sketch reads that line, takes the three numbers after T:, H: and S:, and sends them to ThingSpeak fields 1, 2 and 3 with a plain HTTP GET request. You can watch all three values on your ThingSpeak channel charts.
  • The Arduino's Serial goes to the USB monitor AND to the NodeMCU at the same time, so the Serial Monitor still shows the T:.. H:.. S:.. lines while the system runs.

How to Make the System

Step 1: Wire the Sensors

Wire the LM35 (A0), MAX30102 (A4/A5, 3.3V) and LCD (A4/A5, 5V) as in the tables above. Join all grounds together. Fit the 1k + 2k divider between the UNO TX and the NodeMCU RX.

Step 2: Install the Libraries and Upload Both Sketches

Install the two libraries above, upload the Arduino sketch to the UNO and the NodeMCU sketch to the NodeMCU board. Put your own Wi-Fi name, password and ThingSpeak API key in the NodeMCU sketch first (create a free ThingSpeak account and channel with three fields).

Step 3: Test on Yourself

Place a fingertip flat on the MAX30102 sensor and hold still for a few seconds. The LCD should show a temperature near 36–37 °C, a heart rate, and an SpO2 value. Compare the temperature with an ordinary thermometer — if it is off by more than a degree, check the LM35 wiring.

Step 4: Check the Cloud

Open your ThingSpeak channel after a few minutes — fields 1, 2 and 3 should plot temperature, heart rate and SpO2 over time. If the charts stay flat, check the NodeMCU Wi-Fi connection and the API key.

Testing the System

  1. Test the LM35 first. Open the Serial Monitor at 9600 baud — the T: value should read near room temperature (25–30 °C) with nothing touching the sensor, and rise when you hold it between your fingers.
  2. Test the MAX30102. If the LCD shows "MAX30102 missing", check the I2C wiring (SDA → A4, SCL → A5) and that VCC is 3.3V. If the heart rate stays 0, keep your finger still and flat on the sensor — movement breaks the reading.
  3. Test the LCD. If it stays blank, try the other I2C address (0x3F instead of 0x27) and check SDA → A4, SCL → A5.
  4. Test the NodeMCU link. The Serial Monitor should show T:.. H:.. S:.. lines — if it does, the Arduino side is fine and the fault is on the NodeMCU side. Check the divider and the common GND.

Troubleshooting

  • MAX30102 not found: check SDA → A4 and SCL → A5, VCC is 3.3V (not 5V), and the sensor is firmly connected.
  • Heart rate stays 0: the finger must be still and flat on the sensor. Cold hands and movement both break the reading. Wait a few seconds after placing the finger.
  • SpO2 shows 0 or jumps around: the SpO2 estimate needs a good signal — same fix as above. It is an estimate for a student model, not a clinical measurement.
  • LCD shows nothing: check the I2C address — most modules use 0x27, some use 0x3F. Also check SDA → A4 and SCL → A5, and that the backlight is on.
  • ThingSpeak never updates: check the Wi-Fi name and password, the API key, and that the NodeMCU is connected. ThingSpeak's free account allows one update every 15 seconds — faster updates are ignored.
  • NodeMCU not receiving: check the divider (UNO TX → 1k → NodeMCU RX, RX → 2k → GND) and the common GND.

Limitations — Read This Before You Build

  • This is a student model, not a medical device. The heart rate and SpO2 numbers are estimates from a hobby sensor. They can be wrong — cold hands, movement, bright light and skin tone all affect them. Never use them to diagnose or treat anyone.
  • The SpO2 number is an estimate. A real pulse oximeter is calibrated against clinical data; this one is not. Compare your readings with a proper device and say so in your project report.
  • Blood pressure and ECG are not included. They need extra hardware (a pressure cuff sensor, an ECG module) — this build covers temperature, heart rate and SpO2 only.
  • A similar ESP32-based health project is our ESP32 health monitoring patch — it uses the same MAX30100-family sensor with an ESP32 instead of an UNO + NodeMCU.

Cost Estimation

The old version of this post listed prices that are no longer current (Arduino UNO at Rs 1,800, and the Pulse Sensor missing from the total). At today's live QKZEE prices, the parts with listings cost: Arduino UNO Rs 1,200 + LCD Rs 660 + NodeMCU Rs 590 + breadboard Rs 250 + jumper wires Rs 180 = Rs 2,880. Add the three sensors (LM35, MAX30102, Pulse Sensor) — ask at the Hall Road shop for their current prices, as they are not listed on the site.

Applications

  • Hospitals for continuous patient monitoring (as a demonstration model)
  • Home healthcare systems for elderly or chronic patients (as a learning project)
  • Wearable devices for fitness tracking
  • University and school projects to learn biomedical IoT applications

Advantages

  • Low-cost and easy to assemble
  • Real-time monitoring through IoT
  • Gives a live demonstration of real sensor data
  • Expandable for additional parameters (ECG, blood pressure, etc.)

Challenges and Solutions

ChallengeSolution
Signal noise from sensorsUse proper filtering and shielded cables
Wi-Fi disconnectionAdd reconnection logic in code
Sensor calibrationCompare readings with clinical devices

IoT Integration

You can send data to cloud platforms like ThingSpeak, Firebase or Blynk. Using the NodeMCU, you can monitor patient data on a smartphone dashboard in real time. We use the same ThingSpeak approach in our IoT smart parking guide — the NodeMCU sketch there sends a single number to field1, and this project extends the same pattern to three fields (temperature, heart rate, SpO2).

Where to Buy the Components

The Arduino UNO, LCD, NodeMCU, breadboard and jumper wires are available at QKZEE Technologies, Shop 20, 1st Floor, Al-Khalil Center, Hall Road, Lahore, and online at qkzeetech.com with delivery all over Pakistan — see the links above. The LM35, MAX30102 and Pulse Sensor are available at the Hall Road shop on request.

Still Stuck?

If a sensor is not found, or ThingSpeak never updates, message us — tell us what the Serial Monitor shows and what the LCD displays, and we will help you find the fault.

WhatsAppYouTube

FAQs

Is this a real medical device?

No. It is a student model for learning. Its readings are estimates and can be wrong. Never use it to diagnose or treat anyone.

Why does the heart rate show 0 sometimes?

The sensor needs a still, flat fingertip. Movement, cold hands and bright light break the reading. Hold still for a few seconds and it should appear.

Can I use the Pulse Sensor instead of the MAX30102?

Yes — connect it to A1 and use the PulseSensor Playground library for the heart-rate number. You lose SpO2, which only the MAX30102 gives.

Why does the NodeMCU need a voltage divider?

The Arduino TX pin sends 5V, but the NodeMCU RX pin is 3.3V logic. The 1k + 2k divider drops the signal to about 3.3V. Without it the NodeMCU may read garbage or be damaged over time.

Why 3.3V for the MAX30102?

The MAX30102 chip runs on 3.3V logic. Powering it from 5V can damage it. The UNO's 3.3V pin is the right supply.

Can I add blood pressure or ECG?

Yes, with extra hardware — a pressure sensor with a cuff for blood pressure, an AD8232 module for ECG. They are separate additions, not part of this build.

Conclusion

A biomedical patient monitoring system is an innovative and practical IoT-based project suitable for students, electrical engineers and medical technology enthusiasts. It merges electronics, sensors and software to create a working demonstration of remote health monitoring. Build it with the correct 3.3V wiring, test it against a real thermometer and a proper pulse oximeter, and report your measured accuracy honestly in your project report.