Created
December 18, 2016 21:48
-
-
Save jamesbulpin/ec819028f5f0a1a8686173da014fcbe3 to your computer and use it in GitHub Desktop.
Hacked up WS2811 LED connector for Octoblu
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var ws281x = require('rpi-ws281x-native'); | |
var meshblu = require('meshblu'); | |
var meshbluJSON = require("./meshblu.json"); | |
var tinycolor = require("tinycolor2"); | |
var mic = require('microphone'); | |
var VUMeter = require('vu-meter') | |
// Specifies how you want your message payload to be passed | |
// from Octoblu to your device | |
var MESSAGE_SCHEMA = { | |
type: 'object', | |
properties: { | |
mode: { | |
type: 'string', | |
required: true | |
}, | |
color: { | |
type: 'integer', | |
required: false | |
} | |
} | |
}; | |
var NUM_LEDS = 100, | |
pixelData = new Uint32Array(NUM_LEDS), | |
modulation = new Uint32Array(NUM_LEDS); | |
var mode = ""; | |
var color = 0xffffff; | |
ws281x.init(NUM_LEDS); | |
var meter = new VUMeter(); | |
var vu = 0; | |
last_vals = [] | |
for (var i =0; i < 50; i++) { | |
last_vals.push(-33.0); | |
} | |
last_val_index = 0; | |
meter.on('data', function(data) { | |
last_vals[last_val_index] = data[0]; | |
last_val_index++; | |
if (last_val_index >= last_vals.length) last_val_index = 0; | |
var min = last_vals.reduce(function(a, b) {return Math.min(a,b);}, 100); | |
var max = last_vals.reduce(function(a, b) {return Math.max(a,b);}, -100); | |
var x = Math.floor(50*(data[0] - min)/(max-min)); | |
if (x < 0) x = 0; | |
if (x > 49) x = 49; | |
vu = x; | |
//console.log("Input=%s min/max %s/%s vu=%s", data[0], min, max, vu); | |
}); | |
mic.audioStream.on('data', function(data) { | |
meter.write(data, function(err) { | |
if (err) { | |
console.log("mic.audioStream.on error: " + str(err)); | |
} | |
}); | |
}); | |
mic.startCapture(); | |
process.on('SIGINT', function () { | |
ws281x.reset(); | |
process.nextTick(function () { process.exit(0); }); | |
}); | |
// Animation loop | |
var offset = 0; | |
setInterval(function () { | |
for (var i = 0; i < NUM_LEDS; i++) { | |
if ((offset % 40) == (i % 40)) { | |
modulation[i] = Math.round(Math.random()); | |
} | |
switch (mode) { | |
case "colorwheel": | |
pixelData[i] = colorwheel((offset + i) % 256); | |
break; | |
case "bars": | |
case "yoyo": | |
case "yo-yo": | |
pixelData[i] = bars(i, offset % (NUM_LEDS*2)); | |
break; | |
case "solid": | |
pixelData[i] = color; | |
break; | |
case "echo": | |
case "vu": | |
pixelData[i] = vubar(i); | |
break; | |
case "snowfall": | |
pixelData[i] = snowfall(i, (offset/2) % 100); | |
break; | |
case "twinkle": | |
case "twinklewhite": | |
pixelData[i] = twinklewhite(i); | |
break; | |
case "flashing": | |
case "twinklecolor": | |
pixelData[i] = twinklecolor(i); | |
break; | |
case "nothing": | |
pixelData[i] = 0; | |
break; | |
} | |
} | |
offset = (offset + 1); | |
ws281x.render(pixelData); | |
}, 1000 / 120); | |
console.log('Press <ctrl>+C to exit.'); | |
function bars(pos, step) { | |
if (step > (NUM_LEDS-1)) { | |
step = (NUM_LEDS*2) - 1 - step; | |
} | |
if (step >= pos) { | |
return colorwheel((offset + pos) % 256); | |
} | |
return 0; | |
} | |
var snowfall_rg = {} | |
function snowfall(pos, step) { | |
pos = 99 - pos; | |
if (Math.abs(pos-step) < 5) { | |
var rg = 0; | |
if (snowfall_rg[pos]) { | |
rg = snowfall_rg[pos]; | |
} | |
else { | |
rg = 127 + Math.random() * 128; | |
snowfall_rg[pos] = rg; | |
} | |
return rgb2Int(rg, rg, 255); | |
} | |
snowfall_rg[pos] = null; | |
return rgb2Int(0, 0, 0); | |
} | |
function twinklewhite(pos) { | |
return modulation[pos] * rgb2Int(255, 255, 255); | |
} | |
function twinklecolor(pos) { | |
return modulation[pos] * colorwheel((offset + pos)%256); | |
} | |
// rainbow-colors, taken from http://goo.gl/Cs3H0v | |
function colorwheel(pos) { | |
pos = 255 - pos; | |
if (pos < 85) { return rgb2Int(255 - pos * 3, 0, pos * 3); } | |
else if (pos < 170) { pos -= 85; return rgb2Int(0, pos * 3, 255 - pos * 3); } | |
else { pos -= 170; return rgb2Int(pos * 3, 255 - pos * 3, 0); } | |
} | |
function vubar(pos) { | |
var vpos = pos / (NUM_LEDS/50); | |
if (vpos <= vu) return colorwheel((offset + pos) % 256); | |
return 0; | |
} | |
function rgb2Int(r, g, b) { | |
return ((g & 0xff) << 16) + ((r & 0xff) << 8) + (b & 0xff); | |
} | |
connectionTimer = setTimeout(function() { | |
console.log("Timeout setting up connections, exiting."); | |
process.exit(1); | |
}, 120000); | |
// Not specifying a UUID/Token auto-registers a new device | |
var conn = meshblu.createConnection({ | |
"uuid": meshbluJSON.uuid, | |
"token": meshbluJSON.token, | |
"server": "meshblu.octoblu.com", | |
"port": 80 | |
}); | |
conn.on('notReady', function(data){ | |
console.log('Octoblu UUID FAILED AUTHENTICATION!'); | |
console.log(data); | |
}); | |
function handle_message(newmode, newcolor) { | |
if (newmode == "solid") { | |
if (newcolor) { | |
var c = parseInt(newcolor); | |
if (isNaN(c)) { | |
var rgb = tinycolor(newcolor).toRgb(); | |
c = rgb2Int(rgb.r, rgb.g, rgb.b); | |
} | |
color = c; | |
console.log("Setting colour " + c); | |
} | |
} | |
mode = newmode; | |
} | |
conn.on('ready', function(rdata){ | |
console.log('UUID AUTHENTICATED!'); | |
console.log(rdata); | |
clearTimeout(connectionTimer); | |
connectionTimer = undefined; | |
conn.update({ | |
"uuid": meshbluJSON.uuid, | |
"token": meshbluJSON.token, | |
"messageSchema": MESSAGE_SCHEMA, | |
}); | |
conn.on('message', function(data){ | |
console.log('Octoblu message received'); | |
console.log(data); | |
mode = data.mode; | |
color = ("color" in data)?data.color:null; | |
handle_message(mode, color) | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment