Low-level Commands Quickstart | LabJack
 
« Close

Datasheets and User Guides

App Notes

Software & Driver

 

Low-level Commands Quickstart

This quickstart uses a U3-HV, but the principles apply to all LabJack devices.

Opening the device

Here’s how to open a U3:

>>> import u3
>>> d = u3.U3()

The constructor for the U3 class will try to automatically open the first found U3. This is good if you only have one U3 connected.

Basic I/O: DAC0, AIN0, FIO4, FIO6

First, before we perform any other operation we will get your LabJack device’s calibration data. This 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.

>>> d.getCalibrationData()

Wire a jumper from DAC0 to AIN0, connect an LED from FIO4 to VS and jumper FIO6 to GND. Wire the anode (longer-lead) of the LED to VS and the cathode to FIO4. For more information on controlling LEDs, refer to the LED app note . The LED connection we are using is configuration 2 in the app note.

Setting the DAC is a U3 Feedback command, so we’ll use the U3 class getFeedback() function. For every Feedback command, there is a corresponding class in u3.py. The 8-bit DAC0 feedback command from Section 5.2.5.13 has a class named DAC0_8. Here’s how to use it:

>>> DAC0_VALUE = d.voltageToDACBits(1.5, dacNumber = 0, is16Bits = False)
>>> d.getFeedback(u3.DAC0_8(DAC0_VALUE))        # Set DAC0 to 1.5 V

As the documentation in Section 5.2.5.13 states, the 8-bit DAC0 feedback command takes a value between 0 and 255 (inclusive) and the output of the DAC is between 0 and 4.95 V. The variable DAC0_VALUE converts between the two. Reading from an analog input is also a feedback command. As Section 5.2.5.1 of the U3 User’s Guide states, the feedback command returns a 16-bit unsigned value that we must convert into a voltage. Here’s how to do that:

>>> ain0bits, = d.getFeedback(u3.AIN(0)) # Read from raw bits from AIN0
>>> print(ain0bits)
37584
>>> ainValue = d.binaryToCalibratedAnalogVoltage(ain0bits, isLowVoltage = False, channelNumber = 0)
>>> print(ainValue)
1.501376

Similar to setting DAC0, we use the getFeedback() function and the AIN class from the u3 module (u3.AIN). The getFeedback() function always returns a list (more on that later), and so we use a trailing comma after ain0bits:

>>> ain0bits, = d.getFeedback(u3.AIN(0))

to automatically unpack the first value in the list. The U3 class has a function binaryToCalibratedAnalogVoltage() that knows how to apply the proper calibration constants to convert the reading into a voltage. The function applies different constants based on whether we are using a U3-LV (default) or one of the high-voltage inputs on the U3-HV. When using a high-voltage input, we specify isLowVoltage = False. For the U3-HV, the channel number (channelNumber) also needs to be specified so the correct calibrations constants are applied.

Because reading an analog input is such a common operation, the U3 class provides a getAIN() function that calls getFeedback() and binaryToCalibratedAnalogVoltage() with the correct parameters:

>>> ainValue = d.getAIN(0)  # Read from AIN0 in one function
>>> print(ainValue)
1.501376

Setting a digital line’s state and direction is also done with feedback commands. The relevant ones are BitStateRead, BitStateWrite, BitDirRead, and BitDirWrite.

# Check which FIOs are analog and which are digital
>>> configDict = d.configIO()
>>> configDict["FIOAnalog"]
63
# Set the first four (0-3) to analog (15 = 1111 binary) and the rest to digital
>>> d.configIO(FIOAnalog = 15)
>>> d.getFeedback(u3.BitDirWrite(4, 1))   # Set FIO4 to digital output
[None]
>>> d.getFeedback(u3.BitStateWrite(4, 0)) # Set FIO4 to output low
[None]
>>> d.getFeedback(u3.BitStateWrite(4, 1)) # Set FIO4 to output high
[None]

Note the call to configIO(), which is not a feedback command, but a low-level command in Section 5.2.3. All the low-level commands in Section 5.2 are implemented as functions of the U3 class.

The getFeedback() function also accepts a list of feedback commands, and it accepts multiple arguments directly. For example, here’s how to set FIO4 for digital output and output high at the same time:

>>> outputDirCmd = u3.BitDirWrite(4, 1)
>>> outputHighCmd = u3.BitStateWrite(4, 1)
>>> cmdList = [outputDirCmd, outputHighCmd]
>>> d.getFeedback(cmdList) # Pass a list of commands
[None, None]
>>> d.getFeedback(outputDirCmd, outputHighCmd) # Or pass the commands as arguments
[None, None]

which is exactly what the setFIOState() convenience function does:

>>> d.setFIOState(4, 1) # Set FIO output high in one function
>>> d.setFIOState(4, 0) # Set FIO output low in one function

When you pass multiple feedback commands to getFeedback(), the function processes them sequentially. Here is an example of checking the direction of FIO4 (it’s set to output from above), set the FIO4 direction to input, and read FIO4 direction:

# Multiple feedback commands
#    Read FIO4's direction
#    Set FIO4's direction to input
#    Read FIO4's direction again
>>> d.getFeedback(u3.BitDirRead(4), u3.BitDirWrite(4, 0), u3.BitDirRead(4))
[1, None, 0]

Now lets set FIO6’s direction to input and read its state:

# Set FIO6's direction to input and read its state
>>> d.getFeedback(u3.BitDirWrite(6, 0), u3.BitStateRead(6))
[None, 0]
# Disconnect from GND
>>> d.getFeedback(u3.BitStateRead(6))
[1]
# Reconnect to GND
>>> d.getFeedback(u3.BitStateRead(6))
[0]

To get the actual state (first value of the list), don’t forget to unpack it:

>>> v, = d.getFeedback(u3.BitStateRead(6))
>>> v
0
>>> v = d.getFeedback(u3.BitStateRead(6))[0]
>>> v
0

which is exactly what the getFIOState() convenience function does:

>>> d.getFIOState(6)
0

We’ve recreated all the Modbus Quickstart (readRegister() and writeRegister()) operations on DAC0, AIN0, FIO4 and FIO6 using low-level commands. Many low-level commands are feedback commands, so they are passed into getFeedback(), which is a very flexible function that can perform many operations on your device at once. To go beyond the basic low-level operations (basic analog and digital), explore the other built-in commands and the LabJackPython source as described in the sections below.

Low-level commands as built-in functions

Section 5.2 of the U3 User’s Guide describes the low-level commands. All the low-level commands in the U3 User’s Guide have been defined as functions in the U3 LabJackPython class.

For example, we can get helpful information by calling the configU3() command. Note that in Section 5.2.2 of the U3 User’s Guide is a description of ConfigU3.

Here we are going to call configU3 with no arguments to perform only a read.

>>> print(d.configU3())
{
    'LocalID': 5, 
    'SerialNumber': 32003XXXX, 
    'DAC1Enable': 1,
    'EIODirection': 0,
    'DeviceName': 'U3-HV',
    ...
}

The configU3() function sets the power-up defaults of the U3. To change the U3’s behavior for this session only, use configIO(). For example, to set the first five FIO lines to analog for this session, call configIO with the FIOAnalog keyword argument:

>>> d.configIO(FIOAnalog = 0x1F)
{'DAC1Enable': 0, 'FIOAnalog': 31, 'EIOAnalog': 0, 'TimerCounterConfig': 64}

The value 0x1F is a bitmask with the lower 5 bits set to 1, signifying the first five FIOs. To make this setting survive power cycles, call configU3() instead of configIO():

>>> d.configU3(FIOAnalog = 0x1F)
{...
'FIOAnalog': 31,
...
}
>>> d.close()
>>> # Unplug the U3 and plug it back in
>>> d.open()
>>> d.configU3()
{...
'FIOAnalog': 31,
...
}

Examining the source

Some of the best low-level documentation of LabJackPython is in the docstrings of the source code. For example, the u3.py module has many examples for the low-level commands. The feedback commands are implemented as classes at the bottom of the module, and every feedback command has a docstring with an example of how to use it.

