1
0
mirror of https://github.com/vimagick/dockerfiles.git synced 2024-12-04 10:35:07 +02:00

update node-red-arm

This commit is contained in:
kev 2017-04-26 17:35:14 +08:00
parent c05e1ead36
commit 3af6c8c05f
7 changed files with 608 additions and 98 deletions

View File

@ -6,21 +6,24 @@ FROM easypi/alpine-arm
MAINTAINER EasyPi Software Foundation
RUN set -xe \
&& apk add --no-cache bash \
build-base \
&& apk add --no-cache build-base \
ca-certificates \
curl \
nodejs \
python \
python-dev \
sudo \
&& curl https://bootstrap.pypa.io/get-pip.py | python \
&& pip install rpi.gpio \
python3 \
python3-dev \
&& ln -sf /usr/bin/python3 /usr/bin/python \
&& pip3 install --no-cache-dir rpi.gpio \
six \
&& npm install -g --unsafe-perm node-red \
node-red-admin \
node-red-dashboard \
&& cd /usr/lib/node_modules/node-red/nodes/core/hardware \
&& curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/arm/patch/nrgpio > nrgpio \
&& curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/arm/patch/nrgpio.py > nrgpio.py \
&& curl -sSL https://github.com/vimagick/dockerfiles/raw/master/node-red/arm/patch/36-rpi-gpio.js > 36-rpi-gpio.js \
&& apk del build-base \
python-dev \
python3-dev \
&& rm -rf /tmp/npm-*
WORKDIR /root/.node-red

View File

