Побитый временем холодильник Stinol 104 потерял термостат, таймер, и все термодатчики. Длительное время работал на китайском терморегуляторе, разморозка морозильной камеры производилась вручную перемыканием контактов. Ввиду отсутствия понимания принципов работы плачущего испарителя терморегулятор был настроен неправильно, из-за чего приходилось периодически доставать из холодильного отделения брусок льда… Было принято решение восстановить полную функциональность при помощи Arduino, благо нашлось свободное время и желание заняться медитативным программированием. Да, можно купить термоконтроллер на 2 датчика и закрыть вопрос, но…
Что из себя представляет холодильник Stinol 104 с NoFrost: Морозильное отделение содержит испаритель оснащенный контуром размораживания и принудительным обдувом вентилятором, холодильное содержит плачущий испаритель. Все это означает что через 6-12 часов должна автоматически запускаться разморозка испарителя морозильной камеры. Размораживать холодильное отделение нет никакой необходимости, так как плачущий испаритель оттаивает после каждого цикла работы компрессора без участия нагревателя.
Железо:
- Arduino Nano - 1шт.
- LCD 8X2 - 1шт.
- Энкодер - 1шт.
- Модуль реле 4 канала - 1шт.
- Термодатчик LM75A - 2шт.
- Блок питания 7.5V 2A - 1шт.
Термодатчики расположены на испарителях морозильного и холодильного отделения. По термодатчику холодильного отделения происходит терморегуляция, а по термодатчику морозильного отделения, происходить контроль включения вентилятора и терморегуляция нагревателя. Вентилятор включается только когда температура испарителя морозильного отделения опустится до -10С, далее включается и выключается вместе с компрессором.
Циклограммы:
// нормальный режим работы
10 пауза 5 минут
20 режим заморозки 12 часов
30 режим разморозки 25 минут
40 goto 20
// режим в случае ошибки любого термодатчика
10 пауза 30 минут
20 включить компрессор на 30 минут
30 goto 10
Установка температуры осуществляется в “попугаях”. При нажатии на энкодер появляется возможность установить температуру, чем больше блоков тем сильнее морозит. Полный аналог механического регулятора. Минимальное значение -14С максимальное -21С. В верхней строке отображается температура испарителя холодильного отделения. Дальше птичками отображается текущее состояние реле в последовательности: fan, frost, defrost (вентилятор, компрессор и нагреватель). Нижняя строка отображает текщий режим работы.
Лицевая панель вмонтирована в боковую панель холодильника прямо в тело утеплителя. Было прорезано окно, просверлены отверстия, нарезана резьба М3. Блок питания и релейный модуль расположены под верхней крышкой холодильника так-же в теле утеплителя. Ардуино расположена за дисплеем. Там все соединено проводами. Для термодатчиков были изготовлены маленькие коробочки в которые были залиты прозрачным силиконовым гелем готовые модули с термодатчиками LM75A.
В процессе тестирования периодически в моменты отключения компрессора, Arduino сбрасывался, помогла решить проблему только снаберная цепочка подключенная параллельно компрессору. Резистор 5 Ом, и конденсатор на 5 Мкф 400 В. Конденсатор пусковой, резистор проволочный 5 Вт.
Важно! Возможно кому-то будет предостережением. Один раз я недоглядел и от нагревателя оплавился пластик, нагрелось очень сильно. Планирую восстановить термопредохранитель на 72С во избежании повторения ситуации на случай если залипнет реле.
#include <LiquidCrystal.h>
#include <EEPROM.h>
#include <LM75A.h> // https://github.com/QuentinCG/Arduino-LM75A-Temperature-Sensor-Library
#include <avr/wdt.h>
const int8_t s1 = 7; // энкодер
const int8_t s2 = 8; // энкодер
const int8_t key = 9; // кнопка энкодера
const int8_t fan = A1; // вентилятор
const int8_t frost = A2; // компрессор
const int8_t defrost = A3; // нагреватель
LiquidCrystal lcd(12, 10, 5, 4, 3, 2); // lcd 8x2
LM75A sensor_1(0, 0, 0); // lm75a_sensor_1 0x48 I2C: A4 (SDA), A5 (SCL)
LM75A sensor_2(1, 0, 0); // lm75a_sensor_2 0x49 I2C: A4 (SDA), A5 (SCL)
const int8_t on_temp = 4; // температура включения компрессора
const int8_t off_temp = -14; // температура отключения компрессора
int8_t set_temp = 0; // установка температуры отключения компрессора от -21 до -14 (off_temp)
int8_t batton = 0; // переменная кнопки
bool error = 0; // переменная ошибки
uint16_t val_action = 200; // режим работы
float temp_1 = 0; // температура испарителя холодильной камеры
float temp_2 = 0; // температура испарителя морозильной камеры
bool relay[3] = {0, 0, 0}; // fan, frost, defrost
const uint32_t second = 1000;
const uint32_t minute = 60000;
const uint32_t hour = 3600000;
void setup() {
Serial.begin(9600);
pinMode(fan, OUTPUT); // вентилятор
pinMode(frost, OUTPUT); // компрессор
pinMode(defrost, OUTPUT); // нагреватель
set_relay(0, 0, 0); // все выключаем
lcd.begin(8, 2);
lcd.clear();
set_temp_check(); // проверка и установка температуры из EEPROM
Serial.println("Copyright (c) 2024 RasselFast");
Serial.print("set temperature: ");
Serial.println(set_temp);
wdt_enable (WDTO_2S);
}
void loop() {
wdt_reset(); // Сбрасывам счетчик wdt
if (!batton) if (!digitalRead(key)) batton = 1;
encoder(batton); // установка температуры отключения компрессора
static uint32_t previous_millis = 0;
uint32_t current_millis = millis();
if (current_millis - previous_millis > second) { // выполнять раз в секунду
previous_millis = current_millis;
serial_print_telemetry(); // печать телеметрии в Serial
get_sensor(); // читаем данные с термодатчиков
action(val_action); // режим работы
print_temperature(); // печать температуры испарителя холодильной камеры
print_relay_state(); // печать состояния реле: fan, frost, defrost
if (!batton) print_action(val_action); // печать текущего режима работы
}
}
/// установка температуры отключения компрессора относительно off_temp ///
void encoder(int8_t val) {
static uint32_t previous_millis;
static uint32_t current_millis;
static int8_t bar_temp = 0;
const int8_t min_bar = 1;
const int8_t max_bar = 8;
switch (val) {
case 1:
previous_millis = millis();
bar_temp = (set_temp - off_temp - 1) * -1; // прыжок с переворотом
print_progress_bar(bar_temp); // отображение температуры в "попугаях" от 1 до 8
batton = 2;
break;
case 2:
static int8_t pos = 0;
static int8_t last_state = 0b11;
static int8_t increment[16] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
int8_t state = digitalRead(s1) | (digitalRead(s2) << 1);
if (state != last_state) {
pos += increment[state | (last_state << 2)];
if (!(pos % 4) and pos) { // реагируем только на полный щелчек энкодера
previous_millis = millis();
bar_temp += increment[state | (last_state << 2)];
bar_temp = constrain(bar_temp, min_bar, max_bar);
print_progress_bar(bar_temp); // отображение температуры в "попугаях" от 1 до 8
pos = 0;
}
last_state = state;
}
current_millis = millis();
if (current_millis - previous_millis > second * 5) { // через 5 секунд сохраняем
set_temp = off_temp - bar_temp + 1; // обратный прыжок с переворотом
EEPROM.write(0, set_temp);
batton = 0;
Serial.print("set temperature: ");
Serial.println(set_temp);
}
break;
}
}
/// селектор режимов работы ///
void action(uint16_t val) {
static uint32_t previous_millis;
static uint32_t current_millis;
switch (val) {
case 100: // режим работы без термодатчиков 30\30 -----ERROR
Serial.println("thermal sensors off");
previous_millis = millis();
action_error(); // переключатель
val_action = 101;
break;
case 101:
current_millis = millis();
if (current_millis - previous_millis > (minute * 30)) {
val_action = 100;
}
break;
case 200: // пауза перед включением 5 минут -----PAUSE
Serial.println("pause: 5 min");
previous_millis = millis();
val_action = 201;
break;
case 201:
current_millis = millis();
if (current_millis - previous_millis > (minute * 5)) {
val_action = 300;
}
break;
case 300: // запуск заморозки на 12 часов -----FROST
Serial.println("frost: 12 hour");
previous_millis = millis();
val_action = 301;
break;
case 301:
action_frost(); // терморегулятор заморозки
current_millis = millis();
if (current_millis - previous_millis > (hour * 12)) {
set_relay(0, 0, 0); // все выключаем
val_action = 400;
}
break;
case 400: // запуск разморозки на 25 минут -----DEFROST
Serial.println("defrost: 25 min");
previous_millis = millis();
val_action = 401;
break;
case 401: //
action_defrost(); // терморегулятор разморозки
current_millis = millis();
if (current_millis - previous_millis > (minute * 25)) {
set_relay(0, 0, 0); // все выключаем
val_action = 300;
}
break;
}
}
/// переключатель ///
void action_error() {
static bool flag = 0;
if (!flag) set_relay(0, 0, 0); // все выключаем
else set_relay(1, 1, 0); // включаем вентилятор и компрессор
flag = !flag;
}
/// терморегулятор заморозки ///
void action_frost() {
const int8_t temp_fan_on = -10; // температура включения вентилятора
if (temp_1 >= on_temp) {
set_relay((temp_2 < temp_fan_on), 1, 0); // включаем компрессор и вероятно вентилятор
}
else if (temp_1 <= set_temp) {
set_relay(0, 0, 0); // все выключаем
}
if (relay[1]) {
if (temp_2 < temp_fan_on) {
set_relay(1, 1, 0); // включаем вентилятор и компрессор
}
}
}
/// терморегулятор разморозки ///
void action_defrost() {
const int8_t temp_defrost_off = 6; // температура выключения нагревателя
const int8_t hysteresis = 2; // гистерезис
if (temp_2 < (temp_defrost_off - hysteresis)) {
set_relay(0, 0, 1); // включаем нагреватель
}
else if (temp_2 > temp_defrost_off) {
set_relay(0, 0, 0); // все выключаем
}
}
/// печать текущего режима работы ///
void print_action(uint16_t val) {
lcd.setCursor(0, 1);
switch (val) { // Список параметров
case 101: lcd.print("ERROR "); break; // ошибка
case 201: lcd.print("PAUSE "); break; // пауза
case 301: lcd.print("FROST "); break; // заморозка
case 401: lcd.print("DEFROST "); break; // разморозка
}
}
/// печать температуры испарителя холодильной камеры ///
void print_temperature() {
lcd.setCursor(0, 0);
if (!error) lcd.print(temp_1, 1);
else lcd.print("---");
lcd.print(" ");
}
/// печать состояния реле: fan, frost, defrost ///
void print_relay_state() {
lcd.setCursor(5, 0);
for (int8_t i = 0; i < 3; i++) {
if (relay[i]) lcd.print("^");
else lcd.print(" ");
}
}
/// печать сегментов прогрессбара ///
void print_progress_bar(int8_t val) {
lcd.setCursor(0, 1);
for (int8_t i = 0; i < 8; i++) {
if (i < val) lcd.write(0xFF);
else lcd.print(" ");
}
}
/// установка состояний реле ///
void set_relay (bool set_fan, bool set_frost, bool set_defrost) { // 1 - on, 0 - off
digitalWrite(fan, !set_fan);
digitalWrite(frost, !set_frost);
digitalWrite(defrost, !set_defrost);
relay[0] = set_fan;
relay[1] = set_frost;
relay[2] = set_defrost;
}
/// получение температуры ///
void get_sensor() {
if (!error) {
temp_1 = sensor_1.getTemperatureInDegrees();
temp_2 = sensor_2.getTemperatureInDegrees();
if (temp_1 == INVALID_LM75A_TEMPERATURE) error_sensor(1);
if (temp_2 == INVALID_LM75A_TEMPERATURE) error_sensor(2);
}
}
/// ошибка датчика температуры ///
void error_sensor(int8_t sensor) {
Serial.print("error sensor: ");
Serial.println(sensor);
val_action = 100; // режим работы без термодатчиков
error = 1;
}
/// печать телеметрии в Serial раз в минуту ///
void serial_print_telemetry() {
static int8_t count = 0;
count++;
if (count == 60) {
Serial.print("sensors: ");
if (!error) Serial.print(temp_1); // temp_1
else Serial.print("---");
Serial.print(" ");
if (!error) Serial.print(temp_2); // temp_2
else Serial.print("---");
Serial.print(" ");
for (int8_t i = 0; i < 3; i++) { // fan, frost, defrost
if (relay[i]) Serial.print("1");
else Serial.print("0");
}
Serial.println(" ");
count = 0;
}
}
/// проверка и установка температуры из EEPROM ///
void set_temp_check() {
set_temp = EEPROM.read(0);
if (set_temp < (off_temp - 7) or set_temp > off_temp) {
set_temp = off_temp;
EEPROM.write(0, off_temp);
}
}