I am having a lot of trouble on generating a modbus CRC16 code using PHP. I have found a lot of different codes over the internet but i have tried them and for some reason i didnt get right results. I have found a PHP code for generating CRC16-CCITT. I have chenge the look up table to the modbus CRC corresponding table but the result is still not the right one. The code is bellow. What do i need to do more in order to transform a CRC16-CCITT code into CRC16-MODBUS code.
<?php
/*************************************************************************
* phpCrc16 v1.1 -- CRC16/CCITT implementation
*
* By Matteo Beccati <[email protected]>
*
* Original code by:
* Ashley Roll
* Digital Nemesis Pty Ltd
* www.digitalnemesis.com
* [email protected]
*
* Test Vector: "123456789" (character string, no quotes)
* Generated CRC: 0x29B1
*
*************************************************************************/
/*
* Returns CRC16 of a string as int value
*/
function CRC16($str)
{
static $CRC16_Lookup = array(
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
);
$crc16 = 0xFFFF; // the CRC
$len = strlen($str);
for($i = 0; $i < $len; $i++ )
{
$t = ($crc16 >> 8) ^ ord($str[$i]); // High byte Xor Message Byte to get index
$crc16 = (($crc16 << 8) & 0xffff) ^ $CRC16_Lookup[$t]; // Update the CRC from table
}
// crc16 now contains the CRC value
return $crc16;
}
/*
* Returns CRC16 of a string as hexadecimal string
*/
function CRC16HexDigest($str)
{
return sprintf('%04X', crc16($str));
}
echo CRC16HexDigest('123456789');
?>
The Modbus Polynomial is 0xA001, but to calculate the Modbus CRC in the 'K40 you have to use the bit-reversed value 0x8005. The final contents of the 16-bit CRC Accumulator must be bit-reversed to get the actual Modbus CRC.
The CRC- 16 bits code computes a 16-bit cyclical redundancy check (CRC) algorithm on an input serial data stream. The polynomial can be defined to implement CRC functions, such as the CRC-16 or CCITT algorithm. A seed value can be specified to initialize the starting data value.
Step-01: Calculation Of CRC At Sender Side-A string of n 0's is appended to the data unit to be transmitted. Here, n is one less than the number of bits in CRC generator. Binary division is performed of the resultant string with the CRC generator. After division, the remainder so obtained is called as CRC.
The CRC generation code uses the generator polynomial 0x1021 which was chosen as one it is one version of CRC 16-CCITT [1]. The initial value used for the CRC buffer was all zeros. The algorithm then runs through the message byte by byte.
This Pascal snippet is MODBUS RTU (ASCII is different) protocol online calculation without any table:
function mb_CalcCRC16(ptr: pointer to byte; ByteCount: byte): word;
var
crc: word;
b, i, n: byte;
begin
crc := $FFFF;
for i := 0 to ByteCount do
if i = 0 then // device id is 1st byte in message, and it is not in the buffer
b := mb_GetMessageID; // so we have to calculate it and put it as 1st crc byte
else
b := ptr^;
Inc(ptr);
endif;
crc := crc xor word(b);
for n := 1 to 8 do
if (crc and 1) = 1 then
crc := (crc shr 1) xor $A001;
else
crc := crc shr 1;
endif;
endfor;
endfor;
Return(crc);
end;
function mb_CalcCRC: word; // Calculate CRC for message in mb_pdu
begin // this message can be one that is just received, or in a reply we have just composed
Return(mb_CalcCRC16(@mb_pdu[1], mb_GetEndOfData));
end;
That's a quote from a working embedded AVR device with implemented MODBUS RTU slave protocol.
This is faster than lookup table
function crc16($data)
{
$crc = 0xFFFF;
for ($i = 0; $i < strlen($data); $i++)
{
$crc ^=ord($data[$i]);
for ($j = 8; $j !=0; $j--)
{
if (($crc & 0x0001) !=0)
{
$crc >>= 1;
$crc ^= 0xA001;
}
else
$crc >>= 1;
}
}
return $crc;
}
years after, your answers are very usefull for me. I combine your answers in a non lookup table solution:
function crc16_modbus($msg)
{
$data = pack('H*',$msg);
$crc = 0xFFFF;
for ($i = 0; $i < strlen($data); $i++)
{
$crc ^=ord($data[$i]);
for ($j = 8; $j !=0; $j--)
{
if (($crc & 0x0001) !=0)
{
$crc >>= 1;
$crc ^= 0xA001;
}
else $crc >>= 1;
}
}
return sprintf('%04X', $crc);
}
The results I am waiting for are consistent with online tools like:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With