1
0
mirror of https://github.com/node-red/node-red-nodes.git synced 2023-10-10 13:36:58 +02:00

Added discrete-out node

The output node is currently in 145-digital-in. This situation is
hopefully only temporary…
This commit is contained in:
Maxwell Hadley 2014-02-26 18:37:11 +00:00
parent 42c91dd6a3
commit 430300fa04
2 changed files with 208 additions and 12 deletions

View File

@ -61,6 +61,14 @@
<label for="node-input-debounce">Debounce</label>
<input type="checkbox" id="node-input-debounce" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label for="node-input-outputOn">Output on</label>
<select type="text" id="node-input-outputOn" style="width: 200px;">
<option value="both">both changes</option>
<option value="rising">0 to 1 change only</option>
<option value="falling">1 to 0 change only</option>
</select>
</div>
<div class="form-row">
<label for="node-input-updateInterval"><i class="icon-repeat"></i> Update at</label>
<input id="node-input-updateInterval" type="text" style="width: 65px">
@ -80,10 +88,10 @@
<script type="text/x-red" data-help-name="discrete-in">
<p>
Discrete input for the Beaglebone Black. Sends a message with payload 0 or 1 on the
first output each time the pin changes state, and logs the total time in the active state.
first output when the pin changes state, and logs the total time in the active state.
</p>
<p>
The node periodically sends a message with a payload of the current total active time
The node sends a message with a payload of the current total active time
(in seconds) on the second output at selectable intervals. An input message with topic 'load'
and a numeric payload will set the total active time to that value: any other input message
will reset it to zero.
@ -92,6 +100,11 @@ will reset it to zero.
The active state may be set to be high or low: this only affects the calculation
of the active time, not the pin state value sent on the first output.
</p>
<p>
The pin state messages may be generated for both directions of change, or for just 0 to 1
or just 1 to 0 changes. This is useful to generate a single message from a button
press. When using buttons or switches, enable debouncing to improve reliability.
</p>
</script>
<!-- Register discrete-in -->
@ -102,7 +115,8 @@ of the active time, not the pin state value sent on the first output.
defaults: { // defines the editable properties of the node
pin: { value:"", required:true },
activeLow: { value:false, required:true },
debounce: { value:false, required: true },
debounce: { value:false, required:true },
outputOn: { value:"both", required:true },
updateInterval: { value:60, required:true, validate:RED.validators.number() },
topic: { value:"" },
name: { value:"" }
@ -230,3 +244,111 @@ any other input message will reset it to zero.
});
</script>
<!-- Edit dialog for discrete-out -->
<script type="text/x-red" data-template-name="discrete-out">
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i>Output pin</label>
<select type="text" id="node-input-pin" style="width: 200px;">
<option value="">select pin</option>
<option value="P8_7">GPIO2_2 (P8 pin 7)</option>
<option value="P8_8">GPIO2_3 (P8 pin 8)</option>
<option value="P8_9">GPIO2_5 (P8 pin 9)</option>
<option value="P8_10">GPIO2_4 (P8 pin 10)</option>
<option value="P8_11">GPIO1_13 (P8 pin 11)</option>
<option value="P8_12">GPIO1_12 (P8 pin 12)</option>
<option value="P8_13">GPIO0_23 (P8 pin 13)</option>
<option value="P8_14">GPIO0_26 (P8 pin 14)</option>
<option value="P8_15">GPIO1_15 (P8 pin 15)</option>
<option value="P8_16">GPIO1_14 (P8 pin 16)</option>
<option value="P8_17">GPIO0_27 (P8 pin 17)</option>
<option value="P8_18">GPIO2_1 (P8 pin 18)</option>
<option value="P8_19">GPIO0_22 (P8 pin 19)</option>
<option value="P8_26">GPIO1_29 (P8 pin 26)</option>
<option value="P9_11">GPIO0_30 (P9 pin 11)</option>
<option value="P9_12">GPIO1_28 (P9 pin 12)</option>
<option value="P9_13">GPIO0_31 (P9 pin 13)</option>
<option value="P9_14">GPIO1_18 (P9 pin 14)</option>
<option value="P9_15">GPIO1_16 (P9 pin 15)</option>
<option value="P9_16">GPIO1_19 (P9 pin 16)</option>
<option value="P9_17">GPIO0_5 (P9 pin 17)</option>
<option value="P9_18">GPIO0_4 (P9 pin 18)</option>
<option value="P9_21">GPIO0_3 (P9 pin 21)</option>
<option value="P9_22">GPIO0_2 (P9 pin 22)</option>
<option value="P9_23">GPIO1_17 (P9 pin 23)</option>
<option value="P9_24">GPIO0_15 (P9 pin 24)</option>
<option value="P9_26">GPIO0_14 (P9 pin 26)</option>
<option value="P9_27">GPIO3_19 (P9 pin 27)</option>
<option value="P9_30">GPIO3_16 (P9 pin 30)</option>
<option value="P9_41">GPIO0_20 (P9 pin 41)</option>
<option value="P9_42">GPIO0_7 (P9 pin 42)</option>
<option value="USR0">User LED 0</option>
<option value="USR1">User LED 1</option>
<option value="USR2">User LED 2</option>
<option value="USR3">User LED 3</option>
</select>
</div>
<div class="form-row">
<label for="node-input-inverting">Inverting</label>
<input type="checkbox" id="node-input-inverting" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label for="node-input-toggle">Toggle state</label>
<input type="checkbox" id="node-input-toggle" style="display:inline-block; width:auto; vertical-align:top;">
</div>
<div class="form-row">
<label for="node-input-defaultState">Startup as</label>
<select type="text" id="node-input-defaultState" style="width: 80px;">
<option value="0">0</option>
<option value="1">1</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<!-- Help text for discrete-out -->
<script type="text/x-red" data-help-name="discrete-out">
<p>
Discrete output for the Beaglebone Black.
</p>
<p>
Sets the output pin high or low depending on the payload of the input message. Numeric
payloads > 0.5 are 'high', payloads <= 0.5 are 'low'. Other payloads which
evaluate to true are 'high', if not then 'low'. Selecting the Inverting checkbox will
switch the sense of the comparison.
</p>
<p>
If the Toggle state checkbox is checked, any message will cause the pin to switch from
1 to 0 and back.
</P>
<p>
The pin will be initially set to the given Startup state until the first message arrives:
the Inverting property is not applied to this value.
</p>
</script>
<!-- Register discrete-out -->
<script type="text/javascript">
RED.nodes.registerType('discrete-out', {
category: 'advanced-input', // the palette category
color:"#c6dbef",
defaults: { // defines the editable properties of the node
pin: { value:"", required:true },
inverting: { value:false, required:true },
toggle: { value:false, required:true },
defaultState: { value:"0", required:true },
name: { value:"" }
},
inputs:1, // set the number of inputs - only 0 or 1
outputs:0, // set the number of outputs - 0 to n
icon: "arrow-out.png", // set the icon (held in public/icons)
label: function() { // sets the default label contents
return this.name || "discrete-out: " + this.pin;
},
labelStyle: function() { // sets the class to apply to the label
return this.name ? "node_label_italic" : "";
}
});
</script>

