Jump to content

V130 problem with Modbus TCP/IP Function Code #15


Recommended Posts

Hi!
So, I am writing ASP Core 2 project and one of the components is communicating with Unitronics V130 unit (as a Modbus Slave). Since provided .NET Driver library is not portable to ASP Core I need to implement this communication with Modbus. I have limited experience with Modbus protocols but I have used some basic functions in the past – mostly reading Registers and Coils. Just to sample some data. I used python library modbus_tk for this purpose. Using Modbus gives me options for adding more optional hardware down the line.

After reviewing some libraries supporting Core I selected library Modbus https://github.com/AndreasAmMueller/Modbus made by AndreasAmMueller, written in C# as Net Standard 2.0 library. I started testing this library by communicating over Modbus TCP/IP with V130 PLC. I got mixed results. - namely "Write Multiple Coils " or "Force Coils " (Function code 15) does not work correctly.

So. Modbus read functions all work great, these are:

  • Read Coils (Function code 01)
  • Read Holding Registers (Function code 03)

Out of write functions these work great too:

  • Write Multiple Registers (Function code 16) – named “#16 Preset Holding Registers” in VisiLogic help files
  • Write Single Coil (Function code 05) – not referenced in Unitronics (VisiLogic) help files under “Slave Addressing”
  • Write Single Register (Function code 06) - not referenced in Unitronics (VisiLogic) help files under “Slave Addressing”

These functions are documented in help files of VisiLogic at “Slave Addressing” topic (where address offsets are defined).


image.png.7105d0a6c4510f1c239c1b5e7542560e.png

Now, function that does not work (at least with V130) is:

  •  Write Multiple Coils (Function code 15) – named “#15 Force Coils” in VisiLogic help files

This function takes in “n” sequential bit values (“coils”) and a “starting address”. Then it writes these values in slave (in my case V130 device), starting at “start address” and for “n” values.

It never sets coils to provided values. But it does set provided coils (addresses) just not to provided values. Meaning, if I want to set 16 coils (lets say some Memory Bits – MB) to 1, only some 8 coils would be set to 1, other 8 coils would be set to 0. It appears that only some byte is set to provided values (but that varies with number of provided addresses/values).

                             Let me repeat again – all provided addresses are written to, just not the values that are provided.

Interesting thing is that even if I provide only “true” (1) values, somehow “false” (0) is written to address.
AFAIK if I provide only “false” values, no “true” value is written to address.

Let’s see what is sent to PLC in next section :)

 

Modbus implementation on PLC?
I suppose Unitronics PLC Modbus implementation is compliant to Modbus specifications and implementation guides, defined by http://www.modbus.org/specs.php (I don’t know, but I would hope). 

As such

This is nicely presented in image bellow – Modbus ADU (MBAP Header + MODBUS request). Here request is made for reading register #5 in remote server (page 22/46 from Application Protocol document):


image.png.0d8919c36cb8ef113b9ad4edbb12ae1f.png

There are 6 bytes of MBAP Header, followed by “length – 1” bytes of MODBUS request (we must subtract 1 byte for Unit identifier, which is included in “length”). In example above there are 5 bytes of Modbus request that makes “length” equal 6.

Function “Write Multiple Coils (code 15)Modbus request is defined in Messaging Implementation Guide at page 29/50:

image.png.2bd83c129fd88f1699832cae19973d4e.png

This block is inserted at MOBUS request field in Modbus ADU, presented above. It takes 1 byte to describe the function code, 2 bytes for starting address, 2 bytes for quantities of values (number of “coils” we want to write), 1 byte for number of bytes and finally N Bytes for associated values.

 

Simple test
For simple test I am going to use function “Write Multiple Coils (code 15)”, writing 16 (2 bytes in length) “true” (1) values, starting at address 1200, which means Memory Bit (MB) #1200. Device ID is 1.
This is Modbus request ADU, which is sent to PLC:

image.png.30fcfc42457f9c66817bf39f8abe4a33.png

It appears that the implementation of Modbus in NET Standard 2.0 library is compliant to Modbus specifications and implementation guides, defined by modbus.org. There is correct MBAP Header and MODBUS request. It provides correct address (1200), number of coils (16) and correct values of coils, which is “true” 1 for all, thus 2 bytes with value of 255.
But the result of this operation yields, starting at correct address of 1200: 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0. We were hoping for all 1.

I doubt its wrong implementation in the library, I have checked many requests against specifications from modbus.org and all seem to comply. Ofc the working functions (Read Coils) etc. all comply to modbus.org specifications and yield correct results.

Only “Write Multiple Coils (code 15)” function yields wrong results and is there any chance that its not implemented according to modbus.org?
 

Solution/hack that seems to work
I found one hack that seems to work (at least with V130 PLC). But I wouldn’t recommend to use it until there is conformation that this hack does not cause some unwanted consequences.

Here goes, it appears that appending one (dummy) byte (of arbitrary value) before bytes that represents values (in the above case [12] and [13] bytes) somehow causes the function to work and we can update Length byte [5]. But it seems that updating Length does not have any effect, it works regardless (need to test that more rigorously).

New (working) request would look like this, with added dummy byte:
image.png.8837caf926c3e965e5a2911d74dc9de4.png

As you can see there is added dummy byte at [12] and length updated to 9.

Result of this operation yields correct state - all 16 coils MBs are set correctly. This seems to works with any combination of values and number of coils. 

But this hack is untested, I don’t know if this command is really contained to just defined addresses or does it write all over the memory. I tested this only for some addresses beyond defined range by manually setting values via VisiLogic “online mode".

 

More importantly, why does this hack work?

Or even more, why “normal” Write Multiple Coils (code 15) does not work? Is this V130 specific?

 

Any help appreciated!

Best Regards!
 

PS: To add, I did change "MODBUS: SCAN_EX" block to SCAN and SCAN_32 - nothing worked. Now I left it with "MODBUS: SCAN" block to support "older working applications" as stated in help files of VisiLogic.

Link to comment
Share on other sites

  • MVP 2023

Nice request.  You've put everything in the post we need to know about your problem.

Thoughts that come to mind-

1.  You are rolling your own Modbus TCP code and using the V130 as a standard.  You've stated that it is mostly successful, which is good. 

If two things talking don't work the way you think they should, test one or the other.  I would recommend installing a Modbus slave program on a PC and testing your code on that-

Here's one I found with a quick search- https://www.modbustools.com/modbus_slave.html

Then you can determine which is wonky - the PLC or your code.

I have a bought-and-paid-for Modbus simulator that has proven invaluable over the years when I'm banging my head on why the PLC won't work in ModbusLand.  It's usually because of some misunderstanding on my part of the PLCs Modbus idiosyncrasies.

2.  The question of a V130 not doing Modbus right bothered me and I happen to have a V130 with an Ethernet card handy so I put a little slave program in and fired up my simulator.  I set it up to write 16 "1"s to MB's 1200..1215.  My simulator is 1-based so I start at address 1201-

image.png.b5e0ee9b70bf1e9bd40ce600c591b2d4.png

There are 16 "1"s in the value field separated by commas.

It wrote the data properly to the PLC.

image.png.f83f6af5b8c78e5f7fb6469a16e3f88a.png

So here's the command string - 

image.png.10a11d66cf5f632d53198cfeb3af4874.png

Very similar to yours, except what's that crazy "02" in front of the "FF FF" data?  (The first part of the line is a time stamp, and this program doesn't show TCP packet data in the monitor).

In your post, you mention a "dummy byte" in this position as part of the "hack".  That's no dummy byte.  You list several documents above, and you've demonstrated that you understand Modbus TCP is it really an old-school Modbus frame encapsulated in a TCP packet.  There's one more document you need to consult, which is the original Modbus definition from Modicon-

http://modbus.org/docs/PI_MBUS_300.pdf

For your specific case, jump to the example on page 45-

image.png.157254c42abac3d9fde3218e0380c2e5.png

As you can see, your "dummy byte" is a required part of this command for Byte Count.  It's mentioned in the 6.11 definition image in your post but it is not included in your "Simple Test" table.

Hopefully this helps.

Joe T.

 

V130.vlp

Link to comment
Share on other sites

Thanks for your insights.

So, if I understand correctly, I was referring to more recent documents of Modbus implementation?

Does the V130 supports only older Modbus implementation? Described in the document you posted, which is from 1996 http://modbus.org/docs/PI_MBUS_300.pdf .
If we compare “Write Multiple Coils (code 15)” function Request from 2012 and 1996 side by side:
image.png.656769ed0842552bac0aecd6c7bcae98.png

Both define Byte Count field – I completely missed that. It appears the folly is with the protocol implementation and NOT V130 Modbus (what was I thinking ofc…).
There is one more field defined by 1996 implementation: Error Check (LRC or CRC), is that field still required?
 

“Write Multiple Registers (code 16)” works well and it appears that is because it contains Byte Count field (but no Error Check), which Write Multiple Coils does not. It still begs me why the value in that field has no impact on function execution, if I define Byte Count as 0 or 255, the function executes correctly regardless. Maybe V130 references Length value from MBAP Header and trims command to specified length or sth.

Best Regards!

Link to comment
Share on other sites

  • 3 weeks later...

Just offering my two cents....

I just finished a VS.net application that shares a common MODBUS between six devices. None of them seemed to precisely follow the MODBUS standard. After pulling my hair out for days, I decided to download a serial sniffer https://www.serialmon.com/ and run it while communicating with each device's native software. This allowed me to see the exact MODBUS bytes being shared. I simply copied the bytes into my application as needed. This eliminated the need for reading manuals and selecting the registers and offsets and calculating CRCs and so on just to find that the device "closely" followed the protocol.

Fortunately, my Samba has an ethernet port so it wasn't a part of the MODBUS problem.

Link to comment
Share on other sites

  • MVP 2023

You have sort of stumbled on the method that I (and others) repeatedly mention on the forum with anything involving serial comms.  Monitor all comms using just your PC and the device, with a serial stream recorder of any description between the two, to initially find exactly what is going on when you do something successfully.  Once you have that information you then transfer it to the plc program.  It is a complete PITA trying to troubleshoot serial comms issues only using the plc.

cheers, Aus

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...