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

Added BBB-digital-in

Added digital input node - initially this is just BBB-discrete-in
This commit is contained in:
Maxwell Hadley 2014-02-03 20:08:33 +00:00
parent 62d26c04fb
commit 83f7ff429f
2 changed files with 248 additions and 0 deletions

View File

@ -0,0 +1,111 @@
<!--
Copyright 2014 Maxwell R Hadley
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- First, the content of the edit dialog is defined. -->
<script type="text/x-red" data-template-name="BBB-discrete-in">
<div class="form-row">
<label for="node-input-pin"><i class="icon-asterisk"></i>Input pin</label>
<select type="text" id="node-input-pin" style="width: 180px;">
<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>
</select>
</div>
<div class="form-row">
<label for="node-input-updateInterval"><i class="icon-repeat"></i>Update every</label>
<input id="node-input-updateInterval" type="text">
<label>s</label>
</div>
<div class="form-row">
<label for="node-input-topic"><i class="icon-tasks"></i> Topic</label>
<input type="text" id="node-input-topic" placeholder="Topic">
</div>
<!-- By convention, most nodes have a 'name' property. The following div -->
<!-- provides the necessary field. -->
<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>
<!-- Next, some simple help text is provided for the node. -->
<script type="text/x-red" data-help-name="BBB-discrete-in">
<!-- data-help-name identifies the node type this help is for -->
<!-- This content appears in the Info sidebar when a node is selected -->
<!-- The first <p> is used as the pop-up tool tip when hovering over a -->
<!-- node in the palette. -->
<p>Discrete input for the Beaglebone Black. Sends a message on the first output
each time the pin changes state, and
records the total time in the active state</p>
<p>A timer updates the total active time, sending a message on the 2nd output at the chosen interval.
An input (any input) message will reset the total active time</p>
</script>
<!-- Finally, the node type is registered along with all of its properties -->
<!-- The example below shows a small subset of the properties that can be set-->
<script type="text/javascript">
RED.nodes.registerType('BBB-discrete-in',{
category: 'advanced-input', // the palette category
color:"#c6abef",
defaults: { // defines the editable properties of the node
name: { value:"" }, // along with default values.
updateInterval: { value:5 },
topic: { value:"", required:true },
pin: { value:"", required:true },
},
inputs:1, // set the number of inputs - only 0 or 1
outputs:2, // set the number of outputs - 0 to n
icon: "arrow-in.png", // set the icon (held in public/icons)
label: function() { // sets the default label contents
return this.name || "discrete-in: " + this.pin;
},
labelStyle: function() { // sets the class to apply to the label
return this.name?"node_label_italic":"";
}
});
</script>

View File

@ -0,0 +1,137 @@
/**
* Copyright 2014 Maxwell R Hadley
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
// Require main module
var RED = require(process.env.NODE_RED_HOME + "/red/red");
// Require bonescript
try {
var bs = require("bonescript");
} catch(err) {
require("util").log("[BBB-discrete-in] Error: cannot find module 'bonescript'");
}
// The node constructor
function DiscreteInputNode(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.updateInterval = n.updateInterval*1000; // How often to send total active time messages
// Define 'node' to allow us to access 'this' from within callbacks (the 'var' is essential -
// otherwise there is only one 'node' for all instances of DiscreteInputNode!)
var node = this;
this.interruptAttached = false; // Flag: should we detach interrupt when we are closed?
this.intervalId = null; // Remember the timer ID so we can delete it when we are closed
this.currentState = 0; // The pin input state "1" or "0"
this.lastActiveTime = 0; // The date (in ms since epoch) when the pin last went high
this.totalActiveTime = 0; // The total time in ms that the pin has been high (since reset)
this.starting = true;
// This function is called whenver the input pin changes state. We update the currentState
// and the ActiveTime variables, and send a message on the first output with the new state
var interruptCallback = function (x) {
if (node.currentState == x.value) {
node.error("Spurious interrupt" + x.value);
} else {
node.currentState = x.value;
var now = Date.now();
if (node.currentState == "1") {
node.lastActiveTime = now;
} else {
node.totalActiveTime += now - node.lastActiveTime;
}
}
var msg = {};
msg.payload = node.currentState;
node.send([msg, null]);
};
// This function is called by the timer. It updates the ActiveTime variables, and sends a
// message on the second output with the latest value of the total active time, in seconds
var timerCallback = function () {
if (node.currentState == "1") {
var now = Date.now();
node.totalActiveTime += now - node.lastActiveTime;
node.lastActiveTime = now;
}
var msg = {};
msg.payload = node.totalActiveTime/1000;
node.send([null, msg]);
};
// This function is called when we receive an input message. Clear the ActiveTime variables
// (so we start counting from zero again)
var inputCallback = function (msg) {
node.totalActiveTime = 0;
if (node.currentState == "1") {
node.lastActiveTime = Date.now();
}
if (node.starting) {
node.starting = 0;
var msg1 = {};
msg1.payload = node.currentState;
var msg2 = {};
msg2.payload = node.totalActiveTime/1000;
node.send([null, msg2]);
node.send([msg1, null]);
node.error("Initial message" + msg1 + " " + msg2);
}
};
// If we have a valid pin, set it as an input and read the (digital) 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"].indexOf(node.pin) >= 0) {
bs.pinMode(node.pin, bs.INPUT);
bs.digitalRead(node.pin, function (x) {
// Initialise the currentState and lastActveTime variables based on the value read
node.currentState = x;
if (node.currentState == "1") {
node.lastActiveTime = Date.now();
}
// Attempt to attach a change-of-state interrupt handler to the pin. If we succeed,
// set the input event and interval handlers, then send an initial message with the
// pin state on the first output
if (bs.attachInterrupt(node.pin, true, bs.CHANGE, interruptCallback)) {
node.interruptAttached = true;
node.on("input", inputCallback);
node.intervalId = setInterval(timerCallback, node.updateInterval);
} else {
node.error("Failed to attach interrupt");
}
setTimeout(function () { node.starting = 1; node.emit("input", {}); }, 250);
});
} else {
node.error("Unconfigured input pin");
}
}
// Register the node by name. This must be called before overriding any of the Node functions.
RED.nodes.registerType("BBB-discrete-in", DiscreteInputNode);
// On close, detach the interrupt (if we attaced one) and clear the interval (if we set one)
DiscreteInputNode.prototype.close = function () {
if (this.interruptAttached) {
bs.detachInterrupt(this.pin);
}
if (this.intervalId!= null) {
clearInterval(this.intervalId);
}
};