Touchscreen RGB Light controller

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!

 

 

 

You'll need:

  • A HY Mini-STM32 VCT6 3.2"  board - see the Other Boards page. There are different boards and most should work - however they all have different pin connections
  • A WS2811 LED string
  • USB phone charger
 

Then connect:

  • The white+red wires of the WS2811s to 0v and 5v
  • The (input) green wire of the WS2811s to pin PB15. There are usually 2 green wires and the input is the female connector. Note that PB15 is a bit hard to see - it's on the top-right of the board, opposite the mislabelled pin PD14 (actually PB14)

 

The Code:

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();