✅ CRC16 Explained · RTU Only

Modbus CRC Calculation: How the RTU Checksum Works

If your RTU frame looks correct but the slave stays silent, the problem may be the CRC. This guide explains the Modbus CRC16 algorithm, the byte order on the wire, and how to verify frames with code.

⬇ Download Free Trial Read FAQ ↓

Quick answer: Modbus RTU uses a CRC16 with polynomial 0xA001, initial value 0xFFFF, and low byte first transmission. If your checksum is wrong, the slave usually ignores the frame completely.

Why Modbus uses CRC

Modbus RTU runs over serial links such as RS-485 and RS-232. Those links can be noisy, especially on long cables or poorly terminated buses. CRC adds error detection so the receiving device can reject corrupted frames instead of acting on bad data.

Unlike Modbus TCP, RTU does not have TCP/IP transport checksums protecting the Modbus payload. The CRC is therefore essential. Without it, a single flipped bit could turn a valid register read into the wrong address or the wrong value.

The Modbus CRC rules

How the algorithm works

The algorithm starts with 0xFFFF in the CRC register. It XORs each byte of the frame into the low byte of the CRC, then processes each bit. If the least significant bit is set, the register shifts right and XORs with 0xA001. If not, it just shifts right. That repeated bitwise process is what produces the final 16-bit checksum.

Worked example

Take a simple RTU request: slave 01, function 03, starting address 0000, quantity 0001. The frame bytes before the CRC are:

01 03 00 00 00 01

When the CRC is computed correctly, the final two bytes for this frame are commonly shown as 84 0A or as the 16-bit value 0x0A84 depending on how the result is displayed. The key point is the byte order: the low byte is sent first.

Important: Many tools display the CRC as a single 16-bit hex value. On the wire, Modbus RTU transmits the low byte first. If you swap the bytes, the slave will treat the frame as invalid.

Python example

Here is a straightforward CRC implementation that matches the Modbus RTU rules:

def modbus_crc(data: bytes) -> int:
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc = (crc >> 1) ^ 0xA001
            else:
                crc >>= 1
    return crc & 0xFFFF

frame = bytes.fromhex("01 03 00 00 00 01")
crc = modbus_crc(frame)
print(hex(crc))

To append the CRC to the RTU frame, send crc & 0xFF first, then (crc >> 8) & 0xFF.

How to check a bad frame

If you suspect a CRC problem, do this:

  1. Capture the raw bytes from the master.
  2. Remove the final two CRC bytes.
  3. Run the remaining bytes through a known-good CRC routine.
  4. Compare the expected CRC to the transmitted CRC.
  5. If they differ, check byte order first and then the actual data bytes.

Common CRC mistakes

MistakeWhat happensWhat to check
Using the wrong polynomialAll frames fail validationConfirm 0xA001 reflected form
Wrong initial valueCRC never matches vendor examplesStart with 0xFFFF
High byte sent firstSlave discards the frameTransmit low byte first
Including the CRC bytes in the calculationChecksum is always wrongOnly compute over the payload bytes
Using TCP rules on RTUProtocol mismatchRemember that Modbus TCP does not use the RTU CRC

CRC vs timeout: how to tell the difference

A bad CRC usually produces no response from the slave. That looks exactly like a timeout at the application level. The difference is in the wire capture: with a CRC problem, the master sent bytes, but the slave intentionally ignored them because they failed validation.

If you see a timeout, do not assume the address is wrong immediately. Check the CRC, serial settings, and termination first. A valid-looking register read with a bad CRC is still a bad frame.

Testing with a simulator

ModbusSimulator helps here in two ways. First, you can verify that your master is building a valid RTU request and getting a proper response. Second, you can compare the raw frame bytes against a known-good simulator response so you know the stack works before introducing field wiring noise.

Stop guessing at RTU frames

Use ModbusSimulator to verify your polling workflow and isolate whether the issue is CRC, wiring, or register mapping.

Download ModbusSimulator

Frequently Asked Questions

Is Modbus CRC the same as CRC-16/IBM?

Modbus uses the reflected CRC-16/IBM style algorithm with polynomial 0xA001 and initial value 0xFFFF. In practice, Modbus RTU implementations refer to it as Modbus CRC16.

Why does the low byte come first?

That is part of the Modbus RTU framing convention. The checksum is stored little-endian on the wire even though people often display it as a 16-bit hex value.

Does Modbus ASCII use the same CRC?

No. Modbus ASCII uses a longitudinal redundancy check (LRC), not the RTU CRC16.

Can I use a library to avoid writing CRC code?

Yes. Most serial Modbus libraries calculate CRC automatically. But understanding the algorithm helps when debugging vendor examples, packet captures, or custom firmware.

What is the fastest way to debug CRC failures?

Capture the raw frame, compute the expected CRC with a trusted routine, and compare the two bytes at the end of the packet. Then check byte order before looking for deeper protocol issues.

Can ModbusSimulator validate my CRC?

Yes. If your master communicates successfully with the simulator, the request framing and CRC are likely correct. If it fails only on the field device, the issue may be noise or device-specific serial settings.