Выносной проводной термостат для газового котла.
- Войдите на сайт для отправки комментариев
Пнд, 11/04/2022 - 10:12
Простенький скетч для газового котла, у меня в деревне стоит BAXI, гистериз поставил +- 0.5 градуса, вполне комфортно, настраивается в скетче.
Перемычка от котла подключается к нормально замкнутым контактам реле, на случай внезапного отключения МК и/или проблем с термодатчиком, отопление должно работать в любом случае.
Сама плата была снят с ранее используемого устройства, поэтому оставил два реле и в схеме и в логике, выходные контакты реле запараллелил.
Управление одной кнопкой - циклическое переключение температуры контроля, пределы регулировки настраиваются в скетче.
На кнопке так же индикаторный светодиод - загорается когда включены - котел соотвественно отключен.
// heatterm - выносной термостат для газового котла
#include <avr\wdt.h> // стандартная библиотека работы с WDT
#include <OneWire.h>
#include "DallasTemperature.h"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#define deltaTempControl ((signed short)5) // дельта в десятых долях градуса +- от установленной температуры вкл/откл реле котла
// устанавливать строго больше 0.5 градуса (т е 5 значение) - больше точности температурного датчика
signed short controlHeaterTemp; // температура контроля помещения
#define pos_eeprom_save_temp ((word)156) // позиция EEPROM где храниться температура контроля
#define min_lim_temp ((signed short)180) // минимальная возможная темп установки контроля
#define max_lim_temp ((signed short)280) // максимальная возможная темп установки контроля
#define pin_key_control ((unsigned char)A3) // пин кнопки изменения контрольной температуры
#define READKEYCTRL !digitalRead(pin_key_control) // макрос чтения кнопки управления
#define INLEDON digitalWrite(LED_BUILTIN,HIGH) // максрос зажеч встроенный на плате светодиод
#define INLEDOFF digitalWrite(LED_BUILTIN,LOW) // максрос погасить встроенный на плате светодиод
#define pin_led_status ((unsigned char)A2) // пин статусного светодиода
#define STONLED digitalWrite(pin_led_status,HIGH) // максрос зажеч светодиод - реле термостата включено - котел не работает
#define STOFFLED digitalWrite(pin_led_status,LOW) // максрос погасить светодиод - реле термостата отключено - работает котел
#define pin_relay1 ((unsigned char)4) // пин 1 реле
#define pin_relay2 ((unsigned char)5) // пин 2 реле
unsigned char flagIsHeaterON; // флаг что котел включен
unsigned long timerServiceRelay; // таймер профилактического переключения реле
unsigned long currentMillis; // глобальный миллисекундный таймер
// Data wire is plugged into port on the Arduino
#define ONE_WIRE_BUS A1 // пин куда подключен DS18B20
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void saveControlTemp(signed short locTemp) { // запись в EEPROM температуры контроля
EEPROM.write(pos_eeprom_save_temp, (unsigned char) (locTemp / 10));
}
void loadControlTemp(void) { // чтение из EEPROM температуры контроля
unsigned char lb = EEPROM.read(pos_eeprom_save_temp);
signed short gtemp = lb * 10; // преобразуем в знаковый тип
if ((gtemp >= min_lim_temp) && (gtemp <= max_lim_temp)) { // если значение в норме
controlHeaterTemp = gtemp; // считали знгачение в переменную
} else { // ошибка
controlHeaterTemp = max_lim_temp - 30; // присваиваем максимальное значение контроля минус три градуса
saveControlTemp(controlHeaterTemp); // и записываем его в EEPROM
}
}
void HEATERON(void) { // отключение реле - котел работает
digitalWrite(pin_relay1, HIGH);
delay(20);
digitalWrite(pin_relay2, HIGH);
STOFFLED;
flagIsHeaterON = 1;
}
void HEATEROFF(void) { // включение реле - не работает котел
digitalWrite(pin_relay1, LOW);
delay(20);
digitalWrite(pin_relay2, LOW);
STONLED;
flagIsHeaterON = 0;
timerServiceRelay = currentMillis; // сбросим таймер профилактики реле
}
// инициализация WDT (код из даташита + из форума)
uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
void get_mcusr(void) \
__attribute__((naked)) \
__attribute__((used)) \
__attribute__((section(".init3")));
void get_mcusr(void)
{
mcusr_mirror = MCUSR;
MCUSR = 0;
wdt_disable();
}
void setup() {
MCUSR = 0; // отключение WDT
wdt_disable(); // отключение WDT
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
display.display();
delay(1000); // Pause for 1 second
display.clearDisplay(); // Clear the buffer
display.display();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Start!");
display.display();
// настройка пинов МК
pinMode(LED_BUILTIN, OUTPUT);
INLEDOFF;
pinMode(pin_key_control, INPUT_PULLUP);
pinMode(pin_led_status, OUTPUT);
HEATERON(); // инициализируем уровень на выходных пинов реле до того как установили режим выхода, чтобы при включении устройства реле не щелкало
pinMode(pin_relay1, OUTPUT);
pinMode(pin_relay2, OUTPUT);
// put your setup code here, to run once:
loadControlTemp(); // прочитаем из EEPROM температуру контроля
wdt_enable(WDTO_1S); // включение WDT
}
void loop() {
wdt_reset(); // сброс WDT
currentMillis = millis(); // глобальный миллисекундный таймер
static unsigned dispInSetMode = 0; // дисплей в режиме настройки температуры
// put your main code here, to run repeatedly:
static unsigned long timerGetTemp; // таймер получения температуры - каждые 2 секунды
static unsigned char flagReqTemp = 1; // флаг запроса температуры
if ((currentMillis - timerGetTemp) >= 1000UL) { // каждую секунду работаем с датчиком температуры
timerGetTemp = currentMillis; // сбросили таймер
if (flagReqTemp) { // запрос температуры
INLEDON; // зажгли светодиод на плате
sensors.requestTemperatures(); // Send the command to get temperatures
flagReqTemp = 0; // сбросили флаг чтоб считать значение температуры в след шаге
} else { // получение температуры
flagReqTemp = 1; // установили флаг для последующего запроса DS18B20
signed short currentTemp = (sensors.getTempCByIndex(0) * 10); // прочитали температуру с датчика и умножили на 10 чтоб с точностью до десятых хранить
if ((currentTemp >= 50) && (currentTemp <= 350)) { // температура от 5 до 35 градосов плюс - внутри поменщения вполне реальная
INLEDOFF; // погасили на плате светодиод
// отображение температуры
if (!dispInSetMode) { // если не в режиме настроек - выводим температуру
display.clearDisplay(); // Clear the buffer
display.setTextSize(4);
display.setCursor(0, 0);
display.print(currentTemp / 10);
display.print(".");
display.print(currentTemp % 10);
display.print("`");
display.display();
}
// начала котроля/проверки управлением
static unsigned char modeGisteris = 0; // текущий режим работы 0 - начальный режим, котел включен, реле отключено, ждем как повысится температура
if (modeGisteris) { // режим остывания, ждем как температура упадет
if (currentTemp <= (controlHeaterTemp - deltaTempControl)) { // остыло помещение
HEATERON(); // включили котел
modeGisteris = 0; // переводим в режим нагрева
}
} else { // режим нагрева, начальный режим, ждем как температура повысится
if (currentTemp >= (controlHeaterTemp + deltaTempControl)) { // нагрелось помещение
HEATEROFF(); // отключили котел
modeGisteris = 1; // переводим в режим остывания
}
}
// окончание управления
} else { // ошибочная температура или датчик дохлый/отсутствует
HEATERON(); // в любом случае отключили реле - передали управление котлу
INLEDOFF; delay(200); INLEDON; delay(200); INLEDOFF; // моргнули чтоб понять что проблема - для визуального контроля
delay(1000); // сброс всего устройства - что то не так с DS18B20
}
}
}
// работаем с кнопкой контроля/дисплеем
static unsigned char lastValuePin = 0; // предыдущее значение пина кнопки
unsigned char currentPinKey = READKEYCTRL; // читаем текущее значение пина кнопки
static unsigned long timerKeyDown; // таймер антидребезга кнопки и/или следующего нажатия
static unsigned char flagWaitDeb = 0; // флаг что было нажание и ждем время антидребезга для проверки состояния пина
static unsigned char flagNextPress = 0; // флаг что кнопка нажималась и ждем время до след нажатия
static unsigned long timerOptionsMode; // таймер не нажатия кнопок для выхода из режима настроек
if (flagNextPress) { // было нажатие - не реагируем пока не закончиться время
if ((currentMillis - timerKeyDown) >= 100UL) { // ждем чтоб было не более 10 нажатий в секунду
flagNextPress = 0; // сброс флага - можно ждать кнопку
}
} else { // не было нажатий - ждем кнопку
if (flagWaitDeb) { // если сработала кнопка
if ((currentMillis - timerKeyDown) >= 20UL) { // ждем время антидребезга
flagWaitDeb = 0; // сбросили флаг антидребезга
if (currentPinKey) { // если кнопка так и осталась нажатой
timerKeyDown = currentMillis; // сбросили таймер
flagNextPress = 1; // установили флаг, след нажатие может произойти только черех Н время
// обработка кнопки нажатие
timerOptionsMode = currentMillis; // сбросим таймер нахождения в режиме настроек
if (dispInSetMode) { // если мы уже в режиме настройки температуры
if (controlHeaterTemp >= max_lim_temp) { // если дощелкали до максимума
controlHeaterTemp = min_lim_temp; // присваиваем минимальную температуру
} else { // можно увеличивать
controlHeaterTemp += 10; // увеличили на градус
}
saveControlTemp(controlHeaterTemp); // сохранили изменения в EEPROM
} else { // были в режиме отображения текущей температуры
dispInSetMode = 1; // переходим в режим настройки
}
display.clearDisplay(); // Clear the buffer
display.setTextSize(4);
display.setCursor(0, 0);
display.print(" ");
display.print(controlHeaterTemp / 10); // вывели текущую температуру контроля
display.display();
// конец обработки нажатия
} else { // отжали кнопку
flagNextPress = 0; // ждем след нажатия
}
}
} else { // не нажималась кнопка
if ((currentPinKey) && (currentPinKey != lastValuePin)) { // нажали кнопку
timerKeyDown = currentMillis; // сбросили таймер
flagWaitDeb = 1; // поставили флаг антидребезка
}
}
}
lastValuePin = currentPinKey; // всегда предыдущему значению пина присваиваем текущее
if (((currentMillis - timerOptionsMode) >= 2000UL) && (dispInSetMode)) { // если мы в режиме настроек не нажимали кнопку
dispInSetMode = 0; // переходим в режим отображения температуры
}
// поскольку реле по несколько часов будет во включенном состоянии, необходимо периодически перещелкивать им
// что бы контакты не залипали и пружинки не уставали
static unsigned char currentServiceRelay = 0; // текущее реле
if (!flagIsHeaterON) { // есле реле включены
if ((currentMillis - timerServiceRelay) >= 900000UL) { // прошло 15 минут
timerServiceRelay = currentMillis; // сбросим таймер
if (currentServiceRelay) { // по очереди переключаем реле
currentServiceRelay = 0; // потом щелкаем другим реле
digitalWrite(pin_relay1, HIGH);
delay(20);
digitalWrite(pin_relay1, LOW);
} else {
currentServiceRelay = 1; // потом щелкаем другим реле
digitalWrite(pin_relay2, HIGH);
delay(20);
digitalWrite(pin_relay2, LOW);
}
}
}
// end loop
}
// Скетч использует 17040 байт (52%) памяти устройства. Всего доступно 32256 байт.
// Глобальные переменные используют 431 байт (21%) динамической памяти, оставляя 1617 байт для локальных переменных. Максимум: 2048 байт.
---

Куда и как крепили датчик температуры?
Куда и как крепили датчик температуры?
Выносной китайский DS18B20 в холодной комнате на высоте примерно 1 метр от пола, в ~40 см от стены.
Засунул в корпус.
Куда и как крепили датчик температуры?
Выносной китайский DS18B20 в холодной комнате на высоте примерно 1 метр от пола, в ~40 см от стены.
В этом случае и гистерезис не нужен - после отключения котла температура еще с полчаса расти будет и наоборот )))
Куда и как крепили датчик температуры?
Выносной китайский DS18B20 в холодной комнате на высоте примерно 1 метр от пола, в ~40 см от стены.
В этом случае и гистерезис не нужен - после отключения котла температура еще с полчаса расти будет и наоборот )))
Ну сами попробуйте, потом нам расскажете как без гистериза термостат сделать, и да, нихрена она не растет в течении получаса после отключения котла, все выходные тестировал в реальном использовании.
andycat, да шутю я, шутю ))
andycat, да шутю я, шутю ))
Начинающих вводите в заблуждение, мне то пофиг :)
Ну, тут вроде готовый проект, а не просьба о помощи ))