Home  |  Contents 

Microcontroller and PC projects
  Forum Index : Microcontroller and PC projects         Section
Subject Topic: Modbus CRC. Post ReplyPost New Topic
<< Prev Page of 3 Next >>
Author
Message << Prev Topic | Next Topic >>
Turbo46
Regular Member
Regular Member


Joined: 24 December 2017
Location: Australia
Online Status: Offline
Posts: 69
Posted: 10 April 2018 at 6:05pm | IP Logged Quote Turbo46

Previously I wrote:

Quote:
I believe that if you receive a message, then process the whole message (CRC included) with the CRC generation function, the resultant 'CRC' will be zero. That would be a simpler way of checking the validity of the message. I'm yet to try that though.


Well I have tried it with the examples in Jim's CRC calculation function and with some other real world examples and it does work eg:

test1$ = CHR$(01)+CHR$(03)+CHR$(04)+CHR$(&H3F)+CHR$(&H80)+CHR$(0)+CHR$(0)
check1$= CHR$(&HF7)+CHR$(&HCF)
test1$ = test1$+check1$

PRINT HEX$(CRC(test1$),4)

will return zero (0000)

Bill

Edited by Turbo46 on 10 April 2018 at 6:05pm



Back to Top View Turbo46's Profile Search for other posts by Turbo46
 
Phil23
Guru
Guru


Joined: 27 March 2016
Location: Australia
Online Status: Offline
Posts: 1526
Posted: 15 April 2018 at 9:08am | IP Logged Quote Phil23

Wondering if anyone can see fault in this variation.

It returns a 2 character string that I can add to the string I'm building to sent.

Appears to test Ok, and I assume from above if I pass a response string to it, it should return "String"=Chr$(0)+Chr$(0).

Thanks

Phil.

PS, I am now successfully reading the first Six registers with a single request & converting them successfully.

They all reside in the address range from 30001 to 30032.

Really need a second request for the others as they are up at 30071 to 30080.
If I tried to get them all with one request the response string would be too long.

Quote:
FUNCTION CrcStr(a$) As String
LOCAL ErrorWord% = &HFFFF, n, j, ByteVal, LSB
LOCAL CrcHex As Integer
FOR n = 1 TO LEN(A$)
ByteVal =
ASC(MID$(a$, n, 1))
ErrorWord% = (ErrorWord%
AND &HFFFF) XOR ASC(MID$(a$, n, 1))
FOR j = 1 TO 8
LSB = ErrorWord%
AND &H0001
IF LSB = 1 THEN ErrorWord% = ErrorWord% - 1
ErrorWord% = ErrorWord% /
2
IF LSB = 1 THEN ErrorWord% = ErrorWord% XOR &HA001
NEXT j
NEXT n
CrcHex = ErrorWord%
AND &HFFFF
CrcStr=
Chr$(CrcHex AND &hFF)+Chr$(CrcHex>>8)

END FUNCTION


Back to Top View Phil23's Profile Search for other posts by Phil23
 
goc30
Senior Member
Senior Member
Avatar

Joined: 12 April 2017
Location: France
Online Status: Offline
Posts: 119
Posted: 15 April 2018 at 1:44pm | IP Logged Quote goc30

Hi Phil

In the MODBUS protocol, the frames are limited to 128 bytes, including crc, which means that if a register is 4 bytes long, the slave can only send 30 registers values
Back to Top View goc30's Profile Search for other posts by goc30
 
Turbo46
Regular Member
Regular Member


Joined: 24 December 2017
Location: Australia
Online Status: Offline
Posts: 69
Posted: 15 April 2018 at 3:05pm | IP Logged Quote Turbo46

Hi Phil

I think it does work but you are ending up with a null string.
CHR$(0) + CHR$(0) = CHR$(0) = nothing.

Bill

Edit: From what I have found the maximum Modbus frame size is 256 bytes.

Edited by Turbo46 on 15 April 2018 at 3:23pm
Back to Top View Turbo46's Profile Search for other posts by Turbo46
 
goc30
Senior Member
Senior Member
Avatar

Joined: 12 April 2017
Location: France
Online Status: Offline
Posts: 119
Posted: 16 April 2018 at 12:32am | IP Logged Quote goc30

hi Turbo46

Edit: From what I have found the maximum Modbus frame size is 256 bytes.

in wikipedia for modbus they say:

Because register values are 2-bytes wide and only 127 bytes worth of values can be sent, only 63 holding registers can be preset/written at once

Wikipedia

Edited by goc30 on 16 April 2018 at 12:34am
Back to Top View goc30's Profile Search for other posts by goc30
 
Turbo46
Regular Member
Regular Member


Joined: 24 December 2017
Location: Australia
Online Status: Offline
Posts: 69
Posted: 16 April 2018 at 8:23am | IP Logged Quote Turbo46

