Temperature control of ceiling fan, how best to do this?

I have put a ceiling fan into my closet (that contains the home servers and can get hot in the summer). The fan is switched by a relay driven by a Raspberry Pi in the attic. The RPi also has several DS18B20 temperature sensors attached. I’m investigating alternatives for the software, which, you guessed it, is supposed to switch on/off the ceiling fan based on closet temperature, room temperature differential, possibly weather forecast etc. I also want a three-way setting I can control over the web: fan-off, fan-on, and fan-auto. On the web, I also want to see the current temperatures, a graph of historical temperatures and when the fan was on/off.

I’m new to OpenHAB. Is this scenario a good use for OpenHAB? Has anybody ever created a similar feedback-control setup like mine and could somebody point me to any info on that if it exists? I’ve been unable to find any similar project. Thanks.

Depending upon your expectations, you may want to create your own web page on your home server(s) to display the graph, etc. You may also want to push the historical data to one of your servers as well, maybe mysql persistence (there may be a better choice - I am all ears - I am currently working with mysql and have some difficult things at times…)

On/Off/Auto Modes is logical, but you may want some rules to make a lot of noise if the fan is in OFF mode and the temp goes too high!

I would suggest starting simple with the rules while collecting historical temperature data and then look at cases where you think the fan did the wrong thing, but we’re protecting serves and running a fan too much may not cost much. It’s not like we’re kicking on an A/C system to cool things, is it?

Tom

Well, openHAB can do much more, it’s probably the most complete HA package, suited for the most complex home automation projects.
No pun intended, but that’s probably why noone proudly presents his ‘hey, I use openhab to control my fan’ project.
Quite some people use OH to control their heating valves, now while a full-range ‘heating/cooling’ implementation is a pretty complex task, the (minimum) rule to implement that will just have a couple of lines.
But the baseline is yes, it’s (also) the right software for your job.

You can run it on the Pi you have. It provides several modules to access sensor data and actors such as the relay you’re using (see GPIO binding documentation in Wiki, that’s what you would use as you say your sensors and fan relay are attached to the GPIO pins). Openhab supports a number of database to store historical data (most simple to use is the rrd4j tool, search openhab Wiki for “persistence”) and it has a GUI included (see the demo.sitemap file for how to create a three-way button and a demo chart).
It has a rule engine that you would need to use to implement the feedback control system you’re looking for.
To start with, there’s a couple of rule examples in the Wiki.

This is a reasonable job for OH.
I’ll ignore the issue of how to get OH to talk to your fan (I use MQTT to talk to a RasPi wired through GPIO to a relay to open my garage. Similarly I have Reed switches wired to RasPi GPIO which report their open/closed to OH over MQTT). There is plenty on this forum and elsewhere to help with that. And if you run into trouble ask specific questions and I and others will be happy to help.

Assuming you have everything hooked up so OH can send and receive info to/from your devices, the rest would be implemented in Rules. Rules are Turing complete so if you can think of it and it can be computed, one can write a rule to do it. I actually do something pretty similar with my HVAC. I have a forced air heater but no air conditioner. Most of the time that isn’t a problem but it can get hot upstairs during the summer and the basement is usually 10-15 degrees F cooler than the top floor. So I turn on the fan when the inside temp is greater than the target temp and the outside temp (based on the weather forecast) is greater than the target.

I use a Nest but the basic logic should apply to your situation. At a minimum it should give you a starting point.

NOTE: I’ve posted my full Nest rules and Items without simplifying them. Ask questions if you see something you don’t understand.

#Items

// Persistence Groups
Group gMyOpenhab // for Items that are exposed to IFTTT
Group gChart     // for Items to persist for charting
Group gHistory   // for Items to preserve their history
//Group gRestore // for Items to restore on startup (currently everything so commented out)

Number N_V_NestHumidity "Main Floor Humidity [%d %%]"     <temperature> (gChart) { nest="<[thermostats(Entryway).humidity]" }
Number N_V_NestCurrTemp "Main Floor Ambient Temp [%d °F]" <temperature> (gChart) { nest="<[thermostats(Entryway).ambient_temperature_f]" }
Number Nest_Temp_Chart_Period "Chart Period"
Number Nest_Hum_Chart_Period "Chart Period"
Number S_V_NestTargetTemp "Furnace Target Temp [%.1f °F]"  <temperature> { nest="=[thermostats(Entryway).target_temperature_f]" }
String S_C_NestAway "Nest Away Status [%s]"                <present>     { nest="=[structures(Wuthering Heights).away]" }
Switch S_C_NestFan "Fan Active [%s]"                       <socket>      { nest="=[thermostats(Entryway).fan_timer_active]" }
Switch N_NestOnline "Nest Status [%s]"                     <network>     { nest="<[thermostats(Entryway).is_online]" }
Number Weather_Temperature "Outside Temperature [%.0f] °F" <temperature> (gChart)  { weather="locationId=home, type=temperature, property=current, unit=fahrenheit" }

#Rules

import org.openhab.core.library.types.*
import org.joda.time.*

var DateTime openWindowsNotif = null
var DateTime closeBlindsNotif = null

rule "Set Nest to Away if No One is Home"
when
        Item Present changed
then
        if(Present.state != OFF) {
                logInfo("Nest", "Setting Nest to HOME")
                S_C_NestAway.postUpdate("home")
        }
        else {
                logInfo("Nest", "Setting Nest to AWAY")
                S_C_NestAway.postUpdate("away")
        }
end

rule "Turn on fan when it gets hot"
when
        Time cron "0 0/30 * * * ?" or // in case the temps don't change but it is still too hot
        Item N_V_NestCurrTemp changed or
        Item S_V_NestTargetTemp changed
then

    // Get current temps
    Thread::sleep(5000) // don't overload the Nest server by asking for too much too fast
    val curr = N_V_NestCurrTemp.state as DecimalType
    Thread::sleep(5000)
    val tgt = S_V_NestTargetTemp.state as DecimalType

    val outside = Weather_Temperature.state as DecimalType
    Thread::sleep(5000)

    // If outside is hotter than our target, this is a good check to disable the rule in the winter
    if(tgt < outside) {

        // Inside is warmer than target, fan isn't already on
        if(curr > (tgt + 1) && S_C_NestFan.state != ON){
            logInfo("Nest", "The temp is " + curr + ". Turning on the fan")
            S_C_NestFan.sendCommand(ON)
        }

        // Inside is cooler than target and fan is on
        else if(curr <= tgt && S_C_NestFan.state == ON) {
            logInfo("Nest", "The temp is " + curr + ". Turning off the fan")
            S_C_NestFan.sendCommand(OFF)
        }

        // Send recommendation alerts during the day when someone is home
        if(TimeOfDay.state.toString == "Day" && Present.state == ON){

            // Inside is warmer than outside, open the windows
            if(curr > outside && (openWindowsNotif == null || openWindowsNotif.isBefore(now.minusHours(24)))) {
                Notification_Proxy_Alarm.postUpdate("It is cooler outside ("+outside+") than inside ("+curr+"), consider opening the windows")
                openWindowsNotif = now
            }

            // Inside it cooler than outside, close the blinds
            else if(curr < outside && (closeBlindsNotif == null || closeBlindsNotif.isBefore(now.minusHours(24)))){
                Notification_Proxy_Alarm.postUpdate("It is warmer outside ("+outside+") than inside ("+curr+"), consider closing the blinds")
                closeBlindsNotif = now
            }
        }
    }
end

#Sitemap

                // Climate Control
                Text item=N_V_NestCurrTemp {
                        Switch item=S_C_NestAway mappings=[home="Home",away="Away"]
                        Switch item=S_C_NestFan
                        Text item=N_V_NestCurrTemp {
                                Frame{
                                        Switch item=Nest_Temp_Chart_Period label="Period" mappings=[0="Hour", 1="Day", 2="Week"]
                                        Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestCurrTemp,Weather_Temperature&period=h" refresh=6000 visibility=[Nest_Temp_Chart_Period==0, Nest_Temp_Chart_Period=="Uninitialized"]
                                        Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestCurrTemp,Weather_Temperature&period=D" refresh=30000 visibility=[Nest_Temp_Chart_Period==1]
                                        Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestCurrTemp,Weather_Temperature&period=W" refresh=30000 visibility=[Nest_Temp_Chart_Period==2]
                                }
                        }
                        Text item=N_V_NestHumidity{
                                Frame{
                                        Switch item=Nest_Hum_Chart_Period label="Period" mappings=[0="Hour", 1="Day", 2="Week"]
                                        Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestHumidity,Weather_Humidity&period=h" refresh=6000 visibility=[Nest_Hum_Chart_Period==0, Nest_Hum_Chart_Period=="Uninitialized"]
                                        Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestHumidity,Weather_Humidity&period=D" refresh=30000 visibility=[Nest_Hum_Chart_Period==1]
                                        Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestHumidity,Weather_Humidity&period=W" refresh=30000 visibility=[Nest_Hum_Chart_Period==2]
                                }
                        }
                        Setpoint item=S_V_NestTargetTemp minValue=50 maxValue=80 step=1
                        Text item=N_NestOnline
                }
        }

#Persistence

// persistence strategies have a name and a definition and are referred to in the "Items" section
Strategies {
        // for rrd charts, we need a cron strategy
        everyMinute : "0 * * * * ?"

        default = everyChange
}

Items {

        // additionally persist weather info every minute
        gHistory*,gChart* : strategy = everyUpdate, everyMinute
}

Theory of Operation:

Every 30 minutes , when the current inside temp changes, and when the target temp changes trigger a rule to see if the fan needs to be turned on.

In the rule, if our target temp is less than the current outside temp (obtained from the Weather binding) see if we should turn on the fan.

If the current temp is warmer than the target + 1 degree and the fan isn’t already on, turn on the fan (NOTE: nest will only keep the fan on for 30 minutes then shut it off automatically).

If the current temp is cooler than the target and the fan is running, turn the fan off.

NOTE: Our house does not change temperature that fast and turning on the fan does not really drop the inside temp but instead keeps it from raising or raising as fast as it otherwise would so for my setup I don’t need to worry too much about rapidly cycling the fan when we current inside temp and target temp are really close together.

That is the main part of the logic you care about. The rule then goes on to send a notification (no more than once per day) to open the windows or close the blinds when it is appropriate.

The sitemap looks like this:

You can configure your three-way setting the same way I handle the chart period on the sitemap above.

In order for charting to work I configured persistence. I use rrd4j which requires values be saves at least once per minute for charting to work. So you will notice that each of the Items that I create charts for are members of the gChart group which causes them to be persisted so I can create charts.

NOTE: What you see with these charts is what you get. If you want something configurable or more attractive you must save to an externally accessible database (e.g. MySQL) and use a third party charting tool.

You will need to do a good deal of customization to this but it should be enough to get you started.

1 Like

Thanks, guys, this is all very useful. @rlkoshak’s example is particularly useful as it is complete and real world. (PS You want to change your password on localhost now :-))

I’ll give it a shot and see where it goes.

Oops! At least that was only my OH password (emphasis on was). In fact the tragic part is I don’t think the user/password is even required now that I’m using security=EXTERNAL. I set up these charts a loooong time ago.