Node-RED: 1. čtení dat z teploměrů přes Ethernet a RS485

V tomto návodu si ukážeme jak pomocí Node-RED přečíst (1) teplotu z našich webových stránek, (2) měření provedená ethernetovým senzorem TH2E a (3) teploty ze dvou teploměrů TQS4 připojených pomocí RS485.

Co je to Node-RED?

Node-RED je velmi schopný automatizační nástroj pro zpracování a zobrazení veličin a stavů. Může být "mozkem" malé automatizace nebo řízení. Umožňuje propojit nejrůznější zařízení a systémy a zpracovávat data z nich. Jde o tzv. programování řízené událostmi, takže je předurčeno proto, aby událost jako sepnutí kontaktu, dosažení teploty, přijetí zprávy nebo paketu, apod. spustila jednu nebo několik dalších událostí, vyhodnocení, databázových operací, atd.

Pomocí grafického rozhraní ve webovém prohlížeči si v uživatel může snadno nadefinovat celou automatizaci a pak sledovat jak pěkně vše funguje, nechat si posílat e-maily o mimořádnostech, apod. Node-RED je možné provozovat na různých systémech - na Windows, na Linuxu a tedy i na oblíbeném mini PC Raspberry Pi.

Node-RED v uvedených příkladech běží na Raspberry Pi 2B. V příkladech počítáme s tím, že Raspberry Pi je připojeno k počítačové síti Ethernet (resp. k WiFi, pokud máte Raspberry Pi Zero W, Raspberry Pi 3 nebo novější).

Instalace Node-RED

Pro instalaci Node-RED na Rapberry Pi 3 doporučujeme tento návod od tvůrců Node-RED. Případně pokud chcete vyzkoušet Node-RED na svém PC s Windows, návod je zde.

Po instalaci je Node-RED dostupný ve výchozím nastavení na adrese počítače a portu 1880. Tedy například na http://192.168.1.254:1880. Pokud jste nainstalovali Node-RED do Windows, můžete použít adresu http://localhost:1880.

Čtení teploty z našeho webu

Nejjednodušší způsob jak si vyzkoušet čtení teploty je sledovat teploměr na našem webu. Strojově čitelná hodnota je ve formátu XML na této adrese: https://papouch.com/tme.asp  Skript generuje podobné XML, jako náš ethernetový teploměr TME. Obsah souboru vypadá takto:

<root xmlns="http://www.papouch.com/xml/TME/act">
<sns id="1" type="4" location="U Papoucha" status="0" hi="0" lo="0" unit="0" val="69" min="-9999" max="9999" />
<status location="U Papoucha" mac="" />
</root>

V tagu sns, v atributu val je aktuálně naměřená venkovní teplota vynásobená deseti. V příkladu je hodnota 69, což znamená teplotu 6,9 °C. Na následujícím obrázku je grafická podoba příkladu čtení teploty a výstupu do konzole v rozhraní Node-RED:

Základní příklad čtení teploty z dat ve formátu XML pomocí Node-RED

  • První modrý prvek (nod) s titulkem Web slouží pouze k odstartování měření. To provedete kliknutím na zakulacené tlačítko vlevo od textu Web. Tím spustíte řetězec událostí, který je představovaný následujícími nody.
  • Druhý nod GET Pap stáhne z našeho webu aktuální XML soubor s teplotou a předá jej dále.
  • Třetí nod to XML převede data na XML objekt a předá jej dále.
  • Čtvrtý nod Get values je jednoduchá funkce v jazyce JavaScript, která teplotu z XML převede na desetinné číslo. Výstupem je teplota ve stupních Celsia.
  • Pátý nod output vše co mu přijde vypíše do boční konzole debug, jak je vidět na obrázku.

Příklad z obrázku (tzv. flow) si můžete velice snadno vyzkoušet i ve svém Node-RED. Stačí následující kód zkopírovat do schránky a vložit do Vašeho Node-RED pomocí Menu > Import > Clipboard.

[{"id":"1a949f52.085419","type":"inject","z":"4fb0c5c4.cb66cc","name":"Web","topic":"TH2E","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":"2","x":650,"y":340,"wires":[["2cc06013.ed74e"]]},{"id":"2cc06013.ed74e","type":"http request","z":"4fb0c5c4.cb66cc","name":"GET Pap","method":"GET","ret":"txt","url":"https://papouch.com/tme.asp","tls":"","x":800,"y":340,"wires":[["3a66564d.8296fa"]]},{"id":"3a66564d.8296fa","type":"xml","z":"4fb0c5c4.cb66cc","name":"to XML","property":"payload","attr":"","chr":"","x":950,"y":340,"wires":[["78ea4c6e.b541d4"]]},{"id":"78ea4c6e.b541d4","type":"function","z":"4fb0c5c4.cb66cc","name":"Get values","func":"msg.topic = \"WEB\";\nmsg.payload = parseFloat(msg.payload.root.sns[0].$.val) / 10;\n\nreturn msg;","outputs":1,"noerr":0,"x":810,"y":420,"wires":[["44984944.e3447"]]},{"id":"44984944.e3447","type":"debug","z":"4fb0c5c4.cb66cc","name":"output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":970,"y":420,"wires":[]}]

Jednotlivé nody mají každý svoje parametry, případně obsahují kód napsaný v programovacím jazyce JavaScript. Poklepáním na každý z nodů se otevře editace jeho parametrů.

Změny provedené na pracovní ploše Node-RED je potřeba uždy uložit červeným tlačítkem Deploy vpravo nahoře na pracovní ploše.

Čtení teploty a vlhkosti z TH2E

TH2E je ethernetový senzor, který umí měřit teplotu a vlhkost a počítat rosný bod. Aktuální údaje jsou k dispozici v XML souboru fresh.xml. My chceme pomocí Node-RED na řádcích sns přečíst atributy status (stav naměřené hodnoty) a val (naměřená hodnota).

<root xmlns="http://www.papouch.com/xml/th2e/act">
<sns id="1" type="1" status="0" unit="0" val="0.8" w-min="" w-max="" e-min-val="-14.9" e-max-val="37.0" e-min-dte="01/11/2017 07:30:35" e-max-dte="06/30/2019 17:10:59" />
<sns id="2" type="2" status="0" unit="3" val="77.5" w-min="" w-max="" e-min-val="17.3" e-max-val="100.0" e-min-dte="06/30/2019 17:20:34" e-max-dte="10/15/2015 22:15:22" />
<sns id="3" type="3" status="0" unit="0" val="-2.7" w-min="" w-max="" e-min-val="-16.5" e-max-val="22.2" e-min-dte="02/27/2018 15:02:52" e-max-dte="08/03/2017 18:49:53" />
<status frm="1" location="U Papoucha" time="12/30/2019 10:53:23" />
</root>

Na následujícím obrázku je grafická podoba příkladu čtení hodnot z TH2E a výstupu naměřených údajů do konzole (debug):

 

Příklad čtení hodnot ve formátu XML z TH2E pomocí Node-RED

  • První nod Run! odstartuje měření.
  • Druhý nod GET TH2E přečte aktuální hodnoty z TH2E a předá data dále.
  • Třetí nod to XML převede odpověď z TH2E na XML objekt a předá jej dále.
  • Čtvrtý nod Get values je funkce, která zjistí zda je dostupné aktuální měření a vytvoří objekt s výstupními hodnotami teploty, vlhkosti a rosného bodu.
  • Pátý nod output vše co mu přijde vypíše do boční konzole debug, jak je vidět na obrázku.

Příklad si také můžete vyzkoušet vložením následujícího kódu do Vašeho Node-RED přes Menu > Import > Clipboard.

[{"id":"4fb0c5c4.cb66cc","type":"tab","label":"TH2E","disabled":false,"info":""},{"id":"1a949f52.085419","type":"inject","z":"4fb0c5c4.cb66cc","name":"Run!","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":"2","x":210,"y":80,"wires":[["2cc06013.ed74e"]]},{"id":"2cc06013.ed74e","type":"http request","z":"4fb0c5c4.cb66cc","name":"GET TH2E","method":"GET","ret":"txt","url":"http://192.168.1.123/fresh.xml","tls":"","x":370,"y":80,"wires":[["3a66564d.8296fa"]]},{"id":"3a66564d.8296fa","type":"xml","z":"4fb0c5c4.cb66cc","name":"to XML","property":"payload","attr":"","chr":"","x":530,"y":80,"wires":[["78ea4c6e.b541d4"]]},{"id":"78ea4c6e.b541d4","type":"function","z":"4fb0c5c4.cb66cc","name":"Get values","func":"// Nody sns z XML si vložíme do samostatné proměnné (pole)\n// Veličiny v poli jsou vždy v pořadí teplota, vlhkost, rosný bod\nlet sns = msg.payload.root.sns;\n\n// Pokud hodnota teploty není aktuální, vyhlásíme chybu a ukončíme zpracování. (Není-li dostupná teplota, nejsou dostupné ani ostatní veličiny.)\nif (sns[0].$.status != \"0\") {\n    node.error(`Měření není dostupné!`);\n    return;\n}\n\n// Vytvoříme si pracovní objekt values a vložíme do nej teplotu\nlet values = {};\nvalues.temperature = parseFloat(sns[0].$.val);\n\n// Pokud je dostupná vlhkost, vložíme ji do objektu values\nif (sns[1].$.status == \"0\") values.humidity = parseFloat(sns[1].$.val);\n\n// Pokud je dostupný rosný bod, vložíme jej do objektu values\nif (sns[2].$.status == \"0\") values.dewpoint = parseFloat(sns[2].$.val);\n\n// Naplníme výstupní objekt msg zjištěnými hodnotami a \"pošleme dál\"\nmsg.payload = values;\nmsg.topic = \"TH2E values\";\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":160,"wires":[["44984944.e3447"]]},{"id":"44984944.e3447","type":"debug","z":"4fb0c5c4.cb66cc","name":"output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":490,"y":160,"wires":[]}]

K vyzkoušení tohoto příkladu budete potřebovat vlastní senzor TH2E. Možná také budete potřebovat změnit IP adresu teploměru v nodu GET TH2E. Stačí na něj poklepat a změnit IP adresu v položce URL.

Tip: Chcete, aby se čtení provádělo automaticky v nastaveném intervalu? Poklepáním otevřete nastavení nodu Run!, zaškrtněte Inject once... a u položky Repeat vyberte požadovaný interval, podobně jako na následujícím obrázku. My máme na obrázku nastaveno opakování po deseti sekundách.

Řetězec za tímto nodem se provede dvě sekundy po spuštění Node-RED a pak každých deset sekund

Čtení teploty z teploměru TQS4

Teploměr TQS4 komunikuje přes RS485. V případě Node-RED na Windows to není problém - stačí použít převodník USB-RS485 nebo převodník RS232-RS485 a můžeme komunikovat. Na Raspberry Pi sice průmyslová sběrnice RS485 není, ale také se to dá snadno vyřešit:

Sběrnice RS485 na Raspberry Pi

Mezi univerzálními digitálními piny na Raspberry je sériový port v úrovních TTL. Ten lze převést na sběrnici RS485 některým z následujících způsobů:

  1. PapPi Power: Rozšiřující deska s portem RS485 a spínaným zdrojem, který umožňuje napájet Raspberry ze zdroje s napětím 8 až 30 V. Napájení i RS485 se připojuje svorkovnicí.
  2. Spojenými převodníky TTL232 a TC485.

Komunikační protokol Modbus RTU

Zapojení tedy máme vyřešené. Jakým protokolem budeme hodnoty z teploměru číst? V případě teploměru TQS4 budeme používat ke čtení protokol Modbus RTU. Do tohoto protokolu je potřeba teploměr přepnout buď programem ModbusConfigurator (pro Windows) nebo snadněji zkratováním propojky CFG přímo na elektronice teploměru. Když je při zapnutí teploměru tato propojka zkratována, teploměr blikne po zapnutí červeně, což znamená, že očekává komunikaci v protokolu Modbus RTU.

Komunikace s více zařízeními na RS485

Pokud budete chtít komunikovat s více teploměry najednou, je potřeba každému z nich nastavit jinou komunikační adresu (id) výše zmíněným programem ModbusConfigurator (pro Windows). Z výroby má totiž každý teploměr nastavenou adresu "1" (49 dekadicky, 0x31 hexadecimálně).

Modbus RTU v Node-RED

Node-RED ve výchozím stavu neobsahuje funkčnost pro komunikaci protokolem Modbus. Funkce (nody) pro komunikaci Modbusem jdou velice snadno doplnit. Postup je následující:

  1. V menu Node-RED vyberte položku Menu > Settings > Palette a přejděte na záložku Install.
  2. Do pole search modules zadejte node-red-contrib-modbus.

  3. Pravděpodobně hned první položka ve vyhledávání bude mít tento název, podobně jako na obrázku výše. Klepněte na tlačítko install.
  4. Možná budete vyzváni k restartu Node-RED.
  5. Tím je instalace Modbusu hotová a Node-RED už umí pomocí Modbusu komunikovat. Další informace a dokumentace nodu node-red-contrib-modbus je zde.

Příklad

V následujícím příkladu si ukážeme jak pomocí Modbusu RTU číst teplotu ze dvou připojených teploměrů TQS4.

Teploměr TQS4 má teplotu k dispozici v Input Registeru ve druhém registru (adresa 1). To, zda je údaj v tomto registru platný jde poznat podle registru status (adresa 0). Budeme tedy číst dva registry od adresy 0. (Detailní popis Modbusu v TQS4 je uveden v dokumentaci.) Input Register budeme číst pomocí funkce Read input register s funkčním kódem 4.

Naše řešení je vidět na následujícím obrázku.

Flow s příkladem čtení více teploměrů TQS4 přes Modbus RTU pomocí modbus-flex-getter v Node-RED

Jak příklad funguje?

  1. Startuje se pro jednoduchost opět manuálně tlačítkem na začátku.
  2. Nod Prepare obsahuje funkci v JavaScriptu, která připraví pro následující nod RTU objekt s parametry, které chceme přečíst přes Modbus. Pokud jde o první čtení v pořadí, jako adresa zařízení na RS485 bude použita hodnota 49 (výchozí adresa TQS4).
    Pokud jde o některé z následujících čtení - to se pozná podle formátu dat ve vstupním objektu msg -, zvýší se id (adresa) zařízení, ze kterého chceme číst o jedničku (tzn. na 50 při druhém čtení). Takto jde přečíst několik teploměrů, které mají nastavené adresy v řadě za sebou. Funkce také obsahuje podmínku při které adrese se má čtení zastavit.
  3. V nodu RTU (modbus-flex-getter) jsou nastavené veškeré parametry komunikace přes RS485. To znamená která sériová linka se má používat (my na Raspberry Pi používáme /dev/ttyAMA0) a také parametry sériové komunikace (9600 Bd, 8 bitů, 1 stopbit, bez parity). Nod RTU zpracuje přijatý objekt a přečte zadaný rozsah registrů ze zařízení.
  4. Přijatá data odešle do nodu Convert, což je funkce, která pokud je k dispozici platná aktuální teplota, převede údaj na číslo ve stupních Celsia.
  5. Stejná data jsou smyčkou se zpožděním 500 ms poslána zpět do Prepare, aby mohlo být hned jako další přečteno následující zařízení.
  6. Nod join sloučí odpovědi přijaté z obou teploměrů a pošle je jako jednu zprávu do nodu output k dobrazení v konzoli.