Hi goc30

You may well be right! I have only had experience with small RTUs. My reference though is the 'Modicon Modbus Protocol Reference Guide' Here. Check out page 58 where it states:

'The quantity of registers to be read, combined with all other fields in the expected response, must not exceed the allowable length of Modbus messages: 256 bytes.'

Cheers
Bill

Back to Top View Turbo46's Profile Search for other posts by Turbo46
 
goc30
Senior Member
Senior Member
Avatar

Joined: 12 April 2017
Location: France
Online Status: Offline
Posts: 119
Posted: 16 April 2018 at 4:22pm | IP Logged Quote goc30

Hi Turbo46

The MODBUS protocol is open, you can create your own function codes (I created some for clients). The 128-byte limit must have a historical reason (maybe for slow speed reasons)

Modicon use a partial modbus protocol (not 100% compatible, and many functions no-standards) but it is not important. They use also an ascii communication, for this they need more bytes maybe
Back to Top View goc30's Profile Search for other posts by goc30
 
Turbo46
Regular Member
Regular Member


Joined: 24 December 2017
Location: Australia
Online Status: Offline
Posts: 69
Posted: 27 September 2018 at 6:50pm | IP Logged Quote Turbo46

Those of you who have researched the Modbus CRC may be aware of the lookup table method of CRC calculation. It is supposed to be much faster than the above presented method. I have found some examples in Basic and converted the program to MMBasic. The program below generates the CRC lookup table rather than having to enter it manually.


' Modbus CRC Generation using lookup table
' Test program

OPTION EXPLICIT

DIM INTEGER crc_table(255)
DIM INTEGER crc_const = &HA001
DIM INTEGER i, j, k
DIM STRING test1$, test2$, test3$

' Make the CRC table first
CRCTable
' Test messages
test1$ =  CHR$(01)+CHR$(03)+CHR$(04)+CHR$(&H3F)+CHR$(&H80)+CHR$(0)+CHR$(0)
'  check1$= CHR$(&HF7)+CHR$(&HCF)
test2$ = CHR$(01)+CHR$(&H10)+CHR$(0)+CHR$(02)+CHR$(0)+CHR$(02)+CHR$(04)+CHR$(&H42)+CHR$(&H70)+CHR$(0)+CHR$(0)
'  check2$= CHR$(&H67)+CHR$(&HD5)
test3$ =  CHR$(01)+CHR$(03)+CHR$(00)+CHR$(00)+CHR$(00)+CHR$(02)
'  check3$= CHR$(&HC4)+CHR$(&H0B)

PRINT HEX$(CRC16(test1$),4)
PRINT HEX$(CRC16(test2$),4)
PRINT HEX$(CRC16(test3$),4)

END

' Generates the lookup table
SUB CRCTable
FOR i = 0 TO 255
  k = i
'  print k
  FOR j = 1 TO 8
    IF k AND 1 THEN
      k = k >> 1
      k = k XOR crc_const
    ELSE
      k = k >> 1
    END IF
  NEXT j
 crc_table(i)=k
NEXT i
END SUB

' Calculates the Modbus CRC
FUNCTION CRC16(a$)
LOCAL CRC = &HFFFF, n
  FOR n = 1 TO LEN(a$)
  CRC = (crc >> 8) XOR crc_table(crc XOR ASC(MID$(a$, n, 1)) AND &HFF)
NEXT n
CRC16 = (CRC MOD 256)*256 + INT(CRC/256) MOD 256
END FUNCTION


The test program above uses the same example messages as TassyJim's program above and the times to calculate the CRC with the Maximite are shown below - the first set of times are for the program above and are about 10 times faster than the second set which is from the TassyJim program.

RUN
4 msec
F7CF
6 msec
67D5
3 msec
C40B
>
> RUN
37 msec
F7CF
61 msec
67D5
32 msec
C40B
>

This should reduce the possibility of communications timeouts.

Bill
Back to Top View Turbo46's Profile Search for other posts by Turbo46
 
Turbo46
Regular Member
Regular Member


Joined: 24 December 2017
Location: Australia
Online Status: Offline
Posts: 69
Posted: 28 November 2018 at 5:05pm | IP Logged Quote Turbo46

I have updated my Modbus CRC program to make it more user friendly. It now returns a string value (as Phil's version of Jim's code above does) and is formatted ready to append to a message string.


OPTION EXPLICIT

DIM INTEGER crc_table(255) ' lookup table for CRC generation

genCRCtable ' generate the lookup table


