Category Archives: Node Red

RFLink and Node-Red

RFLink Board and MegaA couple of weeks ago I went to stay with my pal Jonathan and he had some Byron doorbell pushes to play with.

This article updated 22/03/2017

He showed me a universal RF 433Mhz receiver board he purchased which could look at the signals from the various kids of 433Mhz standard transmitting units.

This was interesting as I have an Acurite weather station with the most appalling interface which requires a PC to be on constantly in order to remotely access the information.

I don’t know what planet the designers were on – but it was a gift from my wife, solar powered and including rain level, wind speed and direction etc.. a nice job other than the software. It has been sitting outside the wall on my office for months doing nothing.  I thought it might be interesting to get that going.

RFLink

The unit my friend was using was rather expensive but he suggested I try the RFLink software along with a DIY unit comprising an Arduino Mega2560, a little board called an RFLink V1.1.4 (now updated) and an antenna – you see the lot here.

tmpC75DThe kit arrived days ago and I put it together with a soldering iron easily enough but had to wait for a Mega board to arrive which it did this week. I downloaded the software – very simply install program for the PC – couple of button presses really – and that was that. I plugged the little RF board into the MEGA, plugged the Mega into my PC and… out of the blue, information from my Acurite board appeared out of no-where, as did  more one-liners from my Byron button presses and even our doorbell. It took no time at all with help from a fellow enthusiast who’s used this stuff before to figure out how to send a signal back to the doorbell to make it work.

All very nice but I needed this into Node-Red.

I took my latest Raspberry Pi using DietPi and after adjusting comms permissions, simply plugged the USB device into the Pi and set up a serial node for both transmit and receive.

From there on it was easy. Far from complete - but thought you might be interested – the combination of the two boards and that software seems to work really well up to now for receiving from 433Mhz sensors and for sending out commands to 433Mhz boards.

I had a slight concern about how long you have to leave the Byron SX35 pushbuttons before pressing again (3 seconds). But I  mentioned this to the author and within an hour he came back with an update which made the delay much more practical.

Valid input instructions from the various sensors is in this link along with the Arduino software download….

http://www.nemcon.nl/blog2/protref

I bought a bog-standard Arduino 2560 (cheap Chinese version)

I bought this board… the RFLink 433 kit – requires a little soldering…

https://www.nodo-shop.nl/en/21-rflink-gateway

10 minute soldering (take note of version numbers – important), 10 minutes max to blow the software. Test the board (56k baud) to ensure when nearby sensors are sending results – they are coming in and then I wrote this test… it isn’t very elegant yet.

My SERIAL node is set to split input on character “\n” so the code has to get rid of return characters as you’ll see in the “replace” line below. Note also that when you send out serial (to actually control something) it should be followed by both “\r\n”.

RF433

// So firstly a generic means of getting incoming items into an object

var the433 = {};
msg.payload = msg.payload.replace(/(\r\n|\n|\r)/gm,"");
node.warn(msg.payload);
var parts433 = msg.payload.split(";");

the433.p1 = parts433[0];
the433.p2 = parts433[1];
the433.name = parts433[2];

var a = 3;
while (a < parts433.length) {
    var bits433 = parts433[a].split("=");
    switch (bits433[0]) {
        case "ID": the433.id = bits433[1]; break;
        case "SWITCH": the433.switch = bits433[1]; break;
        case "CMD": the433.cmd = bits433[1]; break;
        case "SET_LEVEL": the433.set_level = parseInt(bits433[1], 10); break;
        case "TEMP": the433.temp = parseInt(bits433[1], 16) / 10; break;
        case "HUM": the433.hum = parseInt(bits433[1], 10); break;
        case "BARO": the433.baro = parseInt(bits433[1], 16); break;
        case "HSTATUS": the433.hstatus = parseInt(bits433[1], 10); break;
        case "BFORECAST": the433.bforecast = parseInt(bits433[1], 10); break;
        case "UV": the433.uv = parseInt(bits433[1], 16); break;
        case "LUX": the433.lux = parseInt(bits433[1], 16); break;
        case "BAT": the433.bat = bits433[1]; break;
        case "RAIN": the433.rain = parseInt(bits433[1], 16) / 10; break;
        case "RAIN": the433.rainrate = parseInt(bits433[1], 16) / 10; break;
        case "WINSP": the433.winsp = parseInt(bits433[1], 16) / 10; break;
        case "AWINSP": the433.awinsp = parseInt(bits433[1], 16) / 10; break;
        case "WINGS": the433.wings = parseInt(bits433[1], 16); break;
        case "WINDIR": the433.windir = parseInt(bits433[1], 10); break;
        case "WINCHL": the433.winchl = parseInt(bits433[1], 16); break;
        case "WINTMP": the433.wintmp = parseInt(bits433[1], 16); break;
        case "CHIME": the433.chime = parseInt(bits433[1], 10); break;
        case "SMOKEALERT": the433.smokealert = bits433[1]; break;
        case "PIR": the433.pir = bits433[1]; break;
        case "CO2": the433.co2 = parseInt(bits433[1], 10); break;
        case "SOUND": the433.sound = parseInt(bits433[1], 10); break;
        case "KWATT": the433.kwatt = parseInt(bits433[1], 16); break;
        case "WATT": the433.watt = parseInt(bits433[1], 16); break;
        case "CURRENT": the433.current = parseInt(bits433[1], 10); break;
        case "CURRENT2": the433.current2 = parseInt(bits433[1], 10); break;
        case "CURRENT3": the433.current3 = parseInt(bits433[1], 10); break;
        case "DIST": the433.dist = parseInt(bits433[1], 10); break;
        case "METER": the433.meter = parseInt(bits433[1], 10); break;
        case "VOLT": the433.volt = parseInt(bits433[1], 10); break;
        case "RGBW": the433.rgbc = parseInt(bits433[1].substring(0, 2), 16);
            the433.rgbw = parseInt(bits433[1].substring(2, 4), 16); break;
    }
    a++;
}

// SO - the above is general... here is my specific setup for temporarily displaying
// the Acurite info
if ((the433.p1 == "20") && (the433.name == "Acurite") && (the433.id == "c826")) {
    if (typeof the433.temp !== 'undefined') temp = the433.temp;
    if (typeof the433.hum !== 'undefined') hum = the433.hum;
    if (typeof the433.bat !== 'undefined') bat = the433.bat;
    if (typeof the433.rain !== 'undefined') rain = the433.rain;
    if (typeof the433.winsp !== 'undefined') winsp = the433.winsp;
    if (typeof the433.windir !== 'undefined') windir = the433.windir;

    node.warn("Temperature: " + temp + "c");
    node.warn("Humidity: " + hum + "%");
    node.warn("Battery: " + bat);
    node.warn("Rain: " + rain + "mm");
    node.warn("Wind Speed: " + winsp + "km/h");
    node.warn("Wind Dir: " + (windir * 22.5) + " degrees");
}

Put that in a Node-Red template – attach a Node-Red serial Node set to take serial input from USB0 at 56k – character /r as a separator and deliver ascii strings…and that – is just the beginning…  note also that the designer of this free software has also added GPIO control both input and output – on several pins (recently expanded so check his docs).

Tests: Right now for my tests  - I have the K10000 phone acting as a server running Debian and running Tasker and the MQTT client Tasker plugin with the same phone running as a resource…. and I can now fire an MQTT message at the latter to get a doorbell message out! Meanwhile a Raspberry Pi is running that RFLink unit and when one of the Byron doorbell pushes is pressed – a message is sent out to the phone to play the doorbell – yes, I know, somewhat over the top – but I’m just experimenting for now… and sure enough – press the button and pretty much in real time the doorbell sound appears.

433Mhz to MQTT GatewayIn the comments below you’ll see reference to an ESP8266 to MQTT Gateway – and this would be ideal as it would be all in one little box – whereas I need to stick something like a Pi on the end to generate a wireless MQTT signal…. so – I grabbed the software and (disregarding several wasted hours due to a duff FTDI) put together one of these – as you’ll see in the photo on the right-  the antenna is due to kind feedback below – and as you can see, it is a precision job (it is accurate however).

Well, I have a 4-way remote control for a cheap Chinese remote and indeed this little system does pick it up and sends a unique number for each key off as MQTT – lovely – however – even with a decent little aerial the unit does not pick up (or recognise) my weather station of any of my BYRON pushbuttons – and the data coming back is crude compared to the RFLink software so at first glance, not impressed.

Costs:  Ok of course the hardware for the little ESP board is DIRT CHEAP compared to what I’ve put together – which in turn is cheap compared to one of these all in one boxes – but you pay your money – I’m sticking with https://www.nodo-shop.nl/en/21-rflink-gateway – at under £20 plus £9 for the aerial plus a Mega2560 (cheap from China) I think it is worth it (no I don’t know the company and no I didn’t get samples etc).

The transceiver they supplied costs  £16.51 on Ebay so the board with connectors and the transceiver really is a good deal.  You can of course use cheaper receivers – but the software writer suggests these might be naff. I’d like to hear back from someone who had had GOOD results just using the Mega board (they’re only a fiver from China) and other boards. There is information here on that subject. RXB6 board seems cheap but don’t buy from the UK as they seem to be a rip here – on guy wants nearly £8 inc postage – China for same board – under £2 all in. I’m kind of stuck for testing one of these as I’m off to Spain in a few weeks and chances are it won’t get to the UK in time for me leaving!! Shame as I’d like to have given one a shot on my spare MEGA board.

