Controlling Ventilation System Based on CO2 Values with openHAB
Eiko WagenknechtIn this article, I’ll show you how to automatically control a ventilation system with openHAB based on the CO2 level in a room. I’m using the Maico Push/Pull 45 ventilation system and a Netatmo CO2 sensor, but the principles can be easily applied to other systems.
Table of Contents
Version
At the time of publishing this article, openHAB 3.0.1 was current. With newer versions, some details might have changed. Feel free to leave a comment if you know any adjustments needed for newer versions - I’ll update this article accordingly.
Required Hardware
- Ventilation units Maico PushPull 45 RC (approx. €480)
- Homematic CCU3 (approx. €140)
- EnOcean USB 300 Gateway (approx. €57)
- CUxD EnOcean License (approx. €40)
- Netatmo Weather Station (approx. €155)
- Optional additional indoor module (approx. €65)
- Raspberry Pi for openHAB (e.g., Raspberry Pi 4 with 4GB RAM, approx. €95)
- A reliable microSD card with at least 16GB (recommended: Samsung PRO Endurance 64GB, approx. €14)
- Card reader for the microSD card (approx. €14)
The approach described works with other ventilation systems that can be connected to openHAB, or with other CO2 sensors.
Prerequisites
The prerequisite is that both the ventilation system and the CO2 sensor are already integrated into openHAB. In my case, I connected the ventilation system via the Homematic CCU3, simply because I’ve been using Homematic longer than openHAB. Setting up this connection is relatively complex and described in a separate article. The Homematic hub is linked to openHAB via the Homematic Binding. The Netatmo sensors are directly integrated into openHAB using the Netatmo Binding.
Items
For controlling the system, I have the following items available:
Ventilation system:
Group e_EG_Buero_Lueftung "Lüftungsanlage" (r_EG_Buero) ["Fan"]
Number EG_Buero_Lueftung_Stufe_Ist "Aktuelle Stufe [%d]" (e_EG_Buero_Lueftung) ["Measurement"] { channel="homematic:GATEWAY-EXTRAS-CCU3:ccu3:GWE00000000:1#Lueftung-BueroSchlafzimmer-Ist" }
Number EG_Buero_Lueftung_Stufe_Soll "Eingestellte Stufe [%d]" (e_EG_Buero_Lueftung) ["Control"] { channel="homematic:GATEWAY-EXTRAS-CCU3:ccu3:GWE00000000:1#Lueftung-BueroSchlafzimmer-Soll" }
Dimmer EG_Buero_Lueftung_Stufe_Soll_Prozent "Eingestellte Stufe [%d %%]" { ga="Fan" [ name="Büro Lüfter", speeds="0=aus:null,20=eins,40=default:zwei,60=drei,80=vier,100=maximal:fuenf", lang="de", ordered=true ] }
Switch EG_Buero_Lueftung_Automatik "Automatikmodus (CO2)" (e_EG_Buero_Lueftung) ["Switch"]
Netatmo:
Group e_EG_Buero_Netatmo "Netatmo" (r_EG_Buero) ["Sensor"]
Number:Dimensionless EG_Buero_Netatmo_CO2 "CO2 Gehalt [%.0f ppm]" (e_EG_Buero_Netatmo, u_Persist_Change) ["Measurement", "CO2"] { channel="netatmo:NAModule4:api:*****:Co2" }
Number:Temperature EG_Buero_Netatmo_Temperatur "Temperatur [%.1f °C]" (e_EG_Buero_Netatmo, u_Persist_Change) ["Measurement", "Temperature"] { channel="netatmo:NAModule4:api:*****:Temperature" }
Number:Dimensionless EG_Buero_Netatmo_Luftfeuchtigkeit "Luftfeuchtigkeit [%.0f rF]" (e_EG_Buero_Netatmo, u_Persist_Change) ["Measurement", "Humidity"] { channel="netatmo:NAModule4:api:*****:Humidity" }
For actual control, only EG_Buero_Lueftung_Stufe_Soll
, EG_Buero_Lueftung_Automatik
, and EG_Buero_Netatmo_CO2
are needed. The other items are included just for completeness.
Rules
After some brainstorming, I developed the following control logic:
- If the office window is closed and sleep mode is deactivated (the system is coupled with the bedroom and is unfortunately quite loud), then the system runs in automatic mode. Otherwise, it’s switched off.
- In automatic mode, the CO2 value is checked every 5 minutes (at :00, :05, :10, etc.).
- If the value exceeds 900 ppm, the ventilation level increases, but maximally to level 4 (which still has a tolerable noise level during the day).
- If the value falls below 700 ppm, the level decreases, but minimally to level 1 (complete shutdown makes a rather loud noise as the air supply is blocked, hence “1” as the minimum level).
In openHAB, I implemented this with the following rules:
Activating/Deactivating Automatic Mode
rule "Lüftung - deaktivieren - Büro/Schlafzimmer"
when
Item EG_Buero_Fenster_Offen changed or
Item System_Variable_Schlafmodus changed
then
logDebug("co2", "Executing rule 'Ventilation (de)activate Office/Bedroom'.")
if (EG_Buero_Fenster_Offen.state == CLOSED && System_Variable_Schlafmodus.state == OFF) {
logInfo("co2", "Ventilation system Office/Bedroom is being switched to automatic mode.")
EG_Buero_Lueftung_Automatik.sendCommand(ON)
} else {
logInfo("co2", "Ventilation system Office/Bedroom is being switched off.")
EG_Buero_Lueftung_Automatik.sendCommand(OFF)
EG_Buero_Lueftung_Stufe_Soll.sendCommand(0)
}
end
Control Based on Current CO2 Value
rule "Lüftung - An CO2 Wert anpassen - Büro/Schlafzimmer"
when
Time cron "0 0/5 * * * ?" // every 5 minutes
then
logDebug("co2", "Executing rule 'CO2 Office/Bedroom'.")
if (EG_Kueche_Lueftung_Automatik.state != ON || EG_Wohnen_Lueftung_Automatik.state != ON) {
logDebug("co2", "Automatic mode for Kitchen/Living room deactivated. No action.")
return
}
val minLevel = 1
val maxLevel = 4
val setPointLow = 700
val setPointHigh = 900
val currentValue = (EG_Buero_Netatmo_CO2.state as Number).intValue
val currentLevel = (EG_Buero_Lueftung_Stufe_Soll.state as Number).intValue
logDebug("co2", "Current CO2 level:" + currentValue)
logDebug("co2", "Target value (low):" + setPointLow)
logDebug("co2", "Target value (high):" + setPointHigh)
if (currentValue < setPointLow) {
if (currentLevel > minLevel) {
logDebug("co2", "CO2 value below target, reducing ventilation level.")
EG_Buero_Lueftung_Stufe_Soll.sendCommand(currentLevel - 1)
} else {
logDebug("co2", "CO2 value below target, but level already at minimum. No action.")
}
} else if (currentValue > setPointHigh) {
if (currentLevel < maxLevel) {
logDebug("co2", "CO2 value above target, increasing ventilation level.")
EG_Buero_Lueftung_Stufe_Soll.sendCommand(currentLevel + 1)
} else {
logDebug("co2", "CO2 value above target, but level already at maximum. No action.")
}
}
end
Conclusion
I look forward to your comments on whether this article has been helpful. Feel free to write if you have questions or if something remains unclear. I’m also curious about what other automation projects you’ve been able to implement based on this!
No Comments? No Problem.
This blog doesn't support comments, but your thoughts and questions are always welcome. Reach out through the contact details in the footer below.
Support Me
If you found this page helpful and want to say thanks, I would be very grateful if you could use this link for your next purchase on amazon.com. I get a small commission, and it costs you nothing extra. If you'd like to support me in another way, you can find more options here.