//Управление горелкой типа Monarch (Weishaupt L7Z) //Вариант неисправности контроллера температуры и //контроллера управления горением (включение продувки и насоса, //зажигания и топливных клапанов) //включение и выключение котла от термодатчика типа К через //термопреобразователь MAX6675 //последовательный запуск продувки, топливных клапанов и искры по таймерам //сигнализация по низкой и высокой температуре //сигнализация по обрыву пламени //настройка таймеров //эти настройки можно записать в энергонезависимую память //на дисплей выводятся следующие символы : // ! - введена поправка температуры //А - сработала сигнализация по температуре (отображается даже если //сигнализация на внешнее реле отключена) //F - сработала сигнализация по обрыву пламени (отображается даже если //сигнализация на внешнее реле отключена) //если заслонка горелки не работает, сразу установите ее на открытие #include <EEPROM.h> //библиотека работы с памятью #include <LiquidCrystal_I2C.h> //библиотеки работы с LCD через I2C #include <Wire.h> #include <max6675.h> //библиотека работы с термодатчиком типа К const int FlameSensorPin = 1; //пин датчика пламени const int thermoSCK = 2; //Указываем к какому порту подключен вывод SCK MAX6675 const int thermoCS = 3; //Указываем к какому порту подключен вывод CS MAX6675 const int thermoSO = 4; //Указываем к какому порту подключен вывод SO MAX6675 //Не забудь шунтировать выход от кнопок резистором 10 КОм const int BtDownPin = 5; //пин кнопки уменьшить. const int BtUpPin = 6; //пин кнопки увеличить const int BtSettingPin = 7; //пин кнопки настроек const int AlarmTemperaturePin = 8; //пин реле сигнализации по температуре const int MotorPin = 9; //пин управления реле продувки const int IgnitionPin = 10; //пин управления искры const int FuelValvePin = 11; //пин управления реле топливных клапанов const int AlarmFlamePin = 12; //пин реле сигнализации по обрыву пламени const int BtBoilerOnPin = 13; //пин кнопки включения/выключения котла //пины модуля I2C LCD-дисплея : //SDA - A4 //SCL - A5 //питание от шилда int Init_Eeprom_Addr = 1000; //номер ячейки в памяти EEPROM (0-1023), куда мы запишем ключ о //самом первом запуске программы //это нужно, так как при первом запуске в памяти нет наших настроек //и нужно загрузить значения по умолчанию byte Init_Key = 235; //это ключ (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; int MIN_TEMPERATURE=29; //нижнее пороговое значение //предел нижней границы, ниже нее опустить нельзя const int MIN_TEMPERATURE_MIN=0; //предел верхней границы, выше нее подниматься нельзя const int MIN_TEMPERATURE_MAX=980; int MAX_TEMPERATURE=33; //верхнее пороговое значение //предел нижней границы, ниже нее опустить нельзя const int MAX_TEMPERATURE_MIN=0; //предел верхней границы, выше нее подниматься нельзя const int MAX_TEMPERATURE_MAX=999; //в программе есть контроль, чтобы в настройках пользователь не задал //MIN_TEMPERATURE больше, чем MAX_TEMPERATURE плюс TEMPERATURE_DIFF const int TEMPERATURE_DIFF=2; int ALARM_MIN=28; //сигнализация по низкой температуре //предел нижней границы, ниже нее опустить нельзя const int ALARM_MIN_MIN=0; //предел верхней границы, выше нее подниматься нельзя const int ALARM_MIN_MAX=990; int ALARM_MAX=40; //сигнализация по высокой температуре //предел нижней границы, ниже нее опустить нельзя const int ALARM_MAX_MIN=0; //предел верхней границы, выше нее подниматься нельзя const int ALARM_MAX_MAX=999; int ALARM_DIFF=0; //диффиренциал сигнализации //предел нижней границы диффиренциала, ниже нее подниматься нельзя const int ALARM_DIFF_MIN=0; //предел верхней границы диффиренциала, выше нее подниматься нельзя const int ALARM_DIFF_MAX=990; //поправка температуры датчика. Она влияет и на вывод мин макс и сигнал int TEMP_CORRECT=0; //предел верхней границы поправки температуры датчика const int TEMP_CORRECT_MAX=100; //предел нижней границы поправки температуры датчика const int TEMP_CORRECT_MIN=-100; long BlowTick = 0; //нужна в отсчете времени продувки int BlowCountDown = 0; //нужна в отображении времени продувки на LCD int SettingNumber = 0; //пункт меню настройки boolean LastButtonSetting = LOW; //предыдущее состояние кнопки Настройки boolean CurrentButtonSetting = LOW; //текущее состояние кнопки Настройки boolean LastButtonDown = LOW; // --- Меньше boolean CurrentButtonDown = LOW; // --- Меньше boolean LastButtonUp = LOW; // --- Больше boolean CurrentButtonUp = 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 FlagFlameAlarm = false; //сработала ли сигнализация по обрыву пламени //нужна при нажатии и удерживании кнопки ВВЕРХ unsigned long ButtonHoldTimerUp = 0; //нужна при нажатии и удерживании кнопки ВНИЗ unsigned long ButtonHoldTimerDown = 0; //по прошествии этого времени кнопка ВВЕРХ или ВНИЗ //перейдут в режим удержания, мс const long ButtonHoldIncTime = 500; boolean FlagUpHold = true; //были ли мы в цикле удержания кнопки ВВЕРХ boolean FlagDownHold = true; //были ли мы в цикле удержания кнопки ВНИЗ boolean FlameControlOn = true; //включена или нет сигнализация по обрыву пламени boolean TemperatureControlOn = true; //включена или нет сигнализация по температуре boolean FlagSettingIn = false; //находимся ли мы в режиме настройки unsigned long currentTime = 0; //нужна, чтобы убрать delay() unsigned long StartMotorTime = 0; //нужна для начала отсчета времени продувки const long intervalTime = 1000; //интервал опроса датчика в мс float Sensor_Temperature = 0; //значение с MAX6675 в град //значение с термодатчика, преобразованное в температуру и с добавленной поправкой float Temp_Correction = 0; //счетчик циклов ожидания запуска программы. //Нужно при запуске для стабилизации температуры и выхода пока идет усреднение int Start_wait = 1; LiquidCrystal_I2C lcd(0x27,16,2); //Задаем адрес и размерность дисплея. //прописываем пины термопреобразователя MAX6675 thermocouple(thermoSCK, thermoCS, thermoSO); byte degree[8] = { //рисуем пользовательский символ градуса B00110, B01001, B01001, B00110, B00000, B00000, B00000, B00000, }; void setup() //************************************************************* { pinMode (BtSettingPin, INPUT); pinMode (BtDownPin, INPUT); pinMode (BtUpPin, INPUT); pinMode (BtBoilerOnPin, INPUT); pinMode (MotorPin, OUTPUT); pinMode (FuelValvePin, OUTPUT); pinMode (IgnitionPin, OUTPUT); pinMode (AlarmTemperaturePin, OUTPUT); pinMode (FlameSensorPin, INPUT); pinMode (AlarmFlamePin, OUTPUT); //если запись в настройки когда-то была, то читаем их if (EEPROM.read(Init_Eeprom_Addr) == Init_Key) { EEPROM.get(0,MIN_TEMPERATURE); //для типа int нужны 2 ячейки памяти EEPROM.get(2,MAX_TEMPERATURE); EEPROM.get(4,ALARM_MIN); EEPROM.get(6,ALARM_MAX); EEPROM.get(8,ALARM_DIFF); EEPROM.get(10,TEMP_CORRECT); EEPROM.get(12,BLOW_TIMER); //для типа long нужны 4 ячейки памяти EEPROM.get(16,IGNITION_TIMER); EEPROM.get(18,FLAME_TIMER); } else //если записи не было, записываем в память значения по умолчанию { EEPROM.put(0,MIN_TEMPERATURE); EEPROM.put(2,MAX_TEMPERATURE); EEPROM.put(4,ALARM_MIN); EEPROM.put(6,ALARM_MAX); EEPROM.put(8,ALARM_DIFF); EEPROM.put(10,TEMP_CORRECT); EEPROM.put(12,BLOW_TIMER); EEPROM.put(16,IGNITION_TIMER); EEPROM.put(18,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); //Пауза lcd.createChar(0, degree); //прописываем пользовательский символ градуса lcd.setCursor(0,0); lcd.print("Boiler OFF "); lcd.setCursor(0,1); lcd.print("Please Wait "); } void loop() //********************************************* { //следующие действия выполняем только если прошло intervalTime if(millis() - currentTime > intervalTime) { currentTime = millis(); //считываем значение с датчика и усредняем Sensor_Temperature = expRunningAverage(thermocouple.readCelsius()); Temp_Correction = Sensor_Temperature + TEMP_CORRECT; //добавляем поправку //это нужно, чтобы усреднение температуры прошло до начала работы системы Start_wait = Start_wait + 1; if(Start_wait>4) { Start_wait = 10; //10 от балды, лишь бы была больше 4 if (FlagSettingIn == false) //если мы не в настройках { lcd.setCursor(0,1); //прописываем вторую строку lcd.print("Temp="); lcd.print(Temp_Correction, 2); lcd.write((byte)0); lcd.print("C "); //-------------------- вывод подсказок на экран ------------------------- if (TEMP_CORRECT != 0) //если мы ввели корректировку температуры { lcd.print("!"); } //если сработала сигнализация по температуре, выведется буква А (alarm) //даже, если сигнализация на реле отключена if (Temp_Correction <= ALARM_MIN || Temp_Correction >= ALARM_MAX) { lcd.print("A "); } //если темп. за пределами сигнализации + диффиренциал if (Temp_Correction > ALARM_MIN+ALARM_DIFF && Temp_Correction < ALARM_MAX-ALARM_DIFF) { lcd.print(" "); } }//if (FlagSettingIn == false) //------------------ пускаем или останавливаем котел ----------------------- if(FlagBoilerOn == true) //если котел включен { if(FlagStartMotor == false) { lcd.setCursor(0,0); lcd.print("Boiler Stop "); } if (FlagFlameAlarm == true && FlameControlOn == true) { //если сработала сигнализация по обрыву пламени //и сигнализация не отключена, выдаем подсказку и блокируем запуск котла //пока не сбросим аварию или не отключим сенсор digitalWrite(AlarmFlamePin, HIGH); //включаем сигнализацию FlagSettingIn = false; SettingNumber = 22; lcd.setCursor(0,1); lcd.print("Flame Trouble "); delay(1000); ButtonPlus(); ButtonMinus(); lcd.setCursor(0,1); lcd.print("Hold UP - RESET "); delay(1000); ButtonPlus(); ButtonMinus(); } else { //если темп. ниже нижнего и котел не работал if (Temp_Correction <= MIN_TEMPERATURE && FlagStartMotor == false) { BoilerStart(); //функция пуска котла } } //if (FlagFlameAlarm == true && FlameControlOn == false) //если темп. выше верхнего и котел работает, выключаем котел if (Temp_Correction >= MAX_TEMPERATURE && FlagStartMotor == true) { BoilerStop(); //функция остановки котла } if (FlagStartMotor == true) //если котел работает { BoilerRun(); } //----- включаем или выключаем сигнализацию по температуре --------- if (TemperatureControlOn == true) //если сигнализация по температуре включена { if (Temp_Correction <= ALARM_MIN || Temp_Correction >= ALARM_MAX) { digitalWrite(AlarmTemperaturePin, HIGH); } if (Temp_Correction > ALARM_MIN+ALARM_DIFF && Temp_Correction < ALARM_MAX-ALARM_DIFF) { digitalWrite(AlarmTemperaturePin, LOW); } } else { digitalWrite(AlarmTemperaturePin, LOW); //если сигнализация отключена } } }//if(Start_wait>4) }//if(millis() - currentTime > intervalTime) //************ Нажата кнопка Включения/выключения котла ******************** //считываем состояние кнопки с устраненным дребезгом CurrentButtonBoiler = debounce(LastButtonBoiler, BtBoilerOnPin); if(CurrentButtonBoiler == HIGH) //если кнопка нажата { if (FlagBoilerLoop == true) //эта переменная нужна для однократного нажатия кнопки, //даже если ее удерживать { FlagBoilerLoop = false; if(FlagBoilerOn == true) { BoilerOff(); } else { FlagBoilerOn = true; } } } else { FlagBoilerLoop = true; } LastButtonBoiler = CurrentButtonBoiler; //обновляем текущее значение кнопки //************ Нажата кнопка Настройки ************************************ //при каждом нажатии кнопки перескакиваем на следующий параметр в очереди. //Дойдя до конца меню, выходим из настроек //считываем состояние кнопки с устраненным дребезгом CurrentButtonSetting = debounce(LastButtonSetting, BtSettingPin); if(CurrentButtonSetting == HIGH) //если кнопка нажата { //эта переменная нужна для однократного нажатия кнопки, //даже если ее удерживать if (FlagSettingLoop == true) { FlagSettingLoop = false; FlagSettingIn = true; //вошли в режим настройки SettingNumber = SettingNumber + 1; if (SettingNumber > 14) { SettingNumber = 0; FlagSettingIn = false; //вышли из режима настройки } } } else { FlagSettingLoop = true; } LastButtonSetting = CurrentButtonSetting; //обновляем текущее значение кнопки if (FlagSettingIn == true) //если мы в настройках { //******************* Нажата кнопка Увеличить ************************** ButtonPlus(); //функция реакции на нажатие кнопки Увеличить //******************* Нажата кнопка Уменьшить ************************** ButtonMinus(); //функция реакции на нажатие кнопки Уменьшить SettingsOnLCD(SettingNumber); //отображаем строку с настройками } } //void loop() //********************** ФУНКЦИИ ********************************* //*********** функция отображения настроек на LCD ************* void SettingsOnLCD(int SettingItem) { lcd.setCursor(0,1); switch (SettingItem) { case 1: //настройка температуры включения котла lcd.print("1 MinTemp "); lcd.print(MIN_TEMPERATURE); lcd.write((byte)0); lcd.print("C "); break; case 2: //настройка температуры выключения котла lcd.print("2 MaxTemp "); lcd.print(MAX_TEMPERATURE); lcd.write((byte)0); lcd.print("C "); break; case 3: //настройка сигнализации минимальной температуры lcd.print("3 AlarMin "); lcd.print(ALARM_MIN); lcd.write((byte)0); lcd.print("C "); break; case 4: //настройка сигнализации максимальной температуры lcd.print("4 AlarMax "); lcd.print(ALARM_MAX); lcd.write((byte)0); lcd.print("C "); break; case 5: //настройка диффиренциала температуры сигнализации lcd.print("5 AlarmDiff "); lcd.print(ALARM_DIFF); lcd.write((byte)0); lcd.print("C "); break; case 6: //настройка поправки температуры lcd.print("6 TempCor "); lcd.print(TEMP_CORRECT); lcd.write((byte)0); lcd.print("C "); break; case 7: //время продувки lcd.print("7 Blow Time "); lcd.print(BLOW_TIMER/1000); lcd.print(" s "); break; case 8: //время зажигания lcd.print("8 Igni Time "); lcd.print(IGNITION_TIMER/1000); lcd.print(" s "); break; case 9: //настройка времени зажигания lcd.print("9 FlameTime "); lcd.print(FLAME_TIMER/1000); lcd.print(" s "); break; case 10: //включение и выключение сигнализации по температуре if (TemperatureControlOn == true) { lcd.print("10 Alarm ON "); } else { lcd.print("10 Alarm OFF "); } break; case 11: //включение и выключение сигнализации по обрыву пламени if (FlameControlOn == true) { lcd.print("11 AlarFlame ON "); } else { lcd.print("11 AlarFlame OFF"); } break; case 12: //запись настроек в EEPROM lcd.print("12 SaveSet. (UP) "); break; case 13: //чтение настроек из EEPROM lcd.print("13 ReadSet. (UP) "); break; case 14: //загрузка значений по умолчанию lcd.print("14 Default (UP) "); break; } } //********* Функция кнопки Больше, Увеличить, Плюс и т.п. ************* void ButtonPlus() { CurrentButtonUp = debounce(LastButtonUp, BtUpPin); if(CurrentButtonUp == HIGH) //кнопка нажата { if (SettingNumber >= 1 && SettingNumber <= 4 ) //пункты настроек, где нужен разгон параметра в режиме удержания { if(FlagUpHold == true) //отсечка времени удержания кнопки { ButtonHoldTimerUp = millis(); FlagUpHold = false; } if(millis() - ButtonHoldTimerUp > ButtonHoldIncTime) //сначала однократное нажатие, но если держать кнопку более //ButtonHoldIncTime, она переходит в режим удержания { ButtonPlusAction(SettingNumber, true); } if(FlagUpLoop == true) //однократное нажатие, даже если удерживать кнопку, //пока не прошло ButtonHoldIncTime { FlagUpLoop = false; ButtonPlusAction(SettingNumber, false); } } else //пункты настроек, где нужно только однократное нажатие кнопки { if(FlagUpLoop == true) { FlagUpLoop = false; ButtonPlusAction(SettingNumber, false); } } } else //кнопка отжата { FlagUpLoop = true; FlagUpHold = true; } LastButtonUp = CurrentButtonUp; //обновляем текущее значение кнопки } //******** Функция изменения параметров при нажатии кнопки Больше ********** void ButtonPlusAction(int SettingItem, boolean IncTrigger) { switch (SettingItem) { case 1: //минимальная температура MIN_TEMPERATURE = MIN_TEMPERATURE + Inc_Val_Calc(IncTrigger); //если пытаемся уйти за верхний предел if (MIN_TEMPERATURE > MIN_TEMPERATURE_MAX) { MIN_TEMPERATURE = MIN_TEMPERATURE_MAX; lcd.setCursor(0,1); lcd.print("MinTempLimi="); //выводим подсказку lcd.print(MIN_TEMPERATURE_MAX); lcd.print(" "); delay(2000); } //если диффиренциал меньше TEMPERATURE_DIFF if (MAX_TEMPERATURE - MIN_TEMPERATURE < TEMPERATURE_DIFF) { MIN_TEMPERATURE = MIN_TEMPERATURE - Inc_Val_Calc(IncTrigger); lcd.setCursor(0,1); lcd.print("MAX-MIN MUST >="); //выводим подсказку lcd.print(TEMPERATURE_DIFF); delay(2000); } break; case 2: //максимальная температура MAX_TEMPERATURE = MAX_TEMPERATURE + Inc_Val_Calc(IncTrigger); //если пытаемся уйти за верхний предел if (MAX_TEMPERATURE > MAX_TEMPERATURE_MAX) { MAX_TEMPERATURE = MAX_TEMPERATURE_MAX; lcd.setCursor(0,1); lcd.print("MaxTempLimi="); //выводим подсказку lcd.print(MAX_TEMPERATURE_MAX); lcd.print(" "); delay(2000); } break; case 3: //сигнализация минимальной температуры ALARM_MIN = ALARM_MIN + Inc_Val_Calc(IncTrigger); //если пытаемся уйти за верхний предел if (ALARM_MIN > ALARM_MIN_MAX) { ALARM_MIN = ALARM_MIN_MAX; lcd.setCursor(0,1); lcd.print("AlaMinLimit="); //выводим подсказку lcd.print(ALARM_MIN_MAX); lcd.print(" "); delay(2000); } break; case 4: //сигнализация максимальной температуры ALARM_MAX = ALARM_MAX + Inc_Val_Calc(IncTrigger); //если пытаемся уйти за верхний предел if (ALARM_MAX > ALARM_MAX_MAX) { ALARM_MAX = ALARM_MAX_MAX; lcd.setCursor(0,1); lcd.print("AlaMaxLimit="); //выводим подсказку lcd.print(ALARM_MAX_MAX); lcd.print(" "); delay(2000); } break; case 5: //диффиренциал температуры сигнализации ALARM_DIFF = ALARM_DIFF + 1; //если пытаемся уйти за верхний предел if (ALARM_DIFF > ALARM_DIFF_MAX) { ALARM_DIFF = ALARM_DIFF_MAX; lcd.setCursor(0,1); lcd.print("AlaDiffLimit="); //выводим подсказку lcd.print(ALARM_DIFF_MAX); lcd.print(" "); delay(2000); } break; case 6: //поправка температуры TEMP_CORRECT = TEMP_CORRECT + 1; //если пытаемся уйти за верхний предел if (TEMP_CORRECT > TEMP_CORRECT_MAX) { TEMP_CORRECT = TEMP_CORRECT_MAX; lcd.setCursor(0,1); lcd.print("TempCorLimi="); //выводим подсказку lcd.print(TEMP_CORRECT_MAX); lcd.print(" "); delay(2000); } break; case 7: //время продувки BLOW_TIMER = BLOW_TIMER + 1000; //если пытаемся уйти за верхний предел if (BLOW_TIMER > BLOW_TIMER_MAX) { BLOW_TIMER = BLOW_TIMER_MAX; lcd.setCursor(0,1); lcd.print("MaxBlowTime="); //выводим подсказку lcd.print(BLOW_TIMER_MAX/1000); lcd.print(" s "); delay(2000); } break; case 8: //время зажигания IGNITION_TIMER = IGNITION_TIMER + 1000; //если пытаемся уйти за верхний предел if (IGNITION_TIMER > IGNITION_TIMER_MAX) { IGNITION_TIMER = IGNITION_TIMER_MAX; lcd.setCursor(0,1); lcd.print("MaxIgniTime="); //выводим подсказку lcd.print(IGNITION_TIMER_MAX/1000); lcd.print(" s "); delay(2000); } break; case 9: //настройка времени ожидания обрыва пламени FLAME_TIMER = FLAME_TIMER + 1000; //если пытаемся уйти за верхний предел if (FLAME_TIMER > FLAME_TIMER_MAX) { FLAME_TIMER = FLAME_TIMER_MAX; lcd.setCursor(0,1); lcd.print("MaxFlaTime="); //выводим подсказку lcd.print(FLAME_TIMER_MAX/1000); lcd.print(" s "); delay(2000); } break; case 10: //включение и выключение сигнализации по температуре if (TemperatureControlOn == true) { TemperatureControlOn = false; } else { TemperatureControlOn = true; } break; case 11: //включение и выключение сигнализации по обрыву пламени if (FlameControlOn == true) { FlameControlOn = false; } else { FlameControlOn = true; } break; case 12: //запись настроек в EEPROM EEPROM.put(0,MIN_TEMPERATURE); EEPROM.put(2,MAX_TEMPERATURE); EEPROM.put(4,ALARM_MIN); EEPROM.put(6,ALARM_MAX); EEPROM.put(8,ALARM_DIFF); EEPROM.put(10,TEMP_CORRECT); EEPROM.put(12,BLOW_TIMER); EEPROM.put(16,IGNITION_TIMER); EEPROM.put(18,FLAME_TIMER); //ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); lcd.setCursor(0,1); lcd.print("Saving... OK "); delay(2000); break; case 13: //чтение настроек из EEPROM EEPROM.get(0,MIN_TEMPERATURE); //для типа int нужны 2 ячейки памяти EEPROM.get(2,MAX_TEMPERATURE); EEPROM.get(4,ALARM_MIN); EEPROM.get(6,ALARM_MAX); EEPROM.get(8,ALARM_DIFF); EEPROM.get(10,TEMP_CORRECT); EEPROM.get(12,BLOW_TIMER); //для типа long нужны 4 ячейки памяти EEPROM.get(16,IGNITION_TIMER); EEPROM.get(18,FLAME_TIMER); lcd.setCursor(0,1); lcd.print("Reading... OK "); delay(2000); break; case 14: //загрузка значений по умолчанию. Берем из начала скетча MIN_TEMPERATURE = 29; MAX_TEMPERATURE = 33; ALARM_MIN = 28; ALARM_MAX = 40; ALARM_DIFF = 0; TEMP_CORRECT = 0; 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(AlarmFlamePin, LOW); SettingNumber = 0; FlagSettingIn = false; break; } } //********* Функция кнопки Меньше, Уменьшить, Минус или т.п. ************* void ButtonMinus() { CurrentButtonDown = debounce(LastButtonDown, BtDownPin); if(CurrentButtonDown == HIGH) //кнопка нажата { if (SettingNumber >= 1 && SettingNumber <= 4 ) //пункты настроек, где нужен разгон параметра в режиме удержания { if(FlagDownHold == true) //отсечка времени удержания кнопки { ButtonHoldTimerDown = millis(); FlagDownHold = false; } if(millis() - ButtonHoldTimerDown > ButtonHoldIncTime) //сначала однократное нажатие, но если держать кнопку более //ButtonHoldIncTime, она переходит в режим удержания { ButtonMinusAction(SettingNumber, true); } if(FlagDownLoop == true) //однократное нажатие, даже если удерживать кнопку, //пока не прошло ButtonHoldIncTime { FlagDownLoop = false; ButtonMinusAction(SettingNumber, false); } } else //пункты настроек, где нужно только однократное нажатие кнопки { if(FlagDownLoop == true) { FlagDownLoop = false; ButtonMinusAction(SettingNumber, false); } } } else //кнопка отжата { FlagDownLoop = true; FlagDownHold = true; } LastButtonDown = CurrentButtonDown; //обновляем текущее значение кнопки } //******** Функция изменения параметров при нажатии кнопки Меньше ********** void ButtonMinusAction(int SettingItem, boolean IncTrigger) { switch (SettingItem) { case 1: //минимальная температура MIN_TEMPERATURE = MIN_TEMPERATURE - Inc_Val_Calc(IncTrigger); //если пытаемся уйти за нижний предел if (MIN_TEMPERATURE < MIN_TEMPERATURE_MIN) { MIN_TEMPERATURE = MIN_TEMPERATURE_MIN; lcd.setCursor(0,1); lcd.print("MinTempLimit="); //выводим подсказку lcd.print(MIN_TEMPERATURE_MIN); lcd.print(" "); delay(2000); } break; case 2: //максимальная температура MAX_TEMPERATURE = MAX_TEMPERATURE - Inc_Val_Calc(IncTrigger); //если пытаемся уйти за нижний предел if (MAX_TEMPERATURE < MAX_TEMPERATURE_MIN) { MAX_TEMPERATURE = MAX_TEMPERATURE_MIN; lcd.setCursor(0,1); lcd.print("MaxTempLimi="); //выводим подсказку lcd.print(MAX_TEMPERATURE_MIN); lcd.print(" "); delay(2000); } //если диффиренциал меньше TEMPERATURE_DIFF if (MAX_TEMPERATURE - MIN_TEMPERATURE < TEMPERATURE_DIFF) { MAX_TEMPERATURE = MAX_TEMPERATURE + Inc_Val_Calc(IncTrigger); lcd.setCursor(0,1); lcd.print("MAX-MIN MUST >="); //выводим подсказку lcd.print(TEMPERATURE_DIFF); delay(2000); } break; case 3: //сигнализация минимальной температуры ALARM_MIN = ALARM_MIN - Inc_Val_Calc(IncTrigger); //если пытаемся уйти за нижний предел if (ALARM_MIN < ALARM_MIN_MIN) { ALARM_MIN = ALARM_MIN_MIN; lcd.setCursor(0,1); lcd.print("AlaMinLimit="); //выводим подсказку lcd.print(ALARM_MIN_MIN); lcd.print(" "); delay(2000); } break; case 4: //сигнализация максимальной температуры ALARM_MAX = ALARM_MAX - Inc_Val_Calc(IncTrigger); //если пытаемся уйти за нижний предел if (ALARM_MAX < ALARM_MAX_MIN) { ALARM_MAX = ALARM_MAX_MIN; lcd.setCursor(0,1); lcd.print("AlaMaxLimit="); //выводим подсказку lcd.print(ALARM_MAX_MIN); lcd.print(" "); delay(2000); } break; case 5: //диффиренциал температуры сигнализации ALARM_DIFF = ALARM_DIFF - 1; //если пытаемся уйти за нижний предел if (ALARM_DIFF < ALARM_DIFF_MIN) { ALARM_DIFF = ALARM_DIFF_MIN; lcd.setCursor(0,1); lcd.print("AlaDiffLimit="); //выводим подсказку lcd.print(ALARM_DIFF_MIN); lcd.print(" "); delay(2000); } break; case 6: //поправка температуры TEMP_CORRECT = TEMP_CORRECT - 1; //если пытаемся уйти за нижний предел if (TEMP_CORRECT < TEMP_CORRECT_MIN) { TEMP_CORRECT = TEMP_CORRECT_MIN; lcd.setCursor(0,1); lcd.print("TempCorLimi="); //выводим подсказку lcd.print(TEMP_CORRECT_MIN); lcd.print(" "); delay(2000); } break; case 7: //время продувки BLOW_TIMER = BLOW_TIMER - 1000; //если пытаемся уйти за нижний предел if (BLOW_TIMER < BLOW_TIMER_MIN) { BLOW_TIMER = BLOW_TIMER_MIN; lcd.setCursor(0,1); lcd.print("MinBlowTime="); //выводим подсказку lcd.print(BLOW_TIMER_MIN/1000); lcd.print(" s "); delay(2000); } break; case 8: //время зажигания IGNITION_TIMER = IGNITION_TIMER - 1000; //если пытаемся уйти за нижний предел if (IGNITION_TIMER < IGNITION_TIMER_MIN) { IGNITION_TIMER = IGNITION_TIMER_MIN; lcd.setCursor(0,1); lcd.print("MinIgniTime="); //выводим подсказку lcd.print(IGNITION_TIMER_MIN/1000); lcd.print(" s "); delay(2000); } break; case 9: //настройка времени ожидания обрыва пламени FLAME_TIMER = FLAME_TIMER - 1000; //если пытаемся уйти за нижний предел if (FLAME_TIMER < FLAME_TIMER_MIN) { FLAME_TIMER = FLAME_TIMER_MIN; lcd.setCursor(0,1); lcd.print("MinFlaTime="); //выводим подсказку lcd.print(FLAME_TIMER_MIN/1000); lcd.print(" s "); delay(2000); } break; case 10: //включение и выключение сигнализации по температуре if (TemperatureControlOn == true) { TemperatureControlOn = false; } else { TemperatureControlOn = true; } break; case 11: //включение и выключение сигнализации по обрыву пламени if (FlameControlOn == true) { FlameControlOn = false; } else { FlameControlOn = true; } break; } } //*************** Функция устранения дребезга контактов *********** boolean debounce(boolean last, int ButtonPinDebounce) { boolean current = digitalRead(ButtonPinDebounce); //считываем текущее состояние кнопки if(last != current) //если оно иное, чем предыдущее... { delay(50); //ждем 50 мс current = digitalRead(ButtonPinDebounce); //считываем состояние снова } return current; //возвращаем текущее состояние кнопки } //************* функция усреднения в точке замера *************** float expRunningAverage(float newVal) { static float filVal = 0; float k; //резкость фильтра зависит от процента отклонения от предыдущего значения if (abs((newVal - filVal)/filVal) > 0.05) k = 0.9; else k = 0.1; filVal += (newVal - filVal) * k; return filVal; } //*************** функция запуска котла ************************ 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 "); delay(1000); //немного ждем перед выводом температуры FlagStartMotor = true; } //*************** функция остановки котла ************************ void BoilerStop() { digitalWrite(MotorPin, LOW); //останавливаем котел digitalWrite(FuelValvePin, LOW); digitalWrite(IgnitionPin, LOW); if (FlagStartMotor == true) { lcd.setCursor(0,0); lcd.print("Boiler Stop "); delay(1000); //немного ждем перед выводом } FlagStartMotor = false; } //************ функция работы котла *********************** void BoilerRun() { if (FlameControlOn == true) //если датчик огня включен { if (digitalRead(FlameSensorPin) == HIGH) //нет огня, индикатор на модуле горит { lcd.setCursor(0,0); lcd.print("Flame Waiting..."); delay(FLAME_TIMER); //некоторое время ждем if (digitalRead(FlameSensorPin) == HIGH) //проверяем еще раз и если пламени нет { BoilerStop(); //останавливаем котел delay(1000); lcd.setCursor(0,0); lcd.print("No Flame... "); delay(1000); FlagFlameAlarm = true; digitalWrite(AlarmFlamePin, HIGH); } else { FlagFlameAlarm = false; lcd.setCursor(0,0); lcd.print("Boiler Running "); } } else { FlagFlameAlarm = false; lcd.setCursor(0,0); lcd.print("Boiler Running "); } } else //если датчик огня отключен { if (digitalRead(FlameSensorPin) == HIGH) //нет огня, индикатор на модуле горит { lcd.setCursor(0,0); lcd.print("Boiler Running F "); //выводим букву F (flame) FlagFlameAlarm = true; } else { lcd.setCursor(0,0); lcd.print("Boiler Running "); FlagFlameAlarm = false; } } } //************ функция выключения котла *********************** void BoilerOff() { digitalWrite(MotorPin, LOW); //все выключаем digitalWrite(FuelValvePin, LOW); digitalWrite(IgnitionPin, LOW); digitalWrite(AlarmTemperaturePin, LOW); digitalWrite(AlarmFlamePin, LOW); FlagStartMotor = false; FlagFlameAlarm = false; FlagBoilerOn = false; FlagSettingIn = false; SettingNumber = 0; lcd.setCursor(0,0); lcd.print("Boiler OFF "); } //*************** Функция разгона приращения параметра ************ int Inc_Val_Calc(boolean IncTrigger) { static int count_inc = 0; //счетчик итераций if (IncTrigger == true) //пришел сигнал на разгон { count_inc = count_inc + 1; //играя этими числами, вы можете подстраивать разгон параметра под себя if (count_inc > 0 && count_inc <= 30) { return 1; } if (count_inc > 30 && count_inc <= 60) { return 2; } if (count_inc > 60) { return 5; } } else { count_inc = 0; return 1; //приращение по умолчанию } }