456 lines
16 KiB
C++
456 lines
16 KiB
C++
1) #include <WiFi.h>
|
|
2) #include <HTTPClient.h>
|
|
3) #include <SPI.h>
|
|
4) #include <Wire.h>
|
|
5) #include <MFRC522.h>
|
|
6) #include <LiquidCrystal_I2C.h>
|
|
7) #include <ArduinoJson.h>
|
|
8)
|
|
9) #define DEBUG_MODE true
|
|
10)
|
|
11) const char* ssid = "KOPI";
|
|
12) const char* password = "digoreng123";
|
|
13) const String API_BASE_URL = "https://smartlocker.punyapadias.my.id/api/";
|
|
14)
|
|
15) #define SS_PIN 21
|
|
16) #define RST_PIN 5
|
|
17) MFRC522 mfrc522(SS_PIN, RST_PIN);
|
|
18)
|
|
19) LiquidCrystal_I2C lcd(0x27, 16, 2);
|
|
20)
|
|
21) const int trigPins[3] = {17, 16, 33};
|
|
22) const int echoPins[3] = {35, 34, 32};
|
|
23)
|
|
24) #define RELAY1_PIN 14
|
|
25) #define RELAY2_PIN 27
|
|
26) #define RELAY3_PIN 26
|
|
27)
|
|
28) #define BUZZER_PIN 13
|
|
29)
|
|
30) enum SystemState {
|
|
31) STATE_IDLE,
|
|
32) STATE_RFID_DETECTED,
|
|
33) STATE_OPENING_LOCKER,
|
|
34) STATE_REGISTERING_RFID,
|
|
35) STATE_ERROR
|
|
36) };
|
|
37) SystemState currentState = STATE_IDLE;
|
|
38)
|
|
39) unsigned long lastApiCallTime = 0;
|
|
40) const long apiCallInterval = 5000;
|
|
41) unsigned long rfidActionStartTime = 0;
|
|
42) const long rfidActionDuration = 10000;
|
|
43) bool lcdIdleShown = false;
|
|
44)
|
|
45)
|
|
46) struct RFIDMapping {
|
|
47) String uid;
|
|
48) int lokerId;
|
|
49) int relayPin;
|
|
50) };
|
|
51)
|
|
52) RFIDMapping rfidMappings[] = {
|
|
53) {"36CB3503", 1, RELAY1_PIN},
|
|
54) {"5C312903", 2, RELAY2_PIN},
|
|
55) {"611B2803", 3, RELAY3_PIN},
|
|
56) };
|
|
57) const int jumlahRFID = sizeof(rfidMappings) / sizeof(rfidMappings[0]);
|
|
58)
|
|
59) void setRFIDtoLoker(int lokerId, String rfid);
|
|
60) void updateStatusLoker(int lokerId, String status);
|
|
61) int getFirstEmptyRFIDLokerId();
|
|
62) String getStatusLoker(int lokerId);
|
|
63) void clearRFID();
|
|
64) void playBuzzer(int count, int durationMs, int pauseMs);
|
|
65) void displayLcdMessage(String line1, String line2, int duration = 0);
|
|
66) void handleRFIDAction(String uidStr, int mappedLokerId, int mappedRelayPin);
|
|
67) void handleNewRFIDRegistration(String uidStr);
|
|
68)
|
|
69) void displayDefaultScreen() {
|
|
70) lcd.clear();
|
|
71) lcd.setCursor(0, 0);
|
|
72) lcd.print("Loker Pintar");
|
|
73) lcd.setCursor(0, 1);
|
|
74) lcd.print("Scan Kartu");
|
|
75) }
|
|
76)
|
|
77) void setup() {
|
|
78) Serial.begin(115200);
|
|
79) Wire.begin(4, 22);
|
|
80) lcd.init();
|
|
81) lcd.backlight();
|
|
82) lcd.setCursor(0, 0);
|
|
83) lcd.print("Inisialisasi...");
|
|
84)
|
|
85) // Menyambung ke WiFi
|
|
86) WiFi.begin(ssid, password);
|
|
87) lcd.setCursor(0, 1);
|
|
88) lcd.print("WiFi connecting...");
|
|
89) while (WiFi.status() != WL_CONNECTED) {
|
|
90) delay(500);
|
|
91) Serial.print(".");
|
|
92) }
|
|
93)
|
|
94) lcd.setCursor(0, 1);
|
|
95) lcd.print("WiFi connected ");
|
|
96) Serial.println("\nWiFi Connected. IP: " + WiFi.localIP().toString());
|
|
97)
|
|
98) // Inisialisasi RFID
|
|
99) SPI.begin();
|
|
100) mfrc522.PCD_Init();
|
|
101)
|
|
102) // Inisialisasi sensor ultrasonik
|
|
103) for (int i = 0; i < 3; i++) {
|
|
104) pinMode(trigPins[i], OUTPUT);
|
|
105) pinMode(echoPins[i], INPUT);
|
|
106) }
|
|
107)
|
|
108) // Inisialisasi relay dan buzzer
|
|
109) pinMode(RELAY1_PIN, OUTPUT); digitalWrite(RELAY1_PIN, HIGH);
|
|
110) pinMode(RELAY2_PIN, OUTPUT); digitalWrite(RELAY2_PIN, HIGH);
|
|
111) pinMode(RELAY3_PIN, OUTPUT); digitalWrite(RELAY3_PIN, HIGH);
|
|
112)
|
|
113) pinMode(BUZZER_PIN, OUTPUT);
|
|
114) digitalWrite(BUZZER_PIN, LOW);
|
|
115)
|
|
116) // Tampilkan layar utama
|
|
117) lcd.clear();
|
|
118) displayDefaultScreen();
|
|
119)
|
|
120) lastApiCallTime = millis();
|
|
121) }
|
|
122)
|
|
123) void loop() {
|
|
124) // Debug: tampilkan state saat ini setiap 1 detik
|
|
125) static unsigned long lastDebugTime = 0;
|
|
126) if (millis() - lastDebugTime > 1000) {
|
|
127) Serial.println("[DEBUG] Current state: " + String(currentState));
|
|
128) lastDebugTime = millis();
|
|
129) }
|
|
130)
|
|
131) // Recovery: jika RFID tidak terbaca dalam waktu lama, inisialisasi ulang
|
|
132) static unsigned long lastRFIDScanTime = 0;
|
|
133) if (millis() - lastRFIDScanTime > 10000) {
|
|
134) Serial.println("[RFID] Timeout, re-initialize RFID.");
|
|
135) mfrc522.PCD_Init();
|
|
136) lastRFIDScanTime = millis();
|
|
137) }
|
|
138)
|
|
139) switch (currentState) {
|
|
140) case STATE_IDLE:
|
|
141) // Tampilkan LCD hanya sekali saat idle
|
|
142) if (!lcdIdleShown) {
|
|
143) displayDefaultScreen();
|
|
144) lcdIdleShown = true;
|
|
145) }
|
|
146)
|
|
147) // Cek kartu RFID
|
|
148) if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
|
|
149) lcdIdleShown = false; // agar layar bisa refresh nanti
|
|
150) playBuzzer(1, 50, 0);
|
|
151) lastRFIDScanTime = millis(); // reset timeout counter
|
|
152)
|
|
153) // Ambil UID
|
|
154) String uidStr = "";
|
|
155) for (byte i = 0; i < mfrc522.uid.size; i++) {
|
|
156) if (mfrc522.uid.uidByte[i] < 0x10) uidStr += "0";
|
|
157) uidStr += String(mfrc522.uid.uidByte[i], HEX);
|
|
158) }
|
|
159) uidStr.toUpperCase();
|
|
160)
|
|
161) Serial.println("[RFID] UID: " + uidStr);
|
|
162) displayLcdMessage("RFID Terbaca:", uidStr);
|
|
163)
|
|
164) // Cek UID pada daftar
|
|
165) bool found = false;
|
|
166) int lokerId = -1;
|
|
167) int relayPin = -1;
|
|
168) for (int i = 0; i < jumlahRFID; i++) {
|
|
169) if (uidStr == rfidMappings[i].uid) {
|
|
170) found = true;
|
|
171) lokerId = rfidMappings[i].lokerId;
|
|
172) relayPin = rfidMappings[i].relayPin;
|
|
173) break;
|
|
174) }
|
|
175) }
|
|
176)
|
|
177) if (found) {
|
|
178) currentState = STATE_OPENING_LOCKER;
|
|
179) handleRFIDAction(uidStr, lokerId, relayPin);
|
|
180) } else {
|
|
181) // Jika tidak ditemukan, aktifkan buzzer "tit-tit-tit" dan tampilkan pesan
|
|
182) playBuzzer(3, 100, 100); // Bunyi 3 kali dengan interval 100ms
|
|
183) displayLcdMessage("Kartu Tidak", "Terdaftar!", 2000);
|
|
184) currentState = STATE_REGISTERING_RFID;
|
|
185) handleNewRFIDRegistration(uidStr);
|
|
186) }
|
|
187)
|
|
188) clearRFID();
|
|
189) delay(200); // beri waktu recovery RFID
|
|
190) }
|
|
191) break;
|
|
192)
|
|
193) case STATE_REGISTERING_RFID:
|
|
194) case STATE_OPENING_LOCKER:
|
|
195) lcdIdleShown = false;
|
|
196) currentState = STATE_IDLE;
|
|
197) break;
|
|
198)
|
|
199) case STATE_ERROR:
|
|
200) if (WiFi.status() != WL_CONNECTED) {
|
|
201) displayLcdMessage("WiFi Disconnect!", "Reconnecting...");
|
|
202) WiFi.reconnect();
|
|
203) delay(1000);
|
|
204) } else {
|
|
205) lcdIdleShown = false;
|
|
206) currentState = STATE_IDLE;
|
|
207) }
|
|
208) break;
|
|
209) }
|
|
210) }
|
|
211)
|
|
212)
|
|
213)
|
|
214)
|
|
215)
|
|
216) void handleRFIDAction(String uidStr, int mappedLokerId, int mappedRelayPin) {
|
|
217) if (DEBUG_MODE) Serial.println("[RFID Action] Membuka Loker ID: " + String(mappedLokerId) + " dengan UID: " + uidStr);
|
|
218)
|
|
219) // Aktifkan relay untuk membuka loker
|
|
220) digitalWrite(mappedRelayPin, LOW);
|
|
221) playBuzzer(2, 70, 50);
|
|
222) if (DEBUG_MODE) Serial.println("[RFID Action] Relay " + String(mappedRelayPin) + " AKTIF (Membuka).");
|
|
223)
|
|
224) // Menunggu beberapa detik sambil menampilkan jarak & status loker
|
|
225) unsigned long showStart = millis();
|
|
226) float jarak = 0.0;
|
|
227) String status = "";
|
|
228)
|
|
229) // Baca sensor ultrasonik hanya sekali
|
|
230) int trigPin = trigPins[mappedLokerId - 1];
|
|
231) int echoPin = echoPins[mappedLokerId - 1];
|
|
232)
|
|
233) // Tunggu 10 detik dan update status loker
|
|
234) while (millis() - showStart < 10000) {
|
|
235) // Menghitung jarak dengan sensor ultrasonik
|
|
236) digitalWrite(trigPin, LOW);
|
|
237) delayMicroseconds(2);
|
|
238) digitalWrite(trigPin, HIGH);
|
|
239) delayMicroseconds(10);
|
|
240) digitalWrite(trigPin, LOW);
|
|
241)
|
|
242) long durasi = pulseIn(echoPin, HIGH);
|
|
243) jarak = durasi * 0.034 / 2; // Menghitung jarak dalam cm
|
|
244) status = (jarak < 10) ? "digunakan" : "kosong"; // Status jika jarak < 10 cm dianggap 'digunakan'
|
|
245)
|
|
246) // Tampilkan informasi di LCD
|
|
247) displayLcdMessage("Loker " + String(mappedLokerId),
|
|
248) status + " | " + String(jarak, 0) + "cm");
|
|
249) delay(500); // Mengurangi delay untuk responsifitas yang lebih cepat
|
|
250) }
|
|
251)
|
|
252) // Matikan relay setelah 10 detik
|
|
253) digitalWrite(mappedRelayPin, HIGH);
|
|
254) if (DEBUG_MODE) Serial.println("[RFID Action] Relay " + String(mappedRelayPin) + " NONAKTIF (Terkunci).");
|
|
255)
|
|
256) // Perbarui status loker ke API
|
|
257) updateStatusLoker(mappedLokerId, status);
|
|
258)
|
|
259) // Kembali ke state idle setelah selesai
|
|
260) currentState = STATE_IDLE;
|
|
261) }
|
|
262)
|
|
263)
|
|
264)
|
|
265) void handleNewRFIDRegistration(String uidStr) {
|
|
266) displayLcdMessage("Mencari Loker", "Kosong...");
|
|
267) int kosongLokerIdAPI = getFirstEmptyRFIDLokerId();
|
|
268)
|
|
269) if (kosongLokerIdAPI != -1) {
|
|
270) setRFIDtoLoker(kosongLokerIdAPI, uidStr);
|
|
271) displayLcdMessage("RFID Diset", "Loker ID: " + String(kosongLokerIdAPI), 2000);
|
|
272) playBuzzer(1, 150, 0);
|
|
273) } else {
|
|
274) displayLcdMessage("Gagal Set RFID", "Loker Penuh", 2000);
|
|
275) playBuzzer(3, 100, 50);
|
|
276) }
|
|
277) }
|
|
278)
|
|
279) void updateStatusLoker(int lokerId, String status) {
|
|
280) if (WiFi.status() == WL_CONNECTED) {
|
|
281) HTTPClient http;
|
|
282) String url = API_BASE_URL + "lokers/" + String(lokerId) + "/status";
|
|
283)
|
|
284) http.begin(url);
|
|
285) http.setTimeout(3000);
|
|
286) http.addHeader("Content-Type", "application/json");
|
|
287)
|
|
288) String jsonPayload = "{\"status\": \"" + status + "\"}";
|
|
289) int httpResponseCode = http.POST(jsonPayload);
|
|
290)
|
|
291) if (httpResponseCode > 0) {
|
|
292) String response = http.getString();
|
|
293) if (DEBUG_MODE) Serial.println("[API] Status update response: " + response);
|
|
294) } else {
|
|
295) currentState = STATE_ERROR;
|
|
296) displayLcdMessage("API Gagal", "Update Status", 2000);
|
|
297) playBuzzer(2, 100, 50);
|
|
298) }
|
|
299) http.end();
|
|
300) } else {
|
|
301) if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa update status.");
|
|
302) currentState = STATE_ERROR;
|
|
303) displayLcdMessage("WiFi Disconnect!", "API Update Gagal", 2000);
|
|
304) playBuzzer(2, 100, 50);
|
|
305) }
|
|
306) }
|
|
307)
|
|
308) int getFirstEmptyRFIDLokerId() {
|
|
309) if (WiFi.status() == WL_CONNECTED) {
|
|
310) HTTPClient http;
|
|
311) String url = API_BASE_URL + "lokers/pertama-rfid-null";
|
|
312)
|
|
313) http.begin(url);
|
|
314) http.setTimeout(3000);
|
|
315) int httpCode = http.GET();
|
|
316)
|
|
317) if (httpCode == 200) {
|
|
318) String response = http.getString();
|
|
319) StaticJsonDocument<256> doc;
|
|
320) DeserializationError error = deserializeJson(doc, response);
|
|
321)
|
|
322) if (error) return -1;
|
|
323)
|
|
324) if (doc.containsKey("data") && doc["data"].containsKey("id")) {
|
|
325) int parsedId = doc["data"]["id"].as<int>();
|
|
326) if (DEBUG_MODE) Serial.println("[API] ID loker kosong ditemukan: " + String(parsedId));
|
|
327) http.end();
|
|
328) return parsedId;
|
|
329) } else {
|
|
330) if (DEBUG_MODE) Serial.println("[API] Respons JSON tidak mengandung 'data.id'.");
|
|
331) http.end();
|
|
332) return -1;
|
|
333) }
|
|
334) } else if (httpCode == 404) {
|
|
335) http.end();
|
|
336) return -1;
|
|
337) } else {
|
|
338) currentState = STATE_ERROR;
|
|
339) http.end();
|
|
340) }
|
|
341) } else {
|
|
342) currentState = STATE_ERROR;
|
|
343) displayLcdMessage("WiFi Disconnect!", "API Get Gagal", 2000);
|
|
344) playBuzzer(2, 100, 50);
|
|
345) }
|
|
346) return -1;
|
|
347) }
|
|
348)
|
|
349) void setRFIDtoLoker(int lokerId, String rfid) {
|
|
350) if (WiFi.status() == WL_CONNECTED) {
|
|
351) HTTPClient http;
|
|
352) String url = API_BASE_URL + "lokers/" + String(lokerId) + "/setrfid";
|
|
353)
|
|
354) http.begin(url);
|
|
355) http.setTimeout(3000);
|
|
356) http.addHeader("Content-Type", "application/json");
|
|
357)
|
|
358) String jsonPayload = "{\"rfid\": \"" + rfid + "\"}";
|
|
359) int httpResponseCode = http.POST(jsonPayload);
|
|
360)
|
|
361) if (httpResponseCode > 0) {
|
|
362) String response = http.getString();
|
|
363) if (DEBUG_MODE) Serial.println("[API] SET RFID Response: " + response);
|
|
364) StaticJsonDocument<256> doc;
|
|
365) DeserializationError error = deserializeJson(doc, response);
|
|
366)
|
|
367) if (!error && doc.containsKey("message")) {
|
|
368) String message = doc["message"].as<String>();
|
|
369) if (message.indexOf("RFID sudah digunakan") != -1) {
|
|
370) if (DEBUG_MODE) Serial.println("[API] Server Report: RFID sudah digunakan.");
|
|
371) displayLcdMessage("RFID Sudah", "Terdaftar", 2000);
|
|
372) playBuzzer(2, 100, 50);
|
|
373) }
|
|
374) }
|
|
375) } else {
|
|
376) if (DEBUG_MODE) Serial.println("[API] Gagal kirim set RFID: " + http.errorToString(httpResponseCode));
|
|
377) currentState = STATE_ERROR;
|
|
378) displayLcdMessage("API Gagal", "Set RFID", 2000);
|
|
379) playBuzzer(2, 100, 50);
|
|
380) }
|
|
381) http.end();
|
|
382) } else {
|
|
383) if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa set RFID.");
|
|
384) currentState = STATE_ERROR;
|
|
385) displayLcdMessage("WiFi Disconnect!", "API Set RFID Gagal", 2000);
|
|
386) playBuzzer(2, 100, 50);
|
|
387) }
|
|
388) }
|
|
389)
|
|
390) String getStatusLoker(int lokerId) {
|
|
391) if (DEBUG_MODE) Serial.println("[API] getStatusLoker dipanggil untuk Loker " + String(lokerId));
|
|
392) if (WiFi.status() == WL_CONNECTED) {
|
|
393) HTTPClient http;
|
|
394) String url = API_BASE_URL + "single-loker/" + String(lokerId);
|
|
395)
|
|
396) http.begin(url);
|
|
397) http.setTimeout(3000);
|
|
398) int httpCode = http.GET();
|
|
399) if (DEBUG_MODE) Serial.println("[API] HTTP Code (getStatusLoker): " + String(httpCode));
|
|
400)
|
|
401) if (httpCode == 200) {
|
|
402) String payload = http.getString();
|
|
403) if (DEBUG_MODE) Serial.println("[API] GET LOKER Status Response: " + payload);
|
|
404)
|
|
405) StaticJsonDocument<256> doc;
|
|
406) DeserializationError error = deserializeJson(doc, payload);
|
|
407)
|
|
408) if (!error && doc.containsKey("data") && doc["data"].containsKey("status")) {
|
|
409) String status = doc["data"]["status"].as<String>();
|
|
410) if (DEBUG_MODE) Serial.println("[API] Status dari API: " + status);
|
|
411) http.end();
|
|
412) return status;
|
|
413) } else {
|
|
414) if (DEBUG_MODE) Serial.println("[API] Parsing JSON error atau format tidak sesuai.");
|
|
415) if (DEBUG_MODE) Serial.println(error.c_str());
|
|
416) http.end();
|
|
417) }
|
|
418) } else if (httpCode == 404) {
|
|
419) if (DEBUG_MODE) Serial.println("[API] Loker " + String(lokerId) + " tidak ditemukan.");
|
|
420) http.end();
|
|
421) return "NotFound";
|
|
422) } else {
|
|
423) if (DEBUG_MODE) Serial.println("[API] Gagal ambil status loker: " + http.errorToString(httpCode));
|
|
424) currentState = STATE_ERROR;
|
|
425) http.end();
|
|
426) }
|
|
427) } else {
|
|
428) if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa get status loker.");
|
|
429) currentState = STATE_ERROR;
|
|
430) }
|
|
431) return "Unknown";
|
|
432) }
|
|
433)
|
|
434) void clearRFID() {
|
|
435) mfrc522.PICC_HaltA();
|
|
436) mfrc522.PCD_StopCrypto1();
|
|
437) if (DEBUG_MODE) Serial.println("[RFID] RFID Reader Direset.");
|
|
438) }
|
|
439)
|
|
440) void playBuzzer(int count, int durationMs, int pauseMs) {
|
|
441) for (int i = 0; i < count; i++) {
|
|
442) digitalWrite(BUZZER_PIN, HIGH);
|
|
443) delay(durationMs);
|
|
444) digitalWrite(BUZZER_PIN, LOW);
|
|
445) if (i < count - 1) delay(pauseMs);
|
|
446) }
|
|
447) }
|
|
448)
|
|
449) void displayLcdMessage(String line1, String line2, int duration) {
|
|
450) lcd.clear();
|
|
451) lcd.setCursor(0, 0);
|
|
452) lcd.print(line1);
|
|
453) lcd.setCursor(0, 1);
|
|
454) lcd.print(line2);
|
|
455) if (duration > 0) delay(duration);
|
|
456) } |