TKK_E32230859/arduino

320 lines
9.0 KiB
Plaintext

#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include <HTTPClient.h>
#include <ESP32Servo.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#define WIFI_SSID "Hidden Network"
#define WIFI_PASSWORD "tanyapakkost"
#define API_KEY "AIzaSyAHroHSc9Sp2lcZMf7SrCaV9cBjbjlxcfQ"
#define DATABASE_URL "https://smartinfuse-49274-default-rtdb.firebaseio.com/"
#define LEGACY_TOKEN "EQUbBn1dXMb2JQUW9UbMtpNKm2wP8VmcOi3tJDLu"
#define ROOM_NAME "ruang_kamboja"
#define DEVICE_ID "INF-ICU-127"
#define SENSOR_PIN 34
#define SERVO_PIN 21
#define BUZZER_PIN 18
#define BUTTON_PIN 19
#define LED_PIN 22
#define FB_BASE "/realtime/rooms/" ROOM_NAME "/devices/" DEVICE_ID
#define FB_CTRL FB_BASE "/controlling"
#define FB_MON FB_BASE "/monitoring"
#define SERVO_SWEEP_DELAY 12
// ==== KONFIGURASI KALIBRASI SENSOR TETES (BARU) ====
const int jumlahSampelBaca = 20; // averaging tiap pembacaan
const int jumlahSampelKalibrasi = 100; // jumlah sampel saat fase kalibrasi awal
const unsigned long debounceDelayTetes = 300; // ms, jarak minimum antar tetesan
float baseline = 0;
float noiseMax = 0;
int threshold = 0;
int stateSebelumnyaTetes = LOW;
unsigned long waktuTerakhirTetes = 0;
// ======================================================
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
Servo valveServo;
volatile int dropCount = 0;
volatile bool newDrop = false;
unsigned long lastDropTime = 0;
unsigned long windowStart = 0;
unsigned long lastFbCheck = 0;
unsigned long lastClickTime = 0;
unsigned long buttonPressTime = 0;
bool buzzerActive = false;
bool servoOpen = false;
int servoCurrentPos = 0;
int clickCount = 0;
bool fbReady() {
return WiFi.status() == WL_CONNECTED && Firebase.ready();
}
// ==== FUNGSI KALIBRASI & PEMBACAAN SENSOR TETES (BARU) ====
int bacaRataRata() {
long total = 0;
for (int i = 0; i < jumlahSampelBaca; i++) {
total += analogRead(SENSOR_PIN);
delay(1);
}
return total / jumlahSampelBaca;
}
void kalibrasiOtomatis() {
Serial.println("=== KALIBRASI OTOMATIS DIMULAI ===");
Serial.println("Pastikan TIDAK ADA TETESAN selama proses ini...");
delay(2000);
long total = 0;
int nilaiMin = 4095;
int nilaiMax = 0;
for (int i = 0; i < jumlahSampelKalibrasi; i++) {
int nilai = bacaRataRata();
total += nilai;
if (nilai < nilaiMin) nilaiMin = nilai;
if (nilai > nilaiMax) nilaiMax = nilai;
delay(50);
}
baseline = (float)total / jumlahSampelKalibrasi;
noiseMax = nilaiMax - baseline;
threshold = baseline + (noiseMax * 3) + 20;
Serial.println("=== KALIBRASI SELESAI ===");
Serial.print("Baseline: "); Serial.println(baseline);
Serial.print("Noise range (min-max saat kalibrasi): ");
Serial.print(nilaiMin); Serial.print(" - "); Serial.println(nilaiMax);
Serial.print("Threshold terpakai: "); Serial.println(threshold);
Serial.println("=== MULAI MODE DETEKSI TETESAN ===");
}
// Dipanggil tiap loop, menggantikan mekanisme interrupt lama.
// Mengisi dropCount & newDrop persis seperti sebelumnya, supaya
// bagian kode lain (Firebase, servo, dll) tidak perlu diubah.
void cekTetesan() {
int nilai = bacaRataRata();
int stateSekarang = (nilai > threshold) ? HIGH : LOW;
if (stateSekarang == HIGH && stateSebelumnyaTetes == LOW) {
if (millis() - waktuTerakhirTetes > debounceDelayTetes) {
dropCount++;
newDrop = true;
waktuTerakhirTetes = millis();
Serial.print("[TETES] ke-");
Serial.print(dropCount);
Serial.print(" | Nilai: ");
Serial.println(nilai);
}
}
stateSebelumnyaTetes = stateSekarang;
}
// ============================================================
void setupFirebase() {
config.api_key = API_KEY;
config.database_url = DATABASE_URL;
config.signer.tokens.legacy_token = LEGACY_TOKEN;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
}
void activateBuzzer() {
if (buzzerActive) return;
digitalWrite(BUZZER_PIN, HIGH);
buzzerActive = true;
}
void stopBuzzer() {
if (!buzzerActive) return;
digitalWrite(BUZZER_PIN, LOW);
buzzerActive = false;
}
void sweepServo(int from, int to) {
Serial.printf("[SWEEP] %d → %d\n", from, to);
int step = (to > from) ? 1 : -1;
for (int pos = from; pos != to; pos += step) {
valveServo.write(pos);
delay(SERVO_SWEEP_DELAY);
}
valveServo.write(to);
servoCurrentPos = to;
delay(200);
}
void openValve() {
if (servoOpen) return;
Serial.println("[SERVO] Membuka katup → 140°");
sweepServo(servoCurrentPos, 140);
servoOpen = true;
Serial.println("[SERVO] Katup terbuka");
}
void closeValve() {
if (!servoOpen) return;
Serial.println("[SERVO] Menutup katup → 0°");
sweepServo(servoCurrentPos, 0);
servoOpen = false;
Serial.println("[SERVO] Katup tertutup");
}
void kirimAPI(int dpm) {
if (!fbReady()) return;
HTTPClient http;
http.begin("https://smart-infus-service.vercel.app/api/kirimdata");
http.addHeader("Content-Type", "application/json");
String body = "{\"device_id\":\"" DEVICE_ID "\",\"drop_per_menit\":" + String(dpm) + "}";
int httpCode = http.POST(body);
Serial.printf("[API] HTTP %d\n", httpCode);
http.end();
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
delay(500);
Serial.printf("[BOOT] Reset reason: %d\n", esp_reset_reason());
pinMode(SENSOR_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
// NOTE: attachInterrupt lama untuk SENSOR_PIN dihapus,
// karena deteksi tetesan sekarang berbasis analogRead + threshold
// hasil kalibrasi otomatis (dipanggil lewat cekTetesan() di loop()).
valveServo.attach(SERVO_PIN, 500, 2400);
delay(200);
valveServo.write(0);
servoCurrentPos = 0;
delay(600);
servoOpen = false;
Serial.println("[SERVO] Init selesai → posisi 0°");
Serial.print("Connecting WiFi");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.printf("\nIP: %s\n", WiFi.localIP().toString().c_str());
setupFirebase();
Serial.print("Waiting Firebase");
while (!Firebase.ready()) { delay(300); Serial.print("."); }
Serial.println("\nFirebase ready");
Firebase.RTDB.setBool(&fbdo, FB_CTRL "/buzzer_active", false);
Firebase.RTDB.setBool(&fbdo, FB_CTRL "/servo_open", false);
digitalWrite(LED_PIN, HIGH);
windowStart = millis();
lastDropTime = millis();
lastFbCheck = millis();
// Kalibrasi otomatis sensor tetes (BARU) - pastikan tidak ada
// tetesan/aliran saat proses ini berjalan.
kalibrasiOtomatis();
Serial.println("READY");
}
void loop() {
// Polling sensor tetes (BARU) - menggantikan interrupt lama
cekTetesan();
if (newDrop) {
newDrop = false;
lastDropTime = millis();
}
if (millis() - windowStart >= 60000UL) {
portDISABLE_INTERRUPTS();
int dpm = dropCount;
dropCount = 0;
portENABLE_INTERRUPTS();
windowStart = millis();
Serial.printf("[MONITOR] drop/menit = %d\n", dpm);
if (fbReady()) {
Firebase.RTDB.setInt(&fbdo, FB_MON "/drop_rate", dpm);
kirimAPI(dpm);
if (dpm == 0) {
Firebase.RTDB.setBool(&fbdo, FB_CTRL "/servo_open", false);
Firebase.RTDB.setBool(&fbdo, FB_CTRL "/buzzer_active", true);
}
}
}
if (millis() - lastFbCheck >= 2000UL) {
lastFbCheck = millis();
if (fbReady()) {
if (Firebase.RTDB.getBool(&fbdo, FB_CTRL "/servo_open")) {
bool fb = fbdo.boolData();
Serial.printf("[FB] servo_open = %s | state = %s\n",
fb ? "true" : "false", servoOpen ? "OPEN" : "CLOSED");
if (fb && !servoOpen) openValve();
if (!fb && servoOpen) closeValve();
}
if (Firebase.RTDB.getBool(&fbdo, FB_CTRL "/buzzer_active")) {
bool fb = fbdo.boolData();
if (fb && !buzzerActive) activateBuzzer();
if (!fb && buzzerActive) stopBuzzer();
}
}
}
if (digitalRead(BUTTON_PIN) == LOW) {
buttonPressTime = millis();
delay(50);
while (digitalRead(BUTTON_PIN) == LOW);
unsigned long dur = millis() - buttonPressTime;
if (dur > 2000) {
Serial.println("[BTN] Hold → matiin buzzer");
if (fbReady()) Firebase.RTDB.setBool(&fbdo, FB_CTRL "/buzzer_active", false);
clickCount = 0;
} else {
if (clickCount < 2) {
clickCount++;
lastClickTime = millis();
Serial.printf("[BTN] Klik ke-%d\n", clickCount);
}
}
}
if (clickCount > 0 && millis() - lastClickTime > 500UL) {
if (fbReady()) {
if (clickCount == 1) {
Serial.println("[BTN] 1 klik → buka servo + matiin buzzer");
Firebase.RTDB.setBool(&fbdo, FB_CTRL "/servo_open", true);
Firebase.RTDB.setBool(&fbdo, FB_CTRL "/buzzer_active", false); // ← PERUBAHAN
}
if (clickCount == 2) {
Serial.println("[BTN] 2 klik → tutup servo");
Firebase.RTDB.setBool(&fbdo, FB_CTRL "/servo_open", false);
}
}
clickCount = 0;
}
delay(10);
}