Dewpoint controlled dehumidification/ventilation

Tags: #<Tag:0x00007f616eff1988> #<Tag:0x00007f616eff0038> #<Tag:0x00007f616f40bbe0>

Since my initial project to start with smart home devices at all was to dehumidify our 400 year old basement, I decided to post my current results on that project here.

The problem was, to start ventilators only when the air outside has the correct humidity/temperature condition in order to dry the air inside the basement. For us, this is to keep the humidity in the basement as low as possible. We also have a dehumidificator, but it uses about 450 W (the ventilators only around 40 W).

The devices I use:

  • two Sonoff POW switches to control ventilators/dehumidificator and to have an overview of energy consumption.
  • outsite I use several sensors to identify the outdoor dewpoint (homematic weather station, homematic outdoor sensor, netatmo modul).
  • inside I use netatmo modul and a AM2301 Sensor connected to Sonoff TH16 running custom firmware

But you can use whatever type of sensors and switch. I post my code below to make it public. I may translate everything into englisch when I have the time. But my approach may help the one or other already. I wasn’t able to find a complete working system before, so have fun drying you basement in a low cost way:

My script to calculate the dewpoint:

val org.eclipse.xtext.xbase.lib.Functions$Function2<Double, Double, Double> getDewPoint = [
	
	Double Temperature, 
	Double Humidity 
	
	|

	var Double a
	var Double b
	var Double SDD
	var Double DD
	var Double v
	var Double t = Temperature
	var Double h = Humidity
	
	if (t >= 0.0){ // T >= 0 °C
		a = 7.5
		b = 237.3
	} else { // T < 0 °C ĂŒber Wasser
		a = 7.6
		b = 240.7
	}
	SDD=(6.1078 * Math::pow(10.0, ((a*t)/(b+t))))
	DD = (h/100.0*SDD)
	v = Math::log10((DD/6.107))
	return ((b*v)/(a-v))

]

rule "Taupunktberechnung Außenbereich" // calculation of outdoor dewpoint
when

	Item Aussenbereich_Temperature received update or
	Item Aussenbereich_Humidity received update or
	System started

then

	if ((Aussenbereich_Temperature.state != NULL) && (Aussenbereich_Humidity.state != NULL))
	{

		var t = (Aussenbereich_Temperature.state as DecimalType).doubleValue
		var h = (Aussenbereich_Humidity.state as DecimalType).doubleValue
		
		// OutsideThermometerCombined.sendCommand(String::format("%.1f°C / %.0f%%", t, h))
	
		Aussenbereich_Taupunkt.postUpdate(getDewPoint.apply(t, h))
			
	}

end

rule "Taupunktberechnung Keller" // calculation of basement dewpoint
when

	Item Keller_Temperature received update or
	Item Keller_Humidity received update or
	System started

then

	if ((Keller_Temperature.state != NULL) && (Keller_Humidity.state != NULL))
	{

		var t = (Keller_Temperature.state as DecimalType).doubleValue
		var h = (Keller_Humidity.state as DecimalType).doubleValue
		
		// OutsideThermometerCombined.sendCommand(String::format("%.1f°C / %.0f%%", t, h))
	
		Keller_Taupunkt.postUpdate(getDewPoint.apply(t, h))
			
	}

end

rule "Taupunktdifferenz berechnen" // for statistics: dewpoint difference
when
	Item Keller_Taupunkt changed or
	Item Aussenbereich_Taupunkt changed
then
	var dewpoint_keller = (Keller_Taupunkt.state as DecimalType).doubleValue
	var dewpoint_aussen = (Aussenbereich_Taupunkt.state as DecimalType).doubleValue		
	Taupunkt_Differenz.postUpdate(dewpoint_keller - dewpoint_aussen)
end

My script to control ventilation:

var Timer ventilation_timer = null
var Timer interval_timer = null

rule "Dehumidification" // this is to switch the dehumidificator when the inner humidity runs above a certain value
when
	Item Keller_Humidity changed or
	Item Keller_Dehumidification_Steady changed
