Search code examples
arduinomodbuscrc16

How to format a byte array to a 16bit CRC check for a Modbus frame?


I am currently trying to use an Arduino UNO R3 as a master in a Modbus serial (not TCP/IP) communications protocol. I have attempted to use outdated libraries for this with no avail, mostly due to deprecated dependencies. Anyways I decided to start from scratch and my current goal is to write a value to register 301 (starting address 300) to a slave I.D. "5". Before connecting to my slave I want to make sure I can regenerate the CRC for the frame depending on whether I write a 0, 1, or 2 to the register. For writing a 1 I have the frame as "05 06 01 2D 00 01 D8 7B" and from my research I can tell that "D8 7B" is the 16-bit CRC for the data. However the problem is that I can't seem to replicate the CRC output on my Arduino. I believe it has to do with how I have prepared and formatted my byte array but I am not sure. I do not understand the CRC algorithms well which is why I am using a library, any help would be greatly appreciated.

The following code is a slight variation of the example sketch provided with vinmenn's Crc16 library.

#include <Crc16.h>
//Crc 16 library (XModem)
Crc16 crc; 

void setup()
{
    Serial.begin(38400); 
    Serial.println("CRC-16 bit test program");
    Serial.println("=======================");

}

void loop()
{
  /*
    Examples of crc-16 configurations
    Kermit: width=16 poly=0x1021 init=0x0000 refin=true  refout=true  xorout=0x0000 check=0x2189
    Modbus: width=16 poly=0x8005 init=0xffff refin=true  refout=true  xorout=0x0000 check=0x4b37
    XModem: width=16 poly=0x1021 init=0x0000 refin=false refout=false xorout=0x0000 check=0x31c3
    CCITT-False:width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1
    see http://www.lammertbies.nl/comm/info/crc-calculation.html
  */
  //calculate crc incrementally
  byte data[] = {0x05, 0x06, 0x01, 0x2D, 0x00, 0x01};

  Serial.println("Calculating crc incrementally");

  crc.clearCrc();
  for(byte i=0;i<6;i++)
  {
     Serial.print("byte ");
     Serial.print(i);
     Serial.print(" = ");
     Serial.println(data[i]);
     crc.updateCrc(data[i]);
  }
  unsigned short value = crc.getCrc();
  Serial.print("crc = 0x");
  Serial.println(value, HEX);

  Serial.println("Calculating crc in a single call");

  //Modbus
  value = crc.Modbus(data,0,7);
  Serial.print("Modbus crc = 0x");    
  Serial.println(value, HEX);

  while(true);
}

The following is the serial console output when executing the aforementioned code.

CRC-16 bit test program
=======================
Calculating crc incrementally
byte 0 = 5
byte 1 = 6
byte 2 = 1
byte 3 = 45
byte 4 = 0
byte 5 = 1
crc = 0x2C86
Calculating crc in a single call
Modbus crc = 0x5A7B

I am using a program called Modbus Poll to organize my bytes for me into frames. Bellow is a screenshot of the program's output.

Modbus Poll

Again, any help would be appreciated. Thanks in advance!

edit: I didn't mention how the provided materials detail my issue. According the the Modbus poll program the CRC is "D8 7B" however my serial console is reporting "0x5A7B"

edit: I fixed the code to reflect CRC over six bytes (0, 5) and updated the serial console output.

solution: Corrected the issue by adjusting value = crc.Modbus(data,0,7); to value = crc.Modbus(data,0,6);


Solution

  • As for replicating Modbus CRC16 with byte by byte method, according to the source, you need to use the parameters:

    poly=0x8005
    init=0xffff
    refin=true
    refout=true
    xorout=0x0000
    check=0x4b37

    which means you should initialize crc as:

    Crc16 crc(true, true, 0x8005, 0xffff, 0x0000, 0x8000, 0xffff);
    

    This returns consistent CRC's.

    Calculating crc incrementally
    byte 0 = 5
    byte 1 = 6
    byte 2 = 1
    byte 3 = 45
    byte 4 = 0
    byte 5 = 1
    crc = 0x7BD8
    Calculating crc in a single call
    Modbus crc = 0x7BD8


    But you actual problem is calculating CRC over 7 bytes instead of 6 (crc.Modbus(data,0,7)). If the 7th byte is 0, the resulting CRC becomes 0x5A7B.