Выносной проводной термостат для газового котла.
- Войдите на сайт для отправки комментариев
Пнд, 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, да шутю я, шутю ))
Начинающих вводите в заблуждение, мне то пофиг :)
Ну, тут вроде готовый проект, а не просьба о помощи ))