Need help optimising flow and Chart output


I'm working on my 2nd flow only for learning how things working. Still my java script knowledge is close to zero..working hard on it, but not easy @myAge :wink:
Still proud I made it that far after investigating, reading hundreds of documents, watching videos etc. I'm not going to tell you that it took me about a week to get there :wink:

What I'm trying to achieve in this flow is to (1) listen a UDP stream, which amongst other things sends a constant AIS (ships Automatic Identification System) NMEA data stream. Then (2) split it up for Channel A and Channel B messages by looking for a specific string inside the message. (3) Display LEDs of the Sense Hat matrix for as kind of an dynamic visual indicator that messages coming in. Red for Channel A, green for Channel B. And finally (4) count the number of messages coming through and (5) show the number of messages received for every minute.

(1) is working
(2) is working, but maybe can be optimised?
(3) is working, but for sure can be optimised (4 function nodes feels a bit too much) and has a big disadvantage: there can be up to 10 messages per second. Switching LEDs on and off takes time and sometimes leads to displaying a constant LED light for a second or longer. If technically possible I would like to limit the timeframe for a LED to show to max. 50-100ms.
(4) working to some extent. I cannot get the counter to reset after a minute and I don't know how to show the total message count for each minute on the chart. Below you find an example image of how I think the chart should look like. It's all about showing message count per minute (which obviously requires reset of the counter) in a chart, not about the beauty.

The single purpose of the flow is to learn and is of no other avail.
Any help much appreciated.

Here is the flow:

[{"id":"52ea2419.bf152c","type":"function","z":"6a37a171.1f5858","name":"matrix red on","func":"var newMsg = {payload: \"4,0,red,4,1,red,4,2,red,4,3,red,5,0,red,5,1,red,5,2,red,5,3,red,6,0,red,6,1,red,6,2,red,6,3,red,7,0,red,7,1,red,7,2,red,7,3,red\"};\nreturn newMsg;","outputs":1,"noerr":0,"x":450,"y":160,"wires":[["c01f39a.795c1c8","ed4f63bd.5d86e"]]},{"id":"bd2090d2.44a8a8","type":"switch","z":"6a37a171.1f5858","name":"channel A","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"!AIVDM,1,1,,A,","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":240,"y":180,"wires":[["52ea2419.bf152c"],["44434248.39b55c"]]},{"id":"44434248.39b55c","type":"function","z":"6a37a171.1f5858","name":"matrix red off","func":"var newMsg = {payload: \"4,0,off,4,1,off,4,2,off,3,off,5,0,off,5,1,off,5,2,off,5,3,off,6,0,off,6,1,off,6,2,off,6,3,off,7,0,off,7,1,off,7,2,off,7,3,off\"};\nreturn newMsg;","outputs":1,"noerr":0,"x":450,"y":200,"wires":[["c01f39a.795c1c8"]]},{"id":"c01f39a.795c1c8","type":"rpi-sensehat out","z":"6a37a171.1f5858","name":"","x":790,"y":260,"wires":[]},{"id":"2fb29b0d.c63ebc","type":"function","z":"6a37a171.1f5858","name":"matrix green on","func":"var newMsg = {payload: \"0,4,green,0,5,green,0,6,green,0,7,green,1,4,green,1,5,green,1,6,green,1,7,green,2,4,green,2,5,green,2,6,green,2,7,green,3,4,green,3,5,green,3,6,green,3,7,green\"};\nreturn newMsg;","outputs":1,"noerr":0,"x":460,"y":320,"wires":[["c01f39a.795c1c8","ed4f63bd.5d86e"]]},{"id":"9579c0b4.73eb4","type":"switch","z":"6a37a171.1f5858","name":"channel B","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"!AIVDM,1,1,,B,","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":240,"y":340,"wires":[["2fb29b0d.c63ebc"],["ec89eb67.627b28"]]},{"id":"ec89eb67.627b28","type":"function","z":"6a37a171.1f5858","name":"matrix green off","func":"var newMsg = {payload: \"0,4,off,0,5,off,0,6,off,0,7,off,1,4,off,1,5,off,1,6,off,1,7,off,2,4,off,2,5,off,2,6,off,2,7,off,3,4,off,3,5,off,3,6,off,3,7,off\"};\nreturn newMsg;","outputs":1,"noerr":0,"x":460,"y":360,"wires":[["c01f39a.795c1c8"]]},{"id":"5f4df95c.c75ea8","type":"udp in","z":"6a37a171.1f5858","name":"","iface":"","port":"10110","ipv":"udp4","multicast":"false","group":"","datatype":"utf8","x":80,"y":260,"wires":[["bd2090d2.44a8a8","9579c0b4.73eb4"]]},{"id":"ed4f63bd.5d86e","type":"counter","z":"6a37a171.1f5858","name":"","init":"0","step":"1","lower":null,"upper":null,"mode":"increment","outputs":2,"x":680,"y":400,"wires":[["4ff86015.b464d8"],[]]},{"id":"4ff86015.b464d8","type":"ui_chart","z":"6a37a171.1f5858","name":"","group":"e49d7e43.d049e8","order":0,"width":"0","height":"0","label":"","chartType":"line","legend":"false","xformat":"auto","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"","removeOlder":"1","removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"colors":["#1F77B4","#AEC7E8","#FF7F0E","#2CA02C","#98DF8A","#D62728","#FF9896","#9467BD","#C5B0D5"],"useOldStyle":false,"outputs":1,"x":850,"y":400,"wires":[[]]},{"id":"854da129.5e8158","type":"comment","z":"6a37a171.1f5858","name":"listen for Channel A and B messages","info":"","x":190,"y":140,"wires":[]},{"id":"d9ab82be.da8888","type":"comment","z":"6a37a171.1f5858","name":"switch LED matrix on/off depending on Channel","info":"","x":540,"y":120,"wires":[]},{"id":"fea1f3d0.9b9d1","type":"comment","z":"6a37a171.1f5858","name":"Total AIS Messages","info":"","x":760,"y":360,"wires":[]},{"id":"e49d7e43.d049e8","type":"ui_group","z":"","name":"AIS Message Count","tab":"f1a698b4.d47f4","order":1,"disp":true,"width":"6","collapse":false},{"id":"f1a698b4.d47f4","type":"ui_tab","z":"","name":"AIS","icon":"dashboard","order":6,"disabled":false,"hidden":false}]

And the nice to have chart image:


How about using an inject node sending a number 0 - set to fire every minute - connected to a change node - that moves msg.payload to msg.reset - that is connected to the counter?


Thank you, zenofmud. That could work. I will play with and report back in a few.


Maybe I'm not understanding the Rules for the Change node. The counter node expects msg.reset to reset. But output I get is this:

17.3.2019, 11:36:15node: 239e78f6.65c258
msg.payload : undefined

When I set it to move instead of change, I get the same output.
Here is what I set it to:


Hi, your post was very clear, included correct formatting, examples, and indicated you tried stuff before asking so i felt inclined to to try and help you.

NOTE: there are many ways to skin a cat & others will have other ideas. This is just one.

Screen shots...


The flow...
[{"id":"e99d400c.6ac15","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"d1da66ed.8ed4f8","type":"function","z":"e99d400c.6ac15","name":"matrix red on","func":"msg.payload = \"4,0,red,4,1,red,4,2,red,4,3,red,5,0,red,5,1,red,5,2,red,5,3,red,6,0,red,6,1,red,6,2,red,6,3,red,7,0,red,7,1,red,7,2,red,7,3,red\";\nmsg.topic = \"A\";\nreturn msg;","outputs":1,"noerr":0,"x":490,"y":80,"wires":[["eefa2946.5b3d08"]]},{"id":"ef34d559.e4a878","type":"switch","z":"e99d400c.6ac15","name":"A or B or other","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"!AIVDM,1,1,,A,","vt":"str"},{"t":"cont","v":"!AIVDM,1,1,,B,","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":3,"x":280,"y":100,"wires":[["d1da66ed.8ed4f8"],["2dd5498a.db6576"],["5503f0a6.0505c","1d88b922.cfa7f7"]]},{"id":"5503f0a6.0505c","type":"function","z":"e99d400c.6ac15","name":"matrix red off","func":"msg.payload = \"4,0,off,4,1,off,4,2,off,3,off,5,0,off,5,1,off,5,2,off,5,3,off,6,0,off,6,1,off,6,2,off,6,3,off,7,0,off,7,1,off,7,2,off,7,3,off\";\nmsg.topic = \"\";\nreturn msg;","outputs":1,"noerr":0,"x":490,"y":160,"wires":[[]]},{"id":"2dd5498a.db6576","type":"function","z":"e99d400c.6ac15","name":"matrix green on","func":"msg.payload = \"0,4,green,0,5,green,0,6,green,0,7,green,1,4,green,1,5,green,1,6,green,1,7,green,2,4,green,2,5,green,2,6,green,2,7,green,3,4,green,3,5,green,3,6,green,3,7,green\";\nmsg.topic = \"B\";\nreturn msg;","outputs":1,"noerr":0,"x":500,"y":120,"wires":[["eefa2946.5b3d08"]]},{"id":"1d88b922.cfa7f7","type":"function","z":"e99d400c.6ac15","name":"matrix green off","func":"msg.payload = \"0,4,off,0,5,off,0,6,off,0,7,off,1,4,off,1,5,off,1,6,off,1,7,off,2,4,off,2,5,off,2,6,off,2,7,off,3,4,off,3,5,off,3,6,off,3,7,off\";\nmsg.topic = \"\";\nreturn msg;","outputs":1,"noerr":0,"x":500,"y":200,"wires":[[]]},{"id":"daa58ccf.31e4a","type":"udp in","z":"e99d400c.6ac15","name":"","iface":"","port":"10110","ipv":"udp4","multicast":"false","group":"","datatype":"utf8","x":100,"y":100,"wires":[["ef34d559.e4a878"]]},{"id":"e64cb1a3.843cb","type":"comment","z":"e99d400c.6ac15","name":"listen for Channel A and B messages","info":"","x":190,"y":40,"wires":[]},{"id":"98e87438.0b9f58","type":"comment","z":"e99d400c.6ac15","name":"switch LED matrix on/off depending on Channel","info":"","x":600,"y":40,"wires":[]},{"id":"682d9713.9e3188","type":"inject","z":"e99d400c.6ac15","name":"A","topic":"","payload":"!AIVDM,1,1,,A,","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":160,"wires":[["ef34d559.e4a878"]]},{"id":"7c475136.306fb","type":"inject","z":"e99d400c.6ac15","name":"B","topic":"","payload":"!AIVDM,1,1,,B,","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":200,"wires":[["ef34d559.e4a878"]]},{"id":"eefa2946.5b3d08","type":"function","z":"e99d400c.6ac15","name":"log counts A and B in flow contect store","func":"if(msg.topic !== \"A\" && msg.topic !== \"B\"){\n return null;//halt flow\n} \n//set the payload to the memory value stored in flow contect\n//note: as we verified above that msg.topic is definitely A or B\n//we simply get from flow contect using msg.topic\nmsg.payload = flow.get(msg.topic) || 0; // if value is empty, ||0 will set it to 0 (like a default)\nmsg.payload += 1;//increment the value \nflow.set(msg.topic, msg.payload);//update the value in flow store\nreturn msg;//return the message\n","outputs":1,"noerr":0,"x":780,"y":100,"wires":[[]]},{"id":"90bc0552.721448","type":"function","z":"e99d400c.6ac15","name":"get & reset counts A and B","func":"var msgA = {};\nmsgA.topic = \"A\";\nmsgA.payload = flow.get(msgA.topic) || 0; // if value is empty, ||0 will set it to 0 (like a default)\n\nvar msgB = {};\nmsgB.topic = \"B\";\nmsgB.payload = flow.get(msgB.topic) || 0; // if value is empty, ||0 will set it to 0 (like a default)\n\n//store prev counters\nflow.set(\"A_Prev\",msgA.payload);\nflow.set(\"B_Prev\",msgB.payload);\n//reset counters\nflow.set(\"A\",0);\nflow.set(\"B\",0);\n\n//for learning purposes, i return 2 objects on 2 outputs\n//we could skip this by using 2 node.send(msg) if we wanted \nreturn [msgA, msgB];","outputs":2,"noerr":0,"x":400,"y":360,"wires":[["c0288161.721c3"],["c0288161.721c3"]]},{"id":"c0288161.721c3","type":"ui_chart","z":"e99d400c.6ac15","name":"","group":"4680bb7c.986364","order":0,"width":"0","height":"0","label":"","chartType":"line","legend":"true","xformat":"auto","interpolate":"step","nodata":"","dot":false,"ymin":"0","ymax":"","removeOlder":"1","removeOlderPoints":"","removeOlderUnit":"86400","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"outputs":1,"x":670,"y":360,"wires":[[]]},{"id":"beaa55c1.fd55e8","type":"inject","z":"e99d400c.6ac15","name":"Every minute","topic":"","payload":"true","payloadType":"bool","repeat":"60","crontab":"","once":true,"onceDelay":0.1,"x":140,"y":360,"wires":[["90bc0552.721448"]]},{"id":"cc2cfba3.3669e8","type":"comment","z":"e99d400c.6ac15","name":"Trigger chart update every minute","info":"","x":170,"y":320,"wires":[]},{"id":"4680bb7c.986364","type":"ui_group","z":"","name":"AIS Message Count","tab":"e1ead9a8.b76758","order":1,"disp":true,"width":"6","collapse":false},{"id":"e1ead9a8.b76758","type":"ui_tab","z":"","name":"AIS","icon":"dashboard","order":6,"disabled":false,"hidden":false}]


Hi Steve,
and thank you so much. That is a lot more than expected!

Now I need to work my way through your nodes and see how you had things done. That will be the fun part :slight_smile:

Just deployed and noticed that you split into A/B in the chart. That is not necessary as I want the total amount of messages (A+B). In real life there are these two channels just for the purpose of providing more bandwidth. Imagine a main port like Hongkong or wherever with hundreds if not thousands of ships. Each one sending an AIS message every 3 to 30 seconds. For the receiver it doesn't matter whether it was from channel A or B.

Another thing I discovered is that both LEDs (red and green) stays lit with your flow. Not sure what's going on, but will look into it. edit: my fault. For debug purposes I disconnected the off function nodes...display works fine.


Hi yeah - I was not 100% sure of your final intent (some assumptions were made) .

The changes I made were mostly academic & for showing you alternative methods.

Mainly I wanted to show you how the graph can be populated.

From what I've seen from you I am sure you can integrate what I did with your work & get it going pretty quickly & easily.

Key points I tried to make were...

  • using topics
  • use of flow context
  • use of function nodes with multiple outputs
  • how to populate graph

Let us know if you get stuck.


oh and in case you dont know of it, check out the right hand side bar in particular the Context Data - here you can see what value things are.



Thanks again very much!

The intent is only for learning. Sorry if I was not clear enough.
And no better way than to learn from others, how they do it or how they solve problems.

I must admit I've never heard of flow context, but I can see and understand how it works. When I discovered node-red I really thought it is all plug'n play. Wire a few nodes and there you go. How wrong I was :wink: But thats the fun of it. Finally I have a lot of ideas how to make real use of NR.


Sorry I should have said 'set msg.reset to msg.payload' like this:


zenofmud, not your fault. But a better understanding of this sure will help me.

When I set msg.reset to msg.payload per your example, it sends 0 (as was injected) to the output. The other way around is undefined.


I've always found the SET confusing - it sets the top item to what ever the bottom item is. So it is setting msg.reset' to what evermsg.payload' contains. I probably should have set set msg.reset to 0 because - if you read the info on the node you will see:

It's possible to control the counter with incoming msg properties:

  • msg.increment : counter will be incremented by the given value.
  • msg.decrement : counter will be decremented by the given value.
  • msg.reset : resets the counter to it's initial count, or to the given value, when it's a number.


Indeed, very confusing with the change node. I read that too that msg.reset could be a number and so I accepted 0 to be valid. But is not. As soon as I set msg.reset to 1 or any other number, it works.

Ugh, hard bread. But we finally got there :slight_smile: Thanks very much, zenofmud!


did you mke sure to send the number 0 and not the letter 0. i.e. This:

and not this:


Note the a/z vrs 0/9 for the type


Of course, I did and always verified the output to be a number. It accepts 0, but 0 doesn't reset the counter node.

Here is how it will reset. Please notice that once it resets the counter, it starts counting 1 twice. Means the counter does not start at 0. I cannot explain this as I'm too much of a novice in these things. But sure someone can. Or maybe a bug in the counter node?



Which counter node are you using (node-red-contrib-something probably)?


It's the node-red-contrib-counter.


About msg.reset the readme says " msg.reset : resets the counter to it's initial count, or to the given value, when it's a number." The emboldening is mine. Look at what value you have given for msg.reset. The counter is reset to that value.


@colin - it looks like there is a bug when msg.reset is set to zero. In that case it doesn't reset the counter. I'm going to take a look at the node and see if I can see why


Yup there is a bug! in the code it does this

            // handle reset
            if( msg.hasOwnProperty("reset") && msg.reset ) {
                node.count = typeof msg.reset === "number" ? msg.reset : node.init;

the problem is that if msg.reset containg a zero the if statement will always fail because msg.reset evaluate to false. the code should be

            // handle reset
            if( msg.hasOwnProperty("reset") &&  typeof msg.reset === "number" ) {
                node.count = typeof msg.reset === "number" ? msg.reset : node.init;

I will create a PR for this
@Stefanie in the meantime you can fix this in the code yourself if you are daring :stuck_out_tongue_winking_eye:
If you are on a Pi:

  1. open a terminal window
  2. enter nano $HOME/.node-red/node_modules/node-red-contrib-counter/counter.js
  3. use the down arrow to scroll down until you find the code above.
  4. make the change and press ctrl-x the y then the enter key
  5. restart NR and giveit a whirl

NOTE: I updated this code to the correct fix, my original fix only worked it the msg.reset was 0 but it can be any number.