ESP32 CRC16

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
AntonioBrito
Posts: 7
Joined: Wed Jul 27, 2022 10:39 am

ESP32 CRC16

Post by AntonioBrito » Mon Aug 01, 2022 1:32 pm

hey guys,

I'm using a esp32 to communicate with a sensor, im getting a response from the sensor (8 bits).
For example, I get this: FF 03 02 15 28 9F 1E, I then use 0x15 * 256 + 0x28 = (humidity for example)
The last 2 bits are the checksum.
How do I put in the code for the CRC16 to check if everything is correct?

Thanks in advance to anyone that can help

User avatar
karfas
Posts: 193
Joined: Sat Jan 16, 2021 12:53 pm
Location: Vienna, Austria

Re: ESP32 CRC16

Post by karfas » Mon Aug 01, 2022 3:01 pm

According to https://en.wikipedia.org/wiki/Cyclic_redundancy_check there exist ~15 different CRC16 algorithms.

For a start, you could ask google for "python CRC16".
A few hours of debugging might save you from minutes of reading the documentation! :D
My repositories: https://github.com/karfas

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: ESP32 CRC16

Post by Roberthh » Tue Aug 02, 2022 5:48 am

@AntonioBrito: We had that discussion already at the PM, and I gave you a suitable code for the CRC calculation: Here it is again:

Code: Select all

PRESET = 0xFFFF
POLYNOMIAL = 0xA001 # bit reverse of 0x8005

def crc16(data):
    crc = PRESET
    for c in data:
        crc = crc ^ c
        for j in range(8):
            if (crc & 1) == 0:
                crc = crc >> 1
            else:
                crc = crc >> 1
                crc = crc ^ POLYNOMIAL
    return crc

# test case
data = b'\xFF\x03\x02\x15\x28\x9F\x1E'
crc = crc16(data)
print(hex(crc), len(data))
I added as test case the message you gave as example. If you calculate the CRC over the full message including the CRC, you get 0 as result. That is a property of CRC and an easy check for pass. If you omit the last two bytes in the CRC calculation, then you get the CRC from the message. Note, that not the ASCII representation of the message is used, but the binary content. If you receive the ASCII string, you have to convert it first to binary.
But according the the MODBUS spec for messages with CRC, the message is transferred binary.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: ESP32 CRC16

Post by Roberthh » Tue Aug 02, 2022 5:51 am

Another implementation uses a pre-compiled table, which is faster, since that table case to be created only once.

Code: Select all

PRESET = 0xFFFF
POLYNOMIAL = 0xA001 # bit reverse of 0x8005

def _initial(c):
    crc = 0
    for j in range(8):
        if (crc ^ c) & 0x1:
            crc = (crc >> 1) ^ POLYNOMIAL
        else:
            crc = crc >> 1
        c = c >> 1
    return crc

import array
_tab = array.array("H", [_initial(i) for i in range(256)])

def crc16t(str):
    crc = PRESET
    for c in str:
        crc = (crc >> 8) ^ _tab[(crc ^ c) & 0xff]
    return crc

data = b'\xFF\x03\x02\x15\x28\x9F\x1E'
crc = crc16t(data)
print(hex(crc), len(data))

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: ESP32 CRC16

Post by Roberthh » Tue Aug 02, 2022 6:59 am

A slightly simplified version of the table approach, which also prints the table for comparison:

Code: Select all

# --------------------------------------------------------
# Table based implementation

PRESET = 0xFFFF
POLYNOMIAL = 0xA001 # bit reverse of 0x8005

# create a single entry to the CRC table
def _initial(c):
    crc = c
    for j in range(8):
        if crc & 0x01:
            crc = (crc >> 1) ^ POLYNOMIAL
        else:
            crc = crc >> 1

    return crc

# Create the table
import array
_tab = array.array("H", [_initial(i) for i in range(256)])

# Checkum calculation
def crc16t(str):
    crc = PRESET
    for c in str:
        crc = (crc >> 8) ^ _tab[(crc ^ c) & 0xff]
    return crc

# Test case

print("\nCRC Table:")
for _ in range(256):
    if (_ % 8) == 0:
        print()
    print("0x%04x " % _tab[_], end="")
print()

data = b'\xFF\x03\x02\x15\x28\x9F\x1E'
crc = crc16t(data)
print("\nCRC Test result: ", hex(crc), len(data))
Everything is properly documented in the modbus spec. See: https://www.modbustools.com/modbus.html

The direct calculation can also be written slighly clearer as:

Code: Select all

# Direct calculation

PRESET = 0xFFFF
POLYNOMIAL = 0xA001 # bit reverse of 0x8005

def crc16(data):
    crc = PRESET
    for c in data:
        crc = crc ^ c
        for j in range(8):
            if crc & 0x01:
                crc = (crc >> 1) ^ POLYNOMIAL
            else:
                crc = crc >> 1
    return crc


# test case
data = b'\xFF\x03\x02\x15\x28\x9F\x1E'
crc = crc16(data)
print("CRC Test result: ", hex(crc), len(data))
If you compare the two implementations you'll see, that the table is created by calculating the CRC over each single byte in the range 0 - 0xff in the code line:

Code: Select all

_tab = array.array("H", [_initial(i) for i in range(256)])

AntonioBrito
Posts: 7
Joined: Wed Jul 27, 2022 10:39 am

Re: ESP32 CRC16

Post by AntonioBrito » Tue Aug 02, 2022 8:34 pm

thank you Robert I figure it out with your help

Post Reply