Using a python script in Node-RED to control a Raspberry PLC

Learn how to use the exec node to execute python scripts
February 2, 2023 by
Using a python script in Node-RED to control a Raspberry PLC
Boot & Work Corp. S.L., Bernat Brunet Pedra

Introduction

The best programming interface to work with Raspberry PLC is Node-RED, but it can be difficult to get used to it because the function node (one of the most used nodes) must be programmed in JavaScript, and without the right amount of knowledge can be tricky to get used to it. 

Luckily, there is a node called "exec node" that can execute commands simulating a CLI. In this post we will build a simple flow in Node-RED with the help of a python script to control some outputs based on an analog input.


Requirements


Objective of the program

The function of the program will be to read an analog input from the PLC and compare it with a threshold  value. Depending on the result, the low will keep requesting the input value until it reaches the limit. Then, a digital pin will turn on in order to notify the user.

While the program is running and the comparison has not become true yet, the flow will also keep the count on how many tries have been done since the start and will turn some digital outputs on to make the program more dynamic. simulating a custom loading display. 

The objective, as mentioned before, is to show how easy it can be to use any programming language in Node-RED and avoid as much as possible the use of JavaScript, which can be difficult to learn for new users.


Building the flow

After connecting yourself to the Raspberry PLC through SSH or similar, open Node-RED with

$ node-red-start

And point a browser at https://localhost:1880/


Executing the python script

Start dragging an inject node. Connect it to a function node, then to an analog read node from Industrial Shields and finally to an exec node:


These nodes need to be properly configured. The function node will not be modified now, but we will use it later on. The analog read node must have your model and the input you want to work with. In our case, we will be using a Raspberry PLC 58+ and input I0.7:


The exec node will be set like so:


First, the path to the python script (in our case, /home/pi/p.py). Remember to click on the Append box so we can pass arguments to the script.

Now, every time we click the inject node, the python script will be executed with an additional argument, which will be the value of the analog input.


Python script

The python script will be very simple, as this part of the post is for only demonstration purposes of the exec node.

#!/usr/bin/env python3
import sys
print(int(sys.argv[1])) 

After adding a debug node connected to the stdout output from the exec node and clicking the inject node, you will see how the value of the input is printed in the debug screen:


The value may fluctuate a bit, but as the input is not connected, it stays at 0.


Counting the tries

To count how many tries have been done, we will need a variable to remember the number. As the counter needs to be updated every time we execute the script, connect a function node after the exec node, replacing the old debug node.

Note: The objective of the post was to avoid the usage of the JavaScript language, but, as it is required to remember old variables, this job cannot be done in the same python script or with a new one without creating an additional file to save the variable.

The code in the function node will be the following:

var tries = flow.get('tries') || 0;
flow.set('tries', tries + 1);
node.warn("Number of tries: " + (tries + 1));
if (parseInt(msg.payload) == 0) {
    return msg;
}
else {
    node.warn("Threshold surpassed with a total of tries: " + (tries + 1));
}

Here, we initialize a variable called "tries", that will keep the count on the total tries until the input value surpasses a threshold (we will get to that later). Then, we save the variable again but adding 1 and we print in the debug window the total number of tries. Finally, we check if the python script returned 0 or 1, depending on the comparison result.

To reset this variable, the first function node will be used:

flow.set('tries', 0);
return msg;

With this modification, by clicking the inject node the variable "tries" will get resetted. Finally, let's add a threshold value in the python script. The script will get executed infinitely every second until the input value gets to the limit we established.

#!/usr/bin/env python3
import sys
import time
i = int(sys.argv[1])
threshold = 1000
if (i < threshold):
    print("0")
    time.sleep(1)
else:
    print("1")

After clicking the inject node, you should get something like this:



With this step, we have been able to read an analog input, send the value to a python script, do a comparison between it and a threshold, return the value to the Node-RED flow and loop again and again if necessary while keeping the count of the total tries.

This could be the end of the post, but let's take it a step further by playing with the digital output pins. THis step is only for aesthetic reasons, but it can be useful for projects that require an output configuration based on the input pins.


Extra features

To start, let's modify the last function node. What we want to do is to simulate a loading symbol using the output LEDs from the Raspberry PLC:

var tries = flow.get('tries') || 0;
flow.set('tries', tries + 1);
node.warn("Number of tries: " + (tries + 1));
if (parseInt(msg.payload) == 0) {
    msg.payload = tries % 6;
    return msg;
}
else {
    node.warn("Threshold surpassed with a total of tries: " + (tries + 1));
}

Now, msg.payload will take values from 0 to 5. With every number (0-4), the specific LED of the PLC will turn on. When the number is 5, all the LEDs will turn off and the process will repeat. This task will be done using a switch node connected right after the second function node.

