Skip to Navigation

Reply to comment

Modbus

Modbus Software

The LJSocket page catalogs our experience connecting to Modbus software. We list of some of the quirks we’ve seen, e.g., a zero-based offset vs. one-based offset.

Examples of Modbus communication in LabVIEW are attached to the bottom of this page. These work for direct Modbus communication to the UE9, or Modbus communication over USB using LJSocket.

Modbus Protocol

LabJack devices implement the Modbus TCP protocol. For USB devices such as the U3 and U6, use LJSocket to provide a TCP socket interface. Without LJSocket, there is no COTS software we know of that can communicate using Modbus TCP over the LabJack’s USB interface.

Only Modbus functions 3 (Read Multiple), 4 (Read One), 6 (Write One), and 16 (Write Multiple) are supported. In COTS software this often means specifying “Holding” registers.

On the U3/U6/UE9, when a Modbus command is sent by USB the low-level packet must have 2 zeros appended to the front.  This is how the U3/U6/UE9 knows that the packet (after the 2 zeros) is Modbus, and not the normal low-level protocol.  The response does not have anything added and is pure Modbus.  This is generally handled automatically at higher levels (e.g. LJSocket adds the zeros).  SkyMotes only speak Modbus, so commands should always be pure Modbus with no added zeros.

Ethernet Modbus packets on the UE9 are limited to 256 bytes.  USB Modbus packets on the UE9 are limited to 256 bytes, including the 2 zeros appended to the command.  USB Modbus packets on the U3 and U6 are limited to 64 bytes, including the 2 zeros appended to the command.  SkyMote Modbus packets over USB or Ethernet are limited to 64 bytes.

While the Modbus website goes into more detail, here are the bytes the LabJack devices expect:

 

If developing your own software, functions 3 (Read Multiple Registers) and 16 (Write Multiple Registers) are all that is needed, so here is more detail about those:

