//часы-будильник на базе RTC DS1302 с показом температуры от термодатчика Pt100 //настройка параметров //эти настройки можно записать в энергонезависимую память #include <EEPROM.h> //библиотека работы с памятью #include <LiquidCrystal_I2C.h> //библиотеки работы с LCD через I2C #include <Wire.h> #include <iarduino_RTC.h> //библиотека для работы с модулем RTC DS1302 //Не забудь шунтировать выход от кнопок резистором 10 КОм const byte BtDownPin = 5; //пин кнопки Уменьшить const byte BtUpPin = 6; //пин кнопки Увеличить const byte BtSettingPin = 7; //пин кнопки настроек //пин реле сигнализации (зумера) //ШИМ пинов 3 и 11 используют один внутренний таймер, поэтому если на //пине 3 уже что-то висит (например CLK RTC), не сажай зумер на 11 пин и наоборот //пины ШИМ-сигналов у Arduino UNO : 3, 5, 6, 9, 10, 11 const byte AlarmBuzzerPin = 10; //пин зумера const byte AlarmLedPin = 8; //пин сигнального светодиода const byte Pt100Pin = A0; //аналоговый пин термодатчика Pt100 //Объявляем объект watch для работы с RTC модулем на базе чипа DS1302, //указывая выводы Arduino подключённые к выводам модуля RST, CLK, DAT. iarduino_RTC watch(RTC_DS1302, 2, 3, 4); //пины модуля I2C LCD-дисплея : //SDA - A4 //SCL - A5 //питание от шилда int Init_Eeprom_Addr = 1000; //номер ячейки в памяти EEPROM (0-1023), куда мы запишем ключ о //самом первом запуске программы //это нужно, так как при первом запуске в памяти нет наших настроек //и нужно загрузить значения по умолчанию byte Init_Key = 247; //это ключ (0-255). //Если при пуске скетча данные из ячейки Init_Eeprom не совпадают //с Init_Key, вводим в настройки значения по умолчанию, //иначе берем настройки из памяти boolean LastButtonSetting = LOW; //предыдущее состояние кнопки boolean CurrentButtonSetting = LOW; //текущее состояние кнопки boolean LastButtonDown = LOW; boolean CurrentButtonDown = LOW; boolean LastButtonUp = LOW; boolean CurrentButtonUp = LOW; boolean FlagSettingLoop = true; //были ли мы в цикле нажатия кнопки Настройки boolean FlagDownLoop = true; //были ли мы в цикле нажатия кнопки Меньше boolean FlagUpLoop = true; //были ли мы в цикле нажатия кнопки Больше //нужна при нажатии и удерживании кнопки ВВЕРХ unsigned long ButtonHoldTimerUp = 0; //нужна при нажатии и удерживании кнопки ВНИЗ unsigned long ButtonHoldTimerDown = 0; //по прошествии этого времени кнопка ВВЕРХ или ВНИЗ //перейдет в режим удержания, мс const long ButtonHoldIncTime = 500; boolean FlagUpHold = true; //были ли мы в цикле удержания кнопки ВВЕРХ boolean FlagDownHold = true; //были ли мы в цикле удержания кнопки ВНИЗ float Pt100_Val = 0; //значение с термодатчика Pt100 (0...1023) //значение с термодатчика Pt100, преобразованное в температуру float Pt100_Temp = 0; float Pt100_Temp_Correction = 0; //температура в град с поправкой //данные для интерполяции датчика (значения по умолчанию) //нижняя граница вольтажа с аналогового порта переведенный в цифровой вид float Min_Volt = 39; //верхняя граница вольтажа с аналогового порта переведенный в цифровой вид float Max_Volt = 546; //соответствующая нижней границе температура float Min_Temp = 0; //соответствующая верхней границе температура float Max_Temp = 100; unsigned long currentTime = 0; //нужна, чтобы убрать delay() const long intervalTime = 1000; //интервал опроса датчика в мс int TEMP_CORRECT=0; //поправка температуры датчика. const int TEMP_CORRECT_MAX = 100; //верхний порог const int TEMP_CORRECT_MIN = -100; //нижний порог boolean AlarmControlOn = false; //включен или нет будильник boolean FlagSettingIn = false; //находимся ли в настройках int8_t SettingNumber = 0; //пункт меню настройки //Объявляем переменные для настройки времени //D-день, M-месяц, Y-год, h-часы, m-минуты, s-секунды, W-день недели. //int8_t D, M, Y, h, m, s, W; int8_t D = 1; //текущий день месяца 1-31 int8_t M = 1; //текущий месяц 1-12 int8_t Y = 23; //текущий год 0-99 int8_t h = 0; //текущие часы 0-23 int8_t m = 0; //текущие минуты 0-59 int8_t s = 0; //текущие секунды 0-59 int8_t W = 0; //текущий день недели 0-6 //Объявляем переменные для настройки будильника //int8_t D_alarm, M_alarm, Y_alarm, h_alarm, m_alarm, s_alarm, W_alarm; int8_t D_alarm = 1; //текущий день месяца 1-31 int8_t M_alarm = 1; //текущий месяц 1-12 int8_t Y_alarm = 23; //текущий год 0-99 int8_t h_alarm = 0; //текущие часы 0-23 int8_t m_alarm = 0; //текущие минуты 0-59 int8_t s_alarm = 0; //текущие секунды 0-59 int8_t W_alarm = 1; //опции будильника по дням недели long AlarmBuzzerTime = 60000; //продолжительность звонка будильника в мс unsigned long BuzzerTime = 0; //нужна, чтобы убрать delay() LiquidCrystal_I2C lcd(0x27,16,2); //Задаем адрес и размерность дисплея. byte degree[8] = { //рисуем пользовательский символ градуса B00110, B01001, B01001, B00110, B00000, B00000, B00000, B00000, }; byte arrow_up[8] = { //рисуем пользовательский символ стрелки вверх B00100, B01110, B10101, B00100, B00100, B00100, B00100, B00100, }; byte arrow_down[8] = { //рисуем пользовательский символ стрелки вниз B00100, B00100, B00100, B00100, B00100, B10101, B01110, B00100, }; void setup() //************************************************ { analogReference(INTERNAL); //устанавливаем внутреннее опорное напряжение 1,1 В pinMode (Pt100Pin, INPUT); //устанавливаем режимы входов платы (ввод или вывод) pinMode (AlarmBuzzerPin, OUTPUT); pinMode (AlarmLedPin, OUTPUT); pinMode (BtSettingPin, INPUT); pinMode (BtDownPin, INPUT); pinMode (BtUpPin, INPUT); watch.begin(); //Инициируем работу с модулем RTC. //если запись в настройки когда-то была, то читаем их if (EEPROM.read(Init_Eeprom_Addr) == Init_Key) { EEPROM.get(0,TEMP_CORRECT); //для типа int нужны 2 ячейки памяти EEPROM.get(4,Min_Volt); //для типа float нужны 4 ячейки памяти EEPROM.get(8,Min_Temp); EEPROM.get(12,Max_Volt); EEPROM.get(16,Max_Temp); EEPROM.get(20,AlarmControlOn); EEPROM.get(21,D); EEPROM.get(22,M); EEPROM.get(23,Y); EEPROM.get(24,h); EEPROM.get(25,m); EEPROM.get(26,s); EEPROM.get(27,W); EEPROM.get(28,D_alarm); EEPROM.get(29,M_alarm); EEPROM.get(30,Y_alarm); EEPROM.get(31,h_alarm); EEPROM.get(32,m_alarm); EEPROM.get(33,s_alarm); EEPROM.get(34,W_alarm); EEPROM.get(35,AlarmBuzzerTime); } else //если записи не было, записываем в память значения по умолчанию { EEPROM.put(0,TEMP_CORRECT); //для типа int нужны 2 ячейки памяти EEPROM.put(4,Min_Volt); //для типа float нужны 4 ячейки памяти EEPROM.put(8,Min_Temp); EEPROM.put(12,Max_Volt); EEPROM.put(16,Max_Temp); EEPROM.put(20,AlarmControlOn); EEPROM.put(21,D); EEPROM.put(22,M); EEPROM.put(23,Y); EEPROM.put(24,h); EEPROM.put(25,m); EEPROM.put(26,s); EEPROM.put(27,W); EEPROM.put(28,D_alarm); EEPROM.put(29,M_alarm); EEPROM.put(30,Y_alarm); EEPROM.put(31,h_alarm); EEPROM.put(32,m_alarm); EEPROM.put(33,s_alarm); EEPROM.put(34,W_alarm); EEPROM.put(35,AlarmBuzzerTime); //и ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); } lcd.init(); //инициализация lcd lcd.backlight(); //включаем подсветку lcd.setCursor(0,0); //ставим курсор в начало первой строки lcd.print(" Alarm "); lcd.setCursor(0,1); //ставим курсор в начало второй строки lcd.print(" Clock "); BuzzerCheck(); //проверяем звонок lcd.createChar(0, degree); //прописываем пользовательский символ градуса lcd.createChar(1, arrow_up); //прописываем пользовательский символ стрелки вверх lcd.createChar(2, arrow_down); //прописываем пользовательский символ стрелки вниз } void loop() //*************************************************** { //цикл замера температуры if(millis() - currentTime > intervalTime) //опрос датчика через 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; //****************** Вывод на экран ****************************** if(FlagSettingIn == false) //если мы не в настройках { lcd.setCursor(0,0); //прописываем первую строку lcd.print(watch.gettime("H:i:s")); lcd.print(" "); lcd.print(Pt100_Temp_Correction,1); lcd.write((byte)0); //рисуем пользовательский символ градуса lcd.print("C"); //если мы ввели корректировку показания датчика, //за символом градуса будет выводиться восклицательный знак if (TEMP_CORRECT != 0) { lcd.print("! "); } else { lcd.print(" "); } lcd.setCursor(0,1); //прописываем вторую строку if(AlarmControlOn == true) //это нужно для центрирования { lcd.print(" "); } else { lcd.print(" "); } lcd.print(watch.gettime("d.m.y D")); if(AlarmControlOn == true) //если будильник включен { lcd.print(" A "); //рисуем пользовательский символ будильника } else { lcd.print(" "); } } } //if(millis() - currentTime > intervalTime) //нажата кнопка ВВЕРХ в режиме показа времени if(digitalRead(BtUpPin) == HIGH && FlagSettingIn == false) { //показываем уставку будильника lcd.setCursor(0,0); lcd.print(" "); lcd.print(ZeroFormat(h_alarm)); lcd.print(":"); lcd.print(ZeroFormat(m_alarm)); lcd.print(":"); lcd.print(ZeroFormat(s_alarm)); lcd.print(" "); lcd.setCursor(0,1); switch (W_alarm) { case 0: lcd.print("Date "); lcd.print(ZeroFormat(D_alarm)); lcd.print("."); lcd.print(ZeroFormat(M_alarm)); lcd.print("."); lcd.print(ZeroFormat(Y_alarm)); lcd.print(" "); break; case 1: lcd.print("Every Day "); break; case 2: lcd.print("From Mon to Fri "); break; case 3: lcd.print("Only Sat and Sun "); break; case 4: lcd.print("Only Monday "); break; case 5: lcd.print("Only Tuesday "); break; case 6: lcd.print("Only Wednesday "); break; case 7: lcd.print("Only Thursday "); break; case 8: lcd.print("Only Friday "); break; case 9: lcd.print("Only Saturday "); break; case 10: lcd.print("Only Sunday "); break; } delay(2000); } //нажата кнопка ВНИЗ в режиме показа времени if(digitalRead(BtDownPin) == HIGH && FlagSettingIn == false) { noTone(AlarmBuzzerPin); //остановливаем звучание digitalWrite(AlarmLedPin, LOW); //гасим светодиод } //********** Включаем звуковой сигнал ********************** //если сигнализация включена и мы не в настройках if (AlarmControlOn == true && FlagSettingIn == false) { watch.gettime(); //получаем время switch (W_alarm) //действуем в зависимости от режима дней недели { case 0: //одиночный звонок if (watch.year == Y_alarm && watch.month == M_alarm && watch.day == D_alarm && watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm) { BuzzerOnFunction(); } break; case 1: //звонок каждый день в одно и тоже время if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm) { BuzzerOnFunction(); } break; case 2: //звонок с понедельника по пятницу (будни) if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && (watch.weekday >= 1 && watch.weekday <= 5)) { BuzzerOnFunction(); } break; case 3: //звонок в субботу и воскресенье (выходные) if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && watch.weekday == 0 && watch.weekday == 6) { BuzzerOnFunction(); } break; case 4: //звонок в понедельник if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && watch.weekday == 1) { BuzzerOnFunction(); } break; case 5: //звонок во вторник if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && watch.weekday == 2) { BuzzerOnFunction(); } break; case 6: //звонок в среду if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && watch.weekday == 3) { BuzzerOnFunction(); } break; case 7: //звонок в четверг if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && watch.weekday == 4) { BuzzerOnFunction(); } break; case 8: //звонок в пятницу if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && watch.weekday == 5) { BuzzerOnFunction(); } break; case 9: //звонок в субботу if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && watch.weekday == 6) { BuzzerOnFunction(); } break; case 10: //звонок в воскресенье if (watch.Hours == h_alarm && watch.minutes == m_alarm && watch.seconds == s_alarm && watch.weekday == 0) { BuzzerOnFunction(); } break; } } else { noTone(AlarmBuzzerPin); //остановливаем звучание } //по прошествии AlarmBuzzerTime выключаем звонок и светодиод if(millis() - BuzzerTime > AlarmBuzzerTime) { noTone(AlarmBuzzerPin); //остановливаем звучание digitalWrite(AlarmLedPin, LOW); //гасим светодиод } //********************* Нажата кнопка Настройки ************************ //при каждом нажатии кнопки Настройки //перескакиваем на следующий параметр в очереди. И так по кругу //считываем состояние кнопки с устраненным дребезгом CurrentButtonSetting = debounce(LastButtonSetting, BtSettingPin); if(CurrentButtonSetting == HIGH) //кнопка нажата { if (FlagSettingLoop == true) //эта переменная нужна для однократного нажатия кнопки, //даже если ее удерживать { FlagSettingLoop = false; FlagSettingIn = true; SettingNumber = SettingNumber + 1; if (SettingNumber > 31) { SettingExit(); //выход из настроек } } } else //кнопка отжата { FlagSettingLoop = true; } //обновляем текущее значение кнопки LastButtonSetting = CurrentButtonSetting; if (FlagSettingIn == true) //если мы в настройках { //******************* Нажата кнопка Увеличить ************************** ButtonPlus(); //функция реакции на нажатие кнопки Увеличить //******************* Нажата кнопка Уменьшить ************************** ButtonMinus(); //функция реакции на нажатие кнопки Уменьшить SettingsOnLCD(SettingNumber); //отображаем строку с настройками } } //void loop() //********************** ФУНКЦИИ ********************************* //*********** Функция отображения настроек на LCD ************* void SettingsOnLCD(int SettingItem) { lcd.setCursor(0,0); switch (SettingItem) { case 1: //настройка будильника : заставка lcd.print(" Alarm Settings "); lcd.setCursor(0,1); lcd.print(" "); lcd.write((byte)1); lcd.print(" Next "); lcd.write((byte)2); lcd.print(" Exit "); break; case 2: //настройка часов будильника lcd.print("HourNow "); watch.blinktime(3); lcd.print(watch.gettime("H:i:s")); lcd.setCursor(0,1); lcd.print("HourNew "); lcd.print(ZeroFormat(h_alarm)); //если цифра одна, добавляем в начало "0" lcd.print(":"); lcd.print(ZeroFormat(m_alarm)); lcd.print(":"); lcd.print(ZeroFormat(s_alarm)); lcd.print(" "); break; case 3: //настройка минут будильника lcd.print("Min_Now "); watch.blinktime(2); lcd.print(watch.gettime("H:i:s")); lcd.setCursor(0,1); lcd.print("Min_New "); lcd.print(ZeroFormat(h_alarm)); lcd.print(":"); lcd.print(ZeroFormat(m_alarm)); lcd.print(":"); lcd.print(ZeroFormat(s_alarm)); lcd.print(" "); break; case 4: //настройка секунд будильника lcd.print("Sec_Now "); watch.blinktime(1); lcd.print(watch.gettime("H:i:s")); lcd.setCursor(0,1); lcd.print("Sec_New "); lcd.print(ZeroFormat(h_alarm)); lcd.print(":"); lcd.print(ZeroFormat(m_alarm)); lcd.print(":"); lcd.print(ZeroFormat(s_alarm)); lcd.print(" "); break; case 5: //настройка дня будильника lcd.print("Day_Now "); watch.blinktime(4); lcd.print(watch.gettime("d.m.y")); lcd.setCursor(0,1); lcd.print("Day_New "); lcd.print(ZeroFormat(D_alarm)); lcd.print("."); lcd.print(ZeroFormat(M_alarm)); lcd.print("."); lcd.print(ZeroFormat(Y_alarm)); lcd.print(" "); break; case 6: //настройка месяца будильника lcd.print("Mon_Now "); watch.blinktime(5); lcd.print(watch.gettime("d.m.y")); lcd.setCursor(0,1); lcd.print("Mon_New "); lcd.print(ZeroFormat(D_alarm)); lcd.print("."); lcd.print(ZeroFormat(M_alarm)); lcd.print("."); lcd.print(ZeroFormat(Y_alarm)); lcd.print(" "); break; case 7: //настройка года будильника lcd.print("YearNow "); watch.blinktime(6); lcd.print(watch.gettime("d.m.y")); lcd.setCursor(0,1); lcd.print("YearNew "); lcd.print(ZeroFormat(D_alarm)); lcd.print("."); lcd.print(ZeroFormat(M_alarm)); lcd.print("."); lcd.print(ZeroFormat(Y_alarm)); lcd.print(" "); break; case 8: //опции по дням недели будильника lcd.print("Week Mode "); lcd.setCursor(0,1); switch (W_alarm) { case 0: lcd.print("Only One Time "); break; case 1: lcd.print("Every Day "); break; case 2: lcd.print("From Mon to Fri "); break; case 3: lcd.print("Only Sat and Sun "); break; case 4: lcd.print("Only Monday "); break; case 5: lcd.print("Only Tuesday "); break; case 6: lcd.print("Only Wednesday "); break; case 7: lcd.print("Only Thursday "); break; case 8: lcd.print("Only Friday "); break; case 9: lcd.print("Only Saturday "); break; case 10: lcd.print("Only Sunday "); break; } break; case 9: //продолжительность звонка будильника lcd.print("Buzzer Time "); lcd.setCursor(0,1); lcd.print(AlarmBuzzerTime/1000); lcd.print(" sec "); break; case 10: //включение-выключение будильника lcd.print("Alarm ON/OFF "); lcd.setCursor(0,1); if(AlarmControlOn == true) { lcd.print("Alarm ON "); } else { lcd.print("Alarm OFF "); } break; case 11: //проверка звонка lcd.print("Buzzer Check "); lcd.setCursor(0,1); lcd.print("Press UP button "); break; case 12: //запись настроек будильника в EEPROM lcd.print("Save Alarm Sett. "); lcd.setCursor(0,1); lcd.print("Press UP button "); break; case 13: //настройка времени : заставка lcd.print(" Time Settings "); lcd.setCursor(0,1); lcd.print(" "); lcd.write((byte)1); lcd.print(" Next "); lcd.write((byte)2); lcd.print(" Exit "); break; case 14: //настройка часов lcd.print("HourOld "); watch.blinktime(3); lcd.print(watch.gettime("H:i:s")); lcd.setCursor(0,1); lcd.print("HourNew "); lcd.print(ZeroFormat(h)); //если цифра одна, добавляем в начало "0" lcd.print(":"); lcd.print(ZeroFormat(m)); lcd.print(":"); lcd.print(ZeroFormat(s)); lcd.print(" "); break; case 15: //настройка минут lcd.print("Min_Old "); watch.blinktime(2); lcd.print(watch.gettime("H:i:s")); lcd.setCursor(0,1); lcd.print("Min_New "); lcd.print(ZeroFormat(h)); lcd.print(":"); lcd.print(ZeroFormat(m)); lcd.print(":"); lcd.print(ZeroFormat(s)); lcd.print(" "); break; case 16: //настройка секунд lcd.print("Sec_Old "); watch.blinktime(1); lcd.print(watch.gettime("H:i:s")); lcd.setCursor(0,1); lcd.print("Sec_New "); lcd.print(ZeroFormat(h)); lcd.print(":"); lcd.print(ZeroFormat(m)); lcd.print(":"); lcd.print(ZeroFormat(s)); lcd.print(" "); break; case 17: //настройка дня lcd.print("Day_Old "); watch.blinktime(4); lcd.print(watch.gettime("d.m.y")); lcd.setCursor(0,1); lcd.print("Day_New "); lcd.print(ZeroFormat(D)); lcd.print("."); lcd.print(ZeroFormat(M)); lcd.print("."); lcd.print(ZeroFormat(Y)); lcd.print(" "); break; case 18: //настройка месяца lcd.print("Mon_Old "); watch.blinktime(5); lcd.print(watch.gettime("d.m.y")); lcd.setCursor(0,1); lcd.print("Mon_New "); lcd.print(ZeroFormat(D)); lcd.print("."); lcd.print(ZeroFormat(M)); lcd.print("."); lcd.print(ZeroFormat(Y)); lcd.print(" "); break; case 19: //настройка года lcd.print("YearOld "); watch.blinktime(6); lcd.print(watch.gettime("d.m.y")); lcd.setCursor(0,1); lcd.print("YearNew "); lcd.print(ZeroFormat(D)); lcd.print("."); lcd.print(ZeroFormat(M)); lcd.print("."); lcd.print(ZeroFormat(Y)); lcd.print(" "); break; case 20: //настройка дня недели lcd.print("WeekOld "); watch.blinktime(0); watch.gettime(); lcd.print(WeekLongFormat(watch.weekday)); lcd.print(" "); lcd.setCursor(0,1); lcd.print("WeekNew "); lcd.print(WeekLongFormat(W)); lcd.print(" "); break; case 21: //запись настроек в EEPROM и модуль времени lcd.print("Save Time Settin"); lcd.setCursor(0,1); lcd.print("Press UP button "); break; case 22: //настройка температуры заставка lcd.print("Temperature Set. "); lcd.setCursor(0,1); lcd.print(" "); lcd.write((byte)1); lcd.print(" Next "); lcd.write((byte)2); lcd.print(" Exit "); break; case 23: //поправки температуры датчика lcd.print("Temp Correction "); lcd.setCursor(0,1); lcd.print(TEMP_CORRECT); lcd.write((byte)0); lcd.print("C "); break; //данные для калибровки датчика Pt100 //нижняя граница вольтажа с аналогового порта переведенный в цифровой вид case 24: lcd.print("Min_Volt "); lcd.setCursor(0,1); lcd.print(Min_Volt,0); lcd.print(" "); break; case 25: //соответствующая нижней границе температура lcd.print("MinPt "); lcd.setCursor(0,1); lcd.print(Min_Temp, 1); lcd.write((byte)0); lcd.print("C "); break; //верхняя граница вольтажа с аналогового порта переведенный в цифровой вид case 26: lcd.print("Max_Volt "); lcd.setCursor(0,1); lcd.print(Max_Volt,0); lcd.print(" "); break; case 27: //соответствующая верхней границе температура lcd.print("MaxPt "); lcd.setCursor(0,1); lcd.print(Max_Temp, 1); lcd.write((byte)0); lcd.print("C "); break; case 28: //сохранения-загрузки заставка lcd.print("Read/Save/Defaul "); lcd.setCursor(0,1); lcd.print(" "); lcd.write((byte)1); lcd.print(" Next "); lcd.write((byte)2); lcd.print(" Exit "); break; case 29: //чтение настроек из EEPROM lcd.print("Read Settings "); lcd.setCursor(0,1); lcd.print("Press UP button "); break; case 30: //запись настроек в EEPROM lcd.print("Save Settings "); lcd.setCursor(0,1); lcd.print("Press UP button "); break; case 31: //загрузка значений по умолчанию lcd.print("Load Default "); lcd.setCursor(0,1); lcd.print("Press UP button "); break; } } //********* Функция кнопки Больше, Увеличить, Плюс и т.п. ************* void ButtonPlus() { CurrentButtonUp = debounce(LastButtonUp, BtUpPin); if(CurrentButtonUp == HIGH) //кнопка нажата { if ((SettingNumber >= 2 && SettingNumber <= 7) || (SettingNumber >= 14 && SettingNumber <= 19) || (SettingNumber >= 23 && SettingNumber <= 27) || SettingNumber == 9) //пункты настроек, где нужен разгон параметра в режиме удержания { 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: //настройка будильника : заставка SettingNumber = 13; //уходим в настройку времени break; case 2: //настройка часов будильника h_alarm = h_alarm + Inc_Val_Calc(IncTrigger,1); if(h_alarm > 23) //верхний порог { h_alarm = 23; } break; case 3: //настройка минут будильника m_alarm = m_alarm + Inc_Val_Calc(IncTrigger,1); if(m_alarm > 59) //верхний порог { m_alarm = 59; } break; case 4: //настройка секунд будильника s_alarm = s_alarm + Inc_Val_Calc(IncTrigger,1); if(s_alarm > 59) //верхний порог { s_alarm = 59; } break; case 5: //настройка дня будильника D_alarm = D_alarm + Inc_Val_Calc(IncTrigger,1); if(D_alarm > 31) //верхний порог { D_alarm = 31; } break; case 6: //настройка месяца будильника M_alarm = M_alarm + Inc_Val_Calc(IncTrigger,1); if(M_alarm > 12) //верхний порог { M_alarm = 12; } break; case 7: //настройка года будильника Y_alarm = Y_alarm + Inc_Val_Calc(IncTrigger,1); if(Y_alarm > 99) //верхний порог { Y_alarm = 99; } break; case 8: //опции по дням недели будильника W_alarm = W_alarm + 1; if(W_alarm > 10) //верхний порог { W_alarm = 0; } break; case 9: //продолжительность звонка будильника AlarmBuzzerTime = AlarmBuzzerTime + Inc_Val_Calc(IncTrigger,3); if(AlarmBuzzerTime > 600000) //верхний порог { AlarmBuzzerTime = 600000; } break; case 10: //включение-выключение будильника if(AlarmControlOn == true) { AlarmControlOn = false; } else { AlarmControlOn = true; } break; case 11: //проверка звонка BuzzerCheck(); //проверяем звонок break; case 12: //запись настроек будильника в EEPROM EEPROM.put(20,AlarmControlOn); EEPROM.put(28,D_alarm); EEPROM.put(29,M_alarm); EEPROM.put(30,Y_alarm); EEPROM.put(31,h_alarm); EEPROM.put(32,m_alarm); EEPROM.put(33,s_alarm); EEPROM.put(34,W_alarm); EEPROM.put(35,AlarmBuzzerTime); lcd.setCursor(0,1); lcd.print("Saving... OK "); delay(1500); SettingExit(); break; case 13: //настройка времени : заставка SettingNumber = 22; //уходим в калибровку датчика break; case 14: //настройка часов h = h + Inc_Val_Calc(IncTrigger,1); if(h > 23) //верхний порог { h = 23; } break; case 15: //настройка минут m = m + Inc_Val_Calc(IncTrigger,1); if(m > 59) //верхний порог { m = 59; } break; case 16: //настройка секунд s = s + Inc_Val_Calc(IncTrigger,1); if(s > 59) //верхний порог { s = 59; } break; case 17: //настройка дня D = D + Inc_Val_Calc(IncTrigger,1); if(D > 31) //верхний порог { D = 31; } break; case 18: //настройка месяца M = M + Inc_Val_Calc(IncTrigger,1); if(M > 12) //верхний порог { M = 12; } break; case 19: //настройка года Y = Y + Inc_Val_Calc(IncTrigger,1); if(Y > 99) //верхний порог { Y = 99; } break; case 20: //настройка дня недели W = W + 1; if(W > 6) //верхний порог { W = 0; } break; case 21: //запись настроек в EEPROM и модуль времени watch.settime(s,m,h,D,M,Y,W); //Записываем время в модуль EEPROM.put(21,D); EEPROM.put(22,M); EEPROM.put(23,Y); EEPROM.put(24,h); EEPROM.put(25,m); EEPROM.put(26,s); EEPROM.put(27,W); lcd.setCursor(0,1); lcd.print("Saving... OK "); delay(1500); SettingExit(); break; case 22: //настройка температуры : заставка SettingNumber = 28; //уходим в сохранения загрузки break; case 23: //настройка поправки температуры датчика TEMP_CORRECT = TEMP_CORRECT + Inc_Val_Calc(IncTrigger,1); if(TEMP_CORRECT > TEMP_CORRECT_MAX) //верхний порог { TEMP_CORRECT = TEMP_CORRECT_MAX; } break; //данные для калибровки датчика Pt100 //нижняя граница вольтажа с аналогового порта переведенный в цифровой вид case 24: 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 25: //соответствующая нижней границе температура Min_Temp = Min_Temp + Inc_Val_Calc(IncTrigger,2); if (Min_Temp > 1000) { Min_Temp = 1000; } break; //верхняя граница вольтажа с аналогового порта переведенный в цифровой вид case 26: Max_Volt = Max_Volt + Inc_Val_Calc(IncTrigger,1); if (Max_Volt > 1023) { Max_Volt = 1023; } break; case 27: //соответствующая верхней границе температура Max_Temp = Max_Temp + Inc_Val_Calc(IncTrigger,2); if (Max_Temp > 1000) { Max_Temp = 1000; } break; case 28: //настройка температуры : заставка SettingNumber = 1; //уходим в настройки будильника break; case 29: //чтение настроек из EEPROM EEPROM.get(0,TEMP_CORRECT); //для типа int нужны 2 ячейки памяти EEPROM.get(4,Min_Volt); //для типа float нужны 4 ячейки памяти EEPROM.get(8,Min_Temp); EEPROM.get(12,Max_Volt); EEPROM.get(16,Max_Temp); EEPROM.put(20,AlarmControlOn); EEPROM.get(21,D); EEPROM.get(22,M); EEPROM.get(23,Y); EEPROM.get(24,h); EEPROM.get(25,m); EEPROM.get(26,s); EEPROM.get(27,W); EEPROM.get(28,D_alarm); EEPROM.get(29,M_alarm); EEPROM.get(30,Y_alarm); EEPROM.get(31,h_alarm); EEPROM.get(32,m_alarm); EEPROM.get(33,s_alarm); EEPROM.get(34,W_alarm); EEPROM.get(35,AlarmBuzzerTime); lcd.setCursor(0,1); lcd.print("Reading... OK "); delay(1500); SettingExit(); break; case 30: //запись настроек в EEPROM EEPROM.put(0,TEMP_CORRECT); //для типа int нужны 2 ячейки памяти EEPROM.put(4,Min_Volt); //для типа float нужны 4 ячейки памяти EEPROM.put(8,Min_Temp); EEPROM.put(12,Max_Volt); EEPROM.put(16,Max_Temp); EEPROM.put(20,AlarmControlOn); EEPROM.put(21,D); EEPROM.put(22,M); EEPROM.put(23,Y); EEPROM.put(24,h); EEPROM.put(25,m); EEPROM.put(26,s); EEPROM.put(27,W); EEPROM.put(28,D_alarm); EEPROM.put(29,M_alarm); EEPROM.put(30,Y_alarm); EEPROM.put(31,h_alarm); EEPROM.put(32,m_alarm); EEPROM.put(33,s_alarm); EEPROM.put(34,W_alarm); EEPROM.put(35,AlarmBuzzerTime); //ставим метку, что запись в настройки произведена EEPROM.put(Init_Eeprom_Addr,Init_Key); lcd.setCursor(0,1); lcd.print("Saving... OK "); delay(1500); SettingExit(); break; case 31: //загрузка значений по умолчанию TEMP_CORRECT = 0; Min_Volt = 39; Min_Temp = 0; Max_Volt = 546; Max_Temp = 100; AlarmControlOn = false; D = 1; //текущий день месяца 1-31 M = 1; //текущий месяц 1-12 Y = 23; //текущий год 0-99 h = 0; //текущие часы 0-23 m = 0; //текущие минуты 0-59 s = 0; //текущие секунды 0-59 W = 0; //текущий день недели 0-6 D_alarm = 1; //текущий день месяца 1-31 M_alarm = 1; //текущий месяц 1-12 Y_alarm = 23; //текущий год 0-99 h_alarm = 0; //текущие часы 0-23 m_alarm = 0; //текущие минуты 0-59 s_alarm = 0; //текущие секунды 0-59 W_alarm = 0; //опции будильника по дням недели AlarmBuzzerTime = 60000; lcd.setCursor(0,1); lcd.print("Loading... OK "); delay(1500); SettingExit(); break; } } //********* Функция кнопки Меньше, Уменьшить, Минус или т.п. ************* void ButtonMinus() { CurrentButtonDown = debounce(LastButtonDown, BtDownPin); if(CurrentButtonDown == HIGH) //кнопка нажата { if ((SettingNumber >= 2 && SettingNumber <= 7) || (SettingNumber >= 14 && SettingNumber <= 19) || (SettingNumber >= 23 && SettingNumber <= 27) || SettingNumber == 9) //пункты настроек, где нужен разгон параметра в режиме удержания { 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: //настройка будильника : заставка SettingExit(); //выход из настроек break; case 2: //настройка часов будильника h_alarm = h_alarm - Inc_Val_Calc(IncTrigger,1); if(h_alarm < 0) //нижний порог { h_alarm = 0; } break; case 3: //настройка минут будильника m_alarm = m_alarm - Inc_Val_Calc(IncTrigger,1); if(m_alarm < 0) //нижний порог { m_alarm = 0; } break; case 4: //настройка секунд будильника s_alarm = s_alarm - Inc_Val_Calc(IncTrigger,1); if(s_alarm < 0) //нижний порог { s_alarm = 0; } break; case 5: //настройка дня будильника D_alarm = D_alarm - Inc_Val_Calc(IncTrigger,1); if(D_alarm < 1) //нижний порог { D_alarm = 1; } break; case 6: //настройка месяца будильника M_alarm = M_alarm - Inc_Val_Calc(IncTrigger,1); if(M_alarm < 1) //нижний порог { M_alarm = 1; } break; case 7: //настройка года будильника Y_alarm = Y_alarm - Inc_Val_Calc(IncTrigger,1); if(Y_alarm < 0) //нижний порог { Y_alarm = 0; } break; case 8: //опции по дням недели будильника W_alarm = W_alarm - 1; if(W_alarm < 0) //нижний порог { W_alarm = 10; } break; case 9: //продолжительность звонка будильника AlarmBuzzerTime = AlarmBuzzerTime - Inc_Val_Calc(IncTrigger,3); if(AlarmBuzzerTime < 10000) //нижний порог { AlarmBuzzerTime = 10000; } break; case 10: //включение-выключение будильника if(AlarmControlOn == true) { AlarmControlOn = false; } else { AlarmControlOn = true; } break; case 13: //настройка времени : заставка SettingExit(); break; case 14: //настройка часов h = h - Inc_Val_Calc(IncTrigger,1); if(h < 0) //нижний порог { h = 0; } break; case 15: //настройка минут m = m - Inc_Val_Calc(IncTrigger,1); if(m < 0) //нижний порог { m = 0; } break; case 16: //настройка секунд s = s - Inc_Val_Calc(IncTrigger,1); if(s < 0) //нижний порог { s = 0; } break; case 17: //настройка дня D = D - Inc_Val_Calc(IncTrigger,1); if(D < 1) //нижний порог { D = 1; } break; case 18: //настройка месяца M = M - Inc_Val_Calc(IncTrigger,1); if(M < 1) //нижний порог { M = 1; } break; case 19: //настройка года Y = Y - Inc_Val_Calc(IncTrigger,1); if(Y < 0) //нижний порог { Y = 0; } break; case 20: //настройка дня недели W = W - 1; if(W < 0) //нижний порог { W = 6; } break; case 22: //настройка температуры : заставка SettingExit(); break; case 23: //настройка поправки температуры датчика TEMP_CORRECT = TEMP_CORRECT - Inc_Val_Calc(IncTrigger,1); if(TEMP_CORRECT < TEMP_CORRECT_MIN) //нижний порог { TEMP_CORRECT = TEMP_CORRECT_MIN; } break; //данные для калибровки датчика Pt100 //нижняя граница вольтажа с аналогового порта переведенный в цифровой вид case 24: Min_Volt = Min_Volt - Inc_Val_Calc(IncTrigger,1); if (Min_Volt < 0) { Min_Volt = 0; } break; case 25: //соответствующая нижней границе температура Min_Temp = Min_Temp - Inc_Val_Calc(IncTrigger,2); if (Min_Temp < 0) { Min_Temp = 0; } break; //верхняя граница вольтажа с аналогового порта переведенный в цифровой вид case 26: 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 27: //соответствующая верхней границе температура Max_Temp = Max_Temp - Inc_Val_Calc(IncTrigger,2); if (Max_Temp < 0) { Max_Temp = 0; } break; case 28: //сохранения-загрузки заставка SettingExit(); 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; } //*************** Функция разгона приращения параметра ************ float Inc_Val_Calc(boolean IncTrigger, int IncVariant) { static int count_inc = 0; //счетчик итераций //IncVariant - разные варианты разгона if (IncTrigger == true) //пришел сигнал на разгон { count_inc = count_inc + 1; switch (IncVariant) { case 1: //играя этими числами, вы можете подстраивать разгон параметра под себя if (count_inc > 0 && count_inc <= 10) { return 1; } if (count_inc > 10 && count_inc <= 20) { return 2; } if (count_inc > 20) { return 3; } break; case 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; } break; case 3: //играя этими числами, вы можете подстраивать разгон параметра под себя if (count_inc > 0 && count_inc <= 30) { return 1000; } if (count_inc > 30 && count_inc <= 60) { return 2000; } if (count_inc > 60 && count_inc <= 90) { return 5000; } if (count_inc > 90) { return 10000; } break; } } else { count_inc = 0; switch (IncVariant) { case 1: return 1; //приращение по умолчанию break; case 2: return 0.1; //приращение по умолчанию break; case 3: return 1000; //приращение по умолчанию break; } } } //******************* добавляем 0 в начало, если цифра < 10 *************************** String ZeroFormat(int8_t digitIn) { if(digitIn < 10) { return "0" + String(digitIn); } else { return String(digitIn); } } //********** возвращаем полное название дня недели по цифре ********************* String WeekLongFormat(int8_t digitIn) { switch (digitIn) { case 0: return "Sunday"; break; case 1: return "Monday"; break; case 2: return "Tuesday"; break; case 3: return "Wednesday"; break; case 4: return "Thursday"; break; case 5: return "Friday"; break; case 6: return "Saturday"; break; } } //****************** выход из настроек ************************* void SettingExit() { SettingNumber = 0; FlagSettingIn = false; //вышли из режима настройки watch.blinktime(0); //останавливаем мигание } //************** запуск звонка ******************* void BuzzerOnFunction() { BuzzerTime = millis(); //производим отсечку времени сигнала tone(AlarmBuzzerPin, 2000); //включаем сигнал digitalWrite(AlarmLedPin, HIGH); //зажигаем светодиод } //************** проверка звонка ******************* void BuzzerCheck() { digitalWrite(AlarmLedPin, HIGH); //зажигаем светодиод tone(AlarmBuzzerPin, 1000); delay(500); //пауза в мс tone(AlarmBuzzerPin, 500); delay(500); //пауза в мс tone(AlarmBuzzerPin, 2000); delay(500); //пауза в мс noTone(AlarmBuzzerPin); //остановливаем звучание digitalWrite(AlarmLedPin, LOW); //гасим светодиод }