Идея банальна, понадобился контроллер для управления нагрузкой в доме:
1. Котел отопления.
2. Накопительный бойлер для водоснабжения.
3. Насос в скважине.

Читал массу увлекательных статей на тему ХХ на Ардуино, читая которые четко фиксировал в голове мысль «хочу Ардуино». Прикинув стоимость компонентов и готовых решений, посчитал явную выгоду от внедрения Ардуино.

image

Итак, программа минимум:


1. 4 реле, часы (RTC), ЖК экран;
2. Режимы работы каждого реле: включено, выключено, суточный таймер, одноразовое включение;
3. Кнопки управления для настройки времени и режимов реле;

В доме установлен двухтарифный счетчик, поэтому бойлер нагревает воду с 23 до 7 утра. Аналогично отопление: два из трех тэнов, по моей задумке будут включаться ночью. Управление температурой пока остается родное на штатном пульте. Одноразовое включение в качестве резерва пойдет на насос, программируем включение, например, на набор емкости или прокачку скважины, после чего реле переходит в режим выключено. Основная особенность: изготовлено законченное устройство, управляемое кнопками, и не требующее подключения к ПК.

Конечно, хотелось в перспективе все повесить на контроллер, так как для отопления целесообразно сделать 3 режима работы: день с 7 до 23 в целях экономии, ночь, разогрев к утреннему отключению с 5..6 до 7. Но пока реализована программа минимум.

Аппаратная часть:


При изготовлении была задача получить как можно более дешевое изделие, поэтому максимально присутствует колхоз. На Али были заказаны стартовый комплект для arduino Uno R3, 4 релейный модуль, жк-экран I2C 20*4, часы RTC DS1307 I2C, цифровой датчик температуры и влажности Dht21.

Поскольку все это видел первый раз пришлось осваивать. Общие понятия почерпнул с помощью гугла из:
http://habrahabr.ru/company/masterkit/blog/257747/
http://arduino.ru/Reference

Красивую схему подключения сделать не могу, не в чем. Во Fritzing к примеру, из компонентов только сам микроконтроллер.

Подключение реле и кнопок проблем не вызвало, единственно включил подтягивающие резисторы. Это есть в руководстве. В подключении ЖК экрана помогла ссылка https://arduino-info.wikispaces.com/LCD-Blue-I2C#v3. Потребовалось регулировка подстроечным резистором, «из коробки» экран не горел совсем, чем вызвал у меня легкое замешательство.

Часы потребовали только батарейку, подключил по типовой схеме http://zelectro.cc/RTC_DS1307_arduino

Синхронизацию часов с компьютером делать не стал. При запуске производится проверка, если дата меньше 2000 года или больше 2100, выводится меню настройки часов.

Подключение нескольких кнопок к аналоговому входу описано по ссылке http://arduino.net.ua/Arduino_articles/Arduino_proekty/Podkljuchenie%20knopok%20k%20odnomu%20analogovomu%20vhodu/, там же в комментариях описано как включить подтягивающие резисторы «pinMode (A2, INPUT_PULLUP);»

Управление классическое, «мониторное»: кнопки «меню», "+","-", «set».

Процесс сборки (с фото)
Взял монтажный щиток на 6 автоматов:

image

Поставил монтажные стойки под модули:

image

Прикрутил реле, часы, контроллер:

image

От принтера взял пару валиков и какую-то втулку. Втулка приклеена на двухсторонний скотч. На них будет крепиться еще одна плата, об этом ниже.

Блок питания взял от какого-то роутера Dlink, 5В 2А, ломать голову не стал запаял прямо на него USB провод:

image

Вырезал из пластика панель для крепления экрана:

image

Установил экран. Закрепил монтажными стойками, под клавиатурой — винт. Стойки подобраны по высоте с расчетом, что в них будет упираться крышка щитка, придавая жесткость конструкции. Втулка на блоке реле предотвращает продавливание платы вниз при нажатии кнопок.

image

Кнопки изначально планировал подключить к цифровым входам, но внезапно нашел модуль клавиатуры от монитора, который подошел как родной (схема кнопок от монитора).

Клавиатуру приклеил на двухсторонний скотч через прокладку, что бы поднять плату над правым нижним винтом. Кнопки нажимаю спичкой через отверстия. В идеале надо рассверлить отверстия и вставить туда нормальные толкатели. Может быть сделаю холодным зимним вечером, а сейчас потребовалось срочно внедрить реле на нагрев воды.

Фото готового устройства:
image
image

Мигающий светодиод также присутствует.


В данный момент реле висит «на соплях», управляет бойлером, окончательный монтаж будет произведен после установки проводки и контакторов для котла отопления. Фазу с колодки тоже надо убрать, конечно, при монтаже проводки. Сейчас некогда, надо делать наружные работы по дому. Затраты на детали составили около 2 тысяч рублей.

Программная часть:


Программная часть далась нелегко: 90% времени ушло на написание меню, годный код удалось осилить только с третьей версией прошивки.

Первый подход вырос из тестовых образцов на проверку деталей. Классический образец процедурного программирования, развития не получил. Пришлось вспоминать принципы написания хорошего, годного кода, который бы поддавался последующему чтению и правке.

Второй подход был переводом кода на принципы ООП. За основу был взят определенный класс TMenu, от которого наследовались непосредственно элементы меню.

Вкратце. Указателю CurrentMenu присваивается адрес текущего элемента меню Основные элементы класса это бит ItemIsValue, который определяет является ли текущий элемент подменю или изменяемым значением и функции OnKey(), Increment(), Decrement() и Print(). Также класс меню содержит указатель на родительское меню и массив указателей. В общем использование наследования позволяло сделать произвольное многоуровневое меню, в принципе можно сказать, что это динамическое меню, только в в данной реализации оно формируется один раз при инициализации. Во всяком случае код легко редактируется, добавляются элементы меню. Жестокая реальность поставила меня на место. В UNO R3 на всю эту роскошь не хватает памяти.

Третий подход — урезка второго. Главное отличие одно — конкретный объект класса меню содержит либо вложенные меню, либо переменные — редактируемые значения, тип которых задан классом.

Итак, Определен класс:
class TMenu
{
  public:
    byte _ItemsCount;
    TMenu *Parent;
    String *MenuName;
    boolean ItemIsValue;
    byte CurrentItem;

    TMenu **Items;
    String *ItemsName;
    byte ItemsCount(void) ;
    bool AddItem(TMenu *NewItem);

    virtual void Print(void);
    void OnKey(byte KeyNum);
    void ChangeItem(byte value);

    virtual void Increment(void);
    virtual void Decrement(void);

    virtual void OnSet(void);
    DateTime CheckDateTime(DateTime OldDate, int Increment, byte DatePart);
};


