Category Archives: node-red


BigTimerNode-RED is a fantastic and powerful yet easy to use programming tool for wiring together hardware devices, APIs and online services in new and interesting ways. For more information on Node-Red go to the Node-Red site.

BigTimer is the the most BigTimerpowerful and best-ever, most comprehensive timing node for Node-Red, providing a general purpose timer as well as handling summer/winter correctly as well as (importantly) lighting-up time (for which you should provide your longitude and latitude from Google maps or similar).

After all you probably don’t turn the outside lights on at 6pm!! You most likely want them on when it gets DARK.

At it’s simplest, BigTimer can be used as the equivalent of a simple mechanical timer, deeding other Node-Red nodes or directly manipulating devices by, for example, MQTT protocol.

BigTimers can be used singly or in groups. Full support is provided for dusk/sunset dawn/sunrise and variations also for day/week/month (and special days) control.

BigTimer has three outputs, the first of which updates when there is a change of state and presents one of two messages (for, for example, MQTT or other control mechanism)

The node offers outputs suitable for MQTT, speech and databases (but you don’t HAVE to use any of these). You can also manually over-ride the UTC time setting on the host computer if required.

The second of three outputs offers a simple 1 or 0 every minute in the output “payload” and also presents additional information reflecting the status message in msg.state, message.time and others – see the entire msg object output in debug for more.

The third output presents a message which could be used for speech or debugging.

There is also real-time status information displayed below each BigTimer node.

As always, the latest node-red-contrib-bigtimer is available at node-red-flows and at - put "scargill" in the search and you'll see all my nodes and flows.

Continue reading BigTimer


The Thermostat Continues

Hard to believe the amount of time I’ve spent on this thing. I’m still too deeply buried in R&D right now to fully develop another full article on my thermostat but here is some info to add to the previous blog entry on the subject. My Node-Red/ESP-GO thermostat is now in operation with three stat heads, 2 in active service as one has an issue whereby it locks up occasionally, maybe once a day, maybe once every couple of days – unpredictable – a hardware reset brings it back – but I need to get to the bottom of it – the other two units don’t do that.

The 2.5w laser engraver turned out to be less of a blessing than it first seemed, thanks to it’s inability to handle clear Perspex (does a lovely job on coloured material) I’ve had to go back to traditional DIY techniques for this project.

While I’m here, I’ve just reduced the e-newsletter frequency as we’re getting ready for the summer exodus to the sun and that will impact my writing ability somewhat until early April when I hope to have lots of new toys to talk about.

I’ve now published updates to ESP-GO-3 using Espressif Non-OS SDK 3.1 – which in itself is still only on GIT at the time of writing but seems to work a treat. My thanks to helpers on the Espressif forum – I didn’t really need this for current projects but the 5K of saved iRam gives me bags of room for expansion, so now was as good a time as any to upgrade to the latest SDK. Perhaps in the summer I’ll take a look at a better web interface and more SSID options.

Some new visuals – here is my Grafana logging – accessible externally thanks to PIVPN as is the stat itself.

Grafana Logging for Pete's Stat

