« Close

Datasheets and User Guides

App Notes

Software & Driver

 

25.0 Lua Scripting [T-Series Datasheet]

Subsections

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.

 

MB.R

Value = 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.

 

MB.W

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.

 

MB.WA

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.

 

MB.RA

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

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

 

LJ.Tick

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

 

LJ.DIO_D_W

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

 

LJ.DIO_S_W

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

 

LJ.DIO_S_R

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

 

LJ.CheckFileFlag

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:

Example:

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
  f:close()
  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")
end

 

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.

 

Example:

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

 

LJ.setLuaThrottle

LJ.setLuaThrottle(newThrottle)

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.

 

LJ.getLuaThrottle

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#(0:39)_F32
- Starting Address: 46000
Generic RAM registers. Useful for passing data between a host computer and a Lua script. Will not return an error if alternate data types are used.
  • Data type: FLOAT32  (type index = 3)
  • Readable and writable
Expanded Names Addresses
USER_RAM0_F32, USER_RAM1_F32, USER_RAM2_F32, USER_RAM3_F32, USER_RAM4_F32, USER_RAM5_F32, USER_RAM6_F32, USER_RAM7_F32, USER_RAM8_F32, USER_RAM9_F32, USER_RAM10_F32, USER_RAM11_F32, USER_RAM12_F32, USER_RAM13_F32, USER_RAM14_F32, USER_RAM15_F32, USER_RAM16_F32, USER_RAM17_F32, USER_RAM18_F32, USER_RAM19_F32, USER_RAM20_F32, USER_RAM21_F32, USER_RAM22_F32, USER_RAM23_F32, USER_RAM24_F32, USER_RAM25_F32, USER_RAM26_F32, USER_RAM27_F32, USER_RAM28_F32, USER_RAM29_F32, USER_RAM30_F32, USER_RAM31_F32, USER_RAM32_F32, USER_RAM33_F32, USER_RAM34_F32, USER_RAM35_F32, USER_RAM36_F32, USER_RAM37_F32, USER_RAM38_F32, USER_RAM39_F32 Show All 46000, 46002, 46004, 46006, 46008, 46010, 46012, 46014, 46016, 46018, 46020, 46022, 46024, 46026, 46028, 46030, 46032, 46034, 46036, 46038, 46040, 46042, 46044, 46046, 46048, 46050, 46052, 46054, 46056, 46058, 46060, 46062, 46064, 46066, 46068, 46070, 46072, 46074, 46076, 46078 Show All
USER_RAM#(0:9)_I32
- Starting Address: 46080
Generic RAM registers. Useful for passing data between a host computer and a Lua script. Will not return an error if alternate data types are used.
  • Data type: INT32  (type index = 2)
  • Readable and writable
Expanded Names Addresses
USER_RAM0_I32, USER_RAM1_I32, USER_RAM2_I32, USER_RAM3_I32, USER_RAM4_I32, USER_RAM5_I32, USER_RAM6_I32, USER_RAM7_I32, USER_RAM8_I32, USER_RAM9_I32 Show All 46080, 46082, 46084, 46086, 46088, 46090, 46092, 46094, 46096, 46098 Show All
USER_RAM#(0:39)_U32
- Starting Address: 46100
Generic RAM registers. Useful for passing data between a host computer and a Lua script. Will not return an error if alternate data types are used.
  • Data type: UINT32  (type index = 1)
  • Readable and writable
Expanded Names Addresses
USER_RAM0_U32, USER_RAM1_U32, USER_RAM2_U32, USER_RAM3_U32, USER_RAM4_U32, USER_RAM5_U32, USER_RAM6_U32, USER_RAM7_U32, USER_RAM8_U32, USER_RAM9_U32, USER_RAM10_U32, USER_RAM11_U32, USER_RAM12_U32, USER_RAM13_U32, USER_RAM14_U32, USER_RAM15_U32, USER_RAM16_U32, USER_RAM17_U32, USER_RAM18_U32, USER_RAM19_U32, USER_RAM20_U32, USER_RAM21_U32, USER_RAM22_U32, USER_RAM23_U32, USER_RAM24_U32, USER_RAM25_U32, USER_RAM26_U32, USER_RAM27_U32, USER_RAM28_U32, USER_RAM29_U32, USER_RAM30_U32, USER_RAM31_U32, USER_RAM32_U32, USER_RAM33_U32, USER_RAM34_U32, USER_RAM35_U32, USER_RAM36_U32, USER_RAM37_U32, USER_RAM38_U32, USER_RAM39_U32 Show All 46100, 46102, 46104, 46106, 46108, 46110, 46112, 46114, 46116, 46118, 46120, 46122, 46124, 46126, 46128, 46130, 46132, 46134, 46136, 46138, 46140, 46142, 46144, 46146, 46148, 46150, 46152, 46154, 46156, 46158, 46160, 46162, 46164, 46166, 46168, 46170, 46172, 46174, 46176, 46178 Show All
USER_RAM#(0:19)_U16
- Starting Address: 46180
Generic RAM registers. Useful for passing data between a host computer and a Lua script. Will not return an error if alternate data types are used.
  • Data type: UINT16  (type index = 0)
  • Readable and writable
Expanded Names Addresses
USER_RAM0_U16, USER_RAM1_U16, USER_RAM2_U16, USER_RAM3_U16, USER_RAM4_U16, USER_RAM5_U16, USER_RAM6_U16, USER_RAM7_U16, USER_RAM8_U16, USER_RAM9_U16, USER_RAM10_U16, USER_RAM11_U16, USER_RAM12_U16, USER_RAM13_U16, USER_RAM14_U16, USER_RAM15_U16, USER_RAM16_U16, USER_RAM17_U16, USER_RAM18_U16, USER_RAM19_U16 Show All 46180, 46181, 46182, 46183, 46184, 46185, 46186, 46187, 46188, 46189, 46190, 46191, 46192, 46193, 46194, 46195, 46196, 46197, 46198, 46199 Show All

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
    end
  end
end

 

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)_ALLOCATE_NUM_BYTES 47900 UINT32 R/W
USER_RAM_FIFO#(0:3)_NUM_BYTES_IN_FIFO 47910 UINT32 R
USER_RAM_FIFO#(0:3)_EMPTY 47930 UINT32 W
USER_RAM_FIFO#(0:3)_DATA_U16
- Starting Address: 47000
Generic FIFO buffer. Useful for passing ORDERED or SEQUENTIAL data between various endpoints, such as between a host and a Lua script. Use up to 4 FIFO buffers simultaneously->1 of each data type, all 4 different data types, or a mixture. e.g. FIFO0_DATA_U16 points to the same memory as other FIFO0 registers, such that there are a total of 4 memory blocks: FIFO0, FIFO1, FIFO2 and FIFO3. It is possible to write into a FIFO buffer using a different datatype than is being used to read out of it. This register is a buffer. Underrun behavior - throws an error.
  • Data type: UINT16  (type index = 0)
  • Readable and writable
  • Default value: 0
  • This register is a Buffer Register
Expanded Names Addresses
USER_RAM_FIFO0_DATA_U16, USER_RAM_FIFO1_DATA_U16, USER_RAM_FIFO2_DATA_U16, USER_RAM_FIFO3_DATA_U16 Show All 47000, 47001, 47002, 47003 Show All
USER_RAM_FIFO#(0:3)_DATA_U32
- Starting Address: 47010
Generic FIFO buffer. Useful for passing ORDERED or SEQUENTIAL data between various endpoints, such as between a host and a Lua script. Use up to 4 FIFO buffers simultaneously->1 of each data type, all 4 different data types, or a mixture. e.g. FIFO0_DATA_U16 points to the same memory as other FIFO0 registers, such that there are a total of 4 memory blocks: FIFO0, FIFO1, FIFO2 and FIFO3. It is possible to write into a FIFO buffer using a different datatype than is being used to read out of it. This register is a buffer. Underrun behavior - throws an error.
  • Data type: UINT32  (type index = 1)
  • Readable and writable
  • Default value: 0
  • This register is a Buffer Register
Expanded Names Addresses
USER_RAM_FIFO0_DATA_U32, USER_RAM_FIFO1_DATA_U32, USER_RAM_FIFO2_DATA_U32, USER_RAM_FIFO3_DATA_U32 Show All 47010, 47012, 47014, 47016 Show All
USER_RAM_FIFO#(0:3)_DATA_I32
- Starting Address: 47020
Generic FIFO buffer. Useful for passing ORDERED or SEQUENTIAL data between various endpoints, such as between a host and a Lua script. Use up to 4 FIFO buffers simultaneously->1 of each data type, all 4 different data types, or a mixture. e.g. FIFO0_DATA_U16 points to the same memory as other FIFO0 registers, such that there are a total of 4 memory blocks: FIFO0, FIFO1, FIFO2 and FIFO3. It is possible to write into a FIFO buffer using a different datatype than is being used to read out of it. This register is a buffer. Underrun behavior - throws an error.
  • Data type: INT32  (type index = 2)
  • Readable and writable
  • Default value: 0
  • This register is a Buffer Register
