APA102 individually addressable LEDs

Posted on
Page
of 2
Prev
/ 2
  • 
    
    
    
    SPI1.setup({sck:14,mosi:13,mode:1,order:­"msb",baud:4000000});
    I2C1.setup({scl:5,sda:4});
    
    var http = require("http");
    var eeprom=require("AT24").connect(I2C1, 32, 32);
    
    setBusyIndicator(2);
    require("ESP8266").logDebug(0);
    require("ESP8266").setLog(0);
    
    
    // Parameters:
    numleds=10;
    
    //global functions
    function animate() {
      setTimeout("animate()",20);
      //var x;
      //x=getTime();
      leds.flip();
      leds.dotwinkle();
      //console.log(getTime()-x);
    }
    
    
    // Network
    
    function onPageRequest(req, res) {
      var a = url.parse(req.url, true);
      if (a.pathname.split(".")[1]=="cmd"){
      	if (handleCmd(a.pathname,a.query,res)) {
      		res.writeHead(200,{'Access-Control-Allow­-Origin':'*'});
      		res.end("OK");
      	} else {
      		res.end();
      	}
      } else {
    	res.writeHead(404);
    	res.end("NOT FOUND");	
      }
    }
    require("http").createServer(onPageReque­st).listen(80);
    
    function handleCmd(pn,q,r) {
    	try {
      if (pn=="/save.cmd") {
        //lreq=a.query;
        if (q.index==undefined || q.index>memmap.statMax || q.index < 0) {
        	r.write("MAX INDEX:");
        	r.write(memmap.statMax);
        	return 0;
        }
        leds.save(q.index);
        return 1;
      } if (pn=="/load.cmd") {
        if (q.index==undefined || q.index>memmap.statMax || q.index < 0) {
        	r.write("MAX INDEX:");
        	r.write(memmap.statMax);
        	return 0;
        }
        if (!leds.load(q.index)){
        	r.writeHead(400);
        	r.write("No such pattern");
        	return 0;
        }
        return 1;
      }if (pn=="/showState.cmd") {
        //lreq=a.query;
        r.write(JSON.stringify({"base":leds.tbuf­,"twinkle":[leds.tm,leds.ti,leds.ta]}));­
        return 1;
      }if (pn=="/setAll.cmd") {
        //lreq=a.query;
        leds.setAll(eval(q.color),eval(q.mode),e­val(q.max),eval(q.min));
        return 1;
      }else if (pn=="/setPixel.cmd") {
        leds.setPixel2(q.led,0,eval(q.color),eva­l(q.mode),eval(q.max),eval(q.min));
        return 1;
      } else {
      	r.writeHead(404);
    	r.write("NO CMD");
      	return 0;
      }
    	} 
    	catch (err) {
    		r.writeHead(500);
    		r.write("ERROR");
    		return 0;
    	}
    }
    
    
    var memmap={
    	slen:32,
    	rlen:40,
    	statOff:0x100,
    	statMax:14,
    	sEep:eeprom,
    	oEep:eeprom,
    	oOff:0x800,
    	statMax:64
    };
    
    
    // LEDS
    gtab=new Uint16Array(256);
    gtab=new Uint16Array([0,1,2,3,4,5,6,7,8,9,11,13,1­5,17,19,21,23,25,27,30,33,36,39,42,45,48­,51,54,58,62,66,70,74,78,82,86,91,96,101­,106,111,116,121,126,132,138,144,150,156­,162,168,174,181,188,195,202,209,216,223­,230,238,246,254,262,270,278,286,294,303­,312,321,330,339,348,357,366,376,386,396­,406,416,426,436,446,457,468,479,490,501­,512,523,534,546,558,570,582,594,606,618­,630,643,656,669,682,695,708,721,734,748­,762,776,790,804,818,832,846,861,876,891­,906,921,936,951,966,982,998,1014,1030,1­046,1062,1078,1094,1111,1128,1145,1162,1­179,1196,1213,1230,1248,1266,1284,1302,1­320,1338,1356,1374,1393,1412,1431,1450,1­469,1488,1507,1526,1546,1566,1586,1606,1­626,1646,1666,1686,1707,1728,1749,1770,1­791,1812,1833,1854,1876,1898,1920,1942,1­964,1986,2008,2030,2053,2076,2099,2122,2­145,2168,2191,2214,2238,2262,2286,2310,2­334,2358,2382,2406,2431,2456,2481,2506,2­531,2556,2581,2606,2631,2657,2683,2709,2­735,2761,2787,2813,2839,2866,2893,2920,2­947,2974,3001,3028,3055,3083,3111,3139,3­167,3195,3223,3251,3279,3308,3337,3366,3­395,3424,3453,3482,3511,3541,3571,3601,3­631,3661,3691,3721,3751,3782,3813,3844,3­875,3906,3937,3968,3999,4031,4063,4095])­;
    var leds = {};
    leds.map=memmap;
    leds.spi=SPI1;
    leds.num=numleds;
    leds.afr=0;
    leds.fbuf=new Uint8Array(numleds*4);
    leds.buff=new Uint8Array(numleds*3);
    leds.tbuf=new Uint8ClampedArray(numleds*3);
    leds.t=new Int8Array(numleds*3);
    leds.tm=new Uint8Array(numleds*3);
    leds.ti=new Int8Array(numleds*3);
    leds.ta=new Int8Array(numleds*3);
    leds.overlay=new Uint8Array(numleds*3);
    leds.tclb=new Uint8ClampedArray(numleds*3);
    for (var tem=0;tem<numleds;tem++){
    	for (var j=0;j<3;j++){
    		leds.ti[tem*3+j]=-10;
    		leds.ta[tem*3+j]=10;
    	}
    }
    leds.ison=1;
    leds.animode=0;
    leds.aniframe=0;
    leds.anilast=0;
    leds.aniaddr=0;
    leds.zz="\x00\x00";
    
    leds.dotwinkle = function () {
    	var t=this.t;
    	var tm= this.tm;
    	var ta=this.ta;
    	var ti=this.ti;
    	var b=this.buff;
    	var z=this.tbuf;
    	var o=this.overlay;
    	if (this.animode) {
    		if (this.aniframe > this.anilast) {
    			this.animode=0;
    			this.anilast=0;
    			this.aniframe=0;
    			leds.aniaddr=0;
    			this.overlay.fill(0);
    		} else {
    			this.overlay=this.map.oEep.read(this.ani­addr+this.map.slen*this.aniframe++,this.­num*3);
    		}
    	}
    	
    	for (var i=0;i<this.num*3;i++){
    		var mode=tm[i];
    		var mo=mode&0x0F;
    		var pr=mode>>4;
    		if (!(this.animode&2)) {
    			if (mo==1) { //0x01 - high nybble is chance to change, from 0 (1/16) to 15 (16/16 chance to change)
    				var n=Math.random(); //3ms
    				var th=(pr+1)/32;
          				if (n<0.5+th){ //8ms
          					if(n<=(0.5-th) && t[i]>ti[i]){t[i]--;}
    	      			} else {
          					if (t[i]<ta[i]){t[i]++;}
          				}
    			} else if (mo==2) { //fade/pulse. 
              			if (this.afr%((1+pr)&7)==0){
                				t[i]+=(pr&8?1:-1);
    					if (t[i] == ti[i] || t[i] == ta[i]) {
    						tm[i]=mode^128;
    					}
            			}
    			} else {
    				if (t[i]!==0){if(t[i]>0){t[i]--;} else {t[i]++;}}
    			}
    		}
    		var c=b[i];
    		if (mo || this.afr%((1+pr)&7)==0) {
    			b[i]+=E.clip(z[i]-c,-1,1);
    		}
    		leds.tclb[i]=c+(c?t[i]:0)+o[i]; //10ms
    	}
    	this.afr=this.afr==255?0:this.afr+1;
    };
    
    leds.setAll= function (color,tmode,tmax,tmin) {
    	for (var i=0;i<this.num;i++) {
    		for (j=0;j<3;j++){
    			//this.t[3*i+j]=0;
    			this.tbuf[3*i+j]=color[j];
    			if (tmode) {
    				this.tm[3*i+j]=tmode[j];
    				this.ti[3*i+j]=tmin[j];
    				this.ta[3*i+j]=tmax[j];
    			}
    		}
    	}
    };
    
    leds.load = function (index) {
    	var s=this.map.slen;
    	var addr=this.map.statOff+(4*index*s);
    	if (this.map.sEep.read(addr+s-1,1)[0]==255)­ {
    		return 0;
    	}
    	this.tbuf=this.map.sEep.read(addr,this.n­um*3);
    	this.tm=this.map.sEep.read((addr+s),this­.num*3);
    	this.ti=new Int8Array(this.map.sEep.read((addr+s*2),­this.num*3));
    	this.ta=new Int8Array(this.map.sEep.read((addr+s*3),­this.num*3));
    	return 1;
    };
    
    leds.del = function (index) {
    	var t=new Uint8Array(this.map.slen*4);
    	t.fill("\xFF");
    	this.map.sEep.write(this.map.statOff+(4*­index*this.map.slen),t);
    };
    leds.setAnimate = function (mode,address,frames){
      leds.anilast=frames;
      leds.aniaddr=address;
      leds.animode=mode;
    };
    
    leds.save = function (index) {
    	var s=this.map.slen;
    	var addr=this.map.statOff+(4*index*s);
    	this.map.sEep.write(addr,E.toString(this­.tbuf)+leds.zz+E.toString(this.tm)+leds.­zz+E.toString(this.ti)+leds.zz+E.toStrin­g(this.ta)+leds.zz);
    };
    
    
    leds.setPixel = function (x, y, color) {
    	this.tbuf[x*3]=color[0];
    	this.tbuf[x*3+1]=color[1];
    	this.tbuf[x*3+2]=color[2];
    };
    
    leds.adjPixel=function (led,color) {
    	this.tbuf[led*3]+=color[0];
    	this.tbuf[led*3+1]+=color[1];
    	this.tbuf[led*3+2]+=color[2];
    };
    
    leds.adjAll=function (color) {
    	for (var i=0;i<this.num;i++){
    		this.adjPixel(i,color);
    	}
    };
    
    leds.setPixel2 = function (x, y, color,mode,mintwi,maxtwi) {
    	x*=3;
    	for (var i=0;i<3;i++){
    		this.tbuf[x+i]=color[i];
    		this.tm[x+i]=mode[i];
    		this.ta[x+i]=maxtwi[i];
    		this.ti[x+i]=mintwi[i];
    	}
    };
    
    leds.flip = function () {
    	var j=0;
    	var i=0;
    	var z=leds.num*3;
    	while (i<z) {
          var rch=gtab[leds.tclb[i++]];
    		var gch=gtab[leds.tclb[i++]];
    		var bch=gtab[leds.tclb[i++]];
    		
    		var ma = Math.max(rch,gch,bch);
    		var mult=1;
            var gdim=31;
    		
    			if (ma <390) {
    				gdim=3;
    				mult=10.33;
    			} else if (ma <700) {
    				gdim=7;
    				mult=4.4;
    			} else if (ma <1700) {
    				gdim=15;
    				mult=2.06;
    			} 
    		
    		this.fbuf[j++]=(this.ison?(gdim|224):224­);
    		this.fbuf[j++]=(bch?Math.max((bch*mult)>­>4,1):0);
    		this.fbuf[j++]=(gch?Math.max((gch*mult)>­>4,1):0);
    		this.fbuf[j++]=(rch?Math.max((rch*mult)>­>4,1):0);
    
    	}
    
    	this.spi.write(0,0,0,0,this.fbuf,0xFF,0x­FF,0xFF,0xFF);
    };
    
    leds.zflip = function () {
    	var ca=startAddr+(frameLength*curFrame++);
    	this.fbuf=eep.read(ca,frameLength);
    	this.spi.write(0,0,0,0,this.fbuf,0xFF,0x­FF,0xFF,0xFF);
    };
    
    
    setBusyIndicator(2);
    leds.setPixel(0,0,[0,255,255]);
    
    animate();
    
    

    Latest version of code. I can put animations on the eeprom like this, and play them back with leds.setAnimation(), and I can load and save the static (well static including twinkle/pulse data) frame data as well with leds,save() and leds.load().

    
    leds.map.oEep.write(2048,[0,0,0,0,0x40,0­x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,­0,0,0,0,0,0,0,0 ,0,0,0xa0,0,0,0xe6,0,0,0x9b,0,0,0,0,0,0,­0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0xd9,0,0,0xff,0,0,0xd9,0,0,0,0,0,0,­0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0x80,0xff,0,0x79,0xff,0,0x80,0xff,0,0­,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0­,0]);
    leds.map.oEep.write(2176,[0x80,0xff,0xff­,0,0xec,0xff,0x80,0xff,0xff,0,0,0,0,0,0,­0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0x97,0xff,0xff,0xc4,0xff,0xff,0x97,0xff­,0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,­0,0,0,0,0,0 ,0,0xc4,0xc4,0xc4,0xc4,0xc4,0,0xe6,0xe6,­0,0xae,0xae,0,0,0,0,0,0,0,0,0,0,0,0,0,0,­0,0,0,0,0,0 ,0,0,0,0x9b,0x9b,0x9b,0,0x75,0x75,0,0x48­,0x48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,­0,0,0]);
    leds.map.oEep.write(2304,[0,0,0,0x64,0x6­4,0x64,0,0x3c,0x3c,0,0,0,0,0,0,0,0,0,0,0­,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0x2c,0x2c,0x2c,0,0,0,0,0,0,0,0,0,­0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0­,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0­,0,0,0,0,0,0,0,0,0,0,0,0]);
    leds.map.oEep.write(2432,[0xff,0xff,0x5e­,0xff,0xff,0x80,0,0,0,0,0,0,0,0,0,0,0,0,­0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0xca,0xca,0x35,0,0,0,0,0,0,0,0,0,­0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0x15,0x15,0x03,0x0c,0x0c,0x03,0x0a,0x0­c,0x03,0x0d,0x0c,0,0xff,0xff,0,0,0,0,0,0­,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0xff,0xff,0,0,0­,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
    leds.map.oEep.write(2560,[0x80,0,0xff,0,­0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,­0,0,0,0,0,0,0,0 ,0,0,0xff,0x80,0,0xff,0,0,0,0,0,0,0,0,0,­0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0xff,0,0,0,0xff,0x80,0,0xff,0,0,0,0,0­,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0xff,0xff,0,0,0xff,0,0,0,0xff,0x80,0,0x­ff,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0­,0]);
    leds.map.oEep.write(2688,[0xff,0x80,0,0x­ff,0xff,0,0,0xff,0,0,0,0xff,0x80,0,0xff,­0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0xff,0,0,0xff,0x80,0,0xff,0xff,0,0,0xff­,0,0,0,0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0,­0,0,0 ,0,0,0,0xff,0,0,0xff,0x80,0,0xff,0xff,0,­0,0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0­,0 ,0,0,0,0,0,0,0xff,0,0,0xff,0x80,0,0xff,0­xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]­);
    leds.map.oEep.write(2816,[0,0,0,0,0,0,0,­0,0,0xff,0,0,0xff,0x80,0,0,0,0,0,0,0,0,0­,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0­,0,0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,­0,0,0]);
    
    

    with that, try:
    leds.setAnimate(1,2048,12)

    leds.setAnimate(1,2432,4);

    leds.setAnimate(1,2560,10);

    Upcoming is a way to track which index slots are occupied, same with animations. My big problem now is just finding ways to make the animation data....

    Well, that and performance - with ten pixels, I get about 8FPS, which is pretty shit.

    I'll probably redo it when we get the cheap Espruino - use that to handle the updates, and use compiled JS to juice the execution speed, and communicate over serial with an ESP8266 running Espruino to handle the web interface. Hell, it could just send javascript commands down serial, and use the Espruino's serial console! That'd be neat, and if it got me more than 10 LEDs that would be awesome ;-)

  • 
    
    
    
    SPI1.setup({sck:14,mosi:13,mode:1,order:­"msb",baud:4000000});
    I2C1.setup({scl:5,sda:4});
    
    var http = require("http");
    var eeprom=require("AT24").connect(I2C1, 32, 32);
    
    setBusyIndicator(2);
    require("ESP8266").logDebug(0);
    require("ESP8266").setLog(0);
    
    
    // Parameters:
    numleds=10;
    
    //global functions
    function animate() {
      setTimeout("animate()",20);
      var x;
      if (leds.animode) {x=getTime();}
      leds.flip();
      leds.dotwinkle();
      if (leds.animode) {console.log(getTime()-x);}
    }
    
    
    // Network
    
    function onPageRequest(req, res) {
      var a = url.parse(req.url, true);
      if (a.pathname.split(".")[1]=="cmd"){
      	if (handleCmd(a.pathname,a.query,res)) {
      		res.writeHead(200,{'Access-Control-Allow­-Origin':'*'});
      	} 
      	res.end();
      } else {
    	res.writeHead(404);
    	res.end("NOT FOUND");	
      }
    }
    require("http").createServer(onPageReque­st).listen(80);
    
    function handleCmd(pn,q,r) {
    	try {
      if (pn=="/save.cmd") {
        //lreq=a.query;
        if (q.index==undefined || q.index>memmap.statMax || q.index < 0) {
        	r.write("MAX INDEX:");
        	r.write(memmap.statMax);
        	return 0;
        }
        leds.save(q.index);
        return 1;
      } if (pn=="/load.cmd") {
        if (q.index==undefined || q.index>memmap.statMax || q.index < 0) {
        	r.write("MAX INDEX:");
        	r.write(memmap.statMax);
        	return 0;
        }
        if (!leds.load(q.index)){
        	r.writeHead(404);
        	r.write("No such pattern");
        	return 0;
        }
        r.write("OK");
        return 1;
      }if (pn=="/showState.cmd") {
        //lreq=a.query;
        r.write(JSON.stringify({"base":leds.tbuf­,"twinkle":[leds.tm,leds.ti,leds.ta]}));­
        return 1;
      }if (pn=="/setAll.cmd") {
        //lreq=a.query;
        leds.setAll(eval(q.color),eval(q.mode),e­val(q.max),eval(q.min));
        r.write("OK");
        return 1;
      }else if (pn=="/setPixel.cmd") {
        leds.setPixel2(q.led,0,eval(q.color),eva­l(q.mode),eval(q.max),eval(q.min));
        r.write("OK");
        return 1;
      } else {
      	r.writeHead(404);
    	r.write("NO CMD");
      	return 0;
      }
    	} 
    	catch (err) {
    		r.writeHead(500);
    		r.write("ERROR");
    		return 0;
    	}
    }
    
    
    var memmap={
    	slen:32,
    	rlen:40,
    	statOff:0x100,
    	statMax:14,
    	sEep:eeprom,
    	oEep:eeprom,
    	oOff:0x800,
    	oMax:64,
    	oIEE:eeprom,
    	oIOF:0
    };
    
    
    // LEDS
    gtab=new Uint16Array(256);
    gtab=new Uint16Array([0,1,2,3,4,5,6,7,8,9,11,13,1­5,17,19,21,23,25,27,30,33,36,39,42,45,48­,51,54,58,62,66,70,74,78,82,86,91,96,101­,106,111,116,121,126,132,138,144,150,156­,162,168,174,181,188,195,202,209,216,223­,230,238,246,254,262,270,278,286,294,303­,312,321,330,339,348,357,366,376,386,396­,406,416,426,436,446,457,468,479,490,501­,512,523,534,546,558,570,582,594,606,618­,630,643,656,669,682,695,708,721,734,748­,762,776,790,804,818,832,846,861,876,891­,906,921,936,951,966,982,998,1014,1030,1­046,1062,1078,1094,1111,1128,1145,1162,1­179,1196,1213,1230,1248,1266,1284,1302,1­320,1338,1356,1374,1393,1412,1431,1450,1­469,1488,1507,1526,1546,1566,1586,1606,1­626,1646,1666,1686,1707,1728,1749,1770,1­791,1812,1833,1854,1876,1898,1920,1942,1­964,1986,2008,2030,2053,2076,2099,2122,2­145,2168,2191,2214,2238,2262,2286,2310,2­334,2358,2382,2406,2431,2456,2481,2506,2­531,2556,2581,2606,2631,2657,2683,2709,2­735,2761,2787,2813,2839,2866,2893,2920,2­947,2974,3001,3028,3055,3083,3111,3139,3­167,3195,3223,3251,3279,3308,3337,3366,3­395,3424,3453,3482,3511,3541,3571,3601,3­631,3661,3691,3721,3751,3782,3813,3844,3­875,3906,3937,3968,3999,4031,4063,4095])­;
    var leds = {};
    leds.map=memmap;
    leds.spi=SPI1;
    leds.num=numleds;
    leds.afr=0;
    leds.fbuf=new Uint8Array(numleds*4);
    leds.buff=new Uint8Array(numleds*3);
    leds.tbuf=new Uint8ClampedArray(numleds*3);
    leds.t=new Int8Array(numleds*3);
    leds.tm=new Uint8Array(numleds*3);
    leds.ti=new Int8Array(numleds*3);
    leds.ta=new Int8Array(numleds*3);
    leds.overlay=new Uint8Array(numleds*3);
    leds.tclb=new Uint8ClampedArray(numleds*3);
    for (var tem=0;tem<numleds;tem++){
    	for (var j=0;j<3;j++){
    		leds.ti[tem*3+j]=-10;
    		leds.ta[tem*3+j]=10;
    	}
    }
    leds.ison=1;
    leds.animode=0;
    leds.aniframe=0;
    leds.anilast=0;
    leds.aniaddr=0;
    leds.zz="\x00\x00";
    
    leds.dotwinkle = function () {
    	var t=this.t;
    	var tm= this.tm;
    	var ta=this.ta;
    	var ti=this.ti;
    	var b=this.buff;
    	var z=this.tbuf;
    	var o=this.overlay;
    	if (this.animode) {
    		if (this.aniframe > this.anilast) {
    			this.animode=0;
    			this.anilast=0;
    			this.aniframe=0;
    			leds.aniaddr=0;
    			this.overlay.fill(0);
    		} else {
    			this.overlay=this.map.oEep.read(this.ani­addr+this.map.slen*this.aniframe++,this.­num*3);
    		}
    	}
    	if (this.animode & 4 ){
    		leds.tclb.set(o);
        } else if (this.animode & 2){
            for (var i=0;i<30;i++){
              leds.tclb[i]=b[i]+t[i]+o[i];
            }
    	} else {
    		for (var i=0;i<30;i++){
    			var mode=tm[i];
    			var mo=mode&0x0F;
    			var pr=mode>>4;
    			if (!(this.animode&2)) {
    				if (mo==1) { //0x01 - high nybble is chance to change, from 0 (1/16) to 15 (16/16 chance to change)
    					var n=Math.random(); //3ms
    					var th=(pr+1)/32;
    	      				if (n<0.5+th){ //8ms
    	      					if(n<=(0.5-th) && t[i]>ti[i]){t[i]--;}
    		      			} else {
    	      					if (t[i]<ta[i]){t[i]++;}
    	      				}
    				} else if (mo==2) { //fade/pulse. 
    	          			if (this.afr%((1+pr)&7)==0){
    	            				t[i]+=(pr&8?1:-1);
    						if (t[i] == ti[i] || t[i] == ta[i]) {
    							tm[i]=mode^128;
    						}
    	        			}
    				} else {
    					if (t[i]!==0){if(t[i]>0){t[i]--;} else {t[i]++;}}
    				}
    			}
    			var c=b[i];
    			if (mo || this.afr%((1+pr)&7)==0) {
    				b[i]+=E.clip(z[i]-c,-1,1);
    			}
    			leds.tclb[i]=c+(c?t[i]:0)+o[i]; //10ms
    		}
    	}
    	this.afr=this.afr==255?0:this.afr+1;
    };
    
    leds.setAll= function (color,tmode,tmax,tmin,instant) {
    	for (var i=0;i<this.num;i++) {
    		for (j=0;j<3;j++){
    			//this.t[3*i+j]=0;
    			this.tbuf[3*i+j]=color[j];
                if (instant) {this.buff[3*i+j]=color[j];}
    			if (tmode) {
    				this.tm[3*i+j]=tmode[j];
    				this.ti[3*i+j]=tmin[j];
    				this.ta[3*i+j]=tmax[j];
    			}
    		}
    	}
    };
    
    leds.load = function (index) {
    	var s=this.map.slen;
    	var addr=this.map.statOff+(4*index*s);
    	if (this.map.sEep.read(addr+s-1,1)[0]==255)­ {
    		return 0;
    	}
    	this.tbuf=this.map.sEep.read(addr,this.n­um*3);
    	this.tm=this.map.sEep.read((addr+s),this­.num*3);
    	this.ti=new Int8Array(this.map.sEep.read((addr+s*2),­this.num*3));
    	this.ta=new Int8Array(this.map.sEep.read((addr+s*3),­this.num*3));
    	return 1;
    };
    
    leds.del = function (index) {
    	var t=new Uint8Array(this.map.slen*4);
    	t.fill("\xFF");
    	this.map.sEep.write(this.map.statOff+(4*­index*this.map.slen),t);
    };
    leds.setAnimate = function (mode,address,frames){
      leds.anilast=frames;
      leds.aniaddr=address;
      leds.animode=mode;
    };
    
    leds.save = function (index) {
    	var s=this.map.slen;
    	var addr=this.map.statOff+(4*index*s);
    	this.map.sEep.write(addr,E.toString(this­.tbuf)+leds.zz+E.toString(this.tm)+leds.­zz+E.toString(this.ti)+leds.zz+E.toStrin­g(this.ta)+leds.zz);
    };
    
    
    leds.setPixel = function (x, y, color) {
    	this.tbuf[x*3]=color[0];
    	this.tbuf[x*3+1]=color[1];
    	this.tbuf[x*3+2]=color[2];
    };
    
    leds.adjPixel=function (led,color) {
    	this.tbuf[led*3]+=color[0];
    	this.tbuf[led*3+1]+=color[1];
    	this.tbuf[led*3+2]+=color[2];
    };
    
    leds.adjAll=function (color) {
    	for (var i=0;i<this.num;i++){
    		this.adjPixel(i,color);
    	}
    };
    
    leds.setPixel2 = function (x, y, color,mode,mintwi,maxtwi,instant) {
    	x*=3;
    	for (var i=0;i<3;i++){
    		this.tbuf[x+i]=color[i];
          if (instant) {this.buff[x+i]=color[i];}
    		this.tbuf[x+i]=color[i];
    		this.tm[x+i]=mode[i];
    		this.ta[x+i]=maxtwi[i];
    		this.ti[x+i]=mintwi[i];
    	}
    };
    
    leds.flip = function () {
    	var j=0;
    	var i=0;
    	var z=leds.num*3;
    	while (i<z) {
          var rch=gtab[leds.tclb[i++]];
    		var gch=gtab[leds.tclb[i++]];
    		var bch=gtab[leds.tclb[i++]];
    		
    		var ma = Math.max(rch,gch,bch);
    		var mult=1;
            var gdim=31;
    		
    			if (ma <390) {
    				gdim=3;
    				mult=10.33;
    			} else if (ma <700) {
    				gdim=7;
    				mult=4.4;
    			} else if (ma <1700) {
    				gdim=15;
    				mult=2.06;
    			} 
    		
    		this.fbuf[j++]=(this.ison?(gdim|224):224­);
    		this.fbuf[j++]=(bch?Math.max((bch*mult)>­>4,1):0);
    		this.fbuf[j++]=(gch?Math.max((gch*mult)>­>4,1):0);
    		this.fbuf[j++]=(rch?Math.max((rch*mult)>­>4,1):0);
    
    	}
    
    	this.spi.write(0,0,0,0,this.fbuf,0xFF,0x­FF,0xFF,0xFF);
    };
    
    leds.zflip = function () {
    	var ca=startAddr+(frameLength*curFrame++);
    	this.fbuf=eep.read(ca,frameLength);
    	this.spi.write(0,0,0,0,this.fbuf,0xFF,0x­FF,0xFF,0xFF);
    };
    
    
    setBusyIndicator(2);
    
    

    Alternate modes for animations - this lets me get ~30 fps with animode =5 (where the background is totally replaced) or 18fps with animode=3 (where the background and current twinkle state is used, but all other calculations are stopped. I just wish I could do the twinkle calculations faster.

  • For the twinkle, can you just use a table of intensities that you cycle through over time? Maybe just multiply 'mult' by the value that's in it?

    I'm not quite sure what the twinkle effect is (I haven't tried the code) but if it could be replaced by a table, it'd probably speed things up a lot.

  • The "twinkle" effect sets the contents of leds.t, a signed int8 array.... I don't see how it could be done with a table though:

    There are two twinkle modes (so far) - the basic random twinkle - each channel of each LED has a chance (from 1/16 to 16/16, depending on high nybble of the mode for that channel of that LED in leds.tm) to change by 1 (plus or minus) within the range set by leds.ta and leds.ti.

    At high chances of change, and lowish brightness, you can get an effect reminiscent of flickering candle flames. At higher brightnesses and/or lower chances of change, the changes are less visible, but you can have all of the LEDs, or groups of the LEDs set to the same color, but with twinkle on, so they're not each exactly the same color - which is a key goal.

    The other mode is simple fade back and forth of the twinkle brightness between the minimum and maximum brightnesses.

    All of these are per channel not per LED.
    At the end, the twinkle, base, and animation overlay are added together to get the output buffer. Then flip takes that buffer, and uses the gtab lookup table and the other logic there to take advantage of the global dimming to get a less horribly non-linear relationship between the number for the color, and what you see.

  • i want to know if those APA102 led code can work with Arduino or not . thanks

  • With Arduino, or Espruino? If you're asking about Arduino this is the wrong place I'm afraid...

  • APA-102 can be used with both Arduino and Espruino - anything that can do SPI should be able to drive them. As Gordon noted, this is the Espruino forum, so if you're looking for Arduino information, you're in the wrong place. Arduino is programmed in compiled C, Espruino is programmed in interpreted javascript - the code is totally different.

    The thing you linked to - I'm not sure how that link was relevant. It looks like they just assemble APA102 LEDs into strips... ?

  • I just noticed the E.mapInPlace() function. Would using this be expected to improve performance where I was looping over the array?

    What version of Espruino was that added with?

  • Would using this be expected to improve performance where I was looping over the array?

    In a lot of cases, yes. You can also use it with a lookup table which will be really fast.

    What version of Espruino was that added with?

    1v77 by the look of it :)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

APA102 individually addressable LEDs

Posted by Avatar for DrAzzy @DrAzzy

Actions