LabJackPython for UD, Exodriver, U12 - Windows, Mac, Linux | LabJack
« Close

Datasheets and User Guides

App Notes

Software & Driver


LabJackPython for UD, Exodriver, U12 - Windows, Mac, Linux

LabJack Python Overview

LabJackPython is our cross-platform Python module for communicating with the LabJack U3, U6, UE9, and U12. It works on Windows with the UD Driver and U12 Driver, and on Linux and macOS with the Exodriver. For the LJM library (T7, T4, and Digit), use Python_LJM.


For LabJackPython examples, please see the Examples directory in the LabJackPython download, or see on GitHub:


The latest release of LabJackPython is 2.0.4 from September 9, 2020: Download the 2.0.4 release of LabJackPython.

For the latest development version, archived versions and code repository, visit LabJackPython on GitHub.

LabJackPython 2.0.4 is available using pip via PyPI.


  • Python 2.6, 2.7 and 3.x for the latest LabJackPython release.
  • Install the appropriate driver:
  • If using Modbus, make sure your LabJack meets the minimum firmware requirement mentioned in the Modbus Support table on the UD Modbus page.


To control UD devices using Python, the LabJackPython Python package and the UD Driver or Exodriver must be installed.

if pip is available, the latest LabJackPython package can be downloaded by navigating to a command line and running:

python -m pip install LabJackPython


pip install LabJackPython

LabJackPython can also be downloaded from our source archives. Unzip the file and open a terminal/command prompt. In the terminal, use the command line to go to the unzipped LabJackPython directory (e.g., “cd Desktop/LabJackPython”). Then run one of the following commands to install the LabJackPython modules:


python install

Linux and macOS

$ sudo python install

Note the LabJackPython zip file and unzipped directory name will vary on the download.

Windows Troubleshooting

If running the above command causes a "'python' is not recognized as an internal or external command, operable program or batch file." or similar error, that means either Python is not installed on your computer or the Python executable's path was not added to the Windows user or system path. To help resolve this issue, please run the Python Windows installer and in the "Customize Python" window, click the "Add python.exe to Path" option towards the bottom and select "Will be installed on local hard drive".

Then finish the Python installation. Open a new terminal/console window and then go through our LabJackPython installation instructions again.

Alternatively, after Python is installed you can run the Python executable from its path. For example:

C:\Python27\python.exe install

Note that the above command uses the Python 2.7 path, and you should use the correct path on your computer which may differ.

Another option is to manually add the Python executable's path to the Windows path as described in the Python documentation.

Known Issues

macOS: As of macOS 10.15, opening a U6 the first time after plugging it in sometimes fails. Subsequent attempts to open succeed.


For documentation, please refer to the docstrings in the source code or use the help function on the module, class, or method.

For the U3, refer to its source, or use the “help(u3)” call in Python (“import u3” beforehand).

For the U6, refer to its source, or use the “help(u6)” call in Python (“import u6” beforehand).

For the UE9, refer to its source, or use the “help(ue9)” call in Python (“import ue9” beforehand).

For the U12, refer to its source, or use the “help(u12)” call in Python (“import u12” beforehand).


LabJackPython provides both a Modbus and low-level command interface for performing operations on a LabJack device. Quickstarts can be found for both in the Modbus Quickstart and Low-level Commands Quickstart sections.


File Attachment: 


Error when I tried to set DAC0:

>>> DAC0_REGISTER = 5000
>>> d.writeRegister(DAC0_REGISTER, 1.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.6/dist-packages/", line 409, in writeRegister
    return self._parseWriteRegisterResponse(response, pkt, value)
  File "/usr/local/lib/python2.6/dist-packages/", line 465, in _parseWriteRegisterResponse
    if request[7] != response[7]:
IndexError: list index out of range

Which device do you have?  My first guess is that perhaps you need newer firmware.

I have a U3. Thank you, I believe you are correct. I was able to update the firmware using Windows and the device is now working in Linux.

Would be nice to have high level commands for EIO and CIO too.


Through the Feedback functions you are able to get EIO and CIO readings in a flexible, high-level way.  I call the Feedback functions high-level since you do not need to set up a low-level packet. 

Here are feedback examples for reading EIO0 on the U3, U6 and UE9:

d.getFeedback(u3.BitStateRead(IONumber = 8)) #U3

d.getFeedback(u6.BitStateRead(IONumber = 8)) #U6["EIOState"] & 0x01 #UE9

In the U3 class, there is a high-level digital function that will read EIO, MIO lines.  setFIOState and getFIOState will get/set EIO lines if you specify a fioNum of 8-15 (->EIO0-8), and CIO lines are 16-19 (->CIO0-4).  An example for reading EIO0:

d.getFIOState(8) #U3 only


Examples of LabJackPython for the UE9 would be helpful. The copious examples in C are very nice, but I get weary translating them to python, as my python experience is limited.

Thank you for your input.  Currently on our list of things to do is to improve our Python examples, or in the case of the UE9 have some.  I do not know when we will work on those, but in the meantime if you have questions feel free to ask.  Our forum is a good place for that.

I will point out that most low-level functions have an equivalent function in the UE9 class that handle the raw bytes of low-level functions and communications for you.  The UE9 class and methods are documented in the source, which can also be viewed with the "help(ue9.UE9)" call in Python.

Since this page may be a bit jumbled or confusing, which we plan to improve, I would like to clarify some things which may be helpful.  There are two protocols to interact with our devices.  One is Modbus and the other is the low-level functions. 

The Modbus protocol is discussed in the "Basic I/O: DAC0, AIN0, FIO4", "Going further with Modbus", and "Beyond Modbus: Low-level commands" sections.  The example code here can be used on a UE9, U3 and U6.  The "Modbus Map" link provides more in depth details on the protocol and UE9 firmware requirements.

The "Low-level Commands" section discusses the U3 Python class and its methods that use the low-level function protocol.  Along with the U6 and UE9 classes, whose methods are not all exactly the same, they provide a Python interface that handle most low-level functions for you.  They also provide helpful functions for sending/receiving the raw bytes of the low-level functions and converting the raw byte data to proper units.

Hi. Can you add just a small example about how to use the timers and counters for the U6? There isn't an example I can find in the collection of examples supplied with LabJackPython.

Take a look at the U3 examples in the Feedback low-level section of the U3 User's Guide.  In particular, sections,, and provide timer and counter examples.  These are U3 examples, but they should work on a U6 if you replace the U3 import and u3 class calls with U6/u6.  Also, for configIO the FIOAnalog parameter does not exist for the U6.

When we do the Python examples update I'll add a U6 timer/counter example, though there currently is no time frame for that.

I'm having troubles with LabJackPython on Windows.  In contrast, with TinyCore Linux (it's convenient) I have not yet had any issues.  So far I've been able to run some of the examples and feel that I'm making progress.

I have a netbook running Windows 7 and a desktop with XP that I can also use.  On the netbook I have ActiveState Python 2.6 and generic Python 2.7.2.  In following the quickstart, I can manually open, assign, and read from pins.  The following work as expected.

>>> import u3
>>> d = u3.U3()
>>> DAC0_REGISTER = 5000 >>> d.writeRegister(DAC0_REGISTER, 1.5) # Set DAC0 to 1.5
>>> AIN0_REGISTER = 0 >>> d.readRegister(AIN0_REGISTER) # Read from AIN0V

With Win7 I can run with Python under the command prompt but under idle the program misses the majority of the samples.  I guess idle is slow.

The example works under Linux but under Win7 I get this:

This program will attempt to generate a sine wave with a frequency of 10 Hz, upd
ating once every 0.005 seconds.
Opening LabJack... Done
Traceback (most recent call last):
  File "", line 86, in <module>
    signal.signal(signal.SIGALRM, dacs.handleSetDac)
AttributeError: 'module' object has no attribute 'SIGALRM'

Another time I saw a "LabJack already open" error but I used the task manager to kill the lingering process. 

In looking at the code for I see that it's expecting the number of channels as an argument, so that for Win7 for two channels I get:

>python 2
Time difference:  0:00:03.305000
Time per iteration:  0:00:00.003305
Time per iteration in millis:  3.305
Latest readings:  [1.4177564800000002, 0.280572816]

I hope this feedback helps.

With and IDLE, as you mentioned it looks like IDLE is slow.  Run in the Windows command prompt or remove the slow "print "Average of", len(r['AIN0']), "reading(s):", sum(r['AIN0'])/len(r['AIN0'])" print code and run IDLE.  That should take care of the missing packets.

As for the "AttributeError: 'module' object has no attribute 'SIGALRM'" exception, the signal module code requires Unix based systems to run.  In this case the SIGALRM signal is not available on Windows.  Refer to the top of the source code for more details.

