Free the Hardware from the Dead Cloud

How a discontinued guitar pedal became FreedomHawk — and accidentally gave birth to Sequin

Published · Filed under Development

This project started because I wanted to use a piece of recording gear I already owned.

That shouldn’t have required reverse-engineering an abandoned Android app, decoding a binary symbol table, digging through native libraries, reconstructing a Bluetooth protocol, and building two separate pieces of software. But apparently that’s what we were doing.

I own a Line 6 Firehawk FX — a floor-based guitar processor stuffed with amps, cabinets, reverbs, delays, modulation, distortion, wahs, and enough editable parameters to keep a guitarist busy for the rest of their natural life. The hardware itself is still perfectly good. The problem is that most of it was never really accessible to me.

The pedal has six knobs and a small display I can’t read. Almost everything else was meant to be controlled through the Firehawk Remote mobile app — which was never designed for screen-reader users, was discontinued by Line 6 in 2024, pulled from the Play Store, and hung off an account-and-cloud layer that wasn’t exactly getting more reliable with age. So I had a solid piece of hardware holding hundreds of sounds and parameters, and no practical way to reach most of them.

It wasn’t broken. It was being made useless by software, and that distinction is the whole reason I’m writing this.

One thing before any of the technical stuff, because it changes how you should read the rest of this: I didn’t do this the way a career reverse-engineer would have, and I didn’t do it alone. I built it with Claude — mostly in Claude Code — across a long back-and-forth where I brought the problem, the requirements, the accessibility judgment, and the testing, while the AI wrote most of the actual code and did a lot of the disassembly work under my direction.

So when I say “we” through the rest of this, that’s what I mean, and I’ll try to be clear about who did what. I’m not going to pretend I hand-wrote a CRC decoder from ARM assembly. I’m also not going to pretend the parts that made this an accessible tool wrote themselves. Both of those are true at once, and there’s a fuller accounting of it near the end.

The obvious path wasn’t available

My original idea was pretty simple: build an accessible Windows editor for the pedal. There had to be some control surface we could use. Maybe it exposed MIDI over USB. Maybe it had a documented command system. Maybe somebody else had already written a library we could build on.

So I did what I usually do when I have a technical problem and no clear path forward. I went to talk to Claude.

The research was not encouraging. The Firehawk FX doesn’t expose its useful editing functions through standard MIDI — the USB connection handles recording, audio, and firmware updates, but it doesn’t simply show up as a MIDI device you can send program changes and controller messages to. The physical expression-pedal input is handy while you’re playing, but it’s not a software interface. That left Bluetooth.

Every meaningful editing action — loading models, adjusting parameters, changing presets, rearranging the signal chain — went through a proprietary Bluetooth connection used by the original app. And at the time, we couldn’t find a published protocol spec, an open-source editor, or a public reverse-engineering project for this thing anywhere. There was no convenient repository where somebody had already documented which bytes meant “turn up the amp drive.” We didn’t even know at first whether it used Bluetooth Low Energy or an older serial connection over Bluetooth Classic.

The first approach on the table was dynamic capture: run the original app, turn on Android’s Bluetooth logging, perform one action at a time, and stare at the traffic. Change the amp drive, capture the bytes. Toggle a delay, capture the bytes. Load a preset, capture the bytes. Repeat until the protocol slowly gives itself up.

That can work, but it comes with an accessibility tax. Android developer tools, packet capture, Wireshark, and the old app itself were none of them designed around a blind person using TalkBack and NVDA. And dynamic capture only ever shows you the actions you think to perform — it gives you individual examples, not the complete vocabulary of the device.

Then my friend Flint suggested something more direct: why not just take the app apart?

Fine. Let’s decompile it.

The Firehawk Remote app was discontinued. Support for the hardware was fading. The only official editor had never been accessible to begin with. So instead of treating the APK as a sealed black box and guessing what came out of it, we could crack it open and read what Line 6 had shipped inside.

Claude agreed this wasn’t just a backup plan — it was probably the better starting point.

Static analysis had real advantages for me specifically. A decompiled application becomes a big pile of text files, data files, and code, and text is searchable. Text works with NVDA. Text can be piped through scripts, compared, indexed, and summarized without me repeatedly wrestling an inaccessible mobile interface. It also meant the app’s login system stopped mattering entirely — we didn’t need to authenticate, reach an old cloud service, or even successfully run the thing to read what was inside it.

So I sent Claude the APK. That was the moment this stopped being a vague idea and became a real reverse-engineering project.

The APK held far more than we expected

An Android APK is basically a specially structured ZIP archive. The first step was verifying the file itself — we checked its SHA-256 hash against the copy described in the project notes, so everything downstream would rest on a known file rather than a corrupted or altered download. Then we extracted it.

That produced the first small dead end. The available unzip command quietly mishandled part of the file pattern, which briefly made it look like the native libraries were missing. They weren’t missing. The extraction tool had just lied by omission. A short Python script using the standard zipfile module pulled everything out properly — the compiled Java, plus both the ARM and x86 builds of the native library.

Then we looked inside the assets directory, and there it was. The app shipped with its entire model catalog.

Not just the names shown in the interface — the package contained structured data describing the amps, cabinets, effects, reverbs, wahs, and their parameters. Each model had a symbolic name, a numeric ID, parameter names, minimum and maximum values, defaults, and value types. There were 261 models described in the shipped data. The APK also carried a readable default preset laying out the shape of the signal chain: amp, cab, compressor, EQ, effects slots, gate, reverb, volume, wah, global tempo, and their parameters.

That changed the problem completely. We’d expected to infer hundreds of meanings from an opaque stream of numbers. Instead, Line 6 had put the meanings inside the app. The question stopped being:

What does every number represent?

and became:

How does the app package a known instruction into the bytes it sends to the pedal?

That’s still a genuine reverse-engineering problem, but it’s a much smaller and more bounded one.

Decoding the model data

The .models files were readable structured data — each one describing a model and the parameters available for it. Only one value still needed interpretation: valueType.

Rather than guessing from a few examples, we compared the values across the full catalog, and their meaning came clear:

  • Integer and enumerated choices
  • Continuous floating-point values
  • Boolean switches
  • String or model references

This would later drive the whole accessible interface. A continuous value becomes a slider, a true-or-false parameter becomes a checkbox, and a choice among named models or fixed values becomes a dropdown. The protocol data and the accessibility design were tied together from the very beginning — we weren’t building a visual editor first and planning to sprinkle labels over it afterward. The metadata decided the controls.

The binary symbol table

The APK also contained a file called defaultSymbolTable.bin. Unlike the model files, this one wasn’t readable JSON. It was a compact binary table linking names to numeric indexes and hashes, and its structure had to be worked out from the bytes themselves.

We found a small header followed by fixed-size records and a pool of null-terminated strings. Each record held a hash, a string length, and an offset pointing to the end of that string in the pool. The first read was close but not quite right — some entries decoded cleanly, others came out as garbage. So instead of hardcoding assumptions until the output looked plausible, we wrote a solver to test candidate base locations and score how many valid printable strings each one produced. That found the correct base address and reconstructed 610 of the table’s 611 symbols.

The output included real amp and effect names alongside parameter names like:

  • Bass
  • Mid
  • Treble
  • Presence
  • Drive
  • BassState
  • DriveState

Each one tied to a stable numeric index — Bass at 17, Drive at 23, and so on.

We tested common hash algorithms against the stored hash values, and none of them matched. That could have felt like a wall, but it wasn’t one: if the protocol addresses parameters by symbol-table index rather than recomputing their hashes, the original hash algorithm simply doesn’t matter. Which turned out to be one of the more useful habits of the whole project — don’t solve a mystery just because it’s sitting there. Solve it when the project actually depends on it.

Finding the protocol engine

The readable Java showed us how the Bluetooth connection actually worked. The app opened a standard Bluetooth Serial Port Profile connection over RFCOMM, and on Windows that kind of connection can show up as a COM port — which means a replacement app can talk through ordinary serial I/O instead of needing some exotic Bluetooth framework.

The Java itself did almost nothing with the bytes. A background thread read data off the socket and handed it straight into native code; the native code produced finished outgoing messages and passed them back to Java to write out. That told us exactly where the protocol lived:

libAmplifiRemoteNdk.so

Native libraries are usually where reverse-engineering gets significantly less pleasant — instead of near-readable Java you’re squinting at machine code and reconstructed C. Fortunately, this library hadn’t been stripped of its function names, and the names were almost comically helpful:

  • CRC16_Process
  • MsgTransportHeader_Pack
  • MsgTransportHeader_Unpack
  • RobustSerialMsgChannel
  • MsgRouter
  • MsgPort
  • MsgSegmentIter
  • L6SPePresetSerializer

We weren’t staring at ten thousand functions named a, b, and c. The original developers had basically left us a map.

From function names to a real protocol

The native library turned out to be a small network stack running over the Bluetooth serial link. There was a reliable message channel with sequence numbers and acknowledgements, addressed ports for different parts of the device, a transport header, segmentation for longer messages, and a CRC integrity check.

None of the digging needed a heavyweight setup. We pulled the library’s exported symbols with pyelftools and grepped them for anything that smelled like CRC, message, serial, or frame. A small capstone-based disassembler read the short, decisive functions one instruction at a time. androguard let us read the compiled Java classes with no Java runtime anywhere in the loop. Ghidra came out only for the parts that needed full decompilation rather than raw assembly. The comedy beat: the machine didn’t even have the strings command installed — the second time a basic tool quit on us — so we wrote a ten-line regex extractor and carried on.

The CRC came first, and it came out clean. CRC16_Process is a tiny 79-byte function. We disassembled it and hand-translated it into Python one instruction at a time, then ran the result against 123456789, the standard string people use to fingerprint CRCs. It reproduced the textbook values exactly — 0x31C3 from an init of 0x0000 (that’s CRC-16/XMODEM) and 0x29B1 from an init of 0xFFFF (CRC-16/CCITT-FALSE). Reading the framing layer showed it seeds with 0xFFFF, which settles it:

  • Polynomial: 0x1021
  • Initial value: 0xFFFF
  • No reflection
  • No final XOR

That wasn’t a statistical guess from a handful of captured messages — it was a translation of the pedal app’s own implementation, confirmed against known test values.

Decompiling the reliable-channel code (RobustSerialMsgChannel) with Ghidra gave the whole on-wire frame:

byte 0–1    sync        0x55 0x55
byte 2      flags
byte 3      seq         sender sequence number
byte 4      window      channel window / rx index
byte 5      ack         acknowledgement number
byte 6–7    length      payload length (0 for a control/ack frame)
byte 8–9    payloadCRC  CRC-16/CCITT-FALSE over the payload
byte 10–11  headerCRC   CRC-16/CCITT-FALSE over bytes 0..11, with 10–11 zeroed
byte 12..   payload     sent in 4-byte-aligned segments

It’s a proper little reliable transport — sequence numbers, acknowledgements, a bare control-frame form (just the 12-byte header) and a data-frame form (header plus payload). The receiver runs as a three-state machine: scan for the 0x55 0x55 sync, read and CRC-check the header, then read and CRC-check the payload. And the payload itself carries another transport header identifying the kind of message and the endpoints it’s addressed to inside the device. This is exactly why recording a knob movement and replaying a few bytes would never have been a complete solution — the app and pedal maintain a real conversation, tracking message order, acknowledging frames, verifying checksums, and routing to internal services. But once that structure was legible, the mystery was gone. It was just engineering.

One edit was actually two different systems

Another thing surfaced that mattered a lot: the Firehawk doesn’t use one universal format for every kind of edit. Saving or transferring a whole preset runs through one serialization path — the L6SPePresetSerializer from that symbol list. Changing a single parameter live runs through a completely different one: the ToneMatch editor commands, each shaped as [uint32 command ID][uint32 data length][data]. If I’d assumed a single format, I’d have chased my own tail for days.

The live command set we confirmed:

Command ID Data
Load DSP model 0x08 slot, model ID
Set model param 0x0A slot, param ID, type, value
Set global tempo 0x0E float BPM
Set preset PSKey param 0x13 PSKey, type, value

Parameter addressing splits the same way. Structural settings — a block’s enable, its mix, the global tempo — are named on the wire by a numeric PSKey from a lookup table we read straight out of the binary. Model knobs like Bass, Drive, and Treble aren’t in that table at all; they’re addressed by their index in the symbol table we decoded earlier, inside whatever model happens to be loaded in that block’s DSP slot. (We read the block→slot mapping out of the binary too, and checked every slot symbol against the decoded table — amp resolves to Amp, cab to StudioCab, and so on.)

So “set the amp’s Bass to 0.5” stops being a vague intention and turns into an actual 0x0A command: the amp’s DSP slot, Bass resolved to symbol index 17, the value 0.5 sent as a normalized float, and a one-byte type tag — which then gets wrapped in the transport header and the reliable frame from the last section, with both CRCs computed, and put on the wire. That command shape is implemented and unit-tested against the real symbol table. The single piece still marked inferred is the exact value of that type tag, and it’s one of the two constants waiting on a hardware capture — more on that in a second.

Building FreedomHawk

Once the pieces made sense, the replacement app split cleanly into layers. The transport layer only deals with raw bytes moving through the paired Bluetooth COM port. The protocol layer builds and interprets frames, headers, checksums, acknowledgements, and parameter commands. The model layer loads the amp, cabinet, effect, parameter, and preset data extracted from the user’s own APK. And the interface sits on top of all of it.

The project became FreedomHawk. The name came out of the Cathedral — my local ecosystem of AI agents — and it fit too well to swap for anything else: free the hardware from the dead cloud.

FreedomHawk is being built as an accessible, local Windows editor for the Firehawk FX — everything the old app did, minus the inaccessible mobile interface and the fading online service. As it stands, the app can browse the signal chain, swap models, edit parameters through labeled controls, and create, load, save, and organize presets locally.

Live transmission to the pedal is being handled more carefully. Most of the protocol is understood and implemented, but two small values still need confirmation from a real Bluetooth capture: the exact float type tag and the final ToneMatch port address. Both should fall out of a single controlled capture of the original app changing a known parameter. Until that happens, writes to physical hardware stay disabled by default. The app can construct and display the message it would send, but it won’t transmit speculative bytes into somebody’s pedal.

Could we guess? Probably. Are we going to? No. I’d rather wait for one confirming capture than turn a useful guitar processor into an unusually complicated doorstop.

There’s a smaller version of that same refusal-to-guess baked into how the app figures out which pedal it’s talking to. The Firehawk has a numeric device code, but the original app’s Java only ever identified units by a name-and-ordinal enum, and the numeric codes live in a separate numbering I couldn’t cleanly line up against it. So rather than hardcode a guess about which code meant which device, FreedomHawk reads the pedal’s own product ID when it connects, and offline it just offers every model. Robust beats clever.

Accessibility wasn’t a finishing step

FreedomHawk uses wxPython and native Windows controls, and that was a deliberate choice — NVDA’s own interface is written in wxPython, which is about the strongest signal you can get that its controls announce properly. (Qt, by contrast, draws its own controls and bridges them into the accessibility layer; wxPython uses the real Win32 controls.) That gave us a solid baseline for widgets that expose their names, roles, states, and values to a screen reader.

But “native controls” did not automatically mean “accessible.” The interface had to be tested with NVDA the whole way through, and several early controls failed in ways a sighted developer would probably never notice. A checkbox could technically exist in the accessibility tree and still announce with no useful label. A slider might read out “slider 59” without ever telling me what 59 was. A spin control could speak its value while dropping the name of the parameter entirely.

That testing hardened into a permanent design rule. FreedomHawk uses three main interactive control types — checkboxes for on/off values, sliders for continuous values, dropdowns for models, enumerations, and stepped values. No spin controls. No custom-painted knobs. No inaccessible imitation of a physical pedalboard. If a control doesn’t speak properly, it doesn’t stay.

Keyboard behavior got the same care: direct shortcuts to the major sections, predictable Escape handling for moving back through the interface, standard commands for creating and saving presets, and a navigable HTML manual with headings and a table of contents. The low-vision mode layers on high contrast, enlarged labels, and a clearer sidebar without replacing or weakening the screen-reader workflow.

Accessibility here isn’t a separate mode. It’s the architecture.

Then the project grew another project

While the last live-control details waited on a hardware capture, there was plenty of FreedomHawk I could build and test without the pedal. So I started adding practice tools.

First came a tuner built to work by ear, using sustained reference tones instead of a visual needle. Then a metronome — with support for odd meters and accent groupings, because I listen to and play enough Tool, Soen, and Dream Theater that stopping at 4/4 would have been personally offensive. Then I figured a drum machine would be useful.

This is how scope creep introduces itself: very politely, as one useful extra feature.

The drum machine picked up swing, humanization, per-line tuning, choke groups, a count-in, a tempo trainer, improvised fills, odd meters, and polymeters. Then we built a pattern editor. The first version worked, technically, but it was still too shaped by the idea of a visual grid — so I stopped and described how the interface should actually behave for someone who can’t see a grid at all.

Each drum part is a line. Up and down arrows move between parts. Left and right arrows move through time. You can move by step, beat, or bar. Every movement is spoken immediately, and every hit, accent, ghost note, ornament, and edit is reachable from the keyboard. The spoken cursor shouldn’t describe the interface. The spoken cursor should be the interface.

We rebuilt it around that idea, and that’s when the drum machine stopped being a side feature and became its own instrument.

Sequin

On July 18, I named it Sequin.

It’s partly a play on “sequencer.” It’s also personal — I’m autistic, sequins are a stim for me, and a dense pattern of musical switches and hits feels a little like running my fingers through a handful of them: lots of small, distinct points forming something larger together. More practically, it needed a real name so that someone searching for an accessible step sequencer could actually find it.

Sequin is designed non-visually from the ground up. It’s not a visual drum machine with a screen-reader narration layer bolted on after the fact — its tracker grid is spoken directly, and the whole interface was designed for the ear first. It now ships with a full drum kit, 500 built-in grooves across roughly sixty genres, odd and progressive meters, per-line polymeter, improvised fills, a song builder, kit-building tools, WAV rendering, portable pattern files, and MIDI import and export. It also has its own physics-based drum synthesizer, Spangle, so it can produce a complete kit without shipping anyone else’s samples.

Sequin runs inside FreedomHawk, but the underlying practice engine was kept deliberately separate from the pedal-control code, which is what let it break off into a standalone application instead of staying buried inside an editor for one specific piece of hardware.

What started as something to jam along with while testing a guitar utility became a complete screen-reader-first drum machine. That was not the plan. It was, however, extremely on brand.

Yes, this was vibe coded

I said up top that I didn’t build this alone. Here’s the full version. I’m not an expert programmer who sat down and hand-wrote every class, parser, test, and interface component. This was a vibe-coding project from start to finish.

The initial research happened in conversations with Claude. The APK analysis, the scripts, the decompilation strategy, and the handoff document were all developed with Claude. Then I carried those findings into Claude Code, where the application, the tests, the protocol implementation, the documentation, and the interface got built through an ongoing development conversation. The code was written by AI, under my direction.

My work was in the rest of it — identifying the problem, setting the requirements, guiding the investigation, supplying the hardware and the source material, making the architectural calls, testing the interface, rejecting designs that didn’t work with NVDA, deciding what the software should even become, and keeping the whole thing pointed at an actual human need. I orchestrated the decompilation. I decided accessibility had to drive the control model, not decorate it. I described how the spoken tracker should behave. I worked out which features mattered to a musician versus which ones were just implementable. I tested what the screen reader actually said, and caught failures that would never show up in a screenshot. And I decided when the software was not yet safe to send data to physical hardware.

Claude did not independently wake up one morning, locate my guitar pedal, get annoyed with its inaccessible app, and decide to spend several days freeing it from the cloud. I brought the problem. AI gave me leverage. That distinction matters — but I don’t think it diminishes the project. I think it’s the reason the project exists.

What authorship looks like here

There’s an understandable temptation to frame AI-assisted work in one of two dishonest ways.

The first is to pretend the AI barely mattered — to present myself as if I hand-authored thousands of lines and used autocomplete once or twice. That’s not what happened. The other is to say the AI did everything and I was basically standing nearby. That’s not true either.

Software development isn’t only the physical act of typing syntax. Someone still has to know which problem is worth solving. Someone has to define what “working” means, make the tradeoffs, spot the failures, interpret the results, test the behavior, and decide what the finished thing is for. And in accessibility work especially, the person living with the access need isn’t a decorative source of feedback at the end. That person may hold the single most important piece of expertise in the project.

I may not have known how to reconstruct a native CRC function from ARM instructions when this started. But I did know that an unnamed slider announcing “59” is not an accessible control. I knew a visual drum grid being read aloud to me is not the same thing as a sequencer designed around non-visual navigation. And I knew a discontinued app shouldn’t have the power to turn working hardware into junk. That knowledge shaped every technical decision that followed.

