---
title: Giving AI eyes to reverse engineer hardware
date: 2026-10-24
keywords: AI, Claude, Reverse engineering, Tagsu
embed: /tagsu/embed/index.html
embedTitle: The Tagsu badge in 3D
---

The kids found some old Tagsu badges and wanted something else on the screens. Tagsu is a wearable LCD name badge that Jari Tulilahti designed at [Rakettitiede](https://rakettitiede.com/) in 2015. You update it by playing audio into its headphone jack, using his own [audio modem for AVR](https://firebay.pelaaja.info/embedded/12kbps-simple-audio-data-transfer-for-avr/). You used to be able to create the audio files with Tagsu's editor at tagsu.io/edit, but that is now offline.

<!-- prompts:1:start -->
<figure class="prompt-log">
<figcaption class="prompt-log-bar"><span class="prompt-log-dots" aria-hidden="true"><i></i><i></i><i></i></span><span class="prompt-log-title">claude <span>~/tagsu</span></span><span class="prompt-log-meta">34 prompts</span></figcaption>
<ol class="prompt-log-body" aria-label="My prompts to Claude, in order">
<li class="prompt-log-day">Fri 23 Oct 2026</li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T19:28">19:28</time><b aria-hidden="true">›</b><span>do you have the tagsu editor, can i run it manually?</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T19:30">19:30</time><b aria-hidden="true">›</b><span>search on internet if tagsu.io/edit is somewhere avialable?</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T19:34">19:34</time><b aria-hidden="true">›</b><span>is here anythign? <span class="prompt-log-attach">[pasted the original editor&#x27;s HTML from the Wayback Machine]</span></span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T19:38">19:38</time><b aria-hidden="true">›</b><span>[photo of the badge]  any clues?</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T19:41">19:41</time><b aria-hidden="true">›</b><span>could it corrupt the chip if i send random sounds?</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T19:44">19:44</time><b aria-hidden="true">›</b><span>[photo of the printed manual]</span></li>
<li class="prompt-log-group"><time datetime="2026-10-23T19:46">19:46</time><b aria-hidden="true">⋯</b><span>18 messages: relayed the badge&#x27;s screen while Claude played test uploads: LOAD&quot;*&quot;,8,1, then ?LOAD ERROR / RUN, then a restart into Tagsu v1.12, over and over</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T19:59">19:59</time><b aria-hidden="true">›</b><span>look at the webcam.</span></li>
</ol>
</figure>
<!-- prompts:1:end -->

So let's give Claude (Opus 5.5 in Claude Code) some eyes. With the webcam pointed at the badge it could see the real world and work on its own. For the next hour it kept sending different data and watching if the badge did anything different.

![The MacBook's webcam pointed at the badge](/assets/images/tagsu-badge-macbook.jpg)

![The badge's loader rejecting an upload: LOAD"*",8,1, ?LOAD ERROR, then a restart into TAGSU v1.12](/assets/images/tagsu-load-error.png)

The board has PRG pads for programming the chip directly, so reading the firmware out with an AVR programmer would have been the easy path, but I did not have the hardware for it.

<!-- prompts:2:start -->
<figure class="prompt-log">
<figcaption class="prompt-log-bar"><span class="prompt-log-dots" aria-hidden="true"><i></i><i></i><i></i></span><span class="prompt-log-title">claude <span>~/tagsu</span></span><span class="prompt-log-meta">continued</span></figcaption>
<ol class="prompt-log-body" aria-label="My prompts to Claude, in order">
<li class="prompt-log-day">Fri 23 Oct 2026</li>
<li class="prompt-log-claude" title="What Claude was doing"><time datetime="2026-10-23T20:09">20:09</time><b aria-hidden="true">⏺</b><span>testing 256 first bytes on camera…</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T20:16">20:16</time><b aria-hidden="true">›</b><span><span class="prompt-log-attach">[5 photos of a Tagsu board]</span></span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T20:23">20:23</time><b aria-hidden="true">›</b><span>Isint there usually some signal start/timing bytes in the beginning of similar syncs?</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T20:25">20:25</time><b aria-hidden="true">›</b><span>I think there is not any random byte in the middle, so focus on the end/the new ideas</span></li>
<li class="prompt-log-claude" title="What Claude was doing"><time datetime="2026-10-23T20:27">20:27</time><b aria-hidden="true">⏺</b><span>testing 51 checksum layouts…</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T20:28">20:28</time><b aria-hidden="true">›</b><span>Also read the hackaday post with comments. Check for other clues in jari blog? Look and the indiego  from wayback machine?</span></li>
<li class="prompt-log-claude" title="What Claude was doing"><time datetime="2026-10-23T20:44">20:44</time><b aria-hidden="true">⏺</b><span>testing 2 KB page uploads…</span></li>
<li class="prompt-log-claude" title="What Claude was doing"><time datetime="2026-10-23T20:48">20:48</time><b aria-hidden="true">⏺</b><span>testing 141 bit orders…</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T20:49">20:49</time><b aria-hidden="true">›</b><span>Sorry but that link is dead <span class="prompt-log-attach">[4 screenshots of the Tagsu Facebook page, one about the firmware update]</span></span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T21:01">21:01</time><b aria-hidden="true">›</b><span>[Google Drive link]</span></li>
<li class="prompt-log-claude" title="What Claude was doing"><time datetime="2026-10-23T21:13">21:13</time><b aria-hidden="true">⏺</b><span>still ?LOAD ERROR</span></li>
<li class="prompt-log-prompt"><time datetime="2026-10-23T21:23">21:23</time><b aria-hidden="true">›</b><span>This one has some audio of an actual upload maybe: https://youtu.be/3ZH9MMPjONw</span></li>
<li class="prompt-log-cursor" aria-hidden="true"><b>›</b><span></span></li>
</ol>
</figure>
<!-- prompts:2:end -->

Using the audio from the YouTube video, Claude was able to reverse engineer the format of the signal and write something that the board accepted:

![A real upload on the badge, straight from the webcam](/assets/images/tagsu-webcam-upload.jpg)

![Badge outputs read back through a webcam with OpenCV](/assets/images/tagsu-opencv-outputs.png)

![The Pac animation, frame by frame through the webcam](/assets/images/tagsu-anim-pac.png)

![Custom logos on the badge](/assets/images/tagsu-custom-logos.png)

![Part of the badge's character set, read from the LCD](/assets/images/tagsu-charset.png)

Of course, if I had checked my email a bit earlier, I would have seen that Jari had given me access to the firmware, which would have helped a lot in Claude's guesswork.

Without the video Claude got surprisingly close. In about 450 attempts it got the modem right, worked out that the screens live in 2 KB of flash written in 64-byte pages, and even picked the right family of checksums, the CRC routines in avr-libc. But it tried the 16-bit CRCs and the 8-bit iButton CRC and left out `_crc8_ccitt_update`, the 8-bit CRC the badge actually uses. It also guessed blocks of 10 or 16 bytes, when a real block is 18: a block number, 16 bytes of data and the checksum. A blind search only works when every piece is right at the same time, so the badge kept answering ?LOAD ERROR. With more time it would probably have got there at some point.

In the end the reverse engineered spec is:

| Part | Spec |
|---|---|
| Signal | Square wave at 44.1 kHz, each half-wave is one symbol: 3 samples = bit 0, 5 samples = bit 1, 17 samples = sync. Bytes go least significant bit first |
| Start | 0.5 s of sync pulses |
| Block | `[block number][16 data bytes][CRC-8, polynomial 0x07]`, then 38 syncs (88 after the last block) |
| Blocks | 128 blocks numbered 1 to 128, in order. The badge writes block n to flash at `0x17F0 + 16n` |
| Screens | 42 screens of 2 × 16 characters, 32 bytes each |
| Screen list | 6 bytes per screen: text number, delay in seconds, in effect, out effect, logo number, logo kind (`0x40` built-in, `0x80` custom). `FF` ends the list. Bit 7 of the text number = double height, bit 7 of an effect = blink |
| Effects | 0 none, 1 random, 2–3 walls, 4–5 scroll, 6–7 curtains, 8 Invaders, 16 rockets, 32–36 Pac, Boulder, Miner, Archer, Aeroplane |
| Custom logos | 10 logos of 15 × 16 pixels, 32 bytes each, shown with custom characters 0–2 and 4–6 |
| Character set | Not the usual HD44780 table: European letters at `0x80`–`0x9F` (ä `0x84`, ö `0x94`, å `0x86`), katakana at `0xA0`–`0xDF` |

Here is a reverse engineered editor for editing your Tagsu:

<div class="post-app"><a href="/tagsu/" target="_blank" rel="noopener"><img src="/assets/images/tagsu-icon.svg" alt="" width="44" height="44"><span class="post-app-text"><strong>Tagsu editor</strong><small>Write screens and send them to your badge</small></span><span class="post-app-go">Open editor <span aria-hidden="true">→</span></span></a></div>

### Links

- [Tagsu Editor](/tagsu/)
- [github.com/infr/tagsu](https://github.com/infr/tagsu)
- [12kbps data transfer for AVR using audio](https://firebay.pelaaja.info/embedded/12kbps-simple-audio-data-transfer-for-avr/)
- [tagsu-avr-modem](https://github.com/Jartza/tagsu-avr-modem)
- [The story of Tagsu (archived, in Finnish)](https://web.archive.org/web/20160531133748/http://labs.rakettitiede.com/tagsun-tarina/)

---

_The editor is an unofficial compatibility fix because the original site is gone. Not affiliated with Rakettitiede._