Класс содержит:
— число элементов меню (подменю или переменная), указатель на родительское меню (если указатель равен 0, то достигнут верх);
MenuName имя меню;
ItemIsValue описан выше
— номер позиции курсора в меню (CurrentItem);
— указатель на массив указателей Items. Адреса подменю. Если меню содержит редактируемые элементы, это значение равно 0;
— функция Print() вызывается из цикла loop от имени текущего меню «CurrentMenu->Print();» таким образом отрисовывается экран с нужным текстом.
— функция OnKey(byte KeyNum) также вызывается из цикла loop в блоке подавления дребезга контактов, он же декодер клавиатуры от монитора.
— функции ChangeItem(byte value), virtual void Increment(void), virtual void Decrement(void) вызываются из OnKey() и обрабатывают кнопки "+" и "-". ChangeItem() — это переборка элементов меню, Increment() и Decrement() — полиморфные, переборка значений текущей переменной.
— функция CheckDateTime(DateTime OldDate, int Increment, byte DatePart) проверяет введенную дату и время. Распознается вискозный год и количество дней в месяце 28/29, 30, 31. Исходя из логики в функцию передается текущая дата, +1 или -1 и индекс части даты/времени (0 — год, 5 — секунды)

Навигация по меню реализована присвоением адреса объекта указателю CurrentMenu:
— CurrentMenu = CurrentMenu->Items[CurrentMenu->CurrentItem]; вход в выбранное меню
— CurrentMenu = CurrentMenu->Parent; переход в предыдущее меню

Логика работы:

Цикл loop непрерывно опрашивает клавиатуру, проверяет настройки реле и мигает светодиодом.

Клавиатура опрашивается в качестве рудимента и по цифровым входам 2-6 (menu,-,+,set), к этим кодам пересчитываются значения аналоговых портов.
— при нажатии на кнопку «menu» вне меню происходит вызов меню, в противном случае переход на меню вверх;
— при нажатии "+" или "-" происходит циклическая переборка элементов меню или циклическое изменение текущего параметра. При нажатии кнопки "'set" вход в выбранное меню либо сохранение значения переменной во флеш с одновременным выбором следующего значения.

Дребезг подавляется программно, каждой кнопке присваивается счетчик нажатия и отпускания, который увеличивается в случае нажатия или отпускания. Опрос проводится 3 раза с интервалом 15 мс. Счетчик нажатия или отпускания увеличивается на 1 либо сбрасывается. Таким образом распознается дребезг как нажатия так и отпускания. Состояние отпускания фиксируется для однократного срабатывания при удержании кнопки.

В настройках реле проверяется режим работы, в режиме «Daily» вводится и проверяется только время, с точностью до минут. Правильно распознается время включения больше времени выключения, например, включение в 23 и выключение в 7. В режиме «Оnce» задается дата и время. Для удобства настройки планирую подключить пятую кнопку и задать на нее функцию установки текущей даты и времени в режиме редактирования.

Это вкратце. Небольшие функции классов объявлены, как правило при объявлении класса, заголовочные файлы и библиотеки не используются. Код и так небольшой.

Код программы
#include <EEPROM.h>

#include <DHT.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#define LEFT 0
#define CENTER 1
#define RIGHT 2

#define RelayModesCount 4
#define KeyFirst 2
#define KeyLast 6

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
RTC_DS1307 RTC; // RTC Modul
DHT dht(7, DHT21); // pin, type
volatile boolean Blinker = true;
volatile long BlinkerTime;
volatile byte ButtonPress[8];
const String RelayModeNames[] = {«OFF», «ON», «Once», «Daily»};

int aKey1 = 0;
int aKey2 = 0;

DateTime NowDate;

boolean DoBlink(void)
{
boolean Result = false;
long NBlinkerTime = millis();
if (Blinker)
{
if (NBlinkerTime — BlinkerTime > 200)
{
digitalWrite(8, HIGH);
BlinkerTime = NBlinkerTime;
Blinker = false;
Result = true;
}
}
else
{
if (NBlinkerTime — BlinkerTime > 300 )
{
digitalWrite(8, LOW);
BlinkerTime = NBlinkerTime;
Blinker = true;
}

}
return Result;
}
String BlinkString(String string, byte Cur, byte ItemsCount)
{
String result = string;
byte len = string.length();
if (!Blinker && Cur == ItemsCount)
{
for (byte i = 0; i < len; i++) result.setCharAt(i, ' ');
}
return result;
}

/********************************************************************************************************/
/********************************** Объявление классов *********************************************/
/********************************************************************************************************/

class TMenu
{
public:
byte _ItemsCount;
TMenu *Parent;
String *MenuName;
boolean ItemIsValue;
byte CurrentItem;

TMenu **Items;
String *ItemsName;
// byte ItemsCount(void);
byte ItemsCount(void) {
return _ItemsCount;
};
bool AddItem(TMenu *NewItem);

virtual void Print(void);
void OnKey(byte KeyNum);
void ChangeItem(byte value);

virtual void Increment(void);
virtual void Decrement(void);

virtual void OnSet(void);
DateTime CheckDateTime(DateTime OldDate, int Increment, byte DatePart);
};

class TNoMenu: public TMenu
{
public:
void Print(void);
TNoMenu(TMenu *ParentMenu) {
MenuName = 0;
CurrentItem = 0;
_ItemsCount = 0;
Parent = ParentMenu;
Items = 0;
ItemsName = 0;
ItemIsValue = false;
};
void Increment(void) {};
void Decrement(void) {};
void OnSet(void) {};

};

class TSelectMenu: public TMenu
{
public:
void Print(void);
TSelectMenu(TMenu *ParentMenu, String NewName) {
MenuName = new String(NewName);
CurrentItem = 0;
_ItemsCount = 0;
Parent = ParentMenu;
Items = 0;
ItemsName = 0;
ItemIsValue = false;
};
void Increment(void) {};
void Decrement(void) {};
void OnSet(void) {};
};

