Home assistant CO2 control

bitters

New Member
Hello everyone, I've lurked here for a while and finally made an account.
MJ was legalized in my state August 1st last year and I now have a grow room controlled by home assistant.
The entire setup is a bit complicated for a single post, so I figure I'll just share the part that I googled many times and never found any good results: Increasing Co2.

First, equipment involved:
2x scd30 ndir CO2 sensors (from adafruit)
1x sonoff s31 (from amazon)
Each scd30 is attached to an esp32 along with some other sensors.

I have a CO2 controller that claims to use fuzzy logic. It does an OK job of maintaining CO2 levels, but I thought I could do better and so, why not try? Note that I am not a software developer, yaml is weird and everything could probably be done in a more efficient way.

Step 1
Create a template to average the 2 sensors. The sensors report every 5 seconds and this gets updated any time a sensor reports.
- name: Average CO2
unit_of_measurement: "ppm"
device_class: carbon_dioxide
icon: mdi:molecule-co2
state: >
{% if states('sensor.s3_1_co2') != 'unknown' and states('sensor.s3_2_co2') != 'unknown' %}
{{ (float(states('sensor.s3_1_co2')) + float(states('sensor.s3_2_co2'))) /2 }}
{% elif states('sensor.s3_1_co2') == 'unknown' and states('sensor.s3_2_co2') != 'unknown' %}
{{ float(states('sensor.s3_2_co2')) }}
{% elif states('sensor.s3_1_co2') != 'unknown' and states('sensor.s3_2_co2') == 'unknown' %}
{{ float(states('sensor.s3_1_co2')) }}
{% else %}
{{ states('sensor.s3_2_co2') }}
{% endif %}
If they are both NOT unknown (unavailable for some reason) I average them, if one or the other IS unknown I use the reading from the good one. If they're both unknown, I give up, set the value to unknown and go to the pub.

Step 2
Setup and tune a PID controller
This guy made one and you can install it from HACS
It does have a problem, which can be patched by looking at the diff here
There are many different techniques for tuning a PID, I'll let you google them.
I ended up with
P=1.29
I=0.005
D=60
Other settings that make this work
Sample time 10
Minimum 0
Maximum 10
Precision 2
This gives me a number between 0 and 10 every 10 seconds. This is how long the CO2 solenoid will be powered over the next 10 seconds.

Step 3
Setup an Automation to turn the CO2 on and off
- id: '1708797883319'
alias: CO2 PID Controller
description: new version
trigger:
- platform: time_pattern
seconds: /10
id: time
- platform: homeassistant
event: start
id: start
- platform: homeassistant
event: shutdown
id: shutdown
condition: []
action:
- choose:
- conditions:
- condition: trigger
id:
- shutdown
sequence:
- type: turn_off
device_id: 0cd50923611a62ad97b9616e57540f20
entity_id: 72509f79d6dc144ec07ea8b38599a438
domain: switch
- conditions:
- condition: trigger
id:
- start
sequence:
- type: turn_off
device_id: 0cd50923611a62ad97b9616e57540f20
entity_id: 72509f79d6dc144ec07ea8b38599a438
domain: switch
- conditions:
- condition: trigger
id:
- time
sequence:
- if:
- condition: state
entity_id: binary_sensor.is_it_day_time
state: 'off'
then:
- type: turn_off
device_id: 0cd50923611a62ad97b9616e57540f20
entity_id: 72509f79d6dc144ec07ea8b38599a438
domain: switch
else:
- if:
- condition: state
entity_id: switch.co2_switch_relay
state: 'off'
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.co2_pid_control
below: 1
then:
- type: turn_off
device_id: 0cd50923611a62ad97b9616e57540f20
entity_id: 72509f79d6dc144ec07ea8b38599a438
domain: switch
else:
- if:
- condition: numeric_state
entity_id: sensor.co2_pid_control
above: 9
then:
- type: turn_on
device_id: 0cd50923611a62ad97b9616e57540f20
entity_id: 72509f79d6dc144ec07ea8b38599a438
domain: switch
else:
- type: turn_on
device_id: 0cd50923611a62ad97b9616e57540f20
entity_id: 72509f79d6dc144ec07ea8b38599a438
domain: switch
- delay:
seconds: '{{ states(''sensor.co2_pid_control'') }}'
- type: turn_off
device_id: 0cd50923611a62ad97b9616e57540f20
entity_id: 72509f79d6dc144ec07ea8b38599a438
domain: switch
mode: single

I actually build that in the GUI mostly, I think I had to edit the yaml to set the delay to the value of the PID output ... it was a bit painful
Triggers are every 10 seconds, when home assistant starts and when it shuts down
If the trigger is home assistant start or stop, shutoff CO2
If it is time, then we first check if it's daytime (lights on), if not, shutoff CO2
If it is daytime, then we check the output of the PID
More Ifs
If CO2 is off and PID output is less than 1 stay off (I don't wanna switch the relay faster than once a second)
Similarly, if PID output is above 9, I'll just stay on for all 10 seconds (don't wanna turn it on for 9.5 seconds, off for 0.5 seconds, the back on for the next 10 second cycle)
Else - here's the part we're usually in, PID gives us something between 1 and 9
Turn on
Wait for value of PID
Turn off

I still need to fine tune a bit, I do overshoot when the lights turn on, but the line on the graph is smoother that it was with a grow store controller.

Hope this is useful to someone, let me know if you have questions

co2.png
 
Back
Top Bottom