then
	var Number basement_humidity = Keller_Humidity.state as DecimalType
	var Number dehumitification_bottom = Dehumitification_Bottom.state as DecimalType
	var Number dehumitification_top = Dehumitification_Top.state as DecimalType
	
	if(basement_humidity >= dehumitification_top) {
		sendCommand(Keller_Entfeuchter_Steckdose, ON)
	}
	if(basement_humidity <= dehumitification_bottom) {
		sendCommand(Keller_Entfeuchter_Steckdose, OFF)
	}
end

// this is the core ventilation rule. I checks the surrounding parameters an starts the ventilators for 5 minutes and waits a certain time period to let the "fresh" air collecting the humidity from the inside. 
rule "Ventilation"
when
	Item Aussenbereich_Taupunkt changed or
	Item Aussenbereich_Temperature changed or
	Item Keller_Taupunkt changed or
	Item Keller_Ventilation_Interval changed or
	Item Keller_Temperatur_Min changed or
	Item Keller_Temperature changed or
	Item Keller_Ventilation_Steady changed or
	Item Keller_Lueftung_Steckdose changed
then
	var Number outdoor_dewpoint = Aussenbereich_Taupunkt.state as DecimalType
	var Number basement_dewpoint = Keller_Taupunkt.state as DecimalType
	var Number dewpoint_min_gap = Keller_Dewpoint_Gap_Min.state as DecimalType
	var Number outdoor_temperature = Aussenbereich_Temperature.state as DecimalType

	// Wenn LĂŒftung stĂ€ndig an, dann Steckdose an!
	if (Keller_Ventilation_Steady.state == ON) {
		if (Keller_Lueftung_Steckdose.state == OFF) sendCommand(Keller_Lueftung_Steckdose, ON)
	} else if (outdoor_temperature <= 0) {
		if (Keller_Lueftung_Steckdose.state == ON) sendCommand(Keller_Lueftung_Steckdose, OFF)
	}
	// Wenn Taupunkt außen höher ist, als Taupunkt Keller, in jedem Fall ausschalten!
	else if (outdoor_dewpoint > basement_dewpoint) {
		sendCommand(Keller_Lueftung_Steckdose, OFF)
		ventilation_timer = null
		interval_timer = null
	}
	else if (basement_dewpoint - outdoor_dewpoint >= dewpoint_min_gap) {

		var Number ventilation_interval = (Keller_Ventilation_Interval.state as DecimalType).intValue

		if (ventilation_timer == null && interval_timer == null) {
			if (Keller_Lueftung_Steckdose.state == OFF) sendCommand(Keller_Lueftung_Steckdose, ON)
			// 5 Minuten LĂŒften
			ventilation_timer = createTimer(now.plusMinutes(5)) [|
				sendCommand(Keller_Lueftung_Steckdose, OFF)
				ventilation_timer = null
				// Pause gem. Keller_Ventilation_Interval
				interval_timer = createTimer(now.plusMinutes(ventilation_interval)) [|
					sendCommand(Keller_Lueftung_Steckdose, ON)
					interval_timer = null
				]
			]
		}
	}

end

My items

/* **********************************************************************
 * * Einstellung der Taupunktdifferenz (Kellertaupunkt - Außentaupunkt) *
 * *            2°C bis 9°C, 1°-Schritte, Standardwert 5°C              *
 * **********************************************************************
 * Eine Entfeuchtung erfolgt nach der eingestellten Taupunktdifferenz (effektive Feuchtigkeit).
 * Der Standardwert liegt bei ca. 5°C Taupunktdifferenz. Dies ist ein Erfahrungswert um eine Entfeuchtung
 * effektiv zu gewÀhrleisten. Die Werten können zwischen 2°C und 9°C eingestellt werden. Wird die Differenz
 * verkleinert, also gegen 2 gehen gelassen, wird öfter gelĂŒftet, jedoch wesentlich weniger entfeuchtet.
 * 
 */
Number Keller_Dewpoint_Gap_Min

/* ************************************************************************************************
 * *         Einstellung des LĂŒftungsintervalls (Zeit zwischen 5-Minuten-LĂŒftungsphasen)          *
 * * 0 bis 90 Min., Entfeuchtung bei 0 Min., ErhaltungslĂŒftung 10 bis 90 Min. in 10-Min.-Schitten *
 * ************************************************************************************************
 * Zur Entfeuchtung von Kellern wird hier ein automatisches StoßlĂŒften durchgefĂŒhrt (Einstellung 0 Min.).
 * Die Kellerluft wird dann durch die QuerlĂŒftung durch trockene Außenluft ausgetauscht, ohne dass sich
 * die WĂ€nde abkĂŒhlen. Danach stoppt die Steuerung bei Taupunkt < 1°C) und wartet erneut auf die
 * eingestellten LĂŒftungsbedingungen.  
 */
Number Keller_Ventilation_Interval

/* **************************************************
 * *      Einstellung der Innenraumtemperatur       *
 * * 5°C bis 25°C in 1°-Schritten, Standardwert 8°C *
 * **************************************************
 *
 * Mit diesem Wert wird die Absenkung der Innenraumtemperatur begrenzt.
 * 
 */
Number Keller_Temperatur_Min

/* ***********************************************************
 * *                      DauerlĂŒftung                       *
 * * Einschalten der LĂŒftung unabhĂ€ngig von allen Parametern *
 * ***********************************************************
 */
Switch Keller_Ventilation_Steady

// Entfeuchter-Variablen
Switch Keller_Dehumidification_Steady
Number Dehumitification_Bottom
Number Dehumitification_Top

// Berechnete Temperaturwerte (Durchschnittswerte)
Number Aussenbereich_Humidity       "Luftfeuchtigkeit außen [%.2f %%]" 		<humidity>		(Weather, Humidity)
Number Aussenbereich_Temperature    "Temperatur außen [%.2f°C]" 	<temperature>	(Weather, Temperature)
Number Keller_Humidity              "Luftfeuchtigkeit Keller [%.2f %%]" 		<humidity>		(Weather, Humidity)
Number Keller_Temperature           "Temperatur Keller [%.2f°C]" 	<temperature>	(Weather, Temperature)

// Berechnete Taupunktwerte (siehe dewpoint.rules)
Number Keller_Taupunkt "Taupunkt Keller [%.2f°C]"       (Weather, Temperature)
Number Aussenbereich_Taupunkt "Taupunkt außen [%.2f°C]" (Weather, Temperature)
Number Taupunkt_Differenz "Taupunktdifferenz [%.2f°C]"  (Weather, Temperature)

My sitemap

	Frame label="LĂŒftungsprogramm" {
		Setpoint item=Keller_Ventilation_Interval icon="clock" label="LĂŒftungsintervall [%.0f Min.]" step=10 minValue=0 maxValue=90
		Setpoint item=Keller_Dewpoint_Gap_Min icon="water" label="Taupunktdifferenz (5°C) [%.1f°C]" step=1 minValue=2 maxValue=9
		// Setpoint item=Keller_Temperatur_Min icon="temperature" label="Min. Temperatur (8°C) [%.1f°C]" step=1 minValue=5 maxValue=25
		Switch item=Keller_Ventilation_Steady label="DauerlĂŒftung"
		Switch item=Keller_Lueftung_Steckdose
		Text item=Keller_Lueftung_Power {
			Frame {
				Text item=Keller_Lueftung_Power
				Text item=Keller_Lueftung_Pow_Total
				Text item=Keller_Lueftung_Pow_Yesterday
				Text item=Keller_Lueftung_Pow_Today
			}
		}
		Text item=Keller_Lueftung_RSSI
	}
	Frame label="Entfeuchtung" {
		Setpoint item=Dehumitification_Bottom icon="rain" label="Entfeuchter aus [%.0f %%]" step=1 minValue=0 maxValue=90
		Setpoint item=Dehumitification_Top icon="rain" label="Entfeuchter an [%.0f %%]" step=1 minValue=0 maxValue=90
		
		Switch item=Keller_Entfeuchter_Steckdose
		Text item=Keller_Entfeuchter_Power {
			Frame {
				Text item=Keller_Entfeuchter_Power
				Text item=Keller_Entfeuchter_Pow_Total
				Text item=Keller_Entfeuchter_Pow_Yesterday
				Text item=Keller_Entfeuchter_Pow_Today
			}
		}
		Text item=Keller_Entfeuchter_RSSI
	}
