import re with open('esp32_serial_backend/esp32_serial_backend.ino', 'r', encoding='utf8') as f: content = f.read() # 1. Include img_converters.h if '#include "img_converters.h"' not in content: content = content.replace('#include "esp_camera.h"', '#include "esp_camera.h"\n#include "img_converters.h"') # 2. Refactor postFrame to take buffer instead of fb content = content.replace( '''String postFrame(const String &url, camera_fb_t *fb, uint32_t timeoutMs) { if (!fb) return "ERROR no_frame";''', '''String postFrame(const String &url, uint8_t *buf, size_t len, uint32_t timeoutMs) { if (!buf || len == 0) return "ERROR no_frame";''') content = content.replace('int code = http.POST(fb->buf, fb->len);', 'int code = http.POST(buf, len);') content = content.replace( '''String postFrameWithRetry(const String &url, camera_fb_t *fb, uint32_t timeoutMs, int retries = 1) { String resp = postFrame(url, fb, timeoutMs); while (retries-- > 0) { if (resp == "HTTP ERR -11" || resp == "HTTP ERR -1") { delay(250); resp = postFrame(url, fb, timeoutMs); continue; } break; } return resp; }''', '''String postFrameWithRetry(const String &url, uint8_t *buf, size_t len, uint32_t timeoutMs, int retries = 1) { String resp = postFrame(url, buf, len, timeoutMs); while (retries-- > 0) { if (resp == "HTTP ERR -11" || resp == "HTTP ERR -1") { delay(250); resp = postFrame(url, buf, len, timeoutMs); continue; } break; } return resp; }''') # 3. Replace the entire camera fetching block in handleCommand old_camera_block = ''' if (!reinitCamera(PIXFORMAT_JPEG)) { showStatus("GAGAL JEPRET", TFT_RED, "JPEG Init error"); gBusy = false; return false; } delay(80); 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; } // Debug: verify we're actually sending a JPEG. if (fb->len >= 3) { Serial.printf("CAP fmt=%d len=%u magic=%02X %02X %02X\\n", (int)fb->format, (unsigned)fb->len, fb->buf[0], fb->buf[1], fb->buf[2]); } else { Serial.printf("CAP fmt=%d len=%u\\n", (int)fb->format, (unsigned)fb->len); } // If the frame isn't JPEG or doesn't have JPEG SOI marker, don't upload. if (fb->format != PIXFORMAT_JPEG || fb->len < 3 || !(fb->buf[0] == 0xFF && fb->buf[1] == 0xD8 && fb->buf[2] == 0xFF)) { showStatus("BUKAN JPEG", TFT_RED, "Format salah"); esp_camera_fb_return(fb); reinitCamera(PIXFORMAT_RGB565); gBusy = false; return false; }''' new_camera_block = ''' // 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, 80, &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; }''' content = content.replace(old_camera_block, new_camera_block) # 4. Modify postFrameWithRetry calls in handleCommand content = content.replace('postFrameWithRetry(url, fb, 60000);', 'postFrameWithRetry(url, jpeg_buf, jpeg_len, 60000);') # 5. Modify cleanup in handleCommand old_cleanup_enroll = ''' showStatus("NAMA KOSONG", TFT_RED, "Silakan isi nama"); esp_camera_fb_return(fb); reinitCamera(PIXFORMAT_RGB565); gBusy = false;''' new_cleanup_enroll = ''' showStatus("NAMA KOSONG", TFT_RED, "Silakan isi nama"); if (is_allocated) free(jpeg_buf); esp_camera_fb_return(fb); gBusy = false;''' content = content.replace(old_cleanup_enroll, new_cleanup_enroll) old_cleanup_recog_fail = ''' if (resp.startsWith("HTTP ERR") || resp.startsWith("HTTP 4") || resp.startsWith("HTTP 5") || resp.startsWith("WIFI")) { Serial.println("[RECOG] upload FAILED"); showStatus("SERVER DISABLE/RTO", TFT_RED, ""); esp_camera_fb_return(fb); reinitCamera(PIXFORMAT_RGB565); gBusy = false; return false; }''' new_cleanup_recog_fail = ''' if (resp.startsWith("HTTP ERR") || resp.startsWith("HTTP 4") || resp.startsWith("HTTP 5") || resp.startsWith("WIFI")) { Serial.println(String("[RECOG] upload FAILED: ") + resp); showStatus("ERR: " + resp, TFT_RED, ""); // show the real error on screen if (is_allocated) free(jpeg_buf); esp_camera_fb_return(fb); gBusy = false; return false; }''' content = content.replace(old_cleanup_recog_fail, new_cleanup_recog_fail) old_final_cleanup = ''' esp_camera_fb_return(fb); reinitCamera(PIXFORMAT_RGB565); gBusy = false; return true;''' new_final_cleanup = ''' if (is_allocated) free(jpeg_buf); esp_camera_fb_return(fb); gBusy = false; return true;''' content = content.replace(old_final_cleanup, new_final_cleanup) with open('esp32_serial_backend/esp32_serial_backend.ino', 'w', encoding='utf8') as f: f.write(content) print("Update complete")