Creating your own heart health
monitor with an ESP8266 is a fantastic way to dive into DIY electronics while
enhancing your wellness journey. This compact device allows you to monitor key
indicators of heart health, including your heart rate (BPM) and blood oxygen
levels (SpO2), all displayed in real-time on a small screen. By tracking these
metrics, you gain valuable insights into your cardiovascular health, making it
a unique tool for personal wellness.
The project isn’t just
functional—it’s interactive. It includes a visual breathing monitor that
responds to your inhale and exhale patterns, with colours changing to reflect
each breath cycle. This breathing feature, along with the heart rate display,
adds a mindful, meditative element to the monitor, helping you observe your
breathing and its impact on your heart health.
Whether you're a beginner in
electronics or an experienced maker, this heart monitor project with ESP8266 is
a rewarding experience. Not only will it expand your tech skills, but it also
provides a portable wellness tool that can help you stay conscious of your
heart health in real-time.
Keep in mind, this is a
prototype intended for personal wellness insights, suitable for general daily
check-ups for healthy individuals. However, if you have a serious
cardiovascular condition, it’s important not to rely on this as a primary
monitoring tool, as the readings may occasionally differ from clinical values.
Please use it as a supplementary device rather than a substitute for
professional medical equipment.
How This Circuit Works
The heart of this DIY heart
health monitor is the MAX30105 sensor, which is specially designed to measure
heart rate and SpO2 (blood oxygen saturation). This sensor uses photo plethysmography
(PPG) to detect changes in blood volume by shining LED light (red, infrared,
and green) through your skin and measuring how much light is absorbed.
Variations in light absorption correspond to heartbeats, which allows the
sensor to calculate both your pulse and SpO2 levels. The ESP8266 reads these
measurements, processes the data, and displays the information on a screen.
In the code, the checkForBeat()
function monitors infrared light levels
from the sensor, detecting peaks that signal heartbeats. Each peak is processed
to calculate beats per minute (BPM), while a simple SpO2 formula computes
oxygen levels based on the ratio of red to infrared light absorption. The
ESP8266 also updates the display with this data, using the added memory to
ensure smooth, accurate readings. This combination of hardware and code creates
a functional and compact heart health monitor prototype, providing real-time
feedback on your pulse and oxygen levels in a user-friendly display.
Components Needed:
1x ESP8266 NodeMCU Module
1x MAX30102 Heart Rate
Sensor
1x1.8 Inch ST7735 LCD
Display (In future, you can update with the other displays.)
1x Neopixel LED (This is not the serious component, it’s just for heart beat visualization, you can add this or not it’s your choice.)
1x 470uF Capacitor 25V(for smooth the voltage and reduce noise)
1x 100nF Capacitor(for smooth the voltage and reduce noise)
Why I Chose ESP8266 over Arduino Boards
The
ESP8266 offers a significant advantage over standard Arduino boards, especially
when it comes to memory management and handling more complex code. In this
heart health monitor project, the sensor readings, display updates, and data
buffering all demand substantial memory. With limited memory on most Arduino
boards, adding code for continuous monitoring, displaying BPM and SpO2 values,
and rendering graphics would quickly exceed their capabilities, leading to
slower performance or even crashes.
The
ESP8266, however, has a larger memory capacity and can handle this more
demanding setup with ease. This extra memory allows for smoother data
processing and larger buffers, meaning it can store and update sensor values
more effectively, which is crucial for maintaining accurate real-time readings.
Additionally, the ESP8266’s support for Wi-Fi makes it ideal for future
upgrades, like sending health data to a smartphone app or cloud storage.
Overall, the ESP8266 is a better choice for this project, balancing memory
needs and enabling additional features beyond what typical Arduino boards can
handle.
Connections or Circuit Diagram
Setting up the ESP8266 Heart
Health Monitor involves a few simple connections between the ESP8266, MAX30105
sensor, and the display. The MAX30105 sensor communicates with the ESP8266 via
I2C, requiring only two pins for data transfer, which keeps the wiring
straightforward. The display is also connected to the ESP8266 using specific
data pins, allowing the screen to display real-time heart rate and SpO2
readings effectively.
Here’s a table showing the
necessary connections between the ESP8266 and the other components:
ESP8266 Pin |
MAX30105 Pin |
Display Pin |
Function |
3V3 and
5V |
5V |
5V,RST |
Power
Supply for Sensor and Display |
GND |
GND |
GND |
Ground
Connection |
D1 |
SDA |
N/A |
I2C
Data Line |
D2 |
SCL |
N/A |
I2C
Clock Line |
D5 |
N/A |
SCL |
SPI
Clock |
D7 |
N/A |
SDA
(MOSI) |
SPI
Data In |
D8 |
N/A |
CS |
Chip
Select |
D4 |
N/A |
DC |
Data/Command
Line |
In this setup:
- 3V3 and GND provide the power and
ground connections to the sensor and display.
- I2C Lines (D1 and D2) connect the MAX30105 sensor
for heart rate and SpO2 data.
- SPI Lines (D5, D7, D8 and
D4)
control the display to show the readings.
The simplicity of these
connections makes this project accessible to beginners, while the ESP8266’s
processing power and memory allow it to handle real-time calculations and
display updates efficiently.
Code Overview
#include <Wire.h> #include "MAX30105.h" #include "heartRate.h" #include <Adafruit_GFX.h> #include <Adafruit_ST7735.h> #include <Adafruit_NeoPixel.h> MAX30105 particleSensor; #define SCALING 12 #define TRACE_SPEED 0.5 #define TRACE_MIDDLE_Y_POSITION 90 #define TRACE_HEIGHT 60 #define HALF_TRACE_HEIGHT TRACE_HEIGHT / 2 #define TRACE_MIN_Y TRACE_MIDDLE_Y_POSITION - HALF_TRACE_HEIGHT + 1 #define TRACE_MAX_Y TRACE_MIDDLE_Y_POSITION + HALF_TRACE_HEIGHT - 1 #define TFT_CS D8 #define TFT_RST -1 #define TFT_DC D4 #define TFT_SDA D7 #define TFT_SCL D5 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); #define NEOPIXEL_PIN D6 #define NUMPIXELS 16 Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); long lastBeat = 0; float beatsPerMinute = 0.0; float spO2 = 0.0; bool isInhaling = false; bool isExhaling = false; int stableCount = 0; const int inhaleExhaleThreshold = 5; long irMovingAverage = 0; const int smoothingFactor = 20; const long MIN_IR_VALUE = 5000; bool fingerDetected = false; // Variables for Neopixel Heartbeat Animation uint8_t heartBrightness = 0; bool increasingBrightness = true; unsigned long lastHeartBeatUpdate = 0; const unsigned long heartbeatInterval = 10; // Faster interval for animation void setup() { tft.initR(INITR_GREENTAB); tft.setRotation(3); tft.fillScreen(ST7735_BLACK); tft.setTextSize(1); tft.setTextColor(ST7735_WHITE); pixels.begin(); pixels.clear(); pixels.show(); Serial.begin(115200); Serial.println("Initializing..."); if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { Serial.println("MAX30105 was not found. Please check wiring/power."); while (1); } byte ledBrightness = 0x1F; byte sampleAverage = 8; byte ledMode = 3; int sampleRate = 100; int pulseWidth = 411; int adcRange = 4096; particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); particleSensor.enableDIETEMPRDY(); tft.setTextSize(2); tft.setCursor(0, 10); tft.print("BPM"); tft.setTextSize(1); tft.setCursor(1, 0); tft.print("ElecoTechoz Heart Health"); tft.setTextSize(2); tft.drawRect(0, TRACE_MIN_Y - 1, 160, TRACE_HEIGHT + 2, ST7735_BLUE); } void loop() { static float lastx = 1; static int lasty = TRACE_MIDDLE_Y_POSITION; static float x = 1; int32_t y; static uint32_t tsLastReport = 0; static int32_t SensorOffset = 10000; long irValue = particleSensor.getIR(); long redValue = particleSensor.getRed(); // Check if a finger is detected based on IR value fingerDetected = (irValue > MIN_IR_VALUE); if (fingerDetected) { // BPM detection if (checkForBeat(irValue)) { long delta = millis() - lastBeat; if (delta > 300) { lastBeat = millis(); float bpm = 60 / (delta / 1000.0); if (bpm > 40 && bpm < 180) { beatsPerMinute = bpm; tft.fillRect(0, 30, 160, 20, ST7735_BLACK); tft.setCursor(0, 30); if (beatsPerMinute < 60 || beatsPerMinute > 110) tft.setTextColor(ST7735_BLUE); else tft.setTextColor(ST7735_GREEN); tft.setTextSize(2); tft.print(beatsPerMinute); } } } spO2 = calculateSpO2Simple(redValue, irValue); tft.fillRect(90, 20, 60, 16, ST7735_BLACK); tft.setTextSize(1); tft.setCursor(90, 20); tft.setTextColor(ST7735_MAGENTA); tft.print("SpO2:"); tft.print(spO2, 1); tft.print(" %"); float temperatureC = particleSensor.readTemperature(); tft.fillRect(90, 40, 60, 16, ST7735_BLACK); tft.setTextSize(1); tft.setCursor(90, 40); tft.setTextColor(ST7735_YELLOW); tft.print("Temp:"); tft.print(temperatureC, 1); tft.print(" C"); irMovingAverage = (irMovingAverage * (smoothingFactor - 1) + irValue) / smoothingFactor; if (irValue < irMovingAverage - 300) { if (!isInhaling) { isInhaling = true; isExhaling = false; tft.fillRect(0, 50, 60, 10, ST7735_BLACK); tft.setCursor(0, 50); tft.setTextColor(ST7735_GREEN); tft.print("Inhale"); pixels.fill(pixels.Color(0, 255, 0)); pixels.show(); } } else if (irValue > irMovingAverage + 300) { if (!isExhaling) { isExhaling = true; isInhaling = false; tft.fillRect(0, 50, 60, 10, ST7735_BLACK); tft.setCursor(0, 50); tft.setTextColor(ST7735_RED); tft.print("Exhale"); pixels.fill(pixels.Color(255, 0, 0)); pixels.show(); } } } else { tft.fillRect(0, 50, 60, 10, ST7735_BLACK); pixels.clear(); pixels.show(); } y = calculateGraphPosition(fingerDetected ? irValue : SensorOffset, SensorOffset); tft.drawLine(lastx, lasty, x, y, ST7735_YELLOW); lasty = y; lastx = x; x += TRACE_SPEED; if (x > 158) { tft.fillRect(1, TRACE_MIN_Y, 158, TRACE_HEIGHT, ST7735_BLACK); x = 1; lastx = x; } updateNeopixelHeartbeat(); } void updateNeopixelHeartbeat() { unsigned long currentMillis = millis(); if (fingerDetected && currentMillis - lastHeartBeatUpdate >= heartbeatInterval) { lastHeartBeatUpdate = currentMillis; if (increasingBrightness) { heartBrightness += 15; if (heartBrightness >= 255) increasingBrightness = false; } else { heartBrightness -= 15; if (heartBrightness <= 0) increasingBrightness = true; } uint32_t color = pixels.Color(255, 105, 180); // True pink color uint32_t pulsingColor = pixels.Color((heartBrightness * 255) / 255, (heartBrightness * 105) / 255, (heartBrightness * 180) / 255); pixels.fill(pulsingColor); pixels.show(); } else if (!fingerDetected) { pixels.clear(); pixels.show(); } } int calculateGraphPosition(long irValue, int &SensorOffset) { int32_t Diff = irValue - SensorOffset; Diff /= SCALING; if (Diff < -HALF_TRACE_HEIGHT) SensorOffset += (SCALING * (abs(Diff) - 32)); if (Diff > HALF_TRACE_HEIGHT) SensorOffset += (SCALING * (abs(Diff) - 32)); int y = Diff + (TRACE_MIDDLE_Y_POSITION - HALF_TRACE_HEIGHT); y += TRACE_HEIGHT / 4; return constrain(y, TRACE_MIN_Y, TRACE_MAX_Y); } boolean checkForBeat(long irValue) { static long prevIrValue = 0; static boolean prevState = false; boolean beatDetected = (irValue > prevIrValue + 20) && prevState && (millis() - lastBeat > 300); prevIrValue = irValue; prevState = (irValue > 5000); return beatDetected; } float calculateSpO2Simple(long redValue, long irValue) { float ratio = (float)redValue / irValue; float spO2 = 110.0 - (25.0 * ratio); return constrain(spO2, 0.0, 100.0); }
The code for this DIY Heart
Health Monitor with ESP8266 gathers heart rate (BPM) and SpO2 data from the
MAX30105 sensor, processes the readings, and displays them on the screen. It
starts by initializing the sensor and display, then enters a continuous loop
where it checks for heartbeats, calculates SpO2 levels, and shows the results
in real time. Each heartbeat triggers a visual pulse, and the display updates
with BPM, SpO2, and indicators for breathing and heart status.
To upload the code, open the
Arduino IDE, select ESP8266
as the board, choose the correct COM
port, and copy-paste the code. After uploading, the device
should be ready to monitor and display live heart health data in real-time.
Conclusion and Future Upgrades
Congratulations!
You've now successfully built a heart rate and SpO2 monitoring system using the
MAX30102 sensor and an ST7735 TFT display. This project not only provides
real-time health monitoring but also includes an interactive Neopixel heartbeat
animation, enhancing the visual appeal and user experience.
The
versatility of this setup opens doors for exciting upgrades:
- WI-FI Integration: Send the BPM and SpO2 data
to a mobile app for remote health tracking.
- Battery Operability: Make the device portable
by incorporating a rechargeable battery.
- Data Logging: Store the readings over
time on an SD card for trend analysis.
- Advanced Visuals: Upgrade to a larger
display or add touch functionality for easier interaction.
- Health Alerts: Integrate sound or vibration
alerts for abnormal readings.
With
further refinements, this project could even be the foundation for a wearable
health tracker. Feel free to experiment and expand the functionality as your
skills grow. If you found this tutorial helpful, don’t forget to share your
creations and improvements!
Happy
making! 😊
Comments
Post a Comment