News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

Long shot request for crc16 modbus code

Started by TimB, Nov 28, 2025, 10:31 AM

Previous topic - Next topic

TimB


I HATE CRC's and encryption coding. They do my head in..  :o

So now I have to implements a CRC16 for Modbus, I have code converted from C that uses a look table as its quicker to calc the CRC. It's only a few lines of code.

To check my result I'm comparing it to a couple of tools
https://www.scadacore.com/tools/programming-calculators/online-checksum-calculator/
And an spread sheet from here https://www.simplymodbus.ca/crc.xls

The later is handy as it shows the crc after every element processed

If I enter (as hex) 8501020304 Both the above return 0x48D1

Mine returns 0xFE9D

If I look at the first loop I get 0xE37E which is actually a reverse of what it should 0x3EE7 so I swapped the bytes to make it right. But next pass goes wrong and in the end I get 0xFE9D

I tried asking 3 AI tools for the crc16 modbus of 8501020304 and got 3 different answers all of which do not match the 2 tools I have

So I'm here asking if anyone has any code to generate the CRC16 for modbus generation type "Normal"

Thanks

Tim

TimB


Hi All

I managed to work out the issues



Below is the code. I have hard coded the buffer its using as not sure how to pass pointers to buffers but you can modify as needed

   

    WHile 1 = 1

        bComsBuffer[0] = 0x85
        bComsBuffer[1] = 0x01
        bComsBuffer[2] = 0x02
        bComsBuffer[3] = 0x03
        bComsBuffer[4] = 0x04

        wtemp1 = CalcCRC16_MODBUS(5)

    Wend


    ' Constants
    DIm CRC16_INIT_VALUE as 0xFFFF
    Dim CRC16_POLY_REVERSED as 0xA001

    ' Function to calculate CRC-16
    Proc CalcCRC16_MODBUS(bLEN AS byte), word


        DIM wCRC AS word
        DIM bIndex AS Byte
        DIm bLoop  as byte
        DIm wtemp1 as word

        Dim bCurrentByte as byte



        wCRC = CRC16_INIT_VALUE

        ' Process each byte
        FOR bLoop = 0 TO bLEN - 1


        bCurrentByte = bComsBuffer[bLoop]

        ' 1. Calculate Index
        '    XOR the LSB of wCRC with the input byte.
        bINDEX = (wCRC & 0xFF) ^ bCurrentByte


        ' 2. CRC Update
        wtemp1 = cread16 crc16_table[bINDEX]

        ' 3. Update wCRC:
        '    Shift the old wCRC right 8, XOR with the table value.
        wCRC = (wCRC >> 8) ^ wtemp1



        NEXT bLoop

        ' 4 Swap the bytes to get a matching CRC Endnian
        swap wCRC.byte0, wCRC.byte1

        Result = wCRC

    ENDProc



'// ============================================================================
'// CRC-16 Lookup Table (512 bytes in Flash)
'// ============================================================================
'// Store in program memory to save RAM

 DIm CRC16_TABLE as flash16  =  0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,_
    0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,_
    0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,_
    0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,_
    0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,_
    0x1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,_
    0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,_
    0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,_
    0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,_
    0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,_
    0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,_
    0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,_
    0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,_
    0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,_
    0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,_
    0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,_
    0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,_
    0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,_
    0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,_
    0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,_
    0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,_
    0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,_
    0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,_
    0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,_
    0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,_
    0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,_
    0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,_
    0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,_
    0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,_
    0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,_
    0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,_
    0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040

top204

#2
Glad you got it working Tim, and well done.

Try the code listing below, which is a re-arrangement of yours above:

    Dim bComsBuffer[20] As Byte Heap
    Dim wCRC            As Word
    
    bComsBuffer = $85, $01, $02, $03, $04      ' Load the first 5 elements of bComsBuffer
    wCRC = CalcCRC16_MODBUS(bComsBuffer, 5)
    HRsout1Ln Hex4 wCRC

