Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
|
Forum Index : Microcontroller and PC projects : Modbus CRC.
Page 1 of 2 | |||||
Author | Message | ||||
Phil23 Guru Joined: 27/03/2016 Location: AustraliaPosts: 1664 |
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? [Code] 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 [/code] Also have this example from Eastron's Modbus Manual, [Code] 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 [/code] Thanks Phil. |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5867 |
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. 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 VK7JH MMedit  MMBasic Help |
||||
goc30 Guru Joined: 12/04/2017 Location: FrancePosts: 425 |
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 |
||||
Phil23 Guru Joined: 27/03/2016 Location: AustraliaPosts: 1664 |
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. |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5867 |
The datasheet is the one you referred to in an earlier post 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. Should do the trick. Jim VK7JH MMedit  MMBasic Help |
||||
goc30 Guru Joined: 12/04/2017 Location: FrancePosts: 425 |
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 |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1584 |
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 Keep safe. Live long and prosper. |
||||
Phil23 Guru Joined: 27/03/2016 Location: AustraliaPosts: 1664 |
Next step in the phase, I'm now sending 02 04 00 00 00 02 71 F8 with this:- 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. |
||||
Phil23 Guru Joined: 27/03/2016 Location: AustraliaPosts: 1664 |
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. |
||||
Phil23 Guru Joined: 27/03/2016 Location: AustraliaPosts: 1664 |
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. |
||||
Phil23 Guru Joined: 27/03/2016 Location: AustraliaPosts: 1664 |
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. 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. |
||||
goc30 Guru Joined: 12/04/2017 Location: FrancePosts: 425 |
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) ? |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1584 |
Previously I wrote: 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 Keep safe. Live long and prosper. |
||||
Phil23 Guru Joined: 27/03/2016 Location: AustraliaPosts: 1664 |
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. |
||||
goc30 Guru Joined: 12/04/2017 Location: FrancePosts: 425 |
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 |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1584 |
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. Keep safe. Live long and prosper. |
||||
goc30 Guru Joined: 12/04/2017 Location: FrancePosts: 425 |
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 |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1584 |
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 Keep safe. Live long and prosper. |
||||
goc30 Guru Joined: 12/04/2017 Location: FrancePosts: 425 |
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 |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1584 |
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 Keep safe. Live long and prosper. |
||||
Page 1 of 2 |
Print this page |