Garden Monitoring and Watering

Hey everyone!

Wanted to share my most recent project, a garden monitoring and watering system. Still lots of room to grow the project, but it's starting to take root (ha... I'm so sorry).

The system consists of a Raspberry Pi, an ESP32, and an ESP8266. All three are connected to my home Wifi and communicating through MQTT.

The ESP32 is battery / solar powered and embedded in the garden. In between periods of deep sleep it wakes and collects data, then transmits back to Node-RED. Node-RED parses the data and stores it in an Influxdb database. The main user interaction with the data is through Grafana.

The ESP8266 has a single purpose. When it receives an MQTT signal from node-red it turns the garden watering system on. It also transmits a message back to Node-Red indicating valve Status.

Planned but not implemented is a connection to openweathermap to allow Node-RED to use feed-forward control when watering if rain is expected.

Let me know your thoughts! Is there something you'd recommend changing direction on while the project is still young?

One issue I'm running in to now is intermittent MQTT message receipt. You can see gaps in the database where sensor data didn't make it back to Node-RED. Likewise, the control messages for the watering valve and LED's seem to be 'lost' about 50% of the time. If I continue to cycle LED's or the valve control from the UI they eventually actuate.

The next planned step is a failsafe to ensure the garden isn't flooded one day when I return from work.

Here are some pictures, enjoy!









4 Likes

Zach,
What are you using on the ESP32 and ESP8266?

@dynamicdave has a great circuit to cut off the power to any i@c devices to save battery power when you go into deep sleep. see Solar-powered weather station

One thing you could do to increase the WiFi signal is to use a WeMos Pro with an external antenna, though you have to do a tricky solder job to move a tiny resister.

Love to see your flow as I've just done something similar (great minds think alike). I have built a PCB board holdng a WeMos D1 Mini pro (running ESPEasy), li-ion battery, INA219 to connect to a Solar panel, a voltage divider to measure the battery voltage, the power saver circuit, a BME280 and a ADS1115 to report the solar power level and battery voltage and a I2C header for any other devices I might add (luminosity sensor??).

And it is also storing the data in Influx and using Grafana.

1 Like

Nice project!

Have you investigated the cause for the loss of the WiFi messages: distance? power consumption?

I’m using sub-1GHz radio for a similar purpose where distance and power consumption are critical.

The protocol is less polished than MQTT, no QoS for example, and messages are plain JSON, encrypted.

I'm also using RF for battery powered sensors, because of the wifi battery drain.
Despite sleeping between data packets the battery drain is much more with wifi.

Using the RFM69CW modules, I'm getting over 2 years life from 2xAA battery outdoor sensors which reliably update every 5 minutes.

Great project,

For your dataloss you can play with the qos of mqtt, With 1 or higher and not too much messages, mqtt cliënts have a max of 5000 buffer or so it should resolve it.

For the flooding, my solonoids have a max runtime. For the ESP you should give a long Pulse command so it runs for an amount of time and stops even when there is no connection. I learned the hard way, after flooding my garden.

I have integrated it with buienradar (dutch weather site) but you also want to know how much it rained. Connecting a tipping bucket to Node red is a Nice project.

This comes at a good time for me as I have been tasked with creating an auto-watering scheme for our herb garden (a raised bed arrangement of maybe 1.5m^2) and some flower pots. Fortunately they are all near sources of both water and power. I have a spare sprinkler valve and lots of ESP8266 boards lying about. My situation is considerably simpler than yours but being an engineer, I will gravitate to overcomplexity whenever possible.

1 Like

Nice work.

I've not deployed an esp32 like this yet, only esp07. Do you have stats on the solar charge cycle and components you have used (size of solar panels, batteries, charge circuitry etc)?

Thanks.

Is that outdoor unit in the garden a commercially built system or 3d printed ? Either way i would probably paint it (white to reflect some heat and give it a bit of a better lifetime) - i know here in Australia some 3d printed stuff i have made does not do well in our summer heat on an ongoing basis.

Presumaby you have power to your external solenoid - why not try to put a cheap wifi repeater out there ? One of the cheap TPlink units that you could then hardwire back to your home network ? It would also allow you to setup a seperate wireless network for your IOT stuff and implement some physical level security

Craig

I like this idea. Battery life is pretty low right now, about 24 hours even with deep sleep. Still waiting on my solar panels from Banggood as well. I see some soldering in my future.

