feat(ir): add Tag Tinker ESL app - #2856
Open
sosnek wants to merge 37 commits into
Open
Conversation
Ninja-jr
pushed a commit
to Ninja-jr/Bruce_firmware
that referenced
this pull request
Sep 8, 2026
Spec for a faithful port of TagTinker's IR ESL transmit flow onto Bruce ESP32 firmware: vendored wire protocol from TagTinker PR BruceDevices#53, a new ESP-IDF v5 RMT PP4 IR driver (~1.25 MHz hardware carrier), and an image TX UX. Verification target is the SmartTAG Color 2.6 (type 1626) on a LilyGo T-Embed CC1101. M0 (IR first-light) and M1 (image TX) specced in detail; M2-M5 recorded as an approved roadmap. Pins the load-bearing details that a first implementation attempt would otherwise get wrong: exact per-stage frame repeat counts, the extern "C" linkage rule for the vendored C core, RMT idle level (IR LED must idle off), the copy encoder and carrier polarity, and the encode-fully-to-RAM ordering invariant that keeps SD reads from straddling IR pin setup.
Seven TDD tasks. The protocol core, PP4 symbol timing, frame sequencing and BMP pixel mapping are all pure C with no hardware dependency, so they are unit-tested on the host with cc; only the RMT driver and the Bruce UI need the device. Golden test vectors were generated by compiling the real PR BruceDevices#53 protocol on the host, so frame bytes and CRCs are measured rather than derived by hand. Also corrects two things in the spec that this planning work uncovered: - The vendored files must live in esl/protocol/ with the compat shim at esl/tagtinker_app.h, because tagtinker_proto.c includes "../tagtinker_app.h" and that path has to resolve to the shim. - The 80 MHz tick values were slightly wrong ({4840, 19352, 9676, 14512}). Derived properly they are burst 3226 and gaps {4838, 19353, 9676, 14515}, and the plan computes them from the 64 MHz cycle counts at compile time so no converted constant is ever hardcoded. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Re-reviewed both documents against the upstream TagTinker source and removed every place the port had quietly substituted its own choice. - No hardcoded barcode anywhere. M0 becomes a wake-frame-only timing checkpoint driven by the real barcode prompt; upstream has no "send a blank image" operation, so the synthetic payload is gone too. - Restore support for every dot-matrix profile. M1 no longer refuses non-1626 tags; the hardware-verification limit is stated, not enforced as a feature gate. - Add the page picker (0-7), matching scene_image_options. Position, compression and frame-repeat stay fixed, as upstream deliberately does. - Split data-frame pacing per family: Color 2.6 waits 50 ms after every frame, generic waits 1 ms after every 32nd. Sharing one policy would have given generic tags a gap they never see upstream. - Accept bpp 1/2/24/32 with upstream's per-bpp strides; refusing non-1/2 is the Color 2.6 send path's job, not the parser's. - Use TX_COLOR26_BMP_MAX (24576) rather than an invented cap, and thread data_frame_repeats through instead of baking in the default. Also corrects an error in my own earlier review: chunked streaming is used only by the text path upstream, never by tx_stream_bmp_image, so it belongs to M2 and is now explicitly forbidden in the image path. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
… tests Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…bootstrap Co-authored-by: Cursor <cursoragent@cursor.com>
…order independent Co-authored-by: Cursor <cursoragent@cursor.com>
…to.h Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ncel Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…branches Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…h guess Co-authored-by: Cursor <cursoragent@cursor.com>
…the default Co-authored-by: Cursor <cursoragent@cursor.com>
- Page remap is a prepare-time default, not a wire invariant. Upstream resolves in prepare_bmp_tx, the picker overwrites job->page with the raw index, and the send path transmits it unresolved — so an explicit page 0 must reach the wire as 0. - SD remount is recorded as a known gap rather than a delivered behaviour: it never triggers on the verification board's pinout, and every existing Bruce IR feature has the same gap. - data_frame_repeats is threaded as a parameter; the Settings screen that exposes 1-10 is M2, so M1 using the default is correct. - Drop the stale claim that generic tags ship as a tested refusal; they are implemented and unit-tested, with only hardware verification deferred. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Raise loopTask stack and stream HTTPS bodies so plugin fetch/render survive TLS. Scan only UL pages 0–11. Dismiss About on Esc. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed Changes
Add a 1:1 user-facing port of TagTinker (I12BP8) so Bruce can talk to infrared electronic shelf labels the same way the Flipper app does.
The app lives under Infrared → Tag Tinker (
src/modules/ir/esl/). Wire protocol is vendored TagTinker C (esl_proto.honly). UI, storage, NFC decode, Set Image / Set Text, LED Test, broadcast Change Page / Diagnostic Page, and WiFi Plugins (Bruce STA →https://tagtinker.jhackerr.workers.dev) match Flipper labels and behavior. BLE phone-sync and the companion-UART path are out of scope.Small core touch:
ScrollableTextArea::show()also dismisses on Esc so About Back works. Tag Tinker raisesloopTaskto 16 KB via the Arduino stack hook so nested IR menus + WiFi scan do not Cache/MMU-fault on ESP32-S3.Types of Changes
Verification
Host tests (no hardware):
Device: LilyGo T-Embed CC1101, SmartTAG Color 2.6.
296x152BMP), Set Text, LED Testpio run -e lilygo-t-embed-cc1101(full, non-LITE) and flash to/dev/cu.usbmodem2101.Testing
Covered by
tools/esl_host_tests(BMP, PP4, TX sequences, store, NFC barcode decode, text, WiFi manifest/header parse, menu labels). Network and IR TX are device-verified on T-Embed as above; host tests do not hit the radio or the worker.Linked Issues
Closes #2686.
User-Facing Change
Further Comments
Credits in-app: Developed by I12BP8, Ported by sosnek, Research by furrtek, NFC by 7h30th3r0n3.
Use only on tags you own or are authorized to test.