589 lines
10 KiB
C++
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);
|
|
} |