Exec Node not giving same output as linux terminal

Hi guys, I'm brand new to Node Red and learning linux command line on the fly. I have come up with a command that returns my CPU Temp. I'm trying to implement this for an ARM64 CPU piece of hardware.

After having weird results in Node Red I decided to test this same code on my laptop running Ubuntu 20.04 with Node Red as the local host and get the same result of 0 C. This is just the start of a dashboard with lots of hardware info but I've been stumped since this works just fine when I enter it in the shell on two different systems, both debian based. Both terminals give me somewhere between 30 - 45 C which I feel is fairly reasonable.

My gut says Exec Node doesn't recognize the cputemp variable?
cputemp=$(</sys/class/thermal/thermal_zone0/temp) && echo "$((cputemp/1000)) c"

I've also tried just to read the raw data and this does work in Exec Node:
cat /sys/class/thermal/thermal_zone0/temp

Here is my Flow:

[{"id":"ae9aa0fe.aa951","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"90cedbc2.449788","type":"inject","z":"ae9aa0fe.aa951","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":140,"wires":[["daeb5567.14cd78"]]},{"id":"daeb5567.14cd78","type":"exec","z":"ae9aa0fe.aa951","command":"cputemp=$(</sys/class/thermal/thermal_zone0/temp) && echo \"$((cputemp/1000)) c\"","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"CPU Temp","x":290,"y":140,"wires":[["29dc069.cfc19fa"],[],[]]},{"id":"29dc069.cfc19fa","type":"debug","z":"ae9aa0fe.aa951","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":490,"y":140,"wires":[]}]```

please read how-to-share-code-or-flow-json

Sorry, I missed that. Corrected. Thank you.

Somehow you have ``` backticks at the end of it that is messing up importing.

[{"id":"9aae2789.323f2","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"ea9b41b9.08d6f8","type":"inject","z":"9aae2789.323f2","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":140,"wires":[["a7fb4343.890b7"]]},{"id":"a7fb4343.890b7","type":"exec","z":"9aae2789.323f2","command":"cputemp=$(</sys/class/thermal/thermal_zone0/temp) && echo \"$((cputemp/1000)) c\"","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"CPU Temp","x":290,"y":140,"wires":[["9a6de0c4.e7e3d"],[],[]]},{"id":"9a6de0c4.e7e3d","type":"debug","z":"9aae2789.323f2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":490,"y":140,"wires":[]}]

I believe your issue with the exec node is caused by the use of input redirection and/or trying to use Command Substitution.

Digging deeper now.

Thank you, appreciate the help. Open to suggestions.

The first issue you should tackle when using node-red's exec node is to break up any piped and/or chain commands. As the last command executed will give you your Standard output and Standard error.

I'm going to walk you through how my brain tries to find a bug. I'm sure your higher up in skill than how I'm going to answer this issue. However I'm writing this for every one from the lowest level of understanding up.

In the exec node I changed:

cputemp=$(</sys/class/thermal/thermal_zone0/temp) && echo "$((cputemp/1000)) c"



and I get no error.

As with your original command we get 0 c I think to myself "what is the value of $cputemp".
so I changed the exec node to:

cputemp=$(</sys/class/thermal/thermal_zone0/temp) && echo "$cputemp"

and I get an empty string. I try the same/above command in terminal and get a value of 40780
so I think the exec node is not able to use input redirection

So I try calling cat in the exec node:

cputemp=$(cat /sys/class/thermal/thermal_zone0/temp) && echo "$cputemp"

and I still get an empty string. I also try the above command in terminal and get a value of 40723

So my brain now thinks: "OK the exec node does not support Command Substitution" now what do I do.

Well as you stated the exec node does output the raw value for cpu temp with:

cat /sys/class/thermal/thermal_zone0/temp

I would just do the math in a function node:

Import this flow:

[{"id":"9aae2789.323f2","type":"tab","label":"Tempature C","disabled":false,"info":""},{"id":"1abdc1a4.ce76a6","type":"inject","z":"9aae2789.323f2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":540,"wires":[["6ae64401.52c0d4"]]},{"id":"6ae64401.52c0d4","type":"exec","z":"9aae2789.323f2","command":"cat /sys/class/thermal/thermal_zone0/temp","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"cat /sys/class/thermal/thermal_zone0/temp","x":390,"y":540,"wires":[["7746bdf6.05a88c"],["7365596f.a61ab"],[]]},{"id":"7365596f.a61ab","type":"debug","z":"9aae2789.323f2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":490,"y":600,"wires":[]},{"id":"f740e654.a85ef8","type":"function","z":"9aae2789.323f2","name":"Cahnge CPU temp to celsius","func":"var raw_temp = msg.payload;\n\n// .toFixed(2) number of digits after the decimal point\nmsg.payload = (raw_temp / 1000).toFixed(2);\n\n\nreturn msg;","outputs":1,"noerr":0,"x":980,"y":540,"wires":[["f55bcc47.2a19d"]]},{"id":"7746bdf6.05a88c","type":"function","z":"9aae2789.323f2","name":"remove carrage return","func":"msg.payload = msg.payload.replace(/\\n/g,\"\");\nreturn msg;","outputs":1,"noerr":0,"x":720,"y":540,"wires":[["f740e654.a85ef8"]]},{"id":"f55bcc47.2a19d","type":"debug","z":"9aae2789.323f2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1210,"y":540,"wires":[]}]

HOLD UP!!!!!!!

old-style backquote method of command substitution works. Go figure.
When the old-style backquote form of substitution is used, backslash retains its literal meaning

So I use:

cputemp=`cat /sys/class/thermal/thermal_zone0/temp` && echo "$((cputemp/1000)) c"

in node-red exec node and I get 42 c

However I then tried input redirection:

cputemp=`</sys/class/thermal/thermal_zone0/temp` && echo "$((cputemp/1000)) c"

and it does not work.

Oh the joy of bug hunting.

Note: the command that works

cputemp=`cat /sys/class/thermal/thermal_zone0/temp` && echo "$((cputemp/1000)) c"

Does not support decimal places (floating point) I would use the function node version I created in the post above or use printf floating point precision if I need greater precision.

example of working command using printf:

cputemp=`cat /sys/class/thermal/thermal_zone0/temp` | printf %.2f "$((10**3 * $cputemp/1000))e-3"

I haven't tried it but perhaps it would be better to use a file in node to read it rather than cat.

1 Like

You are Correct! the file method works just fine......

Better to use a all node-red implementation than using exec node for this one.

import this flow:

[{"id":"e80a2f5b.7a9de8","type":"tab","label":"File Method Temp C","disabled":false,"info":""},{"id":"510da242.80f2dc","type":"file in","z":"e80a2f5b.7a9de8","name":"","filename":"/sys/class/thermal/thermal_zone0/temp","format":"utf8","chunk":false,"sendError":false,"encoding":"none","x":430,"y":420,"wires":[["49890a23.64a18c"]]},{"id":"f1d37dfc.30fbe","type":"inject","z":"e80a2f5b.7a9de8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":420,"wires":[["510da242.80f2dc"]]},{"id":"82952606.0c10d8","type":"function","z":"e80a2f5b.7a9de8","name":"Cahnge CPU temp to celsius","func":"var raw_temp = msg.payload;\n\n// .toFixed(2) number of digits after the decimal point\nmsg.payload = (raw_temp / 1000).toFixed(2);\n\n\nreturn msg;","outputs":1,"noerr":0,"x":980,"y":420,"wires":[["c626175d.9e237"]]},{"id":"49890a23.64a18c","type":"function","z":"e80a2f5b.7a9de8","name":"remove carrage return","func":"msg.payload = msg.payload.replace(/\\n/g,\"\");\nreturn msg;","outputs":1,"noerr":0,"x":720,"y":420,"wires":[["82952606.0c10d8"]]},{"id":"c626175d.9e237","type":"debug","z":"e80a2f5b.7a9de8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1210,"y":420,"wires":[]}]

Sorry I get so wrapped up in exec node I could not see the obvious in front of me. Thanks @Colin

I'm way more comfortable in bash and command line than I am in JS so I became good friends with the exec node early on :stuck_out_tongue:

I tend to use allot of command line calls in my flows :slight_smile:

I was too slow to edit NR on my tablet but here's another example using JSONata expression in a change node. No need for function nodes.

[{"id":"6da8bc3b.faa0a4","type":"inject","z":"bf210454.b85538","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":80,"y":80,"wires":[["6e3c66c6.b91638"]]},{"id":"fcdb79b4.82ece8","type":"debug","z":"bf210454.b85538","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":450,"y":140,"wires":[]},{"id":"729bdb8a.a6fa44","type":"change","z":"bf210454.b85538","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number($trim(msg.payload))/ 1000 & '°C'","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":280,"y":140,"wires":[["fcdb79b4.82ece8"]]},{"id":"6e3c66c6.b91638","type":"file in","z":"bf210454.b85538","name":"","filename":"/sys/class/thermal/thermal_zone0/temp","format":"utf8","chunk":false,"sendError":false,"encoding":"none","x":360,"y":80,"wires":[["729bdb8a.a6fa44"]]}]
1 Like

@ristomatti So many ways to skin this cat......loving it :heart_eyes:

The original command from @MrRum likely did not work as it uses shell features. If insisted, it should work if it was a separate script to exec. Let's say read_temp.sh:


cputemp=$(</sys/class/thermal/thermal_zone0/temp) && echo "$((cputemp/1000)) c"

# or alternate easier to read version
echo "$((cputemp/1000)) c"

Change the script permissions to executable:

chmod u+x read_temp.sh

Then running read_temp.sh in an exec node.

...but if course this maksa no sense as the Node-RED version is so much cleaner. :slightly_smiling_face:

Thanks @meeki007, @ristomatti, Colin for the help. As you can probably tell I don’t have a CS background but I’ve been on the hardware side for many years and I’ve hacked other things together in the past but not using Node Red or Linux.

Since the primary function of the device is to collect data / protocol converter I don’t want or need the dashboard to be super flashy or resource intensive, so the simpler the better.

I definitely like the simple flow and file suggestion which if needed I could pull later for a snap shot if there is an issue.

Thanks again and I’ll work on adding the other HW info and will post when I have a more comprehensive flow. Cheers!

So here is what I got so far. I haven't figured out how I'm going to add Send and Receive Network Traffic yet.

Any suggestions I would greatly appreciate it. I loaded the LXDE desktop on this device and ran gnome-system-monitor and the Node Red Dashboard Outputs for CPU0/1, Mem, and Disc are extremely close.

I will also be modifying this for the QuadCore version of the IOT2040 as well.

Siemens IOT2050 Dual Core.json (9.1 KB)

For my system dashboard network stats, I use this node; https://flows.nodered.org/node/node-red-contrib-linux-network-stats

Thanks, installed. How do I implement it? I did a quick search and all I got was the github repository.

Here is an example flow to get you started.


How you refine it is up to your use case.

What is the name of the Node I need to install? Your Network Stats Node. Thank you...

The node is named and linked here; Exec Node not giving same output as linux terminal

Or do I misunderstand your question?