mirror of
https://github.com/node-red/node-red-nodes.git
synced 2023-10-10 13:36:58 +02:00
Add Pi Neopixel node
This commit is contained in:
parent
ff8f95066b
commit
5ebbf546d2
177
hardware/neopixel/LICENSE
Normal file
177
hardware/neopixel/LICENSE
Normal 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
|
42
hardware/neopixel/README.md
Normal file
42
hardware/neopixel/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
node-red-node-pi-neopixel
|
||||
=========================
|
||||
|
||||
A <a href="http://nodered.org" target="_new">Node-RED</a> node to drive a strip
|
||||
of Neopixel or WS2812 LEDs.
|
||||
|
||||
Pre-requisites
|
||||
--------------
|
||||
|
||||
The Unicorn HAT python drivers need to be pre-installed... This is the easiest way to get the neopixel driver installed... see the
|
||||
<a "href=http://learn.pimoroni.com/tutorial/unicorn-hat/getting-started-with-unicorn-hat">
|
||||
Pimorini Getting Started with Unicorn HAT</a> page.
|
||||
|
||||
curl -sS get.pimoroni.com/unicornhat | bash
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
Run the following command in the root directory of your Node-RED install.
|
||||
Usually this is `~/.node-red`
|
||||
|
||||
npm install node-red-node-pi-neopixel
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Defaults to a bar chart style mode using configured foreground and background colours.
|
||||
|
||||
It can accept a number in **msg.payload** that can be either the number of pixels, or a percentage of the total length.
|
||||
|
||||
If you want to change the foregound colour, you can set **msg.payload** to a comma separated string of `html_colour,length`.
|
||||
|
||||
To set the background just set **msg.payload** `html_colour` name.
|
||||
<a href="http://html-color-codes.info/color-names/" target="_top">Here
|
||||
is a list</a> of html_colour names.
|
||||
|
||||
The `nth` pixel is set by **msg.payload** with a CSV string `n,r,g,b`
|
||||
|
||||
A range of pixels from `x` to `y` can be set by **msg.payload**
|
||||
with a CSV string `x,y,r,g,b`
|
||||
|
||||
The pixels data line should be connected to Pi physical pin 12 - GPIO 18. *Note:* this may conflict with audio playback.
|
204
hardware/neopixel/colours.js
Normal file
204
hardware/neopixel/colours.js
Normal file
@ -0,0 +1,204 @@
|
||||
/**
|
||||
* Copyright 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.
|
||||
**/
|
||||
|
||||
var colours = {
|
||||
'aqua':'#00FFFF',
|
||||
'aliceblue':'#F0F8FF',
|
||||
'antiquewhite':'#FAEBD7',
|
||||
'black':'#000000',
|
||||
'off':'#000000',
|
||||
'blue':'#0000FF',
|
||||
'cyan':'#00FFFF',
|
||||
'darkblue':'#00008B',
|
||||
'darkcyan':'#008B8B',
|
||||
'darkgreen':'#006400',
|
||||
'darkturquoise':'#00CED1',
|
||||
'deepskyblue':'#00BFFF',
|
||||
'green':'#008000',
|
||||
'lime':'#00FF00',
|
||||
'mediumblue':'#0000CD',
|
||||
'mediumspringgreen':'#00FA9A',
|
||||
'navy':'#000080',
|
||||
'springgreen':'#00FF7F',
|
||||
'teal':'#008080',
|
||||
'midnightblue':'#191970',
|
||||
'dodgerblue':'#1E90FF',
|
||||
'lightseagreen':'#20B2AA',
|
||||
'forestgreen':'#228B22',
|
||||
'seagreen':'#2E8B57',
|
||||
'darkslategray':'#2F4F4F',
|
||||
'darkslategrey':'#2F4F4F',
|
||||
'limegreen':'#32CD32',
|
||||
'mediumseagreen':'#3CB371',
|
||||
'turquoise':'#40E0D0',
|
||||
'royalblue':'#4169E1',
|
||||
'steelblue':'#4682B4',
|
||||
'darkslateblue':'#483D8B',
|
||||
'mediumturquoise':'#48D1CC',
|
||||
'indigo':'#4B0082',
|
||||
'darkolivegreen':'#556B2F',
|
||||
'cadetblue':'#5F9EA0',
|
||||
'cornflowerblue':'#6495ED',
|
||||
'mediumaquamarine':'#66CDAA',
|
||||
'dimgray':'#696969',
|
||||
'dimgrey':'#696969',
|
||||
'slateblue':'#6A5ACD',
|
||||
'olivedrab':'#6B8E23',
|
||||
'slategray':'#708090',
|
||||
'slategrey':'#708090',
|
||||
'lightslategray':'#778899',
|
||||
'lightslategrey':'#778899',
|
||||
'mediumslateblue':'#7B68EE',
|
||||
'lawngreen':'#7CFC00',
|
||||
'aquamarine':'#7FFFD4',
|
||||
'chartreuse':'#7FFF00',
|
||||
'gray':'#808080',
|
||||
'grey':'#808080',
|
||||
'maroon':'#800000',
|
||||
'olive':'#808000',
|
||||
'purple':'#800080',
|
||||
'lightskyblue':'#87CEFA',
|
||||
'skyblue':'#87CEEB',
|
||||
'blueviolet':'#8A2BE2',
|
||||
'darkmagenta':'#8B008B',
|
||||
'darkred':'#8B0000',
|
||||
'saddlebrown':'#8B4513',
|
||||
'darkseagreen':'#8FBC8F',
|
||||
'lightgreen':'#90EE90',
|
||||
'mediumpurple':'#9370DB',
|
||||
'darkviolet':'#9400D3',
|
||||
'palegreen':'#98FB98',
|
||||
'darkorchid':'#9932CC',
|
||||
'yellowgreen':'#9ACD32',
|
||||
'sienna':'#A0522D',
|
||||
'brown':'#A52A2A',
|
||||
'darkgray':'#A9A9A9',
|
||||
'darkgrey':'#A9A9A9',
|
||||
'greenyellow':'#ADFF2F',
|
||||
'lightblue':'#ADD8E6',
|
||||
'paleturquoise':'#AFEEEE',
|
||||
'lightsteelblue':'#B0C4DE',
|
||||
'powderblue':'#B0E0E6',
|
||||
'firebrick':'#B22222',
|
||||
'darkgoldenrod':'#B8860B',
|
||||
'mediumorchid':'#BA55D3',
|
||||
'rosybrown':'#BC8F8F',
|
||||
'darkkhaki':'#BDB76B',
|
||||
'silver':'#C0C0C0',
|
||||
'mediumvioletred':'#C71585',
|
||||
'indianred':'#CD5C5C',
|
||||
'peru':'#CD853F',
|
||||
'chocolate':'#D2691E',
|
||||
'tan':'#D2B48C',
|
||||
'lightgray':'#D3D3D3',
|
||||
'lightgrey':'#D3D3D3',
|
||||
'thistle':'#D8BFD8',
|
||||
'goldenrod':'#DAA520',
|
||||
'orchid':'#DA70D6',
|
||||
'palevioletred':'#DB7093',
|
||||
'crimson':'#DC143C',
|
||||
'gainsboro':'#DCDCDC',
|
||||
'plum':'#DDA0DD',
|
||||
'burlywood':'#DEB887',
|
||||
'lightcyan':'#E0FFFF',
|
||||
'lavender':'#E6E6FA',
|
||||
'darksalmon':'#E9967A',
|
||||
'palegoldenrod':'#EEE8AA',
|
||||
'violet':'#EE82EE',
|
||||
'azure':'#F0FFFF',
|
||||
'honeydew':'#F0FFF0',
|
||||
'khaki':'#F0E68C',
|
||||
'lightcoral':'#F08080',
|
||||
'sandybrown':'#F4A460',
|
||||
'beige':'#F5F5DC',
|
||||
'mintcream':'#F5FFFA',
|
||||
'wheat':'#F5DEB3',
|
||||
'whitesmoke':'#F5F5F5',
|
||||
'ghostwhite':'#F8F8FF',
|
||||
'lightgoldenrodyellow':'#FAFAD2',
|
||||
'linen':'#FAF0E6',
|
||||
'salmon':'#FA8072',
|
||||
'oldlace':'#FDF5E6',
|
||||
'warmwhite':'#FDF5E6',
|
||||
'bisque':'#FFE4C4',
|
||||
'blanchedalmond':'#FFEBCD',
|
||||
'coral':'#FF7F50',
|
||||
'cornsilk':'#FFF8DC',
|
||||
'darkorange':'#FF8C00',
|
||||
'deeppink':'#FF1493',
|
||||
'floralwhite':'#FFFAF0',
|
||||
'fuchsia':'#FF00FF',
|
||||
'gold':'#FFD700',
|
||||
'hotpink':'#FF69B4',
|
||||
'ivory':'#FFFFF0',
|
||||
'lavenderblush':'#FFF0F5',
|
||||
'lemonchiffon':'#FFFACD',
|
||||
'lightpink':'#FFB6C1',
|
||||
'lightsalmon':'#FFA07A',
|
||||
'lightyellow':'#FFFFE0',
|
||||
'magenta':'#FF00FF',
|
||||
'mistyrose':'#FFE4E1',
|
||||
'moccasin':'#FFE4B5',
|
||||
'navajowhite':'#FFDEAD',
|
||||
'orange':'#FFA500',
|
||||
'orangered':'#FF4500',
|
||||
'papayawhip':'#FFEFD5',
|
||||
'peachpuff':'#FFDAB9',
|
||||
'pink':'#FFC0CB',
|
||||
'red':'#FF0000',
|
||||
'seashell':'#FFF5EE',
|
||||
'snow':'#FFFAFA',
|
||||
'tomato':'#FF6347',
|
||||
'white':'#FFFFFF',
|
||||
'yellow':'#FFFF00',
|
||||
'amber':'#FFD200'
|
||||
};
|
||||
|
||||
var p1 = /^\#[A-Fa-f0-9]{6}$/
|
||||
|
||||
module.exports.getRGB = function(col) {
|
||||
col = col.toString().toLowerCase();
|
||||
if (col in colours) {
|
||||
col = colours[col];
|
||||
}
|
||||
if (p1.test(col)) {
|
||||
r = parseInt(col.slice(1,3),16);
|
||||
g = parseInt(col.slice(3,5),16);
|
||||
b = parseInt(col.slice(5),16);
|
||||
return r+","+g+","+b;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.getHex = function(col) {
|
||||
col = col.toString().toLowerCase();
|
||||
if (col in colours) {
|
||||
return colours[col];
|
||||
}
|
||||
else { return null; }
|
||||
}
|
||||
|
||||
module.exports.HexRGB = function(hex) {
|
||||
try {
|
||||
r = parseInt(hex.slice(1,3),16);
|
||||
g = parseInt(hex.slice(3,5),16);
|
||||
b = parseInt(hex.slice(5),16);
|
||||
return r+","+g+","+b;
|
||||
}
|
||||
catch(e) { return null; }
|
||||
}
|
17
hardware/neopixel/neopix
Executable file
17
hardware/neopixel/neopix
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 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.
|
||||
#
|
||||
|
||||
BASEDIR=$(dirname $0)
|
||||
sudo python -u $BASEDIR/neopix.py $@
|
114
hardware/neopixel/neopix.py
Executable file
114
hardware/neopixel/neopix.py
Executable file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 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.
|
||||
#
|
||||
|
||||
# Import library functions we need
|
||||
import sys
|
||||
import time
|
||||
from neopixel import *
|
||||
|
||||
# LED strip configuration:
|
||||
LED_COUNT = 8 # Number of LED pixels.
|
||||
LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!).
|
||||
LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
|
||||
LED_DMA = 5 # DMA channel to use for generating signal (try 5)
|
||||
LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest
|
||||
LED_INVERT = False # True to invert the signal (when using NPN transistor level shift)
|
||||
|
||||
if sys.version_info >= (3,0):
|
||||
print("Sorry - currently only configured to work with python 2.x")
|
||||
sys.exit(1)
|
||||
|
||||
LED_COUNT = int(sys.argv[1])
|
||||
WAIT_MS = int(sys.argv[2])
|
||||
|
||||
# Define functions which animate LEDs in various ways.
|
||||
def setPixel(strip, i, color):
|
||||
"""Set a single pixel"""
|
||||
strip.setPixelColor(i, color)
|
||||
strip.show()
|
||||
|
||||
def setPixels(strip, s, e, color, wait_ms=30):
|
||||
"""Set pixels from s(tart) to e(nd)"""
|
||||
for i in range(s, e+1):
|
||||
strip.setPixelColor(i, color)
|
||||
strip.show()
|
||||
time.sleep(wait_ms/1000.0)
|
||||
|
||||
def colorWipe(strip, color, wait_ms=30):
|
||||
"""Wipe color across display a pixel at a time."""
|
||||
for i in range(strip.numPixels()):
|
||||
strip.setPixelColor(i, color)
|
||||
strip.show()
|
||||
time.sleep(wait_ms/1000.0)
|
||||
|
||||
def wheel(pos):
|
||||
"""Generate rainbow colors across 0-255 positions."""
|
||||
if pos < 85:
|
||||
return Color(pos * 3, 255 - pos * 3, 0)
|
||||
elif pos < 170:
|
||||
pos -= 85
|
||||
return Color(255 - pos * 3, 0, pos * 3)
|
||||
else:
|
||||
pos -= 170
|
||||
return Color(0, pos * 3, 255 - pos * 3)
|
||||
|
||||
def rainbow(strip, wait_ms=20, iterations=2):
|
||||
"""Draw rainbow that fades across all pixels at once."""
|
||||
for j in range(256*iterations):
|
||||
for i in range(strip.numPixels()):
|
||||
strip.setPixelColor(i, wheel((i+j) & 255))
|
||||
strip.show()
|
||||
time.sleep(wait_ms/1000.0)
|
||||
|
||||
def rainbowCycle(strip, wait_ms=20, iterations=2):
|
||||
"""Draw rainbow that uniformly distributes itself across all pixels."""
|
||||
for j in range(256*iterations):
|
||||
for i in range(strip.numPixels()):
|
||||
strip.setPixelColor(i, wheel(((i * 256 / strip.numPixels()) + j) & 255))
|
||||
strip.show()
|
||||
time.sleep(wait_ms/1000.0)
|
||||
|
||||
# Main loop:
|
||||
if __name__ == '__main__':
|
||||
# Create NeoPixel object with appropriate configuration.
|
||||
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS)
|
||||
# Intialize the library (must be called once before other functions).
|
||||
strip.begin()
|
||||
|
||||
## Color wipe animations.
|
||||
colorWipe(strip, Color(127, 0, 0), WAIT_MS) # Red wipe
|
||||
colorWipe(strip, Color(0, 127, 0), WAIT_MS) # Green wipe
|
||||
colorWipe(strip, Color(0, 0, 127), WAIT_MS) # Blue wipe
|
||||
colorWipe(strip, Color(0, 0, 0), WAIT_MS) # Off wipe
|
||||
|
||||
## Rainbow animations.
|
||||
#rainbow(strip)
|
||||
#rainbowCycle(strip)
|
||||
#colorWipe(strip, Color(0, 0, 0)) # Off wipe
|
||||
|
||||
while True:
|
||||
try:
|
||||
data = raw_input()
|
||||
bits = data.split(',')
|
||||
if len(bits) == 3:
|
||||
colorWipe(strip, Color(int(bits[0]), int(bits[1]), int(bits[2])), WAIT_MS)
|
||||
if len(bits) == 4:
|
||||
setPixel(strip, int(bits[0]), Color(int(bits[1]), int(bits[2]), int(bits[3]) ))
|
||||
if len(bits) == 5:
|
||||
setPixels(strip, int(bits[0]), int(bits[1]), Color(int(bits[2]), int(bits[3]), int(bits[4]) ), WAIT_MS)
|
||||
except (EOFError, SystemExit): # hopefully always caused by us sigint'ing the program
|
||||
sys.exit(0)
|
||||
except Exception as ex:
|
||||
print "bad data: "+data
|
88
hardware/neopixel/neopixel.html
Normal file
88
hardware/neopixel/neopixel.html
Normal file
@ -0,0 +1,88 @@
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="rpi-neopixels">
|
||||
<div class="form-row">
|
||||
<label for="node-input-pixels"><i class="fa fa-sun-o"></i> LEDs</label>
|
||||
<input type="text" id="node-input-pixels" placeholder="number" style="width:60px;"> in the string
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-bgnd"><i class="fa fa-picture-o"></i> Background</label>
|
||||
<input type="text" id="node-input-bgnd" placeholder="HTML colour name or rrr,ggg,bbb">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-fgnd"><i class="fa fa-picture-o"></i> Foreground</label>
|
||||
<input type="text" id="node-input-fgnd" placeholder="HTML colour name or rrr,ggg,bbb">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-mode"><i class="fa fa-cogs"></i> Mode</label>
|
||||
<select id="node-input-mode" style="width:73%">
|
||||
<option value="pcent">Percent of length</option>
|
||||
<option value="pixels">Number of pixels</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-wipe"><i class="fa fa-clock-o"></i> Wipe time</label>
|
||||
<input type="text" id="node-input-wipe" placeholder="number" style="width:60px;"> mS
|
||||
</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">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="rpi-neopixels">
|
||||
<p>Raspberry Pi node to drive a string of neopixel or ws2812 LEDs.</p>
|
||||
<p>Defaults to a bar chart style mode using configured foreground and background colours.
|
||||
It can accept a number in <b>msg.payload</b> that can be either the number of pixels, or a percentage of the total length.</p>
|
||||
<p>If you want to change the foregound colour, you can send a CSV of <i>html colour,length</i>.</p>
|
||||
<p>To set the background just send an <i>html colour</i> name.
|
||||
<a href="http://html-color-codes.info/color-names/" target="_top">Here
|
||||
is a list</a> of html colour names.<p>
|
||||
<p>The <i>nth</i> pixel is set by <b>msg.payload</b> with a CSV string <i>n,r,g,b</i>
|
||||
<!-- <p>The whole strip is set by <b>msg.payload</b> with a CSV string <i>r,g,b</i> -->
|
||||
<p>A range of pixels from <i>x</i> to <i>y</i> can be set by <b>msg.payload</b>
|
||||
with a CSV string <i>x,y,r,g,b</i>
|
||||
<p>The pixels data line should be connected to Pi physical pin 12 - GPIO 18. <i>Note:</i>
|
||||
this may conflict with audio playback.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('rpi-neopixels',{
|
||||
category: 'Raspberry Pi',
|
||||
label: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" },
|
||||
pixels: { value:"", required:true, validate:RED.validators.number() },
|
||||
bgnd: { value:"" },
|
||||
fgnd: { value:"" },
|
||||
wipe: { value:"40", required:true, validate:RED.validators.number() },
|
||||
mode: { value:"pcent" }
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "rpi.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
return this.name||this.pixels+" neopixels";
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
}
|
||||
});
|
||||
</script>
|
149
hardware/neopixel/neopixel.js
Normal file
149
hardware/neopixel/neopixel.js
Normal file
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright 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.
|
||||
**/
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var spawn = require('child_process').spawn;
|
||||
var fs = require('fs');
|
||||
var colors = require('./colours.js');
|
||||
|
||||
var piCommand = __dirname+'/neopix';
|
||||
|
||||
if (!fs.existsSync("/dev/ttyAMA0")) { // unlikely if not on a Pi
|
||||
//RED.log.info(RED._("rpi-gpio.errors.ignorenode"));
|
||||
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
|
||||
}
|
||||
|
||||
if (!fs.existsSync('/usr/local/lib/python2.7/dist-packages/neopixel.py')) {
|
||||
RED.log.warn("Can't find neopixel.py python library");
|
||||
throw "Warning : Can't find neopixel.py python library";
|
||||
}
|
||||
|
||||
if ( !(1 & parseInt ((fs.statSync(piCommand).mode & parseInt ("777", 8)).toString (8)[0]) )) {
|
||||
RED.log.error(piCommand + " command is not executable");
|
||||
throw "Error : "+RED._("rpi-gpio.errors.mustbeexecutable");
|
||||
}
|
||||
|
||||
// the magic to make python print stuff immediately
|
||||
process.env.PYTHONUNBUFFERED = 1;
|
||||
|
||||
function piNeopixelNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.pixels = n.pixels || 1;
|
||||
this.bgnd = n.bgnd || "0,0,0";
|
||||
this.fgnd = n.fgnd || "128,128,128";
|
||||
this.mode = n.mode || "pcent";
|
||||
this.wipe = Number(n.wipe || 40);
|
||||
if (this.wipe < 0) { this.wipe = 0; }
|
||||
var node = this;
|
||||
var p1 = /^\#[A-Fa-f0-9]{6}$/
|
||||
var p2 = /^[0-9]+,[0-9]+,[0-9]+$/
|
||||
var p3 = /^[0-9]+,[0-9]+,[0-9]+,[0-9]+$/
|
||||
var p4 = /^[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+$/
|
||||
|
||||
function inputlistener(msg) {
|
||||
if (msg.hasOwnProperty("payload")) {
|
||||
var pay = msg.payload.toString().toUpperCase();
|
||||
var parts = pay.split(",");
|
||||
if (parts.length <= 2) {
|
||||
if (parts.length === 2) { // it's a colour and length
|
||||
if (isNaN(parseInt(parts[1]))) { parts = parts.reverse(); }
|
||||
if (colors.getRGB(parts[0])) {
|
||||
node.fgnd = colors.getRGB(parts[0]);
|
||||
var l = parts[1];
|
||||
if (node.mode === "pcent") { l = parseInt(l / 100 * node.pixels + 0.5); }
|
||||
l = l - 1;
|
||||
pay = "0,"+l+","+node.fgnd+"\n"+(l+1)+","+(node.pixels-1)+","+node.bgnd;
|
||||
}
|
||||
else { node.warn("Invalid payload : "+pay); return; }
|
||||
}
|
||||
else {
|
||||
if (isNaN(pay)) { // it's a single colour word so set background
|
||||
if (colors.getRGB(pay)) {
|
||||
node.bgnd = colors.getRGB(pay);
|
||||
pay = node.bgnd;
|
||||
}
|
||||
else { node.warn("Invalid payload : "+pay); return; }
|
||||
}
|
||||
else { // it's a single number so just draw bar
|
||||
var l = pay;
|
||||
if (node.mode === "pcent") { l = parseInt(l / 100 * node.pixels + 0.5); }
|
||||
l = l - 1;
|
||||
pay = "0,"+l+","+node.fgnd+"\n"+(l+1)+","+(node.pixels-1)+","+node.bgnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((parts.length <= 2) || p2.test(pay) || p3.test(pay) || p4.test(pay) ) {
|
||||
if (parts.length === 3) { node.bgnd = pay; }
|
||||
node.child.stdin.write(pay+"\n"); // handle 3 parts, 4 part and 5 parts in the python
|
||||
}
|
||||
else { node.warn("Invalid payload : "+pay); }
|
||||
}
|
||||
}
|
||||
|
||||
node.child = spawn(piCommand, [node.pixels, node.wipe]);
|
||||
node.status({fill:"green",shape:"dot",text:"ok"});
|
||||
|
||||
node.on("input", inputlistener);
|
||||
|
||||
node.child.stdout.on('data', function (data) {
|
||||
if (RED.settings.verbose) { node.log("out: "+data+" :"); }
|
||||
});
|
||||
|
||||
node.child.stderr.on('data', function (data) {
|
||||
if (RED.settings.verbose) { node.log("err: "+data+" :"); }
|
||||
});
|
||||
|
||||
node.child.on('close', function (code) {
|
||||
node.child = null;
|
||||
if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
|
||||
if (node.done) {
|
||||
node.status({fill:"grey",shape:"ring",text:"closed"});
|
||||
node.done();
|
||||
}
|
||||
else { node.status({fill:"red",shape:"ring",text:"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:"closed"});
|
||||
if (node.child != null) {
|
||||
node.done = done;
|
||||
node.child.kill('SIGKILL');
|
||||
}
|
||||
else { done(); }
|
||||
});
|
||||
|
||||
if (node.bgnd) {
|
||||
if (node.bgnd.split(',').length === 1) {
|
||||
node.bgnd = colors.getRGB(node.bgnd);
|
||||
}
|
||||
node.child.stdin.write(node.bgnd+"\n");
|
||||
}
|
||||
|
||||
if (node.fgnd) {
|
||||
if (node.fgnd.split(',').length === 1) {
|
||||
node.fgnd = colors.getRGB(node.fgnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("rpi-neopixels",piNeopixelNode);
|
||||
}
|
26
hardware/neopixel/package.json
Normal file
26
hardware/neopixel/package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name" : "node-red-node-pi-neopixel",
|
||||
"version" : "0.0.1",
|
||||
"description" : "A Node-RED node to output to a neopixel (ws2812) string of LEDS from a Raspberry Pi.",
|
||||
"dependencies" : {
|
||||
},
|
||||
"repository" : {
|
||||
"type":"git",
|
||||
"url":"https://github.com/node-red/node-red-nodes/tree/master/hardware/neopixel"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [ "node-red", "ws2812", "neopixel" ],
|
||||
"node-red" : {
|
||||
"nodes" : {
|
||||
"rpi-neopixels": "neopixel.js"
|
||||
}
|
||||
},
|
||||
"scripts" : {
|
||||
"postinstall" : "scripts/checklib.js"
|
||||
},
|
||||
"author": {
|
||||
"name": "Dave Conway-Jones",
|
||||
"email": "ceejay@vnet.ibm.com",
|
||||
"url": "http://nodered.org"
|
||||
}
|
||||
}
|
13
hardware/neopixel/scripts/checklib.js
Executable file
13
hardware/neopixel/scripts/checklib.js
Executable file
@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
var fs = require('fs');
|
||||
|
||||
if (!fs.existsSync('/usr/local/lib/python2.7/dist-packages/neopixel.py')) {
|
||||
console.warn("WARNING : Can't find neopixel.py python library");
|
||||
console.warn("WARNING : Please install using the following command");
|
||||
console.warn("WARNING : Note: this uses root...");
|
||||
console.warn("WARNING : curl -sS get.pimoroni.com/unicornhat | bash\n");
|
||||
//process.exit(1);
|
||||
}
|
||||
else {
|
||||
console.log("Neopixel Python library found OK.\n")
|
||||
}
|
Loading…
Reference in New Issue
Block a user