Summary: Already the RFLINK setup has made my day by turning my otherwise useless weather station into another working part of my home control – and I’ll soon have buttons all over the place. It also has good range though I think aerial design and positioning could be improved.  I can’t do a range comparison with the ESP project as it only recognises one my my 433Mhz devices and even then gives out a number, no proper ID and name… still – worth keeping an eye on for future developments.


If you like this post – please share a link to it by social media, by email with friends or on your website.
More readers means more feedback means more answers for all of us. Thank you!

Facebooktwittergoogle_pluspinterestlinkedin

Android Phone as Server

Why am I showing you a picture of a cracked-screen phone?

Linux on Android PhoneWell because this particular phone is a bust Oukitel K10000, the phone with the world’s longest-lasting battery and an excellent workhorse. My friend Aidan gave me this (rather than chucking it away) some time ago and it has been sitting doing nothing. All that is wrong with it is a cracked (and exceedingly dangerous on the fingers) screen. I’ll bet I’m not the only one with such a phone lying around wasting space.

Well, as of yesterday, it is a Debian server with all my usual stuff going on quietly in the background – with the screen normally off – running freezing cold and hopefully being super reliable.

This is an experiment only – beware – if your phone or tablet dies it is your problem….  oh and your Android phone/tablet needs to be ROOTED. 

Imagine turning your old, dust-covered phone into a sleek, battery backed-up server with unfeasibly long backup time, immunity to any mains spikes, a silent, fast Debian base for all the stuff in my script – which includes Node-Red, Apache/PHP, SQLITE (+ PHPLiteAdmin), MQTT, MC, Ha-Bridge and more!  If you’ve been following this blog you’ll know about the script.

So this article applies to ROOTED Android phones and we’ve only tested around Android 5.0 onwards.  In my case I rooted the phone with KingRoot (note, NOT the one in the PlayStore which is a con – avoid it - but the one at kingroot.net ) – please note that rooting phones is beyond the scope of this article and if you are not confortable with this you should not do it. There are a lot of links out there on the subject and many of them are fraudulent.

tmpCD52There is an APP in the Play Store called Linux Deploy. It is also on GitHub. Beware that this is essentially undocumented unless you are Russian – so please don’t ask how you can use your phone’s GPS or Sound from Debian – not a clue!

You should have a modern-ish WiFi enabled (or hardwired if you like) Android phone or tablet with at least 16GB of internal FLASH (which should give you 10GB working space).   If you only have 8GB that will leave you only 2GB which - really – isn’t enough.

Getting Debian 8 on the phone:  After ensuring your phone/tablet is rooted, Install the App.

Linux on Android PhoneIn the app, on the top LEFT menu – find  REPOSITORIES and select Debian 8 for ARM.

On the bottom right is a drop down – you should go in there and select INSTALLATION TYPE – directory (OR FILE with a reasonable file size limit – say 5GB – the 2GB default will NOT work). Change the user to “pi” with suitable password in the same menu. TICK for allowing initialisation – and use of SSH server. Also where you see INIT SYSTEM change that to sysv. 

Then TOP RIGHT menu  - INSTALL – that might take some time – top right again CONFIGURE – then bottom menu START – and you should have a working Linux you can get into with for example WINSCP for that “pi” user. The IP address is clearly shown in the App.

I suggest not going further until you are comfortable with the above – took me several attempts because I didn’t follow the above exactly (well, and Antonio and I were learning at the same time).

Running the script: Via WinSCP or similar, put the script into the pi directory – changing permissions as normal - run the script – and ensure PHONE is selected – it isn’t by default. Come back after lunch. The script will no doubt suggest a reboot. Instead, hit the STOP button on the bottom of the phone screen – wait for it complete, hit the START button – wait – and you should now have everything in the script running!

Now – I’m using this to run my script – but I’m thinking you COULD use it to serve files etc. – I’ve not tried this but I’m guessing it would be easy to access an SD card…. and make that a folder…. hmmm.

Anyway, it is now the day after I installed all this – the phone is sitting there “off” and unlike my FriendlyArm board with it’s whirling fan, is sitting silently and freezing cold yet ran the script much faster than any of my SBCs – around 40 minutes.

K10000 running Debian 8No guarantees as there just isn’t enough info about Linux Deploy out there (unless you find/translate some) – but it does seem to work well now that we’ve made sufficient alterations to the script to take this particular setup into account. A fun project, to be sure. Now, I know this is a not a fair comparison and tomorrow we might come back and say … no good (for whatever reason).. but at £107 for that particular phone… compare – how much would it costs for a Raspberry Pi 3, uninterruptable power supply able to keep the phone going for something like a couple of days with no power, a high-def touch screen, a solid case for the whole thing.. indeed ,it might be possible to use a cheap tablet…  I was looking on Ebay – 16GB Android tablet – perfectly working except for missing buttons and cracked digitiser – around £10

One thing to note – I’ve turned rsyslog messages off – it was spitting out all sorts of unwanted helpful messages when you changed brilliance on the phone or disconnected the USB etc –REALLY annoying.. If you feel you need logging on -

sudo service rsyslog start

That will stay on until the next stop-start…

Node-Red running on a K10000 phoneSuch a shame it isn’t clear how to access some of the peripherals like audio. But who knows – someone reading this article may already have solved such challenges.

Please note: the pretty colours on the right do not come as standard. Click images to see larger versions.

This is really starting to look GOOD!!!!

Revelation: I’ve now taken on-board ideas from others and thanks for that – both people in here and on Google+ – most of the other solutions are longwinded and need other packaged  so up to now Linux Deploy – I’m now installing on my old Nexus 7 2012 UTTERLY SUCCESSFULLY (though not the fastest kid on the block) ( after rooting it with the Nexus toolkit ) - using Linux Deploy (which is completely self-contained and offers full root access – is looking the best). The ONLY thing you can’t do is use the Android peripherals – because of lack of UK info but this morning I figured it all out. 

We’ve also tested this one OnePlus One (model BACON) and a Xiaomi Redmi 3 model IDO). The K10000 has now been up for several days.

Ok, bear with me – you have Node-Red on Linux – and MQTT. So, you run Tasker on the phone (in the Android section) with MQTT – and now you have access to and control of all of the Android facilities that TASKER can handle (i.e. just about the lot) from within the Debian environment. Doddle.. now all I need is some time!!

Facebooktwittergoogle_pluspinterestlinkedin

Node-RED Global Flow and Context

There’s a mouthful. This short article is about the various kinds of variables used in Node-Red and more importantly how to initialise them and how to view them.

So in Node-Red when you want to use variables – there are three basic types as well as local variables.

So for example – you’ve opened up a FUNCTION block and you want to use some variables. VAR is always a good start.

var fred=1;

A way to get a variable to PERSIST behind that simply function invocation to the next time around, use this;

context.set(“fred”,1);

If you’d like that var to be available to any other function in that flow (or page if you like) - use:

flow.set(“fred”,1);

and if you need a global variable that can be accessed anywhere within Node-Red including other flows  – I use them all the time for things like thermostat objects etc… use global.

global.set(“fred”,1);

Of course you can recall said variables as global.get(“fred”); etc. Don't use the old method (example global.context.fred) - there's a reason the Node-Red guys are steering us in this direction.

This is not meant as a course on programming – you can read all about this on the Node-Red site – just look up variables.

So three useful types of variable.

But there’s a problem. Node-Red runs asynchronously and in my first efforts I would have an INIT tab and initialise my global variables there. Once in a blue moon I would see a non-destructive failure with the logs showing that I’d tried to use a variable before it had been initialised (created, even).  So in a platform where everything is running asynchronously how do you ensure (without checking every single time you use it) that a variable has been initialised…. especially important for globals.

One way which I don’t really like is to ensure that the variable is initialised in the left most tab. as that runs first… ok… kind of….

Well it turns out “there’s a node for that”.  Node-Red-Contrib-Config does that – you can set up a variable – which could be a whole object – an no matter where you put this node – it will run BEFORE the main flows start up – I tested it, it works. My only niggle might be that the VALUE field needs to be expandable – a single line is not much use for large objects.

npm install node-red-contrib-config

So that’s one side of it.

The other side is – would it not be nice if you could have a tab to show you the current status, names and values of all of your global variables – and more. Well, now you can.

This needs a SLIGHT mod to Node-Red so if you’re new you might want to back stuff up.

In the directory you normally (hopefully locally) npm install stuff –

https://www.npmjs.com/package/node-red-contrib-contextbrowser

So the install is:

npm install node-red-contrib-contextbrowser

Stop Node-Red and in (in my case) and make a slight edit

nano /usr/lib/node_modules/node-red/red/runtime/nodes/context.js

This is the function you need to alter - very carefully add one line as I have in bold. That’s it – don’t make any other changes.….and this IS case-sensitive.

function createContext(id,seed) {
var data = seed || {};
var obj = seed || {};
obj.get = function get(key) {
return util.getMessageProperty(data,key);
};
obj.set = function set(key, value) {
util.setMessageProperty(data,key,value);
}
  obj.getKeys = function() { return Object.getOwnPropertyNames(data); }
return obj;
}

If you're something that does not show bold, the line starting "obj.getKeys" is the addition.

(NODE RED GUYS – any chance of making the above standard???)

