How to make the result of switch function only output once

4,405 views
Skip to first unread message

Ian Eagland

unread,
May 30, 2015, 2:57:15 PM5/30/15
to node...@googlegroups.com
Hi

I am monitoring a battery voltage via mqtt in. When the voltage drops below 26v using a switch function I am outputting a debug message.
I want to replace that message with a twilio sms which is straight forward. However I only want to send a single message and then suspend output until the voltage goes above 26v. What is the best strategy for achieving this bearing in mind this is all completely new to me.

Regards

Ian

Julian Knight

unread,
May 30, 2015, 4:20:48 PM5/30/15
to node...@googlegroups.com
This is not quite as simple as perhaps it could be in NR - a source of much heated discussion in this forum! :)

However, the currently best way is to make sure you not only send your message when the voltage goes below 26v but that you also set a flag somewhere. This could be in memory but that wouldn't survive a reboot. Almost certainly you need to set an MQTT "flag"  that the message has been sent. You will also need that information in memory so you will need an MQTT listener for the flag that sets some variable on context.global (you currently need a function node for that).

Now you can extend your current flow with a function node that checks the context.global variable and, if it is true, don't send the msg onwards from the function (simply return null).

When the voltage goes back above the cutoff, make sure you update the flag to reset everything.

Nicholas O'Leary

unread,
May 30, 2015, 4:56:54 PM5/30/15
to Node-RED Mailing LIst
Ian,

was half-way through writing this response, then saw Julian's reply... so there's some repetition here, but also some additional thoughts. There are lots of ways to do this, so don't be surprised if you get a number of suggestions :)

Building a flow that maintains the state over restarts of node-red adds a fair amount of complication if you're new to this stuff. If that isn't important to you (at this stage), here are a couple ways of doing it.


Function node
the function node can store data in a local 'context' that is maintained between each invocation of the node. This lets you store some state to help your flow decide what to do.

Here's a function that will only pass through a message the each time it drops below 26 (and not repeat).:

  // If voltage drops below level and we 
  // haven't sent an alert...
  if (msg.payload < 26 && !context.sentAlert) {
      // Set the content of the alert
      msg.payload = "Oh no!";
      // Set the flag so we know not to alert again
      context.sentAlert = true;
      // Return the message so the alert is sent
      return msg;
  } else if (msg.payload > 26) {
      // Voltage back to normal, reset alert flag
      context.sentAlert = false;
  }
  // Return null so no message is sent
  return null;


Trigger node
the trigger node (which I only got to grips with properly this week... yeah, I know) can also be used. Here's an example flow you can import:

[{"id":"aceb3f69.5314c","type":"inject","name":"","topic":"","payload":"24","payloadType":"string","repeat":"","crontab":"","once":false,"x":190,"y":396,"z":"396c2376.c693dc","wires":[["e63f7184.19c09"]]},{"id":"f7a927e7.0856d8","type":"inject","name":"","topic":"","payload":"27","payloadType":"string","repeat":"","crontab":"","once":false,"x":194,"y":448,"z":"396c2376.c693dc","wires":[["e63f7184.19c09"]]},{"id":"e63f7184.19c09","type":"switch","name":"","property":"payload","rules":[{"t":"lt","v":"26"},{"t":"else"}],"checkall":"true","outputs":2,"x":355,"y":412,"z":"396c2376.c693dc","wires":[["289ed11e.d7612e"],["7c1c26d9.83e3d8"]]},{"id":"fbece5a6.041318","type":"debug","name":"","active":true,"console":"false","complete":"false","x":838,"y":397,"z":"396c2376.c693dc","wires":[]},{"id":"289ed11e.d7612e","type":"trigger","op1":"1","op2":"0","op1type":"pay","op2type":"val","duration":"0","extend":"false","units":"ms","name":"","x":611,"y":405,"z":"396c2376.c693dc","wires":[["fbece5a6.041318"]]},{"id":"7c1c26d9.83e3d8","type":"change","name":"","rules":[{"t":"set","p":"reset","to":"1"}],"action":"","property":"","from":"","to":"","reg":false,"x":475,"y":469,"z":"396c2376.c693dc","wires":[["289ed11e.d7612e"]]}]

