Upload files to "/"

This commit is contained in:
ridhofaisalh 2025-08-15 13:41:16 +07:00
commit 56479e1499
1 changed files with 390 additions and 0 deletions

390
TomatWM.ino Normal file
View File

@ -0,0 +1,390 @@
#include <ArduinoJson.h>
#include <ESP32Servo.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <FirebaseESP32.h>
#include <Wifi.h>
#include <WiFiManager.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <time.h>
const char* API_KEY = "AIzaSyDPRK9A-5inKhas80-d3FJ-c7p9u2Ud51Q";
const char* PROJECT_ID = "database-alat-sortasi-tomat";
#define FIREBASE_HOST "https://database-alat-sortasi-tomat-default-rtdb.firebaseio.com/"
#define FIREBASE_AUTH "55Yl7pfHLt4cMC1VR2qWGCfl91gFYQwWZlMGhotv"
FirebaseData firebaseData;
FirebaseConfig firebaseConfig;
FirebaseAuth firebaseAuth;
String userEmail = "tomat@gmail.com";
String userPassword = "tomat333";
String idToken = "";
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo servoMatang;
Servo servoMentah;
#define SERVO_MATANG_PIN 18
#define SERVO_MENTAH_PIN 19
#define S0 14
#define S1 25
#define S2 26
#define S3 27
#define sensorOut 34
#define RELAY_PIN 23
bool signInWithEmailAndPassword() {
HTTPClient http;
WiFiClientSecure *client = new WiFiClientSecure;
client->setInsecure();
String url = "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=" + String(API_KEY);
String body = "{\"email\":\"" + userEmail + "\",\"password\":\"" + userPassword + "\",\"returnSecureToken\":true}";
http.begin(*client, url);
http.addHeader("Content-Type", "application/json");
int httpCode = http.POST(body);
if (httpCode == 200) {
String payload = http.getString();
// Gunakan ArduinoJson untuk parsing aman
DynamicJsonDocument doc(2048);
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print("Gagal parse JSON login: ");
Serial.println(error.f_str());
return false;
}
idToken = doc["idToken"].as<String>();
Serial.println("Firebase Sign-in success");
Serial.println("Token: " + idToken);
return true;
} else {
Serial.println("Sign-in failed");
Serial.println(http.getString());
return false;
}
}
struct TomatoColor {
String kategori;
int red;
int green;
int blue;
};
int bacaWarna(bool s2, bool s3) {
digitalWrite(S2, s2);
digitalWrite(S3, s3);
delay(100);
return pulseIn(sensorOut, LOW);
}
void nilaiRGB(int &red, int &green, int &blue) {
red = bacaWarna(LOW, LOW);
green = bacaWarna(HIGH, HIGH);
blue = bacaWarna(LOW, HIGH);
}
TomatoColor tomatKalibrasi[] = {
{"Matang", 146, 274, 250},
{"Setengah Matang", 119, 198, 209},
{"Mentah", 140, 139, 173}
};
const int jumlahKategori = sizeof(tomatKalibrasi) / sizeof(tomatKalibrasi[0]);
int jumlahMatang = 0;
int jumlahMentah = 0;
int jumlahSetengahMatang = 0;
void setup() {
Serial.begin(115200);
WiFiManager wm;
if (!wm.autoConnect("ESP32_Config_AP")) {
Serial.println("Gagal konek ke WiFi dan tidak ada konfigurasi baru");
delay(3000);
ESP.restart();
}
Serial.println("Terhubung ke WiFi");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (!signInWithEmailAndPassword()) {
Serial.println("Login gagal saat setup. Firestore tidak akan aktif.");
}
firebaseConfig.database_url = FIREBASE_HOST;
firebaseConfig.signer.tokens.legacy_token = FIREBASE_AUTH;
Firebase.begin(&firebaseConfig, &firebaseAuth);
Firebase.reconnectWiFi(true);
servoMatang.attach(SERVO_MATANG_PIN);
servoMentah.attach(SERVO_MENTAH_PIN);
pinMode(RELAY_PIN,OUTPUT);
digitalWrite(RELAY_PIN, HIGH);
pinMode(S0, OUTPUT);
pinMode(S1, OUTPUT);
pinMode(S2, OUTPUT);
pinMode(S3, OUTPUT);
pinMode(sensorOut, INPUT);
digitalWrite(S0, HIGH);
digitalWrite(S1, LOW);
Serial.println("=== Deteksi Kematangan Tomat ===");
lcd.init();
lcd.backlight();
showLoading();
lcd.setCursor(0, 0);
lcd.print("Mulai Deteksi");
delay(1000);
configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov");
Serial.print("Menunggu sinkronisasi waktu NTP");
struct tm timeinfo;
int retry = 0;
const int maxRetry = 20;
while (!getLocalTime(&timeinfo) && retry < maxRetry) {
Serial.print(".");
delay(500);
retry++;
}
if (retry == maxRetry) {
Serial.println("\nGagal mendapatkan waktu dari NTP");
} else {
Serial.println("\nWaktu berhasil disinkronkan");
}
}
String getWaktuSekarang() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return "unknown_time";
}
char buffer[32];
strftime(buffer, sizeof(buffer), "%Y-%m-%d_%H:%M:%S", &timeinfo);
return String(buffer);
}
String deteksiTomat(int red, int green, int blue) {
float jarakTerdekat = 100000.0;
float threshold = 50.0;
String hasil = "Tidak Terdeteksi";
for (int i = 0; i < jumlahKategori; i++) {
float d = sqrt(
pow(red - tomatKalibrasi[i].red, 2) +
pow(green - tomatKalibrasi[i].green, 2) +
pow(blue - tomatKalibrasi[i].blue, 2)
);
if (d < jarakTerdekat && d < threshold) {
jarakTerdekat = d;
hasil = tomatKalibrasi[i].kategori;
}
}
return hasil;
}
void showLoading() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Inisialisasi");
lcd.setCursor(0, 1);
lcd.print("[ ]");
for (int i = 0; i <= 15; i++) {
lcd.setCursor(1 + i, 1);
lcd.print("=");
delay(100);
}
lcd.setCursor(0, 0);
lcd.print("Siap Sortasi ");
lcd.setCursor(0, 1);
lcd.print("");
delay(1000);
}
void aktifkanServo(String statusTomat) {
if (statusTomat == "Matang") {
servoMatang.write(80);
delay(2000);
servoMatang.write(0);
} else if (statusTomat == "Mentah") {
servoMentah.write(80);
delay(2000);
servoMentah.write(0);
}else if (statusTomat == "Setengah Matang") {
servoMentah.write(0);
servoMatang.write(0);
delay(2000);
servoMentah.write(0);
servoMatang.write(0);
} else {
Serial.println("Warna tidak dikenali. Servo tidak bergerak.");
}
}
void onoffRelay(String statusTomat) {
if (statusTomat == "Matang" || statusTomat == "Setengah Matang" || statusTomat == "Mentah") {
digitalWrite(RELAY_PIN, HIGH); // Nyalakan relay
} else if (statusTomat == "Tidak Terdeteksi") {
digitalWrite(RELAY_PIN, LOW); // Matikan relay
}
}
void kirimDataKeFirebase(String status) {
String waktu = getWaktuSekarang();
int red, green, blue;
nilaiRGB(red, green, blue);
if (Firebase.setString(firebaseData, "tomat/status", status) &&
Firebase.setString(firebaseData, "tomat/waktu", waktu) &&
Firebase.setInt(firebaseData, "tomat/red", red) &&
Firebase.setInt(firebaseData, "tomat/green", green) &&
Firebase.setInt(firebaseData, "tomat/blue", blue) &&
Firebase.setInt(firebaseData, "tomat/jumlahMatang", jumlahMatang) &&
Firebase.setInt(firebaseData, "tomat/jumlahMentah", jumlahMentah) &&
Firebase.setInt(firebaseData, "tomat/jumlahSetengahMatang", jumlahSetengahMatang)) {
Serial.println("Data berhasil dikirim ke Firebase");
} else {
Serial.print("Gagal mengirim data: ");
Serial.println(firebaseData.errorReason());
}
kirimKeFirestore(status, waktu, red, green, blue);
}
void kirimKeFirestore(String status, String waktu, int red, int green, int blue) {
static bool loginPernahGagal = false; // hanya retry login satu kali jika gagal
if (idToken == "") {
Serial.println("Token kosong, tidak kirim ke Firestore.");
return;
}
nilaiRGB(red, green, blue);
String waktuDoc = getWaktuSekarang();
String docName = "data_" + waktuDoc;
String url = "https://firestore.googleapis.com/v1/projects/" + String(PROJECT_ID) +
"/databases/(default)/documents/tomat/" + docName + "?key=" + String(API_KEY);
HTTPClient http;
WiFiClientSecure client;
client.setInsecure();
http.begin(client, url);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + idToken);
if (status == "Matang" || status == "Mentah" || status == "Setengah Matang") {
String payload = "{"
"\"fields\": {"
"\"status\": {\"stringValue\": \"" + status + "\"},"
"\"waktu\": {\"stringValue\": \"" + waktu + "\"},"
"\"nilai_red\": {\"integerValue\": \"" + red + "\" },"
"\"nilai_green\": {\"integerValue\": \"" + green + "\" },"
"\"nilai_blue\": {\"integerValue\": \"" + blue + "\" },"
"\"jumlahMatang\": {\"integerValue\": \"" + String(jumlahMatang) + "\"},"
"\"jumlahMentah\": {\"integerValue\": \"" + String(jumlahMentah) + "\"},"
"\"jumlahSetengahMatang\": {\"integerValue\": \"" + String(jumlahSetengahMatang) + "\"}"
"}"
"}";
int httpCode = http.PATCH(payload);
Serial.printf("Firestore PATCH code: %d\n", httpCode);
if ((httpCode == 401 || httpCode == 403) && !loginPernahGagal) {
Serial.println("Token expired / invalid. Coba login ulang...");
loginPernahGagal = true;
if (signInWithEmailAndPassword()) {
// Coba ulang request 1x setelah login ulang
http.end(); // harus end dulu
http.begin(client, url);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + idToken);
httpCode = http.POST(payload);
Serial.printf("Retry Firestore POST code: %d\n", httpCode);
} else {
Serial.println("Login ulang gagal.");
}
}
if (httpCode > 0) {
Serial.println("Firestore response:");
Serial.println(http.getString());
} else {
Serial.print("Gagal kirim ke Firestore: ");
Serial.println(http.errorToString(httpCode));
}
http.end();
} else {
Serial.println("Status = Tidak Terdeteksi → Data tidak dikirim ke Firebase.");
}
}
void loop() {
int red, green, blue;
nilaiRGB(red, green, blue);
String statusTomat = deteksiTomat(red, green, blue);
String waktuDeteksi = getWaktuSekarang();
if (statusTomat == "Matang") {
jumlahMatang++;
} else if (statusTomat == "Mentah") {
jumlahMentah++;
} else if (statusTomat == "Setengah Matang") {
jumlahSetengahMatang++;
}
Serial.print("Red: ");
Serial.print(red);
Serial.print("\tGreen: ");
Serial.print(green);
Serial.print("\tBlue: ");
Serial.print(blue);
Serial.print("\t-> Status Tomat: ");
Serial.println(statusTomat);
Serial.print("\Jumlah Matang: ");
Serial.println(jumlahMatang);
Serial.print("\Jumlah Mentah: ");
Serial.println(jumlahMentah);
Serial.print("\Jumlah Setengah Matang: ");
Serial.println(jumlahSetengahMatang);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Status Tomat:");
lcd.setCursor(0, 1);
lcd.print(statusTomat);
onoffRelay(statusTomat);
aktifkanServo(statusTomat);
kirimDataKeFirebase(statusTomat);
delay(1000);
}