class TTimeMenu: public TMenu
{
public:
void Print(void);
DateTime *SetDateTime;
long OldDateTime;
TTimeMenu(TMenu *ParentMenu, String NewName, DateTime *ParamDate) {
MenuName = new String(NewName);
CurrentItem = 0; _ItemsCount = 6; Parent = ParentMenu; Items = 0; ItemsName = 0;
ItemIsValue = true; OldDateTime = millis();
SetDateTime = ParamDate;
};
void Increment(void) {
*SetDateTime = CheckDateTime (*SetDateTime, 1, CurrentItem);
};
void Decrement(void) {
*SetDateTime = CheckDateTime (*SetDateTime, -1, CurrentItem);
};
void OnSet(void) {
RTC.adjust(*SetDateTime);
};
void SecondTimer(void) {
long TmpDateTime = millis(); if (TmpDateTime — OldDateTime > 1000) {
OldDateTime = TmpDateTime;
*SetDateTime = *SetDateTime + 1;
};
};
};
class TRelayMenu: public TMenu
{
public:
byte RelayNumber;
byte RelayMode;
// byte Shedule=0;
boolean OnceBit;
DateTime RelayOn;
DateTime RelayOff;
TRelayMenu(TMenu *ParentMenu, byte NewNumber, String NewName) {
MenuName = new String(NewName);
CurrentItem = 0; _ItemsCount = 11; Parent = ParentMenu; Items = 0; ItemsName = 0; ItemIsValue = true, OnceBit = false;
RelayNumber = NewNumber;
RelayMode = 0;
RelayOn = DateTime(2015, 1, 1, 23, 00, 00);
RelayOff = DateTime(2015, 1, 1, 07, 00, 00);
};
void Print(void);
void Increment(void) {
if (!CurrentItem) {
RelayMode++;
if ( RelayMode >= RelayModesCount) RelayMode = 0;
}
else if (CurrentItem < 6) RelayOn = CheckDateTime (RelayOn, 1, CurrentItem — 1);
else RelayOff = CheckDateTime (RelayOff, 1, CurrentItem — 6);
};
void Decrement(void) {
if (!CurrentItem) {
RelayMode--;
if ( RelayMode > 127) RelayMode = RelayModesCount — 1;
}
else if (CurrentItem < 6) RelayOn = CheckDateTime (RelayOn, -1, CurrentItem — 1);
else RelayOff = CheckDateTime (RelayOff, -1, CurrentItem — 6);
};

boolean CheckDaily(void);

void OnSet(void) {
///// здесь надо записать реле в память

byte p_address = RelayNumber * 16;
EEPROM.write(p_address, RelayMode);

EEPROM.write(p_address + 1, byte(RelayOn.year() — 2000));
EEPROM.write(p_address + 2, byte(RelayOn.month() ));
EEPROM.write(p_address + 3, byte(RelayOn.day() ));
EEPROM.write(p_address + 4, byte(RelayOn.hour() ));
EEPROM.write(p_address + 5, byte(RelayOn.minute() ));

EEPROM.write(p_address + 6, byte(RelayOff.year() — 2000));
EEPROM.write(p_address + 7, byte(RelayOff.month() ));
EEPROM.write(p_address + 8, byte(RelayOff.day() ));
EEPROM.write(p_address + 9, byte(RelayOff.hour() ));
EEPROM.write(p_address + 10, byte(RelayOff.minute() ));
};
};

/********************************************************************************************************/
/******************************** Конец объявления классов ******************************************/
/********************************************************************************************************/

TMenu *CurrentMenu = 0;
TNoMenu *NoMenu = 0;
TSelectMenu *SelectMenu;
TTimeMenu *TimeMenu;

TRelayMenu *RelayMenu[4];

/********************************************************************************************************************************************/
/********************************************************************************************************************************************/
/********************************************************************************************************************************************/
void setup()
{
NoMenu = new TNoMenu(0);
SelectMenu = new TSelectMenu (NoMenu, «NoMenu»);
TimeMenu = new TTimeMenu(SelectMenu, «Time Setup», &NowDate);

SelectMenu->AddItem(TimeMenu);

byte p_address;
DateTime DTFlesh;
for (int i = 0; i < 4; i++)
{
// здесь надо добавить загрузку параметров из флеша
RelayMenu[i] = new TRelayMenu (SelectMenu, i, «Relay » + String(i + 1));
SelectMenu->AddItem(RelayMenu[i]);

p_address = i * 16;

RelayMenu[i]->RelayMode = EEPROM.read(p_address);

DTFlesh = DateTime(int(EEPROM.read(p_address + 1) + 2000), EEPROM.read(p_address + 2), EEPROM.read(p_address + 3), EEPROM.read(p_address + 4), EEPROM.read(p_address + 5), 0 );
RelayMenu[i]->RelayOn = RelayMenu[i]->CheckDateTime(DTFlesh, 0, 0);

DTFlesh = DateTime(int(EEPROM.read(p_address + 6) + 2000), EEPROM.read(p_address + 7), EEPROM.read(p_address + 8), EEPROM.read(p_address + 9), EEPROM.read(p_address + 10), 0 );
RelayMenu[i]->RelayOff = RelayMenu[i]->CheckDateTime(DTFlesh, 0, 0);
}

for (byte i = KeyFirst; i < KeyLast; i++)
{
pinMode(i, INPUT); //Keypad 2-«menu» 3-"-" 4-"+" 5-«SET»
digitalWrite(i, HIGH); //setup Resistor input2Vcc
ButtonPress[i] = true;
}
pinMode(8, OUTPUT); //LED
pinMode(9, OUTPUT);
for (byte i = 10; i < 14; i++)
{
pinMode(i, OUTPUT); // relay i
digitalWrite(i, HIGH);
}

pinMode (A2, INPUT_PULLUP);
pinMode (A3, INPUT_PULLUP);

Serial.begin(9600); // Used to type in characters
digitalWrite(8, LOW);
digitalWrite(9, HIGH);

lcd.begin(20, 4); // initialize the lcd for 20 chars 4 lines and turn on backlight
RTC.begin();

lcd.noBacklight();
delay(150);
lcd.backlight();

NowDate = RTC.now();
//проверка времени
if ( NowDate.year() > 2000 && NowDate.year() < 2114 &&
NowDate.month() > 0 && NowDate.month() < 13 &&
NowDate.day() > 0 && NowDate.day() < 32 &&
NowDate.hour() >= 0 && NowDate.hour() < 24 &&
NowDate.minute() >= 0 && NowDate.minute() < 60 &&
NowDate.second() >= 0 && NowDate.second() < 60 )
{
CurrentMenu = NoMenu;
}
else
{
lcd.setCursor(2, 1);
lcd.print(«Clock Failure!»);
delay(700);
RTC.adjust(DateTime(2015, 1, 1, 00, 00, 00));
CurrentMenu = TimeMenu;
}

}

void loop()
{
/********* KEYPAD BUNCLE, 5 keys, from 2 to 6 *********/
byte NButtonPress[8] = {0, 0, 0, 0, 0, 0, 0, 0};
byte NButtonRelease[8] = {0, 0, 0, 0, 0, 0, 0, 0};
const byte ButtonTry = 3;

aKey1 = analogRead (2);
aKey2 = analogRead (3);
byte aKeyNum=0;

/**************** check for key pressed or released ****************/
for (byte i = 0; i < 3; i++)
{
delay(15);
if (aKey1< 64) aKeyNum=2;//AnalogKey 1 = Dig2
else if (aKey1<128) aKeyNum=6;//Analog key 3 = D4
else if (aKey1<256) aKeyNum=4;//key 5=d6
else if (aKey2< 64) aKeyNum=1;//key 6 = menu
else if (aKey2<128) aKeyNum=3;//analogkey 2 = D3
else if (aKey2<256) aKeyNum=5;//key 4 =d5
else aKeyNum=0; // no key

for (byte j = KeyFirst; j < KeyLast; j++) // Read ports 2...6
{
if (digitalRead(j) == LOW || aKeyNum==j)
{
NButtonPress[j]++;
NButtonRelease[j] = 0;
}
else
{
NButtonPress[j] = 0;
NButtonRelease[j]++;
delay(5);
}
}

}
/*************** Do key process ******************/
// byte m;

for (byte j = KeyFirst; j < KeyLast; j++)
{
if (NButtonPress[j] >= ButtonTry && ButtonPress[j] == false)
{
ButtonPress[j] = true;
CurrentMenu->OnKey(j);
}
else
{
if (NButtonRelease[j] >= ButtonTry && ButtonPress[j] == true)
{
ButtonPress[j] = false;
}
}
}
/***************** Relay Check *********************/

CurrentMenu->Print();
DoBlink();
}

void LcdPrint(byte string, String str, byte Align)
{
byte StrTrim1;
byte StrTrim2;
lcd.setCursor(0, string); //Start at character 0 on line 0
switch (Align)
{
case RIGHT:

break;

case CENTER:
StrTrim1 = byte((20 — str.length()) / 2);
StrTrim2 = 20 — str.length() — StrTrim1;
for (byte k = 0; k < StrTrim1; k++) lcd.print(" ");
lcd.print(str);
for (byte k = 0; k < StrTrim2; k++) lcd.print(" ");
break;

default:
lcd.print(str);
StrTrim1 = 20 — str.length();
for (byte k = 0; k < StrTrim1; k++) lcd.print(" ");
}
}

void TNoMenu::Print(void)
{
NowDate = RTC.now();
String Ddate;
Ddate = " R1-" + RelayModeNames[RelayMenu[0]->RelayMode] + " R2-" + RelayModeNames[RelayMenu[1]->RelayMode];
LcdPrint(0, Ddate, CENTER);
Ddate = " R3-" + RelayModeNames[RelayMenu[2]->RelayMode] + " R4-" + RelayModeNames[RelayMenu[3]->RelayMode];
LcdPrint(1, Ddate, CENTER);
Ddate = String (NowDate.year()) + "/" + String(NowDate.month()) + "/" + String(NowDate.day()) + " " + String (NowDate.hour()) + ":" + String(NowDate.minute()) + ":" + String(NowDate.second());
LcdPrint(2, Ddate, CENTER);
Ddate = «Temp » + String (int(dht.readTemperature())) + «C, Hum » + String(int(dht.readHumidity())) + "%";
LcdPrint(3, Ddate, CENTER);

RelayCheck();
}

void TTimeMenu::Print(void)
{
SecondTimer();
String Ddate = BlinkString(String((*SetDateTime).year()), CurrentItem, 0) + "/" +
BlinkString(String( (*SetDateTime).month()), CurrentItem, 1) + "/" +
BlinkString(String((*SetDateTime).day()), CurrentItem, 2) + " ";
LcdPrint(1, Ddate, CENTER);
Ddate = BlinkString(String ((*SetDateTime).hour()), CurrentItem, 3) + ":" +
BlinkString(String((*SetDateTime).minute()), CurrentItem, 4) + ":" +
BlinkString(String((*SetDateTime).second()), CurrentItem, 5);
LcdPrint(2, Ddate, CENTER);

LcdPrint(3, " ", CENTER);
RelayCheck();
}

void TMenu::OnKey(byte KeyNum)
{
switch (KeyNum)
{
case 3: // — if (ItemIsValue) Decrement();
else ChangeItem(-1);
break;
case 4: // +
if (ItemIsValue) Increment();
else ChangeItem(1);
break;
case 5: // SET
if (ItemIsValue)
{
OnSet();
ChangeItem(+1);
}
else // вход в подменю
{
if (Items && ItemsCount())
{
if (CurrentMenu->ItemsCount())
{
CurrentMenu = CurrentMenu->Items[CurrentMenu->CurrentItem];
CurrentMenu->CurrentItem = 0;
}
}
}
break;
default: // 2 -menu
if (Parent) CurrentMenu = CurrentMenu->Parent; //(TMenu *) &NoMenu;
else
{
CurrentMenu = SelectMenu;
CurrentMenu->CurrentItem = 0;
}
}
}

void TMenu::ChangeItem(byte value)
{
CurrentItem += value;
if (CurrentItem > 128) CurrentItem = ItemsCount() — 1;
else if (CurrentItem > ItemsCount() — 1) CurrentItem = 0;
}

boolean TMenu::AddItem(TMenu *NewItem)
{
if (!Items) Items = new TMenu *[_ItemsCount = 1];
else Items = (TMenu **)realloc((void *)Items, (_ItemsCount = _ItemsCount + 1) * sizeof(void *));
Items[_ItemsCount — 1] = NewItem;
}

DateTime TMenu::CheckDateTime(DateTime OldDate, int Increment, byte DatePart)
{
int DTmin[6] = {2000, 1, 1, 0, 0, 0};
int DTmax[6] = {2199, 12, 31, 23, 59, 59};

int DT[6];
int diff;

DT[0] = OldDate.year();
DT[1] = OldDate.month();
DT[2] = OldDate.day();
DT[3] = OldDate.hour();
DT[4] = OldDate.minute();
DT[5] = OldDate.second();
DT[DatePart] = DT[DatePart] + Increment;

if (DT[1] == 1 || DT[1] == 3 || DT[1] == 5 || DT[1] == 7 || DT[1] == 8 || DT[1] == 10 || DT[1] == 12) DTmax[2] = 31;
else if (DT[1] == 2)
{
if ((DT[0] % 4 == 0 && DT[0] % 100 != 0) || (DT[0] % 400 == 0)) DTmax[2] = 29;
else DTmax[2] = 28;
}
else DTmax[2] = 30;

for (byte i = 0; i < 6; i++)
{
if (DT[i] > DTmax[i]) DT[i] = DTmin[i];
else if (DT[i] < DTmin[i]) DT[i] = DTmax[i];
}

return DateTime(DT[0], DT[1], DT[2], DT[3], DT[4], DT[5]);

}

void TSelectMenu::Print(void)
{
NowDate = RTC.now();
byte shift = 0;
if (CurrentItem > 3) shift = CurrentItem — 3;
for (byte i = 0; i < 4; i++)
{
if ((CurrentItem — shift) == i) //&&Blinker)
{
LcdPrint(i, ">> " + * (Items[i + shift]->MenuName) + " <<", CENTER);
}
else LcdPrint(i, *(Items[i + shift]->MenuName), CENTER);
}
RelayCheck();
}

void TRelayMenu::Print(void)
{

String DData;
NowDate = RTC.now();
LcdPrint(0, (*MenuName) + "[" + BlinkString(RelayModeNames[RelayMode], CurrentItem, 0) + "]", CENTER);
DData = «On:»;
switch (RelayMode)
{
case 3: //Daily
// DData = DData + " ";
if (CurrentItem > 0 && CurrentItem < 4) CurrentItem = 4;
break;
default:
DData = DData + BlinkString(String(RelayOn.year(), DEC), CurrentItem, 1) + "/" + BlinkString(String( RelayOn.month(), DEC), CurrentItem, 2) +
"/" + BlinkString(String( RelayOn.day(), DEC), CurrentItem, 3);
}
DData = DData + " " + BlinkString(String (RelayOn.hour(), DEC), CurrentItem, 4) + ":" + BlinkString(String(RelayOn.minute(), DEC), CurrentItem, 5);
LcdPrint(1, DData, CENTER);
DData = «Off:»;
switch (RelayMode)
{
case 3: //Daily
// DData = DData + " ";
if (CurrentItem > 5 && CurrentItem < 9) CurrentItem = 9;
break;
default:
DData = DData + BlinkString(String(RelayOff.year(), DEC), CurrentItem, 6) + "/" + BlinkString(String( RelayOff.month(), DEC), CurrentItem, 7) +
"/" + BlinkString(String( RelayOff.day(), DEC), CurrentItem, 8);
}
DData = DData + " " + BlinkString(String (RelayOff.hour(), DEC), CurrentItem, 9) + ":" + BlinkString(String(RelayOff.minute(), DEC), CurrentItem, 10);
LcdPrint(2, DData, CENTER);
LcdPrint(3, " ", CENTER);
}

boolean TRelayMenu::CheckDaily(void)
{
int TimeOn = 60 * int(RelayOn.hour()) + int(RelayOn.minute());
int TimeOff = 60 * int(RelayOff.hour()) + int(RelayOff.minute());
int NowTime = 60 * int(NowDate.hour()) + int(NowDate.minute());
boolean result; // true = время включения больше времени выключения
if ( TimeOn > TimeOff )
{
if (NowTime <= TimeOff || NowTime >= TimeOn ) result = true;
else result = false;
}
else
{
if (NowTime <= TimeOff && NowTime >= TimeOn ) result = true;
else result = false;
};
return result;

}

void RelayCheck (void)
{
boolean OnceBitCheck;
for (byte i = 0; i < 4; i++)
{
switch (RelayMenu[i]->RelayMode)
{
case 1: //relay 0n
digitalWrite(i + 10, LOW);

break;
case 2: //Once;
OnceBitCheck = (NowDate.unixtime() > RelayMenu[i]->RelayOn.unixtime() && NowDate.unixtime()<RelayMenu[i]->RelayOff.unixtime());

if (OnceBitCheck) RelayMenu[i]->OnceBit = true;
else if (RelayMenu[i]->OnceBit)
{
RelayMenu[i]->RelayMode = 0;
byte p_address = RelayMenu[i]->RelayNumber * 16;
EEPROM.write(p_address, RelayMenu[i]->RelayMode);
}
digitalWrite(i + 10, !OnceBitCheck);
break;
case 3: //Daily
digitalWrite(i + 10, !(RelayMenu[i]->CheckDaily()));
break;
default: //relay 0ff
digitalWrite(i + 10, HIGH);
}
}
}


Вот такая получилась поделка, избавившая меня от необходимости вставать в 7 утра и ложиться в 23, при этом не забывая щелкать тумблерами. На промышленные стандарты не претендую, инкапсуляцию данных в коде делать не стал, глобальные переменные также оставил.

Были опасения в точности хода часов, но пока существенного отклонения не заметил.

Благодарю за внимание.