#a simple script to track a couple of analog inputs.
#note that in "u3.U3()" the first u is small and second is capitalized. Easy to miss!

import u3, time
d = u3.U3()
d.configIO(FIOAnalog = 15)          #set lines 0-3 to analog input

for i in range(1,100):
        input0 = d.getAIN(0)
        input1 = d.getAIN(1)        
        print input0, input1        

Thank you for posting your script.  Here's a modified version of your script using the getFeedback method instead of getAIN.  It's not as simple, but is more efficient speed wise since it performs only one low-level command/response in the loop instead of the two performed by using two getAIN calls.


import u3, time

d = u3.U3()
d.configIO(FIOAnalog = 15)          #set lines 0-3 to analog input
feedbackArguments = [u3.AIN(0), u3.AIN(1)]
for i in range(1,100):
    ain0Bits, ain1Bits = d.getFeedback(feedbackArguments)
    #if using a U3-HV, set isLowVoltage = False
    input0 = d.binaryToCalibratedAnalogVoltage(ain0Bits, isLowVoltage = True)
    input1 = d.binaryToCalibratedAnalogVoltage(ain1Bits, isLowVoltage = True)
    print input0, input1

Thanks, that is more efficient. How would I modify this to work with the U6, which I just ordered? Is it as simple as changing each u3 or U3 to a u6 or U6?

Some of the modifications are just simple u3/U3 to u6/U6 changes, but the configIO call is not needed, you will want to use the U6.AIN24 class instead of U6.AIN and the binaryToCalibratedAnalogVoltage parameters differ.  Here's a quick example of performing +-10 V readings on AIN0 and AIN1:

import u6, time
d = u6.U6()
feedbackArguments = [u6.AIN24(PositiveChannel=0, ResolutionIndex=1, GainIndex=0), u6.AIN24(PositiveChannel=1, ResolutionIndex=1, GainIndex=0)]

for i in range(1,100):
    ain0Bits, ain1Bits = d.getFeedback(feedbackArguments)
    #Can also use d.binaryToCalibratedAnalogVoltage(0, ain0Bits), d.binaryToCalibratedAnalogVoltage(0, ain1Bits)
    input0 = d.binaryToCalibratedAnalogVoltage(gainIndex=0, bytesVoltage=ain0Bits, is16Bits=False)
    input1 = d.binaryToCalibratedAnalogVoltage(gainIndex=0, bytesVoltage=ain1Bits, is16Bits=False)
    print input0, input1


>>> import u6
>>> d = u6.U6()
>>> DAC0_VALUE = d.voltageToDACBits(1.5, dacNumber = 0, is16Bits = False)
>>> d.getFeedback(u6.DAC0_8(DAC0_VALUE))

Gives the following error: AttributeError: 'U6' object has no attribute 'voltageToDACBits'

So far as I can see, neither does the U3 module (not that it'd be of use since I have a U6 but I looked since the quickstart is written for U3). Is the quickstart written for different modules to those that were actually released or am I missing something obvious (I'm fairly new to Python)?

I updated the U6 class to include the voltageToDACBits function since it is useful and should of been there in the first place.  You can download the latest LabJackPython on github.  The code you posted will work with it.  For documentation look at the source code or use the help function built in Python (help(u6.U6) for U6 documentation).

As for the voltageToDACBits in the U3 class, it is there.  If you find a conflict with the quickstart and current releases let us know and we will take a look at it.

Thanks for the previous response. I've also noticed the U6 getTemperature() method doesn't work. I have the latest package from github installed. It causes an AttributeError: 'U6' object has no attribute 'caliInfo'

This is fixed now.  It was actually a bug in the U6.getCalibrationData function that slipped by me in yesterday's update.  "caliInfo" should of been "calInfo".  Thanks for informing us.


>>> DAC0_REGISTER=5000

>>> d.writeRegister(DAC0_REGISTER, 1.5)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

  File "/usr/local/lib/python2.7/dist-packages/", line 435, in writeRegister

    return self._parseWriteRegisterResponse(response, pkt, value)

  File "/usr/local/lib/python2.7/dist-packages/", line 491, in _parseWriteRegisterResponse

    if request[7] != response[7]:

IndexError: list index out of range


My device is ue9-pro..CommFWVersion 1.40 and ControlFWVersion is 2.11..why still i cannot set DAC0 to 1.5V? 


Modbus over both USB and ethernet support was not available until Comm firmware version 1.48.  You are using version 1.40 which did not support Modbus yet, so that is the cause of the problem.  Try installing the newest beta Comm firmware, and see if that help.

How to communicate with multiple LabJacks connected on a system?

When opening your device with the class constructor or the open call, you can specify your LabJack with the serial or localId parameter.  The serial number is located on the back of your LabJack on the sticker, or can be retrieved by a configU3/configU6/commConfig U3/U6/UE9 class call after opening the device.  The local ID is user defined and stored on the LabJack.  It can be read and set with the same config call to get the serial number.  Here's a quick example opening two different LabJacks by serial number:

d1 = u3.U3(serial=320025017)

d2 = u6.U6(serial=360006724)

This is very useful, thanks a lot.

Hello, I got that error message after a while:


in _checkCommandBytes

    raise LabJackException("Got incorrect command bytes.\nExpected: %s\nGot: %s\nFull packet: %s" % (hexWithoutQuotes(commandBytes), hexWithoutQuotes(results[1:(size+1)]), hexWithoutQuotes(results)))

LabJackException: Got incorrect command bytes.

Expected: [0xf8, 0x5, 0x39]

Got: [0xf8, 0x2, 0x0]

Full packet: [0xfa, 0xf8, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x97, 0xb7, 0x4, 0x23] 

Is this my fault?


What LabJack device and operating system are you using?  Also, can you provide the chunk of your code that leads to this exception?

The problem which occurred only once might be attributed to the use of an TCP/IP connected U3 via Silex 1000 SX1000U?

Looking at the exception it looks like you are getting your error during a EI-1050, or some other Sensirion SHT1X sensor, reading.  Instead of the expected SHT1X low-level response you got a Feedback low-level response.  You may be getting a repeat response packet from the previous call (Feedback in this case) which over straight USB I have not encountered.  The cause may stem from the Silex 1000 SX1000U layer as you pointed out.  Check that your Silex 1000 SX1000U has the latest firmware for stability.  If this becomes a common occurrence, test your code with your U3 connected directly to your computer to see if the Silex device is causing the issue.

From the text above:

"First, before we perform any other operation we will get your LabJack device’s calibration data. [getCalibrationData()] only needs to be performed once after opening your device. The calibration data will be used by functions that convert binary data to voltage/temperature and vice versa."

Since calling getCalibrationData() should be done every time you first open a LabJack device before doing readings that convert binary data to temp/voltage values and because it also takes no arguments, why is it not part of the constructor?  Is there any reason you would not call this function?  I can understand that, if you were only doing digital IO, you would not need it however, having a separate call for this seems to imply that it is important to not call this function in some circumstances.  Is this true? i.e. Does calling this function change the behavior of the LabJack device if only doing digital IO?  Does it hurt to call this function every time a LabJack device is used?  If not, why have a separate call?

Using getCalibrationData is only necessary when using low-level commands and temperature/voltages.  Using Modbus (the first set of "Basic I/O" examples) you do not need to read the calibration.  It doesn't hurt to call this automatically, but considering it is only used for low-level commands we let the user perform the getCalibrationData call based on if they need it or not.


Python 2.7.3 (default, Apr 20 2012, 22:44:07) 

[GCC 4.6.3] on linux2

Type "help", "copyright", "credits" or "license" for more information.

>>> import ue9

>>> d=ue9.UE9()

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

  File "/usr/local/lib/python2.7/dist-packages/", line 68, in __init__**kargs)

  File "/usr/local/lib/python2.7/dist-packages/", line 87, in open, 9, Ethernet = ethernet, firstFound = firstFound, serial = serial, localId = localId, devNumber = devNumber, ipAddress = ipAddress, handleOnly = handleOnly, LJSocket = LJSocket)

  File "/usr/local/lib/python2.7/dist-packages/", line 600, in open

    d = openLabJack(devType, ct, firstFound = True, handleOnly = handleOnly, LJSocket = LJSocket)

  File "/usr/local/lib/python2.7/dist-packages/", line 1454, in openLabJack

    return _makeDeviceFromHandle(handle, deviceType)

  File "/usr/local/lib/python2.7/dist-packages/", line 1498, in _makeDeviceFromHandle

    raise e  

