Upload files to "/"
This commit is contained in:
commit
56479e1499
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue