Выносной проводной термостат для газового котла.
- Войдите на сайт для отправки комментариев
Пнд, 11/04/2022 - 10:12
Простенький скетч для газового котла, у меня в деревне стоит BAXI, гистериз поставил +- 0.5 градуса, вполне комфортно, настраивается в скетче.
Перемычка от котла подключается к нормально замкнутым контактам реле, на случай внезапного отключения МК и/или проблем с термодатчиком, отопление должно работать в любом случае.
Сама плата была снят с ранее используемого устройства, поэтому оставил два реле и в схеме и в логике, выходные контакты реле запараллелил.
Управление одной кнопкой - циклическое переключение температуры контроля, пределы регулировки настраиваются в скетче.
На кнопке так же индикаторный светодиод - загорается когда включены - котел соотвественно отключен.
001 | // heatterm - выносной термостат для газового котла |
002 |
003 | #include <avr\wdt.h> // стандартная библиотека работы с WDT |
004 | #include <OneWire.h> |
005 | #include "DallasTemperature.h" |
006 | #include <SPI.h> |
007 | #include <Wire.h> |
008 | #include <Adafruit_GFX.h> |
009 | #include <Adafruit_SSD1306.h> |
010 | #include <EEPROM.h> |
011 |
012 | #define deltaTempControl ((signed short)5) // дельта в десятых долях градуса +- от установленной температуры вкл/откл реле котла |
013 | // устанавливать строго больше 0.5 градуса (т е 5 значение) - больше точности температурного датчика |
014 |
015 | signed short controlHeaterTemp; // температура контроля помещения |
016 | #define pos_eeprom_save_temp ((word)156) // позиция EEPROM где храниться температура контроля |
017 | #define min_lim_temp ((signed short)180) // минимальная возможная темп установки контроля |
018 | #define max_lim_temp ((signed short)280) // максимальная возможная темп установки контроля |
019 |
020 | #define pin_key_control ((unsigned char)A3) // пин кнопки изменения контрольной температуры |
021 | #define READKEYCTRL !digitalRead(pin_key_control) // макрос чтения кнопки управления |
022 |
023 | #define INLEDON digitalWrite(LED_BUILTIN,HIGH) // максрос зажеч встроенный на плате светодиод |
024 | #define INLEDOFF digitalWrite(LED_BUILTIN,LOW) // максрос погасить встроенный на плате светодиод |
025 |
026 | #define pin_led_status ((unsigned char)A2) // пин статусного светодиода |
027 | #define STONLED digitalWrite(pin_led_status,HIGH) // максрос зажеч светодиод - реле термостата включено - котел не работает |
028 | #define STOFFLED digitalWrite(pin_led_status,LOW) // максрос погасить светодиод - реле термостата отключено - работает котел |
029 |
030 | #define pin_relay1 ((unsigned char)4) // пин 1 реле |
031 | #define pin_relay2 ((unsigned char)5) // пин 2 реле |
032 |
033 | unsigned char flagIsHeaterON; // флаг что котел включен |
034 | unsigned long timerServiceRelay; // таймер профилактического переключения реле |
035 | unsigned long currentMillis; // глобальный миллисекундный таймер |
036 |
037 | // Data wire is plugged into port on the Arduino |
038 | #define ONE_WIRE_BUS A1 // пин куда подключен DS18B20 |
039 | // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) |
040 | OneWire oneWire(ONE_WIRE_BUS); |
041 | // Pass our oneWire reference to Dallas Temperature. |
042 | DallasTemperature sensors(&oneWire); |
043 |
044 | #define SCREEN_WIDTH 128 // OLED display width, in pixels |
045 | #define SCREEN_HEIGHT 32 // OLED display height, in pixels |
046 | // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) |
047 | // The pins for I2C are defined by the Wire-library. |
048 | // On an arduino UNO: A4(SDA), A5(SCL) |
049 | #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) |
050 | #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 |
051 | Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); |
052 |
053 | void saveControlTemp(signed short locTemp) { // запись в EEPROM температуры контроля |
054 | EEPROM.write(pos_eeprom_save_temp, (unsigned char ) (locTemp / 10)); |
055 | } |
056 |
057 | void loadControlTemp( void ) { // чтение из EEPROM температуры контроля |
058 | unsigned char lb = EEPROM.read(pos_eeprom_save_temp); |
059 | signed short gtemp = lb * 10; // преобразуем в знаковый тип |
060 | if ((gtemp >= min_lim_temp) && (gtemp <= max_lim_temp)) { // если значение в норме |
061 | controlHeaterTemp = gtemp; // считали знгачение в переменную |
062 | } else { // ошибка |
063 | controlHeaterTemp = max_lim_temp - 30; // присваиваем максимальное значение контроля минус три градуса |
064 | saveControlTemp(controlHeaterTemp); // и записываем его в EEPROM |
065 | } |
066 | } |
067 |
068 | void HEATERON( void ) { // отключение реле - котел работает |
069 | digitalWrite(pin_relay1, HIGH); |
070 | delay(20); |
071 | digitalWrite(pin_relay2, HIGH); |
072 | STOFFLED; |
073 | flagIsHeaterON = 1; |
074 | } |
075 |
076 | void HEATEROFF( void ) { // включение реле - не работает котел |
077 | digitalWrite(pin_relay1, LOW); |
078 | delay(20); |
079 | digitalWrite(pin_relay2, LOW); |
080 | STONLED; |
081 | flagIsHeaterON = 0; |
082 | timerServiceRelay = currentMillis; // сбросим таймер профилактики реле |
083 | } |
084 |
085 | // инициализация WDT (код из даташита + из форума) |
086 | uint8_t mcusr_mirror __attribute__ ((section ( ".noinit" ))); |
087 | void get_mcusr( void ) \ |
088 | __attribute__((naked)) \ |
089 | __attribute__((used)) \ |
090 | __attribute__((section( ".init3" ))); |
091 | void get_mcusr( void ) |
092 | { |
093 | mcusr_mirror = MCUSR; |
094 | MCUSR = 0; |
095 | wdt_disable(); |
096 | } |
097 |
098 | void setup () { |
099 | MCUSR = 0; // отключение WDT |
100 | wdt_disable(); // отключение WDT |
101 | display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS); |
102 | display.display(); |
103 | delay(1000); // Pause for 1 second |
104 | display.clearDisplay(); // Clear the buffer |
105 | display.display(); |
106 | display.setTextSize(1); |
107 | display.setTextColor(SSD1306_WHITE); |
108 | display.setCursor(0, 0); |
109 | display.print( "Start!" ); |
110 | display.display(); |
111 | // настройка пинов МК |
112 | pinMode(LED_BUILTIN, OUTPUT); |
113 | INLEDOFF; |
114 | pinMode(pin_key_control, INPUT_PULLUP); |
115 | pinMode(pin_led_status, OUTPUT); |
116 | HEATERON(); // инициализируем уровень на выходных пинов реле до того как установили режим выхода, чтобы при включении устройства реле не щелкало |
117 | pinMode(pin_relay1, OUTPUT); |
118 | pinMode(pin_relay2, OUTPUT); |
119 | // put your setup code here, to run once: |
120 | loadControlTemp(); // прочитаем из EEPROM температуру контроля |
121 | wdt_enable(WDTO_1S); // включение WDT |
122 | } |
123 |
124 | void loop () { |
125 | wdt_reset(); // сброс WDT |
126 | currentMillis = millis(); // глобальный миллисекундный таймер |
127 | static unsigned dispInSetMode = 0; // дисплей в режиме настройки температуры |
128 | // put your main code here, to run repeatedly: |
129 | static unsigned long timerGetTemp; // таймер получения температуры - каждые 2 секунды |
130 | static unsigned char flagReqTemp = 1; // флаг запроса температуры |
131 | if ((currentMillis - timerGetTemp) >= 1000UL) { // каждую секунду работаем с датчиком температуры |
132 | timerGetTemp = currentMillis; // сбросили таймер |
133 | if (flagReqTemp) { // запрос температуры |
134 | INLEDON; // зажгли светодиод на плате |
135 | sensors.requestTemperatures(); // Send the command to get temperatures |
136 | flagReqTemp = 0; // сбросили флаг чтоб считать значение температуры в след шаге |
137 | } else { // получение температуры |
138 | flagReqTemp = 1; // установили флаг для последующего запроса DS18B20 |
139 | signed short currentTemp = (sensors.getTempCByIndex(0) * 10); // прочитали температуру с датчика и умножили на 10 чтоб с точностью до десятых хранить |
140 | if ((currentTemp >= 50) && (currentTemp <= 350)) { // температура от 5 до 35 градосов плюс - внутри поменщения вполне реальная |
141 | INLEDOFF; // погасили на плате светодиод |
142 | // отображение температуры |
143 | if (!dispInSetMode) { // если не в режиме настроек - выводим температуру |
144 | display.clearDisplay(); // Clear the buffer |
145 | display.setTextSize(4); |
146 | display.setCursor(0, 0); |
147 | display.print(currentTemp / 10); |
148 | display.print( "." ); |
149 | display.print(currentTemp % 10); |
150 | display.print( "`" ); |
151 | display.display(); |
152 | } |
153 | // начала котроля/проверки управлением |
154 | static unsigned char modeGisteris = 0; // текущий режим работы 0 - начальный режим, котел включен, реле отключено, ждем как повысится температура |
155 | if (modeGisteris) { // режим остывания, ждем как температура упадет |
156 | if (currentTemp <= (controlHeaterTemp - deltaTempControl)) { // остыло помещение |
157 | HEATERON(); // включили котел |
158 | modeGisteris = 0; // переводим в режим нагрева |
159 | } |
160 | } else { // режим нагрева, начальный режим, ждем как температура повысится |
161 | if (currentTemp >= (controlHeaterTemp + deltaTempControl)) { // нагрелось помещение |
162 | HEATEROFF(); // отключили котел |
163 | modeGisteris = 1; // переводим в режим остывания |
164 | } |
165 | } |
166 | // окончание управления |
167 | } else { // ошибочная температура или датчик дохлый/отсутствует |
168 | HEATERON(); // в любом случае отключили реле - передали управление котлу |
169 | INLEDOFF; delay(200); INLEDON; delay(200); INLEDOFF; // моргнули чтоб понять что проблема - для визуального контроля |
170 | delay(1000); // сброс всего устройства - что то не так с DS18B20 |
171 | } |
172 | } |
173 | } |
174 | // работаем с кнопкой контроля/дисплеем |
175 | static unsigned char lastValuePin = 0; // предыдущее значение пина кнопки |
176 | unsigned char currentPinKey = READKEYCTRL; // читаем текущее значение пина кнопки |
177 | static unsigned long timerKeyDown; // таймер антидребезга кнопки и/или следующего нажатия |
178 | static unsigned char flagWaitDeb = 0; // флаг что было нажание и ждем время антидребезга для проверки состояния пина |
179 | static unsigned char flagNextPress = 0; // флаг что кнопка нажималась и ждем время до след нажатия |
180 | static unsigned long timerOptionsMode; // таймер не нажатия кнопок для выхода из режима настроек |
181 | if (flagNextPress) { // было нажатие - не реагируем пока не закончиться время |
182 | if ((currentMillis - timerKeyDown) >= 100UL) { // ждем чтоб было не более 10 нажатий в секунду |
183 | flagNextPress = 0; // сброс флага - можно ждать кнопку |
184 | } |
185 | } else { // не было нажатий - ждем кнопку |
186 | if (flagWaitDeb) { // если сработала кнопка |
187 | if ((currentMillis - timerKeyDown) >= 20UL) { // ждем время антидребезга |
188 | flagWaitDeb = 0; // сбросили флаг антидребезга |
189 | if (currentPinKey) { // если кнопка так и осталась нажатой |
190 | timerKeyDown = currentMillis; // сбросили таймер |
191 | flagNextPress = 1; // установили флаг, след нажатие может произойти только черех Н время |
192 | // обработка кнопки нажатие |
193 | timerOptionsMode = currentMillis; // сбросим таймер нахождения в режиме настроек |
194 | if (dispInSetMode) { // если мы уже в режиме настройки температуры |
195 | if (controlHeaterTemp >= max_lim_temp) { // если дощелкали до максимума |
196 | controlHeaterTemp = min_lim_temp; // присваиваем минимальную температуру |
197 | } else { // можно увеличивать |
198 | controlHeaterTemp += 10; // увеличили на градус |
199 | } |
200 | saveControlTemp(controlHeaterTemp); // сохранили изменения в EEPROM |
201 | } else { // были в режиме отображения текущей температуры |
202 | dispInSetMode = 1; // переходим в режим настройки |
203 | } |
204 | display.clearDisplay(); // Clear the buffer |
205 | display.setTextSize(4); |
206 | display.setCursor(0, 0); |
207 | display.print( " " ); |
208 | display.print(controlHeaterTemp / 10); // вывели текущую температуру контроля |
209 | display.display(); |
210 | // конец обработки нажатия |
211 | } else { // отжали кнопку |
212 | flagNextPress = 0; // ждем след нажатия |
213 | } |
214 | } |
215 | } else { // не нажималась кнопка |
216 | if ((currentPinKey) && (currentPinKey != lastValuePin)) { // нажали кнопку |
217 | timerKeyDown = currentMillis; // сбросили таймер |
218 | flagWaitDeb = 1; // поставили флаг антидребезка |
219 | } |
220 | } |
221 | } |
222 | lastValuePin = currentPinKey; // всегда предыдущему значению пина присваиваем текущее |
223 | if (((currentMillis - timerOptionsMode) >= 2000UL) && (dispInSetMode)) { // если мы в режиме настроек не нажимали кнопку |
224 | dispInSetMode = 0; // переходим в режим отображения температуры |
225 | } |
226 | // поскольку реле по несколько часов будет во включенном состоянии, необходимо периодически перещелкивать им |
227 | // что бы контакты не залипали и пружинки не уставали |
228 | static unsigned char currentServiceRelay = 0; // текущее реле |
229 | if (!flagIsHeaterON) { // есле реле включены |
230 | if ((currentMillis - timerServiceRelay) >= 900000UL) { // прошло 15 минут |
231 | timerServiceRelay = currentMillis; // сбросим таймер |
232 | if (currentServiceRelay) { // по очереди переключаем реле |
233 | currentServiceRelay = 0; // потом щелкаем другим реле |
234 | digitalWrite(pin_relay1, HIGH); |
235 | delay(20); |
236 | digitalWrite(pin_relay1, LOW); |
237 | } else { |
238 | currentServiceRelay = 1; // потом щелкаем другим реле |
239 | digitalWrite(pin_relay2, HIGH); |
240 | delay(20); |
241 | digitalWrite(pin_relay2, LOW); |
242 | } |
243 | } |
244 | } |
245 | // end loop |
246 | } |
247 |
248 | // Скетч использует 17040 байт (52%) памяти устройства. Всего доступно 32256 байт. |
249 | // Глобальные переменные используют 431 байт (21%) динамической памяти, оставляя 1617 байт для локальных переменных. Максимум: 2048 байт. |
---
Куда и как крепили датчик температуры?
Куда и как крепили датчик температуры?
Выносной китайский DS18B20 в холодной комнате на высоте примерно 1 метр от пола, в ~40 см от стены.
Засунул в корпус.
Куда и как крепили датчик температуры?
Выносной китайский DS18B20 в холодной комнате на высоте примерно 1 метр от пола, в ~40 см от стены.
В этом случае и гистерезис не нужен - после отключения котла температура еще с полчаса расти будет и наоборот )))
Куда и как крепили датчик температуры?
Выносной китайский DS18B20 в холодной комнате на высоте примерно 1 метр от пола, в ~40 см от стены.
В этом случае и гистерезис не нужен - после отключения котла температура еще с полчаса расти будет и наоборот )))
Ну сами попробуйте, потом нам расскажете как без гистериза термостат сделать, и да, нихрена она не растет в течении получаса после отключения котла, все выходные тестировал в реальном использовании.
andycat, да шутю я, шутю ))
andycat, да шутю я, шутю ))
Начинающих вводите в заблуждение, мне то пофиг :)
Ну, тут вроде готовый проект, а не просьба о помощи ))