p.enthalabs

Reverse engineering a Beyblade launch tracker

October 22, 2026 · 16831 views

!Image 1: spinning beyblade

At the beginning of June last summer, after resetting my Instagram algorithm for the 10th time that year, I started getting recommendations for something I hadn't heard of or seen since I was a kid: Beyblades. This new generation, called _Beyblade X_, was genuinely hypnotic to watch on reels, and I was instantly hooked. So, I decided to pay a visit to my local toy shop in Dublin, where I picked up a stadium and a bunch of _beys_. That same weekend, I called a few friends and organised a games night at my place. It didn't take long for us to understand what all the fuss was about!

Beyblade X battles are incredibly fun and fast paced compared to previous generations of the toy line. Thanks to the stadium's design, the bottom part of each top, known as the _Bit_, can latch onto a side rail and trigger a special "gimmick" called _Xtreme Dash_. The resulting collisions between the beys are absolutely epic! Everyone was ecstatic as we watched these tops fly left and right around the stadium in my living room.

Over the following months, I attended local tournaments, made new friends, and collected far too many Beyblades. Just like everyone else, I wanted to find the ultimate _meta_ combos (Blades + Ratchets + Bits). During one of my procrastination-fuelled research sessions, I stumbled upon an interesting accessory called the Beybattle Pass (BX-09). From what I could gather online, it was developed by Takara Tomy and was (in theory) only available in Japan. When attached to a launcher, it could connect to an official mobile app via Bluetooth and transmit data from each launch, specifically the _Shoot Power_ value, which is literally just the RPM (revolutions per minute) of the launch. Users could then accumulate points and use them to enter a Japan-exclusive lottery system for a chance to obtain rarer Beyblades.

I was fascinated by how this little gadget worked, and with a bit of patience, I managed to find one on eBay Japan, although at an unfortunate _scalper_ price.

How does the Beybattle Pass work?

Here is a quick breakdown of how it works. Some of this is based on hands-on testing. Since this device was so expensive, I wasn't about to tear it apart and risk breaking it! Still, it was pretty straightforward to figure out how key parts operate.

Official Beyblade X launchers (such as the string launcher shown in the video) have two holes on the top. Inside the **first hole** is a small rotating disc that changes color from black to white depending on how quickly you pull the string.

Let's look at the bottom part now:

