Skip to content

native bindings to drive WS2811 (or WS2812) LED-Controllers on a Raspberry Pi

License

Notifications You must be signed in to change notification settings

beyondscreen/node-rpi-ws281x-native

Repository files navigation

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):

  • dma: number: the dma-number to use for the driver's data-transport to the LEDs (default: 10)

  • freq: number: the frequency in Hz of the control-signal. This is 800kHz for ws2812/sk6812 LEDs and 400kHz for older ws2811 LEDs (default 800000).

  • channels: Object[]: an array of one or two objects with channel-specific configuration for the two possible outputs:

    • count: number: the number of LEDs on this channel.
    • gpio: number: the GPIO port-number the strip is connected to. (default: 18 for the first channel and 12 for the second channel)
    • invert: boolean: whether the output-signal should be inverted (needed when a inverting level-shifter is used) (default: false)
    • brightness: number: the brightness, applied to all LEDs on this channel. Value between 0 and 255 (default: 255).
    • stripType: string|number: the LED-type connected on this channel. See ./lib/constants.js. Can be a string-constant or one of the values from ws281x.stripType (default ws281x.stripType.WS2812)

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:

  • [readonly] count: number: number of LEDs on this channel
  • [readonly] stripType: number: the numeric LED-type (see ws281x.stripType)
  • [readonly] invert: boolean: if the signal for the LEDs is inverted
  • [readonly] gpio: number: the GPIO port-number used
  • brightness: number: the current brightness. Setting this property will have an effect with the very next render()-call.
  • array: Uint32Array: the color-data, represented as a Uint32Array. Each index in this array represents the color-value of an LED in 32 bits (8 bit each for white, red, green, blue, counting from MSB to LSB). So the numbers can be specified in hex using 0xwwrrggbb-format. For RGB-LEDs (those without a seperate white-channel) the MSB is ignored.
  • buffer: Buffer: A node-buffer, providing an alternative way to manipulate the color-data. This is a view on the same array-buffer that is used by the Uint32Array. When using the buffer, make sure to check for endianness to prevent problems (I believe it is little endian on the raspebrry-pi).

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:

  • Raspberry-PI and SD-Card
  • 5V power-supply (Meanwell for instance builds really good ones)
  • LED-Strip with WS2811/WS2812 Controllers (there are several other controller-variations that are not supported)
  • a breadboard and some jumper-wires (m/m as well as at least two f/m to connect the GPIO-Pins)
  • a 3.3V to 5V logic-level converter (74AHCT125 or 74HCT125N, others will probably also work)
  • more wire to connect the LED-strips

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!