rpi-sk6812-native
Version:
(raspberry-pi *only*) native bindings to control a strip of WS281x or SK6812 LEDs with node.js
210 lines (164 loc) • 5.94 kB
JavaScript
var EventEmitter = require('events').EventEmitter;
function getNativeBindings() {
var stub = {
init: function() {},
render: function() {},
setBrightness: function() {},
reset: function() {}
};
if (!process.getuid || process.getuid() !== 0) {
console.warn('[rpi-sk6812-native] This module requires being run ' +
'with root-privileges. A non-functional stub of the ' +
'interface will be returned.');
return stub;
}
// the native module might even be harmful (or won't work in the best case)
// in the wrong environment, so we make sure that at least everything we can
// test for matches the raspberry-pi before loading the native-module
if (process.arch !== 'arm' && process.platform !== 'linux') {
console.warn('[rpi-sk6812-native] It looks like you are not ' +
'running on a raspberry pi. This module will not work ' +
'on other platforms. A non-functional stub of the ' +
'interface will be returned.');
return stub;
}
// determine rapsberry-pi version based on SoC-family. (note: a more
// detailed way would be to look at the revision-field from cpuinfo, see
// http://elinux.org/RPi_HardwareHistory)
var raspberryVersion = (function() {
var cpuInfo = require('fs').readFileSync('/proc/cpuinfo').toString(),
socFamily = cpuInfo.match(/hardware\s*:\s*(bcm270[89])/i);
if(!socFamily) { return 0; }
switch(socFamily[1].toLowerCase()) {
case 'bcm2708': return 1;
case 'bcm2709': return 2;
default: return 0;
}
} ());
if (raspberryVersion === 0) {
console.warn('[rpi-sk6812-native] Could not verify raspberry-pi ' +
'version. If this is wrong and you are running this on a ' +
'raspberry-pi, please file a bug-report at ' +
' https://github.com/n-johnson/node-rpi-sk6812-native/issues\n' +
'A non-functional stub of this modules interface will be ' +
'returned.');
return stub;
}
return require('./binding/rpi_ws281x.node');
}
var bindings = getNativeBindings();
/**
* gamma-correction: remap color-values (provided as 0x00rrbbgg) with a
* gamma-factor. Gamma-value and formula are taken from
* http://rgb-123.com/ws2812-color-output/
*
* @type function(number): number
*/
var gammaCorrect = (function() {
var _gamma = 1/0.45;
var _gammaTable = new Uint8Array(256);
for(var i=0; i<256; i++) {
_gammaTable[i] = Math.floor(Math.pow(i / 255, _gamma) * 255 + 0.5);
}
return function gammaCorrect(color) {
return (
_gammaTable[color & 0xff]
| (_gammaTable[(color >> 8) & 0xff] << 8)
| (_gammaTable[(color >> 16) & 0xff] << 16)
);
};
} ());
/**
* remap pixel-positions according to the specified index-mapping.
*
* @type function(Uint32Array, Array.<Number>): Uint32Array
*/
var remap = (function() {
var _tmpData = null;
return function remap(data, indexMapping) {
if(!_tmpData) {
_tmpData = new Uint32Array(data.length);
}
_tmpData.set(data);
for(var i=0; i<data.length; i++) {
data[i] = _tmpData[indexMapping[i]];
}
return data;
};
} ());
var ws281x = new EventEmitter();
var _isInitialized = false;
var _indexMapping = null;
var _outputBuffer = null;
// Gamma correction should only be used for 24-bit RGB strips,
// not 32-bit WRGB strips
var _useGammaCorrection = true;
// ---- EXPORTED INTERFACE
var STRIP_TYPES = bindings.STRIP_TYPES;
ws281x.STRIP_TYPES = STRIP_TYPES;
/**
* configures PWM and DMA for sending data to the LEDs
*
* @param {Number} numLeds number of LEDs to be controlled
* @param {?Object} options (acutally only tested with default-values)
* intialization-options for the library
* (PWM frequency, DMA channel, GPIO, Brightness)
*/
ws281x.init = function(numLeds, options) {
_isInitialized = true;
_outputBuffer = new Buffer(4 * numLeds);
_useGammaCorrection = (options.gammaCorrection !== undefined)
? options.gammaCorrection
: (options.strip_type !== STRIP_TYPES.SK6812W);
bindings.init(numLeds, options);
};
/**
* register a mapping to manipulate array-indices within the
* data-array before rendering.
*
* @param {Array.<Number>} mapping the mapping, indexed by destination.
*/
ws281x.setIndexMapping = function(mapping) {
_indexMapping = mapping;
};
/**
* send data to the LED-strip.
*
* @param {Uint32Array} data the pixel-data, 24bit per pixel in
* RGB-format (0xff0000 is red).
* @return {Uint32Array} data as it was sent to the LED-strip
*/
ws281x.render = function(data) {
if(!_isInitialized) {
throw new Error('render called before initialization.');
}
this.emit('beforeRender', data);
if(_indexMapping) {
data = remap(data, _indexMapping);
}
for(var i=0; i<data.length; i++) {
var color_data = _useGammaCorrection ? gammaCorrect(data[i]) : data[i];
_outputBuffer.writeUInt32LE(color_data, 4 * i);
}
bindings.render(_outputBuffer);
this.emit('render', data);
return data;
};
ws281x.setBrightness = function(brightness) {
if(!_isInitialized) {
throw new Error('setBrightness called before initialization.');
}
bindings.setBrightness(brightness);
// re-render to have the brightness applied
bindings.render(_outputBuffer);
};
/**
* clears all LEDs, resets the PWM and DMA-parts and deallocates
* all internal structures.
*/
ws281x.reset = function() {
_isInitialized = false;
_outputBuffer = null;
bindings.reset();
};
module.exports = ws281x;