« Close

Datasheets and User Guides

Software & Driver


25.0 Lua Scripting [T-Series Datasheet]


Lua Scripting Overview

T-Series devices can execute Lua code to allow independent operation. A Lua script can be used to collect data without a host computer or to perform complex tasks producing simple results that a host can read.  For a good overview on the capabilities of scripting, see this LabJack Lua blog post.

Getting Started

1. Connect your device to your computer, launch Kipling v3, and navigate to the Lua Script Debugger tab.

2. Open the "Get Device Temperature" example and click the Run button. Now click the Stop button. Clicking Stop clears the Lua VM, so even if a program has concluded, it is still necessary to press the Stop button.

3. Try out some other examples.

Running a script when the device powers up

A T-Series device can be configured to run a script when it powers on or resets.  Typically you should test script for a while with the Run/Stop buttons while viewing the debug output in the console. Then once everything is working correctly, enable the script at startup and close Kipling.

To enable the script at startup:

1. Click Save Script to Flash.

2. Click Enable Script at Startup.  Now when the device is powered on or reset, it will run your script.

3. After power cycling the device, if it becomes un-usable and the COMM/Status lights are constantly blinking the Lua Script is likely causing the device to enter an invalid state.  The device can be fixed by connecting a jumper wire between the SPC terminal and either FIO0 or FIO4, see the SPC section of the datasheet for more details.

Learning more about Lua

Learning Lua is very easy. There are good tutorials on Lua.org as well as on several other independent sites. If you are familiar with the basics of programming, such as loops and functions, then you should be able to get going just by looking at the examples. If you have suggestions or comments, please email [email protected].

Lua for T-Series Devices

Try to keep names short. String length directly affects execution speed and code size.

