扫码成功以后,还需要业务边界
这次把S3-EYE板载OV2640的画面送到真实Quirc解码器,再把完整字节交给一个有限状态门禁。只接受精确六字节ITEM:A或ITEM:B,LCD只显示本站生成的有限ASCII状态;不访问码中的URL、不运行命令,也不控制电机。
目标是分清“解出内容”“业务接受”“屏幕显示”和“一个新物件”四件事。本站已经完成同源主机解码和指定SDK构建,尚未取得指定实板的镜头、LCD、速度、扫码距离或电气结果。空物理记录表随例程提供,成功条件由读者逐项执行。
参考:[1] 固定WHO二维码示例:帧格式、任务与默认配置[2] 固定WhoQRCode:灰度转换、Quirc与首个成功回调[3] 固定LCD应用:文本副本、锁与展示帧数
锁定板修订与内部引脚,避免挪用串口脚
仅用ESP32-S3-EYE-MB_V2.2、原装OV2640与ESP32-S3-EYE-SUB V1.1(1.3英寸240×240 ST7789 LCD),8MiB Quad Flash和8MiB Octal PSRAM。不是DevKitC加飞线相机。电脑通过原生Micro-USB Serial/JTAG的19/20供电和下载,没有USB-to-UART桥。
只连接可靠USB数据线;排线、子板和连接检查都先断USB,不带电插拔相机或LCD。无电池、SD、音频、外部按钮或负载。固定指南要求GPIO3电源LED开漏,v2.2有R83;本应用不需要该LED,直接不初始化或写GPIO3。
| 板载用途 | BSP6.0.0引脚;不是外接跳线 |
|---|---|
| 相机SCCB | SDA4 / SCL5,400kHz |
| 相机DVP | XCLK15 / PCLK13 / VSYNC6 / HREF7;D0..7=11/9/8/10/12/18/17/16 |
| LCD SPI | SCLK21 / MOSI47 / DC43 / CS44 / 背光48;reset NC |
| 原生USB | 19/20;43/44属于LCD,不能再套UART桥接法 |
| GPIO3/SD | 本应用不改GPIO3,不挂载或格式化SD |
参考:[4] 固定S3-EYE指南:板修订、原生USB和GPIO3[5] BSP6.0.0引脚及板能力[6] BSP6.0.0相机格式及初始化
完整本地WHO组件与正常Registry锁一起保留
固定ESP-IDF5.5.5/GCC14.2.0和WHO commit 1abda05e…,下载包含必要WHO组件及完整MIT许可,不要求克隆整仓或全部子模块。Registry直接依赖精确esp-dl3.3.8、BSP6.0.0、esp_video2.0.1、Quirc1.2.0、esp_lvgl_port2.8.0~1和LVGL9.5.0;dependencies.lock锁定传递组合。
QIO8MiB Flash、OPI PSRAM80MHz/XIP、CPU240MHz、main栈16KiB、FreeRTOS1000Hz。分区表偏移0x9000,NVS0xa000/0x6000,factory0x10000/0x6f0000,没有OTA槽。main不初始化Wi-Fi、SD或音频;依赖存在不表示这些功能已运行。
# ---- main/idf_component.yml ----
dependencies:
idf: "==5.5.5"
espressif/esp-dl: "==3.3.8"
espressif/esp32_s3_eye: "==6.0.0"
espressif/esp_video: "==2.0.1"
espressif/quirc: "==1.2.0"
espressif/esp_lvgl_port: "==2.8.0~1"
lvgl/lvgl: "==9.5.0"参考:[1] 固定WHO二维码示例:帧格式、任务与默认配置[5] BSP6.0.0引脚及板能力[7] 固定Registry Quirc1.2.0包与许可
灰度、纠错与payload长度各管一层
BSP选择OV2640的240×240 RGB565大端25fps模式,WHO采集用RGB565X、四帧并对S3-EYE垂直翻转;转换器再提供同尺寸灰度给Quirc。25fps是格式配置,不是本站测得扫码速度。采集/LCD在core0,QR任务在core1、栈25600字节,不能用刷新画面代替解码验证。
固定WHO在同帧第一个成功解码回调后break,所以先让画面只有一个码;遍历顺序不是业务优先级。Quirc纠错也不是签名或物品身份认证。原WHO按NUL结尾创建字符串,二进制ITEM:A加空字节再加X会被截成允许的A;本包唯一WHO补丁使用payload_len构造字符串,原字节和可逆patch留证。
参考:[1] 固定WHO二维码示例:帧格式、任务与默认配置[2] 固定WhoQRCode:灰度转换、Quirc与首个成功回调[6] BSP6.0.0相机格式及初始化[9] WHO帧采集:RGB565X、四帧与S3翻转
精确六字节门禁:重复与无结果不会变成新物件
生产Gate只由QR任务拥有。第一次合法A/B或异于上次的合法ID才加一次接受读数;同ID是REPEAT;非法长度、大小写、URL、ITEM:C、空字节都不改变last/count。uint64计数到上限后拒绝新ID,不回绕。
A→B→A得到3次接受事件,不能叫3个不同物件或库存数。同A移走再放回仍重复,因为没有存在传感器或重装填协议。无解码、空白帧、模糊、等一会儿及LCD文字消失都不清last;物理RESET重新开始,本例没有NVS计数或远端去重。
// ---- main/qr_gate.hpp ----
/* SPDX-License-Identifier: MIT */
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstring>
namespace v04 {
enum class Event { ACCEPTED, REPEAT, INVALID, COUNT_FULL };
struct Gate {
char last=0;
uint64_t accepted_reads=0;
Event consume(const char *bytes,size_t length) {
if(!bytes||length!=6||std::memcmp(bytes,"ITEM:",5)!=0||(bytes[5]!='A'&&bytes[5]!='B'))return Event::INVALID;
if(last==bytes[5])return Event::REPEAT;
if(accepted_reads==UINT64_MAX)return Event::COUNT_FULL;
last=bytes[5];++accepted_reads;return Event::ACCEPTED;
}
};
} // namespace v04回调只向屏幕交付自有文本副本
继承WHO的LCD应用,覆盖结果回调后先调用生产Gate,再构造有限ASCII文字交回基类;不会把原payload直接显示、打印或执行。显示线程取得副本,业务状态仍留在QR任务。基类的展示帧数到期只清文本,不重置Gate。
任务run请求成功的启动标记不是相机或LCD已经工作的证明。原WHO若干分配/图像转换路径没有完整的可恢复OOM处理;panic、栈或任务失败先保存原log和同次ELF,停止实验后RESET,不把桌面通过扩展成长稳保证。
// ---- main/main.cpp ----
/* SPDX-License-Identifier: MIT */
#include <cstdio>
#include <cinttypes>
#include "frame_cap_pipeline.hpp"
#include "who_qrcode_app_lcd.hpp"
#include "bsp/esp-bsp.h"
#include "qr_gate.hpp"
class OwnedReader final : public who::app::WhoQRCodeAppLCD {
public:
OwnedReader(who::frame_cap::WhoFrameCap *cap):WhoQRCodeAppLCD(cap,BSP_LCD_H_RES,BSP_LCD_V_RES){}
protected:
void qrcode_result_cb(const std::string &payload) override {
const auto event=gate_.consume(payload.data(),payload.size());
char status[96];
switch(event){
case v04::Event::ACCEPTED:std::snprintf(status,sizeof(status),"ACCEPT %c / read # %" PRIu64,gate_.last,gate_.accepted_reads);break;
case v04::Event::REPEAT:std::snprintf(status,sizeof(status),"SAME %c / no new read",gate_.last);break;
case v04::Event::INVALID:std::snprintf(status,sizeof(status),"INVALID / only ITEM:A or ITEM:B");break;
default:std::snprintf(status,sizeof(status),"STOP / read counter full");break;
}
/* Display only owned finite ASCII text; never echo or execute payload. */
WhoQRCodeAppLCD::qrcode_result_cb(status);
}
private:
v04::Gate gate_; // Only this QR task owns state; display receives a copy.
};
extern "C" void app_main(void){
vTaskPrioritySet(xTaskGetCurrentTaskHandle(),5);
/* GPIO3 power LED is left untouched; this reader needs only camera/LCD. */
auto *cap=get_dvp_frame_cap_pipeline();
auto *reader=new OwnedReader(cap);
if(!reader->run()){std::printf("STOP,reader_task_start_failed\n");abort();}
std::printf("V04,reader_tasks_requested; camera optical/LCD output must be observed physically\n");
}参考:[1] 固定WHO二维码示例:帧格式、任务与默认配置[2] 固定WhoQRCode:灰度转换、Quirc与首个成功回调[3] 固定LCD应用:文本副本、锁与展示帧数
构建留档以后,再选择自己的原生USB端口
激活自己的IDF5.5.5,进入下载工程根运行idf.py -B build-v04 -D IDF_TARGET=esp32s3 build。保存原log、源码和锁、sdkconfig、compile_commands、ELF/map、app/bootloader/分区bin与flasher_args.json并逐SHA归档。app.bin不能当完整Flash写到0x0。
先按E09备份原8MiB完整Flash、布局和SHA,再在确认板型与端口后执行idf.py -B build-v04 -p YOUR_NATIVE_USB_PORT flash monitor。macOS通常为cu.usbmodem,Linux替换实际ttyACM,Windows替换实际COM;不能照抄另一块板的端口。原生USB可能重枚举,无法下载时按住BOOT、点RESET、松BOOT,再核新端口,Ctrl+]退出monitor。
- 核板修订、原装相机/LCD与USB线,断电检查连接。
- 核5.5.5和esp32s3、完整本地组件及原锁;保存实际构建原log和同次产物。
- 自行备份、选择真实端口刷入,观察完整串口与实际LCD;本站没有执行实体烧录。
正常、重复、非法和模糊分别做光学验收
先用fixtures/normal-a.png打印或另一屏显示,让一个码占画面足够面积且保留四模块白边,逐步调清楚;不声称固定几厘米一定成功。先A应ACCEPT/read#1,保持A应SAME;换B再A应read#2/#3。再放URL、ITEM:C、二进制NUL夹具,预期INVALID且last/count不变。
遮挡、移走或模糊再回同A,不应自动加读数。强模糊可能完全没找到/解出码,没有业务回调;这种情况不能写作INVALID。按正常、斜视、模糊、低照、反光、重复、非法内容逐条件记录真值、原log、屏幕/读数、失败和恢复,一次只改一个因素。连续帧高度相关,不把100帧同纸当100个独立样本。
- 做到这里,你应该看到
- A/B交替、重复不增、非法保留、空白不重装填均符合定义,才继续后续实验;记录表不能只留成功行。
参考:[2] 固定WhoQRCode:灰度转换、Quirc与首个成功回调[8] qrcode8.2生成器:静区、尺寸与二进制数据
桌面真实解码能查什么,不能证明什么
Gate的7组ASan/UBSan检查直接包含生产头,覆盖NUL原反例、非法/空长度、重复、A→B→A与64bit溢出。tests/run_decode.py把未改的Registry同版Quirc四C/两头真正编译执行,接生产Gate跑10种生成灰度输入;正常A/B、重复、无关URL、二进制NUL、轻高斯模糊、强模糊与空白均检查了实际结果。
README给出独立venv、固定qrcode8.2/Pillow12.3.0安装和python tests/run_decode.py --output NEW_DIRECTORY命令,拒绝覆盖旧结果。生成PNG是240×240、四模块静区的合成夹具,不是相机照片;模糊半径只是固定软件条件。Quirc包1.2.0内部version函数仍返回1.0,身份由锁和实际源SHA证明。桌面结果不证明相机颜色转换、距离、失焦或LCD已经工作。
参考:[2] 固定WhoQRCode:灰度转换、Quirc与首个成功回调[7] 固定Registry Quirc1.2.0包与许可[8] qrcode8.2生成器:静区、尺寸与二进制数据
按失败层排查,动作系统另设存在与重装填
构建错先核目标、SDK、manifest、锁与本地组件;画面错先留原错误与ELF,断电查MB_V2.2/OV2640/子板连接、供电、RGB565X和BSP。LCD错尤其查43/44是否被套成UART;不要同时乱改所有引脚。一直REPEAT先看last定义,不靠加超时让计数看起来变化。
无码或纠错失败依次查单码/白边、光照、清晰度、反光和运动;panic查同次OPI8MiB/XIP、供电/栈和原backtrace。异常停止后RESET,必要时恢复自己的完整归档。需要物件计数或V06执行器时,必须另有存在检测、重装填、动作与超时边界,不能把本页扫码事件直接接电机。
例程保留WHO MIT、Quirc ISC和各SDK组件许可;本地应用/Gate/原创图/合成夹具为MIT。无实板相机、LCD、识别准确率、距离、时延、电气或长期结果,records/qr-observations.csv仅表头。
参考:[1] 固定WHO二维码示例:帧格式、任务与默认配置[3] 固定LCD应用:文本副本、锁与展示帧数[4] 固定S3-EYE指南:板修订、原生USB和GPIO3[7] 固定Registry Quirc1.2.0包与许可
资料来源与版本说明
已读固定WHO完整必要闭包与BSP相机/LCD/引脚/板指南;14份鲜取、13项安装字节一致及一处明确README链接归一化另有原差异记录。资料核对日期:。资料核对、程序构建与实板测试分别记录,不互相替代。