Why Your CRC Does Not Match
Two devices compute "CRC-16" over the same bytes and get different answers. Neither has a bug in the usual sense. "CRC-16" names a width, not an algorithm, and there are dozens of 16-bit CRCs in use that differ in four other settings. This guide explains those settings, gives reference values to test against, and ends with a procedure for finding which one is wrong.
Five parameters define a CRC
| Parameter | Meaning |
|---|---|
| Width | Size of the result in bits: 8, 16, 32 |
| Polynomial | The divisor, written without its top bit, for example 0x8005 or 0x1021 |
| Init | Value of the register before the first byte |
| Reflection | Whether bytes are processed least significant bit first, and whether the result is bit-reversed |
| XOR-out | Value XORed onto the result at the end |
A mismatch in any one of them gives a completely different result, not a nearly right one. That is by design: a CRC spreads every input bit across the whole output.
Reference values
The standard way to identify a CRC is its check value: the result for the nine ASCII bytes 123456789. If your implementation returns the check value, all five parameters are right.
| Name | Polynomial | Init | Reflected | XOR-out | Check value |
|---|---|---|---|---|---|
| CRC-8 | 0x07 | 0x00 | no | 0x00 | 0xF4 |
| CRC-16/MODBUS | 0x8005 | 0xFFFF | yes | 0x0000 | 0x4B37 |
| CRC-16/ARC | 0x8005 | 0x0000 | yes | 0x0000 | 0xBB3D |
| CRC-16/CCITT-FALSE | 0x1021 | 0xFFFF | no | 0x0000 | 0x29B1 |
| CRC-16/XMODEM | 0x1021 | 0x0000 | no | 0x0000 | 0x31C3 |
| CRC-16/KERMIT | 0x1021 | 0x0000 | yes | 0x0000 | 0x2189 |
| CRC-32 | 0x04C11DB7 | 0xFFFFFFFF | yes | 0xFFFFFFFF | 0xCBF43926 |
| CRC-32/MPEG-2 | 0x04C11DB7 | 0xFFFFFFFF | no | 0x00000000 | 0x0376E6E7 |
Look at the three rows with polynomial 0x1021. Same polynomial, three different results, and all three are called "CCITT" somewhere. The same holds for MODBUS and ARC, which differ only in the initial value. A specification that says "CRC-16 CCITT" without stating init and reflection has not told you which CRC to use.
Reflection, and the two faces of a polynomial
A reflected CRC processes each byte starting from its least significant bit. This matches how a UART puts bits on the wire, which is why serial protocols such as Modbus use reflected CRCs. Software implementations of reflected CRCs shift right instead of left, and to make that work they use the bit-reversed polynomial:
| Polynomial as specified | Bit-reversed form used in right-shifting code |
|---|---|
| 0x8005 | 0xA001 |
| 0x1021 | 0x8408 |
| 0x04C11DB7 | 0xEDB88320 |
So 0x8005 and 0xA001 are the same polynomial in two notations. Seeing 0xA001 in Modbus code and 0x8005 in the Modbus specification is not a contradiction. Mixing them up, by using 0xA001 in left-shifting code, gives a valid but different CRC that matches nothing.
Why the initial value is not zero
With an initial value of zero, leading zero bytes do not change the register. A message of four zero bytes and a message of five zero bytes both produce a CRC of zero under CRC-16/XMODEM, so a dropped or extra leading zero goes undetected. Starting from 0xFFFF fixes this: the same two messages give 0x84C0 and 0x110C. This is the reason most modern protocols use a non-zero initial value, and a good reason to choose one when designing your own.
Byte order on the wire
The CRC is a number; the protocol decides in which order its bytes are sent. Modbus RTU sends the low byte first. For the check string the CRC is 0x4B37, and the bytes appended to the frame are 37 4B. For the common request 01 03 00 00 00 0A the CRC is 0xCDC5 and the frame ends in C5 CD. Getting this backwards is the single most common Modbus CRC mistake, and it shows up as a CRC that looks right in the debugger and is rejected by the other device.
The residue trick
There is a neat way for a receiver to check a frame without separating the CRC from the data: run the CRC over the whole frame, including the received CRC bytes. If the frame is intact and the CRC was appended in the natural byte order for that algorithm, the result is a fixed constant.
- For CRC-16/MODBUS the constant is 0x0000.
- For CRC-32, with its final XOR, the constant is 0x2144DF1C.
This also works as a test of byte order. If the CRC over message plus CRC is not the expected constant, but the separately computed CRC matches, the two CRC bytes are swapped.
What a CRC does and does not guarantee
An n-bit CRC detects every single-bit error and every burst of errors no longer than n bits. For random corruption beyond that, a fraction of about 1 in 2n goes undetected: 1 in 65,536 for a 16-bit CRC, 1 in about 4.3 billion for a 32-bit one. A 16-bit CRC is appropriate for short frames on a serial link. For a firmware image of several hundred kilobytes, use 32 bits.
A CRC is not a security measure. Anyone who can change the data can recompute the CRC. To detect deliberate modification, use a cryptographic hash or a message authentication code.
Bitwise or table-driven
The bitwise algorithm needs no table and eight shift-and-XOR steps per byte. The table-driven version replaces those eight steps with one lookup, at the cost of a 256-entry table: 512 bytes of flash for a 16-bit CRC, 1 kB for a 32-bit one. On a small microcontroller checking a 20-byte frame, bitwise is fine. For megabytes of data, such as an image check at boot, the table pays for itself. Either way, keep the table in flash by declaring it const.
Some microcontrollers have a hardware CRC unit. Before relying on it, feed it 123456789 and compare with the table above. Hardware units often implement the non-reflected 32-bit variant and consume whole 32-bit words, so the result differs from the reflected CRC-32 used by zip, Ethernet and most PC tools unless the unit is configured, or the data pre-processed, to match.
Finding the wrong parameter
- Compute the CRC of
123456789with your code. Compare with the check value of the variant you believe you are implementing. - If it differs, compare against the other rows of the table. A match there tells you which variant you have actually implemented, and therefore which parameter is off.
- If your check value is right but the other device still disagrees, the algorithm is fine. Look at what is being fed in: the range of bytes covered, whether an address or length byte is included, and the byte order of the appended CRC.
- Capture one real frame from the other device and run your CRC over it, with and without the trailing CRC bytes. Use the residue constants above to check the byte order.
Step 3 is where most real problems are. The arithmetic is rarely wrong for long; the disagreement is usually about which bytes are covered.
Run the numbers: CRC Calculator does this calculation for your own values.
More guides
- ADC Resolution Is Not Accuracy: LSB Size, Reference Error, Settling and Oversampling
- C Struct Padding on Cortex-M: Where the Bytes Go and How to Control Them
- CAN Bit Timing Step by Step: Choosing BRP, Segments and Sample Point
- Fixed-Point Arithmetic in Q Format: Scaling, Multiplying and Not Overflowing
- Floating-Point Pitfalls on Microcontrollers: Precision, Timestamps and Accidental Doubles
- Sizing I2C Pull-Up Resistors: Minimum, Maximum and What Bus Capacitance Does
- How Much UART Baud Rate Error Is Too Much? The Sampling Math