Back with an alpha ( some things doesn't work as they should, and I'm still missing the finalized data ( timed mouth indices & mouths data ) ), but it's cleaner than previously ;)
also, I managed to wreck my code somehow somewhere & now I can't control the last led any more :|
if anyone's feeling brave enough to visually troubleshoot, be my guest ;)
( as always, I'll do so after some rest ;) )
this being said, the pre-pre-pre-alpha code
/* ==== Akali's Mask V0.1a ==== */
// -- MODULES --
// - little 'dotstar' module -
// usage:
// var dotStar = exports(spi); // pass spi obj
//dotStar.write(ledsValues); // pass array of leds colors
var exports = function (spi) {
return {
write: function(ledsData){
spi.write([0x00,0x00,0x00,0x00]); // start frame
spi.write(ledsData); // led frames
for(var y=0; y<(ledsData.length/2)/8; y++){ spi.write(0x00); } // end frame
}
};
};
// -- COLORS/PATTERNS HELPER --
// R: APA102 are BGR for me
function getColoredPattern(colR, colG, colB, start, end, clear) {
var tstart = (start > 0)? start*4-3 : 0 || 0;
var tend = end*4 || ledsBuff.length;
for (var i=0;i<ledsBuff.length;i+=4) { // Q: is it where my 'one less led' comes from ? :/
if(i>=tstart && i < tend){
ledsBuff[i] = 0xe0+31;
ledsBuff[i+1] = colB;
ledsBuff[i+2] = colG;
ledsBuff[i+3] = colR;
} else if(clear) { // doesn't work ? :/
ledsBuff[i] = 0xe0+31;
ledsBuff[i+1] = 0;
ledsBuff[i+2] = 0;
ledsBuff[i+3] = 0;
}
}
return ledsBuff;
}
// R: steps may (later ) be used to either:
// - map '0' to '255, 0, 0, 0' & '1' to '255, 255, 255, 255' ( by def we store 1 & 0's, but lookup table set with R,G,B vals at each index can be used )
// Nb: we could then for example get the color of the item above a dot on Illustrator as an idx of its color in our 'shared' palette
// - map 'R, G, B' to '255, B, G, R', what we currently do
var colorsPalette = [[0, 0, 0], [255, 255, 255]];
function getPattern(array, steps) {
for (var i=0, y=0; i<ledsBuff.length;i+=4, y+=steps) {
if(steps == 1){
ledsBuff[i] = 0xe0+31;
//var val = (array[y] == 1)? 255 : 0; // would only support on/ff instead of paletted idx which offer 255 choices
ledsBuff[i+1] = colorsPalette[array[y]][2]; // B
ledsBuff[i+2] = colorsPalette[array[y]][1]; // G
ledsBuff[i+3] = colorsPalette[array[y]][0]; // R
} else if(steps == 3){
ledsBuff[i] = 0xe0+31;
ledsBuff[i+1] = array[y+2]; // B
ledsBuff[i+2] = array[y+1]; // G
ledsBuff[i+3] = array[y]; // R
} else if(steps == 4){
ledsBuff[i] = array[y]; // brightness
ledsBuff[i+1] = array[y+1]; // R
ledsBuff[i+2] = array[y+2]; // G
ledsBuff[i+3] = array[y+3]; // B
}
}
return ledsBuff;
}
// -- LEDS WORKING CHECK --
function checkLeds(delay){
setTimeout(function(){
dotStar.write(getColoredPattern(255, 0, 0, 0, ledsCount));
setTimeout(function(){
dotStar.write(getColoredPattern(0, 255, 0, 0, ledsCount));
setTimeout(function(){
dotStar.write(getColoredPattern(0, 0, 255, 0, ledsCount));
setTimeout(function(){
dotStar.write(getColoredPattern(255, 255, 255, 0, ledsCount));
setTimeout(function(){
dotStar.write(getColoredPattern(0, 0, 0, 0, ledsCount));
},delay);
},delay);
},delay);
},delay);
},delay);
}
// -- MOUTHS --
// TODO: generate dummys & finalize the leds mouths in Illustrator
// for finalized: how many ? number of leds in each ? which leds are on for each ?
// R: since we have to store at least 8 different mouths ( and by default, only on/off states of leds in those ), storing 1's & 0's ( or paletted color idx)
// make more sense to lessen the space taken
// ex: leds * BrRGB * mouths = 45 * 4 * 8 = 1440 bytes ( with brightness control )
// leds * RGB * mouths = 45 * 3 * 8 = 1080 bytes ( any color, no brightness control )
// leds * paletted idx * mouths = 45 * 1 * 8 = 360 bytes ( 255 possible colors )
var ledsBuff = new Uint8ClampedArray(45*4); // used as tmp to fill in /map in values while keeping constant brightness
var mouthsLedsArrays = [
//new Uint8ClampedArray([0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0]),
//new Uint8ClampedArray(45*3),
];
// dummies generator, to test stuff before finalizing the mouths leds arrays
function generateDummies(howMany){
for(var i=0; i<howMany; i++){ // R/ testing with 45 leds
console.log('pushing dummy: [' + (i*9) + '..' + (i*9+9) + ']' );
//ledsBuff = new Uint8ClampedArray(45*4); // fill buff with 0's
getColoredPattern(0, 0, 0, 0, ledsCount); // same as above, faster way that doesn't re-allocate a clamped array
//mouthsLedsArrays.push(getColoredPattern(255, 255, 255, i*9, i*9+9)); // push 9/45 of leds to our array ( uses buff internally ) - increasing
//mouthsLedsArrays.push(getColoredPattern(255, 255, 255, i*9, i*9+9)); // push 9/45 of leds to our array ( uses buff internally ) - moving .. not ? :/
mouthsLedsArrays.push(getColoredPattern(255/9*i, 255/9*i, 255/9*i, i*9, i*9+9)); // push 9/45 of leds to our array ( uses buff internally )
}
}
// -- TIMING --
// TODO: get final timings ( absolute or relative )
// R: we have generated 5 mouths for our debug, each with 9 leds
var absTimeMouthsIdxsArray = [
//[<ms_from_start>, <mouth_idx>]
[37, 0],
[38, 1],
[40, 2],
[45, 3],
[45.2, 4],
[45.5, 3],
[45.7, 2],
[45.8, 1],
[45.9, 0],
[45.95, 1],
[45.99, 2],
[46, 3],
[50, 4]
];
// R: to map abolute times to relative timing in ms: relTime = (absTime * 1000) - (1stAbsTime * 1000)
var reTimeMouthsIdxsArray = [];
for(var i=0; i< absTimeMouthsIdxsArray.length; i++){
var relTime = (i === 0) ? ((absTimeMouthsIdxsArray[i][0] * 1000) - (absTimeMouthsIdxsArray[i][0] * 1000)) :
(absTimeMouthsIdxsArray[i][0] * 1000) - (absTimeMouthsIdxsArray[i-1][0] * 1000);
reTimeMouthsIdxsArray[i] = [relTime, absTimeMouthsIdxsArray[i][1]];
}
// -- ANIMATION --
// transition between mouths
function nxtMouth(){
if(currMouthAnimIdx+1 < reTimeMouthsIdxsArray.length){ // didn't reach end of array yet
currMouthAnimIdx = currMouthAnimIdx+1;
timr = setTimeout(function(){
currMouth = mouthsLedsArrays[reTimeMouthsIdxsArray[currMouthAnimIdx][1]];
// useful for generated dummies
console.log('currMouthIdx: ' +currMouthAnimIdx);
//dotStar.write(getPattern(currMouth, 4));// update leds - we pass an array of paletted idx, rgb values or BrRGB values & a 'step' to indicate format
getColoredPattern(0, 0, 0, 0, ledsCount); // clear buffer
dotStar.write(getColoredPattern(0, 0, 100, 0, currMouthAnimIdx*4, true)); // debuuuuug
// for final impl
//dotStar.write(getPattern(currMouth, 1));// update leds - we pass an array of paletted idx, rgb values or BrRGB values & a 'step' to indicate format
nxtMouth(); // schedule next call
}, reTimeMouthsIdxsArray[currMouthAnimIdx][0]);
} else {
timr = -1;
console.log('end of mouth timed array reached -> animation complete !');
resetState();
}
}
// -- BUTTON PRESS HANDLER: START/RESET --
function handleBtnPress(){
if(animating){ // currently animating: reset
resetState();
}
else { // currently stopped: animate
animating = true;
console.log('animating !');
nxtMouth(); // TODO: uncomment once we have final mouths arrays or generated dummys
}
}
function resetState(){
console.log('resetting !');
if(timr !== -1){
clearTimeout(timr);
timr = -1;
}
currMouthAnimIdx = 0;
currMouth = mouthsLedsArrays[reTimeMouthsIdxsArray[currMouthAnimIdx][1]];
dotStar.write(getColoredPattern(0, 0, 0, 0, ledsCount)); // clear
animating = false;
}
// -- SETUP --
// - modules -
var spi;
var dotStar;
// - pins -
var btn = BTN1;
var dotStarSck = B3;
var dotStarMosi = B5;
// - watches -
var btnW;
// - vars -
var ledsCount = 45;
var animating = false;
var timr = -1;
var currMouthAnimIdx = 0; // where we're at in the 'reTimeMouthsIdxsArray'
//var currMouthIdx = 0;
var currMouth = mouthsLedsArrays[reTimeMouthsIdxsArray[currMouthAnimIdx][1]];
function onInit(){
// setup modules
spi = SPI1;
spi.setup({miso:B4, mosi:dotStarMosi, sck:dotStarSck, mode:0,order:"msb", baud: 4000000});
//spi.setup({mosi:dotStarMosi, sck:dotStarSck, mode:0,order:"msb", baud: 4000000});
dotStar = exports(spi);
dotStar.write(getColoredPattern(0, 0, 0, 0, ledsCount)); // start with all leds fully off
// setup button
//pinMode(btn, 'input_pullup');
btnW = setWatch(function(e) { handleBtnPress(); }, btn, { repeat:true, edge:'rising', debounce: 100 });
// debug logs
console.log('currMouthAnimIdx : ' + currMouthAnimIdx);
console.log('currMouth : ' + currMouth);
console.log('reTimeMouthsIdxsArray : ' + reTimeMouthsIdxsArray);
console.log('reTimeMouthsIdxsArray.length : ' + reTimeMouthsIdxsArray.length);
// generate dummies to test until we have final mouths
generateDummies(5); // we pass how many we want
// quick leds feedback
checkLeds(1000); // we pass the delay between color change
// feedback - fully booted indication
digitalWrite(LED3, 1);
}
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
Back with an alpha ( some things doesn't work as they should, and I'm still missing the finalized data ( timed mouth indices & mouths data ) ), but it's cleaner than previously ;)
also, I managed to wreck my code somehow somewhere & now I can't control the last led any more :|
if anyone's feeling brave enough to visually troubleshoot, be my guest ;)
( as always, I'll do so after some rest ;) )
this being said, the pre-pre-pre-alpha code