Fermentation Controller

I'm working on a fermentation management system for controlling tank temperatures during fermentation in a commercial winery on 17 different tanks. This is using some temperature probes from IOT4.eu and some solenoid valves hooked up to DLI power strips with all communication going through MQTT. This is my first NR project and all of my programming experience is from quite a while ago. This is my example control for one tank and I have 17 tanks total to control. Ideally, this will all be working with a dashboard to graph all of the tanks that are currently "on", controlling temperature and flexibility to control the upper and lower setpoints for the temperature range. I'm interested in any efficiencies in the design that I'm not seeing and I have a few questions below.

Thoughts on how best to turn off controls for specific tanks when temperature control isn't wanted on those tanks? I'd like to tie this into the dashboard as well and have an on-off switch for each tank with the setpoints for each tank.

What's the best option for doing a graph with data from Influxdb? Keeping everything in NR would be ideal but Grafana seems to have some really good options for this but then I would have to have setpoint controls in a different interface from the graph.

It took a little while to work through the logic of controlling the power strip but I think this is pretty good - NR continuously feeds the power strip with what it wants to do, just to make sure that the valve is in the correct state at all times instead of relying on a single command. The command is the same until the temperature determines that it should be different. The power strip will return to its prior state in the event of a power failure as well.

[
    {
        "id": "f6200681b265563b",
        "type": "tab",
        "label": "Prod",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "a95558832e711631",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "TankT1-On",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 610,
        "y": 40,
        "wires": []
    },
    {
        "id": "7c8d261e5f6d0998",
        "type": "mqtt out",
        "z": "f6200681b265563b",
        "name": "",
        "topic": "Power1/outlet/1/set",
        "qos": "2",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "8aa408ca715b9482",
        "x": 720,
        "y": 100,
        "wires": []
    },
    {
        "id": "79ef3fe1a242a713",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "TankT1-Off",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 610,
        "y": 160,
        "wires": []
    },
    {
        "id": "bd1daf1565ee885c",
        "type": "change",
        "z": "f6200681b265563b",
        "name": "Upper",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "1",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 510,
        "y": 80,
        "wires": [
            [
                "7c8d261e5f6d0998"
            ]
        ]
    },
    {
        "id": "897ba8f3f231a1f0",
        "type": "change",
        "z": "f6200681b265563b",
        "name": "Lower",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "0",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 510,
        "y": 120,
        "wires": [
            [
                "7c8d261e5f6d0998"
            ]
        ]
    },
    {
        "id": "83830bf4b0698084",
        "type": "mqtt in",
        "z": "f6200681b265563b",
        "name": "TankT1",
        "topic": "TankT1-Temp/temperature",
        "qos": "2",
        "datatype": "auto",
        "broker": "8aa408ca715b9482",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 90,
        "y": 40,
        "wires": [
            [
                "7b73ace1b6cdb759"
            ]
        ]
    },
    {
        "id": "49cd2c020d59c024",
        "type": "influxdb out",
        "z": "f6200681b265563b",
        "influxdb": "2e2baf68b726c3bb",
        "name": "Temp",
        "measurement": "Temp",
        "precision": "",
        "retentionPolicy": "",
        "database": "database",
        "precisionV18FluxV20": "ms",
        "retentionPolicyV18Flux": "",
        "org": "organisation",
        "bucket": "bucket",
        "x": 250,
        "y": 20,
        "wires": []
    },
    {
        "id": "bccca8d40a6d63f8",
        "type": "switch",
        "z": "f6200681b265563b",
        "name": "Valve",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "gt",
                "v": "TankT1-upper",
                "vt": "flow"
            },
            {
                "t": "lte",
                "v": "TankT1-lower",
                "vt": "flow"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 290,
        "y": 100,
        "wires": [
            [
                "bd1daf1565ee885c",
                "a95558832e711631"
            ],
            [
                "897ba8f3f231a1f0",
                "79ef3fe1a242a713"
            ],
            [
                "e016d9b888302a93"
            ]
        ]
    },
    {
        "id": "e016d9b888302a93",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "In-Range",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 400,
        "y": 160,
        "wires": []
    },
    {
        "id": "7b73ace1b6cdb759",
        "type": "unit-converter",
        "z": "f6200681b265563b",
        "category": "temperature",
        "inputUnit": "F",
        "outputUnit": "F",
        "inputField": "payload",
        "outputField": "payload",
        "inputFieldType": "msg",
        "outputFieldType": "msg",
        "roundOutputField": true,
        "outputFieldDecimals": "1",
        "name": "Convert",
        "x": 100,
        "y": 100,
        "wires": [
            [
                "49cd2c020d59c024",
                "876bf077bc5133ad",
                "bccca8d40a6d63f8",
                "fa5eeedaed9f31e2"
            ]
        ]
    },
    {
        "id": "10ba642e6415f895",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "80",
        "payloadType": "num",
        "x": 130,
        "y": 260,
        "wires": [
            [
                "e2df700bbffcf19c"
            ]
        ]
    },
    {
        "id": "5845311c332faad2",
        "type": "comment",
        "z": "f6200681b265563b",
        "name": "Upper limit",
        "info": "",
        "x": 120,
        "y": 220,
        "wires": []
    },
    {
        "id": "c8ab9cc83c737ff8",
        "type": "comment",
        "z": "f6200681b265563b",
        "name": "Lower limit",
        "info": "",
        "x": 580,
        "y": 220,
        "wires": []
    },
    {
        "id": "694b4fe4d5a16fe4",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "75",
        "payloadType": "num",
        "x": 130,
        "y": 300,
        "wires": [
            [
                "e2df700bbffcf19c"
            ]
        ]
    },
    {
        "id": "ba17d2d3bffdffdc",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "72",
        "payloadType": "num",
        "x": 130,
        "y": 340,
        "wires": [
            [
                "e2df700bbffcf19c"
            ]
        ]
    },
    {
        "id": "d00cc0bcd37d570a",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "77",
        "payloadType": "num",
        "x": 590,
        "y": 260,
        "wires": [
            [
                "281d2c0adf9fce83"
            ]
        ]
    },
    {
        "id": "36355bdc381b94de",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "71",
        "payloadType": "num",
        "x": 590,
        "y": 300,
        "wires": [
            [
                "281d2c0adf9fce83"
            ]
        ]
    },
    {
        "id": "0d755e19e6dfa112",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "65",
        "payloadType": "num",
        "x": 590,
        "y": 340,
        "wires": [
            [
                "281d2c0adf9fce83"
            ]
        ]
    },
    {
        "id": "e2df700bbffcf19c",
        "type": "change",
        "z": "f6200681b265563b",
        "name": "TankT1-Upper Temp",
        "rules": [
            {
                "t": "set",
                "p": "TankT1-upper",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 340,
        "y": 260,
        "wires": [
            [
                "b1e07a828296e378"
            ]
        ]
    },
    {
        "id": "281d2c0adf9fce83",
        "type": "change",
        "z": "f6200681b265563b",
        "name": "Lower Temp",
        "rules": [
            {
                "t": "set",
                "p": "TankT1-lower",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 750,
        "y": 260,
        "wires": [
            [
                "e777b04cf3dcd444"
            ]
        ]
    },
    {
        "id": "b1e07a828296e378",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "Upper Temp",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 350,
        "y": 320,
        "wires": []
    },
    {
        "id": "e777b04cf3dcd444",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "Lower Temp",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 780,
        "y": 330,
        "wires": []
    },
    {
        "id": "876bf077bc5133ad",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 430,
        "y": 20,
        "wires": []
    },
    {
        "id": "243b63027f532058",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "TankT1-Upper",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "TankT1-upper",
        "payloadType": "flow",
        "x": 110,
        "y": 500,
        "wires": [
            [
                "d6968efefdcf0985"
            ]
        ]
    },
    {
        "id": "d6968efefdcf0985",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "Tank-T1Setpoints",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 310,
        "y": 520,
        "wires": []
    },
    {
        "id": "5e673bd9ab566df8",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "TankT1-Lower",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "TankT1-lower",
        "payloadType": "flow",
        "x": 110,
        "y": 540,
        "wires": [
            [
                "d6968efefdcf0985"
            ]
        ]
    },
    {
        "id": "fd8336d590e9745e",
        "type": "ui_chart",
        "z": "f6200681b265563b",
        "name": "",
        "group": "a23b2bf2f261c86b",
        "order": 1,
        "width": "6",
        "height": "6",
        "label": "Tank-Graph",
        "chartType": "line",
        "legend": "true",
        "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": 90,
        "y": 440,
        "wires": [
            []
        ]
    },
    {
        "id": "d6526e17959cd5b1",
        "type": "ui_numeric",
        "z": "f6200681b265563b",
        "name": "",
        "label": "TankT1-Upper",
        "tooltip": "",
        "group": "2df3817beb943471",
        "order": 2,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": false,
        "topic": "topic",
        "topicType": "msg",
        "format": "{{value}}",
        "min": "60",
        "max": "75",
        "step": 1,
        "className": "",
        "x": 100,
        "y": 380,
        "wires": [
            [
                "e2df700bbffcf19c"
            ]
        ]
    },
    {
        "id": "fa5eeedaed9f31e2",
        "type": "warp",
        "z": "f6200681b265563b",
        "name": "",
        "scope": [
            "fd8336d590e9745e"
        ],
        "destination": "selected",
        "x": 240,
        "y": 160,
        "wires": []
    },
    {
        "id": "73cf7760eb69342b",
        "type": "ui_numeric",
        "z": "f6200681b265563b",
        "name": "TankT1-Lower",
        "label": "TankT1-Lower",
        "tooltip": "",
        "group": "2df3817beb943471",
        "order": 2,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": true,
        "topic": "topic",
        "topicType": "msg",
        "format": "{{value}}",
        "min": "60",
        "max": "75",
        "step": 1,
        "className": "",
        "x": 600,
        "y": 380,
        "wires": [
            [
                "281d2c0adf9fce83"
            ]
        ]
    },
    {
        "id": "8aa408ca715b9482",
        "type": "mqtt-broker",
        "name": "MQTTBroker",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    },
    {
        "id": "2e2baf68b726c3bb",
        "type": "influxdb",
        "hostname": "127.0.0.1",
        "port": "8086",
        "protocol": "http",
        "database": "sensors",
        "name": "Sensors",
        "usetls": false,
        "tls": "",
        "influxdbVersion": "1.x",
        "url": "http://localhost:8086",
        "rejectUnauthorized": true
    },
    {
        "id": "a23b2bf2f261c86b",
        "type": "ui_group",
        "name": "Graph",
        "tab": "75e2c4a4bae21b23",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "2df3817beb943471",
        "type": "ui_group",
        "name": "TankT1-Setpoints",
        "tab": "75e2c4a4bae21b23",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "75e2c4a4bae21b23",
        "type": "ui_tab",
        "name": "Home",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]

You better specify the required nodes in this flow. Not all users potentially able to help you have them in the palette

Have you looked at node-red-contrib-uibuilder for the graphs?

Is the process time/ timing of control critical?

Some nice wine would help while thinking about that :wink:

Maybe Billy Jack - @9toejack could offer some advice. He's our resident brewing expert :grinning:

I am on a vacation trip with wife for our 25th anniversary, i can read pist thoroughly later today. And ill try to help. But i have my fermenters controlled with solenoid valves and they work great with esp8266 with tasmota installed.

1 Like

@ryanprel
I do not use influxdb, nor do i use powestrips for control,

I use Dallas 1 wire temp sensor, 3 relays and esp8266 flashed with tasmota, also with a pressure trasducet.

Obviously with wine you shouldn't have a need for co2 pressure, however if you where to have an issue with your air lock being clogged you could use one as a warning device to detect a problem.

I had help with function node to control temp.
I also created a "babysitting flow" that is basically a watch dog for the sensor mqtt message, if it stops receiving an mqtt message from the sensor, it sends email notifying me if the issue. And also send a reboot command to my esp8266 , this reconnected the temp sensor and i receive a new email that is working again

If interested i can share my flows. However im out of town. So i won't be able to until Friday evening usa west coat time.

@Paul-Reed I'm pretty sure I'm not the only brewer here, wbr has brewery and distillery i belive. But having an opportunity to help others is awesome. Thanks for the tag.

Thanks for the info! Not a big rush on anything - my hard deadline on getting everything finalized is about a month away - when grapes start arriving. I'm converting from an old Ubiquiti Mfi setup so all of my solenoid valves were running off power strips with plug ends already so going with a network connected power strip was an easy conversion. The temp sensors are also super easy, already encased and relatively water resistant, wi-fi and just require power. Since they handled MQTT easily as well, that was another easy option. I was looking for commercially available hardware that I didn't have to worry about finagling together. For under $1,500 I can switch over my entire system to this - relatively cheap for the number of tanks I'm controlling - around $80/tank.

I'd definitely be interested in the watchdog logic - that's something I was thinking through the other night. I'll have to figure out if a reboot command can be sent to the temp sensors I'm working with.

Have you worked with any tilt hydrometers? That's a feature I'd love to add into this and really provide the granular data on fermentation progression.

While you're using a few different components, I'm sure the logic on a number of these items would probably be similar.

Power strip

Temp sensor
https://iot4.eu/product-category/smart-home/

node-red-contrib-influxdb
node-red-contrib-Warp
node-red-dashboard

I looked at uibuilder but didn't get too far into it - the complexity seems like more than I want to deal with. I might take a second look, though, if I can find some good examples to work from.

Timing within a few minutes is fine - this isn't quite as time-sensitive as brewing. With this setup, I'm getting an average of 6 temp readings every 30 seconds and that's plenty of granularity for me.

I am not using them, how ever I would love to try them out. I'm very skeptical that they will send data through a stainless fermenter with jackets.

Where is the wineryb located. In assuming by your time of response you are in USA , not in the other side of the pond.
Cheers

Here's a DIY option but I haven't really dug into it yet. I'm located in Wisconsin, USA.

iSpindel Hydrometer
https://www.opensourcedistilling.com/ispindel/

Honestly, I believe that's how tilt started! I use pressure transducers to calculate volume in my kettles for brewing beer, ironically enough using 2 in boil kettle gives me live specific gravity of my wort.

If you think you will target graphs covering longer periods, you need to use something else than the dashboard chart nodes. Many people are using uibuilder- Personally I use Highcharts for all my charts

Hard not to see graphs as a big weak point of node-red out of the box. It's definitely a need of mine - I"ll start digging into Highcharts as it looks like roughly what I need.

If you need to keep the data in order to look back at how previous batches performed then I suggest using influxdb and grafana.

2 Likes

I wish I would've known about those before I built this monstrosity: https://i.imgur.com/6TbdgGT.jpeg

I added on a dashboard Off/On switch that leads to two Gate controls to control the flow of MQTT data and using this as an Off/On switch to control the flows. Also added on a deadman switch, linked to a telegram bot, although telegram isn't entirely setup yet - need to figure out how to send data through. The gate node is not automatically starting in the correct position, even though "Restore from state saved in Memory" is selected in the node - it always reverts to closed on fresh deployment.

The dashboard is showing the upper and lower setpoints starting at the default minimum of the range (60) for each upper and lower. Not sure how to "load" those setpoints on startup from whatever it was previously set to.

All-around at least making some progress.

Gate: node-red-contrib-simple-gate
Deadman Switch: node-red-contrib-deadmanswitch
Telegrambot: node-red-contrib-telegrambot
Warp: node-red-contrib-warp
Unit Converter: node-red-contrib-unit-converter
InfluxDB: node-red-contrib-influxdb

[
    {
        "id": "f6200681b265563b",
        "type": "tab",
        "label": "Prod",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "a95558832e711631",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "TankT1-On",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 870,
        "y": 80,
        "wires": []
    },
    {
        "id": "7c8d261e5f6d0998",
        "type": "mqtt out",
        "z": "f6200681b265563b",
        "name": "",
        "topic": "Power1/outlet/1/set",
        "qos": "2",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "8aa408ca715b9482",
        "x": 980,
        "y": 140,
        "wires": []
    },
    {
        "id": "79ef3fe1a242a713",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "TankT1-Off",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 870,
        "y": 200,
        "wires": []
    },
    {
        "id": "bd1daf1565ee885c",
        "type": "change",
        "z": "f6200681b265563b",
        "name": "Upper",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "1",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 770,
        "y": 120,
        "wires": [
            [
                "7c8d261e5f6d0998"
            ]
        ]
    },
    {
        "id": "897ba8f3f231a1f0",
        "type": "change",
        "z": "f6200681b265563b",
        "name": "Lower",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "0",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 770,
        "y": 160,
        "wires": [
            [
                "7c8d261e5f6d0998"
            ]
        ]
    },
    {
        "id": "83830bf4b0698084",
        "type": "mqtt in",
        "z": "f6200681b265563b",
        "name": "TankT1",
        "topic": "TankT1-Temp/temperature",
        "qos": "2",
        "datatype": "auto",
        "broker": "8aa408ca715b9482",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 90,
        "y": 160,
        "wires": [
            [
                "8d16a88be38e878a",
                "e39e566a7f7e8e14"
            ]
        ]
    },
    {
        "id": "49cd2c020d59c024",
        "type": "influxdb out",
        "z": "f6200681b265563b",
        "influxdb": "2e2baf68b726c3bb",
        "name": "Temp",
        "measurement": "Temp",
        "precision": "",
        "retentionPolicy": "",
        "database": "database",
        "precisionV18FluxV20": "ms",
        "retentionPolicyV18Flux": "",
        "org": "organisation",
        "bucket": "bucket",
        "x": 350,
        "y": 200,
        "wires": []
    },
    {
        "id": "bccca8d40a6d63f8",
        "type": "switch",
        "z": "f6200681b265563b",
        "name": "Valve",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "gt",
                "v": "TankT1-upper",
                "vt": "flow"
            },
            {
                "t": "lte",
                "v": "TankT1-lower",
                "vt": "flow"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 550,
        "y": 140,
        "wires": [
            [
                "bd1daf1565ee885c",
                "a95558832e711631"
            ],
            [
                "897ba8f3f231a1f0",
                "79ef3fe1a242a713"
            ],
            [
                "e016d9b888302a93"
            ]
        ]
    },
    {
        "id": "e016d9b888302a93",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "In-Range",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 660,
        "y": 200,
        "wires": []
    },
    {
        "id": "7b73ace1b6cdb759",
        "type": "unit-converter",
        "z": "f6200681b265563b",
        "category": "temperature",
        "inputUnit": "F",
        "outputUnit": "F",
        "inputField": "payload",
        "outputField": "payload",
        "inputFieldType": "msg",
        "outputFieldType": "msg",
        "roundOutputField": true,
        "outputFieldDecimals": "1",
        "name": "Convert",
        "x": 340,
        "y": 160,
        "wires": [
            [
                "49cd2c020d59c024",
                "876bf077bc5133ad",
                "bccca8d40a6d63f8",
                "fa5eeedaed9f31e2"
            ]
        ]
    },
    {
        "id": "10ba642e6415f895",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "80",
        "payloadType": "num",
        "x": 130,
        "y": 420,
        "wires": [
            [
                "e2df700bbffcf19c"
            ]
        ]
    },
    {
        "id": "5845311c332faad2",
        "type": "comment",
        "z": "f6200681b265563b",
        "name": "Upper limit",
        "info": "",
        "x": 120,
        "y": 380,
        "wires": []
    },
    {
        "id": "c8ab9cc83c737ff8",
        "type": "comment",
        "z": "f6200681b265563b",
        "name": "Lower limit",
        "info": "",
        "x": 580,
        "y": 380,
        "wires": []
    },
    {
        "id": "694b4fe4d5a16fe4",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "75",
        "payloadType": "num",
        "x": 130,
        "y": 460,
        "wires": [
            [
                "e2df700bbffcf19c"
            ]
        ]
    },
    {
        "id": "ba17d2d3bffdffdc",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "72",
        "payloadType": "num",
        "x": 130,
        "y": 500,
        "wires": [
            [
                "e2df700bbffcf19c"
            ]
        ]
    },
    {
        "id": "d00cc0bcd37d570a",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "77",
        "payloadType": "num",
        "x": 590,
        "y": 420,
        "wires": [
            [
                "281d2c0adf9fce83"
            ]
        ]
    },
    {
        "id": "36355bdc381b94de",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "71",
        "payloadType": "num",
        "x": 590,
        "y": 460,
        "wires": [
            [
                "281d2c0adf9fce83"
            ]
        ]
    },
    {
        "id": "0d755e19e6dfa112",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "65",
        "payloadType": "num",
        "x": 590,
        "y": 500,
        "wires": [
            [
                "281d2c0adf9fce83"
            ]
        ]
    },
    {
        "id": "e2df700bbffcf19c",
        "type": "change",
        "z": "f6200681b265563b",
        "name": "TankT1-Upper Temp",
        "rules": [
            {
                "t": "set",
                "p": "TankT1-upper",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 340,
        "y": 420,
        "wires": [
            [
                "b1e07a828296e378",
                "d6526e17959cd5b1"
            ]
        ]
    },
    {
        "id": "281d2c0adf9fce83",
        "type": "change",
        "z": "f6200681b265563b",
        "name": "Lower Temp",
        "rules": [
            {
                "t": "set",
                "p": "TankT1-lower",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 770,
        "y": 420,
        "wires": [
            [
                "e777b04cf3dcd444",
                "73cf7760eb69342b"
            ]
        ]
    },
    {
        "id": "b1e07a828296e378",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "Upper Temp",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 330,
        "y": 380,
        "wires": []
    },
    {
        "id": "e777b04cf3dcd444",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "Lower Temp",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 790,
        "y": 380,
        "wires": []
    },
    {
        "id": "876bf077bc5133ad",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 570,
        "y": 100,
        "wires": []
    },
    {
        "id": "243b63027f532058",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "TankT1-Upper",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "TankT1-upper",
        "payloadType": "flow",
        "x": 110,
        "y": 660,
        "wires": [
            [
                "d6968efefdcf0985"
            ]
        ]
    },
    {
        "id": "d6968efefdcf0985",
        "type": "debug",
        "z": "f6200681b265563b",
        "name": "TankT1-Setpoints",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 310,
        "y": 680,
        "wires": []
    },
    {
        "id": "5e673bd9ab566df8",
        "type": "inject",
        "z": "f6200681b265563b",
        "name": "TankT1-Lower",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "TankT1-lower",
        "payloadType": "flow",
        "x": 110,
        "y": 700,
        "wires": [
            [
                "d6968efefdcf0985"
            ]
        ]
    },
    {
        "id": "fd8336d590e9745e",
        "type": "ui_chart",
        "z": "f6200681b265563b",
        "name": "",
        "group": "a23b2bf2f261c86b",
        "order": 1,
        "width": "6",
        "height": "6",
        "label": "Tank-Graph",
        "chartType": "line",
        "legend": "true",
        "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": 90,
        "y": 280,
        "wires": [
            []
        ]
    },
    {
        "id": "d6526e17959cd5b1",
        "type": "ui_numeric",
        "z": "f6200681b265563b",
        "name": "",
        "label": "TankT1-Upper",
        "tooltip": "",
        "group": "2df3817beb943471",
        "order": 2,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": true,
        "topic": "topic",
        "topicType": "msg",
        "format": "{{value}}",
        "min": "60",
        "max": "75",
        "step": 1,
        "className": "",
        "x": 340,
        "y": 500,
        "wires": [
            [
                "e2df700bbffcf19c"
            ]
        ]
    },
    {
        "id": "fa5eeedaed9f31e2",
        "type": "warp",
        "z": "f6200681b265563b",
        "name": "",
        "scope": [
            "fd8336d590e9745e"
        ],
        "destination": "selected",
        "x": 500,
        "y": 200,
        "wires": []
    },
    {
        "id": "73cf7760eb69342b",
        "type": "ui_numeric",
        "z": "f6200681b265563b",
        "name": "TankT1-Lower",
        "label": "TankT1-Lower",
        "tooltip": "",
        "group": "2df3817beb943471",
        "order": 2,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": true,
        "topic": "topic",
        "topicType": "msg",
        "format": "{{value}}",
        "min": "60",
        "max": "75",
        "step": 1,
        "className": "",
        "x": 800,
        "y": 500,
        "wires": [
            [
                "281d2c0adf9fce83"
            ]
        ]
    },
    {
        "id": "42beff1b1c1f36b1",
        "type": "ui_switch",
        "z": "f6200681b265563b",
        "name": "Off/On",
        "label": "Off/On",
        "tooltip": "",
        "group": "2df3817beb943471",
        "order": 2,
        "width": 0,
        "height": 0,
        "passthru": true,
        "decouple": "false",
        "topic": "control",
        "topicType": "str",
        "style": "",
        "onvalue": "open",
        "onvalueType": "str",
        "onicon": "",
        "oncolor": "",
        "offvalue": "close",
        "offvalueType": "str",
        "officon": "",
        "offcolor": "",
        "animate": false,
        "className": "",
        "x": 90,
        "y": 40,
        "wires": [
            [
                "e39e566a7f7e8e14",
                "3bcd2c394946a7de"
            ]
        ]
    },
    {
        "id": "8d16a88be38e878a",
        "type": "dead-man-switch",
        "z": "f6200681b265563b",
        "name": "TankT1-Deadman",
        "delay": "5",
        "delayUnit": "minutes",
        "x": 130,
        "y": 100,
        "wires": [
            [
                "3bcd2c394946a7de"
            ]
        ]
    },
    {
        "id": "1baa0a1ad242f6cc",
        "type": "telegram sender",
        "z": "f6200681b265563b",
        "name": "Telegram",
        "bot": "a156e0f99dc65097",
        "haserroroutput": false,
        "outputs": 1,
        "x": 540,
        "y": 40,
        "wires": [
            []
        ]
    },
    {
        "id": "e39e566a7f7e8e14",
        "type": "gate",
        "z": "f6200681b265563b",
        "name": "TankT1-Control",
        "controlTopic": "control",
        "defaultState": "closed",
        "openCmd": "open",
        "closeCmd": "close",
        "toggleCmd": "toggle",
        "defaultCmd": "default",
        "statusCmd": "status",
        "persist": true,
        "storeName": "memory",
        "x": 360,
        "y": 100,
        "wires": [
            [
                "7b73ace1b6cdb759"
            ]
        ]
    },
    {
        "id": "729b93d56e548fa5",
        "type": "status",
        "z": "f6200681b265563b",
        "name": "TankT1-Status",
        "scope": [
            "e39e566a7f7e8e14"
        ],
        "x": 100,
        "y": 320,
        "wires": [
            [
                "7f004df53f2dab01"
            ]
        ]
    },
    {
        "id": "7f004df53f2dab01",
        "type": "ui_text",
        "z": "f6200681b265563b",
        "group": "2df3817beb943471",
        "order": 1,
        "width": 0,
        "height": 0,
        "name": "TankT1-Status",
        "label": "Status",
        "format": "{{msg.status.text}}",
        "layout": "row-spread",
        "className": "",
        "x": 300,
        "y": 320,
        "wires": []
    },
    {
        "id": "3bcd2c394946a7de",
        "type": "gate",
        "z": "f6200681b265563b",
        "name": "TankT1-Control",
        "controlTopic": "control",
        "defaultState": "closed",
        "openCmd": "open",
        "closeCmd": "close",
        "toggleCmd": "toggle",
        "defaultCmd": "default",
        "statusCmd": "status",
        "persist": true,
        "storeName": "memory",
        "x": 360,
        "y": 60,
        "wires": [
            [
                "1baa0a1ad242f6cc"
            ]
        ]
    },
    {
        "id": "8aa408ca715b9482",
        "type": "mqtt-broker",
        "name": "MQTTBroker",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    },
    {
        "id": "2e2baf68b726c3bb",
        "type": "influxdb",
        "hostname": "127.0.0.1",
        "port": "8086",
        "protocol": "http",
        "database": "sensors",
        "name": "Sensors",
        "usetls": false,
        "tls": "",
        "influxdbVersion": "1.x",
        "url": "http://localhost:8086",
        "rejectUnauthorized": true
    },
    {
        "id": "a23b2bf2f261c86b",
        "type": "ui_group",
        "name": "Graph",
        "tab": "75e2c4a4bae21b23",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "2df3817beb943471",
        "type": "ui_group",
        "name": "TankT1-Controls",
        "tab": "75e2c4a4bae21b23",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "a156e0f99dc65097",
        "type": "telegram bot",
        "botname": "NodeRedFermentBot",
        "usernames": "prell",
        "chatids": "",
        "baseapiurl": "",
        "updatemode": "polling",
        "pollinterval": "300",
        "usesocks": false,
        "sockshost": "",
        "socksport": "6667",
        "socksusername": "anonymous",
        "sockspassword": "",
        "bothost": "",
        "botpath": "",
        "localbotport": "8443",
        "publicbotport": "8443",
        "privatekey": "",
        "certificate": "",
        "useselfsignedcertificate": false,
        "sslterminated": false,
        "verboselogging": false
    },
    {
        "id": "75e2c4a4bae21b23",
        "type": "ui_tab",
        "name": "Home",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]

ok im back and have my laptop.
@ryanprel
here is flow with my center fermenter.
there is probably a few nodes you may not have installed in your node red. letl me know whats missing.
CenterFerm.json (35.5 KB)

I have a winery too (in Italy) and control my cellar with node red.
For my experience a two points control is good for common time, but during the fermentation the hysteresis of the tanks is too high for it. I use a PID control that act on the duty cycle of the valves. It can be 0 (valve closed full time) to 1 (valve open full time).
The time of the cycle is a parameter I can set manually.
The PID is little bit hard to set, but after I start to use it the peak over value are not more present.

1 Like

Is it node-red-contrib-pid that you are using (possibly with node-red-contrib-timeprop)?

A Full Deploy of your flows or a restart of Node-RED will clear the memory and put the gate in the default state. Partial deploys (Modified Flows or Modified Nodes) will preserve the state if the node or flow has not been modified. This is mostly useful for development. If you want the state restored at all times, you should use a persistent context store.

Thanks - looks like I got persistence to work through restarts.

1 Like