diff --git a/nodes/core/hardware/36-rpi-gpio.html b/nodes/core/hardware/36-rpi-gpio.html
index 0da27b77e..639e10bdb 100644
--- a/nodes/core/hardware/36-rpi-gpio.html
+++ b/nodes/core/hardware/36-rpi-gpio.html
@@ -45,7 +45,6 @@
-
@@ -63,11 +62,11 @@
+
-
@@ -189,7 +210,7 @@
icon: "rpi.png",
align: "right",
label: function() {
- if ((this.pin == 12) && (this.out === "pwm")) { return this.name || "PWM: 12"; }
+ if (this.out === "pwm") { return this.name || "PWM: "+this.pin; }
else { return this.name||"Pin: "+this.pin ; }
},
labelStyle: function() {
@@ -197,6 +218,7 @@
},
oneditprepare: function() {
var pinnow = this.pin;
+ var pinsInUse = {};
if (!$("#node-input-out").val()) { $("#node-input-out").val("out"); }
$.getJSON('rpi-gpio/'+this.id,function(data) {
$('#pitype').text(data.type);
@@ -216,17 +238,26 @@
}
});
- var hidepwm = function() {
- if ($('#node-input-pin').val() == 12) {
- $('#node-set-pwm').show();
+ $.getJSON('rpi-pins/'+this.id,function(data) {
+ pinsInUse = data || {};
+ });
+
+ $("#node-input-pin").change(function() {
+ var pinnew = $("#node-input-pin").val();
+ if ((pinnew) && (pinnew !== pinnow)) {
+ if (pinsInUse.hasOwnProperty(pinnew)) {
+ RED.notify("Pin "+pinnew+" already in use.","info");
+ }
+ pinnow = pinnew;
}
- else {
- $('#node-set-pwm').hide();
- $('#node-input-out').val("out");
+ });
+
+ $("#node-input-out").change(function() {
+ var newtype = $("#node-input-out option:selected").val();
+ if ((pinsInUse.hasOwnProperty(pinnow)) && (pinsInUse[pinnow] !== newtype)) {
+ RED.notify("Pin "+pinnow+" already set as "+pinsInUse[pinnow],"error");
}
- };
- $("#node-input-pin").change(function () { hidepwm(); });
- hidepwm();
+ });
var hidestate = function () {
if ($("#node-input-out").val() === "pwm") {
diff --git a/nodes/core/hardware/36-rpi-gpio.js b/nodes/core/hardware/36-rpi-gpio.js
index 2a56ad17e..9e8048e03 100644
--- a/nodes/core/hardware/36-rpi-gpio.js
+++ b/nodes/core/hardware/36-rpi-gpio.js
@@ -18,179 +18,202 @@ module.exports = function(RED) {
"use strict";
var util = require("util");
var exec = require('child_process').exec;
+ var spawn = require('child_process').spawn;
var fs = require('fs');
- var gpioCommand = '/usr/local/bin/gpio';
+ var gpioCommand = __dirname+'/nrgpio';
if (!fs.existsSync("/dev/ttyAMA0")) { // unlikely if not on a Pi
+ //util.log("Info : Ignoring Raspberry Pi specific node.");
throw "Info : Ignoring Raspberry Pi specific node.";
}
- if (!fs.existsSync(gpioCommand)) { // gpio command not installed
- throw "Info : Can't find Raspberry Pi wiringPi gpio command.";
+ if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
+ util.log("[rpi-gpio] Info : Can't find Pi RPi.GPIO python library.");
+ throw "Warning : Can't find Pi RPi.GPIO python library.";
}
- // Map physical P1 pins to Gordon's Wiring-Pi Pins (as they should be V1/V2 tolerant)
- var pintable = {
- // Physical : WiringPi
- "11":"0",
- "12":"1",
- "13":"2",
- "15":"3",
- "16":"4",
- "18":"5",
- "22":"6",
- "7":"7",
- "3":"8",
- "5":"9",
- "24":"10",
- "26":"11",
- "19":"12",
- "21":"13",
- "23":"14",
- "8":"15",
- "10":"16",
- "27":"30",
- "28":"31",
- "29":"21",
- "31":"22",
- "32":"26",
- "33":"23",
- "35":"24",
- "36":"27",
- "37":"25",
- "38":"28",
- "40":"29"
- }
- var tablepin = {
- // WiringPi : Physical
- "0":"11",
- "1":"12",
- "2":"13",
- "3":"15",
- "4":"16",
- "5":"18",
- "6":"22",
- "7":"7",
- "8":"3",
- "9":"5",
- "10":"24",
- "11":"26",
- "12":"19",
- "13":"21",
- "14":"23",
- "15":"8",
- "16":"10",
- "30":"27",
- "31":"28",
- "21":"29",
- "22":"31",
- "26":"32",
- "23":"33",
- "24":"35",
- "27":"36",
- "25":"37",
- "28":"38",
- "29":"40"
+ if ( !(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]) )) {
+ util.log("[rpi-gpio] Error : "+gpioCommand+" needs to be executable.");
+ throw "Error : nrgpio must to be executable.";
}
+ var pinsInUse = {};
+ var pinTypes = {"out":"digital output", "tri":"input", "up":"input with pull up", "down":"input with pull down", "pwm":"PWM output"};
+
function GPIOInNode(n) {
RED.nodes.createNode(this,n);
this.buttonState = -1;
- this.pin = pintable[n.pin];
+ this.pin = n.pin;
this.intype = n.intype;
this.read = n.read || false;
if (this.read) { this.buttonState = -2; }
var node = this;
+ if (!pinsInUse.hasOwnProperty(this.pin)) {
+ pinsInUse[this.pin] = this.intype;
+ }
+ else {
+ if ((pinsInUse[this.pin] !== this.intype)||(pinsInUse[this.pin] === "pwm")) {
+ node.error("GPIO pin "+this.pin+" already set as "+pinTypes[pinsInUse[this.pin]]);
+ }
+ }
if (node.pin !== undefined) {
- exec(gpioCommand+" mode "+node.pin+" "+node.intype, function(err,stdout,stderr) {
- if (err) { node.error(err); }
- else {
- node._interval = setInterval( function() {
- exec(gpioCommand+" read "+node.pin, function(err,stdout,stderr) {
- if (err) { node.error(err); }
- else {
- if (node.buttonState !== Number(stdout)) {
- var previousState = node.buttonState;
- node.buttonState = Number(stdout);
- if (previousState !== -1) {
- var msg = {topic:"pi/"+tablepin[node.pin], payload:node.buttonState};
- node.send(msg);
- }
- }
- }
- });
- }, 250);
+ if (node.intype === "tri") {
+ node.child = spawn(gpioCommand, ["in",node.pin]);
+ } else {
+ node.child = spawn(gpioCommand, ["in",node.pin,node.intype]);
+ }
+ node.running = true;
+ node.status({fill:"green",shape:"dot",text:"OK"});
+
+ node.child.stdout.on('data', function (data) {
+ data = data.toString().trim();
+ if (data.length > 0) {
+ if (node.buttonState !== -1) {
+ node.send({ topic:"pi/"+node.pin, payload:Number(data) });
+ }
+ node.buttonState = data;
+ node.status({fill:"green",shape:"dot",text:data});
+ if (RED.settings.verbose) { node.log("out: "+data+" :"); }
}
});
+
+ node.child.stderr.on('data', function (data) {
+ if (RED.settings.verbose) { node.log("err: "+data+" :"); }
+ });
+
+ node.child.on('close', function (code) {
+ if (RED.settings.verbose) { node.log("ret: "+code+" :"); }
+ node.child = null;
+ node.running = false;
+ node.status({fill:"red",shape:"circle",text:""});
+ });
+
+ node.child.on('error', function (err) {
+ if (err.errno === "ENOENT") { node.warn('Command not found'); }
+ else if (err.errno === "EACCES") { node.warn('Command not executable'); }
+ else { node.log('error: ' + err); }
+ });
+
}
else {
node.error("Invalid GPIO pin: "+node.pin);
}
node.on("close", function() {
- clearInterval(node._interval);
+ if (node.child != null) {
+ node.child.stdin.write(" close "+node.pin);
+ node.child.kill('SIGKILL');
+ }
+ node.status({fill:"red",shape:"circle",text:""});
+ delete pinsInUse[node.pin];
+ if (RED.settings.verbose) { node.log("end"); }
});
}
+ RED.nodes.registerType("rpi-gpio in",GPIOInNode);
+
function GPIOOutNode(n) {
RED.nodes.createNode(this,n);
- this.pin = pintable[n.pin];
+ this.pin = n.pin;
this.set = n.set || false;
this.level = n.level || 0;
this.out = n.out || "out";
var node = this;
- (node.out === "pwm") ? (node.op = "pwm") : (node.op = "write");
+ if (!pinsInUse.hasOwnProperty(this.pin)) {
+ pinsInUse[this.pin] = this.out;
+ }
+ else {
+ if ((pinsInUse[this.pin] !== this.out)||(pinsInUse[this.pin] === "pwm")) {
+ node.error("GPIO pin "+this.pin+" already set as "+pinTypes[pinsInUse[this.pin]]);
+ }
+ }
+
+ function inputlistener(msg) {
+ if (msg.payload === "true") { msg.payload = true; }
+ if (msg.payload === "false") { msg.payload = false; }
+ var out = Number(msg.payload);
+ var limit = 1;
+ if (node.out === "pwm") { limit = 100; }
+ if ((out >= 0) && (out <= limit)) {
+ if (RED.settings.verbose) { node.log("inp: "+msg.payload); }
+ if (node.child !== null) { node.child.stdin.write(msg.payload+"\n"); }
+ else { node.warn("Command not running"); }
+ node.status({fill:"green",shape:"dot",text:msg.payload});
+ }
+ else { node.warn("Invalid input: "+out); }
+ }
if (node.pin !== undefined) {
- exec(gpioCommand+" mode "+node.pin+" "+node.out, function(err,stdout,stderr) {
- if (err) { node.error(err); }
- else {
- if (node.set && (node.out === "out")) {
- exec(gpioCommand+" write "+node.pin+" "+node.level, function(err,stdout,stderr) {
- if (err) { node.error(err); }
- });
- }
- node.on("input", function(msg) {
- if (msg.payload === "true") { msg.payload = true; }
- if (msg.payload === "false") { msg.payload = false; }
- var out = Number(msg.payload);
- var limit = 1;
- if (node.out === "pwm") { limit = 1023; }
- if ((out >= 0) && (out <= limit)) {
- exec(gpioCommand+" "+node.op+" "+node.pin+" "+out, function(err,stdout,stderr) {
- if (err) { node.error(err); }
- });
- }
- else { node.warn("Invalid input: "+out); }
- });
- }
+ if (node.set && (node.out === "out")) {
+ node.child = spawn(gpioCommand, [node.out,node.pin,node.level]);
+ } else {
+ node.child = spawn(gpioCommand, [node.out,node.pin]);
+ }
+ node.running = true;
+ node.status({fill:"green",shape:"dot",text:"OK"});
+
+ node.on("input", inputlistener);
+
+ node.child.stdout.on('data', function (data) {
+ if (RED.settings.verbose) { node.log("out: "+data+" :"); }
});
+
+ node.child.stderr.on('data', function (data) {
+ if (RED.settings.verbose) { node.log("err: "+data+" :"); }
+ });
+
+ node.child.on('close', function (code) {
+ if (RED.settings.verbose) { node.log("ret: "+code+" :"); }
+ node.child = null;
+ node.running = false;
+ node.status({fill:"red",shape:"circle",text:""});
+ });
+
+ node.child.on('error', function (err) {
+ if (err.errno === "ENOENT") { node.warn('Command not found'); }
+ else if (err.errno === "EACCES") { node.warn('Command not executable'); }
+ else { node.log('error: ' + err); }
+ });
+
}
else {
node.error("Invalid GPIO pin: "+node.pin);
}
node.on("close", function() {
- exec(gpioCommand+" mode "+node.pin+" in");
+ if (node.child != null) {
+ node.child.stdin.write(" close "+node.pin);
+ node.child.kill('SIGKILL');
+ }
+ node.status({fill:"red",shape:"circle",text:""});
+ delete pinsInUse[node.pin];
+ if (RED.settings.verbose) { node.log("end"); }
});
+
}
var pitype = { type:"" };
- exec(gpioCommand+" -v | grep Type", function(err,stdout,stderr) {
+ exec(gpioCommand+" rev 0", function(err,stdout,stderr) {
if (err) {
- util.log('[36-rpi-gpio.js] Error: "'+gpioCommand+' -v" command failed for some reason.');
+ console.log('[rpi-gpio] Version command failed for some reason.');
}
else {
- pitype = { type:(stdout.split(","))[0].split(": ")[1], rev:(stdout.split(","))[1].split(": ")[1] };
+ if (stdout.trim() == "0") { pitype = { type:"Compute" }; }
+ else if (stdout.trim() == "1") { pitype = { type:"A/B v1" }; }
+ else if (stdout.trim() == "2") { pitype = { type:"A/B v2" }; }
+ else if (stdout.trim() == "3") { pitype = { type:"Model B+" }; }
+ else { console.log("SAW Pi TYPE",stdout.trim()); }
}
});
-
- RED.nodes.registerType("rpi-gpio in",GPIOInNode);
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
RED.httpAdmin.get('/rpi-gpio/:id',function(req,res) {
res.send( JSON.stringify(pitype) );
});
+
+ RED.httpAdmin.get('/rpi-pins/:id',function(req,res) {
+ res.send( JSON.stringify(pinsInUse) );
+ });
}
diff --git a/nodes/core/hardware/nrgpio b/nodes/core/hardware/nrgpio
new file mode 100755
index 000000000..88b381a7d
--- /dev/null
+++ b/nodes/core/hardware/nrgpio
@@ -0,0 +1,16 @@
+#
+# Copyright 2014 IBM Corp.
+#
+# 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.
+#
+
+BASEDIR=$(dirname $0)
+sudo python -u $BASEDIR/nrgpio.py $@
diff --git a/nodes/core/hardware/nrgpio.py b/nodes/core/hardware/nrgpio.py
new file mode 100644
index 000000000..122a0f5ee
--- /dev/null
+++ b/nodes/core/hardware/nrgpio.py
@@ -0,0 +1,125 @@
+#
+# Copyright 2014 IBM Corp.
+#
+# 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.
+#
+
+# Import library functions we need
+import RPi.GPIO as GPIO
+import sys
+
+bounce = 20 # bounce time in mS to apply
+
+if len(sys.argv) > 1:
+ cmd = sys.argv[1].lower()
+ pin = int(sys.argv[2])
+ GPIO.setmode(GPIO.BOARD)
+ GPIO.setwarnings(False)
+
+ if cmd == "pwm":
+ #print "Initialised pin "+str(pin)+" to PWM"
+ GPIO.setup(pin,GPIO.OUT)
+ p = GPIO.PWM(pin, 100)
+ p.start(0)
+
+ while True:
+ try:
+ data = raw_input()
+ if data == "close":
+ GPIO.cleanup(pin)
+ sys.exit(0)
+ p.ChangeDutyCycle(float(data))
+ except EOFError: # hopefully always caused by us sigint'ing the program
+ GPIO.cleanup(pin)
+ sys.exit(0)
+ except Exception as ex:
+ print "bad data: "+data
+
+ if cmd == "buzz":
+ #print "Initialised pin "+str(pin)+" to Buzz"
+ GPIO.setup(pin,GPIO.OUT)
+ p = GPIO.PWM(pin, 100)
+ p.stop()
+
+ while True:
+ try:
+ data = raw_input()
+ if data == "close":
+ GPIO.cleanup(pin)
+ sys.exit(0)
+ elif float(data) == 0:
+ p.stop()
+ else:
+ p.start(50)
+ p.ChangeFrequency(float(data))
+ except EOFError: # hopefully always caused by us sigint'ing the program
+ GPIO.cleanup(pin)
+ sys.exit(0)
+ except Exception as ex:
+ print "bad data: "+data
+
+ elif cmd == "out":
+ #print "Initialised pin "+str(pin)+" to OUT"
+ GPIO.setup(pin,GPIO.OUT)
+ if len(sys.argv) == 4:
+ GPIO.output(pin,int(sys.argv[3]))
+
+ while True:
+ try:
+ data = raw_input()
+ if data == "close":
+ GPIO.cleanup(pin)
+ sys.exit(0)
+ data = int(data)
+ except EOFError: # hopefully always caused by us sigint'ing the program
+ GPIO.cleanup(pin)
+ sys.exit(0)
+ except:
+ data = 0
+ if data != 0:
+ data = 1
+ GPIO.output(pin,data)
+
+ elif cmd == "in":
+ #print "Initialised pin "+str(pin)+" to IN"
+ def handle_callback(chan):
+ print GPIO.input(chan)
+
+ if len(sys.argv) == 4:
+ if sys.argv[3].lower() == "up":
+ GPIO.setup(pin,GPIO.IN,GPIO.PUD_UP)
+ elif sys.argv[3].lower() == "down":
+ GPIO.setup(pin,GPIO.IN,GPIO.PUD_DOWN)
+ else:
+ GPIO.setup(pin,GPIO.IN)
+ else:
+ GPIO.setup(pin,GPIO.IN)
+ print GPIO.input(pin)
+ GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback, bouncetime=bounce)
+
+ while True:
+ try:
+ data = raw_input()
+ if data == "close":
+ GPIO.cleanup(pin)
+ sys.exit(0)
+ except EOFError: # hopefully always caused by us sigint'ing the program
+ GPIO.cleanup(pin)
+ sys.exit(0)
+
+ elif cmd == "rev":
+ print GPIO.RPI_REVISION
+
+ elif cmd == "ver":
+ print GPIO.VERSION
+
+else:
+ print "Bad parameters - {in|out|pwm} {pin} {value|up|down}"