news 2026/10/9 1:57:30

TagTinker 红外 ESL 引擎深度解析:PP4 微秒时序 + RLE 流式编码如何驱动电子价签

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TagTinker 红外 ESL 引擎深度解析:PP4 微秒时序 + RLE 流式编码如何驱动电子价签

TagTinker 红外 ESL 引擎深度解析:PP4 微秒时序 + RLE 流式编码如何驱动电子价签

【免费下载链接】TagTinkerFlipper Zero app for ESL research using IR. All based on https://www.furrtek.org/?a=esl项目地址: https://gitcode.com/gh_mirrors/ta/TagTinker

TagTinker 是一款运行在 Flipper Zero 上的红外 ESL(电子货架标签)研究应用,其核心是一套RLE 流式红外引擎:通过 PP4 微秒级时序调制红外载波,把 RLE 压缩后的位图逐帧流式发送,让价签显示文本、甚至红黄双色(Accent)图片。本文将从时序到编码,带你完整看懂这套引擎的运转原理。

上图正是 TagTinker 的输出效果:黑色曲线 + 红色填充面积图,红色部分就是由"双平面 Accent 色编码"实现的。

一、为什么红外 ESL 协议值得研究

超市货架上的电子价签大多靠红外(IR)信号更新——和电视遥控器同一类信号。Flipper Zero 官方也点明过:

"这就是纯红外信号,和你家遥控器一样。整个安全性依赖于协议的保密性。"

TagTinker 就是基于对这类协议逆向研究的工具:它用 Flipper 的 IR LED 复现价签基站的下行信号,支持文本与自定义位图写入,兼容单色与红/黄 Accent 色图形价签。整个协议栈分三层,各有所分工:

层次文件职责
协议层tagtinker_proto.c组帧、RLE 压缩、像素流编码
驱动层tagtinker_ir.cPP4 微秒时序、载波调制、重复发送
场景层scenes/目标管理、图像选择、传输界面

二、PP4 时序引擎:把字节"雕刻"进微秒里

2.1 载波与符号:2 比特一个符号

红外传输的第一步是用 PWM 生成约 38 kHz 的载波。驱动层用 STM32 的 TIM1 CH3N 通道驱动 IR LED,预计算好自动重装载与比较值(ARR=50、CCR=25),开启即载波、关闭即停,见 tagtinker_ir.c 中的CARRIER_ARR/CARRIER_CCR与carrier_on()/carrier_off()。

PP4 编码的关键在于:每个符号只传 2 比特,符号值不靠电平,而靠"突发后的间隙长度"来表达。也就是说,红外灯先亮一段固定长度的突发,再熄灭一段可变长度的间隙——间隙长短就是符号本身。四个符号对应的间隙由一张预计算表给出(64 MHz 下的 CPU 周期数),见 tagtinker_ir.c:

#define PP4_BURST_CYCLES 2581 // 突发 ≈ 40 µs static const uint32_t pp4_gap_cycles[4] = { 3871, /* symbol 0 ~60 us */ 15483, /* symbol 3 ~242 us */ 7741, /* symbol 2 ~121 us */ 11612, /* symbol 1 ~181 us */ };

2.2 微秒级发送循环:DWT 计数器当秒表

要精确到微秒,普通delay不够用。TagTinker 直接读 Cortex-M 的DWT->CYCCNT 硬件周期计数器做忙等延时(delay_cycles,tagtinker_ir.c),再用临界区把整个帧的发送包起来,防止系统调用打断微秒级时序:

FURI_CRITICAL_ENTER(); send_frame_pp4(data, len); // 每字节拆成 4 个 2-bit 符号,突发+间隙交替 FURI_CRITICAL_EXIT();

发送节奏是:取 2 比特符号 → 载波突发 ≈40 µs → 关断并按符号值插入 60~242 µs 间隙,整帧结束前再补一次收尾突发,完整逻辑在 send_frame_pp4()。

2.3 重复与让出的平衡

价签射频链路不可靠,所以帧要连发多遍。tagtinker_ir_transmit()支持指定重复次数与帧间延迟(以 500 µs 为单位),每发满 5 遍就让出一次 OS(furi_delay_ms(1)),避免看门狗饿死——这个细节在 tagtinker_ir.c 中体现得很清楚。

三、RLE 流式压缩:像素不落地,边读边压

3.1 位级 RLE:颜色 1 比特 + 游程变长编码

Flipper 内存极小,传统做法(先把整幅图像压缩完再发)会撑爆 RAM。TagTinker 的解法是流式:图像不整体驻留,而是通过一个"逐像素回调"边取边压。

压缩格式是位级 RLE:

  1. 先写入 1 个像素颜色位(0/1);
  2. 紧接着写入一段游程长度,采用变长编码:占2×bitlen(count) - 1个比特;
  3. 颜色变化则重复 1-2,直到所有像素走完。

