forked from electro/esp-irblaster
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							283 lines
						
					
					
						
							9.4 KiB
						
					
					
				
			
		
		
	
	
							283 lines
						
					
					
						
							9.4 KiB
						
					
					
				//
 | 
						|
// Created by MightyPork on 2022/08/20.
 | 
						|
//
 | 
						|
 | 
						|
#include "fancontrol.h"
 | 
						|
#include "freertos/FreeRTOS.h"
 | 
						|
#include "freertos/timers.h"
 | 
						|
#include "settings.h"
 | 
						|
#include "actuators.h"
 | 
						|
 | 
						|
#define UNIDIR_T_MEAS_PERIOD 15 /* s */
 | 
						|
struct FanControlState gState = {};
 | 
						|
 | 
						|
static void timerCallback(TimerHandle_t xTimer);
 | 
						|
 | 
						|
static void invalidate_temps();
 | 
						|
 | 
						|
void settings_blind_time_set(uint16_t blind_time)
 | 
						|
{
 | 
						|
    bool nadoraz = (gState.blind_position >= gSettings.blind_time) || (gState.blind_position >= blind_time);
 | 
						|
 | 
						|
    gSettings.blind_time = blind_time;
 | 
						|
 | 
						|
    if (nadoraz) {
 | 
						|
        gState.blind_position = blind_time;
 | 
						|
    }
 | 
						|
 | 
						|
    settings_persist(SETTINGS_blind_time);
 | 
						|
}
 | 
						|
 | 
						|
