Congrats, glad you are starting to figure it out. Once the basics start making sense, the pieces fall in place very quickly.
Did you not like the look of node-red-contrib-hysteresis?
I haven't added that complication yet.
I need to see if I can simplify the nodes a bit. They work but could be simpler. I want to use a variable that can be set from the dashboard as the humidity limit and one for the temperature limits. I need to improve the graphs to show all this. Need to add iPhone dashboard. And it would be nice to receive error messages so I will know if there are issues. Of course, the 8266s need to be improved to allow OTA flashing, error message handling, reconnecting MQTT, reconnecting WiFi, rejecting bad or questionable data. I also want to improve the backup system and to auto backup.
So the road is long for me, but I have made a few of the initial steps. I suspect the hysteresis flow may help with some of this. But I want this to run so an idiot can interact with it, not so only a sophisticated programmer can use it.
It is a simplification, not a complication. That is why I suggested it.
You don't say what firmware you are running on the ESP8266. I recommend that you at least take a look at Tasmota because it greatly simplifies regular sending of sensor data via MQTT.
Here is an example message from Tasmota with the base topic set as "bathroom". Note this is BME680 sensor not a DHT22. The message structure is basically automatic, no coding required.
topic: "tele/bathroom/SENSOR",
payload: {
"Time":"2024-04-29T09:24:02",
"BME680": {
"Temperature":16.1,
"Humidity":61.1,
"DewPoint":8.6,
"Pressure":993.8,
"SeaPressure":1011.2,
"Gas":82.06
},
"PressureUnit":"hPa",
"TempUnit":"C"
}
I think you may be making a mistake in trying to write your application using function nodes and context variables.
A simple flow using the hysteresis node: EDIT - fixed sample data inject.
[{"id":"642af639b1f4853b","type":"inject","z":"4bd13f6aa8701f95","name":"75","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"BME680\":{\"Humidity\":75,\"Temperature\":29}}","payloadType":"json","x":110,"y":200,"wires":[["1d7e23d969d1ee36"]]},{"id":"811803b29776f936","type":"inject","z":"4bd13f6aa8701f95","name":"80","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"BME680\":{\"Humidity\":80,\"Temperature\":31}}","payloadType":"json","x":110,"y":240,"wires":[["1d7e23d969d1ee36"]]},{"id":"4dee9df2a586e14e","type":"group","z":"4bd13f6aa8701f95","name":"Simulate input data","style":{"label":true},"nodes":["a2bc44d821eab67e","1d7e23d969d1ee36","4c49ca3c36f1c4ef"],"x":14,"y":119,"w":452,"h":202},{"id":"a2bc44d821eab67e","type":"inject","z":"4bd13f6aa8701f95","g":"4dee9df2a586e14e","name":"58","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"BME680\":{\"Humidity\":58,\"Temperature\":27}}","payloadType":"json","x":110,"y":160,"wires":[["1d7e23d969d1ee36"]]},{"id":"1d7e23d969d1ee36","type":"mqtt out","z":"4bd13f6aa8701f95","g":"4dee9df2a586e14e","name":"","topic":"tele/bathroom/SENSOR","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"91cc30a194bcd3b5","x":330,"y":220,"wires":[]},{"id":"4c49ca3c36f1c4ef","type":"inject","z":"4bd13f6aa8701f95","g":"4dee9df2a586e14e","name":"82","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"BME680\":{\"Humidity\":82,\"Temperature\":29}}","payloadType":"json","x":110,"y":280,"wires":[["1d7e23d969d1ee36"]]},{"id":"91cc30a194bcd3b5","type":"mqtt-broker","name":"Glasspi","broker":"192.168.1.11","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"autoUnsubscribe":true,"birthTopic":"27/birth","birthQos":"0","birthRetain":"false","birthPayload":"hello","birthMsg":{},"closeTopic":"27/close","closeQos":"0","closeRetain":"false","closePayload":"Leaving","closeMsg":{},"willTopic":"27/lwt","willQos":"0","willRetain":"false","willPayload":"I'm dead","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"12bc3d2b544410a3","type":"group","z":"4bd13f6aa8701f95","name":"Show output","style":{"label":true},"nodes":["68fdedf2dfecb1d5","89b6ef749366bf5c"],"x":14,"y":339,"w":452,"h":82},{"id":"68fdedf2dfecb1d5","type":"mqtt in","z":"4bd13f6aa8701f95","g":"12bc3d2b544410a3","name":"","topic":"cmnd/bathroom/fan","qos":"2","datatype":"auto-detect","broker":"91cc30a194bcd3b5","nl":false,"rap":true,"rh":0,"inputs":0,"x":130,"y":380,"wires":[["89b6ef749366bf5c"]]},{"id":"89b6ef749366bf5c","type":"debug","z":"4bd13f6aa8701f95","g":"12bc3d2b544410a3","name":"Control fan","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":350,"y":380,"wires":[]},{"id":"475a26711c84142c","type":"group","z":"4bd13f6aa8701f95","name":"Simple Fan Control flow","style":{"label":true},"nodes":["196efbae5c51f030","b6fbb603345ff2fe","fbf58f5232d4bb24","c0568e20ad05db46"],"x":14,"y":19,"w":812,"h":82},{"id":"196efbae5c51f030","type":"mqtt in","z":"4bd13f6aa8701f95","g":"475a26711c84142c","name":"","topic":"tele/bathroom/SENSOR","qos":"2","datatype":"auto-detect","broker":"91cc30a194bcd3b5","nl":false,"rap":true,"rh":0,"inputs":0,"x":140,"y":60,"wires":[["c0568e20ad05db46"]]},{"id":"b6fbb603345ff2fe","type":"hysteresis","z":"4bd13f6aa8701f95","g":"475a26711c84142c","name":"80 = ON, 60 = OFF","ThresholdType":"fixed","ThresholdRising":"80","ThresholdFalling":"60","TopicThreshold":"","TopicCurrent":"","ThresholdDeltaRising":"","ThresholdDeltaFalling":"","InitialMessage":false,"OutRisingType":"str","OutRisingValue":"ON","OutFallingType":"str","OutFallingValue":"OFF","OutTopicType":"str","OutTopicValue":"cmnd/bathroom/fan","DynRaiseError":false,"x":530,"y":60,"wires":[["fbf58f5232d4bb24"]]},{"id":"fbf58f5232d4bb24","type":"mqtt out","z":"4bd13f6aa8701f95","g":"475a26711c84142c","name":"Control fan","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"91cc30a194bcd3b5","x":730,"y":60,"wires":[]},{"id":"c0568e20ad05db46","type":"change","z":"4bd13f6aa8701f95","g":"475a26711c84142c","name":"Humidity","rules":[{"t":"move","p":"payload.BME680.Humidity","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":60,"wires":[["b6fbb603345ff2fe"]]}]
The hysteresis node offers "Dynamic Thresholds" to set the on/off point dynamically.
You can pass your hysteresis outputs through switch node (passthrough option checked) which will allow you to control the fan both manually and automatically according to sensor data.
I hadn't considered that learning a new node type and dealing with hysteresis would be simpler than ignoring hysteresis. I will reconsider.
When I started this project, I just programmed the NodeMCUs (8266s) using the Arduino IDE. The actual code to send the MQTT message is only a line or two, but there is a bit more to get to that point. Would Tasmota be simpler (i.e., log on to WiFi, read the data, publish over MQTT, sustain WiFi connectivity and MQTT connectivity, log and send error messages, permit over the air re-flashing to update the code, etc.)?
My basic goal is pretty simple. If it is too humid or too hot in the room, turn on the 3 fans. If it is not too humid or too hot, turn them off. If it is too hot in the room open the louvers. If it is too humid in the room and it is less humid outside, open the louvers while the fans are running. Permit manual timed overrides.
I am pretty unskilled at all this. I want to design a system that requires no skills to operate and that will operate from a Pi and a remote screen in the room. Ideally, it would be a stable system and self-healing should predictable issues arise (e.g., losing connectivity or needing a reset after a power loss).
So far, I have kludged together a system that monitors temp and humidity inside and out and displays that on the dashboard. I have switches on the dashboard that show the fan and louver status and which can operate the fans and louver. I now have the automated portion working (too humid on, not too humid off).
I am open to changes that will improve performance and simplify matters.
Thoughts and suggestions are welcome. And, BTW how do you copy the flow pictures into your posting. I don't see a way.
You said in your first post:
If you don't want to have any hysteresis (which will mean that the fans will go on and off with sensor noise when close to the setpoint) then don't use the hysteresis node.
Thanks. I will look at it.
Installing Tasmota on your ESP (Either by visiting https://tasmota.github.io/install/ with a Chrome based browser or else with the Tasmotizer app) takes some getting used to but probably simpler than learning to use the Arduino IDE - which you have already done!
Once installed there is a web based admin page.
You have to tell it which pins the sensor is connected to
Your Wifi credentials
Your MQTT server credentials
Successful setup results in sensor readings being displayed on the ESP's home screen
After that it automatically handles wifi/mqtt connection and reconnection, collection and publishing sensor data.
Yes you can update the firmware OTA but the advice is if you don't need any newly introduced features, don't update.
You can change the default reporting interval, request immediate readings and many other options by sending MQTT messages. No reflash is needed.
As you can see, I like Tasmota. Other people probably prefer OpenHasp or other low code firmware.
I use the Windows Snipping Tool in W11 (yuk) and paste it into my reply by good old CTRL V
Thanks. The Tasmota output sure seems easier than the Arduino IDE.
Do you guys like the DHT22 or is there a better alternative?
Colin makes a point about the hysteresis flow (now I turn fans on at one humidity and turn them off at a lower humidity for a poor man's version).
I have to drop this project tomorrow for a trip. But when I get back, I'll update on my progress.
My 2cents. I like the Arduino better than Tasmota even though there is a little bit more of a learning curve. In pondering this I think it's because there is so much more I can do with an Arduino or maybe it's because I can make the arduino do exactly what I want it to. I have about a dozen arduino's doing various things and for the most part after a learning curve and debugging period they all do what I need. They do OTA and all the other cool stuff. I use DHT22's but have several BME's laying around. The spec's on the BME280 is better but I started with the DHT and it has all the resolution I need. Also, while trying to get the BME280 I kept getting the BMP which does not have humidity. Or it may be the other way around I'm working from memory now. Even when the advert specifically said the humidity model I got the other one. Eventually I gave up although I just ordered two more and will see when they get in if they are actually the humidity ones. To @Colin 's point on hysteresis you can do that in a function node and it is a useful feature if not in reality a necessary one.
Thanks for your 2 cents. I do have some abilities with Arduino IDE and don't really want to switch to Tasmota, if I can avoid it. The DHT 22s work fine, they just seem a bit rinky-dink. Certainly they have sufficient accuracy for me. Right now the hysteresis is an open item. My solution tests a specific humidity number or a specific temperature number. I could kludge together a different number for on vs off. And I can have a user interface to set that number and the difference between the two to avoid short cycling. I may only test temp and humidity once a minute or so, so it is possible that hysteresis isn't a big issue, but I will look at Colin's suggestion.
This is the current flow (the small portion under discussion):
We'll see if the snipping tool will include the image.
Apologies to Colin, the hysteresis node works well. So far so good.
Here's the flow so far:
Things seem to work well. Now I have an issue with my dashboard. Using almost identical code, I end up with nonsense for the left axis on pool room while the outdoor stuff seems fine.
The data is input using MQTT and the gauge and graph have the exact same payloads, so why is the Y axis so weird?
Here is the underlying code:
[
{
"id": "ebbaaf1fbb49d248",
"type": "mqtt in",
"z": "1e6d0cc92960bc78",
"name": "",
"topic": "indoor_temp_topic",
"qos": "0",
"datatype": "auto-detect",
"broker": "c6bf5d021264d8e1",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 170,
"y": 180,
"wires": [
[
"654176514a578ae5",
"f495317ab40f3852",
"a6eb72edd2662bb8"
]
]
},
{
"id": "82604fbed3287434",
"type": "mqtt in",
"z": "1e6d0cc92960bc78",
"name": "",
"topic": "indoor_humidity_topic",
"qos": "0",
"datatype": "auto-detect",
"broker": "c6bf5d021264d8e1",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 180,
"y": 300,
"wires": [
[
"0eec4248f657dcab",
"3a58ae216556eea4",
"fc0b23b51621a17c"
]
]
},
{
"id": "654176514a578ae5",
"type": "ui_gauge",
"z": "1e6d0cc92960bc78",
"name": "",
"group": "570d12a99fb5ce3d",
"order": 4,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Pool Room Temperature",
"label": "°F",
"format": "{{value}} °F",
"min": 0,
"max": "110",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 510,
"y": 200,
"wires": []
},
{
"id": "0eec4248f657dcab",
"type": "ui_gauge",
"z": "1e6d0cc92960bc78",
"name": "",
"group": "570d12a99fb5ce3d",
"order": 5,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Pool Room Humidity ",
"label": "% RH",
"format": "{{value}}% RH",
"min": 0,
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "45",
"seg2": "59",
"diff": false,
"className": "",
"x": 500,
"y": 300,
"wires": []
},
{
"id": "3a58ae216556eea4",
"type": "ui_chart",
"z": "1e6d0cc92960bc78",
"name": "",
"group": "570d12a99fb5ce3d",
"order": 7,
"width": 0,
"height": 0,
"label": "Pool Room Humidity",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": "1",
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 500,
"y": 340,
"wires": [
[]
]
},
{
"id": "f495317ab40f3852",
"type": "ui_chart",
"z": "1e6d0cc92960bc78",
"name": "",
"group": "570d12a99fb5ce3d",
"order": 6,
"width": "0",
"height": "0",
"label": "Pool Room Temperature",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 510,
"y": 160,
"wires": [
[]
]
},
{
"id": "a6eb72edd2662bb8",
"type": "function",
"z": "1e6d0cc92960bc78",
"name": "inTemp",
"func": "flow.get(\"GV_Indoor_Temp\");\nglobal.set(\"GV_Indoor_Temp\", msg.payload);\nreturn msg;",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 360,
"y": 120,
"wires": [
[
"f40ea75a5aa47093"
]
]
},
{
"id": "fc0b23b51621a17c",
"type": "function",
"z": "1e6d0cc92960bc78",
"name": "InHumidy",
"func": "flow.get(\"GV_Indoor_Humidity\");\nglobal.set(\"GV_Indoor_Humidity\", msg.payload);\nreturn msg;",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 360,
"y": 260,
"wires": [
[
"b41f051f986e2444"
]
]
},
{
"id": "b41f051f986e2444",
"type": "link out",
"z": "1e6d0cc92960bc78",
"name": "Indoor Humidity",
"mode": "link",
"links": [
"be08956c157be950"
],
"x": 625,
"y": 260,
"wires": []
},
{
"id": "f40ea75a5aa47093",
"type": "link out",
"z": "1e6d0cc92960bc78",
"name": "Indoor Temp",
"mode": "link",
"links": [
"590d94db0ef1ab01",
"f211bb67c0fddbd4"
],
"x": 625,
"y": 120,
"wires": []
},
{
"id": "c6bf5d021264d8e1",
"type": "mqtt-broker",
"name": "",
"broker": "192.168.50.168",
"port": "1883",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"autoUnsubscribe": true,
"birthTopic": "",
"birthQos": "1",
"birthRetain": "true",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closeRetain": "false",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willRetain": "false",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
},
{
"id": "570d12a99fb5ce3d",
"type": "ui_group",
"name": "",
"tab": "08c882008efdc3a0",
"order": 2,
"disp": true,
"width": "6",
"collapse": false,
"className": ""
},
{
"id": "08c882008efdc3a0",
"type": "ui_tab",
"name": "Pool Room Humidity Control",
"icon": "dashboard",
"order": 1,
"disabled": false,
"hidden": false
}
]
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.