TKK_E32230211/sketch_apr14a_copy_20260414...

589 lines
10 KiB
C++

#include <WiFi.h>
#include <WiFiManager.h>
#include <Firebase_ESP_Client.h>
#include <ESP32Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
// ================= FIREBASE =================
#define API_KEY "AIzaSyDOt0tippHwz6JaPG9Zo147ATJ9USV5bKs"
#define DATABASE_URL "https://tabungankuy-30a9d-default-rtdb.firebaseio.com/"
#define LEGACY_TOKEN "FTqzKI4HW32paLnR9pQXB72YoUHN1NXEjZMfjO4f"
// ================= TCS3200 =================
#define S0 14
#define S1 27
#define S2 26
#define S3 25
#define SENSOR_OUT 33
// ================= HARDWARE =================
#define SERVO_MASUK_PIN 4
#define SERVO_TURUN_PIN 5
#define SERVO_BUKA_PIN 15
// ================= OBJECT =================
LiquidCrystal_I2C lcd(0x27, 16, 2);
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
Servo servoMasuk;
Servo servoTurun;
Servo servoBuka;
// ================= STATE =================
int lastDetectedVal = 0;
bool lastKonfirmasi = false;
bool lastBukaTabungan = false;
bool lastServoMasuk = false;
bool servoAttached = false;
// =====================================================
// LCD
// =====================================================
void lcdDefault() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" TABUNGANKU ");
lcd.setCursor(0, 1);
lcd.print(" SIAP DIGUNAKAN ");
}
void lcdUangTerdeteksi(int nilai) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("UANG TERDETEKSI");
lcd.setCursor(0, 1);
String text = "Rp " + String(nilai);
while (text.length() < 16)
text += " ";
lcd.print(text);
}
void lcdUangMasuk(int nilai) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" UANG MASUK ");
lcd.setCursor(0, 1);
String text = "Rp " + String(nilai);
while (text.length() < 16)
text += " ";
lcd.print(text);
}
void lcdTabunganTerbuka() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" TABUNGAN ");
lcd.setCursor(0, 1);
lcd.print(" TERBUKA ");
}
void updateLCD() {
if (lastBukaTabungan) {
lcdTabunganTerbuka();
} else if (lastKonfirmasi && lastDetectedVal > 0) {
lcdUangMasuk(lastDetectedVal);
} else if (lastDetectedVal > 0) {
lcdUangTerdeteksi(lastDetectedVal);
} else {
lcdDefault();
}
}
// =====================================================
// WIFI
// =====================================================
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFiManager wm;
wm.setTimeout(180); // 3 menit untuk setup WiFi
bool res = wm.autoConnect("TABUNGANKU_SETUP");
if (!res) {
Serial.println("Gagal konek WiFi");
ESP.restart();
}
Serial.println("WiFi Connected!");
Serial.print("IP : ");
Serial.println(WiFi.localIP());
}
// =====================================================
// FIREBASE
// =====================================================
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);
Serial.println("Firebase Connected");
}
// =====================================================
// TCS3200
// =====================================================
void setupTCS3200() {
pinMode(S0, OUTPUT);
pinMode(S1, OUTPUT);
pinMode(S2, OUTPUT);
pinMode(S3, OUTPUT);
pinMode(SENSOR_OUT, INPUT);
digitalWrite(S0, HIGH);
digitalWrite(S1, LOW);
Serial.println("Sensor TCS3200 Ready");
}
int avgFreq(int s2Val, int s3Val) {
long total = 0;
for (int i = 0; i < 3; i++) {
digitalWrite(S2, s2Val);
digitalWrite(S3, s3Val);
delayMicroseconds(300);
total += pulseIn(SENSOR_OUT, LOW);
}
return total / 3;
}
// =====================================================
// DETEKSI UANG
// =====================================================
void cekUangKertas() {
static unsigned long lastCheck = 0;
static unsigned long lastDetectedTime = 0;
static int lastDetectedValue = 0;
if (millis() - lastCheck < 300)
return;
lastCheck = millis();
if (lastKonfirmasi)
return;
int r = avgFreq(LOW, LOW);
int g = avgFreq(HIGH, HIGH);
int b = avgFreq(LOW, HIGH);
Serial.print("R: ");
Serial.print(r);
Serial.print(" | G: ");
Serial.print(g);
Serial.print(" | B: ");
Serial.println(b);
int val = 0;
if (r>=110&&r<=120 && g>=140&&g<=150 && b>=124&&b<=134) val = 100000;
else if (r>=175&&r<=185 && g>=160&&g<=170 && b>=150&&b<=160) val = 50000;
else if (r>=138&&r<=148 && g>=125&&g<=135 && b>=105&&b<=115) val = 20000;
else if (r>=145&&r<=155 && g>=180&&g<=190 && b>=150&&b<=160) val = 10000;
else if (r>=115&&r<=125 && g>=169&&g<=179 && b>=140&&b<=150) val = 5000;
else if (r>=145&&r<=155 && g>=145&&g<=155 && b>=120&&b<=130) val = 2000;
else if (r>=129&&r<=139 && g>=125&&g<=135 && b>=120&&b<=130) val = 1000;
if (val > 0 &&
(millis() - lastDetectedTime > 1500 ||
val != lastDetectedValue)) {
Serial.println("====================");
Serial.print("Uang : Rp ");
Serial.println(val);
Serial.println("====================");
Firebase.RTDB.setInt(
&fbdo,
"/tabungan/detected_value",
val
);
lastDetectedTime = millis();
lastDetectedValue = val;
lastDetectedVal = val;
updateLCD();
}
}
// =====================================================
// SERVO HELPER
// =====================================================
void attachServo() {
if (!servoAttached) {
servoMasuk.attach(SERVO_MASUK_PIN);
servoTurun.attach(SERVO_TURUN_PIN);
servoBuka.attach(SERVO_BUKA_PIN);
servoAttached = true;
delay(300);
servoMasuk.write(0);
servoTurun.write(0);
servoBuka.write(0);
}
}
// =====================================================
// FIREBASE LISTENER
// =====================================================
void cekKonfirmasiFlutter() {
if (!Firebase.RTDB.getBool(
&fbdo,
"/tabungan/konfirmasi_masuk"))
return;
bool konfirmasi = fbdo.boolData();
if (konfirmasi == lastKonfirmasi)
return;
lastKonfirmasi = konfirmasi;
Serial.print("Konfirmasi: ");
Serial.println(konfirmasi);
attachServo();
if (konfirmasi) {
servoTurun.write(90);
Serial.println("Servo Turun Aktif");
} else {
servoTurun.write(0);
Serial.println("Servo Turun Mati");
// reset nominal ketika tombol batalkan ditekan
lastDetectedVal = 0;
Firebase.RTDB.setInt(
&fbdo,
"/tabungan/detected_value",
0
);
Serial.println("Detected Reset");
// kembali ke tampilan awal
lcdDefault();
return;
}
updateLCD();
}
void cekServoMasuk() {
if (!Firebase.RTDB.getBool(
&fbdo,
"/tabungan/masukkan_uang"))
return;
bool buka = fbdo.boolData();
if (buka == lastServoMasuk)
return;
lastServoMasuk = buka;
attachServo();
servoMasuk.write(buka ? 150 : 0);
Serial.print("Servo Masuk: ");
Serial.println(
buka ? "BUKA" : "TUTUP"
);
}
void cekServoBukaTabungan() {
static bool sedangBuka = false;
static unsigned long waktuBuka = 0;
static int lastSisa = -1;
if (!Firebase.RTDB.getBool(
&fbdo,
"/tabungan/buka_tabungan"))
return;
bool buka = fbdo.boolData();
// buka servo sekali saat menerima TRUE
if (buka && !sedangBuka) {
attachServo();
servoBuka.write(150);
waktuBuka = millis();
sedangBuka = true;
lastBukaTabungan = true;
Serial.println("Servo Tabungan: TERBUKA");
}
// countdown 25 detik
if (sedangBuka) {
int sisa = 25 - ((millis() - waktuBuka) / 1000);
if (sisa < 0) sisa = 0;
if (sisa != lastSisa) {
lastSisa = sisa;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pintu Terbuka");
lcd.setCursor(0, 1);
lcd.print("Sisa:");
lcd.print(sisa);
lcd.print(" dtk");
}
// tutup otomatis setelah 25 detik
if (millis() - waktuBuka >= 25000) {
servoBuka.write(0);
sedangBuka = false;
lastBukaTabungan = false;
Firebase.RTDB.setBool(
&fbdo,
"/tabungan/buka_tabungan",
false
);
lcdDefault();
Serial.println("Servo Tabungan: TERTUTUP");
}
}
}
void cekDetectedValueFirebase() {
static unsigned long lastSync = 0;
if (millis() - lastSync < 1000)
return;
lastSync = millis();
if (!Firebase.RTDB.getInt(
&fbdo,
"/tabungan/detected_value"))
return;
int val = fbdo.intData();
// jika nilai direset dari Flutter
if (val == 0) {
lastDetectedVal = 0;
lcdDefault();
return;
}
if (lastKonfirmasi)
return;
if (val == lastDetectedVal)
return;
lastDetectedVal = val;
Serial.print("Sync Firebase: ");
Serial.println(val);
updateLCD();
}
// =====================================================
// SETUP
// =====================================================
void setup() {
// disable brownout sementara
WRITE_PERI_REG(
RTC_CNTL_BROWN_OUT_REG,
0
);
Serial.begin(115200);
Serial.println();
Serial.println("System Booting...");
Wire.begin();
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" TABUNGANKU ");
lcd.setCursor(0, 1);
lcd.print(" BOOTING... ");
setupTCS3200();
// JANGAN attach servo di awal
// supaya tidak brownout
connectWiFi();
setupFirebase();
// reset firebase
Firebase.RTDB.setInt(
&fbdo,
"/tabungan/detected_value",
0
);
Firebase.RTDB.setBool(
&fbdo,
"/tabungan/konfirmasi_masuk",
false
);
Firebase.RTDB.setBool(
&fbdo,
"/tabungan/masukkan_uang",
false
);
Firebase.RTDB.setBool(
&fbdo,
"/tabungan/buka_tabungan",
false
);
Serial.println("Firebase Reset");
lcdDefault();
Serial.println("System Ready");
}
// =====================================================
// LOOP
// =====================================================
void loop() {
static unsigned long lastWiFiCheck = 0;
if (millis() - lastWiFiCheck > 10000) {
lastWiFiCheck = millis();
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
}
cekUangKertas();
cekKonfirmasiFlutter();
cekServoMasuk();
cekServoBukaTabungan();
cekDetectedValueFirebase();
delay(200);
}