AN18 Ovládání sériového portu (RS-232) z Lua
Tagy: 
M2M API

Aplikační poznámka AN18 obsahuje Lua skript, který obsluhuje sériový port RS-232 a ovládá zásuvky. Výsledkem je možnost vypnout a zapnout z RS-232 jednotlivé elektrické zásuvky textovým protokolem, který je stejný jako v M2M protokolu Telnet.  

AN18 ukazuje obsluhu sériového portu RS-232 (protokol otázka - odpověď) pomocí Lua skriptu. Příkazy, které ovládají napájecí výstupy lze modifikovat (Lua skript) a změnit tím protokol na sériovém portu.

 

Můžeme Vám nějak pomoci?

Podporovaná zařízení: NETIO 4C

 

NETIO model 4C obsahuje sériový port RS-232 (3 piny).

V konfiguraci zařízení lze sériový port (sériová konzole) připojit na nastavený TCP/IP port nebo přesměrovat do Lua skriptu jak to používá dále popsaný skript.

 

Lua skript bude reagovat na přijatý řetězec po sériovém portu, odešle odpověď a sepne výstup.

Unikátní vlastností je možnost v Lua skriptu přes web rozhraní měnit textový řetězec a tím i protokol na sériovém portu.

 

 

Pro práci se se sériovým portem je nutné nastavení několika jeho parametrů (baud rate, block delimiter, handshake). Samotný skript pak pomocí základních funkcí načte přijatou zprávu ze sériového portu a podle jejího tvaru provede příslušnou akci a zašle zpět odpověď. Příklad příkazu pro zapnutí výstupu 1: port 1 1

 

V Lua skriptu AN18 jsou připraveny 2 uživatelsky definované zprávy, jejichž formát a obsah příkazu je uživatelsky definovatelný.

 

Nastavení sériového portu

Ve webové aplikaci v sekci M2M API Protocols rozklikněte Serial Console a zvolte možnost Use in actions (Lua scripts).

Block delimiter nastavuje znak, který uzavře poslanou zprávu a spustí Lua skript. Lze využít z nabízených znaků (CR+LF, CR, LF, NULL) nebo pomocí možnosti custom zvolit jakýkoli vámi vybraný znak.

Při použití možnosti custom je nutné požadovaný znak vložit v hexadecimálním tvaru (před znak je nutné vložit 0x) do pole vpravo.

Při využití se pole vyplní samo podle zvolených znaků (CR+LF má hexadecimální tvar “0xd” a “0xa”)

Speed nastavuje rychlost přenosu (Baud rate).

Nastavení potvrďte kliknutím na zelené tlačítko Safe Changes.


 

Po aktivaci sériového portu se vedle záložky objeví tmavě zelená tečka a status se změní na “Connected”.

 

Vytvoření pravidla (rule)

Pro vytvoření a spuštění Lua skriptu je nutné následující:

1) Ve webové administraci NETIO 4 v sekci Actions, přidejte pravidlo pomocí tlačítka Create Rule

 

2) Vyplňte následující parametry:

  • Enable rule: zaškrtnuto
  • Name: Serial console to Lua (uživatelsky definovatelné)
  • Description: Parse message from Serial console and respond (uživatelsky definovatelné)
  • Trigger: Serial console has read line
  • Schedule: libovolný (v této konfiguraci nemá vliv na funkci)

3) Do pole pro skript v jazyce Lua zkopírujte následující kód:

 


------------Section 1------------
local shortTimeMs = 1000 -- "short" time for short on/off [ms]
local definableQuestion1 = "^all%-off$" -- prepared customer string 1
local definableQuestion2 = "^all%-on$" -- prepared customer string 2
local endingString = "\r\n" -- symbol sent at the end of each answer (CR+LF in the default setting)
---------End of Section 1---------

------------Section 2------------
-- Serial port - Telnet commands definition
local portValue = "^port %d$"
local portSet = "^port %d %d$";
local portListValue = "^port list$"
local portListSet = "^port list %d%d%d%d$"
---------End of Section 2---------

local id = event.args.id -- internal variable, do not change
local message = event.args.message -- internal variable, do not change
logf("%s",message)
local function short(output,state)
  devices.system.SetOut{output=output,value=state}
end