Комментарии (71)


  1. neochapay
    23.07.2015 08:32
    +2

    Вот если коробочку не открывать то прям промышленный образец. Редко такие увидишь с ардуино…


    1. TomashUA
      23.07.2015 09:03

      Так то обычный электрический щиток с прозрачной дверцей.


      1. neochapay
        23.07.2015 09:25
        +2

        Ну у большинства всё остаётся в лапше торчащей из стены :)


    1. Aclz
      24.07.2015 00:44

      Это турецкий щиток Makel. Весьма стрёмный причем, т.к. для автоматов неудобный, а для колхозинга на Ардуине неоправдано глубокий, как и все щитки с DIN-рейкой внутри.


  1. viacheslav77 Автор
    23.07.2015 10:02

    можно сделать красивее, но задача была быстро сделать максимально дешевый колхоз. В большинстве случаев логика работы забита в код.


    1. evtomax
      23.07.2015 11:34

      Разве это колхоз? Тут даже платы с монтажными стойками! :-)


      1. Alexeyslav
        23.07.2015 15:50

        Колхоз, однако, разным бывает…


  1. telnov
    23.07.2015 10:29

    В доме установлен двухтарифный счетчик, поэтому бойлер нагревает воду с 23 до 7 утра. Аналогично отопление: два из трех тэнов, по моей задумке будут включаться ночью.

    Вы отапливаете дом электричеством??? Да Вы батенька богатей! Последний раз (лет 7-8 назад), когда я видел дом, который топят электричеством — это была загородная дача какого то футболиста Российской сборной у которого куры денег не клюют. Счета там были космические. Ну или вы электричество воруете. У вас нормальная дровяная печь то там есть? В случае если газа в доме нет и невозможно провести, обычно применяют или автоматический твердотопливный котел. Котельную во дворе устраивают. В бункер закидывается топливо на 2-7 дней (в зависимости от температуры воздуха). Или делается скважина и для отопления и горячей воды применяется тепловой насос. Однажды беседовал с обладателями такого девайса. Подтапливаться дровами приходится только в лютые морозы (-25 и ниже), в остальное время он справлялся. Но в том случае, я думаю что, проблема скорее всего была в недостаточной мощности устройства или в его неправильном монтаже.


    1. Alexeyslav
      23.07.2015 11:15

      Все это актуально для отдельного дома, в квартире многое из этих вещей не применишь а центральное отопление не всегда вовремя работает.


      1. telnov
        23.07.2015 11:30

        Для квартиры тоже есть несколько решений. Ставится кондиционер с функцией отопление и в межсезонье(пока нет отопления) он работает как тепловой насос. Ну а от электрического бойлера никуда в квартире не убежишь. Разве что только газовый котел можно поставить.


        1. Yurich
          23.07.2015 12:18

          Газовый водогрей в квартире, где он не предусмотрен, ставить тупо запрещено.


    1. Yurich
      23.07.2015 12:17
      +1

      Скажите, пожалуйста, а Вы графики выхода на точку равнозатратности рисовать не пытались?
      Я вот это делал. Электричество далеко не самый плохой вариант, ТН сильно хуже (если, конечно, его не из списанных деталей от кондеев делать).
      А рекомендуемый Вами пеллетник в моем юскейсе начинает быть дешевле солярочного только на девятый год эксплуатации, но тут вот ведь какая ерунда: пеллетники служат где-то лет 15, после чего их нужно менять (горелки и шнековые приводы так и еще чаще), Дизельные котлы тоже столько же служат, но экономия на смене котлов и расходки получается весьма существенной. И это я еще не моделировал ситуацию, когда планируемые к затратам на пеллетник деньги (точнее их дельта между ТТ и ДТ) кладется в банк и проценты учитываются в смете общих расходов.

      Что же до пеллет, то кВтч электричества в ночном тарифе стоит дешевле кВтч пеллетного, поэтому экономные граждане на ночь глушат ТТ котел и запускают электрический.

      Да, и про котельную в отдельном строении: Вы, я уверен, считали расходы на возведение строения, регистрацию его в БТИ, налоги со строения и теплопотери в подземной теплотрассе в смете общих расходов на хотя бы 10-летнем отрезке. А, да, ну и, разумеется, не забыть бы еще обустройство прохода теплотрассы в основное здание из-под земли. Эта задача, прямо скажем, не из дешевых, особенно, если нет цокольного этажа.


      1. telnov
        24.07.2015 11:26

        Я не пытался. Я не занимаюсь отоплением. Я рассказал только о том, что видел сам. Тепловой насос лучше, если только его устанавливали не Равшан и Джамшут из соседней стройки. Люди с которыми я разговаривал сначала тоже себе все электричеством сделали, но т.к. они жили в доме постоянно, то зимой счета за электричество доходили до 80-90 т.руб. в месяц(это 6 лет назад). Извините но невозможно зимой при температуре -12 — 25 не топить днем дом. После установки теплового насоса они стали платить максимально по 15-20 т.р. в зимние месяцы. По итогам имеем 2-3 года срок окупаемости по сравнению с электроотоплением. Вы мне про расчеты, а я вам про практику. Я к этим людям вообще по другому вопросу приезжал, просто увидел девайс и спросил. По поводу остальных вопросов. Я хочу сказать следующее. Если дом возможно отопить 15 кВт электрической мощности, то в принципе все ОК. Можно не заморачиваться и топить (хотя суммы счетов будут космическими). Но чаще всего если задействуется электроотопление, то мощность ввода приходится увеличивать. В Подмосковье, например, легко и почти бесплатно получается стандартное подключение 15 кВт на участок. Однако мощность свыше этого лимита стоит таких денег, которых хватит Вам и на постройку котельной, и на котел, и на топливо для этого котла на ближайшие 2-3 года.


  1. Kitsok
    23.07.2015 11:36

    Скажите пожалуйста, что будет, если реле, которым Вы включаете насос, залипнет?


    1. Yurich
      23.07.2015 12:20

      не знаком с этой релейной сборкой, но разве там электромагнитные реле?


      1. Kitsok
        23.07.2015 12:35

        Да это и не важно. Что будет с водопроводом, если реле залипнет в замкнутом состоянии?


        1. Yurich
          23.07.2015 12:36

          Да ничего не будет. Реле давления отключит насос, вот и всё.


          1. Kitsok
            23.07.2015 12:39

            А где Вы увидели реле давления в схеме? И зачем оно нужно, если есть ардуино? И зачем нужно ардуино, если есть реле давления?


            1. Yurich
              23.07.2015 12:44

              Верну Вам Ваш вопрос: где Вы увидели обработку датчика давления ардуиной?
              Ардуина может оказаться полезной, если есть желание глушить насос по датчику протечки, например.


              1. Kitsok
                23.07.2015 12:52
                +1

                Я не увидел вообще никакой безопасности в этом проекте. Совсем и полностью. И, наблюдая за похожими проектами, я крайне редко вижу элементы «правильного» подхода к автоматизации, когда надо закладываться на отказ любого элемента системы.
                «Умный дом в массы» — очень правильный тренд, но забывание о надежности и отказоустойчивости (в классическом понимании) обернется минимум — финансовыми потерями, максимум — трагедиями.


                1. eta4ever
                  23.07.2015 13:20
                  +1

                  … ни одного предохранителя на весь умный дом :)


                1. Yurich
                  23.07.2015 13:34

                  Реле давления к безопасности отношение конечно имеет, но не самое прямое. Оно ставится всегда — просто без него водопровод в автономной системе водоснабщения работать не будет.
                  Кстати вот думаю, не замутить ли обзор реле давления для насосов — я уже четыре разных типа у себя затестдрайвил, но там ни ардуины ни кода — ни фига нет…


                  1. Kitsok
                    23.07.2015 13:43

                    У моих соседей на чердаке стоит 2 кубовые емкости и выключатель по уровню, прекрасно обходятся без реле давления.
                    Обзор РД — хорошая идея. Я тоже оттестил, в общей сложности, одно безымянное РД и два «контроллера». Причем один из них натурально самозатопился (потекла прокладка мембранной полости), и продолжал при этом функционировать.


                    1. Yurich
                      23.07.2015 14:23

                      Я бы не рискнул ставить на чердак две тонны воды.
                      Обрушившееся перекрытие с последующим разливом двух тонн воды в помещении это всегда очень дорого.
                      Особенно зимой.


                      1. Kitsok
                        24.07.2015 11:49

                        Очень странный аргумент. 2 тонны воды Вас на чердаке смущает, а пять тонн кровли — нет?


                        1. Yurich
                          24.07.2015 12:10

                          Не смущает, разумеется. Снег на кровле это распределенная нагрузка, бак с водой распределен куда меньше.
                          Кроме того, межэтажные перекрытия редко строят с учетом такой нагрузки, как две тонны воды локально (не, если у Ваших знакомых монолит, тогда все ок).


      1. eta4ever
        23.07.2015 13:18
        +1

        Да, там простейшие электромагнитные реле, которые могут залипнуть.


        1. Yurich
          23.07.2015 13:29

          я б не рискнул такое в ответственные цепи ставить


          1. eta4ever
            23.07.2015 13:32

            Да можно ставить, только какую-то обратную связь надо сделать, с отрубанием всего нафиг в случае залипания.


            1. Yurich
              23.07.2015 13:35
              +1

              А отрубать будете еще одним реле? :)
              Твердотельное реле рулит, у него залипания вообще не предусмотрено конструкцией. И обойдется дешевле, чем контроль залипания ЭМ-реле.


              1. eta4ever
                23.07.2015 13:38

                Ну, контроль — штука вообще хорошая. Поставить датчик тока, и заодно будем знать потребление нагрузки.
                А отрубать — да, еще одним реле. Или, скорее, пускателем на общем входе всего этого дела. Но в целом — да, симисторы тут будут предпочтительнее.


                1. Yurich
                  23.07.2015 13:43
                  +1

                  Я бы задавался вопросом «а что, если?» при проектировании системы безопасности.
                  Предположим, релюха включает свет в туалете. Залипла — ну и фиг с ней, не критично. Увижу, что свет горит, когда не должен, разберусь — небо на землю не упадет.
                  А вот поверхностный насос, принудительно запитанный в отсутствие тока воды, может доставить неприятностей, ему так работать неполезно. Довольно скоро он закипятит в себе воду и начнет разрушаться, ибо кавитация, ибо прокладка корпуса и сальник не предназначены для кипятка и т.д.


                  1. Kitsok
                    23.07.2015 13:47

                    Эх, не смогу показать, потерял ссылку на форуме, я там разбирал убийственный глубинный насос «российского» производителя. Там тоже не очень думали о безопасности.
                    В итоге, из-за неправильно выбранного материала нижнего подшипника вала, на нем образовалась каверна. Каверна забилась — насос заклинило, заклинило насос — пусковой ток перегрел и взорвал пусковой конденсатор в корпусе насоса. Во время взрыва и расплавления, фазный провод прилёг на корпус насоса.
                    Когда я вынул это чудо на поверхность и стал разбираться, то из трусости решил УЗО поставить прямо рядом, на случай «если что». Не поставил — не писал бы сейчас тут.


                    1. Yurich
                      23.07.2015 13:58

                      Аналогичная история приключилась, когда я крутил потроха китайскому поверхностному насосу. Не удивлен, если честно.
                      Сосед решил, что он самый умный в мире Карлсон, купил что-то экстремально дешевое, типа насосная станция в сборе за 2500 рублей. Все как положено, ГА с пластиковым фланцем, насос с пластиковым корпусом, дребезжащая автоматика и т.п.
                      Ну и прекратила она у него работать почти сразу: подшипники там были из смеси соломы с пластилином, ротор был почти заклинен, он меня позвал посмотреть, и тут, видимо ротор заклинило совсем. Ну а через это дело точно так же подорвался конденсатор, а дальше КЗ, оплавление проводов — вместо автомата у дедушки был нормальный жучок. Тут я уж не стал дожидаться финала и обесточил это все нахрен, мой дом рядом стоит, его жалко.


                1. Alexeyslav
                  23.07.2015 16:09
                  +2

                  Симисторы имеют плохую особенность, они в любой момент могут самопроизвольно открыться, например от помехи по питанию.
                  Неоднократно сталкивался с тем что симистор открывается при внезапном КЗ в нагрузке, когда он должен быть закрыт.
                  Во время грозы, если не предпринять специальных мер они будут периодически открываться на пол периода а то и на целый.


                  1. eta4ever
                    23.07.2015 16:11

                    Да, об этом не стоит забывать. Т.е., тоже не панацея.


              1. Kitsok
                23.07.2015 13:38
                +1

                Да, именно так. Причем на отдельное, аварийное реле, должен быть свой контроллер, считывающий непосредственно информацию с датчиков (возможно, не со всех), и самостоятельно принимающий решение о том, что «все пропало».
                Твердотельное реле, конечно, не залипает. Зато оно может пробиться.
                Не бывает безотказных деталей.


                1. Yurich
                  23.07.2015 13:44

                  А как Вы планируете контролировать исправность аварийного реле?
                  Мне действительно это интересно.


                  1. eta4ever
                    23.07.2015 13:48

                    Мне пока в голову приходит только периодическая «учебная тревога».


                    1. Yurich
                      23.07.2015 13:49

                      Вы же, наверное, сами понимаете, что Вам это надоест за полгода максимум.
                      Вот скажите, как часто Вы нажимаете кнопку «Тест» на УЗО у себя дома? Раз в полгода, как того требует любая инструкция?


                      1. Kitsok
                        23.07.2015 13:50

                        Я раз в год жму. И, что удивительно, они-таки да, выходят из строя. За 14 лет эксплуатации половина УЗО поменяно.


                        1. Yurich
                          23.07.2015 14:04

                          Я жму два раза в год, кроме того, купил тестер для УЗО, кроме самотеста еще и им гоняю, чтоб знать, на каком токе утечки он срубается.
                          Штуковина выглядит вот так: image
                          Покупал тут: http://www.castorama.fr/store/Testeur-VT35-Terre-et-diff-Multimetrix-prod4620001.html
                          Одно УЗО из трех установленных таки выбраковал.


                          1. Aclz
                            24.07.2015 01:05
                            +1

                            Хм…

                            Вилка разборная Евро — 30 р.
                            Цементный резистор 6.8 кОм 10 Вт — ну ещё 5 р.

                            Соединяем резистором L- и PE-контакты внутри вилки и получаем тот же девайс за 35 рублей.

                            Имеем в щитке ещё и 10 мА УЗО? Ну тогда выкладываем ещё 35 рублей)


                            1. eta4ever
                              24.07.2015 01:39

                              Тогда уж взять проволочный переменник (ППБ) и отградуировать.


                            1. Yurich
                              24.07.2015 08:28

                              Ок, ок, ок. Вы себе сделали такую штуку? Я так понимаю, что вряд ли.
                              А у меня вот есть и я ей пользуюсь.
                              Не все проблемы надо решать паяльником. Некоторые можно и баблом — тупо быстрее.

                              Это уж я молчу о том, что если мы всерьез возьмемся бюджетировать полный аналог этого прибора, то по деньгам выйдет как минимум столько же.


                              1. Aclz
                                24.07.2015 11:50

                                Судя по вашей забугорной ссылке в данном случае паяльником будет намного быстрее (отсилы пара часов). Было б надо было — сделал бы. Только надо ли? Были прецеденты? (не берем во внимание щиток, набитый ИЕКами и их родственниками или бюджетными сериями, типа шнайдеровского одномодульного двухполюсника, или купленными на развале — всё это надо не тестировать, а сразу менять).


                                1. Yurich
                                  24.07.2015 12:13

                                  У меня весь щит на ИЭКах собран, разве что УЗО и вводной автомат — АВВ.
                                  Все живет, все работает. Слухи про ненадежность ИЭК изрядно преувеличены. Могу Вам показать фотку выгоревшего дотла легранда, если желаете, сгорел вообще в номинальном режиме работы. Это не автомат, конечно, был, а устройство защиты от молний, но таки вот.
                                  Тестер УЗО — ну я просто жил долгое время в той стране, мне до магазина было ближе, чем до метро. В РФ аналогичные приборы тоже продаются, кстати.


                                  1. Aclz
                                    24.07.2015 13:02

                                    Ну вообще в инетернете есть тесты, разборы и т.д. А чисто на обывательском уровне: неделю назад случилось так, что в одной руке оказалась пачка из пяти С16-х Легранов DX3-E, в другой столько же аналогичных IEK-ов. Так вот, был немало удивлен, что последние по ощущениям оказались в 2 (!) раза легче, что как бы намекает о количестве металла внутри и исполнении в целом.


                      1. eta4ever
                        23.07.2015 13:51

                        Отсюда вывод — еще один девайс, который дергает защиту по расписанию :)


                  1. Kitsok
                    23.07.2015 13:50

                    Никак не планирую. Эшелонированность защиты должна иметь предел.
                    Если совсем все правильно делать, то я бы выбил из производителей деталей исследования по надежности, прикинул бы потребную мне, посчитал бы общую надежность, и сделал бы столько эшелонов, сколько нужно, чтобы выйти на необходимый показатель.
                    По практике — коммутация одной цепи двумя реле + аварийное реле на весь модуль с контролем выходных цепей (после «исполнительных» реле). Стоит — копейки.


              1. Alexeyslav
                23.07.2015 16:05
                +1

                Есть там залипания, называется пробой.
                Второе реле, которое будет всё это обесточивать к моменту залипания основного реле будет иметь гораздо меньшую наработку на количество переключений контактов, а значит его залипание в нужный момент времени маловероятно.


      1. SunX
        23.07.2015 13:48

        Судя по фото — да, электромагнитные


  1. dkukushkin
    23.07.2015 12:23
    +3

    Сделано добротно, однако электромагнитные реле лучше заменить на тиристорные, контакты которых не залипают даже в условиях сильных нагрузок. Типа такого:

    image


    1. eta4ever
      23.07.2015 13:19

      Симисторные, но не суть. Или собрать самому, из оптосимистора MOC3063 и какого-нибудь мощного выходного.


      1. Alexeyslav
        23.07.2015 16:16

        Собственно, этот модуль и есть…


        1. eta4ever
          23.07.2015 16:18

          Ну, да, только с радиатором и клеммами. В принципе, они есть недорогие, иногда проще купить, чем городить.


      1. dkukushkin
        24.07.2015 10:49

        Симисторные, но не суть.

        Симистop (симметричный триодный тиристор) — полупроводниковый прибор, являющийся разновидностью тиристоров… (Wiki)


        1. eta4ever
          24.07.2015 13:15

          Строго говоря — да. И динистор туда же. Но они достаточно отличаются, чтобы иметь собственное название.


    1. nochkin
      23.07.2015 17:28

      На них надо ставить соответствующее охлаждение.
      А поводу незалипания это не совсем так. Там могут быть пробои как и в любой электронике.


      1. dkukushkin
        24.07.2015 10:54

        На них надо ставить соответствующее охлаждение.

        Смотря какая нагрузка. У нас теплый пол работает через такую сборку + ATTiny, включается каждые 5 секунд на определенное время. Тиристорная сборка как на фото, написано 40 А, но при более слабой нагрузке радиатор добавлять не пришлось.

        Недавно чел. описывал опыт включения обогревателя через реле. Реле сининькие (как в статье) работают несколько недель а то и меньше. Выход был найден — именно тиристорные сборки.


        1. eta4ever
          24.07.2015 13:16

          Ну, на этих реле написано 10 или 16А, при этом они долго и хорошо работают на 1-2А, не больше. Проверено.


          1. Alexeyslav
            24.07.2015 15:49

            Потому что они не силовые. Если внимательно посмотреть в даташит, то эти 10-16А это максимальный рабочий ток который реле может выдержать, но ток коммутации гораздо меньше.
            Есть такие же реле но которые нормально коммутируют токи до 5...7А, надо внимательно смотреть на указанную модель реле, силовые они еще и стоят дороже.


            1. eta4ever
              24.07.2015 15:54

              Логично. Вообще, кстати, когда габариты позволяют, я стараюсь использовать реле в колодках на рейку (недавно узнал, что их кличут «промежуточными»).


          1. nochkin
            24.07.2015 18:54

            Реле, как на картинке выше, часто подделывают и поэтому они могут не тянуть нагрузку, которая указана на наклейке.
            Можно найти много интересных картинок и историй по ключевым словам типа «fotek fake ssr». Поэтому надо быть аккуратнее в этом плане.


        1. nochkin
          24.07.2015 18:09

          Какое потребление такого пола? У меня нагрузки, видимо, больше.


  1. eta4ever
    23.07.2015 13:22
    +2

    Раз уж поднята тема безопасности. Для того, чтобы система была хоть немного более правильной, не хватает следующего:
    1) предохранителей;
    2) темопредохранителей;
    3) обратной связи по всему — по выходам, по напряжению питания.
    То есть, сделано все аккуратно, но о некоторых вещах просто не подумали.


  1. Aclz
    23.07.2015 22:24

    Если в бойлере вдруг закончится горячая вода (ну захочет два человека помыться в ванной или ещё что) — то нагрев так и не начнётся до ночи? Если да, то польза от такой автоматизации сомнительна…


  1. viacheslav77 Автор
    24.07.2015 05:02
    +2

    Отвечаю по порядку.
    1. Отопление комбинированное. Я не хоккеист с зарплатой 1 млн рублей, у меня дом 50 кв. м. За глаза хватает 6 кВт, из них 4 кВт включается на ночь. 4 кВт*8часов*30 дней* 0,69 руб/кВтч = 662 рубля в месяц. Я дурак? Альтернатива — дрова.
    2. Это реле времени, а не умный дом. В его задачи входит только переключать режимы. Предохранители установлены в щитке, т.е. на входе. Термопредохранители и прочие защитные элементы установлены в штатной заводской системе управления бойлером и котлом т.е. на выходе. Реле включается в разрыв электроцепи. В случае залипания реле нагреватели будут работать в штатном режиме — будут греть воду или отапливать дом по установленным на штатной панели настройкам. В случае несрабатывания — из крана будут течь холодная вода, а дома будет немного прохладно.
    3. Да реле те самые, залипающие. Тиристорные ставить не буду. Буду перемонтировать котел — поставлю контакторы в щиток.
    4. Вода в бойлере закончится не может, изучите устройство бойлера, прежде чем давать советы. Забор идет из верхней точки. Выкипеть вода не может, так как выход бойлера — это кран, который в нормальном состоянии закрыт.
    5. Обратную связь по всему делать не буду, я критерии устойчивости по Найквисту считать не умею.


    1. viacheslav77 Автор
      24.07.2015 05:20

      прошу прощения, по диагонали прочитал, если закончится горячая вода — будет холодная. В штатном режиме хватает на две помывки тела и одну-две помывки посуды. По тех заданию такой расклад устраивает.


    1. Yurich
      24.07.2015 11:30

      Я полностью согласен со всеми Вашими тезисами, кроме одного: вода в бойлере таки может доставить неприятностей.
      Но для этого надо, чтобы заклинило не только Ваше реле, надо, чтоб еще заклинил терморегулятор, а группа безопасности также заклинила или вовсе не была установлена.
      Впечатляющий видос от разрушителей:


    1. eta4ever
      24.07.2015 13:18

      По 2-му пункту об этом стоило бы сказать в статье. У меня (и не только) сложилось впечатление, что на самоделку возлагается больше, чем на самом деле.