核心打包函数 tagtinker_pack_fn_rle() 只有 12 行,但配合预扫描函数 tagtinker_rle_fn_bit_length() 先算出压缩后总长度,即可判断"压了到底划不划算"。

3.2 像素流:双平面也能流式读取

TagTinkerPixelAtFn这个函数指针是整个流式设计的灵魂(定义于 tagtinker_proto.h):RLE 编码器只认识"第 i 个像素是什么",完全不知道图像从哪来——BMP 文件、文字渲染、双平面色卡对它都是同一种东西。

编码入口tagtinker_encode_fn_payload()在 Auto 模式下自动比较:RLE 结果更短才用压缩(comp_type=2),否则退回原始位流(comp_type=0),并且总长会向上补齐到帧容量整数倍,见 tagtinker_proto.c。

四、红黄双平面 Accent 色编码原理

单色价签只需 1 个位平面;而红/黄 Accent 标签要表达"黑前景 + 彩色前景"两种墨迹。TagTinker 用双平面实现(tagtinker_encode_planes_payload,tagtinker_proto.c):

  • 平面 1(primary):普通黑色前景位图;
  • 平面 2(secondary):Accent 色前景位图,两个平面首尾相接组成一条像素流,plane_pixel_at()按下标自动切换到对应平面(tagtinker_proto.c);
  • 标签收到后,把两平面叠加渲染:平面 1 点亮为黑色,平面 2 点亮为红/黄——于是"红色面积图 + 黑色曲线"这类双色画面就出来了(文章开头那张价签照片)。

对纯黑图tagtinker_encode_image_payload()还会传入color_clear参数补一个全亮的第二平面,保证协议格式统一,见 tagtinker_proto.c。

五、完整传输链路:从一张 BMP 到价签刷屏

把所有环节串起来,一次图片写入的 IR 序列是:

唤醒帧(wake) → 参数帧(param) → 数据帧×N(data 0..N) → 刷新帧(refresh)
  • 唤醒帧:命令 0x17 + 22 个 0x01 尾字节,把休眠的标签叫醒(tagtinker_make_wake_frame());
  • 参数帧:声明字节数、压缩类型、页码、宽高与贴图坐标(tagtinker_make_image_param_frame());
  • 数据帧:每帧固定 20 字节图像数据(TAGTINKER_IMAGE_DATA_BYTES_PER_FRAME),流式 RLE 比特流正好按这个粒度切分逐帧发送;
  • 刷新帧:命令 0x01,通知 MCU 把缓冲内容刷上屏(tagtinker_make_refresh_frame());
  • 每一帧末尾都追加CRC16(初值 0x8408,tagtinker_crc16())保证完整性。

最终由 tagtinker_build_image_sequence() 组装整条序列,交给第二节的 PP4 引擎发射出去。

上图两块 Pricer 标签均由 Flipper Zero 红外驱动:上方为双平面红黑人像图,下方为纯黑底白字文本。

六、上手体验与延伸阅读

想亲手试试?把 web-image-prep/ 中的浏览器端图像预处理器打开,选中标签型号、丢入图片、导出 1-bit 或双平面 BMP,放进 Flipper SD 卡的apps_data/tagtinker/dropped/,然后在Targeted Payloads → <标签> → Set Image中发送即可。

关键模块速查📌

  • 红外驱动(PP4 时序):ir/tagtinker_ir.c
  • 协议与 RLE 流式编码:protocol/tagtinker_proto.c
  • 图像选项场景:scenes/tagtinker_scene_image_options.c
  • 传输界面:scenes/tagtinker_scene_transmit.c
  • 浏览器端图像预处理:web-image-prep/index.html

⚠️ 安全提示:只测试你自己拥有或被授权测试的标签。很多 ESL 标签把固件和显示数据存在易失性 RAM 里,断电就可能"变砖",且可能无法恢复。本项目仅限教育研究与数字艺术展示用途,严禁用于篡改商用价签。

【免费下载链接】TagTinkerFlipper Zero app for ESL research using IR. All based on https://www.furrtek.org/?a=esl项目地址: https://gitcode.com/gh_mirrors/ta/TagTinker

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 1:57:25

面试题:数据湖存储如何加速?用 TaoToken 统一 Key 打通查询链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 1:57:23

MFC取色器实战:全局钩子、DPI适配与色彩空间全链路

简介&#xff1a;这份资源是面向MFC Windows程序设计初学者与进阶学习者的实战型取色器项目源码&#xff0c;围绕对话框程序、自定义控件与颜色选择交互展开&#xff0c;适合正在啃MFC框架、想通过完整案例理解消息映射与控件封装的开发者研究。压缩包共80个文件&#xff0c;约…

作者头像 李华
网站建设 2026/10/9 1:54:55

ESP32-S3红白机模拟器实战:从硬件选型到性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华