Byte order of Modbus CRC

  • Thread starter Steve Ciricillo
  • Start date
S

Thread Starter

Steve Ciricillo

I've recently developed a Modbus slave driver for my embedded application. It needs to support both Modbus ASCII and RTU modes. I've carefully studied the Modicon documentation (ref: PI-MBUS-300 Rev. J) available for download and am concerned about its accuracy.

When it comes to CRC transmission, the document clearly states that the CRC needs to be sent low order byte first. My code is virtually a straight shameless rip off of the code example in the manual. Yet when I test my product with 3rd party Modbus masters I found that I had to reverse the order of my transmitted CRC in order to get successful connections to the master.

My first thought was that the 3rd party product I was using had some bugs. But when I got the same results from 3 different products I concluded that those developers knew something that I did not.

Has anyone else run into this? Can anyone confirm (or dispute) my conclusion that the byte order must be HI/LO and not LO/HI as the manual states? Do I need to by an official Modbus capable product/ PLC to test my protocol implementation rather than using downloaded shareware?

Any thoughts/ comments/ help is greatly appreciated. Thanks in advance.

Regards,
Steve C.
 
Addendum to above posting...

I've read through some other postings here regarding this issue. The replies generally focused on byte order issues with respect to platforms on which the code is run AND on the notion that the Modicon example code is already doing a byte order swap, etc.

My point:
When it says to send the low byte first, that means to me from the perspective of the UART, meaning that the low byte is sent out of the UART first, followed by the high byte. I don't need to care how 16 bit variables are stored on any particular platform (little endian, big endian). When it comes to transmitting at the physical layer the data comes out a byte at a time. Period. So low byte first should mean low byte first. It's up to you, the programmer, to figure out which 8 bit memory location of your 16 bit variable, corresponds to low and high respectively.

However, in the Modicon example code, the CRC is calculated and split between two separate 8-bit variables clearly labeled Hi and Lo. Only at the end of the routine do those two 8-bit pieces get merged into a 16-bit variable and returned to the caller. So one doesn't have to even deal with platform specific byte ordering issues. Just keep the CRC as two separate 8-bit bytes and send them one at a time in the proper order, right? Well guess which of those two variables I think should be sent first? The one labeled Lo. Sadly, this causes CRC errors.

So that's the issue and I think the other poster (back in October 2002) was finding the same thing.

Any takers?
Regards,
Steve C.
 
You have discovered the "Big-endian" vs. the "Little-endian" problem that has been with computing from the beginning. Different computer
architectures use memory in these different ways to store data. So when they decide how to take the parallel 32-bit computer word composed of four 8-bit bytes and place it on a serial communications line, there can be differences. Do you start with the least significant byte first
(Little-endian), or the most significant byte first (Big-endian). Motorola and Intel microprocessors are generally incompatible (cannot share the same memory) because of the byte-order problem. In most cases, it is impossible to predict how a given system will handle this problem, and you have discovered the most typical solution -- trial and error.

Dick Caro
============================================
Richard H. Caro, CEO
CMC Associates
2 Beth Circle, Acton, MA 01720
Tel: +1.978.635.9449 Mobile: +1.978.764.4728
Fax: +1.978.246.1270
E-mail: [email protected]
Web: http://www.CMC.us
============================================
 
D

Diego Urda Carrasco

Steve, You are right.

My bible for thats questions is the book is C Programmmer's Guide To serial Communications (ISBN 84-7614-206-4), by Joe Campbell.

Well, in chapters 3 and 19 on explains the foundations of CRC and it's C prog.

CRC is a method to check errors in blocks of information (files, serial transm, etc), based in polynomial division. Two of this polynoms are the CRC-16: x^16+x^15+z^2+1, (8005 hex.) and his reverse CRC-16rev, (A001 hex).

The generator polynom for Modbus is CRC16-rev, with the following particularity : The first value to load in the calc. register is FFFF hex.

Ending: Making a CRC-16rev algebraic handy and others methods calc of a byte secuence to Read Exception Status Modbus function: (buff[0] = 11hex, buff[1]), on result: Low-byte= 4C hex, and Hi-byte= 22 hex. Accordingly: buff[2]= 4C hex, buff[3] = 22 hex.

If some one test to calc with the CRC Generation Funtion and Byte Tables from Modbus Modicon Protocol Guide, will obtains: Low-byte= 22 hex, and Hi-byte= 4C hex. In this case the bytes are swapped and must be placed fist, 4C hex and then, 22hex, (With this calc mode not apply fist lo-byte. See Note in page 113 on mentionated guide), resulting also: buff[2]= 4C hex, buff[3] = 22 hex.

