Heart Guard: Revolutionizing Health Monitoring with Wireless Data Acquisition¶
You can view the entire report of the project on my partner site Click Here
Introduction¶
Healthcare today is facing major challenges due to growing populations, limited resources, and not enough medical staff. This makes it hard for many people to get timely medical care. Our project aims to help solve these problems by using technology. We propose a solution that reduces the need for patients with chronic conditions to visit treatment centers in person. Our plan involves using a monitoring device that can track important health indicators like blood sugar levels, body temperature, heart rate, blood pressure, and oxygen levels. This device will send data to a system where doctors can monitor multiple patients remotely. This approach will help doctors manage more patients efficiently, reduce the strain on healthcare resources, and make it easier for people to get the care they need
What i Have Done in the Project¶
Task I¶
To begin, we used various microcontrollers, including the MKR1010 and arduino UNO , and connected the MAX30100 sensor to them to understand its functionality. Although the MAX30100 and MAX30102 library provided code for measuring heart rate and SpO2, we needed to make some adjustments for it to work correctly with our setup.
Here is the C code designed to interface with the MAX30100 sensor for measuring heart rate and SpO2 levels. This code initializes the sensor, configures its settings, and continuously reads the sensor data to calculate and display heart rate and oxygen
#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"
// Create an instance of the MAX30105 class to interact with the sensor
MAX30105 particleSensor;
// Define the size of the rates array for averaging BPM; can be adjusted for smoother results
const byte RATE_SIZE = 4; // Increase this for more averaging. 4 is a good starting point.
byte rates[RATE_SIZE]; // Array to store heart rate readings for averaging
byte rateSpot = 0; // Index for inserting the next heart rate reading into the array
long lastBeat = 0; // Timestamp of the last detected beat, used to calculate BPM
float beatsPerMinute; // Calculated heart rate in beats per minute
int beatAvg; // Average heart rate after processing multiple readings
void setup() {
Serial.begin(115200); // Start serial communication at 115200 baud rate
Serial.println("Initializing...");
// Attempt to initialize the MAX30105 sensor. Check for a successful connection and report.
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { // Start communication using fast I2C speed
Serial.println("MAX30102 was not found. Please check wiring/power. ");
while (1); // Infinite loop to halt further execution if sensor is not found
}
Serial.println("Place your index finger on the sensor with steady pressure.");
particleSensor.setup(); // Configure sensor with default settings for heart rate monitoring
particleSensor.setPulseAmplitudeRed(0x0A); // Set the red LED pulse amplitude (intensity) to a low value as an indicator
particleSensor.setPulseAmplitudeGreen(0); // Turn off the green LED as it's not used here
}
void loop() {
long irValue = particleSensor.getIR(); // Read the infrared value from the sensor
if (checkForBeat(irValue) == true) { // Check if a heart beat is detected
long delta = millis() - lastBeat; // Calculate the time between the current and last beat
lastBeat = millis(); // Update lastBeat to the current time
beatsPerMinute = 60 / (delta / 1000.0); // Calculate BPM
// Ensure BPM is within a reasonable range before updating the rates array
if (beatsPerMinute < 255 && beatsPerMinute > 20) {
rates[rateSpot++] = (byte)beatsPerMinute; // Store this reading in the rates array
rateSpot %= RATE_SIZE; // Wrap the rateSpot index to keep it within the bounds of the rates array
// Compute the average of stored heart rates to smooth out the BPM
beatAvg = 0;
for (byte x = 0 ; x < RATE_SIZE ; x++)
beatAvg += rates[x];
beatAvg /= RATE_SIZE;
}
}
// 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();
}
After downloading the required lib, the code was working properly in Arduino IDE C languageā¦
Task II¶
Next, we received an ESP32 S3 microcontroller with a 1.28-inch touch LCD screen. Our task was to write code using MicroPython to get the MAX30102 sensor working with this sensor. This involved creating a program that would enable the sensor to communicate with the ESP32 S3 and display the results on the screen.
Here’s the MicroPython code to interface with the MAX30102 sensor and measure heart rate, written for use with the Thonny IDE
from machine import Pin, SPI, SoftI2C
from utime import ticks_diff, ticks_us, ticks_ms, sleep
import gc9a01py as gc9a01
from max30102 import MAX30102, MAX30105_PULSE_AMP_MEDIUM
from fonts.romfonts import vga2_bold_16x32 as font4
error = True
class HeartRateMonitor:
"""A simple heart rate monitor that uses a moving window to smooth the signal and find peaks."""
def _init_(self, sample_rate=100, window_size=10, smoothing_window=5):
self.sample_rate = sample_rate
self.window_size = window_size
self.smoothing_window = smoothing_window
self.samples = []
self.timestamps = []
self.filtered_samples = []
def add_sample(self, sample):
"""Add a new sample to the monitor."""
timestamp = ticks_ms()
self.samples.append(sample)
self.timestamps.append(timestamp)
# Apply smoothing
if len(self.samples) >= self.smoothing_window:
smoothed_sample = (
sum(self.samples[-self.smoothing_window :]) / self.smoothing_window
)
self.filtered_samples.append(smoothed_sample)
else:
self.filtered_samples.append(sample)
# Maintain the size of samples and timestamps
if len(self.samples) > self.window_size:
self.samples.pop(0)
self.timestamps.pop(0)
self.filtered_samples.pop(0)
def find_peaks(self):
"""Find peaks in the filtered samples."""
peaks = []
if len(self.filtered_samples) < 3: # Need at least three samples to find a peak
return peaks
# Calculate dynamic threshold based on the min and max of the recent window of filtered samples
recent_samples = self.filtered_samples[-self.window_size :]
min_val = min(recent_samples)
max_val = max(recent_samples)
threshold = (
min_val + (max_val - min_val) * 0.5
) # 50% between min and max as a threshold
for i in range(1, len(self.filtered_samples) - 1):
if (
self.filtered_samples[i] > threshold
and self.filtered_samples[i - 1] < self.filtered_samples[i]
and self.filtered_samples[i] > self.filtered_samples[i + 1]
):
peak_time = self.timestamps[i]
peaks.append((peak_time, self.filtered_samples[i]))
return peaks
def calculate_heart_rate(self):
"""Calculate the heart rate in beats per minute (BPM)."""
peaks = self.find_peaks()
if len(peaks) < 2:
return None # Not enough peaks to calculate heart rate
# Calculate the average interval between peaks in milliseconds
intervals = []
for i in range(1, len(peaks)):
interval = ticks_diff(peaks[i][0], peaks[i - 1][0])
intervals.append(interval)
average_interval = sum(intervals) / len(intervals)
# Convert intervals to heart rate in beats per minute (BPM)
heart_rate = (
60000 / average_interval
) # 60 seconds per minute * 1000 ms per second
return heart_rate
def setup_display():
"""Initialize the GC9A01 display."""
spi = SPI(2, baudrate=80000000, polarity=0, sck=Pin(10), mosi=Pin(11))
tft = gc9a01.GC9A01(
spi, dc=Pin(8, Pin.OUT), cs=Pin(9, Pin.OUT),
reset=Pin(14, Pin.OUT), backlight=Pin(2, Pin.OUT),
rotation=0
)
return tft
def update_display(tft, heart_rate):
"""Update the display with the current heart rate."""
tft.fill(gc9a01.BLACK)
line = tft.height // 2
col = tft.width // 3
if heart_rate is not None:
display_text = "HR: {:.0f} BPM".format(heart_rate)
else:
display_text = "No HR Data"
tft.text(font4, display_text, col, line, gc9a01.WHITE, gc9a01.RED)
if error==True:
tft.text(font4, "warning", col,0, gc9a01.WHITE, gc9a01.RED)
def main():
# Initialize I2C and sensor
i2c = SoftI2C(
sda=Pin(15), # Here, use your I2C SDA pin
scl=Pin(16), # Here, use your I2C SCL pin
freq=400000,
)
sensor = MAX30102(i2c=i2c)
if sensor.i2c_address not in i2c.scan():
print("Sensor not found.")
return
elif not (sensor.check_part_id()):
print("I2C device ID not corresponding to MAX30102 or MAX30105.")
return
else:
print("Sensor connected and recognized.")
print("Setting up sensor with default configuration.", "\n")
sensor.setup_sensor()
sensor_sample_rate = 400
sensor.set_sample_rate(sensor_sample_rate)
sensor_fifo_average = 8
sensor.set_fifo_average(sensor_fifo_average)
sensor.set_active_leds_amplitude(MAX30105_PULSE_AMP_MEDIUM)
actual_acquisition_rate = int(sensor_sample_rate / sensor_fifo_average)
sleep(1)
print(
"Starting data acquisition from RED & IR registers...",
"press Ctrl+C to stop.",
"\n",
)
sleep(1)
# Initialize heart rate monitor
hr_monitor = HeartRateMonitor(
sample_rate=actual_acquisition_rate,
window_size=int(actual_acquisition_rate * 3),
)
# Initialize display
tft = setup_display()
hr_compute_interval = 2 # seconds
ref_time = ticks_ms() # Reference time
while True:
sensor.check()
if sensor.available():
red_reading = sensor.pop_red_from_storage()
ir_reading = sensor.pop_ir_from_storage()
hr_monitor.add_sample(ir_reading)
if ticks_diff(ticks_ms(), ref_time) / 1000 > hr_compute_interval:
heart_rate = hr_monitor.calculate_heart_rate()
update_display(tft, heart_rate)
ref_time = ticks_ms()
if _name_ == "_main_":
main()
Final Look¶
This project aims to demonstrate how we can customize health monitoring devices and leverage IoT technology to gather patient data remotely. By doing so, doctors can monitor patients’ conditions without requiring their physical presence. This approach has the potential to reduce the high patient volumes at healthcare facilities worldwide. Additionally, it could generate new job opportunities by enabling hospitals to produce simple. Furthermore, integrating various devices, such as scales and glucose monitors, into a single application would allow doctors to efficiently track and manage the health data of numerous patients daily
Conclusion¶
Heart Guard revolutionizes health monitoring by offering real-time, wireless data acquisition, allowing doctors to track vital signs such as heart rate and SpO2 remotely. This technology alleviates the burden on healthcare facilities by reducing the need for in-person visits, facilitates early detection of health issues, and integrates various health metrics into a single platform. It also promotes job creation through simple manufacturing techniques and improves patient convenience by enabling them to manage their health from home, ultimately leading to more cost-effective and efficient healthcare solutions