I've cleaned my code for big clock, now it is more regular. At the same time @Wilberforce and @JumJum have improved ESP32 build. So I've just cleaned my code even more by removing unnecessary MQTT and NTP implementations.
Now the code works on newest Travis build for ESP32. I think it will work on v2.01 as well.
If you do not need Russian months names just remove var MC and its usage - it is just names mapping.
Additional fields on display may get data from http requests or MQTT channel bigclock/in/temp_1 ... temp_4 (because I use the fields to show temperatures collected by other sensors). If first symbol of the message is ', field icon is omitted. If there is no new data, '---' is shown after timeout.
The program may be easily extended to show more fields on larger display or with multiplexing.
Some ugly code is still present, mostly because I don't understand internal implementation of some components (for example, I have not found sntp_init() implementation and so do not know SNTP renewal interval and also SNTP timezone and E.setTimeZone() interaction).
var DevName='bigclock';
var DevNum=1;
var FullName=DevName+'_'+DevNum;
var neopixel = require("neopixel");
E.setTimeZone(3);
var wifi=require("Wifi");
//var tnum;
function getNTP() {
wifi.setSNTP("pool.ntp.org",0);
// tnum=setTimeout(getNTP,24*3600*1000);
}
wifi.on('connected', ()=>setTimeout(getNTP,30000));
//---------------------------------------------------------------------
//----------------------- Additions to font ---------------------------
//---------------------------------------------------------------------
//require("Font6x8").add(Graphics);
var gt = Graphics.createArrayBuffer(16,64,24,{zigzag:true,color_order:'grb'});
gt.setRotation(3,1);
var font = atob("AAAAAPoAwADAAFhw2HDQAGSS/5JMAGCW+LzSDAxSolIMEsAAPEKBAIFCPABIMOAwSAAQEHwQEAABBgAQEBAQAAIAAwwwwAB8ipKifABA/gBChoqSYgCEkrLSjAAYKEj+CADkoqKinAA8UpKSDACAgI6wwABskpKSbABgkpKUeAAiAAEmABAoRAAoKCgoKABEKBAAQIqQYAA8WqW9RDgOOMg4DgD+kpKSbAB8goKCRAD+goJEOAD+kpKCAP6QkIAAfIKCklwA/hAQEP4A/gAMAgIC/AD+EChEggD+AgICAP5AIED+AP7AMAz+AHyCgoJ8AP6QkJBgAHyChoN8AP6QmJRiAGSSkpJMAICA/oCAAPwCAgL8AOAYBhjgAPAOMA7wAMYoECjGAMAgHiDAAI6SosIA/4EAwDAMAwCB/wBAgEAAAQEBAQEBEn6SggQABCoqHgD+IiIcABwiIhQAHCIi/gAcKioYACB+oIAAGCUlPgD+ICAeAL4AAQG+AP4IFCIA/AIAPiAeIB4APiAgHgAcIiIcAD8kJBgAGCQkPwA+ECAgABIqKiQAIPwiADwCAjwAIBgGGCAAOAYIBjgAIhQIFCIAIRkGGCAAJioyIgAQboEA5wCBbhAAQIDAQIAAPFqlpUI8");
var widths = atob("BAIEBgYGBgIEBAYGAwUCBQYDBgYGBgYGBgYCAwQGBAUGBgYGBgUFBgYCBgYFBgYGBgYGBgYGBgYGBgUDBQMEBgYFBQUFBQUFBQIEBQMGBQUFBQUFBAUGBgYGBQQCBAYG");
font+='\x08\x1E\x32\x52\x32\x1E\x08';
font+='\x41\x93\xAF\x79\xA1\x91\x41';
widths+='\x07\x07';
font+='\x62\x94\x98\x90\xFE\x00';
font+='\x3E\x08\x08\x3E\x00';
font+='\x3E\x2A\x2A\x14\x00';
font+='\x78\x84\xFE\x84\x78\x00';
font+='\x3E\x20\x20\x3E\x00';
font+='\xFE\x0C\x30\x40\xFE\x00';
font+='\x3E\x08\x1C\x22\x1C\x00';
font+='\x06\x18\x20\x3E\x00';
font+='\x3E\x20\x20\x20\x00';
font+='\x3E\x08\x14\x22\x00';
font+='\x20\x3E\x20\x00';
font+='\x12\x2C\x28\x3E\x00';
font+='\x03\x7E\x82\x82\xFE\x03\x00';
widths+='\x06\x05\x05\x06\x05\x06\x06\x05\x05\x05\x04\x05\x07';
//8 NNNN NNN N N NNN
//4 N N N N N N NN N N
//2 N N N N NNN N N N NNNN N N N N N NN NNNN N N NNN NNN N N
//1 NNNN N N N N N N N N N N N N N N N N N N N N N N N N N
//8 N N NNNN NNN N N N N N NN N NNN N N N N NN N NNN N N
//4 N N N N N N NNN N N NN N N N N N N N N N N N N N N
//2 N N N N NNN N N N N N N N N N N N N N N N NNNNNN
//1 N N
// 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E
var MC={'Jan':'\x82\x83\x84', 'Feb':'\x85e\x84', 'Mar':'Map', 'Apr':'A\x86p', 'May':'Ma\x8D',
'Jun':'\x87\x88\x83', 'Jul':'\x87\x88\x89', 'Aug':'A\x84\x8A', 'Sep':'Ce\x83',
'Oct':'O\x8B\x8C', 'Nov':'Ho\x8D', 'Dec':'\x8Ee\x8B'};
Graphics.prototype.setFont6x8 = function() {
this.setFontCustom(font, 32, widths, 8);
};
Graphics.prototype.convertColorFromHTML = function(HTMLColor) {
return parseInt(HTMLColor.substr(5,2)+HTMLColor.substr(1,2)+HTMLColor.substr(3,2),16);
};
//----------------------------------------------------------
//-------------------- main code ---------------------------
//----------------------------------------------------------
gt.setFont6x8();
var br,d;
var ClockBrightness=2,LampBrightness=0;
var ClockB=(Math.pow(2,(ClockBrightness-1)/9*8)+ClockBrightness/4)/256;//add linear component to be better near brightness=1
var LampB =(Math.pow(2,(LampBrightness -1)/9*8)+LampBrightness /4)/256;//add linear component to be better near brightness=1
//var TempIn='---', TempOut='---', TempX1='---', TempX2='---';
function drawFieldDefault(fieldNum) {
var clr=E.HSBtoRGB(fields[fieldNum].hs.h,fields[fieldNum].hs.s,ClockB,true);
gt.setColor(clr[0]/255,clr[1]/255,clr[2]/255);
var text=(fields[fieldNum].text[0]=="'")?
fields[fieldNum].text.substr(1,20): //No icon if started with '
(fields[fieldNum].icon+fields[fieldNum].text);
gt.drawString(text,((fields[fieldNum].w-gt.stringWidth(text))/2|0)+fields[fieldNum].x,fields[fieldNum].y);
}
var fields=[
{x:0, y:0, w:32,hs:{h:0.6, s:1 }, view:[0,1], drawField:drawFieldDefault, icon:'' , text:'---'},
{x:32,y:0, w:32,hs:{h:0.5, s:1 }, view:[0,1], drawField:drawFieldDefault, icon:'' , text:'---'},
{x:0, y:8, w:32,hs:{h:0, s:0 }, view:[0], drawField:drawFieldDefault, icon:'\x80', text:'---', timeout:undefined},
{x:32,y:8, w:32,hs:{h:0.334,s:1 }, view:[0], drawField:drawFieldDefault, icon:'\x81', text:'---', timeout:undefined},
{x:0, y:8, w:32,hs:{h:0.26, s:0.6}, view:[1], drawField:drawFieldDefault, icon:'\x81', text:'---', timeout:undefined},
{x:32,y:8, w:32,hs:{h:0.1 ,s:0.6}, view:[1], drawField:drawFieldDefault, icon:'\x81', text:'---', timeout:undefined}
];
var currentView=0;
var updateCounter=0;
function updateField(fieldNum) {
updateCounter++;
if(updateCounter==1) setTimeout(function() {
if(updateCounter) {
var clr=E.HSBtoRGB(0,0.01,LampB,true);
gt.setBgColor(clr[0]/255,clr[1]/255,clr[2]/255);
gt.clear();
for(let i=0;i<fields.length;i++)
if(fields[i].view.indexOf(currentView)!=-1) fields[i].drawField(i);
neopixel.write(D2, gt.buffer);
updateCounter=0;
}
},1200);
}
function updateSettings() {
//ugly hack because updateField does not really use fieldNum
updateField(-1);
}
function setView(vnum) {
currentView=vnum;
updateField(-1);
}
var vcnt=0;
setInterval(function() {
if(++vcnt==10){ vcnt=0; setView(0); }
if(vcnt==7) setView(1);
},1000);
var oldTime, newTime;
function prepareTime() {
var ds=Date().toString();
newTime=ds.substr((ds[14]==' ')?15:16,5);
if(oldTime != newTime) {
oldTime=newTime;
fields[1].text = newTime;
fields[0].text = Date().getDate()+' '+MC[Date().toString().substr(4,3)];
updateField(0);
updateField(1);
}
}
setInterval(prepareTime,1000);
var mqtt_server = "192.168.1.40"; // the ip of your MQTT broker
var mqtt_options = { // ALL OPTIONAL - the defaults are below
client_id : FullName, // the client ID sent to MQTT - it's a good idea to define your own static one based on `getSerial()`
};
var mqtt=require("MQTT").create(mqtt_server,mqtt_options);
mqtt.on('disconnected', function() {
console.log("MQTT disconnected... reconnecting.");
setTimeout(function() {
var status=wifi.getDetails();
if( status.status &&
status.status=="connected" &&
!mqtt.connected
) mqtt.connect();
}, 30000);
});
wifi.on('connected', ()=>{
mqtt.connect();
setTimeout(getNTP,30000);
});
setInterval(()=>{
var status=wifi.getDetails();
if(!status.status || status.status != "connected")
{
if(tnum) {
clearTimeout(tnum);
tnum=undefined;
}
wifi.restore();
}
},60000);
function prepareTemp(msg,i) {
if(msg[0] != "'") {
let Temp=parseFloat(msg);
fields[1+i].text=((Temp>0)?'+':'')+Temp.toFixed(1);
}
else
fields[1+i].text=msg.substr(0,20);
if(fields[1+i].timeout) clearTimeout(fields[1+i].timeout);
fields[1+i].timeout=setTimeout((i)=>{fields[1+i].text='---'; fields[1+i].timeout=undefined;updateField(1+i);},300000,i);
updateField(1+i);
}
neopixel.write(D2, gt.buffer);
neopixel.write(D2, gt.buffer);
var http=require("http");
function onPageRequest(req, res) {
// console.log('pr',req.url,Date().toString());
var a = url.parse(req.url, true);
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<html><body>');
res.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/jscolor/2.0.4/jscolor.min.js"></script>');
res.write('<script>function update(hsv) { location.search="?time_color_h="+hsv[0]+"&time_color_s="+hsv[1];}</script>');
res.write('<button class="jscolor {mode:\'HS\',valueElement:null,value:\'66ccff\'}" style="border:2px solid black" onchange="update(this.jscolor.hsv)">Pick a color</button>');
// ,value:\'66ccff\'
// res.write('<p>Pin is '+(BTN.read()?'on':'off')+'</p>');
// res.write('<div>Mode: <a href="?mode=lamp">Lamp</a> <a href="?mode=clock">clock</a></div>');
res.write('<div>Clock brightness: ');
for(let i=0; i<=10; i++)
res.write(' <a href="?clock_brightness='+i+'">'+i+'</a>');
res.write('</div>');
res.write('<div>Lamp brightness: ');
for(let i=0; i<=10; i++)
res.write(' <a href="?lam p_brightness='+i+'">'+i+'</a>');
res.write('</div>');
res.end('</body></html>');
if (a.query) {
for(let i=1;i<=fields.length-2;i++)
if("temp_"+i in a.query) {
prepareTemp(a.query["temp_"+i],i);
}
if("lamp_brightness" in a.query) {
LampBrightness=E.clip(parseInt(a.query.lamp_brightness),0,10);
LampB =(Math.pow(2,(LampBrightness -1)/9*8)+LampBrightness /4)/256;//add linear component to be better near brightness=1
updateSettings();
}
if("clock_brightness" in a.query) {
ClockBrightness=E.clip(parseInt(a.query.clock_brightness),0,10);
ClockB=(Math.pow(2,(ClockBrightness-1)/9*8)+ClockBrightness/4)/256;//add linear component to be better near brightness=1
updateSettings();
}
}
// console.log('pr2',Date().toString());
}
require("http").createServer(onPageRequest).listen(80);
mqtt.on('connected', function() {
mqtt.subscribe([
DevName+"/in/#",
FullName+"/in/#"
]);
});
mqtt.on('publish', function (pub) {
console.log("topic: "+pub.topic);
console.log("message: "+pub.message);
var command=pub.topic.split("/",3)[2];
var i;
for(i=1;i<=fields.length-2;i++)
if(command == "temp_"+i)
prepareTemp(pub.message,i);
if(command == "lamp_brightness") {
LampBrightness=E.clip(parseInt(pub.message),0,10);
LampB =(Math.pow(2,(LampBrightness -1)/9*8)+LampBrightness /4)/256;//add linear component to be better near brightness=1
updateSettings();
}
else if(command == "clock_brightness") {
ClockBrightness=E.clip(parseInt(pub.message),0,10);
ClockB=(Math.pow(2,(ClockBrightness-1)/9*8)+ClockBrightness/4)/256;//add linear component to be better near brightness=1
updateSettings();
}
else if(command == "eval") {
setTimeout( function (s) {
var res=eval(s);
console.log(res);
mqtt.publish(DevName+'/out/eval', JSON.stringify(res));
updateSettings();
},0,pub.message);
}
});
//---------- auto brightness ----------
setInterval(function() {
var newCB=ClockBrightness;
if(newTime=="07:00")
newCB=3;
if(newTime=="22:00")
newCB=1;
if(newCB != ClockBrightness) {
ClockBrightness=newCB;
ClockB=(Math.pow(2,(ClockBrightness-1)/9*8)+ClockBrightness/4)/256;//add linear component to be better near brightness=1
updateSettings();
}
},60000);
//---------------- init ---------------
if(wifi.getDetails().status=="connected") {
getNTP();
mqtt.connect();
}
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.
I've cleaned my code for big clock, now it is more regular. At the same time @Wilberforce and @JumJum have improved ESP32 build. So I've just cleaned my code even more by removing unnecessary MQTT and NTP implementations.
Now the code works on newest Travis build for ESP32. I think it will work on v2.01 as well.
If you do not need Russian months names just remove var MC and its usage - it is just names mapping.
Additional fields on display may get data from http requests or MQTT channel bigclock/in/temp_1 ... temp_4 (because I use the fields to show temperatures collected by other sensors). If first symbol of the message is ', field icon is omitted. If there is no new data, '---' is shown after timeout.
The program may be easily extended to show more fields on larger display or with multiplexing.
Some ugly code is still present, mostly because I don't understand internal implementation of some components (for example, I have not found sntp_init() implementation and so do not know SNTP renewal interval and also SNTP timezone and E.setTimeZone() interaction).