//Температурный контроллер типа ON/OFF от термодатчика типа Pt100 //Возможности : //включение и выключение подогревателя от термодатчика типа Pt100 //есть ручной режим вкючения и выключения //сигнализация по низкой и высокой температуре //настройка параметров //эти настройки можно записать в энергонезависимую память //на дисплей выводятся следующие символы : ! - введена поправка температуры //дополнительно есть управление с ИК-пульта //опорное напряжение 1,1 В, поэтому аналоговые входы питай от Ардуино //(убери перемычку на шилде) #include <EEPROM.h> //библиотека работы с памятью #include <LiquidCrystal_I2C.h> //библиотеки работы с LCD через I2C #include <Wire.h> #include "IRremote.h" //библиотека работы с ИК-приемником const int IRPin = 3 ; //пин, к которому подключен ИК-приемник const int BtDownPin = 5 ; //пин кнопки уменьшить. const int BtUpPin = 6 ; //пин кнопки увеличить const int BtSettingPin = 7 ; //пин кнопки настроек const int AlarmPin = 8 ; //пин управления реле сигнализации const int HeaterPin = 9 ; //пин управления реле нагревателя const byte Pt100Pin = A0 ; //аналоговый пин термодатчика Pt100 IRrecv irrecv(IRPin); // указываем вывод, к которому подключен ИК-приемник decode_results results; //пины модуля I2C LCD-дисплея : //SDA - A4 //SCL - A5 //питание от шилда int Init_Eeprom_Addr = 1000; //номер ячейки в памяти EEPROM (0-1023), куда мы запишем ключ о //самом первом запуске программы //это нужно, так как при первом запуске в памяти нет наших настроек //и нужно загрузить значения по умолчанию byte Init_Key = 244; //это ключ (0-255). //Если при пуске скетча данные из ячейки Init_Eeprom не совпадают //с Init_Key, вводим в настройки значения по умолчанию, //иначе берем настройки из памяти //нижнее пороговое значение 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; //в программе есть контроль, //чтобы в настройках пользователь не задал ALARM_MIN //больше, чем ALARM_MAX плюс ALARM_STOP const int ALARM_STOP=2; //диффиренциал сигнализации 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; int SettingNumber = 0; //пункт меню настройки boolean LastButtonSetting = LOW; //предыдущее состояние кнопки Настройки boolean CurrentButtonSetting = LOW; //текущее состояние кнопки Настройки boolean LastButtonDown = LOW; //--- Меньше boolean CurrentButtonDown = LOW; //--- Меньше boolean LastButtonUp = LOW; //--- Больше boolean CurrentButtonUp = LOW; //--- Больше boolean FlagSettingLoop = true; //были ли мы в цикле нажатия кнопки Настройки boolean FlagUpLoop = true; //были ли мы в цикле нажатия кнопки Больше boolean FlagDownLoop = true; //были ли мы в цикле нажатия кнопки Меньше //нужна при нажатии и удерживании кнопки ВВЕРХ unsigned long ButtonHoldTimerUp = 0; //нужна при нажатии и удерживании кнопки ВНИЗ unsigned long ButtonHoldTimerDown = 0; //по прошествии этого времени кнопка ВВЕРХ или ВНИЗ //перейдет в режим удержания, мс const long ButtonHoldIncTime = 500; boolean FlagUpHold = true; //были ли мы в цикле удержания кнопки ВВЕРХ boolean FlagDownHold = true; //были ли мы в цикле удержания кнопки ВНИЗ boolean AlarmControl = true; //включена или нет сигнализация //автоматический или ручной режим (true-автоматический) boolean AutoControl = true; //включение-выключение в ручном режиме boolean HeaterManualOn = false; //включение-выключение в автоматическом режиме boolean FlagAutoOn = false; boolean FlagSettingIn = false; //находимся ли мы в режиме настройки unsigned long currentTime = 0; //нужна, чтобы убрать delay() const long intervalTime = 1000; //интервал опроса датчика в мс float Pt100_Val = 0; //значение с термодатчика Pt100 (0...1023) //значение с термодатчика Pt100, преобразованное в температуру float Pt100_Temp = 0; //значение с термодатчика Pt100, //преобразованное в температуру и с добавленной поправкой float Pt100_Temp_Correction = 0; //цифры ниже взяты по результатам калибровки датчика Pt100 //с АлиЭкспресс при 0 град и 100 град //если измеряемая температура будет выше 100 град, //программа проинтерполирует ее float Min_Volt = 39; //верхняя граница вольтажа с аналогового порта переведенный в цифровой вид float Max_Volt = 546; //соответствующая нижней границе температура float Min_Temp = 0; //соответствующая верхней границе температура float Max_Temp = 100; //счетчик циклов ожидания запуска программы. //Нужно при запуске для стабилизации температуры и выхода пока идет усреднение int Start_wait = 1; LiquidCrystal_I2C lcd(0x27,16,2); //Задаем адрес и размерность дисплея. byte degree[8] = { //рисуем пользовательский символ градуса B00110, B01001, B01001, B00110, B00000, B00000, B00000, B00000, }; void setup() //**************************************************************** { analogReference(INTERNAL); //устанавливаем внутреннее опорное напряжение 1,1В pinMode (BtSettingPin, INPUT); //прописываем режим пинов pinMode (BtDownPin, INPUT); pinMode (BtUpPin, INPUT); pinMode (HeaterPin, OUTPUT); pinMode (AlarmPin, OUTPUT); pinMode (Pt100Pin, INPUT); //выставляем скорость COM порта, включить, если нужно посмотреть коды кнопок ИК-пульта //Serial.begin(9600); irrecv.enableIRIn(); //запускаем прием //если запись в настройки когда-то была, то читаем их 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(14,Min_Volt); //для типа float нужны 4 ячейки памяти EEPROM.get(18,Min_Temp); EEPROM.get(22,Max_Volt); EEPROM.get(26,Max_Temp); } 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(14,Min_Volt); //для типа float нужны 4 ячейки памяти EEPROM.put(18,Min_Temp); EEPROM.put(22,Max_Volt); EEPROM.put(26,Max_Temp); //и ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); } lcd.init(); //Инициализация lcd lcd.backlight(); //Включаем подсветку lcd.setCursor(0,0); //рисуем первую строчку lcd.print(" Temperature "); lcd.setCursor(0,1); //рисуем вторую строчку lcd.print(" Auto Control "); delay(2000); //Пауза в мс. lcd.createChar(0, degree); //прописываем пользовательский символ градуса lcd.setCursor(0,0); lcd.print(" Please Wait "); lcd.setCursor(0,1); lcd.print(" "); } void loop() //************************************************************ { //следующие действия выполняем только если прошло intervalTime if(millis() - currentTime > intervalTime) { currentTime = millis(); //считываем значение с датчика и усредняем Pt100_Val = expRunningAverage(analogRead (Pt100Pin)); //переводим в температуру Pt100_Temp = ((Pt100_Val - Min_Volt) * (Max_Temp - Min_Temp) / (Max_Volt - Min_Volt)) + Min_Temp; //добавляем поправку Pt100_Temp_Correction = Pt100_Temp + 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(Pt100_Temp_Correction, 2); lcd.write((byte)0); lcd.print("C"); lcd.print(" "); //если мы ввели корректировку показания датчика, //за символом градуса будет выводиться восклицательный знак if (TEMP_CORRECT != 0) { lcd.print("! "); } else { lcd.print(" "); } } if (AutoControl == true) //если мы в автоматическом режиме { //включаем или выключаем подогреватель по температуре if (Pt100_Temp_Correction <= MIN_TEMPERATURE) //если темп. ниже нижнего { FlagAutoOn = true; } if (Pt100_Temp_Correction >= MAX_TEMPERATURE) //если темп. выше верхнего { FlagAutoOn = false; } //это нужно, чтобы убрать неопределенность в диапазоне между min и max if(FlagAutoOn == true) { digitalWrite(HeaterPin, HIGH); lcd.setCursor(0,0); lcd.print("Auto ON "); } else { digitalWrite(HeaterPin, LOW); lcd.setCursor(0,0); lcd.print("Auto OFF "); } } else //если мы в ручном режиме { if (HeaterManualOn == true) //включаем подогреватель { lcd.setCursor(0,0); lcd.print("Manual ON "); digitalWrite(HeaterPin, HIGH); } else //выключаем подогреватель { lcd.setCursor(0,0); lcd.print("Manual OFF "); digitalWrite(HeaterPin, LOW); } } //если сработала сигнализация, выведется предупреждение //даже, если сигнализация на реле отключена if (Pt100_Temp_Correction <= ALARM_MIN) //если темп. ниже сигнализации { lcd.print("TLow "); } if (Pt100_Temp_Correction >= ALARM_MAX) //если темп. выше сигнализации { lcd.print("THigh "); } //если темп. за пределами сигнализации. if (Pt100_Temp_Correction > ALARM_MIN+ALARM_DIFF && Pt100_Temp_Correction < ALARM_MAX-ALARM_DIFF) { lcd.print(" "); } //включаем или выключаем реле сигнализации if (AlarmControl == true) //если сигнализация не отключена { if (Pt100_Temp_Correction <= ALARM_MIN || Pt100_Temp_Correction >= ALARM_MAX) //если темп. вышла за уставки { digitalWrite(AlarmPin, HIGH); } if (Pt100_Temp_Correction > ALARM_MIN+ALARM_DIFF && Pt100_Temp_Correction < ALARM_MAX-ALARM_DIFF) { digitalWrite(AlarmPin, LOW); } } else { digitalWrite(AlarmPin, LOW); //если сигнализация отключена } } //if(Start_wait>4) } //if(millis() - currentTime > intervalTime) if ( irrecv.decode( &results )) // если пришли данные с ИК-пульта { //печатаем данные в монитор порта, включить, если нужно посмотреть коды кнопок ИК-пульта //Serial.println( results.value, HEX ); ReadIR(); //функция обработки данных с ИК-пульта } //************ Нажата кнопка Настройки ************************************ //при каждом нажатии кнопки перескакиваем на следующий параметр в очереди. //Дойдя до конца меню, выходим из настроек //считываем состояние кнопки с устраненным дребезгом CurrentButtonSetting = debounce(LastButtonSetting, BtSettingPin); if(CurrentButtonSetting == HIGH) //если кнопка нажата { //эта переменная нужна для однократного нажатия кнопки, даже если ее удерживать if (FlagSettingLoop == true) { FlagSettingLoop = false; FlagSettingIn = true; //вошли в режим настройки SettingNumber = SettingNumber + 1; if (SettingNumber > 16) { SettingNumber = 0; FlagSettingIn = false; //вышли из режима настройки } } } else { FlagSettingLoop = true; } LastButtonSetting = CurrentButtonSetting; //обновляем текущее значение кнопки if (FlagSettingIn == true) //если мы в настройках { //******************* Нажата кнопка Увеличить ************************** CurrentButtonUp = debounce(LastButtonUp, BtUpPin); ButtonPlus(CurrentButtonUp); //функция реакции на нажатие кнопки Увеличить LastButtonUp = CurrentButtonUp; //обновляем текущее значение кнопки //******************* Нажата кнопка Уменьшить ************************** CurrentButtonDown = debounce(LastButtonDown, BtDownPin); ButtonMinus(CurrentButtonDown); //функция реакции на нажатие кнопки Уменьшить LastButtonDown = CurrentButtonDown; //обновляем текущее значение кнопки SettingsOnLCD(SettingNumber); //отображаем строку с настройками } } //void loop() //********************** ФУНКЦИИ ********************************* //*********** Функция отображения настроек на LCD ************* void SettingsOnLCD(int SettingItem) { lcd.setCursor(0,1); switch (SettingItem) { case 1: //настройка температуры включения подогрева lcd.print("MinTemp "); lcd.print(MIN_TEMPERATURE); lcd.write((byte)0); lcd.print("C "); break; case 2: //настройка температуры выключения подогрева lcd.print("MaxTemp "); lcd.print(MAX_TEMPERATURE); lcd.write((byte)0); lcd.print("C "); break; case 3: //настройка сигнализации минимальной температуры lcd.print("AlarMin "); lcd.print(ALARM_MIN); lcd.write((byte)0); lcd.print("C "); break; case 4: //настройка сигнализации максимальной температуры lcd.print("AlarMax "); lcd.print(ALARM_MAX); lcd.write((byte)0); lcd.print("C "); break; case 5: //настройка диффиренциала температуры сигнализации lcd.print("AlarmDiff "); lcd.print(ALARM_DIFF); lcd.write((byte)0); lcd.print("C "); break; case 6: //настройка поправки температуры lcd.print("TempCor "); lcd.print(TEMP_CORRECT); lcd.write((byte)0); lcd.print("C "); break; //данные для калибровки датчика Pt100 //нижняя граница вольтажа с аналогового порта переведенный в цифровой вид case 7: lcd.print("Min_Volt "); lcd.print(Min_Volt, 0); lcd.print(" "); break; case 8: //соответствующая нижней границе температура lcd.print("MinPt "); lcd.print(Min_Temp, 1); lcd.write((byte)0); lcd.print("C "); break; //верхняя граница вольтажа с аналогового порта переведенный в цифровой вид case 9: lcd.print("Max_Volt "); lcd.print(Max_Volt, 0); lcd.print(" "); break; case 10: //соответствующая верхней границе температура lcd.print("MaxPt "); lcd.print(Max_Temp, 1); lcd.write((byte)0); lcd.print("C "); break; case 11: //включение и выключение сигнализации if (AlarmControl == true) { lcd.print("Alarm ON "); } else { lcd.print("Alarm OFF "); } break; case 12: //переключение между автоматическим и ручным режимами if (AutoControl == true) { lcd.print("Aut/Man : Auto "); HeaterManualOn = false; } else { lcd.print("Aut/Man : Manu "); } break; case 13: //включение и выключение подогревателя в ручном режиме if (HeaterManualOn == true) { lcd.print("Heater ON "); } else { lcd.print("Heater OFF "); } break; case 14: //чтение настроек из EEPROM lcd.print("ReadSet. (UP) "); break; case 15: //запись настроек в EEPROM lcd.print("SaveSet. (UP) "); break; case 16: //загрузка значений по умолчанию lcd.print("Default (UP) "); break; } } //********* Функция кнопки Больше, Увеличить, Плюс и т.п. ************* void ButtonPlus(boolean BtState) //BtState - состояние кнопки (нажата-отжата) { if(BtState == HIGH) //кнопка нажата { if (SettingNumber >= 1 && SettingNumber <= 10 ) //пункты настроек, где нужен разгон параметра в режиме удержания { 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; } } //******** Функция изменения параметров при нажатии кнопки Больше ********** void ButtonPlusAction(int SettingItem, boolean IncTrigger) { switch (SettingItem) { case 1: //настройка температуры включения подогрева MIN_TEMPERATURE = MIN_TEMPERATURE + Inc_Val_Calc(IncTrigger,1); //если пытаемся уйти за верхний предел 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,1); 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,1); //если пытаемся уйти за верхний предел 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,1); 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); } //если диффиренциал меньше ALARM_STOP if (ALARM_MAX - ALARM_MIN < ALARM_STOP) { ALARM_MIN = ALARM_MIN - Inc_Val_Calc(IncTrigger,1); lcd.setCursor(0,1); lcd.print("MAX-MIN MUST >="); //выводим подсказку lcd.print(ALARM_STOP); delay(2000); } break; case 4: //настройка сигнализации максимальной температуры ALARM_MAX = ALARM_MAX + Inc_Val_Calc(IncTrigger,1); //если пытаемся уйти за верхний предел 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 + Inc_Val_Calc(IncTrigger,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 + Inc_Val_Calc(IncTrigger,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; //данные для калибровки датчика Pt100 //нижняя граница вольтажа с аналогового порта переведенный в цифровой вид case 7: Min_Volt = Min_Volt + Inc_Val_Calc(IncTrigger,1); if (Min_Volt >= Max_Volt) { Min_Volt = Min_Volt - Inc_Val_Calc(IncTrigger,1); lcd.setCursor(0,1); lcd.print("Min must < Max "); //выводим подсказку delay(2000); } break; case 8: //соответствующая нижней границе температура Min_Temp = Min_Temp + Inc_Val_Calc(IncTrigger,2); if (Min_Temp > 1000) { Min_Temp = 1000; } break; //верхняя граница вольтажа с аналогового порта переведенный в цифровой вид case 9: Max_Volt = Max_Volt + Inc_Val_Calc(IncTrigger,1); if (Max_Volt > 1023) { Max_Volt = 1023; } break; case 10: //соответствующая верхней границе температура Max_Temp = Max_Temp + Inc_Val_Calc(IncTrigger,2); if (Max_Temp > 1000) { Max_Temp = 1000; } break; case 11: //включение и выключение сигнализации if (AlarmControl == true) { AlarmControl = false; } else { AlarmControl = true; } break; case 12: //переключение между автоматическим и ручным режимами if (AutoControl == true) { AutoControl = false; } else { AutoControl = true; } HeaterManualOn = false; break; case 13: //включение и выключение подогревателя в ручном режиме if (AutoControl == false) //если мы в ручном режиме { if (HeaterManualOn == true) { HeaterManualOn = false; } else { HeaterManualOn = true; } } else //если мы в автоматическом режиме { lcd.setCursor(0,1); lcd.print("Switch to manual "); //подсказка HeaterManualOn = false; delay(1500); } break; case 14: //чтение настроек из 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(14,Min_Volt); //для типа float нужны 4 ячейки памяти EEPROM.get(18,Min_Temp); EEPROM.get(22,Max_Volt); EEPROM.get(26,Max_Temp); lcd.setCursor(0,1); lcd.print("Reading... OK "); delay(2000); break; case 15: //запись настроек в 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(14,Min_Volt); //для типа float нужны 4 ячейки памяти EEPROM.put(18,Min_Temp); EEPROM.put(22,Max_Volt); EEPROM.put(26,Max_Temp); //ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); lcd.setCursor(0,1); lcd.print("Saving... OK "); delay(2000); break; case 16: //загрузка значений по умолчанию. Берем из начала скетча MIN_TEMPERATURE = 29; MAX_TEMPERATURE = 33; ALARM_MIN = 28; ALARM_MAX = 40; ALARM_DIFF = 0; TEMP_CORRECT = 0; Min_Volt = 39; Min_Temp = 0; Max_Volt = 546; Max_Temp = 100; lcd.setCursor(0,1); lcd.print("Loading... OK "); delay(2000); break; } } //********* Функция кнопки Меньше, Уменьшить, Минус или т.п. ************* void ButtonMinus(boolean BtState) //BtState - состояние кнопки (нажата-отжата) { if(BtState == HIGH) //кнопка нажата { if (SettingNumber >= 1 && SettingNumber <= 10 ) //пункты настроек, где нужен разгон параметра в режиме удержания { 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; } } //******** Функция изменения параметров при нажатии кнопки Меньше ********** void ButtonMinusAction(int SettingItem, boolean IncTrigger) { switch (SettingItem) { case 1: //настройка температуры включения подогрева MIN_TEMPERATURE = MIN_TEMPERATURE - Inc_Val_Calc(IncTrigger,1); //если пытаемся уйти за нижний предел 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,1); //если пытаемся уйти за нижний предел 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,1); 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,1); 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,1); 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); } //если диффиренциал меньше ALARM_STOP if (ALARM_MAX - ALARM_MIN < ALARM_STOP) { ALARM_MAX = ALARM_MAX + Inc_Val_Calc(IncTrigger,1); lcd.setCursor(0,1); lcd.print("MAX-MIN MUST >="); //выводим подсказку lcd.print(ALARM_STOP); delay(2000); } break; case 5: //настройка диффиренциала температуры сигнализации ALARM_DIFF = ALARM_DIFF - Inc_Val_Calc(IncTrigger,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 - Inc_Val_Calc(IncTrigger,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; //данные для калибровки датчика Pt100 //нижняя граница вольтажа с аналогового порта переведенный в цифровой вид case 7: Min_Volt = Min_Volt - Inc_Val_Calc(IncTrigger,1); if (Min_Volt < 0) { Min_Volt = 0; } break; case 8: //соответствующая нижней границе температура Min_Temp = Min_Temp - Inc_Val_Calc(IncTrigger,2); if (Min_Temp < 0) { Min_Temp = 0; } break; //верхняя граница вольтажа с аналогового порта переведенный в цифровой вид case 9: Max_Volt = Max_Volt - Inc_Val_Calc(IncTrigger,1); if (Max_Volt <= Min_Volt) { Max_Volt = Max_Volt + Inc_Val_Calc(IncTrigger,1); lcd.setCursor(0,1); lcd.print("Max must > Min "); //выводим подсказку delay(2000); } break; case 10: //соответствующая верхней границе температура Max_Temp = Max_Temp - Inc_Val_Calc(IncTrigger,2); if (Max_Temp < 0) { Max_Temp = 0; } break; case 11: //включение и выключение сигнализации if (AlarmControl == true) { AlarmControl = false; } else { AlarmControl = true; } break; case 12: //переключение между автоматическим и ручным режимами if (AutoControl == true) { AutoControl = false; } else { AutoControl = true; } HeaterManualOn = false; break; case 13: //включение и выключение подогревателя в ручном режиме if (AutoControl == false) //если мы в ручном режиме { if (HeaterManualOn == true) { HeaterManualOn = false; } else { HeaterManualOn = true; } } else //если мы в автоматическом режиме { lcd.setCursor(0,1); lcd.print("Switch to manual "); //подсказа HeaterManualOn = false; delay(1500); } break; } } //********** Функция обработки данных с ИК-пульта ****************** void ReadIR() { if ( irrecv.decode( &results )) // если данные пришли { //Обратите внимание, символы, на которые будет реагировать контроллер, //определены для кнопок конкретного пульта управления. //В других пультах они могут быть другие, соответственно, //поменяйте их в скетче ниже. //Чтобы узнать, какие это символы, воспользуйтесь монитором порта //********* Нажаты кнопки CH- и CH+ пульта (Настройки) ******************** //при каждом нажатии кнопок CH- и CH+ перескакиваем на //следующий или предыдущий параметр в очереди. //Дойдя до конца меню, выходим из настроек //нажата кнопка CH- и CH+ на пульте if (results.value == 0xFFA25D || results.value == 0xFFE21D) { if (results.value == 0xFFE21D) //кнопка CH+ { SettingNumber = SettingNumber + 1; if (SettingNumber > 16) { SettingNumber = 0; FlagSettingIn = false; //вышли из режима настройки } else { FlagSettingIn = true; //вошли в режим настройки } } if (results.value == 0xFFA25D) //кнопка CH- { SettingNumber = SettingNumber - 1; FlagSettingIn = true; //вошли в режим настройки if (SettingNumber < 0) { SettingNumber = 1; } if (SettingNumber < 1) { SettingNumber = 0; FlagSettingIn = false; //вышли из режима настройки } } } //Реакция на кнопки, если мы в настройках if (FlagSettingIn == true) { //Нажата кнопка Плюс на пульте if (results.value == 0xFFA857) //кнопка Плюс { ButtonPlusAction(SettingNumber, false); //функция реакции на нажатие кнопки Плюс } //Нажата кнопка Минус на пульте if (results.value == 0xFFE01F) //кнопка Минус { ButtonMinusAction(SettingNumber, false); //функция реакции на нажатие кнопки Минус } } //if (FlagSettingIn == true) //************ кнопка 1 на пульте ****************** //переключение между режимами авто-ручной if (results.value == 0xFF30CF) //кнопка 1 { ButtonPlusAction (12, false); //если мы в настройках, то показываем изменение на LCD if (FlagSettingIn == true) { SettingNumber = 12; } } //************ кнопка 2 на пульте ****************** //включаем или выключаем подогреватель (только в ручном режиме) if (results.value == 0xFF18E7) //кнопка 2 { ButtonPlusAction (13, false); //если мы в настройках, то показываем изменение на LCD if (FlagSettingIn == true) { SettingNumber = 13; } } //************ кнопка 3 на пульте ****************** //включаем или выключаем сигнализацию (только реле) if (results.value == 0xFF7A85) //кнопка 3 { ButtonPlusAction (11, false); //если мы в настройках, то показываем изменение на LCD if (FlagSettingIn == true) { SettingNumber = 11; } } irrecv.resume(); // принимаем следующую команду } } //*************** Функция устранения дребезга контактов *********** 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; } //*************** Функция разгона приращения параметра ************ float Inc_Val_Calc(boolean IncTrigger, int IncVariant) { static int count_inc = 0; //счетчик итераций //IncVariant - разные варианты разгона if (IncTrigger == true) //пришел сигнал на разгон { count_inc = count_inc + 1; if (IncVariant == 1) { //играя этими числами, вы можете подстраивать разгон параметра под себя if (count_inc > 0 && count_inc <= 30) { return 1; } if (count_inc > 30 && count_inc <= 60) { return 2; } if (count_inc > 60 && count_inc <= 90) { return 5; } if (count_inc > 90) { return 10; } } if (IncVariant == 2) { //играя этими числами, вы можете подстраивать разгон параметра под себя if (count_inc > 0 && count_inc <= 30) { return 0.1; } if (count_inc > 30 && count_inc <= 60) { return 1; } if (count_inc > 60 && count_inc <= 90) { return 5; } if (count_inc > 90) { return 10; } } } else { count_inc = 0; if (IncVariant == 1) { return 1; //приращение по умолчанию } if (IncVariant == 2) { return 0.1; //приращение по умолчанию } } }