Zde je kód příkladu pro import do Vašeho Node-RED přes Menu > Import > Clipboard:

[{"id":"5646afe8.0dccc","type":"modbus-flex-getter","z":"4fb0c5c4.cb66cc","name":"RTU","showStatusActivities":false,"showErrors":false,"logIOActivities":false,"server":"3912e8f2.e5dad8","useIOFile":false,"ioFile":"","useIOForPayload":false,"x":650,"y":160,"wires":[["d5a2adc6.942fa","8aa25129.1df158"],[]]},{"id":"f502564b.b827a8","type":"function","z":"4fb0c5c4.cb66cc","name":"Prepare","func":"let unitid = 49;        // Jako první budeme číst senzor s id 49 = 0x31 = \"1\"\n\nif (msg.input) {        // Jde o další senzor v pořadí\n    if (msg.input.queueUnitId) {\n        unitid = parseInt(msg.input.queueUnitId) + 1;\n    }\n}\n        \nif (unitid > 50) return;    // Poslední id, které chceme číst je 50\n\nmsg.payload = { \n    'fc': 4,            // Čteme Input register\n    'unitid': unitid,   // Modbus id senzoru, který chceme číst\n    'address': 0,       // Chceme číst od registru s adresou 0 (první registr)\n    'quantity': 2       // Budeme číst dva registry (status a teplotu)\n};\n\nreturn msg;","outputs":1,"noerr":0,"x":500,"y":200,"wires":[["5646afe8.0dccc"]]},{"id":"b0fa8ba2.05eb18","type":"inject","z":"4fb0c5c4.cb66cc","name":"TQS4","topic":"TQS4","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":330,"y":200,"wires":[["f502564b.b827a8"]]},{"id":"d5a2adc6.942fa","type":"function","z":"4fb0c5c4.cb66cc","name":"Convert","func":"let TqsStatus = msg.payload[0];     // První čtený registr\nlet TqsTempValue = msg.payload[1];  // Druhý čtený registr\n\nmsg.topic = msg.input.payload.unitid;\n\n// Pokud je ve status registru 0, je vše ok a druhý registr obsahuje platnou teplotu\nif (TqsStatus === 0) {\n    msg.payload = Int2Float(TqsTempValue);\n} else {\n    node.error(`Měření není dostupné!`);\n    return;\n}\n\nreturn msg;\n\n\n// Převede číslo z formátu signed integer na desetinné číslo\nfunction Int2Float(v) {\n    \n    let t;\n    \n    if (v > 32767) t = v - 65536;\n\telse t = v;\n\t\n\tt = t / 10;\n\t\n\treturn t;\n\t\n}","outputs":1,"noerr":0,"x":800,"y":140,"wires":[["b9c290cb.81cf38"]]},{"id":"8aa25129.1df158","type":"delay","z":"4fb0c5c4.cb66cc","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":810,"y":200,"wires":[["f502564b.b827a8"]]},{"id":"b9c290cb.81cf38","type":"join","z":"4fb0c5c4.cb66cc","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"1","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":950,"y":140,"wires":[["40be4d5b.a2d624"]]},{"id":"40be4d5b.a2d624","type":"debug","z":"4fb0c5c4.cb66cc","name":"output","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":1090,"y":140,"wires":[]},{"id":"3912e8f2.e5dad8","type":"modbus-client","z":"","name":"","clienttype":"serial","bufferCommands":false,"stateLogEnabled":false,"tcpHost":"127.0.0.1","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyAMA0","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"1000","unit_id":"","commandDelay":1,"clientTimeout":1000,"reconnectTimeout":3000}]

V následující části si ukážeme jak naměřená data odeslat do Google Tabulky...

Vytvořeno30.12.2019