Водяные теплые полы 1-ый этаж;
Батареи на первом и втором этаже (на первом меньше за счет теплого пола);
Газовый котел, который греет воду для теплого пола, батарей и нагрева горячей воды.
Хочется:
Управлять температурой нагрева теплого пола и батарей, чтоб не было душно или холодно в доме, в зависимости от внешней температуры.
Проблема:
Крутить температуру нагрева воды на газовом котле не хочется, потому как котел греет воду в том числе и для нагрева горячей воды которая течет из крана (используется бойлер на 300 л, через него и пропускается горячая вода из газового котла, которая нагревает воду поступающую в краны). Соответственно, если буду крутить температуру нагрева воды, то и в кране будет меняться температура воды, а этого не хочется.
Реализация:
Нагретая вода из газового котла (замкнутый контур нагреваемой воды) поступает в раздаточный коллектор и оттуда идет в контур водяных полов, контур отопления (батареи) и контур нагрева горячей воды в бойлере. Каждый отвод соединен с магистральным насосом который прокачивает воду в своем контуре и по обратке вода поступает опять в котел для нагрева.
Для раздачи воды на теплые полы (после насоса) стоит гребенка с кранами на которых можно порегулировать напор (и соответственно температуру) для теплых полов в разных помещениях(их есть три отдельных контура). Очень не хочется ставить управление на эти краны и постоянно их дрюкать, да и не копеечные они.
Решил управлять временем работы насосов для теплых полов и батарей, каждым отдельно.
Идея следующая – берем период повторения регулировки 1 час. То есть в течении часа, если нагрев полов 0%, то насос 60 минут отключен, если ставим нагрев 17%, то насос в течении часа 5 мин работает 25 мин отдыхает, затем опять 5 мин работает 25 мин отдыхает. Если 34% нагрев, то насос в течении часа 10 мин работает 20 мин отдыхает, затем опять 10 мин работает 20 мин отдыхает. На 50% нагрева насос 10 мин работает, 10 отдыхает и так до смены режима нагрева. Надеюсь идея понятна.
В железе реализовано следующим образом:
Arduino UNO (потому что использую для защиты от зависаний сторожевой таймер)
через реле управляет насосом;
через отдельные кнопки для теплых полов и для батарей устанавливается режим отдельно для каждого контура (по кольцу можно перебирать 6 режимов нагрева);
на индикаторе LCD1604 отображается текущий режим для пола и для батарей, а также знак включенного насоса соответствующего контура;
есть светодиод, моргающий с секундным интервалом, для контроля, что скетч на ардуине крутится;
через RX/TX передаются на ESP8266 (прошивка wifi-iot) текущий режим нагрева теплого пола и отопления в разных переменных, далее в Majordomo;
через ESP8266 посредством RX/TX принимаются режимы нагрева для теплого пола и отопления и отображаются на индикаторе LCD1604;
текущим режимом нагрева является последний введенный либо с кнопок, либо полученный от Majordomo через ESP8266;
Majordomo каждые 7 минут (устанавливается в ESP8266) получает текущие режимы нагрева.
Таким образом контроллер может автономно управляться с кнопок и работать самостоятельно, также воспринимает команды от Majordomo и меняет режим.
Код: Выделить всё
#include <avr/wdt.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//+++++++++++++++++++
#include <RBD_Timer.h>
#include <RBD_Button.h>
#define wf_num_modes 6 // максимальный номер режима
short int wf_max_mode = wf_num_modes + 1; // вспомогательная переменная
int wf_butt = 0; // текущий режим
int wf_butt_temp = 0;
byte wf_buttChar = 'a';
byte wf_buttChar_temp = 'a';
#define rd_num_modes 6 // максимальный номер режима
short int rd_max_mode = rd_num_modes + 1; // вспомогательная переменная
int rd_butt = 0; // текущий режим
int rd_butt_temp = 0;
byte rd_buttChar = 'k';
byte rd_buttChar_temp = 'k';
// input pullup enabled by default
RBD::Button wf_button(9);
RBD::Button rd_button(12);
char esp_serial = 'a';
char wf_common = 'a';
char rd_common = 'k';
//+++++++++++++++++++
LiquidCrystal_I2C lcd(0x27, 16, 2); // Устанавливаем дисплей
//*******************
// Эти переменные хранят временной шаблон для интервалов включения
// и текущее состояние насоса
int wf_relayPin = 10; // номер пина с реле насоса ТП
int rd_relayPin = 8; // номер пина с реле насоса батарей
int ledPin = 11; // номер пина с контрольным светодиодом
int wf_relayState = LOW; // состояние реле насоса ТП
int rd_relayState = LOW; // состояние реле насоса батарей
int ledState = LOW; // состояние контрольного светодиода
// последний момент времени, когда состояние реле насоса изменялось
unsigned long wf_previousMillis = 0;
unsigned long rd_previousMillis = 0;
unsigned long led_previousMillis = 0;
long wf_OnTime = 0; // длительность включения насоса ТП (в миллисекундах)
long wf_OffTime = 36000; // длительность выключения насоса ТП (в миллисекундах)
long rd_OnTime = 0; // длительность включения насоса батарей (в миллисекундах)
long rd_OffTime = 36000; // длительность выключения насоса батарей (в миллисекундах)
int led_OnTime = 1000; // Контрольный светодиод вкл
int led_OffTime = 1000; // Контрольные светодиод выкл
//*******************
//-------------------
//Счетчик для отправки в UART
unsigned long uart_previousMillis;
#define uart_interval 600000 //10 мин интервал отправки данных в Majordomo
//#define uart_interval 5000 //5 сек интервал отправки данных в Majordomo для теста
//-------------------
void setup() {
wdt_disable();
Serial.begin(9600);
pinMode(wf_relayPin, OUTPUT);
pinMode(rd_relayPin, OUTPUT);
pinMode(ledPin, OUTPUT);
lcd.init();
lcd.backlight();// Включаем подсветку дисплея
lcd.print("Floor");
lcd.setCursor(1, 1); //setCursor(column, row) upper-0, left-0
lcd.print("min");
lcd.setCursor(10, 0);
lcd.print("Radiat");
lcd.setCursor(11, 1); //setCursor(column, row) upper-0, left-0
lcd.print("min");
delay(3000);
wdt_enable(WDTO_4S);
}
void loop() {
if (Serial.available() > 0)
{
esp_serial = Serial.read(); //Чтение последнего символа из Serial
if (byte(esp_serial) > 96 & byte(esp_serial) < 104) {
wf_common = esp_serial;
wf_switchCommon();
}
if (byte(esp_serial) > 106 & byte(esp_serial) < 114) {
rd_common = esp_serial;
rd_switchCommon();
}
}
if(wf_button.onPressed()) {
wf_butt++; // увеличиваем счетчик текушего режима
wf_butt %= wf_max_mode; // остаток от целочисленного деления
if (wf_butt == 0) wf_butt = 0; // режим меняется от 0 до wf_num_modes
wf_buttChar = 97 + wf_butt;
wf_common = char(wf_buttChar);
wf_switchCommon();
}
if(rd_button.onPressed()) {
rd_butt++; // увеличиваем счетчик текушего режима
rd_butt %= rd_max_mode; // остаток от целочисленного деления
if (rd_butt == 0) rd_butt = 0; // режим меняется от 0 до wf_num_modes
rd_buttChar = 107 + rd_butt;
rd_common = char(rd_buttChar);
rd_switchCommon();
}
// выясняем не настал ли момент сменить состояние насоса ТП
unsigned long currentMillis = millis(); // текущее время в миллисекундах
// Проверка не пора ли выключать
// если насос включен и работает больше чем надо
if ((wf_relayState == HIGH) && (currentMillis - wf_previousMillis >= wf_OnTime))
{
wf_relayState = LOW; // выключаем
lcd.setCursor(0, 1);
lcd.print(" ");
wf_previousMillis = currentMillis; // запоминаем момент времени
digitalWrite(wf_relayPin, wf_relayState); // реализуем новое состояние реле насоса
}
// Проверка не пора ли включать
else if ((wf_relayState == LOW) && (currentMillis - wf_previousMillis >= wf_OffTime))
{
wf_relayState = HIGH; // включаем
lcd.setCursor(0, 1);
lcd.print("*");
wf_previousMillis = currentMillis ; // запоминаем момент времени
digitalWrite(wf_relayPin, wf_relayState); // реализуем новое состояние реле насоса
}
// выясняем не настал ли момент сменить состояние насоса батарей
currentMillis = millis(); // текущее время в миллисекундах
// Проверка не пора ли выключать
// если насос включен и работает больше чем надо
if ((rd_relayState == HIGH) && (currentMillis - rd_previousMillis >= rd_OnTime))
{
rd_relayState = LOW; // выключаем
lcd.setCursor(15, 1);
lcd.print(" ");
rd_previousMillis = currentMillis; // запоминаем момент времени
digitalWrite(rd_relayPin, rd_relayState); // реализуем новое состояние реле насоса
}
// Проверка не пора ли включать
else if ((rd_relayState == LOW) && (currentMillis - rd_previousMillis >= rd_OffTime))
{
rd_relayState = HIGH; // включаем
lcd.setCursor(15, 1);
lcd.print("*");
rd_previousMillis = currentMillis ; // запоминаем момент времени
digitalWrite(rd_relayPin, rd_relayState); // реализуем новое состояние реле насоса
}
currentMillis = millis(); // текущее время в миллисекундах
// Светодиод выкл
if ((ledState == HIGH) && (currentMillis - led_previousMillis >= led_OnTime))
{
ledState = LOW; // выключаем
led_previousMillis = currentMillis; // запоминаем момент времени
digitalWrite(ledPin, ledState); // реализуем новое состояние
}
// Проверка не пора ли светодиод включать
else if ((ledState == LOW) && (currentMillis - led_previousMillis >= led_OffTime))
{
ledState = HIGH; // выключаем
led_previousMillis = currentMillis; // запоминаем момент времени
digitalWrite(ledPin, ledState); // реализуем новое состояние
}
currentMillis = millis(); // текущее время в миллисекундах
if (currentMillis >= (uart_previousMillis + uart_interval)) // сравниваем текущий таймер с переменной loopTimeUART
{
// отправка текущего режима в UART
Serial.print("&wf=");
Serial.print(wf_common);
Serial.print("&rd=");
Serial.println(rd_common);
uart_previousMillis = currentMillis;
}
// Serial.print("State WF_common after switch: ");
// Serial.println(wf_common);
// Serial.print("On Time WF: ");
// Serial.println(OnTime);
// Serial.print("Off Time WF: ");
// Serial.println(OffTime);
// Serial.print("State RD_common after switch: ");
// Serial.println(rd_common);
// Serial.print("On Time RD: ");
// Serial.println(OnTime_rd);
// Serial.print("Off Time RD: ");
// Serial.println(OffTime_rd);
wdt_reset();
}
//////////////////////////////////////////////////////////////////////////////////////////
// Finish
//////////////////////////////////////////////////////////////////////////////////////////
void wf_switchCommon(){
switch (wf_common)
{
case 'a':
// насос отключен
wf_OnTime = 0;
wf_OffTime = 3600000;
lcd.setCursor(1, 1);
lcd.print("min");
break;
case 'b':
// насос 17% On - 83% Off
wf_OnTime = 300000;
wf_OffTime = 1500000;
lcd.setCursor(1, 1);
lcd.print("17%");
break;
case 'c':
// насос 34% On - 66% Off
wf_OnTime = 600000;
wf_OffTime = 1200000;
lcd.setCursor(1, 1);
lcd.print("34%");
break;
case 'd':
// насос 50% On - 50% Off
wf_OnTime = 900000;
wf_OffTime = 900000;
lcd.setCursor(1, 1);
lcd.print("50%");
break;
case 'e':
// насос 66% On - 34% Off
wf_OnTime = 1200000;
wf_OffTime = 600000;
lcd.setCursor(1, 1);
lcd.print("66%");
break;
case 'f':
// насос 83% On - 17% Off
wf_OnTime = 1500000;
wf_OffTime = 300000;
lcd.setCursor(1, 1);
lcd.print("83%");
break;
case 'g':
// насос постоянно включен
wf_OnTime = 3600000;
wf_OffTime = 0;
lcd.setCursor(1, 1);
lcd.print("max");
break;
}
}
void rd_switchCommon(){
switch (rd_common)
{
case 'k':
// насос отключен
rd_OnTime = 0;
rd_OffTime = 3600000;
lcd.setCursor(11, 1);
lcd.print("min");
break;
case 'l':
// насос 17% On - 83% Off
rd_OnTime = 300000;
rd_OffTime = 1500000;
lcd.setCursor(11, 1);
lcd.print("17%");
break;
case 'm':
// насос 34% On - 66% Off
rd_OnTime = 600000;
rd_OffTime = 1200000;
lcd.setCursor(11, 1);
lcd.print("34%");
break;
case 'n':
// насос 50% On - 50% Off
rd_OnTime = 900000;
rd_OffTime = 900000;
lcd.setCursor(11, 1);
lcd.print("50%");
break;
case 'o':
// насос 66% On - 34% Off
rd_OnTime = 1200000;
rd_OffTime = 600000;
lcd.setCursor(11, 1);
lcd.print("66%");
break;
case 'p':
// насос 83% On - 17% Off
rd_OnTime = 1500000;
rd_OffTime = 300000;
lcd.setCursor(11, 1);
lcd.print("83%");
break;
case 'q':
// насос постоянно включен
rd_OnTime = 3600000;
rd_OffTime = 0;
lcd.setCursor(11, 1);
lcd.print("max");
break;
}
}