Please enable JavaScript to view this site.

DAQFactory User's Guide

Navigation: 16 Serial and Ethernet Communications > 16.9 Predefined Protocols

16.9.5 Modbus RTU, ASCII and TCP protocol

Scroll Prev Top Next More

The Modbus protocol is actually made up of two similar protocols, ModbusASCII and ModbusRTU.  These protocols are almost identical except for the method used to communicate and the functions available.  ModbusASCII uses ASCII to pass the Modbus commands.  ModbusRTU uses binary.  Check your device to determine which mode it prefers.  Modbus TCP is sometimes ModbusRTU on Ethernet, but can also be true ModbusTCP, which is a slightly different protocol.  Since DAQFactory allows you to apply any protocol to both serial and Ethernet, you can do ModbusRTU (or ModbusASCII) protocol on an Ethernet port, but depending on your device, this may not be the ModbusTCP it needs.  For this you will need to use the ModbusTCP protocol.  The difference in the protocol is only a few header and tail bytes, so the two protocols appear the same in DAQFactory.  Simply select which one you need and all the rest of the information described below applies to both, though the examples describe ModbusRTU.  

In general, devices that have a serial port out and then go to a serial to Ethernet adapter will use RTU, while devices with an Ethernet port built in will use TCP.  Only a few older devices still use ModbusASCII.

One difference between the TCP and RTU drivers is that the RTU driver adds the SetDelay() function.  Some devices require a little downtime between requests and can't handle a continual barrage of queries.  This is especially true in a multidrop configuration.  To make this easy to implement, you can call SetDelay() with the number of milliseconds to delay after each request before allowing any communications on the port.  For example, Device.mydevice.SetDelay(50) would cause the driver to pause 50 milliseconds after every query before allowing any other communications on the port.  This was originally designed for the Watlow SD type temperature controllers, but we've found it useful for some other devices.  The typical indicator that a delay may be required is if you are reading non-sequential channels and the first channel reads without a problem, but the second channel times out.  A third channel would then read correctly because the timeout would create the necessary delay.

There are two ways within DAQFactory to communicate with your Modbus device.  You can use channels as you would other devices, or you can use device functions.  You can use a combination of methods as well.  Both methods require you to create a Communications Device as described earlier in this chapter.

Modbus Channels:

The channel method of acquiring data from your Modbus device is the easier method.  To create a Modbus channel, select your communications device, enter the ID of the unit you are trying to communicate with as the Device #, select the particular Modbus command from the I/O type, and select the Modbus memory address as the Channel #.  

Detail: Unfortunately some manufacturers 0 index their addresses and others use 40,001 type notation.  Internally, the Modbus packet itself uses 0 indexing.  The Modbus protocol driver will assume you are using 0 indexed addresses until you try and read from an address greater than 30,000, but less than 50,000.  Once you do this, it will assume you are using 40,001 type notation and subtract the appropriate value to send the correct request.  This will apply to all the devices using the protocol.  

In general you don't have to worry about this, unless you are only setting coils and output registers and still want to use the 1 indexed, 40,001 type notation.  In this case you will either need to read a single holding or input register with an address greater then 30,000 or manually adjust your tag numbers to 0 indexed.

Alas, some devices use 0 index notation, but number their registers up into the 30,000 and 40,000.  To force the driver into 0 index mode, read an address greater than 50,000. Then the driver will remain in 0 indexed mode even if you read a 30,000 or 40,000 address.

In the I/O type, the number in parenthesis refers to the Modbus command number used.  Since even the terminology of Modbus varies some between manufacturers, we have provided this to help keep things clear.

This driver allows you to either create a separate channel for read and write tags for a single address, or you can create a single read tag for each address and simply write to it to make it into a read/write tag.   Obviously, when writing to a read tag the Modbus command number indicated will be substituted for the appropriate output command number.  Also, you can only write to Holding registers and coils and not to input registers.  You also cannot write to the U16 data type.  Use S16 instead.

DAQFactory will automatically combine sequential channels with the same Timing and Offset into a single request to optimize the traffic on your comm port.  When doing floating or 32 bit values, it assumes sequential channel numbers are in steps of 2 (40,001, 40,003, etc.).  For the "skip 2" I/O types, it assumes sequential channel numbers are in steps of 3.

Example - reading a holding register once a second:

1. From a blank document, create a new Communication Device selecting ModbusRTU as the protocol.  Read the section on the creating a Comm Device for more information.  We'll assume for all these examples that you called your device MyDevice.

2. Click on CHANNELS: in the workspace to go to the channel table.  Click Add to add a new channel row.  In that new row, enter a channel name such as Register1.  Select MyDevice for the device type, then enter the ID of your device under D#.  For the I/O type, select Read Holding U16 (3).  You may have to expand the column to read it.  The U16 means "Unsigned 16 bit integer" and is the same as an unsigned word.  (3) means we are using Modbus command 3.  Under Chn #, enter the memory location for the register you would like to read.  

3. Click Apply to save your changes.  Since the channel you just created has a Timing parameter of 1.00, DAQFactory will immediately start polling that holding register once a second.

4. To quickly see the data, click on the + next to CHANNELS: in the workspace to expand the tree.  Then click on your new channel name.  This will display the channel view for this channel.  Click on the Table tab to see the data in tabular form.  You can now either add more channels, or go to the page and use page components to display your acquired data.

