How can I count the running hours of a machine (PLC, S7)

has anyone developed a flow that can count the running hours of a machine using a bit coming from Siemens PLC and emailing the hours in an excel or CSV file on specific time

hi alee,

I would recommend to calculate the running hours on the PLC and provide the value to Nodered afterwards.
Otherwise you are risking to lose information when connection is disturbed or nodered crashes.

I created a flow to collect the running hours from our machines and mail them to people here every monday at 08:00. Let me know if you get stuck.

Hi nico25,

thanks a lot for the suggestion. I have a PC onboard the machine on which I can install Node-Red. PLC is connected with the PC over ethernet so the chances of losing connections with PLC are very low. additionally, if i create an hour meter on PLC the there are two problems;

  1. the program of the PLC can be updated mistakenly and we can lose all the information.
  2. PLC clock does not measure the time accurately over a longer duration of time as it does not have a real-time clock of its own.
    I prefer to just get 1 bit as a trigger for the timer from the PLC and the rest done in PC and on node-red.
    I was having a problem connecting Siemens PLC from node-red. if you have any working flow please share.

alee

Perhaps this will work for you. You can convert the ms to whatever time format you desire for the CSV.

// NOTE:  The time calculations herein are in milliseconds

var now = new Date().getTime();
var run_indicator = msg.payload;
// Assuming msg.payload input is of type number and 1 means "ON" and 0 means "OFF"

if (typeof (context.last_time_check) === 'undefined') context.last_time_check = 0;
if (typeof (context.run_time)        === 'undefined') context.run_time        = 0;
if (typeof (context.run_state)       === 'undefined') context.run_state       = "off";

// This code operates when machine first comes on
if (run_indicator === 1 && context.run_state === "off")
{   context.run_state = "on"; // machine is on, lets remember this
    context.last_time_check = now;
    context.run_time = 0;
}    

// This code operates when machine continues to indicate it's on
if (run_indicator === 1 && context.run_state === "on") context.run_time = context.run_time + (now - context.last_time_check);

// Assuming msg.payload input is of type number and = 0 (bit) to indicate "OFF"
if (run_indicator === 0) context.run_state = "off"; // machine is off, lets remember this

node.warn("State: " + context.run_state + "  Run time: " + context.run_time)
context.last_time_check = now;
return;

Maybe we should start with your connection problem. what kind of plc do you have and how do you want to connect?

I have s7-400 on old equipment and s7-300 on new equipment connected to PC using Industrial Ethernet

thanks a lot. does it restart from 0 every time the system restarts?

That´s one of my connection:


DBs must not be optimized, otherwise it won´t work

Yes it does.
Runtime Clock

Here is the code:


[{"id":"7357d72d8f169424","type":"function","z":"f64d7463a1b093c1","name":"Runtime \\n Clock","func":"// NOTE:  The time calculations herein are in milleseconds\n\nvar now = new Date().getTime();\nvar run_indicator = msg.payload;\n\nif (typeof (context.last_time_check) === 'undefined') context.last_time_check = 0;\nif (typeof (context.run_time)        === 'undefined') context.run_time        = 0;\nif (typeof (context.run_state)       === 'undefined') context.run_state       = \"off\";\n\n// Assuming msg.payload input is of type number and 1 means \"ON\" and 0 means \"OFF\"\n\n// This code operates when machine first comes on\nif (run_indicator === 1 && context.run_state === \"off\")\n{   context.run_state = \"on\"; // machine is on, lets remember this\n    context.last_time_check = now;\n    context.run_time = 0;\n}    \n\n// This code operates when machine continues to indicate it's on\nif ((run_indicator === 1 || msg.payload === \"clock\")  && context.run_state === \"on\") context.run_time = context.run_time + (now - context.last_time_check);\n\n// Assuming msg.payload input is of type number and = 0 (bit) to indicate \"OFF\"\nif (run_indicator === 0) context.run_state = \"off\"; // machine is off, lets remember this\n\n//node.warn(\"State: \" + context.run_state + \"  Run time: \" + context.run_time)\nif (context.run_state === \"on\") node.status({ fill: \"green\", shape: \"dot\", text: \"ON \" + context.run_time })\nelse node.status({ fill: \"red\", shape: \"ring\", text: \"OFF \" + context.run_time });\n\ncontext.last_time_check = now;\nmsg = { payload: context.run_time};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":600,"y":230,"wires":[["8ecf9c23a7e0bc2f"]]},{"id":"2d7c05d5ba560ef9","type":"inject","z":"f64d7463a1b093c1","name":"Running (1)","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":430,"y":205,"wires":[["7357d72d8f169424"]]},{"id":"69b428dc9d65eeb2","type":"inject","z":"f64d7463a1b093c1","name":"Stopped (0)","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":430,"y":250,"wires":[["7357d72d8f169424"]]},{"id":"b97d859f180e66bf","type":"inject","z":"f64d7463a1b093c1","name":"Clock","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"clock","payloadType":"str","x":435,"y":295,"wires":[["7357d72d8f169424"]]},{"id":"8ecf9c23a7e0bc2f","type":"debug","z":"f64d7463a1b093c1","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":755,"y":230,"wires":[]}]

it's working great. but how to continue the counter from the last stopped value? I have used hourglass it works perfectly. the only problem that I have with it is that I want the time from the hourglass in hours and it shows in dd:hh:mm:ss:ms additionally I am want to save the hours in a CSV or Excel file

@nico25 thanks a lot I have tried this but i am getting the fault message "no connection"

Maybe you could try TSAP. Here is a sample config:

grafik

I do it with the DSM node: node-red-contrib-dsm (node) - Node-RED

Here's the flow. Just inject the setup hour meter and you can reset to zero by injecting reset

[{"id":"ea94dabbdf5b8487","type":"inject","z":"780bf582.9ae65c","name":"reset counter","props":[{"p":"payload","v":"reset","vt":"str"},{"p":"topic","v":"reset","vt":"string"}],"repeat":"","crontab":"00 00 * * *","once":false,"onceDelay":0.1,"topic":"reset","payload":"reset","payloadType":"str","x":580,"y":540,"wires":[["787eb536f8f64e14"]]},{"id":"c97ba88edc50d383","type":"inject","z":"780bf582.9ae65c","name":"setup  hour meter","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"set","payload":"{\"triggerInput\":\"payload\",\"currentState\":\"stopped\",\"states\":{\"stopped\":{\"on\":\"started\"},\"started\":{\"inc\":\"counting\",\"off\":\"stopped\"},\"counting\":{\"inc\":\"counting\",\"off\":\"stopped\"}},\"data\":{\"prev_time\":null,\"time\":0,\"seconds\":0,\"interval\":5},\"methods\":{\"init\":[\"sm.calc_time = function () {\",\"   var now = Date.now();\",\"   sm.data.time += now - sm.data.prev_time;\",\"   sm.data.prev_time = now;\",\"   sm.data.seconds = Math.round(sm.data.time / 1000);\",\"};\",\"sm.sec2hhmmss = function(t) {\",\"  var min = t/60;\",\"  var t_h = pad(parseInt(min / 60));\",\"  var t_m = pad(parseInt(min % 60));\",\"  var t_s = pad(Math.round(t % 60));\",\"  return t_h+':'+t_m+':'+t_s;\",\"};\"],\"on\":[\"if (sm.currentState === 'started') {\",\"   sm.data.prev_time = Date.now();\",\"   resume('inc', msg);\",\"}\",\"output = false;\"],\"inc\":[\"timeout.interval = setTimeout(function() {\",\"   sm.calc_time();\",\"   msg.data = sm.data;\",\"   /* msg.payload = sm.sec2hhmmss(sm.data.seconds);\",\"   node.send(msg); */\",\"   resume('inc', msg);\",\"}, sm.data.interval*1000);\",\"output = false;\"],\"off\":[\"clearTimeout(timeout.interval);\",\"sm.calc_time();\",\"msg.payload = sm.sec2hhmmss(sm.data.seconds);\"],\"reset\":[\"sm.data.time = 0;\",\"sm.data.seconds = 0;\",\"node.send(msg);\"],\"status\":{\"fill\":{\"get\":\"sm.currentState === 'counting' ? 'green' : 'grey';\"},\"shape\":\"dot\",\"text\":{\"get\":\"'time '+ sm.sec2hhmmss(sm.data.seconds);\"}}}}","payloadType":"json","x":580,"y":700,"wires":[["787eb536f8f64e14"]]},{"id":"4917bbfdc0cfa85c","type":"inject","z":"780bf582.9ae65c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"on","payloadType":"str","x":590,"y":600,"wires":[["787eb536f8f64e14"]]},{"id":"1156e7ef8bfa46ae","type":"inject","z":"780bf582.9ae65c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"off","payloadType":"str","x":590,"y":640,"wires":[["787eb536f8f64e14"]]},{"id":"787eb536f8f64e14","type":"dsm","z":"780bf582.9ae65c","name":"Total","sm_config":"","x":740,"y":620,"wires":[["078cc63621cba533"]]},{"id":"078cc63621cba533","type":"debug","z":"780bf582.9ae65c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":930,"y":600,"wires":[]}]

@jupiter8 thanks alot.
image

please see the image i am having this DSM undefined. please advise.

@jupiter8 thanks it's done.

Let's see:

  • The S7 PLCs can syncronize the time, for instance with a local NTP server, they're not supposed to measure it internally (only as a last resort). This is a screenshot from one of my PLC's Device Configuration > Properties screen, you'd just have to enable NTP server and connect to your local one (either the gateway, or your main network server):

  • Another option (probably easier) is to sync the HMI to an NTP server (easy, if you're using a Simatic Comfort HMI, they run on Windows CE), then tell the PLC to sync its clock with the HMI one.

Both options will give you an exact time calculation on the PLC. If you do the calculation PLC-side and store the result in a persistent tag, you can read it from NR and back it up.

In the rare event that someone broke the program as you mentioned, you could always use TIA Portal to go online and initialize the value manually. It would take about 2 minutes at most, and it would be far more reliable than measuring the time and doing the calculation with NR (consider that every time you deploy NR you're essentially missing a few seconds of operating time, whereas the PLC can calculate the running time even if the connection is severed).

As for connecting the S7 node to the Siemens PLCs, I've used node-red-contrib-s7 both S7-1200 and S7-300 PLCs (more than 30) in production and without issues, I think S7-400 should work as well, but I can't say for sure.

All of the PLCs I use are using Rack/Slot mode, but you have to doublecheck in TIA Portal that the Rack and Slot numbers are correct (sometimes they're mounted on another rack or slot, and then they won't work). This is the Device Configuration screen on one of my PLCs:

I would also suggest to make the timeout time bigger than the refresh time (I usually give it an additional 50%, for instance, if I read every 2000ms, I set the timeout at 3000ms).

If all this is OK but still does not work, check that the PLC data block is not protected against Reading/Writing, and that the Optimized Block Access for the blocks you want to work with is disabled.
image

Hops this helps.

1 Like

@OriolFM thanks for the information I am using S7-300 and S-7400 with step 7 5.6. do i have to make any changes?

I wouldn't know, since you haven't posted any information on how your PLCs or your S7 nodes are configured.

If you add more details, I can take a look.