Use local variables instead of global variables (it's faster and less error-prone). For example, use:

local a = 10

instead of:

a = 10

You can also assign a function to a local for further optimization:

local locW = MB.W
locW(2003, 0, 1) --Write to address 2003

Lua supports multi-return:

table, error = MB.RA(Address, dataType, nValues)

Both table and error are returned values.

On T-Series devices, Lua's only numeric data type is IEEE 754 single precision (float). This is more important than it sounds. Here is a good article on floating point numbers and their pitfalls: Floating Point Numbers. There are some workarounds for reading values greater than 24-bits in the "Lua Limitations" section.

Examples contain comments, and currently comments consume a lot of code space. You may want to limit comments if you are making a long script (>200 lines).

LabJack's Lua library

Based on eLua 0.8 which is based on Lua 5.1.4.

Data Types - Same types as the LJM Library

  • 0 - unsigned 16-bit integer
  • 1 - unsigned 32-bit integer
  • 2 - signed 32-bit integer
  • 3 - single precision floating point (float)
  • 98 - string
  • 99 - byte - The "byte" dataType is used to pass arrays of bytes in what Lua calls tables


Available Functions - The basic Lua libraries are extended by a LabJack specific library. Below are the available functions.



value, error = MB.R(Address, dataType)  

Modbus read. Reads a single value from a Modbus register. The type can be a u16, u32, a float, or a string. Any errors encountered will be returned in error.



error = MB.W(Address, dataType, value)

Modbus write. Writes a single value to a Modbus register. The type can be a u16, u32, a float, or a string. Any errors encountered will be returned in error.



error = MB.WA(Address, dataType, nValues, table)

Modbus write array. Reads nValues from the supplied table, interprets them according to the dataType and writes them as an array to the register specified by Address. The table must be indexed with numbers from 1 to nValues.



table, error = MB.RA(Address, dataType, nValues)

Modbus read array. Reads nValues of type dataType from Address and returns the results in a Lua table. The table is indexed from 1 to nValues.



LJ.ledtog() --Toggles status LED. Note that reading AINs also toggles the status LED.



Ticks = LJ.Tick() --Reads the core timer (1/2 core frequency).



LJ.DIO_D_W(3, 1) --Quickly change FIO3 direction _D_ to output.



LJ.DIO_S_W(3, 0) --Quickly change the state _S_ of FIO3 to 0 (output low)



state = LJ.DIO_S_R(3) -- Quickly read the state _S_ of FIO3



flag = LJ.CheckFileFlag() and LJ.ClearFileFlag

LJ.CheckFileFlag and LJ.ClearFileFlag work together to provide an easy way to tell a Lua script to switch files. This is useful for applications that require continuous logging in a Lua script and on-demand file access from a host. Since files cannot be opened simultaneously by a Lua script and a host, the Lua script must first close the active file if the host wants to read file contents. The host writes a value of 1 to FILE_IO_LUA_SWITCH_FILE, and the Lua script is setup to poll this parameter using LJ.CheckFileFlag(). If the file flag is set, Lua code should switch files:


fg = LJ.CheckFileFlag() --poll the flag every few seconds
if fg == 1 then
  NumFn = NumFn + 1                --increment filename
  Filename = Filepre..string.format("%02d", NumFn)..Filesuf
  LJ.ClearFileFlag()              --inform host that previous file is available.
  f = io.open(Filename, "w")      --create or replace a new file
  print ("Command issued by host to create new file")


LJ.IntervalConfig & LJ.CheckInterval

LJ.IntervalConfig and LJ.CheckInterval work together to make an easy-to-use timing function. Set the desired interval time with IntervalConfig, then use CheckInterval to watch for timeouts. The interval period will have some jitter but no overall error. Jitter is typically ±30 µs but can be greater depending on processor loading. A small amount of error is induced when the processor's core speed is changed.

Up to 8 different intervals can be active at a time.


LJ.IntervalConfig(handle, time_ms)

Sets an interval timer, starting from the current time.

handle: 0-7. Identifies an interval.

time_ms: Number of milliseconds per interval.


timed_out = LJ.CheckInterval(handle)

handle: 0-7. Identifies an interval.

Returns: 1 if the interval has expired. 0 if not.



LJ.IntervalConfig(0, 1000)
while true do
  if LJ.CheckInterval(0) then
    --Code to run once per second here.




Set the throttle setting. This controls Lua's processor priority. Value is number of Lua instruction to execute before releasing control to the normal polling loop. After the loop completes Lua will be given processor time again.



ThrottleSetting = LJ.getLuaThrottle()

Reads the current throttle setting


Passing data into/out of Lua

User RAM consists of a list of Modbus addresses where data can be sent to and read from a Lua script. Lua writes to the Modbus registers, and then a host device can read that information. 

There are a total of 200 registers of pre-allocated RAM, which is split into several groups so that users may access it conveniently with different data types.

Use the following USER_RAM registers to store information:

User RAM Registers
Name Start Address Type Access
USER_RAM#(0:39)_F32 46000 FLOAT32 R/W    
USER_RAM#(0:9)_I32 46080 INT32 R/W    
USER_RAM#(0:39)_U32 46100 UINT32 R/W    
USER_RAM#(0:19)_U16 46180 UINT16 R/W    

USER_RAM Example script:

while true do
  if LJ.CheckInterval(0) then
    Enable = MB.R(46000, 3) --host may disable portion of the script
    if Enable >= 1 then
     val = val + 1
     print("New value:", val)
     MB.W(46002, 3, val)  --provide a new value to host


There is also a more advanced system for passing data to/from a Lua script referred to as FIFO buffers. These buffers are useful if you want to send an array of information in sequence to/from a Lua script. Usually 2 buffers are used for each endpoint, one buffer dedicated for each communication direction (read and write). A host may write new data for the Lua script into FIFO0, then once the script reads the data out of that buffer, it responds by writing data into FIFO1, and then the host may read the data out of FIFO1.

User RAM FIFO Registers - Advanced
Name Start Address Type Access
USER_RAM_FIFO#(0:3)_DATA_U16 47000 UINT16 R/W    
USER_RAM_FIFO#(0:3)_DATA_U32 47010 UINT32 R/W    
USER_RAM_FIFO#(0:3)_DATA_I32 47020 INT32 R/W    
USER_RAM_FIFO#(0:3)_DATA_F32 47030 FLOAT32 R/W    
USER_RAM_FIFO#(0:3)_EMPTY 47930 UINT32 W    

USER_RAM_FIFO Example script:

aF32_Out= {}  --array of 5 values(floats)
aF32_Out[1] = 10.0
aF32_Out[2] = 20.1
aF32_Out[3] = 30.2
aF32_Out[4] = 40.3
aF32_Out[5] = 50.4

aF32_In = {}
numValuesFIO0 = 5
ValueSizeInBytes = 4
numBytesAllocFIFO0 = numValuesFIO0*ValueSizeInBytes
MB.W(47900, 1, numBytesAllocFIFO0) --allocate USER_RAM_FIFO0_NUM_BYTES_IN_FIFO to 20 bytes

LJ.IntervalConfig(0, 2000)
while true do
  if LJ.CheckInterval(0) then
    --write out to the host with FIFO0
    for i=1, numValuesFIO0 do
      ValOutOfLua = aF32_Out[i]
      numBytesFIFO0 = MB.R(47910, 1)
      if (numBytesFIFO0 < numBytesAllocFIFO0) then
        MB.W(47030, 3, ValOutOfLua)  --provide a new array to host
        print ("Next Value FIFO0: ", ValOutOfLua)
        print ("FIFO0 buffer is full.")
    --read in new data from the host with FIFO1
    --Note that an external computer must have previously written to FIFO1
    numBytesFIFO1 = MB.R(47912, 1) --USER_RAM_FIFO1_NUM_BYTES_IN_FIFO
    if (numBytesFIFO1 == 0) then
      print ("FIFO1 buffer is empty.")
    for i=1, ((numBytesFIFO1+1)/ValueSizeInBytes) do
      ValIntoLua = MB.R(47032, 3)
      aF32_In[i] = ValIntoLua
      print ("Next Value FIFO1: ", ValIntoLua)


Lua Limitations

Lua is using a single precision float for its data-type. This means that working with 32-bit integer registers is difficult (see examples below). If any integer exceeds 24-bits, the lower bits will be lost. The workaround is to access the Modbus register using two numbers, each 16-bits. Lua can specify the data type for the register being written, so if users are expecting a large number that will not fit in a float(>24bits), such as a MAC, then read or write the value as a series of 16-bit integers. If you expect the value to be counting up or down, use MB.RA or MB.RW to access the U32 as a contiguous set of 4 bytes.  If the value isn't going to increment (e.g. the MAC address) it is permissible to read it in two separate packets using MB.R.

Read a 32-bit register

--Value is expected to be changing, and >24 bits (use MB.RA)
aU32[1] = 0x00
aU32[2] = 0x00
aU32, error = MB.RA(3000, 0, 2)   --DIO0_EF_READ_A. Type is 0 instead of 1
DIO0_EF_READ_A_MSW = aU32[1]
DIO0_EF_READ_A_LSW = aU32[2]
--Value constant, and >16,777,216 (24 bits)
--Read ETHERNET_MAC (address 60020)  
MAC_MSW = MB.R(60020, 0)  --Read upper 16 bits. Type is 0 instead of 1
MAC_LSW = MB.R(60021, 0)  --Read lower 16 bits.
--Value <16,777,216 (24 bits)
--Read AIN0_EF_INDEX (address 9000)
AIN0_index = MB.R(9000, 1)  --Type can be 1, since the value will be smaller than 24 bits.

Write a 32-bit register

--Value might be changed or incremented by the T7, and >24 bits (use MB.WA)
aU32[1] = 0xFF2A
aU32[2] = 0xFB5F
error = MB.WA(44300, 0, 2, aU32) --Write DIO0_EF_VALUE_A. Type is 0 instead of 1
--Value constant, and >24 bits
MB.W(44300, 0, 0xFF2A) --Write upper 16 bits. Type is 0 instead of 1
MB.W(44301, 0, 0xFB5F) --Write lower 16 bits.
--Value <16,777,216 (24 bits)
--Write DIO0_EF_INDEX (address 44100)
MB.W(44100, 1, 7)  --Type can be 1, since the value(7) is smaller than 24 bits.


Loading a Lua Script to a T7 Manually

Load Lua Script Manually To Device

While LabJack's Kipling program handles Lua scripting details easily and automatically, the example below shows how to load a Lua script to a T7 manually.  The general process as well as some example psuedo code can be found below:

  1. Define or load a Lua Script and make sure a device has been opened.
  2. Make sure there is a null-character at the end of the string.
  3. Make sure a Lua Script is not currently running.  If one is, stop it and wait for it to be stopped.
  4. Write to the "LUA_SOURCE_SIZE" and "LUA_SOURCE_WRITE" registers to instruct the T7 to allocate space for a script and to transfer it to the device.
  5. (Optional) Enable debugging.
  6. Instruct the T-Series device to run the loaded Lua Script.

The C example below opens a T7, shuts down any Lua script that may be running, loads the script, and runs the script.

const char * luaScript =
    "LJ.IntervalConfig(0, 500)\n"
    "while true do\n"
    "  if LJ.CheckInterval(0) then\n"
    "    print(LJ.Tick())\n"
    "  end\n"

const unsigned scriptLength = strlen(luaScript) + 1;
// strlen does not include the null-terminating character, so we add 1
// byte to include it.

int handle = OpenOrDie(LJM_dtT7, LJM_ctANY, "LJM_idANY");

// Disable a running script by writing 0 to LUA_RUN twice
WriteNameOrDie(handle, "LUA_RUN", 0);
// Wait for the Lua VM to shut down (and some T7 firmware versions need
// a longer time to shut down than others):
WriteNameOrDie(handle, "LUA_RUN", 0);

// Write the size and the Lua Script to the device
WriteNameOrDie(handle, "LUA_SOURCE_SIZE", scriptLength);
WriteNameByteArrayOrDie(handle, "LUA_SOURCE_WRITE", scriptLength, luaScript);

// Start the script with debug output enabled
WriteNameOrDie(handle, "LUA_DEBUG_ENABLE", 1);
WriteNameOrDie(handle, "LUA_DEBUG_ENABLE_DEFAULT", 1);
WriteNameOrDie(handle, "LUA_RUN", 1);

The above example is valid C code, where the following functions are error-handling functions that cause the program to exit if an error occurs:

  • OpenOrDie wraps LJM_Open
  • WriteNameOrDie wraps LJM_eWriteName
  • WriteNameByteArrayOrDie wraps LJM_eWriteNameByteArray
  • The Lua script in the example above is in the form of a C-string, e.g. a string with a null-terminator as the last byte.

    To download a version of the above example, see utilities/lua_script_basic.c, which includes a function that reads debug data from the T7.

    Reading Debug Data/Print Statements

    After a script has started (with debugging enabled) any information printed by a lua script can be read by a user application using the "LUA_DEBUG_NUM_BYTES" and "LUA_DEBUG_DATA" registers.