Skip to Content
Lua 自動化API 參考

Lua API 參考

本頁列出自動化腳本可用的每一個函式,每個函式一節:語法、參數範圍、回傳值、 會出錯的情況,以及一個最小範例。想看完整、可下載的範例,回頭看前面各頁的 <LuaExample> 區塊。

開放的標準函式庫

除了下列 Nexus 自有函式,腳本內也能使用這幾個 Lua 標準函式庫:

函式庫說明
base基本函式(type、pcall、tostring、pairs…);print 被覆寫為寫入 Log
tabletable 操作(table.insert、table.remove…)
string字串處理(string.format、string.find…)
math數學函式

io、os、package、require 不開放——腳本不能讀寫任意檔案、呼叫系統指令, 或載入外部模組,見「腳本執行的機制」。

i2c

以下七個函式共通的 slot:省略時作用在這個分頁預設(第 0 個)掛載;帶 slot 時作用在同一個分頁裡、第 slot 個掛載(0~31)——這是「一個 分頁同時掛好幾顆 IC」的多掛載設定,跟指令列選分頁用的 @p<N> 是完全不同的 兩個軸:@p<N> 選的是分頁本身,slot 選的是這個分頁裡的第幾顆掛載。

offset 的合法範圍取決於這個掛載的暫存器位移定址寬度是 8-bit 還是 16-bit(設定掛載時決定,見指令參考的 rd16/wr16):8-bit 定址(多數 IC,也是預設值)時 offset 不能超過 0xFF;16-bit 定址時可以到 0xFFFF。這跟 Slave 位址是 7-bit 還是 8-bit 格式是兩件不相干的事(後者 見常見問題),不要搞混。

i2c.read(offset [, len [, slot]])

local v = i2c.read(0x10) -- 讀一個位元組,回傳一個數字 local t = i2c.read(0x10, 4) -- 讀 4 個位元組,回傳 table:t[1]~t[4]
  • offset:0~65535(8-bit 定址的掛載上限是 0xFF,見上一段)。
  • len(可省略,預設 1):1~256。
  • 省略 len(或給 1)時回傳單一數字;給了其他 len 時回傳一個 1-based 的 table。
  • 出錯情況:offset/len/slot 不是整數或超出範圍;讀取本身失敗(裝置 沒回應等)。

i2c.write(offset, value_or_table [, slot])

i2c.write(0x10, 0xAB) -- 寫一個位元組 i2c.write(0x10, {1, 2, 3, 4}) -- 用 table 一次連續寫 4 個位元組
  • offset:同 i2c.read。
  • 單一寫入值:0~255。
  • table 形式:長度 1256,每個元素各自 0255——驗證會先掃過整個 table 再開始寫,某個元素超出範圍時完全不會寫入任何位元組,不會有「寫了 一半」的情況。
  • 出錯情況:任一數值不是整數或超出範圍;寫入本身失敗。

i2c.set(offset, mask [, slot])

i2c.set(0x10, 0x80) -- 把 0x10 這個位元組的第 7 個位元設為 1,其餘位元不變

先讀出目前的值、只把 mask 裡是 1 的位元設成 1、再寫回去(讀改寫)。 mask:0~255。出錯情況同 i2c.write。

i2c.clr(offset, mask [, slot])

i2c.clr(0x10, 0x80) -- 把 0x10 這個位元組的第 7 個位元清為 0,其餘位元不變

跟 i2c.set 相反:把 mask 裡是 1 的位元清成 0,其餘位元不變。mask: 0~255。出錯情況同 i2c.write。

i2c.bank(n [, slot])

i2c.bank(2) -- 切到第 2 頁:把 2 寫進頁碼暫存器

切換到第 n 頁(Bank),之後同一個位移存取的是該頁的內容;細節見 Bank 切換。n:0~255。這個掛載如果沒有設定過頁碼暫存器 (沒呼叫過 i2c.bank_reg(),也沒下過 bank reg <位移> <遮罩>),呼叫會直接 出錯(bank not supported for this tab),不會嘗試猜一個。

