Note: This project is now out of date, so it may not work on later versions of the Espruino firmware.
For our wedding we really wanted some lighting that could change colour in a slow, controlled way. It also needed to change between predefined colours that fitted with our colour scheme. We couldn't find a cheap way of doing this, so we decided to make something! This is the result!
There's more information on controlling and wiring up the lights on the WS2811 page. The actual code you need to copy and paste in is:
var col = {r:127,g:127,b:127}; // currently selected colour var touchDown = false; // is a finger on the touchscreen? var cols = [{"r":228,"g":228,"b":11},{"r":170,"g":226,"b":30},{"r":223,"g":97,"b":30},{"r":245,"g":203,"b":119}]; // current colour palette var colFrom = {"r":228,"g":228,"b":11}; // colour used in animation var colTo = {"r":170,"g":226,"b":30}; // colour used in animation var pos = 0.28; // where in the animation are we - from 0 to 1 var rgb = new Uint8Array(50*3); // colours that are used for the animation var onInit = function () { clearInterval(); require("Touchscreen").connect(touchCallback); SPI2.setup({baud:3200000,mosi:B15}); SPI2.send4bit([255,0,0], 0b0001, 0b0011); // test LCD.clear(); drawCols(); drawRGB(); setInterval(step, 50); // call 20 times a second }; // Send the data to the LEDs function updateLEDs() { SPI2.send4bit(rgb, 0b0001, 0b0011); } // Display a solid colour function setSolidCol(c) { var cols = new Uint8Array([c.r,c.g,c.b]); for (var i=0;i<rgb.length;i+=3) rgb.set(cols, i); updateLEDs(); } // Display the nice blended colours function setBlendedCol() { for (var i=0;i<50;i++) { var a = E.clip((i/25.0)+(pos*3)-2, 0, 1); rgb[i*3] = colFrom.r*(1-a) + colTo.r*a; rgb[i*3+1] = colFrom.g*(1-a) + colTo.g*a; rgb[i*3+2] = colFrom.b*(1-a) + colTo.b*a; } updateLEDs(); } // When a touch occurs, this is called function touchCallback(x,y) { touchDown = x!==undefined; var b = (y*1.2/LCD.getHeight() - 0.1)*256; if (b<0) b=0; if (b>255) b=255; // check for colour sliders if (x>280) { col.b = b; setSolidCol(col); drawRGB(); } else if (x>240) { col.g = b; setSolidCol(col); drawRGB(); } else if (x>200) { col.r = b; setSolidCol(col); drawRGB(); } else { // check for taps on the colour boxes for (var i=0;i<cols.length;i++) { var r = getColRect(i); if (x>r[0] && y>r[1] && x<r[2] && y<r[3]) { cols[i] = col.clone(); drawCols(); } } } } // Draw the RGB sliders function drawRGB() { for (var i=0;i<240;i+=16) { LCD.setColor(i*1.0/LCD.getHeight(),0,0); LCD.fillRect(200,i,239,i+15); LCD.setColor(0,i*1.0/LCD.getHeight(),0); LCD.fillRect(240,i,279,i+15); LCD.setColor(0,0,i*1.0/LCD.getHeight()); LCD.fillRect(280,i,319,i+15); } var cr = col.r*LCD.getHeight()/256; var cg = col.g*LCD.getHeight()/256; var cb = col.b*LCD.getHeight()/256; LCD.setColor(1,1,1); LCD.fillRect(200,cr-8,239,cr+8); LCD.fillRect(240,cg-8,279,cg+8); LCD.fillRect(280,cb-8,319,cb+8); } // Get the rectangle of a colour box function getColRect(i) { var x = (i/4)|0; var y = i - (x*4); return [x*60,y*60,(x+1)*60,(y+1)*60]; } // Draw the colour boxes function drawCols() { var s = 60; for (var i=0;i<cols.length;i++) { var c = cols[i]; var r = getColRect(i); LCD.setColor(c.r/255.0,c.g/255.0,c.b/255.0); LCD.fillRect(r[0],r[1],r[2],r[3]); } } function step() { if (touchDown) return; // touch down, so don't set // smoothly move between colours pos += 0.02; if (pos>1) { pos = 0; colFrom = colTo; colTo = cols[(Math.random()*cols.length)|0]; } // send data to the LEDs setBlendedCol(); } onInit();