Quantcast
Channel: OpenEnergyMonitor aggregator
Viewing all articles
Browse latest Browse all 328

mharizanov: NodeRED RFM2Pi to emonCMS gateway

$
0
0

I created a Node-RED RFM2Pi gateway few weeks ago, have it running nicely since then. I decided to use that approach instead of a python script, since it is very flexible, visual and easy to extend. Prior to Node-RED I used the great OEM gateway script by Jérôme Lafréchoux. My inspiration to switch to Node-RED were Nathan Chantrell’s posts and tweets and Don Bramey’s work.

Getting Node-RED installed on Raspberry Pi is best described on Adafruit’s site [Edit: see Ukmoose' comment below on node.js version]. Should be straightforward for most. In addition, I installed MQTT:

sudo apt-get install mosquitto

The idea of the gateway is to

  • Interface with the RFM2Pi board
  • Forward all incoming wireless packets to EmonCMS for logging and further analysis
  • Send current time to wireless nodes like EmonGLCD

Here are the Node-RED flows

multinode-rfm2pi

The RFM2Pi flow containing the inbound and outbound functions

 

multinode-rfm2pi-function

This uses the so called ‘multinode’ approach, i.e. doesn’t really decode or understand what is being sent. It just captures the payload and publishes it to a MQTT topic

 

The RFM2Pi sender function

The RFM2Pi sender function

And here is the source code for this flow:

 
[{"id":"b7108ab7.48ef78","type":"serial-port","serialport":"/dev/ttyAMA0","serialbaud":"9600","newline":"\\r\\n","addchar":"true"},{"id":"ba386057.845d3","type":"mqtt-broker","broker":"localhost","port":"1883"},{"id":"4daa1376.b255ec","type":"function","name":"Parse RF12B ","func":"//we are expecting data in form \" nodeid data1 data2 etc\"\nmsg.payload = msg.payload.trim();\n\nvar tokens = msg.payload.split(\" \",66); //FM2Pi will have max of 66 bytes payload\nvar outString = null;\nvar outTopic = null;\nvar msg2 = null;\nvar nodeid = tokens.shift();\n\nif(!isNaN(nodeid)) {\n\tnodeid = nodeid & 0x1F; // Strip out the additional flags\n\tvar raw= tokens;\n\tbuf = new Buffer(raw);\n\toutString = [];\n\tfor (var i=0; i<=(tokens.length/2); i+=2) {\n\t\toutString.push( buf.readInt16LE(i) );\n\t}\t\n\t//console.log(outString);\n\toutTopic = 'rfm12b/' + nodeid;\n\tmsg2 = { payload:outString, topic:outTopic};\n}\nreturn msg2;","outputs":"1","x":277,"y":111,"z":"437a6787.bc8598","wires":[["d02f9e7.f2fd06"]]},{"id":"d02f9e7.f2fd06","type":"mqtt out","name":"RFM12B IN","topic":"","broker":"ba386057.845d3","x":456,"y":111,"z":"437a6787.bc8598","wires":[]},{"id":"2a97ab9.fd56854","type":"serial in","name":"","serial":"b7108ab7.48ef78","x":89,"y":111,"z":"437a6787.bc8598","wires":[["4daa1376.b255ec","512b148f.aed4ec"]]},{"id":"1af69d77.e50963","type":"mqtt in","name":"RFM12B OUT","topic":"rfm12b/out","broker":"ba386057.845d3","x":86,"y":304,"z":"437a6787.bc8598","wires":[["80d0c97c.7f2f38"]]},{"id":"80d0c97c.7f2f38","type":"function","name":"Send to Node","func":"// The received message is stored in 'msg'\n// It will have at least a 'payload' property:\n//   console.log(msg.payload);\n// The 'context' object is available to store state\n// between invocations of the function\n//   context = {};\n\nvar dataStr = msg.payload;\n\n//if individual node selected - the destination id should be last part of topic\nvar tokens = msg.topic.split(\"/\");\nvar dest = tokens[tokens.length-1];\n\nif(dest == \"out\")\n{\n   dest = \"0\";\n}\nif(isNaN(dest))\n{\n  dataStr='NAN';\n  //return null;\n}\nmsg.payload = dataStr + \",\" + dest + \"s\";\nreturn msg;","outputs":1,"x":271,"y":305,"z":"437a6787.bc8598","wires":[["906cd828.6f9328"]]},{"id":"512b148f.aed4ec","type":"debug","name":"Serial In Debug","active":false,"complete":"false","x":285.42852783203125,"y":183.85711669921875,"z":"437a6787.bc8598","wires":[]},{"id":"ff2da801.00d258","type":"comment","name":"RFM2Pi Inbound ","info":"","x":81,"y":58,"z":"437a6787.bc8598","wires":[]},{"id":"6bb839bd.9447c8","type":"comment","name":"RFM2Pi Outbound","info":"","x":88,"y":258,"z":"437a6787.bc8598","wires":[]},{"id":"67bfcebf.98403","type":"serial out","name":"","serial":"b7108ab7.48ef78","x":634,"y":304,"z":"437a6787.bc8598","wires":[]},{"id":"113cd655.eec32a","type":"debug","name":"Sender Debug","active":true,"complete":"false","x":570,"y":419,"z":"437a6787.bc8598","wires":[]},{"id":"906cd828.6f9328","type":"delay","name":"Delay 5ms","pauseType":"delay","timeout":"5","timeoutUnits":"milliseconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","x":432,"y":306,"z":"437a6787.bc8598","wires":[["67bfcebf.98403","113cd655.eec32a"]]}]

Next is the EmonCMS interface flow:

emoncms-tab

 

Most interesting is the “Prepare for EmonCMS” function:

emoncms-prepare

Mind to place your API key here, emoncms URL as well, if it is not default

And the source code for this flow is:

[{"id":"ba386057.845d3","type":"mqtt-broker","broker":"localhost","port":"1883"},{"id":"909bcfcf.6f643","type":"mqtt in","name":"Send to Emoncms","topic":"home/emoncms/out/#","broker":"ba386057.845d3","x":84.00000762939453,"y":87,"z":"fea0be35.015f4","wires":[["8ebc9733.714368"]]},{"id":"8ebc9733.714368","type":"function","name":"Build EmonCMS url","func":"// The received message is stored in 'msg'\n// It will have at least a 'payload' property:\n//   console.log(msg.payload);\n// The 'context' object is available to store state\n// between invocations of the function\n//   context = {};\n\n//expects JSON payload and destination specified in topic\n\nvar key = \"**** YOUR API *****\";\n\n//destination will be last part node of topic\nvar tokens = msg.topic.split(\"/\");\nvar dest = tokens[tokens.length-1];\n\n\nif(isNaN(dest))\n{\n//not a valid destination\nreturn null;\n}\n\nmsg.url =\"http://emoncms.org/input/post.json?node=\";\nmsg.url += dest;\nmsg.url += \"&json=\";\nmsg.url += msg.payload;\nmsg.url += \"&apikey=\";\nmsg.url += key;\n\n\nreturn msg;","outputs":1,"x":285.00001525878906,"y":119,"z":"fea0be35.015f4","wires":[["f9933cd6.066cc","bef8a264.41076"]]},{"id":"f9933cd6.066cc","type":"http request","name":"HTTP GET to EmonCMS","method":"GET","url":"","x":513.6667175292969,"y":99,"z":"fea0be35.015f4","wires":[["87f944f6.7806b8"]]},{"id":"87f944f6.7806b8","type":"debug","name":"EmonCMS HTTP Request Result","active":false,"complete":"false","x":713.6666870117188,"y":31.333343505859375,"z":"fea0be35.015f4","wires":[]},{"id":"a8c85fa8.5737a","type":"debug","name":"EmonCMS URL","active":false,"complete":"true","x":702,"y":164,"z":"fea0be35.015f4","wires":[]},{"id":"bef8a264.41076","type":"function","name":"Debug EmonCMS URL","func":"// The received message is stored in 'msg'\n// It will have at least a 'payload' property:\n//   console.log(msg.payload);\n// The 'context' object is available to store state\n// between invocations of the function\n//   context = {};\n\n\nreturn msg.url;","outputs":1,"x":517,"y":163,"z":"fea0be35.015f4","wires":[["a8c85fa8.5737a"]]},{"id":"788069a.f877f98","type":"mqtt in","name":"RFM12B IN","topic":"rfm12b/#","broker":"ba386057.845d3","x":65,"y":289,"z":"fea0be35.015f4","wires":[["ca153b2d.35eac8"]]},{"id":"314a8d67.ceb572","type":"mqtt out","name":"Send to Emoncms","topic":"","broker":"ba386057.845d3","x":502,"y":289,"z":"fea0be35.015f4","wires":[]},{"id":"ca153b2d.35eac8","type":"function","name":"Prepare for EmonCMS","func":"// The received message is stored in 'msg'\n// It will have at least a 'payload' property:\n//   console.log(msg.payload);\n// The 'context' object is available to store state\n// between invocations of the function\n//   context = {};\n\n//destination will be last part node of topic\nvar tokens = msg.topic.split(\"/\");\nvar dest = tokens[tokens.length-1];\n\nmsg.topic = \"home/emoncms/out/\";\nmsg.topic += dest;\n\nreturn msg;\n","outputs":1,"x":270,"y":290,"z":"fea0be35.015f4","wires":[["314a8d67.ceb572"]]},{"id":"b8e15d04.471ea","type":"comment","name":"EmonCMS sender","info":"","x":84,"y":44,"z":"fea0be35.015f4","wires":[]},{"id":"7a28503a.85d7b","type":"comment","name":"Send whatever was received on the RFM12B/# MQTT topic to EmonCMS","info":"","x":258,"y":234,"z":"fea0be35.015f4","wires":[]}]

The final component is the time sender flow:

time-sender-flow

I have an inject node that kicks in every minute, you can reduce that to whatever is necessary. Most of the work is done in the “Prepare Time Packet” function:

time-sender-prepare

 

And the source code is:

[{"id":"ba386057.845d3","type":"mqtt-broker","broker":"localhost","port":"1883"},{"id":"df2824d5.20d7d8","type":"inject","name":"Inject time","topic":"","payload":"","repeat":"","crontab":"*/1 * * * *","once":true,"x":196,"y":179,"z":"7733bcb0.88cc44","wires":[["431cf1b3.bce31"]]},{"id":"431cf1b3.bce31","type":"function","name":"Prepare Time Packet","func":"// The received message is stored in 'msg'\n// It will have at least a 'payload' property:\n//   console.log(msg.payload);\n// The 'context' object is available to store state\n// between invocations of the function\n//   context = {};\n\nvar date = new Date;\ndate.getTime();\n\nvar minutes = date.getMinutes();\nvar hour = date.getHours();\n\n//00,20,23,00,s\n\nmsg.payload = '0,' + hour + ',' + minutes + ',0';\nmsg.topic = 'rfm12b/out';\n\nreturn msg;","outputs":1,"x":397,"y":197,"z":"7733bcb0.88cc44","wires":[["36026f66.c9fd9"]]},{"id":"36026f66.c9fd9","type":"mqtt out","name":"RFM12B Sender","topic":"","broker":"ba386057.845d3","x":608,"y":225,"z":"7733bcb0.88cc44","wires":[]}]

As conclusion, I believe Node-RED is the future of IoT wiring. Do get started with it.

  (689)


Viewing all articles
Browse latest Browse all 328

Trending Articles