Skip to content

Instantly share code, notes, and snippets.

@colinl

colinl/README.md Secret

Created November 14, 2016 21:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save colinl/69579a4fe19b62a23aad99ddf377450c to your computer and use it in GitHub Desktop.
Save colinl/69579a4fe19b62a23aad99ddf377450c to your computer and use it in GitHub Desktop.
A time proportion node for driving on/off devices in a pseudo linear way

This flow shows a function node that converts a floating value in the range 0.0 to 1.0 into a time proportioned on/off signal. It can be used, for example, drive a heater that only has on/off control such as to effectively get any power between 0 and 100%. Similarly it could be used to control a valve on a radiator to control the heat output.

It can be used in conjunction with a closed loop control node such as node-red-contrib-pid to control (again, for example) a heating system.

The comments at the start of the function node should be sufficient to configure it to individual circumstances.

[
{
"id": "b51fa4f0.7b618",
"type": "mqtt in",
"z": "fd0a1625.33a968",
"name": "",
"topic": "tydwr/conservatory/power",
"qos": "1",
"broker": "2d6ff793.95b218",
"x": 262,
"y": 686,
"wires": [
[
"bf6ecf4b.1348d"
]
]
},
{
"id": "bf6ecf4b.1348d",
"type": "function",
"z": "fd0a1625.33a968",
"name": "Timeprop",
"func": "// A node that can be used to generate a time proportioned on/off signal\n// from a power requirement value in the range 0 to 1\n// So for example with a cycle time period (set below) of 10 minutes and\n// a power requirement of 0.2 the output will be on for 2 minutes in every\n// ten minutes.\n// In addition to passing in messages with the payload set to the current\n// power requirement (floating point 0.0 to 1.0), provide an input from a \n// repeating inject node with the topic set to 'tick' and the payload \n// containing the current timestamp. The frequency of this will depend upon \n// the cycle time required. For a cycle time period of 10 minutes I use an inject\n// repeat of 5 seconds.\n\n// Set these three variables as required\nvar period = 10*60*1000; // On/off cycle time period millisecs, 10 minutes\nvar deadTime = 15*1000; // number of milliseconds the valve (or whatever) takes to actuate, 30 seconds\nvar invert = true; // set true for active low output, so the output will go low\n // when the valve should actuate, this is the usual case on a\n // pi feeding a relay to drive the actuator\n\n// is this a tick message?\nif (msg.topic !== \"tick\") {\n // no, so it should be a power value, save it and exit\n var power = msg.payload;\n context.set('power', msg.payload);\n msg = null;\n} else {\n // yes, payload is timestamp, calc current wave value between 0 and 1\n var wave = (msg.payload % period)/period; // fraction of way through cycle\n var direction;\n // determine direction of travel and convert to triangular wave\n if (wave < 0.5) {\n direction = 1; // on the way up\n wave = wave*2;\n } else {\n direction = -1; // on the way down\n wave = (1 - wave)*2;\n }\n var requestedPower = context.get('power') || 0;\n // if a dead_time has been supplied for this o/p then adjust power accordingly\n if (deadTime > 0 && requestedPower > 0.0 && requestedPower < 1.0) {\n var dtop = deadTime/period;\n power = (1.0-2.0*dtop)*requestedPower + dtop;\n } else {\n power = requestedPower;\n }\n // cope with end cases in case values outside 0..1\n var opState;\n if (power <= 0.0) {\n opState = 0; // no heat\n } else if (power >= 1.0) {\n opState = 1; // full heat\n } else {\n // only allow power to come on on the way down and off on the way up, to reduce short pulses\n if (power >= wave && direction === -1) {\n opState = 1;\n } else if (power <= wave && direction === 1) {\n opState = 0;\n } else {\n // otherwise leave it as it is\n opState = context.get('opState') || 0;\n } \n }\n context.set('opState', opState);\n msg.payload = invert ? (1-opState) : opState;\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 547.5,
"y": 685,
"wires": [
[
"786e2f70.979ef"
]
]
},
{
"id": "95370fda.bc8ea",
"type": "inject",
"z": "fd0a1625.33a968",
"name": "1 sec",
"topic": "tick",
"payload": "",
"payloadType": "date",
"repeat": "1",
"crontab": "",
"once": true,
"x": 481.5,
"y": 623,
"wires": [
[
"bf6ecf4b.1348d"
]
]
},
{
"id": "786e2f70.979ef",
"type": "rpi-gpio out",
"z": "fd0a1625.33a968",
"name": "",
"pin": "13",
"set": true,
"level": "1",
"out": "out",
"x": 765,
"y": 684,
"wires": []
},
{
"id": "2d6ff793.95b218",
"type": "mqtt-broker",
"z": "fd0a1625.33a968",
"broker": "localhost",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": true,
"keepalive": "60",
"cleansession": true,
"willTopic": "",
"willQos": "0",
"willPayload": "",
"birthTopic": "",
"birthQos": "0",
"birthPayload": ""
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment