Help controlling water tank level

Hi,

Im trying to control the level in a tank with node red on the raspberry pi 3. I have a low and high switch at the bottem and top of the tank and a solenooid to let in water. Im very new to coding and node red so i need some help. Ive done up a flow (I know its ugly) and it kind of works but, if the high switch is open before the low switch opens the solenoid still fills the tank.

[{“id”:“8757536f.2115c8”,“type”:“rpi-gpio in”,“z”:“e4167a44.5763d8”,“name”:“Low Level SW”,“pin”:“40”,“intype”:“up”,“debounce”:“25”,“read”:true,“x”:130,“y”:580,“wires”:[[“26fd1d82.81ceb2”,“580f2422.bc118c”,“42c5da7c.9a4054”,“a1c56cf5.c4674”,“e0876d8c.a7238”]]},{“id”:“ca05192d.5c77b8”,“type”:“rpi-gpio in”,“z”:“e4167a44.5763d8”,“name”:“High level SW”,“pin”:“38”,“intype”:“up”,“debounce”:“25”,“read”:true,“x”:130,“y”:520,“wires”:[[“39d1f00e.2be2b”]]},{“id”:“ec6b2cda.ba1df”,“type”:“rpi-gpio out”,“z”:“e4167a44.5763d8”,“name”:“Solenoid”,“pin”:“37”,“set”:true,“level”:“1”,“freq”:"",“out”:“out”,“x”:800,“y”:580,“wires”:},{“id”:“9138080f.2f1f28”,“type”:“change”,“z”:“e4167a44.5763d8”,“name”:“AND”,“rules”:[{“t”:“change”,“p”:“payload”,“pt”:“msg”,“from”:“0”,“fromt”:“num”,“to”:“0”,“tot”:“num”}],“action”:"",“property”:"",“from”:"",“to”:"",“reg”:false,“x”:610,“y”:580,“wires”:[[“ec6b2cda.ba1df”]]},{“id”:“e0876d8c.a7238”,“type”:“trigger”,“z”:“e4167a44.5763d8”,“op1”:“1”,“op2”:“1”,“op1type”:“num”,“op2type”:“num”,“duration”:“250”,“extend”:false,“units”:“ms”,“reset”:"",“bytopic”:“all”,“name”:"",“x”:420,“y”:580,“wires”:[[“9138080f.2f1f28”]]},{“id”:“39d1f00e.2be2b”,“type”:“change”,“z”:“e4167a44.5763d8”,“name”:“Invert output”,“rules”:[{“t”:“change”,“p”:“payload”,“pt”:“msg”,“from”:“1”,“fromt”:“num”,“to”:“0”,“tot”:“num”}],“action”:"",“property”:"",“from”:"",“to”:"",“reg”:false,“x”:410,“y”:520,“wires”:[[“9138080f.2f1f28”]]}]

This is what i need it to do,
if high switch = 0 & low switch = 0
send = 0
if high switch = 1 & low switch = 0
send = 0
if high switch = 1 & low switch = 1
send = 0
if high switch = 0 & low switch = 1
send = 1, untill high switch = 1

Ive used the trigger node to latch the low switch output to 1 untill the high switch sends 0. I think it can be done in a function node but i dont know how to seperate the 2 switch payloads coming in to the function node

Any help wil be appreciated
Jeremy