i2c.addr(addr [, slot])

i2c.addr(0x58) -- 把這個掛載接下來要對話的位址設成 0x58 i2c.addr(0x58, 1) -- 只改第 1 個掛載槽的位址,其餘掛載槽不受影響
  • addr:0x01~0x7F(0 保留給「未設定」的哨兵值,不能拿來當合法位址)。
  • 沒有回傳值。
  • 只在本次腳本執行期間有效:腳本結束時(不論成功、失敗、fail()、發生 錯誤或被中止)一律自動改回腳本開始前的位址,不會寫進 config.ini,也 不影響你在腳本外用指令列或畫面設定的位址。
  • ⚠ 順序有影響:如果同一個掛載槽已經先用 i2c.bank_reg() 設定好頁碼 暫存器,之後才呼叫 i2c.addr() 換位址,頁碼暫存器設定會被清掉(跟畫面上 先 bank reg、後 addr 的行為一樣)——要用 Bank,請先 i2c.addr()、再 i2c.bank_reg(),見Bank 切換。
  • 出錯情況:addr 不是整數、是 0、或超過 0x7F;slot 未設定(見本節 開頭的共通說明);寫入本身失敗。

i2c.bank_reg(reg, mask [, slot])

i2c.bank_reg(0xF0, 0xFF) -- 位移 0xF0 是頁碼暫存器,遮罩 0xFF 代表整個位元組都是頁碼
  • reg:0x000xFF。mask:0x010xFF(0 保留給「未設定」的哨兵值, 不能拿來當合法遮罩)。
  • 沒有回傳值。效果等同 bank reg <reg> <mask> 指令,設定好之後 i2c.bank()/ dump_all() 才能用。
  • 只在本次腳本執行期間有效,還原規則同上一節的 i2c.addr()。
  • ⚠ 這個掛載實際停在哪一頁不會被自動還原:腳本結束只會把「頁碼暫存器的 位移/遮罩設定」還原成腳本開始前的樣子,不會把晶片實際切回原本那一頁。腳本 如果用 i2c.bank() 切換過頁,收尾前自己呼叫 i2c.bank(0)(或換回原本那 一頁),見Bank 切換。
  • 出錯情況:reg/mask 不是整數或超出範圍、mask 為 0;slot 未設定; 寫入本身失敗。

i2c.recover()

local pins = i2c.recover() -- 3 表示 SCL 與 SDA 兩條線都已放開

要求裝置嘗試恢復被卡住的 I2C 匯流排,回傳腳位狀態遮罩。回傳不代表線已放開: 只有 pins == 3(兩條線都是高電位)才表示恢復成功,其他值代表仍有線被拉住, 請自己檢查。只作用在這支腳本自己的匯流排;裝置不支援時丟錯。

uart

三個函式共通的 panel:省略時,只有這個分頁剛好開了一個 UART 面板才會自動 選它;開了不只一個或一個都沒開,必須自己指定,否則出錯。panel 範圍 0~7。模擬後端(mock)沒有任何 UART 裝置可以回話,這三個函式實際上 需要真實硬體才能跑,見GPIO 與 UART。

⚠ uart.read/uart.expect 只看得到「這支腳本本次執行、第一次呼叫任何 一個 uart.* 之後」才到達的資料——執行前已經在面板上的舊資料,不算「已經 到的」。逾時都回傳 nil(不算錯誤);但如果該看的那幾行在你來得及讀之前就被 覆寫掉,兩者都會出錯("uart: unread lines on panel <N> were lost (ring overwritten)"),提醒你讀得不夠快、該讀的資料已經真的遺失。

uart.write(str [, panel])

uart.write("PING\n")

原樣送出字串(二進位安全,不會自動加換行)。str 長度 1~256 個位元組。 出錯情況:空字串、超過 256 位元組、對應的面板沒開、佇列滿逾時(1 秒)。

