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. V této části našeho seriálu začneme s Quidy komunikovat protokolem Modbus.
Seriál o Node-REDu |
1. čtení dat z teploměrů přes Ethernet a RS485 |
2. zápis dat do Google Tabulky |
3. automatizace s I/O moduly Quido - protokol Modbus |
4. I/O moduly Quido - protokol Spinel |
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ů.
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é.
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ě.
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}]
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.
Č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.
Dobrou zprávou je, že o změně stavu vstupu se nemusíte dozvědět se zpožděním. O kterékoli změně na vstupu se můžete dozvědět ihned, a to dvěma způsoby - u Ethernetových Quid pomocí notifikace http GETem. Hned v příštím pokračování našeho seriálu o Node-REDu si můžete přečíst o okamžitých notifikacích v protokolu Spinel.
Seriál o Node-REDu |
1. čtení dat z teploměrů přes Ethernet a RS485 |
2. zápis dat do Google Tabulky |
3. automatizace s I/O moduly Quido - protokol Modbus |
4. I/O moduly Quido - protokol Spinel |