ByteScope

Intel HEX Converter

Read a firmware image line by line, see exactly where every byte lands, and write it back out in whatever container your flasher wants.

Your firmware never leaves your browser — every file is parsed and rewritten locally.

Files

Drop firmware images here. The container is worked out from the bytes, never from the extension — override it per file when detection gets it wrong.

Drop .hex, .s19, .uf2 or .bin files here

or click to browse. Files are read in the page and never uploaded.

No files yet. Drop a firmware image, or paste some HEX text.

What this file says

The line-by-line report for the selected file. A bad checksum shows both numbers: the byte written on the line and the byte the record's own contents produce.

No files yet. Drop a firmware image, or paste some HEX text.

Combined address map

Every file, with its offset applied, laid out in one address space. Gaps are the holes a flat binary would have to fill.

No files yet. Drop a firmware image, or paste some HEX text.

Convert

Any input format converts to any output format. The options below are the ones that change what the file looks like, not what it contains.

firmware.bin

About this tool

Four containers, one pile of addressed bytes

Intel HEX, Motorola S-record and UF2 all say the same thing in three different accents: *these bytes go at this address*. A raw .bin says only the first half — it is the bytes with the addresses thrown away, which is why a BIN alone can never tell you where it belongs. Drop any of the four here and the format is worked out from the file itself: UF2 plus a newline in the first four bytes, a leading :, or an S followed by a digit. Nothing is guessed from the extension, because half the world names an S-record .hex and the other half names it .s19, .s28, .srec or .mot. If the detection is wrong for your file — a truncated header, a stripped first line — you can override it per file and the page will tell you what it thought it was looking at.

A bad checksum has two numbers, not one

Every tool that reads Intel HEX will tell you a line is broken. Very few tell you *how* broken. This one reports both bytes: the checksum written on the line, and the checksum the record's own contents produce. When those differ by a single bit you are looking at a transfer that flipped one, and when they differ by the byte count you are usually looking at a file somebody edited by hand and forgot to recompute. A malformed line also does not swallow the lines after it — line 5 failing has no effect on line 6, so one damaged record in a 200,000-line image costs you one record, not the rest of the file. Everything else that is structurally wrong is named too: data after the end-of-file record, an address record with the wrong length, a record type nobody defined.

Segments, gaps, and the bytes two files both claim

Once the records are read they collapse into segments — contiguous runs with a start address and a length — and the holes between them are shown as gaps, with the size of each. That is the picture you want before flashing anything: a bootloader at 0x0000, an application at 0x8000, and 30 KB of nothing in between that a flat BIN would have to fill with something. When one file writes the same byte twice the range is listed rather than quietly resolved. When *two* files do, the merge is refused outright and every colliding range is shown with the two file names — because the alternative, letting whichever file loaded last win, is how a bootloader disappears without anybody noticing until the board stops enumerating.

Writing it back out

Any input format converts to any output format. Intel HEX takes a bytesPerLine and can be written with or without type-04 extended-linear-address records. S-record lets you pin the address width — S1/S9 for 16-bit, S2/S8 for 24-bit, S3/S7 for 32-bit — or leave it on the narrowest form that fits the highest byte actually written. UF2 takes a payload size and a family id, and the id is checked against the published family table so a typo shows up as an unknown id rather than as a board that refuses the drag-and-drop. A flat BIN takes a fill byte for the gaps, defaulting to 0xFF because that is what erased flash reads as. All of it runs in a Web Worker inside this tab: nothing is uploaded, and the firmware you are reverse-engineering stays your business.

Frequently asked questions

Why is the BIN much larger than the HEX file it came from?

Because a flat binary has no addresses. If your image writes 4 KB at 0x0000 and 4 KB at 0x0800_0000, the BIN has to be everything in between as well — that span, not the payload. The segment map shows the gaps and their sizes before you convert, and the page refuses to allocate an enormous file without asking first. If the span surprises you, the usual cause is a stray record placing a few bytes far away from the rest, and the segment table will show it as a tiny segment on its own.

What address should I give a raw BIN file?

Whatever address your flasher would have written it to, because the file itself does not know. A BIN is loaded at 0 here and you supply the real load address in its offset field — 0x08000000 for STM32 internal flash, 0x10000 for a typical ESP32 application, 0x0 for a bootloader. Getting this wrong is not a subtle failure: convert to Intel HEX afterwards and the addresses on every line will tell you immediately whether it landed where you meant.

Why will it not merge my bootloader and my application?

Because they both write at least one of the same bytes, and there is no correct way to pick a winner. The conflicting ranges are listed with the two file names and the exact addresses; usually one image was built with the wrong linker script, or an application image still carries a copy of the vector table. Fix the overlap — or shift one file with its offset field — and the merge goes through. Byte-identical overlap is still refused, because two builds of the same thing writing the same bytes is a situation you want to know about rather than to have smoothed over.

Which UF2 family id do I need?

The one your bootloader expects: 0xE48BFF56 for RP2040, 0x1C5F21B0 for ESP32, 0x68ED2B88 for SAMD21, 0x55114460 for SAMD51, 0x1B57745F for nRF52, 0x57755A57 for STM32F4. The full published table is matched against whatever you enter, and a UF2 file with no family id at all is also legal — that is what the flags field being zero means. If a board silently ignores a UF2 you dragged onto it, a wrong or missing family id is the first thing to check.

Is my firmware uploaded anywhere?

No. The files are read with the File API, parsed in a Web Worker in this tab, and dropped when you close it. There is no upload step, no account and no server side to this page — which matters more here than on most tools, because an unreleased firmware image is exactly the kind of file people are told not to paste into online converters.