The initial switch node separates out payloads below 26 and those above. Those below 26 are passed straight to the trigger node. That node is set to pass the first message through and the block all subsequent messages until one arrives with the msg.reset property set. The messages above 26 get msg.reset set, so when they arrive at the trigger node, it gets reset, ready for the next <26 message to arrive and be passed through.


Between the two approaches, the first is more self-contained as it is a single Function node, but does assume you're comfortable maintaining that code. The second has a couple more nodes, but not a single line of code written.


To Julian's point, if you want to preserve the state over restarts of node-red, then you need some other mechanism in place to save that state. That wouldn't work with the Trigger node solution, but the Function node one would - you'd need a flow set to run on start up (the Inject node can do this, for example), that retrieves the state from some external source (such as a database or file), and initialises the function context flag. This adds a few more moving parts, so more in the category of 'running' rather than 'walking'.

Nick





--
http://nodered.org
---
You received this message because you are subscribed to the Google Groups "Node-RED" group.
To unsubscribe from this group and stop receiving emails from it, send an email to node-red+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dave C-J

unread,
May 30, 2015, 6:35:13 PM5/30/15
to node...@googlegroups.com
Nick,

great use of the trigger node  ;-)

<smirk/> 


Julian Knight

unread,
May 31, 2015, 7:38:04 AM5/31/15
to node...@googlegroups.com
Also to note that the maintenance of settings that survive a reset - which I think is critical for your use case as you are using this at a remote location - will get one heck of a lot easier when the guys have added the ability to set/get context & context.global variables from more than just function nodes. Due in a couple of releases hopefully. In order to avoid annoying Nick & Dave, I wont mention my temporary solution for get/set ... Doh!

MQTT seems ideal here for maintaining state since you can set your flag to an MQTT topic with retain & that will ensure that, when the system restarts, your MQTT listener for that topic will automatically get the last flag setting (assuming you make MQTT restart before NR of course). Much easier than faffing with a database which would need  a trigger on start node triggering a database lookup.

For anyone not using MQTT, they could write the flag to a simple text file and recover it on restart or use a db.

Ian Eagland

unread,
May 31, 2015, 9:49:05 AM5/31/15
to node...@googlegroups.com
Hi

Many thanks. Both methods suggested work a treat. Not sure the reboot is a major problem, at worst I would get one redundant text message as far as I can see. Better than getting dozens which was what was happening before I had the one shot I used Android free nma account for testing so it cost nothing.

Regards

Ian

Greg EVA

unread,
Jun 1, 2015, 4:44:38 AM6/1/15
to node...@googlegroups.com
@Ian - adding my two cents; consider employing the RBE node (it's perhaps called "smooth" in the flows.nodered.org).  You can smooth your voltage value over a number of previous values to help avoid bounce.  If the battery voltage is floating just around your 26V, then the RBE output will be more sure that the value has really dropped below.  Another idea is to implement an approach which doesn't un-set your low-battery alarm until the voltage passes a certain value, say 26.8V, to avoid re-alerting you as the value bounces around say as it is recharing, discharing, etc.

Dave C-J

unread,
Jun 1, 2015, 6:26:05 AM6/1/15
to node...@googlegroups.com
there are two nodes... the RBE one is called node-red-node-rbe...   http://flows.nodered.org/node/node-red-node-rbe
and does also perform a hysteresis or deadband operation.

Smooth is another node - http://flows.nodered.org/node/node-red-node-smooth - that can do min, mean, max over a number of readings...
Message has been deleted

Ian Eagland

unread,
Jun 2, 2015, 12:35:42 PM6/2/15
to node...@googlegroups.com

Greg

I had already discovered the need to unset only after voltage had risen by about a volt. Fortunately my testing was with a free nma account as I got about 3000 messages on my first test. A good job I was not using twilio. Also I realised NEVER set twilio to auto top up account when testing software. It could have been expensive! Thanks for the tip about RBE, I will add that in.

Regards

Ian

Greg EVA

unread,
Jun 3, 2015, 4:42:17 AM6/3/15
to node...@googlegroups.com
;-) Automatic stuff can certainly do unplanned damage.

Before deploying this sort of thing live, you can always log the "alert" to a log file or something.  Then lhave a look at it daily to make sure that it's working as expected and make adjustments where needed.  This way you're really sure about those SMSes being sent, or e-mails, or whatever.

Cheers,

Greg
Reply all
Reply to author
Forward
0 new messages