'-------------------------------------------------------------------------------------------------------------
' Calculate CRC16
' Input     : pBuffAddr holds the address of the RAM buffer array
'           : pLen holds the amount of bytes to read from the RAM buffer
' Output    : Returns the CRC16 value
' Notes     : None
'
Proc CalcCRC16_MODBUS(ByRef pBuffAddr As Word, pLen As Byte), Word
    Dim bIndex As Byte
    Symbol cCRC16_Init = $FFFF
'
' CRC16 Lookup Table (512 bytes in Flash)
'
    Dim CRC16_Table As Flash16 = $0000, $C0C1, $C181, $0140, $C301, $03C0, $0280, $C241,_
                                 $C601, $06C0, $0780, $C741, $0500, $C5C1, $C481, $0440,_
                                 $CC01, $0CC0, $0D80, $CD41, $0F00, $CFC1, $CE81, $0E40,_
                                 $0A00, $CAC1, $CB81, $0B40, $C901, $09C0, $0880, $C841,_
                                 $D801, $18C0, $1980, $D941, $1B00, $DBC1, $DA81, $1A40,_
                                 $1E00, $DEC1, $DF81, $1F40, $DD01, $1DC0, $1C80, $DC41,_
                                 $1400, $D4C1, $D581, $1540, $D701, $17C0, $1680, $D641,_
                                 $D201, $12C0, $1380, $D341, $1100, $D1C1, $D081, $1040,_
                                 $F001, $30C0, $3180, $F141, $3300, $F3C1, $F281, $3240,_
                                 $3600, $F6C1, $F781, $3740, $F501, $35C0, $3480, $F441,_
                                 $3C00, $FCC1, $FD81, $3D40, $FF01, $3FC0, $3E80, $FE41,_
                                 $FA01, $3AC0, $3B80, $FB41, $3900, $F9C1, $F881, $3840,_
                                 $2800, $E8C1, $E981, $2940, $EB01, $2BC0, $2A80, $EA41,_
                                 $EE01, $2EC0, $2F80, $EF41, $2D00, $EDC1, $EC81, $2C40,_
                                 $E401, $24C0, $2580, $E541, $2700, $E7C1, $E681, $2640,_
                                 $2200, $E2C1, $E381, $2340, $E101, $21C0, $2080, $E041,_
                                 $A001, $60C0, $6180, $A141, $6300, $A3C1, $A281, $6240,_
                                 $6600, $A6C1, $A781, $6740, $A501, $65C0, $6480, $A441,_
                                 $6C00, $ACC1, $AD81, $6D40, $AF01, $6FC0, $6E80, $AE41,_
                                 $AA01, $6AC0, $6B80, $AB41, $6900, $A9C1, $A881, $6840,_
                                 $7800, $B8C1, $B981, $7940, $BB01, $7BC0, $7A80, $BA41,_
                                 $BE01, $7EC0, $7F80, $BF41, $7D00, $BDC1, $BC81, $7C40,_
                                 $B401, $74C0, $7580, $B541, $7700, $B7C1, $B681, $7640,_
                                 $7200, $B2C1, $B381, $7340, $B101, $71C0, $7080, $B041,_
                                 $5000, $90C1, $9181, $5140, $9301, $53C0, $5280, $9241,_
                                 $9601, $56C0, $5780, $9741, $5500, $95C1, $9481, $5440,_
                                 $9C01, $5CC0, $5D80, $9D41, $5F00, $9FC1, $9E81, $5E40,_
                                 $5A00, $9AC1, $9B81, $5B40, $9901, $59C0, $5880, $9841,_
                                 $8801, $48C0, $4980, $8941, $4B00, $8BC1, $8A81, $4A40,_
                                 $4E00, $8EC1, $8F81, $4F40, $8D01, $4DC0, $4C80, $8C41,_
                                 $4400, $84C1, $8581, $4540, $8701, $47C0, $4680, $8641,_
                                 $8201, $42C0, $4380, $8341, $4100, $81C1, $8081, $4040
    Result = cCRC16_Init                                    ' Place the initial value in the CRC variable
    Repeat                                                  ' Create a loop to process each byte
        bIndex = Result.Byte0 ^ Ptr8(pBuffAddr++)           ' Calculate the Index. Xor the LSB of Result with the input byte
        Result = Result.Byte1 ^ CRead16 CRC16_Table[bIndex] ' Update Result. Shift the old Result right 8, Xor with the table value
        Dec pLen                                            ' \
    Until pLen = 0                                          ' / Loop until all the required bytes in the array have been processed
    Swap Result.Byte0, Result.Byte1                         ' Swap the bytes to get a matching CRC endian