@ -0,0 +1,346 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* 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.
**/
module.exports = function(RED) {
"use strict";
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var fs = require('fs');
var gpioCommand = __dirname+'/nrgpio';
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
if (cpuinfo.indexOf(": BCM") === -1) { throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); }
} catch(err) {
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
}
try {
fs.statSync("/usr/share/doc/python-rpi.gpio"); // test on Raspbian
// /usr/lib/python3.5/dist-packages/RPi/GPIO
} catch(err) {
try {
fs.statSync("/usr/lib/python3.5/site-packages/RPi/GPIO"); // test on Arch
}
catch(err) {
try {
fs.statSync("/usr/lib/python3.5/dist-packages/RPi/GPIO"); // test on Hypriot
}
catch(err) {
RED.log.warn(RED._("rpi-gpio.errors.libnotfound"));
throw "Warning : "+RED._("rpi-gpio.errors.libnotfound");
}
}
}
if ( !(1 & parseInt((fs.statSync(gpioCommand).mode & parseInt("777", 8)).toString(8)[0]) )) {
RED.log.error(RED._("rpi-gpio.errors.needtobeexecutable",{command:gpioCommand}));
throw "Error : "+RED._("rpi-gpio.errors.mustbeexecutable");
}
// the magic to make python print stuff immediately
process.env.PYTHONUNBUFFERED = 1;
var pinsInUse = {};
var pinTypes = {"out":RED._("rpi-gpio.types.digout"), "tri":RED._("rpi-gpio.types.input"), "up":RED._("rpi-gpio.types.pullup"), "down":RED._("rpi-gpio.types.pulldown"), "pwm":RED._("rpi-gpio.types.pwmout")};
function GPIOInNode(n) {
RED.nodes.createNode(this,n);
this.buttonState = -1;
this.pin = n.pin;
this.intype = n.intype;
this.read = n.read || false;
this.debounce = Number(n.debounce || 25);
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.warn(RED._("rpi-gpio.errors.alreadyset",{pin:this.pin,type:pinTypes[pinsInUse[this.pin]]}));
}
}
if (node.pin !== undefined) {
node.child = spawn(gpioCommand, ["in",node.pin,node.intype,node.debounce]);
node.running = true;
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
node.child.stdout.on('data', function (data) {
var d = data.toString().trim().split("\n");
for (var i = 0; i < d.length; i++) {
if (node.running && node.buttonState !== -1 && !isNaN(Number(d[i])) && node.buttonState !== d[i]) {
node.send({ topic:"pi/"+node.pin, payload:Number(d[i]) });
}
node.buttonState = d[i];
node.status({fill:"green",shape:"dot",text:d[i]});
if (RED.settings.verbose) { node.log("out: "+d[i]+" :"); }
}
});
node.child.stderr.on('data', function (data) {
if (RED.settings.verbose) { node.log("err: "+data+" :"); }
});
node.child.on('close', function (code) {
node.running = false;
node.child = null;
if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
if (node.done) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.done();
}
else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
});
node.child.on('error', function (err) {
if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
else { node.error(RED._("rpi-gpio.errors.error",{error:err.errno})) }
});
}
else {
node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin);
}
node.on("close", function(done) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
delete pinsInUse[node.pin];
if (node.child != null) {
node.done = done;
node.child.stdin.write("close "+node.pin);
node.child.kill('SIGKILL');
}
else { done(); }
});
}
RED.nodes.registerType("rpi-gpio in",GPIOInNode);
function GPIOOutNode(n) {
RED.nodes.createNode(this,n);
this.pin = n.pin;
this.set = n.set || false;
this.level = n.level || 0;
this.freq = n.freq || 100;
this.out = n.out || "out";
var node = this;
if (!pinsInUse.hasOwnProperty(this.pin)) {
pinsInUse[this.pin] = this.out;
}
else {
if ((pinsInUse[this.pin] !== this.out)||(pinsInUse[this.pin] === "pwm")) {
node.warn(RED._("rpi-gpio.errors.alreadyset",{pin:this.pin,type: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("out: "+msg.payload); }
if (node.child !== null) {
node.child.stdin.write(msg.payload+"\n");
node.status({fill:"green",shape:"dot",text:msg.payload.toString()});
}
else {
node.error(RED._("rpi-gpio.errors.pythoncommandnotfound"),msg);
node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.not-running"});
}
}
else { node.warn(RED._("rpi-gpio.errors.invalidinput")+": "+out); }
}
if (node.pin !== undefined) {
if (node.set && (node.out === "out")) {
node.child = spawn(gpioCommand, [node.out,node.pin,node.level]);
node.status({fill:"green",shape:"dot",text:node.level});
} else {
node.child = spawn(gpioCommand, [node.out,node.pin,node.freq]);
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
}
node.running = true;
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) {
node.child = null;
node.running = false;
if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
if (node.done) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.done();
}
else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
});
node.child.on('error', function (err) {
if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
});
}
else {
node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin);
}
node.on("close", function(done) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
delete pinsInUse[node.pin];
if (node.child != null) {
node.done = done;
node.child.stdin.write("close "+node.pin);
node.child.kill('SIGKILL');
}
else { done(); }
});
}
RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
function PiMouseNode(n) {
RED.nodes.createNode(this,n);
this.butt = n.butt || 7;
var node = this;
node.child = spawn(gpioCommand+".py", ["mouse",node.butt]);
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
node.child.stdout.on('data', function (data) {
data = Number(data);
if (data === 1) { node.send({ topic:"pi/mouse", button:data, payload:1 }); }
else { node.send({ topic:"pi/mouse", button:data, payload:0 }); }
});
node.child.stderr.on('data', function (data) {
if (RED.settings.verbose) { node.log("err: "+data+" :"); }
});
node.child.on('close', function (code) {
node.child = null;
node.running = false;
if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
if (node.done) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.done();
}
else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
});
node.child.on('error', function (err) {
if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
});
node.on("close", function(done) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
if (node.child != null) {
node.done = done;
node.child.kill('SIGINT');
node.child = null;
}
else { done(); }
});
}
RED.nodes.registerType("rpi-mouse",PiMouseNode);
function PiKeyboardNode(n) {
RED.nodes.createNode(this,n);
var node = this;
node.child = spawn(gpioCommand+".py", ["kbd","0"]);
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
node.child.stdout.on('data', function (data) {
var b = data.toString().trim().split(",");
var act = "up";
if (b[1] === "1") { act = "down"; }
if (b[1] === "2") { act = "repeat"; }
node.send({ topic:"pi/key", payload:Number(b[0]), action:act });
});
node.child.stderr.on('data', function (data) {
if (RED.settings.verbose) { node.log("err: "+data+" :"); }
});
node.child.on('close', function (code) {
node.running = false;
node.child = null;
if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
if (node.done) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.done();
}
else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
});
node.child.on('error', function (err) {
if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
});
node.on("close", function(done) {
node.status({});
if (node.child != null) {
node.done = done;
node.child.kill('SIGINT');
node.child = null;
}
else { done(); }
});
}
RED.nodes.registerType("rpi-keyboard",PiKeyboardNode);
var pitype = { type:"" };
exec(gpioCommand+" info", function(err,stdout,stderr) {
if (err) {
RED.log.info(RED._("rpi-gpio.errors.version"));
}
else {
try {
var info = JSON.parse( stdout.trim().replace(/\'/g,"\"") );
pitype.type = info["TYPE"];
}
catch(e) {
RED.log.info(RED._("rpi-gpio.errors.sawpitype"),stdout.trim());
}
}
});
RED.httpAdmin.get('/rpi-gpio/:id', RED.auth.needsPermission('rpi-gpio.read'), function(req,res) {
res.json(pitype);
});
RED.httpAdmin.get('/rpi-pins/:id', RED.auth.needsPermission('rpi-gpio.read'), function(req,res) {
res.json(pinsInUse);
});
}

View File

@ -1,20 +0,0 @@
--- a/36-rpi-gpio.js
+++ b/36-rpi-gpio.js
@@ -224,7 +224,7 @@
this.butt = n.butt || 7;
var node = this;
- node.child = spawn(gpioCommand+".py", ["mouse,node.butt]);
+ node.child = spawn(gpioCommand, ["mouse",node.butt]);
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
node.child.stdout.on('data', function (data) {
@@ -270,7 +270,7 @@
RED.nodes.createNode(this,n);
var node = this;
- node.child = spawn(gpioCommand+".py", ["kbd","0"]);
+ node.child = spawn(gpioCommand, ["kbd","0"]);
node.status({fill:"green",shape:"dot",text:"common.status.ok"});
node.child.stdout.on('data', function (data) {

View File

@ -1,4 +1,4 @@
Node-RED PATCH
==============
python2 cannot read usb keyboard input device
python2 (on alpine linux) cannot read usb keyboard input device

View File

@ -1,9 +1,17 @@
#!/bin/bash
#!/bin/sh
#
# Copyright JS Foundation and other contributors, http://js.foundation
#
# 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)
if [[ $1 == mouse || $1 == kbd ]]
then
exec python3 -u $BASEDIR/nrinput.py $@
else
sudo python -u $BASEDIR/nrgpio.py $@
fi
python -u $BASEDIR/nrgpio.py $@

235
node-red/arm/patch/nrgpio.py Executable file
View File

@ -0,0 +1,235 @@
#!/usr/bin/python
#
# Copyright JS Foundation and other contributors, http://js.foundation
#
# 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 six
import RPi.GPIO as GPIO
import struct
import sys
import os
import subprocess
from time import sleep
bounce = 25;
if len(sys.argv) > 2:
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"
try:
freq = int(sys.argv[3])
except:
freq = 100
GPIO.setup(pin,GPIO.OUT)
p = GPIO.PWM(pin, freq)
p.start(0)
while True:
try:
data = six.input()
if 'close' in data:
sys.exit(0)
p.ChangeDutyCycle(float(data))
except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program
GPIO.cleanup(pin)
sys.exit(0)
except Exception as ex:
six.print_("bad data: "+data)
elif 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 = six.input()
if 'close' in data:
sys.exit(0)
elif float(data) == 0:
p.stop()
else:
p.start(50)
p.ChangeFrequency(float(data))
except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program
GPIO.cleanup(pin)
sys.exit(0)
except Exception as ex:
six.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 = six.input()
if 'close' in data:
sys.exit(0)
data = int(data)
except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program
GPIO.cleanup(pin)
sys.exit(0)
except:
if len(sys.argv) == 4:
data = int(sys.argv[3])
else:
data = 0
if data != 0:
data = 1
GPIO.output(pin,data)
elif cmd == "in":
#print "Initialised pin "+str(pin)+" to IN"
bounce = float(sys.argv[4])
def handle_callback(chan):
sleep(bounce/1000.0)
six.print_(GPIO.input(chan))
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)
six.print_(GPIO.input(pin))
GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback, bouncetime=int(bounce))
while True:
try:
data = six.input()
if 'close' in data:
sys.exit(0)
except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program
GPIO.cleanup(pin)
sys.exit(0)
elif cmd == "byte":
#print "Initialised BYTE mode - "+str(pin)+
list = [7,11,13,12,15,16,18,22]
GPIO.setup(list,GPIO.OUT)
while True:
try:
data = six.input()
if 'close' in data:
sys.exit(0)
data = int(data)
except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program
GPIO.cleanup()
sys.exit(0)
except:
data = 0
for bit in range(8):
if pin == 1:
mask = 1 << (7 - bit)
else:
mask = 1 << bit
GPIO.output(list[bit], data & mask)
elif cmd == "borg":
#print "Initialised BORG mode - "+str(pin)+
GPIO.setup(11,GPIO.OUT)
GPIO.setup(13,GPIO.OUT)
GPIO.setup(15,GPIO.OUT)
r = GPIO.PWM(11, 100)
g = GPIO.PWM(13, 100)
b = GPIO.PWM(15, 100)
r.start(0)
g.start(0)
b.start(0)
while True:
try:
data = six.input()
if 'close' in data:
sys.exit(0)
c = data.split(",")
r.ChangeDutyCycle(float(c[0]))
g.ChangeDutyCycle(float(c[1]))
b.ChangeDutyCycle(float(c[2]))
except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program
GPIO.cleanup()
sys.exit(0)
except:
data = 0
elif cmd == "mouse": # catch mice button events
file = open( "/dev/input/mice", "rb" )
oldbutt = 0
def getMouseEvent():
global oldbutt
global pin
buf = file.read(3)
pin = pin & 0x07
button = six.byte2int(buf) & pin # mask out just the required button(s)
if button != oldbutt: # only send if changed
oldbutt = button
six.print_(button)
while True:
try:
getMouseEvent()
except:
file.close()
sys.exit(0)
elif cmd == "kbd": # catch keyboard button events
try:
while not os.path.isdir("/dev/input/by-path"):
time.sleep(10)
infile = subprocess.check_output("ls /dev/input/by-path/ | grep -m 1 'kbd'", shell=True).strip()
infile_path = "/dev/input/by-path/" + infile.decode('utf-8')
EVENT_SIZE = struct.calcsize('llHHI')
file = open(infile_path, "rb")
event = file.read(EVENT_SIZE)
while event:
(tv_sec, tv_usec, type, code, value) = struct.unpack('llHHI', event)
#if type != 0 or code != 0 or value != 0:
if type == 1:
# type,code,value
six.print_("%u,%u" % (code, value))
event = file.read(EVENT_SIZE)
six.print_("0,0")
file.close()
sys.exit(0)
except:
file.close()
sys.exit(0)
elif len(sys.argv) > 1:
cmd = sys.argv[1].lower()
if cmd == "rev":
six.print_(GPIO.RPI_REVISION)
elif cmd == "ver":
six.print_(GPIO.VERSION)
elif cmd == "info":
six.print_(GPIO.RPI_INFO)
else:
six.print_("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}")
six.print_(" only ver (gpio version) and info (board information) accept no pin parameter.")
else:
six.print_("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}")

View File

@ -1,62 +0,0 @@
#!/usr/bin/env python3
import os
import struct
import subprocess
import sys
import time
if sys.version_info < (3,0):
print("Sorry - currently only configured to work with python 3.x")
sys.exit(1)
if len(sys.argv) > 2:
cmd = sys.argv[1].lower()
pin = int(sys.argv[2])
if cmd == "mouse": # catch mice button events
file = open( "/dev/input/mice", "rb" )
oldbutt = 0
def getMouseEvent():
global oldbutt
global pin
buf = file.read(3)
pin = pin & 0x07
button = ord( chr(buf[0]) ) & pin # mask out just the required button(s)
if button != oldbutt: # only send if changed
oldbutt = button
print(button)
while True:
try:
getMouseEvent()
except:
file.close()
sys.exit(0)
elif cmd == "kbd": # catch keyboard button events
try:
while not os.path.isdir("/dev/input/by-path"):
time.sleep(10)
infile = subprocess.check_output("ls /dev/input/by-path/ | grep -m 1 'kbd'", shell=True).strip()
infile_path = "/dev/input/by-path/" + infile.decode()
EVENT_SIZE = struct.calcsize('llHHI')
file = open(infile_path, "rb")
event = file.read(EVENT_SIZE)
while event:
(tv_sec, tv_usec, type, code, value) = struct.unpack('llHHI', event)
#if type != 0 or code != 0 or value != 0:
if type == 1:
# type,code,value
print("%u,%u" % (code, value))
event = file.read(EVENT_SIZE)
print("0,0")
file.close()
sys.exit(0)
except:
file.close()
sys.exit(0)
else:
print("Bad parameters - mouse|kbd {pin}")