local function isBetween(number,lowLimit,highLimit)
  if number >= lowLimit and number <= highLimit then
    return true
  end
  return false
end

local function setOutput(output,action)
  if action == 0 then -- turn off
    devices.system.SetOut{output = output, value = false}
  elseif action == 1 then -- turn on
    devices.system.SetOut{output = output, value = true}
  elseif action == 2 then -- short off
    devices.system.SetOut{output = output, value = false}
    milliDelay(shortTimeMs,function() short(output,true) end)
  elseif action == 3 then -- short on
    devices.system.SetOut{output = output, value = true}
    milliDelay(shortTimeMs,function() short(output,false) end)   
  elseif action == 4 then -- toggle
    if devices.system["output" ..output.. "_state"] == 'on' then 
      devices.system.SetOut{output=output,value=false}
    else
      devices.system.SetOut{output=output, value=true}
    end
  elseif action == 5 then
    -- do nothing
  end
end



if string.match(message,portValue) then -- return output state
  local output = tonumber(message:sub(6,6))
  if not isBetween(output,1,4) then
    devices.system.SerialWrite{id=id,message="501 INVALID PARAMETR" .. endingString}
    return
  end
  local answer = "250 0"
  if devices.system["output" ..output.. "_state"] == 'on' then 
    answer = "250 1"
  end
  devices.system.SerialWrite{id=id,message=answer..endingString}
elseif string.match(message,portSet) then -- set output
  local output = tonumber(message:sub(6,6))
  local action = tonumber(message:sub(8,8))
  if not isBetween(output,1,4) or not isBetween(action,0,5) then
    devices.system.SerialWrite{id=id,message="501 INVALID PARAMETR" .. endingString}
    return
  end
  setOutput(output,action)
  devices.system.SerialWrite{id=id,message="250 OK" .. endingString}
elseif string.match(message,portListValue) then -- return output status
  local answer = "250 "
  for i=1,4 do
    if devices.system["output" ..i.. "_state"] == 'on' then 
      answer = answer .. "1"
    else 
      answer = answer .. "0"
    end
  end
  devices.system.SerialWrite{id=id,message=answer .. endingString}
elseif string.match(message,portListSet) then -- set outputs
  local actions = {};
  for i=1,4 do
    actions[i] = tonumber(message:sub(10+i,10+i))
    if not isBetween(actions[i],0,5) then
      devices.system.SerialWrite{id=id,message="501 INVALID PARAMETR" .. endingString}
      return
    end  
  end
  for i=1,4 do
    setOutput(i,actions[i])
  end
  devices.system.SerialWrite{id=id,message="250 OK" .. endingString}
elseif string.match(message,definableQuestion1) then
  -- command 1 start
  for i=1,4 do
    setOutput(i,0)
  end
  devices.system.SerialWrite{id=id,message="250 OK" .. endingString}
  -- command 1 end
elseif string.match(message,definableQuestion2) then
  -- command 2 start
  for i=1,4 do
    setOutput(i,1)
  end
  devices.system.SerialWrite{id=id,message="250 OK" .. endingString}
  -- command 2 end
else
  devices.system.SerialWrite{id=id,message="502 UNKNOWN COMMAND" .. endingString}
  logf("%s",message)
end

4) Tvorbu pravidla ukončete stisknutím kliknutím na tlačítko Create Rule v dolní části obrazovky.

 

Popis kódu

  • Zpráva zaslaná po sériovém portu se načte do proměnné message pomocí příkazu local message = event.args.message
  • V této zprávě se nachází vše, co bylo poslané po sériové lince bez znaku ukončující zprávu.
  • Formát zprávy se porovná s předem nastavenými vzory, popřípadě s vzory vytvořenými uživatelem (viz níže) pomocí funkce string.match. V případě shody se přesune do dané sekce a vykoná zadanou akci.

Proměnné

  • V kódu se vyskytují 2 proměnné, které je možné nastavit
  • shortTimeMs
    •  Nastavuje dobu v milisekundách, za kterou se výstup zapne/vypne ve stavech 2 respektive 3 (short off / short on)
    •  Minimální hodnota je 100ms.
    •  Příklad pro sepnutí na dobu 2 sekundy: shortTimeMs = 2000
  • endingString
    • Nastavuje symbol, který bude ukončovat všechny odpovědi zaslané po sériové lince zpět.
    • Výchozí hodnota jsou symboly CR+LF (v Lua jsou reprezentovány jako \r\n).
    • Aby se celý skript choval jako solidní protokol, je nutné tento ukončující řetězec nastavit stejný, jako je řetězec, kterým se Lua skript spouští.

