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.

Comments
#1
"Modbus is not supported for UE9's over USB. If you try it, a LabJackException is raised."
Can you explain more about this please.
Does it mean that i MUST use LJSocket? (i dont have any machine runs windows..)
#2
Where do you see it say that? USB support for the UE9 was added with Comm firmware version 1.48. With that you can send Modbus packets via USB using the same method as the other LabJack devices (padding with 0x00, 0x00).
#3
I would like to add that when using LabJackPython and the writeRegister, readRegister and similar Modbus functions, the padding is handled for you. If using the raw write and read functions to send/receive the Modbus packet, set the modbus parameter to true so it handles the padding for you.
#4
so i download the UE9comm_152_12152011.bin on beta firmware page..but how do i upgrade it on python 2.7? How to use the Python Firmware Upgrader that requires httplib2 from the Google code page?
#5
I updated the firmware page with a Python 2.7 version of the Firmware Upgrader. Try that out. Since you downloaded the file, you will want to use the "-f" flag to specify the UE9comm_152_12152011.bin file to upgrade with. Refer to the README file for full details on the all the flag options.
If you are having problems with installing httplib2, follow the "Google code page" which has the files and installation instructions.
If you have problems or questions relating to the Python Firmware Upgrader, please post comments on the firmware page.
#6
In the comment to the example below it is written: AIN2 is at register 2.In my opinion, it should be stated: AIN1 is at register 2.
Your example is included below:
>>> 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
AIN2 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:
#7
I agree. Looks like a typo/over-site. I'll get the webpage changed.