开始前适用开发板:ESP32-P4 + ESP32-C5 · 开发框架:ESP-IDF
适用开发板:P4C5 · 工程
04.advanced.qrcode_detection· targetesp32p4
摄像头:MIPI CSI(SC2336)或 USB UVC(由 sdkconfig 选择)
学习时长:约 25 分钟 · 难度:★★★☆☆
学习目标
完成本节后,你将能够:
- 理解实时二维码/条形码识别的完整流程
- 掌握
esp_code_scanner组件的使用方法 - 了解 CSI 与 UVC 两种摄像头输入的切换方式
- 处理 RGB565 字节序与 LVGL 显示的兼容问题
1. 这个例子在干嘛
摄像头实时取帧,调用 esp_code_scanner 解码 QR Code / Data Matrix / PDF417 等二维码/条形码,LVGL 大屏上方显示摄像头预览、下方显示识别结果。纯本地处理,无需联网。
┌───────────────┐ ┌──────────────┐ ┌───────────────┐ ┌──────────┐
│ MIPI CSI 或 │───→│ RGB565 帧 │───→│esp_code_scanner│───→│ LVGL 大屏│
│ USB UVC 摄像头│ │ 缓冲 (PSRAM)│ │ 二维码解码 │ │ 预览+结果│
└───────────────┘ └──────────────┘ └───────────────┘ └──────────┘
与 Korvo 版的区别:
| 对比项 | P4C5 | Korvo |
|---|---|---|
| 摄像头 | MIPI CSI 或 USB UVC(可选) | DVP 并口 |
| 屏幕 | MIPI 480×800 大屏 | SPI 240×280 小屏 |
| 预览画质 | 更高分辨率 | QVGA |
| 双路支持 | CSI + UVC 可切换 | 仅 DVP |
Korvo 版见 Korvo qrcode。
2. 测试准备
- 用手机生成或打开一个二维码(文字/URL/Wi-Fi 配置码均可)
- 距离摄像头 10~30 cm,码要占画面足够大比例
- 光线充足,避免反光和模糊
- 注意:印在纸上的二维码识别率通常比手机屏幕高(无反光)
3. 编译烧录
cd P4_C5_4.3_Firmware\04.advanced.qrcode_detection
idf.py set-target esp32p4
idf.py menuconfig # 选择摄像头类型 CSI / UVC
idf.py build flash monitor
menuconfig 摄像头选择
| 选项 | 说明 |
|---|---|
| CSI 模式 | 使用 SC2336 MIPI CSI 摄像头(FPC 直连) |
| UVC 模式 | 使用 USB 摄像头(需 GPIO20 升压 + Type-A 口) |
UVC 注意:USB 摄像头枚举较慢(5~10 秒),参考 camera_lvgl_display 中的 UVC 说明。
4. 运行现象
| 区域 | 显示内容 | 说明 |
|---|---|---|
| 屏幕上方 | 摄像头实时预览画面 | 连续刷新 |
| 屏幕下方 | 识别到的二维码内容 | 未识别时为空或提示 |
| 串口 | Decoded: <content> | 每次识别成功打印 |
支持的码制:
- QR Code(最常用)
- Data Matrix
- PDF417
- EAN-13、Code 128 等一维条形码
5. 关键文件与代码解析
| 文件 | 作用 | 重点关注 |
|---|---|---|
main/app_main.c | NVS、LVGL、页面加载 | page_cam_load() |
main/page_cam.c | 核心:UI + Cam_Task + scanner | 扫描循环 |
main/app_camera.c | 摄像头初始化(CSI/UVC) | 配置参数 |
核心扫描循环(Cam_Task)
void Cam_Task(void *arg) {
while (1) {
// 1. 获取一帧 RGB565
frame = camera_get_frame();
// 2. 调用二维码扫描器
results = esp_code_scanner_scan(frame->buf, frame->width, frame->height);
// 3. RGB565 字节序转换(CSI 与 LVGL 字节序不同)
swap_bytes(frame->buf, frame->len);
// 4. 更新 LVGL 图片(预览画面)
lv_img_set_src(img_obj, &img_dsc);
// 5. 如果扫描成功,更新 label
if (results->count > 0) {
lv_label_set_text(label, results->data);
printf("Decoded: %s\n", results->data);
}
// 6. 归还帧缓冲
camera_return_frame(frame);
}
}
RGB565 字节序
为什么需要 swap?
CSI 摄像头输出的 RGB565 是大端序,LVGL 期望小端序。不做转换会导致颜色异常(红蓝互换),但 不影响二维码识别(scanner 可以处理两种字节序)。
检验方法:拍红色物体 → 屏幕显示蓝色 = 需要 swap。
6. 扫描性能优化
| 优化项 | 方法 | 效果 |
|---|---|---|
| 分辨率 | 降低到 QVGA(320×240) | 扫描更快 |
| 扫描范围 | 只扫描画面中心区域 | 减少计算量 |
| 码制限制 | 只启用 QR Code(禁用其他) | 加速匹配 |
| 帧跳过 | 每 N 帧扫描一次 | CPU 占用降低 |
分辨率不是越高越好:过高分辨率意味着更多像素需要分析,反而降低识别速度。QVGA 是常用甜点。
7. 常见问题与排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 一直扫不出 | 距离/大小/光线不足 | 调整到 10~30cm;补充光源 |
| 一直扫不出 | 二维码版本过高或特殊格式 | 用标准 QR Code 测试 |
| 识别很慢 | 分辨率过高 | 降低 frame_size |
| 画面花屏 | RGB565 字节序未 swap | 检查 page_cam.c swap 代码 |
| 画面花屏 | buffer 宽高与 img_dsc 不匹配 | 确认一致 |
| UVC 无图 | GPIO20 升压未开 | 确认硬件;等待枚举完成 |
| CSI 黑屏 | FPC 排线松动 | 重新插拔;先跑 board_test |
| 编译缺组件 | esp_code_scanner 下载失败 | idf.py reconfigure |
8. 动手改造建议
- Wi-Fi 二维码自动连接:识别 Wi-Fi QR Code 后自动解析 SSID/密码并
esp_wifi_connect() - URL 打开提示:识别到 URL 后在 label 显示「已识别 URL」
- 成功音效:识别成功时 GPIO 驱动蜂鸣器或 LED 闪烁
- 历史记录:LVGL list 控件保存最近 10 条扫码记录
- 批量扫码:连续扫描不同二维码,去重并统计
- UVC 专项:跑
05.test.usb_qr_recognition工程
9. 知识延伸
- 二维码容错:QR Code 有 4 个纠错等级(L 7% / M 15% / Q 25% / H 30%),即使部分损坏也能识别
- esp_code_scanner 与 ZBar:ESP 版基于 ZBar 优化,减少内存使用,适配嵌入式
- 多码并存:一帧中可能存在多个二维码,
scan返回的是结果列表 - CSI vs UVC 性能:CSI 直连 P4 芯片,帧率和延迟优于 USB UVC
10. 下一步
| 方向 | 推荐例程 | 说明 |
|---|---|---|
| 人脸识别 | face_recognition | AI 视觉进阶 |
| 摄像头预览 | camera_lvgl_display | 双路摄像头 |
| 整板测试 | p4c5_board_test | 验证硬件 |
酷世DIY · Kevincoooool