TKK_E32232028/updater7.py

351 lines
12 KiB
Python

import re
with open('esp32_serial_backend/esp32_serial_backend.ino', 'r', encoding='utf8') as f:
content = f.read()
# 1. Add Task Variables at the top
if 'volatile bool gUploadTaskBusy' not in content:
task_vars = '''// ===== BACKGROUND UPLOAD TASK =====
volatile bool gUploadTaskBusy = false;
volatile bool gUploadFinished = false;
uint8_t *gUploadBuf = NULL;
size_t gUploadLen = 0;
String gUploadUrl = "";
String gUploadCmd = "";
String gUploadResult = "";
TaskHandle_t UploadTaskHandle = NULL;
void uploadTask(void *pvParameters)
{
while (true)
{
if (gUploadTaskBusy && !gUploadFinished)
{
gUploadResult = postFrameWithRetry(gUploadUrl, gUploadBuf, gUploadLen, 60000);
if (gUploadBuf != NULL) {
free(gUploadBuf);
gUploadBuf = NULL;
}
gUploadFinished = true;
}
vTaskDelay(50 / portTICK_PERIOD_MS); // yield
}
}
'''
content = content.replace('void pollAndRunJob()', task_vars + '\nvoid pollAndRunJob()')
# 2. Start Task in setup()
if 'xTaskCreatePinnedToCore(' not in content:
content = content.replace('Serial.println("Starting AP Mode");',
'xTaskCreatePinnedToCore(uploadTask, "UploadTask", 16384, NULL, 1, &UploadTaskHandle, 0);\n Serial.println("Starting AP Mode");')
content = content.replace('connectWiFi();',
'xTaskCreatePinnedToCore(uploadTask, "UploadTask", 16384, NULL, 1, &UploadTaskHandle, 0);\n connectWiFi();')
# 3. Modify handleCommand to only trigger the task
old_handle_cmd = ''' // Kita biarkan kamera di format RGB565 saat ini, encode software
camera_fb_t *fb = esp_camera_fb_get();
if (!fb)
{
Serial.println("ERROR no_frame");
showStatus("KAMERA ERROR", TFT_RED, "Camera fb null");
gBusy = false;
return false;
}
uint8_t *jpeg_buf = NULL;
size_t jpeg_len = 0;
bool is_allocated = false;
if (fb->format != PIXFORMAT_JPEG) {
if (!frame2jpg(fb, 40, &jpeg_buf, &jpeg_len)) {
showStatus("JPEG ENCODE FAIL", TFT_RED, "");
esp_camera_fb_return(fb);
gBusy = false;
return false;
}
is_allocated = true;
} else {
jpeg_buf = fb->buf;
jpeg_len = fb->len;
}
if (cmd.startsWith("ENROLL "))
{
String name = cmd.substring(7);
name.trim();
if (name.length() == 0)
{
showStatus("NAMA KOSONG", TFT_RED, "Silakan isi nama");
if (fb->format != PIXFORMAT_JPEG) free(jpeg_buf);
esp_camera_fb_return(fb);
gBusy = false;
return false;
}
String url = String(SERVER_BASE) + "/enroll?name=" + urlEncode(name);
if (isDigits(gJobPersonId))
{
url += String("&person_id=") + gJobPersonId;
}
else if (gJobPersonId.length())
{
Serial.println(String("[ENROLL] skipping invalid person_id=") + gJobPersonId);
}
// ENROLL runs DeepFace too (slower), so give it a longer timeout.
String resp = postFrameWithRetry(url, jpeg_buf, jpeg_len, 60000);
if (resp.startsWith("HTTP 400")) {
showStatus("COBA LAGI...", TFT_ORANGE, "Wajah tidak jelas");
} else if (resp.startsWith("HTTP ERR") || resp.startsWith("HTTP 4") || resp.startsWith("HTTP 5") || resp.startsWith("WIFI")) {
showStatus("ERR: " + resp, TFT_RED, "");
} else {
showStatus("ENROLL SUKSES", TFT_GREEN, resp);
gLastActionSuccess = true;
}
}
else if (cmd == "RECOG")
{
// Include threshold + uid from job so server uses the asked params and can log attendance.
String uid = "";
if (gJobRecogUid.length())
{
uid = gJobRecogUid;
}
else if (gExpectedUid.length())
{
uid = gExpectedUid;
Serial.println(String("[RECOG] using expected uid=") + gExpectedUid);
}
String endpoint = "/recognize";
if (uid.length())
{
endpoint = "/rfid/attendance";
}
String url = String(SERVER_BASE) + endpoint + "?threshold=" + String(gJobRecogThreshold, 2);
if (uid.length())
{
url += String("&uid=") + uid;
}
Serial.println(String("[RECOG] endpoint=") + endpoint + String(" uid=") + uid);
// RECOG can be slow (DeepFace + DB compare). ERR -11 = read timeout.
showStatus("MENGIRIM...", TFT_CYAN, endpoint);
Serial.println(String("[RECOG] upload url=") + url);
String resp = postFrameWithRetry(url, jpeg_buf, jpeg_len, 60000);
Serial.println(String("[RECOG] resp=") + resp);
if (resp.startsWith("HTTP ERR") || resp.startsWith("HTTP 4") || resp.startsWith("HTTP 5") || resp.startsWith("WIFI"))
{
Serial.println("[RECOG] upload FAILED: " + resp);
// Jika server merespon HTTP 400, kemungkinan besar "No face detected"
if (resp.indexOf("400") >= 0) {
showStatus("COBA LAGI...", TFT_ORANGE, "Wajah tidak jelas");
} else {
showStatus("ERR: " + resp, TFT_RED, "");
}
if (fb->format != PIXFORMAT_JPEG) free(jpeg_buf);
esp_camera_fb_return(fb);
gBusy = false;
return false;
}
// If we have an expected RFID identity, validate it.
bool hasExpected = gExpectedName.length() > 0 && (millis() - gExpectedSetAt) <= EXPECTED_TTL_MS;
if (hasExpected)
{
// We expect server JSON payload to contain the recognized name somewhere.
// Typical examples:
// {"name":"andi","distance":0.31}
// {"match":true,"name":"andi"...}
// We'll do a simple substring match, case-insensitive.
String respLower = resp;
respLower.toLowerCase();
String expLower = gExpectedName;
expLower.toLowerCase();
if (respLower.indexOf(expLower) >= 0)
{
showStatus("ABSEN SUKSES", TFT_GREEN, gExpectedName);
gLastActionSuccess = true;
}
else
{
showStatus("ABSEN GAGAL", TFT_RED, "Wajah tidak cocok");
}
// Clear expectation after one attempt.
gExpectedName = "";
gExpectedUid = "";
gExpectedSetAt = 0;
}
else
{
if (resp.indexOf("true") >= 0 || resp.indexOf("\\"match\\": true") >= 0 || resp.indexOf("\\"match\\":true") >= 0) {
showStatus("ABSEN SUKSES", TFT_GREEN, "");
gLastActionSuccess = true;
} else {
showStatus("SERVER MERESPON", TFT_CYAN, resp);
}
}
}
else
{
// Ignore unknown commands to avoid spamming the TFT (UART noise can trigger this).
// keep silent
}
esp_camera_fb_return(fb);
gBusy = false;
return true;'''
new_handle_cmd = ''' if (cmd.startsWith("ENROLL ") || cmd == "RECOG")
{
if (gUploadTaskBusy) {
Serial.println("[CMD] Upload task is currently busy!");
gBusy = false;
return false;
}
if (cmd.startsWith("ENROLL ")) {
String name = cmd.substring(7);
name.trim();
if (name.length() == 0) {
showStatus("NAMA KOSONG", TFT_RED, "Silakan isi nama");
gBusy = false; return false;
}
gUploadUrl = String(SERVER_BASE) + "/enroll?name=" + urlEncode(name);
if (isDigits(gJobPersonId)) gUploadUrl += String("&person_id=") + gJobPersonId;
} else {
String uid = gJobRecogUid.length() ? gJobRecogUid : gExpectedUid;
String endpoint = uid.length() ? "/rfid/attendance" : "/recognize";
gUploadUrl = String(SERVER_BASE) + endpoint + "?threshold=" + String(gJobRecogThreshold, 2);
if (uid.length()) gUploadUrl += "&uid=" + uid;
}
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
showStatus("KAMERA ERROR", TFT_RED, "Camera fb null");
gBusy = false; return false;
}
// Konversi ke JPEG secepat kilat (beberapa ms)
if (!frame2jpg(fb, 40, &gUploadBuf, &gUploadLen)) {
showStatus("JPEG ENCODE FAIL", TFT_RED, "");
esp_camera_fb_return(fb);
gBusy = false; return false;
}
esp_camera_fb_return(fb); // KEMBALIKAN FRAME SEGERA AGAR LAYAR TIDAK MACET!
gUploadCmd = cmd;
gUploadFinished = false;
gUploadTaskBusy = true; // TRIGGER TASK UPLOAD DI BACKGROUND
showStatus("MENGIRIM...", TFT_CYAN, "");
gBusy = false;
return true;
}
gBusy = false;
return true;'''
content = content.replace(old_handle_cmd, new_handle_cmd)
# 4. Handle Result in loop()
old_loop_retry = ''' if (now >= gNextCaptureTime)
{
Serial.println(String("[AUTO] Executing ") + gKeepTryingCmd);
showStatus("MEMPROSES...", TFT_CYAN, "Jangan bergerak", 2000);
handleCommand(gKeepTryingCmd);
lastJobPoll = millis(); // Reset poll timer
if (gLastActionSuccess)
{
postJobResult(true);
gKeepTryingCmd = ""; // Stop trying
gCameraActive = false; // Turn off camera on success
showStatus("BERHASIL", TFT_GREEN, "", 3000);
}
else
{
// Tunggu 1.5 detik sebelum ambil foto lagi agar preview kamera terlihat
gNextCaptureTime = millis() + 1500;
}
}'''
new_loop_retry = ''' // PROSES HASIL BACKGROUND UPLOAD
if (gUploadTaskBusy && gUploadFinished)
{
String resp = gUploadResult;
String cmd = gUploadCmd;
gUploadTaskBusy = false;
gUploadFinished = false;
if (cmd == "RECOG") {
if (resp.startsWith("HTTP 400")) {
showStatus("COBA LAGI...", TFT_ORANGE, "Wajah tidak jelas", 1000);
} else if (resp.startsWith("HTTP ERR") || resp.startsWith("HTTP 4") || resp.startsWith("HTTP 5") || resp.startsWith("WIFI")) {
showStatus("ERR: " + resp, TFT_RED, "", 1000);
} else {
bool hasExpected = gExpectedName.length() > 0 && (millis() - gExpectedSetAt) <= EXPECTED_TTL_MS;
if (hasExpected) {
String respLower = resp; respLower.toLowerCase();
String expLower = gExpectedName; expLower.toLowerCase();
if (respLower.indexOf(expLower) >= 0) {
showStatus("ABSEN SUKSES", TFT_GREEN, gExpectedName);
gLastActionSuccess = true;
} else {
showStatus("ABSEN GAGAL", TFT_RED, "Wajah tidak cocok", 1000);
}
gExpectedName = ""; gExpectedUid = ""; gExpectedSetAt = 0;
} else {
if (resp.indexOf("true") >= 0 || resp.indexOf("\\"match\\": true") >= 0 || resp.indexOf("\\"match\\":true") >= 0) {
showStatus("ABSEN SUKSES", TFT_GREEN, "");
gLastActionSuccess = true;
} else {
showStatus("SERVER MERESPON", TFT_CYAN, resp, 1000);
}
}
}
} else if (cmd.startsWith("ENROLL ")) {
if (resp.startsWith("HTTP 400")) {
showStatus("COBA LAGI...", TFT_ORANGE, "Wajah tidak jelas", 1000);
} else if (resp.startsWith("HTTP ERR") || resp.startsWith("HTTP 4") || resp.startsWith("HTTP 5") || resp.startsWith("WIFI")) {
showStatus("ERR: " + resp, TFT_RED, "", 1000);
} else {
showStatus("ENROLL SUKSES", TFT_GREEN, resp);
gLastActionSuccess = true;
}
}
if (gLastActionSuccess)
{
postJobResult(true);
gKeepTryingCmd = ""; // Stop trying
gCameraActive = false; // Turn off camera on success
showStatus("BERHASIL", TFT_GREEN, "", 3000);
}
else
{
// Tunggu 500ms saja sebelum mengambil foto berikutnya (karena upload selesai)
gNextCaptureTime = millis() + 500;
}
}
if (now >= gNextCaptureTime && !gUploadTaskBusy)
{
Serial.println(String("[AUTO] Capture frame untuk ") + gKeepTryingCmd);
handleCommand(gKeepTryingCmd);
lastJobPoll = millis(); // Reset poll timer
// Note: gNextCaptureTime will be set when upload finishes above
}'''
content = content.replace(old_loop_retry, new_loop_retry)
with open('esp32_serial_backend/esp32_serial_backend.ino', 'w', encoding='utf8') as f:
f.write(content)
print("Update complete")