Offtopic - Building a PID controller is easy!


 

Bryan Mayland

TVWBB Hall of Fame
A few years back I got fed up with critters eating all my peppers in the garden so I started growing them indoors with a grow light to much success. You know I can't have anything without covering it in sensors and electronics so obviously the whole thing is now monitored and controlled within an inch of its life with no fewer than 7 wifi-enabled ports doing various jobs from running the irrigation pump, to controlling the air circulation, and of course just turning the light on/off/dim. This weekend I wanted to try controlling the air circulator to maintain a constant humidity point, as the San Marzano tomatoes I am growing just put out a TON of moisture to the point they were getting moldy. There was already a controller in there with a BME280 temp/humidity sensor in there reporting back to Node-RED every 5 minutes so how hard can it be?

Code:
    var h = Number(msg.payload.ENV.Humidity);
    if (ctx.lastH === 0)
      ctx.lastH = h;
    var sp = ctx.config.setpoint;
    var delta = h - sp;
    var p = delta * ctx.config.p;
    var i = ctx.sumI + (delta * ctx.config.i);
    var d = (h - ctx.lastH) * ctx.config.d;
    var out = p + i + d;
    if (out < 100.0 && out > 0.0)
      ctx.sumI = i;
    ctx.lastH = h;
That is the entirety of the PID controller code (apart from publishing the 'out' variable)! Even with the 5 minute reporting interval it can respond to a 10% setpoint change in just 4 or 5 updates. It just amuses me how trivial it is to implement controller code without using any sort of library. And hopefully, tomatoes in my near future.
 
Neat!

Where do ctx.lastH, ctx.config and ctx.sumI come from?

I've been getting my own feet with with javascript lately, but very tip-of-the-iceberg level stuff.
 
Node-red nodes have a state object so they can store information across multiple messages. Actually I think you can persist as many variables as you want, but I like to keep them all bundled into one object. The first line of code (which I omitted because it's just the guts of node-red)
Code:
var ctx = context.get('ctx') || {
        lastH: 0,
        sumI: 0.0,
        config: {
            setpoint: 65.0,
            p: 3.0, i: 1.5, d: 2.0
        }
    };
The last line also saves it because it isn't automatic
Code:
    context.set('ctx', ctx);

I could have just hard-coded the config into the code and eliminated it completely, but I thought it would be better to be able to be changed on the fly. In fact, now I've already modified it so it has a daytime and nighttime humidity setpoint and switches based on if the light is on or off. So the long answer here is that they are just variables that persist from message to message, with each message being a new humidity update.
 

 

Back
Top