From 999874f2b5a540dafb413b58907dcd0a68db07ca Mon Sep 17 00:00:00 2001 From: Dave C-J Date: Mon, 30 Jun 2014 23:41:38 +0100 Subject: [PATCH] jsHint passing versions of BBB, scanBLE and wemo nodes. Also fixe and in vs out issue on wemo... no way it could have been working previously.. --- hardware/BBB/145-BBB-hardware.js | 895 ++++++++++++++++--------------- hardware/scanBLE/101-scanBLE.js | 27 +- hardware/wemo/60-wemo.js | 12 +- 3 files changed, 469 insertions(+), 465 deletions(-) diff --git a/hardware/BBB/145-BBB-hardware.js b/hardware/BBB/145-BBB-hardware.js index 78ba7151..af5a70df 100644 --- a/hardware/BBB/145-BBB-hardware.js +++ b/hardware/BBB/145-BBB-hardware.js @@ -14,489 +14,492 @@ * limitations under the License. **/ -// Require main module -var RED = require(process.env.NODE_RED_HOME + "/red/red"); -var bonescript = require("bonescript"); +module.exports = function(RED) { + "use strict"; + var bonescript = require("bonescript"); -// Node constructor for bbb-analogue-in -function AnalogueInputNode(n) { - // Create a RED node - RED.nodes.createNode(this, n); + // Node constructor for bbb-analogue-in + function AnalogueInputNode(n) { + // Create a RED node + RED.nodes.createNode(this, n); - // Store local copies of the node configuration (as defined in the .html) - this.topic = n.topic; - this.pin = n.pin; - this.breakpoints = n.breakpoints; - this.averaging = n.averaging; - if (this.averaging) { - this.averages = 10; - } else { - this.averages = 1; - } + // Store local copies of the node configuration (as defined in the .html) + this.topic = n.topic; + this.pin = n.pin; + this.breakpoints = n.breakpoints; + this.averaging = n.averaging; + if (this.averaging) { + this.averages = 10; + } else { + this.averages = 1; + } - // Define 'node' to allow us to access 'this' from within callbacks - var node = this; + // Define 'node' to allow us to access 'this' from within callbacks + var node = this; - // Variables used for input averaging - var sum; // accumulates the input readings to be averaged - var count; // keep track of the number of measurements made + // Variables used for input averaging + var sum; // accumulates the input readings to be averaged + var count; // keep track of the number of measurements made - // The callback function for analogRead. Accumulates the required number of - // measurements, then divides the total number, applies output scaling and - // sends the result - var analogReadCallback = function (x) { - sum = sum + x.value; - count = count - 1; - if (count > 0) { - bonescript.analogRead(node.pin, analogReadCallback); - } else { - var msg = {}; - msg.topic = node.topic; - sum = sum/node.averages; - // i is the index of the first breakpoint where the 'input' value is strictly - // greater than the measurement (note: a measurement can never be == 1) - var i = node.breakpoints.map(function (breakpoint) { return sum >= breakpoint.input; }).indexOf(false); - msg.payload = node.breakpoints[i-1].output + (node.breakpoints[i].output - node.breakpoints[i-1].output) * - (sum - node.breakpoints[i-1].input)/(node.breakpoints[i].input - node.breakpoints[i-1].input); - node.send(msg); - } - }; - - // If we have a valid pin, set the input event handler to Bonescript's analogRead - if (["P9_39", "P9_40", "P9_37", "P9_38", "P9_33", "P9_36", "P9_35"].indexOf(node.pin) >= 0) { - node.on("input", function (msg) { - sum = 0; - count = node.averages; - bonescript.analogRead(node.pin, analogReadCallback); - }); - } else { - node.error("Unconfigured input pin"); - } -} - -// Node constructor for bbb-discrete-in -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 - if (n.activeLow) // Set the 'active' state 0 or 1 as appropriate - this.activeState = 0; - else - 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? - 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 = NaN; // The date (in ms since epoch) when the pin last went high - // switch to process.hrtime() - this.totalActiveTime = 0; // The total time in ms that the pin has been high (since reset) - this.starting = true; - this.debouncing = false; // True after a change of state while waiting for the 7ms debounce time to elapse - this.debounceTimer = null; - - // Define 'node' to allow us to access 'this' from within callbacks - var node = this; - - // This function is called by the input pin change-of-state interrupt. If - // debounce is disabled, send the output message. Otherwise, if we are - // currently debouncing, ignore this interrupt. If we are not debouncing, - // schedule a re-read of the input pin in 7ms time, and set the debouncing flag - // Note: this function gets called spuriously when the interrupt is first enabled: - // in this case x.value is undefined - we must test for this - var interruptCallback = function (x) { - if (x.value !== undefined && node.currentState !== Number(x.value)) { - if (node.debounce) { - if (node.debouncing === false) { - node.debouncing = true; - node.debounceTimer = setTimeout(function () { bonescript.digitalRead(node.pin, debounceCallback); }, 7); - } + // The callback function for analogRead. Accumulates the required number of + // measurements, then divides the total number, applies output scaling and + // sends the result + var analogReadCallback = function (x) { + sum = sum + x.value; + count = count - 1; + if (count > 0) { + bonescript.analogRead(node.pin, analogReadCallback); } else { + var msg = {}; + msg.topic = node.topic; + sum = sum/node.averages; + // i is the index of the first breakpoint where the 'input' value is strictly + // greater than the measurement (note: a measurement can never be == 1) + var i = node.breakpoints.map(function (breakpoint) { return sum >= breakpoint.input; }).indexOf(false); + msg.payload = node.breakpoints[i-1].output + (node.breakpoints[i].output - node.breakpoints[i-1].output) * + (sum - node.breakpoints[i-1].input)/(node.breakpoints[i].input - node.breakpoints[i-1].input); + node.send(msg); + } + }; + + // If we have a valid pin, set the input event handler to Bonescript's analogRead + if (["P9_39", "P9_40", "P9_37", "P9_38", "P9_33", "P9_36", "P9_35"].indexOf(node.pin) >= 0) { + node.on("input", function (msg) { + sum = 0; + count = node.averages; + bonescript.analogRead(node.pin, analogReadCallback); + }); + } else { + node.error("Unconfigured input pin"); + } + } + + // Node constructor for bbb-discrete-in + 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 + if (n.activeLow) { // Set the 'active' state 0 or 1 as appropriate + this.activeState = 0; + } + else { + 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? + 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 = NaN; // The date (in ms since epoch) when the pin last went high + // switch to process.hrtime() + this.totalActiveTime = 0; // The total time in ms that the pin has been high (since reset) + this.starting = true; + this.debouncing = false; // True after a change of state while waiting for the 7ms debounce time to elapse + this.debounceTimer = null; + + // Define 'node' to allow us to access 'this' from within callbacks + var node = this; + + // This function is called by the input pin change-of-state interrupt. If + // debounce is disabled, send the output message. Otherwise, if we are + // currently debouncing, ignore this interrupt. If we are not debouncing, + // schedule a re-read of the input pin in 7ms time, and set the debouncing flag + // Note: this function gets called spuriously when the interrupt is first enabled: + // in this case x.value is undefined - we must test for this + var interruptCallback = function (x) { + if (x.value !== undefined && node.currentState !== Number(x.value)) { + if (node.debounce) { + if (node.debouncing === false) { + node.debouncing = true; + node.debounceTimer = setTimeout(function () { bonescript.digitalRead(node.pin, debounceCallback); }, 7); + } + } else { + sendStateMessage(x); + } + } + }; + + // This function is called approx 7ms after a potential change-of-state which is + // being debounced. Terminate the debounce, and send a message if the state has + // actually changed + var debounceCallback = function (x) { + node.debounceTimer = null; + node.debouncing = false; + if (x.value !== undefined && node.currentState !== Number(x.value)) { sendStateMessage(x); } - } - }; + }; - // This function is called approx 7ms after a potential change-of-state which is - // being debounced. Terminate the debounce, and send a message if the state has - // actually changed - var debounceCallback = function (x) { - node.debounceTimer = null; - node.debouncing = false; - if (x.value !== undefined && node.currentState !== Number(x.value)) { - sendStateMessage(x); - } - }; + // This function is called when either the interruptCallback or the debounceCallback + // have determined we have a 'genuine' change of state. Update the currentState and + // ActiveTime variables, and send a message on the first output with the new state + var sendStateMessage = function (x) { + node.currentState = Number(x.value); + var now = Date.now(); + if (node.currentState === node.activeState) { + node.lastActiveTime = now; + } else if (!isNaN(node.lastActiveTime)) { + node.totalActiveTime += now - node.lastActiveTime; + } + if (node.activeEdges[node.currentState]) { + var msg = {}; + msg.topic = node.topic; + msg.payload = node.currentState; + node.send([msg, null]); + } + }; - // This function is called when either the interruptCallback or the debounceCallback - // have determined we have a 'genuine' change of state. Update the currentState and - // ActiveTime variables, and send a message on the first output with the new state - var sendStateMessage = function (x) { - node.currentState = Number(x.value); - var now = Date.now(); - if (node.currentState === node.activeState) { - node.lastActiveTime = now; - } else if (!isNaN(node.lastActiveTime)) { - node.totalActiveTime += now - node.lastActiveTime; - } - if (node.activeEdges[node.currentState]) { + // 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 === node.activeState) { + var now = Date.now(); + node.totalActiveTime += now - node.lastActiveTime; + node.lastActiveTime = now; + } 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 - // message on the second output with the latest value of the total active time, in seconds - var timerCallback = function () { - if (node.currentState === node.activeState) { - var now = Date.now(); - node.totalActiveTime += now - node.lastActiveTime; - node.lastActiveTime = now; - } - var msg = {}; - msg.topic = node.topic; - msg.payload = node.totalActiveTime / 1000; - node.send([null, msg]); - // Re-synchronise the pin state if we have missed a state change interrupt for some - // reason, and we are not in the process of debouncing one - if (node.debouncing === false) { - bonescript.digitalRead(node.pin, interruptCallback); - } - }; - - // This function is called when we receive an input message. If the topic contains - // 'load' (case insensitive) set the totalActiveTime to the numeric value of the - // payload, if possible. Otherwise clear the totalActiveTime (so we start counting - // from zero again) - var inputCallback = function (ipMsg) { - if (String(ipMsg.topic).search(/load/i) < 0 || isFinite(ipMsg.payload) == false) { - node.totalActiveTime = 0; - } else { - node.totalActiveTime = Number(ipMsg.payload); - } - 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; - 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.payload = node.totalActiveTime / 1000; + node.send([null, msg]); + // Re-synchronise the pin state if we have missed a state change interrupt for some + // reason, and we are not in the process of debouncing one + if (node.debouncing === false) { + bonescript.digitalRead(node.pin, interruptCallback); } - msg[1].payload = node.totalActiveTime; - node.send(msg); - } - }; + }; - // 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) { - // 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) { - // Initialise the currentState and lastActveTime variables based on the value read - node.currentState = Number(x.value); - if (node.currentState === node.activeState) { - node.lastActiveTime = Date.now(); - // switch to process.hrtime() - } - // 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 (bonescript.attachInterrupt(node.pin, true, bonescript.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.emit("input", {}); }, 50); - }); - }); - } else { - node.error("Unconfigured input pin"); - } -} + // This function is called when we receive an input message. If the topic contains + // 'load' (case insensitive) set the totalActiveTime to the numeric value of the + // payload, if possible. Otherwise clear the totalActiveTime (so we start counting + // from zero again) + var inputCallback = function (ipMsg) { + if (String(ipMsg.topic).search(/load/i) < 0 || isFinite(ipMsg.payload) === false) { + node.totalActiveTime = 0; + } else { + node.totalActiveTime = Number(ipMsg.payload); + } + 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; + 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); + } + }; -// Node constructor for bbb-pulse-in -function PulseInputNode(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 output messages - this.countType = n.countType; // Sets either 'edge' or 'pulse' counting - this.countUnit = n.countUnit; // Scaling appling to count output - this.countRate = n.countRate; // Scaling applied to rate output - - // Working variables - 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.pulseCount = 0; // (Unscaled) total pulse count - // Hold the hrtime of the last two pulses (with ns resolution) - this.pulseTime = [[NaN, NaN], [NaN, NaN]]; - - // Define 'node' to allow us to access 'this' from within callbacks - var node = this; - - // Called by the edge or pulse interrupt. If this is a valid interrupt, record the - // pulse time and count the pulse - var interruptCallback = function (x) { - if (x.value !== undefined) { - node.pulseTime = [node.pulseTime[1], process.hrtime()]; - node.pulseCount = node.pulseCount + 1; - } - }; - - // Called when an input message arrives. If the topic contains 'load' (case - // insensitive) and the payload is a valid number, set the count to that - // number, otherwise set it to zero - var inputCallback = function (msg) { - if (String(msg.topic).search(/load/i) < 0 || isFinite(msg.payload) == false) { - node.pulseCount = 0; - } else { - node.pulseCount = Number(msg.payload); - } - }; - - // Called by the message timer. Send two messages: the scaled pulse count on - // the first output and the scaled instantaneous pulse rate on the second. - // The instantaneous pulse rate is the reciprocal of the larger of either the - // time interval between the last two pulses, or the time interval since the last pulse. - var timerCallback = function () { - var now = process.hrtime(); - var lastTime = node.pulseTime[1][0] - node.pulseTime[0][0] + (node.pulseTime[1][1] - node.pulseTime[0][1]) / 1e9; - var thisTime = now[0] - node.pulseTime[1][0] + (now[1] - node.pulseTime[1][1]) / 1e9; - var msg = [{ topic:node.topic }, { topic:node.topic }]; - msg[0].payload = node.countUnit * node.pulseCount; - // At startup, pulseTime contains NaN's: force the rate output to 0 - msg[1].payload = node.countRate / Math.max(thisTime, lastTime) || 0; - node.send(msg); - }; - - // 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) { - // Don't set up interrupts & intervals until after the close event handler has been installed - bonescript.detachInterrupt(node.pin); - process.nextTick(function () { + // 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) { + // 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) { - // 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, - // set the input event and interval handlers - var interruptType; - if (node.countType === "pulse") { - // interruptType = bonescript.FALLING; <- doesn't work in v0.2.4 - interruptType = bonescript.RISING; - } else { - interruptType = bonescript.CHANGE; - } - if (bonescript.attachInterrupt(node.pin, true, interruptType, interruptCallback)) { - node.interruptAttached = true; - node.on("input", inputCallback); - node.intervalId = setInterval(timerCallback, node.updateInterval); - } else { - node.error("Failed to attach interrupt"); - } + // Initialise the currentState and lastActveTime variables based on the value read + node.currentState = Number(x.value); + if (node.currentState === node.activeState) { + node.lastActiveTime = Date.now(); + // switch to process.hrtime() + } + // 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 (bonescript.attachInterrupt(node.pin, true, bonescript.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.emit("input", {}); }, 50); + }); }); - }); - } else { - node.error("Unconfigured input pin"); + } else { + node.error("Unconfigured input pin"); + } } -} -// Node constructor for bbb-discrete-out -function DiscreteOutputNode(n) { - RED.nodes.createNode(this, n); + // Node constructor for bbb-pulse-in + function PulseInputNode(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; + // 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 output messages + this.countType = n.countType; // Sets either 'edge' or 'pulse' counting + this.countUnit = n.countUnit; // Scaling appling to count output + this.countRate = n.countRate; // Scaling applied to rate output - // Working variables - this.currentState = this.defaultState; + // Working variables + 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.pulseCount = 0; // (Unscaled) total pulse count + // Hold the hrtime of the last two pulses (with ns resolution) + this.pulseTime = [[NaN, NaN], [NaN, NaN]]; - var node = this; + // Define 'node' to allow us to access 'this' from within callbacks + 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; + // Called by the edge or pulse interrupt. If this is a valid interrupt, record the + // pulse time and count the pulse + var interruptCallback = function (x) { + if (x.value !== undefined) { + node.pulseTime = [node.pulseTime[1], process.hrtime()]; + node.pulseCount = node.pulseCount + 1; + } + }; + + // Called when an input message arrives. If the topic contains 'load' (case + // insensitive) and the payload is a valid number, set the count to that + // number, otherwise set it to zero + var inputCallback = function (msg) { + if (String(msg.topic).search(/load/i) < 0 || isFinite(msg.payload) === false) { + node.pulseCount = 0; } else { - newState = false; + node.pulseCount = Number(msg.payload); } - if (node.inverting) { - newState = !newState; - } - } - bonescript.digitalWrite(node.pin, newState ? 1 : 0); - node.send({ topic:node.topic, payload:newState }); - 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"); + // Called by the message timer. Send two messages: the scaled pulse count on + // the first output and the scaled instantaneous pulse rate on the second. + // The instantaneous pulse rate is the reciprocal of the larger of either the + // time interval between the last two pulses, or the time interval since the last pulse. + var timerCallback = function () { + var now = process.hrtime(); + var lastTime = node.pulseTime[1][0] - node.pulseTime[0][0] + (node.pulseTime[1][1] - node.pulseTime[0][1]) / 1e9; + var thisTime = now[0] - node.pulseTime[1][0] + (now[1] - node.pulseTime[1][1]) / 1e9; + var msg = [{ topic:node.topic }, { topic:node.topic }]; + msg[0].payload = node.countUnit * node.pulseCount; + // At startup, pulseTime contains NaN's: force the rate output to 0 + msg[1].payload = node.countRate / Math.max(thisTime, lastTime) || 0; + node.send(msg); + }; + + // 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) { + // 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) { + // 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, + // set the input event and interval handlers + var interruptType; + if (node.countType === "pulse") { + // interruptType = bonescript.FALLING; <- doesn't work in v0.2.4 + interruptType = bonescript.RISING; + } else { + interruptType = bonescript.CHANGE; + } + if (bonescript.attachInterrupt(node.pin, true, interruptType, interruptCallback)) { + node.interruptAttached = true; + node.on("input", inputCallback); + node.intervalId = setInterval(timerCallback, node.updateInterval); + } else { + node.error("Failed to attach interrupt"); + } + }); + }); + } else { + node.error("Unconfigured input pin"); + } } -} -// Node constructor for bbb-pulse-out -function PulseOutputNode(n) { - RED.nodes.createNode(this, n); + // Node constructor for bbb-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.pulseState = Number(n.pulseState); // What state the pulse will be.. - this.defaultState = this.pulseState === 1 ? 0 : 1; - this.retriggerable = n.retriggerable; - this.pulseTime = n.pulseTime * 1000; // Pulse width in milliseconds + // 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.pulseTimer = null; // Non-null while a pulse is being generated + // Working variables + this.currentState = this.defaultState; - var node = this; + var node = this; - // Generate a pulse in response to an input message. If the topic includes the text - // 'time' (case insensitive) and the payload is numeric, use this value as the - // pulse time. Otherwise use the value from the properties dialog. - // If the resulting pulse time is < 1ms, do nothing. - // If the pulse mode is not retriggerable, then if no pulseTimer is active, generate - // a pulse. If the pulse mode is retriggerable, and a pulseTimer is active, cancel it. - // If no timer is active, set the pulse output. In both cases schedule a new pulse - // timer. - var inputCallback = function (msg) { - var time = node.pulseTime; - if (String(msg.topic).search(/time/i) >= 0 && isFinite(msg.payload)) { - time = msg.payload * 1000; - } - if (time >= 1) { - if (node.retriggerable === false) { - if (node.pulseTimer === null) { - node.pulseTimer = setTimeout(endPulseCallback, time); - bonescript.digitalWrite(node.pin, node.pulseState); - node.send({ topic:node.topic, payload:node.pulseState }); - } + // 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 (node.pulseTimer !== null) { - clearTimeout(node.pulseTimer); + if (isFinite(Number(msg.payload))) { + newState = Number(msg.payload) > 0.5 ? true : false; + } else if (msg.payload) { + newState = true; } else { - bonescript.digitalWrite(node.pin, node.pulseState); - node.send({ topic:node.topic, payload:node.pulseState }); + newState = false; + } + if (node.inverting) { + newState = !newState; } - node.pulseTimer = setTimeout(endPulseCallback, time); } - } - }; + bonescript.digitalWrite(node.pin, newState ? 1 : 0); + node.send({ topic:node.topic, payload:newState }); + node.currentState = newState; + }; - // At the end of the pulse, restore the default state and set the timer to null - var endPulseCallback = function () { - node.pulseTimer = null; - bonescript.digitalWrite(node.pin, node.defaultState); - node.send({ topic:node.topic, payload:node.defaultState }); - }; - - // 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); - // Set the pin to the default stte once the dust settles - setTimeout(endPulseCallback, 50); - }); - } else { - node.error("Unconfigured output pin"); + // 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"); + } } + + // Node constructor for bbb-pulse-out + function PulseOutputNode(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.pulseState = Number(n.pulseState); // What state the pulse will be.. + this.defaultState = this.pulseState === 1 ? 0 : 1; + this.retriggerable = n.retriggerable; + this.pulseTime = n.pulseTime * 1000; // Pulse width in milliseconds + + // Working variables + this.pulseTimer = null; // Non-null while a pulse is being generated + + var node = this; + + // Generate a pulse in response to an input message. If the topic includes the text + // 'time' (case insensitive) and the payload is numeric, use this value as the + // pulse time. Otherwise use the value from the properties dialog. + // If the resulting pulse time is < 1ms, do nothing. + // If the pulse mode is not retriggerable, then if no pulseTimer is active, generate + // a pulse. If the pulse mode is retriggerable, and a pulseTimer is active, cancel it. + // If no timer is active, set the pulse output. In both cases schedule a new pulse + // timer. + var inputCallback = function (msg) { + var time = node.pulseTime; + if (String(msg.topic).search(/time/i) >= 0 && isFinite(msg.payload)) { + time = msg.payload * 1000; + } + if (time >= 1) { + if (node.retriggerable === false) { + if (node.pulseTimer === null) { + node.pulseTimer = setTimeout(endPulseCallback, time); + bonescript.digitalWrite(node.pin, node.pulseState); + node.send({ topic:node.topic, payload:node.pulseState }); + } + } else { + if (node.pulseTimer !== null) { + clearTimeout(node.pulseTimer); + } else { + bonescript.digitalWrite(node.pin, node.pulseState); + node.send({ topic:node.topic, payload:node.pulseState }); + } + node.pulseTimer = setTimeout(endPulseCallback, time); + } + } + }; + + // At the end of the pulse, restore the default state and set the timer to null + var endPulseCallback = function () { + node.pulseTimer = null; + bonescript.digitalWrite(node.pin, node.defaultState); + node.send({ topic:node.topic, payload:node.defaultState }); + }; + + // 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); + // Set the pin to the default stte once the dust settles + setTimeout(endPulseCallback, 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("bbb-analogue-in", AnalogueInputNode); + RED.nodes.registerType("bbb-discrete-in", DiscreteInputNode); + RED.nodes.registerType("bbb-pulse-in", PulseInputNode); + RED.nodes.registerType("bbb-discrete-out", DiscreteOutputNode); + RED.nodes.registerType("bbb-pulse-out", PulseOutputNode); + + // On close, detach the interrupt (if we attached one) and clear any active timers + DiscreteInputNode.prototype.close = function () { + if (this.interruptAttached) { + bonescript.detachInterrupt(this.pin); + } + if (this.intervalId !== null) { + clearInterval(this.intervalId); + } + if (this.debounceTimer !== null) { + clearTimeout(this.debounceTimer); + } + }; + + // On close, detach the interrupt (if we attached one) and clear the interval (if we set one) + PulseInputNode.prototype.close = function () { + if (this.interruptAttached) { + bonescript.detachInterrupt(this.pin); + } + if (this.intervalId !== null) { + clearInterval(this.intervalId); + } + }; + + // On close, clear an active pulse timer + PulseOutputNode.prototype.close = function () { + if (this.pulseTimer !== null) { + clearTimeout(this.pulseTimer); + } + }; } - -// Register the nodes by name. This must be called before overriding any of the Node functions. -RED.nodes.registerType("bbb-analogue-in", AnalogueInputNode); -RED.nodes.registerType("bbb-discrete-in", DiscreteInputNode); -RED.nodes.registerType("bbb-pulse-in", PulseInputNode); -RED.nodes.registerType("bbb-discrete-out", DiscreteOutputNode); -RED.nodes.registerType("bbb-pulse-out", PulseOutputNode); - -// On close, detach the interrupt (if we attached one) and clear any active timers -DiscreteInputNode.prototype.close = function () { - if (this.interruptAttached) { - bonescript.detachInterrupt(this.pin); - } - if (this.intervalId !== null) { - clearInterval(this.intervalId); - } - if (this.debounceTimer !== null) { - clearTimeout(this.debounceTimer); - } -}; - -// On close, detach the interrupt (if we attached one) and clear the interval (if we set one) -PulseInputNode.prototype.close = function () { - if (this.interruptAttached) { - bonescript.detachInterrupt(this.pin); - } - if (this.intervalId !== null) { - clearInterval(this.intervalId); - } -}; - -// On close, clear an active pulse timer -PulseOutputNode.prototype.close = function () { - if (this.pulseTimer !== null) { - clearTimeout(this.pulseTimer); - } -}; diff --git a/hardware/scanBLE/101-scanBLE.js b/hardware/scanBLE/101-scanBLE.js index 24532e37..bf07e549 100644 --- a/hardware/scanBLE/101-scanBLE.js +++ b/hardware/scanBLE/101-scanBLE.js @@ -37,29 +37,30 @@ function BleScan(n) { this.ble_name = n.ble_name; this.ble_uuid = n.ble_uuid; - this.on("input", function(msg){ - noble.startScanning(); - }); - noble.on('scanStart', function(msg) { - var msg = {}; + this.on("input", function(msg){ + noble.startScanning(); + }); + + noble.on('scanStart', function(msg) { + msg = {}; msg.topic = node.topic; msg.payload = "Scanning initiated..." //debugging - //console.log('scanning initiated...'); - node.send(msg); - }); - - noble.on('discover', function(peripheral) { + //console.log('scanning initiated...'); + node.send(msg); + }); + noble.on('discover', function(peripheral) { var msg = {}; - msg.topic = node.topic; + msg.topic = node.topic; msg.payload = "not found"; //check for the device name and the UUID (first one from the UUID list) if(peripheral.advertisement.localName==node.ble_name && peripheral.advertisement.serviceUuids[0]==node.ble_uuid) { msg.payload=peripheral.advertisement.localName; - noble.stopScanning(); } + noble.stopScanning(); + } node.send(msg); -}); + }); this.on("close", function() { try { noble.stopScanning(); } diff --git a/hardware/wemo/60-wemo.js b/hardware/wemo/60-wemo.js index c2e89e2c..3f75f8d2 100644 --- a/hardware/wemo/60-wemo.js +++ b/hardware/wemo/60-wemo.js @@ -16,12 +16,12 @@ module.exports = function(RED) { "use strict"; - var WeMo = new require('wemo'); + var Wemo = require('wemo'); - function WeMoOut(n) { + function WemoOut(n) { RED.nodes.createNode(this,n); this.ipaddr = n.ipaddr; - this.wemoSwitch = new WeMo(n.ipaddr); + this.wemoSwitch = new Wemo(n.ipaddr); var node = this; this.on("input", function(msg) { @@ -35,9 +35,9 @@ module.exports = function(RED) { } }); } - RED.nodes.registerType("wemo out",WeMoOut); + RED.nodes.registerType("wemo out",WemoOut); - function WeMoIn(n) { + function WemoIn(n) { RED.nodes.createNode(this,n); this.ipaddr = n.ipaddr; this.wemoSwitch = new WeMo(n.ipaddr); @@ -58,5 +58,5 @@ module.exports = function(RED) { clearInterval(tick); }); } - RED.nodes.registerType("wemo in",WeMoOut); + RED.nodes.registerType("wemo in",WemoIn); }