How to compute CRC 16 in Python and C

The test program shown here demonstrates how to compute CRC-16 DNP in Python using two different packages, PyCRC and crcmod.

The latter not only exports a lot more of precooked CRC polynomials but is even able to generate standard C code that could be embedded into a microcontroller talking to the master (usually implemented in a Linux machien).

The CRC polynomial is a 16 bit in order to save bandwidth on the serial line.Moreover, both the Python modules and the generated C code are able to cope with odd-number of bytes, contributing to a more efficient message format.

    # install crcmod module
    sudo pip install crcmod
    
    # start Python 
    python
    
    import crcmod
    
    # select CRC-16-DNP
    crc16 = crcmod.mkCrcFun(0x13D65, 0xFFFF, True, 0xFFFF)
    
    # test
    hex(crc16('123456789'))
    '0xea82'
    
    # generate the code
    crc16_gen_code = crcmod.Crc(0x13D65, 0xFFFF, True, 0xFFFF)
    f = open('crc-16-dnp.h', 'w')
    crc16_gen_code.generateCode('crcr16dnp.h',f)
    exit
    
/*
 *  CRC-16 DNP test
 *
 *  http://crcmod.sourceforge.net/crcmod.html
 *  http://crcmod.sourceforge.net/crcmod.predefined.html#class-predefinedcrc
 *
 *  crcmod
 *  sudo pip install crcmod
 *  python
 *  import crcmod
 *
 *  crc16 = crcmod.mkCrcFun(0x13D65, 0xFFFF, True, 0xFFFF)
 *  hex(crc16('123456789'))
 *  '0xea82'
 *
 *  crc16_gen_code = crcmod.Crc(0x13D65, 0xFFFF, True, 0xFFFF)
 *  f = open('crc-16-dnp.h', 'w')
 *  crc16_gen_code.generateCode('crcr16dnp.h',f)
 *  exit
 *
 *  gcc test_crc16dnp.c -o test_crc16dnp && ./test_crc16dnp '123456789'
 *
 * http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
 *
 * check with CRC16_DNP
 *
 *
 * PyCRC
 *
 * python
 * input = '123456789'
 * from PyCRC.CRC16DNP import CRC16DNP
 * print(hex(CRC16DNP().calculate(input)))
 * 0x82ea
 */

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define UINT8 uint8_t
#define UINT16 uint16_t
#include "crc-16-dnp.h"


int main (const int argc, char **argv)
{
    if (argc > 1) {
        fprintf(stderr, "Input: [%s]\n", argv[1]);
        uint16_t crc = crcr16dnp(argv[1], strlen(argv[1]), 0xFFFF);
        printf ("%04x", crc);
    } else {
        fprintf(stderr, "Argument missing.\n");
    }
}




