πŸƒ Reverse-Engineering a SupeRun Walking Pad

Published: Mar 2026 Β· Authors: Andre Batista & Joe

Sometimes the most satisfying software projects start with a very dumb emotion: I own this thing, why the hell can't I control it properly?

That was the vibe with my SupeRun walking pad. It worked, technically. But it lived behind a vendor app, an opaque BLE protocol, and the usual pile of consumer-IoT mystery meat. So Joe and I did what any reasonable pair of nerds would do: we started peeling it apart.

The end result was better than expected: a Python controller, an Android MVP app, protocol notes, and a public repo for the clean parts of the work. Not bad for what started as treadmill spite.

The Goal

We wanted direct control over the walking pad without depending on the original app.

APK reverse β†’ BLE capture β†’ Packet decode β†’ Python controller β†’ Android MVP β†’ Public repo

Step 1: Treat the App as Evidence

We started by decompiling the Android app package and looking for anything related to Bluetooth, sound settings, control packets, and treadmill state.

That immediately paid off. Instead of fishing blind, we found explicit code paths for things like:

That's the good kind of reverse engineering: not cinematic hacker nonsense, just carefully reading somebody else's Java/Kotlin and noticing they already told you where the bodies are buried.

Step 2: Find the BLE Shape of the Device

Once we had clues from the app, we matched them against BLE traffic and device behavior. The useful part of the protocol sat behind a simple custom service/characteristic layout:

Service: 0000ffff-0000-1000-8000-00805f9b34fb
Write:   0000ff01-0000-1000-8000-00805f9b34fb
Notify:  0000ff02-0000-1000-8000-00805f9b34fb

We recovered a keepalive packet, control packet family, and enough status structure to start building real tools instead of throwing random hex at the treadmill and hoping it didn't launch me into a wall.

Lesson: consumer BLE devices often aren't cryptographically fancy. They're just undocumented. Once you find the packet family and timing model, the whole thing stops looking magical.

Step 3: Build the Python Controller First

Python was the fastest way to iterate, so we built a small controller around bleak. It handles scan/connect, notifications, keepalive packets, and the obvious treadmill commands.

def make_keepalive(seq: int) -> bytes:
    return bytes([0x4D, 0x00, seq & 0xFF, 0x05, 0x6A, 0x05, 0xFD, 0xF8, 0x43])


def mph_to_raw(speed_mph: float) -> int:
    return max(0, min(int(round(speed_mph * 1600.0)), 0xFFFF))

The interesting detail here is that speed appeared to use a raw scale of mph * 1600. That's one of those protocol facts that looks arbitrary until you test it a few times and realize, no, this weird little number is actually the map.

Once the Python side was stable enough, we had commands for start, set speed, pause, and stop. More importantly, we had a place to encode what we believed the protocol meant.

Step 4: Chase the Beep

Naturally, we also wanted to see whether the walking pad's startup sound and voice prompts could be disabled. The vendor app strongly suggested yes.

From the reversed app logic, we recovered packet forms like these:

Query speaker status: 6B 05 9F 9A 43
Set speaker config:   6B 07 9E <powerOn> <voicePrompts> <xor> 43

That's where the project shifted from "we can definitely drive the treadmill" to "we probably can control the audio settings too, but let's not pretend we've fully nailed that path yet." Some of the reverse engineering was clean. Some of it was still experimental.

And that's fine. A good engineering write-up should separate confirmed behavior from strongly supported hypotheses. Bullshitting your future self is a great way to waste a weekend.

Step 5: Build the Android MVP

After the Python controller proved the basics, we built a minimal Android app that talks directly to the walking pad over BLE. No pretty UX. No account system. No cloud nonsense. Just:

It reuses the same packet model as the Python controller, which is the main point. One clean protocol understanding, two clients.

Step 6: Publish the Clean Version

The raw project folder had a lot of garbage in it: APKs, bugreports, BLE snoop logs, local SDK config, decompiler output, build junk, and a real device MAC address that had no business being shoved into a public repo.

So we cleaned it properly before publishing:

KeptExcluded
Python controllerVendor APK
Android MVP sourceBugreports / BLE snoop logs
Protocol notesDecompiled proprietary sources
Gradle wrapper / project filesLocal SDK config / build artifacts

We also scrubbed the real hardcoded BLE MAC from the public code and replaced it with placeholders. Public repo means public repo, not accidental dox-yourself-as-a-service.

The result lives here: github.com/hdxsfbr/walkingpad

What I Like About This Project

This wasn't just "make a treadmill go brrr." It was a nice little stack of practical engineering skills:

That's my favorite kind of project: the thing itself is kind of funny, but the engineering muscles are real.

What’s Next

Anyway, that's how a walking pad turned into a reverse-engineering project. Not because it needed to. Because it annoyed me. Which, honestly, is often how the good ones start.