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

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

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

Имею Мегу2560, дисплей4-х строчный, всякие термодатчики на ds18b20 и в теромусадке, релюшки (8 канальные).

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

Осталось дождаться посылки с АЛИ часов типа Ds3231 AT24C32 IIC для таймеров и вот тут для меня засада.

Нужно включать модули реле с разницей к примеру в 10 минут для удобства. При этом перед включением первого реле нужно задать уровень 0 для выхода на несколько пинов ( вообще нужно 10) для диммирования (чтоб недержать светодиоды постоянно включёнными). Тоесть я плавно зажгу свет в банке (к примеру в течении 5 минут значения будут от 0 до 254) а потом можно и довключить остальные светики. И тоже самое но в обратно порядке для отключения. Всего два цикла в день на утро и вечер. Никаких там молний и прочего в банке неприемлю, прост осчас страдают рыбки от мгновенного включения 10 сборок по 10Вт :(

И самое самое сложное - осуществить возможность устанавливать темпиратуры включения\отключения реле с помощью кнопок (сейчас приходится заливать сктчем) и тоже самое для таймеров. 

Кнопки сами будут чисто обычные. стандартно 4 штучки (верх\низ\право\лево). Выручайте братцы. Понимаю что нафиг никому ненадо однако поверьте адептов на аквафоруме будет повторить много. Вот моя темка про LED http://www.aqa.ru/forum/Led-svet-na-530-litrov-ne-Cree-diodyi-s-Ali-vzamen-t5-280537-page1 (вдруг есть аквариумисты :) )

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

Вот такой скетчик я уже наваял для темпиратуры:

#include <Wire.h>                                    
#include <LiquidCrystal_I2C.h>  
#include <OneWire.h>
LiquidCrystal_I2C lcd(0x27,20,4);
const int RelayChn1 = 13;
const int RelayChn2 = 14;
int TSensorPin = 10;                               
OneWire ds(TSensorPin); 
byte data[12];
byte addr1[8] = {0x28, 0xF2, 0x29, 0xEB, 0x05, 0x00, 0x00, 0xE1};  //адрес датчика DS18B20
byte addr2[8] = {0x28, 0x86, 0x48, 0xEA, 0x05, 0x00, 0x00, 0xF6};  //адрес датчика DS18B20
unsigned int raw;
float temp1, temp2;

float t0 = 25.5;
float t00 = 26.5;
float tGistrsis = 0.5;

void setup() {
  Serial.begin  (9600);
  Wire.begin();
  pinMode(RelayChn1,OUTPUT);
  digitalWrite(RelayChn1,LOW); 
  digitalWrite(RelayChn2,LOW); 
    lcd.init();                                     
    lcd.setBacklight(1);  
    lcd.clear();
    lcd.setCursor(2, 0);
    lcd.print("Aqua  Controller");
    lcd.setCursor(2, 1);
    lcd.print("      v1.0      ");
  delay(3000);
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("t1=");
    lcd.setCursor(10, 1);
    lcd.print("t2=");
    lcd.setCursor(0, 3);
    lcd.print("R1=");
    lcd.setCursor(6, 3);
    lcd.print("R2=");     
}

void loop(){
  temp1 = DS18B20(addr1);
  temp2 = DS18B20(addr2);
  
  Serial.print("Temp1=");
  Serial.print(temp1);
  Serial.print("\t");
  Serial.print("Temp2=");
  Serial.println(temp2);
  Serial.flush(); 
  lcd.setCursor(3, 1);                       
  lcd.print(temp1);
  lcd.setCursor(13, 1);                          
  lcd.print(temp2);
  
   if (temp1 < t0-tGistrsis/2)
      {
        digitalWrite(RelayChn1,LOW);                 //Устанавливаем на входе релейного модуля НИЗКИЙ уровень - реле срабатывает
        lcd.setCursor(3, 3);
        lcd.print("0");
      }     
    else if (temp1 > t0+tGistrsis/2)
      {
        digitalWrite(RelayChn1,HIGH);                //Устанавливаем на входе релейного модуля ВЫСОКИЙ уровень - реле выключается
        lcd.setCursor(3, 3);
        lcd.print("1");
      } 
   if (temp2 < t00-tGistrsis/2)
      {
        digitalWrite(RelayChn2,LOW);                 //Устанавливаем на входе релейного модуля НИЗКИЙ уровень - реле срабатывает
        lcd.setCursor(9, 3);
        lcd.print("0");
      }     
    else if (temp2 > t00+tGistrsis/2)
      {
        digitalWrite(RelayChn2,HIGH);                //Устанавливаем на входе релейного модуля ВЫСОКИЙ уровень - реле выключается
        lcd.setCursor(9, 3);
        lcd.print("1");
      }    
      
}

//====================================================================//==================================================================================
//                             Считывание температуры
//====================================================================//==================================================================================
float DS18B20(byte *adres){
  ds.reset();
  ds.select(adres);
  ds.write(0x44,1); // start conversion, with parasite power on at the end
  delay(1000);
  ds.reset();
  ds.select(adres);
  ds.write(0xBE); // Read Scratchpad
  for (byte i = 0; i < 9; i++) { // we need 9 bytes
    data[i] = ds.read ();
  }
  raw =  (data[1] << 8) | data[0];//=======Пересчитываем в температуру
  float celsius =  (float)raw / 16.0;
  return celsius;
}

 

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

Можете пояснить - вы планируете диммировать 10Вт светодиоды?

Чем?

Данные вводить лучше энкодером.

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

Gippopotam пишет:

Можете пояснить - вы планируете диммировать 10Вт светодиоды?

Чем?

Данные вводить лучше энкодером.

Диммировал я на своих пробных 10Вт-х сборках драйвером на PT4115, его 8-ая ножка и есть вход. Пробовал просто потенциалом напряжения от 0,5 до 2,5В. так вот при 2,5В и выше он полностью открыт а ниже 0,5В зарыт и света нет.

Так вот я брал стандартный скетч для "Мигаем светодиодом" с delay и без оного и подавал с 10 пина на вход драйвера. Все отлично получалось однако слишком быстро, несмотря на то что делал шаг приращения в 1 .В моем случае нужно "растянуть этот приращение до 10 мин.

 

Есть у меня такой скетчик, однако пока нет модуля часов опробовать его немогу. Пока мне нужно кнопки вставить в имеющийся уже код для терморегулировки :)

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

А вот скетч с моего аквафорума, где уже реализовано диммирование по типу закат-рассвет.  Мне лиш нужно сделать ДВА таких, для утра и вечера:

#include <Wire.h>                                                                           //Подключаем библиотеку для использования I2C интерфейса с модулем RTC 
#include <RTClib.h>                                                                         //Подключаем библиотеку для использования модуля часов реального времени RTC


#define PWM_MIN 0                                                                            //Если необходим ток покоя на LED - изменить эту константу
#define PWM_MAX 255                                                                          //Если необходимо ограничить максимальную яркость - уменьшить значение
#define PWM_PIN 9                                                                            //Пин порта, где будет ШИМ

#define mn 60UL                                                                              //Дополнительные константы для удобства
#define hr 3600UL                                                                            //Отражают соответствующие количества секунд 
#define d 86400UL

RTC_DS1307 RTC;          

long sunrise_start = 11*hr+15*mn;                                                             //Начало восхода в 9 - 45 
long sunrise_duration = 30*mn;                                                                //Длительность восхода 30 минут     
long sunset_start = 21*hr+00*mn;                                                              //начало заката в 21-15
long sunset_duration = 30*mn;                                                                 //Длительность заката 30 минут 
//********************************************************************************************
void setup(){


  Wire.begin();                                                                               //Инициируем I2C интерфейс
  RTC.begin();                                                                                //Инициирум RTC модуль
  analogWrite(PWM_PIN, PWM_MIN);                                                              //Пишем в порт минимальное значение 
  //RTC.adjust(DateTime(__DATE__, __TIME__));
}                                                                                             // КОНЕЦ ИНИЦИАЛИЗАЦИИ

//********************************************************************************************
void loop()                                                                                   // ПРОГРАММЫй безусловный ЦИКЛ 
{
  
    long pwm; 
    DateTime myTime = RTC.now();                                                              //Читаем данные времени из RTC при каждом выполнении цикла
    long Day_time = myTime.unixtime() % 86400;                                                //сохраняем в переменную - время в формате UNIX
//*********************************************************************************************
//           обработка интервала до восхода и после заката
//*********************************************************************************************
    if ((Day_time<sunrise_start) ||                                                           //Если с начала суток меньше чем начало восхода 
        (Day_time>=sunset_start+sunset_duration)) {                                           //Или больше чем начало заката + длительность
                  pwm = PWM_MIN;                                                                    //Величина для записи в порт равна минимуму  

//*********************************************************************************************
//           обработка интервала восхода 
//*********************************************************************************************
    }else if ((Day_time>=sunrise_start) &&                                                    //Если с начала суток больше чем начало восхода
              (Day_time<sunrise_start+sunrise_duration)){                                     //И меньше чем начало восхода + длительность 

                  pwm =  ((Day_time - sunrise_start)*(PWM_MAX-PWM_MIN)) / sunrise_duration;   //Вычисляем для рассвета величину для записи в порт ШИМ

//*********************************************************************************************
//           обработка интервала заката 
//*********************************************************************************************
    }else if ((Day_time>=sunset_start) &&                                                     //Если  начала суток больше чем начало заката и меньше чем  
              (Day_time<sunset_start+sunset_duration)){                                       //начало заката плюс длительность

                  pwm = ((sunset_start+sunset_duration - Day_time)*(PWM_MAX-PWM_MIN)) / sunrise_duration; //Вычисляем для заката величину для записи в порт ШИМ
 
//********************************************************************************************
//           обработка интервала от конца рассвета и до начала заката,
//           когда свет должен быть включен на максимальную яркость 
//********************************************************************************************
    }else {
                  pwm = PWM_MAX;                                                              //Устанавливаем максимальную величину для записи в порт ШИМ 
    }
    
    analogWrite(PWM_PIN, pwm);            //Пишем в порт вычисленное значение           
   
  
}//------------Конец ЦИКЛА-----------------------------

 

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

Для меня это МЕГА-круто но избыточно, морские банки в плане автоматизации нуждаются на несколько порядков выше обычных травников. У нас в основном свет (иногда закат\рассвет с применением красных), подача и контроль СО" и подача УДО. На любителя это автокормушки там и прочее.

Спасибо за сссылку - там товарищ заморочился вообще все с "нуля" практически. Да и сам я несмогу переработать его код под свое (дисплей и кнопки) оборудование.

Кстати с кнопками я погорячился насчёт 4-х штук. Надо минимум 6. Забыл про select и menu :)

Если у кого есть хоть минутка свободная буду рад, хоть копипастом....

Logik
Offline
Зарегистрирован: 05.08.2014

///Для меня это МЕГА-круто но избыточно  - да. При правильном подходе и Микро хватит, при неправильном - ничё не поможет.

///На любителя это автокормушки там и прочее.  - а зря! автоматическая кормушка на случай отпуска не лишнее. Я ещё и УЗ датчик растояния притулить хочу для контроля уровня воды. Чего ему в ящике валятся раз уже куплен ;)

///Кстати с кнопками я погорячился насчёт 4-х штук. Надо минимум 6 - так будет всегда! Кнопок и экрана всегда немного нехватает. Пока не поздно, выкинте эти 4 кнопки напару с 4-х строчным дисплеем, да возьмите LCD дюйма на 3 с тачем. На нем и кнопки нарисуете, сколько нужно и когда нужно. И в корпус совать проще. А за одно и AT24C32 как бы не востребована. С экраном вместе идет ридер для мини SD карты. В либах к ней тоже всё уже есть;) И ещё, похоже Вы забыли одну детальку. ИК приемник для управления всем хозяйством не вставая с дивана. Он к стати и проблему кнопок поможет решить. 

А дальше не спеша прогу писать, годика на два хватит :) Степ бай степ, неспешно развивать. Главное, чтоб железо не апгрейдить по ходу.

 

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

Кнопок с резисторами хоть 100шт на аналоговый вход (хотя право-лево на 4стр.дисплее????), а с тачем действительно нагляднее.

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

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

bwn пишет:

Кнопок с резисторами хоть 100шт на аналоговый вход (хотя право-лево на 4стр.дисплее????), а с тачем действительно нагляднее.

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

Вот вот, с этого места можно конкретно (кодом прошу пожалста),  чуял что можно при стабильном подключении (пока ардуинка включена постоянно) и без часиков замутить и диммирование и программы таймера. Главное правильно задать точку начала и в это время включить ардуинку а дальше кодом будут выполнены замеры секунд.

Парни выручайте. И по поводу кнопок, много инфы бдет и темпиратура и положения реле и приделаю мольтметр (так чтоб было) смотреть на своих каналах ЛЕДов напряжение. Да и установить время для таймеров, темпиратуру. Или к примеру вручную вкл\выкл расписание таймера и принудительно включить реле.

Блин задумка ессть а реализовать лиш на уровне чегото скетча заменив или убавив чтото.

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

bwn
Offline
Зарегистрирован: 25.08.2014
#if defined(ARDUINO) && ARDUINO >= 100 //Определение версии IDE
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#include <Wire.h>                   //Библиотека I2C
#include <DS1307.h>                 //Библиотека для часов
#include <LiquidCrystalCyr_I2C.h>   //Библиотека LCD I2C русифицированная
#include <OneWire.h>                //Для DS18B20
#include "DHT.h"                    //Библиотека DHT
#include <CyberLib.h>               //Библиотека от Cyber-Place.ru
volatile uint8_t tic, Dimmer1, Dimmer2; //0-макс свечение, 200-мин.свечение, 255-выключить
uint8_t data;

LiquidCrystalCyr_I2C lcd(0x20,16,2); //Дисплей 16х2, адрес 0х20

OneWire  ds(8);         //Пин подключения DS18B20
#define DHTPIN 7        // what pin we're connected to
#define DHTTYPE DHT21   // DHT 21 (AM2301)

DHT dht(DHTPIN, DHTTYPE);


//************ Блок инициализации переменных**********

