One name, twenty-eight answers
Someone hands you a serial frame and a checksum, tells you the device uses CRC16, and the number your code produces looks nothing like theirs. Neither of you made a mistake. CRC16 names a width, not an algorithm, and the published catalogue holds more than a hundred sixteen bit variants that share the name.
Take the string 123456789. Under CRC-16/MODBUS it hashes to 4B37. Under CRC-16/XMODEM it is 31C3. Under CRC-16/KERMIT, which uses the same polynomial as XMODEM, it is 2189. Same input, same width, three unrelated numbers.
Most CRC pages pick one variant, label it CRC16 and leave you to work out why your value differs. This one runs all 28 at once and lets you paste the value you expected, so the table tells you which parameter set the other side is running.
Five parameters describe any of them
Ross Williams wrote the model everyone still uses. Five numbers pin a variant down completely:
- Polynomial, the divisor.
0x8005and0x1021cover most of the field, with0x3D65for DNP3 and a long tail of one-off choices. - Init, the starting register value.
0000andFFFFare usual. An init ofFFFFexists so a run of leading zero bytes changes the result, which an init of zero would ignore. - Reflect input, whether each byte is fed in least significant bit first. Hardware shift registers and software table loops disagree here, which is the whole reason the flag exists.
- Reflect output, whether the final register is bit reversed before you read it.
- XorOut, a value XORed with the result at the end. Usually
0000orFFFF.
Every variant in the table above differs only in those five fields. The Custom option in the selector exposes them directly, so a vendor datasheet listing an odd init like B2AA takes seconds to reproduce.
The check value is the fastest way to identify a library
Each catalogued variant publishes the CRC of the nine ASCII bytes 123456789. That single number identifies the parameter set. Run those nine characters through whatever library your firmware links against, look up the result in the Check column, and you know exactly what you have been calling CRC16.
The trap is a library that says CCITT. CRC-16/IBM-3740 has been shipped under the CCITT name for decades even though it starts at FFFF, while the variant the ITU actually specified starts at zero with reflection and now goes by KERMIT. Check 29B1 against 2189 and the label stops mattering.
Byte order breaks more integrations than the polynomial does
A Modbus RTU read request looks like this on the wire:
01 03 00 00 00 0A C5 CD
slave 01, function 03, start 0000, count 000A, then two CRC bytes
Feed the first six bytes into the Hex tab with MODBUS selected and you get CDC5. The frame carries C5 CD. Nothing is wrong: Modbus transmits the CRC low byte first, so the value you compute reads backwards against the capture. That is why the result panel prints both byte orders under the headline number, and why the match field flags a hit with the bytes swapped instead of reporting no match.
Leave the CRC bytes attached and run the whole eight byte frame through MODBUS again. The answer is 0000. Appending the checksum in the order the protocol expects drives the register to a fixed residue, which is how receivers verify a frame without recomputing and comparing. A zero there means the frame survived the wire.
Which variant belongs to which world
- Industrial serial buses
- Modbus RTU uses CRC-16/MODBUS on every frame. DNP3 runs CRC-16/DNP over each 16 byte data block rather than the whole message, so a full frame needs several values. Profibus and IEC 60870-5-1 sit on their own polynomial.
- File transfer that predates the internet
- XMODEM-CRC and ZMODEM use CRC-16/XMODEM. Kermit uses the reflected version with the same polynomial. Both still turn up in bootloaders and device firmware upload paths, long after the modems went away.
- Contactless cards and RFID
- ISO/IEC 14443 type A tags, meaning most Mifare deployments, use CRC-A with init
C6C6. Type B uses CRC-16/IBM-SDLC. Microchip RFID parts use MCRF4XX, and EPC Gen 2 tags use GENIBUS. - Storage and buses
- USB 2.0 data packets carry CRC-16/USB. The T10 Data Integrity Field on 520 byte SCSI sectors uses polynomial
0x8BB7. 1-Wire temperature sensors and iButton keys use MAXIM-DOW. - Archives and legacy formats
- LHA archives and the old ARC format use CRC-16/ARC, which plenty of PLC vendors ship as simply CRC-16. Teledisk floppy images have their own polynomial,
0xA097, and nothing else uses it.
Writing the loop yourself
A table driven CRC-16 is about fifteen lines. Build a 256 entry table from the polynomial, then fold one byte at a time into the register, reflecting the byte first when the variant asks for it. This page uses that structure, which is why a 500 MB file finishes without the tab going grey.
Two mistakes account for most broken implementations. The first is reflecting bytes on input but forgetting the final register reflection, which gives a value that looks plausible and matches nothing. The second is confusing the normal and reversed forms of the polynomial: 0x8005 and 0xA001 describe the same divisor, one written for a left shifting loop and one for a right shifting loop. Feeding 0xA001 into a left shifting implementation produces a checksum no standard recognises.
The Custom panel takes the normal, left shifting form, which is what the Poly column shows for every row.
Where this page stops
No verification pass. Give it bytes and it returns a number. Deciding whether a frame is intact means either stripping the trailing CRC yourself and comparing, or including it and checking for the residue described above. Comparing a file against a published checksum belongs in Checksum Validator.
No widths other than 16. CRC-32 for archives and Ethernet lives on the CRC32 generator, and running one input through several unrelated algorithms belongs in the Hash Generator Suite.
No search for unknown parameters. If you hold a set of frames with their checksums but no datasheet, the match field only tests the 28 variants listed. Recovering an undocumented polynomial from sample data is a different job, and CRC RevEng is the tool built for it.
No block splitting. DNP3 checksums each 16 byte block separately, so pasting a whole DNP3 frame gives you a value the protocol never computes. Feed it one block at a time.
Files over 32 MB run the selected variant alone. Twenty-eight parameter sets across a multi gigabyte file takes long enough to look like a crash, so the table stops filling and the headline number keeps working.