Above is my Grafana info screen – monitoring and logging the two stats (main and aux – I have a third on test, all have now survived power cycling both for the stats and  Node-Red without failure or needing intervention) but I’ve seen dropout after 2-3 days needing power cycling on one of them so the jury is still out). I’m also monitoring (temperature in the hot water tank cupboard from the mains controller supplying on-off instructions to the air—source heating system.

Lots more below the line…

Continue reading The Thermostat Continues


Waiting for Peter, closing out 2017

MrShark at the console, while we're all waiting for Peter to come back, let's try to add some new content to the blog 🙂

I've a couple of new boards to test, unfortunately they've to wait I receive their PSUs... material for an other time 🙂

So, I'll add some valuable resources found on the net in recent times, about Node-Red development...

I wish a Happy New Year to Peter and his wife, and to all of you readers... please keep visiting this blog and comment, let's leave some new material for him when he's back 🙂

Continue reading Waiting for Peter, closing out 2017


A Quick Plotly

Some time ago I wrote about HighCharts – a wonderful set of Javascript tools for doing graphs – and I gave an example of getting the graphs into Node-Red.  I’ll not repeat that as you have the link above.  At the time I’d not added in databases, I’ve since made some VERY simple code to bring in the charts from SQLITE -  I have to say it isn’t the fastest code in the world so I won’t embarrass myself by replicating it here. But at the time, my only limit was Node-Red Dashboard – there was as far as I could tell no way to get full screen or anything like it on a PC  -  until I took the plunge today and ask the right people – turns out that you can set an individual page in Node-Red Dashboard to WAY past the normal 6 blocks width simply by altering the size of a GROUP used only on that page.

Continue reading A Quick Plotly


A Fine Sunny Weekend

Scargill's Man-CabinEvening all, it has been a busy weekend here at the Scargill’s man-cabin. No Friday, frequent blog-visitor JAY popped over for a visit and we spend some time discussing new stuff:

First on the list was a new toy Jay just picked up on offer at Maplin – a “SmartSensor Energy Egg” solo pack which comprises a PIR Egg and a mains socket. The two operate on 433Mhz and allow for turning a light on for a predetermined time when someone moves in the room. Simple enough – but At under £8 on offer I guess Jay could not refuse. I have one sitting in front of me now looking for a home in my office. There is a website – but I advise against it – half of the links go no-where. Anyway – special offer – Maplin.

The gadgets are meant to run stand-alone but of course we had to try it out with the RFLINK 433Mhz setup and sure enough – the RFLINK registers activity in the PIR.

Turning the PIR on and off with the handy button on the top produced this output.



ChatbotEnergy EggStrangely the effect was the opposite of what you might expect – the ALLON command turned the light off!! Anyway the upshot being we can easily monitor this device along with others using the RFLINK.  Sadly when we tried to turn this into a COMMAND to turn the light on and off from the RFLINK unit, while we could turn the device ON, the OFF command stubbornly refused to work – but we started sending emails off to Frank Zirrone of RFLink software fame – and he was very helpful – we’ve now send some diagnostics off (this all comes in the software folder for RFLink) and he’s taking a look.

Meanwhile we discovered a potential issue with the playground covered earlier in this blog – the mobile phone as server. I’ve been using TASKER on the phone to pick up MQTT messages from the Debian installation (to in turn play a really nice doorbell sound) – using the MQTTCLIENT Tasker Plug-in App which you might recall replaced it’s somewhat ill-fated predecessor. Well, it seems it is still not out of the woods.

ChatbotWhile it works GREAT, if you reboot the phone, while everything else comes up perfectly including Debian itself, Tasker and, well, yes, everything – the MQTT plugin – would you believe does NOT re-connect with the MQTT broker until you open the app.  Now this may be because it is starting before Debian is ready – but you would think that such a program designed to run in the background, would try, try, try again until it got through. We even tried using Tasker to initialise it – no joy.  So until the author comes back, we’re on the hunt for alternatives – and it is possible that TELEGRAM may be it  - Chatbotexcept having installed it on my phone (easy) and on my PC (easy) and installed node-red-contrib-chatbot on Node-Red - with a wopping 37 nodes  - I'm now utterly confused. Jay helped me set up a bot and send a message to it - but getting it to arbitrarily send a message... I'm not quite there yet - and what you're supposed to do with all those OTHER nodes....

ChatbotThere is a TELEGRAM app for Android, a plug in for Node-Red and in fact it comes on just about all platforms – this is similar to other messaging apps in that you can send text message back and forth – so the hope is that we can send messages between Node-Red on Debian – and the Android Tasker reliably – more on that later.

Anyone have any experience of using TELEGRAM? I swear this could be the death of me.  A video showing the various nodes in action would be nice!


I’ve fitted some door sensors 433Mhz model DC55 from Friedland – comprising a magnet and a reed-switch-triggered 4533Mhz transmitter – all very nice and small – they work a treat with RFLink but right now I’m looking for the meaning of the codes – clearly I figured out the ones for open and close – and there’s another when an internal spring is released – this is for security – but there is a 4th code which comes out very occasionally – and I have no idea what it is for. Maybe “battery ok” – maybe “battery shot” – no way to tell and their manuals don’t seem to help.  Any ideas anyone?

Finally – the script – just updated it to handle that Node-Red initialisation we discussed in a previous blog entry.

Hope you had a nice weekend.



Thermometers for Node-Red

Three thermometers in a row for Node-Red (or as many or few as you like really) for Node-Red Dashboard. Another fine example of simple gauges and unlike some it is easy to make multiple gauges on one page – I will demonstrate three.

Here is the library source, I grabbed the larger one and saved as gauges-min.js in my /myjs folder – see previous blogs for setting this up with Node-Red.

Here is the code for one – and a flow example with three independent units.

You can of course alter the colours to whatever you want.

thermometer flow for Node-Red

The code for one template:

[pcsh lang="js" tab_size="4" message="" hl_lines="" provider="manual"]

<script src="/myjs/gauge.min.js"></script>

            scope.$watch('msg', function(msg) {
               if (typeof(msg.payload) != "undefined") { gauge2.value=msg.payload; gauge2.draw(); }

var gauge2 = new LinearGauge({
    renderTo: 'mycanvas',
    valueBox: false,
    highlights: [ 
            {"from": 0, "to": 10, "color": "rgba(50, 50, 200, .75)"},
            {"from": 10, "to": 20, "color": "rgba(50, 200, 200, .75)"},
            {"from": 20, "to": 30, "color": "rgba(50, 200, 50, .75)"},
            {"from": 30, "to": 50, "color": "rgba(200, 200, 50, .75)"},
            {"from": 50, "to": 100, "color": "rgba(200, 50, 50, .75)"}
    barWidth: 10,
    units: "°C",
    borderShadowWidth: 0,
    borders: false,
    value: 35


<canvas id="mycanvas" 


The sample flow above

[pcsh lang="js" tab_size="4" message="" hl_lines="" provider="manual"]

[{"id":"ddc888ba.e45578","type":"ui_template","z":"c552e8d2.712b48","group":"33279d5b.72b122","name":"controller","order":0,"width":"2","height":"5","format":"<script src=\"/myjs/gauge.min.js\"></script>\n\n<script>\n  (function(scope){ \n            scope.$watch('msg', function(msg) {\n               if (typeof(msg.payload) != \"undefined\") { gauge3.value=msg.payload; gauge3.draw(); }\n            });\n    })(scope);\n\nvar gauge3 = new LinearGauge({\n    renderTo: 'mycanvas2',\n    valueBox: false,\n    highlights: [ \n            {\"from\": 0, \"to\": 10, \"color\": \"rgba(50, 50, 200, .75)\"},\n            {\"from\": 10, \"to\": 20, \"color\": \"rgba(50, 200, 200, .75)\"},\n            {\"from\": 20, \"to\": 30, \"color\": \"rgba(50, 200, 50, .75)\"},\n            {\"from\": 30, \"to\": 50, \"color\": \"rgba(200, 200, 50, .75)\"},\n            {\"from\": 50, \"to\": 100, \"color\": \"rgba(200, 50, 50, .75)\"}\n            ],\n    barWidth: 10,\n    units: \"°C\",\n    borderShadowWidth: 0,\n    borders: false,\n    value: 35\n}).draw();\n\n</script>\n\n<canvas id=\"mycanvas2\" \n    data-type=\"linear-gauge\"\n    data-width=\"106\"\n    data-height=\"270\"\n    data-units=\"°C\"\n    data-min-value=\"0\"\n    data-start-angle=\"90\"\n    data-ticks-angle=\"180\"\n    data-value-box=\"false\"\n    data-max-value=\"220\"\n    data-major-ticks=\"0,20,40,60,80,100,120,140,160,180,200,220\"\n    data-minor-ticks=\"2\"\n    data-stroke-ticks=\"true\"\n    data-color-plate=\"#fff\"\n    data-border-shadow-width=\"0\"\n    data-borders=\"false\"\n    data-needle-type=\"arrow\"\n    data-needle-width=\"2\"\n    data-needle-circle-size=\"7\"\n    data-needle-circle-outer=\"true\"\n    data-needle-circle-inner=\"false\"\n    data-animation-duration=\"1500\"\n    data-animation-rule=\"linear\"\n    data-bar-width=\"10\"\n    data-value=\"35\"\n></canvas>\n","storeOutMessages":false,"fwdInMessages":false,"x":960,"y":2120,"wires":[[]]},{"id":"27cb8298.0e94de","type":"ui_template","z":"c552e8d2.712b48","group":"33279d5b.72b122","name":"controller","order":0,"width":"2","height":"5","format":"<script src=\"/myjs/gauge.min.js\"></script>\n\n<script>\n  (function(scope){ \n            scope.$watch('msg', function(msg) {\n               if (typeof(msg.payload) != \"undefined\") { gauge4.value=msg.payload; gauge4.draw(); }\n            });\n    })(scope);\n\nvar gauge4 = new LinearGauge({\n    renderTo: 'mycanvas3',\n    valueBox: false,\n    highlights: [ \n            {\"from\": 0, \"to\": 10, \"color\": \"rgba(50, 50, 200, .75)\"},\n            {\"from\": 10, \"to\": 20, \"color\": \"rgba(50, 200, 200, .75)\"},\n            {\"from\": 20, \"to\": 30, \"color\": \"rgba(50, 200, 50, .75)\"},\n            {\"from\": 30, \"to\": 50, \"color\": \"rgba(200, 200, 50, .75)\"},\n            {\"from\": 50, \"to\": 100, \"color\": \"rgba(200, 50, 50, .75)\"}\n            ],\n    barWidth: 10,\n    units: \"°C\",\n    borderShadowWidth: 0,\n    borders: false,\n    value: 35\n}).draw();\n\n</script>\n\n<canvas id=\"mycanvas3\" \n    data-type=\"linear-gauge\"\n    data-width=\"106\"\n    data-height=\"270\"\n    data-units=\"°C\"\n    data-min-value=\"0\"\n    data-start-angle=\"90\"\n    data-ticks-angle=\"180\"\n    data-value-box=\"false\"\n    data-max-value=\"220\"\n    data-major-ticks=\"0,20,40,60,80,100,120,140,160,180,200,220\"\n    data-minor-ticks=\"2\"\n    data-stroke-ticks=\"true\"\n    data-color-plate=\"#fff\"\n    data-border-shadow-width=\"0\"\n    data-borders=\"false\"\n    data-needle-type=\"arrow\"\n    data-needle-width=\"2\"\n    data-needle-circle-size=\"7\"\n    data-needle-circle-outer=\"true\"\n    data-needle-circle-inner=\"false\"\n    data-animation-duration=\"1500\"\n    data-animation-rule=\"linear\"\n    data-bar-width=\"10\"\n    data-value=\"35\"\n></canvas>\n","storeOutMessages":false,"fwdInMessages":false,"x":960,"y":2200,"wires":[[]]},{"id":"7df32c36.100ef4","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"20","payloadType":"num","repeat":"","crontab":"","once":false,"x":770,"y":2100,"wires":[["ddc888ba.e45578"]]},{"id":"54305565.bdfd7c","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"60","payloadType":"num","repeat":"","crontab":"","once":false,"x":770,"y":2140,"wires":[["ddc888ba.e45578"]]},{"id":"7907996d.21af38","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"10","payloadType":"num","repeat":"","crontab":"","once":false,"x":770,"y":2180,"wires":[["27cb8298.0e94de"]]},{"id":"583f639c.99440c","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"90","payloadType":"num","repeat":"","crontab":"","once":false,"x":770,"y":2220,"wires":[["27cb8298.0e94de"]]},{"id":"c8fdd54c.823418","type":"ui_template","z":"c552e8d2.712b48","group":"33279d5b.72b122","name":"controller","order":0,"width":"2","height":"5","format":"<script src=\"/myjs/gauge.min.js\"></script>\n\n<script>\n  (function(scope){ \n            scope.$watch('msg', function(msg) {\n               if (typeof(msg.payload) != \"undefined\") { gauge2.value=msg.payload; gauge2.draw(); }\n            });\n    })(scope);\n\nvar gauge2 = new LinearGauge({\n    renderTo: 'mycanvas',\n    valueBox: false,\n    highlights: [ \n            {\"from\": 0, \"to\": 10, \"color\": \"rgba(50, 50, 200, .75)\"},\n            {\"from\": 10, \"to\": 20, \"color\": \"rgba(50, 200, 200, .75)\"},\n            {\"from\": 20, \"to\": 30, \"color\": \"rgba(50, 200, 50, .75)\"},\n            {\"from\": 30, \"to\": 50, \"color\": \"rgba(200, 200, 50, .75)\"},\n            {\"from\": 50, \"to\": 100, \"color\": \"rgba(200, 50, 50, .75)\"}\n            ],\n    barWidth: 10,\n    units: \"°C\",\n    borderShadowWidth: 0,\n    borders: false,\n    value: 35\n}).draw();\n\n</script>\n\n<canvas id=\"mycanvas\" \n    data-type=\"linear-gauge\"\n    data-width=\"106\"\n    data-height=\"270\"\n    data-units=\"°C\"\n    data-min-value=\"0\"\n    data-start-angle=\"90\"\n    data-ticks-angle=\"180\"\n    data-value-box=\"false\"\n    data-max-value=\"220\"\n    data-major-ticks=\"0,20,40,60,80,100,120,140,160,180,200,220\"\n    data-minor-ticks=\"2\"\n    data-stroke-ticks=\"true\"\n    data-color-plate=\"#fff\"\n    data-border-shadow-width=\"0\"\n    data-borders=\"false\"\n    data-needle-type=\"arrow\"\n    data-needle-width=\"2\"\n    data-needle-circle-size=\"7\"\n    data-needle-circle-outer=\"true\"\n    data-needle-circle-inner=\"false\"\n    data-animation-duration=\"1500\"\n    data-animation-rule=\"linear\"\n    data-bar-width=\"10\"\n    data-value=\"35\"\n></canvas>\n","storeOutMessages":false,"fwdInMessages":false,"x":960,"y":2040,"wires":[[]]},{"id":"8cc2ade9.5db9b","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"5","payloadType":"num","repeat":"","crontab":"","once":false,"x":770,"y":2020,"wires":[["c8fdd54c.823418"]]},{"id":"277f033f.43ecbc","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"48","payloadType":"num","repeat":"","crontab":"","once":false,"x":770,"y":2060,"wires":[["c8fdd54c.823418"]]},{"id":"33279d5b.72b122","type":"ui_group","z":"","name":"LCD Test","tab":"34cddaf3.8a9cd6","disp":true,"width":"6"},{"id":"34cddaf3.8a9cd6","type":"ui_tab","z":"","name":"testz","icon":"dashboard"}]



Yet Another Gauge

tmp6CBFThis one has been long in coming because despite my quickly evolving knowledge of Javascript and Canvas, that last leap – getting variable information out of a canvas and back into Node-Red – has kept me on edge for a couple of days now.  Dave (DCEEJAY) pointed me to various solutions all of which failed for me but then this morning he came up with a winner. I’ll go into detail in this article.

The gauge is simple enough – very much like the ones that Node-Red Dashboard already has – but it is adjustable (hmm, that’s a thought – I have suggested the Node-Red guys add a tick option to make the existing gauges adjustable. For now – this is it.

So – this started off as one of the examples from RGraph – you’ll need to include those libraries as previously. So note that the units used (degrees C) can be changed to anything you want – percentage or whatever. That is in the setup and is the entry marked “unitsPost”.

We have the by now familiar function(scope) function which allows us to inject values into the gauge – in this case msg.payload sets the gauge value.

We also have a special function which returns variable “value” to Node-Red on mouse up. This is the piece of the puzzle that has kept me going for days.  We also have a function which occurs on mouse up that grabs the current gauge value and stores it in “value”.  In the template itself, I’ve un-ticked “pass through messages” so that clicking or touching the gauge returns the new value of the gauge  - but injecting values does NOT product an output.

Now why is that last point important. Well, put such a gauge up on your computer screen and also on a secondary screen or phone – and watch what happens. Adjust one and…. nothing – the OTHER screen shows no change – oh dear. You need to inject the output back into the input for this to happen and unless you un-tick that box – the gauge will disappear up it’s own bum! For reasons beyond me you can’t connect a template’s output back to it’s input – so I just use an empty function to do that – well, empty other than I take the opportunity to put the value under the function just for effect.

I’ve made this to fit a 6*4 block. You can make it smaller by adjusting the size of the canvas, the gutter offsets and font size – but I could only manage to get one on a given tab without some unexplained interaction so I left the gauge this size which is handy for fingers on a typical phone.


A view of the code:

[pcsh lang="js" tab_size="4" message="" hl_lines="" provider="manual"]

<script src="/myjs/RGraph/RGraph.common.core.js"></script>
<script src="/myjs/RGraph/RGraph.common.dynamic.js"></script>
<script src="/myjs/RGraph/RGraph.semicircularprogress.js"></script>

    var value=0;

        scope.$watch('msg', function(msg) {
               if (typeof(msg.payload) != "undefined") { ssp.value=msg.payload; ssp.grow(); }

this.scope.action = function() {
    return value;
    var ssp = new RGraph.SemiCircularProgress({
        id: 'newgauge1',
        min: 0,
        max: 100,
        value: 86,
        options: {
            gutterTop: 2,
            gutterLeft: 20,
            gutterRight: 5,
            gutterBottom: 70,
            unitsPost: '°C',
            labelsCenterSize: 40,
            labelsCenterValign: 'center',
            labelsMinSize: 10,
            labelsMaxSize: 10,
            adjustable: true,
            textAccessiblePointerevents: false,
            colors: ['Gradient(#224499:white)'],
            anglesStart: RGraph.PI - 0.5,
            anglesEnd: RGraph.TWOPI + 0.5
ssp.canvas.onmouseup = function (e)
    var obj   =;

<canvas ng-mouseup="send({payload:action()})" class="knob" id="newgauge1" width="300" height="200">
    [No canvas support]


The actual flow for copying into Node-Red:

[pcsh lang="js" tab_size="4" message="" hl_lines="" provider="manual"]

[{"id":"3cbe837a.faf1dc","type":"ui_template","z":"c552e8d2.712b48","group":"33279d5b.72b122","name":"controller","order":0,"width":"6","height":"4","format":"<script src=\"/myjs/RGraph/RGraph.common.core.js\"></script>\n<script src=\"/myjs/RGraph/RGraph.common.dynamic.js\"></script>\n<script src=\"/myjs/RGraph/RGraph.semicircularprogress.js\"></script>\n\n<script>\n    var value=0;\n\n    (function(scope){\n        scope.$watch('msg', function(msg) {\n               if (typeof(msg.payload) != \"undefined\") { ssp.value=msg.payload; ssp.grow(); }\n        });\n  \n})(scope);\n\nthis.scope.action = function() {\n    return value;\n}\n    \n    var ssp = new RGraph.SemiCircularProgress({\n        id: 'newgauge1',\n        min: 0,\n        max: 100,\n        value: 86,\n        options: {\n            gutterTop: 2,\n            gutterLeft: 20,\n            gutterRight: 5,\n            gutterBottom: 70,\n            unitsPost: '°C',\n            labelsCenterSize: 40,\n            labelsCenterValign: 'center',\n            labelsMinSize: 10,\n            labelsMinOffsetAngle:-0.1,\n            labelsMinOffsetx:0,\n            labelsMinOffsety:0,\n            labelsMaxSize: 10,\n            labelsMaxOffsetAngle:0.1,\n            labelsMaxOffsetx:0,\n            labelsMaxOffsety:0,\n            adjustable: true,\n            textAccessiblePointerevents: false,\n            colors: ['Gradient(#224499:white)'],\n            anglesStart: RGraph.PI - 0.5,\n            anglesEnd: RGraph.TWOPI + 0.5\n        }\n    }).grow();\n  \nssp.canvas.onmouseup = function (e)\n{\n    var obj   =;\n    value=obj.value;\n}  \n </script>\n\n<canvas ng-mouseup=\"send({payload:action()})\" class=\"knob\" id=\"newgauge1\" width=\"300\" height=\"200\">\n    [No canvas support]\n</canvas>","storeOutMessages":true,"fwdInMessages":false,"x":940,"y":1880,"wires":[["5ea77ad6.1b8064","8dc0930c.a5aaf"]]},{"id":"ecf3d231.ddba3","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"40","payloadType":"str","repeat":"","crontab":"","once":false,"x":770,"y":1880,"wires":[["8dc0930c.a5aaf"]]},{"id":"d9c0b1d6.35dbb","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"80","payloadType":"str","repeat":"","crontab":"","once":false,"x":770,"y":1920,"wires":[["8dc0930c.a5aaf"]]},{"id":"5ea77ad6.1b8064","type":"debug","z":"c552e8d2.712b48","name":"","active":true,"console":"false","complete":"false","x":1130,"y":1880,"wires":[]},{"id":"8dc0930c.a5aaf","type":"function","z":"c552e8d2.712b48","name":"pass thru","func":"node.status({fill:\"blue\",shape:\"dot\",text:msg.payload});\n\nreturn msg;","outputs":1,"noerr":0,"x":940,"y":1940,"wires":[["3cbe837a.faf1dc"]]},{"id":"33279d5b.72b122","type":"ui_group","z":"","name":"LCD Test","tab":"34cddaf3.8a9cd6","disp":true,"width":"6"},{"id":"34cddaf3.8a9cd6","type":"ui_tab","z":"","name":"testz","icon":"dashboard"}]



Another LCD

LCD colours by Peter ScargillWhen writing that last blog entry about an LCD with time and date display and programmable temperature for Node-Red Dashboard, it occurred to me that it would be worthwhile making a simple one-liner (6*1) LCD with a 20-character display. At the same time I wanted to simplify it right down and here’s the result. Should make a good starting point for anyone wanting to develop their own.


In this case you simply fire a message at the template node. You only need one font for this from the ones used in the previous entry (really you should read the previous article to understand how to load the fonts in – trivial).

I’ve done a check on msg.payload and if the first character is a period I then use the remainder of the message to select colour – hence…


And that really is it – really simple to use – make the template 6*1,insert the code, make sure the font is in place and when you run it – you can select the sample text or play with the colours – enjoy.

LCD colours by Peter Scargill

LCD colours by Peter Scargill

LCD colours by Peter Scargill

[pcsh lang="js" tab_size="4" message="" hl_lines="" provider="manual"]

[{"id":"7744e0e4.38986","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"Hello there","payloadType":"str","repeat":"","crontab":"","once":false,"x":800,"y":1180,"wires":[["864d97a5.30d348"]]},{"id":"543d4cba.38a7a4","type":"inject","z":"c552e8d2.712b48","name":"green","topic":"","payload":".blackOnGreen","payloadType":"str","repeat":"","crontab":"","once":false,"x":790,"y":1220,"wires":[["864d97a5.30d348"]]},{"id":"658a2f15.d95bc","type":"inject","z":"c552e8d2.712b48","name":"orange","topic":"","payload":".blackOnOrange","payloadType":"str","repeat":"","crontab":"","once":false,"x":790,"y":1260,"wires":[["864d97a5.30d348"]]},{"id":"b60e3a3f.931078","type":"inject","z":"c552e8d2.712b48","name":"blue","topic":"","payload":".blackOnBlue","payloadType":"str","repeat":"","crontab":"","once":false,"x":790,"y":1300,"wires":[["864d97a5.30d348"]]},{"id":"62767df.a99d984","type":"inject","z":"c552e8d2.712b48","name":"yellow","topic":"","payload":".blackOnYellow","payloadType":"str","repeat":"","crontab":"","once":false,"x":790,"y":1340,"wires":[["864d97a5.30d348"]]},{"id":"ad983ec0.bd2ed","type":"inject","z":"c552e8d2.712b48","name":"white","topic":"","payload":".blackOnWhite","payloadType":"str","repeat":"","crontab":"","once":false,"x":790,"y":1380,"wires":[["864d97a5.30d348"]]},{"id":"a046dc0e.953ea","type":"inject","z":"c552e8d2.712b48","name":"pink","topic":"","payload":".blackOnPink","payloadType":"str","repeat":"","crontab":"","once":false,"x":790,"y":1420,"wires":[["864d97a5.30d348"]]},{"id":"8372ff69.e0a4c","type":"inject","z":"c552e8d2.712b48","name":"red and light text","topic":"","payload":".yellowOnRed","payloadType":"str","repeat":"","crontab":"","once":false,"x":820,"y":1460,"wires":[["864d97a5.30d348"]]},{"id":"5fcd3b1d.a64954","type":"inject","z":"c552e8d2.712b48","name":"cyan and white","topic":"","payload":".whiteOnCyan","payloadType":"str","repeat":"","crontab":"","once":false,"x":820,"y":1500,"wires":[["864d97a5.30d348"]]},{"id":"a81aab9f.8f78a8","type":"inject","z":"c552e8d2.712b48","name":"lime on black","topic":"","payload":".limeOnBlack","payloadType":"str","repeat":"","crontab":"","once":false,"x":810,"y":1540,"wires":[["864d97a5.30d348"]]},{"id":"864d97a5.30d348","type":"ui_template","z":"c552e8d2.712b48","group":"33279d5b.72b122","name":"LCD","order":0,"width":"6","height":"1","format":"<script>\n    var LCDColours={\n    \"blackOnOrange\": {items:[\"#222\",\"#fb7c00\"]},    \n    \"blackOnGreen\" : {items:[\"#222\",\"#66ac66\"]},\n    \"blackOnBlue\" : {items:[\"#222\",\"#8888ff\"]},\n    \"blackOnYellow\" : {items:[\"#222\",\"#bbbb44\"]},\n    \"blackOnWhite\" : {items:[\"#222\",\"#aaaaaa\"]},\n    \"blackOnPink\" : {items:[\"#222\",\"#ff8888\"]},\n    \"yellowOnRed\" : {items:[\"#ccaa22\",\"#aa2222\"]},\n    \"whiteOnCyan\" : {items:[\"#dddddd\",\"#227777\"]},\n    \"orangeOnBlack\" : {items:[\"#ff8800\",\"#000000\"]},  \n    \"limeOnBlack\" : {items:[\"#00cc55\",\"#000000\"]} \n    } ;\n\n   (function(scope){ \n            scope.$watch('msg', function(msg) {\n                if (typeof(msg.payload) != \"undefined\") \n                        {\n                            if (msg.payload.substring(0,1)!=\".\") $(\"#LCDTextBody\").text(msg.payload);  \n                            else\n                            {\n                              msg.payload=msg.payload.substring(1);\n                              $(\".LCDWrapper\").css('background-color', LCDColours[msg.payload].items[1]);  $(\".LCDTextFront\").css('color', LCDColours[msg.payload].items[0]); \n                              if (LCDColours[msg.payload].items[1]==\"#000000\") $(\".LCDTextBack\").css('color',\"rgba(255,255,255,0.15)\"); else  $(\".LCDTextBack\").css('color',\"rgba(0,0,0,0.1)\");                                \n                                \n                            }\n                        }   \n            });\n    })(scope);\n\n</script>\n\n<style type=\"text/css\">\n@font-face {\n  font-family: \"D14MI\";\n  src: url(\"/myfonts/DSEG14Modern-Italic.woff\") format('woff');\n}\n\n\n.LCDWrapper{\n\tposition:relative;\n\tborder:3px solid #000;\n\tborder-radius:8px;\n\theight:66px;\n\twidth:304px;\n\tcolor: 0;\n    font-family: \"D14MI\";\n\tbackground-color:#66ac66;\n\tbox-shadow: 3px 3px 10px 0px rgba(0,0,0,0.3) inset; \n}\n\n.LCDTextBack{\n\tz-index:50; color:rgba(0,0,0,0.1); \n}\n\n.LCDTextFront{\n\tz-index:51; color:rgba(0,0,0,1);\n}\n\n.LCDTextBack,.LCDTextFront {\n   \tposition:absolute;\n\ttop:3px;\n\tleft:6px; \n\tfont-size:18px;\n}\n\n</style>\n\n<div class=\"LCDWrapper\">\n\t<span class=\"LCDTextBack\">~~~~~~~~~~~~~~~~~~~~</span>\n\t<span class=\"LCDTextFront\" id=\"LCDTextBody\" ></span>\n\n</div>\n","storeOutMessages":false,"fwdInMessages":false,"x":990,"y":1320,"wires":[[]]},{"id":"8e623ee.81f9bc","type":"inject","z":"c552e8d2.712b48","name":"","topic":"","payload":"Goodbye","payloadType":"str","repeat":"","crontab":"","once":false,"x":800,"y":1140,"wires":[["864d97a5.30d348"]]},{"id":"33279d5b.72b122","type":"ui_group","z":"","name":"LCD Test","tab":"34cddaf3.8a9cd6","disp":true,"width":"6"},{"id":"34cddaf3.8a9cd6","type":"ui_tab","z":"","name":"testz","icon":"dashboard"}]



SteelSeries and Node-Red

tmp768EYou should read the previous blog entry before this one – as the principles are the same as are some of the details such as the location of your /myjs folder for Node-Red – so read the other article first. This guage IMHO is the best yet!

To run the example on the right here you should get the SteelSeries library. 2 minified JS files is all you need.

See flow below… you will need the following incoming variables – all done for you in the example but of course you’ll want to customise…

msg.value   // the value of the pointer – in this case 0-100

msg.threshold // the value of the red threshold indicator

msg.userLed  // if true the green user LED will be flashing. You can turn this off if you don’t need it. I’ll use it to indicate a dehumidifier is running.

If the value is less than the threshold, the red LED will flash. This can be inverted in the code. If msg.userLed is true the green LED will be flashing.

I’ve ALSO added as you can see separate ODOMETER (msg.odo - you could have LCD instead) and a trending UP/DOWN/STEADY/OFF  indicator and demo controls – discovering what is available on this gauge is half the battle!!

Everything is configurable, I’ve brought out what I think is useful – the rest is buried in steelseries.js which comes with the package – there is no documentation as such. All you need to store in your myjs library is steelseries-min.js and tween-min.js – but is it worth it? SURE IS  - this is a wonderful device… I can’t wait to get one on a nice large display on the wall.

I’m thinking general use to show temperature, odometer could show humidity, green user LED for dehumidifier on, RED light for heating on and the trending indicator to show maybe whether the heat is on the way up or down – though that much should be obvious from whether the heating is on or not… maybe a use I’ve not thought of yet. Or not – you can turn it off if you want. Wonderful.


Prerequisites: Far too often in blogs like this we “assume” that everyone is keeping up – if not – may I suggest a quick look at this page I put up specifically to give a little background – which might help explain this article.

Looking for help: If anyone knows how to get rid of that chrome frame - DO let me know. frameVisible=false makes it hide but does not expand the gauge to fill in the space -in other words you are still left with an invisible frame - the only article I can find suggests this should not be the case - but it is. Given a small 3*3 frame size I'm after all the resolution I can get).

Here is my code for the example above (also available here):

[pcsh lang="js" tab_size="4" message="" hl_lines="" provider="manual"]

[{"id":"8d862a4b.c1d588","type":"ui_template","z":"c552e8d2.712b48","group":"1e03a2b2.83a61d","name":"rgraph","order":1,"width":"6","height":"6","format":"<script src=\"/myjs/tween-min.js\"></script>\n<script src=\"/myjs/steelseries-min.js\"></script>\n<script>\nvar radial4;\n    (function(scope){ \n        scope.$watch('msg', function(msg) {\n           if (typeof(msg.value) != \"undefined\") radial4.setValueAnimated(msg.value);\n           if (typeof(msg.threshold) != \"undefined\") radial4.setThreshold(msg.threshold);\n           if (typeof(msg.odo) != \"undefined\")radial4.setOdoValue(msg.odo);  \n           if (typeof(msg.userLed) != \"undefined\") radial4.setUserLedOnOff(msg.userLed);  \n           if (typeof(msg.trend) != \"undefined\")\n            {\n                if (msg.trend==1)     radial4.setTrend(steelseries.TrendState.UP);\n                if (msg.trend==0)     radial4.setTrend(steelseries.TrendState.STEADY);\n                if (msg.trend==-1)    radial4.setTrend(steelseries.TrendState.DOWN);\n                if (msg.trend==-2)    radial4.setTrend(steelseries.TrendState.OFF);\n            }\n        });\n    })(scope);\n\n    var sections = [steelseries.Section(0, 25, 'rgba(0, 0, 220, 0.3)'),\n                        steelseries.Section(25, 50, 'rgba(0, 220, 0, 0.3)'),\n                        steelseries.Section(50, 75, 'rgba(220, 220, 0, 0.3)') ],\n\n            // Define one area\n    areas = [steelseries.Section(75, 100, 'rgba(220, 0, 0, 0.3)')],\n\n    radial4 = new steelseries.Radial('canvasRadial4', {\n            gaugeType: steelseries.GaugeType.TYPE4,\n            size: 292,\n            section: sections,\n            area: areas,\n            titleString: \"Heating\",\n            unitString: \"Degrees C\",\n            threshold: 50,\n            thresholdRising: false,\n            userLedVisible: true,\n            useOdometer: true,\n            lcdVisible: true,\n            trendVisible: true\n        });\n                        \n    radial4.setFrameDesign(steelseries.FrameDesign.BLACK_METAL);\n    radial4.setValueAnimated(0);\n    radial4.setThreshold(50);\n    radial4.blinkUserLed(0);\n    radial4.setOdoValue(0);\n\n\n</script>\n\n<canvas id=\"canvasRadial4\" width=\"401\" height=\"401\"></canvas>\n","storeOutMessages":true,"fwdInMessages":false,"x":490,"y":920,"wires":[[]]},{"id":"94d08897.6b6f78","type":"inject","z":"c552e8d2.712b48","name":"Set needle to 88","topic":"","payload":"88","payloadType":"num","repeat":"","crontab":"","once":false,"x":140,"y":700,"wires":[["a72af19d.e17e2"]]},{"id":"f5301eaa.f021d","type":"inject","z":"c552e8d2.712b48","name":"Set needle to 33","topic":"","payload":"33","payloadType":"num","repeat":"","crontab":"","once":false,"x":140,"y":740,"wires":[["a72af19d.e17e2"]]},{"id":"a72af19d.e17e2","type":"function","z":"c552e8d2.712b48","name":"","func":"msg.value=msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":720,"wires":[["8d862a4b.c1d588"]]},{"id":"cb9d2598.de4918","type":"inject","z":"c552e8d2.712b48","name":"Set threshhold to 60","topic":"","payload":"60","payloadType":"num","repeat":"","crontab":"","once":false,"x":150,"y":780,"wires":[["d1f505b0.62c9f8"]]},{"id":"b50f3ae1.7cbdd8","type":"inject","z":"c552e8d2.712b48","name":"Set threshold to 30","topic":"","payload":"30","payloadType":"num","repeat":"","crontab":"","once":false,"x":150,"y":820,"wires":[["d1f505b0.62c9f8"]]},{"id":"d1f505b0.62c9f8","type":"function","z":"c552e8d2.712b48","name":"","func":"msg.threshold=msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":800,"wires":[["8d862a4b.c1d588"]]},{"id":"cd9b1fe2.9c989","type":"inject","z":"c552e8d2.712b48","name":"Set user LED ON","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"x":140,"y":861,"wires":[["199bc0e7.607eff"]]},{"id":"59205954.a730c8","type":"inject","z":"c552e8d2.712b48","name":"Setuser LED OFF","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"x":150,"y":901,"wires":[["199bc0e7.607eff"]]},{"id":"199bc0e7.607eff","type":"function","z":"c552e8d2.712b48","name":"","func":"msg.userLed=msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":881,"wires":[["8d862a4b.c1d588"]]},{"id":"f13cc85.7df0038","type":"comment","z":"c552e8d2.712b48","name":"Example use of Steelseries Gauge","info":"","x":180,"y":660,"wires":[]},{"id":"ffa9ff04.4fc7f","type":"inject","z":"c552e8d2.712b48","name":"Trend UP","topic":"","payload":"1","payloadType":"num","repeat":"","crontab":"","once":false,"x":120,"y":940,"wires":[["2320de53.8b2e42"]]},{"id":"3c6f1b6f.e42924","type":"inject","z":"c552e8d2.712b48","name":"Steady","topic":"","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"x":110,"y":980,"wires":[["2320de53.8b2e42"]]},{"id":"2320de53.8b2e42","type":"function","z":"c552e8d2.712b48","name":"","func":"msg.trend=msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":960,"wires":[["8d862a4b.c1d588"]]},{"id":"e0daa005.96fd5","type":"inject","z":"c552e8d2.712b48","name":"Down","topic":"","payload":"-1","payloadType":"num","repeat":"","crontab":"","once":false,"x":110,"y":1020,"wires":[["2320de53.8b2e42"]]},{"id":"893a4668.be5a78","type":"inject","z":"c552e8d2.712b48","name":"Off","topic":"","payload":"-2","payloadType":"num","repeat":"","crontab":"","once":false,"x":110,"y":1060,"wires":[["2320de53.8b2e42"]]},{"id":"a222fdf0.4934d","type":"inject","z":"c552e8d2.712b48","name":"ODO 50","topic":"","payload":"50","payloadType":"num","repeat":"","crontab":"","once":false,"x":120,"y":1100,"wires":[["f7bee9bf.d05478"]]},{"id":"7a3e24c1.17e7dc","type":"inject","z":"c552e8d2.712b48","name":"ODO 78.6","topic":"","payload":"78.6","payloadType":"num","repeat":"","crontab":"","once":false,"x":120,"y":1140,"wires":[["f7bee9bf.d05478"]]},{"id":"f7bee9bf.d05478","type":"function","z":"c552e8d2.712b48","name":"","func":"msg.odo=msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":1120,"wires":[["8d862a4b.c1d588"]]},{"id":"1e03a2b2.83a61d","type":"ui_group","z":"","name":"testy","tab":"f9bab960.c839b8","disp":true,"width":"6"},{"id":"f9bab960.c839b8","type":"ui_tab","z":"","name":"testy","icon":"dashboard"}]



RGraph with Node-Red

RGraph GaugeHaving gone from spending countless hours staring at HTML5 CANVAS, I’m now at the “meh” stage as it starts to dawn on my how it works.

And so it was that I stumbled on RGraph – or put another way, Christmas for widget-lovers.

If you’ve been following these blog entries you’ll know that Node-Red has TEMPLATES in the UI – and that you can put your own stuff into the templates and that recently the fog has lifted on getting variables in and out of the templates.

In recent blogs I’ve been constantly improving a thermostat control page and that took me off looking for a gauge with two pointers – one to show temperature, the other to show humidity.

And that’s when I stumbled upon RGraph. If you read this – and understand it – you will open the doors to a boatload of gauges, thermometers, charts and graphs so tuck in:

First things first, if you’ve already played with Node-Red in here you will likely have made a /myjs folder (home/pi/.node-red/public/myjs or similar – defined in your Node-Red settings.js file) to put various Javascript files in. Well, add this lot in a sub-folder called RGraph – you can call it freddy if you like but I thought it reasonable to use the name the way they use it. I grabbed the latest stable version from here. Inside there is a folder called RGraph – and inside that is a folder called libraries – I grabbed the contents of that folder and put it inside my /public/RGraph folder. It may be there is a use for other stuff in there – but for now that’s all I’ve taken.

So – then I dropped in a template – made it 6*6 and inside that template I put this lot – code shown below.

Now, if you don’t like my colours – change them. You can change just about anything including the size but you may need to adjust the font size if you do that. Experiment!

To change the two pointers – which I’ve chosen to call temperature and humidity – you might use them for petrol and oil – or whatever….I simply pass MSG as is common in Node-Red – but not msg.template – instead msg.temperature and msg.humidity – you can call them whatever you like.

The point of this is not to demonstrate my crap taste in colours – once you follow what I’ve done here – that entire, massive library of CANVAS-related gauges and charts is yours for the taking!  You can make the gauges interactive – but as I had two, not one pointers in this example, I skipped that. Details are in the extensive RGraph documentation.  Copy me and drop them an encouraging lines to say MORE IOT PLEASE!!

Oh and if you don’t like animation – where I say “grow” say “draw”.

(As an aside, I got this working today as well -  very pretty but doesn’t scale well.

Prerequisites: Far too often in blogs like this we “assume” that everyone is keeping up – if not – may I suggest a quick look at this page I put up specifically to give a little background – which might help explain this article.

[pcsh lang="js" tab_size="4" message="" hl_lines="" provider="manual"]

<script src="/myjs/RGraph/RGraph.common.core.js" ></script>
<script src="/myjs/RGraph/RGraph.gauge.js" ></script>

<script language="javascript" type="text/javascript">

                scope.$watch('msg', function(msg) {
            var gauge3 = new RGraph.Gauge({
                id: 'cvs',
                min: 0,
                max: 100,
                value: [23,60],
                options: {
                    titleTop: 'Temperature',
                    titleTopSize: '16',
                    titleTopFont: 'Impact',
                    titleTopColor: '#ff8888',
                    titleTopPos: 0.25,
                    titleBottom: 'Humidity',
                    titleBottomSize: '14',
                    titleBottomFont: 'Impact',
                    titleBottomColor: '#8888ff',
                    titleBottomPos: 0.3,
                    backgroundColor: 'black',
                    backgroundGradient: true,
                    centerpinColor: '#666',
                    needleSize: [null, 50],
                    needleColors: ['Gradient(transparent:yellow:orange:#ff8888:#ff8888)', 
                    textColor: 'white',
                    tickmarksBigColor: 'white',
                    tickmarksMediumColor: 'white',
                    tickmarksSmallColor: 'white',
                    borderWidth: 1,
                    borderOuter: '#666',
                    borderInner: '#3333',
                    colorsRanges: [
                    textAccessible: true


    <canvas id="cvs" width="300" height="300">[No canvas support]</canvas>