LabJackPython.LabJackException: Could only write 0 of 38 bytes.

Do you know what seems to be the problem? Normally I never get this kind of problem..this thing never occurred before..please help. 


Looks like a command is not getting through when opening your device.  You can try your "d=ue9.UE9()" call a couple of more times and see if that helps to clear the issue, otherwise power cycling your UE9 usually fixes most problems.  Let us know if this does not help.

I would need more information to figure out what caused the problem.  If this occurs again, let us know of any other calls you have made previous to this error.  If you ran a previous program/Python interactive mode session please provide the calls you made there.  Also, provide your UE9's Control and Comm firmware versions.

I think that the "try - except - finally" construct in Python is really useful for instrumentation applications. I am using it in my ue9 development activity, but I am not sure what exception codes to use in the "except:" clauses I write. There are standard Python exception classes, but I wonder - are there any LabJack specific exception codes available?  Thanks!


There is the LabJackException class that is thrown for LabJack specific exceptions.  It contains an errorCode and errorString.  The errorCode is a UD driver error code (Windows only), the errno set by the Exodriver (Linux/Mac OS X only), -1 for Python wrapper errors, or 0 for a nonspecific (default) error code number.  The errorString describes the error.  The LabJackException class can be found in the source code.


So, I've had luck getting the LabJack to control LEDs (on/off) when they are connected to ports FIO0 & FI01, using the following code I call "":


import u3, random

d = u3.U3()





for i in range(0, 100):


   d.writeRegister(FIO0_STATE_REGISTER, random.choice((0,1)))

    d.writeRegister(FIO1_STATE_REGISTER, random.choice((0,1)))



But now I want to use the 15-pin digital output port to send a signal (like a TTL pulse do a different device). I've tried using the same code, and changing the register numbers, but nothing seems to work. Any suggestions on how I should modify the code to send a signal out of the digital output port? If I install the software on the CD, I can generate a signal, so I know my hardware is working. It's just a question of finding the right Python syntax. 

Any help would be greatly appreciated, thanks in advance,

Josh Salmon

I tested the FIO, EIO and CIO digital lines using Modbus and they are working fine.  Writing to Modbus addresses 6000-6019 will set the digital output states, where addresses 6000-6007 correspond to FIO0-FIO7, 6008-6015 to EIO0-EIO7 and 6016-6019 to CIO0-CIO3.  So something like this in Python will set EIO1 (DB15 pin 12) to an output-high state:

d.writeRegister(EIO1_STATE_REGISTER, 1)

If you are still seeing problems, try updating your U3 to the latest firmware and see if that helps:

I have the same issue of #16:

Traceback (most recent call last):

File "C:\Python25\", line 11, in <module>

d.writeRegister(DAC0_REGISTER, 1.5) # Set DAC0 to 1.5 V

File "C:\Python27\lib\site-packages\", line 434, in writeRegister

return self._parseWriteRegisterResponse(response, pkt, value)

File "C:\Python27\lib\site-packages\", line 490, in _parseWriteRegisterResponse

if request[7] != response[7]:

IndexError: list index out of range



When I read the content in #17:

"Modbus over both USB and ethernet support was not available until Comm firmware version 1.48. You are using version 1.40 which did not support Modbus yet, so that is the cause of the problem. Try installing the newest beta Comm firmware, and see if that help."

I am not sure what "Comm firmware" is and how to upgrade it?



If you are running a 64-bit version of python and you get the following error:

<class 'LabJackPython.LabJackException'>: Could not load labjackud driver.

    The error was: [Error 126] The specified module could not be found

Please install the 64-bit driver install of the driver package located here:

For the developers, could you please correct the python library to reference 32-bit / 64-bit dlls properly? It appears if you have a 64-bit python and try to run the examples, python errors due to missing dlls because the driver installer only installs 32-bit dlls on a 64-bit windows system. 


This cannot be fixed in the Python library.  64-bit Python programs can only load 64-bit dlls and cannot load/reference 32-bit dlls.  So user's with 64-bit operating systems need 64-bit Python and the 64-bit UD driver, or 32-bit Python and the 32-bit UD driver.  We have however just updated our UD installer to install both 32 and 64-bit versions of the driver that should help with future cases of this.  This starts with UD installer version 3.32, which is currently (01/24/2013) is in beta:


I am trying to get the Python library work in Cygwin, running Python 2.7.3. Installation seems to work out fine, but when running one of the examples, the library tries to load the Exodriver, not the UD driver:

$ python2.7

<class 'LabJackPython.LabJackException'>: Could not load the Exodriver driver. Ethernet connectivity only.

Etc. Is there some way to force the setup to use UD instead?


It looks like Cygwin's Linux-like environment makes a Python module we use report the OS as "posix" instead of "nt". You can force LabJackPython to use the UD driver by modifying its source code. The dll/library load code is in the _loadLibrary function in the file but the Python call to detect the OS,, is used throughout the file. You can try to fake the call by removing the the "import os" import in and adding a class and global object after the imports like this:

class OS(object):
    def __init__(self): = "nt"

os = OS()

I did a quick test and that got the example running in Cygwin.

Got it running with the example code you provided, with the addition that I needed to change ctypes.windll to ctypes.cdll everywhere in


Glad to hear you got it running. In my Cygwin tests ctypes.windll was working. I only needed code changes to force to equal "nt".

Is there a way to set the mode of an analog in to the 0-3v range?  I didn't see that in the python API however, I may have missed it.

If using a UE9 or U6, the Feedback command provides a BipGain (UE9) or GainIndex (U6) option that sets the range when reading analog inputs, or using Modbus starting at address 1500 you can set the range. Section 2.7.2 of the UE9 User's Guide and section 2.6.2 of the U6 User's Guide documents gain and voltage ranges.

If using a U3, use the getAIN and set negChannel to 32 (special). This sets the range to 0-3.6 V or -10/+20 (HV). See section 2.6.2 of the U3 User's Guide for voltage ranges. Look at the U3.getAIN method's source code to see how the reading is done and section 2.6.1 describes what negative channel 32 does in the UD driver and the getAIN call.


I've started using LabJack U3-HV with LabJackPython. So I began reading here about LabJackPython and tried to use Low-Level Commands.

There is one thing that directed my attention: If I'am using d.getAIN(3), it results differ from the ones yielding by d.binaryToCalibratedAnalogVoltage(d.getFeedback(u3.AIN(3)) [0], isLowVoltage=False). (Besides fluctiuation from noise.)

This holds true after reaping calibration data. Using them uncalibrated they yield the same values.



  1. Putting a random chosen voltage on input 3.
  2. On python shell I'm typing:

>>> import u3
>>> d = u3.U3()
>>> for i in range (10): print d.binaryToCalibratedAnalogVoltage(d.getFeedback(u3.AIN(3)) [0], isLowVoltage=False), d.getAIN(3)
2.440864 2.440864
2.440864 2.440864
2.440864 2.440864
2.440864 2.440864
2.440864 2.440864
2.440864 2.440864
2.43584 2.43584
2.440864 2.440864
2.440864 2.43584
2.440864 2.43584

(There are some LSB-fluctuation due to noise.)

Let's continue:

>>> d.getCalibrationData()

(gives some output)

>>> for i in range (10): print d.binaryToCalibratedAnalogVoltage(d.getFeedback(u3.AIN(3)) [0], isLowVoltage=False), d.getAIN(3)
2.46680050949 2.46430067183
2.46680050949 2.46430067183
2.46680050949 2.46430067183
2.46680050949 2.46430067183
2.46171616996 2.46430067183
2.46171616996 2.4592151402
2.46680050949 2.46430067183
2.46171616996 2.46430067183
2.46680050949 2.46430067183
2.46680050949 2.4592151402

There are some LSB-fluctuation, too. It can be clearly seen that both columns benefit from calibration data, but not in the same way.


Thanks for any hints – Peter

In your d.binaryToCalibratedAnalogVoltage call, you will want to set the channelNumber parameter to 3. It defaults to 0, meaning currently you are using AIN0 calibration constants for AIN3. getAIN is returning the correctly calibrated reading.

I noticed the example on this page isn't demonstrating this, so I will update it.

Ok, that resolves it. The missing parameter led to my conclusion that there is only one set of calbration data for all channels. And because in my example the difference is roughly half an LSB-step (albeit not observed exactly) it led me to the conclusion that there must be something like a kind of undocumented rounding feature.

Thanks – Peter

How do I stop the LabJack (U6 in my case) from outputting a voltage once I call d.writeRegister(DAC0_REGISTER, 1.5)? Is it as simple as setting it to d.writeRegister(DAC0_REGISTER, 0.)?