int key=0;            //Переменная кода кнопки
long metDat[11];      //Массив для хранения времени счетчиков
byte flag[8];         //0- флаг признака влажности
                      //1- флаг включения прерывания
                      //2- флаг коррекции времени
                      //3- флаг признака нагрева
                      //5- флаг признака усреднения
                      //6- признак числа в меню
                           //1- BYTE
                           //2- INT
                           //3- LONG
                     //7- Признак аварии     
                      
const int knop[]={510,825,687}; 
const long zadTime[]={50,750,1000,3000,20000,60000,180000,10000,3600000};

//************Блок переменных для часов****************
byte Hour, Mn, Sk, Dy, Mes, TimeSum;
int Yr;               //Переменные часов
byte TimeRazd=0;      //Разделитель

//************Конец блока переменных для часов*********

//**********Блок переменных для DS18B20 и AM2301**************
  
  byte present = 0;
  byte datadall[12];         //Данные температуры
  byte addr[9];              //Переменная считанных данных адреса
  int HighByte, LowByte, Fract, TReading, HighTemp;
  float Whole, Tc_100;
  byte count=0;
  byte jDall;
  float tDall[4];       //Массив температур датчиков Даллас и DHT
  long thVrem[2];        //Массив темп. и вл. для обработки
                        //[0] - влажность
                        //[1] - температура
  
//***********Конец блока переменных для DS18B20****************

//************Текстовые переменные для вывода на экран**********

//char* str[]={" "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "};

const char* str[]={"                ","   Дата/Время   ",
" Время осв.минут"," Температура `C ","   Влажность %  ",
"      Выход     ","  Редакт.наж.ОК ","      Год       ",
"     Месяц      ","     День       ","     Часы       ",
"    Минуты      ","   Корр.время   ","  Корр.темп.низ ",
"  Корр.темп.ул. ","  Корр.темп.верх","  Корр.влажность",
"Ту Тп Вл","Нагреть дат.пом.","Нажмите  Set 3с.",
"Нажмите  UP  3с.","Нажмите DOWN 3с."};

//************Переменные для обработки меню********************

int eeDat;  //Номер ячейки в EEPROM хранящей значение, значение в устанавливаемом регистре для времени
byte prStr; //Номер текстовой строки из str[]
int znMax; //Максимально допустимое значение
int znMin; //Минимально допустимое значение
byte shag;  //Шаг приращения, убывания

//************Блок исполнительных переменных***********
const byte alarm=9;         //Номер пина тревоги
const byte ventV=10;        //Номер пина вытяжного вентилятора
const byte ventP=11;        //Номер пина перемешивающего вентилятора
const byte osv=12;          //Номер пина освещения
const byte led=13;          //Индикатор и аварийный сброс

//************Конец блока исполнительных переменных**********

//**********Конец блока инициализации переменных*************

//***********БЛОК НАЧАЛЬНОЙ ИНИЦИАЛИЗАЦИИ********************
void setup () 
{
   Serial.begin(9600);
  
   Wire.begin();            // Инициализация I2C
   lcd.init();              // Инициализация lcd             
   lcd.backlight();         // Включаем подсветку
   dht.begin();             // Инициализация DHT
        
 //**********Инициализация исполнительных выводов*********
   pinMode(alarm, OUTPUT);
   pinMode(ventV, OUTPUT);
   pinMode(ventP, OUTPUT);
   pinMode(osv, OUTPUT);
   pinMode(led, OUTPUT);
   digitalWrite(alarm, LOW);
   digitalWrite(ventV, LOW);
   digitalWrite(ventP, LOW);
   digitalWrite(osv, HIGH);
   digitalWrite(led, HIGH);
  
 //*******Инициализация диммеров*********
 
   Dimmer1=255;
   Dimmer2=255;
  
 //***********Инициализация массива задержек****************************** 
   
   byte i;
   for(i=0; i<11; i++)
   {
     metDat[i]=millis();
   }
 //***** metDat[0] счетчик задержки на мигание сторожевым светододом (1000 мсек.)
 //***** metDat[1] счетчик работы освещения от момента включения
 //***** metDat[2] счетчик времени нажатия клавиши, необходимо обнулять
 //***** metDat[3] счетчик времени нахождения в меню  
 //***** metDat[4] счетчик времени для исключения ложных срабатываний в меню
 //***** metDat[5] счетчик задержки DS18B20
 //***** metDat[6] счетчик периода считывания DS18B20
 //***** metDat[7] счетчик времени работы вытяжного вентилятора
 //***** metDat[8] счетчик задержки включения выт.вентилятора
 //***** metDat[9] счетчик времени работы диммера (нагрев)
 //***** metDat[10] счетчик времени усреднения температуры


//************Проверка и установка констант в EEPROM***********************

if (ReadEEPROM_Word(0)<400) {WriteEEPROM_Word(0, 510);}  //Значение клавиши Set
if (ReadEEPROM_Word(2)<400) {WriteEEPROM_Word(2, 825);}  //Значение клавиши UP
if (ReadEEPROM_Word(4)<400) {WriteEEPROM_Word(4, 690);}  //Значение клавиши DOWN
if (ReadEEPROM_Byte(6)<1||ReadEEPROM_Byte(6)>20) {WriteEEPROM_Byte(6, 5);} //Время освещения по умолчанию
if (ReadEEPROM_Byte(7)<8||ReadEEPROM_Byte(7)>20) {WriteEEPROM_Byte(7, 10);} //Температура по умолчанию
if (ReadEEPROM_Byte(28)<30||ReadEEPROM_Byte(28)>80) {WriteEEPROM_Byte(28, 40);} //Влажность по умолчанию
long t=ReadEEPROM_Long(2);
if (t<-29||t>29) {WriteEEPROM_Long(2, 0);} //Корр. времени по умолчанию
t=ReadEEPROM_Long(3);
if (t<-5||t>5) {WriteEEPROM_Long(3, 0);} //Корр. темп.помещения низ даллас
t=ReadEEPROM_Long(4);
if (t<-5||t>5) {WriteEEPROM_Long(4, 0);} //Корр.темп.улица даллас
t=ReadEEPROM_Long(5);
if (t<-5||t>5) {WriteEEPROM_Long(5, 0);} //Корр.темп.помещение DHT
t=ReadEEPROM_Long(6);
if (t<-15||t>15) {WriteEEPROM_Long(6, 0);} //Корр.влажности DHT


//***********Блок проверки и редактирования адресов датчиков Даллас**********
   
   byte dall[2][9];      //Массив адресов датчиков даллас
   byte j=0;             //Переменная кол-ва датчиков
   byte flagdal=0;       //Переменная признака
   
 label_2:
   if ( !ds.search(addr))  //Проверка окончания перечня адресов
   {
    ds.reset_search();     //Сброс опроса адресов
    goto label_1;          //Переход на цикл сравнения адресов с EEPROM
   } 
  
  for (i=0; i<8; i++)              //Создание адресного массива
     {  dall[count][i]=addr[i]; }
     dall[count][8]=1;
     count++;
     
     if (OneWire::crc8(addr, 7) != addr[7]) //Проверка на  соотв.контрольной суммы
     {
     ds.reset_search();
     count=0;
     return;
     }
     goto label_2;          //Возврат на считывание адресов датчиков
     
 label_1:                   //Проверка соответствия адресов
      
     for (j=0; j<count; j++)
     {
     flagdal=0;              //Обнуление переменной признака
        if (dall[j][8]!=0)   //Проверка условия соответствия адресов
        {
          for (i=0; i<8; i++) //Побайтовое сравнение адреса
          {
            if (dall[j][i]!=ReadEEPROM_Byte(i+31+j*9)) //Сверка значения
            { flagdal=1; }                             //Признак несоответствия
          dall[j][8]=flagdal;                         //Запись признака несоответствия 
          }
        }
     }
     
     flagdal=0;                    //Обнуление признака
     for ( j=0; j<count; j++ )     //Считывание признака несоответствия
     {
     flagdal=flagdal+dall[j][8];   //При 0 все значения соответствуют
     }
     
     if (flagdal!=0)                //Проверка несоответствия адресов
     {
       for ( j=0; j<count; j++ )    //Цикл перебора датчиков
       {
         for ( i=0; i<8; i++ )       //Цикл перебора байт адреса
         {
         WriteEEPROM_Byte( i+31+j*9, dall[j][i] ); //Запись байта в EEPROM
         WriteEEPROM_Byte( i+32+j*9, 0 );          //Очистка байта признака датчика
         }
       }   
 }
 //********Конец блока проверки и редактирования адресов датчиков*************

 //********Блок проверки принадлежности датчика**************
 
  DallasStartTemp();                       //Старт конвертации Далласов
  delay(zadTime[1]);                       //Задержка на конвертацию
  
   for (jDall=0; jDall<count; jDall++)    //Перебор адресов
 {
     dallRead();                          //Считывание температуры
     
   if (jDall==0)                         //Определение датчика
   {
     tDall[0]=Whole;                      //Присвоение температуры
   }
   if (jDall==1)                         //Определение датчика
   {
     tDall[1]=Whole;                    //Присвоение температуры
   }
      tDall[2]=dht.readTemperature();     //Температура с DHT
      tDall[3]=dht.readHumidity();        //Влажность с DHT
 }
  
    int tempDall;                       //Целочисленная переменная температуры
    lcd.clear();                        //Очистка экрана
  label_3:                            //Метка возврата
    if (ReadEEPROM_Byte(39)+ReadEEPROM_Byte(48)==0) //Проверка наличия признака 
 {
    DallasStartTemp();               //Старт конвертации температуры
    lcd.setCursor(0,0);              //Установка курсора
    lcd.print(str[18]);              //Печать строки
    delay(zadTime[1]);                      //Задержка на конвертацию
    
     for (jDall=0; jDall<count; jDall++) //Перебор датчиков
   {
     dallRead();                        //Считывание температуры датчика
     
   if (jDall==0)                        //Определение датчика
     {  
       tempDall=abs(tDall[0]-Whole);    //Расчет разницы в целочисленном значении
       if (tempDall>5)                  //Проверка условия
       {
       WriteEEPROM_Byte(39,1);          //Запись байта признака если ДА 
       }
     }
   if (jDall==1)                       //Определение датчика
     {
       tempDall=abs(tDall[1]-Whole);  //Подсчет разницы в целочисленном значении
       if (tempDall>5)                //Проверка условия
       {
        WriteEEPROM_Byte(48,1);       //Запись байта признака
       }
     }
    }
        if (metDat[0]+zadTime[2]<millis())            //Проверка условия
      {
      metDat[0]=millis();                  //Установка задержки
      digitalWrite(led,!digitalRead(led)); //Инверсия значения
      }
 goto label_3;                       //Возврат к проверке принадлежности
 }
 lcd.clear();                       //Очистка экрана

 //*****Конец блока определения принадлежности Далласов*******    
         
//************Инициализация диммеров*************************************** 
  D4_Out; D5_Out;          //Настраиваем порты на выход
  D4_Low; D5_Low;          //установить на выходах низкий уровень сигнала
  D2_In;                   //настраиваем порт на вход для отслеживания прохождения сигнала через ноль  
  
//CHANGE – прерывание вызывается при любом изменении значения на входе; 
//RISING – вызов прерывания при изменении уровня напряжения с низкого (Low) на высокий(HIGH) 
//FALLING – вызов прерывания при изменении уровня напряжения с высокого (HIGH) на низкий (Low) 
    //attachInterrupt(0, detect_up, LOW);  // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
    StartTimer1(halfcycle, 40); //время для одного разряда ШИМ
    StopTimer1(); //остановить таймер     

//********************Запуск счетного выхода RTC на 8192Гц.*******************
    Wire.beginTransmission(0x68);     
    Wire.send(0x07);
    Wire.send(B00010010);
    Wire.endTransmission();
 }
//*************КОНЕЦ БЛОКА НАЧАЛЬНОЙ ИНИЦИАЛИЗАЦИИ**************

//********************ОБРАБОТКА ПРЕРЫВАНИЙ*******************************
void halfcycle()  //прерывания таймера
{ 
  tic++;  //счетчик  
  if(Dimmer1 < tic ) D4_High; //управляем выходом
  if(Dimmer2 < tic ) D5_High;  //управляем выходом 
}

void  detect_up()  // обработка внешнего прерывания. Сработает по переднему фронту
{  
 tic=0;             //обнулить счетчик
 ResumeTimer1();   //запустить таймер
 attachInterrupt(0, detect_down, HIGH);  //перепрограммировать прерывание на другой обработчик
}  

void  detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту
{   
 StopTimer1(); //остановить таймер
 D4_Low; D5_Low; //логический ноль на выходы
 tic=0;       //обнулить счетчик
 attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
} 
//*************КОНЕЦ ОБРАБОТКИ ПРЕРЫВАНИЙ**********************

//*****************ФУНКЦИИ*************************************

//*************Блок печати меню********************************

void printMenu()
{
          lcd.setCursor(0,0);               //Установка курсора
          lcd.print(str[0]);                //Очистка строки вывода
          lcd.setCursor(0,0);               //Установка курсора
          lcd.print(str[prStr]);            //Вывод названия пункта меню
          lcd.setCursor(0,1);
          lcd.print(str[6]); 
}

//*********Конец блока печати меню*****************************

//**************Блок бип***************************************

void pic()
{
  digitalWrite(alarm,HIGH); 
  delay(zadTime[0]);
  digitalWrite(alarm,LOW);
  
}
//************Конец блока бип**********************************
 
//*****************Считывание температуры Далласов**********

void dallRead()
{
  if (flag[7]!=1)
  {
    flag[7]=0;
  }
  byte i;                         //Переменная для цикла
  for (i=0; i<10; i++)            //Цикл считывания адреса
     {
       addr[i]=ReadEEPROM_Byte(i+31+jDall*9);  //Чтение адреса из EEPROM
     }
   present = ds.reset();          //Обращение к датчикам
   ds.select(addr);               //Выбор датчика
   ds.write(0xBE);                //Команда считывания
   for ( i = 0; i < 9; i++)       //Цикл считывания данных
    {
      datadall[i] = ds.read();    //Считывание данных в массив
    }
   if (OneWire::crc8(datadall, 8)!= datadall[8])
   {
     flag[7]=2;
   }
   
   LowByte = datadall[0];         //Младший байт
   HighByte = datadall[1];        //Старший байт
   HighTemp = datadall[2];
   TReading = (HighByte << 8) + LowByte;   //Сдвиг с присвоением
   Tc_100 = (6 * TReading) + TReading / 4; //Расчет температуры для 12бит.
   Whole = Tc_100 / 100;                   //Температура
   if (Whole<-50 || Whole>70)
   {
     flag[7]=2;
   }
}

