Gроект для аквариума (акваконтроллер для led, temp, time, co2)

Thorn
Offline
Зарегистрирован: 07.11.2014

Вот именно так работает Ваш ВАРИАНТ: 

byte key(){
  
  if (irrecv.decode(&results))
  {irrecv.resume();}
  val=results.value;
  results.value=0;
  Serial.println(val,HEX);
  if (val == 0x41BEB04F) return 5;
  if (val == 0x41BE609F) return 4;
  if (val == 0x41BED02F) return 3;
  if (val == 0x41BE40BF) return 2;
  if (val == 0x41BEC03F) return 1;
  else return 0; 
}

Идут "0" если ничего или кнопки нажатые.

другой Ваш варант, который мой :) :

byte key(){
  if (irrecv.decode(&results)){
//  if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
//      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
//  }   
  irrecv.resume();
  Serial.println(results.value,HEX);
  if (results.value == 0x41BEB04F)  return 5;
  if (results.value == 0x41BE609F)  return 4;
  if (results.value == 0x41BED02F)  return 3;
  if (results.value == 0x41BE40BF)  return 2;
  if (results.value == 0x41BEC03F)  return 1;
  else  return 0; 
}
}

НИЧЕГО невозвращает если НИЧЕГО ненажато, тоетсь или КНОПКИ (если нажаты, без удержания что классно) или НИЧЕГО если ненажаты.

bwn
Offline
Зарегистрирован: 25.08.2014

Это вы контролируете в 07 строке функции key() второй вариант или уже в меню?

Thorn
Offline
Зарегистрирован: 07.11.2014

В одном случае ПАРАМЕТРЫ  неизменяются, но кнопка срабатывает:

byte key(){
  if (irrecv.decode(&results)){
//  if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
//      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
//  }   
  irrecv.resume();
  Serial.println(results.value,HEX);
  if (results.value == 0x41BEB04F)  return 5;
  if (results.value == 0x41BE609F)  return 4;
  if (results.value == 0x41BED02F)  return 3;
  if (results.value == 0x41BE40BF)  return 2;
  if (results.value == 0x41BEC03F)  return 1;
  else  return 0; 
}
}

И в другом случае, когда нажатие кнопки сопровождается её "удержанием" которого и нет:

byte key(){
  if (irrecv.decode(&results))
//  if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
//      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
//  }   
  irrecv.resume();
  Serial.println(results.value,HEX);
  if (results.value == 0x41BEB04F)  return 5;
  if (results.value == 0x41BE609F)  return 4;
  if (results.value == 0x41BED02F)  return 3;
  if (results.value == 0x41BE40BF)  return 2;
  if (results.value == 0x41BEC03F)  return 1;
  else  return 0; 
}

Отличие лиш в скобках, в пределах которых цикл - может в этом собака порылась...

Thorn
Offline
Зарегистрирован: 07.11.2014

bwn пишет:

Это вы контролируете в 07 строке функции key() второй вариант или уже в меню?

Это в ЛЮБОМ месте и в меню и при выборе параметров и в окне"cikl" - в моем случае чаиски с календарём.

bwn
Offline
Зарегистрирован: 25.08.2014

А если что то вроде этого?



 byte key(){
  if (irrecv.decode(&results)){
  if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
  }   
  irrecv.resume();}
  Serial.println(val,HEX);
  if (val == 0x41BEB04F)  return 5;
  if (val == 0x41BE609F)  return 4;
  if (val == 0x41BED02F)  return 3;
  if (val == 0x41BE40BF)  return 2;
  if (val == 0x41BEC03F)  return 1;
  else  return 0; 
}

Чет больше ничего в голову не приходит(((

 

Thorn
Offline
Зарегистрирован: 07.11.2014

Ох, спасиб что ещё участвуете..  Я это все перепробовал, в различных комбинациях. Результат один Всё работает но "зажатая" кнопка сами понимаете делает что хочет, или всё нажимается чётко и без задержек и "самовзводов" но ПАРАМЕТР неизменяется. Я уже полез снова в void setOnOff() добавлять работу с ирдой (хотя кнопки то НАЖИМАЮТСЯ и есть регистрация нажатий в serial.print - один чёрт. Эх ведь почти всё идеально уже делай только стрчек побольше и количесвто экранов меняй и готовое меню. Так нет жеж. 

bwn
Offline
Зарегистрирован: 25.08.2014

Вы знаете, где то у нас не там собака роется. По логике работы IR - нажали кнопку, получили ее код в переменную, продолжаем нажимать - в переменной ffffffff, как у нас может оказатся в переменной код уже отпущенной кнопки, если перед этим был ffffffff и она запоминает последний?

Попробуйте чисто голый IR помониторить, что все таки получается - при быстром нажатии, долгом, отпущенной, что дала в момент отпускания.

Thorn
Offline
Зарегистрирован: 07.11.2014

bwn пишет:

Так я и не могу понять почему не так. Сейчас выходит: если клавиша нажата, key() возвращает что то от 1 до 5. Если отпустили, идет 0. То есть полностью соответствует мех.клавишам. При этом первый экран меню работает корректно или нет?

Извиняюсь, дети.школа.спорт.

Всё правильно полагаете, варианта два. В одном, который вы тольок что обрисовали так и есть, НО ежели нажать setup (она же key1) то выводится строка типа "set Timer-1 >>". Стоит только попробовать жмакнуть down то значения будут менятся сами от 1. set Timer-1 >> до 5. set menu5.

Если успеваеш жмакнуть на примере 2. set Timer-2 >> к примеру то попадаеш в меню настройки параметров, и снова кнопки up или down меняют значения самостоятельно в само последней позиции а это 

else if (pos == 12 && KEY == 3) setMinClockOff++; или else if (pos == 12 && KEY == 4) setMinClockOff--; в зависимости up иди down нажали. Тоетсь всё работает но нам ненужно "удержание".

И второй вариант когда ничего не "удерживается" но "0" не возвращается и возвращается тольок когда кнопка нажата. НО в этом случае параметра НЕИЗМЕНЯЮТСЯ уже.

Thorn
Offline
Зарегистрирован: 07.11.2014

bwn пишет:

Вы знаете, где то у нас не там собака роется. По логике работы IR - нажали кнопку, получили ее код в переменную, продолжаем нажимать - в переменной ffffffff, как у нас может оказатся в переменной код уже отпущенной кнопки, если перед этим был ffffffff и она запоминает последний?

Попробуйте чисто голый IR помониторить, что все таки получается - при быстром нажатии, долгом, отпущенной, что дала в момент отпускания.

Код что с куском:

 if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
    val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
  }  

что без него, работает одинаково. У нас "удержание" чисто какоето циклическое получается, понимаете, ненужное... Может свободная минутка и в аське спишемся - а там Aadmin или TeamViewer ... вы чутку посмотрите serial :) 

Thorn
Offline
Зарегистрирован: 07.11.2014

bwn пишет:

Так как насчет первого экрана, где выбираем таймер?

Отвечал быстрее чем читал, первый экран это сводная информация с часиками, температурками, положениями релюшек, давлением и прочими хитростями, хотя может и избыточно нуда ничего. Может потмо найду как сделать два экрана и листать меду ними (left\right) тогда можно будет на одном таймерные показатели на другом температурные. Это уже в процеесе - я жёско пока в рамках 30кб особо неразгуляешся с моим кодо, хот я и пытался реже пользоваться переменными типа byte и int а использовал #define.

bwn
Offline
Зарегистрирован: 25.08.2014

Да у меня аськи и нет, тогда уж mail агент. Не могу придумать, откуда датчик выдрать, чтобы помониторить. А код львиную долю у вас сожрали библиотеки и выводы на экран. Переменные кушают оперативку. Экранные выводы оптимизировать надо будет и все поместится.

Я если не найду IR-а, набросаю скетчики и посмотрим, что они в сериал будут кидать.

Thorn
Offline
Зарегистрирован: 07.11.2014

Тут очень странная ситуация, кнопки то нажимаются и в serial выводятся, а значит IR прекрасно обрабатывается. И при изменении параметров в меню кнопки ТОЖЕ работают и в serial тоже выводятся их нажатия - но неменяются значнеия (в нашем случае времени), ну заколдованный круг. Ране все просто, кнопки просто НЕОБРАБАТЫВАЛИСЬ при работе с параметрами. С void menu() - работают и изменяют значения. С void setOnOff() - работают только на вход в функцию и на сохранение с выходом (поменять время нельзя) и повторюсь кнопки выводятся в seial.print 

Почему же код один а так по разному скобки заставляют работать в комментарии #203

Thorn
Offline
Зарегистрирован: 07.11.2014

И вот. после применения настроек кнопкой setup выводится сообщение 

tft.print("Saved");
delay(1000);

и мы попадаем в предыдущее меню с выбором char menuTxt[5][15] = {"set Timer-1 >>", "set Timer-2 >>","set menu-3 >>","set menu-4 >>","set menu-5 >>"}; 

Где определено что мы туда попдаем, почему именно в ту char menuTxt в которой только что настраивали параметры, это удобно вдруг перепутал что то или нужно настроить след. пункт а на начинать с самого начала с win==0.

bwn
Offline
Зарегистрирован: 25.08.2014

Доброе утро. Начну с последнего вопроса - когда мы уходим в функцию (в данном случае setOnOff() ), в регистрах сохраняется вся информация о месте и состоянии выхода и возврат осуществляется в тоже место откуда ушли.

А по посту212 поподробнее - в void menu() эффект "залипания" сохраняется? Если да, значит ничего у нас не работает(((. По setOnOFF() я особо и не заморачивался, там скорее всего подвело отсутствие blink. Проверить просто - в код из 182поста (где с часами и прочим) в 282 строке вставьте Serial.println(pos,DEC); работать будет только если 3,6,9,12 все иные false. Эта переменная используется и на печать и для проверки условия. Пока экраны модифицировали, реально могли изменить логику.

Thorn
Offline
Зарегистрирован: 07.11.2014

Доброго утречка.

Да "залипание" сохраняется когда у нас работа с IR - без {}, вот я тут описывал #203  когда есть залипание а когда нет, код одинаковый при этом. Ну я бы несказал что Неработают кнопки, они и нажимаются и меняют значения времени.

В случае, когда нет "залипания" и кнопки работают ДО изменения - я преполагал что может и blink тут как то замешан, но в других то случаях он кроме "невидимости" себя никак на изменениях времени непроявлял. Да неудобно наугад перемещаться с часов и минут включения на минуты и часы включения но значения хотябы менялись. ПО поводу Serial.println(pos,DEC); счас влуплю помониторю...

Thorn
Offline
Зарегистрирован: 07.11.2014

В 282 строке вставьте Serial.println(pos,DEC); нельзя вставить синтаксис... вставил в после 295. И что вы думаете.... С кодом кнопок:

void keyM(){
  byte KEY=key();            //***ВХОД В МЕНЮ и проверка блокировки от возврата. Принимаем код клавиши
  if (KEY==1 && temp[0]<millis()) {
        temp[0]=millis()+2000;
        menu();}            //Если setup идем в меню
  tft.setCursor(90,150);
  tft.setTextSize(1);
  tft.setTextColor(ST7735_RED, ST7735_BLACK);
  tft.print("cikl"); 
}
byte key(){
  if (irrecv.decode(&results)){
if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
} 
  irrecv.resume();
  val=results.value; 
  Serial.println(val,HEX);
  if (val == 0x41BEB04F) return 5;
  if (val == 0x41BE609F) return 4;
  if (val == 0x41BED02F) return 3;
  if (val == 0x41BE40BF) return 2;
  if (val == 0x41BEC03F) return 1;
  else return 0;
  }
}

при вхождении в void setOnOff() сразу начинают возвращаться "0", если нажимаем кнопку - возвращается её HEX (это видимо отсюда Serial.println(val,HEX);) и всё, значения НЕМЕНЯЮТСя а кнопки то НАЖИМАЮТСЯ...

Thorn
Offline
Зарегистрирован: 07.11.2014

А вот с кодом кнопок :

void keyM(){
  byte KEY=key();            //***ВХОД В МЕНЮ и проверка блокировки от возврата. Принимаем код клавиши
  if (KEY==1 && temp[0]<millis()) {
        temp[0]=millis()+2000;
        menu();}            //Если setup идем в меню
  tft.setCursor(90,150);
  tft.setTextSize(1);
  tft.setTextColor(ST7735_RED, ST7735_BLACK);
  tft.print("cikl"); 
}
byte key(){
  if (irrecv.decode(&results))
if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
} 
  irrecv.resume();
  val=results.value; 
  Serial.println(val,HEX);
  if (val == 0x41BEB04F) return 5;
  if (val == 0x41BE609F) return 4;
  if (val == 0x41BED02F) return 3;
  if (val == 0x41BE40BF) return 2;
  if (val == 0x41BEC03F) return 1;
  else return 0;
 }

тоесть без {} и Serial.println(pos,DEC); после 295 при входе в void setOnOff() мы получаем "зажатую, залипшую" кнопу right, соответсвенно курсор двигается попорядку в pos == 3 далее pos == 6 далее pos == 9далее  pos == 12 там и остаётся при этом идет возврат кнопки right . Далее любое нажатие кнопок up иди down бесконечно изменяет параметр pos == 12

Вот, вроде все подробно ОПИСАЛ

Thorn
Offline
Зарегистрирован: 07.11.2014

Код из поста #216. Вставил Serial.println(pos,DEC);  в void menu(); в самый конец после break;}.

Итак кнопки ненажатые НЕВОЗВРАЩАЮТСЯ - это ГУД. Нажимаем setup (она же KEY5) сразу возвращается нажатая кнопка в HEX и позиция poc.DEC = 0? всё отлично, далее листаем вниз при этом меняется и позиция от 0 до 4 , всё пока РПАВИЛЬНО как и ожидалось. Выбрали нужную позицию и входим в void setOnOff() - начинаются возвращатсья 0 и нажатые кнопки (если нажимаю, но толку нет, ничегоже неменяют). Значит чт ото нужно добавить по кнопкам именно в void setOnOff()

bwn
Offline
Зарегистрирован: 25.08.2014

в setOnOff() , если pos=0, ничего и не должно менятся. только 3,6,9,12. Это потом подправим. Я вроде IR подключил, сейчас попробую что выдает.

Thorn
Offline
Зарегистрирован: 07.11.2014

Вот, на самом деле blink в своем tft тестово я могу заменить просто другим цветом и фоном однако это недаст ПЕРЕРИСОВКИ как в Lcd но оно то и ненадо, этоже дисплейчки чтоб место уменьшить таская с собой макет. Это я к предмету, что без blink - нет изменения параметра.

bwn
Offline
Зарегистрирован: 25.08.2014

Короче, что один человек построил, другой завсегда сломать сможет. Бубен, хрустальный шар, змеиная шкура нам в помощь.

Победил я эту дрянь. Теперь работает. Почему работает, не спрашивайте, это очень сильное колдунство))). Короче если обнулять "val" сразу после обращение к key() все идет корректно, если дальше или перед обращением снова начинается бубен(((.

Получилось так. При установке времени первая цифра теперь показывает, что устанавливаем от 1 до 4. Где эти грабли вынырнут снова, предугадать пока сложно, так что имейте их в виду.

#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#include <IRremote.h>

#define RECV_PIN 3                          //IR-приемник
uint32_t val; 
IRrecv irrecv(RECV_PIN);
decode_results results;


LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//_____________________________________________________________
//  Переменные для меню
byte setMinClockOn; 
byte setHorClockOn;
byte setMinClockOff;  
byte setHorClockOff;
byte win;              //Номер экрана меню
unsigned long temp[2]; //Массив для задержек меню
//_____________________________________________________________

void setup()
{
  Wire.begin(); 
  lcd.begin(16, 2);
  lcd.clear();
  Serial.begin(9600);
  irrecv.enableIRIn(); 
}

//______________________________________________________________

 byte key(){
  if (irrecv.decode(&results)){
  //if (results.value != 0xFFFFFFFF)
  //{ //Если пришел FF, соответственно пропускаем.
      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
  //} 
    irrecv.resume();}
  Serial.println(val,HEX);
  Serial.println(" ");
  if (val == 0x8D47834C) return 5; //Right
  if (val == 0xFA3301F0) return 4; //Down
  if (val == 0xE52E70B2) return 3; //Up
  if (val == 0x14FD8504) return 2; //Left
  if (val == 0x520C78C2) return 1;
  else  return 0;
}

//_____________________________________________________________

void setOnOff(){    
  byte pos = 0;   
     
      //Считываем записанные значения таймеров.
      //Адрес определяется как номер таймера*4+четыре ячейки.
  setMinClockOn = EEPROM.read(win*4+0); //
  setHorClockOn = EEPROM.read(win*4+1);
  setMinClockOff = EEPROM.read(win*4+2);
  setHorClockOff = EEPROM.read(win*4+3);
    
    lcd.clear();
    lcd.blink();

    while(key() != 1) //Крутим цикл пока не будет Setup
    {   
    byte KEY = key(); // читаем состояние кнопок
    val=0;
      delay(200); //Если убрать, не работает подсветка уст.позиции
    lcd.setCursor(1, 1);
    lcd.print("set to save");
    lcd.setCursor(0, 0);
    lcd.print(pos/3,DEC);    //Печатаем номер прогр.таймера.
    
    lcd.setCursor(2, 0);     // выводим инфу
     if (setHorClockOn < 10) lcd.print("0");
    lcd.print(setHorClockOn,DEC);
    lcd.print(":");
     if (setMinClockOn < 10) lcd.print("0"); 
    lcd.print(setMinClockOn,DEC);  
    lcd.print(" ");     
     if (setHorClockOff < 10) lcd.print("0");
    lcd.print(setHorClockOff,DEC);
    lcd.print(":");
     if (setMinClockOff < 10) lcd.print("0");
    lcd.print(setMinClockOff,DEC); 
    
    lcd.setCursor(pos, 0); // устанавливаем курсор согласно позиции
    
    if (pos<3) pos=3;
    if (KEY == 5 && pos < 11) pos += 3; // крутим позицию право-лево
    else if (KEY == 2 && pos > 3) pos -= 3;
    
    else if (pos == 3 && KEY == 3) setHorClockOn++; // крутим значения
    else if (pos == 3 && KEY == 4) setHorClockOn--;
    else if (pos == 6 && KEY == 3) setMinClockOn++;
    else if (pos == 6 && KEY == 4) setMinClockOn--;    
    else if (pos == 9 && KEY == 3) setHorClockOff++;
    else if (pos == 9 && KEY == 4) setHorClockOff--;    
    else if (pos == 12 && KEY == 3) setMinClockOff++;
    else if (pos == 12 && KEY == 4) setMinClockOff--;    
 
    
    if (setHorClockOn > 23) setHorClockOn = 0;  //Ограничиваем значения
    else if (setMinClockOn > 59) setMinClockOn = 0;
    else if (setHorClockOff > 23) setHorClockOff = 0;
    else if (setMinClockOff > 59) setMinClockOff = 0;
    
  }// конец цикла
   lcd.noBlink(); 
   lcd.clear();

   EEPROM.write(win*4+0, setMinClockOn); //Записываем значения
   EEPROM.write(win*4+1, setHorClockOn);
   EEPROM.write(win*4+2, setMinClockOff);
   EEPROM.write(win*4+3, setHorClockOff);

   lcd.print("     Saved");
   delay(1500);
}///

//_________________________________________________________________
void menu()
{
  //tft.fillScreen(ST7735_BLACK); // Clear screen
  //tft.setCursor(0,5);
     //tft.setTextColor(ST7735_WHITE,ST7735_BLACK);
     //tft.setTextSize(1);
  
    //Массив с наименованиями для экрана
  char menuTxt[5][14] = {"set menu1 >>", "set menu2 >>","set menu3 >>","set menu4 >>","set menu5 >>"};
  byte pos = 0;
  byte KEY;
  
  while(1) //Бесконечный цикл
  {   
      KEY=0;
      if (temp[1]<millis()) KEY = key(); //Если прошел интервал, читаем код
      val=0;     
     //tft.setCursor(0,5);
     //tft.setTextColor(ST7735_WHITE,ST7735_BLACK);
     //tft.setTextSize(1);
    lcd.setCursor(0,0);
    lcd.print(pos+1);  //Печатаем номер.
    lcd.print(".");
    lcd.print(menuTxt[pos]); //Печатаем название
    
    // Уменьшить увеличить + блокировка кнопки
    if (KEY == 3 && pos > 0) {pos--; temp[1]=millis()+400;}
    if (KEY == 4 && pos < 4) {pos++; temp[1]=millis()+400;}
    
    win=pos; //Переменная номера таймера
    
    // Здесь переходим на подменю. Если останется как сейчас, то достаточно
    // if (KEY == 5) setOnOff();
    // Сейчас можно перейти на разные подменю.
    if (KEY == 5 && pos == 0) setOnOff();
    if (KEY == 5 && pos == 1) setOnOff();
    if (KEY == 5 && pos == 2) setOnOff();
    if (KEY == 5 && pos == 3) setOnOff();
    if (KEY == 5 && pos == 4) setOnOff();
    //Выход из меню с проверкой и установкой блокировки от возврата
    if (KEY ==1 && temp[0]<millis())        
       { 
         temp[0]=millis()+2000;
         lcd.clear(); 
         break;} 
  }
} 

//_________________________________________________________________

void loop()
{
  lcd.clear();
  //____________________________________________________________
  
   //***ВХОД В МЕНЮ и проверка блокировки от возврата.
  byte KEY=key();            //Принимаем код клавиши
  val=0;
  //Serial.println(KEY,DEC);
  if (KEY==1 && temp[0]<millis()) 
      {
        temp[0]=millis()+2000;
        menu();
      }                       //Если setup идем в меню
  //____________________________________________________________
  
  lcd.setCursor(10,1);
  lcd.print("cikl");
}

Да, пин на IR-ку поставте правильный, я менял на свой свободный.

Thorn
Offline
Зарегистрирован: 07.11.2014

Ну что скахать, чтобы несказать - ничего несказать. Смею заверить что эта КНОНСТРУКЦИЯ работает и работает ЧЁТКО.

Блин заметил особенность, пока она непарит. Когда вошли в void setOnOff() то кнопки (left | right) изменяют номер нашего таймера, может эт связано с заменой:

tft.print(win+1,DEC); //Печатаем номер прогр.таймера.

на

 tft.print(pos/3,DEC); //Печатаем номер прогр.таймера.  

Это как вы и писали ...... про грабли: "Получилось так. При установке времени первая цифра теперь показывает, что устанавливаем от 1 до 4. Где эти грабли вынырнут снова, предугадать пока сложно, так что имейте их в виду."

(это срока 75 вашего кода). Далее всё работает отлично(хотя и ненаглядно без blink на TFT) потом сохранение, сохраняется нужная нам позиция несмотря на  указанную выше неточность. Потом вывод "saved" в полторы сек и возврат сначала в строку меню, которую только что изменяли небольшая задержка и снова возврат в окно с "cikl".

Признаться в посте я спрашивал про то и первый вариант был луче, можно было невыходя поправить оставшиеся 4 строки меню. Ну это ВСЁ ceoobt мелочи. Чичас попробую на нагрузку применить меню а потмо и в код.

Спасибо ОГРОМНОЕ за проделанную работу, я так понял обнуление  это val=0;

Thorn
Offline
Зарегистрирован: 07.11.2014

Вернул в зад и "грабли исчезли" - я про tft.print(win+1,DEC); в строке 75 вашго код (да что уж, тут почти весь код ваш :) ).

Счас покурю возврат не в главное окошко а в настраиваемое после save

bwn
Offline
Зарегистрирован: 25.08.2014

Я наверно неясно выразился, цифирка и должна была менятся. она blink вам заменяет. экран времени  00-00  00-00  1-2-3-4.

А грабли это непонятное поведение IR в функции key(). Я логически так и не могу объяснить почему это происходит, а значит можно ждать сюрпризов. А автовыхода из первого экрана меню быть не должно, значит задержка маловата, после 122 строки поставьте еще эту  temp[0]=millis()+2000;

 

Thorn
Offline
Зарегистрирован: 07.11.2014

Блин, а я непойму - чего это менятся стал номер экрана.. А оно воно как. Я его сейчас в продолжении сделаю к номеру, о как. И наглядно и непутает.

Вот Подскажите вот unsigned long temp[2]; //Массив для задержек меню. Это как я понимаю "безразмерная" переменаая как для любой переменной работы со ВРЕМЕНЕМ, а вот [2] это количество значений которое внутри может приниматься или равняться им, если в коде будет указано?

 

bwn
Offline
Зарегистрирован: 25.08.2014

long - четырехбайтовый целочисленный тип переменной (очень даже размерной) принимающей как отрицательные, так и положителные значения. unsigned long - четырехбайтовый целочисленный тип переменной принимающей только положительные значения, фактически в два раза больше чем просто long. 

float - можно считать безразмерным (что то в 38 степени), но с ним долгая математика и плавающая точность результата.

temp[x] - массив состоящий из х-ячеек. Каждую ячейку можем рассматривать как отдельную переменную. Условно temp0, temp1, temp2...temp(x-1). Массив считается с 0, поэтому задекларированный как temp[2] содержит две ячейки и при обращении будет вызыватся temp[0], temp[1].

Если будете с "pos" играться, смотрите чтобы она значение не изменила, а то опять регулироватся перестанет.

bwn
Offline
Зарегистрирован: 25.08.2014

Кстати по long, везде в своем коде где с ним сделаны задержки с millis(), исправте на unsigned long, иначе через двадцать суток непрерывной работы они примут отрицательное значение и вас ждет большой сюрприз)))). Сам недавно заметил((((.

Thorn
Offline
Зарегистрирован: 07.11.2014

по long понял, поправлю. Вставил всё в свой рабочий код, тот что в аквариуме работает. Первым делом размер - на TFT ровно 32 718 байт, что ОЧЕНЬ и ОЧЕНЬ шикарно даже с моим подходом к переменным и ихобъявлению явно (до void setup() ) Я та понял наши кнопки объявлены внутри byte KEY =key(); - неявно. Ладно попродяку, так вот размер можно считать верным для Nano за минусом 7 кб (разница между lcd и TFT) + мелочь по датчикам, на тестовом больше :) для пробы. Ага далее...

Везде поменял свои старые кнопки типа:

void auto_key_dimmer(){                        //Считываем значение кнопки. 
      if (val == KEY4 & flagDimm == 0){        //Если нажата кнопка УВЕЛИЧЕНИЯ диммирования

на функцию + byte KEY:

void auto_key_dimmer(){  //Считываем значение кнопки. 
  byte KEY=keyIr();     
      if (KEY == 8 & flagDimm == 0){        //Если нажата кнопка УВЕЛИЧЕНИЯ диммирования

а также кнопки ручного вкл\откл релюшек ЛЕДов: с if (results.value == KEY2 && !digitalRead(OUT1) !=0 && analogData2 >= 1) digitalWrite(OUT2, !digitalRead(OUT2)); на  if (KEY == 2 && !digitalRead(OUT1) !=0 && analogData2 >= 1) digitalWrite(OUT2, !digitalRead(OUT2));

само собой предварительно и в эту функцию добавил byte KEY - вышло СУПЕРСКИ и нетак размыто и длиииино как ранее. Тепреь имеем практически готовый продукт (дома попробую всё это хозяйство на Nane и Lcd) и всё, прощай загрузка времени по USB )

Значит теперь определюсь с количесвом окон win и количеством har menuTxt[5][17] = {"set Timer 1 >>", "set Timer 2 >>","set Timer CO2 >>",

есчо у меня будут вопросики, непротив? Нетакие, которые из RTFM можно почерпнуть а больше из практики :)

Thorn
Offline
Зарегистрирован: 07.11.2014

Автовыход, работает если ..... после 122 строки поставьте еще эту temp[0]=millis()+2000; при millis()+600; тоесть не более полсекунды при temp[0]=millis()+2000; при millis()+700; и более (не говоря по 2000) просто висит БЕСКОНЕЧНО долго, небеда и полсекунды пока хватит.

И да, горько осознавать но все что печатается в void setup() ну всякая статика, после выхода из меню недоступна (оно и понятно), вводится только динамическая инфа, что или постоянно перерисовывается (чего старался избежать даже в часах, там печать каждую секунду) или согласно задержек millis. Кстати ваш вариант вида

 millis()+2000; к примеру

мне больше нравится чем мой вида:

unsigned long currentMillis = millis();
if(currentMillis - prvMlsTemp > tempIntv){     //Проверяем интервал для обновления 
prvMlsTemp = currentMillis;
//печать//
}

потому как в начале каждой функции приходится указывать unsigned long currentMillis = millis(); а это стопудово при большом количестве занимает процессорные ресурсы, ежели к примеру брать один раз миллисекунды и к ним добавлять сколько бы то нибыло, верно? Вот так верно будет описать по вашему вывод времени:

if (temp[1]=millis()+1000);
tft.print(hours);

возможно придется задать дополнительную переменную для задержки, чтобы temp незанимать.

bwn
Offline
Зарегистрирован: 25.08.2014

Что ж, рад, что не зря мучались. Теперь треба улучшать и уменьшать. Первый и основной путь - отображения экранов свести к функциям. Как - определяем какие у нас есть экраны. смотрим на каких отображается подобная информация (по знакоместам), если таких 2 и больше можно рисовать функцию. В функции на экранах есть статичная информация (фразы, наименования) и динамичная (время, температуры и пр.). Статичную можем отображать как lcd.print(text[i]) передавая нужный [i] в функцию, динамичную отображаем как lcd.print(h,DEC) h опять таки передаем в функцию. В итоге для вызова экрана надо будет сделать вроде i=5; h=dallTemp; printText1(); . Скетч начинает уменьшатся на глазах.

Меню  вложенность можно по образу и подобию до бесконечности наращивать. Оно меня этим и подкупило.

А вопросы будут, задавайте.

bwn
Offline
Зарегистрирован: 25.08.2014

Thorn пишет:

Автовыход, работает если ..... после 122 строки поставьте еще эту temp[0]=millis()+2000; при millis()+600; тоесть не более полсекунды при temp[0]=millis()+2000; при millis()+700; и более (не говоря по 2000) просто висит БЕСКОНЕЧНО долго, небеда и полсекунды пока хватит.

Автовыхода быть в данном случае не должно (он происходил, т.к. не успевало стерется значение setup и соответсвенно выход из цикла), я эту строку вставлял для блокировки клавиши setup на две секунды, а дальше выход по ее повторному нажатию. Для автовыхода надо добавлять логику. Если у вас выскакивает на loop само (при 600 скорее всего еще успевает выйти), значит что то идет не так.

Мне кстати еще больше нравится  millis()+temp[3]; где temp[3]=2000; тогда этот temp[3] можно использовать в любом месте где нужно 2000. А если редактировать задержки, достаточно только изменить temp[3] при инициации. Такой массив можете задать при инициации до void setup,  long temp[]={100,200,500,2000,30000}; и иметь на бумажке 0-100, 1-200, 2-500, 3-2000, 4-30000.

возможно придется задать дополнительную переменную для задержки, чтобы temp незанимать.

если знаете, что она ни на что не повлияет, то почему бы и не занять. Либо инициировать long temp[3] и у вас появилась еще одна переменная temp[2] (главное всегда помнить, что при обращении максимальный индекс массива всегда на 1 меньше чем объявленный т.к. начинается с 0. Если обратитесь к большему номеру, компилятор кушает молча, а результат бывает веселый).

Thorn
Offline
Зарегистрирован: 07.11.2014

Доброе утро. Постараюсь задавать вопросы по минимуму и пореже, сперва решил избавиться от всяких расчётов и сравнений миллисекун текущих с заданными иесли интервал сравнялся то что то делаем...

Для начала создал "простынку" вида: const long zadTime[]={100,750,1000,3000,10000,60000}; ("0" -для вывода значений диммирования, нужно побыстрее для динамики вывода, а дальше до 1 минуты)

Попытался неочнеь "красиво": if (zadTime[1]=millis()+1000); { - получаем ругань в синтаксисе но оно и правильно, простынка заданя (несмотря на то чт опеременная, значения ПОСТОЯННЫЕ, поменять их нельзя а я пытаюсь присвоить первому значению (оно ранво 750мс) значение миллисекунд  плюс 1 сек., вобще нельзя так.

Попробовал совсем просто: if (millis())+zadTime[3]; {  - сами понимаете раз пишу.. что ничево невышло. 

Как понял нужно сначала получить переменную, полученную сложением миллисек + zadTime[номер] и если усллвие выполенно т опечатать чт-либо. Подскажите (мелким примером) как описать, может добавить какую временную metadata для этого.

ПО экранчикам. Решил точно. будет два+менюшный, но так как он выводится когда основной цикл стоит (с одной стороны быстрее работать будет и надёжнее, чем мой самы первый на if-ах :), с другой стороны автовозврат НУЖЕН обязательно чтоб независнуть в нем.

На первом динамичная инфа, ну в рамках :). Часики с календариком, температурЫ, давление, влажность, напряжениЯ.

На втором более-менее статичные показатели, это положения релюшек и диммеров, время следующих таймеров, которые в скором времени будут выполнены. Скорее всего сделаю их меняющимися, первый более продолжительный, будет вообще супер смотреться, динамика придает живости ардуинке. Сегодня буду курить всё свободное время функцию вывода на экранчики (экранчик) после разбора полётов с millis.

Потом попробую добавить в меню не временные значения on\off для реле а температурные (помню что работать можно будет лиш с целыми числами, без запятой дабы неразбивать а потом несобирать значения.

Вот... добавлю "пищалку" на принятие значений и на "avilable" (доступнсть) для RTC, ds18b20 .... вобщем на всё что управляет впоследствии реле и нагрузкой

bwn
Offline
Зарегистрирован: 25.08.2014

По миллисам, вы скорее всего просто проморгали, там используется переменная и константа - temp[0]=millis()+zadTime[2]; if (millis()<temp[0]) - это на 1 секунду по вашему посту.

Для автовыхода, надо чтобы каждое нажатие кнопки увеличивало какую-то переменную на 10-20 секунд. Для примера возьмем код из 221 поста, тогда в строках после 71 и 141 и 182: if (KEY>0) {здесь увеличить}. А после 110 и 169: if (перем<millis()) {break;} ну еще может что то для экрана добавить (очистить и т.п.)

Когда будете переделывать температуры, не используйте ==, могут быть косяки.

 

Thorn
Offline
Зарегистрирован: 07.11.2014

У нас объявлено: unsigned long temp[2];

в коде имеется: if (temp[1]<millis()) KEY = keyIr(); - чему равно temp[1]?

также имеется: 

if (KEY==1 && temp[0]<millis()) {
temp[0]=millis()+1000;

тут temp[0] равно 1 сек, так? получается если жмакнута кей1 и времени прошло менше 1 сек то... А чему равно temp[1] и [2] тогда?

а вот с выводм у меня так неработает. Прописал так:

temp[0]=millis()+zadTime[3];
if (millis()<temp[0]) {...}

Получается переменаня приравнивается: миллисекунды+время зарежки в массиве - она же константа (у меня это 3000). Если милисекунды больше переменной то {.....}

втоге все выводится на печать мгновенно. Блин массив с миллисекундами сложнее в моём понимании пока чем простая работа с ними и двума параметрами, интервал и предыдущее показание.

И вот так: const long zadTime[]={100,750,1000,3000,10000,60000}; - совершенно понятно что берём. 

А так: unsigned long temp[2]; - и далее в коде temp[] может использоваться и 0 и 1 и 2 а чему он равен если до этого неприравнивался "=" никчему, немогу разобраться.

bwn
Offline
Зарегистрирован: 25.08.2014

Так, на вопросы буду по коду отвечать. Код из 221 поста:

в коде имеется: if (temp[1]<millis()) KEY = keyIr(); - чему равно temp[1]? - она изменяется в строках 151 и 152 (задержка)

тут temp[0] равно 1 сек, так? получается если жмакнута кей1 и времени прошло менше 1 сек то... А чему равно temp[1] и [2] тогда? - правильно. temp[1] равно какому то ранее присвоенному значению (стр 151, 152) или 0, а temp[2] не равно ничему, ее просто не существует. Мы объявили unsigned long temp[2]; это массив из двух элементов temp[0] и temp[1].

temp[0]=millis()+zadTime[3];
if (millis()<temp[0]) {...}

Получается переменаня приравнивается: миллисекунды+время зарежки в массиве - она же константа (у меня это 3000). Если милисекунды больше переменной то {.....} - а где они у вас больше? Наверно все таки if (temp[0]<millis()) {...}.

А так: unsigned long temp[2]; - и далее в коде temp[] может использоваться и 0 и 1 и 2 а чему он равен если до этого неприравнивался "=" никчему, немогу разобраться. - действия  как с обычными переменными. Для глобальных (с локальными не так) можем объявить переменную двумя способами. int a; или int a=15; В первом случае будет равно 0 во втором 15. Мы массив объявляли первым способом, значит изначально все равны 0.  А вот здесь объвление вторым способом -   long zadTime[]={100,750,1000,3000,10000,60000}; Компилятор сам посчитал количество элементов массива [6] и заполнил их заявленными значениями.

Использоватся может только 0 и 1 ибо объявлено unsigned long temp[2];. Если хотим 0, 1, 2 надо объявлять unsigned long temp[3];. Последний индекс массива всегда на единицу меньше объявленного размера, т.к. первая ячейка нулевая (позагибайте пальцы). Ошибка очень распространенная, как и подлая т.к. компилятор при обработке конструкции int temp[2]; и далее в коде a=temp[2]; не распознает что такого индекса быть не может и продолжает работу как ни в чем не бывало присваивая переменной "а" разный мусор из памяти.

bwn
Offline
Зарегистрирован: 25.08.2014

Доброго дня, похоже все получилось. Теперь наверно будет хорошо создать новую тему в проектах и выложить схему, фоточки и видео готового устройства. Кому то будет интересно взглянуть. А на эту тему дать ссылку, если у кого будет желание читать 5 листов. Кстати неплохой стимул для новичков, вам в начале пророчили пару лет, а управились за полтора месяца.

Thorn
Offline
Зарегистрирован: 07.11.2014

Здравствуйте уважаемый BWN :)

Вы неповерите сколько "граблей" я успел нахвататься с "новой" работой кнопок. ранее все просто, объявил вначале функции работы с кнопками: if (irrecv.decode(&results)){ ..... 

завершил в конце: irrecv.resume(); } и собственно закрыл функцию }

Как только мы приравняли работу IR кнопок к нопкам механическим, задали каждому HEX коду кнопки её "возврат" от 1 до 5 и ежели ничего ненажато то 0 тут и понеслось у меня далее везде где было и "удержание" и просто отработка вкл\выкл реле, подсветки lcd и прочее. Ну и попорядку.

bwn
Offline
Зарегистрирован: 25.08.2014

Доброго дня. Ну если действительно такой геморр,  вернутся как было, пусть действительно в каждой функции свой опрос(((.

Thorn
Offline
Зарегистрирован: 07.11.2014

Нет нет, всё приемлемо. Я сам незнал КАК правильно работать с кнопками и что работу с ними нужно ОПРЕДЕЛЯТЬ и описывать в самом начале чтобы потом нековырять и неределывать, но мне это нравится всегда что то новое выходит. Итак, ежели неприравнивать val=0; тоесть неопускать результат после нажатия к нопки к 0, то у нас некое подобие "удержания". Где нужно обработать только вкл или выкл, просто "обнулял" кнопку после. А вот где нужно удержание (в диммировании оставил, незабыв в самом конце выполенния снова приравнивал к 0, чтоб "красиво"). 

Теперь по экрану. Пока работаю с ОДНИМ где всё, выглядит так, делал для себя так что мне понятно где- чего а значит можно и без доп. текстовых полей :) :

где, первая строчка, это часики + календарик. 

Вторая, это давление, день недели и положение реле на OUT0 - важный параметр, именно он отвечает за ПОЛНОЕ вкл\откл ИБП питания всей системы и нагрузки (у меня все 12В и 5В). Сама ардуинка, напомню питается +5vsb (питается даже при ВЫКЛЮЧЕННОМ ИБП).

Третья строка, это температуры (воды в аквариуме, радиаторов охлаждения led, внутри блокка коммутации и реле (токи большие и малоли!!!!) Так как у меня все уже по таймеру отработатно то и температуры на фото почти равны :)

И четвёртая строка, это положения цифровых выходов реле OUT1, 2, 3 А также напряжения (оно выше на самом деле, просто в коде надо поработать с номиналами Rезисторов, но всё недосуг. И Значения диммириования analogData1, 2 - сейчас это 16 из 255. Тоесть "легкая" досветка интерьера). замечу что положения реле OUT2, 3 независимы друг от друга и могут включатся как в функции (далее в коде все увидите) 

//========== Увеличение яркости (auto)
void dimmUp() {

так и просто с пульта. Положение реле OUT1 определяется значениями analogData1, 2 и неможет "просто так" быть отключено!!!.

А далее постик про меню...

Thorn
Offline
Зарегистрирован: 07.11.2014

Менюшечка, эх эта функция и работа с ней поистине испытание для начинающего. Я грешным делом думал, вот ЕПРОМ - а на деле это вообще ниочем (ну на  моем этапе. где это просто ячеечка и в ней хранится). Итак, я чуть сдвинул строки с позициями окон и параметров, оттго и "поехала" видимо у меня реализация blink.

Вот, как выглядит окно с настройкой парметров в 1 меню, оноже char menuTxt[5][17] = {"set Timer 1 >>>>"}; скрин: 

где 1 | 1 - это номер  окна меню и позиция параметра ( в данном случае часов) включения. Всё прсото однако blink падлюка мигает где то в 0 строке а никак непервой, как бы я ниуказывал в коде (и после указания позиции печати и до) вобщем мигает нета, вот и пригодилась отображение позиции :).

И собственн, добавил есчо один таймер для отключения "чуть ранее" (на 30-40 мин) релюшем НЕДИММИРУЕМЫХ Led-ов (перед сном, антураж лунного света, ну якобы) очень приятно так засыпать). Однако этот таймер НЕИМЕЕТ включения он имеет только определение ВЫКЛЮЧЕНИЯ, потому обыграл так:

где: 4 | 3 - номер окна меню (char menuTxt[5][17] = {"set MoonLight >>"};? позиция курсора - это часы выключения OUT2 (22 часа) 

Тоесть ежели убрать текстовое поле 0 строки ON Off то и  непутает ну да позже допилю. Ровно как и Параметры для термореле, счас они (невключены) имхо - зима да и радиаторы LED-ов расчитаны НА пассивное охлаждение (запас по площади двухкратный почти).

Корпус акваконтроллера как видно очень мал и компактен и если бы не 6 и 10кв.мм провода для питания ... то был бы ещё меньше

Thorn
Offline
Зарегистрирован: 07.11.2014

Вот как в целом всё. Так как IR-сенсор  пока один и приходилось вынимать его из домашнего Nano в тестовую Mega на работе то его пока так и оставил "необыгранным". Скоро приедут IR vs1838 три штуки за 70 руб (давно надо было).

  и 

  и 

Схему принципиальную электрическую (распиновку) подключения и видео с работой выложу сегодня-завтра :)

Библиотеки тоже могут быть нелишними, тоже выложу. 

Надеюсь проект будут интересен в лане реализации IR и блютусинки (в appInventor попытался чт ото набросать и даже получилось на HC-06 модуле но самого интересного - получения инфы о темпиратурах и прочих параметрах пока никак, токль всякие переключения работают). Скрин с приложения что вышло: 

так как диммирование пока Неработает с блютусинки и всё сырое и мне МЕНЮ было важнее и нужнее чем эта игрушка пока отложил но как доделаю, ВСЁ выложу и *,aia файлик и код для HC-06 в ардуинке.

Собственно сам код для Ардуинки:

#include <EEPROM.h>
#include <Wire.h>                     //Подключаем библиотеку для использования однопроводного интерфейса                                     
#include <LiquidCrystal_I2C.h>        //Подключаем библиотеку для использования I2C интерфейса
#include <OneWire.h>                  //Подключаем библиотеку для температурного датчика DS18B20
#include <BMP085.h>                   //Подключаем библиотеку для маленького барометра
#include <IRremote.h>                 //Подключаем библиотеку для IR сенсора
LiquidCrystal_I2C lcd(0x27,20,4);     //Устанавливаем LCD-адрес 0x27 для отображения 20 символов и 4 линии
byte currentLight=0;                  //Переменная включения подсветки LCD 

//=====BT*
char inByte;                          //Ввходящие данные BT (Bluetooth модуля)

//=====IR_Sensor*
#define recvPin 3                    //Вход IR-приемника и кнопки пульта
uint32_t val; 
IRrecv irrecv(recvPin);
decode_results results;

//=====IR_Dimmer*
byte angData1, angData2=0;        //Переменная выходов диммирования (из двух половинок :))
byte maxDimm=255;                 //Переменная определяет МАКСИМАЛЬНОЕ значение диммирования
byte upR2=210;                    //Переменная включения 2 свето-релейного канала
byte upR3=240;                    //Переменная включения 3 свето-релейного канала
byte downR2=205;                  //Переменная выключения 2 свето-релейного канала
byte downR3=235;                  //Переменная выключения 3 свето-релейного канала
int pManDimmUp=800;               //Период увеличения яркости 
int pManDimmDown=500;             //Период уменьшения яркости
byte flagDimm=0;                  //Флаг для обработки "НЕОБРАТИМОГО" выключения Power_Off
unsigned long tDimm=millis();     //Переменная задержки auto_dimmer

//=====Termo_Relay*
const int RelayChn1=4;             //Используем цифровой ПОРТ 4 для ПЕРВОГО канала термо-реле (вода) 
const int RelayChn2=A1;            //Используем цифровой ПОРТ A1 для ВТОРОГО канала термо-реле (радиатор)

//=====Led_Relay*
#define OUT1 7                      //Используем цифровой ПОРТ 7 для 1 свето-релейного канала Dimm
#define OUT2 8                      //Используем цифровой ПОРТ 8 для 2 свето-релейного канала
#define OUT3 9                      //Используем цифровой ПОРТ 9 для 3 свето-релейного канала

//=====Other_Relay*
#define OUT0 10                     //Используем цифровой ПОРТ 10 для ATX (включение ATX (зелёный + черный))
//#define OUT6 31                     //Используем цифровой ПОРТ 11 для CO2
//#define OUT7 32                     //Используем цифровой ПОРТ 12 для другое

//=====Dimmer_Out*
#define OUT4 5                      //Используем цифровой ПОРТ 5 для 1/2 SunSet канала (dimmer)
#define OUT5 6                      //Используем цифровой ПОРТ 6 для 1/2 SunSet канала (dimmer)

//=====VoltMeter*
int voltInput=A7;                   //Используем аналоговый вход A7 для измерения напряжения
float vout=0.0;
float vin=0.0;
float R1=21700.0;                   //Resistance of R1 (22K) - see text!
float R2=12770.0;                   //Resistance of R2 (12K) - see text!
int valueVo=0;

//=====Pressure*
BMP085 dps=BMP085();                // Digital Pressure Sensor 
long Pressure=0;
unsigned long timeDps=0;

//=====Termo_Sensor*
int tempPin=2;                      //Определяем порт шины OneWire (IC) для температурного датчика DS18B20                               
OneWire ds(tempPin);                //Создаем объект для работы с термометром
byte flagDallas=0;                  //Флаг для обработки показаний с датчиков Dallas
byte data[12];
byte addr1[8] = {0x28, 0xC5, 0x3B, 0x5C, 0x06, 0x00, 0x00, 0x85};     //адрес датчика DS18B20_Вода в аквариуме
byte addr2[8] = {0x28, 0x86, 0x48, 0xEA, 0x05, 0x00, 0x00, 0xF6};     //адрес датчика DS18B20_Радиатор (2-ой канал, центр)
byte addr3[8] = {0x28, 0xF2, 0x29, 0xEB, 0x05, 0x00, 0x00, 0xE1};     //адрес датчика DS18B20_Блок реле
unsigned int raw;                  //Если экранированный кабель, можно подключать до 32 термо-датчиков DS18B20
float temp1, temp2, temp3;         //Температура аквариума \ радиаторов Led \ блока реле
float t1=25.0;                     //Переменная предела срабатывания реле RelayChn1
float t2=47.0;                     //Переменная предела срабатывания реле RelayChn2
float t3=55.0;                     //Переменная предела срабатывания реле RelayChn3
float tGistrsis=1.0;               //Гистерезис температур (по 0,25 в каждую сторону)

//=====Rtc_Clock*
#define DS3231_I2C_ADDRESS 104 
byte seconds, minutes, hours, day, date, month, year;

//=====Timer*
//    long lghtIntv=60000;            //Интервал для проверки вкл./выкл. освещения аквариума, 1 минута
byte lcdStat=0;                //Флаг подсветки Lcd если включена, то в 1
byte lghtStat1=0;              //Флаг освещения, если включена, то в 1 в первом таймере
byte lghtStat2=0;              //Флаг освещения, если включена, то в 1 во втором таймере
byte co2Stat=0;                //Флаг Co2, если включена, то в 1
byte isLcdt=0;                 //Флаг для обработки необходимости включить подсветку Lcd
byte isLght1=0;                //Флаг для обработки необходимости включить реле освещения в первом таймере
byte isLght2=0;                //Флаг для обработки необходимости включить реле освещения во втором таймере
byte isCo2t=0;                 //Флаг для обработки необходимости включить реле Co2
byte isNight=0;                //Если включаем на ночь, т.е. начальное время больше конечного

//=====Menu*
byte setHorClockOn;                 //Часы включения
byte setMinClockOn;                 //Минуты включения
byte setHorClockOff;                //Часы вЫключения
byte setMinClockOff;                //Минуты вЫключения  
byte setTempt1;                     //Максимальная температура t1 (вода в аквариуме)
byte setTempt2;                     //Максимальная температура t2 (радиатор Led)
byte setTempt3;                     //Максимальная температура t3 (блок коммутации, реле)
byte winMe;                         //Номер экрана меню

//=====Delays*
unsigned long tmIntv=1000;          //Интервал для обновления времени на экране каждую секунду
unsigned long razdIntv=2000;        //Интервал для обновления ":" на экране каждые 2 секунды
unsigned long prvMlsTm=0;           //Предыдущее показание миллисекунд для обновления показания часов
unsigned long prvMlsRazd=0;         //Предыдущее показание миллисекунд для обновления ":"
//unsigned long prvMlsLght=0;         //Предыдущее показание проверки временного интервала
unsigned long prvMlsVo=0;           //Предыдущее показание обновления показания вольтметра
unsigned long voIntv=5000;          //Интервал для обновления напряжения
unsigned long prvMlsOut=0;          //Предыдущее показание обновления показания OUT
unsigned long outIntv=1000;         //Интервал для обновления OUT
unsigned long prvMlsBar=0;          //Предыдущее показание обновления показания барометра
unsigned long barIntv=10000;        //Интервал для обновления барометра
unsigned long prvMlsTemp=0;         //Предыдущее показание обновления температур
unsigned long tempIntv=5000;        //Интервал для обновления температур
unsigned long prvMlsTimer=0;        //Предыдущее показание обновления таймера
unsigned long timerIntv=10000;      //Интервал для обновления таймера
unsigned long tzad=millis();        //Переменная задержки (пока для ds18b20, ds.write) и непользую....

unsigned long zadM[4];              //Массив для задержек меню
const long zadTime[] = {100,750,1000,3000,10000,60000};

//=====Byte_Symbol*
byte equ[8] =                    //Символ "=" но КОРОТКИЙ :)
{
  B00000,
  B00000,
  B01110,
  B00000,
  B01110,
  B00000,
  B00000,
}; 


void setup() {
  Wire.begin();
//  Serial.begin(9600);
  irrecv.enableIRIn();                                //Включаем IR-сенсор
  dps.init(MODE_STANDARD, 25000, true);               //250 meters, true = using meter units
     pinMode(voltInput,INPUT);                        //Определяем вход вольтметра  
//    pinMode(RelayChn1,OUTPUT);                      //Определяем выход 1-термореле охлаждения воды аквариума 
//    pinMode(RelayChn2,OUTPUT);                      //Определяем выход 2-термореле охлаждения радиаторов Led
//  digitalWrite(RelayChn1,HIGH);                     //Определяем инверсный выход в HIGH охлаждения воды аквариума
//  digitalWrite(RelayChn2,HIGH);                     //Определяем инверсный выход в HIGH охлаждения радиаторов Led
     pinMode(OUT0,OUTPUT);                            //Определяем инверсный выход реле ATX 
     pinMode(OUT1,OUTPUT);                            //Определяем инверсные выходы реле 1 канала Led
     pinMode(OUT2,OUTPUT);                            //Определяем инверсные выходы реле 2 канала Led
     pinMode(OUT3,OUTPUT);                            //Определяем инверсные выходы реле 3 канала Led
 //    pinMode(OUT6,OUTPUT);
 //    pinMode(OUT7,OUTPUT);
  digitalWrite(OUT0,HIGH);                            //Определяем инверсный выход в HIGH реле ATX
  digitalWrite(OUT1,HIGH);                            //Определяем инверсный выход в HIGH реле 1 канала Led
  digitalWrite(OUT2,HIGH);                            //Определяем инверсный выход в HIGH реле 2 канала Led
  digitalWrite(OUT3,HIGH);                            //Определяем инверсный выход в HIGH реле 3 канала Led
//  digitalWrite(OUT6,HIGH);
//  digitalWrite(OUT7,HIGH);
  analogWrite(OUT4,angData2);                      //Инициация выхода 1/2 диммера
  analogWrite(OUT5,angData1);                      //Инициация выхода 1/2 диммера
      lcd.init();                                     //Инициализируем дисплейчик
      lcd.setBacklight(1);  
      lcd.clear();                                    //Очищаем на всякий случай дисплейчик
      lcd.setCursor(2,0);
      lcd.print("Aqua  Controller");                  //Выводим версию и прочее
      lcd.setCursor(2,1);
      lcd.print("      v1.1      ");
      lcd.setCursor(1,3);
      lcd.print("_0802_menu_NoEp_Dim_");
  delay(2000);
      lcd.clear();                                    //Очистка дисплея и void (printTime, printDin, printStat)
}

//========== Обработка IR-сенсора, определение кнопок
byte keyIr(){
  if (irrecv.decode(&results)) {      //Если пришел пакет и этот пакет не FF сохраняем правильный пакет в переменную
//  if (results.value!=0xFFFFFFFF) {  //Если пришел FF, соответственно пропускаем.
    val=results.value; //}            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
    irrecv.resume(); }                //В переменной останется прошлый, правильный, пакет и код выполнится повторно.
//  Serial.println(val,HEX);
//  Serial.println(" ");
  if (val==0x41BEB04F) return 5;    //right      KEY5
  if (val==0x41BE609F) return 4;    //down       KEY4
  if (val==0x41BED02F) return 3;    //up         KEY3
  if (val==0x41BE40BF) return 2;    //left       KEY2
  if (val==0x41BEC03F) return 1;    //setup      KEY1
  if (val==0x41BEA05F) return 10;   //stop       KEY10
  if (val==0x41BE20DF) return 9;    //slide      KEY9
  if (val==0x41BE10EF) return 8;    //vol_up     KEY8
  if (val==0x41BE708F) return 7;    //vol_down   KEY9
  if (val==0x41BEF00F) return 6;    //exit       POWER_KEY
//  else return 0;
}

//========== Обработка IR-кнопок
void keyI(){
  byte KEY=keyIr();            

//========== Обработка Входа в Меню
     if (KEY==1){                          //***ВХОД В МЕНЮ и проверка блокировки от возврата. Принимаем код клавиши
        val=0;
        menu(); }                          //Если setup идем в меню
    
//========== Отключение LCD с кнопки
  if (KEY==9){ val=0;
    lcd.setBacklight(currentLight);
    currentLight = !currentLight; }

//========== Увеличение яркости (удержание кнопки)
      if (KEY==5){ val=0;
      if (angData1, angData2<255) angData1++, angData2++;   //Начало увеличения диммирования
         analogWrite(OUT4, angData2);
         analogWrite(OUT5, angData1);
      if (angData1, angData2>0) {            //Включаем реле ATX и диммера на первом шаге (начало диммирования)
         digitalWrite(OUT0,LOW);             //Включаем реле ATX
         digitalWrite(OUT1,LOW); }
}

//========== Уменьшение яркости (удержание кнопки)
      if (KEY==2){ val=0;
      if (angData1, angData2!=0) angData1--, angData2--;  //Начало снижения диммирования 
         analogWrite(OUT4, angData2);
         analogWrite(OUT5, angData1);
      if (angData1, angData2==0) {           //Выключаем реле диммера (по окончании диммирования)
         digitalWrite(OUT1,HIGH);
         digitalWrite(OUT2,HIGH);
         digitalWrite(OUT3,HIGH);
         digitalWrite(OUT0,HIGH); }           //Выключаем реле ATX
}

//========== Обработка "одного" нажатия   
      if (KEY==8 & flagDimm==0){              //Если нажата кнопка УВЕЛИЧЕНИЯ диммирования
      if (tDimm<millis()){                    //и не дана команда на полное выключение
  pManDimmUp; dimmUp(); }                     //Интервал УВЕЛИЧЕНИЯ диммирования
}
      if (KEY==7 & flagDimm==0){              //Здесь аналогично увеличению яркости
      if (tDimm<millis()) {
  pManDimmDown; dimmDown(); }                 //Интервал Снижения диммирования
}
      if (KEY==6 & angData2!=0){              //Power_Off. Если диммер включен, устанавливаем флаг
    flagDimm=1; }
      if (flagDimm==1) {                      //Если флаг установлен, продолжаем уменьшение яркости
      if (angData2==0) {                      //Яркость на 0, флаг сбросили.
      flagDimm=0;
    digitalWrite(OUT1,HIGH);
    digitalWrite(OUT2,HIGH);
    digitalWrite(OUT3,HIGH);
    digitalWrite(OUT0,HIGH); }
      if (tDimm<millis()){                     //Последовательно уменьшаем яркость, кнопки заблокированы
  pManDimmDown=300; dimmDown(); }              //Интервал СНИЖЕНИЯ диммирования (по Power_Off)
  val=0; 
}  

//========== Обработка ATX, НЕдиммируемых реле
//     if (KEY == 6) digitalWrite(OUT0, !digitalRead(OUT0));      //Power_KEY, ON/OFF_KEY, включение ATX
//   else if (KEY == 6) digitalWrite(OUT0, !digitalRead(OUT0));
 if (KEY==3) {
     val=0; if (!digitalRead(OUT1)!=0 && angData2 >=1) digitalWrite(OUT2,!digitalRead(OUT2));}
 if (KEY==3) { 
     val=0; if (!digitalRead(OUT1)!=0 && angData2!=0) digitalWrite(OUT2,digitalRead(OUT2));}
 if (KEY==4) { 
     val=0; if (digitalRead(OUT1)==0 && angData2>=1) digitalWrite(OUT3,!digitalRead(OUT3));}
 if (KEY==4) { 
     val=0; if (digitalRead(OUT1)==0 && angData2!=0) digitalWrite(OUT3,digitalRead(OUT3));}
}

//========== Увеличение яркости (auto)
void dimmUp() {
      if (angData2<maxDimm){                //Ограничение верхнего значения диммирования, (0-255)
    angData2++;                             //Начало подъёма диммирования
    angData1++;
    analogWrite(OUT4,angData2);             //Пишем в порт
    analogWrite(OUT5,angData1);
      if (angData2>0){                      //Включаем реле ATX и диммера на первом шаге (начало диммирования)
    digitalWrite(OUT0,LOW);
    digitalWrite(OUT1,LOW); }
      if (angData2==upR2) {                  //Включаем Второе реле при upR2
    digitalWrite(OUT2,LOW); }
      if (angData2==upR3) {                  //Включаем Третье реле при upR3
    digitalWrite(OUT3,LOW); }
    tDimm=millis()+pManDimmUp; }             //Устанавливаем время следующего шага
    if (angData2==maxDimm){val=0;}
}

//========== Уменьшение яркости (auto)
void dimmDown() {
      if (angData2>0) {
    angData2--;                            //Начало снижения диммирования 
    angData1--;
    analogWrite(OUT4,angData2);
    analogWrite(OUT5,angData1);
      if (angData2==downR3){               //Выключаем Третье реле при downR3
    digitalWrite(OUT3,HIGH); }
      if (angData2==downR2){               //Выключаем Втором реле при downR2
    digitalWrite(OUT2,HIGH); }
      if (angData2 == 0){                  //Выключаем реле диммера (по окончании диммирования)
    digitalWrite(OUT0,HIGH);
    digitalWrite(OUT1,HIGH);
    digitalWrite(OUT2,HIGH);
    digitalWrite(OUT3,HIGH); }
    tDimm=millis()+pManDimmDown; }         //Устанавливаем время следующего шага
    if (angData2==0){val=0;}
}

//=========== Обработка Меню, выбор экрана
void menu(){
  lcd.clear();                                    //Очищаем на всякий случай дисплейчик
  char menuTxt[5][17] = {"set Timer 1 >>>>", "set Timer 2 >>>>","set Timer Lcd >>","set MoonLight >>","set Timer Co2 >>"};  //Массив с наименованиями для экрана
  byte pos=0;
  while(1) {                                                    //Бесконечный цикл
    byte KEY=keyIr();
      val=0;  
    lcd.setCursor(0,1);
    lcd.print(pos+1);                                           //Печатаем номер.
    lcd.print(". ");
    lcd.print(menuTxt[pos]);                                    //Печатаем название
    if (KEY==3 && pos>0) {pos--;}                               //Уменьшить увеличить + блокировка кнопки
    if (KEY==4 && pos<4) {pos++;}
    winMe=pos;                                                  //Переменная номера таймера
/* 
    Здесь переходим на подменю. Если останется как сейчас, то достаточно. Сейчас можно перейти на разные подменю.
    if (KEY == 5) setOnOff();
*/
    if (KEY==5 && pos==0) setOnOff();
    if (KEY==5 && pos==1) setOnOff();
    if (KEY==5 && pos==2) setOnOff();
    if (KEY==5 && pos==3) setOnOff();
    if (KEY==5 && pos==4) setOnOff();
    if (KEY==1) {                                   //Выход из меню с проверкой и установкой блокировки от возврата       
    lcd.clear();                                    //Очищаем на всякий случай дисплейчик
      break; } 
    }
} 

//=========== Обработка Меню, параметры и настройка
void setOnOff(){    
  byte pos=0;   
  setHorClockOn=  EEPROM.read(winMe*4+1);                //Считываем записанные значения таймеров
  setMinClockOn=  EEPROM.read(winMe*4+2);                //Адрес определяется как номер таймера*4 + четыре ячейки
  setHorClockOff= EEPROM.read(winMe*4+3);
  setMinClockOff= EEPROM.read(winMe*4+4);
      lcd.clear();                                       //Очищаем на всякий случай дисплейчик
      lcd.blink();
    while(keyIr()!=1) {                                  //Крутим цикл пока не будет Setup
    byte KEY=keyIr();                                    //Читаем состояние кнопок
    val=0;
//      delay(200);                                      //Если убрать, не работает подсветка уст.позиции
    lcd.setCursor(7,0);
    lcd.print("-On-  -Off-"); 
    lcd.setCursor(0,3);
    lcd.print("=== push to save ===");
    lcd.setCursor(1, 1);
    lcd.print(winMe+1,DEC);                              //Печатаем номер программы таймера
    lcd.print("|");
    lcd.print(pos/3,DEC);                                //Печатаем позицию настройки параметра (замена blink)
    lcd.print("->");  
    lcd.setCursor(7,1);                                  //Выводим инфу о часах и минутах
     if (setHorClockOn<10) lcd.print("0");
    lcd.print(setHorClockOn,DEC);
    lcd.print(":");
     if (setMinClockOn<10) lcd.print("0"); 
    lcd.print(setMinClockOn,DEC);  
    lcd.print(" ");     
     if (setHorClockOff<10) lcd.print("0");
    lcd.print(setHorClockOff,DEC);
    lcd.print(":");
     if (setMinClockOff<10) lcd.print("0");
    lcd.print(setMinClockOff,DEC); 
    lcd.setCursor(pos, 0);                                //Устанавливаем курсор согласно позиции
    
    if (pos<3) pos=3;
    if (KEY==5 && pos<11) pos += 3;                       //Крутим позицию право-лево
    else if (KEY==2 && pos>3) pos -= 3;
    
    else if (pos==3 && KEY==3) setHorClockOn++;           //Крутим значения
    else if (pos==3 && KEY==4) setHorClockOn--;
    else if (pos==6 && KEY==3) setMinClockOn++;
    else if (pos==6 && KEY==4) setMinClockOn--;    
    else if (pos==9 && KEY==3) setHorClockOff++;
    else if (pos==9 && KEY==4) setHorClockOff--;    
    else if (pos==12 && KEY==3) setMinClockOff++;
    else if (pos==12 && KEY==4) setMinClockOff--; 
    
    if (setHorClockOn>23) setHorClockOn=0;                //Ограничиваем значения
    else if (setMinClockOn>59) setMinClockOn=0;
    else if (setHorClockOff>23) setHorClockOff=0;
    else if (setMinClockOff>59) setMinClockOff=0;
}                                                         //Конец цикла
      lcd.noBlink(); 
      lcd.clear();                                        //Очищаем на всякий случай дисплейчик
   EEPROM.write(winMe*4+1, setHorClockOn);                //Записываем НОВЫЕ значения
   EEPROM.write(winMe*4+2, setMinClockOn); 
   EEPROM.write(winMe*4+3, setHorClockOff);
   EEPROM.write(winMe*4+4, setMinClockOff);
   lcd.setCursor(4,2);
   lcd.print("== Saved ==");
   delay(2000);
}

//========== Считывание напряжения
void voltM() {
  valueVo=analogRead(voltInput);               //Read the value at analog input
  vout=(valueVo*5.0)/1024.0;               //See text
  vin=vout/(R2/(R1+R2)); 
      if (vin<0.09) {
  vin=0.0; }                                  //Statement to quash undesired reading
}

//========== Считывание Давления
void bar() {
  if (((millis()-timeDps)/1000.0)>=1.0) {     
     dps.calcTrueTemperature();
     timeDps=millis(); }
  dps.getPressure(&Pressure);
}

//=========== Считывание температур
void dallas(){
  ds.reset();
  ds.write(0xCC);                             //Команда инициации  
  ds.write(0x44);                             //Start conversion, with parasite power on at the end
    tzad=millis()+750; flagDallas=1; }
  float DS18B20(byte *adres){ //flagDallas=0;
  ds.reset();
  ds.select(adres);
  ds.write(0xBE);                                          //Read Scratchpad
    for (byte i=0; i<9; i++) data[i]=ds.read();     //We need 9 bytes
    int raw=(data[1]<<8) | data[0];                    // Переводим в температуру   
    float celsius=(float)raw/16.0;
    return celsius;
}

//=========== Обработка Термо-Реле
void tempRelay(){
    temp1 = DS18B20(addr1);
    temp2 = DS18B20(addr2);
    temp3 = DS18B20(addr3);
      if (temp1>t1-tGistrsis/2){
    digitalWrite(RelayChn1,LOW);                 //Термо-реле 1-включается
//        lcd.setCursor(8, 2);
//        lcd.print("*");
  }     
      else if (temp1<t1+tGistrsis/2){
    digitalWrite(RelayChn1,HIGH);                //Термо-реле 1-выключается
//        lcd.setCursor(8, 2);
//        lcd.print("-");
  } 
      if (temp2>t2-tGistrsis/2){
    digitalWrite(RelayChn2,LOW);                 //Термо-реле 2-включается
//        lcd.setCursor(18, 2);
//        lcd.print("*");
  }     
      else if (temp2<t2+tGistrsis/2){
    digitalWrite(RelayChn2,HIGH);                //Термо-реле 2-выключается
//        lcd.setCursor(18, 2);
//        lcd.print("-");
  } 
} 

//=========== Обработка RTC часов
void timeRtc(){
  Wire.beginTransmission(DS3231_I2C_ADDRESS);       //104 is DS3231 device address
  Wire.write(0x00);                                 //Start at register 0
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);          //Request seven bytes
      if(Wire.available()) { 
    seconds = Wire.read();                           //Get second
    minutes = Wire.read();                           //Get minute
    hours   = Wire.read();                           //Get hour
    day     = Wire.read();
    date    = Wire.read();
    month   = Wire.read();                          //Get month
    year    = Wire.read();
       
    seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111));  //Convert BCD to decimal
    minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111));
    hours   = (((hours & B00110000)>>4)*10 + (hours & B00001111));      //Convert BCD to decimal (assume 24 hour mode)
    day     = (day & B00000111); // 1-7
    date    = (((date & B00110000)>>4)*10 + (date & B00001111));     //Convert BCD to decimal  1-31
    month   = (((month & B00010000)>>4)*10 + (month & B00001111));   //msb7 is century overflow
    year    = (((year & B11110000)>>4)*10 + (year & B00001111));
  }
}

//=========== Обработка Таймеров
void timer(){

//========== Таймер №1 
  int fullMinutes=hours*60+minutes;                                 //Переводим часы + минуты к полным минутам
  int fullMinutesOn1=EEPROM.read(1)*60+EEPROM.read(2);              //Переводим часы + минуты включения 1 реле к полным минутам
  int fullMinutesOff1=EEPROM.read(3)*60+EEPROM.read(4);             //Переводим часы + минуты выключения 1 реле к полным минутам
  int fullMinutesOn2=EEPROM.read(5)*60+EEPROM.read(6);              //Переводим часы + минуты включения 2 реле к полным минутам
  int fullMinutesOff2=EEPROM.read(7)*60+EEPROM.read(8);             //Переводим часы + минуты выключения 2 реле к полным минутам
  int fullMinutesOnLcd=EEPROM.read(9)*60+EEPROM.read(10);           //Переводим часы + минуты включения Lcd к полным минутам
  int fullMinutesOffLcd=EEPROM.read(11)*60+EEPROM.read(12);         //Переводим часы + минуты выключения Lcd к полным минутам
  int fullMinutesOffOut3=EEPROM.read(13)*60+EEPROM.read(14);        //Переводим часы + минуты включения OUT3 к полным минутам
  int fullMinutesOffOut2=EEPROM.read(15)*60+EEPROM.read(16);        //Переводим часы + минуты выключения OUT2 к полным минутам
          
    if (fullMinutesOn1>fullMinutesOff1) {isNight=1;}                //Если ночное время
    if (isNight==0) {                          //Если день
    if (fullMinutes>=fullMinutesOn1&&fullMinutes<fullMinutesOff1) {isLght1=1;}        //Проверяем интервал
   else {isLght1=0;}                           //Если необходимо включить свет
  } else {                                       //Если ночь
      if(fullMinutes-fullMinutesOn1>=0) {isLght1=1;}        //Если больше или равно верхнему значению, то необходимо включить свет
   else {                                        //Если необходимо включить свет
      if(fullMinutes<fullMinutesOff1) {isLght1=1;}            //Если меньше нижнего значения, то необходимо включить свет
   else {isLght1=0;}                           //Если необходимо включить свет
    }
  }  
    if((isLght1==1)&&(lghtStat1==0)){      //Если свет еще не включен и выставлен флаг необходимости включить
    if (tDimm < millis()) {                //И не дана команда на полное выключение
  pManDimmUp; dimmUp(); } 
      if (angData2==maxDimm) {lghtStat1=1;}
  } else {
      if(isLght1==0&&lghtStat1==1){
    if (tDimm < millis()) {
  pManDimmDown; dimmDown(); } 
      if (angData2==0){lghtStat1=0;}
  }
}
//========== Таймер №2
      if (fullMinutesOn2>fullMinutesOff2) {isNight=1;}                  //Если ночное время
    if (isNight==0) {                          //Если день
    if (fullMinutes>=fullMinutesOn2&&fullMinutes<fullMinutesOff2) {isLght2=1;}        //Проверяем интервал
   else {isLght2=0; }                           //Если необходимо включить свет
  } else {                                       //Если ночь
      if(fullMinutes-fullMinutesOn2>=0) {isLght2=1;}        //Если больше или равно верхнему значению, то необходимо включить свет
   else {                                        //Если необходимо включить свет
      if(fullMinutes<fullMinutesOff2) {isLght2=1;}            //Если меньше нижнего значения, то необходимо включить свет
   else {isLght2=0; }                           //Если необходимо включить свет
    }
  }  
    if((isLght2==1)&&(lghtStat2==0)){      //Если свет еще не включен и выставлен флаг необходимости включить
    if (tDimm < millis()) {                //И не дана команда на полное выключение
  pManDimmUp; dimmUp(); }
      if (angData2==maxDimm) {lghtStat2=1;}
  } else {
      if(isLght2==0&&lghtStat2==1){
    if (tDimm < millis()) {
  pManDimmDown; dimmDown(); }
      if (angData2==0){lghtStat2=0;}
  }
}
//========== Таймер Lcd-подсветки  
      if (fullMinutesOnLcd>fullMinutesOffLcd) {isNight=1;}        //Если ночное время
    if (isNight==0) {                          //Если день
    if (fullMinutes>=fullMinutesOnLcd&&fullMinutes<fullMinutesOffLcd) {isLcdt=1;}        //Проверяем интервал
   else {isLcdt=0;}                           //Если необходимо включить подсветку Lcd
  } else {                                       //Если ночь
      if(fullMinutes-fullMinutesOnLcd>=0) {isLcdt=1;}        //Если больше или равно верхнему значению, то необходимо включить подсветку Lcd
   else {                                        //Если необходимо включить подсветку Lcd
      if(fullMinutes<fullMinutesOffLcd) {isLcdt=1;}            //Если меньше нижнего значения, то необходимо включить подсветку Lcd
   else {isLcdt=0;}                           //Если необходимо включить подсветку Lcd
    }
  }  
    if((isLcdt==1)&&(lcdStat==0)){        //Если подсветка Lcd еще не включена и выставлен флаг необходимости включить
    lcd.backlight(); lcdStat=1;
  } else {
      if(isLcdt==0&&lcdStat==1){
    lcd.noBacklight(); lcdStat=0;
  }
}
//========== Таймер "Лунного света"  
    if (fullMinutes>=fullMinutesOffOut3) {
       digitalWrite(OUT3,HIGH);}
    if (fullMinutes>=fullMinutesOffOut2) {
       digitalWrite(OUT2,HIGH);
} 
//========== Таймер СО2
}
//=========== Обработка Bluetooth

//=========== Обработки печати и вывода на дисплейчик (часы)
void printTime() {
  unsigned long currentMillis=millis();
       if(currentMillis-prvMlsTm>tmIntv) {   //Проверяем интервал для обновления часов
  prvMlsTm=currentMillis;                    //Вызываем ф-цию вывода времени на экран
  lcd.setCursor(0,0);
      if (hours<10) {lcd.print(0);lcd.print(hours);} else {lcd.print(hours);} 
  lcd.print(":"); 
      if (minutes<10) {lcd.print(0);lcd.print(minutes);} else {lcd.print(minutes);} 
  lcd.print(":"); 
      if (seconds<10) {lcd.print(0);lcd.print(seconds);} else {lcd.print(seconds);}
  } 
        if(currentMillis - prvMlsRazd > razdIntv) {  //Проверяем интервал для обновления ":"
  prvMlsRazd=currentMillis;
    lcd.setCursor(2,0);
    lcd.print(" ");
    lcd.setCursor(5,0);
    lcd.print(" ");
    lcd.setCursor(8,0);
    lcd.print(" ");
  }
}
//=========== Обработки печати и вывода на дисплейчик (напряжения, температуры, давление)
void printDin() {
  unsigned long currentMillis=millis();
        if(currentMillis-prvMlsVo>voIntv){   //Проверяем интервал для обновления напряжения
  prvMlsVo=currentMillis;
  lcd.setCursor(2,2);                        //Вывод значений температур на LCD                     
  lcd.print((temp1),1);
  lcd.setCursor(9,2);                          
  lcd.print((temp2),1);  
  lcd.setCursor(16,2);                          
  lcd.print((temp3),1);
  lcd.setCursor(9,3);
  lcd.print(vin);
  lcd.setCursor(4,1);
  lcd.print(Pressure/133.3,1);                //Выводим на экранчик показания атм.давления в мм.
  } 
}

//=========== Обработки печати и вывода на дисплейчик СТАТИКИ
void printStat() {
  unsigned long currentMillis=millis();
        if(currentMillis-prvMlsTimer>timerIntv){   //Проверяем интервал для обновления 
      prvMlsTimer=currentMillis;
      lcd.setCursor(16,1);
      lcd.print("pw\1");
      lcd.setCursor(0,1);
      lcd.print("bar\1");
      lcd.setCursor(0,2);
      lcd.print("a\1");
      lcd.setCursor(7,2);
      lcd.print("b\1");
      lcd.setCursor(14,2);
      lcd.print("c\1");
      lcd.setCursor(0,3);
      lcd.print("~");
      lcd.setCursor(2,3);
      lcd.print(":");
      lcd.setCursor(4,3);
      lcd.print(":");
      lcd.setCursor(7,3);
      lcd.print("v\1");
      lcd.setCursor(15,3);
      lcd.print("%\1");
      lcd.setCursor(9,0);
      if (date<10) {lcd.print(0);lcd.print(date);} else {lcd.print(date);} 
      lcd.print("/");
   if (month==1){lcd.print ("Jan");} 
   if (month==2){lcd.print ("Feb");} 
   if (month==3){lcd.print ("Mar");} 
   if (month==4){lcd.print ("Apr");} 
   if (month==5){lcd.print ("May");} 
   if (month==6){lcd.print ("Jun");} 
   if (month==7){lcd.print ("Jul");} 
   if (month==8){lcd.print ("Aug");} 
   if (month==9){lcd.print ("Sep");} 
   if (month==10){lcd.print ("Oct");} 
   if (month==11){lcd.print ("Nov");} 
   if (month==12){lcd.print ("Dec");} 
     lcd.print("/"); 
     lcd.print(year+2000); 
     lcd.setCursor(12,1);
   if (day==1){lcd.print ("Sun");} 
   if (day==2){lcd.print ("Mon");} 
   if (day==3){lcd.print ("Tue");} 
   if (day==4){lcd.print ("Wed");} 
   if (day==5){lcd.print ("Thu");} 
   if (day==6){lcd.print ("Fri");} 
   if (day==7){lcd.print ("Sat");}
   lcd.setCursor(19,1);                  //Вывод состояния ATX (Power ON\OFF)
   lcd.print(!digitalRead(OUT0));
   lcd.setCursor(1,3);                   //Вывод состояния выходов реле освещения
   lcd.print(!digitalRead(OUT1));
   lcd.setCursor(3,3);
   lcd.print(!digitalRead(OUT2));
   lcd.setCursor(5,3);
   lcd.print(!digitalRead(OUT3));
    }
    lcd.setCursor(17,3);                 //Вывод состояния аналогового выхода диммирования
    lcd.print(angData2);
    lcd.setCursor(18,3);
    lcd.print("  ");
    lcd.setCursor(17,3);
    lcd.print(angData2);
}

void loop(){
    timeRtc();
    timer();
    keyI();
    dallas();
    tempRelay();
    voltM();
    bar();
//    bluetooth();
    printTime();
    printDin();
    printStat();
}

Всего 717 строк и 25 кб в памяти (ещё целых 5 + оптимизация самого кода).

Работать приходится пока с :

unsigned long prvMlsTimer=0;        //Предыдущее показание обновления таймера
unsigned long timerIntv=10000;      //Интервал для обновления таймера

потому как с : const long zadTime[] = {100,750,1000,3000,10000,60000}; - пока НЕАУЧИЛСЯ корректно.

Также с выводом на lcd текстовой инфы как вы советовали пока неосилил. 

Thorn
Offline
Зарегистрирован: 07.11.2014

В планах на сегодня-завтра,  работа с выводом на экранчик в компактном виде: http://arduino.ru/forum/proekty/groekt-dlya-akvariuma-akvakontroller-dlya-led-temp-time-co2?page=4#comment-98369

И "раскурить"  работу с задержками и массивами: http://arduino.ru/forum/proekty/groekt-dlya-akvariuma-akvakontroller-dlya-led-temp-time-co2?page=4#comment-98561

И ещё, ув. bwn напишите мне пожалста, со совего адреса сюда Thorn-deep@mail.ru :)))

p.s. Зря неверил что годика на "два" растянется идея, поправде - каждый чень чтото меняеш, добавляеш, сначала в схему. потмо в коде. Вот нет предела - классный конструктор, было в само мначале термометр  а счас целая погодная станция 9кстати жду DHT-11 для влажности. 

В само мначале смутно представляя что есть analogRead и digitaWrite непонимал особого их различия - а всего два месяца и вотон, мо акваконтроллер да ещё и рабочий полностью за смешные деньги и огромный функционал. Код коненчое не идеал ДАЛЕКО, но помогал ЗНАЮЩИЙ и ТЕРПЕЛИВЫЙ (неожидал) а значит не всё в нем потеряно!

 

bwn
Offline
Зарегистрирован: 25.08.2014

На почту отписался. DHT11 зря брали, единственное достоинство - дешевый. Остальное редкостное г... Для нормального измерения в помещении самое лучшее SHT10 ну или DHT22.

По аквариуму не понял одной вещи - а чем рыбки дышут, если барботаж не делаете? А если начнете делать, тогда светики загадит(((.

Thorn
Offline
Зарегистрирован: 07.11.2014

bwn пишет:

По аквариуму не понял одной вещи - а чем рыбки дышут, если барботаж не делаете? А если начнете делать, тогда светики загадит(((.

Хорошо что на связи :) постараюсь как и прежде "реже" одолевать вопросами (несчитая 200 с лишним постов и 5 страниц, это почти рекорд в щадящем режиме обучения новичка, постараюсь оправдать :)) ).

ПО аэрации, её нет я вообще ПРОТИВНИК именно аэраци, есть поверхностное течение, создаваемой внешним фильтром "заявленной" производительности под 1200Л !!!!!! час что конечное БРЕХНЯ, от силы 700-800 после трех и более месяцев работы. Далее это "зеркало" воды, оно у меня почти квадрат, если точнее то 0,75м.кв это очнеь много, много ТРАВЫ, Со2 подаю постояно но всего 20-30% от "нормы" подачи (по дроппчекеру), рыбасики кстати МЕНЬШЕ всего страдают от ПЕРЕИЗБЫТКА СО2 - и кстати содержание Со2 не ПРЯМО пропорционально содежанию о2 в воде, потому "недостаток" сказывается быстрее на креветосиках и улитках, те первыми "лезут" на стенки и травку к верху, даже сомы (штерба и джулии - обычные коридорасики) - ОЧНЬ терпеливы.

ПО ДХТ-11 взял опять-же из за дешевизны и пробы работы, стоимость ну вообще ниочем, считай морженое съели :).

И теперь о страшном.... беда с IR, так как второй день код юзаю, обнаружил что этот "возврат" мне аукается. Иногда видимо что-то "улавливается IR - и она подлая преобразует аккуратно в какую-нибудь кнопку... Пару раз в "меню" обнаруживал дислпей, разок в void setOnOff() , разок в pManDimmUp; dimmUp(); - и как нитстранно диммирование "повисло" на каком-то значнеии, просто остановилсоь и всё! Я чуть паникую - эдак Ардуинка могет и ночью включиться или чего хуже "зависнуть" на меню..... Хорошо УДО пока неподаю таймером и ОХЛАЖДАТЬ ненужно ничего горячего - потому несильно страшно, однако...

И самое страшное, вынул даже IR сенсор - чтоб исключить любой сигнал уже со сторонних нерпописанных пультов - а ситуация с нажатием повторилась. Возможно, что провода просто скручены и "неэкранированы" да и сам сенсор "неэкеранирован" как тот - что заказал, вот мусор всякий и ловит, а потом его и "повторяет" - типа удержания, мы-же от него и избавляемя val=0

Важно, я мониторил  Serial.println(val,HEX); в byte keyIr() и обнаружил - что ВСЁ нажатое мне и определяется в serial потом, даже то что явно (в кнопках от 1 до 10) нерпописано. Может ка кто ЖЁСТКО задать именно наши кнопки остальные "пропускать", думаю попробую сделать через:







if (val==0x41BEB04F) return 5;    //right      KEY5
if (val!=0x41BEB04F) return 0;

т.е. приравнивать НЕЧТО нажатое к = 0

И по blink - подскажите почему, это засранец мигает "нетам" где прописана координата для печати на lcd.print

Блин, финиш был почти рядом, рановато пока в "совсем" готовый продукт отдавать наше произведение.

Забыл упомянуть - убрал из кода меню и кнопок ВСЕ задержки с millis - потому как "дребезга" нет, а появились лишние "затыки", но есть"мусор" а от него просто задержками неизбавится.

bwn
Offline
Зарегистрирован: 25.08.2014

По IR меня смутило, что вы закомментили 192 строку " else return 0;". По идее она чистила возврат, без нее может сохранится какое то значение. А ваша предложенная конструкция будет хорошо только на одну кнопку работать. Теоретически можно реализовать исполнение на две кнопки. Пока не нажал "Power" остальные игнорируются, после последнего нажатия секунд через 20 опять в этот режим. И попробуйте в коде расставить кнопки по возрастанию return.

По blink, это 369 строка. В начальном коде, pos  устанавливался на знакоместо (там где он меняется, магические цифры это оно и есть) и там мигал блинк. У себя вы эту логику нарушили.

Thorn
Offline
Зарегистрирован: 07.11.2014

bwn пишет:

По IR меня смутило, что вы закомментили 192 строку " else return 0;". По идее она чистила возврат, без нее может сохранится какое то значение.

немного нетак, а возьму смелость поправить. У нас код по "умолчанию" это ПОСТОЯННОЕ нажатие или как в тесте выше "удержание". Тоесть когда мониторится serial кнопок даже после старта ардуинки бегут 0, бегут мягко сказано - с tft дисплейчиком и его прорисовкой идууут. а вот когда я посмотрел эту кухню на lcd ? это я вам скажу летят так... вобщем иной раз мусора столько с IR .... куски кода, куски солнца и видимо света от всего включая что попадает с ЛЕДов подсветки аквариума. Я почему и убрал ВСЕ задержки - они ещё сильнее усугубляли прием\раскодировку видимо. 

Именно поэтому "нужно обнулять" результат с IR после каждого нажатия, именно поэтому я вставил val=0 в конце каждой функции диммирования, дабы ненажималась кнопка и шёл 0. 

На то, что принимается и выводится в serialPrint 192 стркоа никак неможет повлиять - я уже это проверил. Кнопки я уже расставил в "возрастание".

bwn пишет:

По blink, это 369 строка. В начальном коде, pos  устанавливался на знакоместо (там где он меняется, магические цифры это оно и есть) и там мигал блинк. У себя вы эту логику нарушили.

ВОт почему и воспрошаю, на экранчике счас красиво и можно-ли ЗАСТАВИТЬ blink работать там где нужно сейчас а не в 0 строке.

Блин. как же тогда правильно заставить работать только то, что определено - остальное или игнор или 0

 

bwn
Offline
Зарегистрирован: 25.08.2014

Почему ухватился за 192 строку - функция обязана что то вернуть, если убираем это условие - возникает нестабильность. Что вернет функция по своему обязательству? Возможно в данном случае непринципиально, но логические прорехи надо убивать на корню, где и как они вынырнут предугадать сложно.

По блинку надо попробовать lcd.setCursor(pos,1); ну и возможно придется в строках 369-382 поигратся. У вас там delay(200) закомменчен, у меня без него не мигало.

Thorn
Offline
Зарегистрирован: 07.11.2014

ПО всем эти "атвонажатиям" видите-ли в чём дело, на tft и макетной Меге всю неделю почти код сидел на работе, тестились включения, таймер и небыло НИЧЕГО, может "тупо" нерпопай где, да и наводки, всеже рядом с низковольтной но СИЛЬНОтоковой проводкой, ну не шутка ли, подходиш к дисплейчику а там "вход в функцию меню" без подключенного IR -сенсора. Вот монтаж, хотя тут без хорошей экранки неузнать результата.

 и  и 

Провода к rts (часикам) и барометру на BMP085 (последовательно к часикам) не длиннее 3 см! Читал что по I2C длинна имеет значние. ds18b20 подключал к экранированному. Но на фото видно что монтажная плата с распайкой навесных элементов (резисторы, пины и прочее) подключена к ножкам Ардуинки проводами длинной до 7-10 см, уж некритично ли это.

Thorn
Offline
Зарегистрирован: 07.11.2014

bwn пишет:

Почему ухватился за 192 строку - функция обязана что то вернуть, если убираем это условие - возникает нестабильность. Что вернет функция по своему обязательству? Возможно в данном случае непринципиально, но логические прорехи надо убивать на корню, где и как они вынырнут предугадать сложно.

По блинку надо попробовать lcd.setCursor(pos,1); ну и возможно придется в строках 369-382 поигратся. У вас там delay(200) закомменчен, у меня без него не мигало.

192 вернул взад, всеже - опыт и спорить нестану.

ПО блинку счас попробую, закоментил потому как блин "где зря" просто мешал :) и Кстати ближе к delay(500); плавнее прорисовка но зачем и без того небыструю IR-ду замедлять.

Thorn
Offline
Зарегистрирован: 07.11.2014

Ну вот и нашлась помойму "соль" всех бед с IR. Тупо запустил serialPrint на кнопках. Таймер показал что дескать свет то ещё рано выключать и наал включение, после определенного периода вместо 0 поперло такое вот (это и есть мусор, который при определённых обстоятельствах, генетически мог дорасти до нужного HEX кода кнопки)

мусор в Serial.println(val,HEX);

FFFFFFFF
 
2AD785FC

6A19A4D3
 
22AE7A2A
 
22AE7A2A
 
22AE7A2A
 

 

и когда диммирование закончилось

0

0

0

Само собой когда я касался провода БЕЗ IR-сенсора то код менялся уже "революционно" и готов был родится нужны HEX :)

Здается дело всеже в "наводках" и драйверах ЛЕДов, они падла чуть попискивают при PWM и по отзывам фонят, вот и меня коснулась.