#include <Wire.h>
#include "MAX30105.h"
#include "heartRate.h"
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
// ---------------- TFT PINS ----------------
#define TFT_CS 10
#define TFT_RST 8
#define TFT_DC 9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
// ---------------- BUZZER ----------------
#define BUZZER 3
// ---------------- SENSOR ----------------
MAX30105 particleSensor;
// ---------------- VARIABLES ----------------
long lastBeat = 0;
float beatsPerMinute;
int beatAvg = 0;
int spo2 = 98;
bool beatState = false;
// =====================================================
// HEART ICON
// =====================================================
void drawHeart(int x, int y, uint16_t color)
{
tft.fillCircle(x - 8, y, 8, color);
tft.fillCircle(x + 8, y, 8, color);
tft.fillTriangle(x - 16, y + 2, x + 16, y + 2, x, y + 22, color);
}
// =====================================================
// BLOOD DROP ICON
// =====================================================
void drawDrop(int x, int y, uint16_t color)
{
tft.fillCircle(x, y + 8, 10, color);
tft.fillTriangle(x - 10, y + 8, x + 10, y + 8, x, y - 15, color);
}
// =====================================================
// STATIC UI
// =====================================================
void drawUI()
{
tft.fillScreen(ST77XX_BLACK);
// TITLE
tft.setTextSize(1);
tft.setTextColor(ST77XX_GREEN);
tft.setCursor(18, 5);
tft.print("HEART & SPO2 MONITOR");
// LINES
tft.drawLine(0, 18, 160, 18, ST77XX_WHITE);
tft.drawLine(0, 80, 160, 80, ST77XX_WHITE);
// HEART
drawHeart(40, 40, ST77XX_RED);
// DROP
drawDrop(40, 105, ST77XX_CYAN);
// LABELS
tft.setTextSize(2);
tft.setTextColor(ST77XX_WHITE);
tft.setCursor(80, 30);
tft.print("HR");
tft.setCursor(80, 92);
tft.print("SpO2");
// BPM TEXT
tft.setTextSize(1);
tft.setCursor(130, 60);
tft.print("BPM");
}
// =====================================================
// UPDATE HR
// =====================================================
void updateHR(bool fingerDetected)
{
tft.fillRect(80, 52, 50, 20, ST77XX_BLACK);
tft.setTextSize(2);
tft.setCursor(80, 52);
if (fingerDetected)
{
tft.setTextColor(ST77XX_GREEN);
tft.print(beatAvg);
}
else
{
tft.setTextColor(ST77XX_RED);
tft.print("--");
}
}
// =====================================================
// UPDATE SPO2
// =====================================================
void updateSPO2(bool fingerDetected)
{
tft.fillRect(80, 115, 60, 20, ST77XX_BLACK);
tft.setTextSize(2);
tft.setCursor(80, 115);
if (fingerDetected)
{
tft.setTextColor(ST77XX_CYAN);
tft.print(spo2);
tft.print("%");
}
else
{
tft.setTextColor(ST77XX_RED);
tft.print("--%");
}
}
// =====================================================
// STATUS TEXT
// =====================================================
void updateStatus(bool fingerDetected)
{
tft.fillRect(0, 145, 160, 15, ST77XX_BLACK);
tft.setTextSize(1);
if (fingerDetected)
{
tft.setCursor(35, 145);
tft.setTextColor(ST77XX_GREEN);
tft.print("FINGER DETECTED");
}
else
{
tft.setCursor(42, 145);
tft.setTextColor(ST77XX_YELLOW);
tft.print("PLACE FINGER");
}
}
// =====================================================
// HEART ANIMATION
// =====================================================
void animateHeart()
{
if (beatState)
{
drawHeart(40, 40, ST77XX_RED);
}
else
{
drawHeart(40, 40, ST77XX_MAGENTA);
}
beatState = !beatState;
}
// =====================================================
// SETUP
// =====================================================
void setup()
{
Serial.begin(115200);
pinMode(BUZZER, OUTPUT);
// TFT START
tft.initR(INITR_BLACKTAB);
// HORIZONTAL
tft.setRotation(3);
// SENSOR START
if (!particleSensor.begin(Wire, I2C_SPEED_STANDARD))
{
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_RED);
tft.setTextSize(2);
tft.setCursor(10, 50);
tft.print("MAX30102");
tft.setCursor(20, 80);
tft.print("ERROR");
while (1);
}
particleSensor.setup();
particleSensor.setPulseAmplitudeRed(0x0A);
particleSensor.setPulseAmplitudeGreen(0);
drawUI();
}
// =====================================================
// LOOP
// =====================================================
void loop()
{
long irValue = particleSensor.getIR();
bool fingerDetected = irValue > 50000;
// HEART DETECT
if (fingerDetected && checkForBeat(irValue))
{
long delta = millis() - lastBeat;
lastBeat = millis();
beatsPerMinute = 60 / (delta / 1000.0);
if (beatsPerMinute > 40 && beatsPerMinute < 180)
{
beatAvg = (int)beatsPerMinute;
// DEMO SPO2
spo2 = random(96, 100);
// BUZZER
tone(BUZZER, 2000, 60);
// HEART ANIMATION
animateHeart();
}
}
updateHR(fingerDetected);
updateSPO2(fingerDetected);
updateStatus(fingerDetected);
delay(50);
}