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