« Close

Datasheets and User Guides

App Notes

Software & Driver

 

25.0 Lua Scripting

Subsections

 

Lua Scripting Overview

Please update your firmware to at least 1.0163

The T7 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. Get, or launch Kipling v3.

2. Make sure your T7 has firmware v1.0100 or later.  Minimum of 1.0163 is recommended.

3. Launch Kipling v3, and navigate to the Lua Script Debugger. Connect your T7 to your computer and press run. Open the Get Device Temperature example, and click Run. Now click Stop. Note that clicking stop clears the Lua VM, so even if a program has concluded, it is still necessary to press the stop button.

4. Try out some other examples.

Running a script when the T7 powers up:

The T7 can be configured to run a script when it powers on or resets.  Typically scripts are tested for a while with the Run/Stop button, while viewing the debug output in the console. Then once everything is working correctly, users will enable the script at startup and close Kipling.

1. Click Save Script to Flash.

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

 

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].

 

Not sure how to accomplish a goal?

Shoot us an email. We will make a new example and add functions as necessary. 

 

While writing Lua for the T7:

  • 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
  • Based on eLua 0.8 which is based on Lua 5.1.4
  • Lua supports multi-return:   table, error = MB.RA(Address, dataType, nValues). Both table and error are returned values.
  • On the T7, 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 at the bottom of this 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:

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. Will read a single value from a Modbus register. That item 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.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 as shown in the example below.

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)

handle: 0-7

time_ms: Number of milliseconds per interval.

 

timed_out = LJ.CheckInterval(handle)

handle: 0-7

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.[1]

Register Listing

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 for users who 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

 

 

Future Features:

Firmware Related:

  • Write data to web services such as DAQConnect.

 

Notes for Kipling:

  • Easy toggle between Modbus Names and Addresses.  Since Addresses are required at the low level, customers must search for what names correspond with what addresses using the Modbus map.  We hope to integrate this lookup process eventually.
  • When switching away from the Lua Script Debugger tab, the 'active script' is forgotten.  We plan to cache the open script file path, so that users don't have to keep re-opening their script file each time.
  • View logged data on microSD card.  Currently the easiest ways to read files from the microSD card are to physically remove the SD card from the device (inside the enclosure), or use the Beta T7 SD Card File Downloader. Note that the beta program is subject to changes, and will not work with firmware older than 1.0150.

 

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.