EndProc

I'm not sure how to test it, or what values to get back etc, so please let me know.

I get back, the value of $48D1, which I also get back when using another CRC16 procedure I have created that does not use a lookup table. If it is correct Tim, I'll post the smaller CRC16 procedure.

Pepe

demo proteus

TimB


Thanks Pepe

The CRC produced unfortunately is not matching the results I get from several online CRC16 Modbus calculators. But many thanks any case, much appreciated.

The formatting of my code post got screwed with the data table ending up in the middle of the code. I will repost it later

Tim

TimB



    ' Constants
    DIm CRC16_INIT_VALUE as 0xFFFF
    Dim CRC16_POLY_REVERSED as 0xA001

    ' Function to calculate CRC-16
    Proc CalcCRC16_MODBUS(bLEN AS byte), word


        DIM wCRC AS word
        DIM bIndex AS Byte
        DIm bLoop  as byte
        DIm wtemp1 as word

        Dim bCurrentByte as byte



        wCRC = CRC16_INIT_VALUE

        ' Process each byte
        FOR bLoop = 0 TO bLEN - 1


        bCurrentByte = bComsBuffer[bLoop]

        ' 1. Calculate Index
        '    XOR the LSB of wCRC with the input byte.
        bINDEX = (wCRC & 0xFF) ^ bCurrentByte


        ' 2. CRC Update
        wtemp1 = cread16 crc16_table[bINDEX]

        ' 3. Update wCRC:
        '    Shift the old wCRC right 8, XOR with the table value.
        wCRC = (wCRC >> 8) ^ wtemp1



        NEXT bLoop

        ' 4 Swap the bytes to get a matching CRC Endnian
        swap wCRC.byte0, wCRC.byte1

        Result = wCRC

    ENDProc



'// ============================================================================
'// CRC-16 Lookup Table (512 bytes in Flash)
'// ============================================================================
'// Store in program memory to save RAM

 DIm CRC16_TABLE as flash16  =  0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,_
    0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,_
    0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,_
    0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,_
    0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,_
    0x1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,_
    0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,_
    0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,_
    0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,_
    0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,_
    0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,_
    0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,_
    0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,_
    0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,_
    0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,_
    0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,_
    0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,_
    0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,_
    0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,_
    0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,_
    0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,_
    0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,_
    0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,_
    0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,_
    0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,_
    0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,_
    0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,_
    0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,_
    0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,_
    0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,_
    0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,_
    0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040

top204


Pepe

I tested in my program with $85, $01, $02, $03, $04 and my calculation also gives $48D1

TimB

#8
As I said I hate CRC's and encryption

I'm at the point where I don't even know the name of the CRC. The website I'm using CRC Web says its CRC 16 Modbus but other sources says its CRC-16/CCITT-FALSE which according to the link above gives a different result

I do not care but I need to make it readable at both ends, and compatible with a pressure sensor I'm using. I checked in the data sheet and it gave an example

0x01,0x03,0x06,0x00,0x01,0x00,0x02,0x00,0x64 and the CRC is 0xBC,0x9E

Putting that into the website gives a CRC of 0xBC, 0x9E and lits it as Modbus CRC16

And putting that into my code produces the same result 0xBC, 0x9E

Les...

Really sorry I missed your post. I thought the code was mine that got screwed on posting :(

As always your code is way better and I'm going to use that as it can pass arrays, something I have not figured out yet. Yes it works!

Many many thanks for the help I feel I'm getting somewhere now and can get on with documenting the protocol I developing and implementing it