Thoughts on backing off room thermostat when 10c or 5c or 0c outside

Hi,

I have coded my own Arduino thermostat that is controlled by Node-RED and I can set it to various temperature 21c to 24c in .5c increments. I have noticed that at the the moment it is 10c outside and the room heating overshoots by .4c, I would like to get it closet to .1c.

I will modify my Arduino code so that it can adjust the set point down by .1 c increments

As an example set Arduino thermostat to 21c.
My thoughts are measure temperature increase over 1 minute and if it has increased set Arduino thermostat to stop heating .2c earlier. ( That's a very simplified way of how I will do it )

For this I am going to use Node-RED and some sought of timer, but not sure what to use in Node-RED, as I am sure there are many ways to do this, what are your thoughts ?

Thanks

You should checkout the PID offering our very own @Colin offers.

demo flow: PID control of a heating/cooling system with node-red-contrib-pid (flow) - Node-RED

1 Like

I recommend using node-red-contrib-pid along with node-red-contrib-timeprop. The timeprop node will generate a time proportioned on/off signal to drive your heater. Modify the arduino code so that it just switches on/off as required by the timeprop signal.

Disclosure: I am the developer of both of those nodes.

Hi, Thanks Colin and Steve

Having thought about this more, I would like to develop simple code, that can be eventually incorporated into my Arduino thermostat code, so I can use if, then, millis, true, false ... etc

I have a working Node-RED code that checks for a difference in temperature and outputs that number and if the same outputs 0.

Also it sends true, but at the moment I am at a loss how to do check if this change is still true after 10 seconds has passed ( in the real code this will be over 5 minutes ) but for testing this is fine.

Here is my Node-RED code so far

[
    {
        "id": "91022929bf1fc133",
        "type": "inject",
        "z": "78823dae3d2dd990",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "20.0",
        "payloadType": "num",
        "x": 400,
        "y": 390,
        "wires": [
            [
                "bc6ef697189fa2a9"
            ]
        ]
    },
    {
        "id": "22cc28fa0c441d5a",
        "type": "inject",
        "z": "78823dae3d2dd990",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "20.01",
        "payloadType": "num",
        "x": 400,
        "y": 430,
        "wires": [
            [
                "bc6ef697189fa2a9"
            ]
        ]
    },
    {
        "id": "60bfe79be59ae3f1",
        "type": "inject",
        "z": "78823dae3d2dd990",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "20.2",
        "payloadType": "num",
        "x": 400,
        "y": 470,
        "wires": [
            [
                "bc6ef697189fa2a9"
            ]
        ]
    },
    {
        "id": "942bb207e1aa8841",
        "type": "debug",
        "z": "78823dae3d2dd990",
        "name": "debug 165",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 650,
        "y": 350,
        "wires": []
    },
    {
        "id": "bc6ef697189fa2a9",
        "type": "function",
        "z": "78823dae3d2dd990",
        "name": "Check against previous",
        "func": "\nvar previous = flow.get(\"previousvalue\") || 0;              // Get stored value, use payload if nothing is stored\nflow.set(\"previousvalue\", msg.payload);                     // Save this value for next time\n\nvar difference = msg.payload - previous;                    // Compare the two\nmsg.payload = difference;\n\n\nif (difference == 0) \n{\n   global.set(\"timer\", false);                        // Save this value for checking against time.\n   node.send(msg); \n}\nelse\n{\n   global.set(\"timer\", true);                         // Save this value for checking against time.\n   node.send(msg); \n}\n\n\n",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 630,
        "y": 390,
        "wires": [
            [
                "942bb207e1aa8841",
                "c62fcc1c2b353cd3"
            ]
        ]
    },
    {
        "id": "c62fcc1c2b353cd3",
        "type": "function",
        "z": "78823dae3d2dd990",
        "name": "Check rise in temperature against time",
        "func": " \nmsg.payload = global.get(\"timer\");\n\nif (msg.payload === true) \n{\n\n// Need code here to check if temperature has increased over 30 seconds\n\n node.send(msg); \n\n// If temperature has NOT increased over 30 seconds\n global.set(\"timer\", false);\n\n}\n\n\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 950,
        "y": 390,
        "wires": [
            [
                "c65b5322fa0ebc4b"
            ]
        ]
    },
    {
        "id": "c65b5322fa0ebc4b",
        "type": "debug",
        "z": "78823dae3d2dd990",
        "name": "debug 166",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 1010,
        "y": 350,
        "wires": []
    },
    {
        "id": "14053d0daad1e222",
        "type": "comment",
        "z": "78823dae3d2dd990",
        "name": "Temperatures",
        "info": "",
        "x": 380,
        "y": 350,
        "wires": []
    }
]

Personally I would do all this using the built in Switch and Trigger nodes, but if the purpose is to end up with code that you can move to the Arduino then I see why you want to write code to do it.

If you save the currrent time in milliseconds in context

const now = new Date().getTime() // current time in milliseconds
let lastSwitchTime = context.get("lastSwitchTime") ?? 0 // time last switched
...
// compare current time with last switched time
...
// when appropriate save the time switched
context.set("lastSwitchTime", now)
...

Hi Colin,

I have something close to what I want ( I had to resort to asking chatgpt ) :roll_eyes:

I an fine writing the code for Arduino as I have something similar already, but as my central heating and the Arduino are running my house heating at the moment, I thought it would be better to do all testing in Node-RED and simple send the command to my Arduino to change it's 'target temperature'. by 0.2 c etc

My only concern is if we have a power cut, I have to manually start Node-RED, so it would be better if eventually I have the code in my Arduino as that does auto recover. :+1:

My code so far ( as I have not written this does it all look ok or anything could be improved )
Thanks

const currentTemp = msg.payload;
const previousTemp = context.get('previousTemp') || currentTemp;
const lastTimestamp = context.get('lastTimestamp') || 0;
const currentTime = Date.now();

// Check if the last reading was within xx seconds
if (currentTime - lastTimestamp <= 30000) {
    // Compare temperatures
    if (currentTemp > previousTemp) {
        msg.payload = true;
    } else {
        msg.payload = false;
    }
} else {
    // If more than xx seconds, reset previous temperature
    msg.payload = false;
}

// Store current temperature and timestamp
context.set('previousTemp', currentTemp);
context.set('lastTimestamp', currentTime);

return msg;

There are ways to autostart node red on boot. What OS are you running on?

As in my suggested code, I suggest you work directly in msec, using getTime() to get that. The interpretor has to do that anyway any time you do arithmentic on a Date object so it is marginally more efficient to do that conversion once. Also the code analyser may give warnings when you do arithmetic on Dates as it does not know about the implicit conversion that will happen.

That code will fail to do what you expect if ever the previous temp is zero. You should use the newer ?? operator which tests for undefined and null rather than anything that is falsy. So use
const previousTemp = context.get('previousTemp') ?? currentTemp;

Hi Colin,

I am using linux OS and Node-RED is running in Docker ( I know you dislike that, but I wanted to learn how to use it ). I can most probably start Docker if a power cut, but I also like my Arduino thermostat to run in a default mode set to 22c , without requiring Node-RED to be running.

I use Node-RED to adjust the temperature as my Arduino has no display i just use Node-RED GUI..

Thanks for the other suggestions, so much to learn over the years that I have been developing my home automation, I can do the code I need to in Node-RED and Arduino but certainly not anywhere near a master, so that's why I ask from time to time the wise people on here :+1:

When someone points something out I always look for more info so I learn from it. :thinking:

Thanks, I may well be back when I am further down the line on this particular code.

I don't dislike Docker, it is a tool which is excellent when it is the appropriate tool. For most node-red users it adds just complexity for little, if any, gain.

Yes i agree, but in my case I knew I wanted Node-RED and grafana and sqlite and PostgreSQL as well.

So I gradually learnt how to use these without corrupting my linux OS and at worst just crashing Docker.

As I said I am no expert but I get by :upside_down_face:

Anyway thank you so much for your help and comments, i have just watched a video and learnt what ?? means.

1 Like

Hi,

Just modified my code purely for testing and if the temperature is equal or above 20.3 to send out a command to my Arduino and now I have an error ?

Not sure if this is a problem and thoughts ?

Thanks

I did try const currentTemp = msg.payload ?? 0; but it didn't change that error

=> is a fat arrow function. >= is the "Greater than or Equal to" operator.

Thanks steve my silly mistake :roll_eyes: