Zero motorcycle log reader
Drop the .bin your bike or your dealer gave you and read it as plain text — here, now, in this tab. Then download the .txt.
Your file is never uploaded. It is decoded by JavaScript in your browser.
You will see some traffic, and it is worth knowing what it is: the page loads its own scripts and fonts, quietly prefetches a few of its own pages (the cookie and privacy notices), and loads Google Analytics if you accepted analytics cookies. That is the site, and none of it carries your log. The decoder itself contains no fetch, no XMLHttpRequest, no WebSocket and no beacon: your file is read with File.arrayBuffer(), decoded in memory, and written to a blob: URL — an in-memory handle, not a request — when you download it. Close the tab and it is gone. Nothing about your bike is stored, and there is no account to create.
The other half of privacy is what you publish yourself, so this page starts with Mask identifiers already ticked: the VIN and the board serial are replaced with X in the result, in the decoded text, in the .txt and in the name that file is saved under — a Zero export is named by its VIN, so the file name alone identifies the bike. Untick it to read your own log in full; nothing is withheld from the decode itself.
What comes out, and what does not
Three of the four log families Zero has shipped decode here, with the same field maps the server-side pipeline uses. The fourth is identified and handed to the right tool. The reader tells you which one you dropped, and why it decided that, before you read a single entry.
| Log family | Bikes | Here | What you get |
|---|---|---|---|
| Gen3 MBB | Cypher III: SR/F, SR/S, DSR/X, FXE | Decoded | Text events, the state machine, state of charge, four temperatures. No electrical telemetry — on this platform that lives in the BMS log. |
| Gen3 BMS | Cypher III battery boards | Decoded | Cell minimum and maximum, state of charge, pack current and voltage, pack temperature, the BMS state machine and the full-charge-capacity register. |
| 538XX-firmware MBB | Gen2-era bikes carrying the Gen3-style body | Decoded | The one MBB that does carry pack voltage, current, speed and motor/controller temperatures alongside the odometer. |
| Gen1 / Gen2 | Cypher II / SDS: S, SR, DS, DSR through 2023, and the FX/FXS/FXE light bikes | Identified | Recognised and identified — header, VIN and board come back — but the entries are not decoded here. See below for what to do with one. |
Why Gen1/Gen2 is not decoded here
Gen2 spreads the telemetry this port reads across four record types — 0x2c, 0x3c, 0x2d and 0x33 — and the browser port reads one of them. Its typed events (Riding, Charging, Discharge level, SOC Data) need a message table this port does not carry.
Shipping that anyway would recover 0.73% of pack-voltage rows, 0.96% of current, 1.12% of state of charge and nothing at all from a BMS file — measured, not estimated. A decode that thin, labelled as support, is worse than an honest refusal.
Two things do read these files: zero-log-parser, which runs on your own machine as a command-line tool, and the full analyser here, which runs that same engine server-side — upload the file and it is read properly.
It reports what it could not read
Every decode prints its own diagnostics, never behind a toggle: the framing strategy, how many 0xB2 delimiters were found, how many became records, and — separately — why the rest did not. An erased flash slot is not a torn record, and a decoder that reported them as one number would be hiding the difference.
Measured on a real 256 KB 538XX log: 7,776 delimiters, 6,458 records framed, 1,211 erased flash slots, 106 timestamps outside the window, one record too short and one with an implausible length. Also printed: a record-type histogram, and every field value a physical range check refused rather than guessed.
The same decode was validated field-for-field against the Python parser that runs our server pipeline, over 352 real log files and 1,190,446 entries, with zero divergences. That is a portability guarantee, not a correctness one: where the server decoder is wrong, this one is wrong identically — which is exactly what makes it a useful cross-check.
A valid Gen3 log can contain no rides at all
This is the failure that looks like a broken tool and is not one. Each Gen3 board keeps its own ~128 KB ring buffer holding roughly 2,300 entries. A parked bike is not idle: the MBB wakes about once an hour and writes a measured median of 31 entries per wake-up, so a bike left alone fills its buffer with hibernation in about three days — 3.25 at the measured median rate — and everything older is overwritten, oldest first.
So a file exported a week after your last ride can decode perfectly and still hold nothing but hibernation. The rides were gone before you dropped the file. Export within a day or two of riding to keep them, and read why your Gen3 log shows no rides for how to recognise a buffer that has already wrapped.
What an account adds
Nothing above is held back for it. The reader gives you every entry it can decode, the full diagnostics and the complete .txt, with no sign-up and no cap. An account exists for the one thing a single file cannot give you: a single file is a three-day window on a parked Gen3, because the bike overwrites it — not us.
History across files
Every export you keep is stitched into one continuous timeline per VIN, with overlapping files deduplicated instead of double-counted.
Rides and charges
Sessions are detected from the telemetry — distance, energy, duration — including charges the current sensor was too sparse to record directly.
Battery health and faults
Usable pack capacity measured from real charges against the nameplate figure, cell imbalance over time, and error codes grouped into findings.
Uploaded logs are also read by an AI pass that explains faults and the state of the pack in plain language — and, unlike the reader on this page, the full pipeline reads Gen1/Gen2 files too.
How these files are put together
Everything this reader does is documented, down to the byte offsets it reads.
Gen3 log format: the 0xB2 record spec
The byte-level specification this decoder implements — record framing, the MBB and BMS record maps, and the measurement behind every offset.
Gen2 vs Gen3: log differences
Which platform your bike is on, what each generation stores, and why one of them needs a different engine to read.
Why your Gen3 log shows no rides
How the ring buffer overwrites itself, how to recognise a log that has already wrapped, and when to export so it has not.
No log file to hand? How to export one from the Zero app, or pull a text dump straight off the bike with a serial console.
Frequently asked questions
Is my log file uploaded anywhere?
No. The decoder is JavaScript running in your own tab. Your file is read with the browser file API, decoded in memory and rendered from a blob URL when you download it. There is no fetch, no XHR, no WebSocket and no beacon in the decoder or in the decoding library, so there is no request to intercept. Open your browser developer tools, select Network, then drop the file: your log never appears in the list. Any traffic you do see is the site loading its own scripts and prefetching its own pages, never your file.
Which Zero log files can this reader decode?
Gen3 MBB, Gen3 BMS and the 538XX-firmware MBB variant decode in full, with the same field maps the server-side pipeline uses. Gen1/Gen2 logs (Cypher II / SDS) are recognised and identified but not decoded in the browser. Of the 352 real log files this decoder was validated against, 67 decode here, for 317,124 entries.
My Gen3 log decoded but shows no rides. Is the tool broken?
No — the rides were already gone from the file. Gen3 boards write into a fixed ring buffer that overwrites itself oldest-first. A parked bike wakes about once an hour and writes a median of 31 entries, so roughly three days of standing fills the buffer with hibernation and pushes everything older out of it. Export within a day or two of a ride to keep the ride in the file.
Can I read a Gen1/Gen2 log (S, SR, DS, DSR, FX) here?
The reader identifies it and tells you what it is, but it does not decode the entries. Gen2 stores type-tagged variable-length messages rather than the 0xB2 ring buffer this port reads. Run the upstream zero-log-parser project locally, or upload the file to the full analyser here, which runs that engine server-side.
Does the firmware build date in the header tell me whether my log will decode?
No, and it is worth saying so because it looks like it should. Across the 352-file corpus, 135 files carry a build date in the header. No cut-off year separates the files that decode from the ones that do not: the best threshold still misclassifies eleven of them, because a Gen2 bike can carry firmware built in 2024 and a Gen3 bike can carry firmware built in 2019. The reader decides by looking at the file itself, and tells you which rule fired, in about twenty milliseconds.
Do I need an account to use this?
No. There is no sign-up, no cap on file size beyond a 32 MB refusal that protects your own browser, no watermark and no truncated output. An account exists for the things one file cannot give you — history stitched across several exports per VIN, sessions, battery health, faults and AI analysis — not for this conversion.
What is in the .txt file I download?
A header block (source file, size, log kind, VIN, model, board, firmware, serial), then every decoded entry as a timestamped table, then a decode summary listing the framing strategy, how many 0xB2 records were framed, why the rest were not, a record-type histogram and the values each plausibility gate refused. Timestamps are UTC exactly as stored in the record, with no timezone applied. "Mask identifiers" starts ticked on this page, so the VIN and the board serial are replaced with X in the header block, in the entry text, and in the file name the download is saved under. Untick it to get the log in full.
How large is the decoded text?
Measured on real logs: a 128 KB Gen3 MBB export with 2,535 entries produces about 226 KB of text, a 128 KB Gen3 BMS export with 899 entries about 81 KB, and a 256 KB 538XX MBB export with 4,269 entries about 732 KB. A Gen1/Gen2 file produces a 1.3 KB identification report instead of an entry table.