Expanded Names Addresses
USER_RAM_FIFO0_DATA_I32, USER_RAM_FIFO1_DATA_I32, USER_RAM_FIFO2_DATA_I32, USER_RAM_FIFO3_DATA_I32 Show All 47020, 47022, 47024, 47026 Show All
USER_RAM_FIFO#(0:3)_DATA_F32
- Starting Address: 47030
Generic FIFO buffer. Useful for passing ORDERED or SEQUENTIAL data between various endpoints, such as between a host and a Lua script. Use up to 4 FIFO buffers simultaneously->1 of each data type, all 4 different data types, or a mixture. e.g. FIFO0_DATA_U16 points to the same memory as other FIFO0 registers, such that there are a total of 4 memory blocks: FIFO0, FIFO1, FIFO2 and FIFO3. It is possible to write into a FIFO buffer using a different datatype than is being used to read out of it. This register is a buffer. Underrun behavior - throws an error.
  • Data type: FLOAT32  (type index = 3)
  • Readable and writable
  • Default value: 0
  • This register is a Buffer Register
Expanded Names Addresses
USER_RAM_FIFO0_DATA_F32, USER_RAM_FIFO1_DATA_F32, USER_RAM_FIFO2_DATA_F32, USER_RAM_FIFO3_DATA_F32 Show All 47030, 47032, 47034, 47036 Show All
USER_RAM_FIFO#(0:3)_ALLOCATE_NUM_BYTES
- Starting Address: 47900
Allocate memory for a FIFO buffer. Number of bytes should be sufficient to store users max transfer array size. Note that FLOAT32, INT32, and UINT32 require 4 bytes per value, and UINT16 require 2 bytes per value. Maximum size is limited by available memory. Care should be taken to conserve enough memory for other operations such as AIN_EF, Lua, Stream etc.
  • Data type: UINT32  (type index = 1)
  • Readable and writable
  • Default value: 0
Expanded Names Addresses
USER_RAM_FIFO0_ALLOCATE_NUM_BYTES, USER_RAM_FIFO1_ALLOCATE_NUM_BYTES, USER_RAM_FIFO2_ALLOCATE_NUM_BYTES, USER_RAM_FIFO3_ALLOCATE_NUM_BYTES Show All 47900, 47902, 47904, 47906 Show All
USER_RAM_FIFO#(0:3)_NUM_BYTES_IN_FIFO
- Starting Address: 47910
Poll this register to see when new data is available/ready. Each read of the FIFO buffer decreases this value, and each write to the FIFO buffer increases this value. At any point in time, the following equation holds: Nbytes = Nwritten - Nread.
  • Data type: UINT32  (type index = 1)
  • Read-only
  • Default value: 0
Expanded Names Addresses
USER_RAM_FIFO0_NUM_BYTES_IN_FIFO, USER_RAM_FIFO1_NUM_BYTES_IN_FIFO, USER_RAM_FIFO2_NUM_BYTES_IN_FIFO, USER_RAM_FIFO3_NUM_BYTES_IN_FIFO Show All 47910, 47912, 47914, 47916 Show All
USER_RAM_FIFO#(0:3)_EMPTY
- Starting Address: 47930
Write any value to this register to efficiently empty, flush, or otherwise clear data from the FIFO.
  • Data type: UINT32  (type index = 1)
  • Write-only
  • Default value: 0
Expanded Names Addresses
USER_RAM_FIFO0_EMPTY, USER_RAM_FIFO1_EMPTY, USER_RAM_FIFO2_EMPTY, USER_RAM_FIFO3_EMPTY Show All 47930, 47932, 47934, 47936 Show All

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)
      else
        print ("FIFO0 buffer is full.")
      end
    end
    --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.")
    end
    for i=1, ((numBytesFIFO1+1)/ValueSizeInBytes) do
      ValIntoLua = MB.R(47032, 3)
      aF32_In[i] = ValIntoLua
      print ("Next Value FIFO1: ", ValIntoLua)
    end
  end
end

 

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.