NI Labs Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

NI LabVIEW Modbus API Discussion

Hey prose,

I could have sworn I replied to this. Oops. So, I definitely agree that its one big dependency (technically four, actually--one for coils, one for DI, one for registers, one for input registers...but I get your point). However, thats going to be more or less true of any global dataset in LabVIEW.

It is also true to say that using a variant attribute could work, I would caution against it for performance reasons (depending on the needs of your application). If you look into the CVT, you'll notice that I used a variant attribute to look up the index of the tag in the dataset, but kept the actual data separate. That allowed me to cache what was essentially a pointer and re-use it without paying the lookup penalty. It also allows for super-fast performance when needed, as you can look up all data indices when your program starts and just re-use them in your code.

To clarify, this is essentially what I was suggesting (for one of the options--the other was just copying everything as you say):

asdas.png

Instead of adding additional layers of indirection, the "holding register" values are replaced with clusters, and these clusters contain a data type, and a cvt index (or potentially N cvt indices, as for booleans or U8s, and potentially a length, if you wish to support fixed-size strings). It would probably also have a "valid?" flag, to make sure requests don't come on boundaries of multi-byte data like floats or singles. The point is, when the request comes in saying give me holding registers 0 through 44, you go to this lookup array and step through it element by element. As you step through this lookup array, you are actually reading from or writing to the CVT.

But yeah...if you only have 50 variables than I think most of these concerns are not a big deal. The action of reading or writing in the slave code is pretty fast (replace array subset or get array subset, basically), so I doubt you'll ever notice an issue with the data being locked for too long. Its also worth noting that a slave accessing its own data space is not a network operation and thus respects priority inheritence. That is, if you are reading from the slave interface in your timed loop, it will respect the fact that this is higher priority than the network listener loops.

Thanks,

Daniel

p-rose wrote:

I'm not too concerned about writing data to the modbus slave loop because the calling code can just send a message to the modbus loop and move on, it's a "set it and forget it" kind of operation. On the other hand, reading data out of the modbus slave data space from multiple places  presents a scalability challenge  because the entire application is throttled by how fast the modbus slave loop can process simultaneous requests. It's one giant dependency. Calling code has to wait to be serviced.

To try and decouple it, I need to copy the modbus variable data into a super fast mechanism that uses key (string)-value pairs so that I can quickly get a value by "tag name". Maybe a solution is to use a variant attribute lookup table in my custom data model's "execute function" vi. The details are still a bit fuzzy but I think it could work. I would have to use your unpack PDU vi's to give me the function code and start address. From that I would form a string to do a lookup on a variant attribute LUT which would return a cluster containing the "tag" name and data type (size inferred from the datatype). Then I'd have to get the new value from the PDU data or perhaps after calling the parent "execute function" and then shove it inside another variant LUT which resides inside of a FGV that I can use throughout my application to get the value by "tag" name. Obviously this would only work if my HMI only writes one value at a time.

Maybe your idea to update all variables on any write would be OK for me. I only have 50 or so variables at this point. That would certainly be easier.

I would be intersted in unifying the CVT and this library. I find myself needing these two technologies fairly regularly. It would be nice to combine the best of both worlds.

0 Kudos
Message 161 of 529
(4,287 Views)

Hi DaHelmut,

Thanks for the feedback. On the topic of Unit ID, thats a known issue with the documentation...its not technically incorrect, it just isn't very clear. What it is trying to say is that you can set the unit ID, but all of the code you're using (all the master code) will completely ignore it. It doesn't mean that the slave will ignore it. That is, setting the unit ID will do exactly what you want it to do

I believe I mentioned this previously in the thread but its faster to mention it than find it. The tcp wire is completely reference based, meaning that if you have N identical slaves on your rs485 network that you wish to communicate with, you can split the wire and everything should still work. The way I see this happening is like so:

asdfsafj.png

The create master initiates the connection, but you can create N copies of that connection (since it is by-reference) and assign a unit ID to each of the copies. There is more going on under the hood to make this work, but it should meet your needs.

Thanks,

Daniel

DaHelmut wrote:

Hi everybody,

I just played around with the Modbus API today and this is a very cool library.

It is a very intuitive API and very efficient.

However, in the case of using an Ethernet to RS485 gateway with several RS485 slaves chained together the TCP/IP Master is not usable because we cannot set the slave ID (Set Unit ID.vi) since it is ignored with a TCP/IP transmission data unit according to the VI documentation.

Is there a way to modify the library to take it into account ? Is there a workaround ?

My thoughts are that you created the TCP/IP Master to only adress one slave that is also a TCP/IP slave and not a gateway.

Thanks in advance for your answers.

Have a nice day !

Regards,

Message 162 of 529
(4,286 Views)

I am trying to implement this with the use of a Modbus TCP-RTU gateway;

Reading registers works, although I cannot write holding registers (both single and multiple doesnt work).

I do need the Unit ID to be passed along as I am using the gateway to RTU; works for sure with reading but as I cannot write I am guessing it may have to do with this. Will investigate:

