Shop OBEX P1 Docs P2 Docs Learn Events
Modbus/CRC 16 revisited — Parallax Forums

Modbus/CRC 16 revisited

Philip GamblinPhilip Gamblin Posts: 202
edited 2007-07-21 03:54 in BASIC Stamp
Quite a while back I queried the forum regarding CRC 16 generation. As usual Parallax responded Jon had a working example. Months later, comparing Jon's code to mine and referring back to the Modbus description of teh CRC generation revealed my problem. I missed a missed a shift byte right which was clearly was called out in the standard I simply missed it. Thanks again·Jon for showing me the error of my ways. Fast forward to now.

I purchased a Modbus simulator ( cheap at $19 ) and began testing. For some reason this simulator will not allow 2400 baud. I just tested 1200 baud and there was no reply to the message. I am communicating at 4800 baud.

Here is where I am really puzzed now. My program is successfully capturing the 5 byte Master message and generating the CRC. My CRC generated from the recieved Master message matches the CRC sent by the Master. The routine works. I then went on to the second phase.

I created a·reply·to the Master interrogation, ran the bytes through the same routine ( I copied and pasted·a second instance of it·) and sent it back to the master. The CRC doesn't match.· I believe I'm resetting everything andI know·using the identical code that works when recieving data.

·The Modbus port uses pins 5 and 6 with a Max232 driver which allows me keep the debug port intact for programming changes.
Thanks to anyone who looks.


' {$STAMP BS2}
' {$PBASIC 2.5}
'
'SERIN Rpin {\Fpin}, Baudmode, {Plabel,} {Timeout, Tlabel,} [noparse][[/noparse]InputData]
'SEROUT Tpin {\Fpin}, Baudmode, {Pace,} {Timeout, Tlabel,} [noparse][[/noparse]OutputData]

start:
······· q··············· VAR··········· Bit
······· cntr············ VAR··········· Nib
······· x··············· VAR··········· Nib
······· IO·············· VAR··········· Byte
······· IO1············· VAR··········· Byte
······· IO2············· VAR··········· Byte
······· addr············ VAR··········· Byte
······· funct··········· VAR··········· Byte
······· bytecnt········· VAR··········· Byte
······· RecDat·········· VAR··········· Byte(8)
······· Tx_dat·········· VAR··········· Word
······· CRC_Reg········· VAR··········· Word
······· crclo··········· VAR··········· Byte
······· crchi··········· VAR··········· Byte
·'**************** Initialize VARs *************************************
······ bytecnt=$03
······ funct=$02
······ IO = $AA
······ IO1 =$FF
······ IO2 =$AA
Chirp:
······· FREQOUT 0,500,3500

DatIn:
······· SERIN 5,188,[noparse][[/noparse]RecDat(0),RecDat(1),RecDat(2),RecDat(3),· 'Receive data to array
··················· RecDat(4),RecDat(5),RecDat(6),RecDat(7)]
······· CRC_Reg=$FFFF······················································ 'Reset CRC_Reg
······· GOSUB CRC_Gen
······· GOSUB TxDat
······· SEROUT 6,188,[noparse][[/noparse]RecDat(0),RecDat(1),bytecnt,RecDat(3),RecDat(4),RecDat(5),Crc_Reg.LOWBYTE,Crc_Reg.HIGHBYTE]
······· GOTO DatIn

CRC_Gen:
······ FOR x= 0 TO 5································· 'this counter is for the 6 bytes in the Master Message
········ CRC_Reg.LOWBYTE =(CRC_Reg.LOWBYTE ^ RecDat(x)) '

·········· FOR cntr = 0 TO 7
·············· IF· (Crc_Reg.BIT0 = 0) THEN
·················· Crc_Reg = Crc_Reg >> 1
·············· ELSE
·················· Crc_Reg=Crc_Reg >> 1
·················· Crc_Reg = Crc_Reg ^ $A001
·············· ENDIF
·········· NEXT
······ NEXT
······· DEBUG "RX CRC low","·· ",HEX2 Crc_Reg.LOWBYTE,"· RX CRC HIGH","·· ",HEX2 Crc_Reg.HIGHBYTE,CR
······ RETURN

TxDat:
······· CRC_Reg=$FFFF································ 'Initialize CRC register
······· RecDat(3)=$AA································ 'Three
······· RecDat(4)=$FF································ ' bytes
······· RecDat(5)=$AA································ ' of data
······ FOR x= 0 TO 5································· 'this counter is for the 6 bytes in the Reply Message
········ CRC_Reg.LOWBYTE =(CRC_Reg.LOWBYTE ^ RecDat(x)) '

·········· FOR cntr = 0 TO 7
·············· IF· (Crc_Reg.BIT0 = 0) THEN
·················· Crc_Reg = Crc_Reg >> 1
·············· ELSE
·················· Crc_Reg=Crc_Reg >> 1
·················· Crc_Reg = Crc_Reg ^ $A001
·············· ENDIF
·········· NEXT
······ NEXT
······ DEBUG "TX CRC low","·· ",HEX2 Crc_Reg.LOWBYTE," TX CRC HIGH","·· ",HEX2 Crc_Reg.HIGHBYTE,CR,CR,CR
······ RETURN

