Время
- Войдите на сайт для отправки комментариев
Ср, 09/11/2016 - 10:57
Прошу помощи. Я начинающий программист в свере ардуино(нуб короче). Вожусь над проектом 2 недели.
Надо было вывести на экран показания двух датчиков температуры(dht21) и времени без даты на lcd1302(16/2). есть пять кнопок set.left.down.up.right. Надо было создать менюшку на включение серво приводов(2) на включение по таймеру( таймер что бы можно было водить время вкл и вкл с кнопок) и остальные серво включаться при нужной температуре(что бы тоже можно было ставить температуру с кнопок) Пользовался скетчами с инета) Но только плачевно мой навык не вырос походу до этого уровня.
Скетч который я делал но не смог доделать
#include "DHT.h"
#include <Arduino.h>
#include <TimeLib.h>
#include "LCD_1602_RUS.h"
#include <EEPROM.h>
#include <Wire.h>
#include <OneWire.h>
#include <iarduino_RTC.h>
/*********************************************************************************************/
/* Объявление различных констант (номера пинов, смещения и т.п.) */
/*********************************************************************************************/
LCD_1602_RUS lcd(8, 9, 4, 5, 6, 7);
#define DHTPIN 33 // what pin we're connected to
#define DHTPIN_2 35 // what pin we're connected to
iarduino_RTC time(RTC_DS1302, 50, 46, 48);
int button;
const int BUTTON_NONE = 0;
const int BUTTON_RIGHT = 1;
const int BUTTON_UP = 2;
const int BUTTON_DOWN = 3;
const int BUTTON_LEFT = 4;
const int BUTTON_SELECT = 5;
DHT dht(DHTPIN, DHTTYPE);
DHT dht_2(DHTPIN_2, DHTTYPE);
/// Смещения в EEPROM
/// По этим смещениям хранятся соответствующие параметры, задаваемые пользователем
const int8_t ADDR_START_HOUR = 0; // Час начала полива
const int8_t ADDR_START_MINUTE = 1; // Минута начала полива
const int8_t ADDR_DURATION_HOUR = 2; // Длительность полива по расписанию, часов
/*********************************************************************************************/
/* Реализация вспомогательных классов */
/*********************************************************************************************/
/**
@brief Примитивный класс для работы с кнопками. Поддерживает обработку коротких и длинных нажатий
*/
int getPressedButton()
{
int buttonValue = analogRead(0); // считываем значения с аналогового входа(A0)
if (buttonValue < 50) {
return BUTTON_RIGHT;
}
else if (buttonValue < 200) {
return BUTTON_UP;
}
else if (buttonValue < 400) {
return BUTTON_DOWN;
}
else if (buttonValue < 600) {
return BUTTON_LEFT;
}
else if (buttonValue < 800) {
return BUTTON_SELECT;
}
return BUTTON_NONE;
}
/**
@brief TimePair структура, хранящая время в виде часы-минуты
*/
struct TimePair
{
int8_t hour;
int8_t minute;
};
/**
@brief PlannedActions структура, описывающая действие по времени
*/
struct PlannedActions
{
TimePair startTime; // время начала
TimePair stopTime; // время остановки
bool isActive; // признак обработки линии планировщиком
bool activated; // признак работы текущей линии
bool currentState; // состояние реле
};
/// Инициализируем LCD-дисплей. Возможно использование до 8 кириллических символов для "эмуляции" поддержки русского
/// языка на неруссифицированных экранах, все подробности - в документации к библиотеке LCD_1602_RUS
LCD_1602_RUS lcd(0x27, 16, 2);
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("Test ");
lcd.setCursor(0, 1);
lcd.print("Hello KZ");
time.begin();
dht.begin();
}
void loop() {
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
float t = dht.readTemperature();
float h_2 = dht_2.readHumidity();
float t_2 = dht_2.readTemperature();
if (millis() % 1000 == 0) { // если прошла 1 секунда
Serial.println()
} else {
lcd.setCursor(0, 0);
lcd.print(" %\t");
lcd.print(t);
lcd.println(" *C");
lcd.setCursor(7, 0);
lcd.print(" %\t2");
lcd.print(t2);
lcd.println(" *C");
lcd.clear();
lcd.print();
lcd.setCursor(0, 1);
lcd.print(time.gettime("H:i:s, D"));
Serial.println(time.gettime("d-m-Y, H:i:s, D")); // выводим время
delay(1); // приостанавливаем на 1 мс, чтоб не выводить время несколько раз за 1мс
}
void set_temp(){
{
// RTC.read(tm);
currentTime.Hour = dVal[0];
currentTime.Minute = dVal[1];
RTC.write(currentTime);
}
/**
@brief timeSumm добавить к начальному времени диапазон заданной длительности
@param startTime начало (часы-минуты)
@param duration длительность (часы-минуты)
@return конец временного диапазона (часы-минуты)
*/
TimePair timeSumm(TimePair &startTime, TimePair &duration)
{
TimePair result;
result.minute = startTime.minute + duration.minute;
result.hour = startTime.hour + duration.hour + (result.minute / 60);
result.minute = result.minute % 60;
return result;
}
/*********************************************************************************************/
/**
@brief calculateShedule пересчитать время начала и конца полива для заданной линии
@param idx номер линии
@param tStart время начала полива
@param tDuration длительность полива
*/
void calculateShedule(int8_t idx, TimePair &tStart, TimePair &tDuration)
{
shedule[idx].startTime = tStart;
shedule[idx].stopTime = timeSumm(tStart, tDuration);
shedule[idx].activated = false;
if (EEPROM.read(ADDR_LINE_1 + idx) != 0)
{
shedule[idx].isActive = true;
if (++idx < 4)
calculateShedule(idx, shedule[idx - 1].stopTime, tDuration);
} else
{
shedule[idx].isActive = false;
if (++idx < 4)
calculateShedule(idx, tStart, tDuration);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
Второй скетч который я пытался переделать но не вышло нифига
#include "DHT.h"
#include <Arduino.h>
#include <TimeLib.h>
#include "LCD_1602_RUS.h"
#include <LiquidCrystal.h>
#include <EEPROM.h>
#include <Wire.h>
#include <OneWire.h>
#include <iarduino_RTC.h>
/// Если не нужна отладочная информация в консоли, то строка с дефайном должна быть закомментирована.
/// При включении отладки возможны проблемы в работе скетча, т.к. компилятор ругается что ему мало памяти
//#define SERIAL_DEBUG
/*********************************************************************************************/
/* Объявление различных констант (номера пинов, смещения и т.п.) */
/*********************************************************************************************/
/// Константы, необходимые для работы скетча
const int8_t MAX_VALUES_COUNT = 4; // Максимальное число значений в строке, формируемой для вывода на экран ()
const int8_t LIGHT_TIMEOUT = 10; // Время до отключения подсветки экрана при бездействии
const int8_t MENU_ITEMS_COUNT = 16; // Количество строк в меню
//const int16_t HUMIDITY_MIN_PROBE = 200; // Минимальное значение влажности почвы (очень сухо)
//const int16_t HUMIDITY_MAX_PROBE = 840; // Максимальное значение влажности почвы (болото)
/// Смещения в EEPROM
/// По этим смещениям хранятся соответствующие параметры, задаваемые пользователем
const int8_t ADDR_START_HOUR = 0; // Час начала полива
const int8_t ADDR_START_MINUTE = 1; // Минута начала полива
const int8_t ADDR_DURATION_HOUR = 2; // Длительность полива по расписанию, часов
const int8_t ADDR_DURATION_MINUTE = 3; // Длительность полива по расписанию, минут
const int8_t ADDR_LINE_1 = 4; // Необходимость полива первой линии по расписанию
const int8_t ADDR_LINE_2 = 5; // Необходимость полива второй линии по расписанию
const int8_t ADDR_LINE_3 = 6; // Необходимость полива третьей линии по расписанию
const int8_t ADDR_LINE_4 = 7; // Необходимость полива четвертой линии по расписанию
const int8_t ADDR_BUTTON_HOUR = 8; // Длительность полива по кнопке, часов
const int8_t ADDR_BUTTON_MINUTE = 9; // Длительность полива по кнопке, минут
/* SDA - a4, SCL - a5*/
// аналоговые пины
//const int8_t PIN_RESERVE_A0 = 0; // Пин резерв (14 цифровой, не задействован как аналог)
//const int8_t PIN_RESERVE_A1 = 1; // Пин резерв (15 цифровой, не задействован как аналог)
//const int8_t PIN_SOIL_HUMIDITY = 2; // Пин датчик влажности почвы
//const int8_t PIN_SOIL_TEMPERATURE = 3; // Пин датчик температуры почвы
//const int8_t PIN_SDA = 4; // Пин SDA
//const int8_t PIN_SCL = 5; // Пин SCL
const int8_t PIN_CHANGE_VALUE = 7; // Пин для смены пункта меню или значения в меню
const int8_t PIN_CHANGE_MODE = 6; // Пин для изменения значения в выбранном пункте меню
// цифровые пины
//const int8_t PIN_SOIL_LEFT =- ; // Пин датчика влажности почвы 1 (полярность меняется со 2)
//const int8_t PIN_SOIL_RIGHT = -; // Пин датчика влажности почвы 2 (полярность меняется с 1)
//const int8_t PIN_BUTTON[] = {-}; // Массив пинов кнопок принудительного включения полива
//const int8_t PIN_DHT[] = {-}; // Массив пинов датчиков температуры и влажности
//const int8_t PIN_MOTOR = -; // Пин для включения реле мотора
//const int8_t PIN_RELAY[] = {-}; // Массив пинов реле электромагнитных клапанов
const int8_t PIN_BUTTON = {0, 1, 2, 3, 4, 5}; // Пин для выбора режима или подтверждения изменений (аналоговый A0)
//const int8_t PIN_FLOWMETER = -; // Пин расходомера (аналоговый A1)
/*********************************************************************************************/
/* Реализация вспомогательных классов */
/*********************************************************************************************/
/**
@brief Примитивный класс для работы с кнопками. Поддерживает обработку коротких и длинных нажатий
*/
class ActionButton
{
private:
int64_t clickTime; // Время, когда была нажата кнопка
bool statusClicked[2]; // Предыдущее и текущее состояние кнопки (нажата или нет)
int8_t PIN_BUTTON; // На каком цифровом входе находится кнопка
public:
/**
@brief Конструктор ActionButton
@param PIN_BTN номер цифрового входа ардуины, на котором висит кнопка
*/
ActionButton(int8_t PIN_BTN)
{
PIN_BUTTON = PIN_BTN;
clickTime = 0;
statusClicked[0] = false;
statusClicked[1] = false;
}
/**
@brief buttonPressed Текущее состояние кнопки
@return true, если кнопка нажата, иначе false
*/
bool buttonPressed()
{
statusClicked[1] = statusClicked[0];
statusClicked[0] = !digitalRead(PIN_BUTTON);
if (statusClicked[0] && (!statusClicked[1]) && (clickTime == 0)) // если нажали кнопку только что,
clickTime = millis() / 1000; // то запомнить это время
return statusClicked[0];
}
/**
@brief buttonClicked Результат последнего нажатия кнопки
@return 0 - кнопка еще не была нажата и отпущена; 1 - было сделано короткое нажатие; 2 - было сделано долгое нажатие (более 2 секунд)
*/
int8_t buttonClicked()
{
if (!statusClicked[0]) // если кнопка сейчас не нажата
{
if (statusClicked[1]) // а раньше она была нажата
{
if ((millis() / 1000 - clickTime) > 2) // и нажатие на кнопку было дольше 2 секунд
{
clickTime = 0;
return 2;
} else // нажатие на кнопку было коротким
{
clickTime = 0;
return 1;
}
}
}
return 0;
}
};
// Максимальное значение аналогового датчика
#define MAX_POS 1023
// VRx и VRy выдают значения от 0 до 1023
// Максимальный угол наклона джойстика
#define MAX_ANGLE 60
// Минимальное значение угла наклона джойстика, которое засчитывается для навигации по меню
#define CLICK_ANGLE 25
/**
@brief Класс для навигации по меню поливатора
*/
// Нажатые кнопки
int button;
const int BUTTON_NONE = 0;
const int BUTTON_RIGHT = 1;
const int BUTTON_UP = 2;
const int BUTTON_DOWN = 3;
const int BUTTON_LEFT = 4;
const int BUTTON_SELECT = 5;
// функция обработки нажатия кнопок
int getPressedButton()
{
int buttonValue = analogRead(0); // считываем значения с аналогового входа(A0)
if (buttonValue < 50) {
return BUTTON_RIGHT;
}
else if (buttonValue < 200) {
return BUTTON_UP;
}
else if (buttonValue < 400) {
return BUTTON_DOWN;
}
else if (buttonValue < 600) {
return BUTTON_LEFT;
}
else if (buttonValue < 800) {
return BUTTON_SELECT;
}
return BUTTON_NONE;
}
/**
@brief buttonClicked Результат последнего нажатия кнопки джойстика
@return 0 - кнопка еще не была нажата и отпущена; 1 - было сделано короткое нажатие; 2 - было сделано долгое нажатие (более 2 секунд)
*/
int8_t buttonClicked()
{
return button->buttonClicked();
}
};
/*********************************************************************************************/
/* Объявление структур и глобальных переменных */
/*********************************************************************************************/
void(* softReset) (void) = 0; // объявляем функцию с адресом 0, для софт-ресета
int8_t lastAction; // Время последнего действия (используется для активации/деактивации подсветки экрана)
int8_t forcedUpdate; // Время принудительной перерисовки меню
int tmpValues[MAX_VALUES_COUNT]; // массив для временного хранения значений
int8_t currentMenu; // Номер текущего пункта меню
bool menuIsSelected; // Признак того, что текущее меню было выбрано пользователем
bool motorIsActive; // Признак работы насоса
bool displayIsActive; // Признак включенной подсветки дисплея
tmElements_t currentTime, previousTime;
/**
@brief TimePair структура, хранящая время в виде часы-минуты
*/
struct TimePair
{
int8_t hour;
int8_t minute;
};
/**
@brief ManualActions структура, описывающая действие по нажатию кнопки
*/
/*struct ManualActions
{
TimePair stopTime; // время остановки
ActionButton* starter; // привязанная кнопка
bool isUnlimited; // бесконечный полив - включается при долгом нажатии на кнопку
bool isActive; // признак активности текущего действия
};
*/
/**
@brief PlannedActions структура, описывающая действие по времени
*/
struct PlannedActions
{
TimePair startTime; // время начала
TimePair stopTime; // время остановки
bool isActive; // признак обработки линии планировщиком
bool activated; // признак работы текущей линии
bool currentState; // состояние реле
};
/// Инициализируем LCD-дисплей. Возможно использование до 8 кириллических символов для "эмуляции" поддержки русского
/// языка на неруссифицированных экранах, все подробности - в документации к библиотеке LCD_1602_RUS
LCD_1602_RUS lcd(0x27, 16, 2);
/// Инициализируем кнопки
int buttonValue = analogRead(0); // считываем значения с аналогового входа(A0)
/* Инициализируем датчик температуры почвы
OneWire soilT(PIN_SOIL_TEMPERATURE);
int8_t soilHumidity; // значение влажности почвы, необходимо читать его раз в 10 минут или при обращении к нему
int8_t soilTemperature; // значение температуры почвы, необходимо читать его раз в 10 минут или при обращении к нему
*/
/// Объявляем и заполняем массив действий по 4 кнопкам
ManualActions control[] = {
{{0, 0}, 0, false},
{{0, 0}, 0, false},
{{0, 0}, 0, false},
{{0, 0}, 0, false}
};
/// Объявляем массив запланированных действий по 4 линиям
PlannedActions shedule[4];
/// Объявляем и инициализируем датчики температуры и влажности
DHT dht1(PIN_DHT[0], DHT11);
DHT dht2(PIN_DHT[1], DHT11);
DHT dht3(PIN_DHT[2], DHT11);
/**
@brief ValueView структура, хранящая начальную позицию и количество символов в отображаемом значении
*/
struct ValueView
{
int8_t pos;
int8_t length;
};
/**
@brief RowParameters структура, описывающая параметры выводимой строки для пункта меню одного типа
*/
struct RowParameters
{
int minimum[4];
int maximum[4];
ValueView value[4];
};
/// Заполняем параметры выводимых строк для разных типов отображаемой информации
RowParameters paramId[] = {
{{1, 1, 2016, 0}, {31, 12, 2050, 0}, {{2, 2}, {6, 2}, {10, 4}, {0, 0}}}, // строка с датой
{{0, 0, 0, 0}, {23, 59, 0, 0}, {{5, 2}, {9, 2}, {0, 0}, {0, 0}}}, // строки со временами
{{0, 0, 0, 0}, {1, 1, 1, 1}, {{1, 2}, {5, 2}, {9, 2}, {13, 2}}}, // строка с клапанами
{{0, 0, 0, 0}, {0, 0, 0, 0}, {{1, 0}, {10, 0}, {0, 0}, {0, 0}}}, // строки с влажностью и температурой
{{0, 0, 0, 0}, {0, 0, 0, 0}, {{2, 2}, {10, 5}, {0, 0}, {0, 0}}}, // строка с данными о расходе воды
{{0, 0, 0, 0}, {0, 0, 0, 0}, {{3, 3}, {10, 2}, {0, 0}, {0, 0}}} // строка с запросом перезагрузки
};
/**
@brief MenuInfo структура, описывающая строку меню
*/
struct MenuInfo
{
const wchar_t* menuName;
int8_t firstPosition;
int8_t menuValues;
int8_t activeSubmenu;
RowParameters* rowParameter;
};
/// Заполняем меню. Префикс "L" перед каждой строкой необходим для корректного вывода
/// кириллических символов с помощью библиотеки LCD_1602_RUS, подробности - в документации к ней
MenuInfo allMenu[] = {
{L"TEK. ДATA", 3, 3, 0, ¶mId[0]},
{L"TEK. BPEMЯ", 3, 2, 0, ¶mId[1]},
{L"HAЧAЛO ПOЛИBA", 1, 2, 0, ¶mId[1]},
{L"ДЛИTEЛbHOCTb", 2, 2, 0, ¶mId[1]},
{L"ПOЛИB C KHOПKИ", 1, 2, 0, ¶mId[1]},
{L"AKTИBHЫE ЛИHИИ", 1, 4, 0, ¶mId[2]},
{L"ПOЧBA", 5, 0, 0, ¶mId[3]},
{L"HACOC. CTAHЦИЯ", 1, 0, 0, ¶mId[3]},
{L"TEПЛИЦA", 4, 0, 0, ¶mId[3]},
{L"KPЫЛbЦO", 4, 0, 0, ¶mId[3]},
{L"PACXOД BOДЫ", 2, 0, 0, ¶mId[4]},
{L"PECTAPT", 4, 2, 0, ¶mId[5]}
};
/**
@brief MessageType различные типы сообщений, которые необходимо обрабатывать
*/
enum MessageType
{
MTSheduleOn = 0, // начало по расписанию
MTSheduleOff = 1, // завершение по расписанию
MTControlOn = 2, // кнопка включена
MTControlOff = 3, // кнопка выключена
MTUnlimited = 4, // начат бесконечный полив
MTCompleted = 5 // успешно завершено
};
volatile int waterCounter; // переменная для хранения объема прошедшей через насос воды
int waterCountPrevious; // предыдущее значение счетчика воды
int currentFlowRate; // текущий расход воды (л/с) - считается ежесекундно
float currentConsumption; // текущее потребление (л) - накапливается
/*********************************************************************************************/
/* Функции */
/*********************************************************************************************/
/**
@brief waterFlow подсчет воды (функция должна висеть на прерывании)
*/
void waterFlow ()
{
waterCounter++;
}
/*********************************************************************************************/
/**
@brief setSensorPolarity установка полярности для датчика влажности почвы
@param flip true или false для переключения полярности
*/
void setSensorPolarity(boolean flip)
{
if (flip) {
digitalWrite(PIN_SOIL_LEFT, HIGH);
digitalWrite(PIN_SOIL_RIGHT, LOW);
} else {
digitalWrite(PIN_SOIL_LEFT, LOW);
digitalWrite(PIN_SOIL_RIGHT, HIGH);
}
}
/*********************************************************************************************/
/**
@brief getSoilHumidity получение информации о влажности почвы с датчика
*/
void getSoilHumidity()
{
setSensorPolarity(true);
delay(500);
int val1 = analogRead(PIN_SOIL_HUMIDITY);
delay(500);
setSensorPolarity(false);
delay(500);
int val2 = 1023 - analogRead(PIN_SOIL_HUMIDITY);
digitalWrite(PIN_SOIL_LEFT, LOW);
digitalWrite(PIN_SOIL_RIGHT, LOW);
soilHumidity = ((val1 + val2) / 2 - HUMIDITY_MIN_PROBE) / (HUMIDITY_MAX_PROBE - HUMIDITY_MIN_PROBE) * 100;
if (soilHumidity < 0)
soilHumidity = 0;
if (soilHumidity > 100)
soilHumidity = 100;
}
/*********************************************************************************************/
/**
@brief getSoilTemperature получение информации о температуре почвы с датчика
*/
void getSoilTemperature()
{
uint8_t address[8];
if (!soilT.search(address))
{
#ifdef SERIAL_DEBUG
Serial.println("Soil temperature sensor is not exist");
#endif
return;
}
byte data[2];
soilT.reset();
soilT.write(0xCC);
soilT.write(0x44);
delay(750);
soilT.reset();
soilT.write(0xCC);
soilT.write(0xBE);
data[0] = soilT.read();
data[1] = soilT.read();
int Temp = (data[1] << 8) + data[0];
soilTemperature = Temp >> 4;
}
/*********************************************************************************************/
/**
@brief showInfo формирование сообщения по событиям и номерам линий
@param mt тип события, которое необходимо
@param mask маска линий, к которым относится событие
*/
void showInfo(MessageType mt, int mask)
{
const wchar_t* textOn = L" BKЛ. ";
const wchar_t* textOff = L" BЫKЛ. ";
const wchar_t* textShedule = L"ПЛAHOBOE";
const wchar_t* textControl = L"(KHOПKA)";
const wchar_t* textUnlimited = L"ПOCTOЯHHO";
const wchar_t* textCompleted = L"** BЫПOЛHEHO! **";
const wchar_t* lineTxt;
const wchar_t* lineMulti = L"ЛИHИИ: ";
const wchar_t* lineOne = L"ЛИHИЯ # ";
bool used[4];
int8_t count = 0;
for (int8_t i = 0; i < 4; i++)
{
used[i] = bool (1 & (mask >> i));
if (used[i])
count++;
}
activateLight(true);
lcd.clear();
lcd.setCursor(0, 0);
switch (mt)
{
case MTCompleted:
lcd.print(textCompleted);
break;
case MTControlOff:
lcd.print(textOff);
lcd.print(textControl);
break;
case MTControlOn:
lcd.print(textOn);
lcd.print(textControl);
break;
case MTSheduleOff:
lcd.print(textOff);
lcd.print(textShedule);
break;
case MTSheduleOn:
lcd.print(textOn);
lcd.print(textShedule);
break;
case MTUnlimited:
lcd.print(textUnlimited);
break;
}
if (count > 1)
lineTxt = lineMulti;
else
lineTxt = lineOne;
int lineBegin = ((16 - sizeof(lineTxt)) / 2) - count;
lcd.setCursor(lineBegin, 1);
lcd.print(lineTxt);
for (int8_t i = 0; i < 4; i++)
if (used[i])
{
lcd.print(i + 1);
lcd.print(" ");
}
forcedUpdate = 5;
}
/*********************************************************************************************/
/**
@brief currentToArray текущие значения (с датчиков или из EEPROM) поместить в массив для отображения
@param ta указатель на массив
*/
void currentToArray(int ta[])
{
float fT, fH;
// int tmpFR;
switch (currentMenu) {
case 0: // текущая дата
getData(ta);
break;
case 1: // текущее время
getTime(ta);
break;
case 2: // начало полива
ta[0] = EEPROM.read(ADDR_START_HOUR);
ta[1] = EEPROM.read(ADDR_START_MINUTE);
break;
case 3: // длительность полива
ta[0] = EEPROM.read(ADDR_DURATION_HOUR);
ta[1] = EEPROM.read(ADDR_DURATION_MINUTE);
break;
case 4: // длительность полива, запускаемого с кнопки
ta[0] = EEPROM.read(ADDR_BUTTON_HOUR);
ta[1] = EEPROM.read(ADDR_BUTTON_MINUTE);
break;
case 5: // активные клапаны
for (int8_t i = 0; i < 4; i++)
{
ta[i] = EEPROM.read(ADDR_LINE_1 + i);
if (ta[i] != 0)
ta[i] = 1;
}
break;
case 6: // температура и влажность почвы
getSoilHumidity();
getSoilTemperature();
ta[0] = soilTemperature;
ta[1] = soilHumidity;
break;
case 7: // температура и влажность
ta[0] = dht1.readTemperature();
ta[1] = dht1.readHumidity();
break;
case 8: // температура и влажность
ta[0] = dht2.readTemperature();
ta[1] = dht2.readHumidity();
break;
case 9: // температура и влажность
ta[0] = dht3.readTemperature();
ta[1] = dht3.readHumidity();
break;
case 10: // расход воды
ta[0] = currentFlowRate;
ta[1] = int(currentConsumption);
break;
default: // ошибка
break;
}
}
/*********************************************************************************************/
/**
@brief arrayToCurrent сохранить введенные пользователем данные
@param ta указатель на массив, в котором находятся пользовательские данные
*/
void arrayToCurrent(int ta[])
{
TimePair t1, t2;
switch (currentMenu) {
case 0: // текущая дата
setNewData(ta);
break;
case 1: // текущее время
setNewTime(ta);
break;
case 2: // начало полива
EEPROM.write(ADDR_START_HOUR, ta[0]);
EEPROM.write(ADDR_START_MINUTE, ta[1]);
t1.hour = ta[0];
t1.minute = ta[1];
t2.hour = EEPROM.read(ADDR_DURATION_HOUR);
t2.minute = EEPROM.read(ADDR_DURATION_MINUTE);
calculateShedule(0, t1, t2);
break;
case 3: // длительность полива
EEPROM.write(ADDR_DURATION_HOUR, ta[0]);
EEPROM.write(ADDR_DURATION_MINUTE, ta[1]);
t1.hour = EEPROM.read(ADDR_START_HOUR);
t1.minute = EEPROM.read(ADDR_START_HOUR);
t2.hour = ta[0];
t2.minute = ta[1];
calculateShedule(0, t1, t2);
break;
case 4: // длительность полива с кнопки
EEPROM.write(ADDR_BUTTON_HOUR, ta[0]);
EEPROM.write(ADDR_BUTTON_MINUTE, ta[1]);
break;
case 5: // активные клапаны
for (int8_t i = 0; i < 4; i++)
{
if (ta[i] == 0)
EEPROM.write(ADDR_LINE_1 + i, 0);
else
EEPROM.write(ADDR_LINE_1 + i, 1);
}
t1.hour = EEPROM.read(ADDR_START_HOUR);
t1.minute = EEPROM.read(ADDR_START_HOUR);
t2.hour = EEPROM.read(ADDR_DURATION_HOUR);
t2.minute = EEPROM.read(ADDR_DURATION_MINUTE);
calculateShedule(0, t1, t2);
break;
case 11:
if (allMenu[currentMenu].activeSubmenu == 1)
softReset();
break;
default: // ошибка
break;
}
}
/*********************************************************************************************/
/**
@brief activateLight активировать подсветку экрана
@param act true - включить подсветку, false - выключить ее
*/
void activateLight(bool act)
{
displayIsActive = act;
if (act)
{
lastAction = LIGHT_TIMEOUT;
lcd.display(); // Включаем подсветку дисплея и сам дисплей
lcd.backlight();
} else
{
lcd.noDisplay(); // Отключаем подсветку дисплея и сам дисплей
lcd.noBacklight();
}
}
/*********************************************************************************************/
/**
@brief print2digits дополнить число от 0 до 9 дополнительным нулем слева
@param number число, которое необходимо привести к двухсимвольному виду
*/
void print2digits(int number)
{
if (number >= 0 && number < 10)
{
lcd.print("0");
}
lcd.print(number);
}
/*********************************************************************************************/
/**
@brief setNewData установка новой даты
@param dVal массив, в котором хранится день, месяц и год, установленные пользователем
*/
void setNewData(int dVal[])
{
// RTC.read(tm);
currentTime.Day = dVal[0];
currentTime.Month = dVal[1];
currentTime.Year = CalendarYrToTm(dVal[2]);
RTC.write(currentTime);
}
/*********************************************************************************************/
/**
@brief setNewTime установка нового времени
@param dVal массив, в котором хранятся часы и минуты, заданные пользователем
*/
void setNewTime(int dVal[])
{
// RTC.read(tm);
currentTime.Hour = dVal[0];
currentTime.Minute = dVal[1];
RTC.write(currentTime);
}
/*********************************************************************************************/
/**
@brief getData получить от часов реального времени текущую дату и поместить ее в массив
@param dVal массив для полученной даты
*/
void getData(int dVal[])
{
// надо получить от часов текущую дату и отобразить ее
if (RTC.read(currentTime))
{
dVal[0] = currentTime.Day;
dVal[1] = currentTime.Month;
dVal[2] = tmYearToCalendar(currentTime.Year);
}
else
{
dVal[0] = 1;
dVal[1] = 1;
dVal[2] = 2016;
}
}
/*********************************************************************************************/
/**
@brief getTime получить от часов реального времени текущее время и поместить его в массив
@param dVal массив для полученного времени
*/
void getTime(int dVal[])
{
// надо получить от часов текущее время и отобразить его
if (RTC.read(currentTime))
{
dVal[0] = currentTime.Hour;
dVal[1] = currentTime.Minute;
// showValues(currSubmenu, tm.Day, tm.Month, tmYearToCalendar(tm.Year));
}
else
{
dVal[0] = 0;
dVal[1] = 0;
}
}
/*********************************************************************************************/
/**
@brief showValues отобразить строку со значениями в соответствии с текущим пунктом меню
@param sv массив с отображаемыми данными
@param activeValue номер значения, выбранного пользователем для изменения
*/
void showValues(int sv[], int8_t activeValue)
{
for (int8_t i = 0; i < allMenu[currentMenu].menuValues; i++)
{
// здесь нужно формировать вывод значений на экран
if (activeValue == i)
{
lcd.setCursor(allMenu[currentMenu].rowParameter->value[i].pos + allMenu[currentMenu].rowParameter->value[i].length, 1);
lcd.print(">");
lcd.setCursor(allMenu[currentMenu].rowParameter->value[i].pos - 1, 1);
lcd.print("<");
}
else
lcd.setCursor(allMenu[currentMenu].rowParameter->value[i].pos, 1);
switch (currentMenu) {
case 0:
case 1:
case 2:
case 3:
case 4:
print2digits(sv[i]);
break;
case 11:
if (i == 0)
lcd.print("HET");
else
lcd.print(L"ДA");
break;
default:
lcd.print(i + 1);
if (sv[i] > 0)
lcd.print("+");
else
lcd.print("-");
break;
}
}
if (allMenu[currentMenu].menuValues == 0)
{
switch (currentMenu) {
case 6:
case 7:
case 8:
case 9:
lcd.setCursor(allMenu[currentMenu].rowParameter->value[0].pos, 1);
lcd.print("T:");
if (sv[0] > 0)
lcd.print("+");
else if (sv[0] < 0)
lcd.print("-");
else
lcd.print(" ");
print2digits(sv[0]);
lcd.print(L"°C");
lcd.setCursor(allMenu[currentMenu].rowParameter->value[1].pos, 1);
lcd.print("B:");
print2digits(sv[1]);
lcd.print("%");
break;
case 10:
lcd.setCursor(0, 1);
lcd.print("T:");
print2digits(sv[0]);
lcd.print(L"Л/c");
lcd.setCursor(8, 1);
lcd.print("C:");
lcd.print(sv[1]);
lcd.print(L"Л");
break;
}
}
}
/*********************************************************************************************/
/**
@brief showSubmenu отобразить на LCD строку со значениями текущего пункта меню
*/
void showSubmenu()
{
int tmpLocal[4];
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
if (menuIsSelected) // если меню выбрано
{ // то надо отображать временные значения
showValues(&tmpValues[0], allMenu[currentMenu].activeSubmenu);
}
else // иначе - получать и отображать текущие значения
{
currentToArray(&tmpLocal[0]);
showValues(&tmpLocal[0], -1);
}
}
/*********************************************************************************************/
/**
@brief markMenuAsSelected пометить квадратными скобками состояние текущего меню - выбрано или не выбрано
@param marked true, если меню выбрано
*/
void markMenuAsSelected(bool marked)
{
char *markedMenu = " [ ]";
int8_t isMarked = (int8_t) marked;
lcd.setCursor(0, 0);
lcd.print(markedMenu[isMarked]);
lcd.setCursor(15, 0);
lcd.print(markedMenu[isMarked + 2]);
}
/*********************************************************************************************/
/**
@brief updateMenu перерисовать меню и подменю
@param idx номер пункта меню
*/
void updateMenu(int8_t idx)
{
if (!menuIsSelected)
{
lcd.clear();
lcd.setCursor(allMenu[idx].firstPosition, 0);
lcd.print(allMenu[idx].menuName);
}
markMenuAsSelected(menuIsSelected);
showSubmenu();
}
/*********************************************************************************************/
/**
@brief moveLeft обработка нажатия кнопки "влево"
*/
void moveLeft()
{
if (menuIsSelected) // если выбран пункт основного меню
{
if (allMenu[currentMenu].menuValues == 0) // и в нем нет изменяемых значений
return; // то выйти
if (--allMenu[currentMenu].activeSubmenu < 0) // иначе уменьшить индекс выбранного значения
{
allMenu[currentMenu].activeSubmenu = allMenu[currentMenu].menuValues - 1;
}
}
else // а если пункт еще не выбран
{
if (--currentMenu < 0) // то уменьшить индекс текущего пункта меню
currentMenu = MENU_ITEMS_COUNT - 1;
}
}
/*********************************************************************************************/
/**
@brief moveRight обработка нажатия кнопки "вправо"
*/
void moveRight()
{
if (menuIsSelected) // если выбран пункт основного меню
{
if (allMenu[currentMenu].menuValues == 0) // и в нем нет изменяемых значений
return; // то выйти
if (++allMenu[currentMenu].activeSubmenu >= // иначе уменьшить индекс выбранного значения
allMenu[currentMenu].menuValues)
{
allMenu[currentMenu].activeSubmenu = 0;
}
}
else // а если пункт еще не выбран
{
if (++currentMenu >= MENU_ITEMS_COUNT) // то уменьшить индекс текущего пункта меню
currentMenu = 0;
}
}
/*********************************************************************************************/
/**
@brief moveUp обработка нажатия кнопки "вверх"
*/
void moveUp()
{
if ((!menuIsSelected) // если не выбран пункт основного меню
|| (allMenu[currentMenu].menuValues == 0)) // или в нем нет изменяемых значений
return; // то выйти
int8_t valueId = allMenu[currentMenu].activeSubmenu;
if (++tmpValues[valueId] > // иначе уменьшить индекс выбранного значения
allMenu[currentMenu].rowParameter->maximum[valueId])
{
tmpValues[valueId] = allMenu[currentMenu].rowParameter->minimum[valueId];
}
}
/*********************************************************************************************/
/**
@brief moveDown обработка нажатия кнопки "вниз"
*/
void moveDown()
{
if ((!menuIsSelected) // если не выбран пункт основного меню
|| (allMenu[currentMenu].menuValues == 0)) // или в нем нет изменяемых значений
return; // то выйти
int8_t valueId = allMenu[currentMenu].activeSubmenu;
if (--tmpValues[valueId] < // иначе уменьшить индекс выбранного значения
allMenu[currentMenu].rowParameter->minimum[valueId])
{
tmpValues[valueId] = allMenu[currentMenu].rowParameter->maximum[valueId];
}
}
/*********************************************************************************************/
/**
@brief applyButton обработка нажатия кнопки подтверждения
*/
void applyButton()
{
if (!menuIsSelected) // если не выбран пункт основного меню
{ // то надо проверить, можно ли его выбрать
if (allMenu[currentMenu].menuValues == 0) // если выбираемых значений нет
return; // то выйти
currentToArray(&tmpValues[0]);
menuIsSelected = true;
}
else
{
arrayToCurrent(&tmpValues[0]);
menuIsSelected = false;
allMenu[currentMenu].activeSubmenu = 0;
}
}
/*********************************************************************************************/
/**
@brief showLogo отобразить надпись при старте поливатора
*/
void showLogo()
{
wchar_t strLogo1[] = L"ПOЛИBATOP -";
wchar_t strLogo2[] = L"И3MEPЯTOP";
wchar_t currSymbol[] = L" ";
lcd.setCursor(0, 0);
for (int8_t i = 0; i < 11; i++)
{
currSymbol[0] = strLogo1[i];
lcd.print(currSymbol);
delay(200);
}
delay(1000);
lcd.setCursor(6, 1);
for (int8_t i = 0; i < 9; i++)
{
currSymbol[0] = strLogo2[i];
lcd.print(currSymbol);
delay(200);
}
delay(2000);
}
/*********************************************************************************************/
/**
@brief initAllPins инициализация входов и выходов ардуины
*/
void initAllPins()
{
// активируем пины электромагнитных клапанов и кнопок
for (int8_t i = 0; i < 4; i++)
{
pinMode(PIN_RELAY[i], OUTPUT);
digitalWrite(PIN_RELAY[i], HIGH);
pinMode(PIN_BUTTON[i], INPUT);
digitalWrite(PIN_BUTTON[i], HIGH);
control[i].starter = new ActionButton(PIN_BUTTON[i]);
}
/* // активируем пин насоса
pinMode(PIN_MOTOR, OUTPUT);
digitalWrite(PIN_MOTOR, HIGH);
*/
// активируем пин кнопки
pinMode(PIN_APPLY_BUTTON, INPUT);
digitalWrite(PIN_APPLY_BUTTON, HIGH); //включаем на нем подтягивающий резистор
/* // активируем пины катода и анода на датчике влажности почвы
pinMode(PIN_SOIL_LEFT, OUTPUT);
pinMode(PIN_SOIL_RIGHT, OUTPUT);
// убираем ток с анода и катода
digitalWrite(PIN_SOIL_LEFT, LOW);
digitalWrite(PIN_SOIL_RIGHT, LOW);
// инициализируем пин расходомера и вешаем на него прерывание
pinMode(PIN_FLOWMETER, INPUT);
attachInterrupt(0, waterFlow, RISING);*/
}
/*********************************************************************************************/
/**
@brief setup старт ардуины с инициализацией датчиков
*/
void setup()
{
#ifdef SERIAL_DEBUG
Serial.begin(9600);
while (!Serial); // wait for serial
#endif
delay(200);
if (!RTC.read(currentTime))
{
currentTime.Hour = 12;
currentTime.Minute = 0;
currentTime.Second = 0;
currentTime.Day = 1;
currentTime.Month = 1;
currentTime.Year = CalendarYrToTm(2016);
RTC.write(currentTime);
}
waterCounter = waterCountPrevious = 0;
currentFlowRate = 0;
currentConsumption = 0.;
initAllPins();
dht1.begin();
dht2.begin();
dht3.begin();
getSoilHumidity();
lcd.init();
currentMenu = 2;
applyButton();
applyButton();
activateLight(true);
showLogo();
previousTime = currentTime;
lcd.clear();
motorIsActive = false;
menuIsSelected = false;
currentMenu = 0;
forcedUpdate = 0;
updateMenu(currentMenu);
}
/*********************************************************************************************/
/**
@brief drawArrow отображение стрелок вправо-влево при перемещении по меню
@param directed направление перемещения
@return 0, если перемещения не будет, -1, если кнопка нажата влево ,
1, если кнопка нажата вправо
*/
int drawArrow(int directed)
{
if (directed == 0)
return 0;
if (!menuIsSelected)
{
if (directed > 0)
{
// вправо
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(15, 0);
lcd.print(">");
}
else
{
// влево
lcd.setCursor(0, 0);
lcd.print("<");
lcd.setCursor(15, 0);
lcd.print(" ");
}
}
return 1;
}
/*********************************************************************************************/
/**
@brief timeSumm добавить к начальному времени диапазон заданной длительности
@param startTime начало (часы-минуты)
@param duration длительность (часы-минуты)
@return конец временного диапазона (часы-минуты)
*/
TimePair timeSumm(TimePair &startTime, TimePair &duration)
{
TimePair result;
result.minute = startTime.minute + duration.minute;
result.hour = startTime.hour + duration.hour + (result.minute / 60);
result.minute = result.minute % 60;
return result;
}
/*********************************************************************************************/
/**
@brief calculateShedule пересчитать время начала и конца полива для заданной линии
@param idx номер линии
@param tStart время начала полива
@param tDuration длительность полива
*/
void calculateShedule(int8_t idx, TimePair &tStart, TimePair &tDuration)
{
shedule[idx].startTime = tStart;
shedule[idx].stopTime = timeSumm(tStart, tDuration);
shedule[idx].activated = false;
if (EEPROM.read(ADDR_LINE_1 + idx) != 0)
{
shedule[idx].isActive = true;
if (++idx < 4)
calculateShedule(idx, shedule[idx - 1].stopTime, tDuration);
} else
{
shedule[idx].isActive = false;
if (++idx < 4)
calculateShedule(idx, tStart, tDuration);
}
}
/*********************************************************************************************/
/**
@brief checkNextSecond проверка - наступила ли следующая секунда
@param ct структура времени
@return true, если наступила новая секунда
*/
bool checkNextSecond(tmElements_t &ct)
{
if (ct.Second != previousTime.Second)
return true;
return false;
}
/*********************************************************************************************/
/**
@brief checkNextMinute проверка - наступила ли следующая минута
@param ct структура времени
@return true, если наступила новая минута
*/
bool checkNextMinute(tmElements_t &ct)
{
if (ct.Minute != previousTime.Minute)
return true;
return false;
}
/*********************************************************************************************/
/**
@brief checkNextHour проверка - наступил ли следующий час
@param ct структура времени
@return true, если наступил новый час
*/
bool checkNextHour(tmElements_t &ct)
{
if (ct.Hour != previousTime.Hour)
return true;
return false;
}
/*********************************************************************************************/
/**
@brief motorOn включение или выключение насоса
@param ns true, если мотор необходимо включить, иначе false
void motorOn(bool ns)
{
if (ns)
{
if (motorIsActive)
return;
delay(1000);
digitalWrite(PIN_MOTOR, LOW);
} else
{
if (!motorIsActive)
return;
digitalWrite(PIN_MOTOR, HIGH);
delay(5000);
}
motorIsActive = ns;
}
/*********************************************************************************************/
/**
@brief relayOn включение или отключение линии (открытие/закрытие клапана)
@param idx номер линии
@param ns true, если клапан необходимо открыть, иначе false
*/
/*
void relayOn(int8_t idx, bool ns)
{
bool anotherLineActive = false;
shedule[idx].currentState = ns;
for (int8_t i = 0; i < 4; i++)
{
if (i == idx)
continue;
if ((shedule[i].currentState))
anotherLineActive = true;
}
if (!anotherLineActive) // если все линии отключены
{
if (ns) // и надо включить полив, то
{
#ifdef SERIAL_DEBUG
Serial.println("switch on relay, then power on motor");
#endif
digitalWrite(PIN_RELAY[idx], LOW); // включить реле,
motorOn(ns); // потом включить мотор
} else
{
#ifdef SERIAL_DEBUG
Serial.println("power off motor, then switch off relay");
#endif
motorOn(ns); // сначала выключить мотор,
digitalWrite(PIN_RELAY[idx], HIGH); // потом выключить реле
}
} else // а если хоть одна линия активна
{
if (ns) // и надо включить полив, то
{
#ifdef SERIAL_DEBUG
Serial.println("just switch on relay");
#endif
digitalWrite(PIN_RELAY[idx], LOW); // включить реле
} else
{
#ifdef SERIAL_DEBUG
Serial.println("just switch off relay");
#endif
digitalWrite(PIN_RELAY[idx], HIGH); // выключить реле
control[idx].isActive = false;
}
}
}
*/
/*********************************************************************************************/
/**
@brief checkButtonActions функция для проверки нажатий кнопок ручного включения полива
*/
/*
void checkButtonActions()
{
int8_t mask = 0;
MessageType mt;
TimePair thisTime, nextTime;
thisTime.hour = currentTime.Hour;
thisTime.minute = currentTime.Minute;
nextTime.hour = EEPROM.read(ADDR_BUTTON_HOUR);
nextTime.minute = EEPROM.read(ADDR_BUTTON_MINUTE);
// Serial.println("==================================================");
for (int8_t i = 0; i < 4; i++)
{
if (!control[i].starter->buttonPressed())
{
switch (control[i].starter->buttonClicked())
{
case 1: // короткое нажатие
if (control[i].isActive)
{
if (control[i].isUnlimited)
{
mt = MTControlOn;
control[i].isUnlimited = false;
control[i].stopTime = timeSumm(thisTime, nextTime);
mask |= (1 << i);
break;
}
mt = MTControlOff;
control[i].isActive = false;
relayOn(i, false);
mask |= (1 << i);
} else
{
mt = MTControlOn;
control[i].stopTime = timeSumm(thisTime, nextTime);
control[i].isActive = true;
relayOn(i, true);
mask |= (1 << i);
}
#ifdef SERIAL_DEBUG
Serial.print("button ");
Serial.print(i + 1);
Serial.println(" was pressed. Now ");
Serial.print(currentTime.Hour);
Serial.print(" : ");
Serial.print(currentTime.Minute);
Serial.print(" : ");
Serial.println(currentTime.Second);
#endif
break;
case 2: // длинное нажатие
if (control[i].isActive)
{
mt = MTUnlimited;
control[i].isUnlimited = false;
control[i].isActive = false;
relayOn(i, false);
mask |= (1 << i);
} else
{
mt = MTCompleted;
control[i].stopTime = timeSumm(thisTime, nextTime);
control[i].isUnlimited = true;
control[i].isActive = true;
relayOn(i, true);
mask |= (1 << i);
}
break;
default: // нажатия не было
break;
}
}
}
if (mask > 0)
showInfo(mt, mask);
}
*/
/*********************************************************************************************/
/**
/*********************************************************************************************/
/**
@brief checkControlledOperations проверка необходимости выполнения ручных действий
*/
void checkControlledOperations()
{
int8_t mask = 0;
MessageType mt;
int minuteCurrent = currentTime.Hour * 60 + currentTime.Minute;
int minuteControl;
for (int8_t i = 0; i < 4; i++)
{
if ((!control[i].isActive) || (control[i].isUnlimited))
continue;
minuteControl = control[i].stopTime.hour * 60 + control[i].stopTime.minute;
if (minuteCurrent > minuteControl)
{
if (shedule[i].activated)
{
control[i].stopTime.hour = shedule[i].stopTime.hour;
control[i].stopTime.minute = shedule[i].stopTime.minute;
} else
{
mt = MTControlOff;
control[i].isActive = false;
relayOn(i, false);
mask |= (1 << i);
}
}
}
if (mask > 0)
showInfo(mt, mask);
}
/*********************************************************************************************/
/**
@brief checkSheduledOperations проверка необходимости выполнения запланированных действий
*/
void checkSheduledOperations()
{
int8_t mask = 0;
MessageType mt;
for (int8_t i = 0; i < 4; i++)
{
if (!shedule[i].isActive)
continue;
if ((shedule[i].activated) && (shedule[i].stopTime.hour == currentTime.Hour) && (shedule[i].stopTime.minute == currentTime.Minute))
{
mt = MTSheduleOff;
shedule[i].activated = false;
relayOn(i, false);
// надо принудительно прекратить и полив линии, запущенный с кнопки, если он был активен
if (control[i].isActive)
{
control[i].isUnlimited = false;
control[i].isActive = false;
control[i].stopTime.hour = shedule[i].stopTime.hour;
control[i].stopTime.minute = shedule[i].stopTime.minute;
}
mask |= (1 << i);
}
if ((!shedule[i].activated) && (shedule[i].startTime.hour == currentTime.Hour) && (shedule[i].startTime.minute == currentTime.Minute))
{
mt = MTSheduleOn;
relayOn(i, true);
shedule[i].activated = true;
mask |= (1 << i);
}
}
if (mask > 0)
showInfo(mt, mask);
}
/*********************************************************************************************/
/**
@brief denyByFlowmeter проверка данных с расходомера и аварийное отключение мотора в случае необходимости
*/
void denyByFlowmeter()
{
bool activeLines = false;
for (int8_t i = 0; i < 4; i++)
{
if (shedule[i].activated)
activeLines = true;
if (control[i].isActive)
activeLines = true;
}
if (!activeLines)
motorOn(false);
}
/*********************************************************************************************/
/**
@brief loop рабочий цикл поливатора
*/
void loop()
{
RTC.read(currentTime);
if (checkJoystickActions())
updateMenu(currentMenu);
checkButtonActions();
if (checkNextSecond(currentTime))
{
cli(); // отключим обработку прерываний для корректной работы со счетчиком
currentFlowRate = ((waterCounter - waterCountPrevious) * 60 / 5.5);
currentConsumption += (float(currentFlowRate) / 60.);
waterCountPrevious = waterCounter;
sei(); // снова включим обработку прерываний через минимально короткое время
if ((currentMenu == 10) && (displayIsActive))
updateMenu(currentMenu);
if (checkNextMinute(currentTime))
{
if (checkNextHour(currentTime))
{
if (currentTime.Hour == 0) // если полночь, то перегрузиться
softReset();
}
checkControlledOperations();
checkSheduledOperations();
if ((currentMenu == 1) && (!menuIsSelected) && (forcedUpdate == 0) && (displayIsActive))
updateMenu(currentMenu);
if (currentTime.Minute % 10 == 0)
getSoilHumidity();
}
if ((currentMenu == 1) && (!menuIsSelected) && (forcedUpdate == 0) && (displayIsActive))
{
lcd.setCursor(7, 1);
if ((currentTime.Second % 2) > 0)
lcd.print(": ");
else
lcd.print(" :");
}
if (forcedUpdate > 0)
{
if (--forcedUpdate == 0)
updateMenu(currentMenu);
}
if (displayIsActive && (lastAction > 0) && (currentMenu != 1))
{
if (--lastAction == 0)
activateLight(false);
}
}
previousTime = currentTime;
}

Фига се "нуб". Я с контроллерами уже лет 20-ть знаком, а таких больших программ так и не написал (ну разве что на ассемблере, когда то, давно). И это за две недели то.
Вы ни разу не слыхали, что слона кушают маленькими кусочками. И чем заниматься селекцией непородистых кодов, проще научиться выводить свои. Вот и начните с кнопочек и экранчика. Если договоритесь с 234, он покажет как найти ссылку на "титановый лисапед" для кнопок.
Можно использовать виртуальную машину отладки, собрать код там, загрузить в Proteus (создав схему) и отладить самостоятельно.
Вот пример как это могло бы выглядеть:
Можно использовать виртуальную машину отладки.
теперь к неработающим кускам кода у него будет неработающая виртуальная машина
дай ему ссылку на виртуального юзера.
Мужики как убрать бегане символов по экрану подсажите плиз . Вот таким скейчем удалось запустить DS 1302 и вывести на lcd16*2