rpi-ws2801
Version:
A node.js library to control a WS2801 RGB LED stripe via SPI with your Raspberry Pi
262 lines (235 loc) • 7.62 kB
JavaScript
var fs = require('fs');
var microtime = require('microtime');
/*
A node.js library to control a WS2801 RGB LED stripe via SPI with your Raspberry Pi
@copyright 2013 Jürgen Skrotzky (MIT-License)
*/
function RPiWS2801(){
this.spiDevice = '/dev/spidev0.0';
this.numLEDs = 32;
this.spiFd = null; //filedescriptor for spidevice
this.inverted = false;
this.reversed = false;
this.gamma = 2.5;
this.redIndex = 0;
this.greenIndex = 1;
this.blueIndex = 2;
this.gammatable = new Array(256);
this.bytePerPixel = 3; //RGB
this.channelCount = this.numLEDs*this.bytePerPixel;
this.values = new Buffer(this.channelCount);
this.rowResetTime = 1000; // number of us CLK has to be pulled low (=no writes) for frame reset
// manual of WS2801 says 500 is enough, however we need at least 1000
this.lastWriteTime = microtime.now()-this.rowResetTime-1; //last time something was written to SPI
//required for save WS2801 reset
// clear buffer
for( var i = 0; i < this.channelCount; i++) {
this.values[i] = 0;
}
}
RPiWS2801.prototype = {
/*
* connect to SPI port
*/
connect: function(numLEDs, spiDevice, gamma){
// sanity check for params
if ((numLEDs !== parseInt(numLEDs)) || (numLEDs<1)) {
console.error("invalid param for number of LEDs, plz use integer >0");
return false;
}
if (spiDevice){
this.spiDevice = spiDevice;
}
// connect synchronously
try{
this.spiFd = fs.openSync(this.spiDevice, 'w');
} catch (err) {
console.error("error opening SPI device "+this.spiDevice, err);
return false;
}
this.numLEDs = numLEDs;
this.channelCount = this.numLEDs*this.bytePerPixel;
this.values = new Buffer(this.channelCount);
this.gamma = gamma ? gamma : 2.5; //set gamma correction value
// compute gamma correction table
for (var i=0; i<256; i++)
this.gammatable[i] = Math.round(255*Math.pow(i/255, this.gamma));
//console.log("gammatable" + this.gammatable);
},
/*
* disconnect from SPI port
*/
disconnect : function(){
if (this.spiFd) fs.closeSync(this.spiFd);
},
/*
* send stored buffer with RGB values to WS2801 stripe
*/
update: function(){
if (this.spiFd) {
this.sendRgbBuffer(this.values);
}
},
/*
* invert all colors
*/
invert: function(){
this.inverted = !this.inverted;
}, // end invert
/*
* send buffer with RGB values to WS2801 stripe in reverse order
*/
reverse: function(){
this.reversed = !this.reversed;
}, // end reverse
/*
* clear RGB stripe
*/
clear: function(){
this.fill(0x00, 0x00, 0x00);
}, // end clear
/*
* set new RGB index order
*/
setColorIndex: function(redIndex, greenIndex, blueIndex){
this.redIndex = redIndex;
this.greenIndex = greenIndex;
this.blueIndex = blueIndex;
}, // end setColorIndex
/*
* send buffer with RGB values to WS2801 stripe
*/
sendRgbBuffer : function(buffer){
// checking if enough time passed for resetting stripe
if (microtime.now() > (this.lastWriteTime + this.rowResetTime)){
// yes, its o.k., lets write
// but first do gamma correction
var adjustedBuffer = new Buffer(buffer.length);
for (var i=0; i < buffer.length; i++){
adjustedBuffer[i]=this.gammatable[buffer[i]];
}
fs.writeSync(this.spiFd, adjustedBuffer, 0, buffer.length, null);
this.lastWriteTime = microtime.now();
return true;
}
console.log('writing too fast, data dropped');
return false;
}, // end sendRgbBuffer
/*
* fill whole stripe with one color
*/
fill: function(r,g,b){
if (this.spiFd) {
var colors = this.getRGBArray(r,g,b);
var colorBuffer = new Buffer(this.channelCount);
for (var i=0; i<(this.channelCount); i+=3){
colorBuffer[i+0]=colors[0];
colorBuffer[i+1]=colors[1];
colorBuffer[i+2]=colors[2];
}
this.sendRgbBuffer(colorBuffer);
}
}, //end fill
/*
* set color of led index [red, green, blue] from 0 to 255
*/
setColor: function(ledIndex, color) {
if (this.spiFd) {
var colors = this.getRGBArray(color[0],color[1],color[2]);
var r, g, b;
r = colors[0] / 255;
g = colors[1] / 255;
b = colors[2] / 255;
var redChannel = this.getRedChannelIndex(ledIndex);
this.setChannelPower(redChannel, r);
this.setChannelPower(redChannel+1, g);
this.setChannelPower(redChannel+2, b);
}
},
/*
* set power of channel from 0 to 1
*/
setChannelPower: function(channelIndex, powerValue) {
if (this.spiFd) {
if(channelIndex > this.channelCount || channelIndex < 0) {
return false;
}
if(powerValue < 0)
powerValue = 0;
if(powerValue > 1)
powerValue = 1;
this.values[channelIndex] = Math.floor((Math.pow(16, 2) - 1) * powerValue);
}
}, // end setChannelPower
/*
* set RGB hexcolor to LED index
*/
setRGB: function(ledIndex, hexColor) {
if (this.spiFd) {
var rgb = this.getRGBfromHex(hexColor);
var colors = this.getRGBArray(rgb.r,rgb.g,rgb.b);
var redChannel = this.getRedChannelIndex(ledIndex);
this.setChannelPower(redChannel+this.redIndex, colors[0]);
this.setChannelPower(redChannel+this.greenIndex, colors[1]);
this.setChannelPower(redChannel+this.blueIndex, colors[2]);
}
}, // end setRGB
mySin: function(a, min, max){
return min + ((max-min)/2.)*(Math.sin(a)+1)
},
rainbow: function(a){
var intense = 255;
var r = parseInt(this.mySin(a, 0, intense));
var g = parseInt(this.mySin(a+Math.pi/2, 0, intense));
var b = parseInt(this.mySin(a + Math.pi, 0, intense));
var colors = this.getRGBArray(r,g,b);
return [colors[0],colors[1],colors[2]];
},
getChannelCount: function() {
return this.channelCount;
},
getRedChannelIndex: function(ledIndex) {
return ledIndex * 3;
},
getGreenChannelIndex: function(ledIndex) {
return ledIndex * 3 + 1;
},
getBlueChannelIndex: function(ledIndex) {
return ledIndex * 3 + 2;
},
getRGBArray: function(r, g, b){
var colorArray = new Array(3);
colorArray[this.redIndex] = r;
colorArray[this.greenIndex] = g;
colorArray[this.blueIndex] = b;
if(this.inverted) {
colorArray[0] = (1 - colorArray[0]/255)*255;
colorArray[1] = (1 - colorArray[1]/255)*255;
colorArray[2] = (1 - colorArray[2]/255)*255;
}
return colorArray;
},
getRGBfromHex: function(color) {
var r, g, b;
if (color.length == 4) {
r = parseInt(color.replace("#", "").substr(0,1)+color.replace("#", "").substr(0,1),16);
g = parseInt(color.replace("#", "").substr(1,1)+color.replace("#", "").substr(1,1),16);
b = parseInt(color.replace("#", "").substr(2,1)+color.replace("#", "").substr(2,1),16);
}
if (color.length == 7) {
r = parseInt(color.replace("#", "").substr(0,2),16);
g = parseInt(color.replace("#", "").substr(2,2),16);
b = parseInt(color.replace("#", "").substr(4,2),16);
}
var colors = this.getRGBArray(r,g,b);
r = colors[0] / 255;
g = colors[1] / 255;
b = colors[2] / 255;
if (r>=0 && r<=1 && g>=0 && g<=1 && b>=0 && b<=1) {
return {r:r,g:g,b:b};
} else {
return {r:0,g:0,b:0};
}
}
}
module.exports = new RPiWS2801();