The switch will have 6 different outputs. Outputs number 1 to 5 will be for every digital output in HIGH, and number 6 for all of them but in LOW mode. Take a look at the image below:


And the switch node:


Complete flow code:

[{"id":"1ca9cbdb7a453a30","type":"inject","z":"0727cd3d0291c3a3","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":100,"wires":[["a79d112cd32173ad"]]},{"id":"a79d112cd32173ad","type":"function","z":"0727cd3d0291c3a3","name":"function 1","func":"flow.set('tries', 0);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":100,"wires":[["208173a4226f1ecd"]]},{"id":"208173a4226f1ecd","type":"rpiplc-analog-read","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"I0.7","name":"","x":420,"y":100,"wires":[["7fea86886d62f14a","f7e313cfb4659f79"]]},{"id":"40d74a62cbe0aadc","type":"debug","z":"0727cd3d0291c3a3","name":"debug 2","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":400,"y":220,"wires":[]},{"id":"5f98e491e9b75172","type":"function","z":"0727cd3d0291c3a3","name":"function 3","func":"var tries = flow.get('tries') || 0;\n\nflow.set('tries', tries + 1);\n\nnode.warn(\"Number of tries: \" + (tries + 1));\n\nif (parseInt(msg.payload) == 0) {\n    msg.payload = tries % 6;\n    return msg;\n}\nelse {\n    node.warn(\"Threshold surpassed with a total of tries: \" + (tries + 1));\n}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":180,"wires":[["208173a4226f1ecd","75ede61acb554e43"]]},{"id":"7fea86886d62f14a","type":"exec","z":"0727cd3d0291c3a3","command":"python3 /home/pi/p.py","addpay":"payload","append":"","useSpawn":"false","timer":"","winHide":false,"oldrc":false,"name":"","x":200,"y":180,"wires":[["40d74a62cbe0aadc","5f98e491e9b75172"],[],[]]},{"id":"f7e313cfb4659f79","type":"debug","z":"0727cd3d0291c3a3","name":"debug 3","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":580,"y":80,"wires":[]},{"id":"75ede61acb554e43","type":"switch","z":"0727cd3d0291c3a3","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"num"},{"t":"eq","v":"4","vt":"num"},{"t":"eq","v":"5","vt":"num"}],"checkall":"false","repair":false,"outputs":6,"x":550,"y":180,"wires":[["82d25edaf09c9043"],["426f1490b7fa34ad"],["c2faa787cf4e0e7c"],["1c0f19f76a739b67"],["6882cb9a974e6f53"],["cd9e0a7da9b87c08","5f0bf1b5e0065206","f049f4a7930543b7","54b2e4aca2d3aee5","c0563b371f6f81af"]]},{"id":"426f1490b7fa34ad","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.1","value":"1","name":"","x":770,"y":140,"wires":[]},{"id":"c2faa787cf4e0e7c","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.2","value":"1","name":"","x":770,"y":180,"wires":[]},{"id":"1c0f19f76a739b67","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.3","value":"1","name":"","x":770,"y":220,"wires":[]},{"id":"6882cb9a974e6f53","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.4","value":"1","name":"","x":770,"y":260,"wires":[]},{"id":"82d25edaf09c9043","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.0","value":"1","name":"","x":770,"y":100,"wires":[]},{"id":"cd9e0a7da9b87c08","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.0","value":"0","name":"","x":760,"y":300,"wires":[]},{"id":"5f0bf1b5e0065206","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.1","value":"0","name":"","x":760,"y":340,"wires":[]},{"id":"f049f4a7930543b7","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.2","value":"0","name":"","x":760,"y":380,"wires":[]},{"id":"54b2e4aca2d3aee5","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.3","value":"0","name":"","x":760,"y":420,"wires":[]},{"id":"c0563b371f6f81af","type":"rpiplc-digital-write","z":"0727cd3d0291c3a3","rpiplc":"ac1bec80d88e6db9","pin":"Q0.4","value":"0","name":"","x":760,"y":460,"wires":[]},{"id":"ac1bec80d88e6db9","type":"rpiplc-config","model":"RPIPLC_58","name":""}]


Conclusions

As we have seen, it is not necessary to use different programming languages such as Python to work with Node-RED, but it is possible. Also, it is very simple both calling the script and reading the output of it.

Your Dynamic Snippet will be displayed here... This message is displayed because you did not provided both a filter and a template to use.


​Search in our Blog

Using a python script in Node-RED to control a Raspberry PLC
Boot & Work Corp. S.L., Bernat Brunet Pedra February 2, 2023
Share this post

Looking for your ideal Programmable Logic Controller?

Take a look at this product comparison with other industrial controllers Arduino-based. 

We are comparing inputs, outputs, communications and other features with the ones of the relevant brands.


Industrial PLC comparison >>>