PhatFil
Posts: 602
Joined: Thu Apr 13, 2017 3:55 pm

3rd generation patio plant care system.

Wed Sep 12, 2018 6:10 am

Utilising Xiaomi miflora ble plant care sensors (circa 10-£15 from the usual far east portals) and a Sonoff/esp8266 controlled relay modules flashed with tasmota firmware controlling pumps, though could easily be valves, to ensure plants remain watered.

its 3rd gen because gen1 was an arduino and electro resistance measuring using the cheapest sensors which soon corroded even with vcc switched on for readings only. gen2 involved i2c capacitive sensors, but suffered diy cable issues with moisture ingress and was unreliable.

I was planning on using esp modules with batteries and the i2c sensors but the battery boards i bought with on board power regulators were pretty inefficient battery wise bleeding current even when the esp was sleeping.. aarrgghh and then i discovered the miflora sensors and the work done by Thom Dietrich, and nde red had been talked about in lists i frequent.. ;)
so we have this project.

Max number of sensors/controllers is only limited by the sensor polling rate,which may vary from location to location? currently 6 are employed (7x in the gui,, work in progress) the gui dispalys 4 of the 5 readings the sensors broadcast in graphs plotting the recent history.
1) Moisture level of soil as a %
2) Battery Level of the sensor as a %
3) Temperature measured by the sensor C
4) Electro conductivity indicating nutrient level /soil health as a numeric in the range 0-9999
the 5th metric from the sensor light level is of no current use to me and is ignored in this project currently though adding it in would be trivial
adding it to the gui on the other hand may not be so trivial.. footprint wise.

Users can set the MML Minimum moisture level for each of the sensors/plant areas in the gui and if the sensor reading drops below that threshold the plant will receive a timed dose of water (yuo have to test your own pumps/valves for flow rates im afraid..)
Using tasmota on the controller all you need do is issue the pulsetime command to determine how long the relay will latch on for when triggered. ie the command

Code: Select all

PulseTime 130
will configure the tasmota device to automatically switch off after 30 seconds when turned on.

i wont post the email node ;) if that interested you can add your own easily enough..

the gui
Image

the main flows
Image

and the gui flows

Image

I cant see how to attach a file to the post so if the flow text kills the code container dont blame me too much..

In order to run thses flows in node red you will need the dasboard and the node-red-contrib-xiaomi-ble both installing via the node red palette manager.
Main flows

Code: Select all

