Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Nathanaël Lécaudé 2021-01-12 11:04:35 -05:00
commit e035fa8697
316 changed files with 12401 additions and 6582 deletions

34
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,34 @@
<!--
## Before you hit that Submit button....
This issue tracker is for problems with the extra nodes maintained by the Node-RED project.
If your issue is:
- a general 'how-to' type question,
- a feature request or suggestion for a change,
- or problems with 3rd party (`node-red-contrib-`) nodes
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack), or ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
That way the whole Node-RED user community can help, rather than rely on the core development team.
## So you have a real issue to raise...
To help us understand the issue, please fill-in as much of the following information as you can:
-->
### Which node are you reporting an issue on?
### What are the steps to reproduce?
### What happens?
### What do you expect to happen?
### Please tell us about your environment:
- [ ] Node-RED version:
- [ ] node.js version:
- [ ] npm version:
- [ ] Platform/OS:
- [ ] Browser:

34
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,34 @@
<!--
## Before you hit that Submit button....
Please read our [contribution guidelines](https://github.com/node-red/node-red-nodes/blob/master/CONTRIBUTING.md)
before submitting a pull-request.
## Types of changes
What types of changes does your code introduce?
Put an `x` in the boxes that apply
-->
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
<!--
If you want to raise a pull-request with a new feature, or a refactoring
of existing code, it **may well get rejected** if it hasn't been discussed on
the [forum](https://discourse.nodered.org) or
[slack team](https://nodered.org/slack) first.
-->
## Proposed changes
<!-- Describe the nature of this change. What problem does it address? -->
## Checklist
<!-- Put an `x` in the boxes that apply -->
- [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red-nodes/blob/master/CONTRIBUTING.md)
- [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team.
- [ ] I have run `grunt` to verify the unit tests pass
- [ ] I have added suitable unit tests to cover the new/changed functionality

3
.gitignore vendored
View File

@ -5,3 +5,6 @@ coverage
puball.sh
setenv.sh
/.project
package-lock.json
social/xmpp/92-xmpp.old
*.tgz

View File

@ -1,19 +1,15 @@
{
"asi": true, // allow missing semicolons
"curly": true, // require braces
"eqnull": true, // ignore ==null
//"eqeqeq": true, // enforce ===
"freeze": true, // don't allow override
"indent": 4, // default indent of 4
"forin": true, // require property filtering in "for in" loops
"immed": true, // require immediate functions to be wrapped in ( )
"nonbsp": true, // warn on unexpected whitespace breaking chars
//"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
//"unused": true, // Check for unused functions and variables
"loopfunc": true, // allow functions to be defined in loops
//"expr": true, // allow ternery operator syntax...
"shadow": true, // allow variable shadowing (re-use of names...)
"sub": true, // don't warn that foo['bar'] should be written as foo.bar
"proto": true, // allow setting of __proto__ in node < v0.12
"esversion": 6 // allow es6
"asi": true,
"curly": true,
"eqnull": true,
"freeze": true,
"indent": 4,
"forin": true,
"immed": true,
"nonbsp": true,
"loopfunc": true,
"shadow": true,
"sub": true,
"proto": true,
"esversion": 8
}

View File

@ -1,27 +1,32 @@
sudo: false
language: node_js
env:
- CXX="g++-4.8"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- gcc-4.8
matrix:
allow_failures:
- node_js: "7"
node_js:
- "8"
- "7"
- "6"
- "4"
# allow_failures:
# - node_js: 14
include:
- node_js: 14
- node_js: 12
- node_js: 10
- node_js: 8
# - python: 2.7
# language: python
# before_script: pip install flake8
# script: flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
- python: 3.7
language: python
dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069)
sudo: required # required for Python 3.7 (travis-ci/travis-ci#9069)
before_script: pip install flake8
script: flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
before_install:
- npm i -g npm
before_script:
- npm install -g istanbul grunt-cli
- npm install coveralls
- npm install git+https://github.com/node-red/node-red.git
- export NODE_RED_HOME=`pwd`/node_modules/node-red
- (cd $NODE_RED_HOME ; npm install nock@~0.48.0)
# Remove the './node_modules/.bin:' entry, see https://github.com/travis-ci/travis-ci/issues/8813
- export PATH=`echo ${PATH} | sed -re 's,(^|:)(./)?node_modules/.bin($|:),\1,'`
- npm install -g nyc grunt-cli coveralls
- npm install node-red
script:
- istanbul cover grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js || true ) && rm -rf coverage
# - istanbul cover grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
# - nyc --check-coverage --reporter=text --reporter=lcovonly --reporter=html grunt
# - nyc grunt && rm -rf coverage .nyc_output
- nyc grunt && nyc report --reporter=text-lcov | coveralls

View File

@ -6,7 +6,7 @@
<!-- First, the content of the edit dialog is defined. -->
<script type="text/x-red" data-template-name="sample">
<script type="text/html" data-template-name="sample">
<!-- data-template-name identifies the node type this is for -->
<!-- Each of the following divs creates a field in the edit dialog. -->
@ -30,7 +30,7 @@
<!-- Next, some simple help text is provided for the node. -->
<script type="text/x-red" data-help-name="sample">
<script type="text/html" data-help-name="sample">
<!-- 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 -->

View File

@ -30,13 +30,13 @@ At a minimum, please include:
## Feature requests
For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red).
For feature requests, please raise them on the [Node-RED Forum](https://discourse.nodered.org).
## Pull-Requests
If you want to raise a pull-request with a new feature, or a refactoring
of existing code, it may well get rejected if you haven't discussed it on
the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
the [Node-RED Forum](https://discourse.nodered.org) first.
### Contributor License Agreement

121
README.md
View File

@ -4,17 +4,17 @@ A collection of nodes for [Node-RED](http://nodered.org). See below for a list.
## Installation
Most of these nodes are available as npm packages. See the list below for the
All of these nodes are available as individual npm packages. See the list below for the
npm package names, or [search npm](https://www.npmjs.org/search?q=node-red-node-).
To install - change to your Node-RED user directory.
This repository acts as an overall store for these nodes - and is not
intended as a way to install them - unless you really do want some development.
To install - either use the manage palette option in the editor, or change to your Node-RED user directory.
cd ~/.node-red
npm install node-red-node-{filename}
This repository acts as an overall store for these nodes - and is no longer
intended as a way to install them - unless you really do want some bloat.
To manually install using this repo:
1. cd into the `nodes` directory of Node-RED
@ -23,15 +23,19 @@ To manually install using this repo:
- run `git clone https://github.com/node-red/node-red-nodes.git`
3. run `npm install` in any of the node subfolders to install individual node's dependencies
## Running Tests
Node.js v6 or newer is required. To run tests on all of the nodes you will need the node-red runtime:
npm i node-red-nodes
npm test
## Contributing
Now that we support npm installation of nodes we recommend people post their own
nodes via [npm](https://www.npmjs.org/). Please read the
[packaging guide notes](http://nodered.org/docs/creating-nodes/packaging.html).
If you are an IBMer, please contact us directly as the contribution process
is slightly different.
For simple typos and single line fixes please just raise an issue pointing out
our mistakes. If you need to raise a pull request please read our
[contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
@ -39,7 +43,7 @@ before doing so.
## Copyright and license
Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).
Copyright JS Foundation and other contributors, http://js.foundation under [the Apache 2.0 license](LICENSE).
# Extra Node Information
@ -51,89 +55,103 @@ Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).
### Analysis
**node-red-node-wordpos** - *[72-wordpos](analysis/wordpos)* - Analyses the payload and classifies the part-of-speech of each word. The resulting message has msg.pos added with the results. A word may appear in multiple categories (eg, 'great' is both a noun and an adjective).
**node-red-node-badwords** - *[74-swearfilter](analysis/swearfilter)* - Analyses the payload and tries to filter out any messages containing bad swear words. This only operates on payloads of type string. Everything else is blocked.
**node-red-node-wordpos** - *[72-wordpos](analysis/wordpos)* - Analyses the payload and classifies the part-of-speech of each word. The resulting message has msg.pos added with the results. A word may appear in multiple categories (eg, 'great' is both a noun and an adjective).
### Function
**node-red-node-smooth** - *[17-smooth](function/smooth)* - A simple node to provide various functions across several previous values, including max, min, mean, high and low pass filters.
**node-red-node-rbe** - *[rbe](function/rbe)* - A simple node to provide report by exception and deadband / bandgap capability for simple inputs.
**node-red-node-datagenerater** - *[datagenerator](function/datagenerator)* - A node that can generate dummy data in various formats, names, addresses, emails, numbers, words, etc
**node-red-node-pidcontrol** - *[pidcontrol](function/PID)* - A PID control node for numeric inputs - provides simple contoll loop feedback capability.
**node-red-node-random** - *[random](function/random)* - A simple random number generator - can generate integers for x to y - or floats between x and y.
**node-red-node-rbe** - *[rbe](function/rbe)* - A simple node to provide report by exception and deadband / bandgap capability for simple inputs.
**node-red-node-smooth** - *[17-smooth](function/smooth)* - A simple node to provide various functions across several previous values, including max, min, mean, high and low pass filters.
### Hardware
**node-red-node-arduino** - *[35-arduino](hardware/Arduino)* - A collection of analogue & digital input & output nodes for the Arduino board - uses firmata protocol to talk to the board.
**node-red-node-beaglebone** - *[145-BBB-hardware](hardware/BBB)* - A collection of analogue & digital input & output nodes for the [Beaglebone Black](http://beagleboard.org/black).
**node-red-node-blink1** - *[77-blink1](hardware/blink1)* - Provides support for the [Blink1](http://blink1.thingm.com/) USB LED from ThingM.
**node-red-node-blinkstick** - *[76-blinkstick](hardware/blinkstick)* - Provides support for the [BlinkStick](http://www.blinkstick.com/) USB LED device.
**node-red-node-digirgb** - *[78-digiRGB](hardware/digiRGB)* - Provides support for the DigiSpark RGB USB LED.
**node-red-node-heatmiser** - *[100-heatmiser-in](hardware/heatmiser)* - Read and writes settings for temperature and frost protection to Heatmiser thermostats.
**node-red-node-intel-galileo** - *[mraa-spio](hardware/intel)* - A collection of analogue & digital input & output nodes for the Intel Galileo and Edison.
**node-red-node-ledborg** - *[78-ledborg](hardware/LEDborg)* - A simple driver for the [LEDborg](https://www.piborg.org/ledborg) plug on module for Raspberry Pi.
**node-red-node-makeymakey** - *[42-makey](hardware/makey)* - A Node-RED node to read from a [MakeyMakey](http://www.makeymakey.com/) input device.
**node-red-node-pi-gpiod** - *[pigpiod](hardware/pigpiod)* - An alternative to the default PI GPIO nodes that allows remote access - so a host machine can access a remote Pi (via network) and is better for driving multiple servos.
**node-red-node-pi-mcp3008** - *[pimcp3008](hardware/mcp3008)* - Allows A Raspberry Pi to node to read from an MCP3008 Analogue to Digital Converter chip via the SPI bus.
**node-red-node-pi-mcp3008** - *[pimcp3008](hardware/mcp3008)* - Allows A Raspberry Pi to node to read from MCP300x series Analogue to Digital Converter chips via the SPI bus.
**node-red-node-pi-neopixel** - *[neopixel](hardware/neopixel)* - Allows A Raspberry Pi to drive a strip of NeoPixels directly.
**node-red-node-pi-unicorn-hat** - *[unicorn](hardware/unicorn)* - Lets a Raspbeery Pi control a Pimorini Unicorn HAT 8x8 LED display.
**node-red-node-piface** - *[37-rpi-piface](hardware/PiFace)* - Adds support for the [PiFace](http://www.piface.org.uk/) interface module for Raspberry Pi.
**node-red-node-pibrella** - *[38-rpi-pibrella](hardware/Pibrella)* - Controls a [Pibrella](http://pibrella.com/) add-on board for a Raspberry-Pi.
**node-red-node-piface** - *[37-rpi-piface](hardware/PiFace)* - Adds support for the [PiFace](http://www.piface.org.uk/) interface module for Raspberry Pi.
**node-red-node-piliter** - *[39-rpi-piliter](hardware/PiLiter)* - Controls a Pimorini Pi-LITEr 8 LED add-on board for a Raspberry-Pi.
**node-red-node-blinkstick** - *[76-blinkstick](hardware/blinkstick)* - Provides support for the [BlinkStick](http://www.blinkstick.com/) USB LED device.
**node-red-node-blink1** - *[77-blink1](hardware/blink1)* - Provides support for the [Blink1](http://blink1.thingm.com/) USB LED from ThingM.
**node-red-node-ledborg** - *[78-ledborg](hardware/LEDborg)* - A simple driver for the [LEDborg](https://www.piborg.org/ledborg) plug on module for Raspberry Pi.
**node-red-node-digirgb** - *[78-digiRGB](hardware/digiRGB)* - Provides support for the DigiSpark RGB USB LED.
**node-red-node-wemo** - *[60-wemo](hardware/wemo)* - Basic node to drive a [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) socket and switch. Does not use discovery.
**node-red-node-makeymakey** - *[42-makey](hardware/makey)* - A Node-RED node to read from a [MakeyMakey](http://www.makeymakey.com/) input device.
**node-red-node-sensortag** - *[79-sensorTag](hardware/sensorTag)* - Reads data from the Ti Bluetooh Low Energy (BLE) SensorTag device.
**node-red-node-heatmiser** - *[100-heatmiser-in](hardware/heatmiser)* - Read and writes settings for temperature and frost protection to Heatmiser thermostats.
**node-red-node-wemo** - *[60-wemo](hardware/wemo)* - Basic node to drive a [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) socket and switch. Does not use discovery.
**N/A** - *[101-scanBLE](hardware/scanBLE)* - Scans for a particular Bluetooth Low Energy (BLE) device.
### I/O
**node-red-node-discovery** - *[mdns](io/mdns)* - discovers other Avahi/Bonjour services on the network.
**node-red-node-emoncms** - *[88-emoncms](io/emoncms)* - Adds node to post to an [Emoncms](http://emoncms.org/) server.
**node-red-node-mqlight** - *[mqlight](io/mqlight)* - Adds nodes to send and receive using MQlight.
**node-red-node-ping** - *[88-ping](io/ping)* - Pings a machine and returns the trip time in mS. Returns false if no response received within 3 seconds, or if the host is unresolveable. Default ping is every 20 seconds but can be configured.
**node-red-node-serialport** - *[25-serial](io/serialport)* - Node to send messages to and receive messages from a physical serial port.
**node-red-node-snmp** - *[snmp](io/snmp)* - Adds simple snmp receivers for single OIDs or OID tables.
**node-red-node-stomp** - *[18-stomp](io/stomp)* - A Node-RED node to publish and subscribe to and from a [STOMP server](https://stomp.github.io/implementations.html#STOMP_Servers).
**node-red-node-wol** - *[39-wol](io/wol)* - Sends a Wake-On-LAN magic packet to the mac address specified. You may instead set msg.mac to dynamically set the target device mac to wake up.
**node-red-node-ping** - *[88-ping](io/ping)* - Pings a machine and returns the trip time in mS. Returns false if no response received within 3 seconds, or if the host is unresolveable. Default ping is every 20 seconds but can be configured.
**node-red-node-discovery** - *[mdns](io/mdns)* - discovers other Avahi/Bonjour services on the network.
**node-red-node-mqlight** - *[mqlight](io/mqlight)* - Adds nodes to send and receive using MQlight.
**node-red-node-snmp** - *[snmp](io/snmp)* - Adds simple snmp receivers for single OIDs or OID tables.
**node-red-node-emoncms** - *[88-emoncms](io/emoncms)* - Adds node to post to an [Emoncms](http://emoncms.org/) server.
### Parsers
**node-red-node-msgpack** - *[70-msgpack.js](parsers/msgpack)* - Converts a payload to/from msgpack binary packed format.
**node-red-node-base64** - *[70-base64.js](parsers/base64)* - Converts a payload to/from base64 encoded format.
**node-red-node-geohash** - *[70-geohash.js](parsers/geohash)* - Converts a lat, lon payload to/from geohash format.
### Social
**node-red-node-msgpack** - *[70-msgpack.js](parsers/msgpack)* - Converts a payload to/from msgpack binary packed format.
**node-red-node-twilio** - *[56-twilio](social/twilio)* - Uses [Twilio](https://www.twilio.com/) service to send/receive text messages.
**node-red-node-what3words** - *[what3words.js](parsers/what3words)* - Encodes or Decodes a lat, lon position into what3words text format.
### Social
**node-red-node-dweetio** - *[55-dweetio](social/dweetio)* - Uses [dweetio](https://dweet.io/) to send/receive messages.
**node-red-node-nma** - *[57-nma](social/nma)* - Sends alerts to Android devices via the [Notify-My-Android](http://www.notifymyandroid.com/) app.
**node-red-node-email** - *[61-email](social/email)* - Sends and receives simple emails from services like gmail or smtp or imap servers.
**node-red-node-feedparser** - *[32-feedparse](social/feedparser)* - Reads messages from an atom or rss feed.
**node-red-node-irc** - *[91-irc](social/irc)* - Connects to an IRC server to send and receive messages.
**node-red-node-nma** - *[57-nma](social/nma)* - DEPRECATED as NMA closed down operations.
**node-red-node-notify** - *[57-notify](social/notify)* - Uses [Growl](http://growl.info/) to provide a desktop popup containing the payload. Only useful on the local Apple machine.
@ -141,11 +159,16 @@ Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).
**node-red-node-pushbullet** - *[57-pushbullet](social/pushbullet)* - Uses [PushBullet](https://www.pushbullet.com/) to push the payload to an Android device that has the [PushBullet](https://www.pushbullet.com/) app installed.
**node-red-node-pusher** - *[114-pusher](social/pusher)* - Publish-Subscribe to a [Pusher](http://pusher.com/) channel/event.
**node-red-node-pushover** - *[57-pushover](social/pushover)* - Sends alerts via [Pushover](https://pushover.net/).
**node-red-node-twilio** - *[56-twilio](social/twilio)* - Uses [Twilio](https://www.twilio.com/) service to send/receive text messages.
**node-red-node-twitter** - *[27-twitter](social/twitter)* - Listens to Twitter feeds and can also send tweets. (**NOTE**: this will break soon when Twitter remove their streaming API)
**node-red-node-xmpp** - *[92-xmpp](social/xmpp)* - Connects to an XMPP server to send and receive messages.
**node-red-node-pusher** - *[114-pusher](social/pusher)* - Publish-Subscribe to a [Pusher](http://pusher.com/) channel/event.
### Storage
@ -157,10 +180,12 @@ Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).
### Time
**node-red-node-suncalc** - *[79-suncalc](time/suncalc)* - Uses the suncalc module to generate an output at sunrise and sunset based on a specified location. Several choices of definition of sunrise and sunset are available,
**node-red-node-suncalc** - *[79-suncalc](time/suncalc)* - Uses the suncalc module to generate an output at sunrise and sunset based on a specified location. Several choices of definition of sunrise and sunset are available.
**node-red-node-timeswitch** - *[timeswitch](time/timeswitch)* - Lets the user set simple repeating timers for example for simple heating control, etc.
### Utility
**node-red-node-exif** - *[94-exif](utility/exif)* - Extracts GPS and other EXIF information from a passed in jpeg image,
**node-red-node-daemon** - *[daemon](utility/daemon)* - starts up (calls) a long running system program and pipes STDIN, STDOUT and STDERR to and from that process. Good for monitoring long running command line applications,
**node-red-node-exif** - *[94-exif](utility/exif)* - Extracts GPS and other EXIF information from a passed in jpeg image,

View File

@ -0,0 +1,13 @@
Copyright 2019 JS Foundation and other contributors, https://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.

View File

@ -0,0 +1,33 @@
node-red-node-multilang-sentiment
=================================
A <a href="http://nodered.org" target="new">Node-RED</a> node that scores incoming words
using the AFINN-165 wordlist and attaches a sentiment.score property to the msg.
This is similar to the default sentiment node but supports many more languages at the cost of using more disk space. (approx 11MB)
Install
-------
Either use the Menu - Manage palette - Install option in the editor or run the following command in your Node-RED user directory - typically `~/.node-red`
npm install node-red-node-multilang-sentiment
Usage
-----
Uses the AFINN-165 wordlist to attempt to assign scores to words in text.
Attaches `msg.sentiment` to the msg and within that `msg.sentiment.score` holds the score.
Supports multiple languages. These can be preselected in the node configuration. You can also set it so that `msg.lang` can be used to set the language dynamically if required. The cldr language codes supported are:
af, am, ar, az, be, bg, bn, bs, ca, ceb, co, cs, cy, da, de, el, en, eo, es, et, eu, fa, fi,
fr, fy, ga, gd, gl, gu, ha, haw, hi, hmn, hr, ht, hu, hy, id, ig, is, it, iw, ja, jw, ka, kk, km, kn, ko, ku, ky, la, lb, lo, lt,
lv, mg, mi, mk, ml, mn, mr, ms, mt, my, ne, nl, no, ny, pa, pl, ps, pt, ro, ru, sd, si, sk, sl, sm, sn, so, sq, sr, st, su, sv,
sw, ta, te, tg, th, tl, tr, uk, ur, uz, vi, xh, yi, yo, zh, zh-tw, zu
A score greater than zero is positive and less than zero is negative. The score typically ranges from -5 to +5, but can go higher and lower.
See the <a href="https://github.com/marcellobarile/multilang-sentiment/blob/develop/README.md" target="_blank">Multilang Sentiment docs here</a>.</p>

View File

@ -0,0 +1,8 @@
{
"mlsentiment": {
"sentiment": "sentiment",
"label": {
"language": "Language"
}
}
}

View File

@ -0,0 +1,20 @@
<script type="text/x-red" data-help-name="mlsentiment">
<p>指定したプロパティ(デフォルトは<code>payload</code>)を分析し、<code>sentiment</code>オブジェクトを追加します。</p>
<h3>出力</h3>
<dl class="message-properties">
<dt>sentiment <span class="property-type">オブジェクト</span></dt>
<dd>AFINN-111による感情分析の結果</dd>
<dt>sentiment.score <span class="property-type">数値</span></dt>
<dd>感情分析スコア</dd>
</dl>
<h3>入力</h3>
<dl class="message-properties">
<dt>overrides <span class="property-type">オブジェクト</span></dt>
<dd>単語スコアの上書きをするためのオブジェクト - <code>{ word:score,... }</code></dd>
</dl>
<h3>詳細</h3>
<p>ゼロ以上のスコアはポジティブ、ゼロ以下はネガティブを意味します。</p>
<p>スコアの範囲は通常-5から+5ですが、より大きかったり小さかったりすることもあります。</p>
<p>詳細は<a href="https://github.com/marcellobarile/multilang-sentiment/blob/develop/README.md" target="_blank">the Multilang-Sentiment docs here</a>を参照してください。</p>
</script>

View File

@ -0,0 +1,8 @@
{
"mlsentiment": {
"sentiment": "sentiment",
"label": {
"language": "言語"
}
}
}

View File

@ -0,0 +1,176 @@
<script type="text/x-red" data-template-name="mlsentiment">
<div class="form-row">
<label for="node-input-lang"><i class="fa fa-language"></i> <span data-i18n="mlsentiment.label.language"></span></label>
<select type="text" id="node-input-lang" style="width:70%;">
<option value="">set by msg.lang</option>
<option value="af">Afrikaans</option>
<option value="sq">Albanian</option>
<option value="am">Amharic</option>
<option value="ar">Arabic</option>
<option value="hy">Armenian</option>
<option value="az">Azerbaijani</option>
<option value="bn">Bangla</option>
<option value="eu">Basque</option>
<option value="be">Belarusian</option>
<option value="bs">Bosnian</option>
<option value="bg">Bulgarian</option>
<option value="my">Burmese</option>
<option value="ca">Catalan</option>
<option value="ceb">Cebuano</option>
<option value="zh">Chinese</option>
<option value="zh-tw">Chinese (Taiwanese)</option>
<option value="co">Corsican</option>
<option value="hr">Croatian</option>
<option value="cs">Czech</option>
<option value="da">Danish</option>
<option value="nl">Dutch</option>
<option value="en">English</option>
<option value="eo">Esperanto</option>
<option value="et">Estonian</option>
<option value="fi">Finnish</option>
<option value="fr">French</option>
<option value="fy">Frisian</option>
<option value="gl">Galician</option>
<option value="ka">Georgian</option>
<option value="de">German</option>
<option value="el">Greek</option>
<option value="gu">Gujarati</option>
<option value="ht">Haitian Creole</option>
<option value="ha">Hausa</option>
<option value="haw">Hawaiian</option>
<option value="hi">Hindi</option>
<option value="hmn">Hmong</option>
<option value="hu">Hungarian</option>
<option value="is">Icelandic</option>
<option value="ig">Igbo</option>
<option value="id">Indonesian</option>
<option value="ga">Irish</option>
<option value="it">Italian</option>
<option value="ja">Japanese</option>
<option value="kn">Kannada</option>
<option value="kk">Kazakh</option>
<option value="km">Khmer</option>
<option value="ko">Korean</option>
<option value="ku">Kurdish</option>
<option value="ky">Kyrgyz</option>
<option value="lo">Lao</option>
<option value="la">Latin</option>
<option value="lv">Latvian</option>
<option value="lt">Lithuanian</option>
<option value="lb">Luxembourgish</option>
<option value="mk">Macedonian</option>
<option value="mg">Malagasy</option>
<option value="ms">Malay</option>
<option value="ml">Malayalam</option>
<option value="mt">Maltese</option>
<option value="mi">Maori</option>
<option value="mr">Marathi</option>
<option value="mn">Mongolian</option>
<option value="ne">Nepali</option>
<option value="no">Norwegian</option>
<option value="ny">Nyanja</option>
<option value="ps">Pashto</option>
<option value="fa">Persian</option>
<option value="pl">Polish</option>
<option value="pt">Portuguese</option>
<option value="pa">Punjabi</option>
<option value="ro">Romanian</option>
<option value="ru">Russian</option>
<option value="sm">Samoan</option>
<option value="gd">Scottish Gaelic</option>
<option value="sr">Serbian</option>
<option value="sn">Shona</option>
<option value="sd">Sindhi</option>
<option value="si">Sinhala</option>
<option value="sk">Slovak</option>
<option value="sl">Slovenian</option>
<option value="so">Somali</option>
<option value="st">Southern Sotho</option>
<option value="es">Spanish</option>
<option value="su">Sundanese</option>
<option value="sw">Swahili</option>
<option value="sv">Swedish</option>
<option value="tl">Tagalog</option>
<option value="tg">Tajik</option>
<option value="ta">Tamil</option>
<option value="te">Telugu</option>
<option value="th">Thai</option>
<option value="tr">Turkish</option>
<option value="uk">Ukrainian</option>
<option value="ur">Urdu</option>
<option value="uz">Uzbek</option>
<option value="vi">Vietnamese</option>
<option value="cy">Welsh</option>
<option value="xh">Xhosa</option>
<option value="yi">Yiddish</option>
<option value="yo">Yoruba</option>
<option value="zu">Zulu</option>
</select>
</div>
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>
<script type="text/x-red" data-help-name="mlsentiment">
<p>Analyses the chosen property, default <code>payload</code>, and adds a <code>sentiment</code> object.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>sentiment <span class="property-type">object</span></dt>
<dd>contains the resulting AFINN-111 sentiment.</dd>
<dt>sentiment.score <span class="property-type">number</span></dt>
<dd>the sentiment score.</dd>
</dl>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>words <span class="property-type">object</span></dt>
<dd>an object of words and scores to override or add words can be supplied - <code>{ word:score,... }</code>.</dd>
</dl>
<dl class="message-properties">
<dt>lang <span class="property-type">string</span></dt>
<dd>Two letter cldr code to specify the language to use - from the list below</dd>
</dl>
<h3>Details</h3>
<p>A score greater than zero is positive and less than zero is negative.</p>
<p>The score typically ranges from -5 to +5, but can go higher and lower.</p>
<p>See <a href="https://github.com/marcellobarile/multilang-sentiment/blob/develop/README.md" target="_blank">the Sentiment docs here</a>.</p>
<p>The node can also be configured to let the language be specified by setting <code>msg.lang</code> to one of the codes below</p>
<p>The language codes supported are - af, am, ar, az, be, bg, bn, bs, ca, ceb, co, cs, cy, da, de, el, en, eo, es, et, eu, fa, fi,
fr, fy, ga, gd, gl, gu, ha, haw, hi, hmn, hr, ht, hu, hy, id, ig, is, it, iw, ja, jw, ka, kk, km, kn, ko, ku, ky, la, lb, lo, lt,
lv, mg, mi, mk, ml, mn, mr, ms, mt, my, ne, nl, no, ny, pa, pl, ps, pt, ro, ru, sd, si, sk, sl, sm, sn, so, sq, sr, st, su, sv,
sw, ta, te, tg, th, tl, tr, uk, ur, uz, vi, xh, yi, yo, zh, zh-tw, zu</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('mlsentiment',{
category: 'analysis-function',
color:"#E6E0F8",
defaults: {
name: {value:""},
property: {value:"payload",required:true},
lang: {value:"en"}
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
paletteLabel: "multilang sentiment",
label: function() {
return this.name||"multilang sentiment";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
}
});
</script>

View File

@ -0,0 +1,28 @@
module.exports = function(RED) {
"use strict";
var multilangsentiment = require('multilang-sentiment');
function MultiLangSentimentNode(n) {
RED.nodes.createNode(this,n);
this.lang = n.lang;
this.property = n.property||"payload";
var node = this;
this.on("input", function(msg) {
var value = RED.util.getMessageProperty(msg,node.property);
if (value !== undefined) {
if (msg.hasOwnProperty("overrides")) {
msg.extras = msg.overrides;
delete msg.overrides;
}
multilangsentiment(value, node.lang || msg.lang || 'en', {words: msg.extras || null}, function (err, result) {
msg.sentiment = result;
node.send(msg);
});
}
else { node.send(msg); } // If no matching property - just pass it on.
});
}
RED.nodes.registerType("mlsentiment",MultiLangSentimentNode);
}

View File

@ -0,0 +1,24 @@
{
"name" : "node-red-node-multilang-sentiment",
"version" : "0.1.0",
"description" : "A Node-RED node that uses the AFINN-165 wordlists for sentiment analysis of words translated into multiple languages including emojis.",
"dependencies" : {
"multilang-sentiment" : "^1.2.0"
},
"repository" : {
"type":"git",
"url":"https://github.com/node-red/node-red-nodes/tree/master/analysis/sentiment"
},
"license": "Apache-2.0",
"keywords": [ "node-red", "sentiment", "anaylsis", "AFINN" ],
"node-red" : {
"nodes" : {
"mlsentiment": "mlsentiment.js"
}
},
"author": {
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
}
}

View File

@ -0,0 +1,36 @@
<script type="text/x-red" data-template-name="sentiment">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('sentiment',{
category: 'analysis-function',
color:"#E6E0F8",
defaults: {
name: {value:""},
property: {value:"payload",required:true}
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"sentiment";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
}
});
</script>

View File

@ -0,0 +1,22 @@
module.exports = function(RED) {
"use strict";
var sentiment = require('sentiment');
function SentimentNode(n) {
RED.nodes.createNode(this,n);
this.property = n.property||"payload";
var node = this;
this.on("input", function(msg) {
var value = RED.util.getMessageProperty(msg,node.property);
if (value !== undefined) {
sentiment(value, msg.overrides || null, function (err, result) {
msg.sentiment = result;
node.send(msg);
});
}
else { node.send(msg); } // If no matching property - just pass it on.
});
}
RED.nodes.registerType("sentiment",SentimentNode);
}

View File

@ -0,0 +1,14 @@
Copyright 2016, 2019 JS Foundation and other contributors, https://js.foundation/
Copyright 2013-2016 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.

View File

@ -0,0 +1,26 @@
node-red-node-sentiment
========================
A <a href="http://nodered.org" target="new">Node-RED</a> node that scores incoming words
using the AFINN-165 wordlist and attaches a sentiment.score property to the msg.
NOTE: There is also a multi-language version available - **node-red-node-multilang-sentiment**.
Install
-------
This is a node that should be installed by default by Node-RED so you should not have to install it manually. If you do then run the following command in your Node-RED user directory - typically `~/.node-red`
npm install node-red-node-sentiment
Usage
-----
Uses the AFINN-165 wordlist to attempt to assign scores to words in text.
Attaches `msg.sentiment` to the msg and within that `msg.sentiment.score` holds the score.
A score greater than zero is positive and less than zero is negative. The score typically ranges from -5 to +5, but can go higher and lower.
See the <a href="https://github.com/thisandagain/sentiment/blob/develop/README.md" target="_blank">Sentiment docs here</a>.</p>

View File

@ -0,0 +1,20 @@
<script type="text/html" data-help-name="sentiment">
<p>Analyses the chosen property, default <code>payload</code>, and adds a <code>sentiment</code> object.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>sentiment <span class="property-type">object</span></dt>
<dd>contains the resulting AFINN-111 sentiment.</dd>
<dt>sentiment.score <span class="property-type">number</span></dt>
<dd>the sentiment score.</dd>
</dl>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>overrides <span class="property-type">object</span></dt>
<dd>an object of word score overrides can be supplied - <code>{ word:score,... }</code>.</dd>
</dl>
<h3>Details</h3>
<p>A score greater than zero is positive and less than zero is negative.</p>
<p>The score typically ranges from -5 to +5, but can go higher and lower.</p>
<p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_blank">the Sentiment docs here.</a>.</p>
<p><b>Note</b>: There is also a multi-language version available - <b>node-red-node-multilang-sentiment</b></p>
</script>

View File

@ -0,0 +1,8 @@
{
"sentiment": {
"sentiment": "sentiment",
"label": {
"language": "Language"
}
}
}

View File

@ -0,0 +1,35 @@
<!--
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.
-->
<script type="text/html" data-help-name="sentiment">
<p>指定したプロパティ(デフォルトは<code>payload</code>)を分析し、<code>sentiment</code>オブジェクトを追加します。</p>
<h3>出力</h3>
<dl class="message-properties">
<dt>sentiment <span class="property-type">オブジェクト</span></dt>
<dd>AFINN-111による感情分析の結果</dd>
<dt>sentiment.score <span class="property-type">数値</span></dt>
<dd>感情分析スコア</dd>
</dl>
<h3>入力</h3>
<dl class="message-properties">
<dt>overrides <span class="property-type">オブジェクト</span></dt>
<dd>単語スコアの上書きをするためのオブジェクト - <code>{ word:score,... }</code></dd>
</dl>
<h3>詳細</h3>
<p>ゼロ以上のスコアはポジティブ、ゼロ以下はネガティブを意味します。</p>
<p>スコアの範囲は通常-5から+5ですが、より大きかったり小さかったりすることもあります。</p>
<p>詳細は<a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_blank">the Sentiment docs here</a>を参照してください。</p>
</script>

View File

@ -0,0 +1,8 @@
{
"sentiment": {
"sentiment": "sentiment",
"label": {
"language": "言語"
}
}
}

View File

@ -0,0 +1,24 @@
{
"name" : "node-red-node-sentiment",
"version" : "0.1.6",
"description" : "A Node-RED node that uses the AFINN-165 wordlists for sentiment analysis of words.",
"dependencies" : {
"sentiment" : "2.1.0"
},
"repository" : {
"type":"git",
"url":"https://github.com/node-red/node-red-nodes/tree/master/analysis/sentiment"
},
"license": "Apache-2.0",
"keywords": [ "node-red", "sentiment", "anaylsis", "AFINN" ],
"node-red" : {
"nodes" : {
"sentiment": "72-sentiment.js"
}
},
"author": {
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
}
}

View File

@ -1,15 +1,14 @@
module.exports = function(RED) {
"use strict";
var badwords = require('badwords');
if (badwords.length === 0 ) { return; }
var badwordsRegExp = require('badwords/regexp');
function BadwordsNode(n) {
RED.nodes.createNode(this,n);
var badwordsRegExp = require('badwords/regexp');
var node = this;
this.on("input", function(msg) {
node.on("input", function(msg) {
if (typeof msg.payload === "string") {
badwordsRegExp.lastIndex = 0
if ( !badwordsRegExp.test(msg.payload) ) { node.send(msg); }
}
});

View File

@ -1,9 +1,9 @@
{
"name" : "node-red-node-badwords",
"version" : "0.0.5",
"version" : "0.1.0",
"description" : "A Node-RED node that attempts to filter out messages containing swearwords.",
"dependencies" : {
"badwords" : "0.0.3"
"badwords" : "^1.0.0"
},
"repository" : {
"type":"git",

View File

@ -1,3 +1,3 @@
# check coverage of tests... and browse report
istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report html
nyc --check-coverage --reporter=html grunt
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome coverage/index.html

View File

@ -7,26 +7,26 @@ data values from a template. Useful for building test-cases.
Install
-------
Run the following command in your Node-RED user directory - typically `~/.node-red`
Either use the Manage Palette option in the Node-RED Editor menu, or run the following command in your Node-RED user directory - typically `~/.node-red`
npm install node-red-node-data-generator
npm i node-red-node-data-generator
Usage
-----
Creates dummy data based on a handlebars-style template.
Uses the <i><a href="https://github.com/webroo/dummy-json" target="_new">dummy-json</a></i>
Uses the <i><a href="https://github.com/webroo/dummy-json/blob/master/README.md" target="_new">dummy-json</a></i>
module, which can create rich sets of dummy data for testing or other uses.
It will build a **string**, or a **parsed JSON object**, creating values based
on the helper names below:
title, firstname, lastname, company, domain, tld, email,
title, firstName, lastName, company, domain, tld, email,
street, city, country, countryCode, zipcode, postcode,
lat, long, phone, color, hexColor, guid,
ipv4, ipv6, lorem, date, time,
lowercase, uppercase, int, float, boolean
lat, long, phone "+xx (x) xxxx xxx xxx", color, hexColor, guid,
ipv4, ipv6, lorem nn, date, time,
lowercase (helper), uppercase (helper), int, float, boolean
Multiple values can be generated by use of the `repeat` syntax.

View File

@ -27,12 +27,12 @@
<script type="text/x-red" data-help-name="data-generator">
<p>Creates dummy data strings based on a handlebars-style template.</p>
<p>Uses the <i><a href="https://github.com/webroo/dummy-json" target="_new">dummy-json</a></i>
<p>Uses the <i><a href="https://github.com/webroo/dummy-json/blob/master/README.md" target="_new">dummy-json</a></i>
module, which can create rich sets of dummy data for testing or other uses.</p>
<p>Will build a string or a parsed JSON object, creating values based on the helper names below:
<pre style="word-break:normal">title, firstname, lastname, company, domain, tld, email, street, city, country, countryCode, zipcode, postcode, lat, long, phone, color, hexColor, guid, ipv4, ipv6, lorem, date, time, lowercase, uppercase, int, float, boolean</pre>
<pre style="word-break:normal">title, firstName, lastName, company, domain, tld, email, street, city, country, countryCode, zipcode, postcode, lat, long, phone, color, hexColor, guid, ipv4, ipv6, lorem [nn], date, time, lowercase, uppercase, int, float, boolean</pre>
<p>Multiple values can be generated by use of the <i>repeat</i> syntax.</p>
<p>In addition any properties passed in on <code>msg</code> can also be used - for example {{payload}}.</p>
<p>In addition any properties passed in on <code>msg</code> can also be used - for example <code>{{payload}}</code>.</p>
<p>Finally <code>msg.seed</code> can be used to preset the pseudo-random seed to ensure repeatability across calls.</p>
</script>
@ -40,7 +40,6 @@
RED.nodes.registerType('data-generator',{
color:"rgb(243, 181, 103)",
category: 'function',
paletteLabel:"data&nbsp;generator",
defaults: {
name: {value:""},
field: {value:"payload"},
@ -51,9 +50,13 @@
inputs:1,
outputs:1,
icon: "template.png",
paletteLabel: function() {
return this._("datagen.datagen");
},
label: function() {
return this.name || "data generator";
},
outputLabels: function() { return this.syntax === "json" ? "object" : "string"; },
oneditprepare: function() {
var that = this;
if (!this.fieldType) {

View File

@ -1,5 +1,6 @@
{
"datagen": {
"datagen": "data generator",
"label": {
"syntax": "Return",
"text": "a text string",

View File

@ -1,9 +1,9 @@
{
"name" : "node-red-node-data-generator",
"version" : "0.0.4",
"version" : "0.1.1",
"description" : "A Node-RED node to create a string of dummy data values from a template. Useful for test-cases.",
"dependencies" : {
"dummy-json": "1.0.*"
"dummy-json": "^2.0.0"
},
"repository" : {
"type":"git",

View File

@ -1,4 +1,4 @@
Copyright 2016 JS Foundation and other contributors, https://js.foundation/
Copyright 2016,2020 JS Foundation and other contributors, https://js.foundation/
Copyright 2013-2016 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,23 +1,24 @@
node-red-node-random
====================
# node-red-node-random
A <a href="http://nodered.org" target="_new">Node-RED</a> node that when triggered generates a random number between two values.
Install
-------
## Install
Run the following command in your Node-RED user directory - typically `~/.node-red`
Either use the Manage Palette option in the Node-RED Editor menu, or run the following command in your Node-RED user directory - typically `~/.node-red`
npm install node-red-node-random
npm i node-red-node-random
Usage
-----
## Usage
A simple node to generate a random number when triggered.
If integer mode is selected (default) it will return an integer **between and including** the two values given - so selecting 1 to 6 will return values 1,2,3,4,5 or 6.
If set to return an integer it can include both the low and high values.
`min <= n <= max` - so selecting 1 to 6 will return values 1,2,3,4,5 or 6.
If floating point mode is selected then it will return a number **between** the two values given - so selecting 1 to 6 will return values 1 < x < 6 .
If set to return a floating point value it will be from the low value, up to, but
**not** including the high value. `min <= n < max` - so selecting 1 to 6 will return values 1 <= n < 6 .
**Note:** This generates **numbers**.
You can dynamically pass in the 'From' and 'To' values to the node using msg.to and/or msg.from. **NOTE:** hard coded values in the node **always take precedence**.
**Note:** This returns numbers - objects of type **number**.

View File

@ -0,0 +1,16 @@
<script type="text/html" data-help-name="random">
<p>Generates a random number between a low and high value. Defaults to 1 to 10.</p>
<p>If left blank <code>from</code> and <code>to</code> can be set dynamically as below.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>from <span class="property-type">number</span></dt>
<dd>containing the low value to be used.</dd>
<dt>to <span class="property-type">number</span></dt>
<dd>containing the high value to be used.</dd>
</dl>
<h3>Details</h3>
<p>If set to return an integer it can <i>include</i> both the low and high values.
<code>min <= n <= max</code></p>
<p>If set to return a floating point value it will be from the low value, up to, but
not including the high value. <code>min <= n < max</code></p>
</script>

View File

@ -0,0 +1,13 @@
{
"random": {
"label": {
"generate": "Generate",
"wholeNumber": "a whole number - integer",
"realNumber": "a real number - floating point",
"from": "From",
"lowestNumber": "lowest number",
"to": "To",
"highestNumber": "highest number"
}
}
}

View File

@ -0,0 +1,16 @@
<script type="text/html" data-help-name="random">
<p>最小値と最大値との間の乱数を生成します。デフォルトは1から10です。</p>
<p><code>最小</code><code>最大</code> を空にした場合は、以下の様に動的に値を設定できます。</p>
<h3>入力</h3>
<dl class="message-properties">
<dt>from <span class="property-type">数値</span></dt>
<dd>使用する最小値を含みます。</dd>
<dt>to <span class="property-type">数値</span></dt>
<dd>使用する最大値を含みます。</dd>
</dl>
<h3>詳細</h3>
<p>整数値を返すように設定した場合は、乱数には最大値と最小値の両方が<i>含まれます</i>
<code>min <= n <= max</code></p>
<p>浮動小数点値を返すように設定した場合、乱数は最小値から最大値未満の値を含み、最大値は含まれません。
<code>min <= n < max</code></p>
</script>

View File

@ -0,0 +1,13 @@
{
"random": {
"label": {
"generate": "生成",
"wholeNumber": "整数 - 整数値",
"realNumber": "実数 - 浮動小数点",
"from": "最小",
"lowestNumber": "最小値",
"to": "最大",
"highestNumber": "最大値"
}
}
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-random",
"version" : "0.0.8",
"version" : "0.3.1",
"description" : "A Node-RED node that when triggered generates a random number between two values.",
"dependencies" : {
},
@ -19,5 +19,8 @@
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
}
},
"contributors": [
{"name": "@zenofmud"}
]
}

View File

@ -1,42 +1,40 @@
<script type="text/x-red" data-template-name="random">
<script type="text/html" data-template-name="random">
<div class="form-row">
<label for="node-input-inte"><i class="fa fa-random"></i> Generate</label>
<select type="text" id="node-input-inte" style="width: 300px;">
<option value="true">a whole number - integer</option>
<option value="false">a real number - floating point</option>
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<label for="node-input-inte"><i class="fa fa-random"></i> <span data-i18n="random.label.generate"></label>
<select type="text" id="node-input-inte" style="width:70%;">
<option value="true" data-i18n="random.label.wholeNumber"></option>
<option value="false" data-i18n="random.label.realNumber"></option>
</select>
</div>
<div class="form-row">
<label for="node-input-low"><i class="fa fa-arrow-down"></i> From</label>
<input type="text" id="node-input-low" placeholder="lowest number" style="width: 300px;">
<label for="node-input-low"><i class="fa fa-arrow-down"></i> <span data-i18n="random.label.from"></label>
<input type="text" id="node-input-low" data-i18n="[placeholder]random.label.lowestNumber" style="width:70%;">
</div>
<div class="form-row">
<label for="node-input-high"><i class="fa fa-arrow-up"></i> To</label>
<input type="text" id="node-input-high" placeholder="highest number" style="width: 300px;">
<label for="node-input-high"><i class="fa fa-arrow-up"></i> <span data-i18n="random.label.to"></label>
<input type="text" id="node-input-high" data-i18n="[placeholder]random.label.highestNumber" style="width:70%;">
</div>
<br/>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name" style="width: 300px;">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name" style="width:70%;">
</div>
</script>
<script type="text/x-red" data-help-name="random">
<p>Generates a random number between a low and high value.</p>
<p>If you return an integer it can <i>include</i> both the low and high values.</p>
<p>If you return a floating point value it will be <i>between</i> the low and high values.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('random',{
category: 'function',
color:"#E2D96E",
defaults: {
name: {value:""},
low: {value:"1"},
high: {value:"10"},
inte: {value:"true"}
low: {value: 1,validate:function(v) { return !isNaN(v) || v.length === 0;} },
high: {value: 10,validate:function(v) { return !isNaN(v) || v.length === 0;} },
inte: {value:"true"},
property: {value:"payload",required:true}
},
inputs:1,
outputs:1,
@ -46,6 +44,12 @@
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
}
});
</script>

View File

@ -3,18 +3,73 @@ module.exports = function(RED) {
"use strict";
function RandomNode(n) {
RED.nodes.createNode(this,n);
this.low = Number(n.low || 1);
this.high = Number(n.high || 10);
this.low = n.low
this.high = n.high
this.inte = n.inte || false;
this.property = n.property||"payload";
var node = this;
var tmp = {};
this.on("input", function(msg) {
if (node.inte == "true" || node.inte === true) {
msg.payload = Math.round(Number(Math.random()) * (node.high - node.low + 1) + node.low - 0.5);
tmp.low = 1 // set this as the default low value
tmp.low_e = ""
if (node.low) { // if the the node has a value use it
tmp.low = Number(node.low);
} else if ('from' in msg) { // else see if a 'from' is in the msg
if (Number(msg.from)) { // if it is, and is a number, use it
tmp.low = Number(msg.from);
} else { // otherwise setup NaN error
tmp.low = NaN;
tmp.low_e = " From: " + msg.from; // setup to show bad incoming msg.from
}
}
else {
msg.payload = Number(Math.random()) * (node.high - node.low) + node.low;
tmp.high = 10 // set this as the default high value
tmp.high_e = "";
if (node.high) { // if the the node has a value use it
tmp.high = Number(node.high);
} else if ('to' in msg) { // else see if a 'to' is in the msg
if (Number(msg.to)) { // if it is, and is a number, use it
tmp.high = Number(msg.to);
} else { // otherwise setup NaN error
tmp.high = NaN
tmp.high_e = " To: " + msg.to // setup to show bad incoming msg.to
}
}
// if tmp.low or high are not numbers, send an error msg with bad values
if ( (isNaN(tmp.low)) || (isNaN(tmp.high)) ) {
this.error("Random: one of the input values is not a number. " + tmp.low_e + tmp.high_e);
} else {
// at this point we have valid values so now to generate the random number!
// flip the values if low > high so random will work
var value = 0;
if (tmp.low > tmp.high) {
value = tmp.low
tmp.low = tmp.high
tmp.high = value
}
// if returning an integer, do a math.ceil() on the low value and a
// Math.floor()high value before generate the random number. This must be
// done to insure the rounding doesn't round up if using something like 4.7
// which would end up with 5
if ( (node.inte == "true") || (node.inte === true) ) {
tmp.low = Math.ceil(tmp.low);
tmp.high = Math.floor(tmp.high);
// use this to round integers
value = Math.round(Math.random() * (tmp.high - tmp.low + 1) + tmp.low - 0.5);
} else {
// use this to round floats
value = (Math.random() * (tmp.high - tmp.low)) + tmp.low;
}
RED.util.setMessageProperty(msg,node.property,value);
node.send(msg);
}
node.send(msg);
});
}
RED.nodes.registerType("random",RandomNode);

View File

@ -1,12 +1,11 @@
node-red-node-rbe
=================
A <a href="http://nodered.org" target="_new">Node-RED</a> node that provides
A <a href="http://nodered.org" target="_new">Node-RED</a> node that
provides report-by-exception (RBE) and deadband capability.
The node blocks unless the incoming value changes - RBE mode, or
changes by more than a certain amount (absolute value or percentage) - deadband
mode.
changes by more than a certain amount (absolute value or percentage) - bandgap modes.
Install
-------
@ -19,7 +18,7 @@ Run the following command in your Node-RED user directory - typically `~/.node-r
Usage
-----
A simple node to provide report by exception (RBE) and deadband function
A simple node to provide report by exception (RBE) and bandgap functions
- only passes on data if it has changed.
This works on a per `msg.topic` basis. This means that a single rbe node can
@ -31,12 +30,13 @@ The node doesn't send any output until the `msg.payload` is different to the pre
Works on numbers and strings. Useful for filtering out repeated messages of the
same value. Saves bandwidth, etc...
### Deadband modes
### Deadband and Narrowband modes
In deadband mode the incoming payload should contain a parseable *number* and is
In deadband modes the incoming payload should contain a parseable *number* and is
output only if greater than + or - the *band gap* away from the previous output.
It can also be set to block values more than a certain distance away from the present value.
This can be used to remove outliers or unexpected readings.
The narrowband modes will block if the incoming value change is greater than + or - the band gap
away from the previous value. Useful for ignoring outliers from a faulty sensor for example.
You can specify compare with *previous valid output value* or *previous input value*.
The former ignores any values outside the valid range, whereas the latter allows

View File

@ -0,0 +1,38 @@
<script type="text/html" data-help-name="rbe">
<p>Report by Exception node - only passes on data if the payload has changed.</p>
<p>It can also block unless, or ignore if the value changes by a specified amount.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">number | string | (object)</span>
</dt>
<dd>RBE mode will accept numbers, strings, and simple objects. Other modes must provide a parseable number.</dd>
<dt class="optional">topic <span class="property-type">string</span>
</dt>
<dd>if specified the function will work on a per topic basis.</dd>
<dt class="optional">reset<span class="property-type">any</span></dt>
<dd>if set clears the stored value for the specified msg.topic, or
all topics if msg.topic is not specified.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">as per input</span>
</dt>
<dd>If triggered the output will be the same as the input.</dd>
</dl>
<h3>Details</h3>
<p>In RBE mode this node will block until the <code>msg.payload</code>,
(or selected property) value is different to the previous one. If required
it can ignore the intial value, so as not to send anything at start.</p>
<p>In the Deadband modes the incoming value must contain a parseable number and will block
unless the change is greater than + or - the band gap away from a previous value.</p>
<p>Deadband also supports % - only sends if the input differs by more than x% of the original value.</p>
<p>The Narrowband modes will block if the incoming value change is greater than + or - the band gap
away from the previous value. Useful for ignoring outliers from a faulty sensor for example.</p>
<p>Both Deadband and Narrowband allow comparison against either the previous valid output value, thus
ignoring any values out of range; or the previous input value, which resets the set point, thus allowing
gradual drift (deadband), or a step change (narrowband).</p>
<p><b>Note:</b> This works on a per <code>msg.topic</code> basis. This means that a single rbe node can
handle multiple different topics at the same time.</p>
</script>

View File

@ -1,5 +1,6 @@
{
"rbe": {
"rbe": "rbe",
"label": {
"func": "Mode",
"init": "Send initial value",

View File

@ -0,0 +1,31 @@
<!-- Source revision: https://github.com/node-red/node-red-nodes/commit/467907776088422882076f46d85e25601449564d -->
<script type="text/html" data-help-name="rbe">
<p>Report by Exception(例外データの報告)ノード - ペイロードの値が変化した場合だけデータを送信。</p>
<p>値が指定した量変化するまでブロックすることもできます- 不感帯(deadband)モード。</p>
<h3>入力</h3> <dl class="message-properties">
<dt>payload
<span class="property-type">数値 | 文字列 | (オブジェクト)</span>
</dt>
<dd>RBEモードでは数値、文字列、シンプルなオブジェクトを受け付けます。他のモードではパース可能な数値を指定します。</dd>
<dt class="optional">topic <span class="property-type">文字列</span>
</dt>
<dd>指定すると、トピックごとに動作します。</dd>
<dt class="optional">reset<span class="property-type">任意</span></dt>
<dd>値を設定すると、保存した値をクリアします。msg.topicを指定した場合は対応する値、指定しなければ全てのトピックが対象となります。</dd>
</dl>
<h3>出力</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">入力と同じ</span>
</dt>
<dd>トリガー条件を満たした場合、入力を出力として送信。</dd>
</dl>
<h3>詳細</h3>
<p>RBEモードでは、<code>msg.payload</code>が前の値から変化するまでブロックします。</p>
<p>不感帯モードでは、入力データはパース可能な数値でなければなりません。以前の入力値に対する差分が指定値より大きな場合にのみメッセージを出力します。</p>
<p>不感帯モードでは%による指定もサポートしています。入力と前の値の差分がX%より大きな場合に出力を行います。</p>
<p>狭帯域(narrowband)モードでは、前の値に対する差分が一定値より大きな場合に入力ペイロードをブロックします。このモードは、故障したセンサから発生する外れ値を無視する時などに有用です。</p>
<p>不感帯モードと狭帯域モードでは、以前の有効出力値、もしくは、以前の入力値との比較ができます。有効出力値を用いると範囲外の値を無視することが、入力値を用いると設定点がリセットされるため漸次的変化(不感帯モード)もしくは段階的変化(狭帯域モード)が可能です。</p>
<p><b>注:</b> このノードは<code>msg.topic</code>毎に動作します。そのため、ひとつのrbeードで複数の異なるトピックを同時に扱うことができます。</p>
</script>

View File

@ -13,12 +13,12 @@
"opts": {
"rbe": "値が変化した時のみメッセージを中継",
"rbei": "値が変化した時のみメッセージを中継(初期値を無視)",
"deadband": "値が比較値を超える時のみメッセージを中継",
"deadbandEq": "値が比較値以上の時のみメッセージを中継",
"narrowband": "初期値、値が比較値を超える時のみメッセージを中継",
"narrowbandEq": "初期値、値が比較値以上の時のみメッセージを中継",
"in": "最後の入力値と比較",
"out": "最後の出力値と比較"
"deadband": "値が指定した変化量を超える時のみメッセージを中継",
"deadbandEq": "値が指定した変化量以上の時のみメッセージを中継",
"narrowband": "値が指定した変化量を超えない時のみメッセージを中継",
"narrowbandEq": "値が指定した変化量以上でない時のみメッセージを中継",
"in": "最後の入力値と比較",
"out": "最後の出力値と比較"
},
"warn": {
"nonumber": "ペイロードに数値が含まれていません"

View File

@ -1,7 +1,7 @@
{
"name" : "node-red-node-rbe",
"version" : "0.1.14",
"description" : "A Node-RED node that provides report-by-exception (RBE) and deadband capability.",
"version" : "0.2.9",
"description" : "A Node-RED node that provides report-by-exception (RBE) and deadband capabilities.",
"dependencies" : {
},
"repository" : {

View File

@ -1,8 +1,8 @@
<script type="text/x-red" data-template-name="rbe">
<script type="text/html" data-template-name="rbe">
<div class="form-row">
<label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="rbe.label.func"></span></label>
<select type="text" id="node-input-func" style="width:74%;">
<select type="text" id="node-input-func" style="width:70%;">
<option value="rbe" data-i18n="rbe.opts.rbe"></option>
<option value="rbei" data-i18n="rbe.opts.rbei"></option>
<option value="deadbandEq" data-i18n="rbe.opts.deadbandEq"></option>
@ -20,51 +20,17 @@
</select>
</div>
<div class="form-row" id="node-startvalue">
<label for="node-input-start"><i class="fa fa-thumb-tack"/> <span data-i18n="rbe.label.start"></span></label>
<input type="text" id="node-input-start" data-i18n="[placeholder]rbe.placeholder.start" style="width:71%;">
<label for="node-input-start"><i class="fa fa-thumb-tack"></i> <span data-i18n="rbe.label.start"></span></label>
<input type="text" id="node-input-start" data-i18n="[placeholder]rbe.placeholder.start" style="width:70%;">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"/> <span data-i18n="rbe.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]rbe.label.name" style="width:71%;">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="rbe.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]rbe.label.name" style="width:70%;">
</div>
</script>
<script type="text/x-red" data-help-name="rbe">
<p>Report by Exception node - only passes on data if the payload has changed.</p>
<p>It can also block until the value changes by a specified amount - deadband modes.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">number | string | (object)</span>
</dt>
<dd>RBE mode will accept numbers, strings, and simple objects. Other modes must provide a parseable number.</dd>
<dt class="optional">topic <span class="property-type">string</span>
</dt>
<dd>if specified the function will work on a per topic basis.</dd>
<dt class="optional">reset<span class="property-type">any</span></dt>
<dd>if set clears the stored value for the specified msg.topic, or
all topics if msg.topic is not specified.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">as per input</span>
</dt>
<dd>If triggered the output will be the same as the input.</dd>
</dl>
<h3>Details</h3>
<p>In RBE mode this node will block until the <code>msg.payload</code> is
different to the previous one. </p>
<p>In Deadband modes the incoming payload must contain a parseable number and is
output only if greater than + or - the band gap away from a previous value.</p>
<p>Deadband also supports % - only sends if the input differs by more than x% of the original value.</p>
<p>In Narrowband mode the incoming payload is blocked if it is more than + or - the band gap
away from the previous value. Useful for ignoring outliers from a faulty sensor for example.</p>
<p>Both Deadband and Narrowband allow comparison against either the previous valid output value, thus
ignoring any values out of range; or the previous input value, which resets the set point, thus allowing
gradual drift (deadband), or a step change (narrowband).</p>
<p><b>Note:</b> This works on a per <code>msg.topic</code> basis. This means that a single rbe node can
handle multiple different topics at the same time.</p>
</script>
<script type="text/javascript">
@ -76,19 +42,24 @@
func: {value:"rbe"},
gap: {value:"",validate:RED.validators.regex(/^(\d*[.]*\d*|)(%|)$/)},
start: {value:""},
inout: {value:"out"}
inout: {value:"out"},
property: {value:"payload",required:true}
},
inputs:1,
outputs:1,
icon: "rbe.png",
label: function() {
var ll = (this.func||"").replace("Eq","").replace("rbei","rbe")||"rbe";
return this.name||ll||"rbe";
var ll = (this.func||"").replace("Eq","").replace("rbei","rbe")||this._("rbe.rbe");
return this.name||ll||this._("rbe.rbe");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
//$( "#node-input-gap" ).spinner({min:0});
if ($("#node-input-inout").val() === null) {
$("#node-input-inout").val("out");
@ -99,7 +70,7 @@
} else {
$("#node-bandgap").show();
}
if ($("#node-input-func").val() === "narrowband") {
if (($("#node-input-func").val() === "narrowband")||($("#node-input-func").val() === "narrowbandEq")) {
$("#node-startvalue").show();
} else {
$("#node-startvalue").hide();

View File

@ -13,6 +13,7 @@ module.exports = function(RED) {
this.gap = parseFloat(this.gap);
}
this.g = this.gap;
this.property = n.property||"payload";
var node = this;
@ -20,41 +21,42 @@ module.exports = function(RED) {
this.on("input",function(msg) {
if (msg.hasOwnProperty("reset")) {
if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) {
delete node.previous[msg.topic];
delete node.previous[msg.topic];
}
else { node.previous = {}; }
}
if (msg.hasOwnProperty("payload")) {
var value = RED.util.getMessageProperty(msg,node.property);
if (value !== undefined) {
var t = msg.topic || "_no_topic";
if ((this.func === "rbe") || (this.func === "rbei")) {
var doSend = (this.func !== "rbei") || (node.previous.hasOwnProperty(t)) || false;
if (typeof(msg.payload) === "object") {
if (typeof(value) === "object") {
if (typeof(node.previous[t]) !== "object") { node.previous[t] = {}; }
if (!RED.util.compareObjects(msg.payload, node.previous[t])) {
node.previous[t] = RED.util.cloneMessage(msg.payload);
if (!RED.util.compareObjects(value, node.previous[t])) {
node.previous[t] = RED.util.cloneMessage(value);
if (doSend) { node.send(msg); }
}
}
else {
if (msg.payload !== node.previous[t]) {
node.previous[t] = RED.util.cloneMessage(msg.payload);
if (value !== node.previous[t]) {
node.previous[t] = RED.util.cloneMessage(value);
if (doSend) { node.send(msg); }
}
}
}
else {
var n = parseFloat(msg.payload);
var n = parseFloat(value);
if (!isNaN(n)) {
if ((typeof node.previous[t] === 'undefined') && (this.func === "narrowband")) {
if (node.start === '') { node.previous[t] = n; }
else { node.previous[t] = node.start; }
}
if (node.pc) { node.gap = (node.previous[t] * node.g / 100) || 0; }
if (node.pc) { node.gap = Math.abs(node.previous[t] * node.g / 100) || 0; }
else { node.gap = Number(node.gap); }
if ((node.previous[t] === undefined) && (node.func === "narrowbandEq")) { node.previous[t] = n; }
if (node.previous[t] === undefined) { node.previous[t] = n - node.gap; }
if (node.previous[t] === undefined) { node.previous[t] = n - node.gap - 1; }
if (Math.abs(n - node.previous[t]) === node.gap) {
if (this.func === "deadbandEq") {
if ((this.func === "deadbandEq")||(this.func === "narrowband")) {
if (node.inout === "out") { node.previous[t] = n; }
node.send(msg);
}

View File

@ -1,7 +1,11 @@
<script type="text/x-red" data-template-name="smooth">
<script type="text/html" data-template-name="smooth">
<div class="form-row">
<label for="node-input-action"><i class="fa fa-bolt"></i> Action</label>
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<label for="node-input-action"><i class="fa fa-bolt"></i> Action</label>
<select id="node-input-action" style="width:60%; margin-right:5px;">
<option value="max">Return the maximum value seen</option>
<option value="min">Return the minimum value seen</option>
@ -28,6 +32,11 @@
<option value="multi">Different msg.topic as individual streams.</option>
</select>
</div>
<div class="form-row" id="row-input-reduce">
<label for="node-input-reduce"><i class="fa fa-compress"></i> Reduce</label>
<input type="checkbox" id="node-input-reduce" style="display:inline-block; width:20px; vertical-align:baseline;">
only emit one message per most recent N values
</div>
<br/>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
@ -36,13 +45,12 @@
<div class="form-tips" id="node-tip">Tip: This node ONLY works with numbers.</div>
</script>
<script type="text/x-red" data-help-name="smooth">
<script type="text/html" data-help-name="smooth">
<p>A simple node to provide various functions across several previous values, including max, min, mean, high and low pass filters.</p>
<p>Messages arriving with different <code>msg.topic</code> can be treated as separate streams if so configured.</p>
<p>Max, Min and Mean work over a specified number of previous values.</p>
<p>The High and Low pass filters use a smoothing factor. The higher the number the more the smoothing. E.g. a value of 10 is
similar to an &alpha; of 0.1. It is analagous to an RC time constant - but there is no time component to this as the
time is based on events arriving.</p>
<p>The High and Low pass filters use a smoothing factor. The higher the number the more the smoothing. E.g. a value of 10 is similar to an &alpha; of 0.1. It is analagous to an RC time constant - but there is no time component to this as the time is based on events arriving.</p>
<p>Enabling the Reduce option causes the node to only emit one message per N values (available for the Max, Min and Mean functions). E.g. if set to Mean over 10 values, there will only be one outgoing message per 10 incoming ones.</p>
<p>If <code>msg.reset</code> is received (with any value), all the counters and intermediate values are reset to an initial state.</p>
<p><b>Note:</b> This only operates on <b>numbers</b>. Anything else will try to be made into a number and rejected if that fails.</p>
</script>
@ -53,10 +61,12 @@
category: 'function',
defaults: {
name: {value:""},
property: {value:"payload",required:true},
action: {value:"mean"},
count: {value:"10",required:true,validate:RED.validators.number()},
round: {value:""},
mult: {value:"single"}
mult: {value:"single"},
reduce: {value:false}
},
inputs: 1,
outputs: 1,
@ -67,7 +77,12 @@
labelStyle: function() {
return this.name ? "node_label_italic" : "";
},
outputLabels: function() { return this.reduce === true ? (this.action+" of "+this.count) : (this.action); },
oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
$("#node-input-count").spinner({
min:1
});
@ -78,10 +93,12 @@
if ((a === "high") || ( a === "low" )) {
$("#node-over").html("with a smoothing factor of ");
$("#node-over2").html("");
$("#row-input-reduce").hide();
}
else {
$("#node-over").html("over the most recent ");
$("#node-over2").html(" values");
$("#row-input-reduce").show();
}
});
$("#node-input-action").change();

View File

@ -9,11 +9,15 @@ module.exports = function(RED) {
if (this.round == "true") { this.round = 0; }
this.count = Number(n.count);
this.mult = n.mult || "single";
this.reduce = n.reduce || false;
this.property = n.property || "payload";
var node = this;
var v = {};
this.on('input', function (msg) {
var value = RED.util.getMessageProperty(msg,node.property);
var top = msg.topic || "_my_default_topic";
var reduce = node.reduce;
if (this.mult === "single") { top = "a"; }
if ((v.hasOwnProperty(top) !== true) || msg.hasOwnProperty("reset")) {
@ -24,44 +28,51 @@ module.exports = function(RED) {
v[top].pop = 0;
v[top].old = null;
v[top].count = this.count;
v[top].iter = 0;
}
if (msg.hasOwnProperty("payload")) {
var n = Number(msg.payload);
if (value !== undefined) {
var n = Number(value);
if (!isNaN(n)) {
v[top].iter++;
if ((node.action === "low") || (node.action === "high")) {
if (v[top].old == null) { v[top].old = n; }
v[top].old = v[top].old + (n - v[top].old) / v[top].count;
if (node.action === "low") { msg.payload = v[top].old; }
else { msg.payload = n - v[top].old; }
if (node.action === "low") { value = v[top].old; }
else { value = n - v[top].old; }
reduce = false;
}
else {
v[top].a.push(n);
if (v[top].a.length > v[top].count) { v[top].pop = v[top].a.shift(); }
if (node.action === "max") {
msg.payload = Math.max.apply(Math, v[top].a);
value = Math.max.apply(Math, v[top].a);
}
if (node.action === "min") {
msg.payload = Math.min.apply(Math, v[top].a);
value = Math.min.apply(Math, v[top].a);
}
if (node.action === "mean") {
v[top].tot = v[top].tot + n - v[top].pop;
msg.payload = v[top].tot / v[top].a.length;
value = v[top].tot / v[top].a.length;
}
if (node.action === "sd") {
v[top].tot = v[top].tot + n - v[top].pop;
v[top].tot2 = v[top].tot2 + (n*n) - (v[top].pop * v[top].pop);
if (v[top].a.length > 1) {
msg.payload = Math.sqrt((v[top].a.length * v[top].tot2 - v[top].tot * v[top].tot)/(v[top].a.length * (v[top].a.length - 1)));
value = Math.sqrt((v[top].a.length * v[top].tot2 - v[top].tot * v[top].tot)/(v[top].a.length * (v[top].a.length - 1)));
}
else { msg.payload = 0; }
else { value = 0; }
}
}
if (node.round !== false) {
msg.payload = Math.round(msg.payload * Math.pow(10, node.round)) / Math.pow(10, node.round);
value = Math.round(value * Math.pow(10, node.round)) / Math.pow(10, node.round);
}
if (reduce == false || v[top].iter == v[top].count) {
v[top].iter = 0;
RED.util.setMessageProperty(msg,node.property,value);
node.send(msg);
}
node.send(msg);
}
else { node.log("Not a number: "+msg.payload); }
else { node.log("Not a number: " + value); }
} // ignore msg with no payload property.
});
}

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-smooth",
"version" : "0.0.11",
"version" : "0.1.2",
"description" : "A Node-RED node that provides several simple smoothing algorithms for incoming data values.",
"dependencies" : {
},
@ -19,5 +19,6 @@
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
}
},
"contributors" : [ "@clickworkorange" ]
}

View File

@ -1,13 +1,14 @@
<script type="text/x-red" data-template-name="arduino in">
<script type="text/html" data-template-name="arduino in">
<div class="form-row">
<label for="node-input-arduino"><i class="fa fa-tasks"></i> Arduino</label>
<input type="text" id="node-input-arduino">
</div>
<div class="form-row">
<label for="node-input-state"><i class="fa fa-wrench"></i> <span data-i18n="arduino.label.type"></span></label>
<select type="text" id="node-input-state" style="width: 150px;">
<select type="text" id="node-input-state" style="width:200px;">
<option value="INPUT" data-i18n="arduino.state.in.digital"></option>
<option value="PULLUP" data-i18n="arduino.state.in.pullup"></option>
<option value="ANALOG" data-i18n="arduino.state.in.analogue"></option>
<option value="STRING" data-i18n="arduino.state.in.string"></option>
</select>
@ -23,11 +24,12 @@
<div class="form-tips"><span data-i18n="[html]arduino.tip.io"></span></div>
</script>
<script type="text/x-red" data-help-name="arduino in">
<script type="text/html" data-help-name="arduino in">
<p>Arduino input node. Connects to a local Arduino and monitors the selected pin for changes. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p>
<p>The Arduino must be loaded with the Standard Firmata sketch available in the Arduino examples.</p>
<p>You can select either Digital or Analogue input. Outputs the value read as <code>msg.payload</code> and the pin number as <code>msg.topic</code>.</p>
<p>It only outputs on a change of value - fine for digital inputs, but you can get a lot of data from analogue pins which you must then handle.</p>
<p>For example you could use a <code>delay</code> node set to rate limit and drop intermediate values, or an <code>rbe</code> node to only report when it changes by a certain amount.</p>
</script>
<script type="text/javascript">
@ -58,14 +60,14 @@
});
</script>
<script type="text/x-red" data-template-name="arduino out">
<script type="text/html" data-template-name="arduino out">
<div class="form-row">
<label for="node-input-arduino"><i class="fa fa-tasks"></i> <span data-i18n="arduino.label.arduino"></span></label>
<label for="node-input-arduino"><i class="fa fa-tasks"></i> Arduino</label>
<input type="text" id="node-input-arduino">
</div>
<div class="form-row">
<label for="node-input-state"><i class="fa fa-wrench"></i> <span data-i18n="arduino.label.type"></span></label>
<select type="text" id="node-input-state" style="width: 200px;">
<select type="text" id="node-input-state" style="width:200px;">
<option value="OUTPUT" data-i18n="arduino.state.out.digital"></option>
<option value="PWM" data-i18n="arduino.state.out.analogue"></option>
<option value="SERVO" data-i18n="arduino.state.out.servo"></option>
@ -83,7 +85,7 @@
<div class="form-tips"><span data-i18n="[html]arduino.tip.io"></span></div>
</script>
<script type="text/x-red" data-help-name="arduino out">
<script type="text/html" data-help-name="arduino out">
<p>Arduino output node. Connects to local Arduino and writes to the selected digital
pin. Uses <a href="http://firmata.org/" target="_new"><i>Firmata</i>.</a></p>
<p>The Arduino must be loaded with the Standard Firmata sketch available in the Arduino examples.</p>
@ -119,11 +121,11 @@
</script>
<script type="text/x-red" data-template-name="arduino-board">
<script type="text/html" data-template-name="arduino-board">
<div class="form-row">
<label for="node-config-input-device"><i class="fa fa-random"></i> <span data-i18n="arduino.label.port"></span></label>
<input type="text" id="node-config-input-device" style="width:60%;" data-i18n="[placeholder]arduino.placeholder.port"/>
<a id="node-config-lookup-serial" class="btn"><i id="node-config-lookup-serial-icon" class="fa fa-search"></i></a>
<a id="node-config-lookup-serial" class="red-ui-button"><i id="node-config-lookup-serial-icon" class="fa fa-search"></i></a>
</div>
<div class="form-tips"><span data-i18n="[html]arduino.tip.conf"></span></div>
</script>
@ -150,10 +152,7 @@
$("#node-config-lookup-serial-icon").addClass('fa-search');
$("#node-config-lookup-serial-icon").removeClass('spinner');
$("#node-config-lookup-serial").removeClass('disabled');
var ports = [];
$.each(data, function(i, port) {
ports.push(port.comName);
});
var ports = data || [];
$("#node-config-input-device").autocomplete({
source:ports,
minLength:0,

View File

@ -9,32 +9,46 @@ module.exports = function(RED) {
function ArduinoNode(n) {
RED.nodes.createNode(this,n);
this.device = n.device || null;
this.running = false;
this.reported = false;
var node = this;
node.board = Board(node.device, function(e) {
//console.log("ERR",e);
if ((e !== undefined) && (e.toString().indexOf("cannot open") !== -1) ) {
node.error(RED._("arduino.errors.portnotfound",{device:node.device}));
}
else if (e === undefined) {
node.board.on('ready', function() {
node.log(RED._("arduino.status.connected",{device:node.board.sp.path}));
if (RED.settings.verbose) {
node.log(RED._("arduino.status.version",{version:node.board.firmware.name+"-"+node.board.version.major+"."+node.board.version.minor}));
var startup = function() {
node.board = new Board(node.device, function(e) {
if ((e !== undefined) && (e.toString().indexOf("cannot open") !== -1) ) {
if (node.reported === false) {
node.error(RED._("arduino.errors.portnotfound",{device:node.device}));
node.reported = true;
}
});
}
node.board.on('close', function() {
node.error(RED._("arduino.status.portclosed"));
}
else if (e === undefined) {
node.running = true;
node.reported = false;
node.board.once('ready', function() {
node.log(RED._("arduino.status.connected",{device:node.board.sp.path}));
if (RED.settings.verbose) {
node.log(RED._("arduino.status.version",{version:node.board.firmware.name+"-"+node.board.version.major+"."+node.board.version.minor}));
}
});
node.board.once('close', function() {
node.error(RED._("arduino.status.portclosed"));
});
node.board.once('disconnect', function() {
if (node.running === true) { setTimeout(function() { node.running = false; startup(); }, 5000); }
});
}
});
});
setTimeout(function() { if (node.running === false) { startup(); } }, 5000);
};
startup();
node.on('close', function(done) {
node.running = false;
if (node.board) {
try {
node.board.sp.close(function() {
done();
node.board.transport.close(function() {
if (RED.settings.verbose) { node.log(RED._("arduino.status.portclosed")); }
done();
});
}
catch(e) { done(); }
@ -53,43 +67,69 @@ module.exports = function(RED) {
this.state = n.state;
this.arduino = n.arduino;
this.serverConfig = RED.nodes.getNode(this.arduino);
this.running = false;
var node = this;
if (typeof this.serverConfig === "object") {
this.board = this.serverConfig.board;
var node = this;
node.status({fill:"red",shape:"ring",text:"node-red:common.status.connecting"});
var doit = function() {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
if (node.state === "ANALOG") {
node.board.pinMode(node.pin, 0x02);
node.board.analogRead(node.pin, function(v) {
node.send({payload:v, topic:"A"+node.pin});
var startup = function() {
node.board = node.serverConfig.board;
node.board.setMaxListeners(0);
node.oldval = "";
node.status({fill:"grey",shape:"ring",text:"node-red:common.status.connecting"});
var doit = function() {
node.running = true;
if (node.state === "ANALOG") { node.board.pinMode(node.pin, 0x02); }
if (node.state === "INPUT") { node.board.pinMode(node.pin, 0x00); }
if (node.state === "PULLUP") { node.board.pinMode(node.pin, 0x0B); }
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
if (node.state === "ANALOG") {
node.board.analogRead(node.pin, function(v) {
if (v !== node.oldval) {
node.oldval = v;
node.send({payload:v, topic:"A"+node.pin});
}
});
}
if (node.state === "INPUT") {
node.board.digitalRead(node.pin, function(v) {
if (v !== node.oldval) {
node.oldval = v;
node.send({payload:v, topic:node.pin});
}
});
}
if (node.state === "PULLUP") {
node.board.digitalRead(node.pin, function(v) {
if (v !== node.oldval) {
node.oldval = v;
node.send({payload:v, topic:node.pin});
}
});
}
if (node.state == "STRING") {
node.board.on('string', function(v) {
if (v !== node.oldval) {
node.oldval = v;
node.send({payload:v, topic:"string"});
}
});
}
node.board.once('disconnect', function() {
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
if (node.running) { setTimeout(function() { node.running = false; startup(); }, 5500); }
});
}
if (node.state === "INPUT") {
node.board.pinMode(node.pin, 0x00);
node.board.digitalRead(node.pin, function(v) {
node.send({payload:v, topic:node.pin});
});
}
if (node.state == "STRING") {
node.board.on('string', function(v) {
node.send({payload:v, topic:"string"});
});
}
// node.board.on('close', function() {
// node.board.removeAllListeners();
// node.status({fill:"grey",shape:"ring",text:"node-red:common.status.not-connected"});
// });
if (node.board.isReady) { doit(); }
else { node.board.once("ready", function() { doit(); }); }
setTimeout(function() { if (node.running === false) { startup(); } }, 4500);
}
if (node.board.isReady) { doit(); }
else { node.board.on("ready", function() { doit(); }); }
node.on("close", function() {
if (node.tout) { clearTimeout(node.tout); }
})
startup();
}
else {
this.warn(RED._("arduino.errors.portnotconf"));
node.warn(RED._("arduino.errors.portnotconf"));
}
node.on('close', function() {
node.running = false;
});
}
RED.nodes.registerType("arduino in",DuinoNodeIn);
@ -102,63 +142,78 @@ module.exports = function(RED) {
this.state = n.state;
this.arduino = n.arduino;
this.serverConfig = RED.nodes.getNode(this.arduino);
if (typeof this.serverConfig === "object") {
this.board = this.serverConfig.board;
var node = this;
node.status({fill:"red",shape:"ring",text:"node-red:common.status.connecting"});
var doit = function() {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
node.on("input", function(msg) {
if (node.state === "OUTPUT") {
node.board.pinMode(node.pin, 0x01);
if ((msg.payload === true)||(msg.payload.toString() == "1")||(msg.payload.toString().toLowerCase() == "on")) {
node.board.digitalWrite(node.pin, node.board.HIGH);
this.running = false;
var node = this;
if (typeof node.serverConfig === "object") {
var startup = function() {
node.board = node.serverConfig.board;
node.board.setMaxListeners(0);
node.status({fill:"grey",shape:"ring",text:"node-red:common.status.connecting"});
var doit = function() {
node.running = true;
if (node.state === "OUTPUT") { node.board.pinMode(node.pin, 0x01); }
if (node.state === "PWM") { node.board.pinMode(node.pin, 0x03); }
if (node.state === "SERVO") { node.board.pinMode(node.pin, 0x04); }
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
node.on("input", function(msg) {
if (node.board.isReady) {
if (node.state === "OUTPUT") {
if ((msg.payload === true)||(msg.payload.toString() == "1")||(msg.payload.toString().toLowerCase() == "on")) {
node.board.digitalWrite(node.pin, node.board.HIGH);
}
if ((msg.payload === false)||(msg.payload.toString() == "0")||(msg.payload.toString().toLowerCase() == "off")) {
node.board.digitalWrite(node.pin, node.board.LOW);
}
}
if (node.state === "PWM") {
msg.payload = parseInt((msg.payload * 1) + 0.5);
if ((msg.payload >= 0) && (msg.payload <= 255)) {
node.board.analogWrite(node.pin, msg.payload);
}
}
if (node.state === "SERVO") {
msg.payload = parseInt((msg.payload * 1) + 0.5);
if ((msg.payload >= 0) && (msg.payload <= 180)) {
node.board.servoWrite(node.pin, msg.payload);
}
}
if (node.state === "SYSEX") {
node.board.sysexCommand(msg.payload);
}
if (node.state === "STRING") {
node.board.sendString(msg.payload.toString());
}
}
if ((msg.payload === false)||(msg.payload.toString() == "0")||(msg.payload.toString().toLowerCase() == "off")) {
node.board.digitalWrite(node.pin, node.board.LOW);
}
}
if (node.state === "PWM") {
node.board.pinMode(node.pin, 0x03);
msg.payload = parseInt((msg.payload * 1) + 0.5);
if ((msg.payload >= 0) && (msg.payload <= 255)) {
node.board.analogWrite(node.pin, msg.payload);
}
}
if (node.state === "SERVO") {
node.board.pinMode(node.pin, 0x04);
msg.payload = parseInt((msg.payload * 1) + 0.5);
if ((msg.payload >= 0) && (msg.payload <= 180)) {
node.board.servoWrite(node.pin, msg.payload);
}
}
if (node.state === "SYSEX") {
node.board.sysexCommand(msg.payload);
}
if (node.state === "STRING") {
node.board.sendString(msg.payload.toString());
}
});
// node.board.on('close', function() {
// node.status({fill:"grey",shape:"ring",text:"node-red:common.status.not-connected"});
// });
});
node.board.once('disconnect', function() {
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
if (node.running === true) { setTimeout(function() { node.running = false; startup(); }, 5500); }
});
}
if (node.board.isReady) { doit(); }
else { node.board.once("ready", function() { doit(); }); }
setTimeout(function() { if (node.running === false) { startup(); } }, 4500);
}
if (node.board.isReady) { doit(); }
else { node.board.on("ready", function() { doit(); }); }
node.on("close", function() {
if (node.tout) { clearTimeout(node.tout); }
})
startup();
}
else {
this.warn(RED._("arduino.errors.portnotconf"));
node.warn(RED._("arduino.errors.portnotconf"));
}
node.on('close', function() {
node.running = false;
});
}
RED.nodes.registerType("arduino out",DuinoNodeOut);
RED.httpAdmin.get("/arduinoports", RED.auth.needsPermission("arduino.read"), function(req,res) {
SP.list(function(error, ports) {
res.json(ports);
});
SP.list().then(
ports => {
const a = ports.map(p => p.comName);
res.json(a);
},
err => {
this.log('Error listing serial ports', err)
}
)
});
}

View File

@ -7,7 +7,7 @@ Arduino running standard firmata 2.2 or better.
Install
-------
Run the following command in your Node-RED user directory - typically `~/.node-red`
Either use the Menu - Manage Palette option or run the following command in your Node-RED user directory - typically `~/.node-red`
npm i --unsafe-perm node-red-node-arduino
@ -23,12 +23,10 @@ details and examples of how to use this node.
Connects to local Arduino and monitors the selected pin for changes.
You can select either **Digital**, **Analogue**, or **String** input type.
You can select either **Digital**, **Pullup**, **Analogue**, or **String** input type.
Outputs the value read as `msg.payload` and the pin number as `msg.topic`.
It only outputs on a change of value - fine for digital inputs, but you can get a lot of data from analogue pins which you must then handle.
You can set the sample rate from `20` to `65535` mS.
It only outputs on a change of value - fine for digital inputs, but you can get a lot of data from analogue pins which you must then handle. For example you could use a `delay` node set to rate limit and drop intermediate values, or an `rbe` node to only report when it changes by a certain amount.
### Output Node

View File

@ -18,6 +18,7 @@
"state": {
"in": {
"digital": "Digital pin",
"pullup": "Digital pin with pullup",
"analogue": "Analogue pin",
"string": "String"
},

View File

@ -1,9 +1,9 @@
{
"name" : "node-red-node-arduino",
"version" : "0.0.14",
"version" : "0.3.1",
"description" : "A Node-RED node to talk to an Arduino running firmata",
"dependencies" : {
"firmata" : "~0.17.0"
"firmata" : "^2.3.0"
},
"repository" : {
"type":"git",
@ -16,6 +16,7 @@
"arduino": "35-arduino.js"
}
},
"engines" : { "node" : ">=8" },
"author": {
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",

View File

@ -8,23 +8,26 @@ module.exports = function(RED) {
var LedBorgInUse = false;
var gpioCommand = __dirname+'/nrgpio';
var allOK = true;
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
if (cpuinfo.indexOf(": BCM") === -1) { throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); }
if (cpuinfo.indexOf(": BCM") === -1) {
RED.log.warn("ledborg : "+RED._("node-red:rpi-gpio.errors.ignorenode"));
allOK = false;
}
else if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
RED.log.warn("ledborg : "+RED._("node-red:rpi-gpio.errors.libnotfound"));
allOK = false;
}
else if ( !(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]) )) {
RED.log.warn("ledborg : "+RED._("node-red:rpi-gpio.errors.needtobeexecutable",{command:gpioCommand}));
allOK = false;
}
}
catch(err) {
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
}
if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
util.log("[rpi-ledborg] Info : Can't find RPi.GPIO python library.");
throw "Warning : Can't find RPi.GPIO python library.";
}
if ( !(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]) )) {
util.log("[rpi-ledborg] Error : "+gpioCommand+" needs to be executable.");
throw "Error : nrgpio must to be executable.";
RED.log.warn("ledborg : "+RED._("node-red:rpi-gpio.errors.ignorenode"));
allOK = false;
}
// GPIO pins 11 (R), 13 (G), 15 (B).
@ -95,46 +98,50 @@ module.exports = function(RED) {
}
}
node.child = spawn(gpioCommand, ["borg","11"]);
node.running = true;
node.status({fill:"green",shape:"dot",text:"OK"});
if (allOK === true) {
node.child = spawn(gpioCommand, ["borg","11"]);
node.running = true;
node.status({fill:"green",shape:"dot",text:"OK"});
node.on("input", inputlistener);
node.on("input", inputlistener);
node.child.stdout.on('data', function (data) {
if (RED.settings.verbose) { node.log("out: "+data+" :"); }
});
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.stderr.on('data', function (data) {
if (RED.settings.verbose) { node.log("err: "+data+" :"); }
});
node.child.on('close', function () {
node.child = null;
node.running = false;
node.status({fill:"red",shape:"circle",text:""});
if (RED.settings.verbose) { node.log("closed"); }
if (node.done) { node.done(); }
});
node.child.on('close', function () {
node.child = null;
node.running = false;
node.status({fill:"red",shape:"circle",text:""});
if (RED.settings.verbose) { node.log("closed"); }
if (node.finished) { node.finished(); }
});
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); }
});
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); }
});
node.on("close", function(done) {
node.on("close", function(done) {
LedBorgInUse = false;
node.status({fill:"red",shape:"circle",text:""});
if (node.child != null) {
node.fisnished = done;
node.child.stdin.write(" close 11");
node.child.kill('SIGKILL');
}
else { done(); }
});
}
else {
LedBorgInUse = false;
node.status({fill:"red",shape:"circle",text:""});
if (node.child != null) {
node.done = done;
node.child.stdin.write(" close 11");
node.child.kill('SIGKILL');
}
else { done(); }
});
node.status({fill:"grey",shape:"dot",text:"node-red:rpi-gpio.status.not-available"});
}
}
RED.nodes.registerType("ledborg",LedBorgNode);
}

View File

@ -4,11 +4,12 @@
import RPi.GPIO as GPIO
import sys
bounce = 20 # bounce time in mS to apply
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
if sys.version_info >= (3,0):
print("Sorry - currently only configured to work with python 2.x")
sys.exit(1)
bounce = 20 # bounce time in mS to apply
if len(sys.argv) > 1:
cmd = sys.argv[1].lower()
@ -17,7 +18,7 @@ if len(sys.argv) > 1:
GPIO.setwarnings(False)
if cmd == "pwm":
#print "Initialised pin "+str(pin)+" to PWM"
#print("Initialised pin "+str(pin)+" to PWM")
GPIO.setup(pin,GPIO.OUT)
p = GPIO.PWM(pin, 100)
p.start(0)
@ -32,10 +33,10 @@ if len(sys.argv) > 1:
GPIO.cleanup(pin)
sys.exit(0)
except Exception as ex:
print "bad data: "+data
print("bad data: "+data)
elif cmd == "buzz":
#print "Initialised pin "+str(pin)+" to Buzz"
#print("Initialised pin "+str(pin)+" to Buzz")
GPIO.setup(pin,GPIO.OUT)
p = GPIO.PWM(pin, 100)
p.stop()
@ -54,10 +55,10 @@ if len(sys.argv) > 1:
GPIO.cleanup(pin)
sys.exit(0)
except Exception as ex:
print "bad data: "+data
print("bad data: "+data)
elif cmd == "out":
#print "Initialised pin "+str(pin)+" to 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]))
@ -78,9 +79,9 @@ if len(sys.argv) > 1:
GPIO.output(pin,data)
elif cmd == "in":
#print "Initialised pin "+str(pin)+" to IN"
#print("Initialised pin "+str(pin)+" to IN")
def handle_callback(chan):
print GPIO.input(chan)
print(GPIO.input(chan))
if len(sys.argv) == 4:
if sys.argv[3].lower() == "up":
@ -91,7 +92,7 @@ if len(sys.argv) > 1:
GPIO.setup(pin,GPIO.IN)
else:
GPIO.setup(pin,GPIO.IN)
print GPIO.input(pin)
print(GPIO.input(pin))
GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback, bouncetime=bounce)
while True:
@ -104,7 +105,7 @@ if len(sys.argv) > 1:
sys.exit(0)
elif cmd == "byte":
#print "Initialised BYTE mode - "+str(pin)+
#print("Initialised BYTE mode - "+str(pin)+)
list = [7,11,13,12,15,16,18,22]
GPIO.setup(list,GPIO.OUT)
@ -127,7 +128,7 @@ if len(sys.argv) > 1:
GPIO.output(list[bit], data & mask)
elif cmd == "borg":
#print "Initialised BORG mode - "+str(pin)+
#print("Initialised BORG mode - "+str(pin)+)
GPIO.setup(11,GPIO.OUT)
GPIO.setup(13,GPIO.OUT)
GPIO.setup(15,GPIO.OUT)
@ -154,10 +155,10 @@ if len(sys.argv) > 1:
data = 0
elif cmd == "rev":
print GPIO.RPI_REVISION
print(GPIO.RPI_REVISION)
elif cmd == "ver":
print GPIO.VERSION
print(GPIO.VERSION)
elif cmd == "mouse": # catch mice button events
file = open( "/dev/input/mice", "rb" )
@ -171,7 +172,7 @@ if len(sys.argv) > 1:
button = ord( buf[0] ) & pin # mask out just the required button(s)
if button != oldbutt: # only send if changed
oldbutt = button
print button
print(button)
while True:
try:
@ -181,4 +182,4 @@ if len(sys.argv) > 1:
sys.exit(0)
else:
print "Bad parameters - in|out|pwm|buzz|byte|borg|mouse|ver pin {value|up|down}"
print("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|ver pin {value|up|down}")

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-ledborg",
"version" : "0.0.18",
"version" : "0.0.22",
"description" : "A Node-RED node to control a PiBorg LedBorg board for a Raspberry Pi.",
"dependencies" : {
},

View File

@ -0,0 +1,610 @@
<style>
.rpi-gpio-pinTable {
width: 340px;
display: inline-table;
font-size: 13px;
height: 380px;
min-height: 380px;
max-height: 380px;
}
.rpi-gpio-pinTable input[type="radio"] {
width: auto;
margin: 3px 2px;
vertical-align: top;
}
.rpi-gpio-pinTable label {
width: auto;
margin: 0;
display: block;
}
.rpi-gpio-pinTable .pinTableBody {
width: 340px;
display: table-row-group;
line-height: 12px;
}
.rpi-gpio-pinTable .pinTableRow {
width: 340px;
display: table-row;
height: 20px;
}
.rpi-gpio-pinTable .pinTableCellL {
width: 170px;
height: 20px;
display: table-cell;
text-align: right;
padding-right: 4px;
vertical-align: middle;
border: 1px solid #444;
}
.rpi-gpio-pinTable .pinTableCellL label {
width: 170px;
}
.rpi-gpio-pinTable .pinTableCellR {
width: 170px;
height: 22px;
display: table-cell;
text-align: left;
padding-left: 4px;
vertical-align: middle;
border: 1px solid #000;
}
.rpi-gpio-pinTable .pinTableCellR label {
width: 170px;
}
.rpi-gpio-pinTable .pinColorPower {
background-color:#FECBCE;
}
.rpi-gpio-pinTable .pinColorGround {
background-color:#DDDDDD;
}
.rpi-gpio-pinTable .pinColorGPIO {
background-color:#BFEBBF;
}
.rpi-gpio-pinTable .pinColorDual {
background-color:#D0E6F4;
}
.rpi-gpio-pinTable .pinColorSD {
background-color:#FFFDD0;
}
</style>
<script type="text/html" data-template-name="rpi-gpio in">
<div class="form-row" style="min-width: 540px">
<label><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.pinname"></span></label>
<div class="rpi-gpio-pinTable">
<div class="pinTableBody" id="pinform">
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower"><label>3.3V Power - 1 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorPower"><label><input disabled type="radio" name="pins" value=""> 2 - 5V Power</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-3">SDA1 - GPIO02 - 3 <input id="pinTable-pin-3" type="radio" name="pins" value="3"></label></div>
<div class="pinTableCellR pinColorPower"><label><input disabled type="radio" name="pins" value=""> 4 - 5V Power</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-5">SCL1 - GPIO03 - 5 <input id="pinTable-pin-5" type="radio" name="pins" value="5"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 6 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-7">GPIO04 - 7 <input id="pinTable-pin-7" type="radio" name="pins" value="7"></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-8"><input id="pinTable-pin-8" type="radio" name="pins" value="8"> 8 - GPIO14 - TxD</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround"><label>Ground - 9 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-10"><input id="pinTable-pin-10" type="radio" name="pins" value="10"> 10 - GPIO15 - RxD</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-11">GPIO17 - 11 <input id="pinTable-pin-11" type="radio" name="pins" value="11"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-12"><input id="pinTable-pin-12" type="radio" name="pins" value="12"> 12 - GPIO18</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-13">GPIO27 - 13 <input id="pinTable-pin-13" type="radio" name="pins" value="13"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 14 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-15">GPIO22 - 15 <input id="pinTable-pin-15" type="radio" name="pins" value="15"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-16"><input id="pinTable-pin-16" type="radio" name="pins" value="16"> 16 - GPIO23</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower"><label>3.3V Power - 17 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-18"><input id="pinTable-pin-18" type="radio" name="pins" value="18"> 18 - GPIO24</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-19">MOSI - GPIO10 - 19 <input id="pinTable-pin-19" type="radio" name="pins" value="19"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 20 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-21">MISO - GPIO09 - 21 <input id="pinTable-pin-21" type="radio" name="pins" value="21"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-22"><input id="pinTable-pin-22" type="radio" name="pins" value="22"> 22 - GPIO25</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-23">SCLK - GPIO11 - 23 <input id="pinTable-pin-23" type="radio" name="pins" value="23"></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-24"><input id="pinTable-pin-24" type="radio" name="pins" value="24"> 24 - GPIO8 - CE0</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround"><label>Ground - 25 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-26"><input id="pinTable-pin-26" type="radio" name="pins" value="26"> 26 - GPIO7 - CE1</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorSD"><label>SD - 27 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorSD"><label><input disabled type="radio" name="pins" value=""> 28 - SC</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-29">GPIO05 - 29 <input id="pinTable-pin-29" type="radio" name="pins" value="29"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 30 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-31">GPIO06 - 31 <input id="pinTable-pin-31" type="radio" name="pins" value="31"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-32"><input id="pinTable-pin-32" type="radio" name="pins" value="32"> 32 - GPIO12</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-33">GPIO13 - 33 <input id="pinTable-pin-33" type="radio" name="pins" value="33"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 34 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-35">GPIO19 - 35 <input id="pinTable-pin-35" type="radio" name="pins" value="35"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-36"><input id="pinTable-pin-36" type="radio" name="pins" value="36"> 36 - GPIO16</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-37">GPIO26 - 37 <input id="pinTable-pin-37" type="radio" name="pins" value="37"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-38"><input id="pinTable-pin-38" type="radio" name="pins" value="38"> 38 - GPIO20</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround"><label>Ground - 39 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-40"><input id="pinTable-pin-40" type="radio" name="pins" value="40"> 40 - GPIO21</label></div>
</div>
</div>
</div>
</div>
<div class="form-row">
<label>&nbsp;&nbsp;&nbsp;&nbsp;</label>
<input type="text" id="node-input-pin" style="width: 352px">
</div>
<div class="form-row">
<label for="node-input-intype"><i class="fa fa-level-up"></i> <span data-i18n="rpi-gpio.label.resistor"></span></label>
<select type="text" id="node-input-intype" style="width:100px;">
<option value="tri" data-i18n="rpi-gpio.resistor.none"></option>
<option value="up" data-i18n="rpi-gpio.resistor.pullup"></option>
<option value="down" data-i18n="rpi-gpio.resistor.pulldown"></option>
</select>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="rpi-gpio.label.debounce"></span>
<input type="text" id="node-input-debounce" style="width:47px; text-align:right"/>&nbsp;mS
</div>
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-read" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-read" style="width:70%;"><span data-i18n="rpi-gpio.label.readinitial"></span></label>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-tips" id="pin-tip"><span data-i18n="[html]rpi-gpio.tip.pin"></span></div>
<div class="form-tips"><span data-i18n="[html]rpi-gpio.tip.in"></span></div>
</script>
<script type="text/javascript">
var bcm2pin = {
"2":"3", "3":"5", "4":"7", "14":"8", "15":"10", "17":"11", "18":"12", "27":"13", "22":"15",
"23":"16", "24":"18", "10":"19", "9":"21", "25":"22", "11":"23", "8":"24", "7":"26",
"5":"29", "6":"31", "12":"32", "13":"33", "19":"35", "16":"36", "26":"37", "20":"38", "21":"40"
};
var pinsInUse = {};
var validPinValues = Object.values(bcm2pin);
var isEnvVar = function (value) {
var re = /^\${([0-9a-zA-Z_]+)}$/;
var match = value.match(re);
return Boolean(match);
};
var isInt = function (value) {
return parseInt(value).toString() === value.trim();
};
var uncheckAll = function() {
for (var i=0; i< validPinValues.length; i++) {
$("#pinform input[value="+validPinValues[i]+"]").prop('checked', false);
}
}
var validatePin = function (value) {
return isEnvVar(value) || (isInt(value) && validPinValues.includes(value));
};
RED.nodes.registerType('rpi-gpio in',{
category: 'Raspberry Pi',
color:"#c6dbef",
defaults: {
name: { value:"" },
pin: { value:"tri",required:true,validate:validatePin },
intype: { value:"tri" },
debounce: { value:"25" },
read: { value:false }
},
inputs:0,
outputs:1,
icon: "rpi.png",
info: function() {
if ( Object.keys(pinsInUse).length !== 0 ) {
return "**Pins in use** : "+Object.keys(pinsInUse);
}
else { return ""; }
},
label: function() {
var suf = "";
if (this.intype === "up") { suf = "↑ "}
if (this.intype === "down") { suf = "↓ "}
return this.name || "PIN: "+suf+this.pin ;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
outputLabels: function() { return "GPIO"+this.pin; },
oneditprepare: function() {
var pinnow = this.pin;
var pintip = this._("rpi-gpio.tip.pin");
var pinname = this._("rpi-gpio.pinname");
var alreadyuse = this._("rpi-gpio.alreadyuse");
var alreadyset = this._("rpi-gpio.alreadyset");
$.getJSON('rpi-pins/'+this.id,function(data) {
pinsInUse = data || {};
$('#pin-tip').html(pintip + Object.keys(data));
});
for (var i=0; i< validPinValues.length; i++) {
$("#pinform input[value="+validPinValues[i]+"]").on("change", function (evt) {
$("#node-input-pin").val(evt.currentTarget.value);
$("#node-input-pin").removeClass("input-error");
});
}
$("#node-input-pin").on("change", function() {
var pinnew = $("#node-input-pin").val();
if (pinnew && isInt(pinnew) && validPinValues.includes(pinnew)) {
$("#pinform input[value="+pinnew+"]").prop('checked', true);
} else {
uncheckAll();
}
if ((pinnew) && (pinnew !== pinnow)) {
if (pinsInUse.hasOwnProperty(pinnew)) {
RED.notify(pinname+" "+pinnew+" "+alreadyuse,"warn");
}
pinnow = pinnew;
}
});
$("#node-input-intype").on("change", function() {
var newtype = $("#node-input-intype").val();
if ((pinsInUse.hasOwnProperty(pinnow)) && (pinsInUse[pinnow] !== newtype)) {
RED.notify(pinname+" "+pinnow+" "+alreadyset+" "+pinsInUse[pinnow],"error");
}
});
$('#pinform input').on('change', function() {
this.pin = $("#pinform input[type='radio']:checked").val();
$("#node-input-pin").val(this.pin);
});
}
});
</script>
<script type="text/html" data-template-name="rpi-gpio out">
<div class="form-row" style="min-width: 540px">
<label><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.pinname"></span></label>
<div class="rpi-gpio-pinTable">
<div class="pinTableBody" id="pinform">
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower"><label>3.3V Power - 1 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorPower"><label><input disabled type="radio" name="pins" value=""> 2 - 5V Power</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-3">SDA1 - GPIO02 - 3 <input id="pinTable-pin-3" type="radio" name="pins" value="3"></label></div>
<div class="pinTableCellR pinColorPower"><label><input disabled type="radio" name="pins" value=""> 4 - 5V Power</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-5">SCL1 - GPIO03 - 5 <input id="pinTable-pin-5" type="radio" name="pins" value="5"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 6 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-7">GPIO04 - 7 <input id="pinTable-pin-7" type="radio" name="pins" value="7"></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-8"><input id="pinTable-pin-8" type="radio" name="pins" value="8"> 8 - GPIO14 - TxD</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround"><label>Ground - 9 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-10"><input id="pinTable-pin-10" type="radio" name="pins" value="10"> 10 - GPIO15 - RxD</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-11">GPIO17 - 11 <input id="pinTable-pin-11" type="radio" name="pins" value="11"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-12"><input id="pinTable-pin-12" type="radio" name="pins" value="12"> 12 - GPIO18</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-13">GPIO27 - 13 <input id="pinTable-pin-13" type="radio" name="pins" value="13"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 14 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-15">GPIO22 - 15 <input id="pinTable-pin-15" type="radio" name="pins" value="15"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-16"><input id="pinTable-pin-16" type="radio" name="pins" value="16"> 16 - GPIO23</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower"><label>3.3V Power - 17 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-18"><input id="pinTable-pin-18" type="radio" name="pins" value="18"> 18 - GPIO24</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-19">MOSI - GPIO10 - 19 <input id="pinTable-pin-19" type="radio" name="pins" value="19"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 20 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-21">MISO - GPIO09 - 21 <input id="pinTable-pin-21" type="radio" name="pins" value="21"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-22"><input id="pinTable-pin-22" type="radio" name="pins" value="22"> 22 - GPIO25</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-23">SCLK - GPIO11 - 23 <input id="pinTable-pin-23" type="radio" name="pins" value="23"></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-24"><input id="pinTable-pin-24" type="radio" name="pins" value="24"> 24 - GPIO8 - CE0</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround"><label>Ground - 25 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-26"><input id="pinTable-pin-26" type="radio" name="pins" value="26"> 26 - GPIO7 - CE1</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorSD"><label>SD - 27 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorSD"><label><input disabled type="radio" name="pins" value=""> 28 - SC</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-29">GPIO05 - 29 <input id="pinTable-pin-29" type="radio" name="pins" value="29"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 30 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-31">GPIO06 - 31 <input id="pinTable-pin-31" type="radio" name="pins" value="31"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-32"><input id="pinTable-pin-32" type="radio" name="pins" value="32"> 32 - GPIO12</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-33">GPIO13 - 33 <input id="pinTable-pin-33" type="radio" name="pins" value="33"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 34 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-35">GPIO19 - 35 <input id="pinTable-pin-35" type="radio" name="pins" value="35"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-36"><input id="pinTable-pin-36" type="radio" name="pins" value="36"> 36 - GPIO16</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-37">GPIO26 - 37 <input id="pinTable-pin-37" type="radio" name="pins" value="37"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-38"><input id="pinTable-pin-38" type="radio" name="pins" value="38"> 38 - GPIO20</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround"><label>Ground - 39 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-40"><input id="pinTable-pin-40" type="radio" name="pins" value="40"> 40 - GPIO21</label></div>
</div>
</div>
</div>
</div>
<div class="form-row">
<label>&nbsp;&nbsp;&nbsp;&nbsp;</label>
<input type="text" id="node-input-pin" style="width: 352px">
</div>
<div class="form-row" id="node-set-pwm">
<label>&nbsp;&nbsp;&nbsp;&nbsp;<span data-i18n="rpi-gpio.label.type"></span></label>
<select id="node-input-out" style="width: 250px;">
<option value="out" data-i18n="rpi-gpio.digout"></option>
<option value="pwm" data-i18n="rpi-gpio.pwmout"></option>
</select>
</div>
<div class="form-row" id="node-set-tick">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-set" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-set" style="width: 70%;"><span data-i18n="rpi-gpio.label.initpin"></span></label>
</div>
<div class="form-row" id="node-set-state">
<label for="node-input-level">&nbsp;</label>
<select id="node-input-level" style="width: 250px;">
<option value="0" data-i18n="rpi-gpio.initpin0"></option>
<option value="1" data-i18n="rpi-gpio.initpin1"></option>
</select>
</div>
<div class="form-row" id="node-set-freq">
<label for="node-input-freq"> <span data-i18n="rpi-gpio.label.freq"></span></label>
<input type="text" id="node-input-freq" placeholder="100"> Hz
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-tips" id="pin-tip"><span data-i18n="[html]rpi-gpio.tip.pin"></span></div>
<div class="form-tips" id="dig-tip"><span data-i18n="[html]rpi-gpio.tip.dig"></span></div>
<div class="form-tips" id="pwm-tip"><span data-i18n="[html]rpi-gpio.tip.pwm"></span></div>
</script>
<script type="text/javascript">
var bcm2pin = {
"2":"3", "3":"5", "4":"7", "14":"8", "15":"10", "17":"11", "18":"12", "27":"13", "22":"15",
"23":"16", "24":"18", "10":"19", "9":"21", "25":"22", "11":"23", "8":"24", "7":"26",
"5":"29", "6":"31", "12":"32", "13":"33", "19":"35", "16":"36", "26":"37", "20":"38", "21":"40"
};
var pinsInUse = {};
var validPinValues = Object.values(bcm2pin);
var isEnvVar = function (value) {
var re = /^\${([0-9a-zA-Z_]+)}$/;
var match = value.match(re);
return Boolean(match);
};
var isInt = function (value) {
return parseInt(value).toString() === value.trim();
};
var uncheckAll = function() {
for (var i=0; i< validPinValues.length; i++) {
$("#pinform input[value="+validPinValues[i]+"]").prop('checked', false);
}
}
var validatePin = function (value) {
return isEnvVar(value) || (isInt(value) && validPinValues.includes(value));
};
RED.nodes.registerType('rpi-gpio out',{
category: 'Raspberry Pi',
color:"#c6dbef",
defaults: {
name: { value:"" },
pin: { value:"",required:true,validate:validatePin },
set: { value:"" },
level: { value:"0" },
freq: {value:""},
out: { value:"out" }
},
inputs:1,
outputs:0,
icon: "rpi.png",
info: function() {
if ( Object.keys(pinsInUse).length !== 0 ) {
return "**Pins in use** : "+Object.keys(pinsInUse);
}
else { return ""; }
},
align: "right",
label: function() {
if (this.out === "pwm") { return this.name || "PWM: "+this.pin; }
else if (this.out === "ser") { return this.name || "Servo: "+this.pin; }
else {
var suf = "";
if (this.set == true) { suf = (this.level === "1") ? " ¹" : " ₀"; }
return this.name||"PIN: "+ this.pin + suf ;
}
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
inputLabels: function() { return "GPIO"+this.pin; },
oneditprepare: function() {
var pinnow = this.pin;
var pintip = this._("rpi-gpio.tip.pin");
var pinname = this._("rpi-gpio.pinname");
var alreadyuse = this._("rpi-gpio.alreadyuse");
var alreadyset = this._("rpi-gpio.alreadyset");
if (!$("#node-input-out").val()) { $("#node-input-out").val("out"); }
$.getJSON('rpi-pins/'+this.id,function(data) {
pinsInUse = data || {};
$('#pin-tip').html(pintip + Object.keys(data));
});
for (var i=0; i< validPinValues.length; i++) {
$("#pinform input[value="+validPinValues[i]+"]").on("change", function (evt) {
$("#node-input-pin").val(evt.currentTarget.value);
$("#node-input-pin").removeClass("input-error");
});
}
$("#node-input-pin").on("change", function() {
var pinnew = $("#node-input-pin").val();
if (pinnew && isInt(pinnew) && validPinValues.includes(pinnew)) {
$("#pinform input[value="+pinnew+"]").prop('checked', true);
} else {
uncheckAll();
}
if ((pinnew) && (pinnew !== pinnow)) {
if (pinsInUse.hasOwnProperty(pinnew)) {
RED.notify(pinname+" "+pinnew+" "+alreadyuse,"warn");
}
pinnow = pinnew;
}
});
$("#node-input-out").on("change", function() {
var newtype = $("#node-input-out").val();
if ((pinsInUse.hasOwnProperty(pinnow)) && (pinsInUse[pinnow] !== newtype)) {
RED.notify(pinname+" "+pinnow+" "+alreadyset+" "+pinsInUse[pinnow],"error");
}
});
var hidestate = function () {
if ($("#node-input-out").val() === "pwm") {
$('#node-set-tick').hide();
$('#node-set-state').hide();
$('#node-input-set').prop('checked', false);
$("#dig-tip").hide();
$("#pwm-tip").show();
$('#node-set-freq').show();
}
else {
$('#node-set-tick').show();
$("#dig-tip").show();
$("#pwm-tip").hide();
$('#node-set-freq').hide();
}
};
$("#node-input-out").on("change", function () { hidestate(); });
hidestate();
var setstate = function () {
if ($('#node-input-set').is(":checked")) {
$("#node-set-state").show();
} else {
$("#node-set-state").hide();
}
};
$("#node-input-set").on("change", function () { setstate(); });
setstate();
$('#pinform input').on('change', function() {
this.pin = $("#pinform input[type='radio']:checked").val();
$("#node-input-pin").val(this.pin);
});
}
});
</script>
<script type="text/html" data-template-name="rpi-mouse">
<div class="form-row">
<label for="node-input-butt"><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.label.button"></span></label>
<select type="text" id="node-input-butt" style="width: 250px;">
<option value="1" data-i18n="rpi-gpio.left"></option>
<option value="2" data-i18n="rpi-gpio.right"></option>
<option value="4" data-i18n="rpi-gpio.middle"></option>
<option value="7" data-i18n="rpi-gpio.any"></option>
</select>
</div>
<br/>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-mouse',{
category: 'Raspberry Pi',
color:"#c6dbef",
defaults: {
name: { value:"" },
butt: { value:"1",required:true }
},
inputs:0,
outputs:1,
icon: "rpi.png",
label: function() {
var na = this._("rpi-gpio.label.pimouse");
if (this.butt === "1") { na += " "+this._("rpi-gpio.label.left"); }
if (this.butt === "2") { na += " "+this._("rpi-gpio.label.right"); }
if (this.butt === "4") { na += " "+this._("rpi-gpio.label.middle"); }
return this.name||na;
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/html" data-template-name="rpi-keyboard">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-keyboard',{
category: 'Raspberry Pi',
color:"#c6dbef",
defaults: {
name: { value:"" }
},
inputs:0,
outputs:1,
icon: "rpi.png",
label: function() {
return this.name || this._("rpi-gpio.label.pikeyboard");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>

View File

@ -0,0 +1,359 @@
module.exports = function(RED) {
"use strict";
var execSync = require('child_process').execSync;
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var testCommand = __dirname+'/testgpio.py'
var gpioCommand = __dirname+'/nrgpio';
var allOK = true;
try {
execSync(testCommand);
} catch(err) {
allOK = false;
RED.log.warn("rpi-gpio : "+RED._("rpi-gpio.errors.ignorenode"));
}
// 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 (allOK === true) {
if (node.pin !== undefined) {
node.child = spawn(gpioCommand, ["in",node.pin,node.intype,node.debounce]);
node.running = true;
node.status({fill:"yellow",shape:"dot",text:"rpi-gpio.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 (d[i] === '') { return; }
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.finished) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.finished();
}
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);
}
}
else {
node.status({fill:"grey",shape:"dot",text:"rpi-gpio.status.not-available"});
if (node.read === true) {
var val;
if (node.intype == "up") { val = 1; }
if (node.intype == "down") { val = 0; }
setTimeout(function() {
node.send({ topic:"pi/"+node.pin, payload:val });
node.status({fill:"grey",shape:"dot",text:RED._("rpi-gpio.status.na",{value:val})});
},250);
}
}
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.finished = 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, send, done) {
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: "+out); }
if (node.child !== null) {
node.child.stdin.write(out+"\n", () => {
if (done) { done(); }
});
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 (allOK === true) {
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:"yellow",shape:"dot",text:"rpi-gpio.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.finished) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.finished();
}
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);
}
}
else {
node.status({fill:"grey",shape:"dot",text:"rpi-gpio.status.not-available"});
node.on("input", function(msg) {
node.status({fill:"grey",shape:"dot",text:RED._("rpi-gpio.status.na",{value:msg.payload.toString()})});
});
}
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.finished = 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;
if (allOK === true) {
node.child = spawn(gpioCommand+".py", ["mouse",node.butt]);
node.status({fill:"green",shape:"dot",text:"rpi-gpio.status.ok"});
node.child.stdout.on('data', function (data) {
data = Number(data);
if (data !== 0) { 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.finished) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.finished();
}
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.finished = done;
node.child.kill('SIGINT');
node.child = null;
}
else { done(); }
});
}
else {
node.status({fill:"grey",shape:"dot",text:"rpi-gpio.status.not-available"});
}
}
RED.nodes.registerType("rpi-mouse",PiMouseNode);
function PiKeyboardNode(n) {
RED.nodes.createNode(this,n);
var node = this;
var doConnect = function() {
node.child = spawn(gpioCommand+".py", ["kbd","0"]);
node.status({fill:"green",shape:"dot",text:"rpi-gpio.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 (d[i] !== '') {
var b = d[i].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.finished) {
node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
node.finished();
}
else {
node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"});
setTimeout(function() { doConnect(); },2000)
}
});
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); }
});
}
if (allOK === true) {
doConnect();
node.on("close", function(done) {
node.status({});
if (node.child != null) {
node.finished = done;
node.child.kill('SIGINT');
node.child = null;
}
else { done(); }
});
}
else {
node.status({fill:"grey",shape:"dot",text:"rpi-gpio.status.not-available"});
}
}
RED.nodes.registerType("rpi-keyboard",PiKeyboardNode);
var pitype = { type:"" };
if (allOK === true) {
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);
});
}

177
hardware/PiGpio/LICENSE Normal file
View File

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

63
hardware/PiGpio/README.md Normal file
View File

@ -0,0 +1,63 @@
node-red-node-pi-gpio
=====================
A set of <a href="http://nodered.org" target="_new">Node-RED</a> nodes to interact with Pi GPIO using the RPi.GPIO python library that is part of Raspbian.
It also include a simple node that detect mouse buttons and also keyboard clicks. Note: this
picks up mouse keys direct from the keyboard so should work even when the app does not have
focus, but YMMV.
If you need servo control then look at the
<a href="https://flows.nodered.org/node/node-red-node-pi-gpiod">node-red-node-pi-gpiod</a> node
as this is a lot more accurate timing wise, and more suitable for driving servos
## Install
Either use the Node-RED Menu - Manage Palette option to install, or run the following
command in your Node-RED user directory - typically `~/.node-red`
npm i node-red-node-pi-gpio
The python library may also work with other distros running on a Pi (like Ubuntu or Debian) - you will need to install the PIGPIO package and run the following commands in order to gain full access to the GPIO pins as this ability is not part of the default distro. This is NOT necessary on Raspbian.
sudo apt-get install python-pip python-dev
sudo pip install RPi.GPIO
sudo addgroup gpio
sudo chown root:gpio /dev/gpiomem
sudo adduser $USER gpio
echo 'KERNEL=="gpiomem", NAME="%k", GROUP="gpio", MODE="0660"' | sudo tee /etc/udev/rules.d/45-gpio.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
## Usage
**Note:** the pin numbers refer the physical pin numbers on connector P1 as they are easier to locate.
### Input node
Generates a `msg.payload` with either a 0 or 1 depending on the state of the input pin.
##### Outputs
- `msg.payload` - *number* - the level of the pin (0 or 1)
- `msg.topic` - *string* - pi/{the pin number}
You may also enable the input pullup resistor &uarr; or the pulldown resistor &darr;.
### Output node
Can be used in Digital or PWM modes.
##### Input
- `msg.payload` - *number | string*
- Digital - 0, 1 - set pin low or high. (Can also accept boolean `true/false`)
- PWM - 0 to 100 - level from 0 to 100%
*Hint*: The `range` node can be used to scale inputs to the correct values.
Digital mode expects a `msg.payload` with either a 0 or 1 (or true or false),
and will set the selected physical pin high or low depending on the value passed in.
The initial value of the pin at deploy time can also be set to 0 or 1.
When using PWM mode, the input value should be a number 0 - 100, and can be floating point.

View File

@ -0,0 +1,74 @@
<!--
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.
-->
<script type="text/html" data-help-name="rpi-gpio in">
<p> Raspberry Pi-Empfangs-Node. Generiert eine <code>msg.payload</code> mit einem Wert von
0 oder 1 in Abhängigkeit vom Status des Eingabepins. </p>
<h3> Ausgaben </h3>
<dl class="message-properties">
<dt> payload <span class="property-type"> Zahl </span> </dt>
<dd> Die Nutzdaten sind 1 oder 0. </dd>
<dt> topic <span class="property-type"> Zeichenfolge </span> </dt>
<dd> Das Topic wird auf <code>pi/ { the pin number }</code> gesetzt. </dd>
</dl>
<h3> Details </h3>
<p> Sie können auch den Eingangs-Pullup-Widerstand oder den Pulldown-Widerstand aktivieren. </p>
<p> Erfordert die Python-Bibliothek RPi.GPIO in der Version 0.5.10 (oder besser). </p>
</script>
<script type="text/html" data-help-name="rpi-gpio out">
<p> Raspberry Pi-Ausgabe-Node. Kann im Digital- oder PWM-Modus verwendet werden. </p>
<h3> Eingaben </h3>
<dl class="message-properties">
<dt> payload <span class="property-type"> Zahl | Zeichen | Boolean </span> </dt>
</dl>
<h3> Details </h3>
<p> Der digitale Modus erwartet der Node eine <code>msg.payload</code> von entweder 0 oder 1 (oder false oder true)
und schaltet den ausgewählten physischen Pin in Abhängigkeit von dem übergebenen Wert entweder auf LOW oder HIGH. </p>
<p> Der Anfangswert für den Pin kann bei der Implementierung auf 0 oder 1 gesetzt werden. </p>
<p> Im PWM-Modus erwartet der Node einen Eingabewert mit der Zahl 0-100. Es kann ein Gleitkommazahl sein. </p>
<p> Der PWM-Modus kann verwendet werden, um einen Servo unter Verwendung von Eingabewerten nur zwischen 10 und 20 zu steuern,
akzeptiert aber auch Gleitkommawerte. Der GPIO2-Pin eignet sich am besten dafür, da er Hardware-PWM unterstützt. Für eine bessere Servounterstützung
sollten Sie den alternativen Node <pre>Node-red-node-pi-gpiod</pre> berücksichtigen. </p>
<p> Erfordert die Python-Bibliothek RPi.GPIO in der Version 0.5.10 (oder besser). </p>
</script>
<script type="text/html" data-help-name="rpi-mouse">
<p> Raspberry Pi-Maustasten-Node. Erfordert eine USB-Maus. </p>
<h3>Ausgaben </h3>
<dl class="message-properties">
<dt> payload <span class="property-type"> Zahl </span> </dt>
<dd> 1 oder 0, wenn die ausgewählte Maustaste gedrückt und losgelassen wird. </dd>
<dt> Schaltfläche <span class="property-type"> Zahl </span> </dt>
<dd> 1, 2, 4 entsprechend der linken, der rechten und der mittleren Taste, so dass Sie herausfinden können,
welche Schaltfläche oder Kombination gedrückt wurde. </dd>
<dt> topic <span class="property-type"> Zeichenfolge </span> </dt>
<dd>wird auf: <code>pi/mouse</code> gesetzt</dd>
</dl>
</script>
<script type="text/html" data-help-name="rpi-keyboard">
<p>Raspberry Pi Tastatur-Node. Benötigt eine USB Tastatur.</p>
<h3>Ausgaben</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">Zahl</span></dt>
<dd>enthält den Tastaturcode value</dd>
<dt>action <span class="property-type">Zeichenfolge</span></dt>
<dd>wird auf "up", "down", oder "repeat" gesetzt</dd>
<dt>topic <span class="property-type">Zeichenfolge</span></dt>
<dd>wird auf <code>pi/key</code> gesetzt</dd>
</dl>
</script>

View File

@ -0,0 +1,73 @@
"rpi-gpio" : {
"label" : {
"gpiopin" : "GPIO",
"selectpin" : "Auswahlstift",
"resistor" : "Widerstand?",
"readinitial" : "Anfangsstatus des Pins bei Implementierung/Neustart lesen?",
"type" : "Typ",
"initpin" : "Pin-Status initialisieren?",
"debounce" : "Debounce",
"freq" : "Frequenz",
"button" : "Knopf",
"pimouse" : "Pi-Maus",
"pikeyboard" : "Pi-Tastatur",
"left" : "Links",
"right" : "Rechts",
"middle" : "Mitte"
},
"resistor" : {
"none" : "keine",
"pullup" : "pullup",
"pulldown" : "Pulldown"
},
"digout" : "Digitale Ausgabe",
"pwmout" : "PWM-Ausgabe",
"servo" : "Servo-Ausgabe",
"initpin0" : "Anfangsstand des Pin-Niedrig (0)",
"initpin1" : "Anfangsebene von Pin-High (1)",
"left" : "links",
"right" : "rechts",
"middle" : "Mitte",
"any" : "beliebig",
"pinname" : "Pin",
"alreadyuse" : "bereits im Gebrauch",
"alreadyset" : "bereits festgelegt als",
"tip" : {
"pin" : "<b> Verwender Pins </b>: ",
"in" : "Tipp: Es wird nur die digitale Eingabe unterstützt. Die Eingabe muss 0 oder 1 sein.",
"dig" : "Tipp: Für die digitale Ausgabe muss der Wert 0 oder 1 sein.",
"pwm" : "Tipp: Für die PWM-Ausgabe muss der Wert zwischen 0 und 100 liegen; die Einstellung der Hochfrequenz kann mehr CPU beanspruchen als erwartet.",
"ser" : "<b> Tipp </b>: Für die Servo-Ausgabe muss ein Wert zwischen 0 und 100 eingegeben werden. 50 ist das Zentrum."
},
"types" : {
"digout" : "digitale Ausgabe",
"input" : "Eingabe",
"pullup" : "Eingabe mit Pull-up",
"pulldown" : "Eingabe mit Pull-down",
"pwmout" : "PWM-Ausgabe",
"servo" : "Servo-Ausgabe"
},
"status" : {
"stopped" : "Gestoppt",
"closed" : "geschlossen",
"not-running" : "nicht aktiv",
"not-available" : "nicht verfügbar",
"na" : "N/A: __Wert__",
"ok": "OK"
},
"errors" : {
"ignorenode" : "Raspberry Pi-spezifische Nodes inaktiv",
"version" : "Abrufen der Version von Pi fehlgeschlagen",
"sawpitype" : "Saw-Pi-Typ",
"libnotfound" : "Pi RPi.GPIO-Python-Bibliothek nicht gefunden",
"alreadyset" : "GPIO-Stift __pin__ ist bereits als Typ festgelegt: __type__",
"invalidpin" : "Ungültiger GPIO-Pin",
"invalidinput" : "Ungültige Eingabe",
"needtobeexecutable" : "__command__ muss ausführbar sein",
"mustbeexecutable" : "nrgpio muss ausführbar sein",
"commandnotfound" : "Befehl 'nrgpio' nicht gefunden",
"commandnotexecutable" : "nrgpio-Befehl nicht ausführbar",
"error" : "Fehler: __error__",
"pythoncommandnotfound" : "Befehl 'nrgpio python' nicht aktiv"
}
}

View File

@ -0,0 +1,75 @@
<!--
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.
-->
<script type="text/html" data-help-name="rpi-gpio in">
<p>Raspberry Pi input node. Generates a <code>msg.payload</code> with either a
0 or 1 depending on the state of the input pin.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">number</span></dt>
<dd>the payload will be a 1 or a 0.</dd>
<dt>topic <span class="property-type">string</span></dt>
<dd>the topic will be set to <code>pi/{the pin number}</code>.</dd>
</dl>
<h3>Details</h3>
<p>You may also enable the input pullup resistor or the pulldown resistor.</p>
<p>Requires the RPi.GPIO python library version 0.5.10 (or better) in order to work.</p>
</script>
<script type="text/html" data-help-name="rpi-gpio out">
<p>Raspberry Pi output node. Can be used in Digital or PWM modes.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">number | string | boolean</span></dt>
</dl>
<h3>Details</h3>
<p>Digital mode - expects a <code>msg.payload</code> with either a 0 or 1 (or true or false),
and will set the selected physical pin high or low depending on the value passed in.</p>
<p>The initial value of the pin at deploy time can also be set to 0 or 1.</p>
<p>PWM mode - expects an input value of a number 0 - 100. It can be floating point.</p>
<p>PWM mode can be used to drive a servo using input values between 10 and 20 only,
but will accept floating point values.
The GPIO2 pin is best for this as it uses hardware to do the PWM. For better servo support
consider the alternative node-red-node-pi-gpiod node.</p>
<p>Requires the RPi.GPIO python library version 0.5.10 (or better) in order to work.</p>
</script>
<script type="text/html" data-help-name="rpi-mouse">
<p>Raspberry Pi mouse button node. Requires a USB mouse.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">number</span></dt>
<dd>1 or 0 when the selected mouse button is pressed and released.</dd>
<dt>button <span class="property-type">number</span></dt>
<dd>1, 2, 4 corresponding to left, right and middle buttons, so you
can work out which button or combination was pressed.</dd>
<dt>topic <span class="property-type">string</span></dt>
<dd>set to <code>pi/mouse</code></dd>
</dl>
</script>
<script type="text/html" data-help-name="rpi-keyboard">
<p>Raspberry Pi keyboard handling node. Requires a USB keyboard.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">number</span></dt>
<dd>contains the keycode value</dd>
<dt>action <span class="property-type">string</span></dt>
<dd>set to "up", "down", or "repeat"</dd>
<dt>topic <span class="property-type">string</span></dt>
<dd>set to <code>pi/key</code></dd>
</dl>
</script>

View File

@ -0,0 +1,75 @@
{
"rpi-gpio": {
"label": {
"gpiopin": "GPIO",
"selectpin": "select pin",
"resistor": "Resistor?",
"readinitial": "Read initial state of pin on deploy/restart?",
"type": "Type",
"initpin": "Initialise pin state?",
"debounce": "Debounce",
"freq": "Frequency",
"button": "Button",
"pimouse": "Pi Mouse",
"pikeyboard": "Pi Keyboard",
"left": "Left",
"right": "Right",
"middle": "Middle"
},
"resistor": {
"none": "none",
"pullup": "pullup",
"pulldown": "pulldown"
},
"digout": "Digital output",
"pwmout": "PWM output",
"servo": "Servo output",
"initpin0": "initial level of pin - low (0)",
"initpin1": "initial level of pin - high (1)",
"left": "left",
"right": "right",
"middle": "middle",
"any": "any",
"pinname": "Pin",
"alreadyuse": "already in use",
"alreadyset": "already set as",
"tip": {
"pin": "<b>Pins in Use</b>: ",
"in": "Tip: Only Digital Input is supported - input must be 0 or 1.",
"dig": "Tip: For digital output - input must be 0 or 1.",
"pwm": "Tip: For PWM output - input must be between 0 to 100; setting high frequency might occupy more CPU than expected.",
"ser": "<b>Tip</b>: For Servo output - input must be between 0 to 100. 50 is centre."
},
"types": {
"digout": "digital output",
"input": "input",
"pullup": "input with pull up",
"pulldown": "input with pull down",
"pwmout": "PWM output",
"servo": "Servo output"
},
"status": {
"stopped": "stopped",
"closed": "closed",
"not-running": "not running",
"not-available": "not available",
"na": "N/A : __value__",
"ok": "OK"
},
"errors": {
"ignorenode": "Raspberry Pi specific node set inactive",
"version": "Failed to get version from Pi",
"sawpitype": "Saw Pi Type",
"libnotfound": "Cannot find Pi RPi.GPIO python library",
"alreadyset": "GPIO pin __pin__ already set as type: __type__",
"invalidpin": "Invalid GPIO pin",
"invalidinput": "Invalid input",
"needtobeexecutable": "__command__ needs to be executable",
"mustbeexecutable": "nrgpio must to be executable",
"commandnotfound": "nrgpio command not found",
"commandnotexecutable": "nrgpio command not executable",
"error": "error: __error__",
"pythoncommandnotfound": "nrgpio python command not running"
}
}
}

View File

@ -0,0 +1,71 @@
<!--
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.
-->
<script type="text/html" data-help-name="rpi-gpio in">
<p>Raspberry Piの入力ード。入力ピンの状態に応じて、0 または 1 の値を持つ<code>msg.payload</code>を生成します。</p>
<h3>出力</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">数値</span></dt>
<dd>ペイロードには、0 または 1 が設定されます。</dd>
<dt>topic <span class="property-type">文字列</span></dt>
<dd>トピックには、<code>pi/{ピン番号}</code>が設定されます。</dd>
</dl>
<h3>詳細</h3>
<p>入力のプルアップ抵抗またはプルダウン抵抗を有効にすることもできます。</p>
<p>動作にはRPi.GPIO pythonライブラリのバージョン 0.5.10 (またはそれ以上)が必要です。</p>
</script>
<script type="text/html" data-help-name="rpi-gpio out">
<p>Raspberry Piの出力ード。デジタルモードまたはPWMモードで利用できます。</p>
<h3>入力</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">数値 | 文字列 | 真偽値</span></dt>
</dl>
<h3>詳細</h3>
<p>デジタルモード - <code>msg.payload</code>に 0 または 1 (あるいは true または false ) を指定すると、入力値に応じて選択された物理ピンにハイまたはローを設定します。</p>
<p>デプロイ時にピンの初期値として 0 または 1 を設定することもできます。</p>
<p>PWMモード - 入力値に 0 から 100 の数値を指定でき。小数値の指定も可能です。</p>
<p>サーボの制御にPWMモードが利用でき、入力に小数値も含む 10 から 20 の値が指定可能です。
PWMを行うハードウェアを利用していることから、PWMモードの指定にはGPIO2ピンが最も適しています。
より良くサーボの制御を行いたい場合は、node-red-node-pi-gpiod ノードの利用も検討してください。</p>
<p>動作にはRPi.GPIO pythonライブラリのバージョン 0.5.10 (またはそれ以上)が必要です。</p>
</script>
<script type="text/html" data-help-name="rpi-mouse">
<p>Raspberry Pi のマウスボタンード。USBマウスが必要です。</p>
<h3>出力</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">数値</span></dt>
<dd>選択されたマウスのボタンが押された、または離された場合に 1 または 0 が設定されます。</dd>
<dt>button <span class="property-type">数値</span></dt>
<dd>左、右、真ん中のボタンに応じて 1, 2, 4 が設定され、ボタンあるいはボタンの組み合わせに応じた処理ができます。</dd>
<dt>topic <span class="property-type">文字列</span></dt>
<dd><code>pi/mouse</code>が設定されます。</dd>
</dl>
</script>
<script type="text/html" data-help-name="rpi-keyboard">
<p>Raspberry Pi のキーボードを制御するード。USBキーボードが必要です。</p>
<h3>出力</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">数値</span></dt>
<dd>キーコードを含みます。</dd>
<dt>action <span class="property-type">文字列</span></dt>
<dd>"up", "down", または "repeat" が設定されます。</dd>
<dt>topic <span class="property-type">文字列</span></dt>
<dd><code>pi/key</code>が設定されます。</dd>
</dl>
</script>

View File

@ -0,0 +1,75 @@
{
"rpi-gpio": {
"label": {
"gpiopin": "GPIO",
"selectpin": "端子の選択",
"resistor": "抵抗",
"readinitial": "デプロイや再起動時に端子の初期状態を読み込む",
"type": "出力形式",
"initpin": "端子の状態を初期化",
"debounce": "デバウンス",
"freq": "頻度",
"button": "ボタン",
"pimouse": "Pi Mouse",
"pikeyboard": "Pi Keyboard",
"left": "Left",
"right": "Right",
"middle": "Middle"
},
"resistor": {
"none": "なし",
"pullup": "プルアップ",
"pulldown": "プルダウン"
},
"digout": "デジタル出力",
"pwmout": "PWM出力",
"servo": "サーボ出力",
"initpin0": "端子の初期レベル - Low (0)",
"initpin1": "端子の初期レベル - High (1)",
"left": "左",
"right": "右",
"middle": "中間",
"any": "全て",
"pinname": "端子",
"alreadyuse": "使用中",
"alreadyset": "設定済",
"tip": {
"pin": "<b>使用中の端子</b>: ",
"in": "注釈: 入力値は、0または1の数値のみ対応しています。",
"dig": "注釈: 「出力形式」として「デジタル出力」を用いる場合、入力値は0または1の数値である必要があります。",
"pwm": "注釈: 「出力形式」として「PWM出力」を用いる場合、入力値は0100の数値である必要があります。",
"ser": "<b>注釈</b>: サーボ出力向け - 入力値は0100の間である必要があります。50が中心値です。"
},
"types": {
"digout": "デジタル出力",
"input": "入力",
"pullup": "プルアップの入力",
"pulldown": "プルダウンの入力",
"pwmout": "PWM出力",
"servo": "サーボ出力"
},
"status": {
"stopped": "停止",
"closed": "切断",
"not-running": "停止中",
"not-available": "利用不可",
"na": "N/A : __value__",
"ok": "OK"
},
"errors": {
"ignorenode": "Raspberry Pi固有のードを無視しました",
"version": "バージョンコマンドが失敗しました",
"sawpitype": "Saw Pi Type",
"libnotfound": "RPi.GPIO pythonライブラリを見つけられませんでした",
"alreadyset": "GPIO端子 __pin__ は既に出力形式が設定されています: __type__",
"invalidpin": "GPIO端子が不正です",
"invalidinput": "入力が不正です",
"needtobeexecutable": "__command__ は実行可能である必要があります",
"mustbeexecutable": "nrgpio は実行可能である必要があります",
"commandnotfound": "nrgpio コマンドが見つかりません",
"commandnotexecutable": "nrgpio コマンドが実行可能ではありません",
"error": "エラー: __error__",
"pythoncommandnotfound": "nrgpio python コマンドが実行されていません"
}
}
}

View File

@ -0,0 +1,71 @@
<!--
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.
-->
<script type="text/html" data-help-name="rpi-gpio in">
<p>Raspberry Pi의 입력노드. 입력 핀의 상태에 따라, 0 또는 1의 값을 갖는 <code>msg.payload</code>을 생성합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치</span></dt>
<dd>페이로드에는, 0 또는 1이 설정됩니다.</dd>
<dt>topic <span class="property-type">문자열</span></dt>
<dd>토픽에는, <code>pi/{핀 번호}</code>가 설정됩니다.</dd>
</dl>
<h3>상세</h3>
<p>입력의 풀 업 저항 또는, 풀 다운 저항을 유효화 할 수도 있습니다.</p>
<p>작동하려면 RPi.GPIO python라이브러리 버젼0.5.10 (또는 그 이상)이 필요합니다.</p>
</script>
<script type="text/html" data-help-name="rpi-gpio out">
<p>Raspberry Pi의 출력노드. 디지털모드 또는 PWM모드에서 이용할 수 있습니다.</p>
<h3>입력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치 | 문자열 | 진위값</span></dt>
</dl>
<h3>상세</h3>
<p>디지털모드 - <code>msg.payload</code>에 0 또는 1 (혹은 true 또는 false) 을 지정하면, 입력값에 따라 선택된 물리핀에 high 또는 low를 설정합니다.</p>
<p>배포시에 핀의 초기값으로 0 또는 1을 설정할 수도 있습니다.</p>
<p>PWM모드 - 입력값에 0에서 100의 수치를 지정할수 있고, 소수값도 지정할수 있습니다.</p>
<p>서보제어에 PWM모드를 이용할수 있으며, 입력에 소수값을 포함한 10에서 20의 값을 지정할 수 있습니다.
PWM를 실행하는 하드웨어를 이용하여, PWM모드의 지정에는 GPIO2핀이 가장 적합합니다.
보다 좋은 서보제어를 원하는 경우에는, node-red-node-pi-gpiod 노드를 이용할 것을 검토해 주십시오.</p>
<p>작동하려면 RPi.GPIO python라이브러리 버젼0.5.10 (또는 그 이상)이 필요합니다.</p>
</script>
<script type="text/html" data-help-name="rpi-mouse">
<p>Raspberry Pi 의 마우스버튼노드. USB마우스가 필요합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치</span></dt>
<dd>선택된 마우스의 버튼이 눌려지거나 떨어졌을 경우에 1 또는 0이 설정됩니다.</dd>
<dt>button <span class="property-type">수치</span></dt>
<dd>좌, 우, 중앙 버튼에 따라 1, 2, 4 가 설정되어, 버튼 혹은 버튼의 조합에 따른 처리를 할 수 있습니다.</dd>
<dt>topic <span class="property-type">문자열</span></dt>
<dd><code>pi/mouse</code>이 설정됩니다.</dd>
</dl>
</script>
<script type="text/html" data-help-name="rpi-keyboard">
<p>Raspberry Pi 의 키보드를 제어하는 노드. USB키보드가 필요합니다.</p>
<h3>출력</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">수치</span></dt>
<dd>키 코드를 포함합니다.</dd>
<dt>action <span class="property-type">문자열</span></dt>
<dd>"up", "down", 또는 "repeat" 이 설정됩니다.</dd>
<dt>topic <span class="property-type">문자열</span></dt>
<dd><code>pi/key</code>가 설정됩니다.</dd>
</dl>
</script>

View File

@ -0,0 +1,73 @@
"rpi-gpio": {
"label": {
"gpiopin": "GPIO",
"selectpin": "단자의 선택",
"resistor": "저항",
"readinitial": "배포나 재시작시에 단자의 초기상태를 불러옴",
"type": "출력형식",
"initpin": "단자의 상태를 초기화",
"debounce": "디바운스",
"freq": "빈도",
"button": "버튼",
"pimouse": "Pi Mouse",
"pikeyboard": "Pi Keyboard",
"left": "Left",
"right": "Right",
"middle": "Middle"
},
"resistor": {
"none": "없음",
"pullup": "풀 업",
"pulldown": "풀 다운"
},
"digout": "디지털 출력",
"pwmout": "PWM 출력",
"servo": "서보 출력",
"initpin0": "단자의 초기레벨 - Low (0)",
"initpin1": "단자의 초기레벨 - High (1)",
"left": "좌",
"right": "우",
"middle": "중간",
"any": "모두",
"pinname": "단자",
"alreadyuse": "사용중",
"alreadyset": "설정됨",
"tip": {
"pin": "<b>사용중인 단자</b>: ",
"in": "주석: 입력값은, 0 혹은 1의 수치만 대응하고 있습니다.",
"dig": "주석: ’출력형식’으로 ’디지털출력’을 사용하는 경우, 입력값은 0 혹은 1의 수치일 필요가 있습니다.",
"pwm": "주석: ’출력형식’으로 PWM출력을 사용하는 경우, 입력값은 0100의 수치일 필요가 있습니다.",
"ser": "<b>주석</b>: 서보 출력용 - 입력값은 0100 사이일 필요가 있습니다. 50이 중심값입니다."
},
"types": {
"digout": "디지털 출력",
"input": "입력",
"pullup": "풀 업 입력",
"pulldown": "풀 다운 입력",
"pwmout": "PWM 출력",
"servo": "서보 출력"
},
"status": {
"stopped": "정지",
"closed": "절단",
"not-running": "정지중",
"not-available": "이용불가",
"na": "N/A : __value__",
"ok": "OK"
},
"errors": {
"ignorenode": "Raspberry Pi고유의 노드를 무시했습니다",
"version": "버젼커맨드에 실패했습니다",
"sawpitype": "Saw Pi Type",
"libnotfound": "RPi.GPIO python라이브러리를 발견하지 못했습니다",
"alreadyset": "GPIO단자 __pin__ 은 이미 출력형식이 설정되어 있습니다: __type__",
"invalidpin": "GPIO단자가 올바르지 않습니다",
"invalidinput": "입력이 올바르지 않습니다",
"needtobeexecutable": "__command__ 은 실행가능상태일 필요가 있습니다 ",
"mustbeexecutable": "nrgpio 은 실행가능상태일 필요가 있습니다 ",
"commandnotfound": "nrgpio 커맨드를 찾을수 없습니다",
"commandnotexecutable": "nrgpio 커맨드가 실행가능상태가 아닙니다",
"error": "에러: __error__",
"pythoncommandnotfound": "nrgpio python 커맨드가 실행되지 않았습니다"
}
}

17
hardware/PiGpio/nrgpio Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
#
# 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)
python -u $BASEDIR/nrgpio.py $@

244
hardware/PiGpio/nrgpio.py Executable file
View File

@ -0,0 +1,244 @@
#!/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 RPi.GPIO as GPIO
import struct
import sys
import os
import subprocess
from time import sleep
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
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 = raw_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:
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 = raw_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:
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 '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):
if bounce > 0:
sleep(bounce/1000.0)
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)
if bounce > 0:
GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback, bouncetime=int(bounce))
else :
GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback)
sleep(0.1)
print(GPIO.input(pin))
while True:
try:
data = raw_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 = raw_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 = raw_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 = ord( 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"):
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
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)
elif len(sys.argv) > 1:
cmd = sys.argv[1].lower()
if cmd == "rev":
print(GPIO.RPI_REVISION)
elif cmd == "ver":
print(GPIO.VERSION)
elif cmd == "info":
print(GPIO.RPI_INFO)
else:
print("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}")
print(" only ver (gpio version) and info (board information) accept no pin parameter.")
else:
print("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}")

View File

@ -0,0 +1,28 @@
{
"name": "node-red-node-pi-gpio",
"version": "1.2.3",
"description": "The basic Node-RED node for Pi GPIO",
"dependencies" : {
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red-nodes/tree/master/hardware/PiGpio"
},
"keywords": [
"node-red", "Pi", "GPIO", "PiGpio"
],
"author": {
"name": "Dave Conway-Jones",
"email": "ceejay@vnet.ibm.com",
"url": "http://nodered.org"
},
"license": "Apache-2.0",
"node-red" : {
"nodes": {
"rpi-gpio": "36-rpi-gpio.js"
}
}
}

7
hardware/PiGpio/testgpio.py Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/python
import sys
try:
import RPi.GPIO as GPIO
sys.exit(0)
except ImportError:
sys.exit(1)

View File

@ -1,4 +1,4 @@
Copyright 2016 JS Foundation and other contributors, https://js.foundation/
Copyright 2016,2020 JS Foundation and other contributors, https://js.foundation/
Copyright 2013-2016 IBM Corp.
Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,18 +1,16 @@
node-red-node-pilcd
===================
# node-red-node-pilcd
A <a href="http://nodered.org" target="_new">Node-RED</a> node for a Raspberry Pi
to write to a GPIO connected HD44780 style LCD panels.
Install
-------
## Install
Run the following command in your Node-RED user directory - typically `~/.node-red`
Either use the Node-RED Menu - Manage Palette option to install, or run the following
command in your Node-RED user directory - typically `~/.node-red`
npm install node-red-node-pilcd
Usage
-----
## Usage
Raspberry Pi output to HD44780 style LCD module - typically 1, 2, or 4 lines.

View File

@ -0,0 +1,9 @@
<script type="text/html" data-help-name="rpi-lcd">
<p>Raspberry Pi output to a HD44780 style LCD. Usually 1, 2, or 4 lines of characters.</p>
<p>Expects a <code>msg.payload</code> with a string in it.</p>
<p>Strings for the second line of the display must start <b>2:</b> - the third start <b>3:</b> - and the fourth <b>4:</b></p>
<p>To clear the display send the string <b>clr:</b></p>
<p>It is up to you to manage string lengths to suit the display.</p>
<p>Requires the RPi.GPIO python library version 0.5.8 (or better) in order to work.</p>
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
</script>

View File

@ -0,0 +1,19 @@
{
"pilcd": {
"label": {
"pins": "Pins"
},
"tip": {
"tip": "<b>Tip</b>: Pins MUST be a comma separated list of the 6 GPIO connector pin numbers that are connected to the RS, E, D4, D5, D6 and D7 pins of the LCD."
},
"status": {
"not-available": "not available",
"na": "N/A : __value__"
},
"errors": {
"ignorenode": "Raspberry Pi specific node set inactive",
"libnotfound": "Cannot find Pi RPi.GPIO python library",
"needtobeexecutable": "__command__ needs to be executable"
}
}
}

View File

@ -0,0 +1,9 @@
<script type="text/html" data-help-name="rpi-lcd">
<p>Raspberry PiがHD44780スタイルのLCDへ、通常1、2、4行の文字列を表示します。</p>
<p>本ノードは、文字列型の<code>msg.payload</code>を受け付けます。</p>
<p>ディスプレイの2行目に文字列を表示するには、<b>2:</b>から始まる文字列にします。3行目の場合は<b>3:</b>、4行目の場合は<b>4:</b>です。</p>
<p>表示をクリアするには、文字列<b>clr:</b>を送信します。</p>
<p>ディスプレイに合わせて文字列の長さを調整するのは、あなた自身で行ってください。</p>
<p>このードを動作させるには、RPi.GPIO Pythonライブラリバージョン0.5.8(またはそれ以上)が必要です。</p>
<p><b>注釈:</b> 位置付けしやすい様に、コネクタP1の実際の物理ピン番号を使用しています。</p>
</script>

View File

@ -0,0 +1,19 @@
{
"pilcd": {
"label": {
"pins": "ピン"
},
"tip": {
"tip": "<b>注釈</b>: LCDのRS、E、D4、D5、D6、およびD7ピンに接続されている6つのGPIOコネクタのピン番号を、コンマで区切りでピンに入力する必要があります。"
},
"status": {
"not-available": "利用不可",
"na": "N/A : __value__"
},
"errors": {
"ignorenode": "Raspberry Pi固有のードを無視しました",
"libnotfound": "RPi.GPIO pythonライブラリを見つけられませんでした",
"needtobeexecutable": "__command__ は実行可能である必要があります"
}
}
}

View File

@ -11,7 +11,13 @@
import RPi.GPIO as GPIO
import time
import sys
import os, select
import os
import select
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
# Turn off warnings if you run it a second time...
GPIO.setwarnings(False)
@ -31,21 +37,22 @@ LCD_CMD = False
LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line
LCD_LINE_3 = 0xA0 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xE0 # LCD RAM address for the 4th line
LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line
# Timing constants
E_PULSE = 0.00005
E_DELAY = 0.00005
E_PULSE = 0.0005
E_DELAY = 0.0005
def lcd_init():
# Initialise display
lcd_byte(0x33,LCD_CMD)
lcd_byte(0x32,LCD_CMD)
lcd_byte(0x28,LCD_CMD)
lcd_byte(0x0C,LCD_CMD)
lcd_byte(0x06,LCD_CMD)
lcd_byte(0x28,LCD_CMD)
lcd_byte(0x01,LCD_CMD)
time.sleep(E_DELAY)
def lcd_string(message):
# Send string to display
@ -106,8 +113,8 @@ def lcd_byte(bits, mode):
if len(sys.argv) > 1:
pins = sys.argv[1].lower().split(',')
if len(pins) != 6:
print "Bad number of pins supplied"
print " "+pins
print("Bad number of pins supplied")
print(" "+pins)
sys.exit(0)
LCD_RS = int(pins[0])
@ -117,6 +124,7 @@ if len(sys.argv) > 1:
LCD_D6 = int(pins[4])
LCD_D7 = int(pins[5])
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD) # Use GPIO BOARD numbers
GPIO.setup(LCD_RS, GPIO.OUT) # RS
GPIO.setup(LCD_E, GPIO.OUT) # E
@ -179,6 +187,6 @@ if len(sys.argv) > 1:
sys.exit(0)
else:
print "Bad params"
print " sudo nrlcd.py RS,E,D4,D5,D6,D7"
print("Bad params")
print(" sudo nrlcd.py RS,E,D4,D5,D6,D7")
sys.exit(0)

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-pilcd",
"version" : "0.0.6",
"version" : "0.1.0",
"description" : "A Node-RED node for Raspberry Pi to write to HD44780 style LCD panels.",
"dependencies" : {
},

View File

@ -1,26 +1,14 @@
<script type="text/x-red" data-template-name="rpi-lcd">
<script type="text/html" data-template-name="rpi-lcd">
<div class="form-row">
<label for="node-input-pins"><i class="fa fa-circle"></i> Pins</label>
<label for="node-input-pins"><i class="fa fa-circle"></i> <span data-i18n="pilcd.label.pins"></label>
<input type="text" id="node-input-pins" placeholder="RS,E,D4,D5,D6,D7">
</div>
<br/>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-tips"><b>Tip</b>: Pins MUST be a comma separated list of the 6 GPIO
connector pin numbers that are connected to the RS, E, D4, D5, D6 and D7 pins of the LCD.</div>
</script>
<script type="text/x-red" data-help-name="rpi-lcd">
<p>Raspberry Pi output to a HD44780 style LCD. Usually 1, 2, or 4 lines of characters.</p>
<p>Expects a <code>msg.payload</code> with a string in it.</p>
<p>Strings for the second line of the display must start <b>2:</b> - the third start <b>3:</b> - and the fourth <b>4:</b></p>
<p>To clear the display send the string <b>clr:</b></p>
<p>It is up to you to manage string lengths to suit the display.</p>
<p>Requires the RPi.GPIO python library version 0.5.8 (or better) in order to work.</p>
<p><b>Note:</b> we are using the actual physical pin numbers on connector P1 as they are easier to locate.</p>
<div class="form-tips" data-i18n="[html]pilcd.tip.tip"></div>
</script>
<script type="text/javascript">

View File

@ -6,20 +6,26 @@ module.exports = function(RED) {
var fs = require('fs');
var gpioCommand = __dirname + '/nrlcd';
var allOK = true;
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.";
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
if (cpuinfo.indexOf(": BCM") === -1) {
RED.log.warn("rpi-lcd : "+RED._("pilcd.errors.ignorenode"));
allOK = false;
}
else if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
RED.log.warn("rpi-lcd : "+RED._("pilcd.errors.libnotfound"));
allOK = false;
}
else if (!(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]))) {
RED.log.warn("rpi-lcd : "+RED._("pilcd.errors.needtobeexecutable",{command:gpioCommand}));
allOK = false;
}
}
if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
util.log("[rpi-lcd] Info : Can't find Pi RPi.GPIO python library.");
throw "Warning : Can't find Pi RPi.GPIO python library.";
}
if (!(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]))) {
util.log("[rpi-lcd] Error : " + gpioCommand + " needs to be executable.");
throw "Error : " + gpioCommand + " must to be executable.";
catch(err) {
RED.log.warn("rpi-lcd : "+RED._("pilcd.errors.ignorenode"));
allOK = false;
}
function PiLcdNode(n) {
@ -33,54 +39,61 @@ module.exports = function(RED) {
else { node.warn("Command not running"); }
}
if (node.pins !== undefined) {
node.child = spawn(gpioCommand, [node.pins]);
node.running = true;
if (RED.settings.verbose) { node.log("pin: " + node.pins + " :"); }
node.on("input", inputlistener);
if (allOK === true) {
if (node.pins !== undefined) {
node.child = spawn(gpioCommand, [node.pins]);
node.running = true;
if (RED.settings.verbose) { node.log("pin: " + node.pins + " :"); }
node.on("input", inputlistener);
node.child.stdout.on('data', function(data) {
if (RED.settings.verbose) { node.log("out: " + data + " :"); }
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.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 pins: " + node.pin);
}
var wfi = function(done) {
if (!node.running) {
if (RED.settings.verbose) { node.log("end"); }
done();
return;
}
setTimeout(function() { wfi(done); }, 333);
}
node.on("close", function(done) {
if (node.child != null) {
node.child.stdin.write("c:lose" + node.pin);
node.child.kill('SIGKILL');
}
wfi(done);
});
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.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 pins: " + node.pin);
node.status({fill:"grey",shape:"dot",text:"pilcd.status.not-available"});
node.on("input", function(msg){
node.status({fill:"grey",shape:"dot",text:RED._("pilcd.status.na",{value:msg.payload.toString()})});
});
}
var wfi = function(done) {
if (!node.running) {
if (RED.settings.verbose) { node.log("end"); }
done();
return;
}
setTimeout(function() { wfi(done); }, 333);
}
node.on("close", function(done) {
if (node.child != null) {
node.child.stdin.write("c:lose" + node.pin);
node.child.kill('SIGKILL');
}
wfi(done);
});
}
RED.nodes.registerType("rpi-lcd", PiLcdNode);
}

View File

@ -6,20 +6,26 @@ module.exports = function(RED) {
var fs = require('fs');
var gpioCommand = __dirname+'/nrgpio.py';
var allOK = true;
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.";
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
if (cpuinfo.indexOf(": BCM") === -1) {
RED.log.warn("rpi-liter : "+RED._("node-red:rpi-gpio.errors.ignorenode"));
allOK = false;
}
else if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
RED.log.warn("rpi-liter : "+RED._("node-red:rpi-gpio.errors.libnotfound"));
allOK = false;
}
else if (!(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]))) {
RED.log.warn("rpi-liter : "+RED._("node-red:rpi-gpio.errors.needtobeexecutable",{command:gpioCommand}));
allOK = false;
}
}
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.";
}
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 : " + gpioCommand + " must to be executable.";
catch(err) {
RED.log.warn("rpi-liter : "+RED._("node-red:rpi-gpio.errors.ignorenode"));
allOK = false;
}
function PiLiter(n) {
@ -28,75 +34,80 @@ module.exports = function(RED) {
this.dir = (n.dir ? 1 : 0) || 0;
var node = this;
if (this.pinv === "bar") {
node.child = spawn(gpioCommand, ["byte",node.dir]);
node.on("input", function(msg) {
var out = Number(msg.payload);
if ((out >= 1) && (out <= 8)) { out = Math.pow(2, out) - 1; }
else { out = 0; }
if (node.child !== null) { node.child.stdin.write(out+"\n"); }
else { node.warn("Command not running"); }
});
}
else if (this.pinv === "meter") {
node.child = spawn(gpioCommand, ["byte",node.dir]);
node.on("input", function(msg) {
var out = Number(msg.payload);
if ((out >= 1) && (out <= 8)) { out = Math.pow(2, (out-1)); }
else { out = 0; }
if (node.child !== null) { node.child.stdin.write(out+"\n"); }
else { node.warn("Command not running"); }
});
}
else if (this.pinv === "all") {
node.child = spawn(gpioCommand, ["byte",node.dir]);
node.on("input", function(msg) {
var out = msg.payload;
if ((out === 1)|(out === true)|(out === "1")|(out === "on")) {
out = 255;
}
else { out = 0; }
if (node.child !== null) { node.child.stdin.write(out+"\n"); }
else { node.warn("Command not running"); }
});
}
else if (this.pinv === "pin") {
node.child = spawn(gpioCommand, ["byte",node.dir]);
var byte = 0;
node.on("input", function(msg) {
if (typeof msg.payload === "object") {
var out = Number(msg.payload.led);
var l = Number(msg.payload.state);
if ((out >= 1) && (out <= 8)) {
out = (Math.pow(2, (out-1)));
if (l === 0) { byte = (byte & (~out) & 255); }
else { byte = (byte | out) & 255; }
if (node.child !== null) { node.child.stdin.write(byte+"\n"); }
else { node.warn("Command not running"); }
if (allOK === true) {
if (this.pinv === "bar") {
node.child = spawn(gpioCommand, ["byte",node.dir]);
node.on("input", function(msg) {
var out = Number(msg.payload);
if ((out >= 1) && (out <= 8)) { out = Math.pow(2, out) - 1; }
else { out = 0; }
if (node.child !== null) { node.child.stdin.write(out+"\n"); }
else { node.warn("Command not running"); }
});
}
else if (this.pinv === "meter") {
node.child = spawn(gpioCommand, ["byte",node.dir]);
node.on("input", function(msg) {
var out = Number(msg.payload);
if ((out >= 1) && (out <= 8)) { out = Math.pow(2, (out-1)); }
else { out = 0; }
if (node.child !== null) { node.child.stdin.write(out+"\n"); }
else { node.warn("Command not running"); }
});
}
else if (this.pinv === "all") {
node.child = spawn(gpioCommand, ["byte",node.dir]);
node.on("input", function(msg) {
var out = msg.payload;
if ((out === 1)|(out === true)|(out === "1")|(out === "on")) {
out = 255;
}
else { out = 0; }
if (node.child !== null) { node.child.stdin.write(out+"\n"); }
else { node.warn("Command not running"); }
});
}
else if (this.pinv === "pin") {
node.child = spawn(gpioCommand, ["byte",node.dir]);
var byte = 0;
node.on("input", function(msg) {
if (typeof msg.payload === "object") {
var out = Number(msg.payload.led);
var l = Number(msg.payload.state);
if ((out >= 1) && (out <= 8)) {
out = (Math.pow(2, (out-1)));
if (l === 0) { byte = (byte & (~out) & 255); }
else { byte = (byte | out) & 255; }
if (node.child !== null) { node.child.stdin.write(byte+"\n"); }
else { node.warn("Command not running"); }
}
else { node.warn("Not a valid object - see Info panel."); }
}
else { node.warn("Not a valid object - see Info panel."); }
});
}
else {
node.child = spawn(gpioCommand, ["byte",node.dir]);
node.on("input", function(msg) {
var out = Number(msg.payload);
if ((out >= 0) && (out <= 255)) {
if (node.child !== null) { node.child.stdin.write(out+"\n"); }
else { node.warn("Command not running"); }
}
else { node.warn("Invalid input - not between 0 and 255"); }
});
}
node.on("close", function() {
if (node.child != null) {
node.child.kill('SIGKILL');
}
else { node.warn("Not a valid object - see Info panel."); }
if (RED.settings.verbose) { node.log("end"); }
});
}
else {
node.child = spawn(gpioCommand, ["byte",node.dir]);
node.on("input", function(msg) {
var out = Number(msg.payload);
if ((out >= 0) && (out <= 255)) {
if (node.child !== null) { node.child.stdin.write(out+"\n"); }
else { node.warn("Command not running"); }
}
else { node.warn("Invalid input - not between 0 and 255"); }
});
node.status({fill:"grey",shape:"dot",text:"node-red:rpi-gpio.status.not-available"});
}
node.on("close", function() {
if (node.child != null) {
node.child.kill('SIGKILL');
}
if (RED.settings.verbose) { node.log("end"); }
});
}
RED.nodes.registerType("rpi-liter",PiLiter);
}

View File

@ -4,11 +4,12 @@
import RPi.GPIO as GPIO
import sys
bounce = 20 # bounce time in mS to apply
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
if sys.version_info >= (3,0):
print("Sorry - currently only configured to work with python 2.x")
sys.exit(1)
bounce = 20 # bounce time in mS to apply
if len(sys.argv) > 1:
cmd = sys.argv[1].lower()
@ -17,7 +18,7 @@ if len(sys.argv) > 1:
GPIO.setwarnings(False)
if cmd == "pwm":
#print "Initialised pin "+str(pin)+" to PWM"
#print("Initialised pin "+str(pin)+" to PWM")
GPIO.setup(pin,GPIO.OUT)
p = GPIO.PWM(pin, 100)
p.start(0)
@ -32,10 +33,10 @@ if len(sys.argv) > 1:
GPIO.cleanup(pin)
sys.exit(0)
except Exception as ex:
print "bad data: "+data
print("bad data: "+data)
elif cmd == "buzz":
#print "Initialised pin "+str(pin)+" to Buzz"
#print("Initialised pin "+str(pin)+" to Buzz")
GPIO.setup(pin,GPIO.OUT)
p = GPIO.PWM(pin, 100)
p.stop()
@ -54,10 +55,10 @@ if len(sys.argv) > 1:
GPIO.cleanup(pin)
sys.exit(0)
except Exception as ex:
print "bad data: "+data
print("bad data: "+data)
elif cmd == "out":
#print "Initialised pin "+str(pin)+" to 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]))
@ -78,9 +79,9 @@ if len(sys.argv) > 1:
GPIO.output(pin,data)
elif cmd == "in":
#print "Initialised pin "+str(pin)+" to IN"
#print("Initialised pin "+str(pin)+" to IN")
def handle_callback(chan):
print GPIO.input(chan)
print(GPIO.input(chan))
if len(sys.argv) == 4:
if sys.argv[3].lower() == "up":
@ -91,7 +92,7 @@ if len(sys.argv) > 1:
GPIO.setup(pin,GPIO.IN)
else:
GPIO.setup(pin,GPIO.IN)
print GPIO.input(pin)
print(GPIO.input(pin))
GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback, bouncetime=bounce)
while True:
@ -104,7 +105,7 @@ if len(sys.argv) > 1:
sys.exit(0)
elif cmd == "byte":
#print "Initialised BYTE mode - "+str(pin)+
#print("Initialised BYTE mode - "+str(pin)+)
list = [7,11,13,12,15,16,18,22]
GPIO.setup(list,GPIO.OUT)
@ -127,7 +128,7 @@ if len(sys.argv) > 1:
GPIO.output(list[bit], data & mask)
elif cmd == "borg":
#print "Initialised BORG mode - "+str(pin)+
#print("Initialised BORG mode - "+str(pin)+)
GPIO.setup(11,GPIO.OUT)
GPIO.setup(13,GPIO.OUT)
GPIO.setup(15,GPIO.OUT)
@ -154,10 +155,10 @@ if len(sys.argv) > 1:
data = 0
elif cmd == "rev":
print GPIO.RPI_REVISION
print(GPIO.RPI_REVISION)
elif cmd == "ver":
print GPIO.VERSION
print(GPIO.VERSION)
elif cmd == "mouse": # catch mice button events
file = open( "/dev/input/mice", "rb" )
@ -171,7 +172,7 @@ if len(sys.argv) > 1:
button = ord( buf[0] ) & pin # mask out just the required button(s)
if button != oldbutt: # only send if changed
oldbutt = button
print button
print(button)
while True:
try:
@ -181,4 +182,4 @@ if len(sys.argv) > 1:
sys.exit(0)
else:
print "Bad parameters - in|out|pwm|buzz|byte|borg|mouse|ver pin {value|up|down}"
print("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|ver pin {value|up|down}")

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-piliter",
"version" : "0.0.11",
"version" : "0.0.14",
"description" : "A Node-RED node to drive a Raspberry Pi Pi-LITEr 8 LED board.",
"dependencies" : {
},

View File

@ -8,6 +8,11 @@ import RPi.GPIO as GPIO
import time
import sys
import os, select
import signal
def signal_handler(sig, frame):
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
# Turn off warnings if you run it a second time...
GPIO.setwarnings(False)
@ -16,61 +21,52 @@ ECHO = 0
TRIGGER = 0
OLD = 0
SLEEP = 0.5
MAX_DIST = 400
def Measure():
start = 0
realstart = time.time()
GPIO.output(TRIGGER, True)
time.sleep(0.00001)
GPIO.output(TRIGGER, False)
channel = GPIO.wait_for_edge(ECHO, GPIO.BOTH, timeout=200)
if channel is None:
print("Ultrasonic sensor timed out (pre-echo).")
GPIO.remove_event_detect(ECHO)
restart()
# else:
# print("Echo start detected")
start = time.time()
GPIO.wait_for_edge(ECHO, GPIO.BOTH, timeout=400)
if channel is None:
print("Ultrasonic sensor timed out (post-echo).")
GPIO.remove_event_detect(ECHO)
restart()
# else:
# print("Echo finish detected")
stop = time.time()
elapsed = stop-start
distance = (elapsed * 34300)/2 # Using speed of sound at 20C (68F)
while GPIO.input(ECHO)==0:
start = time.time()
Dif = time.time() - realstart
if Dif > 0.2:
# print("Ultrasonic Sensor Timed out, Restart.")
time.sleep(0.4)
return 400
while GPIO.input(ECHO)==1:
stop = time.time()
Dif = time.time() - realstart
if Dif > 0.4:
print("Ultrasonic Sensor Timed out, Restarting.")
time.sleep(0.2)
return 400
elapsed = stop - start
distance = (elapsed * 36000)/2
return distance
def restart():
# print("Restarting...")
GPIO.setmode(GPIO.BOARD) # Use GPIO BOARD numbers
GPIO.setup(TRIGGER, GPIO.OUT) # Trigger
GPIO.output(TRIGGER, False) # Set low
GPIO.setup(ECHO, GPIO.OUT) # Echo
GPIO.output(ECHO, False)
time.sleep(0.1)
GPIO.setup(ECHO,GPIO.IN)
GPIO.add_event_detect(ECHO, GPIO.BOTH)
time.sleep(2.0)
# Main program loop
if len(sys.argv) > 1:
pins = sys.argv[1].lower().split(',')
if len(pins) != 3:
print "Bad parameters supplied"
print pins
print("Bad parameters supplied")
print(pins)
sys.exit(0)
TRIGGER = int(pins[0])
ECHO = int(pins[1])
SLEEP = float(pins[2])
restart()
GPIO.setmode(GPIO.BOARD) # Use GPIO BOARD numbers
GPIO.setup(TRIGGER, GPIO.OUT) # Trigger
GPIO.output(TRIGGER, False)
GPIO.setup(ECHO, GPIO.OUT) # Echo
GPIO.output(ECHO, False)
GPIO.setup(ECHO,GPIO.IN)
# Flush stdin so we start clean
while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0])>0:
@ -83,14 +79,10 @@ if len(sys.argv) > 1:
print(distance)
OLD = distance
time.sleep(SLEEP)
except Exception as e: # try to clean up on exit
print(e) # Print error message on exception
GPIO.remove_event_detect(ECHO)
GPIO.cleanup(TRIGGER)
GPIO.cleanup(ECHO)
sys.exit(0)
except: # try to clean up on exit
print("0.0")
else:
print "Bad params"
print " sudo nrsrf.py trigger_pin,echo_pin,rate_in_seconds"
print("Bad params")
print(" nrsrf.py trigger_pin, echo_pin, rate_in_seconds")
sys.exit(0)

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-pisrf",
"version" : "0.1.0",
"version" : "0.1.4",
"description" : "A Node-RED node for a Raspberry Pi to use a SRF04 or SRF05 range finder",
"dependencies" : {
},

View File

@ -6,20 +6,26 @@ module.exports = function(RED) {
var fs = require('fs');
var gpioCommand = __dirname + '/nrsrf.py';
var allOK = true;
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.";
try {
var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
if (cpuinfo.indexOf(": BCM") === -1) {
RED.log.warn("rpi-srf : "+RED._("node-red:rpi-gpio.errors.ignorenode"));
allOK = false;
}
else if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
RED.log.warn("rpi-srf : "+RED._("node-red:rpi-gpio.errors.libnotfound"));
allOK = false;
}
else if (!(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]))) {
RED.log.warn("rpi-srf : "+RED._("node-red:rpi-gpio.errors.needtobeexecutable",{command:gpioCommand}));
allOK = false;
}
}
if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
util.log("[rpi-srf] Info : Can't find Pi RPi.GPIO python library.");
throw "Warning : Can't find Pi RPi.GPIO python library.";
}
if (!(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]))) {
util.log("[rpi-srf] Error : " + gpioCommand + " needs to be executable.");
throw "Error : " + gpioCommand + " must to be executable.";
catch(err) {
RED.log.warn("rpi-srf : "+RED._("node-red:rpi-gpio.errors.ignorenode"));
allOK = false;
}
function PiSrfNode(n) {
@ -29,56 +35,60 @@ module.exports = function(RED) {
this.pins += ","+(n.pulse || 0.5);
var node = this;
if (node.pins !== undefined) {
node.child = spawn(gpioCommand, [node.pins]);
node.running = true;
if (RED.settings.verbose) { node.log("parameters: " + node.pins + " :"); }
if (allOK === true) {
if (node.pins !== undefined) {
node.child = spawn(gpioCommand, [node.pins]);
node.running = true;
if (RED.settings.verbose) { node.log("parameters: " + node.pins + " :"); }
node.child.stdout.on('data', function(data) {
if (RED.settings.verbose) { node.log("out: " + data + " :"); }
data = data.toString().trim();
if (data.length > 0) {
node.send({topic:node.topic, payload:data});
node.child.stdout.on('data', function(data) {
if (RED.settings.verbose) { node.log("out: " + data + " :"); }
data = data.toString().trim();
if (data.length > 0) {
node.send({topic:node.topic, payload: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.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 Parameters: " + node.pins);
}
var wfi = function(done) {
if (!node.running) {
if (RED.settings.verbose) { node.log("end"); }
done();
return;
}
});
setTimeout(function() { wfi(done); }, 333);
}
node.child.stderr.on('data', function(data) {
if (RED.settings.verbose) { node.log("err: " + data + " :"); }
node.on("close", function(done) {
if (node.child != null) {
node.child.kill('SIGKILL');
}
wfi(done);
});
node.child.on('close', function(code) {
if (RED.settings.verbose) { node.log("ret: " + code + " :"); }
node.child = null;
node.running = false;
});
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 Parameters: " + node.pins);
node.status({fill:"grey",shape:"dot",text:"node-red:rpi-gpio.status.not-available"});
}
var wfi = function(done) {
if (!node.running) {
if (RED.settings.verbose) { node.log("end"); }
done();
return;
}
setTimeout(function() { wfi(done); }, 333);
}
node.on("close", function(done) {
if (node.child != null) {
node.child.kill('SIGKILL');
}
wfi(done);
});
}
RED.nodes.registerType("rpi-srf", PiSrfNode);
}

View File

@ -1,6 +1,6 @@
node-red-node-pibrella
======================
A <a href="http://nodered.org" target="_new">Node-RED</a> node to control a <a href="http://pibrealla.com/" target="_new">Pibrella</a> add-on board for a Raspberry-Pi.
A <a href="http://nodered.org" target="_new">Node-RED</a> node to control a <a href="http://pibrella.com/" target="_new">Pibrella</a> add-on board for a Raspberry-Pi.
Install
-------

View File

@ -8,11 +8,12 @@ import os
import subprocess
from time import sleep
bounce = 25;
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
if sys.version_info >= (3,0):
print("Sorry - currently only configured to work with python 2.x")
sys.exit(1)
bounce = 25;
if len(sys.argv) > 2:
cmd = sys.argv[1].lower()
@ -21,7 +22,7 @@ if len(sys.argv) > 2:
GPIO.setwarnings(False)
if cmd == "pwm":
#print "Initialised pin "+str(pin)+" to PWM"
#print("Initialised pin "+str(pin)+" to PWM")
GPIO.setup(pin,GPIO.OUT)
p = GPIO.PWM(pin, 100)
p.start(0)
@ -36,10 +37,10 @@ if len(sys.argv) > 2:
GPIO.cleanup(pin)
sys.exit(0)
except Exception as ex:
print "bad data: "+data
print("bad data: "+data)
elif cmd == "buzz":
#print "Initialised pin "+str(pin)+" to Buzz"
#print("Initialised pin "+str(pin)+" to Buzz")
GPIO.setup(pin,GPIO.OUT)
p = GPIO.PWM(pin, 100)
p.stop()
@ -58,10 +59,10 @@ if len(sys.argv) > 2:
GPIO.cleanup(pin)
sys.exit(0)
except Exception as ex:
print "bad data: "+data
print("bad data: "+data)
elif cmd == "out":
#print "Initialised pin "+str(pin)+" to 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]))
@ -82,11 +83,11 @@ if len(sys.argv) > 2:
GPIO.output(pin,data)
elif cmd == "in":
#print "Initialised pin "+str(pin)+" to IN"
#print("Initialised pin "+str(pin)+" to IN")
bounce = int(sys.argv[4])
def handle_callback(chan):
sleep(bounce/1000)
print GPIO.input(chan)
print(GPIO.input(chan))
if sys.argv[3].lower() == "up":
GPIO.setup(pin,GPIO.IN,GPIO.PUD_UP)
@ -95,7 +96,7 @@ if len(sys.argv) > 2:
else:
GPIO.setup(pin,GPIO.IN)
print GPIO.input(pin)
print(GPIO.input(pin))
GPIO.add_event_detect(pin, GPIO.BOTH, callback=handle_callback, bouncetime=bounce)
while True:
@ -108,7 +109,7 @@ if len(sys.argv) > 2:
sys.exit(0)
elif cmd == "byte":
#print "Initialised BYTE mode - "+str(pin)+
#print("Initialised BYTE mode - "+str(pin)+)
list = [7,11,13,12,15,16,18,22]
GPIO.setup(list,GPIO.OUT)
@ -131,7 +132,7 @@ if len(sys.argv) > 2:
GPIO.output(list[bit], data & mask)
elif cmd == "borg":
#print "Initialised BORG mode - "+str(pin)+
#print("Initialised BORG mode - "+str(pin)+)
GPIO.setup(11,GPIO.OUT)
GPIO.setup(13,GPIO.OUT)
GPIO.setup(15,GPIO.OUT)
@ -169,7 +170,7 @@ if len(sys.argv) > 2:
button = ord( buf[0] ) & pin # mask out just the required button(s)
if button != oldbutt: # only send if changed
oldbutt = button
print button
print(button)
while True:
try:
@ -181,7 +182,7 @@ if len(sys.argv) > 2:
elif cmd == "kbd": # catch keyboard button events
try:
while not os.path.isdir("/dev/input/by-path"):
time.sleep(10)
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
EVENT_SIZE = struct.calcsize('llHHI')
@ -194,7 +195,7 @@ if len(sys.argv) > 2:
# type,code,value
print("%u,%u" % (code, value))
event = file.read(EVENT_SIZE)
print "0,0"
print("0,0")
file.close()
sys.exit(0)
except:
@ -204,14 +205,14 @@ if len(sys.argv) > 2:
elif len(sys.argv) > 1:
cmd = sys.argv[1].lower()
if cmd == "rev":
print GPIO.RPI_REVISION
print(GPIO.RPI_REVISION)
elif cmd == "ver":
print GPIO.VERSION
print(GPIO.VERSION)
elif cmd == "info":
print GPIO.RPI_INFO
print(GPIO.RPI_INFO)
else:
print "Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}"
print " only ver (gpio version) and info (board information) accept no pin parameter."
print("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}")
print(" only ver (gpio version) and info (board information) accept no pin parameter.")
else:
print "Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}"
print("Bad parameters - in|out|pwm|buzz|byte|borg|mouse|kbd|ver|info {pin} {value|up|down}")

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-pibrella",
"version" : "0.0.12",
"version" : "0.0.15",
"description" : "A Node-RED node to read from and write to a Pibrella Raspberry Pi add-on board",
"dependencies" : {
},

View File

@ -61,7 +61,7 @@
<li>A hex string <b>"#rrggbb"</b> triple</li>
<li><b>"red,green,blue"</b> three 0-255 values as a string</li>
<li><b>"random"</b> will generate a random color</li>
<li><i><a href="http://www.w3schools.com/html/html_colornames.asp" target="_new">Standard HTML color</a></i> name</li>
<li><i><a href="https://www.w3schools.com/colors/colors_names.asp" target="_new">Standard HTML color</a></i> name</li>
<li>An <b>object</b> can override any of the parameters</li>
<li><b>array</b> of colours for a neopixel rgb strip - either name,name,... or r,g,b,r,g,b,... where r,g,b are 0 to 255.</li>
</ul>

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-blinkstick",
"version" : "0.1.15",
"version" : "0.1.16",
"description" : "A Node-RED node to control a Blinkstick",
"dependencies" : {
"blinkstick" : "1.1.3"

View File

@ -145,43 +145,6 @@ module.exports = function(RED) {
break;
}
//case "holiday" : {
//if (DEBUG) {
//hminnode.log("Hit the holiday case");
//}
//if (!('enabled' in message.payload[key]) && !('time' in message.payload[key])) {
//hminnode.log("Warning: Unsupported 'holiday' value passed!");
//eturn;
//}
//var time = message.payload[key].time;
//// Ensure hminnode time is a date
//if (typeof(time) == "string") {
//hminnode.log("Typeof time was " +typeof(message.payload[key].time));
//// message.payload[key].time = new Date(message.payload[key].time);
//message.payload[key].time = new Date(2014, 02, 15, 12, 0, 0);
//hminnode.log("Typeof time is now " +typeof(message.payload[key].time));
//}
//// Also add in away mode (for hot water) if we're on hols
//if (message.payload[key].time) {
//message.payload.away_mode = 1;
//}
//else {
//message.payload.away_mode = 0;
//}
//break;
// }
//case "hotwater" : {
//if (DEBUG) {
//hminnode.log("Hit the hotwater case");
//}
//if (message.payload[key] !== "on" && message.payload[key] !== "boost" && message.payload[key] !== "off") {
//hminnode.log("Warning: Unsupported 'hotwater' value passed!");
//return;
//}
//break;
// }
case "heating" : {
// Ensure heating stays last! It's got a multi write scenario
if (DEBUG) {

View File

@ -22,7 +22,7 @@ hardware / downloads for your particular board.
Ensure the latest version of mraa libraries are installed: (version 1.5 as of Sept 2015)
echo "src mraa-upm http://iotdk.intel.com/repos/1.5/intelgalactic" > /etc/opkg/mraa-upm.conf
okpg update
opkg update
opkg upgrade
**NOTE** : This node assumes that the mraa npm is already installed globally - as

View File

@ -1,6 +1,6 @@
{
"name" : "node-red-node-intel-gpio",
"version" : "0.0.5",
"version" : "0.0.6",
"description" : "A Node-RED node to talk to an Intel Galileo or Edison using mraa",
"dependencies" : {
},

Some files were not shown because too many files have changed in this diff Show More