Writing Modbus TCP/IP Driver

D

Thread Starter

db

I'm hoping that somebody can help me understand how to approach the following scenario.

I have a PLC (Modicon M340) which spits out Modbus TCP/IP over ethernet. This ethernet link is connected to a PC where we need to develop a C++ application to read and write data to the M340 using Modbus TCP/IP. In other words, I need to write a Modbus TCP/IP driver.

Has anyone had any experience with this? I understand the basic concept of opening a TCP/IP socket and modbus data wrapped in the TCP/IP packet structure down it. What I am not sure of is where to start. I have never coded anything to do with TCP/IP before and my C++ skills are fairly basic. I'll pick it up just fine though if I can get a starting point.

Incase it is mentioned, I know that we could use a SCADA package to read the data from the PLC and this would be usual practice. However on this occasion we need to develop our own software.

Thanks
 
Affordable, field tested, highly optimized, off-the-shelf drivers are available so there is generally no need to write a driver from scratch.

For Windows applications, check out http://automatedsolutions.com for Modbus/TCP Client and Server components that can be used in c++ applications.

Free 30-day, fully functioning trial software is available for download.
 
Thanks for the link! I have taken a look and will definitely give the trial a go. Unfortunately though I don't think it will be suitable due to it being a subscription service. The software we require is for a customer and will be installed on site. It wouldn't be practical if there was a yearly subscription involved.

If anybody could help further to my original post that would be brilliant :)
 
T

Tallak Tveide

Have a look at mblogic (python) or rmodbus (ruby) (or the mentioned pvbrowser). You should be able to quite easily implement a necessary subset of modbus from these examples. You should be able to get hold of the modbus spec quite easily on the net.
 
The software itself is not a subscription service. The term 'subscription' refers only to technical support and product updates and is optional.

Subscription Edition: provides 12 months technical support and product updates.

Standard Edition: provides current version and 30 days technical support.

-Mark
http://automatedsolutions.com
 
J

James Ingraham

Even with your self-described basic knowledge of C++ this application should be easy. I did it (in C) in an afternoon. I hired a college intern for six months, and gave him writing a Modbus/TCP program as a first example to see what kind of stuff he would be working on. It took him 3 days from a standing start.

Modbus/TCP is EASY. That's one of the wonderful things about it. The documentation is straightforward. There are plenty of open source projects out there that you can look at for help. Heck, I'll help. I actually think using a third-party component would be HARDER than coding it yourself.

By the way, platform DOES matter. You didn't specify whether this application was going to run on Windows, Linux, or whatever. That would be useful information.

-James Ingraham
Sage Automation, Inc.
 
The original poster indicated 'I have never coded anything to do with TCP/IP before and my C++ skills are fairly basic.'

So I disagree with your statement 'I actually think using a third-party component would be HARDER than coding it yourself.'

Here's what it takes to read a register using a third party component. <pre>
// Set properties
axASMBTCP1->AddressType = ADDR_TYPE_MODICON_5_DIGIT; // mode addressing scheme implemented on server
axASMBTCP1->Function = FUNC_MB_READ_ HOLDING_REGISTERS; // function for this transaction
axASMBTCP1->NodeAddress = "192.168.0.171"; // modbus/tcp server's ip address
axASMBTCP1->MemQty = 1; // read one word
axASMBTCP1->MemStart = "40001"; // starting address 40001

// Perform a transaction
axASMBTCP1->SyncRefresh();

// Test transaction result
if (axASMBTCP1->Result == 0)
{
CString value;
value.Format("%d", axASMBTCP1->GetDataWordM(0));
textBox1->Text = value;
}
else
// Implement error handler</pre>
Third party component: supports 16 common modbus functions, no socket programming, no protocol learning curve, field proven, help documentation, example source code, technical support.

-Mark
http://automatedsolutions.com
 
T

Tallak tveide

All is well until you need to debug it I guess ;)

And no exceptions support?

Why not make life easier by using Python or Ruby? The simple example in Ruby reads:<pre>
ModBus::TCPClient.connect('127.0.0.1') do |client|
puts client.read_holding_registers(0,4)
client.write_multiple_registers(0, [4,4,4])
puts client.read_holding_registers(0,4)
end</pre>
 
Wow, I didn't expect such a response! Thanks :)

I'll have a look over all the replies and post up again. I'm sure I'll have some questions!

The platform will be Windows. I'm likely to need a GUI to go with it. I have been advised to use MFC and the CAsynchsocket function but I haven't looked any further into it yet.

Am I right in thinking that the general steps to follow in the code would be to:

1. Create and open a socket
2. Use a wrapper to wrap the modbus data inside TCP
3. Send the data down the socket
4. Listen for a reply

No.2 in that list is what I am most unsure of.
 
I've had a look over the replies and any links posted. In particular I have looked at the SCADA/HMI project that was posted and gone through the code (rlmodbus.h/.cpp).