[{"id":"e92f49ff.42fec8","type":"Xiaomi BLE","z":"3780f1f9.595fce","name":"Miflora","address":"","scanningTimeout":60,"x":470,"y":180,"wires":[["d236d076.46815"]]},{"id":"173175c0.a9b83a","type":"function","z":"3780f1f9.595fce","name":"add msg.address","func":"//Requires:\n//a message in the format PlantX/payload where X\n// is the numeric indicator of the sensor to be queried\n// and the payload is the mac address of the deice to be queried.\n//Returns:\n// a message in the same format as recieved\n// after the message address property has been touched\n//(not quite sure whats needed here so msg.address is set to the mac addy too)\n//Results\n// no expected side effets beyond\n// the message topic identifying the sensor is \n// saved in the flow variable plant as te next node \n// overwrites the message topic and looses this crucial data.\nvar plant = flow.get('plant')||'PlantX';\nflow.set(\"plant\", msg.topic)\nmsg.address = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"x":310,"y":180,"wires":[["e92f49ff.42fec8"]]},{"id":"1caa0f41.5dbc11","type":"inject","z":"3780f1f9.595fce","name":"Plant1 inj","topic":"Plant1","payload":"C4:7C:8D:64:43:F5","payloadType":"str","repeat":"3600","crontab":"","once":true,"onceDelay":"","x":110,"y":80,"wires":[["173175c0.a9b83a"]]},{"id":"1ec32429.28c63c","type":"inject","z":"3780f1f9.595fce","name":"Plant2 inj","topic":"Plant2","payload":"C4:7C:8D:64:43:C8","payloadType":"str","repeat":"3600","crontab":"","once":true,"onceDelay":"300","x":110,"y":120,"wires":[["173175c0.a9b83a"]]},{"id":"bb86328e.39713","type":"inject","z":"3780f1f9.595fce","name":"Plant3 inj","topic":"Plant3","payload":"C4:7C:8D:64:43:6F","payloadType":"str","repeat":"3600","crontab":"","once":true,"onceDelay":"600","x":110,"y":160,"wires":[["173175c0.a9b83a"]]},{"id":"fe342446.9dce18","type":"inject","z":"3780f1f9.595fce","name":"Plant4 inj","topic":"Plant4","payload":"C4:7C:8D:64:47:A7","payloadType":"str","repeat":"3600","crontab":"","once":true,"onceDelay":"900","x":110,"y":200,"wires":[["173175c0.a9b83a"]]},{"id":"f1ce3ea5.b6ec6","type":"inject","z":"3780f1f9.595fce","name":"Plant5 inj","topic":"Plant5","payload":"C4:7C:8D:64:43:AE","payloadType":"str","repeat":"3600","crontab":"","once":true,"onceDelay":"1200","x":110,"y":240,"wires":[["173175c0.a9b83a"]]},{"id":"fd76dc24.e5778","type":"inject","z":"3780f1f9.595fce","name":"Plant6 inj","topic":"Plant6","payload":"C4:7C:8D:64:43:61","payloadType":"str","repeat":"3600","crontab":"","once":true,"onceDelay":"1500","x":110,"y":280,"wires":[["173175c0.a9b83a"]]},{"id":"d236d076.46815","type":"function","z":"3780f1f9.595fce","name":"Notification machine","func":"Requires:\n// A sensor id saved in the flow variable 'plant'\n// And a message with a payload containing Json tupples\n// of metric:reading\n// Returns\n// a set (array) of 4 messages with the topic drived from\n// both the plant/sensor id and the metric the data represents\n// in the format Notify/Plant/X(plantid)/metric\n//Results\n// no expected side effects\nvar plant = flow.get('plant')||'PlantY';\nvar part1 = plant.slice(0,5);\nvar part2 = plant.slice(-1);\nplant = part1 +'/' + part2;\nvar msg1 = { payload: msg.payload.moisture };\nvar msg2 = { payload: msg.payload.battery };\nvar msg3 = { payload: msg.payload.temperature };\nvar msg4 = { payload: msg.payload.conductivity };\n//moisture msg1\n//msg1.payload =  msg.payload.moisture;\nmsg1.topic = 'Notify/'+ plant +'/M';\n// battery msg2\n//msg2.payload = msg.payload.battery;\nmsg2.topic = 'Notify/'+ plant +'/B';\n// Temp msg3\n//msg3.payload = msg.payload.temperature; //.payload.temperature;\nmsg3.topic = 'Notify/'+ plant +'/T';\n//EC msg4\n//msg4.payload = msg.payload.conductivity;\nmsg4.topic = 'Notify/'+ plant +'/EC';\nreturn [[msg1,msg2,msg3,msg4]]","outputs":1,"noerr":0,"x":640,"y":180,"wires":[["4272f32c.02468c"]]},{"id":"4272f32c.02468c","type":"mqtt out","z":"3780f1f9.595fce","name":"Notify!","topic":"","qos":"","retain":"","broker":"6b77dcc0.90e0c4","x":810,"y":180,"wires":[]},{"id":"d22587cd.bfed08","type":"mqtt in","z":"3780f1f9.595fce","name":"","topic":"Notify/Plant/#","qos":"2","broker":"6b77dcc0.90e0c4","x":290,"y":280,"wires":[["8936e1e.b30cd2"]]},{"id":"8936e1e.b30cd2","type":"function","z":"3780f1f9.595fce","name":"Warnings Generator","func":"//WORK IN PROGRESS\n// only looking at sensors atm, other notifications to be catered for later\n// Battery, temperature, and EC warning levels are\n// hard coded until moisture gui is sorted..  \n//Requires;\n// a notification message, its topic prefixed with 'Notify/'\n// Sensor data:\n//  sensor data topics will follow 'Notify/' with 'Plant/'\n// the next 2 topic keys are the sensor/plant area number and the metric code\n// the payload is the data referenced by the topic\n//Returns:\n// any message recieved failing any conditions tested a suitable\n// warning/error/action message will be issued.\n//Results:\n// no unexpected side effects\nvar plantid = msg.topic.split(\"/\")[2];\nvar metric = msg.topic.split(\"/\")[3];\nswitch(metric) {\n    case 'M': // moisture\n    var pxmml = 'P' + plantid + 'MML';\n    var mml = global.get(pxmml)||30;\n    if (msg.payload < mml) {\n        msg.topic = 'Action/Water';\n        msg.payload = plantid; \n        return msg;\n        }\n        break;\n    case 'B': //Battery level\n    if(msg.payload < 15){\n        msg.topic = 'Warning/Plants/' + plantid + '/Low battery level';\n        return msg;\n        }\n        break;\n    case 'T': // Temperature\n    if(msg.payload < 10){\n        msg.topic = 'Warning/Plants/' + plantid + '/Low Temperature';\n        return msg;\n        }\n    if(msg.payload > 35) {\n        msg.topic = 'Warning/Plants/' + plantid + '/High Temperature';\n        return msg;\n    }\n    break;\n    case 'EC': // EC\n    if(msg.payload < 150) {\n        msg.topic = 'Warning/Plants/' + plantid + '/Low EC Level';\n        return msg;\n        }\n    break;\n    default:\n    msg,topic = 'Happy/Happy';\n    msg.payload ='Joy/Joy';\n    return msg;\n}\n","outputs":1,"noerr":0,"x":480,"y":280,"wires":[["b736068f.4ecef8"]]},{"id":"b736068f.4ecef8","type":"mqtt out","z":"3780f1f9.595fce","name":"Issue warnings etc","topic":"","qos":"","retain":"","broker":"6b77dcc0.90e0c4","x":710,"y":280,"wires":[]},{"id":"5e96f51d.3a77ec","type":"mqtt in","z":"3780f1f9.595fce","name":"Respond To Water Action Messages","topic":"Action/Water","qos":"2","broker":"4405e653.095428","x":160,"y":360,"wires":[["63933883.be31b8","10df2125.c3f84f"]]},{"id":"63933883.be31b8","type":"function","z":"3780f1f9.595fce","name":"Water indicated area","func":"msg.topic = 'tasmota/sonoff-mypump' + msg.payload + '/cmnd/Power1';\nmsg.payload  = 1;\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":360,"wires":[["cb0cc82a.7a7698"]]},{"id":"cb0cc82a.7a7698","type":"mqtt out","z":"3780f1f9.595fce","name":"Issue water command","topic":"","qos":"","retain":"","broker":"6b77dcc0.90e0c4","x":700,"y":360,"wires":[]},{"id":"431ef56b.60f91c","type":"mqtt in","z":"3780f1f9.595fce","name":"Respond to warnings issued","topic":"Warning/Plants/#","qos":"2","broker":"6b77dcc0.90e0c4","x":140,"y":440,"wires":[["10df2125.c3f84f"]]},{"id":"10df2125.c3f84f","type":"function","z":"3780f1f9.595fce","name":"construct email warning","func":"msg.topic = msg.topic + ' ' + msg.payload ;\nmsg.payload = 'Dear Sucker,  your getting this cos [email protected] has you on the naughty list';\nreturn msg;","outputs":1,"noerr":0,"x":410,"y":440,"wires":[["b292f17e.a76bf"]]},{"id":"6b77dcc0.90e0c4","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"4405e653.095428","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""}]

Gui flows

Code: Select all

[{"id":"5273aba.1064054","type":"ui_chart","z":"bfb29f2b.4dfdb","name":"Moisture","group":"1c127556.33317b","order":3,"width":"6","height":"8","label":"Moisture","chartType":"line","legend":"true","xformat":"auto","interpolate":"linear","nodata":"waiting","dot":true,"ymin":"0","ymax":"100","removeOlder":1,"removeOlderPoints":"1000","removeOlderUnit":"604800","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":880,"y":100,"wires":[["feed02cb.155b1"],[]],"outputLabels":["persistant store",""]},{"id":"feed02cb.155b1","type":"persist in","z":"bfb29f2b.4dfdb","name":"PlantMoisture","storageNode":"742e2b82.7baaf4","x":1110,"y":80,"wires":[]},{"id":"eba5ad37.e8c54","type":"persist out","z":"bfb29f2b.4dfdb","name":"PlantMoisture","storageNode":"742e2b82.7baaf4","x":530,"y":100,"wires":[["5273aba.1064054"]]},{"id":"a1883743.366ff8","type":"ui_chart","z":"bfb29f2b.4dfdb","name":"","group":"1c127556.33317b","order":2,"width":"6","height":"8","label":"Batery Level %","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":true,"ymin":"0","ymax":"106","removeOlder":1,"removeOlderPoints":"1000","removeOlderUnit":"604800","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":900,"y":200,"wires":[["272921df.3e0fde"],[]],"outputLabels":["persistant store",""]},{"id":"272921df.3e0fde","type":"persist in","z":"bfb29f2b.4dfdb","name":"BatteryLevel","storageNode":"cf36a9a7.43f468","x":1110,"y":200,"wires":[]},{"id":"4e08b7cd.602c88","type":"persist out","z":"bfb29f2b.4dfdb","name":"BatteryLevel","storageNode":"cf36a9a7.43f468","x":530,"y":200,"wires":[["a1883743.366ff8"]]},{"id":"ac0f4422.925ad8","type":"ui_chart","z":"bfb29f2b.4dfdb","name":"","group":"1c127556.33317b","order":4,"width":"6","height":"8","label":"Temperature","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":true,"ymin":"5","ymax":"55","removeOlder":1,"removeOlderPoints":"1000","removeOlderUnit":"604800","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":890,"y":420,"wires":[["ce6ac2f3.9972"],[]],"outputLabels":["persistant store",""]},{"id":"ce6ac2f3.9972","type":"persist in","z":"bfb29f2b.4dfdb","name":"PlantTemperature","storageNode":"1af6d261.bce04e","x":1130,"y":400,"wires":[]},{"id":"b06f9ce9.4cb4a","type":"persist out","z":"bfb29f2b.4dfdb","name":"PlantTemperature","storageNode":"1af6d261.bce04e","x":570,"y":420,"wires":[["ac0f4422.925ad8"]]},{"id":"9e61d6d2.1393c8","type":"ui_chart","z":"bfb29f2b.4dfdb","name":"","group":"1c127556.33317b","order":12,"width":"6","height":"8","label":"EC","chartType":"line","legend":"true","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":true,"ymin":"0","ymax":"5555","removeOlder":1,"removeOlderPoints":"1000","removeOlderUnit":"604800","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":true,"x":850,"y":520,"wires":[["fd023fba.c9aff"],[]],"outputLabels":["persistant store",""]},{"id":"fd023fba.c9aff","type":"persist in","z":"bfb29f2b.4dfdb","name":"Plant EC","storageNode":"79b04df0.19e724","x":1100,"y":500,"wires":[]},{"id":"5f1d4002.6a16a","type":"persist out","z":"bfb29f2b.4dfdb","name":"PlantEC","storageNode":"79b04df0.19e724","x":540,"y":520,"wires":[["9e61d6d2.1393c8"]]},{"id":"26091172.178f5e","type":"ui_slider","z":"bfb29f2b.4dfdb","name":"Plant3 Minimum moisture Level","label":"Plant3 Minimum moisture Level","group":"1c127556.33317b","order":9,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant3M","min":"20","max":"40","step":1,"x":160,"y":320,"wires":[["70b5be97.cb7fb"]]},{"id":"46500131.c4f9c","type":"ui_numeric","z":"bfb29f2b.4dfdb","name":"","label":"P3MML","group":"1c127556.33317b","order":10,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant3M","format":"{{value}}%","min":"20","max":"40","step":1,"x":150,"y":400,"wires":[["70b5be97.cb7fb"]]},{"id":"910b8ade.039318","type":"ui_slider","z":"bfb29f2b.4dfdb","name":"Plant4 Minimum moisture Level","label":"Plant4 Minimum moisture Level","group":"1c127556.33317b","order":11,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant4M","min":"20","max":"40","step":1,"x":160,"y":440,"wires":[["f1a18e69.217e4"]]},{"id":"d30baf73.5abd4","type":"ui_numeric","z":"bfb29f2b.4dfdb","name":"","label":"P4MML","group":"1c127556.33317b","order":13,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant4M","format":"{{value}}%","min":"20","max":"40","step":1,"x":170,"y":520,"wires":[["f1a18e69.217e4"]]},{"id":"b9e74a71.a995f8","type":"ui_slider","z":"bfb29f2b.4dfdb","name":"Plant7 Minimum moisture Level","label":"Plant7 Minimum moisture Level","group":"1c127556.33317b","order":18,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant7M","min":"20","max":"40","step":1,"x":180,"y":800,"wires":[["b3c9c5ec.6c4a18"]],"inputLabels":["global.get('P7MML')"]},{"id":"63597afe.3ed714","type":"ui_numeric","z":"bfb29f2b.4dfdb","name":"","label":"P7MML","group":"1c127556.33317b","order":19,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant7M","format":"{{value}}%","min":"20","max":"40","step":1,"x":170,"y":880,"wires":[["b3c9c5ec.6c4a18"]]},{"id":"f371b872.9806d8","type":"ui_slider","z":"bfb29f2b.4dfdb","name":"Plant2 Minimum moisture Level","label":"Plant2 Minimum moisture Level","group":"1c127556.33317b","order":7,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant2M","min":"20","max":"40","step":1,"x":180,"y":200,"wires":[["22acbc7a.4bc224"]]},{"id":"e5460d9a.0f381","type":"ui_numeric","z":"bfb29f2b.4dfdb","name":"","label":"P2MML","group":"1c127556.33317b","order":8,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant2M","format":"{{value}}%","min":"20","max":"40","step":1,"x":170,"y":280,"wires":[["22acbc7a.4bc224"]]},{"id":"ae64b87a.d20a18","type":"ui_slider","z":"bfb29f2b.4dfdb","name":"Plant1 Minimum moisture Level","label":"Plant1 Minimum moisture Level","group":"1c127556.33317b","order":5,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant1M","min":"20","max":"40","step":1,"x":200,"y":60,"wires":[["7fcb1e11.1610c"]]},{"id":"d18edff1.488c6","type":"ui_numeric","z":"bfb29f2b.4dfdb","name":"","label":"P1MML","group":"1c127556.33317b","order":6,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant1M","format":"{{value}}%","min":"20","max":"40","step":1,"x":190,"y":140,"wires":[["7fcb1e11.1610c"]]},{"id":"4b391909.01cad8","type":"ui_slider","z":"bfb29f2b.4dfdb","name":"Plant6 Minimum moisture Level","label":"Plant6 Minimum moisture Level","group":"1c127556.33317b","order":16,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant6M","min":"20","max":"40","step":1,"x":160,"y":680,"wires":[["9b054b89.15cae8"]]},{"id":"472af8f5.767a08","type":"ui_numeric","z":"bfb29f2b.4dfdb","name":"","label":"P6MML","group":"1c127556.33317b","order":17,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant6M","format":"{{value}}%","min":"20","max":"40","step":1,"x":150,"y":760,"wires":[["9b054b89.15cae8"]]},{"id":"366692b0.0f1ebe","type":"ui_slider","z":"bfb29f2b.4dfdb","name":"Plant5 Minimum moisture Level","label":"Plant5 Minimum moisture Level","group":"1c127556.33317b","order":14,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant5M","min":"20","max":"40","step":1,"x":160,"y":560,"wires":[["3abe2867.e66318"]]},{"id":"8b1e2b28.1b09e8","type":"ui_numeric","z":"bfb29f2b.4dfdb","name":"","label":"P5MML","group":"1c127556.33317b","order":15,"width":"6","height":"1","passthru":false,"topic":"Dashboard/Settings/Plant5M","format":"{{value}}%","min":"20","max":"40","step":1,"x":170,"y":640,"wires":[["3abe2867.e66318"]]},{"id":"7fcb1e11.1610c","type":"function","z":"bfb29f2b.4dfdb","name":"Update Plant1 Minimum Moisture Level","func":"//Requires:\n// a numeric payload from a gui tool eiter a Numeric\n// display or a slider which is the new Minimum\n// Moisture Level requiered for Plant area 1\n//Returns:\n// the same payload delivered to both gui tools\n// to ensure both are displaying the same value.\n//Results:\n// no expected side effets beyond\n// global variable P1MML representing the Minimum Moisture \n// Level (MML) to be used for conditional tests on \n// other flows\nvar P1MML = global.get('P1MML')||30;\nglobal.set('P1MML',msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":190,"y":100,"wires":[["d18edff1.488c6","ae64b87a.d20a18"]]},{"id":"22acbc7a.4bc224","type":"function","z":"bfb29f2b.4dfdb","name":"Update Plant2 Minimum Moisture Level","func":"//Requires:\n// a numeric payload from a gui tool eiter a Numeric\n// display or a slider which is the new Minimum\n// Moisture Level requiered for Plant area 2\n//Returns:\n// the same payload delivered to both gui tools\n// to ensure both are displaying the same value.\n//Results:\n// no expected side effets beyond\n// global variable P2MML representing the Minimum Moisture \n// Level (MML) to be used for conditional tests on \n// other flows\nvar P2MML = global.get('P2MML')||30;\nglobal.set('P2MML',msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":180,"y":240,"wires":[["f371b872.9806d8","e5460d9a.0f381"]]},{"id":"70b5be97.cb7fb","type":"function","z":"bfb29f2b.4dfdb","name":"Update Plant3 Minimum Moisture Level","func":"//Requires:\n// a numeric payload from a gui tool eiter a Numeric\n// display or a slider which is the new Minimum\n// Moisture Level requiered for Plant area 3\n//Returns:\n// the same payload delivered to both gui tools\n// to ensure both are displaying the same value.\n//Results:\n// no expected side effets beyond\n// global variable P3MML representing the Minimum Moisture \n// Level (MML) to be used for conditional tests on \n// other flows\nvar P3MML = global.get('P3MML')||30;\nglobal.set('P3MML',msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":360,"wires":[["26091172.178f5e","46500131.c4f9c"]]},{"id":"3abe2867.e66318","type":"function","z":"bfb29f2b.4dfdb","name":"Update Plant5 Minimum Moisture Level","func":"//Requires:\n// a numeric payload from a gui tool eiter a Numeric\n// display or a slider which is the new Minimum\n// Moisture Level requiered for Plant area 5\n//Returns:\n// the same payload delivered to both gui tools\n// to ensure both are displaying the same value.\n//Results:\n// no expected side effets beyond\n// global variable P5MML representing the Minimum Moisture \n// Level (MML) to be used for conditional tests on \n// other flows\nvar P5MML = global.get('P5MML')||30;\nglobal.set('P5MML',msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":600,"wires":[["8b1e2b28.1b09e8","366692b0.0f1ebe"]]},{"id":"f1a18e69.217e4","type":"function","z":"bfb29f2b.4dfdb","name":"Update Plant4 Minimum Moisture Level","func":"//Requires:\n// a numeric payload from a gui tool eiter a Numeric\n// display or a slider which is the new Minimum\n// Moisture Level requiered for Plant area 4\n//Returns:\n// the same payload delivered to both gui tools\n// to ensure both are displaying the same value.\n//Results:\n// no expected side effets beyond\n// global variable P4MML representing the Minimum Moisture \n// Level (MML) to be used for conditional tests on \n// other flows\nvar P4MML = global.get('P4MML')||30;\nglobal.set('P4MML',msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":480,"wires":[["910b8ade.039318","d30baf73.5abd4"]]},{"id":"9b054b89.15cae8","type":"function","z":"bfb29f2b.4dfdb","name":"Update Plant6 Minimum Moisture Level","func":"//Requires:\n// a numeric payload from a gui tool eiter a Numeric\n// display or a slider which is the new Minimum\n// Moisture Level requiered for Plant area 6\n//Returns:\n// the same payload delivered to both gui tools\n// to ensure both are displaying the same value.\n//Results:\n// no expected side effets beyond\n// global variable P6MML representing the Minimum Moisture \n// Level (MML) to be used for conditional tests on \n// other flows\nvar P6MML = global.get('P6MML')||30;\nglobal.set('P6MML',msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":720,"wires":[["4b391909.01cad8","472af8f5.767a08"]]},{"id":"b3c9c5ec.6c4a18","type":"function","z":"bfb29f2b.4dfdb","name":"Update Plant7 Minimum Moisture Level","func":"//Requires:\n// a numeric payload from a gui tool eiter a Numeric\n// display or a slider which is the new Minimum\n// Moisture Level requiered for Plant area 7\n//Returns:\n// the same payload delivered to both gui tools\n// to ensure both are displaying the same value.\n//Results:\n// no expected side effets beyond\n// global variable P7MML representing the Minimum Moisture \n// Level (MML) to be used for conditional tests on \n// other flows\nvar P7MML = global.get('P7MML')||30;\nglobal.set('P7MML',msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":840,"wires":[["63597afe.3ed714","b9e74a71.a995f8"]],"inputLabels":["P7MML"]},{"id":"c178677e.a544f8","type":"ui_text","z":"bfb29f2b.4dfdb","group":"1c127556.33317b","order":1,"width":"6","height":"2","name":"","label":"Plant Status Display","format":"{{msg.payload}}","layout":"col-center","x":710,"y":40,"wires":[]},{"id":"4c2d9bba.a69504","type":"mqtt in","z":"bfb29f2b.4dfdb","name":"Plant Notifications","topic":"Notify/Plant/#","qos":"2","broker":"6b77dcc0.90e0c4","x":440,"y":300,"wires":[["349c69fc.b05046"]]},{"id":"349c69fc.b05046","type":"function","z":"bfb29f2b.4dfdb","name":"order plant metrics","func":"//Requires:\n// message with a numeric payload and topic in the \n// format Notify/Plant/X/Y where X ia a numeric id of \n// a sensor/plant area, and Y is a code indicating the\n// metric the payload represents\n//Returns:\n// a message in the format\n// PlantX\\payload sent to the graph as indicated\n// by the metric code the payload containing the data\n//Results:\n//  no expected side effects beyond\n// the gelevant graphs should get updated with \n// the new data.\nvar plant =  msg.topic.split(\"/\")[2];\nvar Metric =  msg.topic.split(\"/\")[3];\nmsg.topic = msg.topic.split(\"/\")[1]+ plant;\n// 4 x outputs 1 moisture metric = M\n// Battery level metric = B\n// Temperature metric = T\n// EC Metric = EC\n// return[msg1,msg2,msg3,msg4]\nswitch(Metric) {\n    case 'M': // moisture\n        return [msg,null,null,null]\n        break;\n    case 'B': //attery level\n         return [null,msg,null,null]\n        break;\n    case 'T': // Temperature\n        return [null,null,msg,null]\n        break;\n    case 'EC': // EC\n         return [null,null,null,msg];\n        break;\n    default:\n    return msg;\n}","outputs":4,"noerr":0,"x":630,"y":300,"wires":[["5273aba.1064054"],["a1883743.366ff8"],["ac0f4422.925ad8"],["9e61d6d2.1393c8"]],"outputLabels":["Mosture","Battery Level","Temperature","EC"]},{"id":"1c127556.33317b","type":"ui_group","z":"","name":"Overview","tab":"6152efc3.75cfe","order":2,"disp":false,"width":"12","collapse":false},{"id":"742e2b82.7baaf4","type":"persist-store","z":"","filename":"plantmoisture1.json","interval":"3600"},{"id":"cf36a9a7.43f468","type":"persist-store","z":"","filename":"Batterylevel.json","interval":"3600"},{"id":"1af6d261.bce04e","type":"persist-store","z":"","filename":"PlantTemp1.json","interval":"3600"},{"id":"79b04df0.19e724","type":"persist-store","z":"","filename":"PlantEC1.json","interval":"3600"},{"id":"6b77dcc0.90e0c4","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"6152efc3.75cfe","type":"ui_tab","z":"","name":"Patio Plant Monitor","icon":"dashboard","order":1}]
Enjoy..

all i have done is spent a few days (long days) learning node red and i am still a beginner with node.js so please do criticise the code with improvement suggestions and examples but be kind and patient. Most of the work was done by others like Thom mentioned above, check out his awsome project https://github.com/ThomDietrich/miflora-mqtt-daemon
and as for the authors of the super sexy nodered-contrib-xiaomi-ble node you my friends are code warriors of the highest order, i never reached the rank above code monkey myself so i just stand in awe.. ;)

PhatFil
Posts: 602
Joined: Thu Apr 13, 2017 3:55 pm

Re: 3rd generation patio plant care system.

Wed Sep 12, 2018 6:12 am

Btw the Pi is a pi-zeroW headless hosting node red and mosquitto mqtt broker. how could i forget the Pi in here of all places too. :oops:

Also all 6 x sensors are sat in the same pot atm so i can 'calibrate them' there seems to be a significant difference in the moisture readings taken, but the difference seems to be consistent, this is an ongong project ;)

PhatFil
Posts: 602
Joined: Thu Apr 13, 2017 3:55 pm

Re: 3rd generation patio plant care system.

Thu Sep 13, 2018 9:43 am

To add additional sensors/pumps/areas:
add an additional injection node the payload of which is the mac address of the ble sensor

Code: Select all

sudo hcitool lescan
will list all ble devices within range of your pi so you can determine the mac addy of your sensors.

the Topic of the node is the name of your sensor ended with a number. the number will be the pump/sensor id..
currently installed devices are named Plant1 thru to Plant6.

To avoid polling collisions each sensor is triggered 5minutes after the last (add 300 seconds to the delay of the previous injection node. and set it to repeat every hour.

with this set up you can add upto 12 plant areas/sensors befor you need revisit this scheduling.


to action the device ie turn on pump or open valve for watering in Tasmota name your pumps/valves, PumpX, where Xis the same number as the sensor you have associated with the same area/plant.

to use a Gmail account to send warning and action emails to inform you of any watering and any conditions outside optimum the gmail a/c must have pop and imap enabled, and also must have 'less secure app access', google can walk you through that better than i
when posted on github i will link in here...

rickyh89
Posts: 55
Joined: Mon Jun 18, 2018 11:01 pm

Re: 3rd generation patio plant care system.

Thu Sep 13, 2018 9:28 pm

its killing me i only have 1 sensor lol... im dying to make good python script to attach a variable to each sensor reading. You made alot of progress since couple days ago.

PhatFil
Posts: 602
Joined: Thu Apr 13, 2017 3:55 pm

Re: 3rd generation patio plant care system.

Thu Sep 13, 2018 11:07 pm

Cheers, getting there, found a few limitations of node red and even more in myself. have cracked saving everything between system shutdowns/crashes (tho in the latter case 1 set of data records could get lost.

one thing of concern though not found when using Thoms python logger is the xiaomi ble node can fail to successfully poll a device for quite a while (repeatedly so a day with no data from a sensor..), however when stimulated by a test injection the data is forthcoming. so some sort of test for a result and retry at least twice may be in order.. Thoms script certainly took that approach.
I could always point my engine back at thoms output, thats a simple change of flow input from the injection nods to a single mqtt in node ;)

also i am running on very low batteries this is intentional to determine just what is the minimum battery level and could be a contributing factor.

I am considring a new gui, basically the same as is but 4 x 1 per metric measured, in each case Moisture Temp Battery and EC all 4 graphs will be displayed But the metric in focus's chart will be much larger, the other 3 much smaller only visible for a casual trend glance,
That way the limit setting controls for each metric wont be fighting for screen real estate with the graphs.. tho im still mulling that over..

Return to “Automation, sensing and robotics”