uart.read(timeout_ms [, panel])

local line = uart.read(2000) -- 最多等 2000ms;等到就回傳字串,逾時回傳 nil

在時限內等待並回傳下一行收到的資料(見上面的游標範圍限制);逾時回傳 nil (不算錯誤)。timeout_ms 必須是非負數;0 代表「不等待,只看游標範圍內 現在已經到的」。出錯情況:對應的面板沒開;ring overwritten(見上面)。

uart.expect(pattern, timeout_ms [, panel])

local line, capture = uart.expect("^PONG (%d+)", 2000) -- 逾時一樣回傳 nil

在時限內等待符合 pattern(Lua pattern,跟 string.find 同一種語法)的一行; 逾時回傳 nil。等到時的回傳值不只整行內容——第一個回傳值永遠是整行,後面 接著的是 pattern 裡每一組括號 (...) 各自比對到的子字串(跟 string.find 的 captures 是同一回事);pattern 沒有括號就只回傳整行。 出錯情況同 uart.read。

gpio

三個函式共通的 pin:031;**模擬後端只會回應腳位 03**,其餘腳位在 mock 上會失敗。真實硬體支援的腳位數依接的裝置而定。

gpio.write(pin, v)

gpio.write(0, true) -- 設為輸出並寫入高電位 gpio.write(0, 0) -- 數字也可以:非 0 是高、0 是低

把指定腳位設為輸出並寫入電位;v 可以是布林值,也可以是數字。出錯情況: pin 超出範圍、腳位已被別的用途(其他掛載/Plugin)佔用、硬體交易本身失敗。

gpio.read(pin)

local v = gpio.read(0) -- 回傳 0 或 1

讀回指定腳位目前的電位,回傳 0 或 1。如果這支腳本已經用 gpio.write() 把腳位設為輸出,gpio.read() 讀的是目前輸出的鎖存值,不需要先切回輸入。

gpio.release(pin)

gpio.release(0)

釋放腳位的持有權,交還給其他使用者。就算不呼叫,腳本結束時(不論結果如何) 程式都會自動收回這支腳本佔用的全部腳位。

gpio.pulse(pin, level, width_us)

gpio.write(1, 0) local w = gpio.pulse(1, 1, 2000) -- 在腳位 1 輸出 2000 微秒的高電位脈衝

讓裝置自己在指定腳位輸出一個精確寬度的脈衝,回傳實際寬度(微秒)。level 是脈衝 期間的電位(布林或數字,規則同 gpio.write()),width_us 為 1~1000000。 腳位要先設成輸出(例如先 gpio.write());裝置不支援脈衝或寬度超出該裝置的範圍時 丟錯。脈衝進行中腳本無法被中止,中止會在函式返回後生效。

dongle

dongle.op(name[, args])

local r = dongle.op("wave", {scl_hz = 100000, sda_hz = 100000}) print(r.tick_hz) local p = dongle.op("pin_mode", {2, "gpio_out"}) -- 位置參數

執行裝置自己宣告的廠商操作(dongle list 可看到有哪些,dongle help <名稱> 看參數)。 args 是一張表:整數鍵 1、2、… 是依宣告順序的位置參數,字串鍵是具名參數 (位置參數要在前);值可以是數字、布林或字串,省略的參數取宣告的預設值。回傳一張 結果表,鍵是結果欄位名稱:數字型別是數字,bool 是布林,enum 是標籤字串(遇到 目錄沒有的值則是數字),hex 是小寫十六進位字串;沒有結果欄位的操作回傳空表。 出錯情況:裝置沒有目錄、沒有這個操作、參數不合目錄宣告(訊息含參數名稱與原因)、 裝置回傳的位元組與目錄不符、硬體交易本身失敗。只作用在這支腳本自己的匯流排。

int

裝置的 INT 輸入腳位(邊緣偵測)。腳本結束時,這支腳本設定的 INT 會自動還原, 不影響 Plugin 或指令列設定的同一支腳位。