All in all - and this is probably because of my lack of knowledge/experience with modbus - I'm not sure why there are such vast amounts of code? Looking at this link http://www.simplymodbus.ca/TCP.htm it would seem that all I need to do is:

1. Create and register a TCP socket
2. Build up the Modbus TCP/IP ADU (MBAP Header and Modbus PDU)
3. Buffer the PDU eg 0001 0000 0006 11 03 006B 0003
4. Spit it out and down the socket to the Server

Have I got the correct idea of things?
 
I would suggest you contact Cyberlogic. We have the driver you need--the same one Schneider Electric licenses for use with their products--and a software development kit to help you interface your application to it. With that, you can devote your full effort to developing your application.

You'll find details at:
http://www.cyberlogic.com/mbx_sdk_suite.html

Dan Muller
 
> The platform will be Windows.

Our http://pvbrowser.org runs on Windows/Linux/OS-X

> I'm likely to need a GUI to go with it.
> I have been advised to use MFC

With pvbrowser you can design your GUI graphically
and you will have little manual coding.
A side effect is that you get a client/server software. You can connect to the visualization server from many computers at the same time either over the LAN or even over the internet.
Instead of MFC it is based on http://qt.nokia.com/

> and the CAsynchsocket function but I haven't
> looked any further into it yet.

We use the standard socket library.
Asynch functions are implemented with threads.

> 1. Create and open a socket
> 2. Use a wrapper to wrap the modbus data inside TCP
> 3. Send the data down the socket
> 4. Listen for a reply

In principle yes

> In particular I have looked at the SCADA/HMI
> project that was posted and gone through the
> code (rlmodbus.h/.cpp).
> <snip>
> I'm not sure why there are such vast amounts
> of code?

Our rlmodbus is more general.
It supports serial lines (COM ports) and TCP.
It supports ASCII and RTU mode.
It supports Master and Slave.
It is portable between Windows/Linux/OS-X and OpenVMS.
 
J

James Ingraham

"Set properties
axASMBTCP1->AddressType = ..."

No RAII? And why is this a pointer to an object?

Here's what code in a client program looks like for my Mbtcp class:<pre>
int main(int argc, char *argv[]){
int16_t i;
Mbtcp plc1; // default constructor is fine
plc1.mbconnect("192.168.0.10", 0); // IP and node
plc1.read_regs(0, 2); // read register 0, 2 bytes long (i.e. "40001")
memcpy(&i, plc1.response[MBTCP_READ_DATA_START_BYTE], 2);
plc1.mbdisconnect();
}
</pre>
a) Yes, I just return a "bag of bits," or more accurately a "bag of bytes." This makes it somewhat harder to deal with, necessitating the ugly memcpy.

b) I don't support all that many functions. 3, to be precise; read, write, and readwrite regs. (Function codes 3, 16, and 23 for those of you who care.)

c) These 5 lines required me to write some code using sockets and the Modbus/TCP spec. The sum total is a .cpp file and a .hpp file, totaling a massive TWO HUNDRED lines of code. (Note: sarcasm.) Including comments. Again, it took an afternoon to pound it out to get it working. It probably took a week to get in to production shape. I originally did it in C, then made it C++, which probably took another couple of days.

d) I didn't use RAII either. Looking back at it, I probably should have.

e) Yes, I do have error checking in there; I'm just ignoring it in the example.

f) I could be persuaded to share the code if anyone cares.

g) The code is written for QNX. It will probably work under any *nix OS, but might take some tweaking. It would DEFINITELY require work for Windows. (I suppose Cygwin or MinGW would work, but I've never had much luck with them and don't recommend it, especially for production / "saleable" code.)

h) Yes, I can go on, can't I?

i) We're probably at point where this would be better served on another forum. We start talking about RAII and references vs. pointers and we're well into the pure software field, as opposed to controls.

Bottom line: Yes there are benefits to buying a pre-built solution, and of using an open source version. There are also advantages to rolling your own. I normally argue for buy over build whenever possible, but in this case I feel like it's just too damn easy to do yourself.

-James Ingraham
Sage Automation, Inc.
 
B
Hello!

I would be interested in the C codes. I d like to write first a Modbus TCP server in C and run it on windows. Then I would have to load an alternative version to a microcontroller to communicate through Modbus TCP protocol (in server/slave mode). I d be grateful if you could help me by sending me the code. I understand that I d have to change the code to work on windows and then on the microcontroller.

Balazs

>"Set properties
>axASMBTCP1->AddressType = ..."
>
>No RAII? And why is this a pointer to
>an object?
>
>Here's what code in a client program
>looks like for my Mbtcp class:<pre>
>int main(int argc, char *argv[]){
> int16_t i;
---- snip ----
>2);
> plc1.mbdisconnect();
>}
></pre>
>f) I could be persuaded to share the code if anyone cares.
 
Top