Start up Node-Red (this works even if you use HTTPS and have password on Node-Red)

And lo – you should have a new tab on the right… click in the editor on a node or function you know has variables initialised…

and then use the refresh in that new tab..

My test..

tmp6E51

The contents of that orange function

tmpD029

 

And..

tmp65A3

As you can see, you must select the particular node or function with the mouse first – then hit the little refresh in the contextbrowser tab - you then see above freddy and the value in the tab – along with any others you’ve defined in that node or function. Ok it’s not quite debugging but better than nothing.

Now moving to the Global tab is another matter as I have several globals including some big objects – if they are very big – you might have to hit the little … indicator to expand them.

tmp3C87

So for me this is marvellous – I can now set up global vars with the confidence that they will be initialised properly at the start – and I can also get a nice overall view of the globals I’ve used – something I could not easily do before.


If you like this post – please share a link to it by social media, by email with friends or on your website.
More readers means more feedback means more answers for all of us. Thank you!

Facebooktwittergoogle_pluspinterestlinkedin

Big Timer

tmp17DBBig Timer is probably the best timer for Node-Red, providing a general purpose timer as well as  handling summer/winter correctly as well as (importantly) lighting up time (for which it needs longitude and latitude). After all you probably don’t turn the outside lights on at 6pm!! You turn them on when it gets DARK.

Updated to v 1.5.6 –March 06, 2017 - Recommend updating. Here’s a new video!

BigTimers

What: Node-Red-Contrib-Bigtimer is both a simple timer – and a full-function timer – depending on your needs. At it’s simplest, you set on and off times, the topic and payload you need to go out to, say, MQTT and your longitude and latitude (most browsers will fill this in automatically), you then set your schedule and you are done.

Why: Existing timers didn’t do what I wanted – also – I have a watering system – I want it to run at dusk and dawn every day – but not in December through February (ice)!  I had to have control over both days of the week and months. Existing timers didn’t do that either. Many seemed to be based on the old mechanical timers but without moving parts. That won’t do.

Firstly: the name. It’s a timer – but it’s a BIG one – so I’ve called it BigTimer. (note: the original scheduler is no longer developed – BigTimer does everything Scheduler does and much, much more).

Secondly: it has an input. You can inject various words into BigTimer – and get instant over-ride action. "1" or “on”, "0" or “off”. "auto" or  “default” etc.

Does that need explaining? “on” for manual override to on, “off” for manual override to  off (both of which reset on the next auto state change), “default” or “auto” to return the timer to normal operation. More details later.

Manual Override: If you set the timer to turn on, say at dusk and off at midnight, you can override the setting during the day by sending “on” to the input. This override will reset at the next auto change of state.

If you inject "manual" in conjunction with  “on” and “off”, the state will change until the set timeout (in minutes) times out - so if you turn the lights on in manual and forget you did it? The unit will return to default operation in X minutes (which you can set - defaulting to 24 hours - 1440 minutes) after any manual change you make. You can also send a "sync" command which simply outputs the current state with a side effect of decrementing the timeout counter - handy for testing.

Here is a test setup for BigTimer, just using simply injection nodes:

BigTimer test setup

And here’s what happens if you press ON (a simple inject – you can send that message in the payload by any means you like)…

Bigtimer test setup[6]

But what are those other two outputs?

Output 1: is a message – you control the topic and payload – so I use it, for example, as I do, to send a message to MQTT to control something. You can have this message go out once – or every minute. There are other components to the msg object on the first output, there for information only.

Object

In this example (version 1.5.5 of Bigtimer onwards) you see various items in the output -also note that this view is in Node-Red 0.16.2 which is different to older versions – I strongly recommend you upgrade if using an earlier version.

You can see msg.payload which is whatever you enter in order to fire a message off to another node or function to make something happen. In addition (and for information only) we have msg.state, msg.value, msg.AutoState, msg.manualState and others. For simple use, you can of course ignore the additional outputs, but they are handy for checking. msg.state shows “on”, “off” or “none” and in the case of the first two (on/off) msg.time shows the time for the next change of state.

Output 2: outputs a 1 or 0 every minute when checking (in msg.payload) – you can use that to light up something.

Output 3: puts out another text message in msg.payload which you can set – I point it to a speech synth so it might say “external lighting coming on”. There is one message for the ON state, another for the OFF state. Ignore it if you don’t have a use for it.

And to get this node?

Make sure you are in your Node-Red installation folder (for example /home/pi/.node-red) and type:

npm install node-red-contrib-bigtimer

So in my case, I keep all my nodes under /home/pi/.node-red/node-modules.  So I do the install from /home/pi/.node-red

Restart Node-red and Bob’s your uncle – it should appear on the left with the other Nodes. If you happen to have the ADMIN node in Node-red you don’t even need to do any of that – you can install node-red-contrib-bigtimer straight from the admin tab.

In addition, I have recently,  as a response to a sensible request from one of our blog readers, changed the code so that if either the ON payload or OFF payload is blank – then no message will be sent at that time – this means you can now easily OR timers together. So if you need several time intervals in a day or other complicated setup you can simply use a number of timers and feel the first output to one common destination!

Here’s an example of TWO timers simply sending stuff to a debug node for testing…

two BIGTIMERS

Backtrack for Beginners: Node-Red is a simple-to-use but very powerful visual tool for wiring the Internet of Things and a tool for connecting together hardware devices, APIs and online services in new and interesting ways. It is also a lot of fun.

I control a range of Arduino and ESP-8266 devices using Raspberry PI2 and smiilar. You can of course run Node-Red and the other tools on a wide range of machines – Orange Pi, Roseapple Pi, Odroid C2 etc using Debian, DietPi, Xenial etc. I even have my normal installation script running on a laptop using Mint Linux.

tmp871On this particular Pi I run MQTT (Mosquito with Websockets), SQLITE, APACHE and Node-Red. I started using Blynk as my mobile remote controller then went on to using ImperiHome but today I concentrate on using Node-Red Dashboard which has (IMHO) so very much potential.  I also use Nextion touch-LCD display devices for local displays. All of this can be managed within Node-Red. If you find this interesting you might like my  home control 2016 post here or the Nextion Wi-Fi Touch Display article.

Here (as requested): to the right is a screenshot of a typical setup… click on the image to enlarge.

So in total you have a timer you can set to go on and off at specific times of the day, or dusk and dawn, on certain days, certain months, you can even set it to run on specific days of the month (Christmas day?) or even certain weekdays of every month (second Tuesday).

You can tick a box to have it output on power-up or not, you can tick a box to have it auto-repeat the message every minute or not. You can add positive and negative offsets to the times (including dusk and dawn etc.), you can optionally add your OWN offset to the UTC time of the host computer and you even select a random value within the limits of the offsets (security lighting etc.) You can temporarily suspend the schedule via a tick box or a message.

And here are the words the node will accept at the input to override automatic operations. The input is not case-sensitive.

sync simply forced output
on or 1 turn the output on
off or 0 turn the output off
default or auto return to auto state
stop stop the scheduler
on_override manually override the on time (in minutes or hours and minutes - space separated
off_override manually override the off time (in minutes or hours and minutes - space separated)

In the drop-down boxes for on and off times you can select times or words like dusk,dawn, sunrise, sunset, night etc. All are dependent on the longitude and latitude you put into the relevant boxes and will adjust every day as necessary.

A good way to learn about BigTimer is to put a debug node (set to show the complete message) on the top output – and inject text payloads (using the INJECT node) to the inputs as demonstrated above. 

Output from error and warnOut of the first output, as well as the normal msg.payload, you can extract msg.state which might be “on”, “off”, or “auto” – and msg.value which might be 1 or 0.  This is so that if you are using say, ImperiHome and store states in global variables, if the time reverts back to auto, you can let ImperiHome know what is happening.

Another way to test BigTimer a good way to do it is like this..

Big Timer Node

In the FUNCTION nodes to the right of BigTimer, I’ve simply put node.error(msg); and node.error(msg); respectively. The only difference is one has a red bar (error) the other a yellow bar (warn). These make a handy alternative to using the debug node for testing.

Recent additions to the inputs include on_override and off_override. These are in response to requests to be able to change the on and off times from the Dashboard. Please note that override information you inject here (in hours and minute or just minutes, space-delimited) is lost when Node-Red is restarted or the board rebooted.

If you make good use of this node – please put a link to this blog entry somewhere in your writings so people will come back here. Or if proves REALLY useful you could feed my Ebay habit – there’s a link on the right of the blog.

See the Node-Red info panel in Node-Red for up to date information.

Enjoy and please do report any issues back here.


If you like this post – please share a link to it by social media, by email with friends or on your website.
More readers means more feedback means more answers for all of us. Thank you!

Facebooktwittergoogle_pluspinterestlinkedin

Node Red and HighCharts

HiChart Charts in Node-RedFirst things first – HighCharts are not free for commercial use. However, for your own website, a school site or a non-profit organisation they are free to use. You are also OK to make modifications. So – that’s the “cover my back” bit out of the way – now, how to use them in Node-Red.

I’ve looked at this before, because their charts are very, very nice.  As some of you know, I’m now sold on Grafana but I also really like the idea of using charts with my favourite database – SQLITE and this is not supported as far as I can see with Grafana. Indeed I’m glad there is a Node-Red Node for InfluxDB as I had trouble getting my head around inputting data into InfluxDB otherwise (the node makes it REALLY easy).

And so it was that I came back to HighCharts. Now one of the demos of theirs that I really like, involves plotting variable numbers of time-based lines in a graph (temperature, humidity etc)– with potentially missing values. I found when using other chart systems that if you had the odd missing value due to power failure etc, you had to fix that – with the HighCharts demo it just draws a smooth curve between the two.

Another of their demos features zooming and it is only in the last couple of days when taking another look – that I realised you could combine the two – zoomable multi-line graphs.

What you see on the right is little more than a tweeked version of their basic demo – with a few bits taken out to make the whole thing work in the limited space we typically have in Node-Red Desktop – and zooming built in.

I tested everything in JSFiddle and then tried dumping it into Node-Red – nothing – zilch. I found that using the libraries remotely just would not work. Out of desperation I grabbed the libraries, one of which is big and put them into one of my usual “myjs” directories on the Raspberry Pi and lo and behold, it all worked.

Sample data into HiCharts

Here you see above my test – now bear in mind this is very preliminary. In the brown FUNCTION nodes I have 2 different sets of data just to test things… and these feed an object in msg.payload through to the Dashboard UI. I’ve made two of these – using two different DIVs. One is called “mine1” and the other is called “mine2” – they fill DIV ids “container1” and “container2" respectively. Everything else is the same.

So – starting off with their demo here http://www.highcharts.com/demo/spline-irregular-time

I took that demo and wrapped the lot in a function (object) – so as to hide everything from the outside world in case I wanted two or more of these on a page. I then wrapped the code inside a local, externally accessible function. This could be way more efficient – but I wanted something up and running and as you will see I have two completely independent graphs here.

Here is the code inside the blue template. The only thing that changes is the reference to the DIV (container1/container2) and the references to the new object (mine1/mine2) – and of course near the end I’ve fed unique titles and subtitles when creating the object but the latter do not have to be unique.

<script src="/myjs/highcharts.js"></script>
<script src="/myjs/exporting.js"></script>
<div id="container1" style="min-width: 300px; height: 300px; margin: 0 auto"></div>

<script>
   (function(scope){
        scope.$watch('msg', function(msg) {
          mine1.graph(msg.payload);
        });
    })(scope);


var sample_data1=[{
        name: 'Winter 2012-2013',
        // Define the data points. All series have a dummy year
        // of 1970/71 in order to be compared on the same x axis. Note
        // that in JavaScript, months start at 0 for January, 1 for February etc.
        data: [
            [Date.UTC(1970, 9, 21), 0],
            [Date.UTC(1970, 10, 4), 0.28],
            [Date.UTC(1970, 10, 9), 0.25],
            [Date.UTC(1970, 10, 27), 0.2],
            [Date.UTC(1970, 11, 2), 0.28],
            [Date.UTC(1970, 11, 26), 0.28],
            [Date.UTC(1970, 11, 29), 0.47],
            [Date.UTC(1971, 0, 11), 0.79],
            [Date.UTC(1971, 0, 26), 0.72],
            [Date.UTC(1971, 1, 3), 1.02],
            [Date.UTC(1971, 1, 11), 1.12],
            [Date.UTC(1971, 1, 25), 1.2],
            [Date.UTC(1971, 2, 11), 1.18],
            [Date.UTC(1971, 3, 11), 1.19],
            [Date.UTC(1971, 4, 1), 1.85],
            [Date.UTC(1971, 4, 5), 2.22],
            [Date.UTC(1971, 4, 19), 1.15],
            [Date.UTC(1971, 5, 3), 0]
        ]
    }, {
        name: 'Winter 2013-2014',
        data: [
            [Date.UTC(1970, 9, 29), 0],
            [Date.UTC(1970, 10, 9), 0.4],
            [Date.UTC(1970, 11, 1), 0.25],
            [Date.UTC(1971, 0, 1), 1.66],
            [Date.UTC(1971, 0, 10), 1.8],
            [Date.UTC(1971, 1, 19), 1.76],
            [Date.UTC(1971, 2, 25), 2.62],
            [Date.UTC(1971, 3, 19), 2.41],
            [Date.UTC(1971, 3, 30), 2.05],
            [Date.UTC(1971, 4, 14), 1.7],
            [Date.UTC(1971, 4, 24), 1.1],
            [Date.UTC(1971, 5, 10), 0]
        ]
    }, {
        name: 'Winter 2014-2015',
        data: [
            [Date.UTC(1970, 10, 25), 0],
            [Date.UTC(1970, 11, 6), 0.25],
            [Date.UTC(1970, 11, 20), 1.41],
            [Date.UTC(1970, 11, 25), 1.64],
            [Date.UTC(1971, 0, 4), 1.6],
            [Date.UTC(1971, 0, 17), 2.55],
            [Date.UTC(1971, 0, 24), 2.62],
            [Date.UTC(1971, 1, 4), 2.5],
            [Date.UTC(1971, 1, 14), 2.42],
            [Date.UTC(1971, 2, 6), 2.74],
            [Date.UTC(1971, 2, 14), 2.62],
            [Date.UTC(1971, 2, 24), 2.6],
            [Date.UTC(1971, 3, 2), 2.81],
            [Date.UTC(1971, 3, 12), 2.63],
            [Date.UTC(1971, 3, 28), 2.77],
            [Date.UTC(1971, 4, 5), 2.68],
            [Date.UTC(1971, 4, 10), 2.56],
            [Date.UTC(1971, 4, 15), 2.39],
            [Date.UTC(1971, 4, 20), 2.3],
            [Date.UTC(1971, 5, 5), 2],
            [Date.UTC(1971, 5, 10), 1.85],
            [Date.UTC(1971, 5, 15), 1.49],
            [Date.UTC(1971, 5, 23), 1.08]
        ]
    }];
    
function doit(theContainer,title,subtitle)
{
this.graph=function(xx)
{
Highcharts.chart(theContainer, {
    chart: {
        zoomType: 'x',
        type: 'spline'
    },
    title: {
        text: title
    },
    subtitle: {
        text: subtitle
    },
    credits: { enabled: false },
    xAxis: {
        type: 'datetime',
        dateTimeLabelFormats: { // don't display the dummy year
            month: '%e. %b',
            year: '%b'
        },
        title: {
            text: 'Date'
        }
    },
    
    exporting: {
            buttons: {
                contextButton: {
                    enabled: false
                } } },

    yAxis: {
        title: {
            text: 'Snow depth (m)'
        },
        min: 0
    },
    tooltip: {
       headerFormat: '<b>{series.name}</b><br>',
       pointFormat: '{point.x:%e. %b}: {point.y:.2f} m'
    },

    plotOptions: {
        spline: {
            marker: {
                enabled: false
            }
        }
    },

    series: xx
});

}

}
mine1=new doit("container1","title1","Subtitle1");
mine1.graph(sample_data1);
</script>


and here is the code inside one of those orange functions – the only thing that changes is the data.

msg.payload=[{
        name: 'Win 2012-2013',
        // Define the data points. All series have a dummy year
        // of 1970/71 in order to be compared on the same x axis. Note
        // that in JavaScript, months start at 0 for January, 1 for February etc.
        data: [
            [Date.UTC(1970, 9, 21), 0],
            [Date.UTC(1970, 10, 4), 0.28],
            [Date.UTC(1970, 10, 9), 0.25],
            [Date.UTC(1970, 10, 27), 0.2],
            [Date.UTC(1970, 11, 2), 0.28],
            [Date.UTC(1970, 11, 26), 0.28],
            [Date.UTC(1970, 11, 29), 0.47],
            [Date.UTC(1971, 0, 11), 0.79],
            [Date.UTC(1971, 0, 26), 0.72],
            [Date.UTC(1971, 1, 3), 1.02],
            [Date.UTC(1971, 1, 11), 1.12],
            [Date.UTC(1971, 1, 25), 1.2],
            [Date.UTC(1971, 2, 11), 1.18],
            [Date.UTC(1971, 3, 11), 1.19],
            [Date.UTC(1971, 4, 1), 1.85],
            [Date.UTC(1971, 4, 5), 2.22],
            [Date.UTC(1971, 4, 19), 1.15],
            [Date.UTC(1971, 5, 3), 0]
        ]
    }, {
        name: 'Win 2013-2014',
        data: [
            [Date.UTC(1970, 9, 29), 0],
            [Date.UTC(1970, 10, 9), 0.4],
            [Date.UTC(1970, 11, 1), 0.25],
            [Date.UTC(1971, 0, 1), 1.66],
            [Date.UTC(1971, 0, 10), 1.8],
            [Date.UTC(1971, 1, 19), 1.76],
            [Date.UTC(1971, 2, 25), 2.62],
            [Date.UTC(1971, 3, 19), 2.41],
            [Date.UTC(1971, 3, 30), 2.05],
            [Date.UTC(1971, 4, 14), 1.7],
            [Date.UTC(1971, 4, 24), 1.1],
            [Date.UTC(1971, 5, 10), 0]
        ]
    }, {
        name: 'Win 2014-2015',
        data: [
            [Date.UTC(1970, 10, 25), 0],
            [Date.UTC(1970, 11, 6), 0.25],
            [Date.UTC(1970, 11, 20), 1.41],
            [Date.UTC(1970, 11, 25), 1.64],
            [Date.UTC(1971, 0, 4), 1.6],
            [Date.UTC(1971, 0, 17), 2.55],
            [Date.UTC(1971, 0, 24), 2.62],
            [Date.UTC(1971, 1, 4), 2.5],
            [Date.UTC(1971, 1, 14), 2.42],
            [Date.UTC(1971, 2, 6), 2.74],
            [Date.UTC(1971, 2, 14), 2.62],
            [Date.UTC(1971, 2, 24), 2.6],
            [Date.UTC(1971, 3, 2), 2.81],
            [Date.UTC(1971, 3, 12), 2.63],
            [Date.UTC(1971, 3, 28), 2.77],
            [Date.UTC(1971, 4, 5), 2.68],
            [Date.UTC(1971, 4, 10), 2.56],
            [Date.UTC(1971, 4, 15), 2.39],
            [Date.UTC(1971, 4, 20), 2.3],
            [Date.UTC(1971, 5, 5), 2],
            [Date.UTC(1971, 5, 10), 1.85],
            [Date.UTC(1971, 5, 15), 1.49],
            [Date.UTC(1971, 5, 23), 1.08]
        ]
    }];
    
return msg;

With more work, this could be really useful – and don’t forget to check the BIG range of graphs and other widgets in the HiCharts demos – there is some wonderful stuff in there.

Back to databases - as you can see it would not be hard at all to pull data like this out of just about any database! That's my next job after I tidy this up a bit.

Facebooktwittergoogle_pluspinterestlinkedin

Garden Data Collection

MiFloraThis entry is all about InfluxDB, Grafana and a cute little Bluetooth garden sensor!

Some time ago a pal of mine and I did some swaps of spare kit and I ended up with a Mi Flora sensor.  A rather pleasing looking device,  the MI Flora has a 3v lithium button cell and can transmit by Bluetooth LE, the light level, temperature, battery level and moisture.

Battery is supposed to last for months. I’ve provided an AliExpress link above which puts the units at under £8 Inc. postage. No doubt that price will vary dramatically depending on where you chose to spend your money. Amazon charge more like £13.

So having pulled off the protective tab which meant the battery would work, I put the unit to one side as I had no idea how to read it.

A week or so later in Spain, with poor weather my pal Jay came on to ask me if I’d used it. I had not – and so we decided to give it a go. As it turned out I had some cheap Bluetooth 4 dongles I’d bought on a whim last year. I plugged one into my Raspberry Pi 2. The program “hcitool” lets you scan for Bluetooth devices and as it happens it was already installed on the Pi. From the command prompt:

sudo hcitool lescan

Instant success as the Mi Flora device with it’s MAC address was revealed. I made a note of the MAC address.

I would need Python 3 to experiment – but this was already installed on the Pi and some code to get the information from the unit. This code turned out to be ok with a little modification. I ended up creating a folder called /home/pi/myflora and dumping the lot in there.

Jay had modified the program to send MQTT. That of course needed the PAHO client. In order to do that I needed something called PIP.

sudo apt-get install python3-pip

sudo pip3 install paho-mqtt

Note NOT pip (which works for Python 2.7) but pip3.

I then added to the MI flora directory a test program which Jay sent me called bathroom.py – presumably because he’d put the sensor in his bathroom – I adjusted the output slightly as I was putting my unit in the garden

import sys
import paho.mqtt.client as mqtt

from miflora.miflora_poller import MiFloraPoller, \
MI_CONDUCTIVITY, MI_MOISTURE, MI_LIGHT, MI_TEMPERATURE, MI_BATTERY

poller = MiFloraPoller("C4:7C:8D:32:08:63")
print("Getting data from Mi Flora")
print("FW: {}".format(poller.firmware_version()))
print("Name: {}".format(poller.name()))
print("Temperature: {}".format(poller.parameter_value(MI_TEMPERATURE)))
print("Moisture: {}".format(poller.parameter_value(MI_MOISTURE)))
print("Light: {}".format(poller.parameter_value(MI_LIGHT)))
print("Conductivity: {}".format(poller.parameter_value(MI_CONDUCTIVITY)))
print("Battery: {}".format(poller.parameter_value(MI_BATTERY)))

# Publishing the results to MQTT
mqttc = mqtt.Client("miflorabathroom")
mqttc.username_pw_set("admin","xxxx")
mqttc.connect("192.168.1.19", 1883)
mqttc.publish("miflora/garden", "{ \"battery\" : " + str(poller.parameter_value(MI_BATTERY)) + ", \"version\" : \"" + str(poller.firmware_version()) + "\", \"sunlight\" : " + str(poller.parameter_value(MI_LIGHT)) + ", \"temperature\" : " + str(poller.parameter_value(MI_TEMPERATURE)) + ", \"moisture\" : " + str(poller.parameter_value(MI_MOISTURE)) + ", \"fertility\" : " + str(poller.parameter_value(MI_CONDUCTIVITY)) + " }")
mqttc.loop(2)
# End of MQTT section

So the idea was that running “python3 garden.py” would send some MQTT to my Mosquitto MQTT broker – and indeed it did (change MAC address for the MI Flora and MQTT details in the above).  Subscribing to “miflora/garden” did the trick – I could fire off the program and with in seconds I’d see the resulting JSON package coming out into my MQTT-Spy client.

Next job was to get Node-Red talking to the MI Flora – I used an INJECT node, set to retrigger every 15 minutes – firing off to an EXEC node which contained nothing more than “python3 /home/pi/miflora/bathroom.py”

That worked – so now I had a regular supply of data in the form of an easy to use JSON package.

I split the package up – and fed it into a Node-Red Dashboard graph – and that was my first disappointment. If you fire more than 4 items into those graphs – the popup fails to show all of them – and the graphs are not very good anyway. I think a lot more work needs to go into that.

I do use GROVESTREAMS on-line (which is REALLY flexible) to store data remotely but there’s a limit as to how far back you can read for free – that was not the case when I first started using them and wrote the Node-Red-Contrib-Grove node to make it all easy.

So now I was on a search for a decent graphics package that would run on the Raspberry Pi (or similar). NOT as easy as it sound,  unless you’re very easily pleased when it comes to graphics.

After a lot of experimenting I decided to give Grafana a shot. I think if I started writing this blog again, I would would have Grafana in the title because this really, as it turns out, is the star of the show.

Having seen a few screenshots, I had visions of populating  a SQLITE 3 database and firing that data into Grafana.

Erm, no. Despite (wait for it) using SQLITE internally to store users and dashboards, the package does not actually support SQLITE for data !!!  I've explained that now, to save you some confusion later. When I did the install, it kept referring to SQLITE and could I HELL figure out how to set up a database for my data – that’s why.

As a Pi user (that means SD use so I’m not keen on MySQL as SD has limited WRITE capability and I worry about databases shortening the life of the SD - see the later article on SD life - very informative) the options for data sources did not look appealing to me at all, the least offensive being INFLUX and that is the one I settled on – partly as there is a node for it available for Node-Red.  Despite the pretty website this is still very preliminary at version 1.02. Influx doesn’t use terms like tables and fields which kept me going for a couple of hours – but it is good at storing time-stamped data – indeed you don’t even do the time-stamping. You create an empty database and start firing data at the DB in pairs – stream name – and data value – it really is very simple once you get started. I guess you could think of it as a single table with pairs of data-name and data-value.  I went down a few dead ends and discovered if you got your stream names wrong – good luck renaming them as INFLUX does not support renaming!

Anyway, it ‘s all very easy once you get started. I did things backwards and installed Grafana first. Here’s what I did – no guarantee it will work for you.

You might want to consider reading this before using InfluxDB on the Pi for long-term use - there are very few build options for small SBCs and this article gives the impression it likes more RAM than we would normally have on the likes of the Pi. I do have to say though that it is working just fine on mine after several days of operation. Comments welcome.

I grabbed the Grafana file for my Pi from here. https://github.com/fg2it/grafana-on-raspberry

sudo dpkg -i grafana_4.1.2-1487023783_armhf.deb
sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable grafana-server

That was Grafana up and running on port 3000 – user name admin, password admin to actually DO anything – and quickly change that password!!! I did that, went into the ini file and disabled user registration as the admin can do that and opened up port 3000 redirect in my router so I could access this from the outside world. That all worked well.

wget http://ftp.us.debian.org/debian/pool/main/i/influxdb/influxdb_1.0.2+dfsg1-1_armhf.deb

sudo dpkg -i influxdb_1.0.2+dfsg1-1_armhf.deb

That set up influx and that ran without further ado at port 8083. I did NOT open that up to the outside world. In there I created a new empty database called “logger”

logger

In Grafana – which supports influx without any changes, I followed Engineer John’s instructions to set up the data-source and a new Dashboard.

So the next thing – would be to get some data.  I played with this for some time – should I keep incoming data source names simple – as you can’t rename them? Then Jay pointed out that in Grafana you can ALIAS names. That did it. So in influxDB node for Node-Red, you fire in a payload – and depending where you want to put your data – you use msg.measurement.. Yes, exactly, what’s wrong with msg.topic you might ask. So here is my little MI Flora MQTT node firing values into InfluxDB

MiFlora[6]

And that yellow function block…

node.status({fill:"blue",shape:"dot",text: msg.payload});

tmp=JSON.parse(msg.payload);

msg.measurement="battery"; msg.payload=tmp.battery; node.send(msg);
msg.measurement="sunlight"; msg.payload=tmp.sunlight; node.send(msg);
msg.measurement="temperature"; msg.payload=tmp.temperature; node.send(msg);
msg.measurement="moisture"; msg.payload=tmp.moisture; node.send(msg);
msg.measurement="fertility"; msg.payload=tmp.fertility; node.send(msg);

Yes, I know – far better ways to do this – but it was my first stab at it and it worked. For general use however I added an incoming function that merely copies msg.topic into msg.measurement and that means any single data-source coming in via MQTT can be blasted straight into InFluxDB.

Yes, that’s write – you don’t create tables or fields – you just send in field names and data and that’s it!

I also fed in some data from my general sensors – with horrible topics like pergola/lighting but of course thanks to the Grafana alias ability – names like that would not show up in Dashboards.

Having stored up some data it was time to get it into graphs – I took the VERY simplest approach of showing lines as I have at this point no idea what some of the Grafana functions do – I DO know that smoothing the data might be fun as Grafana relies on the data-source to do that and Influx doesn’t do that.  What IS nice is that you don’t have to worry about things like “I want the last 5 minute’s worth” or “show me the last month’s worth” as Grafana does all of that for you – from here  it gets easy.

New dashboard and under metrics ADD ROW – takes an hour or so to get to grips with this..

Grafana setup

I’ve expanded the top one here – I have 3 lines in that one graph – see the blank ALIAS BY  - if you want to change the source name (in this case battery) put something in there.

And that  is how I got to this point…

Graphing

So top left you see battery, moisture and temperature from the MI Flora unit. I chose to separate off light level as the range of values is WAY higher… and on the right I’ve started with another unrelated sensor incoming from MQTT and as you see using the ALIAS I’ve renamed the stream (topic) to “Kitchen OS Temperature” – yes, it really IS that cold inland Spain right now.

But this is merely the beginning – now I will put all my other data from the UK and Spain into here – lots of it and some day I might even manage to extract individual graphs back into Node-Red Dashboard, but right now, I’m happy with these pages. Looking good.

There’s a lot to take in – and I am as you can see NOT an expert at this yet so before firing questions back – you might want to spend some time looking at the links on this blog entry and using Google. If you think another database is better by all means write in to say why – and if you think there’s a better easy-to-fit-in-Pi graphing system, preferable one that works with SQLITE – again do let us know. I started all of this with zero information late afternoon yesterday and had it all working by the end of last night – so it’s not THAT big a deal even if my clumsy writing makes it look that way.

Not entirely sure what’s going on the this battery level on my first day – but it did occur to me that the unit is intended for putting into plant-pots – not sitting outside in the sopping rain so I’ve covered it in cling-film!! As we’re off back to the UK for a few weeks,  I don’t want to come back to a rusted heap.  Note the Mi Flora battery at the top – seems to be self-recovering!!

I have to say, having taken the plunge I DO like Grafana. It isn’t perfect… I tried the GAUGES plug-in which was great using one gauge but as soon as I tried more than one, all SORTS of things went wrong – eventually I stopped using it.. but the graphs are fine. I’m already seeing benefits of monitoring – the top right graph shows the effect of watering a blocked-in area of the patio in Spain – the watering has been good for nearly 3 days up to now and by the look of it will be good for a week. As we pay per litre, such info is useful. Of course, things will be different in the summer but the automatic system currently offline (ice) waters twice a day – this may WELL prove to be totally over the top.

Update: the little Bluetooth sensor has now been running for several weeks and the battery remains at 80%

Grafana output

Update 27/03/2017: I've been hitting issues with out of bounds temperature data - ie a couple of duff readings crept in - with values like 2000 and -64  which of course flattened the data completely. I realised quickly that InfluxDB has no DELETE function and as far as I could tell - things like comparing data with a value made the graph line disappear.

I put something up on the web and someone came back and asked if I was using InfluxDB 1.2 which apparently has a DELETE function. Well, I wasn't - I was way back at 1.01 - worse - the same folder where that came from only went up to 1.1

However, thanks to THIS helpful chap - I easily updated to Influx 1.2 without the issue he described. I STILL can't add something like "where value < 100" but maybe I can delete ??? Anyone with more info on this please by all means chip in...

Update 18/04/2017: I've just fitted the second of these units and also fixed a minor error in the code above.  All that is needed for a second unit is a copy of the program with different MAC address and different MQTT publish address. Sure you could use the same one twice with command line parameters but I took the easy option.

Bluetooth RANGE is becoming an issue. The Pi is in my office - breezeblock wall then the garden - the second unit I tried maybe 15ft or so from the office wall - not a chance.  Clearly BT4 is good for power, not so good for range.

So - there's a nice job for an ESP32... BT in - WIFI out.  A relay!

 

 

 


If you like this post – please share a link to it by social media, by email with friends or on your website.
More readers means more feedback means more answers for all of us. Thank you!

Facebooktwittergoogle_pluspinterestlinkedin

NR Templates and the Inject Node

When testing Node-Red, the common INJECT node is invaluable. Indeed it is just about the only way to instantly inject information into another node. Here is a typical example.

Inject and Debug Nodes

It doesn’t get any simpler than this. Here I have merely dropped an “Inject” node onto the Node-Red working surface along with a “Debug” node. I’ve made no changes to either of them.  Note that “inject” and “debug” nodes come as standard with Node-Red.

Node-Red setup

Correctly set up, on the right of the Node-Red screen, you can see (or make available via the top-right three horizontal bars – view – “debug messages”) a debug window and simply by pressing that button-like item to the left of the inject node, you can put a timestamp into the debug window as such:

Debug window

Simple enough. Note that this says (in red) “msg.payload”.  In homage I guess to MQTT, the inject node outputs a simple object which deals primarily with msg.topic and msg.payload. You can use these for whatever messages you want.  The debug node by default shows only msg.payload but you can of course change this to show the entire message object.

Full debug message

Note that here (image above) we also see a blank topic (because the unmodified inject node has that as empty – you could put “Merry Christmas” in there if you like. Also included is _msgid which is of no interest to us for now unless you have a specific need for a unit number.

In general then,  you can fire out msg.topic and msg.payload from the inject node.  Instead of putting out a timestamp,  you can put out a string (for example “Hello world”) and this is output in msg.payload.

Hello world

Output Hello World

Expanding the INJECT output

For many purposes, we are done – you know know how to output a value into msg.payload and you can easily add msg.topic to that.  An inject node then can directly output data suitable for, say an MQTT node which needs both msg.topic and msg.payload.

For some purposes however, this is not enough.

Messages

In the above image, I need to inject one of two messages into a template. Easy enough, “message 1” as string into one injector, “message 2” into another. But what if we have a string of messages for different purposes going into that template?

On way to differentiate them would be to use topic.

if (msg.topic==”needle1”)  { /*use msg.payload to influence  a gauge needle */ }
if (msg.topic==”lcd”) { /* use msg.payload to influence a display */ }

Another way to do this is by putting a function node after the inject node…

msg.needle=msg.payload;

The output of this then, is your message sitting inside msg.needle instead of msg.payload.

Do-able but two things about that. Firstly you still can only output one message at a time and now you have the extra work and additional screen real estate used up for the additional function node. Not very elegant. If that works for you – stop here.

So what if we want a simpler way that may include sending multiple messages out at once.

In the inject.node you can’t change the name of “msg.payload” – but you can change what it outputs – a number, a string etc.. and in this case of particular interest, JSON.

Let’s take an example. Let’s say that by pressing the test button we want to set a needle value to 10 AND we want to send the message “hello” to an LCD display in our gauge.

Sending an object

Sending several messages in a JSON string

This LOOKS like you are sending a string of text but look what comes out – when you view the debug payload:

image

Not a string, but an object  - but how do you make use of this? Simple. In your template – or function or whatever you are firing this message into:

if (msg.payload.needle!==undefined) { /*make use of msg.payload.needle */ }
if (msg.payload.lcd!==undefined) { /* make use of msg.payload.lcd  */ }

This way, directly from a single inject node you can send either one, or several messages at once. The check for undefined means you don’t always have to send all of them.

Hence, for example, in testing my gauge, I can have a number of buttons testing  different parts of the gauge and one of them sending messages to both lines of the internal LCD at once without making a big mess of the screen. In the example below, the bottom two injects each send 2 different commands to the gauge when pressed.

Testing the gauge

But what about existing objects

Another way to send multiple messages through the injector allows you to make use of existing global objects…

Injecting objects

In this example, I happen to have an existing object called “thermostat” – which can be referred to as “context.global.thermostat” though the more modern way to do that is by use of global.get and global.put.

Armed with the above, which contains in this case lots of information about the state of my house… I can send that – either manually or of course in a timed fashion, to the debug node.

thermostat object

A little confusing at first until you notice the three dots at the bottom… which mean “I’m only showing you some of the result. Click on them and..

expanded object

Far too much information to display here – but I think you get the point – though msg.payload looks restrictive – in fact you can send a whole load of information thanks to the ability of the INJECT node to carry an object in msg.payload.

In this case, if you set DEBUG to look for msg.payload.desired you will indeed see a result of 23.

Can I create an object? Sure:

Test object creation

In this case – the content of the injector is irrelevant. Just drag it onto the screen.  Here we have (for the sake of it) two debug nodes – one is looking for msg.payload.actual and the other is looking for msg.payload.desired.

And here are the contents of that simple function node in the middle.

msg.payload={
desired:25,
actual:23
}
return msg;

Debugging:  Keith Howell wrote in to remind me about node.warn and node.error – two very handy debugging functions. Here’s an example of use…

node.warn

Again out of sheer laziness I’ve used the inject node on the left straight out of the box as it were. On the right the orange “test” function – again – drag from the left… double-click to open and enter the following:

node.warn("just info, really!");
node.error("ooh errr!");

and the result in the debug area when you press the timestamp button…

Debugging

Isn’t that handy – the only difference here being the colour – yellow for warning (I’d have used blue but hey ho) and red for error.  Using this instead of a debug node could save a little real estate on the screen in some cases.

Facebooktwittergoogle_pluspinterestlinkedin

Thermometers

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:

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

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

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
}).draw();

</script>

<canvas id="mycanvas" 
    data-type="linear-gauge"
    data-width="106"
    data-height="270"
    data-units="°C"
    data-min-value="0"
    data-start-angle="90"
    data-ticks-angle="180"
    data-value-box="false"
    data-max-value="220"
    data-major-ticks="0,20,40,60,80,100,120,140,160,180,200,220"
    data-minor-ticks="2"
    data-stroke-ticks="true"
    data-color-plate="#fff"
    data-border-shadow-width="0"
    data-borders="false"
    data-needle-type="arrow"
    data-needle-width="2"
    data-needle-circle-size="7"
    data-needle-circle-outer="true"
    data-needle-circle-inner="false"
    data-animation-duration="1500"
    data-animation-rule="linear"
    data-bar-width="10"
    data-value="35"
></canvas>

The sample flow above

[{"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"}]
Facebooktwittergoogle_pluspinterestlinkedin

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.

Flow

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…

.blackOnOrange
.blackOnGreen
.blackOnBlue
.blackOnYellow
.blackOnWhite
.blackOnPink
.yellowOnRed
.whiteOnCyan
.orangeOnBlack
.limeOnBlack

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

[{"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"}]
Facebooktwittergoogle_pluspinterestlinkedin

LCD Display for Node-Red