Example - forcing a coil:

1. From a blank document, create a new Communication Device selecting ModbusRTU as the protocol.  Read the section on the creating a Comm Device for more information.  We'll assume for all these examples that you called your device MyDevice.

2. Click on CHANNELS: in the workspace to go to the channel table.  Click Add to add a new channel row.  In that new row, enter a channel name such as Coil1.  Select MyDevice for the device type, then enter the ID of your device under D#.  For the I/O type, select Force Coil (5).  You may have to expand the column to read it.  Under Chn #, enter the memory location for the coil you would like to set.  

3. Click Apply to save your changes.  To quickly toggle the state of the coil, go to the Command/Alert window and type either: Coil1 = 0 or Coil1 = 1.   You should see your coil toggling between on and off.  You can now either add more channels, or go to the page and use page components to control your output coil.

I/O Types:

All of the types will read or output to a single memory location.  The number in parenthesis is the Modbus command used.  Many of the commands are identical and the difference is how the data is treated.  Here is a description of the codes used:

U16 : unsigned 16 bit word

S16 : signed 16 bit word

U32 : unsigned 32 bit double-word (long unsigned integer)

S32 : signed 32 bit double-word (long integer)

U32 R Words : unsigned 32 bit double-word with words reversed

S32 R Words : signed 32 bit double-word with words reversed

Float : 32 bit floating point value

Float R Words : 32 bit floating point value with words reversed

Modbus Functions:

This protocol includes functions accessible from sequences for more advanced control over your device.   This is especially useful when you want to optimize the writing of multiple consecutive values to your device as the channels themselves cannot optimize outputs, only inputs.

There are two basic formats for the device functions, input and output.  Input functions take the ID of the device you wish to communicate with, the starting address, and the number of points to read.  Input functions return an array of values corresponding to the data read.  Output functions also take the ID and starting address, but then take an array of values to set your memory locations to.  Output functions do not return anything.

To use these functions you must create a new Communication Device with the ModbusRTU as its protocol.  Read the section on the creating a Comm Device for more information.  We'll assume for all these examples that you called your device MyDevice.

Example - reading 8 holding register values: data = Device.MyDevice.ReadHoldingU16(32,10,8)  This will read 8 values from device ID #32 starting memory location 10 and store the result in the data memory variable.

Example - using the device function to pull blocks of data into channels.  This can also be done by simply setting all your channels to the same Timing / Offset.  The driver will automatically optimize the calls to your device.  But, as an example, you can also:

1. From a blank document, create a new Communication Device selecting ModbusRTU as the protocol.  Read the section on the creating a Comm Device for more information.  We'll assume for all these examples that you called your device MyDevice.

2. Click on CHANNELS: in the workspace to go to the channel table.  Click on the Add button to add a new channel row.  Give the row the name Register10.  You can of course use more appropriate names if you would like.  Set the Device Type to MyDevice.  Set the Timing column to 0.  This last step is very important otherwise DAQFactory will start to poll the device directly for this channel.  With the exception of the channel name, which must be unique, and the Timing, which must be set to 0, you can set the other channel parameters to anything.  We still recommend selecting a device type that matches your device.

3. Click on the Duplicate button 7 times to create 7 new channels based off of the one you just created.  DAQFactory will automatically give each the names: Register11, Register12, etc.  Click Apply to save your changes.

4. Right click on SEQUENCES: in the workspace and select Add Sequence to create a new sequence.  Call the new sequence GroupRead and click OK.  You will be placed in the sequence view ready to edit your sequence.  Here is the code to enter:

 

private data

while (1)

  data = Device.MyDevice.ReadHoldingU16(32,10,8)

  Register10.AddValue(data[0][0])

  Register11.AddValue(data[0][1])

  Register12.AddValue(data[0][2])

  Register13.AddValue(data[0][3])

  Register14.AddValue(data[0][4])

  Register15.AddValue(data[0][5])

  Register16.AddValue(data[0][6])`

  Register17.AddValue(data[0][7])

  delay(1)

endwhile

5. Click Apply to save your sequence.  Now click on the + sign next to SEQUENCES in the workspace to expand the tree, then right click on the GroupRead sequence and select Begin Sequence.

6. Now go to the channel view for any of your register channels and look at the table of the data coming in.  (click on the + next to CHANNELS:, then click on the channel name and select the Table tab).  The sequence loops once a second and reads 8 registers in a single call to your device.  The 8 Register statements in the middle add the newly read values to the channels you created earlier.

For those wondering why the data always has [0] after it, then [x] where x is the tag number: the first dimension of arrays in DAQFactory is always in time.  Since all these values were read at once they all have the same time so all are on the same row (first dimension).  Besides consistency, this allows you to create 2 dimensional arrays of data from multiple tags very easily.

Example - setting 8 coils:

 

Private outdata = {{1,0,0,1,0,0,0,1}}

Device.MyDevice.ForceMultipleCoils(32,20,outdata)

This will set 8 coils on device ID #32 starting at memory location 20.  The values will be on, off, off, on, off, off, off, and on.

Functions:

The functions available start with either ReadInput, ReadHolding, ReadCoil, ForceCoil or SetRegister and follow the same coding as I/O types described above.

Note: Once the data is within DAQFactory, it is treated as a 64 bit floating point value.