int.config(edge[, pull])

int.config("falling", "up") -- 偵測下降沿,內部上拉 int.config("none") -- 提早釋放這支腳本的設定

edge 是 "falling"、"rising"、"both" 或 "none";pull 是 "up"、 "down" 或 "none"(預設)。多個使用者(腳本、Plugin、指令列)同時設定時, 實際套用的是合併結果:邊緣取聯集,上下拉取第一個非 "none" 者。

int.status()

local s = int.status() print(s.edge, s.pull, s.level, s.count, s.dropped, s.last_t_us)

回傳一張表:目前套用的 edge/pull(字串)、腳位電位 level(0/1)、 累計邊緣數 count、累計丟棄數 dropped、最近一次邊緣的裝置時間 last_t_us(微秒, 會繞回)。

int.events([max])

local ev, dropped = int.events() for _, e in ipairs(ev) do print(e.t_us, e.count, e.level) end

取走這支腳本自己的游標之後的事件(從 int.config() 之後發生的邊緣起算),不等待。回傳兩個值:事件陣列(每筆有 t_us、 count、level)與這段期間的丟棄數 dropped。max 預設 64、最多 256。 腳本執行期間背景輪詢不會運作,所以每次呼叫都會先向裝置取一次最新事件。

int.clear()

int.clear()

清除裝置與程式端的 INT 計數和事件。

poll

這兩個函式跟畫面上手動下 poll on/poll off 指令是同一個開關——作用是 整個程式,不是只有這支腳本,而且不會隨腳本結束自動復原。⚠ 這兩個函式 在腳本執行期間沒有任何可觀察的效果:背景輪詢本來就不會在腳本執行期間運作, 跟這個開關是開是關無關;它們唯一有意義的效果,是腳本結束後背景輪詢是開是關。 細節見輪詢與監看。

poll.on()

poll.on()

恢復背景輪詢。無參數,不會失敗。

poll.off()

poll.off()

暫停背景輪詢。無參數,不會失敗。⚠ 沒有自動配對的 poll.on():腳本若在關閉 之後才出錯或被中止,輪詢會維持關閉,要再手動或用下一支腳本開回來。

watch

watch.add(offset [, slot])

watch.add(0x40) -- 加進目前分頁的監看清單 watch.add(0x40, 1) -- 加進「掛載槽 1」自己的監看清單

把一個位移加入監看清單。offset:065535;slot(可省略):031。 ⚠ 清單已滿、或加入的 (offset, slot) 組合已經存在,都不會讓腳本出錯, 只在 Log 留一筆 WARN,呼叫完了但清單裡好像沒東西,記得去 Log 找原因。

  • 出錯情況:offset/slot 不是整數或超出範圍;帶非 0 的 slot 時,那個 掛載槽必須已經設定過(掛載或 i2c.addr()),否則直接讓腳本出錯並中止。

watch.remove(offset [, slot])

watch.remove(0x40) -- 從目前分頁的監看清單移除 watch.remove(0x40, 1) -- 從「掛載槽 1」自己的監看清單移除

把一個位移從監看清單移除。offset:065535;slot(可省略):031, 語意與 watch.add(offset, slot) 對稱——省略 slot 只作用在第 0 個掛載自己的 清單;帶 slot 只移除那個掛載自己的項目。清單裡沒有這一筆(offset/slot 不對,但那個掛載槽本身有設定過)安靜地什麼都不做,不會讓腳本出錯。

  • 出錯情況:offset/slot 不是整數或超出範圍;帶非 0 的 slot 時,那個 掛載槽必須已經設定過,否則直接讓腳本出錯並中止(與 watch.add() 同一個 判準——未設定的掛載槽不會被靜默當成「沒有這一筆」放行)。

watch.clear()

watch.clear()

清空目前分頁的監看清單(含全部掛載槽的項目),不需要參數,不會失敗。

ic

ic.name()

local name = ic.name() -- 沒有掛載 Plugin 時是空字串 ""