[1] Note that in old firmware <1.0163 user RAM was called LUA_IO#(0:39)_READ, and there were specialized functions called IOMEM.W() and IOMEM.R() within Lua.

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 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"
    "end\n"
    "\0";

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):
MillisecondSleep(600);
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.

    8 comments

    Do you have a python example to send the script to a T7.  Also what is the command for wait functions between 5 seconds and 200 ms for setting IO modules from low to high using the RB12.

    No we do not have any examples at the moment. The next version of Kipling (v3) will have scripting support and will be cross platform. Send us an email if you are interested in early-access to Kipling, or writing your own python program.

    Tested lua and liked it!

    I understand that it is considered beta. I have, however a few proposals:

      - Would be helpful if lua file in memory would stay persistent when swithcing from "Lua Script Debugger" to other tab, say "Register Matrix" and back to "Lua Script Debugger". Currently one has to reload his program file.

      - Would be nice if in the file open dialog the default file-extension would be "*.lua"

      - warning on syntax-errors would be quite helpful. Currently T7 hangs if erroneous script is downloaded and exected

      - preprocessor that

          - removes whitespaces and comments before downloading to hardware.

          - replaces symbolic register names by register constants (based on ljm_constants.json)

      - some basic debugging functionality would be appreciated

    However, I am really grateful that there is scripting at all!

    Best regards,

    Martin

    Thanks for the feedback!  The good news is that you've basically re-created our internal to-do list related to the Lua scripting module, so we are in-process of implementing all of these items. 

    The only item not on our internal list is to change the default file extensions shown to only "*.lua".  I will add this to the list.

    Bear in mind that LabJack is resource constrained on the Kipling 3 project, since we try to keep most of our efforts focused on making hardware. However, it's good to know that scripting has been useful to you.  Keep an eye on our software installs, and Kipling 3 application page for new updates, as some of these changes will be ready in a matter of weeks.  Also, the improved debugging functionality will appear in the form of a T7 firmware update, since the T7 contains the Lua interpreter/compiler, K3 simply relays the output.

    I have used a U6 for quite some time with great results at modest speed. The T7 scripting option is especially appealing both for autonomous operation but more importantly speed. PID motion control is especially challenging with the inherent USB latency of the U6. Consider a 2D motion control example. The host must do one read from the vector of AI's or I2C devices, think about it some, and then do one write to the AO's or DO servo counters. At least 2 mS down the tubes, for a maximum system rate of a few hundred Hz. The question is - how much speed to expect from a U7 scripting system? Is the T7 processor busy interpreting Lua all the time or does it effectively compile it first? We are only ahead of the U6 USB 2-command latency if the T7 can do the modest amount of math pretty fast. Otherwise it will fall behind a PC based solution which would be all over the math but waiting on I/O a lot. What do you think? Thanks 

    You've identified a common thought process for Lua scripting.  Unfortunately there is no clear winner.  If the T7s clock rate were on the order of GHz, there would be no question that Lua would be faster.  Unfortunately, the T7 core runs at 80MHz, and Lua operations aren't incredibly optimized, so if you have a lot of computations to perform, then a host computer will win out.  However, if computations are minimal, then removing the USB latency and doing things in script ends up being faster.

    Furthermore, when you code Lua to run as fast as it can, the timing is jittery because Lua stuff runs on the same core as non-lua suff, so an external computer querying things can slow Lua execution down.

    There isn't an exact equation that maps "X number of lines of Lua takes X many micro seconds", but we do have some benchmarking examples in Kipling that should give you a feel for speed.

    This extremely basic example(3 lines, toggles a DIO) runs at 20kHz

    LJ.DIO_D_W(3, 1)        --FIO3 to output.
    LJ.setLuaThrottle(50)
    c = 0
    LJ.IntervalConfig(0, 1000) --1000ms
    while true do
      LJ.DIO_S_W(3, 0)
      LJ.DIO_S_W(3, 1)
      c = c + 1
      if LJ.CheckInterval(0) then --every 1000ms or 1s
        print ("Frequency in Hz: ", c)
        c = 0
      end
    end
    

    This more complicated example(~20 lines, reads 5 analog inputs, and sets 2 digital outputs) only runs at ~3kHz.

    LJ.DIO_D_W(3, 1)  --FIO3 to output.
    MB.W(43903, 0, 1) --AIN_ALL_RESOLUTION_INDEX to 1
    LJ.setLuaThrottle(100)
    c = 0
    ThresholdVoltage = 2.5
    LJ.IntervalConfig(0, 1000) --1000ms
    while true do
      V1 = MB.R(0, 3)     --read AIN0
      V2 = MB.R(2, 3)     --read AIN1
      V3 = MB.R(4, 3)
      V4 = MB.R(6, 3)
      V5 = MB.R(8, 3)
      if V4 > ThresholdVoltage then
        MB.W(2003, 0, 1)  --set FIO3 high
      else
        MB.W(2003, 0, 0)
      end
      if V5 > ThresholdVoltage then
        MB.W(2000, 0, 1)  --set FIO0 high
      else
        MB.W(2000, 0, 0)
      end
      c = c + 1
      if LJ.CheckInterval(0) then --every 1000ms or 1s
        print ("Frequency in Hz: ", c)
        c = 0
      end
    end
    

    So it's apparent that once you get into 50+ lines, the code isn't going to run very fast.

    novice's picture

    What if one wanted to control the pulse width using this command: LJ.DIO_S_W Would this be doable?
    e.g. FIO3 on for 20 ms then repeat after 100 ms (20% duty cycle)? And what if one wanted to do this in sequence with 3 outputs?

    I did notice that there is PWM Out with phase option, but is this also an option using Lua scripting? And if so, how would one implement it? Are there more scripting examples available?
    https://labjack.com/support/datasheets/t7/digital-io/extended-features

    LabJack Support's picture

    You can create an object and attach various properties to it in Lua but we don't have an LJ.xxx function available for you to call. The example below may be of assistance.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    -- Create PWM library.
    PWM = {}--add local variables here
    function PWM.init (self, ichan, ifreq, iduty, iclk, idivisor)--duty should be in %, not decimal
      irollValue = (80000000/ifreq)/idivisor
      self.rollValue = irollValue--store local values for future use vvvv
      self.chan = ichan
      self.freq = ifreq
      self.duty = iduty
      self.clk = iclk
      self.divisor = idivisor--store local values for future use ^^^^
      MB.W(44900+iclk*10, 0, 0)--Disable clock source
      --Set Clock#'s divisor and roll value to configure frequency
      MB.W(44901+iclk*10, 0, idivisor)--Configure Clock#'s divisor
      MB.W(44904+iclk*10, 1, irollValue)--Configure Clock#'s roll value
      MB.W(44900+iclk*10, 0, 1)--enable clock source
      --Configure EF Channel Registers:
      MB.W(44000+ichan*2, 1, 0)--Disable the EF system for initial configuration
      MB.W(44100+ichan*2, 1, 0)--Configure EF system for PWM
      MB.W(44200+ichan*2, 1, iclk)--Configure what clock source to use: Clock#
      MB.W(44300+ichan*2, 1, irollValue*iduty/100)--Configure duty cycle
    end
    function PWM.enable (self)
      MB.W(44000+self.chan*2, 1, 1)--enable the EF system
    end
    function PWM.disable (self)
      MB.W(44000+self.chan*2, 1, 0)--disable the EF system
    end
    function PWM.changeFreq (self, ifreq)
      irollValue = (80000000/ifreq)/self.divisor
      self.rollValue = irollValue--store local values for future use
      self.freq = ifreq
      MB.W(44904+self.clk*10, 1, self.rollValue)--Configure Clock#'s roll value
      MB.W(44300+self.chan*2, 1, self.rollValue*self.duty/100)--reconfigure duty cycle
    end
    function PWM.changeDutyCycle (self, iduty)
      self.duty = iduty--re-store local value
      MB.W(44300+self.chan*2, 1, self.rollValue*self.duty/100)--Configure duty cycle
    end
    
    FIO0PWM = PWM
    FIO0PWM.init(FIO0PWM, 0, 50, 50, 0, 1)--init
    FIO0PWM.enable(FIO0PWM)
    
    FIO0PWM.changeFreq(FIO0PWM, 200)
    FIO0PWM.changeDutyCycle(FIO0PWM, 10)
    

    As far as PWM output with phase, there should be some examples in other languages but we don't have any examples not in Kipling as of right now. We will be posting the available lua scripting examples to the T7 lua-scripting page eventually.