Jump to content
Porter

[LVTN] Plasmionique Modbus Master

Recommended Posts

I like that you decided to simplify the RTU serial read...I was stubborn and wanted to follow the spec, I think that was a mistake. Serial APIs have come too far since RTU was created.

 

The visa lock thing is neat, I didn't know that existed until recently. My only comment there is that if you went with a DVR for both you could put that in the parent class so you can't execute a query unless the parent lets you, which feels nicer to me. Probably not any real reason to do it.

 

I also like that you somewhat simplified the class hierarchy. I can tell you right now I've never seen anyone make a new ADU or implement a new network protocol, so simplifying things on that front is great. The places I've seen extension are adding new function codes or changing slave behavior (which obviously doesn't apply here).

 

A few other thoughts from that perspective:

-I'd recommend pulling the PDU out of the ADU class and instead make the PDU a parameter of TX/RX and an output of RX. This is just a style thing of course but it makes the code easier to comprehend and for people adding new function codes its clearer where the data is going and what the library is doing with it.

-I'd just make the PDU a typedef cluster. I went back and forth on this and ended up with the class, but I think that was a mistake. It doesn't need extension and doesn't need encapsulation, so..

-Pulling the individual 'build write...'  and 'interpret response' functions out of the PDU class or library would, I think, make it feel like more of a level playing field for people adding new functionality. Like, it makes it clear that the function codes are happily mutable while in contrast you really shouldn't be touching "TX ADU". More importantly, it makes it less likely that someone adding a custom function code just adds it to your class and then saves it, making their version incompatible with the baseline.

Edited by smithd
  • Like 1

Share this post


Link to post
Share on other sites

Thanks for taking the time for review my code!

I was originally thinking about doing something fancy with the PDU inside of the ADU but decided that it was better to let the PDU deal with itself. I agree there is no reason to have it in the ADU class.

The simplification that I made to the RTU serial read was to cheat by having the "build PDU request" function calculate the expected number of data bytes of the PDU response. This way the RTU serial read procedure doesn't depend on timeouts or the function code. The downside of this is that when implementing new function codes, you need to remember to set the NumBytes parameter of the PDU request properly otherwise RTU serial read will not produce the expected response. This is why I was tempted to encapsulate the PDU in a class instead of a cluster.

However, to make it easier to add functionality, I agree that it's a better idea make the PDU a cluster. Implementing a new function code is simply a matter of creating a VI that calls Querry.vi with the correct parameters.

I've prepared an update with the following changes:

  • Converted MB_PDU class to cluster
  • Removed RTU inter-frame wait time optimization in TX ADU. It may have caused problems when sharing the serial port.
  • Renamed NumBytes of MB_PDU to RTU_DataBytes
  • Split interpret response VI of PDU into 3 VIs to handle different message types
  • Set build and interpret VIs of PDU to private
  • Removed function VIs from MB_Master class
  • Removed PDU_req and PDU_resp from the MB_ADU class
  • Like 1

Share this post


Link to post
Share on other sites

Hi Porter,

Very nice library you put together.  One quick question.  Is there any reason the RX/TX data and timestamps are not available as a Read Property from parent Modbus Master Class.  In situations where a device could be either a serial or tcp modbus, it would be nice to have access to that information at the parent level since it appears (unless I'm missing something) that the data is common to both the TCP and Serial Class.

Brian

  • Like 1

Share this post


Link to post
Share on other sites

During development I was using the RX/TX data and timestamps for debugging purposes only. I didn't really intend to expose them to the top level. I will look into creating accessors for them in the parent modbus master class. I'd rather not have the parent ADU class in the parent modbus master class though.

Share this post


Link to post
Share on other sites

OK. I've worked it out. Modbus Master now has a dynamic dispatch accessor for ADU and Session Valid. I'm going to make an example for a TCP/Serial device this weekend and post an update.

  • Like 2

Share this post


Link to post
Share on other sites

New version uploaded (V1.0.4). Now supports function codes with responses of unpredictable length in RTU mode (other modes already supported this). In RTU mode, if the RTU_DataBytes is set to less than zero, RX_ADU.vi expects that, in the response, the byte after function code is the number of data bytes. However, if the function code is 43, Modbus encapsulated interface transport message format is assumed. For now, only MEI type 14 is properly supported. Other MEI types rely on a timeout of 200 ms to determine the end of message.

Share this post


Link to post
Share on other sites

Version 1.0.5 uploaded. Does anyone know the reason why Byte Count of Read FIFO Queue (function code 24) is 2 bytes long?

Share this post


Link to post
Share on other sites

Perhaps they had the intention of allowing longer length responses at some point. According to the spec, Read FIFO Queue is limited to a maximum byte count of 64. The MSB of byte count will always be zero. This is just a little annoying though since the byte count parameter for any other function code is only 1 byte long.

Share this post


Link to post
Share on other sites

Hi Porter

Great work, this looks excellent, thanks for sharing it.

I am currently writing a development application to test an embedded device where we use Modbus to externalise values. Due to memory / power constraints we are probably going to create a custom Modbus version where the transmission protocol is RTU, but we want to use the LRC (ASCII) to check the transmission contents. It looks like I can do this using your library by adding another type of ADU class, inheriting from MB_ADU.lvclass. I have not used classes much at all yet, so I would be grateful if you could tell me whether I am on the right track.

Thanks


Ray

Share this post


Link to post
Share on other sites

You can create a copy of the MB_ADU_RTU class and replace the Calc CRC-16 with whatever check that you want to do. You will probably also need to modify the RX ADU and TX ADU. After that, you should create a copy of MB_Master_Serial class and modify Open Serial Session to use your new MB_ADU_RTU class as its ADU.

You can then use the function code vis on your new MB_Master_Serial class to communicate with your slave device.

Let me know if you come up with a better way of doing this. I don't like creating copies of classes but this avoids modifying the original library.

Edited by Porter

Share this post


Link to post
Share on other sites

Thanks Porter, I did not want to alter the original library either. I will implement this and report back.

 

Thanks for the assistance.

Share this post


Link to post
Share on other sites

Version 1.2.1 uploaded. It attempts to resolve a bug that we found in the VISA locking behavior implemented in MB_Master_Serial. See this forum topic for more information: https://lavag.org/topic/19871-visa-lock-behavior/

This version also adds a 10ms delay between retries in MB_Master_Serial "Querry.vi".

Share this post


Link to post
Share on other sites

Hello.

I am trying to use this API. My first attempt in my personal computer was succes (serial RTU with PLC Panasonic) with MB_Master Comm Tester.vi example.

Now I am triying to do the same in a laptop (same windows 10, same PLC, same wire, same Modbus configuration, same everything...) but it gives me an error when I click on "open device" and it says me that he could not open the device...

The error is -1073807246

Any ideas to solve it?

Thanks a lot

Share this post


Link to post
Share on other sites
16 hours ago, Porter said:

I haven't run into this error yet with this Library. It seems to be generated when VISA can not access the serial port.

http://digital.ni.com/public.nsf/allkb/6807113B057FDE4C86256B41008212ED

Sounds like the first thing to try is close LabVIEW and reboot the computer.

Hello. Thanks for replying. I fixed that error. It seems that another program was using the COM.

Now I have 3 questions more and any help will be apprecciate it!:

1 - Before using this modbus Library I was using ni OPC Server and program run fine. Now I have an issue update rate with this library. I have a while and inside I have an event structure. In timeout event structure I read a holding register each 10ms (timeout=10ms). In some iterations for any reason Labview or modbus can not read the holding register (maybe once per 8 iterations). So Is there any way to solve this? Any update rate? or change time of timeout?

2 - With ni OPC server was easy to read a Real data type (unsigned 32 bits). Now I would like to read DDT8 (double word 32 bits) so I can not read just holding register addres 0x8 because it has just 16bits and I need 32bits. So I need to read 0x8 and 0x9 to form 32 bits. But how can I join those registers? and then How can I pass them to real? Because for exame if number real data type is 1,00 then holding registers are: 0x8=0, 0x9=16256 . So... how can I pass those registers to real data type?

3 - When I run my application the first time It opens modbus session well. Then when I abort the applicacion and restar aggain it says me that "modbus session is invalid". I think that the problem is that my program does not close fine the session. So is there any way to close every sessions before open one? So the first thing that the program does will be to close every session modbus.

Config of my modbus session: baud rate=115200, stop bit = 1, parity = odd, timeout = 1000, retries = 1.

Thanks a lot.

Share this post


Link to post
Share on other sites

Please post your code, or send it to me via PM and I can take a look.

#1, it would be very useful to know what error is reported when the read fails.

#2, see attached. 0x8 is the low byte, 0x9 is the high-byte, the output is a single precision floating point number (32-bit)

#3, it is very important to run "Close Session.vi" before exiting the program. This releases the lock on the serial port.

Join registers to float.vi

Share this post


Link to post
Share on other sites
On 31/1/2017 at 7:07 PM, Porter said:

Please post your code, or send it to me via PM and I can take a look.

#1, it would be very useful to know what error is reported when the read fails.

#2, see attached. 0x8 is the low byte, 0x9 is the high-byte, the output is a single precision floating point number (32-bit)

#3, it is very important to run "Close Session.vi" before exiting the program. This releases the lock on the serial port.

Join registers to float.vi

Thanks for replying.

1 - I've fixed the problem. Just I create a while to read every critic input each 10ms.

2 - Thanks a lot. It works.

3 - I am trying to fix this.

Thanks again.

Share this post


Link to post
Share on other sites

For #3, check the "MB_Master Simple Serial.vi" example located in "<LabVIEW>\examples\Plasmionique\MB Master\" or search for it in the Example Finder.

Share this post


Link to post
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.

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...

Important Information

By using this site, you agree to our Terms of Use.