12 Likes

Thank you for sharing this with us ! It is always great to see what problems and solutions other have found.

Do you happen to have any before/after data ? (Power consumption, humidity, 
 ?)

Sorry for reviving this old thread but I would like to thank @kisseler very much for this excellent tutorial! I am currently using it as a very helpful starting point for my own basement ventilation. In my new home, I found out that the basement is quite wet, too. An approach like this looks very promising!

Just as @Spottyq I would be glad to hear something about your experiences with this topic. Maybe you have refined your ideas and may share them with the community? Thanks a lot!

1 Like

Many thanks for sharing this project :slight_smile:

I have quite the same situation at my house.
It would be great if you could tell me which ventilators do you have and how they are installed. I’m thinking about to install two ventilators - one for blowing air into the cellar and one for blowing air out of the cellar 


I’m not sure about this part in the items code:


 the comment “Berechnete Temperaturwerte (Durchschnittswerte)” confuses me.
Where one gets the data from for these items?

That’s exacly how I do it at the moment. I installed one ventilator in the one end of the basement and another on the other end. I used the ventilators that I bought with my former control from keller-doktor.de: https://www.kellerlueftung24.de/epages/17633109.sf/de_DE/?ObjectPath=/Shops/17633109/Products/CF12452
And I have a dehmidificator, that you can turn on by connecting mains and turn off by deconnecting.
Both systems are powered/controlled by a Sonoff Pow in order to have an eye on the power consumption. For me that is especially interesting because the ventilators use about 50 W in total and the dehumidificator about 300 W. The dehumidificator is made use of only when the outdoor/indoor climate gap does not allow ventilation.

The Headline Berechnete Temperaturwerte (Durchschnittswerte) is only a headline. The temperature/humidity values can be gained by any sensor in your smart home setup. I have two different sensors in the outside (outdoor sensor on the north and weather station on the roof). And I have two DHT22 sensors in the basement on different places. So I calculate the average values that are used by my rules.

I did not have too many time to do evaluation. But I can say, that especially in the summer it is amazingly often that the weather conditions don’t allow ventilation - even during the night!

And with a new setup I would have chosen stronger/bigger ventilators (I have currently the 150 mm type installed).

My current rules look like that:

var Timer ventilation_timer = null
var Timer interval_timer = null

rule "Dehumidification"
when
	Item Keller_Humidity changed or
	Item Keller_Dehumidification_Steady changed
then
	var Number basement_humidity = Keller_Humidity.state as DecimalType
	var Number dehumitification_bottom = Dehumitification_Bottom.state as DecimalType
	var Number dehumitification_top = Dehumitification_Top.state as DecimalType
	var Number dewpoint_gap = Taupunkt_Differenz.state as DecimalType
	var Number dewpoint_min_gap = Keller_Dewpoint_Gap_Min.state as DecimalType
	
	if(basement_humidity >= dehumitification_top && dewpoint_gap <= dewpoint_min_gap) {
		sendCommand(Keller_Entfeuchter_Steckdose, ON)
	}
	if(basement_humidity <= dehumitification_bottom) {
		sendCommand(Keller_Entfeuchter_Steckdose, OFF)
	}
end

rule "Ventilation"
when
	Item Taupunkt_Differenz changed or
	Item Keller_Ventilation_Interval changed or
	Item Keller_Temperatur_Min changed or
	Item Keller_Ventilation_Steady changed or
	Item Keller_Lueftung_Steckdose changed
