CRC Calculator

Compute CRC-8, CRC-16, and CRC-32 checksums for hex bytes or ASCII text. Covers Modbus RTU, Dallas 1-Wire, Ethernet, USB, DNP3, iSCSI, and more — with a full CRC catalog showing which algorithm is used where.

Parameters

Algorithm
Input format

8 bytes

Result

CRC-8 result

0x7E

decimal: 126  ·  binary: 0b01111110

Algorithm parameters

Width8 bits
Polynomial0x07
RefIn / RefOutfalse / false
Init0x00
XorOut0x00
Check ("123456789")0xF4
Used in: Generic CRC-8, poly 0x07 — configure init and XorOut freely

How CRC works

CRC (Cyclic Redundancy Check) treats your data as a very large binary number and divides it by a fixed polynomial. The remainder of that division is the CRC. Both sender and receiver compute it independently; if the values match, the data arrived intact.

The “division” is done in GF(2) — polynomial arithmetic with coefficients modulo 2. That means addition is XOR and there are no carries. This makes CRC extremely fast to implement in hardware (a chain of XOR gates and a shift register) and cheap in software.

Key parameters

Every CRC variant is defined by five parameters:

ParameterEffect
WidthHow many bits in the remainder: 8, 16, or 32. More bits = smaller false-positive probability (CRC-32: 1 in ~4 billion).
PolyThe generator polynomial. Written without the implicit leading 1. CRC-32 poly 0x04C11DB7 is actually x³² + x²⁶ + ... + x + 1.
InitInitial value of the CRC register. 0x0000 or 0xFFFF are typical. 0xFFFF catches leading zeros that 0x0000 would ignore.
RefIn / RefOutWhether to reflect (reverse the bit order of) each input byte / the final output. Many protocols use reflected CRC to simplify bit-serial hardware.
XorOutXOR the final CRC with this before returning. 0xFFFF / 0xFFFFFFFF inverts the result, again to catch all-zeros edge cases.

The “check value” in the catalog is the CRC of the ASCII string 123456789. This is the standard test vector — if your implementation gives the right check value, the parameters are correct.

Choosing the right algorithm

Use whatever the protocol mandates. Don’t invent your own. Mixing up CRC-16/MODBUS (init=0xFFFF) and CRC-16/ARC (init=0x0000) is a classic integration bug — both use the same polynomial but produce different results.

If you’re designing a proprietary protocol:

  • CRC-8: Only 256 possible remainders. Only suitable for very short messages (< 8 bytes) where a 1-in-256 false-positive rate is acceptable. 1-Wire and SMBus use it because the messages are tiny.
  • CRC-16: 1-in-65536 false-positive rate. Fine for packets up to a few hundred bytes. Modbus RTU (max 256 bytes) is a good example.
  • CRC-32: 1-in-4-billion. Use this for flash validation, firmware update images, or anything where corruption would be catastrophic. This is what Ethernet, ZIP, and gzip use.
  • CRC-32C (Castagnoli): Better error detection properties than CRC-32 IEEE 802.3 for the same width. Preferred for storage (Btrfs, ext4) and modern networking (iSCSI, SCTP). Intel’s _mm_crc32_u8 instruction and ARM’s __crc32cb intrinsic compute this in a single CPU cycle.

Implementing CRC on your MCU

STM32 hardware CRC

STM32F4/F7/H7 have a hardware CRC unit (CRC_DR). By default it runs CRC-32 IEEE 802.3 (poly 0x04C11DB7, init 0xFFFFFFFF, input data reversed per-word). The HAL function is HAL_CRC_Calculate().

Watch out: the STM32 peripheral processes 32-bit words with the bytes in each word reversed. If you feed it byte-by-byte, you get a different result than a standard CRC-32 software implementation. Either process words and account for the reversal, or use the CRC_INPUTDATA_FORMAT_BYTES configuration available on STM32L4 and newer which handles byte-level input correctly.

/* STM32 HAL — CRC-32 of a byte array */
uint32_t crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, length / 4);

nRF52 hardware CRC

The nRF52 series has a RADIO peripheral with a built-in CRC engine used for BLE packets. For general-purpose CRC you typically use a software implementation. The nRF5 SDK includes crc16.c (CRC-16/CCITT, poly 0x1021) for use in DFU and mesh protocols.

Software bit-by-bit implementation

The bit-by-bit approach is slow but universal and matches the reference catalog exactly:

uint16_t crc16_modbus(const uint8_t *data, size_t len) {
    uint16_t crc = 0xFFFF;
    for (size_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001; /* reflected poly 0x8005 */
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

0xA001 is the bit-reversed (reflected) form of 0x8005. When refIn=true, processing the bits LSB-first with the reflected polynomial gives the same result as the canonical MSB-first algorithm after reflection.

Table-driven CRC (fast)

For throughput-sensitive code, precompute a 256-entry lookup table and reduce the inner loop to one XOR and one table lookup per byte. This runs ~8× faster than bit-by-bit:

/* Precompute at startup or generate offline as a const table */
static uint16_t crc_table[256];

void crc16_modbus_init_table(void) {
    for (int i = 0; i < 256; i++) {
        uint16_t crc = i;
        for (int j = 0; j < 8; j++) {
            crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
        }
        crc_table[i] = crc;
    }
}

uint16_t crc16_modbus_fast(const uint8_t *data, size_t len) {
    uint16_t crc = 0xFFFF;
    while (len--) {
        crc = (crc >> 8) ^ crc_table[(crc ^ *data++) & 0xFF];
    }
    return crc;
}

Byte order in the output

CRC-16 and CRC-32 results are multi-byte values. Protocols differ on which byte goes first on the wire.

Modbus RTU: transmits CRC low byte first, then high byte. So if crc16_modbus() returns 0x4B37, you send 0x37 then 0x4B.

Ethernet / ZIP / PNG: transmit CRC big-endian (most significant byte first).

Always check the protocol spec for byte order. This is different from the RefIn/RefOut bit reflection which affects the computation, not the wire encoding.

Common mistakes

Wrong initial value. CRC-16/MODBUS uses init=0xFFFF. CRC-16/ARC uses init=0x0000. Same polynomial, different results. If your CRC matches sometimes but not always, suspect the init value.

Forgetting XorOut. CRC-32 XORs the final result with 0xFFFFFFFF. Leaving this out produces a completely wrong result. The “check” value in the catalog is your ground truth — run 123456789 through your implementation and compare.

Including the CRC bytes in the CRC calculation. Compute CRC over the payload only, then append it. Don’t include the CRC field itself. (Exception: some protocols verify by computing CRC over payload+CRC and expecting a fixed “residue” value — check your spec.)

STM32 word alignment issues. The STM32 CRC peripheral requires 32-bit aligned, big-endian word input. If your buffer is byte-addressed and not a multiple of 4 bytes long, you need to handle the tail bytes in software.

Using CRC as a security hash. CRC detects accidental corruption. It is trivially forgeable — an attacker can craft data with any target CRC. For firmware authentication, use HMAC-SHA256 or a proper digital signature. CRC is for noise, not adversaries.