Home  |  Contents 

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


Joined: 27 March 2016
Location: Australia
Online Status: Online
Posts: 1526
Posted: 30 March 2018 at 10:26am | IP Logged Quote Phil23

Hi All,

Now that I've got a function to convert IEEE754 to decimal, the next thing I need is a Modbus CRC.

I've found a VB code, but not sure how the "Bit" variable needs to be treated as it's Boolean. Also the usage of "\", integer division. Is that an alternate way of shifting bits?


Function CRC_16(OutputString As String) As String
Dim Generator, CRC As Long
Dim i As Integer, j As Integer, Length As Integer
Dim Bit As Boolean
'Dim Temp As Integer
Dim Temp As Byte
Length = Len(OutputString)
CRC = 65535
Generator = 40961

For i = 1 To Length
    Temp = Asc(Mid(OutputString, i, 1))
    CRC = CRC Xor Temp
    For j = 1 To 8
        Bit = CRC And 1
        CRC = CRC \ 2
        If Bit = True Then
           CRC = CRC Xor Generator
        End If
     Next j
Next i
CRC_16 = Chr(CRC Mod 256) & Chr(CRC \ 256)
'Buffor = Buffor & Chr(CRC Mod 256)
'Buffor = Buffor & Chr(CRC \ 256)
'Text1.Text = Str(CRC \ 256)
'Text2.Text = Str(CRC Mod 256)
End Function


Also have this example from Eastron's Modbus Manual,


BEGIN
   Error Word = Hex (FFFF)
      FOR Each byte in message
         Error Word = Error Word XOR byte in message
            FOR Each bit in byte
               LSB = Error Word AND Hex (0001)
               IF LSB = 1 THEN Error Word = Error Word  1
               Error Word = Error Word / 2
               IF LSB = 1 THEN Error Word = Error Word XOR Hex (A001)
            NEXT bit in byte
      NEXT Byte in message
END


Thanks

Phil.


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

Joined: 07 August 2011
Location: Australia
Online Status: Online
Posts: 2755
Posted: 30 March 2018 at 12:26pm | IP Logged Quote TassyJim

I am sure that there is a much tidier way but this is a 'translation' of the example form the datasheet.
The three tests match those given in the datasheet.

Quote:
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$(CRC(test1$),4)
PRINT HEX$(swapHiLo(CRC(test1$)),4)
PRINT
PRINT HEX$(CRC(test2$),4)
PRINT HEX$(swapHiLo(CRC(test2$)),4)
PRINT
PRINT HEX$(CRC(test3$),4)
PRINT HEX$(swapHiLo(CRC(test3$)),4)

FUNCTION CRC(a$)
LOCAL ErrorWord% = &HFFFF, n, j, ByteVal, LSB
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
CRC = ErrorWord%
AND &HFFFF
END FUNCTION

FUNCTION swapHiLo(x)
' swap the high and low bytes of a 16 bit number
swapHiLo = (x MOD 256)*256 + INT(x/256) MOD 256
END FUNCTION

The swapHiLo function is there to sway the High and Low bytes to match the order given in the datasheet.

I am never sure how often you have to mask off the bits above 16 when doing this sort of maths.
Jim


__________________
It all started with the ZX81....
VK7JH
http://www.c-com.com.au/MMedit.htm
Back to Top View TassyJim's Profile Search for other posts by TassyJim Visit TassyJim's Homepage
 
goc30
Senior Member
Senior Member
Avatar

Joined: 12 April 2017
Location: France
Online Status: Offline
Posts: 119
Posted: 30 March 2018 at 2:11pm | IP Logged Quote goc30

in vb6 CRC16:

'*****************************************
Sub calcrc(buf1, nbb, v0)
'*******************************
'*    PRG de Calcul du CRC16
'*        buf1=buffer string
'*        nbb nb bytes in string with 2 bytes CRC
'*        v0=Value CRC in output
'*******************************
Dim V1 As Integer
Dim v2 As Long
Dim v21 As Long
Dim v3 As Long
Dim i As Integer
Dim j As Integer
v2 = &HFFFF&
V1 = 0
calcrc1:
    For i = 1 To nbb - 2
        'Print Len(buf1)
        v3 = Asc(Mid$(buf1, i, 1))
        v2 = v2 Xor v3
        For j = 1 To 8
            v21 = v2
            v2 = (v2 \ 2)
            If (v21 And 1) = 1 Then v2 = v2 Xor &HA001&
        Next j
   Next i
   v0 = v2
End Sub


Edited by goc30 on 30 March 2018 at 2:12pm
Back to Top View goc30's Profile Search for other posts by goc30
 
Phil23
Guru
Guru


Joined: 27 March 2016
Location: Australia
Online Status: Online
Posts: 1526
Posted: 30 March 2018 at 2:31pm | IP Logged Quote Phil23

Thanks Jim.

I presume the Swap could be included inside the main function.

I probably won't need to add a CRC as I think I'll just build a few different variables for strings for the requests,

ReqV, ReqA, ReqW, ReqkWh etc, so they can be constant.

But for the received string I will want to get the CRC for the returned data & compare it.

Which data sheet are you referring to in your post above?

Phil.
Back to Top View Phil23's Profile Search for other posts by Phil23
 
TassyJim
Guru
Guru
Avatar

Joined: 07 August 2011
Location: Australia
Online Status: Online
Posts: 2755
Posted: 30 March 2018 at 2:48pm | IP Logged Quote TassyJim

The datasheet is the one you referred to in an earlier post
Quote:
Eastron SDM220-Modbus Smart Meter Modbus Protocol Implementation V1.2


The swap was there to make it easier to add the CRC to the end of the sending string (provided you are sending as a string).

For receiving, strip the last two bytes of the full string, generate the CRC and compare it to the last two bytes you stripped of earlier.
In that case, you need the swap function also.
Yes it could be incorporated into the CRC function.

Quote:
FUNCTION CRC(a$)
LOCAL ErrorWord% = &HFFFF, n, j, ByteVal, LSB
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
'CRC = ErrorWord% AND &HFFFF
CRC = (ErrorWord% MOD 256)*256 + INT(ErrorWord%/256) MOD 256
END FUNCTION


Should do the trick.

Jim

__________________
It all started with the ZX81....
VK7JH
http://www.c-com.com.au/MMedit.htm
Back to Top View TassyJim's Profile Search for other posts by TassyJim Visit TassyJim's Homepage
 
goc30
Senior Member
Senior Member
Avatar

Joined: 12 April 2017
Location: France
Online Status: Offline
Posts: 119
Posted: 30 March 2018 at 9:18pm | IP Logged Quote goc30

Hi Jim
I think that incorporate compare function in CRC module is not a good idea.
modbus protocol is a exchange communication. In master mode you send and wait response, in slave mode, you wait question and send response. In each case you must create CRC value.
In your example, CRC module need a value to compare, but when you create crc value you have not a value to compare. In your case you need 2 modules, one for send ans one for receive
I think that it is driver's job to compare, not crc module
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: 06 April 2018 at 4:41pm | IP Logged Quote Turbo46

Jim,

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.

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


Joined: 27 March 2016
Location: Australia
Online Status: Online
Posts: 1526
Posted: 07 April 2018 at 1:24pm | IP Logged Quote Phil23

Next step in the phase,

I'm now sending 02 04 00 00 00 02 71 F8 with this:-


Quote:
volts$ = CHR$(02)+CHR$(04)+CHR$(0)+CHR$(0)+CHR$(0)+CHR$(02)+CHR$(&h71)+CHR$(&hF8)
Dim String DataStr
print volts$

Open "Com1:9600,,RecComs,8" as #1

print #1, volts$;

Do
Pause 100
Loop

Sub RecComs

print "In Coms Sub"
Local Integer HdrLoc,FtrLoc,BufLen
'Local String DataStr
If LOC(#1)>8 then 'If COM1 serial port buffer is NOT empty.... Was 100

Do WHILE BufLen<> LOC(#1)
BufLen=
LOC(#1)
Pause 50
Loop
End If


DataStr=
INPUT$(LOC(#1),#1) 'then suck everything in the buffer out and stick it in D$....

print "Volts";DataStr
'IEEE754

End Sub




The meter is returning this in the highlight.
Ignoring the CRC at the moment, I a bit vague on how I extract the value to be converted to the float, as the returned string is unprintable hex values & I only need a portion of it.




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


Joined: 27 March 2016
Location: Australia
Online Status: Online
Posts: 1526
Posted: 07 April 2018 at 1:46pm | IP Logged Quote Phil23

Data returned doesn't look right either.
The 43 76 79 E6 is 246.48 Volts which is about right,
but it looks like there is only 1 CRC byte. 9E.

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


Joined: 27 March 2016
Location: Australia
Online Status: Online
Posts: 1526
Posted: 07 April 2018 at 2:39pm | IP Logged Quote Phil23

This looks better with 9 bytes returned, I can see the voltage before the the 2 byte check sum, but can't see how I turn it into a series of hex numbers as it's sort of that way already in the string. Just not readable hex.





Phil.

Edited by Phil23 on 07 April 2018 at 2:43pm
Back to Top View Phil23's Profile Search for other posts by Phil23
 
Phil23
Guru
Guru


Joined: 27 March 2016
Location: Australia
Online Status: Online
Posts: 1526
Posted: 07 April 2018 at 3:45pm | IP Logged Quote Phil23

Ok,

I now have this mess of workings and it is returning a result.

Just not sure if I'm going around in circles converting data from one form to another un-necessarily.

Any feedback would be greatly appreciated.

Quote:
volts$ = CHR$(02)+CHR$(04)+CHR$(0)+CHR$(0)+CHR$(0)+CHR$(02)+CHR$(&h71)+CHR$(&hF8)
Dim String DataStr
print volts$

Open "Com1:9600,,RecComs,8" as #1
Do

print #1, volts$;

Do While Inkey$="": Loop
' Pause 1000

Loop

Sub RecComs

print "In Coms Sub"
Local Integer BufLen
'BufLen=LOC(#1)
print BufLen 'Local String DataStr
'If LOC(#1)>8 then 'If COM1 serial port buffer is NOT empty.... Was 100

Do While BufLen<> LOC(#1)
BufLen=
LOC(#1)
PAUSE 50
Loop
'End if


DataStr=
INPUT$(LOC(#1),#1) 'then suck everything in the buffer out and stick it in D$....

print "Response ";DataStr
print "Voltage "; Mid$(DataStr,4,4)
print "Length "; BufLen

Response$=
""

For n=1 to BufLen
'Print Hex$(Asc(Mid$(DataStr,n,1)),2)
Response$=Response$+Hex$(Asc(Mid$(DataStr,n,1)),2)
Next n

print Response$

IEEE754

End Sub

Sub IEEE754


ReadingStr$=
"&H"+Mid$(Response$, 7,8)
print ReadingStr$

Reading%=
Val(ReadingStr$)
Sign%=Reading% >>
31 'Read the Sign Bit
Expo%=((Reading% >> 23) And &hff) - 127 'Extract Exponent, Remove sign, Subtract Offset
Ans!=((Reading% Or &h800000) And &hffffff)/2^(23-Expo%)*(-1)^Sign% 'Extract Mantissa & Add implied 24th bit, Shift Point in Binary & apply sign

print "Text Input String: ";ReadingStr$
print "Hex Value Reading: ";Hex$(Reading%,8) 'Reading in Hex
print "Binary Value: ";Bin$(Reading%,32) 'Reading in Binary
print "Sign Bit: ";Hex$(Sign%)
print "Exponent & -offset: ";Bin$(Expo%+127,8), Str$(Expo%)
print "Mantissa Value: ";Bin$((Reading% Or &h800000) And &hffffff,32)
print
print "Returned Value: ";Str$(Ans!,10,1)
print


End Sub






Bit I have trouble getting my head around is that the "String" that comes in the com port is really a series of bytes & how I should treat them.

Phil.



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: 09 April 2018 at 4:54am | IP Logged Quote goc30

hi fil

your IEEE754 subroutine is good
your modbus string is correct (for protocol)
it say: "i want to know 2 values in memory in adr=0 and adr=1 (or register n 0)"
answer is correct??
the response is "i send 4 bytes"

value transmit by RTU is in integer format each time? and for that you want to read 2 values ?
adr 0 is good adr for temp?
what value do you want to receive (format example :24.4 Deg) ?
Back to Top View goc30's Profile Search for other posts by goc30
 


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.1406 seconds.
Privacy Policy     Process times : 0, 0.02, 0, 0.12