then
	var Number dewpoint_gap = Taupunkt_Differenz.state as DecimalType
	var Number dewpoint_min_gap = Keller_Dewpoint_Gap_Min.state as DecimalType
	var Number outdoor_temperature = Aussenbereich_Temperature.state as DecimalType
	var Number ventilation_interval = (Keller_Ventilation_Interval.state as DecimalType).intValue

	logInfo("Dehumidification","Außentaupunkt muss niedriger sein als Kellertaupunkt. Taupunktdifferenz (Keller minus Außen) ist " + dewpoint_gap + ". Mindestdifferenz ist " + dewpoint_min_gap + "." )

	// Wenn LĂŒftung stĂ€ndig an sein soll, dann Steckdose an!
	if (Keller_Ventilation_Steady.state == ON) {
		sendCommand(Keller_Lueftung_Steckdose, ON)
		logInfo("Dehumidification","LĂŒftung an, da DauerlĂŒftung eingeschaltet.")
		ventilation_timer = null
		interval_timer = null
	}
	// AuskĂŒhlung des Kellers verhindern
	else if (outdoor_temperature <= 2) {
		sendCommand(Keller_Lueftung_Steckdose, OFF)
		logInfo("Dehumidification","LĂŒftung aus, da Außentemperatur niedriger als 2°C.")
		ventilation_timer = null
		interval_timer = null
	}
	// Wenn Taupunkt außen höher ist, als Taupunkt Keller, in jedem Fall ausschalten!
	else if (dewpoint_gap <= 0) {
		sendCommand(Keller_Lueftung_Steckdose, OFF)
		logInfo("Dehumidification","LĂŒftung aus, da Außentaupunkt höher als Kellertaupunkt.")
		ventilation_timer = null
		interval_timer = null
	}
	// LĂŒftungsprogramm abfragen
	else if (dewpoint_gap >= dewpoint_min_gap) {


		if (ventilation_interval > 0 && ventilation_timer === null && interval_timer === null) {

			if (Keller_Lueftung_Steckdose.state == OFF) sendCommand(Keller_Lueftung_Steckdose, ON)

			// nach 5 Minuten LĂŒften ausschalten
			ventilation_timer = createTimer(now.plusMinutes(5), [|
				sendCommand(Keller_Lueftung_Steckdose, OFF)
				ventilation_timer = null
				logInfo("Dehumidification","LĂŒftung aus, da 5 Minuten gelĂŒftet.")
				// Pause gem. Keller_Ventilation_Interval
				interval_timer = createTimer(now.plusMinutes(ventilation_interval), [|
					sendCommand(Keller_Lueftung_Steckdose, ON)
					interval_timer = null
					logInfo("Dehumidification","LĂŒftung an, weil " + ventilation_interval + " Minuten Pausen-Intervall vorbei.")
				])
				logInfo("Dehumidification", ventilation_interval + "-Minuten-Timer fĂŒr Pausen-Intervall gesetzt.")
			])
			logInfo("Dehumidification","5 Minuten-Timer fĂŒr LĂŒftung gesetzt.")
		} else if (ventilation_interval == 0) {
			if (Keller_Lueftung_Steckdose.state == OFF) sendCommand(Keller_Lueftung_Steckdose, ON)
		}

	} else if (ventilation_timer === null && interval_timer === null) {

		if (Keller_Lueftung_Steckdose.state == ON) {
			if (Keller_Lueftung_Steckdose.state == ON) sendCommand(Keller_Lueftung_Steckdose, OFF)
			logInfo("Dehumidification","LĂŒftung pauschal aus.")
		}

	}
	
	if (Keller_Lueftung_Steckdose.state == ON) logInfo("Dehumidification","LĂŒftung lĂ€uft.")
	else logInfo("Dehumidification","LĂŒftung ist aus.")

end
1 Like

