Мне нужен некий минимальный набор кода (наиболее простой), но способный обеспечить вполне надёжную работу системы автоматического поддержания температуры в помещении. Основные исполнительные органы - котёл на дизельном топливе, насосы перекачивающие воду. У котла - своя автоматика защиты и контроля процесса горения (штатная, заводская). Я лишь планирую включать/выключать котёл с помощью реле.
Да Вы, батенька, большой оптимист.
То, что сформулировано Вами - это достаточно сложная задача. Судя по Вашим постам на форуме, Вам она явно не по зубам.
По поводу температур (датчиков и дальнейших разбирательств с ними) - я в тупике. Полагаю на данном этапе пока оставить их в покое. Как я себе представляю - наиболее существенным остаётся вопрос как работать с отрицательными значениями температур. Его я оставлю на потом - если проект будет двигаться далее, когда оставшиеся компоненты будут задействованы - удлиню провод и засуну датчик в морозилку. И попытаюсь как-нибудь настроить работу с отрицательными температурами. За совет про signBit = raw &0x8000; - спасибо. Так и буду пытаться получить желаемое.
Подскажите, если я в этой ветке стану обсуждать далее вопросы, не совсем относящиеся к названию темы - в этом ничего страшного? Или лучше создать новую тему?
Далее планирую заняться подключением платы часов реального времени (DS3231). Прошу вашего совета - к какому контроллеру лучше подключить. По моим поверхностным представлениям - можно подключить как к nano, так и к UNO. При этом - в настоящий момент у меня на nano прописан код считывания и пересылки данных с датчиков температуры, скетч использует 9% памяти устройства. На UNO - код прорисовки меню для TFT LCD дисплея и код приёма и вывода значений температур. Скетч использует 54% памяти устройства.
Итого - что без домыслов в двух первых байтах при обрыве и при замыкании?
Ну ОК, будем считать, что вы правы. Хотя мне все равно не понятно, как это может работать. Но лезть в исходники сейчас и правда совершенно некогда. может дома гляну.
Для передачи бита есть таймслот. Его запускает МК прижиманием DQ к земле. Затем слушает. За это время сенсор в ответ должен (судя по диаграмме из даташита) прижать DQ к земле на 60uS, если передает "0", и менее, чем на 15uS, если передает "1". OneWire::read_bit() просто читает состояние пина через N uS (13uS + накладные расходы). Т.е. она принимает за факт, что если через 15uS на шине присутствует GND, то передавался "0". Если на шине V_pull-up - передавался "1". Начальное состояние пина после старта таймслота не контролируется, что ускоряет чтение, но вносит недетектируемую ошибку в принимаемые биты. В большинстве случаев наводки гасятся через pull-up резистор, поэтому тем, кто складывает два бита - просто везет. Но защиты от чтения оторванной или короткозамкнутой шины данная библиотека не содержит.
Именно поэтому DallasTemerature, как я понимаю, не доверяет сформированным байтам и в обязательном порядке производит сверку по CRC.
Далее планирую заняться подключением платы часов реального времени (DS3231). Прошу вашего совета - к какому контроллеру лучше подключить. По моим поверхностным представлениям - можно подключить как к nano, так и к UNO.
Никакой разницы нет. Где свободны A4/A5 и нет жестких ограничений по времени выполнения кода - туда и подключайте. Ну и, в принципе, часы откликаются быстро, так что задержек особых не будет (особенно если их дергать не каждый луп, а через 1000мс минимум). Думайте не куда подключить, а как ими управлять - время выставить, то-сё. Потом будет понятно - куда их выгодней прицепить.
Я подключил ЧРВ к nano (провода соединил). Стал искать библиотеку - и нашёл много вариантов - начиная от того, что используют библиотеку от более ранних версий (DS1307) и заканчивая тем, что есть "самопальные". При этом - зачастую используют две библиотеки для выполнений просто вывода значений времени в Serial. Можете ли посоветовать некий наименее запутанный вариант для меня? Мне в итоге от этого ЧРВ нужно будет чтобы в заданное время началась выполняться процедура и закончила своё выполнение через определённо время.
Ещё любопытно: наткнулся на информацию, что в DS3231 есть встроенный датчик температуры. Мелькнула мысль - насколько разумно попытаться применить этот датчик как датчик температуры воздуха в помещении (но не понятно, какие дополнительные пины и куда подключать, какая библиотека и какая точность датчика).
А управлять ЧРВ мне требуется с TFT LCD экранчика - я уже и менюшку прорисовал для выставления времени включения системы по таймеру.
На UNO у меня пины A4/A5 задействованы на SD карточку TFT LCD дисплея. И немотря на то, что карточкой я пока пользоваться не планирую, я встречал мнение, что и SD карточку и ЧРВ на эти пины можно подключить одновременно (как бы параллельно).
Я подключил ЧРВ к nano (провода соединил). Стал искать библиотеку - и нашёл много вариантов - начиная от того, что используют библиотеку от более ранних версий (DS1307) и заканчивая тем, что есть "самопальные".
Да вы, я смотрю, любитель брать вершины без страховки - датчики читать без CRC, библиотеку брать от другого чипа.
При этом - зачастую используют две библиотеки для выполнений просто вывода значений времени в Serial. Можете ли посоветовать некий наименее запутанный вариант для меня? Мне в итоге от этого ЧРВ нужно будет чтобы в заданное время началась выполняться процедура и закончила своё выполнение через определённо время.
Есть два варианта - в лупе читать время и принимать решение на основании считанных H:m:s или выставлять Alarm на RTC, который дернет ногу через ногу INT ардуиновское ExternalInterrupt (см. attachInterrupt()). Мне кажется, что во втором случае вы больше дров наломаете.
PPeterr пишет:
Ещё любопытно: наткнулся на информацию, что в DS3231 есть встроенный датчик температуры. Мелькнула мысль - насколько разумно попытаться применить этот датчик как датчик температуры воздуха в помещении.
По моему мнению - нинасколько. Мерять вы будете температуру не в помещении, а в корпусе. А туда еще, поди, запихаете модуль питания или еще что-нить нагревающееся. По СанПиновской методике это вообще не измерение. Датчик там для другого - часы изменяют "ход", основываясь на его показаниях. Термокомпенсация.
Кстати, точность 0.5 с 18B20 можете получить выставив разрешение на датчике в 9bit. Но только один раз ставьте (в setup()), а то дыру ему в ROM прожжете )) Судя по всему вы собираетесь долбить в датчики на каждом лупе (что, кстати, разогреет сенсоры дополнительно и внесет искажения до 0.5C).
Кстати, точность 0.5 с 18B20 можете получить выставив разрешение на датчике в 9bit. Но только один раз ставьте (в setup()), а то дыру ему в ROM прожжете ))
Ещё лучше выставить разрешение один раз и сохранить установки в датчике.
Спасибо. Скачал, загрузил библиотеку. Открыл пример "DS3231_simple". Открыл монитор порта - какие-то иероглифы. Взглянул на код - обратил внимание на "Serial.begin(57600);" до этого во всех скетчах у меня стояло 9600. В итоге, в мониторе порта поставил 57600 бод - и только после этого стала отображаться дата, время и температура (корректные). Вопрос: так сколько теперь в моём общем коде должно быть установлено в Serial.begin (чтобы и ЧРВ работали и всё остальное)?
И ещё вопос - после загрузки этого примера, я увидел надпись внизу "скетч использует 29% памяти устойства". Так это что - я обязан смириться с тем, что ЧРВ как минимум займут 29% памяти контоллера?
И ещё вопос - после загрузки этого примера, я увидел надпись внизу "скетч использует 29% памяти устойства". Так это что - я обязан смириться с тем, что ЧРВ как минимум займут 29% памяти контоллера?
Я поменял Serial.begin в "примере" на 9600 и в мониторе порта. Всё работает. "Пример" от этого не ста ли работать некорректно? (показания в мониторе порта коректные)
Теперь Ваш Serial работает в 6 раз медленнее и, соответственно, расходует в 6 раз больше времени контроллера. Насколько это критично для Вашего проекта, не знаю. Я обычно прежде, чем запустить любой пример, устанавливаю скорость порта в 115200.
Спасибо. Оставлю значит пока что 9600 везде (где-то встречал, что это популярная скорость передачи).
Стал смотреть пример "DS3231_alarms". Прочитал также в описании, что эти ЧРВ имеют два будильника - как я понял, один из них можно настроить на начало какой-то задачи, второй - на завершение. Именно это мне от часов и нужно. В коде примера "alarm" написано:
// Interrupt Pin Lookup Table
// (copied from Arduino Docs)
//
// CAUTION: The interrupts are Arduino numbers NOT Atmel numbers
// and may not match (example, Mega2560 int.4 is actually Atmel Int2)
// this is only an issue if you plan to use the lower level interupt features
//
// Board int.0 int.1 int.2 int.3 int.4 int.5
// ---------------------------------------------------------------
// Uno, Ethernet 2 3
// Mega2560 2 3 21 20 [19] 18
// Leonardo 3 2 0 1 7
Не могу понять что это означает. У меня МК nano - насколько мне известно, он аналогичен UNO. Что тут говорится про пины 2 и 3?
Ещё увидел запись в коде (касаемо подключения ЧРВ):
// SQW ---> (Pin19) Don't forget to pullup (4.7k to 10k to VCC)
Это что получается, для работы будильника я должен ещё один повод бросить (от SQW ЧРВ до 19 пина МК)?
PPeterr, этот фрагмент напоминает, что аппаратные прерывания в Uno/Nano находятся на 2 и 3 ногах.
Если Вы еще раз перечитаете то, что сами же цитировали, то обнаружите, что 19-я нога используется для прерывания в контроллере Mega2560, значит, вероятнее всего, именно к нему и относится замечание. В Uno/Nano это будет другой пин, но его так же нужно подтягивать к питанию.
Спасибо. Я правильно понимаю, что если я хочу использовать функции двх будильников, заложенные в DS3231, то выходами для будильников (alarm) будут именно 2-ая и 3-я нога и, соответственно, эти ноги нельза занимать на другие нужды?
По поводу вывода "SQW" на DS3231 в примере "DS3231_alarms" - подскажите про него, на что влияет, и куда его подключать, если у меня nano. Просто, другой пример ("DS3231_simple") работает без подключения этого вывода.
1. Во-первых, не выходами, а входами. Во-вторых, никто не обязывает Вас получать сигналы от будильника именно через прерывания (хотя это удобно). И в третьих - да, если Вы хотите получать сигналы от будильника именно через прерывания, то использовать для этого нужно именно 2 и 3 пины, на других пинах аппаратных прерыаний нет (правда, из этого правила есть исключения, но они не поддерживаются непосредственно библиотеками Ардуино).
Вчера кое-как заставил светодиод (пин 13) включаться в определённое время и выключаться в определённое. Без использования функций "будильника". По-простому, типа: "если минуты больше ... но меньше ... - то включить светодиод. Иначе - выключить". Вроде, работает.
А написать решил - вопрос возник по поводу блока питания. В конечном итоге проект планирует содержать следующие платы:
1. UNO; 2. nano; 3. часы реального времени; 4. три датчика температуры DS18B20; 5. плата с 4-мя реле.
Нашёл у себя вот такой адаптор:
Подскажите, можно ли его применить для питания всех устройств, задействованных в проекте?
Здесь отсутствует МК "nano" - я его позже покупал, когда осознал что на UNO пинов не хватит на всё. Мне кажется, что плата реле вся должна работать от 5В. Но приду домой - уточню.
Посмотрел - плата с реле называется HL-54S. Вот так выглядит:
и имеет такую схему подключения:
Как я понимаю - электромагнитные катушки реле питаются напряжением 5В.
Вопрос: если предположить, что имеющийся у меня адаптор выдаёт, как и написано, 5.4В с током 1.2А, - нормально ли это, если подать с него питание на всё? Не много ли это 5.4В, если написано, что номинальное напряжение питания микроконтроллера должно быть 5.0В?
За прошедшее время я составил необходимый набор функций (как мне кажется) программного кода для контроллера UNO.
Напомню, из чего состоит проект на данный момент. Три датчика температуры опрашиваются с периодичностью 1 сек. контроллером nano. Также на nano поступает сигнал с ЧРВ. Затем nano передаёт набор из нескольких цифр через серийный порт на контроллер UNO (тепрература 1; температура 2; температура 3; часы; минуты; секунды). Контроллер Uno принимает эти 6 значений и обрабатывает их, давая в итоге информацию на выходе (в порт): требуемое состояние для 4-ёх устройств (по сути - для 4-ёх реле) насос 1, насос 2, котёл, водонагреватель.
Так вот, на мой взгляд, для контроллера UNO я заложил всё что хотел (не хватило ресурсов UNO, поэтому два графика из трёх я вывел с шагом 0,5 градуса, а один - с шагом 0,1 градуса).
Весь макет на данный момент выглядит так:
Главное меню содержит динамические данные с датчиков температуры и ЧРВ, а также отображает состояние исполнительных механизмов и актуальную "опцию" (алгоритм работы):
При нажатии сенсорной кнопки "Temp" - открывается окно с выставлением требуемой температуры в помещении. Здесь задаётся желаемая температура (которую в итоге должен поддерживать контроллер), с шагом 0.5 градуса:
Если с главного окна нажать кнопку "Menu", то открывается окно со ссылками на различные другие окна (меню):
Здесь - кнопки "Out", "Econ", "Fast" - просто меняют "опцию" (актуальный алгоритм работы для контроллера).
Кнопка "Graph" открывает окно с динамическим отображением графиков температур с трёх датчиков:
Кнопка "DiapG" открывает окно с выставлением диапазона по оси времени для графиков (от 2 минут до 18 часов):
Кнопка "Work" открывает окно со статическим отображеним состояний трёх исполнительных механизмов (двух насосов и котла) за прошедшее время - выводится время изменения состояния и само состояние (вкл/выкл) для исполнительных механизмов в это время. Очередная запись делается при изменении состояния хотя бы одного механизма:
Кнопка "Timer" открывает окно с выставлением требуемого времени (часы и минуты) включения системы отопления. Отложенный старт:
Сам код контроллера UNO (весь) на данный момент выглядит так:
#include <Adafruit_GFX.h> //Подгружается необходимая библиотеа
#include <MCUFRIEND_kbv.h> //Подгружается необходимая библиотеа
MCUFRIEND_kbv tft; // hard-wired for UNO shields anyway.
#include <TouchScreen.h> //Подгружается необходимая библиотеа
#define sizeTrend 120 //для графиков, количество точек измерений по оси времени
// most mcufriend shields use these pins and Portrait mode:
uint8_t YP = A1; // must be an analog pin, use "An" notation!
uint8_t XM = A2; // must be an analog pin, use "An" notation!
uint8_t YM = 7; // can be a digital pin
uint8_t XP = 6; // can be a digital pin
uint8_t Landscape = 0;
uint16_t TS_LEFT = 920;
uint16_t TS_RT = 150;
uint16_t TS_TOP = 940;
uint16_t TS_BOT = 120;
// For better pressure precision, we need to know the resistance
// between X+ and X- Use any multimeter to read it
// For the one we're using, its 300 ohms across the X plate
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
// Assign human-readable names to some common 16-bit color values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define WHITEGREY 0xC638
//Различные переменные:
byte a=0; // номер текущего меню (окна), переменная
int t=225; //переменная, являющаяся уставкой (заданием) требуемой температуры в комнате (225 -> 22.5 градуса)
int tW=400; //переменная для граничных значений уставки (верхняя граница, бОльшая температура диапазона) температуры воды системы отопления в зависимости от температуры наружного воздуха (400 -> 40.0)
byte b=12; //переменная для меню Timer (обозначает час суток)
byte c=30; //переменная для меню Timer (обозначает минуты часа)
boolean j;//переменная для контроля появления новых данных в серийном порту (меняет значение один раз за период поступления данных из порта)
byte d; //переменная для исключения случайных граничных значений (суммарых) - счётчик количетва выполнения условий
byte e; //переменная для исключения случайных граничных значений температуры, для определения, какое условие отслеживается в данный момент:
// e=1 - условие на включение насосов в режиме антизамерзания
// e=2 - условие на выключение насосов в режиме антизамерзания
// e=3 - условие на срабатывание граничных значений пониженной температуры в режиме поддержания температуры в комнате
// e=4 - условие на срабатывание граничных значений повышенной температуры в режиме поддержания температуры в комнате
// e=5 - условие на отключение насосов при выходе из режима антизамерзания в опции Таймер
// e=6 - условие на срабатывание граничных значений пониженной температуры в режиме поддержания температуры в комнате в опции Таймер
// e=7 - условие на срабатывание граничных значений повышенной температуры в режиме поддержания температуры в комнате в опции Таймер
// e=8 - условие на срабатывание граничных значений пониженной температуры в режиме поддержания температуры в комнате в опции Экономия электроэнергии
// e=9 - условие на срабатывание граничных значений повышенной температуры в режиме поддержания температуры в комнате в опции Экономия электроэнергии
// e=10 - условие перехода в режим поддержания температуры в комнате (+5) если температура воздуха в комнате менее 5 градусов (условие на срабатывание)
int g[]={1,3,15,30,90,180,270,360,540}; //переменная, задающая через сколько секунд появляется новое значение на графике
byte h;//переменная задающая индекс для g при выборе диапазона времени отображения графика
int q;//переменная отсчёта количества полученных данных между очередными выводами на график
boolean floorPump=0;//переменная для управления (и отображения состояния) насоса тёплых полов (on/off)
boolean radiatorPump=0;//переменная для управления (и отображения состояния) насоса радиаторов (on/off)
boolean kotel=0;//переменная для управления (и отображения состояния) котлом (on/off)
boolean boiler=0;//переменная для управления (и отображения состояния) водонагревателем (on/off)
boolean prev_fP=0; //переменная для регистрации предыдущего состояния насоса полов (вкл/выкл) для фиксирования времени срабатывания
boolean prev_rP=0; //переменная для регистрации предыдущего состояния насоса радиаторов (вкл/выкл) для фиксирования времени срабатывания
boolean prev_k=0; //переменная для регистрации предыдущего состояния котла (вкл/выкл) для фиксирования времени срабатывания
int in1[sizeTrend]; //массив для графика - температура в комнате
byte in2[sizeTrend]; //массив для графика - температура воды
char in3[sizeTrend]; //массив для графика - температура на улице
byte hourWork[19]; //массив для вывода времени включения и отключения оборудования (часы)
byte minuteWork[19]; //массив для вывода времени включения и отключения оборудования (минуты)
byte secondWork[19]; //массив для вывода времени включения и отключения оборудования (секунды)
boolean floorPumpWork[19]; //массив для создания отображения состояния насоса тёплых полов
boolean radiatorPumpWork[19]; //массив для создания отображения состояния насоса радиаторов
boolean kotelWork[19]; //массив для создания отображения состояния котла
//относится к получению данных из порта:
String inString;
String message;
byte option=0; //переменная для определения (задания), какая опция регулирования активна (в работе) в данный момент:
//0 - "Out of home" (режим антизамерзания системы);
//1 - "Fast heat" режим быстрого прогрева, натоп (масимальная тепловая мощность системы);
//2 - "Room temperature" режим поддержания заданной температуры воздуха в комнате;
//3 - "Timer" режим отложенного пуска системы на прогрев температуры в комнате. Включается при достижении временем заданного значения;
//31 - режим натоп в опции Таймер;
//32 - режим поддержания температуры в комнате в опции Таймер;
//4 - "Economy electricity" режим работы при отключении электричества (основного)
//Код для вырисовывания различных меню (окон):
void getMenu_Main() { //Главное окно (главное меню дисплея)
a=0;
tft.fillScreen(WHITEGREY); //очистка диспея
getMyButton(2, 268, " Temp"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, " Menu"); //вырисовывание сенсорной кнопки
tft.setCursor(0, 5); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK); // Указываем цвет текста
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
tft.println(" Option:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println();
tft.println();
tft.println(" Room:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println();
tft.println(" Water:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println();
tft.println(" Outdoor:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println();
tft.println(" Time:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println();
tft.println(" Floor pump:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println(" Radiator pump:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println(" Kotel:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println(" Water heater:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
delay (300);
}
void getMenu_Temp() { //меню (окошко) выставления требуемой температуры воздуха в помещении
a=1;
tft.fillScreen(WHITEGREY); //очистка диспея
tft.setCursor(0, 5); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK); // Указываем цвет текста
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
tft.println(" Need room"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(" temperature:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
getMyButton(2, 268, " Exit"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, " OK"); //вырисовывание сенсорной кнопки
getMyButton(121, 168, " v"); //вырисовывание сенсорной кнопки
getMyButton(121, 116, " ^"); //вырисовывание сенсорной кнопки
getLookTemp();
delay (300);
}
void getMenu_Other() { //окошко (меню) имеющее сенсорные кнопки (ссылки) на другие требуемые меню
a=2;
tft.fillScreen(WHITEGREY); //очистка диспея
getMyButton(2, 268, " Exit"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, " Out"); //вырисовывание сенсорной кнопки
getMyButton(2, 216, " Fast"); //вырисовывание сенсорной кнопки
getMyButton(121, 216, " Econ"); //вырисовывание сенсорной кнопки
getMyButton(2, 164, " Timer"); //вырисовывание сенсорной кнопки
getMyButton(121, 164, " Work"); //вырисовывание сенсорной кнопки
getMyButton(2, 112, " Graph"); //вырисовывание сенсорной кнопки
getMyButton(121, 112, " DiapG"); //вырисовывание сенсорной кнопки
delay (300);
}
void getMenu_Timer() { //окошко (меню) задания времени включения котла в режиме "прогрев"
a=3;
tft.fillScreen(WHITEGREY); //очистка диспея
tft.setCursor(0, 5); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK); // Указываем цвет текста
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
tft.print(" Wake up at:"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
getMyButton(2, 268, " Exit"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, " OK"); //вырисовывание сенсорной кнопки
getMyButton(0, 168, " v"); //вырисовывание сенсорной кнопки
getMyButton(0, 116, " ^"); //вырисовывание сенсорной кнопки
getMyButton(121, 168, " v"); //вырисовывание сенсорной кнопки
getMyButton(121, 116, " ^"); //вырисовывание сенсорной кнопки
delay (300);
}
void getMenu_Graphic() { //окошко (меню) отображения графика
a=4;
tft.fillScreen(BLACK);
getMyButton(2, 268, " Press"); //вырисовывание сенсорной кнопки
delay (300);
}
void getMenu_Work() { //окно с отображением времени включения/выключения насосов и котла
a=5;
tft.fillScreen(BLACK); //очистка диспея
tft.setCursor(105, 0); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(MAGENTA); // Указываем цвет текста
tft.print("Flo ");
tft.setTextColor(RED); // Указываем цвет текста
tft.print("Rad ");
tft.setTextColor(YELLOW); // Указываем цвет текста
tft.println("Kot");
tft.setCursor(0, 16); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
for (byte i; i<19; i++) {
tft.setTextColor(BLUE); // Указываем цвет текста
if (hourWork[i]<10) {
tft.print("0");
tft.print(hourWork[i]);
}
else if (hourWork[i]>=10) {
tft.print(hourWork[i]);
}
tft.print(":");
if (minuteWork[i]<10) {
tft.print("0");
tft.print(minuteWork[i]);
}
else if (minuteWork[i]>=10) {
tft.print(minuteWork[i]);
}
tft.print(":");
if (secondWork[i]<10) {
tft.print("0");
tft.println(secondWork[i]);
}
else if (secondWork[i]>=10) {
tft.println(secondWork[i]);
}
if (floorPumpWork[i]==1) {
tft.fillRect (105, (16+i*16), 40, 16, GREEN);
}
if (radiatorPumpWork[i]==1) {
tft.fillRect (152, (16+i*16), 40, 16, GREEN);
}
if (kotelWork[i]==1) {
tft.fillRect (199, (16+i*16), 40, 16, GREEN);
}
}
}
void getMenuDiap() { //меню (окошко) выставления диапазона времени графиков
a=6;
tft.fillScreen(WHITEGREY); //очистка диспея
tft.setCursor(0, 5); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK); // Указываем цвет текста
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
tft.println(" Diapasone of"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.println(" time at graphic"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(" (minutes):"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
getMyButton(2, 268, " Exit"); //вырисовывание сенсорной кнопки
getMyButton(121, 268, " OK"); //вырисовывание сенсорной кнопки
getMyButton(121, 168, " v"); //вырисовывание сенсорной кнопки
getMyButton(121, 116, " ^"); //вырисовывание сенсорной кнопки
getLookDiap ();
delay (300);
}
void changeWorkData() { //перезапись массива, если насос либо котёл поменял состояние, для вывода информации о том, когда и что включалось/выключалось
for (byte i; i<18; i++) {
hourWork[i]=hourWork[i+1];
minuteWork[i]=minuteWork[i+1];
secondWork[i]=secondWork[i+1];
floorPumpWork[i]=floorPumpWork[i+1];
radiatorPumpWork[i]=radiatorPumpWork[i+1];
kotelWork[i]=kotelWork[i+1];
}
}
void drawTrendRoom(byte widthTrend, byte heightTrend, byte xTrend, int yTrend, int cn, int *in){ //для графика температуры в комнате
if (q>=g[h]) { //добавляем новое значение
in[sizeTrend-1]=cn; //сдвигаем график
for (byte x=0;x<(sizeTrend-1);x++){
in[x]=in[x+1];
}
}
if (a==4) { //если открыто окно графиков
byte oldX=0;
int oldY=0+yTrend;
int mn=1000;
int mx=0;
for (byte i=0;i<sizeTrend;i++){ //поиск мин и макс
if (in[i]>mx){
mx=in[i];
}
if (in[i]<mn){
mn=in[i];
}
}
if(mn==mx){
mx=mn+1;
mn=mn-1;
}
//вывод минимума и максимума
tft.setTextColor(BLUE);
tft.setTextSize(2);
tft.setCursor(xTrend+widthTrend-50,yTrend);
tft.print(mx/10);
tft.print(".");
tft.print(mx%10);
tft.setCursor(xTrend+widthTrend-50,yTrend+heightTrend-16);
tft.print(mn/10);
tft.print(".");
tft.print(mn%10);
tft.setCursor(xTrend+widthTrend-50,yTrend+(heightTrend/2)-8);
tft.print(in[sizeTrend-1]/10); //текущее
tft.print(".");
tft.print(in[sizeTrend-1]%10);
//формирование буфера вывода
for(byte x=0;x<sizeTrend-1;x++){
int y=convertTRoom(in[x],mn,mx,yTrend,heightTrend,in);
byte nxt_x=map(x,0,sizeTrend-1,0,widthTrend);
//отрисовка тренда
tft.drawLine(xTrend+oldX, oldY, xTrend+nxt_x, y, YELLOW);
oldX=nxt_x;
oldY=y;
}
oldX=0;
oldY=convertTRoom(in[0],mn,mx,yTrend,heightTrend,in);
}
}
void drawTrendWater(byte widthTrend, byte heightTrend, byte xTrend, int yTrend, byte cn, byte *in){ //для графика температуры воды
if (q>=g[h]) { //добавляем новое значение
in[sizeTrend-1]=cn; //сдвигаем график
for (byte x=0;x<(sizeTrend-1);x++){
in[x]=in[x+1];
}
}
if (a==4) { //если открыто окно графиков
byte oldX=0;
int oldY=0+yTrend;
byte mn=254;
byte mx=0;
for (byte i=0;i<sizeTrend;i++){ //поиск мин и макс
if (in[i]>mx){
mx=in[i];
}
if (in[i]<mn){
mn=in[i];
}
}
if(mn==mx){
mx=mn+1;
mn=mn-1;
}
//вывод минимума и максимума
tft.setTextColor(RED);
tft.setTextSize(2);
tft.setCursor(xTrend+widthTrend-50,yTrend);
tft.print(mx/2);
tft.print(".");
tft.print((mx%2)*5); //остаток от деления на 2 и умноженный на 5
tft.setCursor(xTrend+widthTrend-50,yTrend+heightTrend-16);
tft.print(mn/2);
tft.print(".");
tft.print((mn%2)*5); //остаток от деления на 2 и умноженный на 5
tft.setCursor(xTrend+widthTrend-50,yTrend+(heightTrend/2)-8);
tft.print(in[sizeTrend-1]/2); //текущее
tft.print(".");
tft.print((in[sizeTrend-1]%2)*5); //остаток от деления на 2 и умноженный на 5
//формирование буфера вывода
for(byte x=0;x<sizeTrend-1;x++){
int y=convertWater(in[x],mn,mx,yTrend,heightTrend,in);
byte nxt_x=map(x,0,sizeTrend-1,0,widthTrend);
//отрисовка тренда
tft.drawLine(xTrend+oldX, oldY, xTrend+nxt_x, y, YELLOW);
oldX=nxt_x;
oldY=y;
}
oldX=0;
oldY=convertWater(in[0],mn,mx,yTrend,heightTrend,in);
}
}
void drawTrendOutdoor(byte widthTrend, byte heightTrend, byte xTrend, int yTrend, char cn, char *in){ //для графика температуры на улице
if (q>=g[h]) { //добавляем новое значение
in[sizeTrend-1]=cn; //сдвигаем график
for (byte x=0;x<(sizeTrend-1);x++){
in[x]=in[x+1];
}
}
if (a==4) { //если открыто окно графиков
byte oldX=0;
int oldY=0+yTrend;
char mn=126;
char mx=0;
for (byte i=0;i<sizeTrend;i++){ //поиск мин и макс
if (in[i]>mx){
mx=in[i];
}
if (in[i]<mn){
mn=in[i];
}
}
if(mn==mx){
mx=mn+1;
mn=mn-1;
}
//вывод минимума и максимума
tft.setTextColor(MAGENTA);
tft.setTextSize(2);
tft.setCursor(xTrend+widthTrend-50,yTrend);
tft.print(mx/2);
tft.print(".");
if (mx>=0) {
tft.print((mx%2)*5); //остаток от деления на 2 и умноженный на 5
}
if (mx<0) {
tft.print((-mx%2)*5); //остаток от деления на 2 и умноженный на 5
}
tft.setCursor(xTrend+widthTrend-50,yTrend+heightTrend-16);
tft.print(mn/2);
if (mn/2 > -10) {
tft.print(".");
if (mn>=0) {
tft.print((mn%2)*5); //остаток от деления на 2 и умноженный на 5
}
if (mn<0) {
tft.print((-mn%2)*5); //остаток от деления на 2 и умноженный на 5
}
}
tft.setCursor(xTrend+widthTrend-50,yTrend+(heightTrend/2)-8);
tft.print(in[sizeTrend-1]/2); //текущее
if (in[sizeTrend-1]/2 > -10) {
tft.print(".");
if (in[sizeTrend-1]/2>=0) {
tft.print((in[sizeTrend-1]%2)*5); //остаток от деления на 2 и умноженный на 5
}
if (in[sizeTrend-1]/2<0) {
tft.print((-in[sizeTrend-1]%2)*5); //остаток от деления на 2 и умноженный на 5
}
}
for(byte x=0;x<sizeTrend-1;x++){ //формирование буфера вывода
int y=convertOutDoor(in[x],mn,mx,yTrend,heightTrend,in);
byte nxt_x=map(x,0,sizeTrend-1,0,widthTrend);
//отрисовка тренда
tft.drawLine(xTrend+oldX, oldY, xTrend+nxt_x, y, YELLOW);
oldX=nxt_x;
oldY=y;
}
oldX=0;
oldY=convertOutDoor(in[0],mn,mx,yTrend,heightTrend,in);
}
}
//для графиков:
int convertTRoom(int y, int mn, int mx, int yTrend, byte heightTrend, int *in ){ //температура в комнате
int ny=y;
ny=map(ny,mn,mx,heightTrend-1+yTrend,yTrend);
return ny;
}
int convertWater(int y, byte mn, byte mx, int yTrend, byte heightTrend, byte *in ){ //температура воды
int ny=y;
ny=map(ny,mn,mx,heightTrend-1+yTrend,yTrend);
return ny;
}
int convertOutDoor(int y, char mn, char mx, int yTrend, byte heightTrend, char *in ){ //температура на улице
int ny=y;
ny=map(ny,mn,mx,heightTrend-1+yTrend,yTrend);
return ny;
}
//вырисовывание сенсорных кнопок:
int getMyButton(byte x1, int y1, String z1){
tft.fillRoundRect (x1, y1, 117, 50, 15, BLUE); //залитый прямоугольник со скруглёнными углами
y1=y1+13;
tft.setCursor(x1, y1); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(WHITE); // Указываем цвет текста
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print(z1); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
//выводим значение заданной температуры (в меню настройки задания):
byte getLookTemp() {
tft.setCursor(30, 160); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста и заливки
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print(t/10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(".");
tft.print(t%10);
if (t<100){ //чтобы избежать наложения вывода цифр
tft.print(" ");
}
}
//выводим значение заданного времени включения по таймеру (в меню таймера):
byte getLookDate() {
tft.setCursor(75, 50); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK, WHITEGREY); // Указываем цвет текста и заливки
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print(b); // Выводим текст на экран, текст будет выведен с указанными выше настройками
if (b<10){
tft.print(" ");
}
tft.print(":"); //значок двоеточия для времени
tft.print(c); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
//выводим значение заданного диапазона времени графика (в меню настройки задания):
byte getLookDiap() {
tft.setCursor(5, 160); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK, WHITEGREY); // Указываем цвет текста и заливки
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
if (g[h]<=15) {
tft.print(g[h]*2); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print("min ");
}
else if (g[h]>15){ //чтобы избежать наложения вывода цифр
tft.print(g[h]/30); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print("hour");
}
}
void setup()
{
Serial.begin(9600);
tft.reset();
//Found ILI9341 LCD driver DealExtreme
TS_LEFT = 893; TS_RT = 145; TS_TOP = 930; TS_BOT = 135;
Landscape = 1;
ts = TouchScreen(XP, YP, XM, YM, 300); //call the constructor AGAIN with new values.
tft.begin(0); //включение дисплея
#define MINPRESSURE 10
#define MAXPRESSURE 1000
tft.fillScreen(WHITEGREY); //очистка диспея
getMenu_Main(); //при загрузке - на экран выводится главное окно (меню)
delay(1000); //задержка 1 сек
}
void loop() { //главный повторяющийся цикл
//относится к тачскрину:
int tmp;
TSPoint p = ts.getPoint();
// if sharing pins, you'll need to fix the directions of the touchscreen pins
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
// we have some minimum pressure we consider 'valid'
// pressure of 0 means no pressing!
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
if (Landscape) { // swap X and Y
tmp = p.x;
p.x = p.y;
p.y = tmp;
}
// scale from 0->1023 to tft.width i.e. left = 0, rt = width
// most mcufriend have touch (with icons) that extends below the TFT
// screens without icons need to reserve a space for "erase"
p.x = map(p.x, TS_LEFT, TS_RT, 0, tft.width());
p.y = map(p.y, TS_TOP, TS_BOT, 0, tft.height());
}
//перемещения по меню и нажатие сенсорных кнопок:
if (((p.x >= 0) and (p.x <= 119) and (p.y >= 267) and (p.y <= 319)) and a==0) { //главное окно
getMenu_Temp();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y >= 267) and (p.y <= 319)) and a==0) {
getMenu_Other();
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y >= 267) and (p.y <= 319)) and a==1) { //меню выставления задания температуры "tempRoom"
getMenu_Main();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y >= 267) and (p.y <= 319)) and a==1) {
option=2;
getMenu_Main();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y > 167) and (p.y < 220)) and a==1) {
if (t>90) {
t=t-5;
}
else {
t=90;
}
getLookTemp();
delay(300);
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y > 115) and (p.y < 167)) and a==1) {
if (t<300) {
t=t+5;
}
else {
t=300;
}
getLookTemp();
delay(300);
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y >= 267) and (p.y <= 319)) and a==2) { //окно с ссылками на различные другие меню
getMenu_Main();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y >= 267) and (p.y <= 319)) and a==2) {
option=0;
getMenu_Main();
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y >= 215) and (p.y <= 266)) and a==2) {
option=1;
getMenu_Main();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y >= 215) and (p.y <= 266)) and a==2) {
option=4;
getMenu_Main();
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y >= 163) and (p.y <= 214)) and a==2) {
getMenu_Timer();
getLookDate();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y >= 163) and (p.y <= 214)) and a==2) {
getMenu_Work();
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y >= 111) and (p.y <= 162)) and a==2) {
getMenu_Graphic();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y >= 111) and (p.y <= 162)) and a==2) {
getMenuDiap();
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y >= 267) and (p.y <= 319)) and a==3) { //окно с таймером (выставление времени включения по таймеру)
getMenu_Main();
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y > 115) and (p.y < 167)) and a==3) {
if (b<23) {
b=b+1;
}
else {
b=23;
}
getLookDate();
delay(300);
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y > 167) and (p.y < 219)) and a==3) {
if (b>0) {
b=b-1;
}
else {
b=0;
}
getLookDate();
delay(300);
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y > 115) and (p.y < 167)) and a==3) {
if (c<50) {
c=c+10;
}
else {
c=50;
}
getLookDate();
delay(300);
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y > 167) and (p.y < 219)) and a==3) {
if (c>0) {
c=c-10;
}
else {
c=0;
}
getLookDate();
delay(300);
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y >= 267) and (p.y <= 319)) and a==3) {
option=3;
getMenu_Main();
}
else if (((p.x >= 0) and (p.x <= 239) and (p.y >= 0) and (p.y <= 259)) and a==4) { //окно с выводами графиков
getMenu_Main();
}
else if (((p.x >= 0) and (p.x <= 239) and (p.y >= 0) and (p.y <= 319)) and a==5) { //окно с выводами времени включения/отключения насосов и котла
getMenu_Main();
}
else if (((p.x >= 0) and (p.x <= 119) and (p.y >= 267) and (p.y <= 319)) and a==6) { //меню выставления диапазона времени составления графика
getMenu_Main();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y >= 267) and (p.y <= 319)) and a==6) {
getMenu_Other();
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y > 167) and (p.y < 220)) and a==6) {
if (h>0) {
h=h-1;
}
else {
h=0;
}
getLookDiap();
delay(300);
}
else if (((p.x >= 120) and (p.x <= 239) and (p.y > 115) and (p.y < 167)) and a==6) {
if (h<8) {
h=h+1;
}
else {
h=8;
}
getLookDiap();
delay(300);
}
j=1; //счётчик меняется с 1 на 0, если поступают данные через порт
//принимаем строку из порта:
while (Serial.available()) {
char inChar = Serial.read();
inString += inChar;
if (inChar == 'E') {
Serial.println(inString);
message = inString;
inString = "";
j=0; //отслеживает момент поступления данных через порт
}
}
byte Index1 = message.indexOf(';'); //разделительный символ
byte Index2 = message.indexOf(';', Index1+1); //вычисляем второй символ
byte Index3 = message.indexOf(';', Index2+1); //...третий и т.д.
byte Index4 = message.indexOf(';', Index3+1);
byte Index5 = message.indexOf(';', Index4+1);
byte Index6 = message.indexOf(';', Index5+1);
String Val_1 = message.substring(0, Index1);//что-то перед первым разд. сим.
String Val_2 = message.substring(Index1+1, Index2);//что-то за первым разд. сим.
String Val_3 = message.substring(Index2+1, Index3);//за вторым и т.д
String Val_4 = message.substring(Index3+1, Index4);
String Val_5= message.substring(Index4+1, Index5);
String Val_6 = message.substring(Index5+1, Index6);
int tempRoom=Val_1.toInt();
int tempWater=Val_2.toInt();
int tempOutdoor=Val_3.toInt();
byte hours=Val_4.toInt();
byte minutes=Val_5.toInt();
byte seconds=Val_6.toInt();
//Задаю значения датчиков с реально возможным диапазоном (чтобы фиксировать ошибки датчиков):
if (tempRoom<0 || tempRoom>400) { //температура в комнате нереальная
tft.setCursor(10, 10); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print("t_Room eror"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
floorPump=1; //включить насос тёплых полов
radiatorPump=1; //включить насос радиаторов
kotel=0; //выключить котёл
}
if ( tempWater<0 and tempWater>900) { //температура воды в системе отопления
tft.setCursor(10, 70); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print("t_Water eror"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
floorPump=1; //включить насос тёплых полов
radiatorPump=1; //включить насос радиаторов
kotel=0; //выключить котёл
}
if (tempOutdoor<-300 || tempOutdoor>500) { //температура на улице нереальная
tft.setCursor(10, 40); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print("t_Out eror"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
floorPump=1; //включить насос тёплых полов
radiatorPump=1; //включить насос радиаторов
kotel=0; //выключить котёл
}
if (hours<0 || hours>23 || minutes<0 || minutes>59 || seconds<0 || seconds>59) { //часы нереальные
tft.setCursor(10, 110); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print("Time eror"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
option=0; //перейти в режим антизамерзания
}
if (j==0){ //ели поступили данные через порт
//Зависимость температуры уставки (верхняя граница) воды системы отопления от значения температуры наружного воздуха:
tW = (500 - (0.8*tempOutdoor));
//условие включения водонагревателя:
if (hours < 7 || hours >= 23) {
boiler=1;
}
else {
boiler=0;
}
//Код для программ (обций), алгоритм работы контоллера:
//сброс счётчика d
if ((e==1 and !(tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80))) || (e==2 and !(tempRoom > 80 and tempWater >80)) || (e==3 and !(tempRoom < (t-5) and tempOutdoor <= 180)) ||
(e==4 and !(tempRoom > (t+5))) || (e==5 and !(tempRoom > 80 and tempWater >80)) || (e==6 and !(tempOutdoor <= 180 and tempRoom < (t-5))) || (e==7 and !(tempRoom > (t+5))) ||
(e==8 and !(tempRoom < (t-5) and tempOutdoor <= 180)) || (e==9 and !(tempRoom > (t+5))) || (e==10 and !(tempRoom <= 50))) {
d=0;
}
//Режим антизамерзания (работает всегда)
if (tempRoom <= 50 ) { //условие перехода в режим поддержания положительной температуры в комнате (антизамерзание)
e=10;
d++;
if (d>=10){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
floorPump=1; //включить насос тёплых полов
radiatorPump=1; //включить насос радиаторов
if (tempWater > 250) {
kotel=0; //выключить котёл
}
if (tempWater <= 250){
kotel=1; //включить котёл
}
d=0;
}
}
if (tempRoom > 50 and (tempRoom <= 80 || tempWater <= 80)) { //условие на включение насосов (выполняется при любом режиме системы)
e=1;
d++;
if (d>=10){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
floorPump=1; //включить насос тёплых полов
radiatorPump=1; //включить насос радиаторов
kotel=0; //выключить котёл
d=0;
}
}
//1. Опция "Вне дома" (Out of home):
if (option==0) {
if (tempRoom > 80 and tempWater >80) { //условие на выключение насосов (выполняется только при режиме "Out of home" и "Timer")
e=2;
d++;
if (d>=10){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
floorPump=0; //выключить насос тёплых полов
radiatorPump=0; //выключить насос радиаторов
kotel=0; //выключить котёл
d=0;
}
}
}
//2. Опция "Натоп" (Fast heat), алгоритм работы:
if (option==1) {
if (tempRoom < (t-20)) {
kotel=1; //включить котёл
floorPump=1; //включить насос тёплых полов
radiatorPump=1; //включить насос радиаторов
}
if (tempRoom >= (t-20)) {
option=2; //переходим в режим поддержания температуры в комнате
getMenu_Main(); //для обновления информации о действующей опции на главном экране
}
}
//3. Опция поддержания температуры в комнате ("Room temperature"):
if (option==2) {
if (tempRoom < (t-5) and tempOutdoor <= 180) { //система запустится, если температура на улице менее 18 градусов
e=3;
d++;
if (d>=20){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
floorPump=1; //включить насос тёплых полов
if (tempWater >= (tW-100)){
kotel=0; //выключить котёл
if (tW <= 500) {
radiatorPump=0; //выключить насос радиаторов
}
else if (tW > 500) {
radiatorPump=1; //включить насос радиаторов
}
}
if (tempWater < (tW-100)){
kotel=1; //включить
if (tW <= 500) {
radiatorPump=0; //выключить насос радиаторов
}
else if (tW > 500) {
radiatorPump=1; //включить насос радиаторов
}
}
d=0;
}
}
if (tempWater >= tW) { //если температура воды в системе отопления достигла верхней уставки - выключить котёл
kotel=0; //выключить котёл
}
if (tempRoom > (t+5)) {
e=4;
d++;
if (d>=10){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
kotel=0; //выключить котёл
floorPump=0; //выключить насос тёплых полов
radiatorPump=0; //выключить насос радиаторов
d=0;
}
}
}
//4. Опция "Timer". Запуск системы в заданное время (работает в пределах текущих суток). На следующие сутки - поставить нельзя
if (option==3) {
if (hours<=b) {
if (tempRoom > 80 and tempWater >80) { //условие на выключение насосов (выполняется только при режиме "Out of home" и "Timer")
e=5;
d++;
if (d>=10){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
floorPump=0; //выключить насос тёплых полов
radiatorPump=0; //выключить насос радиаторов
kotel=0; //выключить котёл
d=0;
}
}
}
if (hours>=b and minutes>=c) {
option=31; //фиксирование момента срабатывания таймера
getMenu_Main(); //для обновления информации о действующей опции на главном экране
}
}
if (option==31) {
if (tempOutdoor <= 180 and tempRoom < (t-20)) { //режим "натоп" в опции "Timer"
kotel=1; //включить котёл
floorPump=1; //включить насос тёплых полов
radiatorPump=1; //включить насос радиаторов
}
if (tempRoom >= (t-20)) { //переход в режим "температуры в комнате" в опции "Timer"
option=32; //переходим в режим поддержания заданной температуры в комнате в опции Таймер
getMenu_Main(); //для обновления информации о действующей опции на главном экране
}
}
if (option==32) {
if (tempOutdoor <= 180 and tempRoom < (t-5)) { //режим поддержания температуры в комнате в опции "Timer"
e=6;
d++;
if (d>=20){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
floorPump=1; //включить насос тёплых полов
if (tempWater >= (tW-100)){
if (tW <= 500) {
radiatorPump=0; //включить насос радиаторов
}
else if (tW > 500) {
radiatorPump=1; //включить насос радиаторов
}
}
if (tempWater < (tW-100)){
kotel=1; //включить котёл
if (tW <= 500) {
radiatorPump=0; //включить насос радиаторов
}
else if (tW > 500) {
radiatorPump=1; //включить насос радиаторов
}
}
d=0;
}
}
if (tempWater >= tW) { //если температура воды в системе отопления достигла верхней уставки - выключить котёл
kotel=0; //выключить котёл
}
if (tempRoom > (t+5)) {
e=7;
d++;
if (d>=10){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
kotel=0; //выключить котёл
floorPump=0; //выключить насос тёплых полов
radiatorPump=0; //выключить насос радиаторов
d=0;
}
}
//завершение работы опции "Timer" (переход на опцию "Out of home") если через 4 часа после начала работы опции "Timer" не была выбрана какая-либо другая опция работы
if ((b==20 and hours >= 0) || (b==21 and hours >= 1) || (b==22 and hours >= 2) || (b==23 and hours >= 3) || (hours >= (b+4)) and minutes>=c) {
option=0;
getMenu_Main(); //для обновления информации о действующей опции на главном экране
}
}
//5. Режим работы экономии электроэнергии (опция "Economy electricity"):
if (option==4) {
floorPump=0; //выключить насос тёплых полов
if (tempRoom < (t-5) and tempOutdoor <= 180) { //система запустится, если температура на улице менее 18 градусов
e=8;
d++;
if (d>=20){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
radiatorPump=1; //включить насос радиаторов
if (tempWater >= 300){ //если температура воды более 30 градусов
kotel=0; //выключить котёл
}
if (tempWater < 300){
kotel=1; //включить котёл
}
d=0;
}
}
if (tempWater >= 700) { //если температура воды в системе отопления достигла 70 градусов - выключить котёл
kotel=0; //выключить котёл
}
if (tempRoom > (t+5)) {
e=9;
d++;
if (d==10){ //задаём условие, что граничное условие должно быть выполнено d раз подряд
kotel=0; //выключить котёл
radiatorPump=0; //выключить насос радиаторов
d=0;
}
}
}
Serial.print(floorPump);
Serial.print(";");
Serial.print(radiatorPump);
Serial.print(";");
Serial.print(kotel);
Serial.print(";");
Serial.print(boiler);
Serial.print(";");
Serial.println("D");
//вырисовывание графиков:
q++;
if (q>=g[h] and a != 4) { //если прошло q секунд и окно графиков не открыто
drawTrendRoom(0,0,0,0,tempRoom, in1);
drawTrendWater(0,0,0,0,tempWater/5,in2);
drawTrendOutdoor(0,0,0,0,tempOutdoor/5,in3);
q=0;
}
else if ((q>=g[h] and a == 4) || (a == 4 and ((p.x >= 0) and (p.x <= 119) and (p.y >= 267) and (p.y <= 319)))) { //если прошло q секунд и окно графиков открыто или если открыто окно графиков и нажата кнопка
tft.fillScreen(BLACK);
//сетка по времени:
for (byte i=1; i<6; i++) {
for (byte k=1; k<4; k++) {
tft.drawFastVLine(40*i, 106*k, -10, CYAN);
}
}
tft.drawFastHLine(0, 105, 240, BLUE);
tft.drawFastHLine(0, 212, 240, RED);
tft.drawFastHLine(0, 319, 240, MAGENTA);
tft.setCursor(30, 1); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(1); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(BLUE);
tft.print("Room temperature"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
drawTrendRoom(240,106,0,0,tempRoom,in1);
tft.setCursor(30, 107); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(1); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(RED);
tft.print("Water temperature"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
drawTrendWater(240,106,0,106,tempWater/5,in2);
tft.setCursor(30, 214); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(1); // Указываем размер символов в строке от 1 до 3
tft.setTextColor(MAGENTA);
tft.print("Outdoor temperature"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
drawTrendOutdoor(240,106,0,213,tempOutdoor/5,in3);
tft.setCursor(5, 300); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(1); // Указываем размер символов в строке от 1 до 3
tft.print("Last "); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(g[h]*2); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(" minutes"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
if (q>=g[h]) {
q=0;
}
}
}
//динамические данные в главном окне:
if (a==0){ //вывод актуальной функции (какой процесс в работе), температур с датчиков DS18B20, времени с ЧРВ, состояние насосов, котла, водонагревателя - на главном экране
//отображение на дисплее, какая функция в работе в данный момент:
tft.setCursor(100, 5); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
if (option==0){
tft.setTextColor(BLACK); // Указываем цвет текста
tft.println("Out of home");
}
else if (option==1){
tft.setTextColor(RED); // Указываем цвет текста
tft.print("Fast heat");
}
else if (option==2){
tft.setTextColor(BLUE); // Указываем цвет текста
tft.println("Room");
tft.print(" temperature");
}
else if (option==3){
tft.setTextColor(BLACK); // Указываем цвет текста
tft.println("Timer");
tft.print(" wake up:");
tft.setTextColor(BLUE); // Указываем цвет текста
tft.print(b);
tft.print(":");
tft.print(c);
}
else if (option==31){
tft.setTextColor(BLUE); // Указываем цвет текста
tft.println("Fast heat");
tft.print(" by Timer");
}
else if (option==32){
tft.setTextColor(BLUE); // Указываем цвет текста
tft.println("Room temp");
tft.print(" by Timer");
}
else if (option==4){
tft.setTextColor(RED); // Указываем цвет текста
tft.println("Economy");
tft.print(" electricity");
}
//температуры:
tft.setCursor(140,46); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
tft.setTextSize(3); // Указываем размер символов в строке от 1 до 3
tft.print(tempRoom/10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(".");
tft.print(tempRoom%10);
if (tempRoom<100){ //стирание последнего знака от предыдущих значений
tft.print(" ");
}
tft.setCursor(140,77); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.print(tempWater/10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(".");
tft.print(tempWater%10);
if (tempWater<100){ //стирание последнего знака от предыдущих значений
tft.print(" ");
}
tft.setCursor(140,110); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLACK, WHITEGREY); // Указываем цвет текста
tft.print(tempOutdoor/10); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(".");
if (tempOutdoor>=0) {
tft.print(tempOutdoor%10);
}
if (tempOutdoor<0) {
tft.print(-tempOutdoor%10);
}
if (tempOutdoor>-100 and tempOutdoor<100){ //стирание последнего знака от предыдущих значений
tft.print(" ");
}
//время:
tft.setCursor(80, 145); // Устанавливаем курсор (X = , Y = )
tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
if (hours<10){
tft.print("0"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(hours); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
else {
tft.print(hours); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
tft.print(":"); //двоеточие для времени
if (minutes<10){
tft.print("0"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(minutes); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
else {
tft.print(minutes); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
tft.setCursor(173, 152); // Устанавливаем курсор (X = , Y = )
tft.setTextSize(2); // Указываем размер символов в строке от 1 до 3
if (seconds<10){
tft.print("0"); // Выводим текст на экран, текст будет выведен с указанными выше настройками
tft.print(seconds); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
else {
tft.print(seconds); // Выводим текст на экран, текст будет выведен с указанными выше настройками
}
//отображение состояния насосов, котла, водонагревателя (вкл/выкл):
tft.setCursor(180, 181); // Устанавливаем курсор (X = , Y = )
if (floorPump==1){
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.print ("ON ");
}
if (floorPump==0){
tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
tft.print ("OFF");
}
tft.setCursor(180, 197); // Устанавливаем курсор (X = , Y = )
if (radiatorPump==1){
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.print ("ON ");
}
if (radiatorPump==0){
tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
tft.print ("OFF");
}
tft.setCursor(180, 213); // Устанавливаем курсор (X = , Y = )
if (kotel==1){
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.print ("ON ");
}
if (kotel==0){
tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
tft.print ("OFF");
}
tft.setCursor(180, 229); // Устанавливаем курсор (X = , Y = )
if (boiler==1){
tft.setTextColor(RED, WHITEGREY); // Указываем цвет текста
tft.print ("ON ");
}
if (boiler==0){
tft.setTextColor(BLUE, WHITEGREY); // Указываем цвет текста
tft.print ("OFF");
}
}
//вывод на дисплей информации, когда включался/отключался насосы и котёл:
if (floorPump != prev_fP || radiatorPump != prev_rP || kotel != prev_k) { //если изменилось состояние
changeWorkData();
hourWork[18]=hours;
minuteWork[18]=minutes;
secondWork[18]=seconds;
floorPumpWork[18]=floorPump;
radiatorPumpWork[18]=radiatorPump;
kotelWork[18]=kotel;
if (floorPump != prev_fP) {
prev_fP=floorPump; //запоминаем состояние оборудования
}
if (radiatorPump != prev_rP) {
prev_rP=radiatorPump; //запоминаем состояние оборудования
}
if (kotel != prev_k) {
prev_k=kotel; //запоминаем состояние оборудования
}
}
}
Понимаю, что код очень длинный и разбираться в нём вряд ли у кого-то будет желание. Но всё же прошу (и надеюсь) - посмотрите, укажите на грубые мои ошибки. Хотя бы подскажите, где какие куски кода следует расположить (что надо переместить до void setup, что до loop setup, что после и т.д. Подскажите, как можно оптимизировать код, чтобы поменьше отбирать ресурсов у UNO и чтобы контроллер работал надёжнее (уменьшить вероятность сбоев).
Мне нужен некий минимальный набор кода (наиболее простой), но способный обеспечить вполне надёжную работу системы автоматического поддержания температуры в помещении. Основные исполнительные органы - котёл на дизельном топливе, насосы перекачивающие воду. У котла - своя автоматика защиты и контроля процесса горения (штатная, заводская). Я лишь планирую включать/выключать котёл с помощью реле.
Да Вы, батенька, большой оптимист.
То, что сформулировано Вами - это достаточно сложная задача. Судя по Вашим постам на форуме, Вам она явно не по зубам.
Ключевое слово - надежность.
Попытайтесь написать в "Ищу исполнителя".
По поводу температур (датчиков и дальнейших разбирательств с ними) - я в тупике. Полагаю на данном этапе пока оставить их в покое. Как я себе представляю - наиболее существенным остаётся вопрос как работать с отрицательными значениями температур. Его я оставлю на потом - если проект будет двигаться далее, когда оставшиеся компоненты будут задействованы - удлиню провод и засуну датчик в морозилку. И попытаюсь как-нибудь настроить работу с отрицательными температурами. За совет про signBit = raw & 0x8000; - спасибо. Так и буду пытаться получить желаемое.
Подскажите, если я в этой ветке стану обсуждать далее вопросы, не совсем относящиеся к названию темы - в этом ничего страшного? Или лучше создать новую тему?
Далее планирую заняться подключением платы часов реального времени (DS3231). Прошу вашего совета - к какому контроллеру лучше подключить. По моим поверхностным представлениям - можно подключить как к nano, так и к UNO. При этом - в настоящий момент у меня на nano прописан код считывания и пересылки данных с датчиков температуры, скетч использует 9% памяти устройства. На UNO - код прорисовки меню для TFT LCD дисплея и код приёма и вывода значений температур. Скетч использует 54% памяти устройства.
Ну ОК, будем считать, что вы правы. Хотя мне все равно не понятно, как это может работать. Но лезть в исходники сейчас и правда совершенно некогда. может дома гляну.
Для передачи бита есть таймслот. Его запускает МК прижиманием DQ к земле. Затем слушает. За это время сенсор в ответ должен (судя по диаграмме из даташита) прижать DQ к земле на 60uS, если передает "0", и менее, чем на 15uS, если передает "1". OneWire::read_bit() просто читает состояние пина через N uS (13uS + накладные расходы). Т.е. она принимает за факт, что если через 15uS на шине присутствует GND, то передавался "0". Если на шине V_pull-up - передавался "1". Начальное состояние пина после старта таймслота не контролируется, что ускоряет чтение, но вносит недетектируемую ошибку в принимаемые биты. В большинстве случаев наводки гасятся через pull-up резистор, поэтому тем, кто складывает два бита - просто везет. Но защиты от чтения оторванной или короткозамкнутой шины данная библиотека не содержит.
Именно поэтому DallasTemerature, как я понимаю, не доверяет сформированным байтам и в обязательном порядке производит сверку по CRC.
Далее планирую заняться подключением платы часов реального времени (DS3231). Прошу вашего совета - к какому контроллеру лучше подключить. По моим поверхностным представлениям - можно подключить как к nano, так и к UNO.
Никакой разницы нет. Где свободны A4/A5 и нет жестких ограничений по времени выполнения кода - туда и подключайте. Ну и, в принципе, часы откликаются быстро, так что задержек особых не будет (особенно если их дергать не каждый луп, а через 1000мс минимум). Думайте не куда подключить, а как ими управлять - время выставить, то-сё. Потом будет понятно - куда их выгодней прицепить.
Я подключил ЧРВ к nano (провода соединил). Стал искать библиотеку - и нашёл много вариантов - начиная от того, что используют библиотеку от более ранних версий (DS1307) и заканчивая тем, что есть "самопальные". При этом - зачастую используют две библиотеки для выполнений просто вывода значений времени в Serial. Можете ли посоветовать некий наименее запутанный вариант для меня? Мне в итоге от этого ЧРВ нужно будет чтобы в заданное время началась выполняться процедура и закончила своё выполнение через определённо время.
Ещё любопытно: наткнулся на информацию, что в DS3231 есть встроенный датчик температуры. Мелькнула мысль - насколько разумно попытаться применить этот датчик как датчик температуры воздуха в помещении (но не понятно, какие дополнительные пины и куда подключать, какая библиотека и какая точность датчика).
А управлять ЧРВ мне требуется с TFT LCD экранчика - я уже и менюшку прорисовал для выставления времени включения системы по таймеру.
На UNO у меня пины A4/A5 задействованы на SD карточку TFT LCD дисплея. И немотря на то, что карточкой я пока пользоваться не планирую, я встречал мнение, что и SD карточку и ЧРВ на эти пины можно подключить одновременно (как бы параллельно).
Есть два варианта - в лупе читать время и принимать решение на основании считанных H:m:s или выставлять Alarm на RTC, который дернет ногу через ногу INT ардуиновское ExternalInterrupt (см. attachInterrupt()). Мне кажется, что во втором случае вы больше дров наломаете.
Ещё любопытно: наткнулся на информацию, что в DS3231 есть встроенный датчик температуры. Мелькнула мысль - насколько разумно попытаться применить этот датчик как датчик температуры воздуха в помещении.
По моему мнению - нинасколько. Мерять вы будете температуру не в помещении, а в корпусе. А туда еще, поди, запихаете модуль питания или еще что-нить нагревающееся. По СанПиновской методике это вообще не измерение. Датчик там для другого - часы изменяют "ход", основываясь на его показаниях. Термокомпенсация.
Кстати, точность 0.5 с 18B20 можете получить выставив разрешение на датчике в 9bit. Но только один раз ставьте (в setup()), а то дыру ему в ROM прожжете )) Судя по всему вы собираетесь долбить в датчики на каждом лупе (что, кстати, разогреет сенсоры дополнительно и внесет искажения до 0.5C).
Кстати, точность 0.5 с 18B20 можете получить выставив разрешение на датчике в 9bit. Но только один раз ставьте (в setup()), а то дыру ему в ROM прожжете ))
Ещё лучше выставить разрешение один раз и сохранить установки в датчике.
Спасибо. Скачал, загрузил библиотеку. Открыл пример "DS3231_simple". Открыл монитор порта - какие-то иероглифы. Взглянул на код - обратил внимание на "Serial.begin(57600);" до этого во всех скетчах у меня стояло 9600. В итоге, в мониторе порта поставил 57600 бод - и только после этого стала отображаться дата, время и температура (корректные). Вопрос: так сколько теперь в моём общем коде должно быть установлено в Serial.begin (чтобы и ЧРВ работали и всё остальное)?
И ещё вопос - после загрузки этого примера, я увидел надпись внизу "скетч использует 29% памяти устойства". Так это что - я обязан смириться с тем, что ЧРВ как минимум займут 29% памяти контоллера?
И ещё вопос - после загрузки этого примера, я увидел надпись внизу "скетч использует 29% памяти устойства". Так это что - я обязан смириться с тем, что ЧРВ как минимум займут 29% памяти контоллера?
Не обязаны.
Я поменял Serial.begin в "примере" на 9600 и в мониторе порта. Всё работает. "Пример" от этого не ста ли работать некорректно? (показания в мониторе порта коректные)
Теперь Ваш Serial работает в 6 раз медленнее и, соответственно, расходует в 6 раз больше времени контроллера. Насколько это критично для Вашего проекта, не знаю. Я обычно прежде, чем запустить любой пример, устанавливаю скорость порта в 115200.
Спасибо. Оставлю значит пока что 9600 везде (где-то встречал, что это популярная скорость передачи).
Стал смотреть пример "DS3231_alarms". Прочитал также в описании, что эти ЧРВ имеют два будильника - как я понял, один из них можно настроить на начало какой-то задачи, второй - на завершение. Именно это мне от часов и нужно. В коде примера "alarm" написано:
Не могу понять что это означает. У меня МК nano - насколько мне известно, он аналогичен UNO. Что тут говорится про пины 2 и 3?
Ещё увидел запись в коде (касаемо подключения ЧРВ):
Это что получается, для работы будильника я должен ещё один повод бросить (от SQW ЧРВ до 19 пина МК)?
PPeterr, этот фрагмент напоминает, что аппаратные прерывания в Uno/Nano находятся на 2 и 3 ногах.
Если Вы еще раз перечитаете то, что сами же цитировали, то обнаружите, что 19-я нога используется для прерывания в контроллере Mega2560, значит, вероятнее всего, именно к нему и относится замечание. В Uno/Nano это будет другой пин, но его так же нужно подтягивать к питанию.
Спасибо. Я правильно понимаю, что если я хочу использовать функции двх будильников, заложенные в DS3231, то выходами для будильников (alarm) будут именно 2-ая и 3-я нога и, соответственно, эти ноги нельза занимать на другие нужды?
По поводу вывода "SQW" на DS3231 в примере "DS3231_alarms" - подскажите про него, на что влияет, и куда его подключать, если у меня nano. Просто, другой пример ("DS3231_simple") работает без подключения этого вывода.
1. Во-первых, не выходами, а входами. Во-вторых, никто не обязывает Вас получать сигналы от будильника именно через прерывания (хотя это удобно). И в третьих - да, если Вы хотите получать сигналы от будильника именно через прерывания, то использовать для этого нужно именно 2 и 3 пины, на других пинах аппаратных прерыаний нет (правда, из этого правила есть исключения, но они не поддерживаются непосредственно библиотеками Ардуино).
2. Абсолютно аналогично Uno.
Вчера кое-как заставил светодиод (пин 13) включаться в определённое время и выключаться в определённое. Без использования функций "будильника". По-простому, типа: "если минуты больше ... но меньше ... - то включить светодиод. Иначе - выключить". Вроде, работает.
А написать решил - вопрос возник по поводу блока питания. В конечном итоге проект планирует содержать следующие платы:
1. UNO; 2. nano; 3. часы реального времени; 4. три датчика температуры DS18B20; 5. плата с 4-мя реле.
Нашёл у себя вот такой адаптор:
Подскажите, можно ли его применить для питания всех устройств, задействованных в проекте?
В первую очередь посмотрите, какие у вас реле - чаще всего попадаются на 12в
У меня вот такой набор:
Здесь отсутствует МК "nano" - я его позже покупал, когда осознал что на UNO пинов не хватит на всё. Мне кажется, что плата реле вся должна работать от 5В. Но приду домой - уточню.
Посмотрел - плата с реле называется HL-54S. Вот так выглядит:
и имеет такую схему подключения:
Как я понимаю - электромагнитные катушки реле питаются напряжением 5В.
Вопрос: если предположить, что имеющийся у меня адаптор выдаёт, как и написано, 5.4В с током 1.2А, - нормально ли это, если подать с него питание на всё? Не много ли это 5.4В, если написано, что номинальное напряжение питания микроконтроллера должно быть 5.0В?
Здравствуйте.
За прошедшее время я составил необходимый набор функций (как мне кажется) программного кода для контроллера UNO.
Напомню, из чего состоит проект на данный момент. Три датчика температуры опрашиваются с периодичностью 1 сек. контроллером nano. Также на nano поступает сигнал с ЧРВ. Затем nano передаёт набор из нескольких цифр через серийный порт на контроллер UNO (тепрература 1; температура 2; температура 3; часы; минуты; секунды). Контроллер Uno принимает эти 6 значений и обрабатывает их, давая в итоге информацию на выходе (в порт): требуемое состояние для 4-ёх устройств (по сути - для 4-ёх реле) насос 1, насос 2, котёл, водонагреватель.
Так вот, на мой взгляд, для контроллера UNO я заложил всё что хотел (не хватило ресурсов UNO, поэтому два графика из трёх я вывел с шагом 0,5 градуса, а один - с шагом 0,1 градуса).
Весь макет на данный момент выглядит так:
Главное меню содержит динамические данные с датчиков температуры и ЧРВ, а также отображает состояние исполнительных механизмов и актуальную "опцию" (алгоритм работы):
При нажатии сенсорной кнопки "Temp" - открывается окно с выставлением требуемой температуры в помещении. Здесь задаётся желаемая температура (которую в итоге должен поддерживать контроллер), с шагом 0.5 градуса:
Если с главного окна нажать кнопку "Menu", то открывается окно со ссылками на различные другие окна (меню):
Здесь - кнопки "Out", "Econ", "Fast" - просто меняют "опцию" (актуальный алгоритм работы для контроллера).
Кнопка "Graph" открывает окно с динамическим отображением графиков температур с трёх датчиков:
Кнопка "DiapG" открывает окно с выставлением диапазона по оси времени для графиков (от 2 минут до 18 часов):
Кнопка "Work" открывает окно со статическим отображеним состояний трёх исполнительных механизмов (двух насосов и котла) за прошедшее время - выводится время изменения состояния и само состояние (вкл/выкл) для исполнительных механизмов в это время. Очередная запись делается при изменении состояния хотя бы одного механизма:
Кнопка "Timer" открывает окно с выставлением требуемого времени (часы и минуты) включения системы отопления. Отложенный старт:
Сам код контроллера UNO (весь) на данный момент выглядит так:
Понимаю, что код очень длинный и разбираться в нём вряд ли у кого-то будет желание. Но всё же прошу (и надеюсь) - посмотрите, укажите на грубые мои ошибки. Хотя бы подскажите, где какие куски кода следует расположить (что надо переместить до void setup, что до loop setup, что после и т.д. Подскажите, как можно оптимизировать код, чтобы поменьше отбирать ресурсов у UNO и чтобы контроллер работал надёжнее (уменьшить вероятность сбоев).
Здравствуйте.
Собрал систему, подключил к реальному оборудованию. Очень доволен результатом. Подробнее отписался на этом форуме: https://www.forumhouse.ru/threads/307717/page-4
Всем кто помогал мне здесь - огромное спасибо.
А ежели на дощечку капиток хлынет?
Здравствуйте.
Собрал систему, подключил к реальному оборудованию. Очень доволен результатом. Подробнее отписался на этом форуме: https://www.forumhouse.ru/threads/307717/page-4
Всем кто помогал мне здесь - огромное спасибо.
Отлично, особенно поражает работа с экраном. Удивительно, что код с таким количеством графики и внешних библиотек влез в ардуино Уно...