beyondscreen / node-rpi-ws281x-native

native bindings to drive WS2811 (or WS2812) LED-Controllers on a Raspberry Pi
MIT License
224 stars 101 forks source link

control ws281x-LEDs with node.js

NOTE: This will only ever work on the Raspberry Pi.

This module provides native bindings to the rpi_ws281x library by Jeremy Garff that is used to control strips of individually addressable LEDs directly from a raspberry-pi. Supported are all LEDs of the NEOPIXEL/WS281x-family (specifically WS2811, WS2812, WS2812b, SK6812, SK6812W in all variations).

setup

this module is available via npm:

npm install rpi-ws281x-native

if you prefer installing from source:

npm install -g node-gyp
git clone --recursive https://github.com/beyondscreen/node-rpi-ws281x-native.git
cd rpi-ws281x-native
npm install

node-version

You will need an up-to-date version of nodejs that supports at least some es6-features (>= 6.5).

If you are running on a RaspberryPi 1 or zero (running an ARMv61 processor), you might need to download and install the nodejs-binaries manually. Head over to https://nodejs.org/dist, find the version to install and download the -armv61-version.

See here for more information: https://raspberrypi.stackexchange.com/questions/48303/install-nodejs-for-all-raspberry-pi

Usage Example

This is the simplest example that will actually do something. It will initialize the driver for 100 LEDs and set all LEDs to the same, pinkish color:

const ws281x = require('rpi-ws281x-native');

const channel = ws281x(100, { stripType: 'ws2812' });

const colorArray = channel.array;
for (let i = 0; i < channel.count; i++) {
  colorsArray[i] = 0xffcc22;
}

ws281x.render();

API

ws281x(numLeds: number, options = {}): Channel

For simple setups (i.e. those using just one channel), there is an easy way for initialization using the top-level export function.

Example:

const ws2821x = require('rpi-ws281x-native');
const options = {
  dma: 10,
  freq: 800000,
  gpio: 18,
  invert: false,
  brightness: 255,
  stripType: ws281x.stripType.WS2812
};

const channel = ws281x(20, options);
const colors = channel.array;

// update color-values
colors[42] = 0xffcc22;
ws281x.render();

This function takes two parameters, the number of LEDs (numLeds) and an options-object which is entirely optional. These options combine the channel-options and the global option from the init-function as described below.

The returned object is a channel object (also described below) that gives access to the color-data.

ws281x.init(options: Object): Channel[]

Configures and initializes the drivers and returns an array of channel-interfaces.

Example:

const ws2821x = require('rpi-ws281x-native');

const channels = ws281x.init({
  dma: 10,
  freq: 800000,
  channels: [
    {count: 20, gpio: 18, invert: false, brightness: 255, stripType: 'ws2812'},
    {count: 20, gpio: 13, invert: false, brightness: 128, stripType: 'sk6812-rgbw'}
  ]
});

The only parameter options is an object with the following properties (unspecified properties will use the default-value):

ws281x.render()

Send the current state of the channel color-buffers to the LEDs.

Example:

const ws2821x = require('rpi-ws281x-native');

// initialize
const [channel] = ws281x.init(options);

// set some color-values
channel.array[12] = 0xff0000;

// render
ws281x.render();

ws281x.reset()

Clear all color-values and render.

ws281x.finalize()

Shut down the drivers and free all resources.

Channel

Each of the channels is represented by a channel-object. Channels do not contain any public methods – all interaction happens through the following properties:

testing basic functionality

connect the WS2812-strip to the raspberry-pi as described here and run the command sudo node examples/rainbow.js <numLeds>. You should now see some rainbow-colors animation on the LED-strip.

needs to run as root

As the native part of this module needs to directly interface with the physical memory of the raspberry-pi (which is required in order to configure the PWM and DMA-modules), it always has to run with root-privileges (there are probably ways around this requirement, but that doesn't change the fact that the node-process running the LEDs needs access to the raw physical memory – a thing you should never allow to any user other than root).

If you are using this module as part of a program that should not be run with elevated privileges, it would be a good idea to have the LED-driver running in a seperate process. In such a case you could use the openpixel-control protocol to send the pixel-data to the driver-process. A stream-based node-implementation and some more information can be found here.

Hardware

There is a guide over at adafruit.com on how to get the hardware up and running. I followed these instructions by the word and had a working LED-strip.

Essentially, you need the Raspberry Pi, a logic-level converter and of course a LED-Strip or other types of WS281x/SK6812-LEDs.

The logic-level shifter is required to shift the output-voltage of the GPIO from 3.3V up to 5V. The adafruit-guide mentions the 74AHCT125, but in fact most of the 74HCT-series chips or even a simple transistor can be used for this.

To connect all that together, I'd recommend buying a small breadboard and some jumper-wires. Also, consider buying a 5V power-supply that can deliver up to 60mA per LED (so you'll need up to 6A (30W) to fully power 100 LEDs). For smaller applications, a good USB-charger should do the job just fine.

Buying stuff

A short checklist of what you will need:

You can buy everything at adafruit.com, sparkfun, on ebay or your favourite electronics retailer (germany: check conrad electronic, watterott or reichelt where I bought most of my stuff). If you got more time than money to spend, I recommend buying directly from chinese manufacturers (via aliexpress for example). Shipping takes ages, but you end up paying only half as much for the LEDs for example.

Known Issues

Raspberry integrated soundcard

There is a conflict where the internal soundcard uses the same GPIO / DMA / PWM functions that are needed to run the LED-drivers. As far as I know you can not use both at the same time.

To disable audio, comment out the following line in config.txt contained on the boot partion.

dtparam=audio=on

As @AdyiPool pointed out, that file seems to not exist in newer raspbian-versions, Alternatively, you can create a file /etc/modprobe.d/blacklist-ws281x.conf with the following contents (effectively preventing sound-related modules to be loaded into the kernel):

blacklist snd_bcm2835
blacklist snd_pcm
blacklist snd_timer
blacklist snd_pcsp
blacklist snd

(after updating the file you need to run sudo update-initramfs -u to get the changes into the boot-partition or something like that)

If anyone finds a better solution please get in touch!