// Automatically generated CRC function
// polynomial: 0x13D65, bit reverse algorithm
UINT16
crcr16dnp.h(UINT8 *data, int len, UINT16 crc)
{
    static const UINT16 table[256] = {
    0x0000U,0x365EU,0x6CBCU,0x5AE2U,0xD978U,0xEF26U,0xB5C4U,0x839AU,
    0xFF89U,0xC9D7U,0x9335U,0xA56BU,0x26F1U,0x10AFU,0x4A4DU,0x7C13U,
    0xB26BU,0x8435U,0xDED7U,0xE889U,0x6B13U,0x5D4DU,0x07AFU,0x31F1U,
    0x4DE2U,0x7BBCU,0x215EU,0x1700U,0x949AU,0xA2C4U,0xF826U,0xCE78U,
    0x29AFU,0x1FF1U,0x4513U,0x734DU,0xF0D7U,0xC689U,0x9C6BU,0xAA35U,
    0xD626U,0xE078U,0xBA9AU,0x8CC4U,0x0F5EU,0x3900U,0x63E2U,0x55BCU,
    0x9BC4U,0xAD9AU,0xF778U,0xC126U,0x42BCU,0x74E2U,0x2E00U,0x185EU,
    0x644DU,0x5213U,0x08F1U,0x3EAFU,0xBD35U,0x8B6BU,0xD189U,0xE7D7U,
    0x535EU,0x6500U,0x3FE2U,0x09BCU,0x8A26U,0xBC78U,0xE69AU,0xD0C4U,
    0xACD7U,0x9A89U,0xC06BU,0xF635U,0x75AFU,0x43F1U,0x1913U,0x2F4DU,
    0xE135U,0xD76BU,0x8D89U,0xBBD7U,0x384DU,0x0E13U,0x54F1U,0x62AFU,
    0x1EBCU,0x28E2U,0x7200U,0x445EU,0xC7C4U,0xF19AU,0xAB78U,0x9D26U,
    0x7AF1U,0x4CAFU,0x164DU,0x2013U,0xA389U,0x95D7U,0xCF35U,0xF96BU,
    0x8578U,0xB326U,0xE9C4U,0xDF9AU,0x5C00U,0x6A5EU,0x30BCU,0x06E2U,
    0xC89AU,0xFEC4U,0xA426U,0x9278U,0x11E2U,0x27BCU,0x7D5EU,0x4B00U,
    0x3713U,0x014DU,0x5BAFU,0x6DF1U,0xEE6BU,0xD835U,0x82D7U,0xB489U,
    0xA6BCU,0x90E2U,0xCA00U,0xFC5EU,0x7FC4U,0x499AU,0x1378U,0x2526U,
    0x5935U,0x6F6BU,0x3589U,0x03D7U,0x804DU,0xB613U,0xECF1U,0xDAAFU,
    0x14D7U,0x2289U,0x786BU,0x4E35U,0xCDAFU,0xFBF1U,0xA113U,0x974DU,
    0xEB5EU,0xDD00U,0x87E2U,0xB1BCU,0x3226U,0x0478U,0x5E9AU,0x68C4U,
    0x8F13U,0xB94DU,0xE3AFU,0xD5F1U,0x566BU,0x6035U,0x3AD7U,0x0C89U,
    0x709AU,0x46C4U,0x1C26U,0x2A78U,0xA9E2U,0x9FBCU,0xC55EU,0xF300U,
    0x3D78U,0x0B26U,0x51C4U,0x679AU,0xE400U,0xD25EU,0x88BCU,0xBEE2U,
    0xC2F1U,0xF4AFU,0xAE4DU,0x9813U,0x1B89U,0x2DD7U,0x7735U,0x416BU,
    0xF5E2U,0xC3BCU,0x995EU,0xAF00U,0x2C9AU,0x1AC4U,0x4026U,0x7678U,
    0x0A6BU,0x3C35U,0x66D7U,0x5089U,0xD313U,0xE54DU,0xBFAFU,0x89F1U,
    0x4789U,0x71D7U,0x2B35U,0x1D6BU,0x9EF1U,0xA8AFU,0xF24DU,0xC413U,
    0xB800U,0x8E5EU,0xD4BCU,0xE2E2U,0x6178U,0x5726U,0x0DC4U,0x3B9AU,
    0xDC4DU,0xEA13U,0xB0F1U,0x86AFU,0x0535U,0x336BU,0x6989U,0x5FD7U,
    0x23C4U,0x159AU,0x4F78U,0x7926U,0xFABCU,0xCCE2U,0x9600U,0xA05EU,
    0x6E26U,0x5878U,0x029AU,0x34C4U,0xB75EU,0x8100U,0xDBE2U,0xEDBCU,
    0x91AFU,0xA7F1U,0xFD13U,0xCB4DU,0x48D7U,0x7E89U,0x246BU,0x1235U,
    };

    crc = crc ^ 0xFFFFU;
    while (len > 0)
    {
        crc = table[*data ^ (UINT8)crc] ^ (crc >> 8);
        data++;
        len--;
    }
    crc = crc ^ 0xFFFFU;
    return crc;
}

Links

Andrea Montefusco
Currently employed as network architect, always Internet working man, real C/C++ programmer in the past, network and Unix system engineer as needed, HAM Radio enthusiast (former IW0RDI, now IW0HDV), aeromodeller (a person who builds and flies model airplanes) since 1976 (ex FAI10655).
http://www.montefusco.com - https://github.com/amontefusco - https://github.com/IW0HDV - andrew@montefusco.com