No problem. I'm happy to share what I put together. You'll have to take it with a grain of salt though, if I knew I was having visitors I would've tidied up a little more first.

Node-Red Code:

[{"id":"86d554f6.5fdbd8","type":"tab","label":"Main Dashboard Flow","disabled":false,"info":""},{"id":"ba961cc5.4ee78","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"350b7c13.5b48d4","type":"ui_group","z":"","name":"Current Values","tab":"30c2d97f.8f39f6","disp":true,"width":"6","collapse":false},{"id":"30c2d97f.8f39f6","type":"ui_tab","z":"","name":"Dashboard","icon":"dashboard"},{"id":"ea1cc9df.c0bfe8","type":"ui_base","theme":{"name":"theme-dark","lightTheme":{"default":"#0094CE","baseColor":"#0094CE","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif","edited":true,"reset":false},"darkTheme":{"default":"#097479","baseColor":"#097479","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif","edited":true,"reset":false},"customTheme":{"name":"Untitled Theme 1","default":"#4B7930","baseColor":"#4B7930","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"},"themeState":{"base-color":{"default":"#097479","value":"#097479","edited":false},"page-titlebar-backgroundColor":{"value":"#097479","edited":false},"page-backgroundColor":{"value":"#111111","edited":false},"page-sidebar-backgroundColor":{"value":"#000000","edited":false},"group-textColor":{"value":"#0eb8c0","edited":false},"group-borderColor":{"value":"#555555","edited":false},"group-backgroundColor":{"value":"#333333","edited":false},"widget-textColor":{"value":"#eeeeee","edited":false},"widget-backgroundColor":{"value":"#097479","edited":false},"widget-borderColor":{"value":"#333333","edited":false},"base-font":{"value":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"}},"angularTheme":{"primary":"indigo","accents":"blue","warn":"red","background":"grey"}},"site":{"name":"Node-RED Dashboard","hideToolbar":"false","allowSwipe":"false","lockMenu":"false","allowTempTheme":"true","dateFormat":"DD/MM/YYYY","sizes":{"sx":48,"sy":48,"gx":6,"gy":6,"cx":6,"cy":6,"px":0,"py":0}}},{"id":"302749d6.c240f6","type":"ui_group","z":"","name":"Historic Values","tab":"30c2d97f.8f39f6","disp":true,"width":"6","collapse":false},{"id":"835618b3.589538","type":"ui_group","z":"","name":"Controls","tab":"30c2d97f.8f39f6","disp":true,"width":"6","collapse":false},{"id":"549f8b8f.304664","type":"influxdb","z":"","hostname":"127.0.0.1","port":"8086","protocol":"http","database":"garden","name":"","usetls":false,"tls":""},{"id":"f6560d10.f0974","type":"mqtt in","z":"86d554f6.5fdbd8","name":"","topic":"esp32/soilmoisture","qos":"2","datatype":"auto","broker":"ba961cc5.4ee78","x":370,"y":380,"wires":[["f615d6c8.c22ab8","e59fa17d.d62c4"]]},{"id":"1859c7fd.0b4338","type":"mosca in","z":"86d554f6.5fdbd8","mqtt_port":1883,"mqtt_ws_port":8080,"name":"","username":"","password":"","dburl":"","x":220,"y":120,"wires":[[]]},{"id":"a86bc8b3.f393a8","type":"ui_gauge","z":"86d554f6.5fdbd8","name":"","group":"350b7c13.5b48d4","order":1,"width":0,"height":0,"gtype":"gage","title":"Soil Moisture","label":"units","format":"{{value}}","min":0,"max":"100","colors":["#b40501","#e6e600","#1c02ff"],"seg1":"","seg2":"","x":1470,"y":280,"wires":[]},{"id":"89954073.c498d","type":"mqtt in","z":"86d554f6.5fdbd8","name":"","topic":"esp32/temperaturez","qos":"2","datatype":"auto","broker":"ba961cc5.4ee78","x":370,"y":620,"wires":[["74e04a61.7a8f14","fa5fbfa8.5b656"]]},{"id":"74e04a61.7a8f14","type":"ui_gauge","z":"86d554f6.5fdbd8","name":"","group":"350b7c13.5b48d4","order":2,"width":0,"height":0,"gtype":"gage","title":"Temperature","label":"Fahrenheit","format":"{{value}}","min":0,"max":"100","colors":["#b40501","#e6e600","#1c02ff"],"seg1":"","seg2":"","x":696,"y":533,"wires":[]},{"id":"23536536.40855a","type":"mqtt in","z":"86d554f6.5fdbd8","name":"","topic":"esp32/pressure","qos":"2","datatype":"auto","broker":"ba961cc5.4ee78","x":360,"y":840,"wires":[["73b646c9.4abf38","fdab1d40.4613c"]]},{"id":"73b646c9.4abf38","type":"ui_gauge","z":"86d554f6.5fdbd8","name":"","group":"350b7c13.5b48d4","order":3,"width":0,"height":0,"gtype":"gage","title":"Pressure","label":"inHg","format":"{{value}}","min":0,"max":"100","colors":["#b40501","#e6e600","#1c02ff"],"seg1":"","seg2":"","x":686,"y":753,"wires":[]},{"id":"2eb176d4.4b510a","type":"mqtt in","z":"86d554f6.5fdbd8","name":"","topic":"esp32/humidity","qos":"2","datatype":"auto","broker":"ba961cc5.4ee78","x":360,"y":1060,"wires":[["214b5391.d4325c","e3da261b.e896a8"]]},{"id":"214b5391.d4325c","type":"ui_gauge","z":"86d554f6.5fdbd8","name":"","group":"350b7c13.5b48d4","order":4,"width":0,"height":0,"gtype":"gage","title":"Humidity","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#b40501","#e6e600","#1c02ff"],"seg1":"","seg2":"","x":686,"y":973,"wires":[]},{"id":"24a0a605.3ff4ba","type":"influxdb out","z":"86d554f6.5fdbd8","influxdb":"549f8b8f.304664","name":"","measurement":"SoilMoisture","precision":"","retentionPolicy":"","x":1540,"y":380,"wires":[]},{"id":"6b0e8611.66b458","type":"influxdb out","z":"86d554f6.5fdbd8","influxdb":"549f8b8f.304664","name":"","measurement":"Temperature","precision":"","retentionPolicy":"","x":1540,"y":620,"wires":[]},{"id":"7d5b50e6.7d767","type":"influxdb out","z":"86d554f6.5fdbd8","influxdb":"549f8b8f.304664","name":"","measurement":"Pressure","precision":"","retentionPolicy":"","x":1530,"y":840,"wires":[]},{"id":"e83ccfe3.b617a","type":"influxdb out","z":"86d554f6.5fdbd8","influxdb":"549f8b8f.304664","name":"","measurement":"Humidity","precision":"","retentionPolicy":"","x":1530,"y":1060,"wires":[]},{"id":"f615d6c8.c22ab8","type":"function","z":"86d554f6.5fdbd8","name":"String to Number & Store Variable","func":"msg.payload=Number(msg.payload);\n\nflow.set('soilmoisture',msg.payload)\n\nreturn msg;","outputs":1,"noerr":0,"x":840,"y":380,"wires":[["2911a032.441f3"]]},{"id":"fa5fbfa8.5b656","type":"function","z":"86d554f6.5fdbd8","name":"String to Number","func":"msg.payload=Number(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":620,"wires":[["b7fc79e9.1e5b98"]]},{"id":"fdab1d40.4613c","type":"function","z":"86d554f6.5fdbd8","name":"String to Number","func":"msg.payload=Number(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":840,"wires":[["ee141b3a.2da9c8"]]},{"id":"e3da261b.e896a8","type":"function","z":"86d554f6.5fdbd8","name":"String to Number","func":"msg.payload=Number(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":1060,"wires":[["c5035494.e74f68"]]},{"id":"2911a032.441f3","type":"switch","z":"86d554f6.5fdbd8","name":"Message Bounds","property":"payload","propertyType":"msg","rules":[{"t":"btwn","v":"0","vt":"num","v2":"100","v2t":"num"}],"checkall":"true","repair":false,"outputs":1,"x":1140,"y":380,"wires":[["24a0a605.3ff4ba","a86bc8b3.f393a8"]]},{"id":"b7fc79e9.1e5b98","type":"switch","z":"86d554f6.5fdbd8","name":"Message Bounds","property":"payload","propertyType":"msg","rules":[{"t":"btwn","v":"-40","vt":"num","v2":"120","v2t":"num"}],"checkall":"true","repair":false,"outputs":1,"x":1130,"y":620,"wires":[["6b0e8611.66b458"]]},{"id":"ee141b3a.2da9c8","type":"switch","z":"86d554f6.5fdbd8","name":"Message Bounds","property":"payload","propertyType":"msg","rules":[{"t":"btwn","v":"26","vt":"num","v2":"32","v2t":"num"}],"checkall":"true","repair":false,"outputs":1,"x":1130,"y":840,"wires":[["7d5b50e6.7d767"]]},{"id":"c5035494.e74f68","type":"switch","z":"86d554f6.5fdbd8","name":"Message Bounds","property":"payload","propertyType":"msg","rules":[{"t":"btwn","v":"0","vt":"num","v2":"100","v2t":"num"}],"checkall":"true","repair":false,"outputs":1,"x":1130,"y":1060,"wires":[["e83ccfe3.b617a"]]},{"id":"79469fbf.92751","type":"mqtt in","z":"86d554f6.5fdbd8","name":"","topic":"esp32/battery","qos":"2","datatype":"auto","broker":"ba961cc5.4ee78","x":350,"y":1280,"wires":[["f62f96b9.44c5d8"]]},{"id":"6494df7a.98ba2","type":"ui_gauge","z":"86d554f6.5fdbd8","name":"","group":"350b7c13.5b48d4","order":5,"width":0,"height":0,"gtype":"gage","title":"Battery Percent","label":"%","format":"{{value | number:0}}%","min":0,"max":"100","colors":["#b40501","#e6e600","#00ff00"],"seg1":"","seg2":"","x":1500,"y":1180,"wires":[]},{"id":"ce266617.6e7b78","type":"influxdb out","z":"86d554f6.5fdbd8","influxdb":"549f8b8f.304664","name":"","measurement":"battery","precision":"","retentionPolicy":"","x":1530,"y":1280,"wires":[]},{"id":"f62f96b9.44c5d8","type":"function","z":"86d554f6.5fdbd8","name":"String to Number","func":"msg.payload=Number(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":610,"y":1280,"wires":[["88840e3a.8bec"]]},{"id":"6cfeb5bf.b27bcc","type":"mqtt out","z":"86d554f6.5fdbd8","name":"","topic":"Irrigation/Relay","qos":"","retain":"","broker":"ba961cc5.4ee78","x":1360,"y":1780,"wires":[]},{"id":"658a563c.6cdf18","type":"mqtt in","z":"86d554f6.5fdbd8","name":"","topic":"esp32/sunlight","qos":"2","datatype":"auto","broker":"ba961cc5.4ee78","x":350,"y":1500,"wires":[["6b530fa3.a3298"]]},{"id":"992760a2.66938","type":"ui_gauge","z":"86d554f6.5fdbd8","name":"","group":"350b7c13.5b48d4","order":6,"width":0,"height":0,"gtype":"gage","title":"Sunlight","label":"%","format":"{{value | number:0 }}%","min":0,"max":"100","colors":["#b40501","#e6e600","#1c02ff"],"seg1":"","seg2":"","x":1580,"y":1440,"wires":[]},{"id":"23f30c2d.e9ff64","type":"influxdb out","z":"86d554f6.5fdbd8","influxdb":"549f8b8f.304664","name":"","measurement":"sunlight","precision":"","retentionPolicy":"","x":2070,"y":1500,"wires":[]},{"id":"6b530fa3.a3298","type":"function","z":"86d554f6.5fdbd8","name":"String to Number","func":"msg.payload=Number(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":1500,"wires":[["70fbf071.46b37"]]},{"id":"70fbf071.46b37","type":"switch","z":"86d554f6.5fdbd8","name":"Message Bounds","property":"payload","propertyType":"msg","rules":[{"t":"btwn","v":"0","vt":"num","v2":"4095","v2t":"num"}],"checkall":"true","repair":false,"outputs":1,"x":1050,"y":1500,"wires":[["54f6c2ab.c1ebfc"]]},{"id":"54f6c2ab.c1ebfc","type":"range","z":"86d554f6.5fdbd8","minin":"0","maxin":"4095","minout":"0","maxout":"100","action":"scale","round":false,"property":"payload","name":"Scale Sunlight Percent","x":1360,"y":1500,"wires":[["23f30c2d.e9ff64","992760a2.66938"]]},{"id":"e005cd9.97d553","type":"inject","z":"86d554f6.5fdbd8","name":"Water the Garden","topic":"","payload":"soilwatchdogstatus","payloadType":"flow","repeat":"","crontab":"*/5 17 * * *","once":false,"onceDelay":0.1,"x":330,"y":1920,"wires":[["ff674ca4.5e119"]]},{"id":"88840e3a.8bec","type":"range","z":"86d554f6.5fdbd8","minin":"1750","maxin":"2345","minout":"0","maxout":"100","action":"scale","round":false,"property":"payload","name":"Scale Battery Percent","x":860,"y":1280,"wires":[["e5ce9d50.1c1b","c39bd40b.4fbd48"]]},{"id":"c8f0322e.b2f47","type":"function","z":"86d554f6.5fdbd8","name":"Check Soil Moisture and send water command","func":"var soilmoisture = flow.get('soilmoisture')||0;\n\nif (soilmoisture < 75) {\nmsg.payload = \"on\"\n}\nelse {\n    msg.payload = \"off\"\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":1000,"y":1920,"wires":[["6cfeb5bf.b27bcc","47492b9e.d53f04"]]},{"id":"3172b67b.33422a","type":"mqtt in","z":"86d554f6.5fdbd8","name":"","topic":"Irrigation/Status","qos":"1","datatype":"auto","broker":"ba961cc5.4ee78","x":380,"y":2180,"wires":[["6350514d.50a3","e3643d23.32541"]]},{"id":"6350514d.50a3","type":"ui_text","z":"86d554f6.5fdbd8","group":"835618b3.589538","order":5,"width":0,"height":0,"name":"","label":"Irrigation Status","format":"{{msg.payload}}","layout":"row-spread","x":710,"y":2180,"wires":[]},{"id":"2fccd4c4.6f461c","type":"ui_numeric","z":"86d554f6.5fdbd8","name":"","label":"Water Duration [min]","tooltip":"","group":"835618b3.589538","order":2,"width":0,"height":0,"wrap":false,"passthru":true,"topic":"","format":"{{value}}","min":0,"max":10,"step":1,"x":300,"y":2740,"wires":[["a504bb39.7b35a8"]]},{"id":"5c5dc77e.182338","type":"mqtt out","z":"86d554f6.5fdbd8","name":"","topic":"Irrigation/Duration","qos":"2","retain":"true","broker":"ba961cc5.4ee78","x":870,"y":2740,"wires":[]},{"id":"a504bb39.7b35a8","type":"function","z":"86d554f6.5fdbd8","name":"Scale to msec","func":"msg.payload = msg.payload * 1000*60;\nmsg.payload = msg.payload.toString();\nreturn msg;","outputs":1,"noerr":0,"x":610,"y":2740,"wires":[["5c5dc77e.182338"]]},{"id":"67703e2a.25652","type":"mqtt in","z":"86d554f6.5fdbd8","name":"","topic":"Irrigation/watertime","qos":"2","datatype":"auto","broker":"ba961cc5.4ee78","x":370,"y":2260,"wires":[["744e1c30.c5ebb4"]]},{"id":"2490762e.a6e89a","type":"ui_text","z":"86d554f6.5fdbd8","group":"835618b3.589538","order":3,"width":0,"height":0,"name":"","label":"Irrigation Time [min]","format":"{{msg.payload}}","layout":"row-spread","x":930,"y":2260,"wires":[]},{"id":"744e1c30.c5ebb4","type":"function","z":"86d554f6.5fdbd8","name":"Scale from msec","func":"msg.payload = Number(msg.payload)\n\nmsg.payload = msg.payload / 1000/60;\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":2260,"wires":[["2490762e.a6e89a"]]},{"id":"e59fa17d.d62c4","type":"function","z":"86d554f6.5fdbd8","name":"Reset Watchdog","func":"flow.set('soilmoisturewatchdog',0);\nreturn msg;","outputs":1,"noerr":0,"x":650,"y":440,"wires":[[]]},{"id":"dc50019b.d507e","type":"inject","z":"86d554f6.5fdbd8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":190,"y":180,"wires":[["b0cf89da.7111a8"]]},{"id":"b0cf89da.7111a8","type":"function","z":"86d554f6.5fdbd8","name":"Initiate Watchdog","func":"var soilmoisturewatchdog = flow.set('soilmoisturewatchdog',100);\n\nvar soilwatchdogstatus = flow.set('soilwatchdogstatus',\"offline\")\n\nreturn msg;","outputs":1,"noerr":0,"x":440,"y":180,"wires":[[]]},{"id":"89f47178.d3715","type":"inject","z":"86d554f6.5fdbd8","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":2540,"wires":[["7d29e08c.2b836"]]},{"id":"7d29e08c.2b836","type":"function","z":"86d554f6.5fdbd8","name":"Check Watchdog","func":"var current = flow.get('soilmoisturewatchdog');\n\nif (current < 100) {\n\nflow.set('soilmoisturewatchdog', current + 1);\n\nflow.set('soilwatchdogstatus', \"online\")\n}\n\nelse{\n\nflow.set('soilwatchdogstatus',\"offline\")\nmsg.payload = 1000000;\n}\n\nmsg.payload = flow.get('soilwatchdogstatus')\n\nreturn msg;","outputs":1,"noerr":0,"x":440,"y":2540,"wires":[["6e095db0.062a74"]]},{"id":"ff674ca4.5e119","type":"switch","z":"86d554f6.5fdbd8","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"online","vt":"str"},{"t":"eq","v":"offline","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":570,"y":1920,"wires":[["c8f0322e.b2f47"],[]]},{"id":"47492b9e.d53f04","type":"debug","z":"86d554f6.5fdbd8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1600,"y":1920,"wires":[]},{"id":"6e095db0.062a74","type":"ui_text","z":"86d554f6.5fdbd8","group":"835618b3.589538","order":4,"width":0,"height":0,"name":"","label":"Monitor Status","format":"{{msg.payload}}","layout":"row-spread","x":800,"y":2540,"wires":[]},{"id":"e3643d23.32541","type":"debug","z":"86d554f6.5fdbd8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":710,"y":2120,"wires":[]},{"id":"e5ce9d50.1c1b","type":"switch","z":"86d554f6.5fdbd8","name":"Message Bounds","property":"payload","propertyType":"msg","rules":[{"t":"btwn","v":"0","vt":"num","v2":"100","v2t":"num"}],"checkall":"true","repair":false,"outputs":1,"x":1190,"y":1280,"wires":[["ce266617.6e7b78","6494df7a.98ba2"]]},{"id":"b60c3926.e82a58","type":"ui_button","z":"86d554f6.5fdbd8","name":"","group":"835618b3.589538","order":1,"width":0,"height":0,"passthru":false,"label":"Water Garden","tooltip":"","color":"","bgcolor":"","icon":"","payload":"on","payloadType":"str","topic":"","x":340,"y":1780,"wires":[["6cfeb5bf.b27bcc"]]},{"id":"c39bd40b.4fbd48","type":"debug","z":"86d554f6.5fdbd8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1260,"y":1180,"wires":[]},{"id":"c4664383.62d5c","type":"comment","z":"86d554f6.5fdbd8","name":"Initialization Nodes","info":"","x":330,"y":80,"wires":[]},{"id":"7c49d449.f5724c","type":"comment","z":"86d554f6.5fdbd8","name":"Sensor Reading, Formatting, Databasing, and Display","info":"","x":1140,"y":240,"wires":[]},{"id":"dcf5495b.dd7178","type":"comment","z":"86d554f6.5fdbd8","name":"Water Garden manually","info":"","x":1040,"y":1740,"wires":[]},{"id":"9b43e52b.4c1e58","type":"comment","z":"86d554f6.5fdbd8","name":"Display Irrigation Status and Watertime feedback from ESP8266","info":"","x":750,"y":2080,"wires":[]},{"id":"12ed5507.18215b","type":"comment","z":"86d554f6.5fdbd8","name":"Make sure we have recent connectivity to ESP32","info":"Lack of recent communication will prevent watering.","x":520,"y":2480,"wires":[]},{"id":"b1bc3bc0.075b68","type":"comment","z":"86d554f6.5fdbd8","name":"Set length of watering pulse on ESP8266. 1-10 minutes.","info":"","x":620,"y":2680,"wires":[]},{"id":"73c90d75.191564","type":"comment","z":"86d554f6.5fdbd8","name":"Water Garden automatically","info":"","x":620,"y":1860,"wires":[]}]

ESP8266 code:

#include <PubSubClient.h>

const int ledPin = 4;
const int relayPin = 16;
int waterduration = 10000;
// Replace the next variables with your SSID/Password combination
char* ssid = "Redacted";
char* password = "Redacted";

// Add your MQTT Broker IP address, example:
const char* mqtt_server = "Redacted";

WiFiClient Irrigation;
PubSubClient client(Irrigation);

long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {
  pinMode (ledPin, OUTPUT);
  pinMode (relayPin, OUTPUT);
 
  
  Serial.begin(115200);
    
  digitalWrite (ledPin, HIGH);
  delay(500);
  digitalWrite (ledPin, LOW);
  delay(500);
  digitalWrite (ledPin, HIGH);
  delay(500);
  digitalWrite (ledPin, LOW);
  delay(500);
  digitalWrite (ledPin, HIGH);
  delay(500);
  digitalWrite (ledPin, LOW);
 

  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
 }

void setup_wifi() {
  int wificount = 0;
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);

     digitalWrite (ledPin, HIGH);
     delay(50);
     digitalWrite (ledPin, LOW);
     delay(50);
     digitalWrite (ledPin, HIGH);
     delay(50);
     digitalWrite (ledPin, LOW);
     delay(50);
     digitalWrite (ledPin, HIGH);
     delay(50);
     digitalWrite (ledPin, LOW);

    Serial.print(".");

wificount++;
        if (wificount > 5) {
      ESP.restart();
      
    }
    
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  char wateringtimeString[8];
  

  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  
  Serial.println();
 
  if (String(topic) == "Irrigation/Relay") {
    Serial.print("Changing relay to on");
    
    if (messageTemp == "on") {
      Serial.println("Starting water cycle");
      Serial.println();
      water();
    }
    
    else if (messageTemp == "off") {
      Serial.println("off");
      digitalWrite(ledPin, LOW);
      digitalWrite(relayPin, LOW);
      client.publish("Irrigation/Status", "off");
    }
  }
   if (String(topic) == "Irrigation/Duration") {
    Serial.print("Changing on duration to minutes: ");
        waterduration = messageTemp.toInt();  
    Serial.println(waterduration);
   dtostrf(waterduration, 1, 2, wateringtimeString);
    client.publish("Irrigation/watertime", wateringtimeString);
        }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Subscribe
      client.subscribe("Irrigation/Relay",1);
      client.subscribe("Irrigation/Duration",1);
      client.subscribe("Irrigation/Status",1);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void water() {
  client.publish("Irrigation/Status", "on");
  
  digitalWrite(ledPin, HIGH);
  digitalWrite(relayPin, HIGH);
  delay(waterduration);
  
  client.publish("Irrigation/Status", "off");
  
  digitalWrite(ledPin, LOW);
  digitalWrite(relayPin, LOW);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  long now = millis();
    
  }

ESP32 Code:

#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SEALEVELPRESSURE_HPA (1013.25)

#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  40        /* Time ESP32 will go to sleep (in seconds) */

RTC_DATA_ATTR int bootCount = 0;

Adafruit_BME280 bme; // I2C

unsigned long delayTime;
const int ledPin = 33;

// Replace the next variables with your SSID/Password combination
const char* ssid = "Redacted";
const char* password = "Redacted";

// Add your MQTT Broker IP address
const char* mqtt_server = "Redacted";

WiFiClient espClient;
PubSubClient client(espClient);

long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {

  Serial.begin(115200);
  delay(1000);

  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");
  
  pinMode (ledPin, OUTPUT);
 
  Serial.println(F("BME280 test"));
  
  setup_wifi();
  
  client.setServer(mqtt_server, 1883);

  pinMode(ledPin, OUTPUT);

  bool status;

  status = bme.begin(0x77);
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  Serial.println("-- Default Test --");
  delayTime = 1000;

  Serial.println();

    if (!client.connected()) {
    reconnect();
  }
  client.loop();

  long now = millis();
  if (now - lastMsg > 1000) {
    lastMsg = now;

    // Soil Moisture in % of full range
    int soilmoisture = (((analogRead(34) - 3600) * 100) / -2400);

    // Temperature in Fahrenheit
    int temperaturez = 1.8 * bme.readTemperature() + 32;

    // Pressure in inches of mercury
    float pressure = bme.readPressure() / 100.0F * 0.029529983071445;

    // Humidity in % relative humidity
    int humidity = bme.readHumidity();

    //Battery %
    int BatteryPercent = analogRead(36);

    //Sunlight
    int Sunlight = analogRead(35);

    // Convert the value to a char array
    char soilmoistureString[8];
    char temperaturezString[8];
    char pressureString[8];
    char humidityString[8];
    char BatteryString[8];
    char SunlightString[8];

    Serial.print("pressure message sending to MQTT = ");
    Serial.print(pressureString);
    Serial.print("pressure reading = ");
    Serial.print(pressure);

    dtostrf(soilmoisture, 1, 2, soilmoistureString);
    dtostrf(temperaturez, 1, 2, temperaturezString);
    dtostrf(pressure, 1, 2, pressureString);
    dtostrf(humidity, 1, 2, humidityString);
    dtostrf(BatteryPercent, 1, 2, BatteryString);
    dtostrf(Sunlight, 1, 2, SunlightString);
    
    client.publish("esp32/soilmoisture", soilmoistureString);
    client.publish("esp32/temperaturez", temperaturezString);
    client.publish("esp32/pressure", pressureString);
    client.publish("esp32/humidity", humidityString);
    client.publish("esp32/battery", BatteryString);
    client.publish("esp32/sunlight", SunlightString);

    digitalWrite (ledPin, HIGH);
    delay(50);
    digitalWrite (ledPin, LOW);
    delay(100);
    digitalWrite (ledPin, HIGH);
    delay(50);
    digitalWrite (ledPin, LOW);
    delay(100);
    digitalWrite (ledPin, HIGH);
    delay(50);
    digitalWrite (ledPin, LOW);
 
    Serial.println("Going to sleep now");
    delay(1000);
    Serial.flush(); 
    esp_deep_sleep_start();
    
  }

}

void setup_wifi() {
  int wificount = 0;
  delay(100);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
    wificount++;
        if (wificount > 5) {
      ESP.restart();
    }
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Subscribe
      client.subscribe("esp32/output");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void loop(){
  //This is not going to be called
}

I'm not certain my connectivity issues are with the wifi network. Even when all the devices are inside and ~20 feet from the router I still have message delay issues, particularly with the relay status feedback from ESP8266. I'd be grateful if anyone can spot the issue, but it's working well enough for now.

Great call EmilWesselink. I updated the code this morning with a pulse. The system now checks soil moisture every 5 minutes between 5 and 6pm and pulses water if it's below a certain threshold.

I'll keep you updated as I have more info. Still waiting on my panels to arrive from China, and the power saving circuit zenofmud suggested.

Another great call. That's a 3D printed enclosure I'm using. The data that's been collected shows interesting (and expected) correlation between solar loading and measured temperature. In full sunlight the difference between measured and Google's temps for my area can be as high as 20F.

@zacharyalan09 please click the pencil icon (edit) on your post above, and format your flow code as per How to share code or flow json
Thanks

I like to set up all ioT devices with a fixed ip as it makes for easier management. Also, if you have more than one Wifi AP available you could set up the devices to connect to the AP with the strongest signal at each restart.

Just a couple of thoughts and imo only of course :slight_smile:

Zach a question and a suggestion about your flow.

Q - on a couple of your flows (like Temperature) you send the incoming value to a gauge and send to a node to convert it to a number. You then test if it is in a range and then send it to the database. Why don't you have the guage at same point as the influx node? i.e. why send it to the guage if you are not sending it to the database?

Suggestion, add a change node before the influx nodes to set msg.measurement to the measurement type. Then you can just use one influx node. To keep the clutter down, where you have the influx nodes change them to a link-out node. Then have a link-in node connected to a single influx node. Connect all the link-out nodes to the one link-in node which will be connected to the single influx node.

I was using the gauge as a debugging tool on the node-red dashboard. It does make more sense to have the gauge after the range test.

I would occasionally get values that were way out of scale (1,000,000 degrees F). The gauge was placed before the range test so I could see the values momentarily as they came through, but not have to deal with them permanently cluttering the database.Once the out-of-scale values were in the database I was forced to manually scale the Y axis on the grafana displays.

I haven't played with change-nodes much at all. I'll have to check it out, thanks for the tip.

Nice project.

I had problems with loss of MQTT messages, it turned out to be the deep sleep was kicking in before the messages had time to send, especially if a re-connection was required. No indication of any errors, just nothing. Took me a while to realise.

Nice garden, over here we call that a country park!

Your 3D printed enclosures look awesome! Nice project and great work!

This topic was automatically closed after 60 days. New replies are no longer allowed.