Upload files to "/"

Source Code ESP32
This commit is contained in:
Firman_Nabil 2026-08-04 11:39:25 +07:00
parent 1134cadbe7
commit 98c0039af7
1 changed files with 476 additions and 0 deletions

476
HealthMonitor_v2_fixed.ino Normal file
View File

@ -0,0 +1,476 @@
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <MAX30105.h>
#include <heartRate.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFi.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include "spo2_algorithm.h"
#define TFT_CS 5
#define TFT_RST 4
#define TFT_DC 2
#define ONE_WIRE_BUS 13
const char* ssid = "sembarang";
const char* password = "12345678";
const char* serverName = "http://10.107.21.239/iot/save_data.php";
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
MAX30105 particleSensor;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature ds18b20(&oneWire);
enum State {
WAIT_FINGER,
MEASURE_BPM,
WAIT_TEMP_STABILIZE,
MEASURE_TEMP,
SHOW_RESULT
};
State currentState = WAIT_FINGER;
const byte RATE_SIZE = 8;
byte rates[RATE_SIZE];
byte rateSpot = 0;
byte validRateCount = 0;
long lastBeat = 0;
int beatAvg = 0;
int lastStoredBeatAvg = 0;
long lastIR = 0;
float suhuCelcius = 0;
unsigned long stateStart = 0;
unsigned long lastTempRead = 0;
unsigned long lastSerial = 0;
float bpmFilteredSum = 0;
int bpmFilteredCount = 0;
float tempSum = 0;
int tempCount = 0;
int finalBPM = 0;
float finalTemp = 0;
int finalSpO2 = 0;
int systolic = 0;
int diastolic = 0;
bool resultSent = false;
// Heart Animation
bool heartBeatAnimation = false;
unsigned long heartAnimStart = 0;
// Variabel baru untuk stabilisasi suhu
float initialTemp = 0;
bool tempStabilized = false;
unsigned long tempStableStart = 0;
const float TEMP_RISE_THRESHOLD = 0.5;
// SpO2 Variables
uint32_t irBuffer[100];
uint32_t redBuffer[100];
int32_t bufferLength = 100;
int32_t spo2 = 0;
int8_t validSPO2 = 0;
int32_t heartRateSpO2 = 0;
int8_t validHeartRate = 0;
int spo2Sum = 0;
int spo2Count = 0;
// Countdown
unsigned long lastCountdownUpdate = 0;
void showMessage(const char* line1, const char* line2, const char* line3 = "") {
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_YELLOW);
tft.setTextSize(2);
tft.setCursor(15, 50);
tft.println(line1);
tft.setTextSize(1);
tft.setCursor(15, 100);
tft.println(line2);
tft.setCursor(15, 120);
tft.println(line3);
}
// ==================== UPDATE STATUS ====================
String getStatusBPM(int bpm) {
if (bpm < 60) return "Rendah";
if (bpm > 100) return "Tinggi";
return "Normal";
}
String getStatusTemp(float temp) {
if (temp < 35.5) return "Rendah";
if (temp > 38.0) return "Tinggi";
return "Normal";
}
String getStatusSpO2(int spo2Val) {
if (spo2Val < 95) return "Rendah";
if (spo2Val > 100) return "Tinggi";
return "Normal";
}
String getStatusBP(int sys, int dia) {
if (sys < 90 || dia < 60) return "Rendah";
if (sys > 120 || dia > 80) return "Tinggi";
return "Normal";
}
void showFinalResult() {
tft.fillScreen(ILI9341_BLACK);
tft.setTextSize(2);
tft.setTextColor(ILI9341_GREEN);
tft.setCursor(45, 15);
tft.println("HASIL AKHIR");
tft.setTextSize(2);
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(20, 55);
tft.print("BPM : ");
tft.print(finalBPM);
tft.print(" ");
tft.setTextColor(ILI9341_CYAN);
tft.println(getStatusBPM(finalBPM));
tft.setCursor(20, 80);
tft.setTextColor(ILI9341_WHITE);
tft.print("SpO2 : ");
tft.print(finalSpO2);
tft.print("% ");
tft.setTextColor(ILI9341_CYAN);
tft.println(getStatusSpO2(finalSpO2));
tft.setCursor(20, 105);
tft.setTextColor(ILI9341_WHITE);
tft.print("TEMP : ");
tft.print(finalTemp, 1);
tft.print(" C ");
tft.setTextColor(ILI9341_CYAN);
tft.println(getStatusTemp(finalTemp));
tft.setCursor(20, 130);
tft.setTextColor(ILI9341_WHITE);
tft.print("BP : ");
tft.print(systolic);
tft.print("/");
tft.print(diastolic);
tft.print(" ");
tft.setTextColor(ILI9341_CYAN);
tft.println(getStatusBP(systolic, diastolic));
}
void showTempWaitingScreen() {
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.setCursor(30, 20);
tft.println("MENGUKUR SUHU");
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(20, 70);
tft.print("BPM : ");
tft.println(finalBPM);
tft.setCursor(20, 100);
tft.print("SpO2: ");
tft.print(finalSpO2);
tft.println("%");
tft.setCursor(20, 145);
tft.setTextColor(ILI9341_YELLOW);
tft.println("Genggam Tabung Besi");
}
void drawHeart(bool big) {
uint16_t color = ILI9341_RED;
tft.fillRect(240, 10, 70, 60, ILI9341_BLACK);
if (big) {
tft.fillCircle(255, 22, 10, color);
tft.fillCircle(275, 22, 10, color);
tft.fillTriangle(245, 28, 285, 28, 265, 58, color);
} else {
tft.fillCircle(257, 24, 7, color);
tft.fillCircle(273, 24, 7, color);
tft.fillTriangle(250, 28, 280, 28, 265, 50, color);
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
ds18b20.begin();
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
showMessage("MAX30102 ERROR", "Check Sensor");
while (true);
}
particleSensor.setup(0x5F, 8, 2, 400, 411, 4096);
particleSensor.setPulseAmplitudeRed(0x1F);
particleSensor.setPulseAmplitudeIR(0x1F);
showMessage("TEMPEL JARI", "Pada cahaya merah", "Menunggu BPM...");
}
void loop() {
readBPMRealtime();
readTemperatureRealtime();
if (currentState == MEASURE_BPM) {
if (heartBeatAnimation) {
drawHeart(true);
if (millis() - heartAnimStart > 180) heartBeatAnimation = false;
} else {
drawHeart(false);
}
}
if (millis() - lastSerial > 1000) {
lastSerial = millis();
Serial.print("IR: "); Serial.print(lastIR);
Serial.print(" | BPM: "); Serial.print(beatAvg);
Serial.print(" | SpO2: "); Serial.print(spo2);
Serial.print(" | TEMP: "); Serial.println(suhuCelcius, 1);
}
switch (currentState) {
case WAIT_FINGER:
if (beatAvg > 0) {
bpmFilteredSum = 0; bpmFilteredCount = 0;
spo2Sum = 0; spo2Count = 0;
lastStoredBeatAvg = 0;
stateStart = millis();
lastCountdownUpdate = millis();
currentState = MEASURE_BPM;
showMessage("MENGUKUR BPM", "Jangan lepaskan jari", "40 detik...");
}
break;
case MEASURE_BPM:
if (millis() - lastCountdownUpdate >= 1000) {
lastCountdownUpdate = millis();
int sisa = 40 - ((millis() - stateStart) / 1000);
if (sisa < 0) sisa = 0;
tft.fillRect(20, 140, 200, 30, ILI9341_BLACK);
tft.setCursor(20, 140);
tft.setTextColor(ILI9341_CYAN);
tft.setTextSize(2);
tft.print("Sisa: "); tft.print(sisa); tft.println(" detik");
}
if (beatAvg > 0 && beatAvg != lastStoredBeatAvg) {
Serial.print("BEATAVG BARU = "); Serial.println(beatAvg);
lastStoredBeatAvg = beatAvg;
if (bpmFilteredCount == 0) {
bpmFilteredSum += beatAvg; bpmFilteredCount++;
} else {
float runningAvg = bpmFilteredSum / bpmFilteredCount;
if (abs(beatAvg - runningAvg) <= 10) {
bpmFilteredSum += beatAvg; bpmFilteredCount++;
}
}
}
if (millis() - stateStart >= 40000UL) {
finalBPM = (bpmFilteredCount > 0) ? round(bpmFilteredSum / bpmFilteredCount) : 0;
finalSpO2 = (spo2Count > 5) ? round((float)spo2Sum / spo2Count) : 0;
initialTemp = suhuCelcius;
tempStabilized = false;
tempSum = 0; tempCount = 0;
stateStart = millis();
lastCountdownUpdate = millis();
currentState = WAIT_TEMP_STABILIZE;
showMessage("MENGUKUR SUHU", "Menunggu Suhu Stabil", "");
}
break;
case WAIT_TEMP_STABILIZE:
if (suhuCelcius > initialTemp + TEMP_RISE_THRESHOLD) {
if (!tempStabilized) {
tempStabilized = true;
tempStableStart = millis();
}
}
if (tempStabilized && (millis() - tempStableStart >= 5000UL)) {
stateStart = millis();
lastCountdownUpdate = millis();
currentState = MEASURE_TEMP;
showTempWaitingScreen();
}
if (millis() - stateStart >= 25000UL) {
stateStart = millis();
lastCountdownUpdate = millis();
currentState = MEASURE_TEMP;
showTempWaitingScreen();
}
break;
case MEASURE_TEMP:
tempSum += suhuCelcius;
tempCount++;
if (millis() - lastCountdownUpdate >= 1000) {
lastCountdownUpdate = millis();
int sisa = 40 - ((millis() - stateStart) / 1000);
if (sisa < 0) sisa = 0;
tft.fillRect(20, 200, 200, 30, ILI9341_BLACK);
tft.setCursor(20, 200);
tft.setTextColor(ILI9341_CYAN);
tft.setTextSize(2);
tft.print("Sisa: "); tft.print(sisa); tft.println(" dtk");
}
if (millis() - stateStart >= 40000UL) {
finalTemp = (tempCount > 0) ? tempSum / tempCount : suhuCelcius;
currentState = SHOW_RESULT;
showFinalResult();
}
break;
case SHOW_RESULT:
if (!resultSent) {
resultSent = true;
sendDataToServer(finalBPM, finalTemp);
}
break;
}
delay(10);
}
void readBPMRealtime() {
long irValue = particleSensor.getIR();
long redValue = particleSensor.getRed();
if (lastIR == 0) lastIR = irValue;
long filteredIR = (irValue * 0.3) + (lastIR * 0.7);
lastIR = filteredIR;
static int bufferIndex = 0;
if (currentState == MEASURE_BPM) {
redBuffer[bufferIndex] = redValue;
irBuffer[bufferIndex] = irValue;
bufferIndex++;
if (bufferIndex >= bufferLength) {
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRateSpO2, &validHeartRate);
if (validSPO2 && spo2 > 70 && spo2 <= 100) {
spo2Sum += spo2;
spo2Count++;
}
bufferIndex = 0;
}
}
if (filteredIR > 25000) {
if (checkForBeat(filteredIR)) {
heartBeatAnimation = true;
heartAnimStart = millis();
long delta = millis() - lastBeat;
lastBeat = millis();
float bpm_raw = 60.0 / (delta / 1000.0);
if (delta < 450 || delta > 1200) return;
if (bpm_raw > 50 && bpm_raw < 150) {
static float lastValidBPM = 0;
if (lastValidBPM > 0 && abs(bpm_raw - lastValidBPM) > 8) return;
lastValidBPM = bpm_raw;
rates[rateSpot++] = (byte)bpm_raw;
rateSpot %= RATE_SIZE;
if (validRateCount < RATE_SIZE) validRateCount++;
beatAvg = 0;
for (byte x = 0; x < validRateCount; x++) beatAvg += rates[x];
beatAvg /= validRateCount;
}
}
}
}
void readTemperatureRealtime() {
if (millis() - lastTempRead >= 2000) {
lastTempRead = millis();
ds18b20.requestTemperatures();
float t = ds18b20.getTempCByIndex(0);
if (t > 10 && t < 50) suhuCelcius = t + 1.5;
}
}
void sendDataToServer(int bpm, float temp) {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
String url =
"http://10.107.21.239/iot/save_data.php?action=insert"
"&bpm=" + String(bpm) +
"&spo2=" + String(finalSpO2) +
"&temperature=" + String(temp, 1) +
"&systolic=120"
"&diastolic=80";
Serial.println(url);
http.begin(url);
int httpCode = http.GET();
Serial.print("HTTP Code : ");
Serial.println(httpCode);
if (httpCode > 0) {
Serial.println(http.getString());
}
http.end();
delay(1500);
getPredictionFromServer();
}
void getPredictionFromServer() {
HTTPClient http;
http.setTimeout(10000);
http.begin("http://10.107.21.239/iot/save_data.php?action=latest");
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
Serial.println("===== RESPONSE SERVER =====");
Serial.println(payload);
JsonDocument doc;
deserializeJson(doc, payload);
systolic = doc["systolic"] | 120;
diastolic = doc["diastolic"] | 80;
Serial.print("Systolic dari server : ");
Serial.println(systolic);
Serial.print("Diastolic dari server : ");
Serial.println(diastolic);
showFinalResult();
}
http.end();
}