Combine different reading (in time) to one average

I have a moisture sensor (esp8266) waking up from deep sleep and sending 2 of 3 reading to a mtqq.
These readings are approx 60 seconds apart.
After this it goes to deep sleep again for an hour.
I would like to make an average from these values and send that to my influxdb.

I'm thinking to use a trigger to detect if it was more than 90 seconds since the last value, but how do you store the previous values to make an average?

Hello Promy ..

You can use Node-red's Context in order to save those values.
Flow Context could be an Array in this case and you can push the moisture values to this array until the time to make the average. You can read more about Context in the documentation here

I think trying to work out a solution based on timing could be a little tricky and values could get out of sync.
Would it be possible, if you have access to the code on the ESP8266, to send a msg to Node-red (ie complete) to notify Node-red that the last value is sent (before it goes to deep sleep) so you can then make the average calculation ?

Example Flow using Context and calc. average on complete :

[{"id":"260f4b90e1219457","type":"function","z":"54efb553244c241f","name":"Average","func":"// if msg from mqtt is \"complete\"\nif (msg.payload === \"complete\") {\n    let moisture = flow.get(\"moisture\")\n    let total = 0\n    let average = 0\n\n    if (moisture.length > 0) {\n        moisture.forEach(val => total += val)\n        average = total / moisture.length\n\n        // send message\n        node.send({\n            \"topic\": \"moisture\",\n            \"payload\": average\n        })\n\n        flow.set(\"moisture\", []) // reset Context to empty array\n    }\n}\n\n// if payload is a Number\nelse if (!isNaN(msg.payload)) {\n    let moisture = flow.get(\"moisture\") || []\n    moisture.push(msg.payload) // add to Array\n    flow.set(\"moisture\", moisture)  // save to Context\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":2160,"wires":[["2bd3a9df9a604fdc"]]},{"id":"2bd3a9df9a604fdc","type":"debug","z":"54efb553244c241f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":610,"y":2160,"wires":[]},{"id":"ccafb3ce4b544815","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":210,"y":2060,"wires":[["260f4b90e1219457"]]},{"id":"90ca977ae11dcbe8","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"20","payloadType":"num","x":210,"y":2120,"wires":[["260f4b90e1219457"]]},{"id":"7e7542a9e2ab98d8","type":"inject","z":"54efb553244c241f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"70","payloadType":"num","x":210,"y":2180,"wires":[["260f4b90e1219457"]]},{"id":"ea19a230362b35c5","type":"inject","z":"54efb553244c241f","name":"complete","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"complete","payloadType":"str","x":200,"y":2260,"wires":[["260f4b90e1219457"]]}]
1 Like

You could set it up to complete a join and average the array. logic as below. You never gave any examples of the payloads so you will have to edit to suit. Edit 5 seconds to 90 or what ever suits.

[{"id":"fd0823d2.c0d288","type":"inject","z":"264dfde8ecb5ecec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"20","payloadType":"num","x":220,"y":160,"wires":[["3c3dc8f9.5284b","b5b5e1fa.953208"]]},{"id":"3c3dc8f9.5284b","type":"trigger","z":"264dfde8ecb5ecec","name":"","op1":"","op2":"true","op1type":"nul","op2type":"bool","duration":"5","extend":true,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":400,"y":160,"wires":[["7b983965.74a0f8"]]},{"id":"b5b5e1fa.953208","type":"join","z":"264dfde8ecb5ecec","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":420,"y":240,"wires":[["3ce507b4.961278"]]},{"id":"7b983965.74a0f8","type":"change","z":"264dfde8ecb5ecec","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"complete","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":160,"wires":[["b5b5e1fa.953208"]]},{"id":"3ce507b4.961278","type":"change","z":"264dfde8ecb5ecec","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$average($$.payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":260,"wires":[["96d12108.11f94"]]},{"id":"96d12108.11f94","type":"debug","z":"264dfde8ecb5ecec","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":860,"y":260,"wires":[]}]
1 Like

i was using the smooth node to get the mean value of so many readings.
have you looked into using it for your average needs?
here is a small example.

[{"id":"e693b71ef4b3f0ae","type":"inject","z":"ef2a7eeb036d3b29","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"16","payloadType":"num","x":85.56596374511719,"y":296.9331833862305,"wires":[["ab1b33eeda1dfa36"]]},{"id":"39e3b18a3298285e","type":"inject","z":"ef2a7eeb036d3b29","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"15","payloadType":"num","x":80.56596374511719,"y":349.9279648803711,"wires":[["ab1b33eeda1dfa36"]]},{"id":"cb5c2adb8c74fe7b","type":"inject","z":"ef2a7eeb036d3b29","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"22","payloadType":"num","x":71.56423950195312,"y":393.9297043823242,"wires":[["ab1b33eeda1dfa36"]]},{"id":"ab1b33eeda1dfa36","type":"smooth","z":"ef2a7eeb036d3b29","name":"","property":"payload","action":"mean","count":"3","round":"","mult":"single","reduce":false,"x":274.55902099609375,"y":363.9904648803711,"wires":[[]]}]
1 Like

I'm not sure this will help, but I went down a similar path a while ago.

I am getting timed things and wanted to get Maximum, Minimum and Average values.
(Daily)

Of course Average in itself is a can of worms.
There are a lot of different meanings in every day talk.

There is "Average", then "Median", "Mean" and a few more.

This function node with a bit of code may help.

[{"id":"acbd4dca.cd318","type":"function","z":"78d17e5b.9e6ff","g":"7e49ed3d.35f9cc","name":"Avg/Max/Min readings","func":"let max = context.get(\"max\") || 0;\nlet counter = context.get(\"counter\") || 0;\nlet min = context.get(\"min\") || null;\nlet avg = context.get(\"avg\") || null;\nlet total = context.get(\"total\") || 0;\n\n\n//node.warn(\"Initial total reading is \" + total);\n//total = total;\n\nlet new_value = parseInt(msg.payload);\n\nmsg1 = {};\nmsg2 = {};\n\nif (msg.reset == true)\n{\n    //  Reset all things\n    node.warn(\"RESET\");\n    context.set(\"max\",0);\n    context.set(\"counter\",0);\n    context.set(\"min\",0);\n    context.set(\"avg\",0);\n    context.set(\"total\",0);\n    return;\n}\n\nif (counter === 0)\n{\n    //  First pass\n    context.set(\"max\",new_value);\n    context.set(\"min\",new_value)\n    context.set(\"avg\",new_value);\n    min = new_value;\n    max = new_value;\n    avg = new_value;\n    total = new_value;\n}\n\n//new_value = msg.payload;\n\nif (new_value > max)\n{\n    max = new_value;\n    context.set(\"max\",new_value);\n}\n\nif (new_value < min)\n{\n\tmin = new_value;\n\tcontext.set(\"min\",new_value);\n}\n\ncounter = counter + 1;\ncontext.set(\"counter\",counter);\n\ntotal = context.get(\"total\") || 0;\n//node.warn(\"context TOTAL is \" + total);\ntotal = total + new_value;\n//node.warn(\"TOTAL IS NOW \" + total);\ncontext.set(\"total\",total);\navg = Math.round((total / counter));\n\n//\n\n//msg.payload = \"My calculated average is \" + avg;\nmsg.payload = Math.round(avg);\nmsg1.payload = Math.round(max);\nmsg2.payload = Math.round(min);\nreturn [msg,msg1,msg2];","outputs":3,"noerr":0,"initialize":"","finalize":"","x":2170,"y":1670,"wires":[["b6b37cab.88821","267b42e0.270b16"],["e631bde7.36527","1b588bd.547a2f4"],["e8ec38d8.a3c568","89fd7801.01c23"]],"outputLabels":["AVG","MAX","MIN"]}]

You will need to send a special message into it to signal a New day and that will reset things for the next/new day.

Hope it helps.

This is a bigger picture of how it is in my flow.

The input is at the red arrow.
The New day pulse is into the New Day input.
The 3 outputs of the node are labelled if/when you import it and put the mouse over them.
At the top that is a bit more stuff I do which you may/not want to use.

But the function node does what you want - I hope.

1 Like

For an average (mean) value you only need a total and a count. It seems over the top to save all the readings in memory.

You could accumulate the total and count in flow context.

To reset them a trigger node set to send nothing, after 5 minutes send a message to a change node to reset.

I am not very familiar with influxdb. If it was SQL I would insert a record if flow.count == 1, update the latest record if flow.count > 1.

Or, if you prefer, insert an average reading immediately before resetting the context variables

1 Like