Read Multiple Registers (function #3)

Command [# Bytes = 12]
Bytes 0-1:  0-65535 (Transaction ID, echoed by device)
Bytes 2-3: 0 (Protocol ID)
Byte 4:  0 (MSB of Length)
Byte 5:  6 (LSB of Length)
Byte 6:  0-254 (Unit ID, echoed on U3/U6/UE9, 0 for SkyMote bridge, 1-254 for mote)
Byte 7:  3 (function #)
Bytes 8 & 9:  0-65535 (MSB and LSB, respectively, of starting register address)
Byte 10:  0 (MSB of #Registers)
Byte 11:  1-27 or 1-123 (LSB of #Registers, # of registers to read)

Response [# Bytes = 9 + 2*#Registers, max is 64 or 256]
Bytes 0-3:  Echo of command bytes 0-3 (Transaction ID and Protocol ID)
Byte 4:  0 (MSB of Length)
Byte 5:  3 + 2*#Registers (LSB of Length)
Byte 6:  Unit ID
Byte 7:  3 (function #)
Bytes 8:  2-54 or 2-246 (2*#Registers)
Bytes 9+:  Data

Write Multiple Registers (function #16)

Command [# Bytes = 13 + 2*#Registers, max is 62/64 or 254/256]
Bytes 0-1:  0-65535 (Transaction ID, echoed by device)
Bytes 2-3: 0 (Protocol ID)
Byte 4:  0 (MSB of Length)
Byte 5:  7 + 2*#Registers (LSB of Length)
Byte 6:  0-254 (Unit ID, echoed on U3/U6/UE9, 0 for SkyMote bridge, 1-254 for mote)
Byte 7:  16 (function #)
Bytes 8 & 9:  0-65535 (MSB and LSB, respectively, of starting register address)
Byte 10:  0 (MSB of #Registers)
Byte 11:  1-24/25 or 1-120/121 (LSB of #Registers, # of registers to write)
Bytes 12:  2-48/50 or 2-240/242 (2*#Registers)
Bytes 13+:  Data

Response [# Bytes = 12]
Bytes 0-3:  Echo of command bytes 0-3 (Transaction ID and Protocol ID)
Byte 4:  0 (MSB of Length)
Byte 5:  6 (LSB of Length)
Byte 6:  Unit ID
Byte 7:  16 (function #)
Bytes 8 & 9:  0-65535 (MSB and LSB, respectively, of starting register address)
Byte 10:  0 (MSB of #Registers)
Byte 11:  1-24/25 or 1-120/121 (LSB of #Registers, # of registers to write)

 

Example

Here is an example LabJackPython session that communicates with a U6 connected over USB. Debugging is turned on to show the bytes sent and received. These are done directly over USB, not through LJSocket, so the outgoing packets have 2 extra zeros in front that tell the USB device this is a Modbus packet.

>>> import u6
>>> d = u6.U6()
>>> d.debug = True
>>> d.readRegister(0)
Sent: [0x0, 0x0, 0xa6, 0x3f, 0x0, 0x0, 0x0, 0x6, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2]
Response: [0xa6, 0x3f, 0x0, 0x0, 0x0, 0x7, 0x0, 0x3, 0x4, 0xb8, 0xf5, 0x70, 0x0]
-0.00011703372001647949

The Python code reads from register 0, which the map below states is AIN0. Here’s how to read from AIN1:

>>> d.readRegister(2)
Sent: [0x0, 0x0, 0xa6, 0x40, 0x0, 0x0, 0x0, 0x6, 0x0, 0x3, 0x0, 0x2, 0x0, 0x2]
Response: [0xa6, 0x40, 0x0, 0x0, 0x0, 0x7, 0x0, 0x3, 0x4, 0x40, 0x9d, 0x94, 0xfc]
4.9244365692138672

AIN1 is at register 2 because each analog input takes 2 registers (32-bits). The Modbus map below lists how many registers each address requires in the “Min Regs” column. Here’s how to read AIN0, AIN1, AIN2, and AIN3 at the same time:

>>> d.readRegister(0, numReg = 8)
Sent: [0x0, 0x0, 0xa6, 0x41, 0x0, 0x0, 0x0, 0x6, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8]
Response: [0xa6, 0x41, 0x0, 0x0, 0x0, 0x13, 0x0, 0x3, 0x10, 0xb8, 0xee, 0xe0, 0x0, 0x40, 0x9d, 0xa7, 0xbe, 0x3f, 0x3, 0x84, 0x62, 0x3f, 0x16, 0x24, 0xe8]
[-0.00011390447616577148, 4.9267263412475586, 0.51373875141143799, 0.58650064468383789]

Because the addresses (0, 2, 6, and 8) are all consecutive, we can request 8 registers starting at address 0. The four floating point values are returned as a sequence of 16 bytes, and LabJackPython (and other Modbus software) knows how to recombine them.

Here’s how to set DAC0 to 3.7 V.

>>> d.writeRegister(5000, 3.7)
Sent: [0x0, 0x0, 0xa6, 0x42, 0x0, 0x0, 0x0, 0xb, 0x0, 0x10, 0x13, 0x88, 0x0, 0x2, 0x4, 0x40, 0x6c, 0xcc, 0xcd]
Response: [0xa6, 0x42, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x13, 0x88, 0x0, 0x2]
3.7000000000000002

We’ve wired DAC0 to AIN1 so that we can read it back:

>>> d.readRegister(2)
Sent: [0x0, 0x0, 0xa6, 0x43, 0x0, 0x0, 0x0, 0x6, 0x0, 0x3, 0x0, 0x2, 0x0, 0x2]
Response: [0xa6, 0x43, 0x0, 0x0, 0x0, 0x7, 0x0, 0x3, 0x4, 0x40, 0x6c, 0x5d, 0x37]
3.6931893825531006

Modbus Map

We've made the latest Modbus map available as a spreadsheet (opens in new window). The spreadsheet may refer to alpha, beta, or unreleased firmware. Contact us for assistance.

In the columns marked “Read” and “Write”, we indicate the progress we’ve made with the following legend:

  • x means that the register is fully implemented and tested to the satisfaction of our team.
  • n means that the register is implemented, but not fully tested to the satisfaction of our team.
  • f means that the register won’t throw an error, but isn’t implemented. This is most commonly used for features that devices don’t offer. For example, the UE9/U6 doesn’t have FIOs that can become Analog, so the FIO Analog registers don’t do anything on the UE9/U6.
  • If the space is blank, then it means it isn’t implemented, and will throw a Modbus error if you try to access it.

    The “Min Regs” column indicates if you need to read more than one register. Each register is 16 bits. For example, AIN readings are 32-bit floating point numbers, and so they must be read in increments of 2 16-bit registers.

File attachment: 
tags:

Reply

Totally secret. Used only if you want to be notified of new comments.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <p> <br>
  • Lines and paragraphs break automatically.

More information about formatting options