//Управление горелкой типа Monarch (Weishaupt L7Z) //Вариант неисправности контроллера управления горением //(включение продувки и насоса, зажигания и топливных клапанов) //Контроллер темпертуры исправен //В этой схеме добавлена защита по обрыву пламени с помощью датчика //включение и выключение котла от контроллера температуры //---------- Внимание -------- //питание контроллера температуры - 220 В, поэтому вход к реле //контроллера на пин 4 подсоедините к питанию Ардуино или шилда 5 В //последовательный запуск продувки, топливных клапанов и искры по таймерам //настройка таймеров //эти настройки можно записать в энергонезависимую память //если заслонка горелки не работает, сразу установите ее на открытие #include <EEPROM.h> //библиотека работы с памятью #include <LiquidCrystal_I2C.h> //библиотеки работы с LCD через I2C #include <Wire.h> const int FlamePin = 2 ; //пин датчика пламени //Не забудь шунтировать выходы от кнопок резистором 10 КОм const int BtDownPin = 5; //пин кнопки уменьшить. const int BtUpPin = 6; //пин кнопки увеличить const int BtSettingPin = 7; //пин кнопки настроек const int AlarmPin = 8; //пин управления сигнализацией const int MotorPin = 9; //пин управления реле продувки const int FuelValvePin = 11; //пин управления реле топливных клапанов const int IgnitionPin = 10; //пин управления искры const int RelayStartPin = 12; //пин от контроллера температуры. //Его тоже нужно шунтировать резистором 10 КОм const int BtBoilerOnPin = 13; //пин кнопки включения/выключения котла //пины модуля I2C LCD-дисплея : //SDA - A4 //SCL - A5 //питание от шилда int Init_Eeprom_Addr = 1000; //номер ячейки в памяти EEPROM (0-1023), куда мы запишем ключ о //самом первом запуске программы //это нужно, так как при первом запуске в памяти нет наших настроек //и нужно загрузить значения по умолчанию byte Init_Key = 231; //это ключ (0-255). //Если при пуске скетча данные из ячейки Init_Eeprom не совпадают //с Init_Key, вводим в настройки значения по умолчанию, //иначе берем настройки из памяти long BLOW_TIMER=30000; //время продувки котла //предел верхней границы, выше нее подниматься нельзя, мс long BLOW_TIMER_MAX=99000; //предел нижней границы, ниже нее опустить нельзя, мс int BLOW_TIMER_MIN=5000; int IGNITION_TIMER=3000; //время поджига искры //предел верхней границы, выше нее подниматься нельзя, мс int IGNITION_TIMER_MAX=5000; //предел нижней грницы, ниже нее опустить нельзя, мс int IGNITION_TIMER_MIN=1000; int FLAME_TIMER=3000; //время ожидания проверки по обрыву пламени //предел верхней границы, выше нее подниматься нельзя, мс int FLAME_TIMER_MAX=5000; //предел нижней грницы, ниже нее опустить нельзя, мс int FLAME_TIMER_MIN=1000; long BlowTick = 0; //нужна в отсчете времени продувки int BlowCountDown = 0; //нужна в отображении времени продувки на LCD int SettingNumber = 1; //пункт меню настройки boolean LastButtonSetting = LOW; //предыдущее состояние кнопки Настройки boolean CurrentButtonSetting = LOW; //текущее состояние кнопки Настройки boolean LastButtonDown = LOW; // --- Меньше boolean CurrentButtonDown = LOW; // --- Меньше boolean LastButtonUp = LOW; // --- Больше boolean CurrentButtonUp = LOW; // --- Больше boolean LastRelayStart = LOW; //предыдущее состояние реле boolean CurrentRelayStart = LOW; //текущее состояние реле boolean LastButtonBoiler = LOW; //предыдущее состояние кнопки включения котла boolean CurrentButtonBoiler = LOW; //текущее состояние кнопки включения котла boolean FlagSettingLoop = true; //были ли мы в цикле нажатия кнопки Настройки boolean FlagUpLoop = true; //были ли мы в цикле нажатия кнопки Больше boolean FlagDownLoop = true; //были ли мы в цикле нажатия кнопки Меньше boolean FlagBoilerLoop = true; //были ли мы в цикле нажатия кнопки включения котла boolean FlagStartMotor = false; //нужна для запуска отсчета времени продувки boolean FlagBoilerOn = false; //включен или нет котел boolean FlagFlameOff = false; //отключение датчика огня boolean FlagFlameAlarm = false; //сработала ли сигнализация LiquidCrystal_I2C lcd(0x27,16,2); // Задаем адрес и размерность дисплея. void setup() //************************************************************ { pinMode (BtSettingPin, INPUT); pinMode (BtDownPin, INPUT); pinMode (BtUpPin, INPUT); pinMode (BtBoilerOnPin, INPUT); pinMode (MotorPin, OUTPUT); pinMode (FuelValvePin, OUTPUT); pinMode (IgnitionPin, OUTPUT); pinMode (RelayStartPin, INPUT); pinMode (AlarmPin, OUTPUT); pinMode (FlamePin, INPUT); //если запись в настройки когда-то была, то читаем их if (EEPROM.read(Init_Eeprom_Addr) == Init_Key) { EEPROM.get(0,BLOW_TIMER); EEPROM.get(4,IGNITION_TIMER); EEPROM.get(6,FLAME_TIMER); } else //если записи не было, записываем в память значения по умолчанию { EEPROM.put(0,BLOW_TIMER); EEPROM.put(4,IGNITION_TIMER); EEPROM.put(6,FLAME_TIMER); //и ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); } lcd.init(); //Инициализация lcd lcd.backlight(); //Включаем подсветку lcd.setCursor(0,0); //ставим курсор в начало первой строки lcd.print(" Boiler"); lcd.setCursor(0,1); //ставим курсор в начало второй строки lcd.print(" Auto Control "); delay(2000); //Пауза SettingsOnLCD(SettingNumber); //отображаем строку с настройками } void loop() //************************************************* { //************ Пуск, остановка или работа котла ******** if(FlagBoilerOn == true) //если котел включен { if (FlagFlameAlarm == true && FlagFlameOff == false) { //если сработала сигнализация по обрыву пламени //и сигнализация не отключена, выдаем подсказку и блокируем запуск котла //пока не сбросим аварию или не отключим сенсор digitalWrite(AlarmPin, HIGH); //включаем сигнализацию lcd.setCursor(0,0); lcd.print(" Flame Trouble "); lcd.setCursor(0,1); lcd.print(" Hold Button "); delay(1000); lcd.setCursor(0,0); //если нажать кнопку "вверх", сброс аварии lcd.print("UP - Reset "); lcd.setCursor(0,1); //если нажать кнопку "вниз", сброс аварии и отключение датчика огня lcd.print("DOWN - Sens. OFF"); delay(1000); SettingNumber = 22; } else { //считываем состояние реле от контроллера температуры с устраненным дребезгом CurrentRelayStart = debounce(LastRelayStart, RelayStartPin); if(CurrentRelayStart == HIGH) //если пришел сигнал от контроллера температуры { if (FlagStartMotor == false) //если котел не работал, то пускаем { BoilerStart(); } else //если котел уже работает { BoilerRun(); } } else //если сигнала от контроллера температуры нет { BoilerStop(); } SettingsOnLCD(SettingNumber); //отображаем строку с настройками } } else //если котел выключен { BoilerOff(); } //************ Нажата кнопка Включения/выключения котла ******************** //считываем состояние кнопки с устраненным дребезгом CurrentButtonBoiler = debounce(LastButtonBoiler, BtBoilerOnPin); if(CurrentButtonBoiler == HIGH) //если кнопка нажата { if (FlagBoilerLoop == true) //эта переменная нужна для однократного нажатия кнопки, //даже если ее удерживать { FlagBoilerLoop = false; if(FlagBoilerOn == true) { FlagBoilerOn = false; SettingNumber = 1; } else { FlagBoilerOn = true; } } } else { FlagBoilerLoop = true; } LastButtonBoiler = CurrentButtonBoiler; //обновляем текущее значение кнопки //************ Нажата кнопка Настройки ******************** //при каждом нажатии кнопки Настройки //перескакиваем на следующий параметр в очереди. И так по кругу //считываем состояние кнопки с устраненным дребезгом CurrentButtonSetting = debounce(LastButtonSetting, BtSettingPin); if(CurrentButtonSetting == HIGH) //если кнопка нажата { if (FlagSettingLoop == true) //эта переменная нужна для однократного нажатия кнопки, //даже если ее удерживать { FlagSettingLoop = false; SettingNumber = SettingNumber + 1; if (SettingNumber > 7) { SettingNumber = 1; } } } else { FlagSettingLoop = true; } LastButtonSetting = CurrentButtonSetting; //обновляем текущее значение кнопки //*************** Нажата кнопка Увеличить ********************** CurrentButtonUp = debounce(LastButtonUp, BtUpPin); if(CurrentButtonUp == HIGH && FlagUpLoop == true) //если кнопка нажата { ButtonPlusAction(SettingNumber, false); //функция реакции на нажатие кнопки Увеличить FlagUpLoop = false; } if(CurrentButtonUp == LOW) { FlagUpLoop = true; } LastButtonUp = CurrentButtonUp; //обновляем текущее значение кнопки //************** Нажата кнопка Уменьшить ********************** CurrentButtonDown = debounce(LastButtonDown, BtDownPin); if(CurrentButtonDown == HIGH && FlagDownLoop == true) //если кнопка нажата { ButtonMinusAction(SettingNumber, false); //функция реакции на нажатие кнопки Уменьшить FlagDownLoop = false; } if(CurrentButtonDown == LOW) { FlagDownLoop = true; } LastButtonDown = CurrentButtonDown; //обновляем текущее значение кнопки SettingsOnLCD(SettingNumber); //отображаем строку с настройками } //void loop() //********************** ФУНКЦИИ ********************************* //*********** функция отображения настроек на LCD ************* void SettingsOnLCD(int SettingItem) { lcd.setCursor(0,1); switch (SettingItem) { case 1: //время продувки lcd.print("1 Blow Time "); lcd.print(BLOW_TIMER/1000); lcd.print(" s "); break; case 2: //время зажигания lcd.print("2 Igni Time "); lcd.print(IGNITION_TIMER/1000); lcd.print(" s "); break; case 3: //время ожидания обрыва пламени lcd.print("3 FlameTime "); lcd.print(FLAME_TIMER/1000); lcd.print(" s "); break; case 4: //включение и выключение датчика огня if (FlagFlameOff == true) { lcd.print("4 FlameSens OFF "); } else { lcd.print("4 FlameSens ON "); } break; case 5: //запись настроек в EEPROM lcd.print("5 SaveSet. (UP) "); break; case 6: //чтение настроек из EEPROM lcd.print("6 ReadSet. (UP) "); break; case 7: //загрузка значений по умолчанию lcd.print("7 Default (UP) "); break; } } //******** Функция изменения параметров при нажатии кнопки Больше ********** void ButtonPlusAction(int SettingItem, boolean IncTrigger) { switch (SettingItem) { case 1: //время продувки BLOW_TIMER = BLOW_TIMER + 1000; if (BLOW_TIMER > BLOW_TIMER_MAX) //если пытаемся уйти за верхний предел { BLOW_TIMER = BLOW_TIMER_MAX; } break; case 2: //время зажигания IGNITION_TIMER = IGNITION_TIMER + 1000; //если пытаемся уйти за верхний предел if (IGNITION_TIMER > IGNITION_TIMER_MAX) { IGNITION_TIMER = IGNITION_TIMER_MAX; } break; case 3: //время ожидания обрыва пламени FLAME_TIMER = FLAME_TIMER + 1000; //если пытаемся уйти за верхний предел if (FLAME_TIMER > FLAME_TIMER_MAX) { FLAME_TIMER = FLAME_TIMER_MAX; } break; case 4: //включение и выключение датчика огня if (FlagFlameOff == true) { FlagFlameOff = false; } else { FlagFlameOff = true; } break; case 5: //запись настроек в EEPROM EEPROM.put(0,BLOW_TIMER); EEPROM.put(4,IGNITION_TIMER); EEPROM.put(6,FLAME_TIMER); //ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); lcd.setCursor(0,1); lcd.print("Saving... OK "); delay(2000); break; case 6: //чтение настроек из EEPROM EEPROM.get(0,BLOW_TIMER); EEPROM.get(4,IGNITION_TIMER); EEPROM.get(6,FLAME_TIMER); lcd.setCursor(0,1); lcd.print("Reading... OK "); delay(2000); break; case 7: //загрузка значений по умолчанию. Берем из начала скетча BLOW_TIMER = 30000; IGNITION_TIMER = 3000; FLAME_TIMER = 3000; lcd.setCursor(0,1); lcd.print("Loading... OK "); delay(2000); break; case 22: //сброс reset аварии по обрыву пламени FlagFlameAlarm = false; digitalWrite(AlarmPin, LOW); SettingNumber = 4; SettingsOnLCD(SettingNumber); //отображаем строку с настройками break; } } //******** Функция изменения параметров при нажатии кнопки Меньше ********** void ButtonMinusAction(int SettingItem, boolean IncTrigger) { switch (SettingItem) { case 1: //время продувки BLOW_TIMER = BLOW_TIMER - 1000; if (BLOW_TIMER < BLOW_TIMER_MIN) //если пытаемся уйти за нижний предел { BLOW_TIMER = BLOW_TIMER_MIN; } break; case 2: //время зажигания IGNITION_TIMER = IGNITION_TIMER - 1000; //если пытаемся уйти за нижний предел if (IGNITION_TIMER < IGNITION_TIMER_MIN) { IGNITION_TIMER = IGNITION_TIMER_MIN; } break; case 3: //время задержки остановки по обрыву пламени FLAME_TIMER = FLAME_TIMER - 1000; //если пытаемся уйти за нижний предел if (FLAME_TIMER < FLAME_TIMER_MIN) { FLAME_TIMER = FLAME_TIMER_MIN; } break; case 4: //включение и выключение датчика огня if (FlagFlameOff == true) { FlagFlameOff = false; } else { FlagFlameOff = true; } break; case 22: //отключение датчика огня digitalWrite(AlarmPin, LOW); FlagFlameAlarm = false; FlagFlameOff = true; SettingNumber = 4; SettingsOnLCD(SettingNumber); //отображаем строку с настройками break; } } //************ функция запуска котла *********************** void BoilerStart() { digitalWrite(MotorPin, HIGH); //пускаем продувку BlowTick = 0; BlowCountDown = BLOW_TIMER/1000; //время для обратного отсчета, сек while (BlowTick < BLOW_TIMER) //пока не кончилось время продувки { BlowTick = BlowTick + 1000; lcd.setCursor(0,0); lcd.print("Blowing "); //выводим на экран обратный отсчет времени продувки lcd.print(BlowCountDown); BlowCountDown = BlowCountDown - 1; lcd.print(" sec "); delay(1000); //задержка в 1 сек } digitalWrite(FuelValvePin, HIGH); //открываем топливные клапана digitalWrite(IgnitionPin, HIGH); //зажигаем искру BlowTick = 0; while (BlowTick < IGNITION_TIMER) //пока не кончилось время поджига искры { lcd.setCursor(0,0); lcd.print("Ignition... "); delay(1000); BlowTick = BlowTick + 1000; } digitalWrite(IgnitionPin, LOW); //гасим искру lcd.setCursor(0,0); lcd.print("Boiler Running "); FlagStartMotor = true; } //************ функция остановки котла *********************** void BoilerStop() { digitalWrite(MotorPin, LOW); //все выключаем digitalWrite(FuelValvePin, LOW); digitalWrite(IgnitionPin, LOW); FlagStartMotor = false; lcd.setCursor(0,0); lcd.print("Boiler Stop "); } //************ функция работы котла *********************** void BoilerRun() { if (FlagFlameOff == false) //если датчик огня не отключен { if (digitalRead(FlamePin) == HIGH) //нет огня, индикатор на модуле горит { lcd.setCursor(0,0); lcd.print("Flame Waiting..."); delay(FLAME_TIMER); //некоторое время ждем //проверяем еще раз и если пламени нет if (digitalRead(FlamePin) == HIGH) { BoilerStop(); //останавливаем котел delay(1000); lcd.setCursor(0,0); lcd.print("No Flame... "); delay(1000); FlagFlameAlarm = true; digitalWrite(AlarmPin, HIGH); } else { FlagFlameAlarm = false; lcd.setCursor(0,0); lcd.print("Boiler Running "); lcd.print(" "); } } else { FlagFlameAlarm = false; lcd.setCursor(0,0); lcd.print("Boiler Running "); lcd.print(" "); } } else //если датчик огня отключен { if (digitalRead(FlamePin) == HIGH) //нет огня, индикатор на модуле горит { lcd.setCursor(0,0); lcd.print("Boiler Running "); lcd.print("F "); //выводим букву F (flame) } else { lcd.setCursor(0,0); lcd.print("Boiler Running "); lcd.print(" "); } } } //************ функция выключения котла *********************** void BoilerOff() { digitalWrite(MotorPin, LOW); //все выключаем digitalWrite(FuelValvePin, LOW); digitalWrite(IgnitionPin, LOW); digitalWrite(AlarmPin, LOW); FlagStartMotor = false; FlagFlameAlarm = false; lcd.setCursor(0,0); lcd.print("Boiler OFF "); } //*************** Функция устранения дребезга контактов *********** boolean debounce(boolean last, int ButtonPinDebounce) { boolean current = digitalRead(ButtonPinDebounce); //считываем текущее состояние кнопки if(last != current) //если оно иное, чем предыдущее... { delay(50); //ждем 50 мс current = digitalRead(ButtonPinDebounce); //считываем состояние снова } return current; //возвращаем текущее состояние кнопки }