void fancontrol_init()
 | 
						|
{
 | 
						|
    gState.set_vent_mode = gSettings.initial_mode;
 | 
						|
    gState.set_power = gSettings.initial_power;
 | 
						|
 | 
						|
    xTimerCreate("fanctl",
 | 
						|
                 pdMS_TO_TICKS(1000),
 | 
						|
                 pdTRUE,
 | 
						|
                 NULL,
 | 
						|
                 timerCallback);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void timerCallback(TimerHandle_t xTimer)
 | 
						|
{
 | 
						|
    // posun rolety
 | 
						|
    if (gAct.blind) {
 | 
						|
        if (gState.blind_position < gSettings.blind_time) {
 | 
						|
            gState.blind_position++;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        if (gState.blind_position > 0) {
 | 
						|
            gState.blind_position--;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (gAct.power > 0) {
 | 
						|
        if (gState.real_direction != gAct.dir) {
 | 
						|
            if (gState.ramp > 0) {
 | 
						|
                gState.ramp--;
 | 
						|
            } else {
 | 
						|
                gState.run_time = 0;
 | 
						|
                gState.real_direction = gAct.dir;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if (gState.ramp < gSettings.ramp_time) {
 | 
						|
                gState.ramp++;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        gState.run_time++;
 | 
						|
    } else {
 | 
						|
        if (gState.ramp > 0) {
 | 
						|
            gState.ramp--;
 | 
						|
        } else {
 | 
						|
            gState.run_time = 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    bool end_temp_meas = false;
 | 
						|
 | 
						|
    switch (gState.effective_vent_mode) {
 | 
						|
        case VENT_MODE_OFF:
 | 
						|
            act_motor_power_set(0);
 | 
						|
            act_blind_set(0);
 | 
						|
            break;
 | 
						|
        case VENT_MODE_FREE:
 | 
						|
            act_motor_power_set(0);
 | 
						|
            act_blind_set(1);
 | 
						|
            break;
 | 
						|
        case VENT_MODE_OUT:
 | 
						|
            act_motor_direction_set(MOTOR_DIR_OUT);
 | 
						|
            act_blind_set(1);
 | 
						|
            if (gState.blind_position >= gSettings.blind_time) {
 | 
						|
                act_motor_power_set(gState.set_power);
 | 
						|
            }
 | 
						|
            if (gState.t_aggr_cnt > UNIDIR_T_MEAS_PERIOD) {
 | 
						|
                end_temp_meas = true;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case VENT_MODE_IN:
 | 
						|
            act_motor_direction_set(MOTOR_DIR_IN);
 | 
						|
            act_blind_set(1);
 | 
						|
            if (gState.blind_position >= gSettings.blind_time) {
 | 
						|
                act_motor_power_set(gState.set_power);
 | 
						|
            }
 | 
						|
            if (gState.t_aggr_cnt > UNIDIR_T_MEAS_PERIOD) {
 | 
						|
                end_temp_meas = true;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case VENT_MODE_RECUP:
 | 
						|
            act_blind_set(1);
 | 
						|
            if (gState.blind_position >= gSettings.blind_time) {
 | 
						|
                act_motor_power_set(gState.set_power);
 | 
						|
 | 
						|
                if (gState.real_direction == gAct.dir && gState.run_time >= gSettings.min_recup_time) {
 | 
						|
                    // Stop condition
 | 
						|
                    bool do_switch = false;
 | 
						|
 | 
						|
                    // TODO questionable logic, verify
 | 
						|
 | 
						|
                    if (gSettings.recup_mode == RECUP_MODE_TIME) {
 | 
						|
                        do_switch = gState.run_time >= gSettings.recup_time;
 | 
						|
                    } else if (gState.run_time >= gSettings.max_recup_time) {
 | 
						|
                        do_switch = true;
 | 
						|
                    } else {
 | 
						|
                        // temp-based switching - magic(tm)
 | 
						|
                        // Delta = IN - OUT
 | 
						|
                        const int16_t ideal_delta = gState.t_indoor - gState.t_outdoor;
 | 
						|
                        int16_t stop_delta = ((int32_t) ideal_delta
 | 
						|
                                              * (int32_t) (100 - gSettings.recup_factor)) / 100;
 | 
						|
                        int16_t delta;
 | 
						|
                        bool allow_temp_switch = false;
 | 
						|
 | 
						|
                        if (gState.real_direction == MOTOR_DIR_OUT) {
 | 
						|
                            if (gState.valid_t_indoor && gState.valid_t_exhaust && gState.valid_t_outdoor) {
 | 
						|
                                delta = gState.t_indoor - gState.t_exhaust;
 | 
						|
                                allow_temp_switch = true;
 | 
						|
                            }
 | 
						|
                        } else {
 | 
						|
                            // IN
 | 
						|
                            if (gState.valid_t_inflow && gState.valid_t_indoor && gState.valid_t_outdoor) {
 | 
						|
                                delta = gState.t_inflow - gState.t_outdoor;
 | 
						|
                                allow_temp_switch = true;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (allow_temp_switch) {
 | 
						|
                            if (gSettings.summer_mode) {
 | 
						|
                                if (ideal_delta < 0) {
 | 
						|
                                    // warmer outside, trying to keep cool in
 | 
						|
                                    if (delta >= stop_delta) {
 | 
						|
                                        do_switch = true;
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                                // colder outside - no stopping (will run to max recup time)
 | 
						|
                            } else {
 | 
						|
                                if (ideal_delta > 0) {
 | 
						|
                                    // colder outside, trying to keep warmth in
 | 
						|
                                    if (delta <= stop_delta) {
 | 
						|
                                        do_switch = true;
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                                // warmer outside - no stopping (will run to max recup time)
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (do_switch) {
 | 
						|
                        // zmena smeru
 | 
						|
                        if (gAct.dir == MOTOR_DIR_IN) {
 | 
						|
                            gState.real_recup_time_in = gState.run_time;
 | 
						|
                        } else {
 | 
						|
                            gState.real_recup_time_out = gState.run_time;
 | 
						|
                        }
 | 
						|
                        gState.run_time = 0;
 | 
						|
                        act_motor_direction_set(1 - gAct.dir);
 | 
						|
                        end_temp_meas = true;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
    if (gState.effective_vent_mode == VENT_MODE_RECUP) {
 | 
						|
        if (gState.real_direction == MOTOR_DIR_OUT) {
 | 
						|
            gState.instantaneous_vent_mode = VENT_MODE_OUT;
 | 
						|
        } else {
 | 
						|
            gState.instantaneous_vent_mode = VENT_MODE_IN;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        gState.instantaneous_vent_mode = gState.effective_vent_mode;
 | 
						|
    }
 | 
						|
 | 
						|
    if (end_temp_meas) {
 | 
						|
        if (gState.t_aggr_cnt > 0) {
 | 
						|
            int16_t t1 = (int16_t) (gState.t1_aggr / (int32_t)gState.t_aggr_cnt);
 | 
						|
            int16_t t2 = (int16_t) (gState.t2_aggr / (int32_t)gState.t_aggr_cnt);
 | 
						|
            switch (gState.real_direction) {
 | 
						|
                case MOTOR_DIR_IN:
 | 
						|
                    gState.t_outdoor = t2;
 | 
						|
                    gState.t_inflow = t1;
 | 
						|
                    gState.valid_t_outdoor = gState.valid_t_inflow = true;
 | 
						|
                    if (gState.effective_vent_mode == VENT_MODE_IN) {
 | 
						|
                        gState.valid_t_indoor = gState.valid_t_exhaust = false;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                case MOTOR_DIR_OUT:
 | 
						|
                    gState.t_indoor = t1;
 | 
						|
                    gState.t_exhaust = t2;
 | 
						|
                    gState.valid_t_indoor = gState.valid_t_exhaust = true;
 | 
						|
                    if (gState.effective_vent_mode == VENT_MODE_OUT) {
 | 
						|
                        gState.valid_t_outdoor = gState.valid_t_inflow = false;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        gState.t1_aggr = gState.t2_aggr = 0;
 | 
						|
        gState.t_aggr_cnt = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (gState.effective_vent_mode == VENT_MODE_IN
 | 
						|
        || gState.effective_vent_mode == VENT_MODE_OUT
 | 
						|
        || gState.effective_vent_mode == VENT_MODE_RECUP) {
 | 
						|
        // Measure temperatures
 | 
						|
        int16_t t1 = act_temp_in();
 | 
						|
        int16_t t2 = act_temp_out();
 | 
						|
 | 
						|
        gState.t_actual_in = t1;
 | 
						|
        gState.t_actual_out = t2;
 | 
						|
 | 
						|
        gState.valid_t_actual_in = gState.valid_t_actual_out = true;
 | 
						|
 | 
						|
        if (gAct.dir == gState.real_direction
 | 
						|
            && gState.ramp >= gSettings.ramp_time)
 | 
						|
        {
 | 
						|
            gState.t1_aggr += t1;
 | 
						|
            gState.t2_aggr += t2;
 | 
						|
            gState.t_aggr_cnt++;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        gState.valid_t_actual_in = gState.valid_t_actual_out = false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void fan_set_vent_mode(enum ventilation_mode mode)
 | 
						|
{
 | 
						|
    if (mode == gState.set_vent_mode) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    gState.set_vent_mode = mode;
 | 
						|
    gState.t1_aggr = gState.t2_aggr = gState.t_aggr_cnt = 0;
 | 
						|
    if (gState.set_power != 0 || mode == VENT_MODE_FREE) {
 | 
						|
        gState.effective_vent_mode = mode;
 | 
						|
    } else if (gState.set_power == 0) {
 | 
						|
        gState.effective_vent_mode = VENT_MODE_OFF;
 | 
						|
    }
 | 
						|
 | 
						|
    if (mode == VENT_MODE_OFF || mode == VENT_MODE_FREE) {
 | 
						|
        invalidate_temps();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void invalidate_temps()
 | 
						|
{
 | 
						|
    gState.valid_t_indoor = false;
 | 
						|
    gState.valid_t_outdoor = false;
 | 
						|
    gState.valid_t_inflow = false;
 | 
						|
    gState.valid_t_exhaust = false;
 | 
						|
    gState.valid_t_actual_in = false;
 | 
						|
    gState.valid_t_actual_out = false;
 | 
						|
}
 | 
						|
 | 
						|
void fan_set_power(uint16_t power)
 | 
						|
{
 | 
						|
    gState.set_power = power;
 | 
						|
    if (power == 0) {
 | 
						|
        gState.effective_vent_mode = VENT_MODE_OFF;
 | 
						|
        invalidate_temps();
 | 
						|
    } else {
 | 
						|
        gState.effective_vent_mode = gState.set_vent_mode;
 | 
						|
    }
 | 
						|
}
 | 
						|
 |