Seznam základních příkazů:

  • Vzory pro tyto příkazy jsou v kódu v Section2 (proměnné portValue, portSet, portListValue, portListSet).
  • port <output> <action>
    •  Příkaz sloužící k ovládání jedné konkrétní zásuvky
    •  <output> nahraďte číslem zásuvky
    •  <action> nahraďte jedním z parametrů
    •  Pokud parametr <action> vynecháte, vypíše se stav zásuvky
    •  Parametry:
      • 0 - vypne zásuvku
      • 1 - zapne zásuvku
      • 2 - krátce vypne zásuvku (pokud byla zásuvky vypnutá, bude po provedení příkazu zapnutá)
      • 3 - krátce zapne zásuvku (pokud byla zásuvky zapnutá, bude po provedení příkazu vypnutá)
      • 4 - změní aktuální stav zásuvky
      • 5 - ponechá zásuvku beze změny
        •  Příklad zapnutí zásuvky 1: port 1 1
        •  Příklad vypnutí zásuvky 3: port 3 0
  • port list [xxxx]
    •  Příkaz sloužící k ovládání všech zásuvek najednou
    •  Bez parametrů vypíše stav zásuvek
    •  Seznam parametrů:
      • 0 - vypne zásuvku
      • 1 - zapne zásuvku
      • 2 - krátce vypne zásuvku (pokud byla zásuvky vypnutá, bude po provedení příkazu zapnutá)
      • 3 - krátce zapne zásuvku (pokud byla zásuvky zapnutá, bude po provedení příkazu vypnutá)
      • 4 - změní aktuální stav zásuvky
      • 5 - ponechá zásuvku beze změny
        •  Příklad zapnutí všech zásuvek: port list 1111
        •  Příklad zapnutí zásuvek 1,3, vypnutí zásuvky 4 a přepnutí stavu zásuvky 2: port list 1410
        •  Příklad zapnutí zásuvek 2,3 a zachování stavu zásuvek 1,4: port list 5115
    •  Poznámka: Je nutné nahradit všechny x v parametru, pokud nechcete, aby se stav zásuvky měnil, použijte jako parametr 5. Příkaz port list 10x1 nebude přijat.

 

Definice uživatelských příkazů po RS-232

