Greenhouse irrigation

It works ok for me. Feed in a noisy value in msg.payload and the output is the smoothed value of the input. Try this flow for example.

[{"id":"4b8d1f71.bf6e88","type":"function","z":"d7ff0732.2f81d8","name":"10 sec RC","func":"// Applies a simple RC low pass filter to incoming payload values\nvar tc = 10*1000;       // time constant in milliseconds\n\nvar lastValue = context.get('lastValue');\nif (typeof lastValue == \"undefined\") lastValue = msg.payload;\nvar lastTime = context.get('lastTime') || null;\nvar now = new Date();\nvar currentValue = msg.payload;\nif (lastTime === null) {\n    // first time through\n    newValue = currentValue;\n} else {\n    var dt = now - lastTime;\n    var newValue;\n    \n    if (dt > 0) {\n        var dtotc = dt / tc;\n        newValue = lastValue * (1 - dtotc) + currentValue * dtotc;\n    } else {\n        // no time has elapsed leave output the same as last time\n        newValue = lastValue;\n    }\n}\ncontext.set('lastValue', newValue);\ncontext.set('lastTime', now);\n\nmsg.payload = newValue;\nreturn msg;","outputs":1,"noerr":0,"x":598,"y":518,"wires":[["7f6ad735.aaf668"]]},{"id":"aa742e9.bf7365","type":"function","z":"d7ff0732.2f81d8","name":"Random numbers 7.5 to 12.5","func":"// return random number between 0 and 1\nvar rand = Math.random( );\n// offset it so it is between 7.5 and 12.5\nmsg.payload = 7.5 + rand*5\nreturn msg;","outputs":1,"noerr":0,"x":356.5,"y":522,"wires":[["4b8d1f71.bf6e88","19a8e088.9240b7"]]},{"id":"b9bf92ef.bddf7","type":"inject","z":"d7ff0732.2f81d8","name":"1 second kick","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":false,"onceDelay":0.1,"x":117,"y":522,"wires":[["aa742e9.bf7365"]]},{"id":"e19dadc9.2c8368","type":"ui_chart","z":"d7ff0732.2f81d8","name":"","group":"1d60c1ef.f5bb16","order":5,"width":0,"height":0,"label":"chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"20","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"x":656.5,"y":661,"wires":[[],[]]},{"id":"19a8e088.9240b7","type":"change","z":"d7ff0732.2f81d8","name":"Topic: Input","rules":[{"t":"set","p":"topic","pt":"msg","to":"Input","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":435.5,"y":662,"wires":[["e19dadc9.2c8368"]]},{"id":"7f6ad735.aaf668","type":"change","z":"d7ff0732.2f81d8","name":"Topic: Output","rules":[{"t":"set","p":"topic","pt":"msg","to":"Output","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":619,"y":597,"wires":[["e19dadc9.2c8368"]]},{"id":"1d60c1ef.f5bb16","type":"ui_group","z":"","name":"Default","tab":"317df29b.5649b6","disp":true,"width":"12","collapse":false},{"id":"317df29b.5649b6","type":"ui_tab","z":"","name":"List","icon":"dashboard"}]
1 Like

@Colin In the RC function shouldn't the statement var newValue be outside the If statement?
ie this

var currentValue = msg.payload;
if (lastTime === null) {
    // first time through
    newValue = currentValue;
} else {
    var dt = now - lastTime;
    var newValue;    //<============= this should be moved outside the IF
    
    if (dt > 0) {
        var dtotc = dt / tc;
        newValue = lastValue * (1 - dtotc) + currentValue * dtotc;
    } else {
        // no time has elapsed leave output the same as last time
        newValue = lastValue;
    }
}

should be changed to

var currentValue = msg.payload;
var newValue;            // <============ better place.
if (lastTime === null) {
    // first time through
    newValue = currentValue;
} else {
    var dt = now - lastTime;
    
    if (dt > 0) {
        var dtotc = dt / tc;
        newValue = lastValue * (1 - dtotc) + currentValue * dtotc;
    } else {
        // no time has elapsed leave output the same as last time
        newValue = lastValue;
    }
}

It works perfectly, thanks!

Yes, sloppy coding on my part. In fact for reasons that are not entirely clear to me it doesn't matter. Looking at the version I currently use it does not have that error.

In addition I have included checks that the value passed in can be converted to a number, and ignores it if not, so all in all this is a better version:

[{"id":"31610c94.859b44","type":"function","z":"d7ff0732.2f81d8","name":"10 sec RC filter","func":"// Applies a simple RC low pass filter to incoming payload values\nvar tc = 10*1000;       // time constant in milliseconds\n\nvar lastValue = context.get('lastValue');\nif (typeof lastValue == \"undefined\") lastValue = msg.payload;\nvar lastTime = context.get('lastTime') || null;\nvar now = new Date();\nvar currentValue = Number(msg.payload);\n// ignore if not a number\nif (!isNaN(currentValue) && isFinite(currentValue)) {\n    var newValue;\n    if (lastTime === null) {\n        // first time through\n        newValue = currentValue;\n    } else {\n        var dt = now - lastTime;\n        if (dt > 0) {\n            var dtotc = dt / tc;\n            newValue = lastValue * (1 - dtotc) + currentValue * dtotc;\n        } else {\n            // no time has elapsed leave output the same as last time\n            newValue = lastValue;\n        }\n    }\n    context.set('lastValue', newValue);\n    context.set('lastTime', now);\n    msg.payload = newValue;\n} else {\n    // not a number so ignore it\n    msg = null;\n}\nreturn msg;","outputs":1,"noerr":0,"x":622,"y":522,"wires":[["7f6ad735.aaf668"]]}]

Hi @Paul , while being on the two first phases mentioned in @Colin's blog, I faced a problem that I couldn't beat maybe it is because I'm trying on a drip irrigation prototype and not in a real environment , my problem is even when I'm not adding any water the soil moisture just keeps increasing instead of decreasing :persevere: , the picture when I tried a value for Proportional Band.
image
I made a lot of holes in the soil container but nothing has changed, do you have any idea where the problem is or how to solve this ?
(I'm using a capcitive soil moisture sensor v1.2)

The PID seems to be working ok, if you check the PID output 12.58 onwards it's not adding further water, but it may be that you need to scale down the amount of water delivered, so that it does not overshoot as much after the PID shuts off.
It also takes time for the water to soak into the soil and be measured by the sensor, so adding water a little at a time may help.
In a real environment, the plants may absorb some of the overshoot anyway, and help keep the moisture closer to the setpoint.

1 Like

I believe you got a point there ,I'll take it in consideration, thanks !

I think you need to sample the humidity much more often, you should have at least ten samples (preferably more) for an oscillation in the output. Since it appears to be oscillating with a period of about 20 minutes I think you should sample it every minute. That will mean it will react much quicker and should stop it adding as much water.

1 Like

I'm measuring the humidity each 30 sec (10 times/ 5 min) and send it to PID every 5 minutes, what I do is I calculate the average of the last 5 measures to send it to PID as an input, should I send it instead every minute,is that what you mean?

If you are measuring it every 30 secs then send that direct to the PID. The more information the PID algorithm has about the process the better. If it turns out that you need additional smoothing then worry about that later (and if so don't use a 5 minute average as that imposes a transport delay, which is a non-linear function, to the measurement, use an RC smoothing filter instead).

I used it but it isn't smoothing it at all, should I change the time 10 seconds in the filter ?

No, take the filter out entirely initially.
[Edit] or is it really horribly noisy? If so, how noisy, plot it on a chart to see.

1 Like

No it's not terribly noisy

In that case feed it straight into the PID node. Unless that will cause your output method a problem. How are you controlling the water feed?

I'm using a submerged 220 v water pump and a relay

Sorry, I should have been more clear. What algorithm are you using for driving the pump from the PID output?

Simply I'm using a range node (from 0 to 1 to 0 to 10sec) for the PID output, send it with MQTT to the ESP8266 NodeMCU and this last turn the relay on for the correspondant time.

1 Like

The problem with that is that the more often you run the PID algorithm the more water it will inject, so effectively if you increased the rate at which you run the PID you would have to increase the Prop Band to compensate for that. Instead you might like to consider using node-red-contrib-timeprop to drive the relay. That is designed for use with the PID node and will remove that problem.
I suggest also you consider what might be the maximum flow that you would ever need under normal operation. If, for example, you decided that the pump On 5% of the time might be the maximum then scale the output of the PID so that a value of 1 is scaled to 0.05 before feeding to the timeprop node. That will reduce the likelihood of over application of water on, for example, initial startup and will make it easier to control the process more accurately.

1 Like

@Colin I discovered that it is a hardware problem in the first place :confused: the sensor isn't giving me accurate values, I put it in the same spot and same conditions and watch the values keep in/decreasing by themselves, @Paul how did you get such accurate values ? I'm using the same sensor as yours but look at the results
changing

Is the soil temperature constant?
Although much better than a 'resistive' sensor, the 'capacitive' sensor readings are influenced by changing soil temperatures.

1 Like