OK when posting code - place ``` (backticks) around it so we can import the code correctly.

Explain in english (once you have fixed up your code above) - what you are trying to achieve ?

I assume

  1. Monitor Tank low level sensor = if it is triggered - then the tank needs to be filled so turn on the solenoid to let water in

  2. Monitor high level sensor = when it is triggered then turn off the solenoid to stop filling the tank

Is this logic correct (i.e. this is what you want to do ?) if so where is the problem ?

Craig

with the flow at the moment if the high level is already triggered before the low is triggered (eg: if the high switch is stuck from the last fill) the tank will start to fill once the low switch is active and continue to fill untill it overflowes

{"id":"8757536f.2115c8","type":"rpi-gpio in","z":"e4167a44.5763d8","name":"Low Level SW","pin":"40","intype":"up","debounce":"25","read":true,"x":130,"y":580,"wires":[["26fd1d82.81ceb2","580f2422.bc118c","42c5da7c.9a4054","a1c56cf5.c4674","e0876d8c.a7238"]]},{"id":"ca05192d.5c77b8","type":"rpi-gpio in","z":"e4167a44.5763d8","name":"High level SW","pin":"38","intype":"up","debounce":"25","read":true,"x":130,"y":520,"wires":[["39d1f00e.2be2b"]]},{"id":"ec6b2cda.ba1df","type":"rpi-gpio out","z":"e4167a44.5763d8","name":"Solenoid","pin":"37","set":true,"level":"1","freq":"","out":"out","x":800,"y":580,"wires":[]},{"id":"9138080f.2f1f28","type":"change","z":"e4167a44.5763d8","name":"AND","rules":[{"t":"change","p":"payload","pt":"msg","from":"0","fromt":"num","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":580,"wires":[["ec6b2cda.ba1df"]]},{"id":"e0876d8c.a7238","type":"trigger","z":"e4167a44.5763d8","op1":"1","op2":"1","op1type":"num","op2type":"num","duration":"250","extend":false,"units":"ms","reset":"","bytopic":"all","name":"","x":420,"y":580,"wires":[["9138080f.2f1f28"]]},{"id":"39d1f00e.2be2b","type":"change","z":"e4167a44.5763d8","name":"Invert output","rules":[{"t":"change","p":"payload","pt":"msg","from":"1","fromt":"num","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":520,"wires":[["9138080f.2f1f28"]]}

i think the easy thing to do would be use a function node but i dont know how to differentiate between the 2 variables from the 2 switches, need help with the '?'

var high switch=? //msg.payload from high switch
var low switch=? //msg.payload from low switch
var output=''
if (low switch=0 && high switch=0) {
output=false
} else
if (low switch=0 && high switch=1) {
output=false
} else
if (low switch=1 && high switch=1) {
output=false
}else
if (low switch=1 && high switch=0) {
output=true
msg.payload=0utput
return msg;

For some reason your flow is not importable (by me at least), but I think you should use a Join node in key/value mode to give you one message containing the current state of both switches. Then I think it becomes easy. See this example in the cookbook for how to do it.#https://cookbook.nodered.org/basic/join-streams

OK when you say the switch is stuck - i assume you mean the "Virtual" switch i.e. your variables you are using - rather than the physical switch being faulty ? (if it is the physical switch then you need to get that replaced, not try to code around edge use cases).

I will assume that once you receive a low level alert you want to fill the tank until you receive a high level alert and then stop filling ?

The easiest way is to use a global variable (Lets call it Tank-Needs-Filling) - this will be set to True when the Low level switch is activated and set to False when the high level switch is activated

So you will have a loop that does the following

Check Low level switch - if activated set Tank-Needs-Filling to True
Check High level switch - if activated set Tank-Needs-Filling to False

This loop would just run every minute (or whatever is appropriate based on the size of the tank and the fill rate)

A 2nd Loop would run and monitor this Global Variable - Tank-Needs-Filling - if it equals true, will turn on the solenoid, if false turn off the solenoid

Craig

Later on if you want to get fancy - you can look at doing this as a Finite State Machine - using the DSM node

(And i can not import your flow either) when pasting a flow - select the nodes and then choose the export option

When you go to paste them in here then put 3 backticks on a single line (by themselves) - then paste your code and then 3 more backticks on a line after your code and you should end up with something that looks like the below - the backtick on a standard (US) keyboard is next to the 1 key and is on the same key as the TILDE ~

{"id":"8757536f.2115c8","type":"rpi-gpio in","z":"e4167a44.5763d8","name":"Low Level SW","pin":"40","intype":"up","debounce":"25","read":true,"x":130,"y":580,"wires":[["26fd1d82.81ceb2","580f2422.bc118c","42c5da7c.9a4054","a1c56cf5.c4674","e0876d8c.a7238"]]},{"id":"ca05192d.5c77b8","type":"rpi-gpio in","z":"e4167a44.5763d8","name":"High level SW","pin":"38","intype":"up","debounce":"25","read":true,"x":130,"y":520,"wires":[["39d1f00e.2be2b"]]},{"id":"ec6b2cda.ba1df","type":"rpi-gpio out","z":"e4167a44.5763d8","name":"Solenoid","pin":"37","set":true,"level":"1","freq":"","out":"out","x":800,"y":580,"wires":[]},{"id":"9138080f.2f1f28","type":"change","z":"e4167a44.5763d8","name":"AND","rules":[{"t":"change","p":"payload","pt":"msg","from":"0","fromt":"num","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":580,"wires":[["ec6b2cda.ba1df"]]},{"id":"e0876d8c.a7238","type":"trigger","z":"e4167a44.5763d8","op1":"1","op2":"1","op1type":"num","op2type":"num","duration":"250","extend":false,"units":"ms","reset":"","bytopic":"all","name":"","x":420,"y":580,"wires":[["9138080f.2f1f28"]]},{"id":"39d1f00e.2be2b","type":"change","z":"e4167a44.5763d8","name":"Invert output","rules":[{"t":"change","p":"payload","pt":"msg","from":"1","fromt":"num","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":520,"wires":[["9138080f.2f1f28"]]}

Craig

[{"id":"8757536f.2115c8","type":"rpi-gpio in","z":"e4167a44.5763d8","name":"Low Level SW","pin":"40","intype":"up","debounce":"25","read":true,"x":150,"y":720,"wires":[["e0876d8c.a7238"]]},{"id":"ca05192d.5c77b8","type":"rpi-gpio in","z":"e4167a44.5763d8","name":"High level SW","pin":"38","intype":"up","debounce":"25","read":true,"x":150,"y":660,"wires":[["12edd188.79e84e"]]},{"id":"ec6b2cda.ba1df","type":"rpi-gpio out","z":"e4167a44.5763d8","name":"Solenoid","pin":"19","set":true,"level":"1","freq":"","out":"out","x":820,"y":720,"wires":[]},{"id":"9138080f.2f1f28","type":"change","z":"e4167a44.5763d8","name":"AND","rules":[{"t":"change","p":"payload","pt":"msg","from":"0","fromt":"num","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":720,"wires":[["ec6b2cda.ba1df"]]},{"id":"e0876d8c.a7238","type":"trigger","z":"e4167a44.5763d8","op1":"1","op2":"1","op1type":"num","op2type":"num","duration":"250","extend":false,"units":"ms","reset":"","bytopic":"all","name":"","x":440,"y":720,"wires":[["9138080f.2f1f28","26fd1d82.81ceb2","580f2422.bc118c","42c5da7c.9a4054"]]},{"id":"12edd188.79e84e","type":"change","z":"e4167a44.5763d8","name":"Invert output","rules":[{"t":"change","p":"payload","pt":"msg","from":"1","fromt":"num","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":660,"wires":[["9138080f.2f1f28"]]}]

how can i post a picture i think it would be easier to show what im trying to do

Maybe i could try somthing like this but i dont know how assign the variables from the 2 switches,

[{"id":"abfdc92b.0f7be8","type":"rpi-gpio in","z":"e4167a44.5763d8","name":"Low Level SW","pin":"40","intype":"up","debounce":"25","read":true,"x":510,"y":1100,"wires":[["c7cf5cde.303d3"]]},{"id":"d398d6cb.dc6c5","type":"rpi-gpio in","z":"e4167a44.5763d8","name":"High level SW","pin":"38","intype":"up","debounce":"25","read":true,"x":510,"y":1040,"wires":[["c7cf5cde.303d3"]]},{"id":"c7cf5cde.303d3","type":"function","z":"e4167a44.5763d8","name":"","func":"var high_switch= ? //msg.payload from high switch\nvar low_switch= ?  //msg.payload from low switch\nvar output;\n\nif (low_switch==0 && high_switch==0) {\n    output=false\n}else\nif (low_switch==0 && high_switch==1) {\n    output=false\n}else  \nif (low_switch==1 && high_switch==1) { \n    output=false\n}else\nif (low_switch==1 && high_switch==0) {\n    output=true\n}\nmsg.payload=output\nreturn msg;","outputs":1,"noerr":8,"x":730,"y":1080,"wires":[["d24cc080.e907f"]]},{"id":"d24cc080.e907f","type":"rpi-gpio out","z":"e4167a44.5763d8","name":"Solenoid","pin":"19","set":true,"level":"1","freq":"","out":"out","x":900,"y":1080,"wires":[]}]

if i use
var low_switch=msg.payload
var high_switch=msg.payload
how would the function node know which is which

Use the Change Node which can take an input value in the message body (payload) and put it into a context variable

I am not at my computer to do a flow now - but if you follow the logic from what i said above you should be able to get a solution

Craig

I think you are making this harder than it needs to be by trying to get your && working in the if statement - if you use the logic that i said before then you are not concerned about transitions of the switches (after the 1st one)

Craig

OK Try this

[{"id":"c78f696.b5cee98","type":"rpi-gpio in","z":"b68d15f7.6a1ab8","name":"Low Level SW","pin":"40","intype":"up","debounce":"25","read":true,"x":210,"y":440,"wires":[["a3253f5a.7b082"]]},{"id":"427da720.c44318","type":"change","z":"b68d15f7.6a1ab8","name":"Set Global Variable to fill tank","rules":[{"t":"set","p":"Tank_Needs_to_Be_Filled","pt":"global","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":440,"wires":[[]]},{"id":"a3253f5a.7b082","type":"switch","z":"b68d15f7.6a1ab8","name":"Test for low level","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":430,"y":440,"wires":[["427da720.c44318"]]},{"id":"6720f018.bed2b","type":"rpi-gpio in","z":"b68d15f7.6a1ab8","name":"High level SW","pin":"38","intype":"up","debounce":"25","read":true,"x":210,"y":500,"wires":[["78e82b4f.1b8884"]]},{"id":"78e82b4f.1b8884","type":"switch","z":"b68d15f7.6a1ab8","name":"Test for High level","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":430,"y":500,"wires":[["31194b7d.27f9b4"]]},{"id":"31194b7d.27f9b4","type":"change","z":"b68d15f7.6a1ab8","name":"Set Global Variable to fill tank","rules":[{"t":"set","p":"Tank_Needs_to_Be_Filled","pt":"global","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":500,"wires":[[]]}]

This sets the global variable for you (you will need to create it first somewhere) and then manipulates its based on the switch positions (i do not know the logic of your switches in terms of 1 and 0 for open and closed)

You then just need an inject node that runs every minute (as appropriate) to check the Global variable and enable the solenoid if it is set to true

Depending on if motors (pumps) are involved you may want to build in some hysterises control but that can come later

Craig

I have a flow I was using in my son's greenhouse (the bum moved!) I had a hydroponics setup and the water tank had two float valves, one at the top and one halfway down. They were tied to two gpio pins on the pi - if the switch was up, it would report 'high', if it was low it would report low. Both switches also put the value in seperate golbal variables. A common function node would determine if the tank was 'full' - both switches on. 'low' - top swotch off and middle switch on, or 'needs water' - both switches off. The msg's were paused for 50 ms to handle any rocking state of the switch due to waves caused by water returning to the tank. The msgs were only sent outside the three times watering went on automatically.

a second flow would look at the status (full, low or needs water) and send out an email if need be.

I had plans to connect this all to an automatic valve but didn't have time before they moved (the bum :smile:)

Here is the entire flow if you want to take a look. greenhouse.json (46.2 KB)

You will note that I also sent the data to a php program running on my web host that stored the data in a mySQL database and I had some php programs to query the data allowed me to look at it from anywhere.

Hi craig,

Thanks for the help so far, I kind of understand about to concept of storing a variable but i dont really understand what i am meant to do with these nodes in your flow.

Here is picture of what i would do if i were to hard wire it.

Hi Zenofmud,

Could you please export your flow so i can import it.

Yep you are getting hardware and software mixed up and making it harder than you need to.

The nodes in my flow are to simply read the state of the input pins on the Rpi, based on them you set the global variable appopriately.

Think about it this way - once the tank goes low you want to know that from the low side valve - you do not care what that valve is doing after it tells you the tank is low - so you only have to capute one state transition - that from when the tank is adequately full to when it is approaching empty

Similarly on the high side valve - you are only interested when it signals that the tank is full - so you record it only when it tells you that the tank is full

The brains of the outfit is done by the third node that i have not given you - it polls the global variable every minute - if it says the variable is null it does nothing, if it says the tank needs filling it opens the solenoid valve, if it says the tank is full it closes the solenoid. Thats it

Craig

Sorry it was stuck in the middle of the last paragraph. I reakky have to reread my posts before pressing reply. Look again I edited my post.

Hey Craig,

I’ve had a bit of a play with the flow you posted but I still can’t get it to work, am I supposed to just add an inject node into each at the start and just join it to the solenoid at the end, is there any chance you could complete the flow for me if you can find some spare time so I can see how it all works altogether, high switch =1 when high and low switch =1 when low, it would be much appreciated.

Cheers,

OK try this as a quick mockup - note the names of the nodes - you will need to put in the correct values for the pins on the Pi i.e. pull up or pull down etc

The trick is that each of the switches only perform one action - if the high tank level is met then it sets the variable to False i.e. stop filling the tank and vice versa - if the low level switch is triggered then it sets the variable to True - you do not care about the opposite state of each

You will need to initialise the state of the global variable and create it with a value in some startup node - so each time NR is started it will set the state of the variable to false (i.e. do not fill the tank) then the RPi pins will be checked

As i do not use a Rpi i can not test this - i assume that the state of the RPi pins is always available - they do not need to be polled (or that is performed automatically by the nodes provided by default)

[{"id":"5058e09.e3f702","type":"rpi-gpio in","z":"23099809.8f0d08","name":"Low Level SW","pin":"40","intype":"up","debounce":"25","read":true,"x":310,"y":380,"wires":[["a7746d38.c5a2d"]]},{"id":"3e47753f.abb36a","type":"change","z":"23099809.8f0d08","name":"Set Global Variable to fill tank to True","rules":[{"t":"set","p":"Tank_Needs_to_Be_Filled","pt":"global","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":870,"y":380,"wires":[[]]},{"id":"a7746d38.c5a2d","type":"switch","z":"23099809.8f0d08","name":"Test for low level","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":530,"y":380,"wires":[["3e47753f.abb36a"]]},{"id":"a5440279.49699","type":"rpi-gpio in","z":"23099809.8f0d08","name":"High level SW","pin":"38","intype":"up","debounce":"25","read":true,"x":310,"y":440,"wires":[["96b5ef29.744cc"]]},{"id":"96b5ef29.744cc","type":"switch","z":"23099809.8f0d08","name":"Test for High level","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":530,"y":440,"wires":[["da13bdd3.ea7ef"]]},{"id":"da13bdd3.ea7ef","type":"change","z":"23099809.8f0d08","name":"Set Global Variable to fill tank to false","rules":[{"t":"set","p":"Tank_Needs_to_Be_Filled","pt":"global","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":870,"y":440,"wires":[[]]},{"id":"8162211f.acc3a","type":"inject","z":"23099809.8f0d08","name":"Control tank filling","topic":"","payload":"","payloadType":"date","repeat":"60","crontab":"","once":true,"onceDelay":0.1,"x":330,"y":520,"wires":[["750f3858.fcfd48"]]},{"id":"750f3858.fcfd48","type":"switch","z":"23099809.8f0d08","name":"Check for action to perform","property":"Tank_Needs_to_Be_Filled","propertyType":"global","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":640,"y":520,"wires":[["15827a1e.07f206"],["9429fa3b.8bc3f8"]]},{"id":"15827a1e.07f206","type":"rpi-gpio out","z":"23099809.8f0d08","name":"Open Solenoid - Fill Tank","pin":"40","set":true,"level":"0","freq":"","out":"out","x":960,"y":520,"wires":[]},{"id":"9429fa3b.8bc3f8","type":"rpi-gpio out","z":"23099809.8f0d08","name":"Close Solenoid - Finish Filling Tank","pin":"40","set":true,"level":"1","freq":"","out":"out","x":1000,"y":580,"wires":[]}]

Craig

thanks for spending some time on this mate, its much appreciated. Ill have a play with it when i get a chance and let you know how it goes. With the whole flow together i can start to under stand how it works (nothing along the lines of what i was thinking).

thanks again,
Jez

no worries - let us know when you have something working we can have a look at.

Craig

Jez,

After thinking about your automation, what comes to mind is the float in your toilet.
The sequence looks like this:

  1. Flush
  2. Float lowers to the point that the fill valve opens (i.e. sensor at the bottom of your tank)
  3. Water starts to fill the tank
  4. Float rises to the point that it closes the fill valve (i.e. sensor at the top of your tank)

There is nothing in the process that keeps track of the current state. It is all based upon state-change events.

Seems like two node-red flows could do the same. Here is some pseudo code.

Flow #1 -- When the sensor at the bottom of the tank indicates that the water level is at the bottom of the tank, turn on the pump.

Flow #2 -- When the sensor at the top of the tank indicates that the water level is at the top of the tank, turn off the pump.

Seems to me that there is no need to keep track of the sensor states. Just need to turn the pump on and off based upon state-change events.

As a measure of prudence should the real, physical switch at the top of the tank fail, you could do this.

  1. Estimate how long it would take to fill the tank to the top
  2. In the flow that turns on the pump, add a timer node for the duration that you calculated above. Be sure that the time is long enough to fill the tank past the top (i.e. there will be some overflow).
  3. When the timer expires, turn off the pump. If the switch at the top of the tank is working correctly, and the delay that you estimated is longer than it would take to fill the tank, the pump should already be turned off by Flow #2. But, juuuuust in case, Flow #1 can save the day and turn off the pump if Flow #2 is never triggered.