Regards,
Diego Urda
 
R

Rob Hulsebos

The example in the Modbus spec assumes that the CRC itself is calculated swapped. So when the lo byte of the CRC calculation result is transmitted, it is actually the hi byte of the CRC. Confusing, but true!

Rob Hulsebos
 
S

Steve Ciricillo

First of all - thank you all for your comments and interest. But my real question remains unanswered. Let me explain.

Diego:
You thoughtfully explained the CRC algorithm and then refered me to the Note on page 113 of PI-MBUS-300 document. Alex, you essentially stated the same thing. I refer you now to that very comment:
"Note: This function performs the swapping of the high/low CRC bytes internally. The bytes are already swapped in the CRC value that is returned from the function. Therefore the CRC value returned from the function can be directly placed in the message for transmission."

My assertion is that no matter what programming language or what microprocessor platform you use, at the lowest level a 16 bit write operation involves writing two distinct 8-bit bytes at two consecutive memory locations. So when the Modicon note quoted above states that the returned value can be "directly placed in the message..." this statement is only true for a specific platform byte ordering scheme. To "directly place" the 16-bit CRC value implies a 16-bit write operation (coded however you like - assembly, C, ADA, Fortran - no matter). The end result of that write operation will be a platform/ language specific byte ordering. Therefore this comment assumes a particular platform and is only sometimes correct.

The only thing that is always unambiguous in my mind is to say that the Lo byte gets transmitted first and the Hi byte transmitted last. There's no way to misconstrue this statement. So take the Modicon code (which I say again, calculates 2 individual bytes and only at the end combines into a word) and send the Lo byte from that code first, then the Hi byte. It's pretty clear from the code which is the Lo byte, since it is conveniently named uchCRCLo. To confirm this further, it's also clear from the code at the end that uchCRCHi is shifted into the upper (Hi) byte of a 2-byte word variable. So my natural conclusion is that Lo means Lo and Hi means Hi. However, in this case it doesn't work.

Bill:
You gave me some information on what byte ordering and endianess is all about.
Dick:
You stated that I'd discovered the Big endian vs. Little endian issues.

Thank you both but I've been aware of endianess issues for years. That is not the question here. The question again is "Is the Modicon documentation correct or incorrect when it states that the Lo order byte gets transmitted first?"

Please note - the documentation does NOT say that the byte located earlier in memory of a multibyte variable gets transmitted first. Lo order means Lo order, regardless of the way in which a particular data type is stored in memory on a particular platform. Regardless.

Byte ordering in consecutive memory of a data type is an architecture and programming issue. Calculation of the CRC and the byte order that it gets transmitted is a protocol issue. The protocol can be sucessfully implemented on any machine, provided the programmer remains aware of the native byte ordering convention of the target platform.

So my conclusion is that the order in which the CRC bytes need to be transmitted are uchCRCHi followed next by uchCRCLo, contrary to what the Modicon documentation would have you believe. If you've actually implemented the Modbus protocol in your application code then I'd be interested in hearing your thoughts on the matter - and how you performed the logic (what language/ platform/ etc.).

Regards and thanks again to all who took the trouble to reply.

Steve C.
 
D
Steve, sorry if my English at Spanish way confuses, but I believe that I was clear. If I say that others CRC16-rev algorithms (hand and computational mode) give right hi-byte and lo-byte order CRC16 values, this mean that I think the CRC Generation Function algorithm in the Modicon Modbus Protocol Reference Guide is wrong. Ok?

Even more, really the arrays have there items swapped. Sure.

Try Following:

First: Swap contents betwen auchCRCLo and auchCRCHi arrays.

Second: Modify code algorith to:

unsigned short CRCC6(...., .....)
.....
.....
{
.......
.......
.......
while (usDataLen--)
{
uIndex = uchCRCLo ^*puchMsg++ ;
uchCRCLo = uchCRCHi ^ auchCRCLo [uIndex] ;
uchCCRCHi = auchCRCHi [uIndex] ;
}
return (uchCRCHi << 8 | uchCRCLo);
}

>Finally: Now you will obtain true CRC16 value, with its Lo-byte and Hi-byte in right order.
Regards, Diego Urda.
 
S

Steve Ciricillo

Thank you again Diego, Rob and all,

I hope this thread can help others bothered by this same predicament. My closing comment is that it's unfortunate that Modicon chose naming conventions for the CRC variables that are misleading and that the code only works when platform specific behavior is assumed. By best regards and again, heartfelt thanks to all who responded. May the force be with you.

