216 lines
5.9 KiB
C++
216 lines
5.9 KiB
C++
#include <WiFi.h>
|
|
#include <Wire.h>
|
|
#include <ThingerESP32.h>
|
|
#include "MAX30105.h"
|
|
#include "heartRate.h"
|
|
#include <Adafruit_GFX.h>
|
|
#include <Adafruit_ST7789.h>
|
|
#include <SPI.h>
|
|
#define BUZZER_PIN 27
|
|
|
|
// --- WiFi & Thinger ---
|
|
#define SSID "AR ID"
|
|
#define PASSWORD "012345678"
|
|
#define USERNAME "amalia_lia"
|
|
#define DEVICE_ID "esp32-mood"
|
|
#define DEVICE_CREDENTIAL "esp32-mood"
|
|
|
|
ThingerESP32 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);
|
|
|
|
// --- MAX30102 ---
|
|
MAX30105 particleSensor;
|
|
const int SDA_PIN = 21;
|
|
const int SCL_PIN = 22;
|
|
const int GSR_PIN = 34;
|
|
|
|
// --- TFT ST7789 ---
|
|
#define TFT_CS 5
|
|
#define TFT_DC 2
|
|
#define TFT_RST 4
|
|
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
|
|
|
|
// --- Variabel Global ---
|
|
float beatAvg = 0;
|
|
float gsrLevel = 0;
|
|
String currentMood = "Unknown";
|
|
|
|
// --- Fungsi Mood ---
|
|
String determineMood(float bpm, float gsr) {
|
|
if (bpm == 0 || gsr == 0) return "Gagal";
|
|
String moodHR = (bpm <= 90) ? "Tenang" : (bpm <= 100 ? "Cemas" : "Tegang");
|
|
String moodGSR = (gsr <= 4.0) ? "Tenang" : (gsr <= 6.0 ? "Cemas" : "Tegang");
|
|
|
|
if (moodHR == "Tenang" && moodGSR == "Tenang") return "Tenang";
|
|
if (moodHR == "Cemas" && moodGSR == "Cemas") return "Cemas";
|
|
if (moodHR == "Tegang" || moodGSR == "Tegang") return "Tegang";
|
|
return "Cemas";
|
|
}
|
|
|
|
// --- TFT Gambar Wajah ---
|
|
void drawArc(int x, int y, int r1, int r2, int start_angle, int end_angle, uint16_t color) {
|
|
for (int angle = start_angle; angle <= end_angle; angle++) {
|
|
float rad = angle * PI / 180;
|
|
int x1 = x + cos(rad) * r1;
|
|
int y1 = y + sin(rad) * r1;
|
|
int x2 = x + cos(rad) * r2;
|
|
int y2 = y + sin(rad) * r2;
|
|
tft.drawLine(x1, y1, x2, y2, color);
|
|
}
|
|
}
|
|
|
|
void drawFace(String mood) {
|
|
uint16_t faceColor = ST77XX_WHITE;
|
|
if (mood == "Tenang") faceColor = ST77XX_YELLOW;
|
|
else if (mood == "Cemas") faceColor = 0xFD20;
|
|
else if (mood == "Tegang") faceColor = ST77XX_RED;
|
|
|
|
tft.fillScreen(ST77XX_BLACK);
|
|
tft.fillCircle(120, 90, 50, faceColor);
|
|
|
|
// Mata
|
|
if (mood == "Tegang") {
|
|
tft.fillCircle(100, 75, 8, ST77XX_BLACK);
|
|
tft.fillCircle(140, 75, 8, ST77XX_BLACK);
|
|
tft.fillCircle(85, 60, 4, ST77XX_BLUE);
|
|
tft.fillCircle(155, 60, 3, ST77XX_BLUE);
|
|
} else {
|
|
tft.fillCircle(100, 75, 5, ST77XX_BLACK);
|
|
tft.fillCircle(140, 75, 5, ST77XX_BLACK);
|
|
}
|
|
|
|
// Mulut
|
|
if (mood == "Tenang") {
|
|
drawArc(120, 105, 18, 21, 45, 135, ST77XX_BLACK);
|
|
} else if (mood == "Cemas") {
|
|
tft.fillRect(105, 110, 30, 4, ST77XX_BLACK);
|
|
} else if (mood == "Tegang") {
|
|
drawArc(120, 120, 18, 21, 225, 315, ST77XX_BLACK);
|
|
}
|
|
|
|
tft.setTextColor(ST77XX_WHITE);
|
|
tft.setTextSize(2);
|
|
tft.setCursor(10, 160); tft.print("HR : "); tft.print(beatAvg, 1); tft.println(" BPM");
|
|
tft.setCursor(10, 185); tft.print("GSR : "); tft.print(gsrLevel, 2);
|
|
tft.setCursor(10, 210); tft.print("Mood : "); tft.println(mood);
|
|
}
|
|
|
|
// --- SETUP ---
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
WiFi.begin(SSID, PASSWORD);
|
|
Wire.begin(SDA_PIN, SCL_PIN);
|
|
pinMode(GSR_PIN, INPUT);
|
|
pinMode(BUZZER_PIN, OUTPUT);
|
|
digitalWrite(BUZZER_PIN, LOW); // Awalnya mati
|
|
|
|
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
|
|
Serial.println("[ERROR] MAX30102 tidak terdeteksi!");
|
|
while (1);
|
|
}
|
|
|
|
particleSensor.setup(0x1F, 4, 2, 100, 411, 4096);
|
|
SPI.begin(18, -1, 23);
|
|
tft.init(240, 240); tft.setRotation(1); tft.fillScreen(ST77XX_BLACK);
|
|
|
|
thing.add_wifi(SSID, PASSWORD);
|
|
thing["mood"] >> [](pson &out) {
|
|
out["bpm"] = beatAvg;
|
|
out["gsr"] = gsrLevel;
|
|
out["mood"] = currentMood;
|
|
};
|
|
|
|
Serial.println("[READY] Silakan sentuhkan jari ke sensor.");
|
|
}
|
|
|
|
// --- LOOP ---
|
|
void loop() {
|
|
Serial.println("=== Mulai Pengukuran (10 detik) ===");
|
|
|
|
unsigned long startTime = millis();
|
|
unsigned long lastBeat = 0;
|
|
float bpmSum = 0;
|
|
int validBeats = 0;
|
|
unsigned long gsrSum = 0;
|
|
int gsrSamples = 0;
|
|
|
|
while (millis() - startTime < 10000) {
|
|
particleSensor.check();
|
|
long irValue = particleSensor.getIR();
|
|
int gsrValue = analogRead(GSR_PIN);
|
|
|
|
gsrSum += gsrValue;
|
|
gsrSamples++;
|
|
|
|
if (checkForBeat(irValue)) {
|
|
unsigned long now = millis();
|
|
if (lastBeat > 0) {
|
|
float delta = (now - lastBeat) / 1000.0;
|
|
float bpm = 60.0 / delta;
|
|
if (bpm >= 40 && bpm <= 180) {
|
|
bpmSum += bpm;
|
|
validBeats++;
|
|
Serial.print("[BEAT] BPM valid: ");
|
|
Serial.println(bpm, 1);
|
|
}
|
|
}
|
|
lastBeat = now;
|
|
}
|
|
|
|
delay(10);
|
|
}
|
|
|
|
// Hitung rata-rata
|
|
beatAvg = (validBeats > 0) ? bpmSum / validBeats : 0;
|
|
float avgGSR = gsrSum / gsrSamples;
|
|
bool isFingerDetected = (avgGSR > 200);
|
|
|
|
if (!isFingerDetected || beatAvg == 0) {
|
|
beatAvg = 0;
|
|
gsrLevel = 0;
|
|
currentMood = "Gagal";
|
|
|
|
tft.fillScreen(ST77XX_BLACK);
|
|
tft.setTextColor(ST77XX_WHITE);
|
|
tft.setTextSize(2);
|
|
tft.setCursor(20, 100);
|
|
tft.println("Tempel jari Anda");
|
|
tft.setCursor(20, 130);
|
|
tft.println("untuk mulai");
|
|
} else {
|
|
gsrLevel = map(avgGSR, 300, 2500, 100, 700) / 100.0;
|
|
gsrLevel = constrain(gsrLevel, 1.0, 7.0);
|
|
currentMood = determineMood(beatAvg, gsrLevel);
|
|
drawFace(currentMood);
|
|
if (currentMood == "Tegang") {
|
|
// Buzzer bunyi panjang (beepppppppppp)
|
|
digitalWrite(BUZZER_PIN, HIGH);
|
|
delay(2000); // 2 detik
|
|
digitalWrite(BUZZER_PIN, LOW);
|
|
} else if (currentMood == "Cemas") {
|
|
// Buzzer bunyi pendek berulang (beep beep beep)
|
|
for (int i = 0; i < 3; i++) {
|
|
digitalWrite(BUZZER_PIN, HIGH);
|
|
delay(300); // 0.3 detik bunyi
|
|
digitalWrite(BUZZER_PIN, LOW);
|
|
delay(300); // 0.3 detik jeda
|
|
}
|
|
}
|
|
}
|
|
|
|
// Kirim ke Thinger.io
|
|
thing.write_bucket("Emotion_data", "mood", [](pson &data) {
|
|
data["bpm"] = beatAvg;
|
|
data["gsr"] = gsrLevel;
|
|
data["mood"] = currentMood;
|
|
});
|
|
|
|
thing.handle();
|
|
|
|
Serial.print("BPM: "); Serial.println(beatAvg, 1);
|
|
Serial.print("GSR: "); Serial.println(gsrLevel, 2);
|
|
Serial.print("Mood: "); Serial.println(currentMood);
|
|
Serial.println("=========================");
|
|
|
|
delay(1000);
|
|
}
|