-TCP connection is OK

-Reading works

-Timeouts etc. set correctly.

-Writing always gives an error.

Any clue? I made a super simple VI; opened a TCP master, and then writing, closing the master.

Error: Modbus Error: Illegal Data Address. The specified data address is not an allowable address for the server.

0 Kudos
Message 163 of 529
(4,286 Views)

Hey _Faust,

What is the error?

If the read is working correctly, then we know unit ID is being sent. The error will tell us more.

-Daniel

0 Kudos
Message 164 of 529
(4,286 Views)

It says Illegal Data Address (error message posted in my previous post).

That it sends the UNIT ID with read requests doesnt mean it also sends it for writes I think (different code?)

My read request fore node 4  is address 10, 1 register. I get a value returned nicely. When I try to write to this exact same address I get the errors.

The node address and timeouts are set before the function code 16 is called. As the  VIs are password protected I have no clue what is going onmodbus.png

0 Kudos
Message 165 of 529
(4,286 Views)

Ah, I didn't see that for some reason. I guess I was looking for an error code (538182 in this case). For codes over 538180, the error is not an error with the API but instead a modbus exception (pg 48 here: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf). Specifically, this exception (02) indicates:

"The data address received in the query is not an allowable address for the server. More specifically, the combination of reference number and transfer length is invalid. For a controller with 100 registers, the PDU addresses the first register as 0, and the last one as 99. If a request is submitted with a starting register address of 96 and a quantity of registers of 4, then this request will successfully operate (address-wise at least) on registers 96, 97, 98, 99. If a request is submitted with a starting register address of 96 and a quantity of registers of 5, then this request will fail with Exception Code 0x02 “Illegal Data Address” since it attempts to operate on registers 96, 97, 98, 99 and 100, and there is no register with address 100."

To be clear, this exception is an exception of the modbus PDU, not the ADU. The ADU is where the unit ID is transmitted. In addition, the exact same code would be used to attach unit ID to a read PDU as would be used to attach unit ID to a write PDU.

What I've seen in the past with gateways and some slaves is that they don't necessarily use all function codes, or block certain register ranges. Can you take a look at your manual and confirm that it supports the function code you're using, as well as the registers you are trying to request?

If your slave does support those, can you please attempt to do the same with the older modbus library (http://sine.ni.com/nips/cds/view/p/lang/en/nid/201711). If you get the same exception it indicates something to do with the slave. If you don't, it indicates something with the master class.

Thanks,

Daniel

_Faust wrote:

It says Illegal Data Address (error message posted in my previous post).

That it sends the UNIT ID with read requests doesnt mean it also sends it for writes I think (different code?)

My read request fore node 4  is address 10, 1 register. I get a value returned nicely. When I try to write to this exact same address I get the errors.

The node address and timeouts are set before the function code 16 is called. As the  VIs are password protected I have no clue what is going on

0 Kudos
Message 166 of 529
(4,286 Views)

It works fine with the older library which I am using for nearly all applicatioms. So I am sure the function code and address are right. Thanks for the suggestion though

0 Kudos
Message 167 of 529
(4,286 Views)

Can you give me a screenshot of the two blocks of code? I have both libraries on my machine and should be able to compare the generated code.

If you have the time, a wireshark capture would be very helpful as far as resolving the current problem and preventing future issues.

Thanks,

Daniel

_Faust wrote:

It works fine with the older library which I am using for nearly all applicatioms. So I am sure the function code and address are right. Thanks for the suggestion though

0 Kudos
Message 168 of 529
(4,286 Views)

Hello all !

I have a question about this library (which is very nice by the way !!), the last version, running on a VxWorks target (cRIO-9074) as SLAVE.

And a computer running the same library as MASTER.

Everything works fine, communication is OK, except some memory leaks (around 0.05MB) on the Real-Time Target (SLAVE) each time the MASTER disconnects and reconnect again.

Do you have any clue for this problem ? Any workaround ?

I think the problem is the same if the SLAVE is not a RT Target.

Thank you a lot in advance for your answer,

Best regards,

Jérôme Henrion | ONERA - Digital RF Engineer | CLA | CTA
0 Kudos
Message 169 of 529
(4,286 Views)

Hey Jerome,

Am I correct in saying that this is with TCP?

If so, there is a definite memory growth (but not leak) in that:

-LabVIEW is allocating space for each listener process. This should be reclaimed when the process exits, but LabVIEW does have weird rules about when it reaps memory.

-I allocate a DVR+error cluster and maybe a few other things to maintain history. That is, the TCP slave has a property node which indicates the status of the background Daemon and the number of connected clients. Rather than removing connections from the list immediately, there is a buffer of 50 or 100 of these connection status references I maintain. If we reach that maximum number they start getting deleted.

Edit: I made some tests and its definitely larger than what I described above. I'm investigating.

0 Kudos
Message 170 of 529
(4,286 Views)