回傳目前分頁掛載的 IC/Plugin 名稱(字串);沒有掛載任何東西時是空字串 "",不是特地的一個詞——只有在連掛載設定本身都讀不到(幾乎不會發生)這種 內部異常情況,才會退回固定字串 "none"。判斷「有沒有掛載」請比對空字串, 不要比對 "none"。

ic.addr()

local addr = ic.addr() -- 沒有掛載時是 0

回傳目前分頁使用的 7-bit Slave 位址(0~0x7F 之間的數字);沒有掛載時 回傳 0。

file

file.append(name, str)

file.append("result.txt", "run 1: PASS\n")

把字串附加寫入目前資料夾(執行時的工作目錄)底下 logs/ 資料夾裡的一個 檔案;檔案不存在時自動建立,logs/ 資料夾不存在也會自動建立。name 限定 英數字、.、_、-,長度 1~64 個字元,不能以 . 開頭或結尾,也不能是 Windows 保留檔名(CON、PRN、COM1…)。str 二進位安全,原樣寫入。 出錯情況:檔名不合法、目錄/檔案無法開啟或寫入失敗。

全域函式

log(level, message)

log("info", "hello from Lua")

把一行訊息寫進 Log;level 可為 info、warn、error、debug、trace、 ic,大小寫需完全相符。出錯情況:level 不是這六個字串之一("invalid log level")。

check(cond [, msg])

check(1 + 1 == 2, "1 + 1 == 2")

記錄一次判定:cond 成立寫 CHECK OK: <msg>,不成立寫 CHECK FAIL: <msg> 並讓失敗計數加一。不論成功或失敗都會繼續往下執行。msg 省略時固定為 "check"。回傳 cond 本身,可以寫成 if check(x, "...") then。細節見 判定與結束摘要。

fail([msg])

fail("device did not respond")

記錄一次判定失敗並立即結束腳本,後面的程式碼不會再執行。msg 省略時固定為 "fail()"。

print(…)

print("value =", 42)

把每個引數轉成字串、以定位字元(Tab)相接後寫進 Log(等同 log("info", ...))。

delay_ms(ms)

delay_ms(50) -- 暫停 50 毫秒

暫停指定的毫秒數,且能被使用者中止(Esc/Ctrl+C,見等待與中止)。 ms 必須是非負數;負數會出錯。

dump([slot])

local t = dump() -- 印出並回傳目前分頁 256 個位元組的內容

讀出並列印 256 個位元組的暫存器表,同時把內容回傳成一個 t[1]t[256] 的 table。slot(可省略):031;帶非 0 的 slot 時,那個掛載槽必須已經 設定過,否則出錯。

dump_all([slot [, count]])

local path = dump_all() -- 依 Bank 逐頁傾印,回傳寫出的檔案路徑 local path2 = dump_all(0, 4) -- 顯式指定傾印 4 頁(遮罩推不出頁數時尤其需要)

依 Bank 逐頁傾印暫存器內容,寫成**目前資料夾(執行時的工作目錄)**底下 logs/ 資料夾裡的一個檔案(檔名帶執行當下的時間戳記),回傳寫出的檔案 相對路徑(logs/dump_...txt 這種形式,不是絕對路徑)。slot(可省略, 預設 0):031;count(可省略):整數 1256,顯式指定要傾印 幾頁,蓋過依遮罩推算出的頁數。

  • 這個掛載沒設定過頁碼暫存器時(沒呼叫過 i2c.bank_reg(),也沒下過 bank reg):省略 count(或給 1)照樣成功,只當成「這顆 IC 沒有 Bank」傾印目前這一頁(1 頁)。給大於 1 的 count 會出錯,不是靜默 只傾印 1 頁卻回報成功——沒有頁碼暫存器就沒有「第 2 頁」可以切換,回報 成功但實際只寫了一部分頁數會讓你誤以為拿到了完整的 count 頁。
  • 設定過頁碼暫存器、遮罩小於 0xFF:頁數由遮罩推算(例如遮罩 0x03 代表 2 個位元、共 4 頁),省略 count 就用這個推算值;顯式給 count 則 蓋過推算值,但不能超過遮罩允許的頁數上限,超過會出錯。
  • 設定過頁碼暫存器、但遮罩是 0xFF(等於「整個位元組都是頁碼,範圍 沒收窄」,例如Bank 切換教的 i2c.bank_reg(0xF0, 0xFF)): 單看遮罩本身無法推算共有幾頁,這時必須顯式給 count(例如 dump_all(0, 256) 傾印滿 256 頁,或依你實際的硬體規格給一個更小的數字) ——省略 count 仍然出錯("cannot infer bank count (mask is 0xFF)")。
  • count 本身不是整數、小於 1 或大於 256,一律出錯。
  • 部分頁面讀取失敗時,已完成的部分仍會被寫進檔案,但這次呼叫本身會回報 錯誤(訊息含檔案路徑)。