//*****Конец функции считывания температуры Даллас************

//********Функция печати температуры и влажности**********
 
 void DallasPrintTemp()
 {
   lcd.setCursor(0,0);
   lcd.print(str[17]);
   lcd.setCursor(0,1); 
   long t=ReadEEPROM_Long(4);
   lcd.print(tDall[0]+t,0);
   lcd.print(" ");
   long tt=ReadEEPROM_Long(3);
   t=ReadEEPROM_Long(5);
   lcd.print(((tDall[1]+tt)+(tDall[2]+t))/2,0);
   lcd.print(" ");
   t=ReadEEPROM_Long(6);
   lcd.print(tDall[3]+t,0);  
 }

//************Конец функции печати температуры и влажности*****

//******Функция запуска конвертации температуры с датчиков Даллас*****

 void DallasStartTemp()
  {
      ds.reset();          //Сброс датчиков
      ds.write(0xCC);      //Команда инициации
      ds.write(0x44);      //Команда на конвертирование
  }
 
//******Конец функции запуска конвертации температуры с DS18B20*******

//**********Функция присвоения температуры и влажности с датчиков***********

 void DallasTempPresent()
 {
   
   for (jDall=0; jDall<count; jDall++)     //Цикл перебора датчиков
   {
     dallRead();                           //Считывание температуры датчика
     
   if (flag[7]==0||flag[7]==1)
 {  
   if (jDall==0)                           //Определение датчика
   {
     tDall[ReadEEPROM_Byte(39)]=(tDall[ReadEEPROM_Byte(39)]+Whole)/2;          //Присвоение температуры датчику
   }
   if (jDall==1)                           //Определение датчика
   {
     tDall[ReadEEPROM_Byte(48)]=(tDall[ReadEEPROM_Byte(48)]+Whole)/2;          //Присвоение температуры датчика
   }
 }
}
   
   float t, h;
   t=dht.readTemperature();
   h=dht.readHumidity();
   if (isnan(t) || isnan(h))
   {
     flag[7]=1;
   }
   else
   {
     if (flag[7]==1) { flag[7]=0; }
   tDall[2]=(tDall[2]+t)/2;    //Температура с DHT
   tDall[3]=(tDall[3]+h)/2;    //Влажность с DHT
   }
   
   DallasStartTemp();                     //Запуск новой конвертации
   metDat[5]=millis()+zadTime[5];         //Запуск переменной ожидания
   
 }
 
//*****Конец функции считывания температуры с датчиков********

//********Блок чтения времени****************
    void readTime()
    {
         Hour=(RTC.get(DS1307_HR,true));
         Mn=(RTC.get(DS1307_MIN,false));
         Sk=(RTC.get(DS1307_SEC,false));
         Dy=(RTC.get(DS1307_DATE,false));
         Mes=(RTC.get(DS1307_MTH,false));
         Yr=(RTC.get(DS1307_YR,false));
  }
//********Конец блока чтения времени***************

//*******Блок ежесуточной коррекции времени********

   void korTime()
   {
    if (Hour==23&&Mn==58&&Sk==30&&flag[2]==0)   //Определение времени коррекции
    { 
    readTime();                                //Считывание с DS1307
    RTC.stop();                                //Остановка часов
    byte i=Sk;                                 //Присвоение значения секунд
    long j=i+ReadEEPROM_Long(2);               //Значение секунд после коррекции
    byte k=j;                                  //Перевод в целочисленное значение
    Sk=k;                                      //Присвоение нового значения
    flag[2]=Hour+Mn;                            //Запись признака коррекции
    writeTime();                               //Запись нового значения времени
    }
   }

//******Конец блока ежесуточной коррекции времени*****

//***********Блок вывода ВРЕМЕНИ на дисплей*********
    void printTime()
{    
   if (TimeSum!=Hour+Mn+Sk)        //Проверка на изменение значения
 {      
       TimeSum=Hour+Mn+Sk;         //Обновление признака
       if (flag[2]!=0)          //Проверка условия
       {
         if (flag[2]<Hour+Mn) { flag[2]=0; } //Присвоение значения если ДА
       } 
       
       korTime();                 //На функцию ежесуточной коррекции времени
       TimeRazd=!TimeRazd;       //Инверсия разделителя
   
    lcd.setCursor(11, 0);         // Курсор в 11поз. 1-й строки
    lcd.print(Hour/10);             // Десятки часов
    lcd.print(Hour%10);             // Единицы часов
       lcd.print(':');            // Разделитель
    lcd.print(Mn/10);             // Десятки минут
    lcd.print(Mn%10);             // Единицы минут
       //lcd.print(':');           // Разделитель
    //lcd.print(Sk/10);  // Десятки секунд
    //lcd.print(Sk%10);  // Единицы секунд
        lcd.setCursor(11, 1);     // Курсор в 11поз. 2-й строки 
    lcd.print(Dy/10);            // Десятки дней
    lcd.print(Dy%10);            // Единицы дней
        lcd.print('-');          // Разделитель     
    lcd.print(Mes/10);           // Десятки месяца
    lcd.print(Mes%10);           // Единицы месяца  
 } 
else
  {
    if (TimeRazd==true)          //Мигаем разделителем
     {
      lcd.setCursor(13, 0);
      lcd.print (':');
     }
    else
        {
         lcd.setCursor(13, 0);
         lcd.print(' ');
        }         
   }
}
//***********Конец блока вывода ВРЕМЕНИ на дисплей******

//******Блок инд.свтодиодом************* 
 void ledBlink()
 {
   if (metDat[0]+zadTime[2]<millis())            //Проверка условия
      {
      metDat[0]=millis();                  //Установка задержки
      digitalWrite(led,!digitalRead(led)); //Инверсия значения
      }
 }
//*********Конец блока индикации светодиодом***********************

//**********Блок подсветки и освещения*****************************
void light()
{
  if (metDat[1]+(ReadEEPROM_Byte(6)*zadTime[5])<millis())      //Проверка времени работы освещения
  {
  digitalWrite(osv, LOW);                                  //Выключение освещения если ДА
  lcd.noBacklight();                                       //Выключение подсветки если ДА
  }
}
//********Конец блока подсветки и освещения************************

//********Блок распознавания значения кнопки***********

void keyPush()
{
        int keyKod=analogRead(0);        //Считываем значение клавиши
        delay(zadTime[0]);
        if (keyKod>(ReadEEPROM_Word(0)-15)&&keyKod<(ReadEEPROM_Word(0)+15)) {  key=1; }  //Клавиша Set
        if (keyKod>(ReadEEPROM_Word(2)-15)&&keyKod<(ReadEEPROM_Word(2)+15)) {  key=2; }  //Клавиша Up
        if (keyKod>(ReadEEPROM_Word(4)-15)&&keyKod<(ReadEEPROM_Word(4)+15)) {  key=3; }  //Клавиша Down
        if (keyKod>850)                           { key=8; }  //Значение для начальной калибровки клавиш (Нажаты все)
         
}

//***********Конец блока распознования значения кнопки************

//***********Блок калибровки кнопок*******************************

void knopKal()
{
      int keyKod;
      int keyZap;
      byte j;
      lcd.clear();
      for (j=0; j<3; j++)
  {
      lcd.setCursor(0,0);
      lcd.print(str[19+j]);
      byte m=j*2;
      long i=millis()+zadTime[3];       //Время задержки считывания
      while (i>millis())                //Цикл задержки
      {   
         ledBlink();   
      int keyKod=analogRead(0);         //Считывание значения кноки
      keyZap=keyKod;
      }
      if (keyZap>400)                   //Проверка нажатия
      { pic(); WriteEEPROM_Word(m, keyZap);}    //Запись в память если есть значение
      else
      { pic(); WriteEEPROM_Word(m, knop[j]);}       //Запись если ничего не нажато
  }
      lcd.clear(); 
}
//*****************Конец блока калибровки кнопок*****************************

//*****************БЛОКИ ОБРАБОТКИ МЕНЮ ПРОГРАММЫ****************************

//**********************Блок обработки меню**********************************

void menuCikl()
{
          lcd.clear();
          lcd.setCursor(0,0);
          lcd.print(str[prStr]);
  long i=0;
  if (flag[6]==1)
  {
     i=ReadEEPROM_Byte(eeDat);    //Считываем ранее записанное значение
  }
  if (flag[6]==2)
  {
     i=eeDat;                                //Старое значение
  }
  if (flag[6]==3)
  {
     i=ReadEEPROM_Long(eeDat);               //Считываем ранее записанное значение
  }
  
  int keyKod=0;                           //Значение нажатой кнопки
  byte key1=0;                            //Статус нажатия кнопки
          lcd.setCursor(0,1);
          lcd.print(str[0]);
          lcd.setCursor(6,1);
          lcd.print(i,DEC);               //Вывод записанного значения на экран
  metDat[3]=millis()+zadTime[4];              //Счетчик времени нахождения в меню
  metDat[4]=millis()+zadTime[2];               //Счетчик задержки на срабатывание клавиши Set
  while(metDat[3]>millis())              //Цикл обработки меню по времени
    { 
       ledBlink();
    keyKod=analogRead(0);                 //Считываем значение кнопки
    delay(zadTime[0]);
    if (key1==1&&keyKod>400) { continue; } //Если не изменялось - возврат
    if (key1==0&&keyKod<400) { continue; } //Если не изменялось - возврат
    if (key1==0&&keyKod>400) { key1=1; } //Изменение статуса состояния
    if (key1==1&&keyKod<400) { key1=0; } //Изменение статуса состояния
    if (keyKod>(ReadEEPROM_Word(2)-15)&&keyKod<(ReadEEPROM_Word(2)+15)) //Проверка значения UP
   {  
     metDat[3]=millis()+zadTime[4];          //Обновление времени нахождения в меню
     pic();
     i=i+shag;                                //Приращение значения 
          if (i>znMax)                     //Проверка на превышение допустимых значений
          {
          i=znMin;                           //Если превышено, переход на минимальное значение
          }
          lcd.setCursor(0,1);            //Вывод на экран
          lcd.print(str[0]);
          lcd.setCursor(6,1);
          lcd.print(i,DEC);
   }
  
    if (keyKod>(ReadEEPROM_Word(4)-15)&&keyKod<(ReadEEPROM_Word(4)+15))  //Проверка на нажатие Down
    {  
     metDat[3]=millis()+zadTime[4];      //Обновление времени нахождения в меню
     pic();
     i=i-shag;                      //Уменьшение задаваемого значения
          if (i<znMin)              //Проверка на достижение минимального значения
        {
        i=znMax;                    //Если достигнуто, переход на максимальное значение
        }
          lcd.setCursor(0,1);       //Вывод на экран
          lcd.print(str[0]);
          lcd.setCursor(6,1);
          lcd.print(i,DEC);  
   }  
        
     if (keyKod>(ReadEEPROM_Word(0)-15)&&keyKod<(ReadEEPROM_Word(0)+15)&&metDat[4]<millis()) //Проверка нажатия клавиши Set с задержкой
   {
        pic();
        lcd.clear();                      //Очистка экрана
        if (flag[6]==1)
        {
          if (ReadEEPROM_Byte(eeDat)==i)  //Сравнение нового значения с сохраненным, выход если равны
          {
            flag[6]=0;
            break;
          }
          WriteEEPROM_Byte(eeDat, i);     //Сохранение нового значения
          flag[6]=0;
          break;
        }
        if (flag[6]==2)
        {
          eeDat=i;
          flag[6]=0;
          break;                          //Выход 
        }
        if (flag[6]==3)
        {
          if (ReadEEPROM_Long(eeDat)==i) //Сравнение нового значения с сохраненным, выход если равны
          {
            flag[6]=0;
            break;
          }
          WriteEEPROM_Long(eeDat, i);    //Сохранение нового значения
          flag[6]=0;
          break;                         //Выход   
        }        
     }
   }
 
      lcd.clear();                      //Очистка экрана
} 

//******Конец блока обработки и записи меню****

//******Блок записи значений времени**********

 void writeTime()
 {
  RTC.set(DS1307_SEC,Sk);        //set the seconds
  RTC.set(DS1307_MIN,Mn);        //set the minutes
  RTC.set(DS1307_HR,Hour);        //set the hours
  //RTC.set(DS1307_DOW,1);      //set the day of the week
  RTC.set(DS1307_DATE,Dy);      //set the date
  RTC.set(DS1307_MTH,Mes);      //set the month
  RTC.set(DS1307_YR,Yr-2000);   //set the year
  RTC.start();
 }
 
 //*******Конец блока записи значений времени******

//***********Блок установки времени*************

void ustTime()
{
  readTime();                      //Чтение текущего времени
  RTC.stop();                      //Остановка часов
  prStr=7; eeDat=Yr; znMin=2010; znMax=2099; shag=1; flag[6]=2;//Новое значение года      
        menuCikl();      
        Yr=eeDat;
  prStr=8; eeDat=Mes; znMin=1; znMax=12; shag=1; flag[6]=2;//Новое значение месяца
        menuCikl();
        Mes=eeDat;
  prStr=9; eeDat=Dy; znMin=1; znMax=31; shag=1; flag[6]=2; //Новое значение даты
        menuCikl();
        Dy=eeDat;
  prStr=10; eeDat=Hour; znMin=0; znMax=23; shag=1; flag[6]=2; //Новое значение часов
        menuCikl();
        Hour=eeDat;
  prStr=11; eeDat=Mn; znMin=0; znMax=59; shag=1; flag[6]=2; //Новое значение минут   
        menuCikl();
        Mn=eeDat;
  writeTime();                 //На функцию записи времени
  
}  
//************Конец блока установки времени************************

//************Блок отработки значения влажности********************

void vlagClear()

