456 lines
13 KiB
C++
456 lines
13 KiB
C++
#include <WiFi.h>
|
|
#include <HTTPClient.h>
|
|
#include <SPI.h>
|
|
#include <Wire.h>
|
|
#include <MFRC522.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <ArduinoJson.h>
|
|
|
|
#define DEBUG_MODE true
|
|
|
|
const char* ssid = "KOPI";
|
|
const char* password = "digoreng123";
|
|
const String API_BASE_URL = "https://smartlocker.punyapadias.my.id/api/";
|
|
|
|
#define SS_PIN 21
|
|
#define RST_PIN 5
|
|
MFRC522 mfrc522(SS_PIN, RST_PIN);
|
|
|
|
LiquidCrystal_I2C lcd(0x27, 16, 2);
|
|
|
|
const int trigPins[3] = {17, 16, 33};
|
|
const int echoPins[3] = {35, 34, 32};
|
|
|
|
#define RELAY1_PIN 14
|
|
#define RELAY2_PIN 27
|
|
#define RELAY3_PIN 26
|
|
|
|
#define BUZZER_PIN 13
|
|
|
|
enum SystemState {
|
|
STATE_IDLE,
|
|
STATE_RFID_DETECTED,
|
|
STATE_OPENING_LOCKER,
|
|
STATE_REGISTERING_RFID,
|
|
STATE_ERROR
|
|
};
|
|
SystemState currentState = STATE_IDLE;
|
|
|
|
unsigned long lastApiCallTime = 0;
|
|
const long apiCallInterval = 5000;
|
|
unsigned long rfidActionStartTime = 0;
|
|
const long rfidActionDuration = 10000;
|
|
bool lcdIdleShown = false;
|
|
|
|
|
|
struct RFIDMapping {
|
|
String uid;
|
|
int lokerId;
|
|
int relayPin;
|
|
};
|
|
|
|
RFIDMapping rfidMappings[] = {
|
|
{"36CB3503", 1, RELAY1_PIN},
|
|
{"5C312903", 2, RELAY2_PIN},
|
|
{"611B2803", 3, RELAY3_PIN},
|
|
};
|
|
const int jumlahRFID = sizeof(rfidMappings) / sizeof(rfidMappings[0]);
|
|
|
|
void setRFIDtoLoker(int lokerId, String rfid);
|
|
void updateStatusLoker(int lokerId, String status);
|
|
int getFirstEmptyRFIDLokerId();
|
|
String getStatusLoker(int lokerId);
|
|
void clearRFID();
|
|
void playBuzzer(int count, int durationMs, int pauseMs);
|
|
void displayLcdMessage(String line1, String line2, int duration = 0);
|
|
void handleRFIDAction(String uidStr, int mappedLokerId, int mappedRelayPin);
|
|
void handleNewRFIDRegistration(String uidStr);
|
|
|
|
void displayDefaultScreen() {
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Loker Pintar");
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("Scan Kartu");
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
Wire.begin(4, 22);
|
|
lcd.init();
|
|
lcd.backlight();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Inisialisasi...");
|
|
|
|
// Menyambung ke WiFi
|
|
WiFi.begin(ssid, password);
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("WiFi connecting...");
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
delay(500);
|
|
Serial.print(".");
|
|
}
|
|
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("WiFi connected ");
|
|
Serial.println("\nWiFi Connected. IP: " + WiFi.localIP().toString());
|
|
|
|
// Inisialisasi RFID
|
|
SPI.begin();
|
|
mfrc522.PCD_Init();
|
|
|
|
// Inisialisasi sensor ultrasonik
|
|
for (int i = 0; i < 3; i++) {
|
|
pinMode(trigPins[i], OUTPUT);
|
|
pinMode(echoPins[i], INPUT);
|
|
}
|
|
|
|
// Inisialisasi relay dan buzzer
|
|
pinMode(RELAY1_PIN, OUTPUT); digitalWrite(RELAY1_PIN, HIGH);
|
|
pinMode(RELAY2_PIN, OUTPUT); digitalWrite(RELAY2_PIN, HIGH);
|
|
pinMode(RELAY3_PIN, OUTPUT); digitalWrite(RELAY3_PIN, HIGH);
|
|
|
|
pinMode(BUZZER_PIN, OUTPUT);
|
|
digitalWrite(BUZZER_PIN, LOW);
|
|
|
|
// Tampilkan layar utama
|
|
lcd.clear();
|
|
displayDefaultScreen();
|
|
|
|
lastApiCallTime = millis();
|
|
}
|
|
|
|
void loop() {
|
|
// Debug: tampilkan state saat ini setiap 1 detik
|
|
static unsigned long lastDebugTime = 0;
|
|
if (millis() - lastDebugTime > 1000) {
|
|
Serial.println("[DEBUG] Current state: " + String(currentState));
|
|
lastDebugTime = millis();
|
|
}
|
|
|
|
// Recovery: jika RFID tidak terbaca dalam waktu lama, inisialisasi ulang
|
|
static unsigned long lastRFIDScanTime = 0;
|
|
if (millis() - lastRFIDScanTime > 10000) {
|
|
Serial.println("[RFID] Timeout, re-initialize RFID.");
|
|
mfrc522.PCD_Init();
|
|
lastRFIDScanTime = millis();
|
|
}
|
|
|
|
switch (currentState) {
|
|
case STATE_IDLE:
|
|
// Tampilkan LCD hanya sekali saat idle
|
|
if (!lcdIdleShown) {
|
|
displayDefaultScreen();
|
|
lcdIdleShown = true;
|
|
}
|
|
|
|
// Cek kartu RFID
|
|
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
|
|
lcdIdleShown = false; // agar layar bisa refresh nanti
|
|
playBuzzer(1, 50, 0);
|
|
lastRFIDScanTime = millis(); // reset timeout counter
|
|
|
|
// Ambil UID
|
|
String uidStr = "";
|
|
for (byte i = 0; i < mfrc522.uid.size; i++) {
|
|
if (mfrc522.uid.uidByte[i] < 0x10) uidStr += "0";
|
|
uidStr += String(mfrc522.uid.uidByte[i], HEX);
|
|
}
|
|
uidStr.toUpperCase();
|
|
|
|
Serial.println("[RFID] UID: " + uidStr);
|
|
displayLcdMessage("RFID Terbaca:", uidStr);
|
|
|
|
// Cek UID pada daftar
|
|
bool found = false;
|
|
int lokerId = -1;
|
|
int relayPin = -1;
|
|
for (int i = 0; i < jumlahRFID; i++) {
|
|
if (uidStr == rfidMappings[i].uid) {
|
|
found = true;
|
|
lokerId = rfidMappings[i].lokerId;
|
|
relayPin = rfidMappings[i].relayPin;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
currentState = STATE_OPENING_LOCKER;
|
|
handleRFIDAction(uidStr, lokerId, relayPin);
|
|
} else {
|
|
// Jika tidak ditemukan, aktifkan buzzer "tit-tit-tit" dan tampilkan pesan
|
|
playBuzzer(3, 100, 100); // Bunyi 3 kali dengan interval 100ms
|
|
displayLcdMessage("Kartu Tidak", "Terdaftar!", 2000);
|
|
currentState = STATE_REGISTERING_RFID;
|
|
handleNewRFIDRegistration(uidStr);
|
|
}
|
|
|
|
clearRFID();
|
|
delay(200); // beri waktu recovery RFID
|
|
}
|
|
break;
|
|
|
|
case STATE_REGISTERING_RFID:
|
|
case STATE_OPENING_LOCKER:
|
|
lcdIdleShown = false;
|
|
currentState = STATE_IDLE;
|
|
break;
|
|
|
|
case STATE_ERROR:
|
|
if (WiFi.status() != WL_CONNECTED) {
|
|
displayLcdMessage("WiFi Disconnect!", "Reconnecting...");
|
|
WiFi.reconnect();
|
|
delay(1000);
|
|
} else {
|
|
lcdIdleShown = false;
|
|
currentState = STATE_IDLE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void handleRFIDAction(String uidStr, int mappedLokerId, int mappedRelayPin) {
|
|
if (DEBUG_MODE) Serial.println("[RFID Action] Membuka Loker ID: " + String(mappedLokerId) + " dengan UID: " + uidStr);
|
|
|
|
// Aktifkan relay untuk membuka loker
|
|
digitalWrite(mappedRelayPin, LOW);
|
|
playBuzzer(2, 70, 50);
|
|
if (DEBUG_MODE) Serial.println("[RFID Action] Relay " + String(mappedRelayPin) + " AKTIF (Membuka).");
|
|
|
|
// Menunggu beberapa detik sambil menampilkan jarak & status loker
|
|
unsigned long showStart = millis();
|
|
float jarak = 0.0;
|
|
String status = "";
|
|
|
|
// Baca sensor ultrasonik hanya sekali
|
|
int trigPin = trigPins[mappedLokerId - 1];
|
|
int echoPin = echoPins[mappedLokerId - 1];
|
|
|
|
// Tunggu 10 detik dan update status loker
|
|
while (millis() - showStart < 10000) {
|
|
// Menghitung jarak dengan sensor ultrasonik
|
|
digitalWrite(trigPin, LOW);
|
|
delayMicroseconds(2);
|
|
digitalWrite(trigPin, HIGH);
|
|
delayMicroseconds(10);
|
|
digitalWrite(trigPin, LOW);
|
|
|
|
long durasi = pulseIn(echoPin, HIGH);
|
|
jarak = durasi * 0.034 / 2; // Menghitung jarak dalam cm
|
|
status = (jarak < 10) ? "digunakan" : "kosong"; // Status jika jarak < 10 cm dianggap 'digunakan'
|
|
|
|
// Tampilkan informasi di LCD
|
|
displayLcdMessage("Loker " + String(mappedLokerId),
|
|
status + " | " + String(jarak, 0) + "cm");
|
|
delay(500); // Mengurangi delay untuk responsifitas yang lebih cepat
|
|
}
|
|
|
|
// Matikan relay setelah 10 detik
|
|
digitalWrite(mappedRelayPin, HIGH);
|
|
if (DEBUG_MODE) Serial.println("[RFID Action] Relay " + String(mappedRelayPin) + " NONAKTIF (Terkunci).");
|
|
|
|
// Perbarui status loker ke API
|
|
updateStatusLoker(mappedLokerId, status);
|
|
|
|
// Kembali ke state idle setelah selesai
|
|
currentState = STATE_IDLE;
|
|
}
|
|
|
|
|
|
|
|
void handleNewRFIDRegistration(String uidStr) {
|
|
displayLcdMessage("Mencari Loker", "Kosong...");
|
|
int kosongLokerIdAPI = getFirstEmptyRFIDLokerId();
|
|
|
|
if (kosongLokerIdAPI != -1) {
|
|
setRFIDtoLoker(kosongLokerIdAPI, uidStr);
|
|
displayLcdMessage("RFID Diset", "Loker ID: " + String(kosongLokerIdAPI), 2000);
|
|
playBuzzer(1, 150, 0);
|
|
} else {
|
|
displayLcdMessage("Gagal Set RFID", "Loker Penuh", 2000);
|
|
playBuzzer(3, 100, 50);
|
|
}
|
|
}
|
|
|
|
void updateStatusLoker(int lokerId, String status) {
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
HTTPClient http;
|
|
String url = API_BASE_URL + "lokers/" + String(lokerId) + "/status";
|
|
|
|
http.begin(url);
|
|
http.setTimeout(3000);
|
|
http.addHeader("Content-Type", "application/json");
|
|
|
|
String jsonPayload = "{\"status\": \"" + status + "\"}";
|
|
int httpResponseCode = http.POST(jsonPayload);
|
|
|
|
if (httpResponseCode > 0) {
|
|
String response = http.getString();
|
|
if (DEBUG_MODE) Serial.println("[API] Status update response: " + response);
|
|
} else {
|
|
currentState = STATE_ERROR;
|
|
displayLcdMessage("API Gagal", "Update Status", 2000);
|
|
playBuzzer(2, 100, 50);
|
|
}
|
|
http.end();
|
|
} else {
|
|
if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa update status.");
|
|
currentState = STATE_ERROR;
|
|
displayLcdMessage("WiFi Disconnect!", "API Update Gagal", 2000);
|
|
playBuzzer(2, 100, 50);
|
|
}
|
|
}
|
|
|
|
int getFirstEmptyRFIDLokerId() {
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
HTTPClient http;
|
|
String url = API_BASE_URL + "lokers/pertama-rfid-null";
|
|
|
|
http.begin(url);
|
|
http.setTimeout(3000);
|
|
int httpCode = http.GET();
|
|
|
|
if (httpCode == 200) {
|
|
String response = http.getString();
|
|
StaticJsonDocument<256> doc;
|
|
DeserializationError error = deserializeJson(doc, response);
|
|
|
|
if (error) return -1;
|
|
|
|
if (doc.containsKey("data") && doc["data"].containsKey("id")) {
|
|
int parsedId = doc["data"]["id"].as<int>();
|
|
if (DEBUG_MODE) Serial.println("[API] ID loker kosong ditemukan: " + String(parsedId));
|
|
http.end();
|
|
return parsedId;
|
|
} else {
|
|
if (DEBUG_MODE) Serial.println("[API] Respons JSON tidak mengandung 'data.id'.");
|
|
http.end();
|
|
return -1;
|
|
}
|
|
} else if (httpCode == 404) {
|
|
http.end();
|
|
return -1;
|
|
} else {
|
|
currentState = STATE_ERROR;
|
|
http.end();
|
|
}
|
|
} else {
|
|
currentState = STATE_ERROR;
|
|
displayLcdMessage("WiFi Disconnect!", "API Get Gagal", 2000);
|
|
playBuzzer(2, 100, 50);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void setRFIDtoLoker(int lokerId, String rfid) {
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
HTTPClient http;
|
|
String url = API_BASE_URL + "lokers/" + String(lokerId) + "/setrfid";
|
|
|
|
http.begin(url);
|
|
http.setTimeout(3000);
|
|
http.addHeader("Content-Type", "application/json");
|
|
|
|
String jsonPayload = "{\"rfid\": \"" + rfid + "\"}";
|
|
int httpResponseCode = http.POST(jsonPayload);
|
|
|
|
if (httpResponseCode > 0) {
|
|
String response = http.getString();
|
|
if (DEBUG_MODE) Serial.println("[API] SET RFID Response: " + response);
|
|
StaticJsonDocument<256> doc;
|
|
DeserializationError error = deserializeJson(doc, response);
|
|
|
|
if (!error && doc.containsKey("message")) {
|
|
String message = doc["message"].as<String>();
|
|
if (message.indexOf("RFID sudah digunakan") != -1) {
|
|
if (DEBUG_MODE) Serial.println("[API] Server Report: RFID sudah digunakan.");
|
|
displayLcdMessage("RFID Sudah", "Terdaftar", 2000);
|
|
playBuzzer(2, 100, 50);
|
|
}
|
|
}
|
|
} else {
|
|
if (DEBUG_MODE) Serial.println("[API] Gagal kirim set RFID: " + http.errorToString(httpResponseCode));
|
|
currentState = STATE_ERROR;
|
|
displayLcdMessage("API Gagal", "Set RFID", 2000);
|
|
playBuzzer(2, 100, 50);
|
|
}
|
|
http.end();
|
|
} else {
|
|
if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa set RFID.");
|
|
currentState = STATE_ERROR;
|
|
displayLcdMessage("WiFi Disconnect!", "API Set RFID Gagal", 2000);
|
|
playBuzzer(2, 100, 50);
|
|
}
|
|
}
|
|
|
|
String getStatusLoker(int lokerId) {
|
|
if (DEBUG_MODE) Serial.println("[API] getStatusLoker dipanggil untuk Loker " + String(lokerId));
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
HTTPClient http;
|
|
String url = API_BASE_URL + "single-loker/" + String(lokerId);
|
|
|
|
http.begin(url);
|
|
http.setTimeout(3000);
|
|
int httpCode = http.GET();
|
|
if (DEBUG_MODE) Serial.println("[API] HTTP Code (getStatusLoker): " + String(httpCode));
|
|
|
|
if (httpCode == 200) {
|
|
String payload = http.getString();
|
|
if (DEBUG_MODE) Serial.println("[API] GET LOKER Status Response: " + payload);
|
|
|
|
StaticJsonDocument<256> doc;
|
|
DeserializationError error = deserializeJson(doc, payload);
|
|
|
|
if (!error && doc.containsKey("data") && doc["data"].containsKey("status")) {
|
|
String status = doc["data"]["status"].as<String>();
|
|
if (DEBUG_MODE) Serial.println("[API] Status dari API: " + status);
|
|
http.end();
|
|
return status;
|
|
} else {
|
|
if (DEBUG_MODE) Serial.println("[API] Parsing JSON error atau format tidak sesuai.");
|
|
if (DEBUG_MODE) Serial.println(error.c_str());
|
|
http.end();
|
|
}
|
|
} else if (httpCode == 404) {
|
|
if (DEBUG_MODE) Serial.println("[API] Loker " + String(lokerId) + " tidak ditemukan.");
|
|
http.end();
|
|
return "NotFound";
|
|
} else {
|
|
if (DEBUG_MODE) Serial.println("[API] Gagal ambil status loker: " + http.errorToString(httpCode));
|
|
currentState = STATE_ERROR;
|
|
http.end();
|
|
}
|
|
} else {
|
|
if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa get status loker.");
|
|
currentState = STATE_ERROR;
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
void clearRFID() {
|
|
mfrc522.PICC_HaltA();
|
|
mfrc522.PCD_StopCrypto1();
|
|
if (DEBUG_MODE) Serial.println("[RFID] RFID Reader Direset.");
|
|
}
|
|
|
|
void playBuzzer(int count, int durationMs, int pauseMs) {
|
|
for (int i = 0; i < count; i++) {
|
|
digitalWrite(BUZZER_PIN, HIGH);
|
|
delay(durationMs);
|
|
digitalWrite(BUZZER_PIN, LOW);
|
|
if (i < count - 1) delay(pauseMs);
|
|
}
|
|
}
|
|
|
|
void displayLcdMessage(String line1, String line2, int duration) {
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print(line1);
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(line2);
|
|
if (duration > 0) delay(duration);
|
|
} |