'------------------------------------------------------------
'generates a lookup table for fast crc calculation
SUB genCRCtable
LOCAL i%, j%, k%, crc_const% = &HA001
FOR i% = 0 TO 255
  k% = i%
  FOR j% = 1 TO 8
    IF k% AND 1 THEN
      k% = k% >> 1
      k% = k% XOR crc_const%
    ELSE
      k% = k% >> 1
    END IF
  NEXT j%
 crc_table(i%)=k%
NEXT i%
END SUB
'
' returns the CRC as a string
FUNCTION CRC16$(a$)
  LOCAL CRC% = &HFFFF, n%
  FOR n% = 1 TO LEN(a$)
    CRC% = (CRC% >> 8) XOR crc_table(CRC% XOR ASC(MID$(a$, n%, 1)) AND &HFF)
  NEXT n%
  CRC16$ = CHR$(CRC% AND &HFF) + CHR$((CRC%>>8) AND &HFF)
END FUNCTION
'-------------------------------------------------------------

To use it: include the Subroutine and Function in your program (of course) and dimension the lookup table -

DIM INTEGER crc_table(255) ' lookup table for CRC generation

Run the table generation subroutine early in the program, at least before using the CRC16$() Function.

genCRCtable ' generate the lookup table

To use the CRC$16() function, first assemble a transmit message as a string (say TxMsg$). Then append the CRC to the end of the message by:

TxMsg$ = TxMsg$ + CRC16$(TxMsg$)

and transmit it.

To check that a received message (say RxMsg$) has the correct CRC and is otherwise not corrupted, test if by:

IF CRC16$(RxMsg$) = CHR$(0) + CHR$(0) THEN ' CRC is OK

Be aware that this routine requires a table of 256 bytes of 64 bit integers, if space is a problem and speed is not then use Phil's routine above. It is used in exactly the same way - except there is no table to initialise (and take up memory). Just use:

TxMsg$ = TxMsg$ + CrcStr(TxMsg$)

and

IF CrcStr(RxMsg$) = CHR$(0) + CHR$(0) THEN ' CRC is OK

instead.

If you want to see what is in the message as a debugging exercise you could use the subroutine below:

SUB PrintStr(b$)
LOCAL i%
  FOR i% = 1 TO LEN(b$)
    PRINT HEX$(ASC(MID$(b$,i%,1)),2); ", ";
  NEXT i%
END SUB

For example:

PrintStr CRC16$(TxMsg$)

Please consider the use of a CRC for any communication programs. I hope the above explanation will encourage some of you to do so.

Bill

PS If someone could tell me how to get colour in the code I would be grateful. I printed to RTF in MMEdit but the colour did not come across when I copied and pasted.

Back to Top View Turbo46's Profile Search for other posts by Turbo46
 
disco4now
Guru
Guru


Joined: 18 December 2014
Location: Australia
Online Status: Offline
Posts: 306
Posted: 28 November 2018 at 9:09pm | IP Logged Quote disco4now

File/Format for TBS Forum gets it into the clipboard.
Then paste into the forum.



Back to Top View disco4now's Profile Search for other posts by disco4now
 
Phil23
Guru
Guru


Joined: 27 March 2016
Location: Australia
Online Status: Offline
Posts: 1526
Posted: 29 November 2018 at 5:53am | IP Logged Quote Phil23

Missed this continuation.

No issues at present, but more efficient is better on all scores.
Can't see any reason not to use the better code.

Incidentally, the 3 meters I'm reading have performed faultlessly for the past 8 of 9 months, 6378kWh used from the grid, 2715kWh of Solar production.

Haven't got back to the updated interface of SD logging yet.
It's that WIP that gets pushed to the bottom of the list.


Cheers.

Edited by Phil23 on 29 November 2018 at 5:54am
Back to Top View Phil23's Profile Search for other posts by Phil23
 
Turbo46
Regular Member
Regular Member


Joined: 24 December 2017
Location: Australia
Online Status: Offline
Posts: 69
Posted: 01 December 2018 at 2:44pm | IP Logged Quote Turbo46

@ disco4now, thanks for that, it didn't work for me but I now realise that you have to use QUOTE instead of CODE.

@ Phil23, I did spot the deliberate mistake in your (and Jim's original) CrcStr(a$) function. ByteVal is assigned a value in line 5 but not used, the byte value itself is calculated again and used in line 6 where ByteVal was probably meant to be used. ByteVal is redundant.

Bill
Back to Top View Turbo46's Profile Search for other posts by Turbo46
 


<< Prev Page of 3 Next >>
In the news...
 
Post ReplyPost New Topic
Printable version Printable version
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot delete your posts in this forum
You cannot edit your posts in this forum
You cannot create polls in this forum
You cannot vote in polls in this forum

Powered by Web Wiz Forums version 7.8
Copyright ©2001-2004 Web Wiz Guide

This page was generated in 0.1211 seconds.
Privacy Policy     Process times : 0.01, 0, 0, 0.11