{
   long t=ReadEEPROM_Long(6);       //Поправка влажности
   t=t+tDall[3];                    //Влажность с поправкой
   long tt=ReadEEPROM_Byte(28);     //Влажность заданная
  
   if (t>tt&&flag[0]==0)    //Вл.факт больше заданной флаг сброшен
   {
     digitalWrite(ventV,HIGH);  //Включить выт.вентилятор
     digitalWrite(ventP,HIGH);  //Включить перем.вентилятор
     metDat[7]=millis()+zadTime[6]; //Время работы до проверки
     thVrem[0]=t;              //Запомнить факт.температуру
     flag[0]=1;                //Флаг сниж.влажности поднят
   }
   if (t<tt&&flag[0]==1)  //Вл.факт меньше заданной, флаг поднят
   {
     digitalWrite(ventV,LOW); //Выключить вентиляторы
     digitalWrite(ventP,LOW);
     thVrem[0]=0;             //Обнулить временную температуру
     flag[0]=0;               //Сбросить флаг
     metDat[7]=millis()+zadTime[6]; //Время до следующей проверки
   }
   if (t<tt&&flag[0]==0)  //Вл.факт. меньше заданной, флаг сброшен
   {
     metDat[7]=millis()+zadTime[6]; //Время до след.проверки
   }
   if (thVrem[0]>t&&flag[0]==1) //Вл.временная больше факт. Флаг поднят
   {
    thVrem[0]=t;                //Запомнить новую температуру
    metDat[7]=millis()+zadTime[6]; //Время для след.проверки
   }
   if (thVrem[0]<=t&&flag[0]==1&&metDat[7]<millis()) //Вл.врем. меньше факт, флаг поднят, время вышло
    {
     thVrem[0]=0;              //Обнулить временную влажность
     digitalWrite(ventV,LOW);  //Вентиляторы выключить
     digitalWrite(ventP,LOW);
     metDat[8]=millis()+zadTime[8]; //Задержка до след.включения
     flag[0]=0;                     //Флаг сбросить
    } 
   
}

//**********Конец блока обработки влажности**********************************

//**********Блок нагрева и диммирования***********************

void tempDimm()
{
   long tt=ReadEEPROM_Long(3);  //Поправка на Даллас помещения
   long t=ReadEEPROM_Long(5);   //Поправка на DHT температуру
   tt=tDall[1]+tt;              //Температура даллас с поправкой
   t=tDall[2]+t;                //Температура DHT с поправкой
   t=(t+tt)/2;                  //Усредненная температура с поправкой
   tt=ReadEEPROM_Byte(7);       //Температура заданная
   if (tt>t&&Dimmer1==255)      //Т.заданная больше, диммер выключен
   {
     Dimmer1=160;               //Включить диммеры на минимум
     Dimmer2=Dimmer1;
     digitalWrite(ventP,HIGH);   //Включить перемеш.вентилятор
     metDat[9]=millis()+zadTime[6];   //Время след.опроса
     thVrem[1]=t;                //Запомнить температуру
     flag[3]=1;
   }
   // Темп.запомненная выше факт., диммер перед максимумом.
   if (thVrem[1]>=t&&Dimmer1<=40&&Dimmer1!=0&&metDat[9]<millis()) 
      {
        Dimmer1=0;                    //Диммеры на максимум                 
        Dimmer2=Dimmer1;
        metDat[9]=millis()+zadTime[6];
       }
    //Темп.запомненная выше факт, диммер включен, но не на максимуме
    if(thVrem[1]>=t&&Dimmer1!=0&&Dimmer1!=255&&metDat[9]<millis())
    {
      Dimmer1=Dimmer1-40;  //Увеличиваем мощность диммера
      Dimmer2=Dimmer1;
      metDat[9]=millis()+zadTime[6];
    }
    //Темп.запомненная выше факт. диммер на максимуме
    if(thVrem[1]>=t&&Dimmer1==0&&metDat[9]<millis())
    {
      metDat[9]=millis()+zadTime[6];
      //Здесь запустить сигнал аварии
    }
    //Температура запомненная ниже фактической, диммер включен
    if(thVrem[1]<t&&Dimmer1!=255&&metDat[9]<millis())
    {
      metDat[9]=millis()+zadTime[6];
      thVrem[1]=t;               //Запоминаем новую температуру
    }
    //Температура фактическая выше заданной, диммер включен
    if(t>tt&&Dimmer1!=255)
    {
      Dimmer1=255;               //Диммеры выключить
      Dimmer2=Dimmer1;
      metDat[9]=millis()+zadTime[6];
      digitalWrite(ventP,LOW);   //Вентилятор перем.выключить
      thVrem[1]=tt;              //Запомнить темп.заданную
      flag[3]=0;                 //Флаг признака сбросить
    } 
}

//**************Конец блока диммирования и нагрева***************

//**************Блок усреднения температуры*********************

void tempUsr()
{
   long tt=ReadEEPROM_Long(3);  //Поправка на Даллас помещения
   long t=ReadEEPROM_Long(5);   //Поправка на DHT температуру
   tt=tDall[1]+tt;              //Температура даллас с поправкой
   t=tDall[2]+t;                //Температура DHT с поправкой
   byte i=ReadEEPROM_Byte(7);   //Температура заданная
   //Темп.верх выше темп.низ, средняя выше заданной, признак сброшен
   if (t-tt>2&&(t+tt)/2>i&&flag[4]==0) 
   {
     metDat[10]=millis()+zadTime[6];  //Счетчик работы
     digitalWrite(ventP,HIGH);   //Вент.перем. включить
     flag[4]=1;                  //Флаг признака включить
   }
   //Темп.верх выше темп.низ, средняя выше заданной, признак включен
   if (t-tt>2&&(t+tt)/2>i&&flag[4]==1) 
   {
     metDat[10]=millis()+zadTime[6];  //Счетчик работы
   }
   //Темп.низ повысилась, признак включен
   if (t-tt<2&&flag[4]==1)
   {
     metDat[10]=millis()+zadTime[6];  //Счетчик работы
     digitalWrite(ventP,LOW);    //Вент.перем.выключить
     flag[4]=0;                  //Флаг признака сбросить
   }
}

//**************Конец блока усреднения температуры**************

//*******************Основное меню*********************************************

void mainMenu()       //Алгоритм перебора значений как в menuCikl()
{
  lcd.clear();
  byte i=1;
  int keyKod=0;
  byte key1=0;
  metDat[2]=0;                //Сбрасываем задержку срабатывания для исключения возврата
  metDat[3]=millis()+zadTime[4];
  metDat[4]=millis()+zadTime[2];
  while(metDat[3]>millis())
   { 
        ledBlink();
    keyKod=analogRead(0);
    delay(zadTime[0]);
    if (key1==1&&keyKod>400) { continue; }
    if (key1==0&&keyKod<400) { continue; }
    if (key1==0&&keyKod>400) { key1=1; }
    if (key1==1&&keyKod<400) { key1=0; }
    if (keyKod>(ReadEEPROM_Word(2)-15)&&keyKod<(ReadEEPROM_Word(2)+15))
   {  
       metDat[3]=millis()+zadTime[4];
       pic();
       i++; 
          if (i==11) {i=1;}
   }
  
    if (keyKod>(ReadEEPROM_Word(4)-15)&&keyKod<(ReadEEPROM_Word(4)+15))
   {  
       metDat[3]=millis()+zadTime[4];
       pic();
       i--; 
          if (i==0) {i=10;}
   }  
   
        if (keyKod>(ReadEEPROM_Word(0)-15)&&keyKod<(ReadEEPROM_Word(0)+15)&&metDat[4]<millis()) 
          {
            lcd.clear();
            lcd.setCursor(0, 1);
            pic();
            if(i==1)
            {
              ustTime();   //На функцию установки времени
            }
            if (i==2)      //Установка времени работы освещения и подсветки
            {
               eeDat=6; prStr=2; znMax=20; znMin=1; shag=1; flag[6]=1;
                   menuCikl();    
            }
            if (i==3)              //Установка значения  температуры
            {
              eeDat=7; prStr=3; znMax=18; znMin=8; shag=1; flag[6]=1;
                  menuCikl();      
            }
            if (i==4)           //Установка значения влажности
            {
              eeDat=28; prStr=4; znMax=80; znMin=30; shag=5; flag[6]=1;
                  menuCikl();  
            }
            if (i==5)           //Установка коррекции времени
            {
              eeDat=2; prStr=12; znMax=29; znMin=-29; shag=1; flag[6]=3;
                   menuCikl();
            }       
            if (i==6)           //Установка коррекции температуры нижнего датчика
            {
              eeDat=3; prStr=13; znMax=5; znMin=-5; shag=1; flag[6]=3;
                   menuCikl();
            }
            if (i==7)           //Установка коррекции температуры нижнего датчика
            {
              eeDat=4; prStr=14; znMax=5; znMin=-5; shag=1; flag[6]=3;
                   menuCikl();
            }              
            if (i==8)           //Установка коррекции температуры нижнего датчика
            {
              eeDat=5; prStr=15; znMax=5; znMin=-5; shag=1; flag[6]=3;
                   menuCikl();
            } 
             if (i==9)           //Установка коррекции температуры нижнего датчика
            {
              eeDat=6; prStr=16; znMax=15; znMin=-15; shag=1; flag[6]=3;
                   menuCikl();
            }             
            if (i==10)
            {
              break;
            }           
     }
      
   switch (i)           //Вывод пунктов меню на экран
   {
     case 1:
          prStr=1;
          printMenu();
          continue;                          //Возврат на цикл отсчета нахождения в меню
     case 2:
          prStr=2;
          printMenu();
          continue; 
     case 3:
          prStr=3;
          printMenu();     
          continue;
     case 4:
          prStr=4;
          printMenu();
          continue;
     case 5:
          prStr=12;
          printMenu();
          continue; 
     case 6:
          prStr=13;
          printMenu();
          continue; 
     case 7:
          prStr=14;
          printMenu();
          continue;
     case 8:
          prStr=15;
          printMenu();
          continue;
     case 9:
          prStr=16;
          printMenu();
          continue;       
     case 10:
          prStr=5;
          printMenu();
          continue;    
          }
    }
    lcd.clear();
}

//****************Конец блока обработки основного меню***********************

//****************КОНЕЦ БЛОКОВ МЕНЮ ПРОГРАММЫ********************************

//**************ОСНОВНОЙ ЦИКЛ ПРОГРАММЫ**************************************

void loop () 
{
 
   readTime();  //На функцию считывания времени
   printTime(); //На функцию вывода времени
   ledBlink();  //На функцию индикации светодиодом
   light();     //На функцию статуса подсветки
     
     if (metDat[5]<millis()) //Счетчик времени опроса датчиков
     {
       DallasTempPresent();
       DallasPrintTemp();
     }
   
        key=0;
        int keyKod=analogRead(0); //Считываем значение нажатой кнопки
        // Резисторы 10К к 0, 10К+4К7+2К2
        // 1 Кнопка1=509
        // 2 Кнопка2=825
        // 3 Кнопка3=687
        // 4 Кн1+Кн2=855
        // 5 Кн1+Кн3=767
        // 6 Кн2+Кн3=878
        // 7 Кн1+Кн2+Кн3=894
        if(keyKod>100)             //Нажатие любой кнопки
        {
          metDat[1]=millis();        //Взведение счетчика освещения
          digitalWrite(osv, HIGH);   //Включение освещения
          lcd.backlight();           //Включение подсветки дисплея
             keyPush();              //На функции считывания и определения кода кнопки
        }
   
 //********Блок выполнения нажатия кнопки******       
        
        switch (key) 
        {
     case 0:
        metDat[2]=0;              //Обнуление счетчика времени нажатия клавиш, если нет нажатия 
      break;
          
      case 1: 
         if (metDat[2]==0)    {metDat[2]=millis()+zadTime[3];} //Взведение счетчика времени нажатия Set
         if (metDat[2]>millis()) {keyPush();}             //Ожидание если клавиша Set нажата
         else
        {pic(); mainMenu();}                                      //Переход на основное меню по достижении времени нажатия
      break;
      
      case 8:
      if (metDat[2]==0)    {metDat[2]=millis()+zadTime[7];} //Взведение счетчика времени нажатия всех кнопок
      if (metDat[2]>millis()) {keyPush();}              //Ожидание если все кнопки нажаты
      else
      { pic(); knopKal();}                                       //Переход на функцию калибровки кнопок по достижении времени
      break;
  }
  
   if (metDat[8]<millis()&&(flag[3]+flag[4])==0&&flag[7]!=1)     //Проверка задержки на отработку влажности
   {
     if (metDat[7]<millis())    //Проверка опроса влажности
     { vlagClear(); }           //Функция обработки влажности
   }
   if (metDat[9]<millis()&&(flag[0]+flag[4])==0)    //Проверка времени работы диммера
     { tempDimm(); }              //Функция подогрева
   if (metDat[10]<millis()&&(flag[0]+flag[3])==0&&flag[7]==0)   //Проверка времени перемешивания
     { tempUsr(); }              //Функцию усреднения температуры
     
   if (millis()>2592000000)      //Проверка на достижение 30 суток
   { delay(zadTime[5]); }        //Инициализация собаки
    
//********Запуск прерывания диммеров после RESET****************
   
 //Запуск прерывания для работы диммеров (если запускать в Setup, не позволяет инициализироватся
 //остальным устройствам, дисплей, часы и т.д. Запускается один раз после перезагрузки.
   if (flag[1]==0)          //Флаг состояния прервания после перезагрузки
  {
    flag[1]++;              //Изменение флага состояния прерывания
   attachInterrupt(0, detect_up, LOW);  // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
  }
}
//********Конец блока запуска прерывания после RESET*************

//***********ОКОНЧАНИЕ ОСНОВНОГО ЦИКЛА*****************************

 

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

Юзайте, на красоту не претендую, вроде все закомменчено, принципы поймете.

На сегодня умеет:

1.Определять и запоминать датчики DS18 2шт, задавать и запоминать температуры и влажность.

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

3. Включать с равномерным увеличением мощности нагреватели.

4. Аварии и прочие полезности пока не делал.

Если что не понятно, спрашивайте.

 

 

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

Пошла вторая неделя - я воюю с вашим кодом :).

По кнопкам. Я свои собрал по простой последовательной схеме резисторов с подтягиванием к "+". где записывал показания их в массив чтобы потмо сравнивать, что же нажато. Все делал по примерам. Вот посмотреть бы видео с работой вашего кода на примере....

