Trying to calcule dew point in a function note by grapping humidity and temp from two devices

751 views
Skip to first unread message

GixxerPilot

unread,
Sep 29, 2016, 2:42:13 PM9/29/16
to Node-RED
I want calculate dew point based on the following function: 

function dCalc(h, t)
{
//iMV is the intermidate Value 
var iMV = (Math.log(h/100)+((17.27* t)/(237.3 +t)))/17.27;
var dewPoint = (237.3 *iMV)/(1-iMV);
return dewPoint.toFixed(2);
}

I have two devices. One is getting the temp the other is getting a humidity reading. I have an idea that I should should use global context to store the two values and them use them in a function node that has the function declared. I am not sure how exactly I should go about this. The dew point should be displayed on a gauge and update live depending what values the two sensors are reading. 

This is what I have been trying so far. The two devices are missing since I saw that when you export them they also give out your api keys.

this is my clipboard export:
[{"id":"23c972a0.f2b22e","type":"debug","z":"bc63e9bb.1b1398","name":"","active":false,"console":"false","complete":"false","x":361.0714416503906,"y":104.4285659790039,"wires":[]},{"id":"7d4ba41f.c027bc","type":"function","z":"bc63e9bb.1b1398","name":"Extract Tempeture","func":"if(msg.payload.d.Numeric) {\n    var temp = ({payload:msg.payload.d.Numeric});\n    context.global.tempG = msg.payload.d.Numeric;\n}\nreturn temp;\n","outputs":1,"noerr":0,"x":405.0714416503906,"y":195.14286041259766,"wires":[["fd5d545c.440f18","eeea1e0b.f542b","17caccc2.af4bf3"]]},{"id":"fd5d545c.440f18","type":"ui_gauge","z":"bc63e9bb.1b1398","name":"","group":"16a67149.7660ff","order":0,"width":0,"height":0,"gtype":"gage","title":"Temperature","label":"celcius","format":"{{value}}","min":0,"max":"40","colors":["#00b500","#e6e600","#ca3838"],"x":563.0715637207031,"y":172.99999237060547,"wires":[]},{"id":"eeea1e0b.f542b","type":"ui_chart","z":"bc63e9bb.1b1398","name":"","group":"16a67149.7660ff","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"%H:%M:%S","interpolate":"linear","nodata":"","ymin":"0","ymax":"40","removeOlder":1,"removeOlderUnit":"3600","x":562.071533203125,"y":215.00000762939453,"wires":[[],[]]},{"id":"1555ea47.b52a76","type":"function","z":"bc63e9bb.1b1398","name":"Extract Humidity","func":"if (msg.payload.d.humidity) {\n    var humidity = ({payload:msg.payload.d.humidity});\n    context.global.humidityG = msg.payload.d.humidity;\n}\n\nreturn humidity;\n\n","outputs":1,"noerr":0,"x":403.0714416503906,"y":295.14286041259766,"wires":[["610d8315.222e9c","5bba22b2.fbdf9c","17caccc2.af4bf3"]]},{"id":"c06a6d2.09f609","type":"debug","z":"bc63e9bb.1b1398","name":"","active":false,"console":"false","complete":"false","x":345.3571472167969,"y":374.7143020629883,"wires":[]},{"id":"610d8315.222e9c","type":"ui_gauge","z":"bc63e9bb.1b1398","name":"","group":"66993376.67f3bc","order":0,"width":0,"height":0,"gtype":"gage","title":"Humidity","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"x":560.3572082519531,"y":272.8571548461914,"wires":[]},{"id":"5bba22b2.fbdf9c","type":"ui_chart","z":"bc63e9bb.1b1398","name":"","group":"66993376.67f3bc","order":0,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"%H:%M:%S","interpolate":"linear","nodata":"","ymin":"0","ymax":"100","removeOlder":1,"removeOlderUnit":"3600","x":560.3572082519531,"y":315.85713958740234,"wires":[[],[]]},{"id":"537be6fc.ca25a8","type":"function","z":"bc63e9bb.1b1398","name":"Calculate dewpoint","func":"var h = global.get('humidityG');\nvar t = global.get('tempG');\n\n\nfunction dp(h,t)\n{\n\t//iMV is the intermidate Value \n\tvar iMV = (Math.log(h/100)+((17.27* t)/(237.3 +t)))/17.27;\n\tvar dewPoint = (237.3 *iMV)/(1-iMV);\n\t//return (Math.round((dewPoint)*100)/100);\n\tmsg.payload = dewPoint.toFixed(2);\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":647.3573760986328,"y":92.85714721679688,"wires":[["ab930804.fed2c8","182b5919.2bc037"]]},{"id":"ab930804.fed2c8","type":"debug","z":"bc63e9bb.1b1398","name":"","active":true,"console":"false","complete":"false","x":733.0714263916016,"y":322.71422576904297,"wires":[]},{"id":"182b5919.2bc037","type":"ui_gauge","z":"bc63e9bb.1b1398","name":"","group":"f1b1c61a.b781b8","order":0,"width":0,"height":0,"gtype":"gage","title":"Gauge","label":"units","format":"{{value}}","min":0,"max":10,"colors":["#00b500","#e6e600","#ca3838"],"x":844.0714893341064,"y":147.42855072021484,"wires":[]},{"id":"17caccc2.af4bf3","type":"function","z":"bc63e9bb.1b1398","name":"","func":"var msg1 = {};\nvar msg2 = {};\nmsg1.payload = msg.payload.temperature;\nmsg2.payload = msg.payload.humidity;\nreturn [msg1,msg2];","outputs":1,"noerr":0,"x":501.0714416503906,"y":473.8571548461914,"wires":[["165fbf48.05fe21","942985c8.3050a8"]]},{"id":"165fbf48.05fe21","type":"ui_gauge","z":"bc63e9bb.1b1398","name":"","group":"f1b1c61a.b781b8","order":0,"width":0,"height":0,"gtype":"gage","title":"Gauge","label":"units","format":"{{value}}","min":0,"max":10,"colors":["#00b500","#e6e600","#ca3838"],"x":675.3571472167969,"y":460.8571548461914,"wires":[]},{"id":"942985c8.3050a8","type":"debug","z":"bc63e9bb.1b1398","name":"","active":true,"console":"false","complete":"false","x":632.3571472167969,"y":528.7143325805664,"wires":[]},{"id":"16a67149.7660ff","type":"ui_group","z":"bc63e9bb.1b1398","name":"NI 6008","tab":"75b920e0.6eeaf","disp":true,"width":"6"},{"id":"66993376.67f3bc","type":"ui_group","z":"bc63e9bb.1b1398","name":"Nodemcu E12","tab":"75b920e0.6eeaf","disp":true,"width":"6"},{"id":"f1b1c61a.b781b8","type":"ui_group","z":"bc63e9bb.1b1398","name":"Calculated Values","tab":"75b920e0.6eeaf","disp":true,"width":"6"},{"id":"75b920e0.6eeaf","type":"ui_tab","z":"bc63e9bb.1b1398","name":"Home","icon":"dashboard"}]

