Node-RED: 3. automatizace s I/O moduly Quido

Po předchozích dvou dílech, kdy jsme se naučili měřit teplotu a posílat ji do Google tabulek, zkusíme tentokrát automatizaci I/O modulů Quido. Automatizace Quid se přímo nabízí, protože Quida mnozí používají pro nejrůznější automatizační a dohledové účely. Na Quidech jsou jak vstupy, tak výstupy, takže zkusíme cvakat s relátky, číst vstupy, počítat impulzy nebo měřit teplotu. 

Příklady v tomto článku jsou testovány na Quidu ETH 2/16. Když budete příklady testovat s jiným Quidem, je možné, že se setkáte se změnami vyplývajícími z jiného počtu vstupů a výstupů.

Pro většinu z těchto úkolů budeme používat protokol Modbus. Quido je Modbus Slave, takže není možné, aby dalo ihned vědět například o změně na vstupu. Proto použijeme http GET a díky němu se dozvíme o každé změně na vstupu prakticky ihned.

Použití nodů modbus-flex pro komunikaci Modbusem TCP/RTU

1. Klíčovým prvkem dnešního dílu jsou nody node-red-contrib-modbus. Hned v první ukázce budeme používat modbus-flex-write pro ovládání výstupů Quida. Podobně dále využijeme i modbus-flex-getter ke čtení stavu vstupů, teplot, apod. Instalaci nodů jsme si vysvětlili v prvním díle, takže zde již jen stručně uvedu, že je třeba pomocí Menu > Manage palette nainstalovat sadu nodů node-red-contrib-modbus.

2. Po instalaci si umístěte na pracovní plochu nod modbus-flex-write a poklepejte na něj - otevře se jeho nastavení. Důležité je políčko Server. Kliknutím na něj se otevře seznam dostupných sériových portů v systému, a také možnost Add new modbus-client... Na obrázku je vidět, že v systému (v tomto konkrétním případě jde o Raspberry Pi) je jeden sériový port, jedno rozhraní, které jsme si pojmenovali QuidoETH a jako třetí je právě volba pro přidání nového rozhraní.

Nastavení komunikačního rozhraní Modbusu

3. Vybráním možnosti Add new modbus-client... se otevře formulář s konfigurací nového připojení. Jediná nastavení, která je potřeba provést je Type: TCP a nastavit Host (IP adresa Quida) a Port (výchozí Modbus port u Quid je 502).

Nastavení připojení přes Modbus TCP

Tímto je nastavení připojení přes Modbus hotové.

Ovládání výstupů Quida v Node-RED

4. Jak vypadá ukázkový flow pro ovládání výstupů? Na obrázku už jistě poznáváte čtyři různé. Každý z příkladů jde spustit samostatně.

  • Set OUT3: Ovládání jednoho výstupu - příklad sepne OUT3
  • Set Multi: Ovládání více výstupů najednou - najednou sepne OUT1, OUT4 a OUT5 a rozepne OUT2 a OUT3
  • Set Random: Náhodné nastavení výstupů - spíše pro ukázku jak se dá pracovat se všemi výstupy najednou.
  • Set Pulse: Spuštění pulzu na výstupu - sepnutí OUT8 na 2 sec.

Ovládání výstupů na Quidu

5. Funkce, která následuje za každým ze čtyř spouštěcích tlačítek má za úkol velmi jednoduchou úlohu - vytvořit objekt s parametry pro nod Quido. Nod Quido je typu modbus-flex-write , který jsme nainstalovali v bodu 1. Stačí mu poslat tento objekt (příklad z první funkce s ovládáním výstupu OUT3):

let arr = [true];       // Pozadovany stav vystupu: true = sepnout, false = rozepnout

msg.payload = {
    value: arr,
    'fc': 15,       // Budeme zapisovat funkcnim kodem 0x0F (= 15)
    'unitid': 49,   // Vychozi Modbus id Quida (49 = 0x31)
    'address': 2,   // Id prvniho vystupu - 0=OUT1, 1=OUT2, 2=OUT3, ...
    'quantity': arr.length
}

return msg;

6. Po importu flow z obrázku do Node-REDu si můžete otevřít poklepáním jednotlivé funkce a přečíst komentované příklady.

7. Jak ale přijít na správné funkční kódy a adresy Modbusu? V manuálu Modbusu ke Quidům je přehledně popsáno jaký funkční kód je potřeba použít v konkrétním případě. Na následujících obrázcích jsou dva příklady.

Stránka v manuálu s popisem ovládání výstupů přes Modbus

Stránka v manuálu s popisem spouštění pulzu na výstupu

8. Pokud chcete nahrát příklad do svého Node-RED, vložte následující kód pomocí Menu > Import > Clipboard.

