Air quality driven ventilator

Many modern homes have mechanical ventilation, with a 3 position switch to control the speed. However, common ventilators allow stepless speed control. They set the speed corresponding the voltage of an input connector. If 0V is provided, the fan goes off, if it is +10V, the fan goes to maximum speed. If only there was a way to set a voltage between 0V and +10V wirelessly, by a popular home automation protocol such as Z-Wave.

Actually, there is. It is a brilliant device by Qubino, called the Flush Dimmer 0-10V. I installed it into our ventilation unit (a Stork CMFe), and connected it to my Z-Wave network and Domoticz.

Qubino 0-10V Flush Dimmer inside Stork CMFe

Now I could control the fan from my Z-Wave network and my Domoticz system, set it any value between off and 100%, and do so from anywhere in the world.

Fan speed control in Domoticz

At first it is nice to control the speed this way, but what I really want is that it is done automatically, based on measured air quality.

For air quality I measure particulates (PM2.5 and PM10, Dutch: fijnstof) outdoor and indoor, and CO2 only indoor. CO2 is a constant 400 ppm outdoor (though our species is trying hard to increase it), so there is no need to measure. This is our outdoor particulates sensor on a Raspberry Pi:

Outdoor particulates sensor on Raspberry Pi

And our indoor particulates sensor, together with the CO2 sensor and the Z-Wave controller on another Pi, which runs our Domoticz system:

Indoor particulates sensor on Raspberry Pi

The sensors are SDS-021 and MH-Z19, each available online for around €20. The software is my own Github project, Fijnstof. This will read the sensors, and inject the values into Domoticz virtual sensors. There you will find more details on how to install it, and how to configure Domoticz.

The next part is to decide what level the mechanical ventilation should have at given levels of CO2 and particulates. For particulates it is easy, if indoor levels are higher than outdoor, set the fan to maximum speed. Otherwise, set the fan lower as the air outside gets dirtier compared to inside. For the CO2, I decided to allow fluctuation between 400 ppm (perfect outdoor quality) and 800 ppm (the norm for classrooms in the Netherlands). At 400 ppm, the fan can be at minimum, 10%, from 800 ppm up, I max out the fan at 90%, with a linear ramp between these values. Next, I consider the difference in particulates. If outdoor levels are much higher than indoor, the calculated value is lowered accordingly, again linearly. This is the LUA script I ended up with:

return {
  on = {
    timer = { 'every 1 minutes' }
  data = {
    previousValue = { initial = 0 }
  execute = function(domoticz)
    if (domoticz.groups('Climate control').state == 'On') then
      local pm25outdoor = domoticz.devices('PM2.5 Outdoor').state
      local pm25indoor = domoticz.devices('PM2.5 Indoor').state
      local pm25diff = pm25outdoor - pm25indoor
      local mv = domoticz.devices('Mechanical ventilation')
      if (pm25diff < 0) then
        mv.switchSelector(100) = 100
        local co2 = domoticz.devices('CO2 Indoor').state
        local co2component = (co2 - 400) / 4
        local pm25component =  -1 * pm25diff
        local newvalue = math.floor(math.min(90, math.max(10, co2component + pm25component)))
        if (math.abs( - newvalue) > 1) then
 = newvalue

The script also needs a Domoticz Group called "Climate control", allowing to toggle the automatic tracking without disabling the script.

Running the setup

In practice, the calculation works very well. The resulted levels are very similar to what I would have set manually before. In a way, it is also an energy saver, because when we leave, the ventilator eventually settles at minimum speed. I would rarely set it to minimum when leaving, allowing the CO2 levels to drop. This is done automatically now. Also at night, the ventilator goes lower. Apparently we do not exhaust as much CO2 when we sleep.

Yesterday, the ventilator suddenly dropped to minimum speed. When checking, I saw a big spike in the outdoor particulates reading. So the system effectively minimised pollution intake. The fan resumed gradually as the pollution cloud disappeared.

When we bake, especially bacon, the particulates level explodes in the kitchen, resulting in a temporary 100% fan speed, until it is lower again than outside.

When we have more CO2 sources (visitors), the fan kicks in to provide fresh air for all.

Here is the chart of the ventilation levels on a typical day where we come home, dine together, sleep, and part ways in the morning:

Chart with fan level going up and down over a 24h time span


Why am I interested in keeping these values low? What we call fresh air, is not so much related to oxygen as it is to CO2. The feeling of drowsiness comes from an excess of the latter, rather than a shortage of the former. We open the windows not so much to let O2 in, as we let CO2 out.

We also actively filter particulates with air cleaners, but this is not automated (yet).

Bestellijst luchtkwaliteit gestuurde MV.pdf