Paul Reed

unread,
Sep 29, 2016, 3:57:42 PM9/29/16
to Node-RED
I've done something similar, if it helps.

Paul

[{"id":"be728125.418d8","type":"function","z":"3f1992f2.c0e66e","name":"Parse METAR","func":"context.noaadata = context.noaadata || null;\ncontext.emontemp = context.emontemp || null;\n//using a switch statement to match payload to topic\nswitch (msg.topic) {\n    case \"noaaTop\":\n        context.noaadata = msg.payload;\n        msg.payload = null;\n        break;\n    case \"emonTop\":\n        context.emontemp = msg.payload;\n        msg.payload = null;\n        break;\n    default:\n        msg.payload = null;\n    \tbreak;\n}\n//check if both data vaues have been received\nif(context.noaadata !== null && context.emontemp !== null) {\n//parse XML feed\nvar METARdata = context.noaadata.response.data[0].METAR[0];\nvar windkt = METARdata.wind_speed_kt[0];\nvar winddir = METARdata.wind_dir_degrees[0];\nvar windkph = (windkt*1.852);\nvar gusts = windkt; //if wind_gust_kt is not present, value defaults to wind_speed_kt\n   if (typeof METARdata.wind_gust_kt != \"undefined\") {\n\t   gusts = METARdata.wind_gust_kt[0];\n       }\nvar altim = METARdata.altim_in_hg[0];\nvar pressure = Math.round(altim * 33.8637526); //convert hg to mb\nvar dew = METARdata.dewpoint_c[0];\ndew = Math.round(dew);\n\n//TempC obtained via emoncms\nvar tempC = context.emontemp;\nvar tempF = ((tempC*1.8)+32);\n\n//August-Roche-Magnus approximation to calculate rh_hum\nvar constA = 17.625;\nvar constB = 243.04;\nvar rh_numer = 10000.0*Math.exp((constA*eval(dew))/(eval(dew)+constB));\nvar rh_denom = Math.exp((constA*eval(tempC))/(eval(tempC)+constB));\nvar rh_hum   = (rh_numer/rh_denom);\nvar humidityemon = Math.round(rh_hum);\nvar humidity = (humidityemon/100);\n\n//Wind Chill Index Calculations using the 'Joint Action Group for Temp Indices' (JAG TI) formula\nvar chill=(13.12+0.6215*tempC-11.37*Math.pow(windkph,0.16)+0.3965*tempC*Math.pow(windkph,0.16));\nif (windkph<4.83) {\nchill=tempC;\n}\n\n//Calculate Apparent Temperature\n//firstly, Calculate Vapor pressure in kPa\nvar vaporp = ((6.11*Math.pow(10,(7.5*dew/(237.3 + dew))))/10);\n//apply Steadman's Apparent Temperature formula\nvar apptemp = -2.7+(1.04*tempC)+(2*vaporp)-(windkt*0.3343886);\n        \n//Calculation of 'feels like' temperature by using the 'chill factor' if\n//the temp is below 10degC, or 'apparent temp' if over 20degC, or if between\n//10 and 20degC using a linear interpolation of both.\nvar feels;\nif (tempC < 10.0) {\n\tfeels=chill;\n\t}\n\telse if (tempC > 20.0) {\n\tfeels=apptemp;\n\t}\n\t\telse {\n\t\tvar calcA=((tempC-10)/10);\n\t\tvar calcB=1-calcA;\n\t\tfeels=((apptemp*calcA)+(chill*calcB));\n\t\t}\n\t\t\tfeels=Math.round(feels*100);\n\t\t\nmsg.payload = ((windkt) + (\",\") + (gusts) + (\",\") + (humidityemon) + (\",\") + (pressure) + (\",\") + (feels) + (\",\") + (winddir));\n\n//reset switch filter\ncontext.noaadata = null;\n//uncomment next line to await for both new values to arrive\ncontext.emontemp = null;\n\nreturn msg;\n}","outputs":1,"noerr":0,"x":734,"y":443,"wires":[["2857ddc.fd7a822","5b713c72.a48ec4"]]}]

Dave C-J

unread,
Sep 29, 2016, 7:44:48 PM9/29/16
to node...@googlegroups.com

Hi,
What device nodes are they ? We need to "have a word"with the author about those credentials... They should not be exported...

Walter Kraembring

unread,
Sep 30, 2016, 1:41:18 AM9/30/16
to Node-RED
I have "traditional" sensors from Oregon for humidity & temperature readings and a RFXtrx transceiver. I calculate the dew point delta (means how far away we are from dew point with the actual temperature) for the attic and the ground of our house. I use the flow below. The two formulas give approximately the same result. In daily operation I decided for the first one since it is a little bit "tougher", it tends to give lower values for the delta calculation means it has an additional safety margin




[{"id":"84c48a06.db3f68","type":"rfx-sensor","z":"4668031a.72819c","name":"","port":"14ce85ac.bb2a6a","topicSource":"single","topic":"TH1","x":380,"y":240,"wires":[["eada6dfd.7069c","6e7815f4.b6c9bc"]]},{"id":"731c1b31.351334","type":"debug","z":"4668031a.72819c","name":"","active":true,"console":"false","complete":"payload","x":790,"y":280,"wires":[]},{"id":"eada6dfd.7069c","type":"function","z":"4668031a.72819c","name":"","func":"// initialise the variables if they doesn't exist already\ncontext.set('dpAttic',context.get('dpAttic')||0);    \ncontext.set('dpGround',context.get('dpGround')||0);    \n\nfunction updateNodeStatus(txt) {\n    node.status({\n    \ttext : txt\n    });\n}\n\nfunction computeDew(temp, rh)\n{\n    if ( (temp === null || temp.length === 0) ||\n        (rh === null || rh.length === 0) ) {\n        return;\n    }\n    if (rh >= 85) {\n        dewp = temp-0.133 * ( 100.0-rh );\n    }    \n    if (rh < 85) {\n        dewp = temp-0.2 * ( 95.0-rh );\n    }    \n    return (temp - dewp).toPrecision(4);\n}\n\ntemp = msg.payload.temperature.value;\nrh = msg.payload.humidity.value;\n\nif (msg.topic === 'TH1/0x0704') {\n    context.set('dpGround',computeDew(temp, rh));\n    msg.payload = 'Ground: '+context.get('dpGround');\n}\nif (msg.topic === 'TH1/0x2002') {\n    context.set('dpAttic',computeDew(temp, rh));\n    msg.payload = 'Attic: '+context.get('dpAttic');\n}\ntxt = 'Ground: '+context.get('dpGround') +' Attic: '+context.get('dpAttic');\nupdateNodeStatus(txt);\n\nreturn msg;","outputs":1,"noerr":0,"x":590,"y":200,"wires":[["558410a5.ef78f"]]},{"id":"6e7815f4.b6c9bc","type":"function","z":"4668031a.72819c","name":"","func":"// initialise the variables if they doesn't exist already\ncontext.set('dpAttic',context.get('dpAttic')||0);    \ncontext.set('dpGround',context.get('dpGround')||0);    \n\nfunction updateNodeStatus(txt) {\n    node.status({\n    \ttext : txt\n    });\n}\n\nfunction computeDew(temp, rh) {\n    if ( (temp === null || temp.length === 0) ||\n        (rh === null || rh.length === 0) ) {\n        return;\n    }\n    tem2 = temp;\n    tm = -1.0*temp;\n    es = 6.112*Math.exp(-1.0*17.67*tm/(243.5 - tm));\n    ed = rh/100.0*es;\n    eln = Math.log(ed/6.112);\n    dewp = -243.5*eln/(eln - 17.67 );\n    return (temp - dewp).toPrecision(4);\n}\n\ntemp = msg.payload.temperature.value;\nrh = msg.payload.humidity.value;\n\nif (msg.topic === 'TH1/0x0704') {\n    context.set('dpGround',computeDew(temp, rh));\n    msg.payload = 'Ground: '+context.get('dpGround');\n}\nif (msg.topic === 'TH1/0x2002') {\n    context.set('dpAttic',computeDew(temp, rh));\n    msg.payload = 'Attic: '+context.get('dpAttic');\n}\ntxt = 'Ground: '+context.get('dpGround') +' Attic: '+context.get('dpAttic');\nupdateNodeStatus(txt);\n\nreturn msg;","outputs":"1","noerr":0,"x":590,"y":280,"wires":[["731c1b31.351334"]]},{"id":"558410a5.ef78f","type":"debug","z":"4668031a.72819c","name":"","active":true,"console":"false","complete":"false","x":790,"y":200,"wires":[]},{"id":"14ce85ac.bb2a6a","type":"rfxtrx-port","z":"","port":"/dev/ttyUSB0"}]


Julian Knight

unread,
Sep 30, 2016, 1:10:52 PM9/30/16
to Node-RED
This is the one I use which I think is similar to the one given at the start of this thread:

// See: http://arduinotronics.blogspot.co.uk/2013/12/temp-humidity-w-dew-point-calcualtions.html
// delta max = 0.6544 wrt dewPoint()
// 6.9 x faster than dewPoint()
// reference: http://en.wikipedia.org/wiki/Dew_point
function dewPointFast(celsius, humidity) {
   
var a = 17.271;
   
var b = 237.7;
   
var temp = Math.log(humidity * 0.01) + ((a * celsius) / (b + celsius));
   
var Td = (b * temp) / (a - temp);
   
return round( Td, 1 );
}

Converted from some C++ code for Arduino.

Paul Reed

unread,
Sep 30, 2016, 2:16:09 PM9/30/16
to Node-RED
The issue I think you will have with your existing flow is that as both values are being sourced from different nodes, and may not arrive at your function node together, therefore the first message received will be passed through by your function node, and the second message may arrive a split second later. As a result, it would be difficult to base calculations on both values together.

I got around this in my flow above by creating a switch statement (lines 4 - 16), which stores the first value as a global context, and awaits the second value. Once that arrives, both values are then processed (line 18).
Lines 78 & 80 reset the global context to null, awaiting the next new message to arrive.

Paul

Walter Kraembring

unread,
Oct 1, 2016, 2:20:44 PM10/1/16
to Node-RED
As a result, it would be difficult to base calculations on both values together.

In my flow the calculation will only start when I have both values collected. In this particular case it is not so important that they are from the same second, it could very well be a minute separation between them since they are just slowly changing their values. In other cases it might be necessary to apply a completely different approach 

Paul Reed

unread,
Oct 1, 2016, 2:25:52 PM10/1/16
to Node-RED
How then are you ensuring that you have both values together at the same moment in time without using a switch statement?

Walter Kraembring

unread,
Oct 1, 2016, 2:36:25 PM10/1/16
to Node-RED
Paul, you are correct, I THOUGHT I did but I see now I did not :(

So the first calculated result will be wrong but then it will be corrected based on the fact that the values does not change much during the readings (I do receive new values approx every 45 sec and I store the last in the context). In other function nodes I have just used the context, setting variables to a certain start value and then checking if it differs. So a kind of switch but coded in a function node...

Julian Knight

unread,
Oct 1, 2016, 3:37:01 PM10/1/16
to Node-RED
In all honesty, even having the measurements a few minutes apart wont make as much difference as the error bounds of the humidity sensor anyway. It is notoriously difficult to get an accurate humidity reading even from pro equipment. And if you are trying to determine the external ground dew point from sensors anywhere other than close to the ground you want to know about, you will be a fair way off anyway I suspect.

Paul Reed

unread,
Oct 1, 2016, 4:46:56 PM10/1/16
to Node-RED
That wasn't really my point Julian. I was suggesting that a framework is needed to capture and store the first value, wait until the second value arrives - and only then do the dew calculation based on the two values, before discarding them and starting afresh.
If the first value isn't stored, it would be lost when the second value arrives.
The example I gave was my way in achieving that, but I'm sure there would be other ways to get the same result.

Paul  

Walter Kraembring

unread,
Oct 2, 2016, 1:05:08 AM10/2/16
to Node-RED
And in my case, the event I receive from the RFXtrx, holds both the temperature & humidity in the same msg. Therefore my flow was somehow misleading but working in my setup thanks to Max node node-red-contrib-rfxcom. Just to explain how it actually could work

Julian Knight

unread,
Oct 2, 2016, 5:39:28 AM10/2/16
to Node-RED
I think we wondered across several subjects in this thread in the end. 8-}

My various sensors all have both temp & humidity since the majority of humidity sensors do this anyway. I use a mix of Oregon and homebrew sensors. The Oregon one will end up outside eventually (when I've stopped messing) and they clearly use cheap components that aren't that accurate which is why I didn't buy any more but it saves me making a weatherproof sensor.

All my newer sensors use Wemo D1 Mini's so are Wi-Fi connected. Much simpler, I'll only go back to something else if I need something with a long battery life. All the Wemo's are connected to power. I use Max's nodes too for both the Oregon and my LightwaveRF, HomeEasy and Nexa kit.
Reply all
Reply to author
Forward
0 new messages