[{"id":"dc99c126247c6de0","type":"inject","z":"f10a275f06993d2e","name":"Set OUT3","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":140,"y":100,"wires":[["780bd225b71b821d"]]},{"id":"780bd225b71b821d","type":"function","z":"f10a275f06993d2e","name":"","func":"// Pozadovany stav vystupu: true = sepnout, false = rozepnout\nlet arr = [true];\n\nmsg.payload = {\n    value: arr,\n    'fc': 15,\n    'unitid': 49,   // Vychozí Modbus id Quida (49 = 0x31)\n    'address': 2,   // Id prvniho vystupu - 0=OUT1, 1=OUT2, 2=OUT3, ...\n    'quantity': arr.length\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":100,"wires":[["1c0c12114a94b773"]]},{"id":"fbd29ac0c44c42fe","type":"debug","z":"f10a275f06993d2e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":690,"y":160,"wires":[]},{"id":"1c0c12114a94b773","type":"modbus-flex-write","z":"f10a275f06993d2e","name":"Quido","showStatusActivities":true,"showErrors":true,"server":"c61e744.c393d88","emptyMsgOnFail":false,"keepMsgProperties":false,"x":510,"y":160,"wires":[["fbd29ac0c44c42fe"],[]]},{"id":"71741091775d6957","type":"inject","z":"f10a275f06993d2e","name":"Set Random","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":150,"y":220,"wires":[["84ac5a57bb92a1f5"]]},{"id":"84ac5a57bb92a1f5","type":"function","z":"f10a275f06993d2e","name":"","func":"let bits = (n,b=32) => [...Array(b)].map((x,i)=>(n>>i)&1);\nlet random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;\n\n// Vygeneruje nahodne 16bit cislo\nlet rnd = random(0, 65535);\n// Prevede nahodne cislo na pole sestnacti hodnot true/false\nlet arr = bits(rnd, 16);\n\nmsg.payload = {\n    value: arr,\n    'fc': 15,\n    'unitid': 49,\n    'address': 0,\n    'quantity': arr.length\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":220,"wires":[["1c0c12114a94b773"]]},{"id":"d2f3d9e5db36eef7","type":"inject","z":"f10a275f06993d2e","name":"Set Multi","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":140,"y":160,"wires":[["e962eb1b0f0c2e01"]]},{"id":"e962eb1b0f0c2e01","type":"function","z":"f10a275f06993d2e","name":"","func":"// true = sepnout, false = rozepnout\n// Prvni prvek pole se zapise na vystup uvedeny nize jako 'address'\n// Nastavime tedy stav prvnich peti vystupu - sepnute budou 1, 4 a 5\nlet arr = [true, false, false, true, true];\n\nmsg.payload = {\n    value: arr,\n    'fc': 15,\n    'unitid': 49,\n    'address': 0,   // Prvni prvek pole bude odpovidat prvnimu vystupu.\n    'quantity': arr.length\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":160,"wires":[["1c0c12114a94b773"]]},{"id":"dd910be3feded5e5","type":"inject","z":"f10a275f06993d2e","name":"Set Pulse","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":140,"y":280,"wires":[["45992380f47a0eca"]]},{"id":"45992380f47a0eca","type":"function","z":"f10a275f06993d2e","name":"","func":"// Priklad sepne OUT8 na 2 sec:\n// 0xFFnn = sepnout\n// 0x00nn = rozepnout\n// 0xnnXX => XX je delka sepnuti/rozepnuti v nasobcich 0.5 sec (4 = 2 sec)\nlet arr = [0xFF04];\n\nmsg.payload = {\n    value: arr,\n    'fc': 16,\n    'unitid': 49,    // Vychozí Modbus id Quida (49 = 0x31)\n    'address': 507,  // Id vystupu - 500=OUT1, ... 507=OUT8, ...\n    'quantity': 1    // Zde musi byt vzdy 1 - nelze vice najednou\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":280,"wires":[["1c0c12114a94b773"]]},{"id":"c61e744.c393d88","type":"modbus-client","name":"QuidoETH","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"192.168.2.140","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":1,"commandDelay":1,"clientTimeout":1001,"reconnectOnTimeout":true,"reconnectTimeout":2000,"parallelUnitIdsAllowed":true}]

Čtení stavu vstupů, výstupů, čítačů a teploty z Quida v Node-RED

9. Podobně jako ovládání výstupů je možné číst Modbusem z Quida různé informace. Příklad na pracovní ploše vypadá hodně podobně jako v předchozím případě, akorát s tím rozdílem, že je použit nod modbus-flex-getter.

  • Get Temps: Přečte aktuální teplotu (funguje jen pokud máte ke konektoru Sensor na Quidu připojen externí teploměr)
  • Get Outs: Přečíst stav výstupů IN1 až IN16 (pokud máte jiné Quido než se 16ti výstupy, bude třeba upravit počet čtených vstupů)
  • Get Ins: Přečíst stav vstupů IN1 a IN2 (počet je také potřeba upravit podle Vašeho Quida)
  • Get Cnts: Přečíst stav prvních dvou počítadel pulzů

Čtení teplot, stavu výstupů a vstupů a aktuálních hodnot počítadel pulzů

Na rozdíl od ovládání výstupů se do vlastnosti msg.topic ukládá typ čtené hodnoty. Ve funkci, která vyhodnocuje odpověď se podle toho jednoduše pozná o jakou hodnotu jde a teplota se převede ze signed integer na číslo se znaménkem, pak se vydělí deseti a teplota ve stupních Celsia je na světě.

10. Tento příklad si můžete do svého Node-RED vložit pomocí tohoto kódu:

[{"id":"0be2c6b7ce50be13","type":"inject","z":"f10a275f06993d2e","name":"Get Temps","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":140,"y":360,"wires":[["4a1e5144da4f8352"]]},{"id":"4a1e5144da4f8352","type":"function","z":"f10a275f06993d2e","name":"","func":"msg.topic = 'Temp'; // Identifikace pozadavku\n\nmsg.payload = {\n    'fc': 4,\n    'unitid': 49,   // Vychozí Modbus id Quida (49 = 0x31)\n    'address': 0,   // \n    'quantity': 2\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":360,"wires":[["8a91283bd3eb63db"]]},{"id":"8a91283bd3eb63db","type":"modbus-flex-getter","z":"f10a275f06993d2e","name":"Quido","showStatusActivities":true,"showErrors":true,"logIOActivities":false,"server":"c61e744.c393d88","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"x":510,"y":420,"wires":[["5f10552e6aaf5390"],[]]},{"id":"b0da426db395a528","type":"debug","z":"f10a275f06993d2e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":830,"y":420,"wires":[]},{"id":"5f10552e6aaf5390","type":"function","z":"f10a275f06993d2e","name":"","func":"if (msg.topic == 'Temp') \n    return readTemp(msg.payload);\nelse\n    return msg;\n\n\nfunction readTemp(arr) {\n    \n    let status = arr[0];\n    if (status !== 0) {   // Hodnota je platna jen pri 0\n        node.warn(\"Chyba teplomeru nebo odpojeny teplomer\");\n        return;\n    }\n    \n    let val = arr[1];   // Hodnota typu signed integer\n    if (val > 32767) val = val - 65536;     // Prevod z hodnoty signed int.\n    \n    return {\n        payload: val / 10     // Delenim deseti ziskame teplotu\n    };\n\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":670,"y":420,"wires":[["b0da426db395a528"]]},{"id":"460d72541a43c41f","type":"inject","z":"f10a275f06993d2e","name":"Get Outs","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":140,"y":420,"wires":[["7b4f92e77f65a6bc"]]},{"id":"7b4f92e77f65a6bc","type":"function","z":"f10a275f06993d2e","name":"","func":"msg.topic = 'Outs'; // Identifikace pozadavku\n\nmsg.payload = {\n    'fc': 1,\n    'unitid': 49,   // Vychozí Modbus id Quida (49 = 0x31)\n    'address': 0,   // Cist prvního vystupu...\n    'quantity': 16  // ...16 vystupu (je treba upravit podle velikosti Quida)\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":420,"wires":[["8a91283bd3eb63db"]]},{"id":"ea5c8b2041122226","type":"inject","z":"f10a275f06993d2e","name":"Get Ins","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":130,"y":480,"wires":[["d2f249d68d8bcf65"]]},{"id":"d2f249d68d8bcf65","type":"function","z":"f10a275f06993d2e","name":"","func":"msg.topic = 'Ins'; // Identifikace pozadavku\n\nmsg.payload = {\n    'fc': 2,\n    'unitid': 49,   // Vychozí Modbus id Quida (49 = 0x31)\n    'address': 0,   // Cist od prvního vstupu...\n    'quantity': 2  // ...2 vstupy (je treba upravit podle velikosti Quida)\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":480,"wires":[["8a91283bd3eb63db"]]},{"id":"c53fa2f9734519c6","type":"inject","z":"f10a275f06993d2e","name":"Get Cnts","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":140,"y":540,"wires":[["7eb7f558ee7f8df0"]]},{"id":"7eb7f558ee7f8df0","type":"function","z":"f10a275f06993d2e","name":"","func":"msg.topic = 'Cnts'; // Identifikace pozadavku\n\nmsg.payload = {\n    'fc': 3,\n    'unitid': 49,   // Vychozí Modbus id Quida (49 = 0x31)\n    'address': 100,   // Cist od prvního citace...\n    'quantity': 2  // ...2 citace (je treba upravit podle velikosti Quida)\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":540,"wires":[["8a91283bd3eb63db"]]},{"id":"c61e744.c393d88","type":"modbus-client","name":"QuidoETH","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"192.168.2.140","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":"1","commandDelay":"1","clientTimeout":"1001","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true}]

11. Někoho možná napadne: "Dobře, takhle si přečtu stavy vstupů Quida. Ale co když se chci o změně na vstupu dozvědět okamžitě?" To je dobrá připomínka! Modbusem totiž není možné poslat automatickou zprávu v okamžiku změny. Je potřeba číst periodicky, takže se o změně člověk dozví s drobným zpožděním - většinou v řádu 1 až 10 vteřin.

Samozřejmě to ale jde elegantně vyřešit. V blízké době sem doplníme další příklad, jak se dozvědět o změně na vstupu okamžitě pomocí notifikace http GETem... Zatím můžete trénovat komunikaci přes Modbus :-)

 

Vytvořeno06.10.2021