Pete's LCDAs must be obvious by now, I’m on a roll here. Having decided that Node-Red Dashboard is the way forward ( would in the past have used BLYNK or Imperihome) I’ve been getting to grips with the dashboard and as you’ll see if you check recent blogs I’ve had a fair bit of success with various CANVAS based libraries, getting them into the Dashboard.

Pete's LCDAnd so it was that over the weekend, MrShark contacted me with this link – a very nice, simple stand-along clock display. Well, that fired me up and so I grabbed the code and started ripping it apart.  One thing led to another and here we have a nice LCD display programmable in a wide range of (I have to say stunning) colours and able to display temperature or weather icons on the right  – and in the process I learned a few things such as what position-absolute is all about and a great deal about transparency.

Pete's LCDIn the display here you see the use of border shadows, 7-segment display fonts and transparency to give the impression of an old-fashioned LCD display – I think it does a nice job.  The code here displays the time, date and day automatically and you can inject temperature into it – but a brief look and you’ll soon realise that you could put just about anything in there – so I look forward to feedback from readers.  To make this work, from the link above I had to grab 3 fonts (DSEG7Modern-Italic.woff,  DSEG14Modern-Italic.woff and DSEG7Modern-BoldItalic.woff – also required is  DSEGWeather.woff) from the link above (into myfonts – see previous blogs for the reason I made up directory names like myfonts and mycss etc).