I have installed just one ventilator like this that blows the air out of the cellar:
https://www.amazon.de/dp/B00RLR6Y14/
But please note - in contrast to its specifications it does not consume 8 W but up to 12 W according to my own measurements. I monitor this by using a FRITZ!DECT 200 which is also controlled by openHAB. My assumption is that incoming air will be automatically sucked into the house as it is not that sealed. I have replaced a cellar window with a moisture-proof panel and I sawed a corresponding hole for the ventlator in it. I do not use any dehumidificator so far. I will monitor the operation of my system next summer and add one if my current setup is not sufficient.

I have the same observation.

Thank you for your response and the updated rules file.

I think I’ll order two 237 mm ventilators of this kind:



 and mount it into the cellar windows.

Why did you switch from the “Kellerdoktor”-Control-Device to your custom solution?

I also already have dehumidification - so it’s quite the same setup.
My cellar consists of 3 rooms which are connected by open doors - in total approx. 65 mÂČ.

I used to have such a small ventilator too - but the performance for my cellar is far too poor. Thus I’m happy that I’ve found this thread.

Hey, I’m just curious, how can I adapt this to my house dehumidifier?
Could someone explain shortly how to do the work with the code?
Thanks!

The Tutorials & Examples section is not for questions unless they’re directly related to the examples or tutorial. Open a new thread.

@kisseler
I’m trying to adapt your code to my items/configuration :slight_smile:

  1. Could you please let me know what the purpose of the item Keller_Dehumidification_Steady is?
    In my setup, I have a Sonoff POW before the dehumidifier to track energy consumption. The dehumidifier itself has a humidity sensor.

In order to follow your setup, I guess I need to replace Keller_Dehumidification_Steady with KE_x_pow1_switch and set the threshold to 100%. So the dehumidifier will run as long as KE_x_pow1_switch is ON, right?

Some more questions:
2) Not sure how to use Keller_Temperatur_Min
3) Do makes these settings sense?

That would be it.

Keller_Temperatur_Min is meant as threashold, so not too cold air is blown into the house. First it might affect the temperature in the rest of the house, second too cold air is quite ineffective in drying.

Your settings (65% turn dehumidifier of, 66% turn it off) might cause a bit of a “nervous” behaviour, so I chose a range between 3 an 10 % as hysteresis.

1 Like

@kisseler

Another question:

It seems that my setup is dealing with different units which switch the dehumidifier always off

Entfeuchter AUS (basement_humidity [76.63 %]  <= dehumitification_bottom [55]
var Timer ventilation_timer = null
var Timer interval_timer = null

rule "Dehumidification"
when
	Item KE_s_XiaomiTemperatursensorC_Humidity changed or
	Item KE_x_pow1_switch changed
then
	var Number basement_humidity = KE_s_XiaomiTemperatursensorC_Humidity.state as QuantityType<Number>
	var Number dehumitification_bottom = Dehumitification_Bottom.state as DecimalType
	var Number dehumitification_top = Dehumitification_Top.state as DecimalType
	var Number dewpoint_gap = Taupunkt_Differenz.state as DecimalType
	var Number dewpoint_min_gap = Keller_Dewpoint_Gap_Min.state as DecimalType
	
	if(basement_humidity >= dehumitification_top && dewpoint_gap <= dewpoint_min_gap) {
		sendCommand(KE_x_pow1_switch , ON)
		logInfo("Dehumidification","Entfeuchter AN (basement_humidity [" + basement_humidity + "]  >= dehumitification_bottom [" + dehumitification_top + "]" )
	}
	if(basement_humidity <= dehumitification_bottom) {
		sendCommand(KE_x_pow1_switch , OFF)
		logInfo("Dehumidification","Entfeuchter AUS (basement_humidity [" + basement_humidity + "]  <= dehumitification_bottom [" + dehumitification_bottom + "]" )
	}
end

How can one fix this?

When multiplying with 100 it works now :slight_smile:

var Number basement_humidity = KE_s_XiaomiTemperatursensorC_Humidity.state as QuantityType<Number> * 100

Just in case someone runs into the same issue concerning lambda expression and type mismatch