Convert CRC16 CCITT code from C to Python

Question:

I want to do a Python implementation (actually MicroPython) of a specific checksum calculation based on CRC16-CCITT.
It will be used on a microcontroller to check data integrity over a serial connection.

The algorithm is available as C implementation.

The checksum is a 16 bit, type CCITT. The checksum starts at the first byte of the message.
Sample code to calculate the CRC16 CCITT in the C language:

#include <stdint.h>

uint16_t crc_ccitt_update( uint16_t crc, uint8_t data ) {

  uint16_t ret_val;

  data ^= ( uint8_t )(crc) & (uint8_t )(0xFF);
  data ^= data << 4;

  ret_val = ((((uint16_t)data << 8) | ((crc & 0xFF00) >> 8))
            ^ (uint8_t)(data >> 4)
            ^ ((uint16_t)data << 3));

  return ret_val;

}

uint16_t get_crc16z(uint8_t *p, uint16_t len) {

  uint16_t crc16_data=0;

  while(len--) {
      crc16_data = crc_ccitt_update(crc16_data, p[0]); p++;
  }

  return(crc16_data);

}

An example of a packet to be checked is:

0x3D 0x01 0x08 0x06 0x3A 0x00 0x98 0x81

The last two bytes carry the checksum as LSB and MSB, so the expected checksum is (bytes reversed):

0x8198

My coding skills in C are way too far away from being able to transfer this into Python code. So I tried at first to find existing functions online. There exist lots of functions but there seem to be always implementation specifics. So I failed to generate a valid CRC value.

Example online locations:
Online CRC calculator, Sunshine’s homepage

Example function I tried (one of many):

def crc16(data : bytearray, offset , length):
    if data is None or offset < 0 or offset > len(data)- 1 and offset+length > len(data):
        return 0
    crc = 0xFFFF
    for i in range(0, length):
        crc ^= data[offset + i] << 8
        for j in range(0,8):
            if (crc & 0x8000) > 0:
                crc =(crc << 1) ^ 0x1021
            else:
                crc = crc << 1
    return crc & 0xFFFF

packet = b'3D0108063A009881'

crc = crc16(packet, 0, int(len(packet)))

print('CRC:', crc)

checksum = int(b'8198', 16)
print('Check:', checksum)

I am even not sure if I use them correctly. Most likely not.

The second step was to try a Ctypes implementation by using the existing C code. I successfully generated a shared library on a Mac with the following command:

gcc -shared -Wl,-install_name,CRC16_CCITT.so -o CRC16_CCITT.so -fPIC crc16_ccitt.c

But again my C skills did not allow me to ‘populate’ the shared library correctly. See on of my failed attempts (which probably isn’t even proper Python code) when I gave up:

from ctypes import *

#load the shared object file
crc16_ccitt = CDLL('./CRC16_CCITT.so')

data = b'3D0108063A009881'

pointer = byref(data, 0)

crc = crc16_ccitt.get_crc16z(pointer, len(data))

print('CRC16-CCITT:', crc)

A first check would be compile, link and run the existing C code in order to see if it produces the expected result. The provided code is already modified by me, because in the original documentation there were some obvious typos (function call mismatch). But I don’t know how to run that in C. There needs to be some main function added and arguments need to be put with the correct data types.

The last thing I tried was to read the theory of the CRC algorithms. That was good enough to make my head explode.

Last resort stackoverflow

So, how to convert the existing C code to pure Python?

Asked By: chiefenne

||

Answers:

The CRC implemented in the provided Python code is not the CCITT CRC-16. It is referred to interestingly as a false CCITT CRC-16 in this catalog. The CRC implemented in the provided C code is in fact the CCITT CRC-16.

The Python code is easily modified to reflect the correct definition:

def crc16(data : bytearray, offset , length):
    if data is None or offset < 0 or offset > len(data)- 1 and offset+length > len(data):
        return 0
    crc = 0
    for i in range(0, length):
        crc ^= data[offset + i]
        for j in range(0,8):
            if (crc & 1) > 0:
                crc = (crc >> 1) ^ 0x8408
            else:
                crc = crc >> 1
    return crc
Answered By: Mark Adler
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.