Regards,
Steve C.
 
C
What should be done in these cases, and what is done for example, in the Internet protocols, NFS, etc. is to simply and unequivocably state what should go out on the wire and leave the implementation to the programmer. Of course, no one said that the sample code was right for all platforms.

Regards

cww
 
J

John Charlton

Thank you for bringing this up. I agree with your original point that the Modicon document is incorrect. From this discussion and my own testing with the CRC code, I agree that the order in the packet is high byte followed by low byte. The CRC function should return a CRC unsigned short in normal byte order for the processor being used. The little endian/big endian conversion should be done by the code creating the packet.

--John
 
CRC16-rev Cyclical Redundancy Check used in Modobus Protocol isn't invented from Modicom, is used for others purposes and has its own rules. One of theses is the low-byte first, then hi-byte transmision order. This don't must be changed. Therefore, the core question is a mistaken algorithm.

Regards, Diego Urda.
 
L

Lynn at Alist

Speaking in an official capacity, I should point out that the PI-MBUS_300.PDF ("original" from 1996) is obsolete. The "Modbus Serial Line Implementation Guide v1.0 November 2002" is the current document you should be using. PI-MBUS_300.PDF is only on www.modbus.org because so many people kept asking for it to look up legacy issues.

Diego Urda makes an excellent point - Modicon didn't invent the CRC16. I discovered and started using some optimized x86 XMODEM code for the Modbus CRC16 long ago (people wanting help with 8051 should look for XMODEM example code also!). I should point out that Rockwell's DF1 also uses the same algorithm - it just starts with a CRC of 0x0000, not 0xFFFF. So don't use the bit-shifting method in the DF1 docs, use a Modbus or XMODEM style table-lookup calc which cuts maybe 95% of the CPU cycles out.

Mr Charlton & others are suffering from lack of experience in BigEndian architectures. Considering that tens of thousands of programmers have been able to implement Modbus/RTU in thousands of interoperable products over several decades, it not likely that now in 2003 we'd all wake up to discover the protocol or documents are "incorrect".

Best Regards

Lynn August Linse, [email protected] IA Firmware Specialist, Digi Int'l (www.digi.com) Board Member, The Modbus Organization (www.modbus.org)

> ------------ Forwarded Message ------------
> From: Diego Urda
> Subject: Re: COMM: Byte order of Modbus CRC
>
> CRC16-rev Cyclical Redundancy Check used in Modobus Protocol isn't
> invented from Modicom, is used for others purposes and has its own rules.
> One of theses is the low-byte first, then hi-byte transmision order. This
don't
> must be changed. Therefore, the core question is a mistaken algorithm.
>
>> John Charlton wrote:
>> Thank you for bringing this up. I agree with your original point that
the
>> Modicon document is incorrect. From this discussion and my own testing
>> with the CRC code, I agree that the order in the packet is high byte
>> followed by low byte. ...
 
S

Steve Ciricillo

> Speaking in an official capacity, I should point out that the PI-MBUS_300.PDF ("original" from 1996) is obsolete. The "Modbus Serial Line Implementation Guide v1.0 November 2002" is the current document you should be using.

I have both documents and they both document the CRC algorithm identically and both make the same misleading statements about byte ordering.

> Diego Urda makes an excellent point - Modicon didn't invent the CRC16.

Irrelevent. Byte ordering in the data stream is a protocol issue, not a CRC algorithm issue. If I tell you to place the low order byte of a variable in a particular position in a byte buffer, will you then ask me which algorithm I'm using? If I give you a hexadecimal number 1234H (or 0x1234 if you prefer) and then ask which is the low order byte, will you say that it depends on the endianness of the target platform? And by the way, there are several CRC16 algorithms in existence, varying in the polynomial used and the starting value (0000 or FFFF). But I'm sure you already knew that.


> Mr Charlton & others are suffering from lack of experience in BigEndian architectures. Considering that tens of thousands of programmers have been able to implement Modbus/RTU in thousands of interoperable products over several decades, it not likely that now in 2003 we'd all wake up to discover the protocol or documents are "incorrect".

I, at least, am not suffering from a lack of experience in BigEndian architectures. I've been programming on multiple architectures with varying endianness for over 20 years. The fact that thousands of programmers have created thousands of functional products is a testament to their talent, but not necessarily to the correctness of the documentation. My product works too, but only if I depart from the documented information, as all other successful Modbus developers have had to do. Earlier comments in this thread from others testify to that fact.