細節見記錄與傾印。

timestamp()

local ts = timestamp() -- 例如 "20260928_221030"

回傳目前時間,格式為 YYYYMMDD_HHMMSS(固定 15 個字元)。

dofile(path)

dofile("helpers.lua")

載入並執行另一個 Lua 檔,回傳該檔的回傳值。路徑可含中文等非 ASCII 字元;檔案不存在或有語法錯誤時丟出錯誤,腳本因此結束。

loadfile(path)

local f, err = loadfile("helpers.lua") if f then f() end

只載入、不執行:成功回傳編譯好的函式,失敗回傳 nil 與錯誤訊息(不丟錯誤)。路徑規則同 dofile。

bit

bit.* 系列函式一律把數字當成剛好 32 個位元處理。

bit.tobit(x)

bit.tobit(0x100000001) -- 1:超過 32 位元的部分被丟掉

把數字轉成有號 32-bit 整數(超過 32 位元的高位直接捨棄)。

bit.tohex(x [, n])

bit.tohex(0xAB, -2) -- "AB"

把數字轉成十六進位字串;n 為負值時輸出大寫,數字大小代表要印幾位。

bit.bnot(x)

bit.bnot(0) -- -1

位元反相(NOT)。結果可能是負數,細節與原因見位元運算的 「為什麼結果是負數」一節。

bit.band(x1 [, x2…])

bit.band(0xFF, 0x0F) -- 0x0F:只留下低 4 位

位元 AND(可以傳多個引數,逐一相 AND)。

bit.bor(x1 [, x2…])

bit.bor(0x10, 0x01) -- 0x11

位元 OR(可以傳多個引數,逐一相 OR)。

bit.bxor(x1 [, x2…])

bit.bxor(0xFF, 0x0F) -- 0xF0:翻轉低 4 位

位元 XOR(可以傳多個引數,逐一相 XOR)。

bit.lshift(x, n)

bit.lshift(1, 4) -- 0x10

邏輯左移 n 格,左端溢出的位元直接捨棄,右端補 0。n 通常是 0~31。

bit.rshift(x, n)

bit.rshift(0x80, 4) -- 0x08

邏輯右移 n 格,左端補 0。常搭配 bit.band 用來讀出某個欄位,見 位元運算。

bit.arshift(x, n)

bit.arshift(-8, 1) -- -4

算術右移,左端補符號位(與 bit.rshift 的差別只在左端補的是 0 還是符號 位)。

bit.rol(x, n)

bit.tohex(bit.rol(0x12345678, 8)) -- "34567812"

位元循環左移:從左端溢出的位元繞回右端補上,不會丟失任何位元。

bit.ror(x, n)

bit.tohex(bit.ror(0x12345678, 8)) -- "78123456"

位元循環右移:從右端溢出的位元繞回左端補上,不會丟失任何位元。

bit.bswap(x)

bit.tohex(bit.bswap(0x12345678)) -- "78563412"

交換 4 個位元組的順序(大小端互換)。