Так-как пока я в ожидании своих часов (ds3231 ) будь повелся на цену копеечную и заказал всё у разных я нашёл ИК-приемник. подключил к нему пульт. Чтобы не кнопки нажимать включая свет а с пульта. Вышло вот такое:






include <Wire.h>                                     //Подключаем библиотеку для использования I2C интерфейса с модулем RTC
#include <LiquidCrystal_I2C.h>                        //Подключаем библиотеку для Модуля ЖК дисплея 20Х4
#include <OneWire.h>                                  //Подключаем библиотеку для использования однопроводного интерфейса Температурного датчика DS18B20
#include <IRremote.h>
iquidCrystal_I2C lcd(0x27,20,4); 

#define RECV_PIN 11 // IR приемник
#define OUT1 8 // выхода на relay
#define OUT2 7 // выхода на dimmer
 
//коды пульта 
#define POWER_KEY 0x41BEF00F // exit
#define KEY1 0x41BED02F // up
#define KEY2 0x41BE609F // down
#define KEY3 0x41BE40BF // left
#define KEY4 0x41BEB04F // right
#define KEY5 0x41BE10EF // vol_up
#define KEY6 0x41BE708F // vol_down
#define KEY7 0x41BEC03F // enter

byte analogData1, analogData2 = 0; // значения аналогово выхода
uint32_t val; 
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
   irrecv.enableIRIn();
   lcd.init();                                     //Инициализируем ЖК дисплей 
   lcd.setBacklight(1);
   lcd.clear();
 
}


void loop() {
  if (irrecv.decode(&results)) { // если пришел пакет  
    if (results.value != 0xFFFFFFFF){ // и этот пакет не FF
      val = results.value; // сохраняем правельный пакет в переменную     
    }                 // если пришел FF, соответственно пропускаем.
    
    /// сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)
    /// в переменной останется прошлый, правельный, пакет и код выполнится повторно. 
 /*   if (val == KEY1){      
       if (analogData1 != 0)analogData1--; // если шим не равно 0 отнимаем 1 
       analogWrite(OUT1, analogData1);    // устанавливаем значение в выход     
    }    
     if (val == KEY2){ // и это вторая кнопка     
       if (analogData1 < 255) analogData1++; // если шим меньше 255 прибовляем 1 
       analogWrite(OUT1, analogData1);  // устанавливаем значение 
    }
 */
    if (val == KEY6){      
       if (analogData2 != 0)analogData2--;  
       analogWrite(OUT2, analogData2);      
    }    
     if (val == KEY5){      
       if (analogData2 < 255) analogData2++;  
       analogWrite(OUT2, analogData2);  
    }
      
     ///
    if (results.value == POWER_KEY) {  
      if (analogData1 != 0 && analogData2 != 0){ // если ШИМ неравно 0
        analogWrite(OUT1, 0); // выключаем
        analogWrite(OUT2, 0);
        analogData1 = 0;
        analogData2 = 0;
      }
      else {
        analogWrite(OUT1, 255); // включаем relay
        analogWrite(OUT2, 0);   // выключаем dimmer
        analogData1 = 255;
        analogData2 = 255;
      }          
    }  
  irrecv.resume(); 
  }
  lcd.setCursor(9, 2);
  lcd.print(analogData2);

}

И всё бы ничего да вот есть пара неудобных мест, именно неудобных.

Задумывалось кнопкой POWER_KEY 0x41BEF00F - включать-выключать реле для подачи питания на драйвер Led.

Так как реле инверсное сейчас происходит следующее. Нажимаю POWER_KEY  - всё пучком реле срабатывает на дисплее пишется значение сначала 255 (я так понял HI) далее нажимаю снова и только тут включается реле и на дисплее 0 (кстати дисплей неочищается и горит значение 055) как это победить.

Далее если я при выключенном реле нажимаю на увеличение или уменьшение ШИМ то значение на выходе диммера всегда 255 (максимум) .В принципе все нормально питания на нём нет и неважно какой параметр на входе диммирования - но зачем ардуинке зря трудится, как сделать недоступной обработку этих кнопок ( #define KEY5 0x41BE10EF // vol_up и  #define KEY6 0x41BE708F // vol_down) пока реле отключено?

 

Спасибо.

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

Чисто мой код вам однозначно не подойдет, у него задачи другие. По ИК не скажу, не работал с ним.

Далее, в setup надо инициировать все цифровые выходы (как вам требуется на начальном этапе LOW или HIGH).

Для очистки дисплея lcd.clear или вывести в эти знакоместа пробелы(мне так больше нравится, меньше мерцает) вернуть курсор и снова печатать.

Блокировка кнопок по if-ам в зависимости от требуемой логики.

Я стараюсь в loop-е оставить минимум и все вывести во внешние функции. Далее в loop отрабатываю тайминги+условия и вызываю эти функции по мере надобности. Для отладки намного проще.

Как-то так.

P.S. посмотрел внимательнее, в строках 53,57 добавить условие для реле. Реле лучше на цифровой выход.

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

 

Итак, по инициализации цифровых выходов. Мне казалось я их уже указал в 

byte analogData1, analogData2 = 0; // значения аналогово выхода

Однако решил setup сделать пока таким:



void setup()
{
   irrecv.enableIRIn();
   pinMode(OUT1, OUTPUT);
   pinMode(OUT2, OUTPUT);
   digitalWrite(OUT1,HIGH);

   lcd.init();                                     //Инициализируем ЖК дисплей 
   lcd.setBacklight(1);
   lcd.clear();
 
}

Релюшка инверсная потому и сразу её в HIGH пришлось

а также перенёс релюшку на цифру, пока туда:

#define OUT1 22 // выхода на relay

Я понимаю что "непонимаю" языка и логически знаю что хочу с чем сравнить а в коде реализовать немогу.

Нужно сначала вкл\выкл - relay и только после этого dimmer

Сейчас dimmer может сразу начать с 255 и далее по 1 единичке в минус.

 

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

C печатью похож тоже разобрался (на lsd) по вашему примеру заполнил пробелами, незнаю правильно-ли, однако работает:

  lcd.setCursor(9, 2);
  lcd.print(analogData2);
  lcd.print("   ");
  lcd.setCursor(9, 2);
  lcd.print(analogData2);

 

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

Да, я это и имел в виду. Перенести реле на цифру, инициировать как выключено.

По работе с диммерами - Проверяем состояние реле. Если включено - начинаем работу с диммером. Если выключено - ушли. Т.е. сперва if с проверкой реле и в нем if-ы с диммерами. Проверка реле либо через digitalRead(), либо завести переменную. Если непонятно объяснил, уточняйте.

Для аквариума алгоритм наверно такой:

1. Команда на включение (часы)

1.1 Диммер на минимум (лучше это сделать в Setup)

1.2 Реле включить

1.3 В цикле с задержкой увеличиваем диммер до требуемой величины.

2. Команда на выключение (часы)

2.1 В цикле с задержками уменьшаем диммер до минимума(выкл)

2.2 Реле выключить.

3. Хотим убавить увеличить яркость (освещение включено, пульт)

3.1 Проверяем состояние диммера

3.2 Если не минимум(выкл), увеличиваем или убавляем яркость (не забыть огр. мин. и макс., минимум не равен (выкл))

3.3 Если на минимуме(выкл) уходим (для проверки используем переменную диммера)

п.п1.3 и 2.1 лучше отдельными функциями и if-ами. Т.к for() затормозит программу

состояния переменной диммера: минимум(выкл) - выключено, любое другое включено. Ручная регулировка минимума не равна минимуму(выкл).

Ручное управление - как вам удобно(выкл.резко, выкл.с плавным затуханием и т.д.).

Не очень понял назначение реле - драйвер с минимальным значением диммера выдает какое то напряжение?

 

 

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

Спасибо вам за поддержку моей темы. Она получилась ... немного разрозненная, нужно было начать сначала с ТАЙМЕРА, потом отлаживать диммирование, потом терморегуляцию, потом уже ручное управление :). Получилось всё через "Ж", xfs с али едут с огромной задержкой вот и играюсь с тем что есть.

Корпус для всего уже нарезал и склеил из вспененного ПВХ. Счас приходится кнопками вкл\выкл реле коммутировать. И тут попался IR- от старого HE вертолеткиа сына, пульт от телека, скетчик для примера и и вот уже вроде все и работает да немного нетак. 

Все ваши пункты счас внимательно вкурю.

С питанием на драйвер и диммированием. У меня свет на COB-сбрках по 10Вт для банки в 530 литров потребовалось минимум ..... 30штук. Драйвер решил использовать методом проб ИБП от ПК. Однако там стабилизируется НАПРЯЖЕНИЕ а нам то нужно ТОК в пределах 0,6-0,9А. Как итог покупка к каждому ЛЕДу своего драйвера и подключать их ПАРАЛЕЛЬНО, Учитываю что БП уже есть и спокойно выдерживает до 45-65А в цепи 12В вопрос был ЭКОНОМИЧЕСКИ расмотрен итого. Один 10Вт ЛЕД стоит около 37руб (1уе на то время) один драывер 63 руб. (2уе). Итого за свет в 300ВТ чистых 6500К (белого почти как солнце света) ушло всего чуть менеее 100уе (2900руб) тоесть менее 3 рублей на литр воды (у нас аквариумистов считают Вт на литр и соответственно руб на литр при затратах.

Так вот я нехочу ПОДАВАТЬ 12В на 10 драйверов (они их в любом случае потребляют, греются немного и прочее, снижая КПД  и увеличивая энергозатраты) - для этого сначала подаем на входы всех дрйверов (они разделены у меня на 3 по 10шт линии) а уже потомм диммируем, иммитируя либо закат\рассвет, либо в ручном режиме вечером к примеру просто досвтеку комнаты перед сном :)

Тема про мой свет тут: http://www.aqa.ru/forum/Led-svet-na-530-litrov-ne-Cree-diodyi-s-Ali-vzamen-t5-280537-page1

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

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

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

Попробуйте на бумажке расписать по шагам что и как будет. Тогда и кодить легче.

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

Я чуток поправил код и вышло вот таке с учетом "резки на блоки", правильно-ли так будет:

void setup() {
  Wire.begin();
  irrecv.enableIRIn();   
 pinMode(OUT1,OUTPUT);
   pinMode(OUT2,OUTPUT);
   pinMode(OUT3,OUTPUT);
   pinMode(OUT4,OUTPUT);
  digitalWrite(OUT1,HIGH);
  digitalWrite(OUT2,HIGH);
  digitalWrite(OUT3,HIGH);
  digitalWrite(OUT4,LOW);
      lcd.init();                                                     //Инициализируем ЖК дисплей
      lcd.setBacklight(1);  
      lcd.clear();      
}
//====================================================================//==================================================================================
//                             Обработка IR-кнопок
//====================================================================//==================================================================================
void IR_key(){
  lcd.setCursor(17, 3);
  lcd.print(analogData2);
  lcd.print(" ");
  lcd.setCursor(17, 3);
  lcd.print(analogData2);
    lcd.setCursor(1, 3);
    lcd.print(digitalRead(OUT1));
    lcd.setCursor(3, 3);
    lcd.print(digitalRead(OUT2));
    lcd.setCursor(5, 3);
    lcd.print(digitalRead(OUT3));

if (irrecv.decode(&results)) {        //Если пришел пакет и этот пакет не FF сохраняем правильный пакет в переменную 
    if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
    }                                 //в переменной останется прошлый, правильный, пакет и код выполнится повторно. 
    if (results.value == KEY1) digitalWrite(OUT1, !digitalRead(OUT1));
    else if (results.value == KEY2) digitalWrite(OUT2, !digitalRead(OUT2));
    else if (results.value == KEY3) digitalWrite(OUT3, !digitalRead(OUT3));
    else if (results.value == KEY4) digitalWrite(OUT4, !digitalRead(OUT4));
     else if (results.value == POWER_KEY) {  
      if (digitalRead(OUT1) || digitalRead(OUT2) || digitalRead(OUT3) || digitalRead(OUT4)){ 
        digitalWrite(OUT1, LOW);
        digitalWrite(OUT2, LOW);
        digitalWrite(OUT3, LOW);
        digitalWrite(OUT4, LOW);
      }
      else {
        digitalWrite(OUT1, HIGH);
        digitalWrite(OUT2, HIGH);
        digitalWrite(OUT3, HIGH);
 //       digitalWrite(OUT4, HIGH);          //Иначе dimmer после POWER_KEY включится на HIGH
      }          
    }
       if (val == KEY5){      
       if (analogData2 != 0)analogData2--;  
       analogWrite(OUT4, analogData2);      
    }    
     if (val == KEY4){      
       if (analogData2 < 255) analogData2++;  
       analogWrite(OUT4, analogData2);  
  }
  irrecv.resume(); 
}
} 

void loop()                                        //Постоянная отработка в цикле
{
  IR_key();    
}

И да, процесс охрененно творческий, я зря незаказал сразу парочку (один уже в работе а с другим твориш), я послучаю заказал всеже есчо Nano - имхо для моих задач Mega избыточна и мааленький tft к ней, для креветочника.

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

У вас опять слились задачи обработки IR, включение и диммирование. Что случится когда вы подключите часы и не дай бог еще и кнопки? Вам придется аналогичный код писать три раза+взаимная путаница. На мой взгляд правильнее по задачам: диммирование, реле, IR.

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

В планах, насколько я понимаю, измерение температуры+ее поддержание, помпа - постоянно или по критерию, что еще? Маленький tft скушает много памяти и может потребоватся Мега (на мой взгляд LCD проще, хоть и менее красиво). Для остальных задач Atmega328 наверно за глаза.

Я с IR не работал, где то проскакивала инфа, что они c DS18 не очень дружат, попробуйте протестируйте.

 