Pete's LCDSo – here is the code to put into a Dashboard template – you just need to inject msg.payload to set the temperature OR weather icons depending on mode – see flow – most of this is just for you to experiment with.

tmp484EThere are several spans in here – all position absolute – which means they overlap and are all relative to the top left of the main DIV – the trick with the LCD is simply the 8 figure semi-transparent – with you number or letter sitting on top of it. Works a treat I should say.  You’ll want a 6*2 size for the template… but of course you may choose to resize this completely.

flow

tmpF88BAn important item include the z-index, I’ve used 50 and 51 arbitrarily – the original code this all came from used 100 and that ended up over-writing the Node-Red Dashboard menu! Of course if you don’t want to show temperature you could make that humidity or anything else really.

tmpA42CInjecting weather – see flow – includes: characters “0” to “9” for; background all segments, sun, cloud, rain, hard rain, snow, thunder rain, thunder hard rain, thunder, sun and cloud and finally “:” for all off.

Below is the code for the flow - make sure you have those fonts and inject this lot into a page in the dashboard - you'll need to change the tab details to suit yourself.

Oh, this page I found while looking for colour ideas…

[{"id":"5439cd84.a88ae4","type":"ui_template","z":"c552e8d2.712b48","group":"1e03a2b2.83a61d","name":"Time and Temp","order":0,"width":"6","height":"2","format":"<script>\n    var icon=\"T\";\n\n        \n    var colours={\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    var daylist = [\"sun\", \"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\"];\n    (function(scope){ \n            scope.$watch('msg', function(msg) {\n               if (typeof(msg.type) != \"undefined\") icon=msg.type;\n    \n               if (icon==\"t\")\n                    {\n                        $(\"#DSEGWEATHER-BACK\").text(\" \");\n                        $(\"#DSEGWEATHER-ICON\").text(\" \");\n                        $(\"#DSEGTempcF\").text(\"C\");\n                        $(\"#DSEGTempcB\").text(\"8\");                        \n                        if (typeof(msg.payload) != \"undefined\") { $(\"#DSEGTempF\").text(msg.payload);  $(\"#DSEGTempB\").text(\"88\"); }\n                    }\n                else\n                    {\n                        $(\"#DSEGTempF\").text(\"\");\n                        $(\"#DSEGTempcF\").text(\"\");\n                        $(\"#DSEGTempB\").text(\"\");\n                        $(\"#DSEGTempcB\").text(\"\");\n                        $(\"#DSEGWEATHER-BACK\").text(\"0\");\n                        if (typeof(msg.payload) != \"undefined\") $(\"#DSEGWEATHER-ICON\").text(msg.payload);\n                        \n                    }\n               if (typeof(msg.colour) != \"undefined\") {\n                              $(\".Clock-Wrapper\").css('background-color', colours[msg.colour].items[1]);  $(\".lcdClock\").css('color', colours[msg.colour].items[0]); \n                              if (colours[msg.colour].items[1]==\"#000000\") $(\".background\").css('color',\"rgba(255,255,255,0.15)\"); else  $(\".background\").css('color',\"rgba(0,0,0,0.1)\");\n                        }\n            });\n    })(scope);\n\n    function genTimerStrings(tm, num){\n    \n    \tvar i;\n    \tvar ret = tm.toString(10);\n    \tvar left = ret.length;\n    \n    \tif( left < num){\n    \t\tfor(i=0; i<( num - left ); i++ ){\n    \t\t\tret = String(0) + ret;\n    \t\t}\n    \t}\n    \treturn ret;\n    }\n\n    function updateTimer(){\n    \tvar ret;\n    \tvar date = new Date();\n    \tvar tm_year, tm_mon, tm_date, tm_hour, tm_min, tm_sec, tm_msec,tm_day;\n    \tvar colon;\n    \ttm_year = date.getFullYear();\n    \ttm_mon = date.getMonth()+1;\n    \ttm_date = date.getDate();\n    \ttm_day = date.getDay();\n    \ttm_hour = date.getHours();\n    \ttm_min = date.getMinutes();\n    \ttm_sec = date.getSeconds();\n    \ttm_msec = date.getMilliseconds();\n    \n    \ttm_mon = genTimerStrings(tm_mon, 2);\n    \ttm_date = genTimerStrings(tm_date, 2);\n    \ttm_hour = genTimerStrings(tm_hour, 2);\n    \ttm_min = genTimerStrings(tm_min, 2);\n    \ttm_sec = genTimerStrings(tm_sec, 2);\n    \ttm_day = daylist[tm_day];\n    \n    \tif( tm_msec > 499 ){\n    \t\tcolon = ' ';\n    \t}else{\n    \t\tcolon = ':';\n    \t}\n    \n    \tdocument.getElementById(\"DSEGClock\").innerHTML = tm_hour + colon + tm_min + \"<span style=\\\"font-size:30px;\\\">\"  + tm_sec + \"</span>\";\n    \tdocument.getElementById(\"DSEGClock-Year\").innerHTML = \"<span class=\\\"D7MI\\\">\" + tm_year + \"-\" + tm_mon + \"-\" + tm_date + ' ' + \"</span><span class=\\\"D14MI\\\">\" + tm_day  +  \".\" + \"</span>\";\n    \n    \tsetTimeout(\"updateTimer()\", 500 - date.getMilliseconds()%500 );\n    }\n\n    updateTimer();\n    \n</script>\n\n<style type=\"text/css\">\n.lcdClock {\n\tbackground-color:#fbfbfb;\n\tfont-size:100%;\n\tpadding-left:10px;\n\tpadding-right:10px;\n\tpadding-bottom:10px;\n\tmax-width:300px;\n\tline-height:160%;\n\tcolor:#222;\n\tfont-family:Meiryo, 'Lucida Grande','Hiragino Kaku Gothic ProN', sans-serif;\n}\n\n@font-face {\n  font-family: \"D7MI\";\n  src: url(\"/myfonts/DSEG7Modern-Italic.woff\") format('woff');\n}\n\n@font-face {\n  font-family: \"D14MI\";\n  src: url(\"/myfonts/DSEG14Modern-Italic.woff\") format('woff');\n}\n\n@font-face {\n  font-family: \"D7MBI\";\n  src: url(\"/myfonts/DSEG7Modern-BoldItalic.woff\") format('woff');\n}\n\n@font-face {\n  font-family: \"DWEATHER\";\n  src: url(\"/myfonts/DSEGWeather.woff\") format('woff');\n}\n\n.Weather-Background{\n\tz-index:50;\n\tposition:absolute;\n\ttop:24px;\n\tleft:226px;\n\tfont-size:68px;\n}\n\n.Weather-Front{\n\tz-index:51;\n\tposition:absolute;\n\ttop:24px;\n\tleft:226px;\n\tfont-size:68px;\n}\n\n.D7MI {\nfont-family: \"D7MI\";\n}\n\n.D7MBI {\nfont-family: \"D7MBI\";\n}\n\n.D14MI {\nfont-family: \"D14MI\";\n}\n\n.DWEATHER {\nfont-family: \"DWEATHER\";\nfont-size:68px;\nheight:68px;\n}\n\n.Clock-Wrapper{\n\tposition:relative;\n\tborder:6px solid #000;\n\tborder-radius:9px;\n\theight:68px;\n\twidth:280px;\n\tbackground-color:#fb7c00;\n\tbackground-color:#66ac66;\n\tbox-shadow: 4px 4px 28px 0px rgba(0,0,0,0.3) inset; \n}\n\n.Clock-Time-Background{\n\tz-index:50;\n}\n\n.Clock-Time-Front{\n\tz-index:51;\n}\n\n.Clock-Time-Background,.Clock-Time-Front {\n   \tposition:absolute;\n\ttop:38px;\n\tleft:5px; \n\tfont-size:42px;\n}\n\n.background { color:rgba(0,0,0,0.1); }\n\n.Clock-Year-Background{\n\tz-index:50;\n\tfont-size:18px;\n}\n\n.Clock-Year-Front{\n\tz-index:51;\n}\n\n.Clock-Year-Background,.Clock-Year-Front {\n   \tposition:absolute;\n\ttop:2px;\n\tleft:5px; \n\tfont-size:18px;\n}\n\n.temp { z-index:51; }\n.tempBack { z-index:50; color:rgba(0,0,0,0.1); }\n.temp,.tempBack {\n   \tposition:absolute;\n\ttop:28px;\n\tleft:210px; \n\tfont-size:42px;\n}\n\n.tempc { z-index:51; }\n.tempcBack { z-index:50;  }\n.tempc,.tempcBack {\n   \tposition:absolute;\n\ttop:36px;\n\tleft:278px; \n\tfont-size:24px;\n}\n\n#DSEG7_OUTPUT{\n\tfont-family: \"D7MI\";\n}\n\n#DSEG14_OUTPUT{\n\tfont-family: \"D14MI\";\n}\n\n#DSEG14_OUTPUT, #DSEG7_OUTPUT{\n\tfont-size:18px;\n\tmargin-top:2px;\n\tmargin-bottom:10px;\n}\n\n</style>\n\n\n<div class=\"Clock-Wrapper center lcdClock\">\n\t<span class=\"Clock-Time-Background background  D7MBI\">88:88<span style=\"font-size:30px;\">88</span></span>\n\t<span id=\"DSEGClock\" class=\"Clock-Time-Front D7MBI\"></span>\n\t<span class=\"Clock-Year-Background background\"><span class=\"D7MI\">2088-88-88</span><span class=\"D14MI\"> ~~~</span></span>\n\t<span id=\"DSEGClock-Year\" class=\"Clock-Year-Front\"></span>\n\t\n\t<span id=\"DSEGTempF\" class=\"temp D7MBI\">00</span>\n\t<span id=\"DSEGTempB\" class=\"tempBack background D7MBI\">88</span>\t\n\t<span id=\"DSEGTempcF\" class=\"tempc D7MI\">C</span>\n\t<span id=\"DSEGTempcB\" class=\"tempcBack background D7MI\">8</span>\n\t\n\t\n\t<span id=\"DSEGWEATHER-BACK\" class=\"Weather-Background background DWEATHER\"></span>\n\t<span id=\"DSEGWEATHER-ICON\" class=\"Weather-Front DWEATHER\"></span>\n\t\t\n</div>\n\n","storeOutMessages":true,"fwdInMessages":false,"x":480,"y":1480,"wires":[[]]},{"id":"e26a920b.fd916","type":"inject","z":"c552e8d2.712b48","name":"21c","topic":"","payload":"21","payloadType":"num","repeat":"","crontab":"","once":false,"x":90,"y":1220,"wires":[["5439cd84.a88ae4"]]},{"id":"a92b9b7a.795228","type":"inject","z":"c552e8d2.712b48","name":"32c","topic":"","payload":"32","payloadType":"num","repeat":"","crontab":"","once":false,"x":90,"y":1260,"wires":[["5439cd84.a88ae4"]]},{"id":"6d378317.dd743c","type":"inject","z":"c552e8d2.712b48","name":"green","topic":"","payload":"blackOnGreen","payloadType":"str","repeat":"","crontab":"","once":false,"x":90,"y":1400,"wires":[["a23865de.5f59b8"]]},{"id":"24a314bf.90927c","type":"inject","z":"c552e8d2.712b48","name":"orange","topic":"","payload":"blackOnOrange","payloadType":"str","repeat":"","crontab":"","once":false,"x":90,"y":1440,"wires":[["a23865de.5f59b8"]]},{"id":"a23865de.5f59b8","type":"function","z":"c552e8d2.712b48","name":"","func":"msg.colour=msg.payload;\nmsg.payload=undefined;\nreturn msg;","outputs":1,"noerr":0,"x":270,"y":1480,"wires":[["5439cd84.a88ae4"]]},{"id":"c8edfc9b.be6ce","type":"inject","z":"c552e8d2.712b48","name":"blue","topic":"","payload":"blackOnBlue","payloadType":"str","repeat":"","crontab":"","once":false,"x":90,"y":1480,"wires":[["a23865de.5f59b8"]]},{"id":"18a1c3a.b9c283c","type":"inject","z":"c552e8d2.712b48","name":"yellow","topic":"","payload":"blackOnYellow","payloadType":"str","repeat":"","crontab":"","once":false,"x":90,"y":1520,"wires":[["a23865de.5f59b8"]]},{"id":"a5c4dd24.4b6ff","type":"inject","z":"c552e8d2.712b48","name":"white","topic":"","payload":"blackOnWhite","payloadType":"str","repeat":"","crontab":"","once":false,"x":90,"y":1560,"wires":[["a23865de.5f59b8"]]},{"id":"83e6ddc0.ec7bd","type":"inject","z":"c552e8d2.712b48","name":"pink","topic":"","payload":"blackOnPink","payloadType":"str","repeat":"","crontab":"","once":false,"x":90,"y":1600,"wires":[["a23865de.5f59b8"]]},{"id":"ebfd33d8.c8b13","type":"inject","z":"c552e8d2.712b48","name":"red and light text","topic":"","payload":"yellowOnRed","payloadType":"str","repeat":"","crontab":"","once":false,"x":120,"y":1640,"wires":[["a23865de.5f59b8"]]},{"id":"26972f6.8af94d","type":"inject","z":"c552e8d2.712b48","name":"cyan and white","topic":"","payload":"whiteOnCyan","payloadType":"str","repeat":"","crontab":"","once":false,"x":120,"y":1680,"wires":[["a23865de.5f59b8"]]},{"id":"10449ff5.d9588","type":"inject","z":"c552e8d2.712b48","name":"Type t for TEXT","topic":"","payload":"t","payloadType":"str","repeat":"","crontab":"","once":false,"x":120,"y":1760,"wires":[["fe927255.d2e2a"]]},{"id":"3e44785a.6181b8","type":"inject","z":"c552e8d2.712b48","name":"Type w for weather","topic":"","payload":"w","payloadType":"str","repeat":"","crontab":"","once":false,"x":130,"y":1800,"wires":[["fe927255.d2e2a"]]},{"id":"fe927255.d2e2a","type":"function","z":"c552e8d2.712b48","name":"","func":"msg.type=msg.payload;\nmsg.payload=undefined;\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":1740,"wires":[["5439cd84.a88ae4"]]},{"id":"94706c5b.a9292","type":"inject","z":"c552e8d2.712b48","name":"weather 2","topic":"","payload":"2","payloadType":"str","repeat":"","crontab":"","once":false,"x":100,"y":1299,"wires":[["5439cd84.a88ae4"]]},{"id":"77b95bb8.1ce774","type":"inject","z":"c552e8d2.712b48","name":"weather 3","topic":"","payload":"3","payloadType":"str","repeat":"","crontab":"","once":false,"x":100,"y":1339,"wires":[["5439cd84.a88ae4"]]},{"id":"30c75186.ef732e","type":"inject","z":"c552e8d2.712b48","name":"lime on black","topic":"","payload":"limeOnBlack","payloadType":"str","repeat":"","crontab":"","once":false,"x":110,"y":1720,"wires":[["a23865de.5f59b8"]]},{"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"}]
Facebooktwittergoogle_pluspinterestlinkedin