• I've been doing some work on this.

    I've added functionality to display a graph of the past 24 hr temp + humidity, and it now knows the time as well. It drives a nixie display (SmartNixie) that displays the time or temp/humidity (I've got the tube with % and degree C symbols on it, but I haven't had a chance to install it. I plan to replace the 6th digit with that.

    One annoyance had always been the slow refresh time for the LCD. This was mostly from the whole frame buffer being run through E.reverseByte() and then sent out over I2C. However, the digole displays have a lot of functionality (almost enough that I don't need the double-buffer) implemented in hardware. So I use a few functions from the digoleHW module, modified a bit - to quickly make simple updates when navigating the fargo control screen, and adjusting the brightness of individual LEDs. It makes a huge difference in the user experience.

    I still haven't done that cleanup, naturally, but I've made a few improvements. Some efforts were made to shrink code size, but nothing to change the nasty structure.

    
     
    //Pins:
    //keypad to A1-B12, jumper to B7
    //Serial for nixies on B6
    //Desk Lamp on C6-A8
    //I2C2 used for the big long I2C chain
    //B0 = backlight, B1 = DHT22
    //A2-A5 used for 
    
    function onInit() {
    	rh=-1;
    	t=-1;
    	pr=0;
    	temh=new Uint8Array(48);
    	rhh=new Uint8Array(48);
    	prh=new Uint8Array(48);
    	usrmsg="";
    	MnuS=0;
    	MnuO=0;
    	inval="";
    	msg="";
    	lastSen=0;
    	setBusyIndicator(A13);
    	fargosturl="http://192.168.2.12/fargosta­tus.php";
    	dateurl="http://192.168.2.12/date.php";
    	fargourl="http://192.168.2.14/api/relay/­";
    	fargo=new Uint8Array(8);
    	colors="BLUECOOLWARMYELLPINK";
    	gw=new Uint8Array([7,9,9,7,7]);
    	gwx=new Uint8Array([3,14,27,40,51]);
    	fargostrs="Colored,White,Wizard,Tentacle­,Desk,Micro,Fan, ";
    	ledstate=new Float32Array([0.0,0.0,0.0,0.0,0.0]);
    	nixs=0;
    	nixr=0;
    	//presets=[new Float32Array([0.0,0.0,0.0,0.0,0.0]),new Float32Array([0.9,1,0.6,0,0]),new Float32Array([0,0.6,1,0.8,0.4]),new Float32Array([0.5,0.5,0.5,0.5,0.5])];
    	//prenames=["OFF","COLD\nWHIT","VERY\nWA­RM","HALF\nHALF"];
    	Clock = require("clock").Clock;
    	ledpins=[C6,C7,C8,A8,C9];
    	upled();
    	I2C2.setup({scl:B10,sda:B11});
    	kp=require("KeyPad").connect([C2,C3,A0,A­1],[C1,C0,B7,C12,B12], function(e) {onKey("AB#*123U456D789CL0RE"[e]);});
    	bmp=require("BMP085").connect(I2C2,3);
    	Serial1.setup(115200,{tx:B6});
    	nixie=require("SmartNixie2").connect(Ser­ial1,6);
    	e=require("DHT22").connect(A6);
    	require("Font8x12").add(Graphics);
    	rom=require("AT24").connect(I2C2,32,64);­
    	eth=require("WIZnet").connect();
    	eth.setIP();
    	setTimeout("var s=require('http').createServer(function (req, res) {var par=url.parse(req.url,true);var q=par.query; var nam=par.pathname; nam=nam.split('/');nam=nam[1];var rd=procreq(nam,q);res.writeHead(rd.code,­{'Content-Type': 'text/plain'}); res.write(rd.body);res.end();}).listen(8­0);",15000);
    	setInterval("upSensors();",15000);
    	setTimeout('setInterval("upHist()",60000­*30);',59000);//every 1 min for testing
    	setTimeout("setInterval(function(){uplcd­();},30000);",15000);
    	setTimeout("setInterval('getfargostatus(­);',30000);",20000);
    	setTimeout("getDate();setInterval('getDa­te();',1800000);",2000);
    	setInterval("delete onInit",500); 
    	g = require("DigoleBuf").connect(I2C2,128,64­);
    	g.clear();
    	g.setFont8x12();
    	g.drawString("LCD OK",0,0);
    	g.flip();
    	g.setContrast(0x26);
    	g.bktm=0;
    	g.HWs= function(c) {console.log(c);this.i2c.writeTo(0x27,c)­;};
    	g.HWdR = function(x1,y1,x2,y2,col) {this.HWs("SC"+(col?1:0));this.HWs("DR"+­String.fromCharCode(x1,y1,x2,y2));};
    	g.HWfR = function(x1,y1,x2,y2,col) {this.HWs("SC"+(col?1:0));this.HWs("FR"+­String.fromCharCode(x1,y1,x2,y2));};
        g.backlighton = function(tim_) {
    		digitalWrite(B0,1);
    		if (this.bktm) {
    			try {
    				clearTimeout(this.bktm);
    			}
    			catch (err) {
    				this.bktm=0;
    			}
    			this.bktm=0;
    		}
    		this.bktm=setTimeout("digitalWrite(B0,0)­;g.bktm=0;",tim_?tim_:10000);
    	};
    	g.backlighton();
    	presets=[new Float32Array([0.0,0.0,0.0,0.0,0.0])];
    	prenames=["OFF"];
    	var i=1;
    	while (rom.reads(32*(i-1),1) != "\xFF") {
    		 prenames[i]=rom.reads(32*(i-1),12);
    		 var temp=rom.read(32*(i-1)+16,5);
    		 presets[i]=new Float32Array([temp[0]/100.0,temp[1]/100.­0,temp[2]/100.0,temp[3]/100.0,temp[4]/10­0.0]);
    		 i++;
    	}
    	menu=[2,4,presets.length-1];
    }
    
    function getDate() {
    	var date="";
    	require("http").get(dateurl, function(res) {
    		res.on('data',function (data) {date+=data;});
    		res.on('close',function() {clk=new Clock(date);});
    	});
    	//delete getDate;
    }
    
    function procreq(path,query) {
    	var rd={};
    	rd.code=404;
    	rd.body="";
    	if (path=="status.json") {
    		rd.code=200;
    		var r='{"lamp":{';
    		for (var i=0;i<5;i++) {
    			r+='"'+colors.substr(i*4,4)+'":'+ledstat­e[i].toFixed(2);
    			if (i<4) {r+=",";}
    		}
    		r+='},\n"sensors":{"rh":'+rh.toFixed(1)+­',"temp":'+t.toFixed(1)+',"pressure":'+p­r.toFixed(2)+'}}';
    		rd.body=r;
    	} else if (path=="lamp.cmd") {
    		rd.code=200;
    		rd.body="Command applied";
    		ledstate[0]=query.BLUE==undefined ? 0:E.clip(query.BLUE,0,1);
    		ledstate[1]=query.COOL==undefined ? 0:E.clip(query.COOL,0,1);
    		ledstate[2]=query.WARM==undefined ? 0:E.clip(query.WARM,0,1);
    		ledstate[3]=query.YELL==undefined ? 0:E.clip(query.YELL,0,1);
    		ledstate[4]=query.PINK==undefined ? 0:E.clip(query.PINK,0,1);
    		setTimeout("uplcd(); upled();",100);
    	} else if (path=="code.run") {
    		if (query.code) {
    			rd.code=200;
    			rd.body=eval(query.code);
    		} else {
    			rd.code=400;
    			rd.body="400 Bad Request: No code supplied!";
    		}
    	} else if (path=="usrmsg.cmd") {
    		if (query.msg && query.msg.length > 1) {
    			rd.code=200;
    			usrmsg=query.msg;
    			rd.body="Message set: "+query.msg;
    			MnuS=10;
    			setTimeout("uplcd();",100);
    		} else {rd.code=400; rd.body="400 Bad Request: No message supplied";}
    	} else {rd.code=403; rd.body="403 Forbidden";}
    	return rd;
    }
    
    function getLedStr() {
    	var rtst="";
    	for (var it=0;it < 5; it++ ) {
    		rtst+=String.fromCharCode(Math.round(led­state[it]*100));
    	}
    	return rtst;
    }
    
    function getT9() {
    	if (KeyN==-1) {
    		return "";
    	} 
    	var bas=58;
    	var maxm=4;
    	if (KeyN==9 || KeyN==7) {
    		maxm++;
    	}
    	if (KeyN>7) {
    		bas+=1;
    	}
    	if (KeyN===0) {
    		if (KeyC==1) {
    			return " ";
    		} else {
    			return "0";
    		}
    	}
    	if (KeyC==maxm) {
    		return String.fromCharCode(KeyN+48);
    	} else {
    		return String.fromCharCode(KeyN*3+bas+KeyC);
    	}
    	return "";
    }
    function t9rst(){
    	KeyS="";
    	KeyC=0;
    	KeyN=-1;
    }
    
    
    function upSensors() {
    	if (lastSen==0) {
    		bmp.getPressure(function(b){if (pr==-1){pr=b.pressure;} else {pr=pr*0.8+b.pressure*0.2;}});
    		lastSen=1;
    	} else if (lastSen==1){
    		e.read(function(a){if(a.rh != -1) {if (rh==-1){rh=a.rh;t=a.temp;} else {rh=rh*0.8+a.rh*0.2;t=t*0.8+a.temp*0.2;}­}});
    		lastSen=0;
    	}
    }
    
    function upHist() {
    	for (i=47;i>0;i--) {
    		temh[i]=temh[i-1];
    		rhh[i]=rhh[i-1];
    		prh[i]=prh[i-1];
    	}
    	temh[0]=E.clip(Math.round(t-10)*2,0,50);­
    	//prh[0]=pr;
    	rhh[0]=E.clip(Math.round(rh/2),0,50);
    }
    
    /*
    
    MnuS:
    0 - Bars, blank space, fargo status
    1 - Bars, per-color adjust, fargo status
    2 - Bars, preset select, fargo status
    3 - Fargo control
    4 - Blank (???)
    5 - Save dialog - not normally accessible. 
    6 - 
    
    KeyW=Text entry timeout
    KeyN=Text entry number pressed
    KeyC=Text entry keypress count
    KeyS=Text entry string
    
    */
    
    
    function onKey(k){
    	msg="";
    	var ulcd=1;
    	if (MnuS==5) { // Save dialog
    		if (k=="L" && KeyS.length > 0) {
    			KeyS=KeyS.substr(0,KeyS.length-1);
    			KeyC=0;
    			KeyN=-1;
    			if (KeyW!=-1) {
    				clearTimeout(KeyW);
    				KeyW=-1;
    			}
    		} else if ( k=="C" ) {
    			MnuS=0;
    			inval="";
    			MnuO=0;
    			t9rst();
    			if (KeyW!=-1) {
    				clearTimeout(KeyW);
    				KeyW=-1;
    			}
    		} else if ( k =="E" && KeyS != "") {
    			prenames[prenames.length]=KeyS;
    			presets[presets.length]=new Float32Array(ledstate);
    			var outstr=KeyS;
    			while (outstr.length <= 15) {
    				outstr+=" ";
    			}
    			outstr+=getLedStr();
    			while (outstr.length <= 31) {
    				outstr+=" ";
    			}
    			rom.writes((presets.length-2)*32,outstr)­;
    			menu[2]=presets.length-1;
    			msg="Saved";
    			MnuS=0;
    			MnuO=0;
    			t9rst();
    			if (KeyW!=-1) {
    				clearTimeout(KeyW);
    				KeyW=-1;
    			}
    		} else  if (KeyS.length < 12) {
    			var a=parseInt(k);
    			if (a != NaN) {
    				if (KeyW!=-1) {
    					clearTimeout(KeyW);
    					KeyW=-1;
    				}
    				if (KeyC==0) {
    					KeyN=a;
    					KeyC=1;
    				} else if (KeyN==1) {
    					KeyS+="1";
    					KeyN=-1;
    					KeyC=0;
    				} else if (KeyN==a) {
    				if (((KeyN==9 || KeyN==7) && KeyC==5) || (KeyN==0 && KeyC==2) || (KeyN!=9 && KeyN!=7 && KeyN!=0 && KeyC==4) ) {
    					KeyC=1;
    				} else {
    					KeyC++;
    				}
    			} else {
    				KeyS+=getT9();
    				KeyN=a;
    				KeyC=1;
    			}
    				KeyW=setTimeout("KeyS+=getT9();KeyN=-1;K­eyC=0;KeyW=-1;;uplcd();",2000);
    			}
    		}
    	} else if ( k=="B" ) { //nixie toggle
        	nixs=!nixs;
        	ulcd=3; //don't redraw LCD
    	} else if (k=="A") { //all off
    		ledstate=new Float32Array([0,0,0,0,0]);
    		upled();
        	ulcd=3;//Don't redraw LCD
    	} else if ( k=="C" ) {
    		MnuS=0;
    		inval="";
    		MnuO=0;
    	} else if (k=="*"){ //advance to next top level menu
    		if (MnuS>=3){
    			MnuS=0;
    		} else {
    			MnuS++;
    			if (MnuS==1){
    				nixr=MnuO;
    			}
    		}
    		MnuO=0;
    		inval="";
    	} else if ((MnuS==2 || MnuS == 1) && k=="#"){ //save command
    		if (MnuO==0 || MnuS ==1) { //new
    			MnuS=5;
    			t9rst();
    		} else { //modift
    			presets[MnuO]=new Float32Array(ledstate);
    			rom.writes((MnuO-1)*32+16,getLedStr());
    			msg="Saved";
    			MnuO=0;
    			MnuS=0;
    		}
    
    	} else if (k=="E" && MnuS) { //enter
    		if (MnuS==1 && inval) { //apply single led changes
    			var v=E.clip(parseInt(inval),0,100);
    			v=v/100;
    			ledstate[MnuO]=v;
    			inval="";
    			upled();
    		} else if (MnuS==2) { //apply preset led changes
    			ledstate=new Float32Array(presets[MnuO]);
    			MnuO=0;
    			MnuS=0;
    			upled();
    		} else if (MnuS==3) { //fargo
    			var ex=fargo[MnuO];
    			setFargo(MnuO,!ex);
    			ulcd=6+(ex?0:16);
    		}
    	} else if ((k=="L"||k=="R") && (MnuS==1||MnuS==2||MnuS==0)) { //left/right arrows
    		if (k=="R") {
    			if (MnuO>=menu[MnuS]) {
    				MnuO=0;
    			} else {
    				MnuO++;
    			}
    		} else {
    			if (MnuO==0) {
    				MnuO=menu[MnuS];
    			} else {
    				MnuO--;
    			}
    		}
    		inval="";
    	} else if ((k=="U"||k=="D") && MnuS==1) { //up/down arrows for individual LED adjust
    		ledstate[MnuO]=E.clip(100*ledstate[MnuO]­+(k=="U"?4:-4),0,100)/100;
    		inval="";
    		ulcd=8;
    		upled();
    	} else if (MnuS==3) { //2-way navigation for fargo screen. 
    		ulcd=5+(MnuO<<4); //hardware update
    		if (k=="D") {
    			MnuO=(MnuO&4)|((MnuO+1)&3);
    		} else if (k=="U") {
    			MnuO=(MnuO&4)|((MnuO-1)&3);
    		} else if (k=="R"||k=="L") {
    			MnuO=MnuO^4;
    		} else { ulcd=3; } //Don't update lcd
    	} else if (MnuS==1) {
    		if (inval.length < 3) {
    			inval=inval+k;
    		} else {
    			inval="";
    		}
    	}
    	uplcd(ulcd);
    	g.backlighton();
    }
    
    function getfargostatus() {
    	var fargost="";
    	require("http").get(fargosturl, function(res) {
    		res.on('data',function (data) {fargost+=data;});
    		res.on('close',function() {var tfs=JSON.parse(fargost); vtfs=tfs; for (var i=0;i<8;i++) { fargo[i]=tfs.relaystate[i].state;} if(MnuS==3){uplcd();}});
    	});
    }
    function setFargo(relay,state) {
    	var postfix = (state) ? "/on":"/off";
    	require("http").get(fargourl+(relay+1).t­oString()+postfix, function(res) {
    		res.on('close',function () {
    			if(this.code!=200) {
    				fargo[relay]=state;
    				uplcd();
    			}
    		});
    	});
    }
    
    
    function slcd() {
    	if (MnuS == 3) {
    		var fst=fargostrs.split(',');
    		g.setFontBitmap();
    		for (var j=0;j<8;j++) {
    			if (fargo[j]==0) {
    				g.drawRect(4+(j>>2)*64,17+(j&3)*12,11+(j­>>2)*64,22+(j&3)*12);
    			} else {
    				g.fillRect(4+(j>>2)*64,17+(j&3)*12,11+(j­>>2)*64,22+(j&3)*12);
    			}
    			g.drawString(fst[j],14+(j>>2)*64,18+(j&3­)*12);
    		}
    		g.drawRect(2+(MnuO>>2)*64,15+(MnuO&3)*12­,61+(MnuO>>2)*64,24+(MnuO&3)*12);
    		g.setFont8x12();
    	} else if (MnuS < 6) {
    		usrmsg="";
    		var vals=new Float32Array(ledstate);
    		if (MnuS==2){
    			vals=new Float32Array(presets[MnuO]);
    		} else if (MnuS==1 && inval) {
    			vals[MnuO]=(parseInt(inval)/100);
    		}
    		var x=3;
    		for (var i=0;i<5;i++) {
    			var nx=x+gw[i];
    			g.drawRect(x,15,nx,47);
    			g.fillRect(x,(47-ledstate[i]*32),nx,47);­
    			if (vals[i] != ledstate[i]) {
    				if (vals[i] > ledstate[i]) {
    					g.fillRect(x+1,(47-vals[i]*32),nx-1,48-v­als[i]*32);
    				} else {
    					g.setColor(0);
    					g.fillRect(x+1,(47-vals[i]*32),nx-1,46-v­als[i]*32);
    					g.setColor(1);
    				}
    			}
    			x=nx+4;
    		}
    		g.drawString("B",4,50);
    		g.drawString("C",16,50);
    		g.drawString("W",29,50);
    		g.drawString("Y",41,50);
    		g.drawString("P",55,50);
    
    		for (var j=0;j<8;j++) {
    			if (fargo[j]==0) {
    				g.drawRect(122,15+j*6,126,19+j*6);
    			} else {
    				g.fillRect(122,15+j*6,126,19+j*6);
    			}
    		}
    	} else if (MnuS == 10) {
    		if (usrmsg==""){
    			MnuS=0;
    			MnuO=0;
    		} else {
    			g.drawString(usrmsg,1,17);
    			g.backlighton(30000);
    		}
    	}
    	if (MnuS===0) {
    		if (MnuO==0){
    			if(msg) {
    				g.drawString(msg,66,17);
    			} else {
    				g.drawString(getDstr(),66,17);
    			}
    		} else { //draw a graph
    			for (var i=47;i>=0;i--) {
    				g.setPixel(120-i,62-(MnuO==1?temh[i]:rhh­[i]),1);
    			}
    		}
    	}else if (MnuS==1) {
    		g.drawString(colors.substr(MnuO*4,4),70,­17);
    		g.drawString((100*ledstate[MnuO]).toFixe­d(),80,32);
    		if (inval) {
    			g.drawString(inval,80,46);
    		}
    	} else if (MnuS==2 || MnuS==5) {
    		var tstr=(MnuS==2 ? prenames[MnuO] : KeyS);
    		if (MnuS==5 && tstr.length < 12 ) {
    			if (getT9() == "" ) {
    				tstr+="_";
    			} else {
    				tstr+=getT9();
    			}
    		}
    		if (tstr.length > 8 ) {
    			g.drawString(tstr,70,17);
    		} else {
    			g.drawString(tstr.substring(0,7),70,17);­
    			g.drawString(tstr.substring(8,tstr.lengt­h),70,32);
    		}
    	}
    	g.flip();
    }
    
    function uplcd(isky) {
    	g.clear();
        var tpr=0.000295333727*pr;
    	g.drawString(t.toFixed(1)+" C "+rh.toFixed(1)+"% "+tpr.toFixed(1)+" "+getTStr(":"),0,0);
    	if ((isky&14)==2) { //do nothing
    		//console.log("donothing");
    	} else if ((isky&14)==4) {
    		//console.log("hwrender")
    		var oldO=isky>>4;
    		g.HWdR(2+(oldO>>2)*64,15+(oldO&3)*12,61+­(oldO>>2)*64,24+(oldO&3)*12,0);
    		g.HWdR(2+(MnuO>>2)*64,15+(MnuO&3)*12,61+­(MnuO>>2)*64,24+(MnuO&3)*12,1);
    	} else if ((isky&14)==6) {
    		//console.log("hwrender")
        	g.HWfR(5+(MnuO>>2)*64,18+(MnuO&3)*12,10+­(MnuO>>2)*64,21+(MnuO&3)*12,isky>>4);
        	if (!(isky>>4)) {g.HWs("SC1");}
    	} else if ((isky&14)==8) {
    		g.HWfR(gwx[MnuO]+1,16,gwx[MnuO]+gw[MnuO]­-1,46,0);
        	g.HWfR(gwx[MnuO],(47-ledstate[MnuO]*32),­gwx[MnuO]+gw[MnuO],47,1);
    	} else {
    		slcd(); //full LCD update
    	}
    	nixie.setAllLED(0,0,0);
    	if (nixs){
          var tp=(MnuS===0?MnuO:nixr);
    		if (tp===0) {
    			nixie.setString(getTStr(" ")+" ");
              nixie.setLED(2,128,32,0);
              if((isky&1)==0){nixr++;}
    		} else if (tp==1) {
    			nixie.setString("  "+t.toFixed(1)+" ");nixie.setLED(3,96,0,0);nixie.setLED(4­,96,0,0);nixie.setLED(2,96,0,0);if((isky­&1)==0){nixr++;}
    		} else if (tp==2) {
    			nixie.setString("  "+rh.toFixed(1)+" ");nixie.setLED(3,16,24,64);nixie.setLED­(4,16,24,64);nixie.setLED(2,16,24,64);if­((isky&1)==0){nixr=0;};
    		}
    	} else {
    		nixie.setString("      ");
    	}
    	nixie.send();
    }
    
    function getTStr(sep){
      var hrs=clk.getDate().getHours();
      var mins=clk.getDate().getMinutes();
      return (hrs>9?hrs:" "+hrs)+sep+(mins>9?mins:"0"+mins);
    }
    
    function getDstr(){
      var mon=clk.getDate().getMonth()+1;
      var day=clk.getDate().getDate();
      var year=clk.getDate().getFullYear();
      return (mon+"/"+day+"/"+year);
    }
    function upled() {
    	for (var i=0;i<5;i++) {
    		analogWrite(ledpins[i],ledstate[i]);
    	}
    }
    
    

    Edit: Updated code to use % and deg C symbols.

About

Avatar for DrAzzy @DrAzzy started