View previous topic :: View next topic |
Author |
Message |
sentinel
Joined: 02 Feb 2009 Posts: 141 Location: Tasmania
|
Posted: Sun Aug 16, 2015 8:06 am Post subject: Parsing RS485 Modbus received data |
|
|
Perhaps it's because I've been staring at it for too long, or maybe because of my streaming cold and bunged-up head, but I have a puzzling problem parsing data from a WattNode smart meter slave on an RS485 bus.
The data snooped directly from the RS485 bus seems to be read properly. It consists of 14 register read requests and the replies from the smart meter.
01 03 04 b0 00 01 84 dd read request number 1 from master
01 03 02 4f 0b cd b3 reply number 1 from slave
01 03 04 b1 00 01 d5 1d
01 03 02 00 03 f8 45
01 03 04 b8 00 01 05 1f
01 03 02 00 00 b8 44
01 03 04 bc 00 01 44 de
01 03 02 06 42 3b d5
01 03 05 20 00 01 85 0c
01 03 02 00 00 b8 44
01 03 05 21 00 01 d4 cc
01 03 02 00 00 b8 44
01 03 05 3a 00 01 a4 cb
01 03 02 00 00 b8 44
01 03 05 46 00 01 65 13
01 03 02 00 33 f8 51
01 03 05 47 00 01 34 d3
01 03 02 00 00 b8 44
01 03 05 48 00 01 04 d0
01 03 02 00 00 b8 44
01 03 05 49 00 01 55 10
01 03 02 00 00 b8 44
01 03 05 4b 00 01 f4 d0
01 03 02 0e ff fc 64
01 03 05 4c 00 01 45 11
01 03 02 00 0d 79 81
01 03 05 4d 00 01 14 d1
01 03 02 00 00 b8 44
However, when the same data is read by the UART on the ATmega168PA, the data part bears no resemblance to the data part in the data logged from the bus.
(I can't get Cntrl+C to work with the BASCOM terminal emulator) see attached screenshot image
Code: |
Read_modbus_reply:
Timeout = 0
I = 0
Do
If Ischarwaiting() <> 0 Then 'was something returned?
B = Waitkey()
Print #4 , Hex(b) ; " " ; 'then get it
Incr I
If I = 4 Then Hibyte = B
If I = 5 Then
Lobyte = B
Register(r) = Makeint(lobyte , Hibyte)
Exit Do
End If
End If
If Timeout > 7000 Then
Print #4 , "Modbus timeout " ; Timeout
Exit Do
End If
Incr Timeout
Loop
Print #4 , ""
Return
|
It's probably something glaringly obvious, but I can't understand why the parsed values should be different from the raw values.
For example, the first value returned on the bus is 2 hex bytes 4f 0b, but this is parsed by the code above to be 00 b8, giving a decimal word value of 184. Confusingly, the first byte (the slave address), second byte (read request) and the third byte (number of data bytes to follow) are all correct.
Can anyone see where I'm going wrong?
(BASCOM-AVR version : 2.0.7.9 , Latest : 2.0.7.8 ) |
|
Back to top |
|
|
MWS
Joined: 22 Aug 2009 Posts: 2262
|
Posted: Sun Aug 16, 2015 5:09 pm Post subject: Re: Parsing RS485 Modbus received data |
|
|
sentinel wrote: |
Code: | B = Waitkey()
Print #4 , Hex(b) ; " " ; |
|
Did you consider that in the same you're receiving one byte, you're sending out three bytes, this way blocking incoming bytes?
What do you do with the 6th+ bytes excess data, not to fool your incoming?
Quote: | (I can't get Cntrl+C to work with the BASCOM terminal emulator) |
Mouse-select, right click --> copy. |
|
Back to top |
|
|
sentinel
Joined: 02 Feb 2009 Posts: 141 Location: Tasmania
|
Posted: Mon Aug 17, 2015 1:09 am Post subject: |
|
|
Thanks, MWS, for the reply.
The RS485 bus is on the hardware UART and I'm printing the received data to a software serial port on other pins just for debug purposes.
I wouldn't have thought that there would be any missed or conflicting data at 9600 baud, but it's the only lead I have at the moment. I'll change the logic so that the received data is printed to the debug port after all bytes are received from the slave.
The 5th and 6th bytes are the CRC check bytes which I've omitted at this early stage while I get the UART serial input working reliably.
Thanks for your tip on the copy function. That'll save a bit of messing round. |
|
Back to top |
|
|
MWS
Joined: 22 Aug 2009 Posts: 2262
|
Posted: Mon Aug 17, 2015 9:07 am Post subject: |
|
|
sentinel wrote: | I wouldn't have thought that there would be any missed or conflicting data at 9600 baud, but it's the only lead I have at the moment. I'll change the logic so that the received data is printed to the debug port after all bytes are received from the slave. |
An unbuffered UART, be it soft or hard, works blocking.
With one difference of the hard UART: sending exactly one byte results in writing the byte to UDR and then returning to main code, as waiting for UDR-empty is done before writing UDR. So writing one byte to an hard UART with enough time inbetween works without much delay.
The soft-UART in contrary has to bang out the first to the last bit, so it returns only if the last bit of the last byte is sent, and be it a single byte.
If in your case both UARTs run at the same baud rate, sending out three bytes will make the incoming at least miss two of them.
However, if you'd make the incoming buffered, there's a risk, the soft UART will be disturbed.
Another option is sending with high baud rate, so the delay of sending everything out is shorter than the time between two incoming bytes.
Quote: | The 5th and 6th bytes are the CRC check bytes which I've omitted at this early stage while I get the UART serial input working reliably. |
Depending on how command start is detected, they may clog your input. |
|
Back to top |
|
|
Paulvk
Joined: 28 Jul 2006 Posts: 1257 Location: SYDNEY
|
Posted: Mon Aug 17, 2015 10:40 am Post subject: |
|
|
Bascom does have a Modbus add on built in I am going to try and use it to communicate with a solar Inverter.
Have you checked it out in the help.
Regards Paul |
|
Back to top |
|
|
sentinel
Joined: 02 Feb 2009 Posts: 141 Location: Tasmania
|
Posted: Mon Aug 17, 2015 10:54 am Post subject: |
|
|
Yes, I'm using the library module and the code that captures replies from the slave are based on the Modbus master example. |
|
Back to top |
|
|
Paulvk
Joined: 28 Jul 2006 Posts: 1257 Location: SYDNEY
|
Posted: Mon Aug 17, 2015 1:05 pm Post subject: |
|
|
OK I will be greatly interested in your progress
When I start I will post my progress.
You may also find the SDM-220 modbus energy meter of interest a search on google with show info on it
Regards Paul |
|
Back to top |
|
|
sentinel
Joined: 02 Feb 2009 Posts: 141 Location: Tasmania
|
Posted: Tue Aug 18, 2015 5:17 am Post subject: |
|
|
Right, this is where I've got to.
The subroutine seems to capture the input a bit more reliably, but with a couple of caveats. It looks like you were right, MWS, about the timing of the software serial debug port.
This is the data on the RS485 bus monitored directly to PC
(interspersed with read requests)
01 03 04 b0 00 01 84 dd
01 03 02 4f 0b cd b3
01 03 04 b1 00 01 d5 1d
01 03 02 00 03 f8 45
01 03 04 b8 00 01 05 1f
01 03 02 00 00 b8 44
01 03 04 bc 00 01 44 de
01 03 02 06 55 7b db
01 03 05 20 00 01 85 0c
01 03 02 00 00 b8 44
01 03 05 21 00 01 d4 cc
01 03 02 00 00 b8 44
01 03 05 3a 00 01 a4 cb
01 03 02 00 00 b8 44
01 03 05 46 00 01 65 13
01 03 02 00 31 79 90
01 03 05 47 00 01 34 d3
01 03 02 00 00 b8 44
01 03 05 48 00 01 04 d0
01 03 02 00 00 b8 44
01 03 05 49 00 01 55 10
01 03 02 00 00 b8 44
01 03 05 4b 00 01 f4 d0
01 03 02 0e ff fc 64
01 03 05 4c 00 01 45 11
01 03 02 00 0c b8 41
01 03 05 4d 00 01 14 d1
01 03 02 00 00 b8 44
..and the data captured by the master and relayed to the debug port, then to the PC
00 01 03 02 00 00 B8 44
00 01 03 02 4F 0B CD B3
00 01 03 02 00 03 F8 45
00 01 03 02 00 00 B8 44
00 01 03 02 06 55 7B DB
00 01 03 02 00 00 B8 44
00 01 03 02 00 00 B8 44
00 01 03 02 00 00 B8 44
00 01 03 02 00 31 79 90
00 01 03 02 00 00 B8 44
00 01 03 02 00 00 B8 44
00 01 03 02 00 00 B8 44
00 01 03 02 0E FF FC 64
00 01 03 02 00 0C B8 41
Registers
512 591 512 512 518 512 512 512 512 512 512 512 526 512
However, several problems remain:
1) The received bytes are being put into the wrong array positions, with a zero value in Slave_string(1), and everything else shifted one position to the right, occupying 8 elements instead of 7.
2) There is also a shifted set of data in the dump to the debug port.
The line 00 01 03 02 00 00 B8 44 at the beginning of the listed data should be at the end, as it belongs to the 14th register, not the first. This can be verified in the data monitored from the bus directly.
This is the code that generates the above.
Code: |
Rs485:
R = 1
Print Makemodbus(1 , 3 , 1200 , 2); ' slave 1, function 03, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 2
Print Makemodbus(1 , 3 , 1201 , 2); ' slave 1, function 03, address 1202 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 3
Print Makemodbus(1 , 3 , 1208 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 4
Print Makemodbus(1 , 3 , 1212 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 5
Print Makemodbus(1 , 3 , 1312 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 6
Print Makemodbus(1 , 3 , 1313 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 7
Print Makemodbus(1 , 3 , 1338 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 8
Print Makemodbus(1 , 3 , 1350 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 9
Print Makemodbus(1 , 3 , 1351 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 10
Print Makemodbus(1 , 3 , 1352 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 11
Print Makemodbus(1 , 3 , 1353 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 12
Print Makemodbus(1 , 3 , 1355 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 13
Print Makemodbus(1 , 3 , 1356 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
R = 14
Print Makemodbus(1 , 3 , 1357 , 2); ' slave 1, function 16, address 1201 , send a word
Gosub Read_modbus_reply
Waitms 100
Print #4 , "Registers"
For R = 1 To 14
Print #4 , Register(r) ; " ";
Next R
Print #4 , ""
Return
Read_modbus_reply:
Timeout = 0
I = 1
Do
If Ischarwaiting() <> 0 Then 'was something returned?
Slave_string(i) = Waitkey() 'Save the byte in an array
B = Slave_string(3) + 5 'Calculate the number of bytes in the slave message, using the 3rd byte, which gives number of bytes of data
If I = B Then 'if input counter reaches message length, then
Exit Do 'exit the loop
End If
Incr I 'Increment the input counter
End If
If Timeout > 1000 Then 'Timeout in case of no reply
Print #4 , "Modbus timeout " ; Timeout
Exit Do
End If
Incr Timeout
Loop
Register(r) = Makeint(slave_string(5) , Slave_string(4)) 'Combine 2 bytes to make a word
For B = 1 To I
Print #4 , Hex(slave_string(b)) ; " "; 'Print the contents of the array to the debug port
Next B
Print #4 , ""
Return
|
I feel that we're getting close, but maybe I'm missing a fundamental understanding such as arrays being zero-based or one-based. |
|
Back to top |
|
|
sentinel
Joined: 02 Feb 2009 Posts: 141 Location: Tasmania
|
Posted: Tue Aug 18, 2015 6:02 am Post subject: |
|
|
Update:
Now here's the thing.
Setting the counter I to zero, instead of one, on the second line of the Read_modbus_reply subroutine fixes BOTH problems simultaneously, which is great.... except I don't understand why, and therefore I will learn nothing from this problem.
On the surface, it looks as if the first received byte is being assigned to Slave_string(0), but the printout reads from Slave_string(1) to the end.
And why does it also fix the out-of-sequence lines?
Is there a stray byte of data in the serial buffer to begin with, and should I have a Clear Serialin instruction somewhere to clear it?
This is the result with I=0
01 03 02 4F 0B CD B3
01 03 02 00 03 F8 45
01 03 02 00 00 B8 44
01 03 02 06 68 BA 0A
01 03 02 00 00 B8 44
01 03 02 00 00 B8 44
01 03 02 00 00 B8 44
01 03 02 00 31 79 90
01 03 02 00 00 B8 44
01 03 02 00 00 B8 44
01 03 02 00 00 B8 44
01 03 02 0E FF FC 64
01 03 02 00 0F F8 40
01 03 02 00 00 B8 44
Registers
20235 3 0 1640 0 0 0 49 0 0 0 3839 15 0
Which is great, but I want to know why! |
|
Back to top |
|
|
MWS
Joined: 22 Aug 2009 Posts: 2262
|
Posted: Tue Aug 18, 2015 9:08 am Post subject: |
|
|
Do you really expect to solve riddles based on some code chunks?
The starting &h00 I may have already mentioned:
Quote: | Depending on how command start is detected, they may clog your input. |
There may be a trailing &h00, which is received by the UART. As it is a hardware UART, it remembers the last received data byte.
This way Ischarwaiting():
Code: | Print Makemodbus ...
'...
If Ischarwaiting() <> 0 Then |
immediately becomes true, reads the &h00 and writes it to the array.
Quote: | And why does it also fix the out-of-sequence lines? |
If you did not include a CONFIG BASE=0, then this:
Code: | I = 0
Slave_string(i) = Waitkey() |
creates a array underrun, which overwrites the previously to Slave_string() dim'd variable with a value, in this case &h00.
That's a first hint to follow. |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum
|
|