So I’m comfortable saying I built FreedomHawk and Sequin with AI. Not despite AI. Not secretly using AI while performing traditional-programmer cosplay. With AI.

Keeping the project clean

FreedomHawk doesn’t redistribute Line 6’s proprietary model files, preset data, application code, or extracted assets. Instead, a user provides their own lawful copy of the discontinued Firehawk Remote APK, and a local extraction tool generates the data the editor needs, on that user’s own machine. None of that extracted information has to be uploaded anywhere. FreedomHawk is an independent reimplementation built to interoperate with hardware the user already owns.

That separation matters technically, ethically, and for the long-term health of the project.

On the legal side — and I’m not a lawyer, so read this as my understanding rather than a ruling — this is interoperability and assistive-technology reverse engineering of hardware I lawfully own, on a product the manufacturer walked away from. No exploit, no copy-protection to circumvent, no third-party target. As I understand it, that’s squarely the kind of work the DMCA’s accessibility and interoperability exemptions exist to protect: reaching hardware you own, and keeping it reachable after a company stops caring whether you can.

The source for both FreedomHawk and Sequin is available under the GNU Affero General Public License. People can use the software freely — including for professional music, recordings, performances, and teaching — and they can inspect it, learn from it, modify it, and share it. The share-alike requirement also means nobody can quietly take the work, close it, strip the credit, and sell it back to the same blind musicians it was built to help.

The point is access.

Where things stand now

FreedomHawk already works as an offline editor. The model catalog loads, the signal chain is navigable, parameters are presented through accessible controls, and presets can be created, opened, saved, and organized locally. The tuner, metronome, and practice tools work. The protocol framing, CRC, transport header, commands, and parameter addressing are all reconstructed and implemented. Live pedal control is the one remaining piece, and it’s deliberately gated until a single real capture confirms the last two inferred constants.

Sequin has split off into its own project and reached its first official release. It runs as a standalone, screen-reader-first step sequencer and drum machine, and it still ships inside FreedomHawk too. It runs from a single unzipped folder with nothing to install — which is its own small accessibility win, since there’s no installer to fight with a screen reader.

The hard part — opening an abandoned application and learning the pedal’s internal language — is behind us. The final hardware validation is now waiting on two deeply sophisticated pieces of laboratory equipment:

A power cord for the Firehawk.

And a USB adapter for testing MIDI input into Sequin.

Apparently even reverse-engineering has fetch quests.

Why I wanted to tell this story

I’m writing this partly because the project is interesting and partly because I think the way it was built matters.

There are a lot of disabled people surrounded by tools that are theoretically powerful and practically unreachable. There are also a lot of people with deep domain expertise who’ve been locked out of software development, because traditional coding demanded years of specialized training before you could make your first useful thing.

AI changes that equation. It doesn’t remove the need for judgment. It doesn’t magically produce good accessibility. It doesn’t decide which problems deserve attention, and it doesn’t replace testing, responsibility, or understanding. What it does is shorten the distance between a person who understands a problem and a working attempt to solve it.

When I started this on Thursday, July 16th, I had an inaccessible guitar processor and no real way to control it. By Monday the 20th, that wasn’t true anymore. We unpacked the app. We read the model data. We decoded the symbol table. We reconstructed the frame. We built the editor. And somewhere in the middle of all that, we accidentally built a drum machine.

That’s the story of FreedomHawk.

Free the hardware from the dead cloud.


Get Sequin

Of the two, Sequin is the one you can use today: version 1.0, a standalone, screen-reader-first step sequencer and drum machine — free, open source, and nothing to install beyond unzipping a folder.

Download Sequin from the software page — the file, its size, a SHA-256 to verify it, and a heads-up about the Windows SmartScreen prompt all live there. FreedomHawk’s own release is still ahead, gated on the one hardware capture described above.

FreedomHawk: GitHub repository

Sequin: GitHub repository

Comments

No comments yet. Be the first.

Leave a comment

Comments are moderated and appear once approved. Please remember to be respectful. Honest conversation and civil debates are fine and good, but no flames or trolling or you will be barred from commenting.

All blog posts