NI Labs Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

NI LabVIEW Modbus API Discussion

Hey Ezags,

A holding register as defined as being an unsigned 16-bit register. If you are writing a unsigned 32 bit integer, you are actually splitting it across two registers. You can use the split number and join numbers functions in LabVIEW to do this. An example is shown here:

write u32.png

Thanks,

Daniel

Ezags1 wrote:

Is there a way to write unsigned 32 bits to the write single holding register function? It seems to only accept 16 bits when i need to write 1,073,741,824 and in between 65535 as an integer.

Message 191 of 527
(1,585 Views)

New build up with the memory leak fix. Still investigating the remaining leak you saw.

This release (1.1.5.39) also sets the tcp connect timeout to a default of 10 seconds. It was previously set to 0 and optional, which caused an immediate error 56.

0 Kudos
Message 192 of 527
(1,584 Views)

two questions

First one relates to addressing I need address range in 6656 (1A00 Hex) that range does not seem to work.  

Secondly does the slave process support multiple instances?  I have need two slave processes to two unique devices

0 Kudos
Message 193 of 527
(1,585 Views)

Hi Scott,

First, why does the address 6656 not seem to work?

As for multiple instances, the slaves do support multiple instances but the specifics depend on the interface. For example, you cannot use multiple TCP slaves on the same port. Similarly, you cannot have two serial slaves on the same COM port. On the other hand you can have many slaves on different ports or visa interfaces. What specifically are you trying to accomplish?

Thanks,

Daniel

0 Kudos
Message 194 of 527
(1,585 Views)

the address 6656 when I write to the slave at that address the master does not get it.  If I cut the addresses down to start at 56 master receivers all the info from the master. 

as to the question of multiple slave processes each would have their own port# but would be supporting different devices.

0 Kudos
Message 195 of 527
(1,585 Views)

As long as they have different port numbers thats fine.

I'm still not sure what you mean about register 6656. Is an error occuring? Is the slave a third-party device or is it using the modbus library. What about the master? On my end I've just quickly modified the example code which is included in the package to access register 6656 on both the slave and the master, and data transmits just fine. can you give me a better idea of what is going on?

Thanks,

Daniel

0 Kudos
Message 196 of 527
(1,585 Views)

Hey Smithd,

I posted a (long) while ago with a question (post 104) regarding an error opening a port. Your answer about the time-wait state was succinct, and, as far as I can tell, spot on. Thanks and sorry for not posting again sooner, I've been distracted. I think I was causing this error in testing, and the circumstances won't occur in "reality."

You mentioned in your response (105):

smithd wrote:

For (2), I need to have a better grasp of your goals. For example, if you want to just make a gateway then it makes sense (potentially) to share data models across multiple slaves, or to create a network component which forwards messages from one device to another. If you are happy with the current solution this isn't necessary, but I don't want you to reinvent the wheel if its something easy to do in the code.

I am reading (cRio as Master) a bunch of data from a 485 serial device and intend to use that data in the cRio code for control and also pass it along to a TCP-modbus connected HMI (cRio as slave). I thought it would make the most sense to mantain all the same register addresses for the serial device, and use a dedicated TCP port to kind of emulate ("mirror"?) the serial device for the HMI. So I did this in a sort of time consuming, wire all the read functions to all the write functions way.

Your post made me think that there's an easier way. I'm a noob, sorry; can you elaborate on sharing data models or creating the network component to forward messages? That sounds like exactly what I want. The code I've written takes a little while to execute; maybe if I were only reading and writing relevant things at a given time it could be sped up.

Here's the .vi that I do the seral read/tcp write in. This is for one of two serial devices connected.

By the way, thanks for your attention to this thread, I've been lurking since my other post and learned some valuable stuff.

0 Kudos
Message 197 of 527
(1,585 Views)

Hey Matteci,

So I've been thinking about how to do this most effectively and I don't think I've come up with a good solution. Of the two I proposed before:

-Sharing data models across multiple slaves is actually pretty straight forward, but wouldn't do what you want because it only works between slaves on the same machine. I've attached an example. (Right now it errors at the end--thats a shut-down issue, and I'll be adding it to the known issue list shortly).

-Using a passthrough data model would work for creating a simple gateway (tcp requests translated into 485), but it would only work for one slave and it would not give local access to the data in any sort of efficient way. In the example I attached, it basically just forwards any request to the TCP slave into a request on the internally contained modbus serial master. Thus if you had a HMI requesting registers 0->30 and then immediately afterwards needed that same data for control purposes, you would generate two serial requests..which would be pretty slow.

I've attached examples of each, but for your use case I believe, unfortunately, you are doing it the most reasonable way.

However, there are still things you can do to improve it (maybe).

First, I notice you are grouping access on the tcp side--thats good. However you are requesting relatively small chunks from your serial slave. You should be able to request chunks of data in size-125 increments, meaning that you could reduce the number of requests to around 8. Of everything you're doing, this is probably the most expensive. The round-trip time for a serial request is usually pretty long, just due to the technology and how the packets work--with RTU, you must wait at least 2 ms on each end just to see if the other device is going to keep talking. In reality, it ends up being longer.

Second, I see a place where you can pretty easily clean up the code and make it easier to modify...potentially. I've attached this as DSMOD. Its just a sample, nothing too in depth. Basic idea is you give your code a spec and it follows the spec for transfer. Then, once you have it in your local TCP slave, its very fast to access the data (basically it just accesses a Data Value Reference and takes a subset of the register array), so you can directly access just the elements you need.

Finally, I have something that *may* help, depending on what the rest of your control code does. You could use the CVT to directly access the data you need by name once its been read from your serial device. This is exactly equivalent to directly accessing your TCP slave--it should have similar performance--except that the CVT lets you access data by name. I've also attached an example of this. Basic idea is that you create a tag for each register and then you can access it by name. Again, how useful this is will be determined by what the rest of your application does, but it was worth mentioning.

Thanks,

Daniel

Message 198 of 527
(1,585 Views)

Smithd,

Thank you for these pointers and examples. I like the idea of making the specification to handle the groups of reads/writes, that sure makes fewer wires to deal with. I'm still getting my feet wet with the CVT stuff but it looks useful too. My development has started with the SCM toolkit (formerly Drivven CalView) which does some similar stuff for me. I have a million little questions, I'll start with some. Hopefully they make sense and/or you can direct me to an alternative pool of knowledge.

I think I understand the data model concept. Seems like, in the example you write, it just allows the programmer to select specific registers to 'copy' from one slave instance to another. Do I have this right? I looked a little for something that would serve as a reference for this but couldn't find much. Can you refer me to some reading?

For the passthrough example: I understand that requests from the TCP device as well as the 'read' performed on the TCP slave instance will cause separate read events on the associated serial master. This still might end up working out better than what I've got because I don't necessarily use all the available registers in the control application (if I'm lucky, maybe just 60-some). I believe that the typical HMI will only request registers that are relevant to the current display, so total serial reads per iteration could be fewer. Is my thinking appropriate on this?

Finally, where do I go to find that passthru data model class? Is that secret sauce or should it be in my palatte somewhere?

Again, thanks, I'm already becoming more confident in implementing your library.

Edit: I wanted to add that the reason why I'm performing so many serial reads is that I can only successfully get <32 registers at a time out of the device I'm working with.

0 Kudos
Message 199 of 527
(1,585 Views)

Hey Matteci

Finally, where do I go to find that passthru data model class? Is that secret sauce or should it be in my palatte somewhere?

It should be in the zip I attached here https://decibel.ni.com/content/servlet/JiveServlet/download/72889-80443/passthru%20data%20model.zip

The parent class, Standard Data Model.lvclass is protected.

I think I understand the data model concept. Seems like, in the example you write, it just allows the programmer to select specific registers to 'copy' from one slave instance to another. Do I have this right? I looked a little for something that would serve as a reference for this but couldn't find much. Can you refer me to some reading?

So the data model concept comes from the spec, pg 6 here: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf

Its also discussed in varying levels of detail here:

http://www.ni.com/white-paper/52134/en/

The unifying concept, as I read it, is that a function code defines behavior. That is, the concept of a coil does not have to actually exist--its really just a response to a function code. That *can* be implemented as an array of booleans. Thats what Standard Data Model.lvclass does--it stores 4 arrays of data.

In the two files I sent you, there are two concepts at play. The first is just sharing the data model. Since I know the standard data model handles information by reference, it means that I can pass it to two modbus API instances and it will access the same data. That is, when the function code 6 is recieved, both slaves will do exactly the same thing on the same data shared data set.

The second concept is just fundamentally changing what the data model does. In the passthru model I sent you, it interprets function code 6 not as being "read my own data" but instead "read this other guy's data". To accomplish this, I used the contained modbus serial class.

<aside:> I noticed that my implementation is flawed because I don't implement "Execute Function.vi" in the data model. The easiest way to fix this is to change the inheritance of the passthrough class to inherit from standard data model rather than Device Data Model.lvclass (the parent data model).

To give you a general idea of what happens in the code, the networking layers get a request. They convert that request into a Modbus PDU (function code + data). They then pass this PDU to the data model and expect a PDU in response. The way they pass it to the data model is by calling execute function.vi:

exec.png

The parent data model class doesnt do anything--it just passes the PDU through.

The Standard Data Model will unpack the function code and pass that to the appropriate function.

f2.png

This function performs the guard logic defined by the standard and unpacks the request data. Is the request in range? Is everything in order? Does the size of the dataset make sense? Etc. Once these checks are performed, it calls the function which is associated with function code 2. That is, it calls Standard Data Model.lvclass::Read Discrete Inputs.vi.

This set of VIs is what you can override with your child class. That is, when this Execute Function.vi calls Read Discrete Inputs.vi, its really going to call PassThrough Data Model.lvclass::Read Discrete Inputs.vi, and inside of that function you can do whatever you want as long as you return an array of discrete inputs. In the standard data model, it just reads from an array:

readDI.png

You can see what it does instead in the passthrough data model--it pulls out a reference to a modbus object and simply forwards the request along.

The nice thing about doing it this way is that execute function is required for the networking, but when you are directly accessing slave data from the slave, you just call the exact same function--Read Discrete Inputs.vi. It works the same way, except without any of the guard logic required to be modbus compliant on the networking side.

Hopefully this makes some sense. Feel free to ask some more questions

For the passthrough example: I understand that requests from the TCP device as well as the 'read' performed on the TCP slave instance will cause separate read events on the associated serial master. This still might end up working out better than what I've got because I don't necessarily use all the available registers in the control application (if I'm lucky, maybe just 60-some). I believe that the typical HMI will only request registers that are relevant to the current display, so total serial reads per iteration could be fewer. Is my thinking appropriate on this?

You're definitely right about the operation. Each read function directly accesses the attached serial slave, no matter where the request originated, because they are both calling that same function described above. As for efficiency, you are *probably* right but I can't be sure. Some if your  UI is attached to a SCADA system like KepServerEx or NI OPC Servers, those guys tend to poll at a pre-defined rate on all channels. Technically the UI could enable or disable certain pieces, but itll depend on specific implementation.

Thanks,

Daniel

0 Kudos
Message 200 of 527
(1,597 Views)