fix(qr): adaptive frame size + OV3660 tuning

This commit is contained in:
Claude Worker claude2
2026-06-11 02:03:13 +02:00
parent 0e4cea9c39
commit 132b88337b
+45 -28
View File
@@ -64,43 +64,48 @@ static void scan_task(void *arg) {
vTaskDelete(NULL);
return;
}
if (quirc_resize(q, QR_WIDTH, QR_HEIGHT) < 0) {
ESP_LOGE(TAG, "quirc_resize(%d,%d) failed", QR_WIDTH, QR_HEIGHT);
quirc_destroy(q);
esp_camera_deinit();
s_task = NULL;
vTaskDelete(NULL);
return;
}
// quirc is (re)sized to the camera's ACTUAL frame geometry. The OV3660
// on this board does not always deliver exactly QVGA — adapt at runtime
// instead of dropping every off-size frame (the old fixed 320x240 guard
// silently starved quirc when the sensor returned another size).
int q_w = 0, q_h = 0;
while (s_run) {
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) { vTaskDelay(pdMS_TO_TICKS(50)); continue; }
// Guard: only copy if the frame geometry matches what quirc was sized to.
if (fb->width == QR_WIDTH && fb->height == QR_HEIGHT) {
if (s_preview_cb) s_preview_cb(fb->buf, QR_WIDTH, QR_HEIGHT);
if (fb->width != q_w || fb->height != q_h) {
if (quirc_resize(q, fb->width, fb->height) < 0) {
ESP_LOGE(TAG, "quirc_resize(%d,%d) failed", fb->width, fb->height);
esp_camera_fb_return(fb);
vTaskDelay(pdMS_TO_TICKS(50));
continue;
}
q_w = fb->width;
q_h = fb->height;
ESP_LOGI(TAG, "scanning at sensor geometry %dx%d", q_w, q_h);
}
uint8_t *img = quirc_begin(q, NULL, NULL);
memcpy(img, fb->buf, (size_t)QR_WIDTH * QR_HEIGHT);
quirc_end(q);
if (s_preview_cb) s_preview_cb(fb->buf, q_w, q_h);
int n = quirc_count(q);
for (int i = 0; i < n; i++) {
// static: ~12.5 KB total — too large for the task stack; single-instance task.
static struct quirc_code code;
static struct quirc_data data;
quirc_extract(q, i, &code);
if (quirc_decode(&code, &data) == 0) {
// data.payload is a NUL-terminated uint8_t string for text QRs.
if (seq_validator_feed(&s_seq, (const char *)data.payload)) {
if (s_cb) s_cb();
s_run = false;
}
uint8_t *img = quirc_begin(q, NULL, NULL);
memcpy(img, fb->buf, (size_t)q_w * q_h);
quirc_end(q);
int n = quirc_count(q);
for (int i = 0; i < n; i++) {
// static: ~12.5 KB total — too large for the task stack; single-instance task.
static struct quirc_code code;
static struct quirc_data data;
quirc_extract(q, i, &code);
if (quirc_decode(&code, &data) == 0) {
// data.payload is a NUL-terminated uint8_t string for text QRs.
ESP_LOGI(TAG, "QR decoded: %s", (const char *)data.payload);
if (seq_validator_feed(&s_seq, (const char *)data.payload)) {
if (s_cb) s_cb();
s_run = false;
}
}
} else {
ESP_LOGW(TAG, "unexpected frame size %dx%d, skipping", fb->width, fb->height);
}
esp_camera_fb_return(fb); // always return the frame
@@ -126,6 +131,18 @@ esp_err_t qr_puzzle_start(const char *const *expected, size_t count, qr_solved_c
return e;
}
// Tune the OV3660 for QR decoding off a backlit LCD: max contrast +
// sharpness, and cap the gain/exposure so the bright screen does not
// bloom and wash out the QR modules.
sensor_t *s = esp_camera_sensor_get();
if (s) {
if (s->set_contrast) s->set_contrast(s, 2);
if (s->set_sharpness) s->set_sharpness(s, 2);
if (s->set_gainceiling) s->set_gainceiling(s, GAINCEILING_2X);
if (s->set_whitebal) s->set_whitebal(s, 1);
if (s->set_aec2) s->set_aec2(s, 1);
}
seq_validator_init(&s_seq, expected, count);
s_cb = cb;
s_run = true;