Formát otázky (přijaté po RS-232)

  • Pro vytváření vlastních vzorů otázek jsou připravené proměnné definableQuestion1 a definableQuestion2.
  • Vlastní vzor musí být umístěn do uvozovek a mezi znaky ^ a $. Tato konvence zajistí, že přijatá zpráva musí přesně odpovídat otázce.
    • Příklad pro otázku all-on: definableQuestion = "^all%-on$"
  • Pokud se v otázce vyskytuje symbol, který slouží jako proměnná, je možné místo tohoto symbolu vložit zastupující symbol (. pro jakýkoli znak (char), %d specificky pro číslo).
    • Příklad z přednastavených příkazů zjišťující stav zadaného výstupu port %d: portValue = "^port %d$"
    • V otázce není možné ověřit o jaké číslo se jedná. Pokud chcete odpověď pouze pro konkrétní číslo, vložte ho klasicky do řetězce (definableQuestion1 = "^getState 1$"). Obsahuje-li otázka mezeru, zpráva ji musí obsahovat také.
  • Při vytváření vlastních otázek je nutné dát si pozor na to, aby se vzory nepřekrývaly. Například "^port %d$" a "^port 1$". V tomto případě by se při poslání znaků port 1 vykonal příkaz, který je v kódu výše.
  • Tyto znaky jsou v Lua označovány jako “magické” a při jejich použití v otázce je nutné před ně vložit symbol %
    • ( ) . % + - * ? [ ^ $
    • Příklad pro otázku all-off : definableQuestion = "^all%-off$"
  • Proces vytváření odpovědi je popsán níže.

Vytvoření odpovědi (odeslané po přijetí odpovědi)

  • Pro obě připravené proměnné, je vyhrazen kus kódu, ve kterém se vykoná příslušná odpověď. Nacházejí se v dolní části kódu (viz obrázek).

 

  • Na místo ukázkového kódu (sekce mezi komentáři) vložte váš kód s příslušnou akcí.
  • Pro jednoduší práci jsou naimplementovány pomocné funkce:
    • setOutput(output,action)
      • Funkce sloužící k změně stavu výstupu (parametr output) podle zadané akce (parametr action).
      • Parametr action může nabývat až 5 hodnot (viz popis příkazu port).
      • Příklad pro přepnutí stavu výstupu 3: setOutput(3,4)
    • isBetween(number,lowLimit,highLimit)
      • Funkce vrací true, pokud se první parametr (number) nachází mezi druhým a třetím parametrem (lowLimit a highLimit).
      • Příklady:
        • isBetween(1,1,4) vrací true
        • isBetween(0,1,4) vrací false
      • Tato funkce se používá pro kontrolu vstupních parametrů.
  • Pro poslání zprávy po sériové lince zpět je možné využít funkci devices.system.SerialWrite{id=id,message="Your message" .. endingString}
    • Na místo "Your message" je možné vložit string se zprávou (do uvozovek), popřípadě proměnnou (bez uvozovek).
    • Pomocí .. endingString se na konec zprávy přidá ukončovací řetězec. Pokud nechte zprávu končit tímto řetězcem, lze tuto konstrukci smazat (včetně dvou teček).
  • Pokud nechcete zaslat žádnou zprávu zpět, je nutné poslat alespoň ukončovací řetězec funkcí: devices.system.SerialWrite{id=id,message=endingString}

 

FAQ:

1) Můžu mít více vlastních otázek (commandů)?

Ano. Pro přidání vzoru stačí vytvořit proměnnou (např. definableMessage3), do které uložíte příslušný vzor. Do kódu je pak dále nutné přidat další podmínku do které budete moci napsat příslušnou akci. Nad poslední else vložte tuto podmínku: elseif string.match(message,definableMessage3) then

Pod tuto podmínku nyní můžete psát vaši akci. Pokud jste si zvolili jiný název proměnné, nezapomeňte ho změnit i v podmínce. Váš kód by měl nyní vypadat takto:

 

2) Nechci používat základní příkazy. Je možné je vypnout?

Stačí v kódu smazat příslušnou sekci pod funkcí setOutput. Kód by měl nyní vypadat takto

 

3) Proč je v ukázkovém příkladu ukončující řetězec “\r\n”?

V základním nastavení je jako řetězec, kterým se spouští CR+LF (viz Nastavení sériového portu). Je nutné, aby byl ukončující řetězec shodný s řetězcem, kterým se Lua skript spouští a v Lua je řetězec CR+LF reprezentován právě řetězcem “\r\n”.

 

4) Je možné získat data o naměřených hodnotách spotřeby?

Jelikož NETIO 4C neumí měřit hodnoty spotřeby, není možné zjistit spotřebu.

 

5) Dají se z Lua skriptu změnit parametry sériového portu?

Ne. Pouze v nastavení sériového portu.

 


Podporované verze FW:

3.1.0 a vyšší (Firmware archiv)


 

 

Tato Aplikační poznámka může být použita v:

Smart power socket NETIO

 

NETIO 4C

NETIO 4C je malé PDU (Power Distribution Unit) na 110/230V se čtyřmi ovládanými výstupy na IEC-320 C13 max 8A. Obsahuje Ethernet switch a 2x LAN port na připojení do sítě. Každý ze čtyř výstupů napájení lze individuálně vypnout/zapnout přes web, z mobilní aplikace nebo pomocí různých M2M API (SNMP, MQTT, XML, Modbus/TCP, ..). V NETIO 4C lze spouštět přímo v zařízení běžící zákaznické Lua skripty. Specialitou NETIO 4C je sériový port RS-232 (3-pin), který lze ovládat z Lua scriptu, nebo jako vzdálený sériový port.

Více o zařízení NETIO 4C

 

Zeptejte se na cenu nebo technické parametry

Pro otestování zařízení použijte jméno/heslo demo/demo