Thorn
Offline
Зарегистрирован: 07.11.2014
#include <Wire.h>							//Подключаем библиотеку для использования однопроводного интерфейса                                     
#include <LiquidCrystal_I2C.h>  					//Подключаем библиотеку для использования I2C интерфейса
#include <OneWire.h>							//Подключаем библиотеку для температурного датчика DS18B20
#include <IRremote.h>                                                   //Подключаем библиотеку для IR датчика
//=====IR*
#define RECV_PIN 11   // IR приемник
#define OUT1 22       // выхода на relay1                               //Используем цифровой ПОРТ 22 для 1 канала свето-релейного модуля
#define OUT2 24       // выхода на relay2                               //Используем цифровой ПОРТ 24 для 2 канала свето-релейного модуля
#define OUT3 26       // выхода на relay3                               //Используем цифровой ПОРТ 26 для 3 канала свето-релейного модуля
#define OUT4 12       // выхода на dimmer                               //Используем цифровой ПОРТ 12 для SunSet канала (dimmer)
//коды кнопок пульта 
#define POWER_KEY 0x41BEF00F // exit-power-OFF
#define KEY1 0x41BED02F // up_relay1_ON/OFF
#define KEY2 0x41BEC03F // enter_relay2_ON/OFF
#define KEY3 0x41BE609F // down_relay3_ON/OFF
#define KEY4 0x41BE10EF // vol_up_dimmer-UP_relay1
#define KEY5 0x41BE708F // vol_down_dimmer-DOWN_relay1
/*
#define KEY6 0x41BE40BF // left
#define KEY7 0x41BEB04F // right
*/
byte analogData1, analogData2 = 0;                                     // значения аналогового выхода
uint32_t val; 
IRrecv irrecv(RECV_PIN);
decode_results results;
//=====IR***
//=====TermoRelay*
LiquidCrystal_I2C lcd(0x27,20,4);					//Устанавливаем LCD-адрес 0x27 для отображения 20 символов и 4 линии
const int RelayChn1 = 30;						//Используем цифровой ПОРТ 30 для ПЕРВОГО канала термо-релейного модуля
const int RelayChn2 = 32;						//Используем цифровой ПОРТ 32 для ВТОРОГО канала термо-релейного модуля
//=====TermoRelay***
//=====VoltMeter*
int analogInput = 0;
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 value = 0;
//=====VoltMeter***
//=====TermoSensor*
int TSensorPin = 10;							//Определяем порт шины OneWire (IC) для температурного датчика DS18B20                               
OneWire ds(TSensorPin);							//Создаем объект для работы с термометром
byte data[12];
byte addr1[8] = {0x28, 0xF2, 0x29, 0xEB, 0x05, 0x00, 0x00, 0xE1};       //адрес датчика DS18B20
byte addr2[8] = {0x28, 0x86, 0x48, 0xEA, 0x05, 0x00, 0x00, 0xF6};       //адрес датчика DS18B20
unsigned int raw;                                                       //Если экранированный кабель, то можно подключать до 32 термо-датчиков DS18B20
float temp1, temp2;                                                     //Температура аквариума \ радиаторов Led
float t0 = 25.5;
float t00 = 26.5;
float tGistrsis = 0.5;
//=====TermoSensor***

void setup() {
  Wire.begin();
  irrecv.enableIRIn();                              //Включение IR-сенсора
  pinMode(analogInput,INPUT);                       //Вход вольтметра
  pinMode(RelayChn1,OUTPUT);                        //Выход термо-релейного модуля охлаждения радиаторов Led
  pinMode(RelayChn2,OUTPUT);                        //Выход термо-релейного модуля охлаждения воды аквариума 
  digitalWrite(RelayChn1,HIGH);                     //Определяем инверсный выход в HIGH термо-релейного модуля охлаждения радиаторов Led
  digitalWrite(RelayChn2,HIGH);                     //Определяем инверсный выход в HIGH термо-релейного модуля охлаждения воды аквариума
   pinMode(OUT1,OUTPUT);
   pinMode(OUT2,OUTPUT);
   pinMode(OUT3,OUTPUT);
   pinMode(OUT4,OUTPUT);
  digitalWrite(OUT1,HIGH);
  digitalWrite(OUT2,HIGH);
  digitalWrite(OUT3,HIGH);
  digitalWrite(OUT4,LOW);
      lcd.init();                                                     //Инициализируем ЖК дисплей
      lcd.setBacklight(1);  
      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Aqua  Controller");
      lcd.setCursor(2, 1);
      lcd.print("      v1.0      ");
  delay(1000);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Time");
      lcd.setCursor(0, 2);
      lcd.print("t1=");
      lcd.setCursor(10, 2);
      lcd.print("t2=");
      lcd.setCursor(0, 3);
      lcd.print("r");
      lcd.setCursor(2, 3);
      lcd.print(":");
      lcd.setCursor(4, 3);
      lcd.print(":");
      lcd.setCursor(7, 3);
      lcd.print("v=");
      lcd.setCursor(15, 3);
      lcd.print("%=");
}
//====================================================================//==================================================================================
//                             Обработка IR-кнопок
//====================================================================//==================================================================================
void IR_key(){
  lcd.setCursor(17, 3);
  lcd.print(analogData2);
  lcd.print(" ");
  lcd.setCursor(17, 3);
  lcd.print(analogData2);
    lcd.setCursor(1, 3);
    lcd.print(!digitalRead(OUT1));
    lcd.setCursor(3, 3);
    lcd.print(!digitalRead(OUT2));
    lcd.setCursor(5, 3);
    lcd.print(!digitalRead(OUT3));

if (irrecv.decode(&results)) {        //Если пришел пакет и этот пакет не FF сохраняем правильный пакет в переменную 
    if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
    }                                 //в переменной останется прошлый, правильный, пакет и код выполнится повторно. 
    if (results.value == KEY1) digitalWrite(OUT1, !digitalRead(OUT1));
    else if (results.value == KEY2) digitalWrite(OUT2, !digitalRead(OUT2));
    else if (results.value == KEY3) digitalWrite(OUT3, !digitalRead(OUT3));
     else if (results.value == POWER_KEY) {  
      if (digitalRead(OUT1) || digitalRead(OUT2) || digitalRead(OUT3)){ // || digitalRead(OUT4)
        digitalWrite(OUT1, HIGH);
        digitalWrite(OUT2, HIGH);
        digitalWrite(OUT3, HIGH);
      }
      else {
        digitalWrite(OUT1, LOW);
        digitalWrite(OUT2, LOW);
        digitalWrite(OUT3, LOW);
      }          
    }
       if (val == KEY5){      
       if (analogData2 != 0)analogData2--;  
       analogWrite(OUT4, analogData2);      
    }    
     if (val == KEY4){      
       if (analogData2 < 255) analogData2++;  
       analogWrite(OUT4, analogData2);  
  }
  irrecv.resume(); 
}
} 
//====================================================================//==================================================================================
//                             Считывание напряжения
//====================================================================//==================================================================================
void printVolt (){  
   value = analogRead(analogInput);                // read the value at analog input
   vout = (value * 5.0) / 1024.0;                  // see text
   vin = vout / (R2/(R1+R2)); 
   if (vin<0.09) {
   vin=0.0;                                       //statement to quash undesired reading !
      }
      lcd.setCursor(9, 3);
      lcd.print(vin);
}
//====================================================================//==================================================================================
//                             Считывание температуры
//====================================================================//==================================================================================
float DS18B20(byte *adres){
  ds.reset();
  ds.select(adres);
  ds.write(0x44,1);                             //Start conversion, with parasite power on at the end
  ds.reset();
  ds.select(adres);
  ds.write(0xBE);                               //Read Scratchpad
  for (byte i = 0; i < 9; i++) {                //We need 9 bytes
    data[i] = ds.read ();
    }
  raw =  (data[1] << 8) | data[0];              //Пересчитываем в температуру
  float celsius =  (float)raw / 16.0;
  return celsius;
   }
//====================================================================//==================================================================================
//                             Обработка Термо-Реле
//====================================================================//==================================================================================
  void printTemp (){
  temp1 = DS18B20(addr1);
  temp2 = DS18B20(addr2);
  lcd.setCursor(3, 2);                       
  lcd.print(temp1);
  lcd.setCursor(13, 2);                          
  lcd.print(temp2);
   if (temp1 > t0-tGistrsis/2)
      {
        digitalWrite(RelayChn1,LOW);                 //Устанавливаем на 1-ом входе релейного модуля НИЗКИЙ уровень - реле срабатывает
        lcd.setCursor(8, 2);
        lcd.print("*");
      }     
    else if (temp1 < t0+tGistrsis/2)
      {
        digitalWrite(RelayChn1,HIGH);                //Устанавливаем на 1-ом входе релейного модуля ВЫСОКИЙ уровень - реле выключается
        lcd.setCursor(8, 2);
        lcd.print("-");
      } 
   if (temp2 > t00-tGistrsis/2)
      {
        digitalWrite(RelayChn2,LOW);                 //Устанавливаем на 2-ом входе релейного модуля НИЗКИЙ уровень - реле срабатывает
        lcd.setCursor(18, 2);
        lcd.print("*");
      }     
    else if (temp2 < t00+tGistrsis/2)
      {
        digitalWrite(RelayChn2,HIGH);                //Устанавливаем на 2-ом входе релейного модуля ВЫСОКИЙ уровень - реле выключается
        lcd.setCursor(18, 2);
        lcd.print("-");
      }      
  } 
  
void loop()                                        //Постоянная отработка в цикле
{
  IR_key();    
  printVolt();
  printTemp();  
}

Блин затупил, для простоты и удобства вместо реле использовал светодиоды и понимал что логика будет инверсная. Счас подключил реле и ужас. Нажимаю POWER_KEY включа.тся СРАЗУ все линии питания ледов и диммирование если небыло выставлено в "0" то само собой вспышка. Кстати Димиирование у меня будет на одной линии всего. Потому как рассвет будем считать произошёл и даже резкое включение остальных 60% светиков невызывает такого дискомфорта.

Поменял логику работы кнопки POWER_KEY, она теперь НЕМОЖЕТ включать реле сразу. а может их выключать либо сразу либо по одному :) что мне и нужно было (в языке процессинга я пока учусь). А вот диммирование я вообще убрал пока из проверки. Хотя желательно чтобы нажатие POWER_KEY сбрасывало в "0" значение яркости (сечас оно сохраняется до reset). Вобщем я уже максимально близок. Покажите мне явно, кодом -как сделать сброс до "0" значения яркости если нажата кнопка POWER_KEY всё-же.

Вывод на экран пришлось также инвертировать lcd.print(!digitalRead(OUT1)); !-символом НЕРАВНО

ПО поводу Далласов, может у меня код кривовато-длинный (хотя цикла всего пока три, без учета времени и таймеров. Пока чисто IR, termorelay, и вольтметр линии питания светиков :). Так вот мне кажется или всетаки как только я добавляю считываение темпираур и работу с ним несразу срабатывают значения нажатий, не так быстро я бы сказал.

ПО поводу Nano и TFT - стоит смешно мало (170+190 руб) взял исключительно продолжать свое развитие, а потмо засуну это в креветочник.

Вот код на сейчас. Осталось прилепить ЧАСЫ, Закат\Рассвет, Таймер на релюшки для света и Co2. И самое главное - МЕНЮ всего этого чтобы непрошиваться постоянно. EPROM и запись туда для меня звучит очень страшно.

 

Кстати счас понимаю что надо было покупать компоненты типа http://reefcentral.ru/forum/topic/2016-akva-kontroller-ot-olega/

и непарить мозг "ГУРУ", все равно пришлось бы учится, однако комплект стоит раза в 3-4 моего сейчас

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

Счас проверил - если включены все три 

pinMode(OUT1,OUTPUT);
pinMode(OUT2,OUTPUT);
pinMode(OUT3,OUTPUT);