10 comments

Hello,

Could you please tell us how to use the PWM feature?  Is it supported in LJ Python?

Thank you...

Peter H.

Yes, it is supported in LabJackPython. Take a look at the PWM-looping.py looping example in the LabJackPython download. It demonstrates Modbus usage for for the UE9, U3 and U6, and low-level usage (commented out code) for the U3 and U6.

General timer/counter documentation is here:

http://labjack.com/support/ue9/users-guide/2.10
http://labjack.com/support/u3/users-guide/2.9
http://labjack.com/support/u6/users-guide/2.9

Note if using a U3 that the lines the timers/counters are on need to be configured for digital I/O.

plummer's picture

print labjackHere.Device1.configU3(EIODirection = 0xFF)

print labjackHere.Device1.configU3(CIODirection = 0xFF)

^^^^ above does NOT work to change two things

print labjackHere.Device1.configU3(EIODirection = 0xFF , CIODirection = 0xFF)

^^^ above DOES work to change two things.

See how I combined the two commands into one line. That works! 

LabJack Support's picture

For further clarification, configU3 (ConfigU3) is for power up settings and configIO (ConfigIO) is for current settings (no flash writes). These Python methods are using these low-level command-responses:

https://labjack.com/support/datasheets/u3/low-level-function-reference/c...
https://labjack.com/support/datasheets/u3/low-level-function-reference/c...

When setting the power up FIO, EIO, CIO direction and state with the configU3 method, and in general with the ConfigU3 low-level command, they need to be set in one command. The command either writes all or none of the digital I/O settings. If a set of directions or states are not set in the method call, the setting is defaulted to zero (input/low) in the low-level command.

Thank you for providing your code for others who may be confused about this functionality.

Kelvinmantey1's picture