But enough already. Thank you all again for your kind replies.

Regards,
Steve
 
Interesting theory about endian being irrelevent. I took at a look at our Motorola Coldfire (Bigendian) Modbus code just to be sure:

1) we calc the CRC16
2) we byte-swap so the LoByte becomes the HiByte (meaning "first" in a Bigendian byte order)
3) we send this outmapping the int to a byte buffer, meaning Lo goes first, then Hi.

This seems to be consistent with the Spec. Does anyone else have BigEndian code they can check?

Many Thanks
- Lynn

> Irrelevent. Byte ordering in the data stream is a protocol issue, not a CRC algorithm issue. If I tell you to place the low order byte of a variable in a particular position in a byte buffer, will you then ask me which algorithm I'm using? If I give you a hexadecimal number 1234H (or 0x1234 if you prefer) and then ask which is the low order byte, will you say that it depends on the endianness of the target platform? And by the way, there are several CRC16 algorithms in existence, varying in the polynomial used and the starting value (0000 or FFFF). But I'm sure you already knew that.<
 
S

Steve Ciricillo

> Interesting theory about endian being irrelevent.

Sorry for the confusion. I didn't say that endian is irrelevent. I said that who invented CRC16 is irrelevant. Endian is in fact the very issue here, and the wording used by the authors of both previously referenced documents is, in my opinion, easily misunderstood.

Regards,
Steve C.
 
L
Hmmmmm... the data encoding on the wire of all other 16 bit data register types in Modbus is *specified* as high byte low byte. I have to agree that the endianness of the host micro is completely irrelevant with respect to protocol data encoding rules. I recall my surprise the first time I encoded a Modbus CRC when finding it came out byte reversed when sent through my (byte orientated) output stream routine. So I byte swapped the word and added the comment "looks wrong but isn't" (which has persisted in our source code for so long now that variants such as "looks wrong but is" have crept in here and there when doing other protocols).

Here's a wild guess: an engineer in the mists of time at Modicon became confused with endianness v. data encoding - this error became irreversible
on the release of the first Modbus device. I doubt anyone will ever get Modicon to admit that this was anything other than inspired design! Now, if we could all agree on a format for sending 32 bit IEEE floats, we would be making progress...

Cheers

Tim
 
Long time ago, when transmision devices are pure hardware, the CRC methods are based in XOR and Shift Registers gates.

CRC is applied to a sequence of bytes (8 bits) wich make up a message.

In the CRC reverse hardware model, the lsb (2^0) is introduced the first, and the msb (2^7) is the last. This type is Modbus CRC. The result is a 16 bits (two bytes) binary value. Not little endian or big endian issues was in mind.

In these times to check if CRC received was coincidental with the calculated, on calculated de CRC to de received message enlarged with the two bytes of the received CRC as a complete message. If the CRC result was zero, all right. Otherwise, error. Is important to appoint that this procedure needs that next to last byte in the calc tallies from the 2^0 to 2^7 bit and last byte from the 2^7 to the 2^15 bit. Why? Because this is that makes a zero result.

Today, software algorythms emulate old hardware procedures. CRC is the same. Bit order is the same. See the example in MODBUS over serial line specification and implemetation guide, page 41. Bit at the left end is 2^15, and bit at the right end is 2^0. Therefore, the byte to the right hand is Least Significant (lo-byte), and the byte to the left hand is the Most Significant (hi-byte), no matter Big endian or Little endian issues (this is binary algebra).

Now, if you print the uchCRCLo variable before return from the proposed CRC16 function, you will see that printed value is the MSB in the example and uchCRCHi variable printed is the LSB. On Intel, on Motorola, or in any other CPU. For this reason I say that C function is wrong and the tables are swapped.

Ending. The composed value returned for CRC16 function is affected by the Big or Little endian issue. If you don't want problems, modify the function and return two separated values. But have in mind the proposed modication to the C algorythm and swapp the tables, as indicated for my in previous reply.

Regards. D. Urda
 
Actually Endian-types are NOT a problem at all as long as the user understands his processor.
If you read the MEMORY directly, you may see this problem depending on the type of processor.

If you do not understand direct memory access for your processor, you may want to try this:
Read/write the memory using a register. You should never see this problem (when looking at the value contained in the register). The way the processor stores and retrieves data in memory is irrelevant since you are using the registers instead of directly accessing memory.

Here is a suggestion:
When reading a modbus packet, be sure to store each integer value, in its order, into a register (Hi|Lo) prior to writing to memory. Do the opposite for generating a packet.
 
Top