Обратите внимание - в скетче есть задержка включения реле, чтобы оно не слишком часто котёл дёргало. Это я к тому, что стОит немного подождать перед тем, как писать, что реле не включается. :-))
Обратите внимание - в скетче есть задержка включения реле, чтобы оно не слишком часто котёл дёргало. Это я к тому, что стОит немного подождать перед тем, как писать, что реле не включается. :-))
Спасибо за проет!!!!!
Уже давно заметил, сейчас всё в порядке!!! РАБОТАЕТ!!!!
а как добавить датчиков температуры хочу один на улицу выкинуть а второй на котел поставить чисто для мониторинга у меня дисплей 20х4 места для вывода предостаточно
Третий сезон работает самодельный контроллер отопления, реализованный на меге 2560. К этому контроллеру подключены: - газовый котел АОГВК-35 с клапаном ХОнивелл, который позволяет подключить удаленный термостат; - 4 циркулящионных насоса для гидрострелки, теплотрассы, теплого пола и радиаторов; - 6 датчиков темпратуры (DS18b20); - 5 реле для управления газовым клапаном и насосами. Информация о температуре выводится на LCD 1620, еще имеется 5 кнопок (2 для управления меню дисплея и 3- для выбора режима работы термостата. В контроллере реализована погодозависимая регулировка температуры воды в котле. На контроллере реализован сервер, есть возможность выводить информацию в локальную сеть. Из наблюдений за работой СВОЕЙ системы отопления могу сказать, что: - управление котлом по времени суток наиболее целесообразно в конце весны и в начале осени; - в управлении котлом кроме наружной температуры необходимо учитывать и скорость ветра (особенно актуально в Ростовской области, где зимой дуют сильные и холодные восточные ветры).
In file included from C:\Documents and Settings\Solar User\My Documents\Arduino\termostat_na_den_2\termostat_na_den_2.ino:3:0:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In constructor 'EEPROMBackupVar<T>::EEPROMBackupVar(T, int, compareFunction, long unsigned int)':
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:41:24: error: 'struct EEPROMClass'has no member named 'getAddress'
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function 'void EEPROMBackupVar<T>::initialize(T)':
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:48:24: error: 'struct EEPROMClass'has no member named 'getAddress'
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function 'void EEPROMBackupVar<T>::save()':
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:108:10: error: 'struct EEPROMClass'has no member named 'updateBlock'
EEPROM.updateBlock<T>(address(position), var);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:108:23: error: expected primary-expression before '>'token
EEPROM.updateBlock<T>(address(position), var);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function 'void EEPROMBackupVar<T>::restore()':
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:116:11: error: 'struct EEPROMClass'has no member named 'readBlock'
EEPROM.readBlock<T>(address(position), var);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:116:22: error: expected primary-expression before '>'token
EEPROM.readBlock<T>(address(position), var);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function 'void EEPROMBackupVar<T>::retreive(T)':
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:127:11: error: 'struct EEPROMClass'has no member named 'readBlock'
EEPROM.readBlock<T>(address(i), candidateValue);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:127:22: error: expected primary-expression before '>'token
EEPROM.readBlock<T>(address(i), candidateValue);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function 'void EEPROMBackupVar<T>::clearMemory(T)':
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:146:11: error: 'struct EEPROMClass'has no member named 'writeBlock'
EEPROM.writeBlock<T>(address(i), init);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:146:23: error: expected primary-expression before '>'token
EEPROM.writeBlock<T>(address(i), init);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\termostat_na_den_2\termostat_na_den_2.ino: In function 'void setup()':
termostat_na_den_2:129: error: 'struct EEPROMClass'has no member named 'setMaxAllowedWrites'
EEPROM.setMaxAllowedWrites(32767);
^
termostat_na_den_2:139: error: 'struct EEPROMClass'has no member named 'updateBlock'
EEPROM.updateBlock(int(&TimerEE[i]), Timer[i]);
^
termostat_na_den_2:141: error: 'struct EEPROMClass'has no member named 'updateFloat'
EEPROM.updateFloat(int(&TstatTempEE), TstatTemp);
^
termostat_na_den_2:142: error: 'struct EEPROMClass'has no member named 'updateByte'
Долго не мог добраться до термостата, всё праздники да праздники. Этот пост читал уже давно. Вобщем много танцев, много бубнов разных племён и ничего. И вот решил перенести с работы всё домой. И о чудо! На вин7 с первого раза, чистая установка последней среды и библиотек напрямую с поиска, всё компилится и всё работает. А ноут на работе остался загадкой, жаль там винду нельзя перебить, думаю проблема в ней.
Добрый день vlad219i ваш проэкт очень интерестный но если можно было бы сделать LED 16:2 подключение по I2С То вобще было бы шикарно, я бы сам сделал только я рукажоп начинающий и могу создать код только мигающего светодиода =)
Доброго времени суток! По поводу библиотек к скетчу на 4ой странице. Не могу найти hd44780.h, RealTimeClockDS1307.h, EEPROM.h, TimerOne.h ... Гопода помогите найти - ткните носом!!!! И какая версия компилятора?
С энкодером, наверное не получится , а с LCD Shield должно (кнопок больше и свободных пинов). Там 5 свободных цифровых выходов. 4 канала под реле, а 5-й можно на зуммер оставить.
Сам могу как и многие лишь моргать светодиодами.
Допилить кто может? можно за небольшое вознаграждение.
...для реализации погодозависимого регулятора нужно либо иметь уличный термодатчик, заточенный под конкретный котёл (датчик стОит зачастую несуразных денег, да и вход для подключения этого датчика предусмотрен не в каждом котле), либо знать протокол, используемый при подключении оригинального терморегулятора данфосс с возможностью управления модулятором (такая возможность есть тоже не везде). В общем - покопав инет на эту тему, я остановился на комнатном термостате. Вопрос дыхания/телевизора/плиты решён установкой термодатчика в определённом месте помещения (в тени, вдали от шальных потоков воздуха, батарей и прочих катаклизмов).
Вкратце - я ж говорю, меня устраивает. Вариант с уличным датчиком наверняка во сто крат правильнее и экономичнее, но в моём случае room thermostat'а мне хватает за глаза. Елозить установкой температуры теплоносителя больше не приходится, ну и, к примеру, котёл реже включается, если солнце через окна подогревает дом, - дык этого я и добивался. :-)
Или вы меня не понимаете или заблуждаетесь.
1. Уличный датчик- тот же самый DS18B20, он не стОит несуразных денег.
2. Протокол нас не интересует- лезть в газовый котёл так глубоко даже опасно. Единственное ЛЕГАЛЬНОЕ место куда можно подключаться как раз и есть те самые контакты комнатного термостата.
А дальше всё просто - второй датчик на подачу котла, как температура достигла необходимого нам значения- выключяем, как упала ниже порога - включаем.
У меня два года у соседа работала Ардуина с блоком реле и ЖКИ шилдом на котле Junkrs Eurostar который по своей природе на минимуме регулятора не мог выдавать меньше 45-50 градусов, а его тёплый пол при этом становился горячим пока не сработают клапана на рейке, потом они отключались и когда снова включались - пол уже был ледяной. И так всё время колебания: холодный-горячий
Ардуина выключала котёл при 32 и снова включала при 28 (условно) Все клапана пола были НАВСЕГДА открыты! Регулировка вручную температура-гистерезис, стабильная температура в доме, сосед был доволен.
У меня у самого дома котёл с погодником- Junkers Cerapur, как лет 8 назад отрегулировал наклон графика так и работает. Клапана тёплого пола тоже сняты навсегда за ненадобностью
у нас уличный датчик стоит данфосс - как часы все работает!
Здравствуйте. Реализовал для загородного дома контроллер и добавил к нему модем siemens mc35i, подключаемый через com порт, для управления изменением температуры и получения уведомления о снижении ниже уровня, а также получания статуса. Огромное спасибо автору проекта vlad219i.
Реализовал для загородного дома контроллер и добавил к нему модем siemens mc35i
ЗдОрово, это как раз то, до чего у меня руки не дошли пока. :-)
Поинтересуюсь - для управления и получения статуса используются смс или интернет-подключение? Лет 10 назад я делал нечто похожее (дачная двусторонняя сигналка), там использовал СМС-команды.
Первую фотку удалось открыть только по прямой ссылке, остальные - не видно никак, а жаль.
Отличный термостатик получился. Автору премного благодарностей! Если кому интересно есть код на два модуля с радиосвязью по RF24.
Два модуля-это два разных канала, с независимым термостатированием??
Если да, то мне очень нужно.
Rukey скажите где брать такие корпуса??? Они идеальны. Я так понимаю, держатели под каждый модуль вы приклеиваете самостоятельно. И по сути это конструктор. Откройте для меня тайну, плиз!!
Отличный термостатик получился. Автору премного благодарностей! Если кому интересно есть код на два модуля с радиосвязью по RF24.
Два модуля-это два разных канала, с независимым термостатированием??
Если да, то мне очень нужно.
Rukey скажите где брать такие корпуса??? Они идеальны. Я так понимаю, держатели под каждый модуль вы приклеиваете самостоятельно. И по сути это конструктор. Откройте для меня тайну, плиз!!
Нет, это значит что модуль с экраном и датчиком висит в комнате, а в котёл засунул вторую ардуинку нано с релюшкой и они общаются через РФ-ку. Хотя собрать ещё один модуль для второго помещения и отправлять команду на тот-же адрес РФ-ки в котле и всё. Код одинаков, только адреса радиомодуей выставить.
Доброй ночи, товарищи! Вот сваял, поставил, все работает, автору респект, все устраивает. Немного допилил под свои задачи, три месяца, полет нормальный. Ежедневный отчет по GSM приходит исправно. Управляется по GSM тоже ок. Можно даже позвонить туда и оттуда и полноценно поговорить. Но вот сижу и думаю... Хата моя стоит за 300 км от меня, хорошо, что в скетче автора не используется адрес датчика DS, заменить сможет любой электрик. А если адрес датчика прописан и он накрылся? тогда брать ноут и новый датчик и ехать менять, т.к. без скетча никто ничего не сможет сделать. Далее, возникла идея прикрутить еще датчик на улицу. Тут уже адрес нужен и ремонтопригодность сводится к 1 человеку, который это сделал. Вот сижу и думаю, ведь можно и без адресов опросить все датчики на шине, но как контроллер узнает, какой из них какой, если адреса не прописаны? Но если это реализовать, то обслуживание и ремонт наших термостатов упростится в разы. Может, у кого есть идеи по этому поводу?
Далее, возникла идея прикрутить еще датчик на улицу. Тут уже адрес нужен и ремонтопригодность сводится к 1 человеку, который это сделал. Вот сижу и думаю, ведь можно и без адресов опросить все датчики на шине, но как контроллер узнает, какой из них какой, если адреса не прописаны?
Никак не узнает.
Я в подобном случае каждый датчик на отдельный пин вешал и адрес датчика не использовал.
Думаю, что мега тут - слишком жирно. LCD можно подключить по i2c, порты расширить 75hc595 (заодно светодиодов для индикации добавить). Ну это так - навскидку...
У меня умный дом немного по-другому организован. База на меге + LCD + LAN-шилд + датчики уличной температуры-давления-влажности + RF24, а уже через RF24 опрашиваются дополнительные модули (контроллер насоса, термостат и тд и тп). Все данные собираются-обрабатываются в меге и отправляются через мобильный инет на сервак, где и ведутся логи и генерятся странички с удобочитаемыми отчётами. Таким образом, из любой точки с интернетом есть доступ к инфе по дому.
else{Serial.print("TIMER OFF ");Serial.print("TSET/TT=");Serial.print(TstatTemp);Serial.print("/");Serial.print(Temperature);Serial.print(" HIST=");Serial.print(Hysteresis);}
Отличный термостатик получился. Автору премного благодарностей! Если кому интересно есть код на два модуля с радиосвязью по RF24.
Два модуля-это два разных канала, с независимым термостатированием??
Если да, то мне очень нужно.
Rukey скажите где брать такие корпуса??? Они идеальны. Я так понимаю, держатели под каждый модуль вы приклеиваете самостоятельно. И по сути это конструктор. Откройте для меня тайну, плиз!!
Спасибо. Корпус проектировал в инвенторе, печатал на 3д принтере. Территориально работаю в Москве, могу распечатать. Ну если проблема, можно файлы выложить для самостоятельной печати, если из другого города есть где распечатать.
Вот плод моих ночных бдений в свободное от работы и семьи время.
Сразу скажу, чтоб потом помидорами не закидали, что язык я специально не изучал, а с программированием до этого сталкивался еще в школе. Так что может быть скетч написан не идеально и м.б. косячки. Работу скетча проверял только на макетке. В бою не тестировал.
vlad219i написал отличный скетч, но бывает так (как у меня), что система отопления состоит не только из одного котла. Например, у меня есть котел, к которому через гидрострелку подключены радиаторы, теплый пол, бойлер косвенного нагрева. Есть 3 насоса+насос рециркуляции ГВС. И всем этим хозяйством нужно управлять. Вот я и решил немного допилить скетч vlad219i.
Изменения:
-изменение температуры термостатирования перенесено в меню
-вращение энкодера до входа в меню теперь переключает каналы термостатирования
-реле основное вкл/выкл котел, замыкает и размыкает контакты термостата котла на плате.
-3 свободных аналоговых входа использованы для подключения сдвигового регистра 74HC595. Т.о. имеем 8 цифровых выходов, к которым подключены реле для управления 8 каналами. По каждому из каналов скетч работает как у vlad219i-задается температура и поддерживается, сохранено время миниманальной паузы между включениями/выключениями. Таймеры сохранены.
Когда по каждому из 8 каналов нет запроса на тепло, то выключается основное реле и котел гаснет.
Таким образом можно регулировать температуру в 8-ми комнатах отдельно, да ещё и по расписанию. Если конечно есть исполнительные устройсва в этих комнатах.
Я планирую управлять температурой в комнатах (один датчик на все комнаты) с помощью вкл/выкл насоса радиаторов. Аналогично для теплого пола и бойлера косвенного нагрева.
Цель-экономия газа. Скетч позволит мне снижать температуру в комнатах в ночное время или во время пребывания на работе, отключать теплый пол теплый пол на ночь, не греть бойлер когда это не нужно и разогревать его максимально в пиковые часы.
Будет ли экономия существенной я узнаю только следующей зимой. А пока это только мечты...
А вообще, лично бы я пострелся ставить что-то что может самостоятельно "зажигать гас". Я бы ставил только больше/меньше. Причем, как правильно заметил JollyBiber нужен еще контроль что "горении идет".
Если погасло (не важно по каким причинам), все перекрывать, слать SMS хозяину, и... по хорошему, еще бы и себя обесточить.
А уж он пусть приходить и разбирается "чего погасло". И запуск только "в ручном режиме".
А если уж хочется все-таки "запуск", то только сертифицированное что-то. Оно потому столько и стоит, что учтена куча всяких нюансов, используются промышленные версии камней, проведены испытания "наработки на отказ.." и т.д. и т.п. (ну по крайней мере в идеале так должно быть. если это не так, то хоть мучать угрызения совести потом не будут. что жаба задавила).
А меня задавила. Имелся работающий котёл (примерно 13 лет эксплуатации). Имелась в нем одна доработка - заглушен контур ГВ, так как оказался самой не надёжной частью и требовал замены уплотнительных резинок. С заглушеным контуром ГВ проработал примерно 9 лет. А осенью 16го автоматика стала выдавать ошибки бредовые. Вызвал ремонтника профессионала, который с ходу сказал, что так долго не живут, мало того, что заказать такую же автоматику не получится, так и железо-то наверняка всё покрыто накипью и прочими отложениями, и насос та уже стаскался... короче есть у них на складе новые котлы из старой партии, и потому они его могут мне продать всего за 750 евросов...
И тут меня жаба и задавила. Я сам занимаюсь установкой и настройкой готового оборудования и автоматики отопления и ГВС в частных и многоквартирных домах. А тут ещё ради интереса прошлой весной собрал на Ардуино простой термостат с управлением сервоприводом трехходового клапана.
В общем как про 750 евро услышал, у меня сразу мысль родилась, попробовать Ардуино подружить с моим котлом. И так как я в схемотекнике не опытен был, и до этого паял только лампочки на новогодней гирлянде, поэтому прект ещё не закончил. Но много сделано, котёл запускается, датчик ионизаци простой как две копейки собрал сам, температуру нагнали - выключили клапана. Сбой электричества, свет пропал - даже при горящем котле - клапана закрылись. И так уже два месяца работает черновой вариант.
Ардуино - 6 евро. Остальное выпаяно из старой автоматики и старого же компьютерного монитора с кинескопом. Бонусом тернеровка серого вещества, новые знания и бесценный опыт.
И никакого привлечения военокосмического комплекса с нанотехнологиями не надо. Просто надо понимать, с чем имеешь дело.
А ещё... Хотел сказать по поводу диферамб в сторону наружнего датчика и погодозависимых графиков...
Во всех автоматиках, по крайней мере, что у нас в Эстонии продают, предусмотрен вход для подключения комнатного датчика. Именно с таким датчиком система работает более эффективно (читай экономно) и комфортней для потребителя. Устанавливать датчик конечно надо с учетом конкретного помещения и ньюансов там не мало.
Утверждать, что погодозависимого управления по графику достаточно для системы отопления дома - можно. Но утверждение, что это лучше управления по температуре внутреннего воздуха противоречит практике.
1. А осенью 16го автоматика стала выдавать ошибки бредовые.
2. Я сам занимаюсь установкой и настройкой готового оборудования и автоматики отопления и ГВС в частных и многоквартирных домах. А тут ещё ради интереса прошлой весной собрал на Ардуино простой термостат с управлением сервоприводом трехходового клапана.
3. И так как я в схемотекнике не опытен был, и до этого паял только лампочки на новогодней гирлянде, поэтому прект ещё не закончил. Но много сделано, котёл запускается, датчик ионизаци простой как две копейки собрал сам, температуру нагнали - выключили клапана. Сбой электричества, свет пропал - даже при горящем котле - клапана закрылись. И так уже два месяца работает черновой вариант.
4. Ардуино - 6 евро. Остальное выпаяно из старой автоматики и старого же компьютерного монитора с кинескопом. Бонусом тернеровка серого вещества, новые знания и бесценный опыт.
5. А ещё... Хотел сказать по поводу диферамб в сторону наружнего датчика и погодозависимых графиков...
Во всех автоматиках, по крайней мере, что у нас в Эстонии продают, предусмотрен вход для подключения комнатного датчика. Именно с таким датчиком система работает более эффективно (читай экономно) и комфортней для потребителя. Устанавливать датчик конечно надо с учетом конкретного помещения и ньюансов там не мало.
6. Утверждать, что погодозависимого управления по графику достаточно для системы отопления дома - можно. Но утверждение, что это лучше управления по температуре внутреннего воздуха противоречит практике.
1. Автоматика показывает ошибки, а вы ее на помойку и все напрямую подключили... однако... (храни вас Господь)
2. Это хорошо когда разбираешься в теме. Вон хирург Леонид Рогозов сам себе аппендикс вырезал, но у него была делема жизнь или смерть. Вы тоже по этому пути собрались? Напишите в своем посте большими буквами "так делать нельзя" все делаете на свой страх и риск...
3. В схеммотехнике придется разбираться и в программировании тоже, ибо просто при пропадании электричества закрыть газ это мало...
4. Да-да 6 евро это стоимость кристала для любителей сделать что то своими руками. В нашем случае все делают устройство которое заставляет котел работать так как нам кажется правильно. Причем мы не лезем в защиту котла, а тупо используем контакты выведенные производителем для наружного таймера. При выходе из строя нашего изделия котел будет работать как и прежде в штатном режиме... Это единственно правильное решение, ибо 97% тех кто делает что то на ардуино, не знает (да это и не нужно) как работает автоматика того же котла...
5. Посмотрел температуру в Эстонии по зимним месяцам, смешно стало... Эффективно и экономно не одно и тоже... (читай п.6)
6. Зачем греть воду до максимума когда за окном +5? Проблема регулировки по температуре помещения это черезмерно горячие батареи, как следствие сухой воздух в помещении, как следствие статическое напряжение на всем... Ну и еще много чего можно сказать в зависимости от строения и степени его утепления и т.д.
leshak правильно сказал, что должно быть что то сертифицированное в части управления котлом... именно в управлении, а не регулировании температуры...
Как связана сухость воздуха в помещении с температурой батарей, если в помещении нормальная температура?
На прямую. В отопительный сезон горячие батареи сушат воздух, проветривание помещения в мороз также сушит воздух. Так что температура в помещении ни при чем. А чем больше температура батареи тем интенсивнее происходит "осушение". Как то так...
В п. 6 можно еще указать что при инерционной системе при регулировании по температуре в помещении могут окна начать "плакать"... своего рода увлажнение... :)
Просто нельзя утверждать что по температуре в помещении регулировка самая-самая... (и она подтверждена практикой в конкретной климатической зоне) Очень много разных факторов влияют на комфорт в помещении в период отопления.
это к этому:
#include <hd44780.h>
0003
#include <hd44780ioClass/hd44780_I2Cexp.h>
Выдает ошибку..а какую библиотеку использовали?
LCD к градуснику не причем.
надо внимательно просмотреть и прозвонить цепи. бывает.
может отвалилось чего...
только что всё проверил - всё так - может среда должна быть другая, например 1.0.1
[/quote]
только что всё проверил - всё так - может среда должна быть другая, например 1.0.1
[/quote]
первая же работает.
может его на ногу А2 подвесить и в скетче подправить. (нога пустая)
только что всё проверил - всё так - может среда должна быть другая, например 1.0.1
[/quote]
первая же работает.
может его на ногу А2 подвесить и в скетче подправить. (нога пустая)
[/quote]
пробую
Всё запустил!!! на А2 не пошёл, вернул на А1 заработал!!!
При каких условиях вкл реле???
Мистика )))
Реле управляется низким уровнем. При Т в комнате = заданной
Обратите внимание - в скетче есть задержка включения реле, чтобы оно не слишком часто котёл дёргало. Это я к тому, что стОит немного подождать перед тем, как писать, что реле не включается. :-))
Обратите внимание - в скетче есть задержка включения реле, чтобы оно не слишком часто котёл дёргало. Это я к тому, что стОит немного подождать перед тем, как писать, что реле не включается. :-))
Спасибо за проет!!!!!
Уже давно заметил, сейчас всё в порядке!!! РАБОТАЕТ!!!!
Виктор R2PM
Больше человеческое спасибо за проэкт!
Есть мысли прикрутить сюда модуль wi-fi для связи с внешним миром?
wi-fi - нет, rf24 - да, мысли есть, а вот времени на это развлечение пока нет. :-)
а как добавить датчиков температуры хочу один на улицу выкинуть а второй на котел поставить чисто для мониторинга у меня дисплей 20х4 места для вывода предостаточно
https://www.youtube.com/watch?v=Vh-LATHeHas
Третий сезон работает самодельный контроллер отопления, реализованный на меге 2560. К этому контроллеру подключены: - газовый котел АОГВК-35 с клапаном ХОнивелл, который позволяет подключить удаленный термостат; - 4 циркулящионных насоса для гидрострелки, теплотрассы, теплого пола и радиаторов; - 6 датчиков темпратуры (DS18b20); - 5 реле для управления газовым клапаном и насосами. Информация о температуре выводится на LCD 1620, еще имеется 5 кнопок (2 для управления меню дисплея и 3- для выбора режима работы термостата. В контроллере реализована погодозависимая регулировка температуры воды в котле. На контроллере реализован сервер, есть возможность выводить информацию в локальную сеть. Из наблюдений за работой СВОЕЙ системы отопления могу сказать, что: - управление котлом по времени суток наиболее целесообразно в конце весны и в начале осени; - в управлении котлом кроме наружной температуры необходимо учитывать и скорость ветра (особенно актуально в Ростовской области, где зимой дуют сильные и холодные восточные ветры).
День добрый.
Первый вариант кода работает отлично. Со вторым появляется ошибка при компиляции, уже и переустанавливал среду и все библиотеки, ошибка одна и та же:
In file included from C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:9:0,
from C:\Documents and Settings\Solar User\My Documents\Arduino\termostat_na_den_2\termostat_na_den_2.ino:3:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMex.h:159:22: error: conflicting declaration
'EEPROMClassEx EEPROM'
extern
EEPROMClassEx EEPROM;
^
In file included from C:\Documents and Settings\Solar User\My Documents\Arduino\termostat_na_den_2\termostat_na_den_2.ino:1:0:
C:\Program Files\Arduino\hardware\arduino\avr\libraries\EEPROM\src/EEPROM.h:145:20: note: previous declaration
as
'EEPROMClass EEPROM'
static
EEPROMClass EEPROM;
^
In file included from C:\Documents and Settings\Solar User\My Documents\Arduino\termostat_na_den_2\termostat_na_den_2.ino:3:0:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In constructor
'EEPROMBackupVar<T>::EEPROMBackupVar(T, int, compareFunction, long unsigned int)'
:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:41:24: error:
'struct EEPROMClass'
has no member named
'getAddress'
baseAddress = EEPROM.getAddress(
sizeof
(T)*addressRange);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function
'void EEPROMBackupVar<T>::initialize(T)'
:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:48:24: error:
'struct EEPROMClass'
has no member named
'getAddress'
baseAddress = EEPROM.getAddress(
sizeof
(T)*addressRange);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function
'void EEPROMBackupVar<T>::save()'
:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:108:10: error:
'struct EEPROMClass'
has no member named
'updateBlock'
EEPROM.updateBlock<T>(address(position), var);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:108:23: error: expected primary-expression before
'>'
token
EEPROM.updateBlock<T>(address(position), var);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function
'void EEPROMBackupVar<T>::restore()'
:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:116:11: error:
'struct EEPROMClass'
has no member named
'readBlock'
EEPROM.readBlock<T>(address(position), var);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:116:22: error: expected primary-expression before
'>'
token
EEPROM.readBlock<T>(address(position), var);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function
'void EEPROMBackupVar<T>::retreive(T)'
:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:127:11: error:
'struct EEPROMClass'
has no member named
'readBlock'
EEPROM.readBlock<T>(address(i), candidateValue);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:127:22: error: expected primary-expression before
'>'
token
EEPROM.readBlock<T>(address(i), candidateValue);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h: In member function
'void EEPROMBackupVar<T>::clearMemory(T)'
:
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:146:11: error:
'struct EEPROMClass'
has no member named
'writeBlock'
EEPROM.writeBlock<T>(address(i), init);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\libraries\EEPROMex-master/EEPROMBackupVar.h:146:23: error: expected primary-expression before
'>'
token
EEPROM.writeBlock<T>(address(i), init);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\termostat_na_den_2\termostat_na_den_2.ino: In function
'void setup()'
:
termostat_na_den_2:129: error:
'struct EEPROMClass'
has no member named
'setMaxAllowedWrites'
EEPROM.setMaxAllowedWrites(32767);
^
termostat_na_den_2:139: error:
'struct EEPROMClass'
has no member named
'updateBlock'
EEPROM.updateBlock(
int
(&TimerEE[i]), Timer[i]);
^
termostat_na_den_2:141: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&TstatTempEE), TstatTemp);
^
termostat_na_den_2:142: error:
'struct EEPROMClass'
has no member named
'updateByte'
EEPROM.updateByte(
int
(&BeepEnabledEE), BeepEnabled);
^
termostat_na_den_2:143: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&TemperatureCorrEE), TemperatureCorr);
^
termostat_na_den_2:144: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&HysteresisEE), Hysteresis);
^
termostat_na_den_2:145: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&AlarmTempEE), AlarmTemp);
^
termostat_na_den_2:156: error:
'struct EEPROMClass'
has no member named
'readByte'
BeepEnabled = EEPROM.readByte(
int
(&BeepEnabledEE));
^
termostat_na_den_2:157: error:
'struct EEPROMClass'
has no member named
'readFloat'
TstatTemp = EEPROM.readFloat(
int
(&TstatTempEE));
^
termostat_na_den_2:158: error:
'struct EEPROMClass'
has no member named
'readFloat'
TemperatureCorr = EEPROM.readFloat(
int
(&TemperatureCorrEE));
^
termostat_na_den_2:159: error:
'struct EEPROMClass'
has no member named
'readFloat'
Hysteresis = EEPROM.readFloat(
int
(&HysteresisEE));
^
termostat_na_den_2:160: error:
'struct EEPROMClass'
has no member named
'readFloat'
AlarmTemp = EEPROM.readFloat(
int
(&AlarmTempEE));
^
termostat_na_den_2:163: error:
'struct EEPROMClass'
has no member named
'readBlock'
EEPROM.readBlock(
int
(&TimerEE[i]), Timer[i]);
^
C:\Documents and Settings\Solar User\My Documents\Arduino\termostat_na_den_2\termostat_na_den_2.ino: In function
'void loop()'
:
termostat_na_den_2:270: error:
'struct EEPROMClass'
has no member named
'readFloat'
if
((EEPROM.readFloat(
int
(&TstatTempEE)) != TstatTemp)&(Seconds == 0)) {
^
termostat_na_den_2:271: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&TstatTempEE), TstatTemp);
^
termostat_na_den_2:281: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&TstatTempEE), TstatTemp);
^
termostat_na_den_2:497: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&HysteresisEE), Hysteresis);
// запись в ЕЕПРОМ
^
termostat_na_den_2:544: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&TemperatureCorrEE), TemperatureCorr);
// запись в ЕЕПРОМ
^
termostat_na_den_2:550: error:
'struct EEPROMClass'
has no member named
'readFloat'
TemperatureCorr = EEPROM.readFloat(
int
(&TemperatureCorrEE));
^
termostat_na_den_2:586: error:
'struct EEPROMClass'
has no member named
'updateByte'
EEPROM.updateByte(
int
(&BeepEnabledEE), BeepEnabled);
^
termostat_na_den_2:589: error:
'struct EEPROMClass'
has no member named
'readByte'
BeepEnabled = EEPROM.readByte(
int
(&BeepEnabledEE));
^
termostat_na_den_2:627: error:
'struct EEPROMClass'
has no member named
'updateFloat'
EEPROM.updateFloat(
int
(&AlarmTempEE), AlarmTemp);
// запись в ЕЕПРОМ
^
termostat_na_den_2:633: error:
'struct EEPROMClass'
has no member named
'readFloat'
AlarmTemp = EEPROM.readFloat(
int
(&AlarmTempEE));
^
C:\Documents and Settings\Solar User\My Documents\Arduino\termostat_na_den_2\termostat_na_den_2.ino: In function
'void TimerXSetup(int)'
:
termostat_na_den_2:853: error:
'struct EEPROMClass'
has no member named
'updateBlock'
EEPROM.updateBlock(
int
(&TimerEE[X]), Timer[X]);
^
termostat_na_den_2:885: error:
'struct EEPROMClass'
has no member named
'updateBlock'
EEPROM.updateBlock(
int
(&TimerEE[X]), Timer[X]);
^
termostat_na_den_2:891: error:
'struct EEPROMClass'
has no member named
'readBlock'
EEPROM.readBlock(
int
(&TimerEE[X]), Timer[X]);
^
termostat_na_den_2:903: error:
'struct EEPROMClass'
has no member named
'readBlock'
EEPROM.readBlock(
int
(&TimerEE[X]), Timer[X]);
^
termostat_na_den_2:905: error:
'struct EEPROMClass'
has no member named
'updateBlock'
EEPROM.updateBlock(
int
(&TimerEE[X]), Timer[X]);
^
exit status 1
'struct EEPROMClass'
has no member named
'setMaxAllowedWrites'
стр. 2 сообщение №85
Или вот здесь почитайте https://forum.pjrc.com/threads/24589-Another-library-fails-to-compile%E2..., нужно установить патч
Добрый день vlad219i, очень хорошая статья. Не смог найти схему подключения в вашем Посте.
стр. 2 сообщение №85
Или вот здесь почитайте https://forum.pjrc.com/threads/24589-Another-library-fails-to-compile%E2..., нужно установить патч
Долго не мог добраться до термостата, всё праздники да праздники. Этот пост читал уже давно. Вобщем много танцев, много бубнов разных племён и ничего. И вот решил перенести с работы всё домой. И о чудо! На вин7 с первого раза, чистая установка последней среды и библиотек напрямую с поиска, всё компилится и всё работает. А ноут на работе остался загадкой, жаль там винду нельзя перебить, думаю проблема в ней.
Добрый день vlad219i, очень хорошая статья. Не смог найти схему подключения в вашем Посте.
Её там и нет. Подключение железа описано в самом начале скетча в дефайнах, там всё прозрачно.
Спасибо.
Добрый день vlad219i ваш проэкт очень интерестный но если можно было бы сделать LED 16:2 подключение по I2С То вобще было бы шикарно, я бы сам сделал только я рукажоп начинающий и могу создать код только мигающего светодиода =)
странициы две назад есть код для I2C
СПС
Доброго времени суток! По поводу библиотек к скетчу на 4ой странице. Не могу найти hd44780.h, RealTimeClockDS1307.h, EEPROM.h, TimerOne.h ... Гопода помогите найти - ткните носом!!!! И какая версия компилятора?
спасибо автору за скетч. Все работает на 5.
Было бы здорово сделать термостат многоканальным. Т.е. отдельно управлять радиаторами, теплым полом и бойлером косвенного нагрева.
У бойлера, например свои температуры, свой гистерезис
001
// Термостат, до пяти датчиков DS18B20.
002
//
003
004
// v 0.2.1 beta
005
006
#include <EEPROM.h>
007
#include <OneWire.h>
008
#include <DallasTemperature.h>
009
#include <LiquidCrystal.h>
010
011
OneWire oneWire(15);
// A1 вход датчиков 18b20
012
DallasTemperature ds(&oneWire);
013
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
014
015
016
const
byte
OUT[5] = {13, 12, 11, 3, 2};
// номера выходов
017
018
byte
pos;
019
byte
tempOUTon[5];
// массив с температурой включения выхода
020
float
tempSensor[5];
// массив куда читается температура
021
float
Hysteresis = 0.5;
// гистерезис
022
byte
qty;
// количество градусников на шине.
023
024
// адреса градусников.
025
DeviceAddress sensor1 = {0x28, 0xFF, 0x08, 0x26, 0x34, 0x16, 0x04, 0x68};
026
DeviceAddress sensor2 = {0x28, 0xFF, 0x01, 0x4D, 0x34, 0x16, 0x04, 0x9F};
027
DeviceAddress sensor3 = {0x28, 0xFF, 0x75, 0x2D, 0x34, 0x16, 0x03, 0xFE};
028
DeviceAddress sensor4 = {0x28, 0xFF, 0x084, 0x4D, 0x34, 0x16, 0x04, 0x86};
029
DeviceAddress sensor5 = {0x28, 0xFF, 0x4D, 0x28, 0x34, 0x16, 0x03, 0xC5};
030
031
byte
gradus[8] = {
032
0b00110,
033
0b01001,
034
0b01001,
035
0b00110,
036
0b00000,
037
0b00000,
038
0b00000,
039
0b00000
040
};
041
byte
on[8] = {
042
0b00110,
043
0b01111,
044
0b11111,
045
0b11111,
046
0b11111,
047
0b11111,
048
0b11110,
049
0b01100
050
};
051
052
void
readSet(){
053
for
(
byte
i = 0; i < qty; i++) tempOUTon[i] = EEPROM.read(i);
054
}
055
056
void
outOff(){
// выключает выходы
057
for
(
byte
i = 0; i < qty; i++) digitalWrite(OUT[i], LOW);
058
}
059
060
void
erorr(){
// останавливает работу программы и сигнализирует ошибку
061
outOff();
// выключаем выходы
062
lcd.clear();
063
lcd.print(
"sensor error"
);
064
while
(1){
// крутим бесконечный цикл
065
analogWrite(10, 100);
066
delay(500);
067
analogWrite(10, 255);
068
delay(500);
069
}
070
}
071
////
072
073
byte
key(){
//// для кнопок ЛСДшилда
074
int
val = analogRead(0);
075
if
(val < 50)
return
5;
076
else
if
(val < 150)
return
3;
077
else
if
(val < 350)
return
4;
078
else
if
(val < 500)
return
2;
079
else
if
(val < 800)
return
1;
080
else
return
0;
081
}
082
////
083
084
void
setMenu(
byte
pos){
// установка температуры
085
outOff();
086
lcd.clear();
087
088
while
(1){
// крутим бесконечный цикл
089
byte
KEY = key();
// читаем состояние кнопок
090
091
lcd.setCursor(2, 0);
// выводим на экран
092
lcd.print(
"Out:"
);
093
lcd.print(pos+1);
094
lcd.setCursor(9, 0);
095
lcd.print(
"Pin:"
);
096
lcd.print(OUT[pos]);
097
lcd.print(
" "
);
098
099
lcd.setCursor(0, 1);
100
lcd.print(
" PinON: "
);
101
lcd.print(tempOUTon[pos]);
102
lcd.write(1);
103
lcd.print(
"C <>"
);
104
105
if
(key() == 4
106
{TimeOUTon[pos]--;
// изменяем значение
107
if
(TimeOUTon[pos] > 125) tempOUTon[pos] = 0;
108
EEPROM.write(
109
}
110
if
(key() == 3 && pos != 0) pos--;
111
112
if
(KEY == 2) {
// если нажата кнопка
113
tempOUTon[pos]--;
// изменяем значение
114
if
(tempOUTon[pos] > 125) tempOUTon[pos] = 0;
115
EEPROM.write(pos, tempOUTon[pos]);
// сохраняем в еепром
116
}
117
else
if
(KEY == 5){
118
tempOUTon[pos]++;
119
if
(tempOUTon[pos] > 125) tempOUTon[pos] = 0;
120
EEPROM.write(pos, tempOUTon[pos]);
121
}
122
123
delay(200);
124
}
/////
125
}
//
126
127
void
getTemp(){
// читаем температуру и заполняем массив
128
ds.requestTemperatures();
129
130
tempSensor[0] = ds.getTempC(sensor1);
// немного китайского кода
131
tempSensor[1] = ds.getTempC(sensor2);
132
tempSensor[2] = ds.getTempC(sensor3);
133
tempSensor[3] = ds.getTempC(sensor4);
134
tempSensor[4] = ds.getTempC(sensor5);
135
}
136
137
void
sensorTest(){
// ищим датчики на шине, если количество изменилось, останавливаем работу
138
ds.begin();
139
if
(ds.getDeviceCount() != qty) erorr();
140
}
141
142
void
setup
() {
143
// Serial.begin(9600);
144
ds.begin();
145
qty = ds.getDeviceCount();
// при включении, сохраняем количество градусников,
146
// можно и лучше количество указать руками.
147
148
for
(
int
i = 0; i < qty; i++) pinMode(OUT[i], OUTPUT);
149
for
(
int
i = 0; i < qty; i++) digitalWrite(OUT[i], LOW);
150
151
lcd.createChar(1, gradus);
152
lcd.createChar(2, on);
153
lcd.begin(16, 2);
154
lcd.clear();
155
156
readSet();
// читаем настройки из еепром
157
}
158
159
void
loop
() {
160
if
(key() == 1) setMenu(pos);
// если нажата селект, уходим в настройки
161
162
else
if
(key() == 4 && pos < qty-1) pos++;
// крутим позицию
163
else
if
(key() == 3 && pos != 0) pos--;
164
165
166
sensorTest();
// тест наличия градусников на шине
167
getTemp();
// читаем температуру с датчиков
168
169
//// сверяем температуру и управляем выходами
170
for
(
int
i = 0; i < qty; i++)
171
{
if
(tempSensor[i] > (tempOUTon[i]+Hysteresis))
172
if
( digitalRead(OUT[i]) == HIGH )
// если горелка включена
173
digitalWrite(OUT[i], LOW);
174
175
176
if
(tempSensor[i] < tempOUTon[i])
177
if
( digitalRead(OUT[i]) == LOW )
// если горелка выключена
178
digitalWrite(OUT[i], HIGH);
179
}
180
181
/////// вывод инфы на экран
182
lcd.setCursor(0, 0);
183
lcd.print(pos+1);
184
lcd.print(
". "
);
185
lcd.print(tempSensor[pos], 1);
// показываем температуру с датчика
186
lcd.write(1);
187
lcd.print(
"C "
);
188
lcd.setCursor(2, 1);
189
lcd.print(tempOUTon[pos]);
// показываем установленную температуру
190
lcd.write(1);
191
lcd.print(
"C "
);
192
193
lcd.setCursor(11, 0);
// рисуем статус выхода
194
for
(
int
i = 0; i < qty; i++){
195
if
(digitalRead(OUT[i])) lcd.write(2);
196
else
lcd.print(
" "
);
197
}
198
lcd.setCursor(11, 1);
// рисуем номера выходов
199
for
(
int
i = 0; i < qty; i++) lcd.print(i+1);
200
201
// delay(500); // необязательная задержка
202
}
и свой график расхода воды.
Ниже код для 5-ти канального термостата (скаченый из http://arrduinolab.blogspot.ru/2014/10/5-ds18b20.html), но функции суточной коррекции температуры там нет.
Задача-слепить из двух один.
С энкодером, наверное не получится , а с LCD Shield должно (кнопок больше и свободных пинов). Там 5 свободных цифровых выходов. 4 канала под реле, а 5-й можно на зуммер оставить.
Сам могу как и многие лишь моргать светодиодами.
Допилить кто может? можно за небольшое вознаграждение.
...для реализации погодозависимого регулятора нужно либо иметь уличный термодатчик, заточенный под конкретный котёл (датчик стОит зачастую несуразных денег, да и вход для подключения этого датчика предусмотрен не в каждом котле), либо знать протокол, используемый при подключении оригинального терморегулятора данфосс с возможностью управления модулятором (такая возможность есть тоже не везде). В общем - покопав инет на эту тему, я остановился на комнатном термостате. Вопрос дыхания/телевизора/плиты решён установкой термодатчика в определённом месте помещения (в тени, вдали от шальных потоков воздуха, батарей и прочих катаклизмов).
Вкратце - я ж говорю, меня устраивает. Вариант с уличным датчиком наверняка во сто крат правильнее и экономичнее, но в моём случае room thermostat'а мне хватает за глаза. Елозить установкой температуры теплоносителя больше не приходится, ну и, к примеру, котёл реже включается, если солнце через окна подогревает дом, - дык этого я и добивался. :-)
Или вы меня не понимаете или заблуждаетесь.
1. Уличный датчик- тот же самый DS18B20, он не стОит несуразных денег.
2. Протокол нас не интересует- лезть в газовый котёл так глубоко даже опасно. Единственное ЛЕГАЛЬНОЕ место куда можно подключаться как раз и есть те самые контакты комнатного термостата.
А дальше всё просто - второй датчик на подачу котла, как температура достигла необходимого нам значения- выключяем, как упала ниже порога - включаем.
У меня два года у соседа работала Ардуина с блоком реле и ЖКИ шилдом на котле Junkrs Eurostar который по своей природе на минимуме регулятора не мог выдавать меньше 45-50 градусов, а его тёплый пол при этом становился горячим пока не сработают клапана на рейке, потом они отключались и когда снова включались - пол уже был ледяной. И так всё время колебания: холодный-горячий
Ардуина выключала котёл при 32 и снова включала при 28 (условно) Все клапана пола были НАВСЕГДА открыты! Регулировка вручную температура-гистерезис, стабильная температура в доме, сосед был доволен.
У меня у самого дома котёл с погодником- Junkers Cerapur, как лет 8 назад отрегулировал наклон графика так и работает. Клапана тёплого пола тоже сняты навсегда за ненадобностью
у нас уличный датчик стоит данфосс - как часы все работает!
а где взять библиотеку вачдог таймер
Отличный термостатик получился. Автору премного благодарностей! Если кому интересно есть код на два модуля с радиосвязью по RF24.
а где взять библиотеку вачдог таймер
Э, он как бы уже на борту. Надо включить или выключить, ну и сбрасывать своевременно.
имелось в виду библиотека из этого скетча немогу найтиhttp://arduino.ru/forum/proekty/sutochnyi-termostat-dlya-gazovogo-kotla?page=2#comment-151943
День добрый. Было бы очень интересно получить и воплотить термостат на 2 модуля. Просьба вышлите на ЭП
dwk26111974@gmail.com Заранее благодарен.
Здравствуйте. Реализовал для загородного дома контроллер и добавил к нему модем siemens mc35i, подключаемый через com порт, для управления изменением температуры и получения уведомления о снижении ниже уровня, а также получания статуса. Огромное спасибо автору проекта vlad219i.
Реализовал для загородного дома контроллер и добавил к нему модем siemens mc35i
ЗдОрово, это как раз то, до чего у меня руки не дошли пока. :-)
Поинтересуюсь - для управления и получения статуса используются смс или интернет-подключение? Лет 10 назад я делал нечто похожее (дачная двусторонняя сигналка), там использовал СМС-команды.
Первую фотку удалось открыть только по прямой ссылке, остальные - не видно никак, а жаль.
Отличный термостатик получился. Автору премного благодарностей! Если кому интересно есть код на два модуля с радиосвязью по RF24.
Два модуля-это два разных канала, с независимым термостатированием??
Если да, то мне очень нужно.
Rukey скажите где брать такие корпуса??? Они идеальны. Я так понимаю, держатели под каждый модуль вы приклеиваете самостоятельно. И по сути это конструктор. Откройте для меня тайну, плиз!!
Отличный термостатик получился. Автору премного благодарностей! Если кому интересно есть код на два модуля с радиосвязью по RF24.
Два модуля-это два разных канала, с независимым термостатированием??
Если да, то мне очень нужно.
Rukey скажите где брать такие корпуса??? Они идеальны. Я так понимаю, держатели под каждый модуль вы приклеиваете самостоятельно. И по сути это конструктор. Откройте для меня тайну, плиз!!
Нет, это значит что модуль с экраном и датчиком висит в комнате, а в котёл засунул вторую ардуинку нано с релюшкой и они общаются через РФ-ку. Хотя собрать ещё один модуль для второго помещения и отправлять команду на тот-же адрес РФ-ки в котле и всё. Код одинаков, только адреса радиомодуей выставить.
Доброй ночи, товарищи! Вот сваял, поставил, все работает, автору респект, все устраивает. Немного допилил под свои задачи, три месяца, полет нормальный. Ежедневный отчет по GSM приходит исправно. Управляется по GSM тоже ок. Можно даже позвонить туда и оттуда и полноценно поговорить. Но вот сижу и думаю... Хата моя стоит за 300 км от меня, хорошо, что в скетче автора не используется адрес датчика DS, заменить сможет любой электрик. А если адрес датчика прописан и он накрылся? тогда брать ноут и новый датчик и ехать менять, т.к. без скетча никто ничего не сможет сделать. Далее, возникла идея прикрутить еще датчик на улицу. Тут уже адрес нужен и ремонтопригодность сводится к 1 человеку, который это сделал. Вот сижу и думаю, ведь можно и без адресов опросить все датчики на шине, но как контроллер узнает, какой из них какой, если адреса не прописаны? Но если это реализовать, то обслуживание и ремонт наших термостатов упростится в разы. Может, у кого есть идеи по этому поводу?
Далее, возникла идея прикрутить еще датчик на улицу. Тут уже адрес нужен и ремонтопригодность сводится к 1 человеку, который это сделал. Вот сижу и думаю, ведь можно и без адресов опросить все датчики на шине, но как контроллер узнает, какой из них какой, если адреса не прописаны?
Никак не узнает.
Я в подобном случае каждый датчик на отдельный пин вешал и адрес датчика не использовал.
Тоже думал над этим вариантом, только у 328 пинов маловато, придется мегу ставить.
Думаю, что мега тут - слишком жирно. LCD можно подключить по i2c, порты расширить 75hc595 (заодно светодиодов для индикации добавить). Ну это так - навскидку...
У меня умный дом немного по-другому организован. База на меге + LCD + LAN-шилд + датчики уличной температуры-давления-влажности + RF24, а уже через RF24 опрашиваются дополнительные модули (контроллер насоса, термостат и тд и тп). Все данные собираются-обрабатываются в меге и отправляются через мобильный инет на сервак, где и ведутся логи и генерятся странички с удобочитаемыми отчётами. Таким образом, из любой точки с интернетом есть доступ к инфе по дому.
Вот код и аппарат)))
#include <Wire.h> // i2c (для RTC)
#include <RealTimeClockDS1307.h> // RTC
#include <EEPROM.h> // EE
#include <LiquidCrystal.h> // LCD 16*2
#include <TimerOne.h> // прерывания по таймеру1
#include <OneWire.h> // 1wire для DS18B20
#include <DallasTemperature.h> // DS18B20
#define ONE_WIRE_BUS 11
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
DeviceAddress DS18B20Address;
#define encoderA 12 // энкодер - поворот вправо (об землю)
#define encoderB 13 // энкодер - поворот влево (об землю)
#define encoderK A3 // энкодер - кнопка (об землю)
#define BeepPin 9 // пищалка
#define BeepToneNo 2000 // тон звука "No", герц
#define BeepToneYes 4000 // тон звука "Yes", герц
#define BeepToneNoDuration 200 // длительность звука "No", мс
#define BeepToneYesDuration 200 // длительность звука "Yes", мс
#define Relay 10 // нога, к которой подключено реле
#define RelayOn LOW // полярность сигнала включения реде (HIGH/LOW)
// LCD connection RS, E, D4, D5, D6, D7
// R/W - to ground
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
byte
block1[8] = {
0x06,0x09,0x09,0x06,0x00,0x04,0x0E,0x1F };
// значок градуса с пламенем снизу
byte
block2[8] = {
0x06,0x09,0x09,0x06,0x00,0x00,0x00,0x00 };
// значок градуса
String currStr =
""
;
boolean isStringMessage =
false
;
// Переменная принимает значение True, если текущая строка является сообщением
boolean SMSsent =
false
;
boolean SMSsenti =
false
;
boolean SMSDelete=
false
;
boolean TimeSMSzapros=
false
;
#define TstatTimerMax 120 //минимальная пауза между включениями горелки, сек
unsigned
int
TstatTimer = 20;
//таймер паузы между включениями/выключениями, начальная установка 20 сек для устаканивания системы после сброса
float
DS18B20Temperature = 0;
//сырая температура от датчика
float
Temperature = 0;
//вычисленная температура с коррекцией
float
DS18B20TempTmp;
//времянка
byte
DS18B20iteration = 0;
//счётчик измерений температуры для усреднения
float
TstatTemp = 23;
//температура термостатирования, может изменяться настройками
float
TemperatureCorr = 0;
//коррекция температуры, может изменяться настройками
float
Hysteresis = 0.1;
// гистерезис термостата, может изменяться настройками
float
HysteresisOld;
int
Hours = 0;
// времянка часов RTC для отображения и установки
int
Minutes = 0;
// времянка минут RTC для отображения и установки
int
Seconds;
boolean PrintYesNo =
false
;
// показывать ли после времени Yes/No (косвенно - указание на режим установка/отображение)
boolean SetH =
false
;
// выделение часов при отображении
boolean SetM =
false
;
// выделение минут при отображении
boolean SetYesNo =
false
;
// выделение Yes/No при установке часов
boolean blink500ms =
false
;
// мигающий бит, инвертируется каждые 500мс
boolean plus1sec =
false
;
// ежесекундно взводится
boolean Zvonok =
true
;
boolean BeepEnabled =
true
;
byte
MenuTimeoutTimer;
byte
Timer1Hours = 0;
byte
Timer1Minutes = 0;
boolean Timer1Enabled =
false
;
boolean Timer1Activated =
false
;
float
Timer1Temp = 23;
//температура термостатирования по таймеру1, может изменяться настройками
byte
Timer2Hours = 0;
byte
Timer2Minutes = 0;
boolean Timer2Enabled =
false
;
boolean Timer2Activated =
false
;
float
Timer2Temp = 23;
//температура термостатирования по таймеру2, может изменяться настройками
byte
Timer3Hours = 0;
byte
Timer3Minutes = 0;
boolean Timer3Enabled =
false
;
boolean Timer3Activated =
false
;
float
Timer3Temp = 23;
//температура термостатирования по таймеру3, может изменяться настройками
float
AlarmTemp = 20;
// температура для замерзательного орала
// encoder vars
static
boolean rotating=
false
;
// debounce management
boolean A_set =
false
;
boolean B_set =
false
;
boolean encoderR =
false
;
boolean encoderL =
false
;
// EEPROM addresses
#define TstatTempEEaddr 0 // EE - адрес для сохранения температуры термостатирования, 4 байта(float)!
#define TemperatureCorrEEaddr 4 // EE - адрес для сохранения коррекции температуры, 4 байта(float)!
#define HysteresisEEaddr 8 // EE - адрес для сохранения гистерезиса, 4 байта(float)!
#define Timer1HoursEEaddr 12 // EE - адрес для сохранения часов таймера 1 (byte)
#define Timer1MinutesEEaddr 13 // EE - адрес для сохранения минут таймера 1 (byte)
#define Timer1EnabledEEaddr 14 // EE - адрес для сохранения статуса таймера 1 (boolean)
#define Timer1TempEEaddr 15 // EE - адрес для сохранения температуры таймера 1, 4 байта(float)!
#define Timer2HoursEEaddr 19 // EE - адрес для сохранения часов таймера 2 (byte)
#define Timer2MinutesEEaddr 20 // EE - адрес для сохранения минут таймера 2 (byte)
#define Timer2EnabledEEaddr 21 // EE - адрес для сохранения статуса таймера 2 (boolean)
#define Timer2TempEEaddr 22 // EE - адрес для сохранения температуры таймера 2, 4 байта(float)!
#define Timer3HoursEEaddr 26 // EE - адрес для сохранения часов таймера 3 (byte)
#define Timer3MinutesEEaddr 27 // EE - адрес для сохранения минут таймера 3 (byte)
#define Timer3EnabledEEaddr 28 // EE - адрес для сохранения статуса таймера 3 (boolean)
#define Timer3TempEEaddr 29 // EE - адрес для сохранения температуры таймера 3, 4 байта(float)!
#define BeepEnabledEEaddr 33 // EE - адрес для сохранения признака разрешения звука (boolean)
#define AlarmTempEEaddr 34 // EE - адрес для сохранения значения недопустимого снижения температуры, 4 байта(float)!
// ===== SETUP ========================================================================
void
setup
() {
Serial
.begin(19200);
pinMode(Relay, OUTPUT);
digitalWrite(Relay, HIGH);
lcd.begin(16, 2);
lcd.createChar(1, block1);
lcd.createChar(2, block2);
pinMode(encoderA, INPUT);
digitalWrite(encoderA, HIGH);
pinMode(encoderB, INPUT);
digitalWrite(encoderB, HIGH);
pinMode(encoderK, INPUT);
digitalWrite(encoderK, HIGH);
attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 0 (pin 2)
attachInterrupt(1, doEncoderB, CHANGE);
// encoder pin on interrupt 1 (pin 3)
Timer1.initialize(500000);
// Timer0 interrupt - set a timer of length 500000 microseconds
Timer1.attachInterrupt( timerIsr );
// attach the service routine here
if
(EEPROM.read(Timer1HoursEEaddr) > 23) {
// если первая запись однокристалки - записать начальные значения в EE
EEPROM.write(BeepEnabledEEaddr, BeepEnabled);
EEPROM_float_write(TstatTempEEaddr, TstatTemp);
EEPROM_float_write(TemperatureCorrEEaddr, TemperatureCorr);
EEPROM_float_write(HysteresisEEaddr, Hysteresis);
EEPROM.write(Timer1HoursEEaddr, Timer1Hours);
EEPROM.write(Timer1MinutesEEaddr, Timer1Minutes);
EEPROM.write(Timer1EnabledEEaddr, Timer1Enabled);
EEPROM_float_write(Timer1TempEEaddr, Timer1Temp);
EEPROM.write(Timer2HoursEEaddr, Timer2Hours);
EEPROM.write(Timer2MinutesEEaddr, Timer2Minutes);
EEPROM.write(Timer2EnabledEEaddr, Timer2Enabled);
EEPROM_float_write(Timer2TempEEaddr, Timer2Temp);
EEPROM.write(Timer3HoursEEaddr, Timer3Hours);
EEPROM.write(Timer3MinutesEEaddr, Timer3Minutes);
EEPROM.write(Timer3EnabledEEaddr, Timer3Enabled);
EEPROM_float_write(Timer3TempEEaddr, Timer3Temp);
EEPROM_float_write(AlarmTempEEaddr, AlarmTemp);
}
BeepEnabled = EEPROM.read(BeepEnabledEEaddr);
TstatTemp = EEPROM_float_read(TstatTempEEaddr);
TemperatureCorr = EEPROM_float_read(TemperatureCorrEEaddr);
Hysteresis = EEPROM_float_read(HysteresisEEaddr);
Timer1Hours = EEPROM.read(Timer1HoursEEaddr);
Timer1Minutes = EEPROM.read(Timer1MinutesEEaddr);
Timer1Enabled = EEPROM.read(Timer1EnabledEEaddr);
Timer1Temp = EEPROM_float_read(Timer1TempEEaddr);
Timer2Hours = EEPROM.read(Timer2HoursEEaddr);
Timer2Minutes = EEPROM.read(Timer2MinutesEEaddr);
Timer2Enabled = EEPROM.read(Timer2EnabledEEaddr);
Timer2Temp = EEPROM_float_read(Timer2TempEEaddr);
Timer3Hours = EEPROM.read(Timer3HoursEEaddr);
Timer3Minutes = EEPROM.read(Timer3MinutesEEaddr);
Timer3Enabled = EEPROM.read(Timer3EnabledEEaddr);
Timer3Temp = EEPROM_float_read(Timer3TempEEaddr);
AlarmTemp = EEPROM_float_read(AlarmTempEEaddr);
DS18B20.begin();
DS18B20.getAddress(DS18B20Address, 0);
DS18B20.setResolution(DS18B20Address, 12);
DS18B20.setWaitForConversion(
false
);
DS18B20.requestTemperatures();
NastroykaGSM();
delay(5000);
tone(BeepPin,2000,50);
delay(50);
tone(BeepPin,3000,50);
delay(50);
tone(BeepPin,4000,50);
delay(50);
startOneSMS();
Serial
.print(
"TERMOSTAT-ON"
); EndSMS();
}
// ===== MAIN CYCLE ===================================================================
void
loop
()
{
sms_read();
lcd.setCursor(8, 0);
//инфо на LCD
if
((Temperature < AlarmTemp)&(blink500ms)) {
lcd.print(F(
"*"
));
}
else
{
lcd.print(F(
" "
));
}
lcd.print(F(
"t="
));
if
(Temperature < 10) {
lcd.print(F(
" "
));
}
lcd.print(Temperature,1);
lcd.write(0x02);
// значок градуса
// если таймер 1 включен - надпись светится, если сработал - мигает
lcd.setCursor(0, 1);
//инфо на LCD
if
((Timer1Enabled)&!((Timer1Activated)&(blink500ms))) {
lcd.print(F(
"T1"
));
}
else
{
lcd.print(F(
" "
));
}
// если таймер 2 включен - надпись светится, если сработал - мигает
lcd.setCursor(3, 1);
//инфо на LCD
if
((Timer2Enabled)&!((Timer2Activated)&(blink500ms))) {
lcd.print(F(
"T2"
));
}
else
{
lcd.print(F(
" "
));
}
// если таймер 3 включен - надпись светится, если сработал - мигает
lcd.setCursor(6, 1);
//инфо на LCD
if
((Timer3Enabled)&!((Timer3Activated)&(blink500ms))) {
lcd.print(F(
"T3"
));
}
else
{
lcd.print(F(
" "
));
}
lcd.setCursor(9, 1);
//инфо на LCD
lcd.print(F(
"s="
));
lcd.print(TstatTemp,1);
if
( digitalRead(Relay) == RelayOn ) {
lcd.write(0x01);
// значок градуса с пламенем
}
else
{
lcd.write(0x02);
// значок градуса
}
// печатаем текущее время
PrintYesNo =
false
;
PrintRTC(0,0);
// термостатирование
if
( TstatTimer == 0 )
{
if
( Temperature > ( TstatTemp + Hysteresis ) )
// гистерезис
{
if
( digitalRead(Relay) == RelayOn )
// если горелка включена -
{
digitalWrite(Relay, !RelayOn);
// выключить горелку
TstatTimer = TstatTimerMax;
// горелку держать выключённой не менее заданного в TstatTimerMax времени
}
}
if
(Temperature < TstatTemp)
{
if
( digitalRead(Relay) == !RelayOn )
// если горелка выключена -
{
digitalWrite(Relay, RelayOn);
// включить горелку
TstatTimer = TstatTimerMax;
// горелку держать включённой не менее заданного в TstatTimerMax времени
}
}
}
// если прошла 1 секунда - делаем ежесекундные дела
if
(plus1sec) {
plus1sec =
false
;
// сбрасываем до следующей секунды
// обновляем часы
RTC.readClock();
Hours=RTC.getHours();
Minutes=RTC.getMinutes();
Seconds=RTC.getSeconds();
// измеряем температуру воздуха
DS18B20TempTmp = DS18B20.getTempCByIndex(0);
// получить температуру от датчика
DS18B20.requestTemperatures();
// запустить новое измерение
if
(DS18B20TempTmp != -127)
{
DS18B20Temperature += DS18B20TempTmp;
// суммируем для усреднения
DS18B20iteration ++;
if
(DS18B20iteration == 10)
{
DS18B20iteration = 0;
Temperature = (DS18B20Temperature / 10) + TemperatureCorr;
//усреднённая + коррекция
DS18B20Temperature = 0;
}
}
// если уставку термостата поменяли вручную - запись её в EE, не чаще 1 раза в минуту
//(экономия ресурса EE)
if
((EEPROM_float_read(TstatTempEEaddr) != TstatTemp)&(Seconds == 0)) {
EEPROM_float_write(TstatTempEEaddr, TstatTemp);
}
// проверка Timer1 и изменение уставки термостата при совпадении
if
((Hours == Timer1Hours)&(Minutes == Timer1Minutes)&(Timer1Enabled)&(Seconds == 0)) {
// время T1 совпадает с RTC
Timer1Activated =
true
;
Timer2Activated =
false
;
Timer3Activated =
false
;
TstatTemp = Timer1Temp;
EEPROM_float_write(TstatTempEEaddr, TstatTemp);
if
(BeepEnabled) {
tone(BeepPin,4000,5);
}
}
// проверка Timer2 и изменение уставки термостата при совпадении
if
((Hours == Timer2Hours)&(Minutes == Timer2Minutes)&(Timer2Enabled)&(Seconds == 0)) {
// время T2 совпадает с RTC
Timer1Activated =
false
;
Timer2Activated =
true
;
Timer3Activated =
false
;
TstatTemp = Timer2Temp;
EEPROM_float_write(TstatTempEEaddr, TstatTemp);
if
(BeepEnabled) {
tone(BeepPin,4000,5);
}
}
// проверка Timer3 и изменение уставки термостата при совпадении
if
((Hours == Timer3Hours)&(Minutes == Timer3Minutes)&(Timer3Enabled)&(Seconds == 0)) {
// время T3 совпадает с RTC
Timer1Activated =
false
;
Timer2Activated =
false
;
Timer3Activated =
true
;
TstatTemp = Timer3Temp;
EEPROM_float_write(TstatTempEEaddr, TstatTemp);
if
(BeepEnabled) {
tone(BeepPin,4000,5);
}
}
if
((Temperature < AlarmTemp)&& (!SMSsent)){
tone(BeepPin,4000,5);startOneSMS();
Serial
.print(
"TREVOGA!!! "
);
Serial
.print(
"TT="
);
Serial
.print(Temperature);EndSMS();
SMSsent =
true
;SMSsenti =
false
;}
if
((Temperature > AlarmTemp)&& (!SMSsenti)){
tone(BeepPin,4000,5);startOneSMS();
Serial
.print(
"TREVOGA-OFF "
);
Serial
.print(
"TT="
);
Serial
.print(Temperature);EndSMS();
SMSsenti =
true
;SMSsent =
false
;}
if
(Hours == 0 && Minutes == 2 && SMSDelete==
false
){
Serial
.print(
"AT+CMGDA=«DEL ALL»\r"
); SMSDelete=
true
;}
// Удаляем в 0:02 все СМС.
if
(Minutes != 2){SMSDelete=
false
;}
if
(Hours == 10 && Minutes == 0 && TimeSMSzapros==
false
){
SMSzapros(); TimeSMSzapros=
true
;}
// в 12:00 отправляем СМС о состоянии.
if
(Minutes != 0){TimeSMSzapros=
false
;}
// сброс флага TimeSMSzapros
}
// обработка поворота энкодера на лету (ручное изменение уставки температуры))
rotating =
true
;
// reset the debouncer
if
((encoderR)^(encoderL)) {
if
(encoderR) {
TstatTemp += 0.1;
}
else
{
TstatTemp -= 0.1;
}
TstatTemp = constrain(TstatTemp, 10, 35);
encoderR =
false
;
encoderL =
false
;
Timer1Activated =
false
;
Timer2Activated =
false
;
Timer3Activated =
false
;
}
// ================ по нажатию кнопки энкодера - меню настроек ====================
if
(digitalRead(encoderK) == 0) {
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"< SETUP >"
));
if
(BeepEnabled) {
tone(BeepPin,4000,50);
}
delay(200);
int
menuitem = 0;
do
{
rotating =
true
;
// reset the debouncer
if
((encoderR)^(encoderL)) {
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
if
(encoderR) { menuitem += 1; }
else
{ menuitem -= 1; }
if
( menuitem > 9 ) { menuitem = 0; }
// границы пунктов меню
if
( menuitem < 0 ) { menuitem = 9; }
encoderR =
false
;
encoderL =
false
;
}
// индикация пункта меню (номер пункта - в menuitem)
lcd.setCursor(0, 1);
//инфо на LCD
switch
(menuitem)
{
case
0:
lcd.print(F(
"0.BACK "
));
break
;
case
1:
lcd.print(F(
"1.TIMER1 SET "
));
break
;
case
2:
lcd.print(F(
"2.TIMER2 SET "
));
break
;
case
3:
lcd.print(F(
"3.TIMER3 SET "
));
break
;
case
4:
lcd.print(F(
"4.CLOCK SET "
));
break
;
case
5:
lcd.print(F(
"5.HYSTERESIS SET"
));
break
;
case
6:
lcd.print(F(
"6.T-CORRECT SET "
));
break
;
case
7:
lcd.print(F(
"7.SOUND SET "
));
break
;
case
8:
lcd.print(F(
"8.T-ALARM SET "
));
break
;
case
9:
lcd.print(F(
"ZVONOK "
));
break
;
}
if
(MenuTimeoutTimer == 0) {
menuitem = 0;
}
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
// если нажата кнопка энкодера или таймаут - обработка пункта меню (номер пункта - в menuitem)
if
(BeepEnabled) {
tone(BeepPin,4000,50);
}
switch
(menuitem)
{
// ====== пункт 0 - выход
case
0:
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
}
//звук "NO"
break
;
// case 0 out
// ====== пункт 1 - установка Timer1
case
1:
MenuTimeoutTimer = 30;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"SETUP TIMER1"
));
delay(200);
Hours=Timer1Hours;
Minutes=Timer1Minutes;
SetYesNo =
false
;
PrintYesNo =
true
;
SetTime(0,1);
// в позиции 0,1 - запрос ввода времени
if
(MenuTimeoutTimer != 0) {
if
(SetYesNo)
// если при установке времени выбрано "Yes"
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
Timer1Hours = Hours;
Timer1Minutes = Minutes;
Timer1Enabled =
true
;
EEPROM.write(Timer1HoursEEaddr, Timer1Hours);
EEPROM.write(Timer1MinutesEEaddr, Timer1Minutes);
EEPROM.write(Timer1EnabledEEaddr, Timer1Enabled);
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"Timer1 Temp. Set"
));
delay(200);
do
{
lcd.setCursor(0,1);
if
(blink500ms) {
lcd.print(F(
" "
));
}
else
{
lcd.print(Timer1Temp, 1);
lcd.write(0x02);
// значок градуса
}
rotating =
true
;
// reset the debouncer
if
(encoderR) {
Timer1Temp += 0.1;
encoderR =
false
;
}
if
(encoderL) {
Timer1Temp -= 0.1;
encoderL =
false
;
}
Timer1Temp = constrain(Timer1Temp, 10, 35);
// крайние значения
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(MenuTimeoutTimer != 0) {
// если после выбора температуры нажата кнопка энкодера
EEPROM_float_write(Timer1TempEEaddr, Timer1Temp);
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
else
{
// если не нажата - используем старую температуру
Timer1Temp = EEPROM_float_read(Timer1TempEEaddr);
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
}
else
// если при установке времени выбрано "No"
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
Timer1Enabled =
false
;
EEPROM.write(Timer1EnabledEEaddr, Timer1Enabled);
Timer1Hours = EEPROM.read(Timer1HoursEEaddr);
Timer1Minutes = EEPROM.read(Timer1MinutesEEaddr);
}
}
else
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
}
break
;
// case 1 out
// ====== пункт 2 - установка Timer2
case
2:
MenuTimeoutTimer = 30;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"SETUP TIMER2"
));
delay(200);
Hours=Timer2Hours;
Minutes=Timer2Minutes;
SetYesNo =
false
;
PrintYesNo =
true
;
SetTime(0,1);
// в позиции 0,1 - запрос ввода времени
if
(MenuTimeoutTimer != 0) {
if
(SetYesNo)
// если при установке времени выбрано "Yes"
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
Timer2Hours = Hours;
Timer2Minutes = Minutes;
Timer2Enabled =
true
;
EEPROM.write(Timer2HoursEEaddr, Timer2Hours);
EEPROM.write(Timer2MinutesEEaddr, Timer2Minutes);
EEPROM.write(Timer2EnabledEEaddr, Timer2Enabled);
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"Timer2 Temp. Set"
));
delay(200);
do
{
lcd.setCursor(0,1);
if
(blink500ms) {
lcd.print(F(
" "
));
}
else
{
lcd.print(Timer2Temp, 1);
lcd.write(0x02);
// значок градуса
}
rotating =
true
;
// reset the debouncer
if
(encoderR) {
Timer2Temp += 0.1;
encoderR =
false
;
}
if
(encoderL) {
Timer2Temp -= 0.1;
encoderL =
false
;
}
Timer2Temp = constrain(Timer2Temp, 10, 35);
// крайние значения
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(MenuTimeoutTimer != 0) {
// если после выбора температуры нажата кнопка энкодера
EEPROM_float_write(Timer2TempEEaddr, Timer2Temp);
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
else
{
// если не нажата - используем старую температуру
Timer2Temp = EEPROM_float_read(Timer2TempEEaddr);
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
}
else
// если при установке времени выбрано "No"
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
Timer2Enabled =
false
;
EEPROM.write(Timer2EnabledEEaddr, Timer2Enabled);
Timer2Hours = EEPROM.read(Timer2HoursEEaddr);
Timer2Minutes = EEPROM.read(Timer2MinutesEEaddr);
}
}
else
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
}
break
;
// case 2 out
// ====== пункт 3 - установка Timer3
case
3:
MenuTimeoutTimer = 30;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"SETUP TIMER3"
));
delay(200);
Hours=Timer3Hours;
Minutes=Timer3Minutes;
SetYesNo =
false
;
PrintYesNo =
true
;
SetTime(0,1);
// в позиции 0,1 - запрос ввода времени
if
(MenuTimeoutTimer != 0) {
if
(SetYesNo)
// если при установке времени выбрано "Yes"
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
Timer3Hours = Hours;
Timer3Minutes = Minutes;
Timer3Enabled =
true
;
EEPROM.write(Timer3HoursEEaddr, Timer3Hours);
EEPROM.write(Timer3MinutesEEaddr, Timer3Minutes);
EEPROM.write(Timer3EnabledEEaddr, Timer3Enabled);
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"Timer3 Temp. Set"
));
delay(200);
do
{
lcd.setCursor(0,1);
if
(blink500ms) {
lcd.print(F(
" "
));
}
else
{
lcd.print(Timer3Temp, 1);
lcd.write(0x02);
// значок градуса
}
rotating =
true
;
// reset the debouncer
if
(encoderR) {
Timer3Temp += 0.1;
encoderR =
false
;
}
if
(encoderL) {
Timer3Temp -= 0.1;
encoderL =
false
;
}
Timer3Temp = constrain(Timer3Temp, 10, 35);
// крайние значения
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(MenuTimeoutTimer != 0) {
// если после выбора температуры нажата кнопка энкодера
EEPROM_float_write(Timer3TempEEaddr, Timer3Temp);
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
else
{
// если не нажата - используем старую температуру
Timer3Temp = EEPROM_float_read(Timer3TempEEaddr);
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
}
else
// если при установке времени выбрано "No"
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
Timer3Enabled =
false
;
EEPROM.write(Timer3EnabledEEaddr, Timer3Enabled);
Timer3Hours = EEPROM.read(Timer3HoursEEaddr);
Timer3Minutes = EEPROM.read(Timer3MinutesEEaddr);
}
}
else
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
}
break
;
// case 3 out
// ====== пункт 4 - установка RTC
case
4:
MenuTimeoutTimer = 30;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"SETUP CLOCK"
));
delay(200);
RTC.readClock();
Hours=RTC.getHours();
Minutes=RTC.getMinutes();
SetYesNo =
false
;
PrintYesNo =
true
;
SetTime(0,1);
// в позиции 0,1 - запрос ввода времени
if
(MenuTimeoutTimer != 0) {
if
(SetYesNo)
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
RTC.setHours(Hours);
RTC.setMinutes(Minutes);
RTC.setSeconds(0);
RTC.setClock();
}
else
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
}
}
else
{
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
}
break
;
// case 4 out
// ====== пункт 5 - установка гистерезиса
case
5:
MenuTimeoutTimer = 30;
//таймер таймаута, секунд
HysteresisOld = Hysteresis;
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"SETUP HYSTERESIS"
));
delay(200);
do
{
lcd.setCursor(0,1);
if
(blink500ms) {
lcd.print(
" "
);
}
else
{
lcd.print(Hysteresis, 1);
lcd.write(0x02);
// значок градуса
}
rotating =
true
;
// reset the debouncer
if
(encoderR) {
Hysteresis += 0.1;
encoderR =
false
;
}
if
(encoderL) {
Hysteresis -= 0.1;
encoderL =
false
;
}
Hysteresis = constrain(Hysteresis, 0.1, 1);
// крайние значения
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(MenuTimeoutTimer != 0) {
EEPROM_float_write(HysteresisEEaddr, Hysteresis);
// запись в ЕЕПРОМ
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
else
{
Hysteresis = HysteresisOld;
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
}
break
;
// case 5 out
// ====== пункт 6 - установка коррекции температуры
case
6:
MenuTimeoutTimer = 30;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"SETUP T-CORRECT "
));
delay(200);
do
{
lcd.setCursor(0,1);
if
(blink500ms) {
lcd.print(F(
" "
));
}
else
{
if
(TemperatureCorr >= 0) {
lcd.print(F(
"+"
));
}
lcd.print(TemperatureCorr, 1);
lcd.write(0x02);
// значок градуса
}
rotating =
true
;
// reset the debouncer
if
(encoderR) {
TemperatureCorr += 0.1;
encoderR =
false
;
}
if
(encoderL) {
TemperatureCorr -= 0.1;
encoderL =
false
;
}
TemperatureCorr = constrain(TemperatureCorr, -10, 10);
// крайние значения
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(MenuTimeoutTimer != 0) {
EEPROM_float_write(TemperatureCorrEEaddr, TemperatureCorr);
// запись в ЕЕПРОМ
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
else
{
TemperatureCorr = EEPROM_float_read(TemperatureCorrEEaddr);
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
}
break
;
// case 6 out
// ====== пункт 7 - вкл/выкл звука
case
7:
MenuTimeoutTimer = 30;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"SOUND SET "
));
delay(200);
do
{
lcd.setCursor(0,1);
if
(BeepEnabled) {
lcd.print(F(
"BEEP ON "
));
}
else
{
lcd.print(F(
"BEEP OFF "
));
}
rotating =
true
;
// reset the debouncer
if
((encoderR)^(encoderL)) {
BeepEnabled = !BeepEnabled;
encoderR =
false
;
encoderL =
false
;
}
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(MenuTimeoutTimer != 0) {
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
EEPROM.write(BeepEnabledEEaddr, BeepEnabled);
}
if
(MenuTimeoutTimer == 0) {
BeepEnabled = EEPROM.read(BeepEnabledEEaddr);
}
break
;
// case 7 out
// ====== пункт 8 - установка коррекции температуры
case
8:
MenuTimeoutTimer = 30;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"ALARM-TEMP SET "
));
delay(200);
do
{
lcd.setCursor(0,1);
if
(blink500ms) {
lcd.print(F(
" "
));
}
else
{
if
(AlarmTemp >= 0) {
lcd.print(F(
"+"
));
}
lcd.print(AlarmTemp, 0);
lcd.write(0x02);
// значок градуса
}
rotating =
true
;
// reset the debouncer
if
(encoderR) {
AlarmTemp += 1;
encoderR =
false
;
}
if
(encoderL) {
AlarmTemp -= 1;
encoderL =
false
;
}
AlarmTemp = constrain(AlarmTemp, 5, 40);
// крайние значения
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(MenuTimeoutTimer != 0) {
EEPROM_float_write(AlarmTempEEaddr, AlarmTemp);
// запись в ЕЕПРОМ
if
(BeepEnabled) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
}
}
else
{
AlarmTemp = EEPROM_float_read(AlarmTempEEaddr);
if
(BeepEnabled) {
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
}
}
break
;
// case 8 out
case
9:
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
lcd.clear();
lcd.setCursor(0, 0);
//инфо на LCD
lcd.print(F(
"ZVONOK SET "
));
delay(200);
do
{
lcd.setCursor(0,1);
if
(Zvonok) {
lcd.print(F(
"YES "
));
}
else
{
lcd.print(F(
"NO "
));
}
rotating =
true
;
// reset the debouncer
if
((encoderR)^(encoderL)) {
Zvonok = !Zvonok;
encoderR =
false
;
encoderL =
false
;
}
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(MenuTimeoutTimer != 0) {
if
(Zvonok) {
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
Dozvon();
}
}
break
;
// case 7 out
}
delay(200);
lcd.clear();
}
}
// ===== SUBROUTINES ==================================================================
// ========================================
void
SetTime(
char
x,
char
y)
{
// ========= set hours
SetH =
true
;
do
{
PrintRTC(x,y);
rotating =
true
;
// reset the debouncer
if
(encoderR) {
Hours += 1;
if
(Hours > 23) {
Hours = 0;
};
encoderR =
false
;
}
if
(encoderL) {
Hours -= 1;
if
(Hours < 0) {
Hours = 23;
};
encoderL =
false
;
}
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(BeepEnabled) {
tone(BeepPin,4000,50);
//звук "YES"
}
SetH =
false
;
delay(200);
// ========= set minutes
SetM =
true
;
do
{
PrintRTC(0,1);
rotating =
true
;
// reset the debouncer
if
(encoderR) {
Minutes += 1;
if
(Minutes > 59) {
Minutes = 0;
};
encoderR =
false
;
}
if
(encoderL) {
Minutes -= 1;
if
(Minutes < 0) {
Minutes = 59;
};
encoderL =
false
;
}
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
if
(BeepEnabled) {
tone(BeepPin,4000,50);
//звук "YES"
}
if
(PrintYesNo) {
SetM =
false
;
delay(200);
// ========= set yes-no
SetYesNo =
false
;
do
{
PrintRTC(0,1);
rotating =
true
;
// reset the debouncer
if
((encoderR)||(encoderL)) {
SetYesNo = !SetYesNo;
encoderR =
false
;
encoderL =
false
;
}
}
while
((digitalRead(encoderK)==1)^(MenuTimeoutTimer==0));
delay(200);
}
}
// ========================================
void
PrintRTC(
char
x,
char
y)
{
lcd.setCursor(x,y);
if
(SetH&&blink500ms) {
lcd.print(F(
" "
));
}
else
{
if
(Hours < 10) {
lcd.print(F(
"0"
));
}
lcd.print(Hours);
}
// мигающее двоеточие, если не в режиме установки времени
if
(!(SetH||SetM||PrintYesNo||blink500ms))
{
lcd.print(F(
" "
));
}
else
{
lcd.print(F(
":"
));
}
if
(SetM&&blink500ms) {
lcd.print(F(
" "
));
}
else
{
if
(Minutes < 10) {
lcd.print(F(
"0"
));
}
lcd.print(Minutes);
}
lcd.print(F(
" "
));
if
(PrintYesNo) {
lcd.print(F(
"["
));
if
(!(SetH||SetM||blink500ms))
{
lcd.print(F(
" "
));
}
else
{
if
(SetYesNo)
{
lcd.print(F(
"YES"
));
}
else
{
lcd.print(F(
"NO "
));
}
}
lcd.print(F(
"]"
));
}
}
// ========= чтение/запись float в EE =====
void
EEPROM_float_write(
int
addr,
float
val)
// запись в ЕЕПРОМ
{
byte
*x = (
byte
*)&val;
for
(
byte
i = 0; i < 4; i++) EEPROM.write(i+addr, x[i]);
}
float
EEPROM_float_read(
int
addr)
// чтение из ЕЕПРОМ
{
byte
x[4];
for
(
byte
i = 0; i < 4; i++) x[i] = EEPROM.read(i+addr);
float
*y = (
float
*)&x;
return
y[0];
}
// ========================================
// ============================ Encoder interrupts =============================
// Interrupt on A changing state
void
doEncoderA(){
if
( rotating ) {
delay (1) ;
// wait a little until the bouncing is done
}
// Test transition, did things really change?
if
( digitalRead(encoderA) != A_set ) {
// debounce once more
A_set = !A_set;
// adjust counter + if A leads B
if
( A_set && !B_set )
{
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
if
(BeepEnabled) {
tone(BeepPin,4000,5);
}
encoderR =
true
;
rotating =
false
;
// no more debouncing until loop() hits again
}
}
}
// Interrupt on B changing state, same as A above
void
doEncoderB(){
if
( rotating ) {
delay (1);
}
if
( digitalRead(encoderB) != B_set ) {
B_set = !B_set;
// adjust counter - 1 if B leads A
if
( B_set && !A_set ) {
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
if
(BeepEnabled) {
tone(BeepPin,4000,5);
}
encoderL =
true
;
rotating =
false
;
}
}
}
// ============================ Timer0 interrupt =============================
// run every 500ms
void
timerIsr()
{
blink500ms = !blink500ms;
// инверсия мерцающего бита
if
(blink500ms) {
plus1sec =
true
;
// ежесекундно взводится
if
(TstatTimer != 0) {
TstatTimer --;
// ежесекундный декремент этого таймера
}
if
(MenuTimeoutTimer != 0) {
MenuTimeoutTimer --;
// ежесекундный декремент этого таймера
}
}
}
void
sms_read()
//_____Цикл чтения входящих СМС-сообщений______________
{
if
(!
Serial
.available())
return
;
char
currSymb =
Serial
.read();
if
(
'\r'
== currSymb)
{
if
(isStringMessage)
{
if
(!currStr.compareTo(
"ZVONOK"
)) { Dozvon();}
if
(!currStr.compareTo(
"ZAPROS"
)) { SMSzapros();}
if
(!currStr.compareTo(
"+30"
)) { TstatTemp = 30;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+29"
)) { TstatTemp = 29;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+28"
)) { TstatTemp = 28;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+27"
)) { TstatTemp = 27;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+26"
)) { TstatTemp = 26;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+25"
)) { TstatTemp = 25;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+24"
)) { TstatTemp = 24;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
// Передача параметров по СМС
if
(!currStr.compareTo(
"+23"
)) { TstatTemp = 23;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+22"
)) { TstatTemp = 22;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+21"
)) { TstatTemp = 21;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+20"
)) { TstatTemp = 20;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+19"
)) { TstatTemp = 19;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+18"
)) { TstatTemp = 18;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+17"
)) { TstatTemp = 17;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+16"
)) { TstatTemp = 16;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+15"
)) { TstatTemp = 15;startOneSMS();
Serial
.print(TstatTemp);
Serial
.print(
" TstatTemp-OK"
);}
if
(!currStr.compareTo(
"+0.1"
)) { Hysteresis = 0.1;startOneSMS();
Serial
.print(Hysteresis);
Serial
.print(
" Hysteresis-OK"
);}
if
(!currStr.compareTo(
"+0.2"
)) { Hysteresis = 0.2;startOneSMS();
Serial
.print(Hysteresis);
Serial
.print(
" Hysteresis-OK"
);}
if
(!currStr.compareTo(
"+0.3"
)) { Hysteresis = 0.3;startOneSMS();
Serial
.print(Hysteresis);
Serial
.print(
" Hysteresis-OK"
);}
if
(!currStr.compareTo(
"+0.4"
)) { Hysteresis = 0.4;startOneSMS();
Serial
.print(Hysteresis);
Serial
.print(
" Hysteresis-OK"
);}
if
(!currStr.compareTo(
"+0.5"
)) { Hysteresis = 0.5;startOneSMS();
Serial
.print(Hysteresis);
Serial
.print(
" Hysteresis-OK"
);}
if
(!currStr.compareTo(
"+1"
)) { Hysteresis = 1;startOneSMS();
Serial
.print(Hysteresis);
Serial
.print(
" Hysteresis-OK"
);}
EndSMS();
isStringMessage =
false
;
}
else
{
if
(currStr.startsWith(
"+CMT"
)) {
// если текущая строка начинается с "+CMT",
isStringMessage =
true
;}}
// то следующая строка является сообщением
currStr =
""
;
}
else
if
(
'\n'
!= currSymb) { currStr += String(currSymb);}
}
void
SMSzapros()
// СМС отчет для хозяина
{
startOneSMS();
if
((Timer1Enabled)&!(Timer1Activated)) {
Serial
.print(
"TIMER 1 "
);
Serial
.print(
"T1="
);
Serial
.print(Timer1Temp);
Serial
.print(
"/TU="
);
Serial
.print(Temperature);
Serial
.print(
" HIST="
);
Serial
.print(Hysteresis);}
if
((Timer2Enabled)&!(Timer1Activated)) {
Serial
.print(
"TIMER 2 "
);
Serial
.print(
"T2="
);
Serial
.print(Timer2Temp);
Serial
.print(
"/TU="
);
Serial
.print(Temperature);
Serial
.print(
" HIST="
);
Serial
.print(Hysteresis);}
if
((Timer3Enabled)&!(Timer1Activated)) {
Serial
.print(
"TIMER 3 "
);
Serial
.print(
"T3="
);
Serial
.print(Timer3Temp);
Serial
.print(
"/TU="
);
Serial
.print(Temperature);
Serial
.print(
" HIST="
);
Serial
.print(Hysteresis);}
else
{
Serial
.print(
"TIMER OFF "
);
Serial
.print(
"TSET/TT="
);
Serial
.print(TstatTemp);
Serial
.print(
"/"
);
Serial
.print(Temperature);
Serial
.print(
" HIST="
);
Serial
.print(Hysteresis);}
if
(digitalRead(12)==HIGH) {
Serial
.println(
" NAGREV-OFF"
); }
else
{
Serial
.println(
" NAGREV-ON"
);}
EndSMS();
}
void
NastroykaGSM()
{
Serial
.print(
"AT+CMGF=1\r"
);
//устанавливает текстовый режим смс-сообщения
delay(300);
Serial
.print(
"AT+IFC=1,1\r"
);
//устанавливает программный контроль потоком передачи данных
delay(300);
Serial
.print(
"AT+CPBS=\"SM\"\r"
);
//открывает доступ к данным телефонной книги SIM-карты
delay(300);
Serial
.print(
"AT+GSMBUSY=1,1\r"
);
//запрет всех входящих звонков
delay(300);
Serial
.print(
"AT+CMGD=4,\r"
);
//Очищаем накопившиеся СМС
delay(300);
Serial
.print(
"AT+CNMI=1,2,2,1,0\r"
);
//включает оповещение о новых сообщениях
delay(300);
}
void
startOneSMS()
//__________________Цикл подготовки модуля к отправке СМС-сообщений по первому номеру
{
Serial
.print(
"AT+CMGF=1\r"
);
delay(100);
Serial
.println(
"AT + CMGS = \"+79206888901\""
);
delay(100);
}
void
EndSMS()
//__________________Цикл окончания и отправки СМС-сообщения_______________________
{
delay(100);
Serial
.println((
char
)26);
// Команда отправки СМС
delay(5000);
}
void
Dozvon()
//__________________Цикл дозвона абоненту (для аудиоконтроля)___________________
{
Serial
.println(
"AT+CMIC=0,12"
);
// Команда для установки чувствительности микрофона Поэкспериментировать с цифрой.
// 0,- это канал микрофона (1,2,3),15 это уровень см.инструкцию к СИМ900
delay(200);
Serial
.println(
"ATD+79206888901;"
);
// Набираем номер
}
только это не умный дом, а термостат.
Красиво. Стимпанк... :-)
Отличный термостатик получился. Автору премного благодарностей! Если кому интересно есть код на два модуля с радиосвязью по RF24.
Два модуля-это два разных канала, с независимым термостатированием??
Если да, то мне очень нужно.
Rukey скажите где брать такие корпуса??? Они идеальны. Я так понимаю, держатели под каждый модуль вы приклеиваете самостоятельно. И по сути это конструктор. Откройте для меня тайну, плиз!!
Спасибо. Корпус проектировал в инвенторе, печатал на 3д принтере. Территориально работаю в Москве, могу распечатать. Ну если проблема, можно файлы выложить для самостоятельной печати, если из другого города есть где распечатать.
У меня при вращении энкодера значения на экране только увеличиваются. Независимо от направления вращения.
энкодер с фиксацией положения https://ru.aliexpress.com/item/Rotary-Encoder-Module-Brick-Sensor-Development-for-Arduino/32452759168.html?spm=2114.13010608.0.0.1YTNVw
В чем может быть причина?
контакты "clk" "DT" вешаю на А4 и А5 соответственно. Пробовал менять местами-результат тот же.
контакты "clk" "DT" вешаю на А4 и А5 соответственно. Пробовал менять местами-результат тот же.
Ошибся-туда часы. Энкодер на 2 и3 пины.
От 2 и 3 пина на землю параллельно энкодеру по керамическому конденсатору (примерно 0,01мкФ) подключите - должно помочь.
Вот плод моих ночных бдений в свободное от работы и семьи время.
Сразу скажу, чтоб потом помидорами не закидали, что язык я специально не изучал, а с программированием до этого сталкивался еще в школе. Так что может быть скетч написан не идеально и м.б. косячки. Работу скетча проверял только на макетке. В бою не тестировал.
За основу были взяты 3 скетча:
1. скетч автора этой темы
2.https://www.youtube.com/watch?v=mFtgO3kWA4g
3.http://cxem.net/arduino/arduino166.php для работы со сдвиговым регистром.
vlad219i написал отличный скетч, но бывает так (как у меня), что система отопления состоит не только из одного котла. Например, у меня есть котел, к которому через гидрострелку подключены радиаторы, теплый пол, бойлер косвенного нагрева. Есть 3 насоса+насос рециркуляции ГВС. И всем этим хозяйством нужно управлять. Вот я и решил немного допилить скетч vlad219i.
Изменения:
-изменение температуры термостатирования перенесено в меню
-вращение энкодера до входа в меню теперь переключает каналы термостатирования
-реле основное вкл/выкл котел, замыкает и размыкает контакты термостата котла на плате.
-3 свободных аналоговых входа использованы для подключения сдвигового регистра 74HC595. Т.о. имеем 8 цифровых выходов, к которым подключены реле для управления 8 каналами. По каждому из каналов скетч работает как у vlad219i-задается температура и поддерживается, сохранено время миниманальной паузы между включениями/выключениями. Таймеры сохранены.
Когда по каждому из 8 каналов нет запроса на тепло, то выключается основное реле и котел гаснет.
Таким образом можно регулировать температуру в 8-ми комнатах отдельно, да ещё и по расписанию. Если конечно есть исполнительные устройсва в этих комнатах.
Я планирую управлять температурой в комнатах (один датчик на все комнаты) с помощью вкл/выкл насоса радиаторов. Аналогично для теплого пола и бойлера косвенного нагрева.
Цель-экономия газа. Скетч позволит мне снижать температуру в комнатах в ночное время или во время пребывания на работе, отключать теплый пол теплый пол на ночь, не греть бойлер когда это не нужно и разогревать его максимально в пиковые часы.
Будет ли экономия существенной я узнаю только следующей зимой. А пока это только мечты...
0001
/*
0002
Суточный термостат для котла Baxi Mainfour 240F
0003
>Encoder control
0004
>DS18B20 thermal sensor
0005
>LCD1602 parallel
0006
>DS1307 RTC
0007
*/
0008
0009
//#include <avr\eeprom.h>
0010
#include <Wire.h> // i2c (для RTC)
0011
#include <RealTimeClockDS1307.h> // RTC
0012
#include <EEPROMex.h> // EE
0013
#include <LiquidCrystal.h> // LCD 16*2
0014
#include <TimerOne.h> // прерывания по таймеру1
0015
0016
#include <OneWire.h> // 1wire для DS18B20
0017
#include <DallasTemperature.h> // DS18B20
0018
0019
#define ONE_WIRE_BUS A1
0020
OneWire oneWire(ONE_WIRE_BUS);
0021
DallasTemperature DS18B20(&oneWire);
0022
DeviceAddress DS18B20Address;
0023
0024
#define encoderA 2 // энкодер - поворот вправо (об землю)
0025
#define encoderB 3 // энкодер - поворот влево (об землю)
0026
#define encoderK 13 // энкодер - кнопка (об землю)
0027
#define BeepPin 12 // пищалка
0028
#define BeepToneNo 2000 // тон звука "No", герц
0029
#define BeepToneYes 4000 // тон звука "Yes", герц
0030
#define BeepToneNoDuration 200 // длительность звука "No", мс
0031
#define BeepToneYesDuration 200 // длительность звука "Yes", мс
0032
#define RelayMain 11 // нога, к которой подключено реле, которое вык/вкл котел
0033
#define RelayOn LOW // полярность сигнала включения реле (HIGH/LOW)
0034
#define qtyChannal 8 //количество каналов/датчиков
0035
int
channalOn;
//количество включенных реле (каналов, температура которых ниже заданной).
0036
// LCD connection RS, E, D4, D5, D6, D7
0037
// R/W - to ground
0038
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
0039
0040
//для регистра 74HС595
0041
#define clock 16
0042
#define data 17
0043
#define latch 14
0044
boolean Relay[8];
0045
0046
0047
0048
byte
block1[8] = {
0049
0x06,0x09,0x09,0x06,0x00,0x04,0x0E,0x1F };
// значок градуса с пламенем снизу
0050
byte
block2[8] = {
0051
0x06,0x09,0x09,0x06,0x00,0x00,0x00,0x00 };
// значок градуса
0052
0053
#define serialenabled // раскомментировать для выдачи в порт отладочной инфы
0054
0055
#define TstatTimerMax 180 //минимальная пауза между включениями горелки, сек
0056
unsigned
int
TstatTimer[qtyChannal] ;
//таймер паузы между включениями/выключениями, начальная установка 20 сек для устаканивания системы после сброса
0057
0058
float
DS18B20Temperature[qtyChannal] ;
//времянка
0059
float
Temperature[qtyChannal] ;
//вычисленная температура с коррекцией
0060
float
DS18B20TempTmp[qtyChannal];
//сырая температура от датчиков
0061
byte
DS18B20iteration = 0;
//счётчик измерений температуры для усреднения
0062
0063
float
TstatTemp[qtyChannal] ;
//температура термостатирования, может изменяться настройками
0064
float
TstatTempOld[qtyChannal];
0065
float
TemperatureCorr[qtyChannal];
//коррекция температуры, может изменяться настройками
0066
float
Hysteresis[qtyChannal] ;
// гистерезис термостата, может изменяться настройками
0067
float
HysteresisOld[qtyChannal];
0068
float
AlarmTemp[qtyChannal] ;
// температура для замерзательного орала
0069
0070
0071
int
Hours = 0;
// времянка часов RTC для отображения и установки
0072
int
Minutes = 0;
// времянка минут RTC для отображения и установки
0073
int
Seconds;
0074
boolean PrintYesNo =
false
;
// показывать ли после времени Yes/No (косвенно - указание на режим установка/отображение)
0075
boolean SetH =
false
;
// выделение часов при отображении
0076
boolean SetM =
false
;
// выделение минут при отображении
0077
boolean SetYesNo =
false
;
// выделение Yes/No при установке часов
0078
0079
boolean blink500ms =
false
;
// мигающий бит, инвертируется каждые 500мс
0080
boolean plus1sec =
false
;
// ежесекундно взводится
0081
0082
boolean BeepEnabled =
true
;
0083
int
channel=0;
//номер канала
0084
0085
byte
MenuTimeoutTimer;
0086
0087
// адреса градусников.
0088
DeviceAddress sensor []{
0089
{0x28, 0xFF, 0x08, 0x26, 0x34, 0x16, 0x04, 0x68},
0090
{0x28, 0xFF, 0x01, 0x4D, 0x34, 0x16, 0x04, 0x9F},
0091
{0x28, 0xFF, 0x75, 0x2D, 0x34, 0x16, 0x03, 0xFE},
0092
{0x28, 0xFF, 0x84, 0x4D, 0x34, 0x16, 0x04, 0x86},
0093
{0x28, 0xFF, 0x4D, 0x28, 0x34, 0x16, 0x03, 0xC5},
0094
{0x28, 0xFF, 0xF7, 0x4D, 0x34, 0x16, 0x04, 0xA5},
0095
{0x28, 0xFF, 0x8C, 0x30, 0x34, 0x16, 0x04, 0x8D},
0096
{0x28, 0xFF, 0xFC, 0x49, 0x34, 0x16, 0x04, 0xDB},
0097
{0x28, 0xFF, 0xAB, 0x30, 0x34, 0x16, 0x03, 0xA7},
0098
{0x28, 0xFF, 0x67, 0x40, 0x34, 0x16, 0x04, 0x8F},
0099
};
0100
// структура для суточных таймеров (8 байт)
0101
struct
buffer_template {
0102
byte
Hours;
0103
byte
Minutes;
0104
float
Temperature;
0105
boolean Enabled;
0106
boolean Activated;
0107
};
0108
buffer_template Timer[4][qtyChannal] = {0, 0, 23.0,
false
,
false
};
//объявление 4-х суточных таймеров для каждого из 9 каналов и их начальные значения
0109
0110
0111
// encoder vars
0112
static
boolean rotating=
false
;
// debounce management
0113
boolean A_set =
false
;
0114
boolean B_set =
false
;
0115
boolean encoderR =
false
;
0116
boolean encoderL =
false
;
0117
0118
// EEPROM
0119
EEMEM
float
TstatTempEE[qtyChannal];
//EE температура термостатирования
0120
EEMEM
float
TemperatureCorrEE[qtyChannal];
// EE коррекция температуры
0121
EEMEM
float
HysteresisEE[qtyChannal];
// EE гистерезис
0122
EEMEM boolean BeepEnabledEE;
// EE признак разрешения звука
0123
EEMEM
float
AlarmTempEE[qtyChannal];
// EE значение недопустимого снижения температуры
0124
EEMEM buffer_template TimerEE[4][qtyChannal];
// EE структуры для 4 суточных таймеров
0125
0126
// ===== SETUP ========================================================================
0127
void
setup
() {
0128
0129
0130
#ifdef serialenabled
0131
Serial
.begin(9600);
0132
#endif
0133
// задаем начальные значения
0134
for
(
int
j=0; j<qtyChannal; j++){
0135
TstatTimer[j]=20 ;
//таймер паузы между включениями/выключениями, начальная установка 20 сек для устаканивания системы после сброса
0136
DS18B20Temperature[j]=0 ;
//времянка
0137
Temperature[j]=0 ;
//вычисленная температура с коррекцией
0138
}
0139
pinMode(RelayMain, OUTPUT);
0140
digitalWrite(RelayMain, HIGH);
0141
lcd.begin(16, 2);
0142
lcd.createChar(1, block1);
0143
lcd.createChar(2, block2);
0144
pinMode(encoderA, INPUT);
0145
digitalWrite(encoderA, HIGH);
0146
pinMode(encoderB, INPUT);
0147
digitalWrite(encoderB, HIGH);
0148
pinMode(encoderK, INPUT);
0149
digitalWrite(encoderK, HIGH);
0150
attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 0 (pin 2)
0151
attachInterrupt(1, doEncoderB, CHANGE);
// encoder pin on interrupt 1 (pin 3)
0152
Timer1.initialize(500000);
// Timer0 interrupt - set a timer of length 500000 microseconds
0153
Timer1.attachInterrupt( timerIsr );
// attach the service routine here
0154
EEPROM.setMaxAllowedWrites(32767);
0155
//для сдвигового регистра
0156
pinMode(clock, OUTPUT);
0157
pinMode(data, OUTPUT);
0158
pinMode(latch, OUTPUT);
0159
digitalWrite(latch, HIGH);
0160
cleanreg();
0161
if
((digitalRead(encoderK)) == 0)
0162
{
// если первая запись однокристалки (зажата кнопка при включении питания)- записать начальные значения в EE
0163
lcd.setCursor(0, 0);
//инфо на LCD
0164
lcd.print(F(
"Cold start..."
));
0165
for
(
int
j=0; j<qtyChannal; j++){
0166
for
(
int
i=0; i<4; i++)
0167
{
0168
Timer[i][j].Hours = Timer[i][j].Minutes = 0;
0169
Timer[i][j].Temperature = 23.0;
0170
Timer[i][j].Enabled = Timer[i][j].Activated =
false
;
0171
EEPROM.updateBlock(
int
(&TimerEE[i][j]), Timer[i][j]);
0172
0173
}
0174
//присвоение начальных значений
0175
0176
TstatTemp[j]=25 ;
//температура термостатирования, может изменяться настройками
0177
TemperatureCorr[j]=0;
//коррекция температуры, может изменяться настройками
0178
Hysteresis[j]=0.5 ;
// гистерезис термостата, может изменяться настройками
0179
AlarmTemp[j]=20 ;
// температура для замерзательного орала
0180
//запись начальных значений в еепром
0181
EEPROM.updateFloat(
int
(&TstatTempEE[j]), TstatTemp[j]);
0182
EEPROM.updateByte(
int
(&BeepEnabledEE), BeepEnabled);
0183
EEPROM.updateFloat(
int
(&TemperatureCorrEE[j]), TemperatureCorr[j]);
0184
EEPROM.updateFloat(
int
(&HysteresisEE[j]), Hysteresis[j]);
0185
EEPROM.updateFloat(
int
(&AlarmTempEE[j]), AlarmTemp[j]);
0186
}
0187
tone(BeepPin,2000,50);
0188
delay(50);
0189
tone(BeepPin,3000,50);
0190
delay(50);
0191
tone(BeepPin,4000,50);
0192
delay(1000);
0193
}
0194
lcd.clear();
0195
lcd.setCursor(0, 0);
//инфо на LCD
0196
lcd.print(F(
"Read settings..."
));
0197
//чтение еепром
0198
BeepEnabled = EEPROM.readByte(
int
(&BeepEnabledEE));
0199
for
(
int
j=0; j<qtyChannal; j++) {
0200
TstatTemp[j] = EEPROM.readFloat(
int
(&TstatTempEE[j]));
0201
TemperatureCorr[j] = EEPROM.readFloat(
int
(&TemperatureCorrEE[j]));
0202
Hysteresis[j] = EEPROM.readFloat(
int
(&HysteresisEE[j]));
0203
AlarmTemp[j] = EEPROM.readFloat(
int
(&AlarmTempEE[j]));
0204
0205
0206
for
(
int
i=0; i<4; i++)
0207
{
0208
EEPROM.readBlock(
int
(&TimerEE[i][j]), Timer[i][j]);
0209
0210
}
0211
}
0212
DS18B20.begin();
0213
0214
tone(BeepPin,4000,50);
0215
delay(100);
0216
tone(BeepPin,4000,50);
0217
delay(1000);
0218
lcd.clear();
0219
RTC.start();
0220
0221
}
0222
// ===== MAIN CYCLE ===================================================================
0223
void
loop
() {
0224
lcd.setCursor(6, 0);
//инфо на LCD
0225
lcd.print(F(
"ch"
));
0226
lcd.print(channel);
0227
lcd.setCursor(9, 0);
0228
if
((Temperature[channel] < AlarmTemp[channel])&(blink500ms)) {
0229
lcd.print(F(
"*"
));
0230
}
0231
else
{
0232
lcd.print(F(
" "
));
0233
}
0234
lcd.print(F(
"t="
));
0235
if
(Temperature[channel] < 10) {
0236
lcd.print(F(
" "
));
0237
}
0238
lcd.print(Temperature[channel],1);
0239
lcd.write(0x02);
// значок градуса
0240
0241
// если таймер включен - надпись светится, если сработал - мигает, обрабатываем все 4 таймера
0242
lcd.setCursor(0, 1);
//инфо на LCD
0243
for
(
int
i=0;i<4;i++){
0244
if
((Timer[i][channel].Enabled)&!((Timer[i][channel].Activated)&(blink500ms))) {
0245
lcd.print(F(
"T"
));
0246
lcd.print(i+1);
0247
}
0248
else
{
0249
lcd.print(F(
" "
));
0250
}
0251
}
0252
0253
lcd.setCursor(9, 1);
//инфо на LCD
0254
lcd.print(F(
"s="
));
0255
lcd.print(TstatTemp[channel],1);
0256
if
( Relay[channel] == RelayOn ) {
0257
lcd.write(0x01);
// значок градуса с пламенем
0258
}
0259
else
{
0260
lcd.write(0x02);
// значок градуса
0261
}
0262
0263
// печатаем текущее время
0264
PrintYesNo =
false
;
0265
PrintRTC(0,0);
0266
Termostat();
// термостатирование каждого канала в отдельности
0267
0268
//вкл и выключение котла
0269
channalOn=qtyChannal;
//количество вкл каналов=количеству каналов
0270
for
(
int
j=0; j<qtyChannal; j++){
0271
if
(Relay[j]==!RelayOn)
//если канал выключен
0272
channalOn-=1;
0273
}
0274
if
(channalOn==0) {
//если нет включенных реле (нет запроса на тепло ни по одному из каналов)
0275
if
( digitalRead(RelayMain) == RelayOn)
//если котел включен
0276
digitalWrite(RelayMain, !RelayOn);
//выключить котел
0277
}
0278
0279
if
(channalOn>0) {
//если есть включенные реле (есть запроса на тепло хотя бы по одному из каналов)
0280
if
( digitalRead(RelayMain) == !RelayOn)
//если котел выключен
0281
digitalWrite(RelayMain, RelayOn);
//включить котел
0282
}
0283
0284
// если прошла 1 секунда - делаем ежесекундные дела
0285
if
(plus1sec) {
0286
plus1sec =
false
;
// сбрасываем до следующей секунды
0287
// обновляем часы
0288
RTC.readClock();
0289
Hours=RTC.getHours();
0290
Minutes=RTC.getMinutes();
0291
Seconds=RTC.getSeconds();
0292
0293
getTemp();
// читаем температуру с датчиков
0294
0295
0296
// проверка таймеров и изменение температуры термостатирования при совпадении (активации таймера)
0297
for
(
int
j=0; j<qtyChannal; j++){
0298
for
(
int
i=0;i<4;i++)
0299
{
0300
if
((Hours == Timer[i][j].Hours)&(Minutes == Timer[i][j].Minutes)&(Timer[i][j].Enabled)&(Seconds == 0)) {
// время T совпадает с RTC
0301
Timer[0][j].Activated = Timer[1][j].Activated = Timer[2][j].Activated = Timer[3][j].Activated =
false
;
0302
Timer[i][j].Activated =
true
;
0303
TstatTemp[j] = Timer[i][j].Temperature;
0304
EEPROM.updateFloat(
int
(&TstatTempEE[j]), TstatTemp[j]);
0305
if
(BeepEnabled) {
0306
tone(BeepPin,4000,5);
0307
}
0308
break
;
// это чтобы статус Activated остался взведённым
0309
}
0310
}
0311
}
0312
// дебаг-инфо - в терминал
0313
0314
#ifdef serialenabled
0315
Serial
.print(F(
"iteration ="
));
0316
Serial
.print (DS18B20iteration);
0317
Serial
.print(F(
" channalOn="
));
0318
Serial
.print (channalOn);
0319
if
(digitalRead(RelayMain) == RelayOn)
Serial
.println(F(
" MAIN Relay ON "
));
0320
else
Serial
.println(F(
" MAIN Relay Of "
));
0321
for
(
int
j=0; j<qtyChannal; j++){
0322
Serial
.print(F(
"sensor"
));
0323
Serial
.print(j,1);
0324
Serial
.print(F(
" Temp="
));
0325
Serial
.print(Temperature[j], 1);
0326
Serial
.print(F(
" TstatTemp="
));
0327
Serial
.print(TstatTemp[j], 1);
0328
Serial
.print(F(
" Hyster="
));
0329
Serial
.print(Hysteresis[j], 1);
0330
Serial
.print(F(
" ("
));
0331
Serial
.print(DS18B20Temperature[j], 1);
0332
Serial
.print(F(
" "
));
0333
Serial
.print(DS18B20TempTmp[j], 1);
0334
Serial
.print(F(
",corr "
));
0335
Serial
.print(TemperatureCorr[j], 1);
0336
Serial
.print(F(
"),Timer="
));
0337
Serial
.print(TstatTimer[j]);
0338
0339
if
(Relay[j]==RelayOn)
Serial
.println(F(
" ON "
));
0340
else
Serial
.println(F(
" OF "
));
0341
}
0342
Serial
.println(F(
" "
));
0343
0344
0345
#endif
0346
0347
for
(
int
j=0; j<qtyChannal; j++){
0348
if
(Temperature[j] < AlarmTemp[j])
0349
tone(BeepPin,4000,5);
0350
}
0351
0352
}
0353
// обработка поворота энкодера на изменение канала))
0354
rotating =
true
;
// reset the debouncer
0355
if
((encoderR)|(encoderL)) {
0356
if
(encoderR) {
0357
channel += 1;
0358
}
0359
else
0360
{
0361
channel -= 1;
0362
}
0363
if
( channel > qtyChannal-1) {
// количество каналов, т.к считаем с нуля
0364
channel = 0;
0365
}
0366
if
( channel < 0 ) {
0367
channel = qtyChannal-1;
0368
}
0369
encoderR =
false
;
0370
encoderL =
false
;
0371
0372
}
0373
// ================ по нажатию кнопки энкодера - меню настроек ====================
0374
if
(digitalRead(encoderK) == 0) {
0375
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0376
lcd.clear();
0377
lcd.setCursor(0, 0);
//инфо на LCD
0378
lcd.print(F(
"< SETUP > CH"
));
0379
lcd.print(channel);
0380
if
(BeepEnabled) {
0381
tone(BeepPin,4000,50);
0382
}
0383
delay(200);
0384
int
menuitem = 0;
0385
0386
0387
do
{
//обработка пунктов меню (по выбранному каналу)
0388
rotating =
true
;
// reset the debouncer
0389
if
((encoderR)|(encoderL)) {
0390
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0391
if
(encoderR) {
0392
menuitem += 1;
0393
}
0394
else
{
0395
menuitem -= 1;
0396
}
0397
if
( menuitem > 10 ) {
0398
menuitem = 0;
0399
}
// границы пунктов меню
0400
if
( menuitem < 0 ) {
0401
menuitem = 10;
0402
}
0403
encoderR =
false
;
0404
encoderL =
false
;
0405
}
0406
0407
// индикация пункта меню (номер пункта - в menuitem)
0408
lcd.setCursor(0, 1);
//инфо на LCD
0409
switch
(menuitem)
0410
{
0411
case
0:
0412
lcd.print(F(
"0.T-ON Relay "
));
0413
break
;
0414
case
1:
0415
lcd.print(F(
"1.TIMER1 SET "
));
0416
break
;
0417
case
2:
0418
lcd.print(F(
"2.TIMER2 SET "
));
0419
break
;
0420
case
3:
0421
lcd.print(F(
"3.TIMER3 SET "
));
0422
break
;
0423
case
4:
0424
lcd.print(F(
"4.TIMER4 SET "
));
0425
break
;
0426
case
5:
0427
lcd.print(F(
"5.CLOCK SET "
));
0428
break
;
0429
case
6:
0430
lcd.print(F(
"6.HYSTERESIS SET"
));
0431
break
;
0432
case
7:
0433
lcd.print(F(
"7.T-CORRECT SET "
));
0434
break
;
0435
case
8:
0436
lcd.print(F(
"8.SOUND SET "
));
0437
break
;
0438
case
9:
0439
lcd.print(F(
"9.T-ALARM SET "
));
0440
break
;
0441
case
10:
0442
lcd.print(F(
"10.BACK "
));
0443
break
;
0444
}
0445
if
(MenuTimeoutTimer == 0) {
0446
menuitem = 10;
0447
}
0448
0449
}
0450
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0451
// если нажата кнопка энкодера или таймаут - обработка пункта меню (номер пункта - в menuitem)
0452
if
(BeepEnabled) {
0453
tone(BeepPin,4000,50);
0454
}
0455
switch
(menuitem)
0456
{
0457
// ====== пункт 0 - выход
0458
case
10:
0459
if
(BeepEnabled) {
0460
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
0461
}
//звук "NO"
0462
break
;
// case 0 out
0463
0464
// ====== пункт 1 - установка Timer1
0465
case
1:
0466
TimerXSetup(0);
0467
break
;
// case 1 out
0468
0469
// ====== пункт 2 - установка Timer2
0470
case
2:
0471
TimerXSetup(1);
0472
break
;
// case 2 out
0473
0474
// ====== пункт 3 - установка Timer3
0475
case
3:
0476
TimerXSetup(2);
0477
break
;
// case 3 out
0478
0479
// ====== пункт 4 - установка Timer4
0480
case
4:
0481
TimerXSetup(3);
0482
break
;
// case 4 out
0483
0484
// ====== пункт 5 - установка RTC
0485
case
5:
0486
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0487
lcd.clear();
0488
lcd.setCursor(0, 0);
//инфо на LCD
0489
lcd.print(F(
"SETUP CLOCK"
));
0490
delay(200);
0491
RTC.readClock();
0492
Hours=RTC.getHours();
0493
Minutes=RTC.getMinutes();
0494
SetYesNo =
false
;
0495
PrintYesNo =
true
;
0496
SetTime(0,1);
// в позиции 0,1 - запрос ввода времени
0497
if
(MenuTimeoutTimer != 0) {
0498
if
(SetYesNo)
0499
{
0500
if
(BeepEnabled) {
0501
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
0502
}
0503
RTC.setHours(Hours);
0504
RTC.setMinutes(Minutes);
0505
RTC.setSeconds(0);
0506
RTC.setClock();
0507
RTC.start();
0508
}
0509
else
0510
{
0511
if
(BeepEnabled) {
0512
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
0513
}
0514
}
0515
}
0516
else
{
0517
if
(BeepEnabled) {
0518
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
0519
}
0520
}
0521
break
;
// case 5 out
0522
0523
// ====== пункт 6 - установка гистерезиса
0524
case
6:
0525
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0526
HysteresisOld[channel] = Hysteresis[channel];
0527
lcd.clear();
0528
lcd.setCursor(0, 0);
//инфо на LCD
0529
lcd.print(F(
"SETUP HYSTERESIS"
));
0530
delay(200);
0531
do
{
0532
lcd.setCursor(0,1);
0533
if
(blink500ms) {
0534
lcd.print(
" "
);
0535
}
0536
else
{
0537
lcd.print(Hysteresis[channel], 1);
0538
lcd.write(0x02);
// значок градуса
0539
}
0540
rotating =
true
;
// reset the debouncer
0541
if
(encoderR) {
0542
Hysteresis[channel] += 0.1;
0543
encoderR =
false
;
0544
}
0545
if
(encoderL) {
0546
Hysteresis[channel] -= 0.1;
0547
encoderL =
false
;
0548
}
0549
Hysteresis[channel] = constrain(Hysteresis[channel], 0.1, 20);
// крайние значения
0550
}
0551
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0552
if
(MenuTimeoutTimer != 0) {
0553
EEPROM.updateFloat(
int
(&HysteresisEE[channel]), Hysteresis[channel]);
// запись в ЕЕПРОМ
0554
if
(BeepEnabled) {
0555
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
0556
}
0557
}
0558
else
{
0559
Hysteresis[channel] = HysteresisOld[channel];
0560
if
(BeepEnabled) {
0561
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
0562
}
0563
}
0564
break
;
// case 6 out
0565
0566
// ====== пункт 7 - установка коррекции температуры
0567
case
7:
0568
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0569
lcd.clear();
0570
lcd.setCursor(0, 0);
//инфо на LCD
0571
lcd.print(F(
"SETUP T-CORRECT "
));
0572
delay(200);
0573
do
{
0574
lcd.setCursor(0,1);
0575
if
(blink500ms) {
0576
lcd.print(F(
" "
));
0577
}
0578
else
{
0579
if
(TemperatureCorr[channel] >= 0) {
0580
lcd.print(F(
"+"
));
0581
}
0582
0583
lcd.print(TemperatureCorr[channel], 1);
0584
lcd.write(0x02);
// значок градуса
0585
}
0586
rotating =
true
;
// reset the debouncer
0587
if
(encoderR) {
0588
TemperatureCorr[channel] += 0.1;
0589
encoderR =
false
;
0590
}
0591
if
(encoderL) {
0592
TemperatureCorr[channel] -= 0.1;
0593
encoderL =
false
;
0594
}
0595
TemperatureCorr[channel] = constrain(TemperatureCorr[channel], -8, 8);
// крайние значения
0596
}
0597
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0598
0599
if
(MenuTimeoutTimer != 0) {
0600
EEPROM.updateFloat(
int
(&TemperatureCorrEE[channel]), TemperatureCorr[channel]);
// запись в ЕЕПРОМ
0601
if
(BeepEnabled) {
0602
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
0603
}
0604
}
0605
else
{
0606
TemperatureCorr[channel] = EEPROM.readFloat(
int
(&TemperatureCorrEE[channel]));
0607
if
(BeepEnabled) {
0608
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
0609
}
0610
}
0611
break
;
// case 7 out
0612
0613
// ====== пункт 8 - вкл/выкл звука
0614
case
8:
0615
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0616
lcd.clear();
0617
lcd.setCursor(0, 0);
//инфо на LCD
0618
lcd.print(F(
"SOUND SET "
));
0619
delay(200);
0620
do
{
0621
lcd.setCursor(0,1);
0622
if
(BeepEnabled) {
0623
lcd.print(F(
"BEEP ON "
));
0624
}
0625
else
{
0626
lcd.print(F(
"BEEP OFF "
));
0627
}
0628
0629
rotating =
true
;
// reset the debouncer
0630
if
((encoderR)|(encoderL)) {
0631
BeepEnabled = !BeepEnabled;
0632
encoderR =
false
;
0633
encoderL =
false
;
0634
}
0635
}
0636
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0637
0638
if
(MenuTimeoutTimer != 0) {
0639
if
(BeepEnabled) {
0640
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
0641
}
0642
EEPROM.updateByte(
int
(&BeepEnabledEE), BeepEnabled);
0643
}
0644
if
(MenuTimeoutTimer == 0) {
0645
BeepEnabled = EEPROM.readByte(
int
(&BeepEnabledEE));
0646
}
0647
break
;
// case 8 out
0648
0649
// ====== пункт 9 - установка предупреждалки о холоде
0650
case
9:
0651
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0652
lcd.clear();
0653
lcd.setCursor(0, 0);
//инфо на LCD
0654
lcd.print(F(
"ALARM-TEMP SET "
));
0655
delay(200);
0656
do
{
0657
lcd.setCursor(0,1);
0658
if
(blink500ms) {
0659
lcd.print(F(
" "
));
0660
}
0661
else
{
0662
if
(AlarmTemp[channel] >= 0) {
0663
lcd.print(F(
"+"
));
0664
}
0665
0666
lcd.print(AlarmTemp[channel], 0);
0667
lcd.write(0x02);
// значок градуса
0668
}
0669
rotating =
true
;
// reset the debouncer
0670
if
(encoderR) {
0671
AlarmTemp[channel] += 1;
0672
encoderR =
false
;
0673
}
0674
if
(encoderL) {
0675
AlarmTemp[channel] -= 1;
0676
encoderL =
false
;
0677
}
0678
AlarmTemp[channel] = constrain(AlarmTemp[channel], 15, 30);
// крайние значения
0679
}
0680
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0681
0682
if
(MenuTimeoutTimer != 0) {
0683
EEPROM.updateFloat(
int
(&AlarmTempEE[channel]), AlarmTemp[channel]);
// запись в ЕЕПРОМ
0684
if
(BeepEnabled) {
0685
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
0686
}
0687
}
0688
else
{
0689
AlarmTemp[channel] = EEPROM.readFloat(
int
(&AlarmTempEE[channel]));
0690
if
(BeepEnabled) {
0691
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
0692
}
0693
}
0694
break
;
// case 9 out
0695
0696
// ====== пункт 10 - установка температуры термостатирования
0697
case
0:
0698
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0699
TstatTempOld[channel]=TstatTemp[channel];
0700
lcd.clear();
0701
lcd.setCursor(0, 0);
//инфо на LCD
0702
lcd.print(F(
"SETUP T-ON Raley"
));
0703
delay(200);
0704
do
{
0705
lcd.setCursor(0,1);
0706
if
(blink500ms) {
0707
lcd.print(
" "
);
0708
}
0709
else
{
0710
lcd.print(TstatTemp[channel], 1);
0711
lcd.write(0x02);
// значок градуса
0712
}
0713
rotating =
true
;
// reset the debouncer
0714
if
(encoderR) {
0715
TstatTemp[channel] += 1;
0716
encoderR =
false
;
0717
}
0718
if
(encoderL) {
0719
TstatTemp[channel] -= 1;
0720
encoderL =
false
;
0721
}
0722
TstatTemp[channel] = constrain(TstatTemp[channel], 1, 80);
// крайние значения
0723
}
0724
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0725
if
(MenuTimeoutTimer != 0) {
0726
EEPROM.updateFloat(
int
(&TstatTempEE[channel]), TstatTemp[channel]);
// запись в ЕЕПРОМ
0727
if
(BeepEnabled) {
0728
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
0729
}
0730
}
0731
else
{
0732
TstatTemp[channel] = TstatTempOld[channel];
0733
if
(BeepEnabled) {
0734
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
0735
}
0736
}
0737
break
;
// case 10 out
0738
}
0739
0740
delay(200);
0741
lcd.clear();
0742
}
0743
}
0744
0745
// ===== SUBROUTINES ==================================================================
0746
//функция очистки сдвигового регистра
0747
void
cleanreg(){
0748
for
(
int
i=0; i<8; i++)
0749
Relay[i]=!RelayOn;
0750
digitalWrite(latch, LOW);
0751
shiftOut(data, clock, LSBFIRST, 0);
0752
digitalWrite(latch, HIGH);
0753
}
0754
// ========================================
0755
//функция управления сдвиговым регистром
0756
void
sendpin(
int
pin, boolean state){
0757
0758
Relay[pin]=state;
0759
0760
byte
value = 0;
0761
byte
add = 1;
0762
for
(
int
i=0; i<8; i++){
0763
if
(Relay[i]==HIGH) value+=add;
0764
add*=2;
0765
}
0766
digitalWrite(latch, LOW);
0767
shiftOut(data, clock, MSBFIRST, value);
0768
digitalWrite(latch, HIGH);
0769
}
0770
// ========================================
0771
void
getTemp(){
// измеряем температуру на всех каналах
0772
DS18B20.requestTemperatures();
0773
for
(
int
j=0; j<qtyChannal; j++){
0774
DS18B20TempTmp[j] = DS18B20.getTempC(sensor[j]);
0775
DS18B20Temperature[j] += DS18B20TempTmp[j];
// суммируем для усреднения
0776
}
0777
DS18B20iteration ++;
0778
if
(DS18B20iteration == 10)
0779
{
0780
DS18B20iteration = 0;
0781
for
(
int
j=0; j<qtyChannal; j++){
0782
Temperature[j] = (DS18B20Temperature[j] / 10) + TemperatureCorr[j];
//усреднённая + коррекция
0783
DS18B20Temperature[j] = 0;
0784
}
0785
}
0786
}
0787
// ========================================
0788
void
Termostat() {
//термостатирование
0789
for
(
int
j=0; j<8; j++){
0790
0791
if
( TstatTimer[j] == 0 )
//если пауза между вкл/выкл реле превысила TstatTimerMax;
0792
{
0793
if
( Temperature[j] > ( TstatTemp[j] + Hysteresis[j] ) )
// если температра канала > температура заданная+гистерезис
0794
{
0795
if
( Relay[j]==RelayOn )
// если реле включено -
0796
{
0797
sendpin(j, !RelayOn);
// выключить реле;
0798
TstatTimer[j] = TstatTimerMax;
// реле держать выключённым не менее заданного в TstatTimerMax времени
0799
}
0800
}
0801
if
(Temperature[j] < TstatTemp[j])
//если температура канала меньше заданной
0802
{
0803
if
( Relay[j]==!RelayOn )
// если реле выключено
0804
{
0805
sendpin(j, RelayOn);
// включить реле
0806
TstatTimer[j] = TstatTimerMax;
// реле держать включённым не менее заданного в TstatTimerMax времени
0807
}
0808
}
0809
}
0810
}
0811
}
0812
// ========================================
0813
void
SetTime(
char
x,
char
y)
0814
{
0815
// ========= set hours
0816
SetH =
true
;
0817
do
{
0818
PrintRTC(x,y);
0819
rotating =
true
;
// reset the debouncer
0820
if
(encoderR) {
0821
Hours += 1;
0822
if
(Hours > 23) {
0823
Hours = 0;
0824
};
0825
encoderR =
false
;
0826
}
0827
if
(encoderL) {
0828
Hours -= 1;
0829
if
(Hours < 0) {
0830
Hours = 23;
0831
};
0832
encoderL =
false
;
0833
}
0834
}
0835
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0836
if
(BeepEnabled) {
0837
tone(BeepPin,4000,50);
//звук "YES"
0838
}
0839
SetH =
false
;
0840
delay(200);
0841
// ========= set minutes
0842
SetM =
true
;
0843
do
{
0844
PrintRTC(0,1);
0845
rotating =
true
;
// reset the debouncer
0846
if
(encoderR) {
0847
Minutes += 1;
0848
if
(Minutes > 59) {
0849
Minutes = 0;
0850
};
0851
encoderR =
false
;
0852
}
0853
if
(encoderL) {
0854
Minutes -= 1;
0855
if
(Minutes < 0) {
0856
Minutes = 59;
0857
};
0858
encoderL =
false
;
0859
}
0860
}
0861
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0862
if
(BeepEnabled) {
0863
tone(BeepPin,4000,50);
//звук "YES"
0864
}
0865
if
(PrintYesNo) {
0866
SetM =
false
;
0867
delay(200);
0868
// ========= set yes-no
0869
SetYesNo =
false
;
0870
do
{
0871
PrintRTC(0,1);
0872
rotating =
true
;
// reset the debouncer
0873
if
((encoderR)||(encoderL)) {
0874
SetYesNo = !SetYesNo;
0875
encoderR =
false
;
0876
encoderL =
false
;
0877
}
0878
}
0879
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
0880
delay(200);
0881
}
0882
0883
}
0884
0885
// ========================================
0886
void
PrintRTC(
char
x,
char
y)
0887
{
0888
lcd.setCursor(x,y);
0889
if
(SetH&&blink500ms) {
0890
lcd.print(F(
" "
));
0891
}
0892
else
{
0893
if
(Hours < 10) {
0894
lcd.print(F(
"0"
));
0895
}
0896
lcd.print(Hours);
0897
}
0898
0899
// мигающее двоеточие, если не в режиме установки времени
0900
if
(!(SetH||SetM||PrintYesNo||blink500ms))
0901
{
0902
lcd.print(F(
" "
));
0903
}
0904
else
{
0905
lcd.print(F(
":"
));
0906
}
0907
0908
if
(SetM&&blink500ms) {
0909
lcd.print(F(
" "
));
0910
}
0911
else
{
0912
if
(Minutes < 10) {
0913
lcd.print(F(
"0"
));
0914
}
0915
lcd.print(Minutes);
0916
}
0917
lcd.print(F(
" "
));
0918
0919
if
(PrintYesNo) {
0920
lcd.print(F(
"["
));
0921
if
(!(SetH||SetM||blink500ms))
0922
{
0923
lcd.print(F(
" "
));
0924
}
0925
else
{
0926
if
(SetYesNo)
0927
{
0928
lcd.print(F(
"YES"
));
0929
}
0930
else
{
0931
lcd.print(F(
"NO "
));
0932
}
0933
}
0934
lcd.print(F(
"]"
));
0935
}
0936
0937
}
0938
0939
// ============================ Encoder interrupts =============================
0940
// Interrupt on A changing state
0941
void
doEncoderA(){
0942
if
( rotating ) {
0943
delay (1) ;
// wait a little until the bouncing is done
0944
}
0945
// Test transition, did things really change?
0946
if
( digitalRead(encoderA) != A_set ) {
// debounce once more
0947
A_set = !A_set;
0948
// adjust counter + if A leads B
0949
if
( A_set && !B_set )
0950
{
0951
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0952
if
(BeepEnabled) {
0953
tone(BeepPin,4000,5);
0954
}
0955
encoderR =
true
;
0956
rotating =
false
;
// no more debouncing until loop() hits again
0957
}
0958
}
0959
}
0960
// Interrupt on B changing state, same as A above
0961
void
doEncoderB(){
0962
if
( rotating ) {
0963
delay (1);
0964
}
0965
if
( digitalRead(encoderB) != B_set ) {
0966
B_set = !B_set;
0967
// adjust counter - 1 if B leads A
0968
if
( B_set && !A_set ) {
0969
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0970
if
(BeepEnabled) {
0971
tone(BeepPin,4000,5);
0972
}
0973
encoderL =
true
;
0974
rotating =
false
;
0975
}
0976
}
0977
}
0978
// ============================ Timer0 interrupt =============================
0979
// run every 500ms
0980
void
timerIsr()
0981
{
0982
blink500ms = !blink500ms;
// инверсия мерцающего бита
0983
if
(blink500ms) {
0984
plus1sec =
true
;
// ежесекундно взводится
0985
for
(
int
j=0; j<qtyChannal; j++) {
0986
if
(TstatTimer[j] != 0) {
0987
TstatTimer[j] --;
// ежесекундный декремент этого таймера
0988
}
0989
}
0990
if
(MenuTimeoutTimer != 0) {
0991
MenuTimeoutTimer --;
// ежесекундный декремент этого таймера
0992
}
0993
0994
}
0995
}
0996
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0997
void
TimerXSetup(
int
X) {
0998
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
0999
lcd.clear();
1000
lcd.setCursor(0, 0);
//инфо на LCD
1001
lcd.print(F(
"SETUP TIMER"
));
1002
lcd.print(X+1);
// выводим номер таймера на LCD
1003
delay(200);
1004
Hours=Timer[X][channel].Hours;
1005
Minutes=Timer[X][channel].Minutes;
1006
SetYesNo =
false
;
1007
PrintYesNo =
true
;
1008
SetTime(0,1);
// в позиции 0,1 - запрос ввода времени
1009
if
(MenuTimeoutTimer != 0) {
1010
if
(SetYesNo)
// если при установке времени выбрано "Yes"
1011
{
1012
if
(BeepEnabled) {
1013
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
1014
}
1015
Timer[X][channel].Hours = Hours;
1016
Timer[X][channel].Minutes = Minutes;
1017
Timer[X][channel].Enabled =
true
;
1018
EEPROM.updateBlock(
int
(&TimerEE[X][channel]), Timer[X][channel]);
1019
1020
MenuTimeoutTimer = 10;
//таймер таймаута, секунд
1021
lcd.clear();
1022
lcd.setCursor(0, 0);
//инфо на LCD
1023
lcd.print(F(
"Timer"
));
1024
lcd.print(X+1);
1025
lcd.print(F(
" Temp. Set"
));
1026
delay(200);
1027
do
{
1028
lcd.setCursor(0,1);
1029
if
(blink500ms) {
1030
lcd.print(F(
" "
));
1031
}
1032
else
{
1033
lcd.print(Timer[X][channel].Temperature, 1);
1034
lcd.write(0x02);
// значок градуса
1035
}
1036
rotating =
true
;
// reset the debouncer
1037
if
(encoderR) {
1038
Timer[X][channel].Temperature += 0.1;
1039
encoderR =
false
;
1040
}
1041
if
(encoderL) {
1042
Timer[X][channel].Temperature -= 0.1;
1043
encoderL =
false
;
1044
}
1045
Timer[X][channel].Temperature = constrain(Timer[X][channel].Temperature, 10, 80);
// крайние значения
1046
}
1047
while
((digitalRead(encoderK)==1)|(MenuTimeoutTimer==0));
1048
1049
if
(MenuTimeoutTimer != 0) {
// если после выбора температуры нажата кнопка энкодера
1050
EEPROM.updateBlock(
int
(&TimerEE[X][channel]), Timer[X][channel]);
1051
if
(BeepEnabled) {
1052
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
1053
}
1054
}
1055
else
{
// если не нажата - используем старую температуру
1056
EEPROM.readBlock(
int
(&TimerEE[X][channel]), Timer[X][channel]);
1057
if
(BeepEnabled) {
1058
tone(BeepPin,BeepToneYes,BeepToneYesDuration);
//звук "YES"
1059
}
1060
}
1061
1062
}
1063
else
// если при установке времени выбрано "No"
1064
{
1065
if
(BeepEnabled) {
1066
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
1067
}
1068
EEPROM.readBlock(
int
(&TimerEE[X][channel]), Timer[X][channel]);
1069
Timer[X][channel].Enabled =
false
;
1070
EEPROM.updateBlock(
int
(&TimerEE[X][channel]), Timer[X][channel]);
1071
}
1072
}
1073
else
{
1074
if
(BeepEnabled) {
1075
tone(BeepPin,BeepToneNo,BeepToneNoDuration);
//звук "NO"
1076
}
1077
}
1078
}
1079
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~````
А вообще, лично бы я пострелся ставить что-то что может самостоятельно "зажигать гас". Я бы ставил только больше/меньше. Причем, как правильно заметил JollyBiber нужен еще контроль что "горении идет".
Если погасло (не важно по каким причинам), все перекрывать, слать SMS хозяину, и... по хорошему, еще бы и себя обесточить.
А уж он пусть приходить и разбирается "чего погасло". И запуск только "в ручном режиме".
А меня задавила. Имелся работающий котёл (примерно 13 лет эксплуатации). Имелась в нем одна доработка - заглушен контур ГВ, так как оказался самой не надёжной частью и требовал замены уплотнительных резинок. С заглушеным контуром ГВ проработал примерно 9 лет. А осенью 16го автоматика стала выдавать ошибки бредовые. Вызвал ремонтника профессионала, который с ходу сказал, что так долго не живут, мало того, что заказать такую же автоматику не получится, так и железо-то наверняка всё покрыто накипью и прочими отложениями, и насос та уже стаскался... короче есть у них на складе новые котлы из старой партии, и потому они его могут мне продать всего за 750 евросов...
И тут меня жаба и задавила. Я сам занимаюсь установкой и настройкой готового оборудования и автоматики отопления и ГВС в частных и многоквартирных домах. А тут ещё ради интереса прошлой весной собрал на Ардуино простой термостат с управлением сервоприводом трехходового клапана.
В общем как про 750 евро услышал, у меня сразу мысль родилась, попробовать Ардуино подружить с моим котлом. И так как я в схемотекнике не опытен был, и до этого паял только лампочки на новогодней гирлянде, поэтому прект ещё не закончил. Но много сделано, котёл запускается, датчик ионизаци простой как две копейки собрал сам, температуру нагнали - выключили клапана. Сбой электричества, свет пропал - даже при горящем котле - клапана закрылись. И так уже два месяца работает черновой вариант.
Ардуино - 6 евро. Остальное выпаяно из старой автоматики и старого же компьютерного монитора с кинескопом. Бонусом тернеровка серого вещества, новые знания и бесценный опыт.
И никакого привлечения военокосмического комплекса с нанотехнологиями не надо. Просто надо понимать, с чем имеешь дело.
А ещё... Хотел сказать по поводу диферамб в сторону наружнего датчика и погодозависимых графиков...
Во всех автоматиках, по крайней мере, что у нас в Эстонии продают, предусмотрен вход для подключения комнатного датчика. Именно с таким датчиком система работает более эффективно (читай экономно) и комфортней для потребителя. Устанавливать датчик конечно надо с учетом конкретного помещения и ньюансов там не мало.
Утверждать, что погодозависимого управления по графику достаточно для системы отопления дома - можно. Но утверждение, что это лучше управления по температуре внутреннего воздуха противоречит практике.
1. А осенью 16го автоматика стала выдавать ошибки бредовые.
2. Я сам занимаюсь установкой и настройкой готового оборудования и автоматики отопления и ГВС в частных и многоквартирных домах. А тут ещё ради интереса прошлой весной собрал на Ардуино простой термостат с управлением сервоприводом трехходового клапана.
3. И так как я в схемотекнике не опытен был, и до этого паял только лампочки на новогодней гирлянде, поэтому прект ещё не закончил. Но много сделано, котёл запускается, датчик ионизаци простой как две копейки собрал сам, температуру нагнали - выключили клапана. Сбой электричества, свет пропал - даже при горящем котле - клапана закрылись. И так уже два месяца работает черновой вариант.
4. Ардуино - 6 евро. Остальное выпаяно из старой автоматики и старого же компьютерного монитора с кинескопом. Бонусом тернеровка серого вещества, новые знания и бесценный опыт.
5. А ещё... Хотел сказать по поводу диферамб в сторону наружнего датчика и погодозависимых графиков...
Во всех автоматиках, по крайней мере, что у нас в Эстонии продают, предусмотрен вход для подключения комнатного датчика. Именно с таким датчиком система работает более эффективно (читай экономно) и комфортней для потребителя. Устанавливать датчик конечно надо с учетом конкретного помещения и ньюансов там не мало.
6. Утверждать, что погодозависимого управления по графику достаточно для системы отопления дома - можно. Но утверждение, что это лучше управления по температуре внутреннего воздуха противоречит практике.
1. Автоматика показывает ошибки, а вы ее на помойку и все напрямую подключили... однако... (храни вас Господь)
2. Это хорошо когда разбираешься в теме. Вон хирург Леонид Рогозов сам себе аппендикс вырезал, но у него была делема жизнь или смерть. Вы тоже по этому пути собрались? Напишите в своем посте большими буквами "так делать нельзя" все делаете на свой страх и риск...
3. В схеммотехнике придется разбираться и в программировании тоже, ибо просто при пропадании электричества закрыть газ это мало...
4. Да-да 6 евро это стоимость кристала для любителей сделать что то своими руками. В нашем случае все делают устройство которое заставляет котел работать так как нам кажется правильно. Причем мы не лезем в защиту котла, а тупо используем контакты выведенные производителем для наружного таймера. При выходе из строя нашего изделия котел будет работать как и прежде в штатном режиме... Это единственно правильное решение, ибо 97% тех кто делает что то на ардуино, не знает (да это и не нужно) как работает автоматика того же котла...
5. Посмотрел температуру в Эстонии по зимним месяцам, смешно стало... Эффективно и экономно не одно и тоже... (читай п.6)
6. Зачем греть воду до максимума когда за окном +5? Проблема регулировки по температуре помещения это черезмерно горячие батареи, как следствие сухой воздух в помещении, как следствие статическое напряжение на всем... Ну и еще много чего можно сказать в зависимости от строения и степени его утепления и т.д.
leshak правильно сказал, что должно быть что то сертифицированное в части управления котлом... именно в управлении, а не регулировании температуры...
По п. 6 не понятно. Как связана сухость воздуха в помещении с температурой батарей, если в помещении нормальная температура?
Как связана сухость воздуха в помещении с температурой батарей, если в помещении нормальная температура?
На прямую. В отопительный сезон горячие батареи сушат воздух, проветривание помещения в мороз также сушит воздух. Так что температура в помещении ни при чем. А чем больше температура батареи тем интенсивнее происходит "осушение". Как то так...
В п. 6 можно еще указать что при инерционной системе при регулировании по температуре в помещении могут окна начать "плакать"... своего рода увлажнение... :)
Просто нельзя утверждать что по температуре в помещении регулировка самая-самая... (и она подтверждена практикой в конкретной климатической зоне) Очень много разных факторов влияют на комфорт в помещении в период отопления.