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.
295 lines
8.8 KiB
295 lines
8.8 KiB
//
|
|
// Created by MightyPork on 2022/08/20.
|
|
//
|
|
|
|
#include <esp_log.h>
|
|
#include "fancontrol.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/timers.h"
|
|
#include "settings.h"
|
|
#include "actuators.h"
|
|
#include "onewires.h"
|
|
#include "tasks.h"
|
|
|
|
const char *TAG = "fc";
|
|
|
|
#define UNIDIR_T_MEAS_PERIOD 60
|
|
struct FanControlState gState = {};
|
|
|
|
static void timerCallback();
|
|
|
|
static void invalidate_temps();
|
|
|
|
void settings_blind_time_set(uint16_t blind_time)
|
|
{
|
|
// if the blind is surely at the end
|
|
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);
|
|
}
|
|
|
|
static void fanctltask(void *dummy) {
|
|
TickType_t last_wake_time = xTaskGetTickCount();
|
|
bool statusled = 0;
|
|
while (1) {
|
|
act_statusled_set(statusled);
|
|
statusled ^= 1;
|
|
//
|
|
timerCallback();
|
|
vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(1000));
|
|
|
|
gState.uptime_secs += 1;
|
|
if (gState.uptime_secs >= 3600) {
|
|
gState.uptime_secs = 0;
|
|
gState.uptime_hours += 1;
|
|
}
|
|
|
|
if ((gState.effective_vent_mode & 6) != 0) {
|
|
gState.motor_secs += 1;
|
|
if (gState.motor_secs >= 3600) {
|
|
gState.motor_secs = 0;
|
|
gState.motor_hours += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void fancontrol_init()
|
|
{
|
|
gState.set_vent_mode = gSettings.initial_mode;
|
|
gState.set_power = gSettings.initial_power;
|
|
|
|
// this doesnt work
|
|
// TimerHandle_t hTimer = xTimerCreate("fanctl",
|
|
// pdMS_TO_TICKS(1000),
|
|
// pdTRUE,
|
|
// NULL,
|
|
// timerCallback);
|
|
//
|
|
// xTimerStart(hTimer, pdMS_TO_TICKS(2000));
|
|
|
|
xTaskCreate(fanctltask, "fc", FCTL_TASK_STACK, NULL, FCTL_TASK_PRIO, NULL);
|
|
}
|
|
|
|
static const char * vent_mode_labels[] = {
|
|
[VENT_MODE_OFF] = "OFF",
|
|
[VENT_MODE_FREE] = "FREE",
|
|
[VENT_MODE_OUT] = "OUT",
|
|
[VENT_MODE_IN] = "IN",
|
|
[VENT_MODE_RECUP] = "RECUP",
|
|
};
|
|
|
|
//static const char * recup_mode_labels[] = {
|
|
// [RECUP_MODE_TIME] = "TIME",
|
|
// [RECUP_MODE_TEMP] = "TEMP",
|
|
//};
|
|
|
|
static void timerCallback()
|
|
{
|
|
// 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++;
|
|
}
|
|
}
|
|
if (gState.run_time < 0xFFFF) {
|
|
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);
|
|
}
|
|
// some time is needed before the temperature means anything
|
|
if (gState.run_time >= 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.run_time >= 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;
|
|
|
|
if (gSettings.recup_mode == RECUP_MODE_TIME) {
|
|
do_switch = gState.run_time >= gSettings.recup_time;
|
|
} else {
|
|
if (gState.run_time >= gSettings.max_recup_time) {
|
|
// Max time elapsed, switch even if the condition was not reached
|
|
do_switch = true;
|
|
} else {
|
|
// TODO
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Measure temperatures
|
|
cels_t t1 = act_temp1();
|
|
cels_t t2 = act_temp2();
|
|
|
|
gState.t_actual_in = t1;
|
|
gState.t_actual_out = t2;
|
|
|
|
gState.valid_t_actual_in = gState.valid_t_actual_out = tempSensorsOk;
|
|
|
|
if (end_temp_meas) {
|
|
switch (gState.real_direction) {
|
|
case MOTOR_DIR_IN:
|
|
gState.t_outdoor = gState.t_actual_out;
|
|
gState.t_inflow = gState.t_actual_in;
|
|
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 = gState.t_actual_in;
|
|
gState.t_exhaust = gState.t_actual_out;
|
|
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;
|
|
}
|
|
}
|
|
|
|
ESP_LOGI(TAG,
|
|
"%s (%s), B%ds, M%ds, %d%%, Tid %.2f%s, Tod %.2f%s, Tit %.2f%s, Teh %.2f%s, T1 %.2f%s, T2 %.2f%s C",
|
|
vent_mode_labels[gState.set_vent_mode], vent_mode_labels[gState.instantaneous_vent_mode],
|
|
gState.blind_position,
|
|
gState.run_time,
|
|
gAct.power,
|
|
|
|
gState.t_indoor, gState.valid_t_indoor ? "" : "!",
|
|
gState.t_outdoor, gState.valid_t_outdoor ? "" : "!",
|
|
gState.t_inflow, gState.valid_t_inflow ? "" : "!",
|
|
gState.t_exhaust, gState.valid_t_exhaust ? "" : "!",
|
|
gState.t_actual_in, gState.valid_t_actual_in ? "" : "!",
|
|
gState.t_actual_out, gState.valid_t_actual_out ? "" : "!"
|
|
);
|
|
}
|
|
|
|
void fan_set_vent_mode(enum ventilation_mode mode)
|
|
{
|
|
ESP_LOGI(TAG, "Set vent mode = %s", vent_mode_labels[mode]);
|
|
|
|
if (mode == gState.set_vent_mode) {
|
|
return;
|
|
}
|
|
|
|
gState.set_vent_mode = mode;
|
|
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(perc_t power)
|
|
{
|
|
ESP_LOGI(TAG, "Set power = %d%%", 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;
|
|
}
|
|
}
|
|
|