Give them all the details (MacOS, psychopy and python version, labjack version (it’s a U3), which input port we are using (analog), our experimental setup (Medoc pathway to labjack to MacBook) and how we programmed the reading in our script. Explain the issues we are having, i.e. not all triggers start the task as supposed and sometimes it is triggered when it is not supposed to. Tell them what we tried to fix it, i.e. modify the triggers to make increase the chances of reading it and that it did not help. Ask them if our command to read it  is correct, or if we should use a different command.

Hello! We've encountered an issue we cannot seem to fix concerning reading triggers. Below is a list of information about our systems and programs. 

System: Mac OS

Psychopy version:

Python version: 3.7

Labjack : U3-HV

Input port: analog

Experimental setup: Medoc pathway -> Labjack -> Macbook

The code below was used to read the signal:

while True:
        #AIN0_REGISTER = 0
        #AIN0_STATE = d.readRegister(AIN0_REGISTER)
        #print(AIN0_STATE)
        ainValue = d.getAIN(0)  # Read from AIN0 in one function
        print(ainValue)
        print(time.clock())

The issue is that some triggers are not starting as they're supposed to. It triggers at times when it shouldn't. In trying to fix it, we tried modifying the triggers to increase the chances of it being read but that didn't work. Is the command right, or could we have done it a better way?

Thank you in advance!

LabJack Support's picture

Provided code reads from AIN0 in an infinite loop. The getAIN call looks correct (assuming d is a valid U3 object) and is a good way to read one analog input. Your call reads from AIN0 using default settings negative channel 31 (single-ended), long settle is false, and quick sample is false. Add exception handling to your getAIN code to detect and display any errors it has.

For best analog input accuracy, after opening your U3 read the calibration constants once before your analog input readings. This is mentioned at the start of the "Basic I/O: DAC0, AIN0, FIO4, FIO6" section of this page.

Ensure connections to your U3 and other hardware/sensors are secure. Connections to the U3 should be to the inside of the screw terminals, clamped down.

As for your triggers, what are your trigger conditions and how does it look in code?

Kelvinmantey1's picture

Hello again! Thank you so much for your response. It definitely helped to put things a bit more into perspective.  In response to the trigger conditions, the only thing we can control is the duration of the plateau. We don't have access to them because it's Medoc-controlled. We use the Medoc Pathway software along with a PC system and this software sends 5-Volt square analog triggers.

A question concerning reading the triggers -- would you recommend implementing a "get.Feedback()" command in addition to what we already have? Is that how we read the calibration constants before our analog input readings?

From what we've seen so far, a possible solution would be to increase the sampling rate (it seems we currently have a sampling rate of 200 milliseconds). Do you have any information on where and how exactly we could change the default settings?

LabJack Support's picture

would you recommend implementing a "get.Feedback() command in addition to what we already have?"

If you are reading only one analog input at a time, using getAIN and getFeedback with one analog input is equivalent time-wise (getAIN uses getFeedback and binaryToCalibratedAnalogVoltage), so there is no recommendation for using one over the other. I recommend getFeedback if reading from multiple analog inputs at a time as you can group multiple readings into one getFeedback call for best USB communications/time efficiency.

Is that how we read the calibration constants before our analog input readings?

With reading the calibration constants, do it once after opening your U3. The calibration constants are stored in your U3 object, and will be used in following getAIN and binaryToCalibratedAnalogVoltage calls. For example:

import u3

# Open first found U3 and get calibration constants
dev = u3.U3()
dev.getCalibrationData()

# Read from AIN0 twice. Calibration constants will be used.
print(dev.getAIN(0))
print(dev.getAIN(0))

Regarding the sample rate, from your code I only see the infinite loop performing the getAIN call. Nothing in particular there is controlling the sample rate to 200 milliseconds. Is there more code in that while loop that could be causing that extra delay? One getAIN call with long settle as false and quick sample as false should be on average 1 to 4 milliseconds. Documentation on execution times is here:

https://labjack.com/support/datasheets/u3/operation/command-response

If sample rate is not controlled by code delays/timers, I am unsure where in your system the sample rate is controlled. Perhaps a setting somewhere with PsychoPy, which I am not too familiar with?

BobvH's picture

I'm using a U6.pro with multiplexer. I try to send all the commands in one list in the getFeedback(["mylist with commands"]) and I get the following error: LabJackPython.LabJackException: ERROR: The feedback command you are attempting to send is bigger than 64 bytes ( 68 bytes ). Break your commands up into separate calls to getFeedback().

Is there a way to check the amount of bytes my commands will be, then I can decide how to split my list. I do not want to do a trial and error since I want to use the code over and over again with different setups. (e.g. different configuration).

In the code I found that the comands are transformed to bytes in a certain way, I couldn't find out how it works

Your help is appreciated.

Kind regards,

BobvH 

LabJack Support's picture

The LabJackPython and u6 modules do not have methods/functions to check the size of Feedback command beyond the error, but you can calculate the Feedback command size. getFeedback uses the Feedback command-response bytes for U6 communications, as documented here:

https://labjack.com/support/datasheets/u6/low-level-function-reference/f...

Each Feedback command class in the u6 module has a similar named Feedback IOType in the documentation. The IOType bytes are what are used in the Feedback command-response. The base Feedback command with no IOTypes/Data is 7 bytes, and the base response is 9 bytes.

For an example of calculating the size with the documentation, say you have a getFeedback call with 5 AIN24s. The base command is 7 bytes and each AIN24 command is 4 bytes, so 7 + 5*4 = 27 bytes will be the size of the command. The base response is 9 bytes and each AIN24 response is 3 bytes, so 9 + 5*3 = 24 bytes will be the size of the response. Note that technically odd sized Feedback packets get an extra byte appended to make them even, but that is not needed in your calculations.

Codewise, each Feedback command class has a cmdBytes member variable which are the IOType/Data bytes, so you can calculate your command size with something like:

 

 

cmd = [...]  # ... is your list of U6 Feedback commands you are passing to getFeedback

ioBytesSize = 0
for io in cmd:
    ioBytesSize += len(io.cmdBytes)
cmdBytesSize = 7 + ioBytesSize  # Our Feedback command size. Needs to be under 64.