View File

@ -37,6 +37,15 @@ function DiscreteInputNode(n) {
this.activeState = 1;
this.updateInterval = n.updateInterval * 1000; // How often to send totalActiveTime messages
this.debounce = n.debounce; // Enable switch contact debouncing algorithm
if (n.outputOn === "rising") {
this.activeEdges = [false, true];
} else if (n.outputOn === "falling") {
this.activeEdges = [true, false];
} else if (n.outputOn === "both") {
this.activeEdges = [true, true];
} else {
node.error("Invalid edge type: " + n.outputOn);
}
// Working variables
this.interruptAttached = false; // Flag: should we detach interrupt when we are closed?
@ -93,10 +102,12 @@ function DiscreteInputNode(n) {
} else if (!isNaN(node.lastActiveTime)) {
node.totalActiveTime += now - node.lastActiveTime;
}
var msg = {};
msg.topic = node.topic;
msg.payload = node.currentState;
node.send([msg, null]);
if (node.activeEdges[node.currentState]) {
var msg = {};
msg.topic = node.topic;
msg.payload = node.currentState;
node.send([msg, null]);
}
};
// This function is called by the timer. It updates the ActiveTime variables, and sends a
@ -131,10 +142,17 @@ function DiscreteInputNode(n) {
if (node.currentState === node.activeState) {
node.lastActiveTime = Date.now();
}
// On startup, send an initial activeTime message, but only send an
// initial currentState message if we are in both edges active mode
if (node.starting) {
node.starting = false;
var msg = [{topic:node.topic}, {topic:node.topic}];
msg[0].payload = node.currentState;
var msg;
if (node.activeEdges[0] && node.activeEdges[1]) {
msg = [{topic:node.topic}, {topic:node.topic}];
msg[0].payload = node.currentState;
} else {
msg = [null, {topic:node.topic}];
}
msg[1].payload = node.totalActiveTime;
node.send(msg);
}
@ -239,8 +257,8 @@ function PulseInputNode(n) {
// Don't set up interrupts & intervals until after the close event handler has been installed
bonescript.detachInterrupt(node.pin);
process.nextTick(function () {
bonescript.pinMode(node.pin, bonescript.INPUT);
bonescript.digitalRead(node.pin, function (x) {
bonescript.pinMode(node.pin, bonescript.INPUT);
bonescript.digitalRead(node.pin, function (x) {
// Initialise the currentState based on the value read
node.currentState = Number(x.value);
// Attempt to attach an interrupt handler to the pin. If we succeed,
@ -260,15 +278,71 @@ function PulseInputNode(n) {
node.error("Failed to attach interrupt");
}
});
});
});
} else {
node.error("Unconfigured input pin");
}
}
// Node constructor for discrete-out
function DiscreteOutputNode(n) {
RED.nodes.createNode(this, n);
// Store local copies of the node configuration (as defined in the .html)
this.topic = n.topic; // the topic is not currently used
this.pin = n.pin; // The Beaglebone Black pin identifying string
this.defaultState = Number(n.defaultState); // What state to set up as
this.inverting = n.inverting;
this.toggle = n.toggle;
// Working variables
this.currentState = this.defaultState;
var node = this;
// If the input message paylod is numeric, values > 0.5 are 'true', otherwise use
// the truthiness of the payload. Apply the inversion flag before setting the output
var inputCallback = function (msg) {
var newState;
if (node.toggle) {
newState = node.currentState === 0 ? 1 : 0;
} else {
if (isFinite(Number(msg.payload))) {
newState = Number(msg.payload) > 0.5 ? true : false;
} else if (msg.payload) {
newState = true;
} else {
newState = false;
}
if (node.inverting) {
newState = !newState;
}
}
bonescript.digitalWrite(node.pin, newState ? 1 : 0);
node.currentState = newState;
};
// If we have a valid pin, set it as an output and set the default state
if (["P8_7", "P8_8", "P8_9", "P8_10", "P8_11", "P8_12", "P8_13", "P8_14", "P8_15",
"P8_16", "P8_17", "P8_18", "P8_19", "P8_26", "P9_11", "P9_12", "P9_13", "P9_14",
"P9_15", "P9_16", "P9_17", "P9_18", "P9_21", "P9_22", "P9_23", "P9_24", "P9_26",
"P9_27", "P9_30", "P9_41", "P9_42", "USR0", "USR1", "USR2", "USR3"].indexOf(node.pin) >= 0) {
// Don't set up interrupts & intervals until after the close event handler has been installed
bonescript.detachInterrupt(node.pin);
process.nextTick(function () {
bonescript.pinMode(node.pin, bonescript.OUTPUT);
node.on("input", inputCallback);
setTimeout(function () { bonescript.digitalWrite(node.pin, node.defaultState); }, 50);
});
} else {
node.error("Unconfigured output pin");
}
}
// Register the nodes by name. This must be called before overriding any of the Node functions.
RED.nodes.registerType("discrete-in", DiscreteInputNode);
RED.nodes.registerType("pulse-in", PulseInputNode);
RED.nodes.registerType("discrete-out", DiscreteOutputNode);
// On close, detach the interrupt (if we attached one) and clear any active timers
DiscreteInputNode.prototype.close = function () {