223 lines
8.2 KiB
Python
223 lines
8.2 KiB
Python
import re
|
|
|
|
with open('esp32_serial_backend/esp32_serial_backend.ino', 'r', encoding='utf8') as f:
|
|
content = f.read()
|
|
|
|
# 1. Globals
|
|
content = content.replace(
|
|
'''bool gPendingRecog = false;
|
|
unsigned long gRecogCaptureAt = 0; // millis() when to actually capture''',
|
|
'''// Auto-retry RECOG / ENROLL state
|
|
String gKeepTryingCmd = "";
|
|
unsigned long gKeepTryingUntil = 0;
|
|
bool gLastActionSuccess = false;''')
|
|
|
|
# 2. RFID Trigger
|
|
old_rfid = ''' // Set pending RECOG - kamera preview akan tampil dulu 3 detik di loop()
|
|
gPendingRecog = true;
|
|
gRecogCaptureAt = millis() + 3000; // capture setelah 3 detik
|
|
gCameraActive = true; // tampilkan kamera preview mulai sekarang'''
|
|
new_rfid = ''' // Mulai proses pengenalan wajah terus-menerus selama 15 detik
|
|
gKeepTryingCmd = "RECOG";
|
|
gKeepTryingUntil = millis() + 15000;
|
|
gLastActionSuccess = false;
|
|
gCameraActive = true; // tampilkan kamera preview mulai sekarang'''
|
|
content = content.replace(old_rfid, new_rfid)
|
|
|
|
# 3. Web Job Trigger (ENROLL)
|
|
old_enroll_job = ''' if (type == "ENROLL")
|
|
{
|
|
showStatus("TAKE ENROLL", TFT_CYAN);
|
|
Serial.println(String("[JOB] ENROLL start name=") + name);
|
|
bool enrollOk = handleCommand(String("ENROLL ") + name);
|
|
String result = "{\\"from\\":\\"esp32\\",\\"type\\":\\"ENROLL\\",\\"name\\":\\"" + name + "\\",\\"ok\\":" + (enrollOk ? "true" : "false");
|
|
if (uid.length())
|
|
result += ",\\"uid\\":\\"" + uid + "\\"";
|
|
if (personId.length())
|
|
result += ",\\"person_id\\":\\"" + personId + "\\"";
|
|
result += "}";
|
|
String jr = postJson(String(SERVER_BASE) + "/job/result", result, 6000);
|
|
Serial.println(String("[JOB] ENROLL result post=") + jr);
|
|
if (jr.startsWith("HTTP") || jr.startsWith("WIFI"))
|
|
showStatus("JOB RESULT FAIL\\n" + jr, TFT_YELLOW);
|
|
return;
|
|
}'''
|
|
new_enroll_job = ''' if (type == "ENROLL")
|
|
{
|
|
showStatus("START ENROLL", TFT_CYAN);
|
|
Serial.println(String("[JOB] ENROLL start name=") + name);
|
|
|
|
// Aktifkan continuous capture
|
|
gKeepTryingCmd = String("ENROLL ") + name;
|
|
gKeepTryingUntil = millis() + 15000;
|
|
gLastActionSuccess = false;
|
|
gCameraActive = true;
|
|
|
|
// We notify the server immediately that we took the job.
|
|
String result = "{\\"from\\":\\"esp32\\",\\"type\\":\\"ENROLL\\",\\"name\\":\\"" + name + "\\",\\"ok\\":true";
|
|
if (uid.length())
|
|
result += ",\\"uid\\":\\"" + uid + "\\"";
|
|
if (personId.length())
|
|
result += ",\\"person_id\\":\\"" + personId + "\\"";
|
|
result += "}";
|
|
postJson(String(SERVER_BASE) + "/job/result", result, 6000);
|
|
return;
|
|
}'''
|
|
content = content.replace(old_enroll_job, new_enroll_job)
|
|
|
|
# 4. Web Job Trigger (RECOG)
|
|
old_recog_job = ''' if (type == "RECOG")
|
|
{
|
|
// Save job params so /recognize uses same threshold+uid as the web request.
|
|
gJobRecogThreshold = thr;
|
|
gJobRecogUid = uid;
|
|
gJobPersonId = personId;
|
|
|
|
showStatus("TAKE RECOG", TFT_CYAN);
|
|
Serial.println(String("[JOB] RECOG start thr=") + String(thr, 2) + String(" uid=") + uid);
|
|
Serial.println(String("[JOB] WiFi=") + (WiFi.status() == WL_CONNECTED ? "OK" : "DISCONNECTED"));
|
|
showStatus("RECOG...", TFT_CYAN);
|
|
bool recogOk = handleCommand("RECOG");
|
|
String result = String("{\\"from\\":\\"esp32\\",\\"type\\":\\"RECOG\\",\\"threshold\\":") + String(thr, 2) + String(",\\"uid\\":\\"") + uid + String("\\",\\"ok\\":") + (recogOk ? "true" : "false") + String("}");
|
|
String jr = postJson(String(SERVER_BASE) + "/job/result", result, 6000);
|
|
Serial.println(String("[JOB] RECOG result post=") + jr);
|
|
if (jr.startsWith("HTTP") || jr.startsWith("WIFI"))
|
|
showStatus("JOB RES:\\n" + jr, TFT_YELLOW);
|
|
return;
|
|
}'''
|
|
new_recog_job = ''' if (type == "RECOG")
|
|
{
|
|
gJobRecogThreshold = thr;
|
|
gJobRecogUid = uid;
|
|
gJobPersonId = personId;
|
|
|
|
showStatus("START RECOG", TFT_CYAN);
|
|
Serial.println(String("[JOB] RECOG start thr=") + String(thr, 2) + String(" uid=") + uid);
|
|
|
|
// Aktifkan continuous capture
|
|
gKeepTryingCmd = "RECOG";
|
|
gKeepTryingUntil = millis() + 15000;
|
|
gLastActionSuccess = false;
|
|
gCameraActive = true;
|
|
|
|
String result = String("{\\"from\\":\\"esp32\\",\\"type\\":\\"RECOG\\",\\"threshold\\":") + String(thr, 2) + String(",\\"uid\\":\\"") + uid + String("\\",\\"ok\\":true}");
|
|
postJson(String(SERVER_BASE) + "/job/result", result, 6000);
|
|
return;
|
|
}'''
|
|
content = content.replace(old_recog_job, new_recog_job)
|
|
|
|
# 5. Success detection in handleCommand(ENROLL)
|
|
old_enroll_resp = ''' String resp = postFrameWithRetry(url, fb, 60000);
|
|
showStatus(resp, TFT_CYAN);'''
|
|
new_enroll_resp = ''' String resp = postFrameWithRetry(url, fb, 60000);
|
|
if (!resp.startsWith("HTTP ERR") && !resp.startsWith("HTTP 4") && !resp.startsWith("HTTP 5") && !resp.startsWith("WIFI")) {
|
|
gLastActionSuccess = true;
|
|
}
|
|
showStatus(resp, TFT_CYAN);'''
|
|
content = content.replace(old_enroll_resp, new_enroll_resp)
|
|
|
|
# 6. Success detection in handleCommand(RECOG)
|
|
old_recog_resp = ''' if (respLower.indexOf(expLower) >= 0)
|
|
{
|
|
showStatus("OK: " + gExpectedName, TFT_GREEN);
|
|
}
|
|
else
|
|
{
|
|
showStatus("FAIL: " + gExpectedName, TFT_RED);
|
|
}'''
|
|
new_recog_resp = ''' if (respLower.indexOf(expLower) >= 0 || respLower.indexOf("\\"match\\":true") >= 0 || respLower.indexOf("\\"match\\": true") >= 0)
|
|
{
|
|
showStatus("OK: " + gExpectedName, TFT_GREEN);
|
|
gLastActionSuccess = true;
|
|
}
|
|
else
|
|
{
|
|
showStatus("FAIL: " + gExpectedName, TFT_RED);
|
|
}'''
|
|
content = content.replace(old_recog_resp, new_recog_resp)
|
|
|
|
# 7. Add fallback success for RECOG when no ExpectedName is set (e.g. from Web Job)
|
|
old_recog_resp_fallback = ''' else
|
|
{
|
|
showStatus(resp, TFT_CYAN);
|
|
}'''
|
|
new_recog_resp_fallback = ''' else
|
|
{
|
|
String respLower = resp;
|
|
respLower.toLowerCase();
|
|
if (respLower.indexOf("\\"match\\":true") >= 0 || respLower.indexOf("\\"match\\": true") >= 0) {
|
|
gLastActionSuccess = true;
|
|
showStatus("Dikenali", TFT_GREEN);
|
|
} else {
|
|
showStatus(resp, TFT_CYAN);
|
|
}
|
|
}'''
|
|
content = content.replace(old_recog_resp_fallback, new_recog_resp_fallback)
|
|
|
|
# 8. Loop logic
|
|
old_loop_broken = '''// ===== PENDING RECOG (dipicu oleh RFID) =====
|
|
// Kamera sudah aktif (gCameraActive=true), tampilkan preview + overlay countdown
|
|
// Saat timer habis, jalankan capture
|
|
if (gPendingRecog && !gBusy)
|
|
{
|
|
if (now >= gRecogCaptureAt)
|
|
{
|
|
// Waktu habis - jalankan RECOG sekarang
|
|
gPendingRecog = false;
|
|
gCameraActive = false;
|
|
showStatus("MEMPROSES...", TFT_CYAN, "", 8000);
|
|
Serial.println("[RFID] Countdown habis - jalankan RECOG");
|
|
bool attendOk = handleCommand("RECOG");
|
|
gCameraActive = false;
|
|
Serial.println(String("[RFID] auto attendance result=") + (attendOk ? "OK" : "FAIL"));
|
|
}
|
|
else
|
|
{
|
|
// Tampilkan countdown di overlay (tapi biarkan kamera preview tetap jalan)
|
|
int secsLeft = (int)((gRecogCaptureAt - now) / 1000) + 1;
|
|
Serial.println("[RECOG] Executing delayed capture...");
|
|
handleCommand("RECOG");
|
|
lastJobPoll = millis(); // Reset penanda waktu poll setelah proses RECOG
|
|
gBusy = false;
|
|
}
|
|
else
|
|
{
|
|
// Tampilkan hitung mundur
|
|
int secsLeft = (gRecogCaptureAt - now) / 1000 + 1;
|
|
showOverlayMessage(String("Siap-siap absen..."),
|
|
String("Capture dalam ") + secsLeft + "s...", 4000);
|
|
}
|
|
}'''
|
|
|
|
new_loop_continuous = '''// ===== CONTINUOUS CAPTURE (Auto-Retry) =====
|
|
if (gKeepTryingCmd.length() > 0 && !gBusy)
|
|
{
|
|
if (now >= gKeepTryingUntil)
|
|
{
|
|
// Timeout
|
|
gKeepTryingCmd = "";
|
|
gCameraActive = false;
|
|
showStatus("TIMEOUT", TFT_RED, "Wajah tidak dikenali", 3000);
|
|
}
|
|
else
|
|
{
|
|
Serial.println(String("[AUTO] Executing ") + gKeepTryingCmd);
|
|
showOverlayMessage("MEMPROSES...", "Jangan bergerak", 2000);
|
|
handleCommand(gKeepTryingCmd);
|
|
lastJobPoll = millis(); // Reset poll timer
|
|
|
|
if (gLastActionSuccess)
|
|
{
|
|
gKeepTryingCmd = ""; // Stop trying
|
|
gCameraActive = false; // Turn off camera on success
|
|
showStatus("BERHASIL", TFT_GREEN, "", 3000);
|
|
}
|
|
}
|
|
}'''
|
|
content = content.replace(old_loop_broken, new_loop_continuous)
|
|
|
|
with open('esp32_serial_backend/esp32_serial_backend.ino', 'w', encoding='utf8') as f:
|
|
f.write(content)
|
|
|
|
print("Update complete")
|