Countdown-3000 and Countdowner do not accurately keep time on Victron Cerbo

No idea how these timers capture running time and if system load could impact this, but am using this timer (running on a Victron Cerbo) and the amount of time that passes is not very accurate. I have determined this by using system logging debug nodes. The time stamps on the events show more than the expected elapsed time.

Here are my log entries from /var/log/node-red-venus/current:
@4000000068a33b571ae03dcc 18 Aug 10:40:13 - [info] [debug:Start Time] true (Aug 18 10:40)
@4000000068a34285002fb4e4 18 Aug 11:10:51 - [info] [debug:Stop Time] false (Aug 18 11:10)

Since this is a 2% error, that seems huge.

I’m almost thinking that this problem may be specific for Node-RED running on Victron Venus OS, but could use any help that’s out there.

Please see this flow for testing:

[
{
"id": "527ab749e2070dab",
"type": "tab",
"label": "Countdown Timer Test",
"disabled": false,
"info": "",
"env":
},
{
"id": "3a78a69f84c97096",
"type": "inject",
"z": "527ab749e2070dab",
"name": "Start",
"props": [
{
"p": "payload"
},
{
"p": "timestamp",
"v": "",
"vt": "date"
},
{
"p": "command",
"v": "reset",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "start",
"payloadType": "str",
"x": 190,
"y": 260,
"wires": [
[
"30fef349703ce032",
"985e2092890824f6"
]
]
},
{
"id": "24c65f19cc74a5c6",
"type": "debug",
"z": "527ab749e2070dab",
"name": "Stop Time",
"active": true,
"tosidebar": true,
"console": true,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "payload",
"statusType": "auto",
"x": 890,
"y": 560,
"wires":
},
{
"id": "852dd574cca62bcf",
"type": "debug",
"z": "527ab749e2070dab",
"name": "Start Time",
"active": true,
"tosidebar": true,
"console": true,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "payload",
"statusType": "auto",
"x": 450,
"y": 500,
"wires":
},
{
"id": "8f66745030415cf7",
"type": "moment",
"z": "527ab749e2070dab",
"name": "",
"topic": "",
"input": "timestamp",
"inputType": "msg",
"inTz": "ETC/UTC",
"adjAmount": 0,
"adjType": "days",
"adjDir": "add",
"format": "MMM D HH:mm",
"locale": "POSIX",
"output": "timestamp",
"outputType": "msg",
"outTz": "America/Puerto_Rico",
"x": 480,
"y": 420,
"wires": [
[
"9e61e56a4da55917"
]
]
},
{
"id": "9e61e56a4da55917",
"type": "string",
"z": "527ab749e2070dab",
"name": "Prepend TimeStamp",
"methods": [
{
"name": "append",
"params": [
{
"type": "str",
"value": " ("
}
]
},
{
"name": "append",
"params": [
{
"type": "msg",
"value": "timestamp"
}
]
},
{
"name": "append",
"params": [
{
"type": "str",
"value": ")"
}
]
}
],
"prop": "payload",
"propout": "payload",
"object": "msg",
"objectout": "msg",
"x": 480,
"y": 460,
"wires": [
[
"852dd574cca62bcf"
]
]
},
{
"id": "4b9d6bc65f990ece",
"type": "change",
"z": "527ab749e2070dab",
"name": "",
"rules": [
{
"t": "set",
"p": "timestamp",
"pt": "msg",
"to": "",
"tot": "date"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 910,
"y": 420,
"wires": [
[
"5f3ad71ed57f3653"
]
]
},
{
"id": "5f3ad71ed57f3653",
"type": "moment",
"z": "527ab749e2070dab",
"name": "",
"topic": "",
"input": "timestamp",
"inputType": "msg",
"inTz": "ETC/UTC",
"adjAmount": 0,
"adjType": "days",
"adjDir": "add",
"format": "MMM D HH:mm",
"locale": "POSIX",
"output": "timestamp",
"outputType": "msg",
"outTz": "America/Puerto_Rico",
"x": 920,
"y": 480,
"wires": [
[
"b53743f2598ef35e"
]
]
},
{
"id": "b53743f2598ef35e",
"type": "string",
"z": "527ab749e2070dab",
"name": "Prepend TimeStamp",
"methods": [
{
"name": "append",
"params": [
{
"type": "str",
"value": " ("
}
]
},
{
"name": "append",
"params": [
{
"type": "msg",
"value": "timestamp"
}
]
},
{
"name": "append",
"params": [
{
"type": "str",
"value": ")"
}
]
}
],
"prop": "payload",
"propout": "payload",
"object": "msg",
"objectout": "msg",
"x": 920,
"y": 520,
"wires": [
[
"24c65f19cc74a5c6"
]
]
},
{
"id": "4fba68aeee42473c",
"type": "switch",
"z": "527ab749e2070dab",
"name": "Pass Stop",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "false"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 690,
"y": 340,
"wires": [
[
"4b9d6bc65f990ece",
"4ee02b2066d88b69"
]
]
},
{
"id": "23e884ce3edbadd0",
"type": "countdowner",
"z": "527ab749e2070dab",
"name": "",
"topic": "",
"payloadTimerStart": "true",
"payloadTimerStartType": "bool",
"payloadTimerStop": "false",
"payloadTimerStopType": "bool",
"timer": "30",
"resetWhileRunning": false,
"setTimeToNewWhileRunning": true,
"startCountdownOnControlMessage": false,
"minuteCounter": true,
"showCleanTimer": true,
"x": 470,
"y": 360,
"wires": [
[
"4fba68aeee42473c"
],

]
},
{
"id": "30fef349703ce032",
"type": "hourglass",
"z": "527ab749e2070dab",
"name": "",
"persistId": "",
"humanizeLocale": "",
"x": 1020,
"y": 60,
"wires": [
[
"eed4c24ab9ae2b7d"
]
]
},
{
"id": "4ee02b2066d88b69",
"type": "change",
"z": "527ab749e2070dab",
"name": "Get Status",
"rules": [
{
"t": "set",
"p": "status",
"pt": "msg",
"to": "true",
"tot": "bool"
},
{
"t": "delete",
"p": "payload",
"pt": "msg"
},
{
"t": "set",
"p": "command",
"pt": "msg",
"to": "stop",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 890,
"y": 260,
"wires": [
[
"30fef349703ce032"
]
]
},
{
"id": "eed4c24ab9ae2b7d",
"type": "debug",
"z": "527ab749e2070dab",
"name": "Elapsed Time",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1240,
"y": 60,
"wires":
},
{
"id": "985e2092890824f6",
"type": "change",
"z": "527ab749e2070dab",
"name": "",
"rules": [
{
"t": "set",
"p": "command",
"pt": "msg",
"to": "start",
"tot": "str"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "Start",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 220,
"y": 360,
"wires": [
[
"23e884ce3edbadd0",
"30fef349703ce032",
"8f66745030415cf7"
]
]
}
]

Your flow json has been corrupted due to not posting using the correct forum guidelines.

In order to make code readable and usable it is necessary to surround your code with three backticks (also known as a left quote or backquote ```)

``` 
   code goes here 
```

You can edit and correct your post by clicking the pencil :pencil: icon.

See this post for more details - How to share code or flow json

Please repost your flow between triple backticks. As it is, it is unusable.

Also it helps, when referencing a node that you give the full name.

That node does not use code to stop the timer from drifting. Not only does it just rely on setInterval which is known to drift, it has set the interval to 1 second. So there is no way it can stay accurate. An accurate countdown timer would, inside the setInterval, check the drift and correct it.

Neither node.js nor any of the devices it runs on can claim to be "real time" even approximately. While there are ways to allow for that, you have to program them in.

You would be better off getting ChatGPT or similar to help you write a function node that includes a correction.

Sorry, I don’t see a pencil button anywhere to be able to edit my post. Is it possible it is only editable for a period of time? (I’m editing this post and found the edit pencil icon, so it must have a timeout.)

At any rate, corrected post:

[
    {
        "id": "527ab749e2070dab",
        "type": "tab",
        "label": "Countdown Timer Test",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "3a78a69f84c97096",
        "type": "inject",
        "z": "527ab749e2070dab",
        "name": "Start",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "timestamp",
                "v": "",
                "vt": "date"
            },
            {
                "p": "command",
                "v": "reset",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "start",
        "payloadType": "str",
        "x": 190,
        "y": 260,
        "wires": [
            [
                "30fef349703ce032",
                "985e2092890824f6"
            ]
        ]
    },
    {
        "id": "24c65f19cc74a5c6",
        "type": "debug",
        "z": "527ab749e2070dab",
        "name": "Stop Time",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 890,
        "y": 560,
        "wires": []
    },
    {
        "id": "852dd574cca62bcf",
        "type": "debug",
        "z": "527ab749e2070dab",
        "name": "Start Time",
        "active": true,
        "tosidebar": true,
        "console": true,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 450,
        "y": 500,
        "wires": []
    },
    {
        "id": "8f66745030415cf7",
        "type": "moment",
        "z": "527ab749e2070dab",
        "name": "",
        "topic": "",
        "input": "timestamp",
        "inputType": "msg",
        "inTz": "ETC/UTC",
        "adjAmount": 0,
        "adjType": "days",
        "adjDir": "add",
        "format": "MMM D HH:mm",
        "locale": "POSIX",
        "output": "timestamp",
        "outputType": "msg",
        "outTz": "America/Puerto_Rico",
        "x": 480,
        "y": 420,
        "wires": [
            [
                "9e61e56a4da55917"
            ]
        ]
    },
    {
        "id": "9e61e56a4da55917",
        "type": "string",
        "z": "527ab749e2070dab",
        "name": "Prepend TimeStamp",
        "methods": [
            {
                "name": "append",
                "params": [
                    {
                        "type": "str",
                        "value": " ("
                    }
                ]
            },
            {
                "name": "append",
                "params": [
                    {
                        "type": "msg",
                        "value": "timestamp"
                    }
                ]
            },
            {
                "name": "append",
                "params": [
                    {
                        "type": "str",
                        "value": ")"
                    }
                ]
            }
        ],
        "prop": "payload",
        "propout": "payload",
        "object": "msg",
        "objectout": "msg",
        "x": 480,
        "y": 460,
        "wires": [
            [
                "852dd574cca62bcf"
            ]
        ]
    },
    {
        "id": "4b9d6bc65f990ece",
        "type": "change",
        "z": "527ab749e2070dab",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "timestamp",
                "pt": "msg",
                "to": "",
                "tot": "date"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 910,
        "y": 420,
        "wires": [
            [
                "5f3ad71ed57f3653"
            ]
        ]
    },
    {
        "id": "5f3ad71ed57f3653",
        "type": "moment",
        "z": "527ab749e2070dab",
        "name": "",
        "topic": "",
        "input": "timestamp",
        "inputType": "msg",
        "inTz": "ETC/UTC",
        "adjAmount": 0,
        "adjType": "days",
        "adjDir": "add",
        "format": "MMM D HH:mm",
        "locale": "POSIX",
        "output": "timestamp",
        "outputType": "msg",
        "outTz": "America/Puerto_Rico",
        "x": 920,
        "y": 480,
        "wires": [
            [
                "b53743f2598ef35e"
            ]
        ]
    },
    {
        "id": "b53743f2598ef35e",
        "type": "string",
        "z": "527ab749e2070dab",
        "name": "Prepend TimeStamp",
        "methods": [
            {
                "name": "append",
                "params": [
                    {
                        "type": "str",
                        "value": " ("
                    }
                ]
            },
            {
                "name": "append",
                "params": [
                    {
                        "type": "msg",
                        "value": "timestamp"
                    }
                ]
            },
            {
                "name": "append",
                "params": [
                    {
                        "type": "str",
                        "value": ")"
                    }
                ]
            }
        ],
        "prop": "payload",
        "propout": "payload",
        "object": "msg",
        "objectout": "msg",
        "x": 920,
        "y": 520,
        "wires": [
            [
                "24c65f19cc74a5c6"
            ]
        ]
    },
    {
        "id": "4fba68aeee42473c",
        "type": "switch",
        "z": "527ab749e2070dab",
        "name": "Pass Stop",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "false"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 690,
        "y": 340,
        "wires": [
            [
                "4b9d6bc65f990ece",
                "4ee02b2066d88b69"
            ]
        ]
    },
    {
        "id": "23e884ce3edbadd0",
        "type": "countdowner",
        "z": "527ab749e2070dab",
        "name": "",
        "topic": "",
        "payloadTimerStart": "true",
        "payloadTimerStartType": "bool",
        "payloadTimerStop": "false",
        "payloadTimerStopType": "bool",
        "timer": "30",
        "resetWhileRunning": false,
        "setTimeToNewWhileRunning": true,
        "startCountdownOnControlMessage": false,
        "minuteCounter": true,
        "showCleanTimer": true,
        "x": 470,
        "y": 360,
        "wires": [
            [
                "4fba68aeee42473c"
            ],
            []
        ]
    },
    {
        "id": "30fef349703ce032",
        "type": "hourglass",
        "z": "527ab749e2070dab",
        "name": "",
        "persistId": "",
        "humanizeLocale": "",
        "x": 1020,
        "y": 60,
        "wires": [
            [
                "eed4c24ab9ae2b7d"
            ]
        ]
    },
    {
        "id": "4ee02b2066d88b69",
        "type": "change",
        "z": "527ab749e2070dab",
        "name": "Get Status",
        "rules": [
            {
                "t": "set",
                "p": "status",
                "pt": "msg",
                "to": "true",
                "tot": "bool"
            },
            {
                "t": "delete",
                "p": "payload",
                "pt": "msg"
            },
            {
                "t": "set",
                "p": "command",
                "pt": "msg",
                "to": "stop",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 890,
        "y": 260,
        "wires": [
            [
                "30fef349703ce032"
            ]
        ]
    },
    {
        "id": "eed4c24ab9ae2b7d",
        "type": "debug",
        "z": "527ab749e2070dab",
        "name": "Elapsed Time",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1240,
        "y": 60,
        "wires": []
    },
    {
        "id": "985e2092890824f6",
        "type": "change",
        "z": "527ab749e2070dab",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "command",
                "pt": "msg",
                "to": "start",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "Start",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 220,
        "y": 360,
        "wires": [
            [
                "23e884ce3edbadd0",
                "30fef349703ce032",
                "8f66745030415cf7"
            ]
        ]
    }
]

Thanks for the pointers and response. Very interesting. I assume the function node would need to look at system time on occasion to correct for this drift. For my actual flow, that drift is acceptable, just wasn’t expected, especially since the documentation on those nodes doesn’t mention it. I will have to keep this in mind for future flows wear the timing may be more important.

1 Like

This is a straight Node.js accurate countdown timer. It would need adaptation for use in a flow with a function node but it gives some ideas.

const duration = 10; // seconds
const startTime = process.hrtime(); // [seconds, nanoseconds]

function formatTime(seconds) {
  const mins = Math.floor(seconds / 60).toString().padStart(2, '0');
  const secs = (seconds % 60).toString().padStart(2, '0');
  return `${mins}:${secs}`;
}

function updateCountdown() {
  const elapsed = process.hrtime(startTime);
  const elapsedSeconds = elapsed[0]; // seconds part
  const remaining = Math.max(0, duration - elapsedSeconds);

  console.log(`Time remaining: ${formatTime(remaining)}`);

  if (remaining === 0) {
    console.log("Countdown complete!");
    clearInterval(timer);
  }
}

// Update every second (1000ms)
const timer = setInterval(updateCountdown, 1000);

process.hrtime() is the high-accuracy time function.

Very cool. I’ve done very little in JS, how did you learn of hrtime, or how would one learn about these seemingly native calls?

I'd like to say that I already knew about it. Sadly, not true :smiley:

Truth is that I asked an AI :rofl:

The trick with AI is that you have to know enough to:

  • Ask an appropriate question
  • Understand when it is lying to you!

The Node.js docs are excellent. The Mozilla Developer Network (MDN) is a fantastic resource for all things JavaScript and web development.

Hello Bucket1616, recently I was in the very same situation than yours. I started with NodeRed using a CerboGX. One of the main questions was on how to deal with time and timers. I got rid verifying all the diffrent timers in the users contribution.

As I had a background in many years of HW design and realtime software, I also came with very special opinions on how a timer should work. Therefore @dynamicdave introduced me to his approach of finite state machines FSM. As we know, FSM deal with actions depending from states and events what leads to transititions. Therefore time is a special kind of event. Same than any output or input ports, time is a system rescource what should be handled by the operation system.

Following this approach, I played around with a traffic light example. Everybody knows how traffic lights work and it does not need any explanations.

[{"id":"d5f1a7b5a23c9330","type":"function","z":"aacb7c81f89be079","name":"YellowBlinker","func":"if (msg.payload === \"on\" || msg.payload === 1) {\n    node.status({fill:\"yellow\",shape:\"dot\",text:\"Yellow ON\"})\n} else {\n    node.status({})\n}\nreturn msg\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":740,"wires":[["d72f625ab9c4af7a"]]},{"id":"01c6e574a44957d2","type":"inject","z":"aacb7c81f89be079","name":"FSM Init","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"0","topic":"","payload":"1","payloadType":"num","x":120,"y":620,"wires":[["1702177de1479e39"]]},{"id":"1702177de1479e39","type":"switch","z":"aacb7c81f89be079","name":"payload == 0? \\n payload == 1?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":360,"y":620,"wires":[["a4fa2f3f3b61baef"],["418da2633199bddb"]]},{"id":"c7b0c0a708b23389","type":"link out","z":"aacb7c81f89be079","name":"link out 16","mode":"link","links":["055afafd32dfa98d"],"x":795,"y":600,"wires":[]},{"id":"a4fa2f3f3b61baef","type":"change","z":"aacb7c81f89be079","name":"Schedule 1 in 400ms","rules":[{"t":"set","p":"topic","pt":"msg","to":"traffic-light","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"1","tot":"num"},{"t":"set","p":"time","pt":"msg","to":"$millis() + 400","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":600,"wires":[["c7b0c0a708b23389"]]},{"id":"640145db8d169250","type":"link out","z":"aacb7c81f89be079","name":"link out 17","mode":"link","links":["055afafd32dfa98d"],"x":795,"y":640,"wires":[]},{"id":"418da2633199bddb","type":"change","z":"aacb7c81f89be079","name":"Schedule 0 in 600ms","rules":[{"t":"set","p":"topic","pt":"msg","to":"traffic-light","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"time","pt":"msg","to":"$millis() + 600\t\t","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":640,"wires":[["640145db8d169250"]]},{"id":"0868153099c8ddb6","type":"link in","z":"aacb7c81f89be079","name":"scheduled event","links":["1d76fac6dd66d991"],"x":120,"y":740,"wires":[["72257b2cf1ac78c8","7f169edcee1c870d","8a44b65c23a423cd"]],"icon":"font-awesome/fa-bolt","l":true},{"id":"8cf6b141538bea22","type":"link in","z":"aacb7c81f89be079","name":"scheduled event","links":["1d76fac6dd66d991"],"x":120,"y":660,"wires":[["1702177de1479e39"]],"icon":"font-awesome/fa-bolt","l":true},{"id":"d72f625ab9c4af7a","type":"debug","z":"aacb7c81f89be079","name":"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":850,"y":740,"wires":[]},{"id":"72257b2cf1ac78c8","type":"switch","z":"aacb7c81f89be079","name":"is traffic-light msg?","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"traffic-light","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":370,"y":740,"wires":[["d5f1a7b5a23c9330"]]},{"id":"7f169edcee1c870d","type":"switch","z":"aacb7c81f89be079","d":true,"name":"is something-else?","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"something-else","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":370,"y":800,"wires":[["321defd47e5b7de5"]]},{"id":"321defd47e5b7de5","type":"debug","z":"aacb7c81f89be079","d":true,"name":"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":850,"y":800,"wires":[]},{"id":"8a44b65c23a423cd","type":"debug","z":"aacb7c81f89be079","name":"msg counter.","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"","statusType":"counter","x":350,"y":880,"wires":[]},{"id":"81c43816b37471ea","type":"group","z":"aacb7c81f89be079","name":"Event Scheduler","style":{"label":true},"nodes":["64f9eca8e60b9404","1d76fac6dd66d991","055afafd32dfa98d","d00d2d62b0b8ecac"],"x":94,"y":459,"w":592,"h":82},{"id":"64f9eca8e60b9404","type":"cronplus","z":"aacb7c81f89be079","g":"81c43816b37471ea","name":"coordinator","outputField":"payload","timeZone":"","storeName":"","commandResponseMsgOutput":"fanOut","defaultLocation":"","defaultLocationType":"default","outputs":2,"options":[],"x":530,"y":500,"wires":[["1d76fac6dd66d991"],[]]},{"id":"1d76fac6dd66d991","type":"link out","z":"aacb7c81f89be079","g":"81c43816b37471ea","name":"link out 15","mode":"link","links":["0868153099c8ddb6","8cf6b141538bea22"],"x":645,"y":500,"wires":[],"icon":"font-awesome/fa-bolt"},{"id":"055afafd32dfa98d","type":"link in","z":"aacb7c81f89be079","g":"81c43816b37471ea","name":"schedule","links":["640145db8d169250","c7b0c0a708b23389"],"x":180,"y":500,"wires":[["d00d2d62b0b8ecac"]],"icon":"font-awesome/fa-clock-o","l":true},{"id":"d00d2d62b0b8ecac","type":"change","z":"aacb7c81f89be079","g":"81c43816b37471ea","name":"Prepare Schedule ","rules":[{"t":"set","p":"sendValue","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"{\"command\":\"add\",\"expressionType\":\"dates\",\"payloadType\":\"json\"}","tot":"json"},{"t":"set","p":"payload.name","pt":"msg","to":"topic ? topic : \"default-schedule\"","tot":"jsonata"},{"t":"set","p":"payload.expression","pt":"msg","to":"time","tot":"msg"},{"t":"set","p":"payload.payload","pt":"msg","to":"sendValue","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":500,"wires":[["64f9eca8e60b9404"]]},{"id":"892df1172a6e1d2b","type":"global-config","env":[],"modules":{"node-red-contrib-cron-plus":"2.1.0"}}]

My core time counter systic is derived from Linux time with


let systic = new Date();        // create date object to generate system time
systic = systic.getTime();      // UTC to milliseconds for system clock 

The FSM runs in a loop and times do not depend from cycle time or if application or NR is realtime or not. There is action to start the time what is

 msg.timer = systic+1000;    // start time of 1000 mseconds

and there is a event if time is elapsed what is

if (msg.timer - systic < 0)     // wait for time elapsed

Thats all you need to know about the system resource of time. Simply use it together with other events in FSM.

Of coarse, there are other methods to deal with times, e.g. the input debounce used in the traffic lights but this is not according to the theory on how all realtime systems deal with time. Internaly, every PLC and every realtime OS works like shown with the 2 code lines above. Good luck with trying the traffic light on your Cerbo.

BTW: If your flow gets bigger and includes graphic visualisation dashboard and several ModbusTCP connections, you need to adjust the Cerbos hardware watchdog to avoid occasional resets. Bigger NR flows slows down the Venus System to sometimes anything like 100mS what triggers HW watchdog reset. The delays are no problem for the Cerbo but it took me some days to debug this (with the developers help of our blue friends). My Cerbo now runs really stable.

18:48:27 up 10 days, 23:30,  load average: 2.70, 3.59, 3.49
root@einstein:/etc# 

NodeRed replaces the complete Victron GUI with a V1 dashboard flow for daily use. At the begining I had many doubts about NR but it really runs 24/7 very reliable.

2 Likes