fix(qr): adaptive frame size + OV3660 tuning
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user