![Image 2: Battlepass lever view](https://blog.simone.computer/assets/beyblade-test2.png)

When you attach a Beyblade to the three hooks at the bottom you will also push a little white lever to the top, this little lever will come out from the **second hole** (blue arrow below):

![Image 3: Battlepass diagram](https://blog.simone.computer/assets/beyblade-test3.png)

We can now align the components shown in the picture above and work out how they interact. When the small white lever is pushed upwards, sliding into the Beybattle Pass, it signals that a Beyblade is attached to the launcher (see blue arrow). The white lever also acts as a button, turning on the Beybattle Pass and activating a sensor (purple arrow). This sensor detects how quickly the black-and-white disc spins as the launcher string is pulled.

From the video above, I was able to identify what appears to be an infrared sensor using my phone's front-facing camera. My theory is that the sensor tracks the rotating disc by detecting changes in the reflected infrared signal, allowing the Beybattle Pass to calculate the _Shoot Power_ value. In the video, I'm simulating a Beyblade being attached to the launcher to demonstrate how the lever activates the sensor.

Analyzing the Bluetooth payloads

When the Beybattle Pass arrived, I immediately powered it on with a long press, then entered pairing mode by holding the button down again until the lights started blinking. The device `BEYBLADE_TOOL01` popped up on my screen. Assuming it wasn't my neighbour's Bluetooth speaker, I connected to it and watched the blinking lights finally stop. I fired up LightBlue and PacketLogger and started sniffing the Bluetooth traffic. I was worried I'd need the official Beyblade X app, but it turned out I could capture launches without it.

After connecting to the device I got the following characteristics:

``` 0x55c4f002f8eb11ecb9390242ac120002 Read and Notify

0x55c4f001f8eb11ecb9390242ac120002 Write without Response ```

At first, I thought, "Ok, this structure looks simple enough." I subscribed to the _Read and Notify_ characteristics, but nothing happened. Then I wondered: what would happen if I connected the Beybattle Pass to the launcher and attached a Beyblade? Almost immediately, I noticed an orange LED lighting up.

![Image 4: Battlepass top view](https://blog.simone.computer/assets/beyblade-orange.png)

_Houston we got something!_

`a0 3a 00 04 ff 00 00 00 00 00 00 35 b4 04 51 c4 da`

After receiving this initial, tiny payload, I had no idea what I was looking at. But when I launched the Beyblade, I received the following, much larger payload:

``` a0 3a 00 00 ff 00 00 a2 0c 01 00 35 b4 04 51 c4 da b0 a2 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b6 00 00 00 00 00 00 a2 0c 01 00 01 00 00 00 00 00 b7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5e 70 f2 28 ef 0c cc 09 0f 09 6e 0b 67 0b 73 29 35 13 71 34 0d e5 0a 00 00 00 00 00 00 00 00 00 00 00 00 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ```

After performing a few more launches, I noticed a consistent pattern: an initial 17-byte header was always followed by a much larger payload once a Beyblade was launched. Starting with the larger payload, I noticed that its first bytes always followed the same structure: `a0`, `b0` through `b7`, and then `70` through `73`. These looked like page tags to me, so I needed to carefully analyze the data that followed each one. Looking at the a0 page, I noticed that the tenth byte increased by one with every launch compared to the corresponding byte in the a0 page of the header payload. Easy enough! This was probably some kind of launch counter, incremented whenever a launch was _successful_. Next, the sequence `35 b4 04 51 c4 da` remained constant across captures. This turned out to be the BLE device's address in reverse byte order. I was still trying to understand why bytes 8 and 9 (`a2 0c`) appeared to be copied into the `b*` pages.

Decoding the launches

After launching a few more times the chunky payload looked like this:

``` a0 3a 00 00 ff 00 00 62 10 03 00 35 b4 04 51 c4 da b0 a2 0c 62 10 95 0d 00 00 00 00 00 00 00 00 00 00 b1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b2 ... ```

I noticed that, with each new launch, a few bytes were appearing in the `b*` pages: first `a2 0c`, then `62 10`, and finally `95 0d`. I tried converting each sequence into a decimal number, but the results were enormous. Like the value `a2 0c`, came out as 41,484. That couldn't possibly be an RPM value! :)

After searching online, I found that an average Beyblade launch could reach around 5,000 RPM. I decided to try swapping the two bytes in `a2 0c`, giving me `0c a2`, which converted to 3,234.

_That's more like it!_

The reason I tried this was that Bluetooth Low Energy (BLE) commonly uses little-endian byte order, where the least significant byte comes first. This made sense, so I had finally managed to decode the actual launch values. Now, back to the smaller header payload.

As I mentioned earlier, bytes 8 and 9 (e.g. `62 10`) from the `a0` page would occasionally appear in the `b*` pages, but their values seemed to change unpredictably. Whenever this happened, the Beybattle Pass would display a different light pattern after the launch. Instead of the usual blinking green , it would flash orange → red → green . Something was definitely different about these launches. But **why?!**

Decoding the numbers I would have:

``` 0c a2 → 3,234 10 62 → 4,194 🔥 0d 95 → 3,477 ```

AH, that looks like a _high score_ to me, mystery solved!

Remaining bytes in the header

At this point, I had managed to decode the launch values, identify the launch counter, and figure out how the high score was stored. However, a few bytes in the header remained unexplained. During testing, I noticed that whenever I _dropped_ or _detached_ a Beyblade without launching it, the LED would blink red, and the device would return a different header payload:

``` Payload when attached: a0 3a 00 04 ff 00 00 02 16 1d 00 35 b4 04 51 c4 da

Payload after being dropped (no launches): <--- a0 3a 00 00 ff 00 00 02 16 1d 00 35 b4 04 51 c4 da

Successfull launch (bulky payload): a0 3a 00 00 ff 00 00 02 16 1e 00 35 b4 04 51 c4 da, b0: d6 0f c1 03 88 13 02 16 f0 14 c7 14 4e 11 98 13, b1: ... ```

The difference was easy to spot. The sequence `00 04` looked like some kind of status flag, possibly indicating that the device was _ready for launch_. When a launch failed, these bytes would revert to `00 00`, and the launch counter (byte 10) would _remain unchanged_. However, when a launch was successful, the counter would increase, while the status bytes would read `00 00`.

Over the following months, I also noticed that byte 5 (`ff`, or 255 in decimal, in the `a0` page) was steadily decreasing. At the time of writing this post, for example, its value was `da`, or 218 in decimal. One possible theory is that this byte represents the battery level. However, I still can't explain why its value differs in the larger payload. One day, I'll figure it out...

Launchmaxxing

As the `b*` pages continued to fill up with data from new launches, my next question was: what happens when we reach the last page, `b7`? After performing a _lot_ of launches, and by a lot, I mean 50 more, I noticed that the values in page `b6` stopped at position 6 (counting from 1), leaving a gap filled with `00 00`. Here's what I mean:

``` 50th launch bulky payload (32 hex): a0 3a 00 00 ff 00 00 61 16 32 00 35 b4 04 51 c4 da b0 d6 0f c1 03 88 13 02 16 f0 14 c7 14 4e 11 98 13 b1 18 12 59 0b 1b 10 b3 09 f8 0c 29 0d 88 06 a2 09 b2 a3 0d 3c 0a 22 0b c4 0a 04 01 67 0c 91 0d 4e 04 b3 09 0b 74 03 86 0d 35 0b a0 04 0e 0b e7 13 b3 15 b4 38 05 9e 04 c8 13 61 16 dd 10 2e 15 b5 0e b2 0b b5 48 0c a6 0c a9 0c 2d 0d 20 0d cb 0d 55 0e 47 0d b6 bc 0d 36 0d 00 00 61 16 32 00 32 00 00 00 00 00 b7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 dc 70 ...

51st launch bulky payload (33 hex): a0 3a 00 00 ff 00 00 61 16 33 00 35 b4 04 51 c4 da b0 c1 03 88 13 02 16 f0 14 c7 14 4e 11 98 13 18 12 b1 59 0b 1b 10 b3 09 f8 0c 29 0d 88 06 a2 09 a3 0d b2 3c 0a 22 0b c4 0a 04 01 67 0c 91 0d 4e 04 09 0b b3 74 03 86 0d 35 0b a0 04 0e 0b e7 13 b3 15 38 05 b4 9e 04 c8 13 61 16 dd 10 2e 15 b5 0e b2 0b 48 0c b5 a6 0c a9 0c 2d 0d 20 0d cb 0d 55 0e 47 0d bc 0d b6 36 0d 2c 13 00 00 61 16 33 00 32 00 00 00 00 00 b7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 37 70 ... ```

_FIFO! I screamed while waking the neighbourhood!_

It turned out that the maximum number of launches that can be recorded is 50. After that, the oldest entry at the beginning of `b0` is discarded, and the new launch value is added to byte position 4 of page `b6` (`2c 13`). After the padding bytes at positions 6 and 7, the same high score showed up again (`61 16`), followed by the total number of launches (`33 00` → 51) and the number of stored entries (`32 00` → 50, the maximum).

I couldn't wrap my head around the very last byte on page `b7` at first, but it turned out to be a simple checksum. It was calculated by adding together every data byte across the `b*` pages, skipping each page's tag byte and the checksum byte itself, and then taking the result modulo 256.

Sending data to the Beybattle Pass

So far, I had managed to retrieve quite a lot of information simply by reading the initial characteristic (`0x55c4f002`). But then I started wondering: what was the other characteristic (`0x55c4f001`) used for?

Unfortunately, answering that question meant installing the official Beyblade X app and sniffing the Bluetooth traffic directly from my iPhone. The process was quite tedious, especially since I had to install an official Apple Bluetooth debugging profile on my iPhone and keep it connected to my Mac via cable. Once that was done, however, I could finally map out all the interactions.

After inspecting the payloads, I identified three distinct scenarios.

Sending the command `0x74` returned:

``` b0 a2 0c 71 0f 06 14 f4 13 55 11 88 13 bd 17 23 15 b1 46 13 c9 11 03 0c 1f 0f 46 0e 4d 1b 00 00 00 00 b2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b6 00 00 00 00 00 00 4d 1b 0e 00 0e 00 00 00 00 00 b7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c ```

_That looks like the bulky payload without the `7*` pages!_

Then `0x51` returned:

`a0 3a 00 00 d3 00 00 4d 1b 0e 00 35 b4 04 51 c4 da` _And that's the the 17-byte header!_

Finally `0x75` returned:

``` a1 00 00 00 a0 3a 00 00 d3 00 00 00 00 00 00 35 b4 04 51 c4 da b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b6 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ```

_Damn, looks like I cleared the whole dataset lol_

It was immediately clear what kind of operations were available. One returned the full list of launch values, another retrieved the initial metadata and the last one cleared the entire internal database. This final command was also easy to identify because the Beybattle Pass returned an extra `a1` page that I hadn't encountered before.

The obscure 7* pages

If you've made it this far, you're probably wondering: what about the `7*` pages? Sadly, I don't have much to say about those. To this day, I still haven't fully figured out what the values stored in these pages represent. After plotting them in a spreadsheet, nothing immediately jumped out at me.

![Image 5: 7* pages plots](https://blog.simone.computer/assets/beyblade-plots.png)

Maybe you guys are smarter than me, but my theory is that these values represent some kind of physical data coming directly from the Beybattle Pass sensor. This would also explain why the `7*` pages only appear when a Beyblade is actually launched, rather than when querying the Beybattle Pass using the _Write Without Response_ commands.

Anyway the big part of this reverse job was finally over.

DiZZi 0.2

Of course, I couldn't just stop there. I now had a fully reverse engineered protocol for this nifty little device, which meant I could build my own Bluetooth app, bring it to tournaments, and compete with my friends to see who could achieve the highest spin score.

![Image 6: Bit floating](https://youtu.be/uy9AJV4qzww?list=PLJ-JBvtUCWiz1Qtne5HlLBaEeTZUZBc7y&t=77%20)

For my favourite part of any project, designing the UI, I already had something in mind. While watching the original anime from the early 2000s, I was immediately inspired to recreate **DiZZi 0.1**, the AI companion that lived inside Kenny's computer. DiZZi was apparently exclusive to the Western version of the anime. She had a distinctive voice and a sharp, sassy personality, analysing data from Beyblade battles and offering tips to Kenny and his friends while occasionally roasting him.

I started by taking a screenshot of the image above and asking Claude Code to help me sketch out the initial layout. After tweaking the measurements a little, I was able to recreate an almost pixel-perfect design. For the functionality, I leveraged the entire BLE protocol reverse engineered from Takara Tomy to display:

- Scores after each launch

- A history of previous launches, color-coded by launch intensity

- The highest score

- Battery level

The AI Interface

But I knew I could take this even further! Why not recreate DiZZi itself, complete with its sassy personality, and have it roast my launches and Beyblading skills? After all, we live in the _AI era_ 💀🔫

So, I fleshed out an architecture similar to the one I used for Max Headbox. This time, though, I decided to use the newly released Phonon TTS, with all Ollama requests handled directly by the Express.js server:

![Image 7: Battlepass architecture](https://blog.simone.computer/assets/beyblade-arch.png)

In more detail, the React frontend communicates with the Express.js backend, which connects to the BLE device `BEYBLADE_TOOL01` and automatically subscribes to its _Notify_ characteristic. Whenever a new launch is detected, the BLE decoder module retrieves the launch data, and Express.js then queries the Ollama API using the `gemma3:1b` model to analyze my launch trends. This is all done using a simple prompt like this:

``` You can roast the player by judging the result or be also be kind to him when the launch power is actually good. Higher RPM is better: under 3000 is weak, 3000-7000 is alright, over 7000 is excellent. Reply with ONE short sentence (max 15 words) about how the player's launches are going. ```

Obviously, this wouldn't be a fully fledged agent (like Max Headbox), but rather a single-turn LLM that activates whenever a new launch event is detected. Small models like `gemma3:1b` aren't great at math, so instead of putting raw numbers into the prompt, I pre-compute the trend and compose a short summary for it to judge:

``` function describeLaunches(launchesValues) { const lastItem = launchesValues.at(-1); const previous = launchesValues.slice(-4, -1);

if (previous.length === 0) { return `First launch: ${lastItem} RPM.`; }

const avgPrevious = Math.round(previous.reduce((a, b) => a + b, 0) / previous.length); const diff = lastItem - avgPrevious; const trend = diff > 300 ? 'improving' : diff < -300 ? 'getting worse' : 'about the same';

return `Previous launches: ${previous.join(', ')} RPM (average ${avgPrevious}). Last launch: ${lastItem} RPM. Trend: ${trend}.`; } ```

The main React frontend communicates with the backend over WebSockets to retrieve real-time data from the Beybattle Pass. It also uses a few REST endpoints to trigger the specific Bluetooth commands (`0x74`, `0x75`, and `0x51`). When it came to managing the app's state, I wanted to represent the following scenarios, all derived directly from the device's events:

- A Bey is attached

- A Bey is launched

- A Bey is dropped (failed launch)

- The BLE device connects or disconnects

These states also drive the animations of the spinning 3D Beyblade model on the main screen!

The cherry on top of the cake was the VoiceFlare component on the left. Whenever the TTS wav file comes back from the backend, the React frontend plays it through the Web Audio API and pipes it into an AnalyserNode. On every animation frame the analyser returns the frequency spectrum of DiZZi's voice. I keep only the lower ~60% of the bins and use them to drive a small canvas animation: blue and orange flame-like peaks and light streaks mirrored around a glowing horizon, very similar to the sequence from the anime.

``` const analyser = audioCtx.createAnalyser(); analyser.fftSize = 256; source.connect(analyser); analyser.connect(audioCtx.destination);

function tick() { analyser.getByteFrequencyData(data); // ...map the bins to peaks and streaks, then draw() if (!audio.paused && !audio.ended) requestAnimationFrame(tick); } ```

Demo

Here's a quick demo of how everything works:

Video 5

The source code is available on GitHub, along with all the payloads I captured during testing. Feel free to take a crack at the 7* pages and see if you can figure out what they are.

This project was absolutely crazy fun to develop, from reverse engineering the device all the way to building the app. And, truth be told, I could never have created that amazing spinning 3D Beyblade model or the TTS voice flare on my own, so instructing Claude to help on those components was certainly entertaining!

Sometimes, it's hard to explain to people that my new favorite hobby is spinning Japanese tops. Looking at it objectively, I do wonder whether I'm heading down a weird path in life. But honestly, I've been having an absolute blast over the past few months, so I think it's all g.!Image 8: plan9 end cursor

> Let it rip!

14 Webmentions

An app that tracks your Beyblade launches and then roasts you is the most honest accountability tool I've seen this week. Does it get meaner when the launch angle slips?

October 22, 2026

![Image 9: P0ttergamerP0ttergamer](https://reddit.com/r/Beyblade/comments/1xcnejq/i_built_a_real_dizzi_from_the_anime_and_it_roasts/19rhurdwaxulg/)

To me, had she been a character in the original Japanese also, her true form would be: https://preview.redd.it/e8zkmna492xh1.jpeg?width=896&format=pjpg&auto=webp&s=e2f4f5988c9e8732718cceb35dce12e9054f134b

October 22, 2026