Comments

  • stamptrolstamptrol Posts: 1,731
    edited 2007-07-19 12:11
    Hi Philip,

    I'm interested in your MODBUS work as I have some on-going applications with Stamps talking to MODBUS plc's.

    To date, we've used the technique of generating the MODBUS query string with a MODBUS simulator and then hard-coding that into the Stamp's SEROUT command. We just arbitrarily read a block of plc register area and move the actual plc data into that register area with the ladder-logic. It works very reliably, but is certainly lacking some of the elegance of on-the-fly CRC generation. In our case, the Stamp is the master, so that simplifies things a bit.

    I'll have a look at your code and report any findings.

    Cheers,

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tom Sisk

    http://www.siskconsult.com
    ·
  • Philip GamblinPhilip Gamblin Posts: 202
    edited 2007-07-19 14:40
    I think we working along the same lines. I do pipeline telemetry stuff. Oddly enough, it works with function code 5 ( read and write coils) but nothing else. I keep wanting to think it's something esoteric. The Slave's CRC generated from the Master message works every time with regardless of the function code or data. But the CRC generated by the Slave and appended to the Slave's reply to the Master only works for function 5.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2007-07-19 16:34
    I'm completely ignorant of Modbus protocol. Okay, the program reads in 8 bytes, and then computes a CRC on the first 6 of those. Are the last two the CRC that was received that is supposed to match the one computed? And from clues elsewhere in the program, it appears that bytes index 3, 4, and 5 are the actual data. And byte index 2 is a count of the number of data bytes, three of them. So what are bytes index 0 and 1? Some kind of identifier?

    The CRC computed for tx uses the new data you assigned explicitly, however, it uses the original received values for index 0, 1 and 2. Then when it transmits, it transmits variable "bytecnt" instead of "recDat(2)", but used "recDat(2) to calculate the CRC. It shouldn't make any difference if there are 3 data bytes in either case. But as I said, I'm ignorant of Modbus protocol. I'd like to learn more, so maybe if you explain it something might click.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • Philip GamblinPhilip Gamblin Posts: 202
    edited 2007-07-20 03:22
    Tracy your right. Here is the message flow.
    Five byte message from the master + two bytes of CRC
    The Slave then generates a CRC from the first five rec'd bytes. If they match the message was rec'd as it was intended. If they don't match, then error.




    Here is a link with something of a tutorial/explantion.
    ·http://www.lammertbies.nl/comm/info/modbus.html#mess

    Here is a quick summation (and only moderately accurate ) description of the Modbus protocol
    Master message consists of 5 bytes

    address, function code,starting addr, ending addr, CRC.High.Byte, CRC.Low.Byte

    Slave reply echoes the addr and function code

    address, function code, number of returning data bytes, Data1, Data2..., CRC.Low.Byte, CRC.High.Byte

    Yes the byte order really is reversed in the Slave reply.
    I have been holding onto this as the gospel of CRC 16 generation. I added the text in red because that is what my program does. Euurreeka!·I just found the source of much wailing and gnashig of teeth from my first battle with this program, the algorithm from which I was working didn't include the red text in step4 which is apparently required.

    To further muddy the waters, there is the issue of ASCII modbus vs. RTU Modbus.· I remember from my last·iteration that some version uses a : for a "synch bit" (??).

    THe crux of the matter is, as·I see it, if the correct CRC is EVER generated, then·I have to assume the CRC generating code is correct (don't I ?). The odds are slim at best,·that·I could bugger the code just so it works sometimes.·The other half of the qustion is if it works at all, why not always?

    The good news is that I have no deadline and am in no hurry to make this work. I do however, plan on completing this and posting to the completed projects one day. Thanks again guys for your contributions, I could·never finish this alone!

    Generating a CRC
    Step 1 Load a 16-bit register with FFFF hex (all 1's). Call this the CRC register.
    Step 2 Exclusive OR the first eight-bit byte of the message with the low order byte of
    ······ the 16-bit CRC register, putting the result in the CRC register.
    Step 3 Shift the CRC register one bit to the right (toward the LSB), zerofilling the MSB.
    ······ Extract and examine the LSB.
    Step 4 If the LSB is 0, repeat Step 3 (another shift).
    ······ If the LSB is 1, Shift Right· and Exclusive OR the CRC register with the polynomial value
    ······ A001 hex (1010 0000 0000 0001).
    Step 5 Repeat Steps 3 and 4 until eight shifts have been performed. When this is
    ······ done, a complete eight-bit byte will have been processed.
    Step 6 Repeat Steps 2 ... 5 for the next eight-bit byte of the message.
    ······ Continue doing this until all bytes have been processed.
    Result The final contents of the CRC register is the CRC value.
    Step 7 When the CRC is placed into the message, its
    ······ upper and lower bytes must be swapped as described below.
    Placing the CRC into the Message
    When the 16-bit CRC (two eight-bit bytes) is transmitted in the message,
    ······ the low order byte will be transmitted first, followed by the high order byte-e.g.,
    ······ if the CRC value is 1241 hex (0001 0010 0100 0001):


    Post Edited (Philip Gamblin) : 7/20/2007 3:31:09 AM GMT
  • Philip GamblinPhilip Gamblin Posts: 202
    edited 2007-07-20 17:38
    It looks like Modbus has undergone a "ownership" change here is a link to the standard

    http://www.modbus-ida.org/specs.php

    You have to click an accept button to download. It's a nicely bookmarked .pdf.
  • Philip GamblinPhilip Gamblin Posts: 202
    edited 2007-07-21 03:54
    Thanks Tracy and Tom. Tracy you nailed it. I must have looked at line 100 times..... Anyhow I have a very minimal application. One digital input and 2 analog values. Now since I know the interrogation will be one or the other, all that's left is a little. Once finished it should be able to properly respond to a read 2 registers or on byte of digital data. I hope to post a completed project soon. Thanks again all for the help
Sign In or Register to comment.