то POWER_KEY их НЕОТКЛЮЧАЕТ :( С одной стороны это гуд, с другой.... пока на pinMode(OUT4,OUTPUT); есть хоть что то pinMode(OUT1,OUTPUT); - не должен отключаться по POWER_KEY.

И второе - вывод напряжения на Lcd как я понял осуществляется со скоростью процессора - это неесть гуд, как мне БЕЗ delay выводить его (ровно как и темпиратуру)

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

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

По временным задержкам:

В loop()    if (tzad<millis() {на треб.функцию}

В функции()

......Код.    tzad=millis()+треб.задержка, возврат.

Соответственно в функцию будем попадать только после требуемого периода времени. Все delay() заменяем на подобную конструкцию.

Между строками 160 и 161 необходима задержка 750мС. В строке 159 вы выбираете конвертацию определенного датчика, можно дать команду для всех

 void DallasStartTemp()
  {
      ds.reset();          //Сброс датчиков
      ds.write(0xCC);      //Команда инициации
      ds.write(0x44);      //Команда на конвертирование
  }

Считывание после задержки проводить по адресам датчиков. Они кстати по двум или трем проводам у вас подключены?

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

Супер, delay я в самом начале ОДИН раз использовал когда менял содержимое заставки при включении Lsd. Понял ч то в цикле delay - зло сильное :)

В петле, тоест в loop счас попробую напихать эти пресловутые millis

А датчики у меня по 3-м проводам (паралельно) пока их два. будет много больше, я даже ветку отдельную создавал - как вывести показания при таком подключении (не на отдельные пины а именно на один, помимо GND и VCC

Покажите мне плиз на примере, как правильно добавить millis между 160 и 161 (кстати ранее таv было  delay и представьте как все мееедленно работало).

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

Первые три строки как у меня, и добавить tzad=millis()+750; flag=1;

в loop: 

if (flag==0) {DallasStartTemp()}

if (tzad<millis()) {tempDallas()}

в tempDallas() считываем и производим расчет температуры. flag=0;

Чтобы DallasStartTemp не запускался при каждом проходе нужна переменная-флаг, что происходит конвертация. В первой функции его подняли, после считывания сняли. Если флаг поднят DallasStartTemp() не вызываем. Дополнительно лучше ввести задаержку на работу с температурой. Каждую секунду это наверно делать бессмысленно 30сек-минута.

По EEPROM, если числа укладываются в byte, то просто прямая адресация к ячейкам. Другие, либо расчленять самому, либо какую либо прогу типа ciberlib.

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

с Millis решил разобраться сам, с вашег опримера-ничего епонял и везде ругалось на меня в коде. Итак. Сзади начну. Далласы поравил согласно вашего замечания об опросе всех а не адресно, вышло так:

//=======================================================================
//                             Считывание температур
//=======================================================================
float DS18B20(byte *adres){
  ds.reset();              //Сброс датчиков
  ds.write(0xCC);          //Команда инициации
  ds.write(0x44,1);        //Команда на конвертирование
  for (byte i = 0; i < 9; i++) {                //We need 9 bytes
    data[i] = ds.read ();
    }
  raw =  (data[1] << 8) | data[0];              //Пересчитываем в температуру
  float celsius =  (float)raw / 16.0;
  return celsius;
   }

Далее задал временные интервалы (пока для Темпиратуры и напряжения, позже по аналогии для таймеров и прочего:

//=====Millis*
long prvMlsTm = 0;                            //предыдущее показание миллисекунд для обновления показания часов
long prvMlsLght = 0;                          //предыдущее показание миллисекунд для проверки временного интервала
long prvMlsVo = 0;                            //предыдущее показание миллисекунд для обновления показания вольтметра
long voIntv = 500;                           //Интервал для обновления напряжения
long prvMlsTemp = 0;                          //предыдущее показание миллисекунд для обновления показания температуы
long tempIntv = 1000;                         //Интервал для обновления температур
//=====Millis***

И в Loop () вот так:

void loop()  
{
    IR_key();
  unsigned long currentMillis = millis();
  if(currentMillis - prvMlsTemp > tempIntv) {      //Проверяем не прошел ли нужный интервал для обновления температуры
    prvMlsTemp = currentMillis;
    printTemp();
    }
  if(currentMillis - prvMlsVo > voIntv) {          //проверяем не прошел ли нужный интервал для обновления напряжения
    prvMlsVo = currentMillis;
    printVolt();
    }
}

И всё заработало (опять возможно неоптимально однако...)

Остались мелкие вопросы про ваш кусочек кода

ds.reset();         //Сброс датчиков
ds.write(0xCC); //Команда инициации
ds.write(0x44); //Команда на конвертирование 

и про проверку условия digitalWrite(OUT1, HIGH); если (analogData2 != 0)

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

Thorn пишет:

с Millis решил разобраться сам, с вашег опримера-ничего епонял и везде ругалось на меня в коде. Итак. Сзади начну. Далласы поравил согласно вашего замечания об опросе всех а не адресно, вышло так - но неработает, забито 00,00 значения :( :



//=======================================================================
//                             Считывание температур
//=======================================================================
float DS18B20(byte *adres){
  ds.reset();              //Сброс датчиков
  ds.write(0xCC);          //Команда инициации
  ds.write(0x44,1);        //Команда на конвертирование
  for (byte i = 0; i < 9; i++) {                //We need 9 bytes
    data[i] = ds.read ();
    }
  raw =  (data[1] << 8) | data[0];              //Пересчитываем в температуру
  float celsius =  (float)raw / 16.0;
  return celsius;
   }

Далее задал временные интервалы (пока для Темпиратуры и напряжения, позже по аналогии для таймеров и прочего:



//=====Millis*
long prvMlsTm = 0;                            //предыдущее показание миллисекунд для обновления показания часов
long prvMlsLght = 0;                          //предыдущее показание миллисекунд для проверки временного интервала
long prvMlsVo = 0;                            //предыдущее показание миллисекунд для обновления показания вольтметра
long voIntv = 500;                           //Интервал для обновления напряжения
long prvMlsTemp = 0;                          //предыдущее показание миллисекунд для обновления показания температуы
long tempIntv = 1000;                         //Интервал для обновления температур
//=====Millis***

И в Loop () вот так:



void loop()  
{
    IR_key();
  unsigned long currentMillis = millis();
  if(currentMillis - prvMlsTemp > tempIntv) {      //Проверяем не прошел ли нужный интервал для обновления температуры
    prvMlsTemp = currentMillis;
    printTemp();
    }
  if(currentMillis - prvMlsVo > voIntv) {          //проверяем не прошел ли нужный интервал для обновления напряжения
    prvMlsVo = currentMillis;
    printVolt();
    }
}

И всё заработало (опять возможно неоптимально однако...)

Остались мелкие вопросы про ваш кусочек кода

ds.reset();         //Сброс датчиков
ds.write(0xCC); //Команда инициации
ds.write(0x44); //Команда на конвертирование 

и про проверку условия digitalWrite(OUT1, HIGH); если (analogData2 != 0)

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

В первом коде, после 7 строки опять нет задержки. Датчик температуру отдаст, но ту которая у него сохранилась в памяти. У нас получается до 7 строки первая функция, после вторая функция, только переменную перенесите во вторую функ.

С millis() посмотрите, я возможно синтаксис неправильно вписал или вы переменные не там объявили - нужны глобальные до setup().

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

Подскажите по кнопкам всё-же. Нужно разрешить диммировнаие (analogData2) если OUT1, HIGH а также не переводить OUT1, LOW если analogData2 !=0

В коде обработки нажатий и положений реле:

if (irrecv.decode(&results)) {        //Если пришел пакет и этот пакет не FF сохраняем правильный пакет в переменную 
    if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
    }                                 //в переменной останется прошлый, правильный, пакет и код выполнится повторно. 
    if (results.value == KEY1) digitalWrite(OUT1, !digitalRead(OUT1));
    if (analogData2 != 0)
    digitalWrite(OUT1, HIGH);
    else if (results.value == KEY2) digitalWrite(OUT2, !digitalRead(OUT2));
    else if (results.value == KEY3) digitalWrite(OUT3, !digitalRead(OUT3));
     else if (results.value == POWER_KEY) {  
      if (digitalRead(OUT2) || digitalRead(OUT3)){ // digitalRead(OUT1) || digitalRead(OUT4)
        digitalWrite(OUT2, HIGH);
        digitalWrite(OUT3, HIGH);
      }
      else {
        digitalWrite(OUT2, LOW);
        digitalWrite(OUT3, LOW);
      }          
    }
       if (val == KEY5){      
       if (analogData2 != 0)analogData2--;  
       analogWrite(OUT4, analogData2);
    }    
     if (val == KEY4){      
       if (analogData2 < 255) analogData2++;  
       analogWrite(OUT4, analogData2); 
  }
  irrecv.resume(); 
}

добавил строки вида:

if (analogData2 != 0)
digitalWrite(OUT1, HIGH);

однако... теперь первое реле включается пока 0 на диммере и выключается :( когда начинаеш диммировать. Поправьте условие моё. Кстати если поставить ; в конце if (analogData2 != 0) - то стоит сплошной треск и срабатывание через раз.

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

Вы знаете, я тоже программист еще тот. Поэтому в голове без железа скомпилить не получается. Единственное, что вижу, инверсию на реле я подразумевал только для power_key (была включена - выключить). Вы ее распространили на кучу кнопок, собака скорее всего там и роется. Давайте по порядку: что должно происходить при нажатиях key1,2,3 и power_key, при первом нажатии, при втором и т.д.

Какая максимально допустимая задержка между нажатиями key1,2,3 и power_key?

С IR я не работал, поэтому вопрос - при длительном нажатии на клавишу, он начинает слать одно значение в цикле или как то иначе? Если одно, то нужна защелка т.к.  строки с 5 по 18 начнут щелкать релюшками, а для key4,5 делать то, что надо, но по кругу (нет ограничения min и max - переход через 0).

И не понял вопрос про кнопки.

 

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

Ок, всё просто с key 1,2,3. Они все должны включать и выключать свои OUT1,2,3. Кнопка power_key может отключать OUT2, 3 (включать неможет). Но есть условие. Первое - power_key может отключать только OUT 2, 3 - с этим я справился. А вот второе условие: включение\отключение OUT1 возможно если analogData2 = 0, если оно > 0 то ни включить ни выключить OUT1 - нельзя.

На пальцах это - сначала подаем 12В на драйвер (OUT1, LOW), потом ШИМ-диммируем. Если на диммере есть хоть что то более "0" (analogData2 > 0), то подать на драйвер 12В - НЕЛЬЗЯ, вдруг диммирование равно 200 (analogData2 = 200) и тп - будет сразу яркий свет. Аналогично выключение  - нельзя (OUT1, HIGH) пока (analogData2 != 0), тоже резкое выключение будет.

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

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

Помоему - получилось, вот так выглядит запись:

if (results.value == KEY1 && analogData2 < 1) digitalWrite(OUT1, !digitalRead(OUT1));
if (results.value == KEY1 && analogData2 != 0) digitalWrite(OUT1, digitalRead(OUT1));

Тяжко незнать языка, получается по собачьи - понимаеш а сказать на Processing не можеш :)

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

Т.Е key 2  и 3 включают и выключают безусловно, power_key выключает 2 и3 если они включены.

По 1 логическая неувязка - если хочу выключить power_key, должен сперва убавить до 0 (key4,5). Если хочу включить или выключить key1, должен сперва снова убавить до 0. Каким образом убавить, когда оно у нас выключено + не забыть про плавность.

Получается, для 1 - сперва убавлять при помощи (key4,5) до 0 и потом выключать key1 или power_key. Кажется это ущербный путь. Думаю, диммер это должен делать сам плавно по команде key1 или power_key.

Вы не ответили по клмандам с IR.

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

Диммер у вас значения 0-минимум, 200 максимум? Задержка между командами вкл выкл в 2сек. нормально будет?

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

Вот аткой код для обработки кнопок (именно обработки и удержаний):

//=================================================================
//                             Обработка IR-кнопок
//=================================================================
void IR_key(){
  lcd.setCursor(17, 3);
  lcd.print(analogData2);
  lcd.print(" ");
  lcd.setCursor(17, 3);
  lcd.print(analogData2);
    lcd.setCursor(1, 3);
    lcd.print(!digitalRead(OUT1));
    lcd.setCursor(3, 3);
    lcd.print(!digitalRead(OUT2));
    lcd.setCursor(5, 3);
    lcd.print(!digitalRead(OUT3));

if (irrecv.decode(&results)) {        //Если пришел пакет и этот пакет не FF сохраняем правильный пакет в переменную 
      if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
    }                                 //в переменной останется прошлый, правильный, пакет и код выполнится повторно. 
       if (val == KEY5){      
       if (analogData2 != 0)analogData2--;  
       analogWrite(OUT4, analogData2);
    }    
       if (val == KEY4){      
       if (analogData2 < 255) analogData2++;  
       analogWrite(OUT4, analogData2); 
  }
      if (results.value == KEY1 && analogData2 < 1) digitalWrite(OUT1, !digitalRead(OUT1));
      if (results.value == KEY1 && analogData2 != 0) digitalWrite(OUT1, digitalRead(OUT1));
    else if (results.value == KEY2) digitalWrite(OUT2, !digitalRead(OUT2));
    else if (results.value == KEY3) digitalWrite(OUT3, !digitalRead(OUT3));
     else if (results.value == POWER_KEY) {  
      if (digitalRead(OUT2) || digitalRead(OUT3)){ // digitalRead(OUT1) || digitalRead(OUT4)
        digitalWrite(OUT2, HIGH);
        digitalWrite(OUT3, HIGH);
      }
      else {
        digitalWrite(OUT2, LOW);
        digitalWrite(OUT3, LOW);
      }          
    }

  irrecv.resume(); 
}
} 

Теперь я в "ручном" режиме могу включать\выключать реле, тем-самым подавая на драйвер питание, после подачи питания - диммировать до 255 и вниз до 0 и после этого отключать питание с OUT1. Остался вопрос - который только что и возник - к запретить обработку KEY2, 3 - пока на OUT1, LOW - тоесть как случайно невключить OUT2, 3 пока невключен OUT1 а то результат новая вспышка светом :)

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

К вашему посту выше - диммирование пока в "ручном" режиме - потому и добавлен if (results.value != 0xFFFFFFFF){

В автоматическом (таймером) там в меню нужно будет задавать время диммирования (не более 20-30 мин) к примеру, sunSet oн-же.

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

Я вечером попробую сам эти кусочки нашкодить с условиями которые вам написал.

Все добавки это уже потом. Сейчас важно плавное вкл,выкл. по кнопкам.

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

Кстати всё чт оя сейчас пишу для пультов (кнопок) несильно усложнит жизнь при обработке кода для таймера, незапутается ли он включая\выключая  OUT, ведь у него будет задано время обработки - а тут я уже чт ото переключил руками :(

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

Таймер на включение срабатывает безусловно в заданное время. Выключене только если еще включено. Остальное руками.

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

Реле - HIGH это выключено?

А как пишите сейчас, усложнит сильно.

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

bwn пишет:

Реле - HIGH это выключено?

А как пишите сейчас, усложнит сильно.

Да HIGH - это ВЫКЛ, оноже инверсное. Вот жеж таки знал что миои кнопки (IR) сейчас так необходимые пока часы едут могут осложнить дальше обработку кода таймерам и предустановкам в меню.

Вот ещё что, как мне сейчас обрабатывать мои KEY1, 2 и тп для к примеру такого СУТОЧНОГО таймера, хочу на нём потренироваться лазать по меню и прочее. Суточный таймер такой: http://arrduinolab.blogspot.ru/2014/10/blog-post.html

Код:

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

#define DS1307_I2C_ADDRESS 0x68

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

const byte outPin = 13; // выход управления нагрузки

byte setMinClockOn; // 
byte setHorClockOn;
byte setMinClockOff; // 
byte setHorClockOff;

byte key(){ //// для кнопок ЛСДшилда
  int val = analogRead(0);
    if (val < 50) return 5;
    else if (val < 150) return 3;
    else if (val < 350) return 4;
    else if (val < 500) return 2;
    else if (val < 800) return 1;
    else return 0;  
}

/////////// часы ..
byte decToBcd(byte val){
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val){
  return ( (val/16*10) + (val%16) );
}

void setDateDs1307(byte second,        // 0-59
                   byte minute,        // 0-59
                   byte hour,          // 1-23
                   byte dayOfWeek,     // 1-7
                   byte dayOfMonth,    // 1-28/29/30/31
                   byte month,         // 1-12
                   byte year)          // 0-99
{
   Wire.beginTransmission(DS1307_I2C_ADDRESS);
   Wire.write(0);
   Wire.write(decToBcd(second));    
   Wire.write(decToBcd(minute));
   Wire.write(decToBcd(hour));     
   Wire.write(decToBcd(dayOfWeek));
   Wire.write(decToBcd(dayOfMonth));
   Wire.write(decToBcd(month));
   Wire.write(decToBcd(year));
   Wire.endTransmission();
}

void getDateDs1307(byte *second,
          byte *minute,
          byte *hour,
          byte *dayOfWeek,
          byte *dayOfMonth,
          byte *month,
          byte *year)
{

  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

  *second     = bcdToDec(Wire.read() & 0x7f);
  *minute     = bcdToDec(Wire.read());
  *hour       = bcdToDec(Wire.read() & 0x3f); 
  *dayOfWeek  = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month      = bcdToDec(Wire.read());
  *year       = bcdToDec(Wire.read());
}
////
void setClock(){ // установка часов
  byte pos = 1;
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; 
    getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  
    lcd.clear();
    lcd.blink();

   while(key() != 1){ // крутим   цикл   
    byte KEY = key(); // читаем состояние кнопок
      delay(200);
    lcd.setCursor(1, 1);
    lcd.print("set to save");
    lcd.setCursor(0, 0);     // выводим инфу
     if (hour < 10) lcd.print("0");
    lcd.print(hour);
    lcd.print(":");
     if (minute < 10) lcd.print("0"); 
    lcd.print(minute);  
    lcd.print(" ");     
     if (dayOfMonth < 10) lcd.print("0");
    lcd.print(dayOfMonth);
    lcd.print("/");
     if (month < 10) lcd.print("0");
    lcd.print(month);
    lcd.print("/");
     if (year < 10) lcd.print("0");
    lcd.print(year);
    
    lcd.setCursor(pos, 0); // устанавливаем курсор согласно позиции
    
    if (KEY == 5 && pos < 13) pos += 3; // крутим позицию
    else if (KEY == 2 && pos > 1) pos -= 3;
    
    else if (pos == 1 && KEY == 3) hour++; // крутим значения
    else if (pos == 1 && KEY == 4) hour--;
    else if (pos == 4 && KEY == 3) minute++;
    else if (pos == 4 && KEY == 4) minute--;    
    else if (pos == 7 && KEY == 3) dayOfMonth++;
    else if (pos == 7 && KEY == 4) dayOfMonth--;    
    else if (pos == 10 && KEY == 3) month++;
    else if (pos == 10 && KEY == 4) month--;    
    else if (pos == 13 && KEY == 3) year++;
    else if (pos == 13 && KEY == 4) year--;  
    
    if (hour > 23) hour = 0;
    else if (minute > 59) minute = 0;
    else if (dayOfMonth > 31) dayOfMonth = 0;
    else if (month > 12) month = 1;
    else if (year > 99) year = 0;
  }// конец цикла
  
 setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year); 
   lcd.noBlink(); 
   lcd.clear();
   lcd.print("     Saved");
   delay(1500);
}///

void setOnOff(){    
  byte pos = 0;   
    lcd.clear();
    lcd.blink();

   while(key() != 1){ // крутим   цикл   
    byte KEY = key(); // читаем состояние кнопок
      delay(200);
    lcd.setCursor(1, 1);
    lcd.print("set to save");
    lcd.setCursor(0, 0);     // выводим инфу
     if (setHorClockOn < 10) lcd.print("0");
    lcd.print(setHorClockOn);
    lcd.print(":");
     if (setMinClockOn < 10) lcd.print("0"); 
    lcd.print(setMinClockOn);  
    lcd.print(" ");     
     if (setHorClockOff < 10) lcd.print("0");
    lcd.print(setHorClockOff);
    lcd.print(":");
     if (setMinClockOff < 10) lcd.print("0");
    lcd.print(setMinClockOff); 
    
    lcd.setCursor(pos, 0); // устанавливаем курсор согласно позиции
    
    if (KEY == 5 && pos < 9) pos += 3; // крутим позицию
    else if (KEY == 2 && pos > 1) pos -= 3;
    
    else if (pos == 0 && KEY == 3) setHorClockOn++; // крутим значения
    else if (pos == 0 && KEY == 4) setHorClockOn--;
    else if (pos == 3 && KEY == 3) setMinClockOn++;
    else if (pos == 3 && KEY == 4) setMinClockOn--;    
    else if (pos == 6 && KEY == 3) setHorClockOff++;
    else if (pos == 6 && KEY == 4) setHorClockOff--;    
    else if (pos == 9 && KEY == 3) setMinClockOff++;
    else if (pos == 9 && 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(0, setMinClockOn);
   EEPROM.write(1, setHorClockOn);
   EEPROM.write(3, setMinClockOff);
   EEPROM.write(4, setHorClockOff);

   lcd.print("     Saved");
   delay(1500);
}///
 
void menu(){
  lcd.clear();
  char menuTxt[2][14] = {"set ON/OFF >>", "set clock  >>"};
  byte pos = 0;
  
  while(1){  
    delay(200);  
    byte KEY = key();
    
    lcd.setCursor(0, 0);
    lcd.print(pos+1);
    lcd.print(".");
    lcd.print(menuTxt[pos]);
    
    if (KEY == 3 && pos != 0) pos--;
    else if (KEY == 4 && pos < 1) pos++;
    
    if (KEY == 5 && pos == 0) setOnOff();
    else if (KEY == 5 && pos == 1) setClock(); 
  }
}  
 
void setup(){
  Wire.begin(); 
  lcd.begin(16, 2);
  lcd.clear();
  
  pinMode(outPin, OUTPUT);
  digitalWrite(outPin, LOW);
  
  setMinClockOn = EEPROM.read(0);
  setHorClockOn = EEPROM.read(1);
  setMinClockOff = EEPROM.read(3);
  setHorClockOff = EEPROM.read(4);
}

void loop()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;  
  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  
  // обработка кнопок
  if (key() == 1) menu(); // если нажата селект
  else if (key() == 3) digitalWrite(outPin, HIGH);
  else if (key() == 4) digitalWrite(outPin, LOW);

  // сравниваем время и управляем выходом// 
  if (setMinClockOff == minute && setHorClockOff == hour
      && second == 0) digitalWrite(outPin, LOW);
  if (setMinClockOn == minute && setHorClockOn == hour
      && second == 0) digitalWrite(outPin, HIGH);    
 
 
//   lcd.clear();
    lcd.setCursor(0, 0);
     if (hour < 10) lcd.print("0"); 
    lcd.print(hour); 
    lcd.print(":");
     if (minute < 10) lcd.print("0"); 
    lcd.print(minute);
//  lcd.print(":");
//   if (second < 10) lcd.print("0");
//  lcd.print(second);
//    lcd.setCursor(8, 0); 
//    lcd.print("    ");  
    lcd.setCursor(0, 1);
     if (dayOfMonth < 10) lcd.print("0");
    lcd.print(dayOfMonth);
    lcd.print("/");
     if (month < 10) lcd.print("0");
    lcd.print(month);
    lcd.print("/");
     if (year < 10) lcd.print("0");
    lcd.print(year);
     //
    lcd.setCursor(11, 0);
     if (setHorClockOn < 10) lcd.print("0"); 
    lcd.print(setHorClockOn); 
    lcd.print(":");
     if (setMinClockOn < 10) lcd.print("0"); 
    lcd.print(setMinClockOn);
    
    lcd.setCursor(11, 1);
     if (setHorClockOff < 10) lcd.print("0"); 
    lcd.print(setHorClockOff); 
    lcd.print(":");
     if (setMinClockOff < 10) lcd.print("0"); 
    lcd.print(setMinClockOff);      
   
    lcd.setCursor(7, 0);
    if (digitalRead(outPin)) lcd.print("ON ");
    else lcd.print("Off");
    
    delay(200); // нужно для нармальной работы кнопок
}

я так понял, мне нужно както заменить:

byte key(){ //// для кнопок ЛСДшилда
int val = analogRead(0);
if (val < 50) return 5;
else if (val < 150) return 3;
else if (val < 350) return 4;
else if (val < 500) return 2;
else if (val < 800) return 1;
else return 0;
}

на мои пультовые:

#define RECV_PIN 11 //IR-приемник
//Key
#define POWER_KEY 0x41BEF00F //exit, коды кнопок пульта
#define KEY1 0x41BED02F //up
#define KEY2 0x41BEC03F //enter
#define KEY3 0x41BE609F //down
#define KEY4 0x41BE10EF //vol_up
#define KEY5 0x41BE708F //vol_down
byte analogData1, analogData2 = 0; //Значения аналоговых выходов dimmer, пока только analogata2
uint32_t val;
IRrecv irrecv(RECV_PIN);
decode_results results;

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

С кнопками не заморачивайтесь сейчас. Я попробую накидать диммер как я это вижу, дальше в него входить по событию. По логике сразу вылезают key 2,3 - они должны быть включены только при максимальном диммере, иначе изменение яркости будет незаметно. Или параллельное диммирование всех. Здесь вам решать. Для меню скорее всего придется лепить еще одну кнопку, чтобы в него войти или использовать цифровые(тогда только пульт)

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

Супер, все именно так. (про пульт) кнопок там много я любую приспособлю под вход в меню к примеру и сохранение (оноже ОК).

И я тогда смогу с этим пультом и в дальнейшем лазать по меню (если к примеру добавлю впоследствии таймер ещё один на кормление или замер влажности)?.

Я вот как обработал кнопки key2,3 (это питание НЕдиммируемых светиков). Теперь они ненажимаются пока на OUT1 (это питание диммируемых светиков) нет 1 или реле ВЫКЛЮЧЕНО (HIGH) а так-же НЕвключаются пока уровень диммирования по key1 не превысит критического значения, к примеру 200 (почти максимум). А то могло быть так, что светики по key1 ещё и незажглись а я тут возьми и нажми key2 или 3, снова вспышка. 

Вот так описал в коде:



void IR_key(){
  lcd.setCursor(17, 3);
  lcd.print(analogData2);
  lcd.print(" ");
  lcd.setCursor(17, 3);
  lcd.print(analogData2);
    lcd.setCursor(1, 3);
    lcd.print(!digitalRead(OUT1));
    lcd.setCursor(3, 3);
    lcd.print(!digitalRead(OUT2));
    lcd.setCursor(5, 3);
    lcd.print(!digitalRead(OUT3));

if (irrecv.decode(&results)) {        //Если пришел пакет и этот пакет не FF сохраняем правильный пакет в переменную 
      if (results.value != 0xFFFFFFFF){ //Если пришел FF, соответственно пропускаем.
      val = results.value;            //Сверяем значение из переменной val.. если пришла команда повтора (пакет с FF)    
    }                                 //в переменной останется прошлый, правильный, пакет и код выполнится повторно. 
       if (val == KEY5){      
       if (analogData2 != 0)analogData2--;  
       analogWrite(OUT4, analogData2);
    }    
       if (val == KEY4){      
       if (analogData2 < 255) analogData2++;  
       analogWrite(OUT4, analogData2); 
  }
      if (results.value == KEY1 && analogData2 < 1) digitalWrite(OUT1, !digitalRead(OUT1));
      if (results.value == KEY1 && analogData2 != 0) digitalWrite(OUT1, digitalRead(OUT1));
    if (results.value == KEY2 && OUT1 != 0 && analogData2 > 200) digitalWrite(OUT2, !digitalRead(OUT2));
        else if (results.value == KEY2 && OUT1 != 0 && analogData2 != 0) digitalWrite(OUT2, digitalRead(OUT2));
     if (results.value == KEY3 && OUT1 != 0 && analogData2 > 200) digitalWrite(OUT3, !digitalRead(OUT3));
        else if (results.value == KEY3 && OUT1 != 0 && analogData2 != 0) digitalWrite(OUT3, digitalRead(OUT3));
          else if (results.value == POWER_KEY) {  
      if (digitalRead(OUT2) || digitalRead(OUT3)){ // digitalRead(OUT1) || digitalRead(OUT4)
        digitalWrite(OUT2, HIGH);
        digitalWrite(OUT3, HIGH);
      }
      else {
        digitalWrite(OUT2, LOW);
        digitalWrite(OUT3, LOW);
      }          
    }

  irrecv.resume(); 
}
} 
Thorn
Offline
Зарегистрирован: 07.11.2014

Блин блин счас обнаружил что кнопки key2, 3 не выполняют проверку на analogData2 != 0 и могут свободно включать и выключать свои выходы OUT2, 3 даже если OUT1 = 0. Тоесть я вывел светики в 200 и могу легко вык\вкл кнопками key2, 3 переключаться, надо как то ЗАПРЕТИТЬ диммирование пока OUT1 = 0. пробую так и писать -ругается, видимо нельзя приравнивать к 0

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

На OUT1 нет 0. Есть HIGH, LOW или TRUE, FALSE

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

bwn пишет:

На OUT1 нет 0. Есть HIGH, LOW или TRUE, FALSE

Помойму понял в чём фишка, наверное нельзя сравнивать с OUT1, нужно типа такого при диммировании:

if (analogData2 < 255 && !digitalRead(OUT1) !=0) analogData2++;

и всё, тепреь пока OUT1 = 0 то нельзя приращивать ++, всего то нужно добавить "!" в код :) счас также опишу key2 и 3

Вы меня спасаете коорый раз, респект.

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

Ура, пока всё работает, и пульт с реле и терморегуляция и напряжометр :) - всё как хотел.

Тепреь МЕНЮ надо освоить, чтобы править время и темпиратуры и прочее без заливки скетчем. Уже особо и нежалею что невзял чейто готовый большой и ДОРОГОЙ проэкт (относительно имеющихся затрат и интриги).

ПО правде меню с кнопками и EPROM - самый для меня сейчас страшный зверь.

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

В идеале помоему, OUT1 не может выключится пока analogData2 >0. Все таки диммер первичен.

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

А меню, это не зверь, это гемморой жрущий гору памяти и ресурсов.

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

bwn пишет:

В идеале помоему, OUT1 не может выключится пока analogData2 >0. Все таки диммер первичен.

Вот у меня сейчас так и есть. Пока analogData2 <1 только key1 кнопка обрабатывает OUT. Как только key1 нажата  analogData2++ и достигнув 200 уже можно обрабатывать нажатие key2, 3 

Счас снвоа нашёл косячок пока писал - если при OUT2, LOW  или OUT3, LOW значение analogData2 примет менее 200 .......... то кнопки key2,3 уже невозвращают OUT2, 3 HIGH - это ведет к невозможности снять напряжение с НЕДИММИРУЕМЫХ светиков, нужно подниматься до 200 и выше :(