Back to primary blog BYOB Party #1, "Bring you own Bulbs" - The Internet of Holiday Lights
At the very beginning of this project kartben (Benjamin Cabe) kindly provided a demo for using the Infinion RGB Shield and with that demo was a colour wheel web page to remotely manage the output f the RGB LEDs
Now while this was a great demo, my plans for world domination my challange needed more, I have utilized at least four separate MQTT topics and some sub topics also.
Yes you can submit publications to a topic using an MQTT client installed on your machine or use one of the several test tools out there
MQTT SPI or MQTT Lense to name a couple of good ones but these are generic test tools and do there job very well but its not the same as a custom control page to do the job
this is what I came up with
Forgive my lack of graphics skills, I'm a software architect, not a UI designer, I can make things work, I struggle to make them pretty
As you can see, most of the page is just logos and stuff, all the interesting parts on on the left side, on to ensure it is still visible to most browsers even of a small screen, secondly as I said im not a graphics UI designer
You will recognize the colour wheel of course, I did re-configure it to send to a different topic and also shrink it down a bit
This was done by adjusting two files
main.js - the java script file loaded up to the browser to help with local code execution
var cw = Raphael.colorwheel($(".colorwheel_small"), 300, 130).color("#00F"); cw.onchange(function(c) { color = c; })
Change the code from .colourwheel_large to .colourwheel_small and change the size to 300, note while we are here the declaration of the call back when the wheel is changed, all it will do is set the variable color. we will use this later
The file main.css also needs updating as even after these changes the space the wheel ocupied was still massive even though the wheel was now a better size, i tracked this down to a style override in the CSS file
/* Responsive: Portrait tablets and up */ @media screen and (min-width: 300px) { .container { max-width: 300px; }
look for the above and adjust to the setting your see here, i tried this on several browsers and it worked ok in all cases, originally it was set to 780px or there about. changing just the min-width was not enough, it needed both changing
to point he topic to one of my choosing modify the following line
function sendColor() { if (connected) { if (color != null) { var message = new Paho.MQTT.Message(color.toString().replace('#', '')); message.destinationName = "theBreadBoard_Strip"; client.send(message); color = null; } } }
where is says "theBreadBoard_Strip", in the original it is "Benjamin_Strip" or similar, you can change this to suit your needs
OK, so now this allows the page to sent to my topic and has freed up some room for more tools
I have 4 topics used on my project
"theBreadBoard_Strip" to adjust the LED strips connected to my Infinion board, this is covered by the wheel already so nothing more to do on this end except slow it down a bit
"element14_IoT" to ping my system presence for other E14 members, I also subscribe to this to affect change in two of my units as you will see in other blogs
"NeoStar" allows me to control my STAR based on the WS2812 LED chips
"BYOB-PARTY" is used to announce the temperature of the party room and the dance floor (Do I need a jacket, I don't know, why don't I check to see how warm it will be ) this topic also has two sub topics
"BYOB-PARTY/PartyTemp"
"BYOB-PARTY/FloorTemp
A single subscription topic of "BYOB-PARTY/#" will capture any changes under the BYOB-PARTY so it saves a subscription. I want to use this topic to display the temperatures on the browser, this means the browser will need to become an MQTT subscriber, I will need to discover how to do this.
First things first, adding the additional publication logic for the Star and the messageing
looking at this code in the main.js file you will get an idea how to add functionality
client = new Paho.MQTT.Client("ws://iot.eclipse.org/ws", "id-" + new Date().getTime()); var connected = false; function onConnect() { connected = true; }; client.connect({ onSuccess: onConnect });
looking at the first line you see the connection being made, the date bit at the end of the line is just to create a random client ID for the connection
next we have a variable to hold the connection status used later in the code
the function "onConnect()" is not called yet, this is the target of a call back when the connection eventually gets established
the code at line 9 above is declaring the call back, as you can see it is passing in the call the name of the call back funtion, once the connection is established, the target function is called and the variable is set.
function sendColor() { if (connected) { if (color != null) { var message = new Paho.MQTT.Message(color.toString().replace('#', '')); message.destinationName = "theBreadBoard_Strip"; client.send(message); color = null; } } }
This is the real working code, it sends the colour to the iot.eclipse.org site for the MQTT broker to process it, the color variable is declared at the top of the file and when this function gets called it will publish the current value but how is it called, glad you ask
at the very end of the file you will see this statement
setInterval(sendColor, 100);
it is not contained within a function so the page will execute it automatically, it sets up a re-occur timer to call the function in this case every 100mS, you may want to slow this down to say 500mS but it is a user choice
so thats how the basic demo page works (Without going into the operation of the wheel etc)
now to add my additional functionality
I need 8 buttons, one for each mode the star is programmed for, I inserted this into the HTML Body of the page (I will include as an attachment the complete code so you can see exactly where I put it)
<table align="center"> <tr><th style=" color:white ; align-content:center " >Star Menues</th></tr> <tr><td style="background-color:crimson" class="text-center"> <input type="button" id="Button10" onclick="sendStar('AUTO')" value="Auto" /> <input type="button" id="Button1" onclick="sendStar('ROTATE')" value="ROTATE" /> <input type="button" id="Button3" onclick="sendStar('ROTATE2')" value="ROTATE2" /> <input type="button" id="Button4" onclick="sendStar('EXPAND')" value="EXPAND" /> </td></tr> <tr><td style="background-color:crimson" class="text-center"> <input type="button" id="Button5" onclick="sendStar('EXPAND2')" value="EXPAND2" /> <input type="button" id="Button6" onclick="sendStar('CYCLE')" value="CYCLE" /> <input type="button" id="Button7" onclick="sendStar('RAINBOW')" value="RAINBOW" /> <input type="button" id="Button8" onclick="sendStar('WIPE')" value="WIPE" /> </td></tr> </table>
as you can see, every button has an onclick event that calls a function called sendStar("xxx") and passes the function the command we need
the function is simply added to the main.js file
here it is
function sendStar(mode) { if (connected) { starMessage = mode; if (starMessage != null) { var message = new Paho.MQTT.Message(starMessage.toString()); message.destinationName = "NeoStar"; client.send(message); } } }
as you can see it is pretty much identical to the color send function. easy right!, not again the use of that "connected" variable to ensure a connection was made before trying to send anything
between the HTML and the java script, that adds the functionality needed for the star.
the text sender is equally as simple
here is the HTML
<table align="center" > <tr><th style=" color:antiquewhite ; align-content:center ">Send a message to the Message Board</th></tr> <tr><td ><input type="text" id="myText" /><input type="button" id="Button9" onclick="sendText()" value="Send Text" /></td></tr> </table>
and here is the java function
function sendText() { if (connected) { dispMessage = document.getElementById("myText").value; if (dispMessage != null) { var message = new Paho.MQTT.Message(dispMessage.toString()); message.destinationName = "element14_IoT"; client.send(message); } } }
again, almost identical... nice
ok so what about the two temperature gauges I showed you in the opening screen shot, that as it happens, ended up relatively simple too once I completed a bit of research
I could not easily find the info on the PAHO site but a little google goes a long way
Server send push notifications to client browser without polling | oriolrius.cat
this still uses the PAHO libraries (Well I still stayed with them anyway), needed to perform a little on-screen (In Browser debugging to get it all working properly) but here is the result of the effort
at the top of the script where the connection is intially made, we need to add a call back for when messages arrive back from the broker
client = new Paho.MQTT.Client("ws://iot.eclipse.org/ws", "id-" + new Date().getTime()); client.onMessageArrived = onMessageArrived;
The "onConnect()" function is added to so the subscription is made automatically when the connected is made
var connected = false; function onConnect() { connected = true; client.subscribe("BYOB-PARTY/#", { qos: 1 }); };
I simplified the call compared to the material I found and of course added my own topic to the subscription
now of course we need the function to receive the data
function onMessageArrived(message) { if (message.destinationName == 'BYOB-PARTY/PartyTemp') { // document.getElementById("partytemperature").innerHTML = message.payloadString; analog0 = message.payloadString; } else if (message.destinationName == 'BYOB-PARTY/FloorTemp') { //document.getElementById("floortemperature").innerHTML = message.payloadString; analog1 = message.payloadString; } };
so this is a little different to the other functions, first of all it required some digging to discover all the additional sub elements of the message structure to allow the topic and content to be extracted, the commented out code was left there to show how to directly target a couple of div takes with IDs declared to receive the updates. I used this to get it working before adding the gauge controls
Pay close attention to the checking for the correct topics that the page is interested in, I discovered that there is quite a few calls made to this function in operation, not all with topic data so dont delete the check if your only looking at one topic
my function above checks the topic and depending on the one arrived, it will update a variable and then exit. these variables are used by the gauges automatically. I am not going to go into detail regarding the operation of the gauges as this is included in a tutorial set I have posted on the forums. for expediency, I added the scripting for the gauges to the main index.html form
this is the html to add the gauges
<table align="center"> <tr> <td ><canvas id="Canvas0"></canvas></td> <td ><canvas id="Canvas1"></canvas></td> </tr> </table>
the following script is added to the header section of the page and is the libraries so to speek
<script> var Gauge = function (b) { function l(a, b) { for (var c in b) "object" == typeof b[c] && "[object Array]" !== Object.prototype.toString.call(b[c]) && "renderTo" != c ? ("object" != typeof a[c] && (a[c] = {}), l(a[c], b[c])) : a[c] = b[c] } function q() { z.width = b.width; z.height = b.height; A = z.cloneNode(!0); B = A.getContext("2d"); C = z.width; D = z.height; t = C / 2; u = D / 2; f = t < u ? t : u; A.i8d = !1; B.translate(t, u); B.save(); a.translate(t, u); a.save() } function v(a) { var b = new Date; G = setInterval(function () { var c = (new Date - b) / a.duration; 1 < c && (c = 1); var f = ("function" == typeof a.delta?a.delta:M[a.delta])(c);a.step(f);1==c&&clearInterval(G)},a.delay||10)}function k(){G&&clearInterval(G);var a=I-n,h=n,c=b.animation;v({delay:c.delay,duration:c.duration,delta:c.fn,step:function(b){n=parseFloat(h)+a*b;E.draw()}})}function e(a){return a*Math.PI/180}function g(b,h,c){c=a.createLinearGradient(0,0,0,c);c.addColorStop(0,b);c.addColorStop(1,h);return c}function p(){var m=93*(f/100),h=f-m,c=91*(f/100),e=88*(f/100),d=85*(f/100);a.save();b.glow&&(a.shadowBlur=h,a.shadowColor= "rgba(0, 0, 0, 0.5)");a.beginPath();a.arc(0,0,m,0,2*Math.PI,!0);a.fillStyle=g("#ddd","#aaa",m);a.fill();a.restore();a.beginPath();a.arc(0,0,c,0,2*Math.PI,!0);a.fillStyle=g("#fafafa","#ccc",c);a.fill();a.beginPath();a.arc(0,0,e,0,2*Math.PI,!0);a.fillStyle=g("#eee","#f0f0f0",e);a.fill();a.beginPath();a.arc(0,0,d,0,2*Math.PI,!0);a.fillStyle=b.colors.plate;a.fill();a.save()}function w(a){var h=!1;a=0===b.majorTicksFormat.dec?Math.round(a).toString():a.toFixed(b.majorTicksFormat.dec);return 1<b.majorTicksFormat["int"]? (h=-1<a.indexOf("."),-1<a.indexOf("-")?"-"+(b.majorTicksFormat["int"]+b.majorTicksFormat.dec+2+(h?1:0)-a.length)+a.replace("-",""):""+(b.majorTicksFormat["int"]+b.majorTicksFormat.dec+1+(h?1:0)-a.length)+a):a}function d(){var m=81*(f/100);a.lineWidth=2;a.strokeStyle=b.colors.majorTicks;a.save();if(0===b.majorTicks.length){for(var h=(b.maxValue-b.minValue)/5,c=0;5>c;c++)b.majorTicks.push(w(b.minValue+h*c));b.majorTicks.push(w(b.maxValue))}for(c=0;c<b.majorTicks.length;++c)a.rotate(e(45+c*(270/(b.majorTicks.length- 1)))),a.beginPath(),a.moveTo(0,m),a.lineTo(0,m-15*(f/100)),a.stroke(),a.restore(),a.save();b.strokeTicks&&(a.rotate(e(90)),a.beginPath(),a.arc(0,0,m,e(45),e(315),!1),a.stroke(),a.restore(),a.save())}function J(){var m=81*(f/100);a.lineWidth=1;a.strokeStyle=b.colors.minorTicks;a.save();for(var h=b.minorTicks*(b.majorTicks.length-1),c=0;c<h;++c)a.rotate(e(45+c*(270/h))),a.beginPath(),a.moveTo(0,m),a.lineTo(0,m-7.5*(f/100)),a.stroke(),a.restore(),a.save()}function s(){for(var m=55*(f/100),h=0;h<b.majorTicks.length;++h){var c= F(m,e(45+h*(270/(b.majorTicks.length-1))));a.font=20*(f/200)+"px Arial";a.fillStyle=b.colors.numbers;a.lineWidth=0;a.textAlign="center";a.fillText(b.majorTicks[h],c.x,c.y+3)}}function x(a){var h=b.valueFormat.dec,c=b.valueFormat["int"];a=parseFloat(a);var f=0>a;a=Math.abs(a);if(0<h){a=a.toFixed(h).toString().split(".");h=0;for(c-=a[0].length;h<c;++h)a[0]="0"+a[0];a=(f?"-":"")+a[0]+"."+a[1]}else{a=Math.round(a).toString();h=0;for(c-=a.length;h<c;++h)a="0"+a;a=(f?"-":"")+a}return a}function F(a,b){var c= Math.sin(b),f=Math.cos(b);return{x:0*f-a*c,y:0*c+a*f}}function N(){a.save();for(var m=81*(f/100),h=m-15*(f/100),c=0,g=b.highlights.length;c<g;c++){var d=b.highlights[c],r=(b.maxValue-b.minValue)/270,k=e(45+(d.from-b.minValue)/r),r=e(45+(d.to-b.minValue)/r);a.beginPath();a.rotate(e(90));a.arc(0,0,m,k,r,!1);a.restore();a.save();var l=F(h,k),p=F(m,k);a.moveTo(l.x,l.y);a.lineTo(p.x,p.y);var p=F(m,r),n=F(h,r);a.lineTo(p.x,p.y);a.lineTo(n.x,n.y);a.lineTo(l.x,l.y);a.closePath();a.fillStyle=d.color;a.fill(); a.beginPath();a.rotate(e(90));a.arc(0,0,h,k-0.2,r+0.2,!1);a.restore();a.closePath();a.fillStyle=b.colors.plate;a.fill();a.save()}}function K(){var m=12*(f/100),h=8*(f/100),c=77*(f/100),d=20*(f/100),k=4*(f/100),r=2*(f/100),l=function(){a.shadowOffsetX=2;a.shadowOffsetY=2;a.shadowBlur=10;a.shadowColor="rgba(188, 143, 143, 0.45)"};l();a.save();n=0>n?Math.abs(b.minValue-n):0<b.minValue?n-b.minValue:Math.abs(b.minValue)+n;a.rotate(e(45+n/((b.maxValue-b.minValue)/270)));a.beginPath();a.moveTo(-r,-d);a.lineTo(-k, 0);a.lineTo(-1,c);a.lineTo(1,c);a.lineTo(k,0);a.lineTo(r,-d);a.closePath();a.fillStyle=g(b.colors.needle.start,b.colors.needle.end,c-d);a.fill();a.beginPath();a.lineTo(-0.5,c);a.lineTo(-1,c);a.lineTo(-k,0);a.lineTo(-r,-d);a.lineTo(r/2-2,-d);a.closePath();a.fillStyle="rgba(255, 255, 255, 0.2)";a.fill();a.restore();l();a.beginPath();a.arc(0,0,m,0,2*Math.PI,!0);a.fillStyle=g("#f0f0f0","#ccc",m);a.fill();a.restore();a.beginPath();a.arc(0,0,h,0,2*Math.PI,!0);a.fillStyle=g("#e8e8e8","#f5f5f5",h);a.fill()} function L(){a.save();a.font=40*(f/200)+"px Led";var b=x(y),h=a.measureText("-"+x(0)).width,c=f-33*(f/100),g=0.12*f;a.save();var d=-h/2-0.025*f,e=c-g-0.04*f,h=h+0.05*f,g=g+0.07*f,k=0.025*f;a.beginPath();a.moveTo(d+k,e);a.lineTo(d+h-k,e);a.quadraticCurveTo(d+h,e,d+h,e+k);a.lineTo(d+h,e+g-k);a.quadraticCurveTo(d+h,e+g,d+h-k,e+g);a.lineTo(d+k,e+g);a.quadraticCurveTo(d,e+g,d,e+g-k);a.lineTo(d,e+k);a.quadraticCurveTo(d,e,d+k,e);a.closePath();d=a.createRadialGradient(0,c-0.12*f-0.025*f+(0.12*f+0.045*f)/ 2,f/10,0,c-0.12*f-0.025*f+(0.12*f+0.045*f)/2,f/5);d.addColorStop(0,"#888");d.addColorStop(1,"#666");a.strokeStyle=d;a.lineWidth=0.05*f;a.stroke();a.shadowBlur=0.012*f;a.shadowColor="rgba(0, 0, 0, 1)";a.fillStyle="#babab2";a.fill();a.restore();a.shadowOffsetX=0.004*f;a.shadowOffsetY=0.004*f;a.shadowBlur=0.012*f;a.shadowColor="rgba(0, 0, 0, 0.3)";a.fillStyle="#444";a.textAlign="center";a.fillText(b,-0,c);a.restore()}Gauge.Collection.push(this);this.config={renderTo:null,width:200,height:200,title:!1, maxValue:100,minValue:0,majorTicks:[],minorTicks:10,strokeTicks:!0,units:!1,valueFormat:{"int":3,dec:2},majorTicksFormat:{"int":1,dec:0},glow:!0,animation:{delay:10,duration:250,fn:"cycle"},colors:{plate:"#fff",majorTicks:"#444",minorTicks:"#666",title:"#888",units:"#888",numbers:"#444",needle:{start:"rgba(240, 128, 128, 1)",end:"rgba(255, 160, 122, .9)"}},highlights:[{from:20,to:60,color:"#eee"},{from:60,to:80,color:"#ccc"},{from:80,to:100,color:"#999"}]};var y=0,E=this,n=0,I=0,H=!1;this.setValue= function(a){n=b.animation?y:a;var d=(b.maxValue-b.minValue)/100;I=a>b.maxValue?b.maxValue+d:a<b.minValue?b.minValue-d:a;y=a;b.animation?k():this.draw();return this};this.setRawValue=function(a){n=y=a;this.draw();return this};this.clear=function(){y=n=I=this.config.minValue;this.draw();return this};this.getValue=function(){return y};this.onready=function(){};l(this.config,b);this.config.minValue=parseFloat(this.config.minValue);this.config.maxValue=parseFloat(this.config.maxValue);b=this.config;n= y=b.minValue;if(!b.renderTo)throw Error("Canvas element was not specified when creating the Gauge object!");var z=b.renderTo.tagName?b.renderTo:document.getElementById(b.renderTo),a=z.getContext("2d"),A,C,D,t,u,f,B;q();this.updateConfig=function(a){l(this.config,a);q();this.draw();return this};var M={linear:function(a){return a},quad:function(a){return Math.pow(a,2)},quint:function(a){return Math.pow(a,5)},cycle:function(a){return 1-Math.sin(Math.acos(a))},bounce:function(a){a:{a=1-a;for(var b=0, c=1;;b+=c,c/=2)if(a>=(7-4*b)/11){a=-Math.pow((11-6*b-11*a)/4,2)+Math.pow(c,2);break a}a=void 0}return 1-a},elastic:function(a){a=1-a;return 1-Math.pow(2,10*(a-1))*Math.cos(30*Math.PI/3*a)}},G=null;a.lineCap="round";this.draw=function(){if(!A.i8d){B.clearRect(-t,-u,C,D);B.save();var g={ctx:a};a=B;p();N();J();d();s();b.title&&(a.save(),a.font=24*(f/200)+"px Arial",a.fillStyle=b.colors.title,a.textAlign="center",a.fillText(b.title,0,-f/4.25),a.restore());b.units&&(a.save(),a.font=22*(f/200)+"px Arial", a.fillStyle=b.colors.units,a.textAlign="center",a.fillText(b.units,0,f/3.25),a.restore());A.i8d=!0;a=g.ctx;delete g.ctx}a.clearRect(-t,-u,C,D);a.save();a.drawImage(A,-t,-u,C,D);if(Gauge.initialized)L(),K(),H||(E.onready&&E.onready(),H=!0);else var e=setInterval(function(){Gauge.initialized&&(clearInterval(e),L(),K(),H||(E.onready&&E.onready(),H=!0))},10);return this}};Gauge.initialized=!1; (function(){var b=document,l=b.getElementsByTagName("head")[0],q=-1!=navigator.userAgent.toLocaleLowerCase().indexOf("msie"),v="@font-face {font-family: 'Led';src: url('fonts/digital-7-mono."+(q?"eot":"ttf")+"');}",k=b.createElement("style");k.type="text/css";if(q)l.appendChild(k),l=k.styleSheet,l.cssText=v;else{try{k.appendChild(b.createTextNode(v))}catch(e){k.cssText=v}l.appendChild(k);l=k.styleSheet?k.styleSheet:k.sheet||b.styleSheets[b.styleSheets.length-1]}var g=setInterval(function(){if(b.body){clearInterval(g); var e=b.createElement("div");e.style.fontFamily="Led";e.style.position="absolute";e.style.height=e.style.width=0;e.style.overflow="hidden";e.innerHTML=".";b.body.appendChild(e);setTimeout(function(){Gauge.initialized=!0;e.parentNode.removeChild(e)},250)}},1)})();Gauge.Collection=[]; Gauge.Collection.get=function(b){if("string"==typeof b)for(var l=0,q=this.length;l<q;l++){if((this[l].config.renderTo.tagName?this[l].config.renderTo:document.getElementById(this[l].config.renderTo)).getAttribute("id")==b)return this[l]}else return"number"==typeof b?this[b]:null};function domReady(b){window.addEventListener?window.addEventListener("DOMContentLoaded",b,!1):window.attachEvent("onload",b)} domReady(function(){function b(b){for(var e=b[0],d=1,g=b.length;d<g;d++)e+=b[d].substr(0,1).toUpperCase()+b[d].substr(1,b[d].length-1);return e}for(var l=document.getElementsByTagName("canvas"),q=0,v=l.length;q<v;q++)if("canv-gauge"==l[q].getAttribute("data-type")){var k=l[q],e={},g,p=parseInt(k.getAttribute("width"),10),w=parseInt(k.getAttribute("height"),10);e.renderTo=k;p&&(e.width=p);w&&(e.height=w);p=0;for(w=k.attributes.length;p<w;p++)if(g=k.attributes.item(p).nodeName,"data-type"!=g&&"data-"== g.substr(0,5)){var d=g.substr(5,g.length-5).toLowerCase().split("-");if(g=k.getAttribute(g))switch(d[0]){case "colors":d[1]&&(e.colors||(e.colors={}),"needle"==d[1]?(d=g.split(/\s+/),e.colors.needle=d[0]&&d[1]?{start:d[0],end:d[1]}:g):(d.shift(),e.colors[b(d)]=g));break;case "highlights":e.highlights||(e.highlights=[]);g=g.split(",");for(var d=0,J=g.length;d<J;d++){var s=g[d].replace(/^\s+|\s+$/g,"").split(/\s+/),x={};s[0]&&""!=s[0]&&(x.from=s[0]);s[1]&&""!=s[1]&&(x.to=s[1]);s[2]&&""!=s[2]&&(x.color= s[2]);e.highlights.push(x)}break;case "animation":d[1]&&(e.animation||(e.animation={}),"fn"==d[1]&&/^\s*function\s*\(/.test(g)&&(g=eval("("+g+")")),e.animation[d[1]]=g);break;default:d=b(d);if("onready"==d)continue;if("majorTicks"==d)g=g.split(/\s+/);else if("strokeTicks"==d||"glow"==d)g="true"==g?!0:!1;else if("valueFormat"==d)if(g=g.split("."),2==g.length)g={"int":parseInt(g[0],10),dec:parseInt(g[1],10)};else continue;e[d]=g}}e=new Gauge(e);k.getAttribute("data-value")&&e.setRawValue(parseFloat(k.getAttribute("data-value"))); k.getAttribute("data-onready")&&(e.onready=function(){eval(this.config.renderTo.getAttribute("data-onready"))});e.draw()}});window.Gauge=Gauge; </script> <style type="text/css"> .centerText{ text-align: center; } .divBorder { border: 2px solid; border-radius: 15px; width: 810px; text-align: center; box-shadow: 6px 6px 3px #888888 } .myRange { border: 2px solid; height: 10px; display: block; margin: 20px; vertical-align: middle; text-align: center; border-radius: 5px; background-color: #444; box-shadow: 6px 6px 3px #888888 } .auto-style3 { width: 123px; height: 28px; } .auto-style4 { height: 28px; } .auto-style6 { width: 149px; text-align: center; } </style>
below the added HTML we also need the script to configure and activate the gauges
<script> var gauge0 = new Gauge({ renderTo: 'Canvas0', width: 200, height: 200, maxValue: 100, valueFormat: { int: 2, dec: 2 }, glow: true, units: 'Deg C', title: 'Party Room', majorTicks: ['0', '20', '40', '60', '80', '100'], strokeTicks: true, highlights: [{from: 00,to: 20,color: 'PaleGreen'}, {from: 20,to: 60,color: 'Khaki'}, {from: 60,to: 100,color: 'LightSalmon'}], animation: {delay: 10, duration: 300,fn: 'bounce' } }); gauge0.onready = function () { setInterval(function () { gauge0.setValue(analog0); }, 1000); }; gauge0.draw(); var gauge1 = new Gauge({ renderTo: 'Canvas1', width: 200, height: 200, maxValue: 100, units: 'Deg C', title: 'Dance Floor', valueFormat: { int: 2, dec: 2 }, glow: true, majorTicks: ['0', '20', '40', '60', '80', '100'], strokeTicks: true, highlights: [{ from: 00, to: 20, color: 'PaleGreen' }, { from: 20, to: 60, color: 'Khaki' }, { from: 60, to: 100, color: 'LightSalmon' }], animation: { delay: 10, duration: 300, fn: 'bounce' } }); gauge1.onready = function () {setInterval(function () {gauge1.setValue(analog1);}, 1000);}; gauge1.draw(); </script>
This is where you get to configure the gauges as you need, in my case, setting the size, the range of the ticks and the format of the data.
the gauge1.onready functions activate the gauges and set a timer (1000) once a second in this case and as you can see, the analog variable mentioned above is now used to pass in the current value, the nice thing here is that the receiving of MQTT data and the updating of the gauge is completely separated making the code more stable and responsive.
and thats all there is too it, simple right (Well straight forward anyway)
if you have questions, please ask, dont be afraid to try this
Oh and the best thing of all, this complete web solution was hosted by the Arduino YUN itself, you dont have to expose your web pages to the outside world if you dont want to, of course if you do you can, and infact Cabe's demo was hosted right from his GIT source site... sweet.
I will attach the complete set-up for the yun as a zip file, simply extract it and save to the root of the uSD card to go into the yu, the rest is simple, browse to arduino.local/sd and your page will appear
you should end up with an Arduino folder in the root of the SD card