Инкубатор

neverok
Offline
Зарегистрирован: 19.01.2017

а как можно в моём случае привязать поворот через два часа

ромаха28
Offline
Зарегистрирован: 17.06.2016

Здравствуйте!уважаемые ГУРУ!Помогите с скетчем,не регулируется влажность.Не пинайте за непонятливость-опыта мало.

[code]
#include <OneWire.h>
#include <LiquidCrystal.h>
#include <Wire.h>
#include <SHT2x.h>
#include <PID_v1.h>
#include <DFR_Key_GhostX.h>
#include <EEPROM.h>

//ds18b20
bool zapros = false;
unsigned long dat_time = 0UL;
float celsius;
//***
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
OneWire  ds(15); // 15 нога вход DS18b20
//***
unsigned long PrevTimes = 0UL; // переменная паузы опроса датчика температуры и влажности.

//объявляем переменные для ПИД
#define heater 3  //Это нога обогревателя.
double Setpoint, Input, Output;
//Инициализируем ПИД-библиотеку и коэффициенты
PID myPID(&Input, &Output, &Setpoint,65,1,0, DIRECT); 
int WindowSize = 100;  // ширина окна времени. 1000 единиц. 
unsigned long oldTime = 0UL;
unsigned long serialTime; //это переменная для времени передичи данных в processing
//***
#define humidity 5//Это нога увлажнителя.
byte value; // переменная состояния релешки увлажнителя
//***
DFR_Key_GhostX keypad(A0); // указываем номер пина на который подключена кнопка
int localKey = 0;
int prevNonWaitKey = 0;
int count = 0;
int Pin = 2;           // нога реле поворота
int motor = LOW;
//long previousMillis = 0; 
//long interval = 1000000; //время переворота

//***
unsigned long pov; //назначаем тип переменной поворота.
unsigned long PrevMenu = 0UL; //переменная паузы выходв из меню
int bitton;// переменная кнопок
//***
float temperatura; //переменная установки температуры. по умолчанию было 38.0гр
float humidity_air; //переменная установки влажности. по умолчанию было 50.0%
///***
byte gradus[8] = {
  0b00110,
  0b01001,
  0b01001,
  0b00110,
  0b00000,
  0b00000,
  0b00000,
  0b00000
};
 
void straj(){
	lcd.createChar(1, gradus);
	lcd.begin(16, 2);
	lcd.clear();
	lcd.setCursor(0, 0); // вывод на экран
	lcd.print("temp     t\1    %");
	lcd.setCursor(0, 1); // вывод на экран
	lcd.print("     %RH      C\1");
}
void OprosKnop(){
 
 /*keypad.getKey();
	Grabs the current key.
	Returns a non-zero integer corresponding to the pressed key,
	OR
	Returns 0 for no keys pressed,
	OR
	Returns -1 (sample wait) when no key is available to be sampled.
	*/
	localKey = keypad.getKey();

	if (localKey != SAMPLE_WAIT){
		if (localKey != prevNonWaitKey){
            bitton = localKey;
			//return bitton; // что то туплю и не знаю зачем я это написал.))
			prevNonWaitKey = localKey;
		}
	}
}

void Komandy(){
	OprosKnop();
	if (bitton == SELECT_KEY) menu();
}



void DS18b20() {
   
   if(!zapros){
      ds.reset();// сброс шины
      ds.write(0xCC);
      //ds.select(addr);//выставить адрес
      ds.write(0x44);        // // начать преобразование (без паразитного питания) ds.write(0x44, 1); с паразитным
      zapros=true;
      dat_time=millis();
   }
   //delay(1000);     // 750 мс может быть, не достаточно,
   if(zapros&&(millis() - dat_time>750)){
     
      ds.reset();// сброс шины present = ds.reset();
      ds.write(0xCC);
      //ds.select(addr);  //выставить адрес 
      ds.write(0xBE);         // читаем из внутренней памяти датчика
      byte data[12];
      byte i;
      for ( i = 0; i < 9; i++) {           // нам нужно 9 байт
         data[i] = ds.read();
      }
      int16_t raw = (data[1] << 8) | data[0];
      celsius = (float)raw / 16.0;
      Input = celsius; // присваиваем переменной PID регулятора, текущие изменения температуры.
	  //Serial.print("  Temperature = ");
      //Serial.print(celsius);
      //Serial.println(" Celsius, ");
      lcd.setCursor(5, 0); 
      lcd.print(celsius); // выводим на жк температуру с DS18b20
      zapros = false;
   
   }

}


void vlajnost(){
   if (millis() >= PrevTimes + 2000){  
		float RH = SHT2x.GetHumidity();
		lcd.setCursor(0, 1);
		lcd.print(RH);
		lcd.setCursor(9, 1); 
		lcd.print(SHT2x.GetTemperature());
		PrevTimes = millis();
		//Serial.println(" tik "); 
				if ((RH >= humidity_air + 2) || (RH <= humidity_air - 2)){ 
			if ((RH <= humidity_air - 2) && (value == LOW)){
			value = HIGH;
			digitalWrite(humidity,value);
			}
			if ((RH >= humidity_air + 2) && (value == HIGH)){
			value = LOW;
			digitalWrite(humidity,value);
			}	
		}
   }
}
 

void Obogrev(){
	//int pws;
	int pow;
	myPID.Compute(); //читаем выходной сигнал ПИД регулятора
 
	unsigned long time = millis();
	
	//pws=(int)Output;
    //analogWrite(heater, pws);
	if(time - oldTime > WindowSize){
		oldTime += WindowSize;
		pow=(int)Output;
		pow = map(pow, 0, 100, 0, 100);
		lcd.setCursor(12, 0); 
		lcd.print(pow); // выводим процентную составляющую
	}
	
	if(Output > time - oldTime){ 
		digitalWrite(heater,HIGH); 
		digitalWrite(13,HIGH);
	}
	else{ 
		digitalWrite(heater,LOW); 
		digitalWrite(13,HIGH);
	}
 
}
void povorot(){
   lcd.clear(); // очищаем экран
   lcd.setCursor(0, 0); 
   lcd.print(" POVOROT LOTKA  ");
   lcd.setCursor(0, 1);
   lcd.print("        min    )");
   lcd.print(pov);
   bool knopka_none = true;
   
   PrevMenu = millis();
   while(10000 >= millis() - PrevMenu) {
      OprosKnop();
      if(bitton == NO_KEY)knopka_none = true;
      
      if(knopka_none){
         switch (bitton) {
            case SELECT_KEY:
              
              knopka_none = false;
              EEPROM_float_write(8, pov);
              menu();
              
              break;
            case LEFT_KEY:
              //выполняется когда  var равно 3
              knopka_none = false;
              pov -= 60000;
              if(pov <= 0)pov = 0;// ограничение диапазона поворота
             
              PrevMenu = millis(); // обнуляем счетчик, чтоб не вылетать из меню, во время работы с ним.
              break;
            case RIGHT_KEY:
              //выполняется, когда var равно 4
              knopka_none = false;
              pov += 60000;
              if(pov >= 21600000)pov = 21600000;// ограничение диапазона поворота 360 минут это 6 часов
              //тут написать ограничение поворота
              PrevMenu = millis(); // обнуляем счетчик, чтоб не вылетать из меню, во время работы с ним.
              break;
            
         }
         lcd.setCursor(0, 1);
         lcd.print(pov);
      }
   }
   EEPROM_float_write(8, pov);// сохраним в ячейки памяти 8,9,10,11 значение поворота, которое выставили. unsigned long 4 байта.
   menu();
}
 
void menu(){
  
   lcd.createChar(1, gradus);
   lcd.setCursor(0, 0);
   lcd.print(" Menu Ustanovok ");
   lcd.setCursor(0, 1);
   lcd.print("Up-t\1Do-RH%R-pov");
   PrevMenu = millis();
   while(10000 >= millis() - PrevMenu) {
      OprosKnop();
      switch (bitton) {
        case SELECT_KEY:
           //выполняется когда  var равно 1
           // тут написать сохранку
           break;
        case UP_KEY:
           //выполняется когда  var равно 3
           temp();
           break;
        case DOWN_KEY:
           //выполняется, когда var равно 4
           RH();
           break;
      case RIGHT_KEY:
          
           povorot();
           break;
      }
   }
   straj();// выход из менюшки
}

void temp(){
	lcd.clear();
	lcd.setCursor(0, 0);
	lcd.print("      temp      ");
	lcd.setCursor(8, 1);
	lcd.print(temperatura);// тут переменная температуры
	bool knopka_none = true;
	
	PrevMenu = millis();
	while(10000 >= millis() - PrevMenu) {
		OprosKnop();
		if(bitton == NO_KEY)knopka_none = true;
		
		if(knopka_none){
			switch (bitton) {
				case SELECT_KEY:
				  //выполняется когда  var равно 1
				  knopka_none = false;
				  EEPROM_float_write(0, temperatura);// сохраним в ячейки памяти 0,1,2,3 значение температуры, которую выставили. float 4 байта.
				  menu();
				  // и написать сохранку
				  break;
				case LEFT_KEY:
				  //выполняется когда  var равно 3
				  knopka_none = false;
				  temperatura -= 0.1;
				  if(temperatura <= 33)temperatura = 33;// ограничение диапазона температуры
				  //тут написать ограничение температуры
				  PrevMenu = millis(); // обнуляем счетчик, чтоб не вылетать из меню, во время работы с ним.
				  break;
				case RIGHT_KEY:
				  //выполняется, когда var равно 4
				  knopka_none = false;
				  temperatura += 0.1;
				  if(temperatura >= 39)temperatura = 39;// ограничение диапазона температуры
				  //тут написать ограничение температуры
				  PrevMenu = millis(); // обнуляем счетчик, чтоб не вылетать из меню, во время работы с ним.
				  break;
				
			}
			lcd.setCursor(8, 1);
			lcd.print(temperatura);
		}
	}
	EEPROM_float_write(0, temperatura);// сохраним в ячейки памяти 0,1,2,3 значение температуры, которую выставили. float 4 байта.
	menu();
}



void RH(){
	lcd.clear();
	lcd.setCursor(0, 0);
	lcd.print("      %RH       ");
	lcd.setCursor(8, 1);
	lcd.print(humidity_air);
	bool knopka_none = true;// ключик для единичного срабатывания нажатия кнопки.
	
	PrevMenu = millis();
	while(10000 >= millis() - PrevMenu) {
		OprosKnop();
		if(bitton == NO_KEY)knopka_none = true;
		
		if(knopka_none){
			switch (bitton) {
				case SELECT_KEY:
					//выполняется когда  var равно 1
					knopka_none = false;
					EEPROM_float_write(4, humidity_air);// сохраним в ячейки памяти 4,5,6,7 значение влажности, которую выставили. float 4 байта.
					menu();
					// и написать сохранку
					break;
				case LEFT_KEY:
					//выполняется когда  var равно 3
					knopka_none = false;
					humidity_air -= 1;
					if(humidity_air <= 40)humidity_air = 40;// ограничение диапазона влажности
					//тут написать ограничение влажности
					PrevMenu = millis(); // обнуляем счетчик, чтоб не вылетать из меню, во время работы с ним.
					break;
				case RIGHT_KEY:
					//выполняется, когда var равно 4
					knopka_none = false;
					humidity_air += 1;
					if(humidity_air >= 90)humidity_air = 90;// ограничение диапазона влажности
					//тут написать ограничение влажности
					PrevMenu = millis(); // обнуляем счетчик, чтоб не вылетать из меню, во время работы с ним.
					break;
				
			}
			lcd.setCursor(8, 1);
			lcd.print(humidity_air);
		}
	}
	EEPROM_float_write(4, humidity_air);// сохраним в ячейки памяти 4,5,6,7 значение влажности, которую выставили. float 4 байта.
	menu();
}
 
void setup() {
	// следующие 4 строчки нужны для определения ног. Выходами их назовем и первичное состояние им зададим выключено.
	
	pinMode(humidity, OUTPUT);
	//pinMode(Pin, OUTPUT); // устанавливаем порт, как выход   
        pinMode(heater, OUTPUT); //для шима нет в этом необходимости
	pinMode(13, OUTPUT); //диодик
	digitalWrite(humidity, LOW);
	digitalWrite(heater, LOW);
	Wire.begin();
	//Serial.begin(9600);
	lcd.createChar(1, gradus);
	lcd.begin(16, 2);
	lcd.clear();
	lcd.setCursor(0, 0); // вывод на экран
	lcd.print("temp     t\1    %");
	lcd.setCursor(0, 1); // вывод на экран
	lcd.print("     %RH      C\1");
	//***
	temperatura = EEPROM_float_read(0); // читаем из памяти значение температуры.
	humidity_air = EEPROM_float_read(4); // читаем из памяти значение влажности.
	pov = EEPROM_float_read(8); // читаем из памяти значение температуры.
	//Setpoint = 38.7;        //задаем требуемое показание датчика температуры
	Setpoint = temperatura;        //задаем требуемое показание датчика температуры
						  
	myPID.SetOutputLimits(0, WindowSize);
	myPID.SetMode(AUTOMATIC); //выключатель автоматик включен всегда, мануал включать в ручную 
	myPID.SetSampleTime(500);  //блин не помню... 
	//***
	/*
	OPTIONAL
	keypad.setRate(x);
	Sets the sample rate at once every x milliseconds.
	Default: 10ms
	*/
	keypad.setRate(10);
  
	
	
}

void loop() { 
	Komandy();//следим за кнопками
	DS18b20();//опрашиваем температуру
	vlajnost();//смотрим и регулируем влажность
	Obogrev();//смотрим и регулируем обогрев
	
	
	
	//send-receive with processing if it's time
	if(millis() >= serialTime)
	{
    SerialReceive();
    SerialSend();
    serialTime+=500;
	}
{
  
 // if (millis() - previousMillis > interval) {
  //  previousMillis = millis();   // запоминаем текущее время
  //  // если светодиод был выключен – включаем и наоборот :)
   // if (value == LOW)
    //  value = HIGH;
  //  else
    //  value = LOW;

   // digitalWrite(Pin, value);
  }
  
  } 
//}

 


//*******функция записи в EEPROM переменных Float
void EEPROM_float_write(int addr, float val) // запись в ЕЕПРОМ
{  
  byte *x = (byte *)&val;
  for(byte g = 0; g < 4; g++) EEPROM.write(g+addr, x[g]);
}

float EEPROM_float_read(int addr) // чтение из ЕЕПРОМ
{    
  byte x[4];
  for(byte g = 0; g < 4; g++) x[g] = EEPROM.read(g+addr);
  float *y = (float *)&x;
  return y[0];
}
//End****функции записи в EEPROM переменных Float
/********************************************
* Serial Communication functions / helpers
********************************************/


union {                // This Data structure lets
  byte asBytes[24];    // us take the byte array
  float asFloat[6];    // sent from processing and
}                      // easily convert it to a
foo;                   // float array



// getting float values from processing into the arduino
// was no small task.  the way this program does it is
// as follows:
//  * a float takes up 4 bytes.  in processing, convert
//    the array of floats we want to send, into an array
//    of bytes.
//  * send the bytes to the arduino
//  * use a data structure known as a union to convert
//    the array of bytes back into an array of floats

//  the bytes coming from the arduino follow the following
//  format:
//  0: 0=Manual, 1=Auto, else = ? error ?
//  1: 0=Direct, 1=Reverse, else = ? error ?
//  2-5: float setpoint
//  6-9: float input
//  10-13: float output 
//  14-17: float P_Param
//  18-21: float I_Param
//  22-245: float D_Param
void SerialReceive()
{

  // read the bytes sent from Processing
  int index=0;
  byte Auto_Man = -1;
  byte Direct_Reverse = -1;
  while(Serial.available()&&index<26)
  {
    if(index==0) Auto_Man = Serial.read();
    else if(index==1) Direct_Reverse = Serial.read();
    else foo.asBytes[index-2] = Serial.read();
    index++;
  }
 
  // if the information we got was in the correct format,
  // read it into the system
  if(index==26  && (Auto_Man==0 || Auto_Man==1)&& (Direct_Reverse==0 || Direct_Reverse==1))
  {
    Setpoint=double(foo.asFloat[0]);
    Input=double(foo.asFloat[1]);       // * the user has the ability to send the
                                          //   value of "Input"  in most cases (as
                                          //   in this one) this is not needed.
    if(Auto_Man==0)                       // * only change the output if we are in
    {                                     //   manual mode.  otherwise we'll get an
      Output=double(foo.asFloat[2]);      //   output blip, then the controller will
    }                                     //   overwrite.
   
    double p, i, d;                       // * read in and set the controller tunings
    p = double(foo.asFloat[3]);           //
    i = double(foo.asFloat[4]);           //
    d = double(foo.asFloat[5]);           //
    myPID.SetTunings(p, i, d);            //
   
    if(Auto_Man==0) myPID.SetMode(MANUAL);// * set the controller mode
    else myPID.SetMode(AUTOMATIC);             //
   
    if(Direct_Reverse==0) myPID.SetControllerDirection(DIRECT);// * set the controller Direction
    else myPID.SetControllerDirection(REVERSE);          //
  }
  Serial.flush();                         // * clear any random data from the serial buffer
}

// unlike our tiny microprocessor, the processing ap
// has no problem converting strings into floats, so
// we can just send strings.  much easier than getting
// floats from processing to here no?
void SerialSend()
{
  Serial.print("PID ");
  Serial.print(Setpoint);   
  Serial.print(" ");
  Serial.print(Input);   
  Serial.print(" ");
  Serial.print(Output);   
  Serial.print(" ");
  Serial.print(myPID.GetKp());   
  Serial.print(" ");
  Serial.print(myPID.GetKi());   
  Serial.print(" ");
  Serial.print(myPID.GetKd());   
  Serial.print(" ");
  if(myPID.GetMode()==AUTOMATIC) Serial.print("Automatic");
  else Serial.print("Manual"); 
  Serial.print(" ");
  if(myPID.GetDirection()==DIRECT) Serial.println("Direct");
  else Serial.println("Reverse");
}  

[/code]

Скетч не мой,взят с форума "перепелка",долго парни разбирались,молодцы, много работы сделано.У них работало,у меня то одно, то другое, и казалось уже все...готовил яйца для инкуба, а влажность не реагирует.Как будто пид на увлажнителе!Работает прирывисто.На реле диод мерцает.

 

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

А что говорит автор?

ромаха28
Offline
Зарегистрирован: 17.06.2016

Здравствуй Роман.У Александра работал.Этой приблуды у него  уже нету.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012
void vlajnost(){
   if (millis() >= PrevTimes + 2000){  
		float RH = SHT2x.GetHumidity();
		lcd.setCursor(0, 1);
		lcd.print(RH);
		lcd.setCursor(9, 1); 
		lcd.print(SHT2x.GetTemperature());
		PrevTimes = millis();
		//Serial.println(" tik "); 
				if ((RH >= humidity_air + 2) || (RH <= humidity_air - 2)){ 
			if ((RH <= humidity_air - 2) && (value == LOW)){
			value = HIGH;
			digitalWrite(humidity,value);
			}
			if ((RH >= humidity_air + 2) && (value == HIGH)){
			value = LOW;
			digitalWrite(humidity,value);
			}	
		}
   }
}

Совершенно нормальная функция. Пином увлажнителя больше никто не дрыгат. Пин в сетапе назначен выходом и на нем низкий уровень. Функция правильная. Если отклонились по влажности на 2 и больше процентов, то включаем или выключаем увлажнитель, проверяя что он не был включен или выключен ранее. 

Может тупо электричества для реле не хватает? Откуда оно запитано?

ромаха28
Offline
Зарегистрирован: 17.06.2016

Роман,спасибо за помощь!Подключился к 11 пину,работает.Тестером позвонил пины,все ШИМ пины под питанием(9в блок питания) звенят.Без питания нет.Битая дунька?

ромаха28
Offline
Зарегистрирован: 17.06.2016

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

ромаха28
Offline
Зарегистрирован: 17.06.2016

Здравствуйте,Роман.Все мучаюсь с автоматикой!Первый вывод из 75 яиц 48 вышло,но проблема осталась.Когда не подключен двигатель на переворот,все нормально,даже когда реле включается на холостую,но когда в сеть включаю мотор,на экране сплошные глюки.Что сделать?Помогите?!Может шунт како надо?

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Помеха. Общие правила борьбы с помехами. Раздельное питание. Силовую часть подальше от процессора. Фильтры питания, стабилизаторы, на конденсаторы не скупитесь. Линию связи дисплея в экран. Ну и тыды.

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

Двигатель переворота помехи создает при работе. Нужно конденсатор на контакты двигателя припаять.

ромаха28
Offline
Зарегистрирован: 17.06.2016

Извините,а можно по точнее,какой кондер и куда?двигатель переменного тока,как на лотках китайских.Поясните пожалуйста?

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

Версия прошивки 2.0. Используется энергонезависимая память, система управления не требует резервного питания, более точно работает нагреватель.

Благодарю за материал!

Есть несколько вопросов, поможете?

в строчке unsigned int t = 378; // требуемая температура 37,8 C нет ошибки? не надо запятую 37,8?

Напишите пожалуйста подробнее про концевик, тумблер, резистор, какие пины рабочие?

Спасибо

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

по подключению экрана, правильно?

пина ардуино Уно 7, 8, 9, 10, 11, 12

и соответственно экрана: 14.13.12.11.6.4

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

Приветствую друзья!

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

Сделал я большой инкубатор на 800 куриных яиц, так же есть маленький на 100 перепелиных. Большой окупил себя при первом цикле инкубирования. Потом еще 4 раза и на этом все, в разбор его. Не потому, что что-то в нем сломалось, мне стало не интересно. Маленький работает, в этом году уже 4 вывода перепелок. Для собственных нужд, так сказать.

Есть два варианта прошивки.

//на второй строке дисплея отображаются: мощность на нагеревателе Pw 1-99%;
//                                       режим инкубации 0 - ручной; 1 - куры; 2 - перепелки; 3 - утки; 4 - гуси;
//                                       включен поворот - |; выключен поворот - 0
//                                       вентиляция работает - *; выключена - -;
//                                       работает вентилятор влажности - #; выключен - -;
//                                       текущее значение температуры


//в меню появились два новых пункта: пункт 7 - включение подсветки дисплея - постоянно горит или выключение - погасает по истечении 4 минут после нажатия клавиши "меню"
//                                   пункт 8 - выбор датчика температуры - DHT22 или 18B20
//ШИМ модуляция на порт 3 со значением 8-200

//работа сценария ошибки: - если по истечении 6 часов после включения температура меньше установленной на 2 градуса срабатывает ошибка
//                        - при достижении температуры установленного значения включается функция определения ошибки - если в течении 20 минут температура
//                          превышает или ниже установленных пределов срабатывает ошибка
//                        - если спустя 2 часа после проветривания температура меньше установленной на 2 градуса включается ошибка







#include <avr/wdt.h> //сторожевой пёс
#include <OneWire.h>
#include <DallasTemperature.h>
#include "DHT.h"
#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>  // Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define DHTPIN 12 // номер пина, к которому подсоединен датчик 
DHT dht(DHTPIN, DHT22);//DHT dht(DHTPIN, DHT11);// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 11 // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

#define turnPin 6 //поворот
#define ventilator 4 //вентилятор проветривания
#define ventilatorH 5 //венилятор для поддержания влажности
#define buttonPlus 15
#define buttonMenu 16
#define buttonMinus 17

#define TimeError 60    //время задержки ошибки 20сек * TimeError = 20 1 минута-3 еденицы 


boolean r_povorota, r_povorot, r_ventilator, r_nasosa, proverka, turn, PowerLigt, dhT;
long prevmicros;
byte Power_promeg, ventilar, H, dlit_raboti_nasosa = 5, vrema_raboti_ventilatora;  //0-255 vrema_roboti_ventilatora

byte gradus[8] = {B01000, B10100, B01000, B00111, B01000, B01000, B00111, B00000,};

boolean counter, r_ventilatora, step, powerTurn, exitMenu, left, right, turnStartPosition, ventilation, error, ErrorPower;
byte hi, sec, mi, sTimer, mTimer, hTimer, dTimer = 1, day = 1; 
byte backLight, menu;
byte avtomatik, timeTurn, setPower, timeTurnExperement, periodTurn, accountTurn, Power;
byte n, rn, rv, m, i, errorTime, errorL, errorH, errorHour = 6; 
unsigned int t, Temperature, b;          //0-65535
float e, h, tt, kof, tSetprintDisplay;
//signed char kof; //-128 - 127



void setup() {
  
  wdt_disable();
  sensors.begin();
  dht.begin();
  lcd.begin(); lcd.clear();
  
  lcd.createChar(1, gradus);  //Регистрируем собственные символы с кодами 1 и ...

  DDRB |= (1 << 5); //моргание светодиода на пине 13 каждую секунду
  pinMode(ventilatorH, OUTPUT); pinMode(ventilator, OUTPUT); digitalWrite(ventilator, LOW);  pinMode(turnPin, OUTPUT); digitalWrite(turnPin, LOW);

  pinMode(buttonPlus, INPUT_PULLUP); pinMode(buttonMinus, INPUT_PULLUP); pinMode(buttonMenu, INPUT_PULLUP);
  
  hTimer = EEPROM.read(20); dTimer = EEPROM.read(3); 
  powerTurn = EEPROM.read(11); 
  kof = EEPROM.read(5); 
  dlit_raboti_nasosa = EEPROM.read(6); vrema_raboti_ventilatora = EEPROM.read(7);
  avtomatik = EEPROM.read(8); t = EEPROM.read(9) + 370; H = EEPROM.read(10); 
  setPower = EEPROM.read(13); PowerLigt = EEPROM.read(15); dhT = EEPROM.read(16); //dhT - флаг для выбора датчика температуры dht|18B20
  
  if (avtomatik) parametri();    
  setTimer();
  lcd.backlight(); //noBacklight();
  backLight = 240;
  wdt_enable (WDTO_4S);

   h = dht.readHumidity(); 
    if (dhT) {tt = dht.readTemperature();  Temperature = (10 * tt) + kof;}                                 //температура с DHT22  
    else {sensors.requestTemperatures(); tt = sensors.getTempCByIndex(0); Temperature = (10 * tt) + kof;}  //температура с 18B20
    lcd.setCursor(2, 0); lcd.print("inkubator_4"); lcd.setCursor(5, 1); lcd.print("v_0.8"); delay(2000);
  displayPrint();      
}






void setTimer(){
  cli();
  TCCR1A = 0b00000000; //none   toggle  clear  set - инверсия установить
  TCCR1B = (1 << WGM12)|(1 << CS12); // 10b00011001;
  TIMSK1 = (1 << OCIE1A);
  OCR1A = 62500; //разрешение ШИМ сигнала (ограничение для счета регистра TCNT1), возможно до 65 535
  sei();
}

ISR (TIMER1_COMPA_vect) //вектор прерывания по СТС таймера/счетчика 2 для работы собственного времени контроллера
{
  PORTB = PORTB ^ (1 << 5); sTimer++; if (sTimer > 59) {mTimer++; sTimer = 0; if (mTimer > 59) {hTimer++; mTimer = 0;} if (hTimer > 23) {dTimer++; hTimer = 0;}}
}


  

void loop() {

if (menu){
switch (menu){
   case 1:
      lcd.setCursor(0, 1); lcd.print("Temp calibration");
      lcd.setCursor(2, 0); lcd.print(kof/10); lcd.print("\1"); //lcd.print(" daL "); lcd.print(e, 1);
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); kof++; EEPROM.write(5, kof); if (kof > 15) {kof = -15; lcd.clear();} exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); kof--; EEPROM.write(5, kof); if (kof < -15) {kof = 15; lcd.clear();} exitMenu = true;}
      break;

   case 2: lcd.setCursor(2, 0); lcd.print("reset time?");
      if (!digitalRead(buttonMinus) || !digitalRead(buttonPlus)) {backLight = 20; delay(200); hTimer = 0; dTimer = 1; day = 1; mTimer = 0;
      EEPROM.write(3, day); EEPROM.write(2, hTimer); menu = 0; lcd.clear(); displayPrint();}
      break;

   case 3: lcd.setCursor(1, 0); lcd.print("setPowerDefaul");
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); setPower--; EEPROM.write(13, setPower); lcd.clear(); exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); setPower++; EEPROM.write(13, setPower); lcd.clear(); exitMenu = true;}
      lcd.setCursor(5, 1);  i = map(setPower, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");  
      break;    

   case 4: lcd.setCursor(3, 0); lcd.print("avtom setap");
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); avtomatik++; lcd.clear(); EEPROM.write(8, avtomatik); if (avtomatik > 4) avtomatik = 4; exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); avtomatik--; lcd.clear(); EEPROM.write(8, avtomatik); if (avtomatik > 4) avtomatik = 0; exitMenu = true;}  
      lcd.setCursor(2, 1); 
      if (!avtomatik) lcd.print("not avtom"); if (avtomatik == 1) lcd.print("kuri"); if (avtomatik == 2) lcd.print("perepelki"); if (avtomatik == 3) lcd.print("ytki"); if (avtomatik == 4) lcd.print("gusi");
      if (avtomatik) parametri();
      break;
      
    case 5: lcd.setCursor(3, 0); lcd.print("test turn");   
      if (!digitalRead(buttonPlus)) {PORTD = PORTD ^ (1 << 6); turn = !turn; lcd.setCursor(4, 1); if (turn) lcd.print("left"); else lcd.print("right"); delay(500); exitMenu = true;}
      if (!digitalRead(buttonMinus)) {PORTD = PORTD ^ (1 << 6); turn = !turn; lcd.setCursor(4, 1); if (turn) lcd.print("left"); else lcd.print("right"); delay(500); exitMenu = true;}
     break;  

    case 6: lcd.setCursor(3, 0); lcd.print("test Vent"); 
      if (!digitalRead(buttonPlus)) {PORTD = PORTD ^ (1 << 4); ventilation = !ventilation; lcd.setCursor(5, 1); if (ventilation) lcd.print("on "); else lcd.print("off"); delay(500); exitMenu = true;}
      if (!digitalRead(buttonMinus)) {PORTD = PORTD ^ (1 << 4); ventilation = !ventilation; lcd.setCursor(5, 1); if (ventilation) lcd.print("on "); else lcd.print("off"); delay(500); exitMenu = true;}
     break;   

  //  case 6: lcd.setCursor(3, 0); lcd.print("Set Power"); lcd.setCursor(2, 1); i = map(Power, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");
  //    if (!digitalRead(buttonPlus)) {delay(200); Power--; if (Power < 60) Power = 60; exitMenu = true;}
  //    if (!digitalRead(buttonMinus)) {delay(200); Power++; if (Power > 200) Power = 200; exitMenu = true;}
  //   break;

    case 7: lcd.setCursor(3, 0); lcd.print("Set Ligt"); lcd.setCursor(5, 1); if (PowerLigt) lcd.print("on "); else lcd.print("off");
      if (!digitalRead(buttonPlus)) {delay(200); PowerLigt = !PowerLigt; exitMenu = true; EEPROM.write(15, PowerLigt);}
      if (!digitalRead(buttonMinus)) {delay(200); PowerLigt = !PowerLigt; exitMenu = true; EEPROM.write(15, PowerLigt);}
    break;

     case 8: lcd.setCursor(3, 0); lcd.print("DHT "); lcd.setCursor(10, 0); lcd.print(" 18B20");
             lcd.setCursor(8, 0); if (dhT) lcd.print("<"); else lcd.print(">");
      if (!digitalRead(buttonPlus)) {delay(300); dhT = !dhT; exitMenu = true; EEPROM.write(16, dhT);}
      if (!digitalRead(buttonMinus)) {delay(300); dhT = !dhT; exitMenu = true; EEPROM.write(16, dhT);}
    break;

    case 9: if (avtomatik) {menu = 0; displayPrint(); backLight = 240;}
            else {lcd.setCursor(1, 0); lcd.print("Set t = "); lcd.setCursor(10, 0); tSetprintDisplay = 370+t; lcd.print(tSetprintDisplay/10); lcd.print("\1");  
                if (!digitalRead(buttonPlus)) {delay(200); t++; if (t > 15) t = 15; EEPROM.write(9, t); exitMenu = true;}
                if (!digitalRead(buttonMinus)) {delay(200); t--; if (t > 8) t = 0; EEPROM.write(9, t); exitMenu = true;}}
     break;

    case 10: lcd.setCursor(1, 0); lcd.print("Set H = "); 
                if (!digitalRead(buttonPlus)) {delay(200); H += 5; if (H > 90) H = 90; EEPROM.write(10, H); exitMenu = true;} 
                if (!digitalRead(buttonMinus)) {delay(200); H -= 5; if (H < 30) H = 30; EEPROM.write(10, H); exitMenu = true;}
     break;

    case 11: lcd.setCursor(3, 0); lcd.print("povorot  "); if (powerTurn) lcd.print("on "); else lcd.print("off");
                if (!digitalRead(buttonPlus)) {delay(200); powerTurn = !powerTurn; EEPROM.write(11, powerTurn); exitMenu = true;}
                if (!digitalRead(buttonMinus)) {delay(200); powerTurn = !powerTurn; EEPROM.write(11, powerTurn); exitMenu = true;}
       break;

    case 15: lcd.setCursor(3, 0); lcd.print("error"); lcd.setCursor(10, 0); lcd.print(Temperature * 0.1, 1); lcd.print("\1");
                lcd.setCursor(1, 1); lcd.print("off"); lcd.setCursor(12, 1); lcd.print("off");
                if (!digitalRead(buttonPlus)) {delay(200); noTone(8);}
                if (!digitalRead(buttonMinus)) {delay(200); noTone(8);}
       break;
            
         
  }
}


 if (mi != mTimer){
        h = dht.readHumidity();     //влажность с DHT22
  if (!menu) {lcd.setCursor(3, 0); if (mTimer < 10) lcd.print("0"); lcd.print(mTimer);
  lcd.setCursor(12, 0); lcd.print("H"); lcd.print(h, 0); lcd.print("%"); }      //влажность
  mi = mTimer;

  if ((Temperature > t - 5) && (Temperature < t + 5) && (h < H - 3)) {digitalWrite(ventilatorH, HIGH);  displayPrint();}  //вентилятор для поддержания влажности
  else {digitalWrite(ventilatorH, LOW); displayPrint();}  
  
  if (ventilation) {displayPrint(); vrema_raboti_ventilatora--; PORTD |= (1 << 4); if (!vrema_raboti_ventilatora) {PORTD &= ~(1 << 4); ventilation = false; vrema_raboti_ventilatora = EEPROM.read(7);  displayPrint();  //включение/выключение работа вентилятора проветривания
        errorHour = 2; ErrorPower = false;}}       // включение отсчета времени для срабатывания ошибки
 }


 if (sec != sTimer){
      counter = !counter; if (!menu) {if (counter) {lcd.setCursor(2, 0); lcd.print(":");} else {lcd.setCursor(2, 0); lcd.print(" ");}}
  
      if (backLight) {backLight--; lcd.backlight(); if (!backLight) {if (!PowerLigt) lcd.noBacklight(); menu = 0; lcd.clear(); displayPrint();}}
     
      if (!ventilation) m++; //если не работает проветривание, то считаем 20 сек для работы нагревателя
      else {if (dhT) {tt = dht.readTemperature();  Temperature = (10 * tt) + kof;}                                 //температура с DHT22  
            else {sensors.requestTemperatures(); tt = sensors.getTempCByIndex(0); Temperature = (10 * tt) + kof;}  //температура с 18B20
            if (!menu) {lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1);}} 
   
      sec = sTimer;}



  if (hi != hTimer){ if (!menu) {lcd.setCursor(0, 0); if (hTimer < 10) lcd.print("0"); lcd.print(hTimer);} EEPROM.write(20, hTimer); //сохранение текущего часа
      
      sensors.requestTemperatures(); // температура с 18b20
      e = sensors.getTempCByIndex(0);
      if ((r_ventilator || (!avtomatik && vrema_raboti_ventilatora)) && (hTimer == 6 || hTimer == 18)) {ventilation = true; Power = 2; analogWrite(3, Power);}//включение вентиляции
      if (powerTurn) {timeTurn++; if (timeTurn > 1) {timeTurn = 0; PORTD = PORTD ^ (1 << 6); accountTurn++;}} //инверсия пина для поворота
      if (errorHour) errorHour--; //истечение 6 часов для включения режима авария
      hi = hTimer;}


  if (day != dTimer) {if (!menu) {lcd.setCursor(6, 0); lcd.print("Day"); lcd.print(dTimer);} 
     EEPROM.write(3, dTimer); day = dTimer; parametri(); }

  if (m > 20) {
    
    if (dhT) {tt = dht.readTemperature();  Temperature = (10 * tt) + kof;}                                 //температура с DHT22  
    else {sensors.requestTemperatures(); tt = sensors.getTempCByIndex(0); Temperature = (10 * tt) + kof;}  //температура с 18B20

   
    if ((!errorHour) && (Temperature < t - 20)) {error = true; menu = 15; errorMelody();}          //включение мелодии ошибки
    if (Temperature == t) ErrorPower = true;
    if (ErrorPower)  {if (Temperature < errorL || Temperature > errorH) errorTime++;                                  //определение ошибки
                      else {errorTime--; if (errorTime > TimeError) {errorTime = 0; noTone(8);}}
    if (errorTime > TimeError) {errorMelody(); error = true; menu = 15; errorTime = 20;}


    if (!menu) {lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1);}   
    if (Temperature != t) nagrev(); m = 0;} //работа нагревателя, выполнется 3 разa в 1 минуту(ы), чтобы не мешать инерционности процессу
  
   
  if (!digitalRead(buttonMenu)) {delay(250); menu++; lcd.clear(); if (exitMenu) {menu = 0; displayPrint(); exitMenu = false;} 
      if (menu > 11) {noTone(8); menu = 0; displayPrint(); backLight = 240;}}
        
  wdt_reset();

}













void nagrev()                              //работа нагревателя, 200 - минимальная мощность, 0 - максимальная
{
  // if (!r_ventilatora) {
    if (Temperature > t + 5) {
//      digitalWrite(ventilator, HIGH);
      rn += 4;
      Power = EEPROM.read(13);
    }
 //   if (Temperature < t + 3) digitalWrite(ventilator, LOW);
//  }

  if (Temperature < t)      n = 1;
  if (Temperature < t - 10) n = 2;
  if (Temperature < t - 40) n = 5;
  if (Temperature > t)      n = 4;
  if (Power == 255)         n = 3;

  switch (n) {
    case 4:
      if (rn > 12) {
        rn = 0;
        if (Temperature >= b) Power -= 1;
        else Power += 1;
        if (Temperature > t + 3) Power -= 6;
      }
      else rn++;
      break;

    case 1:
      if (rv > 14) {
        rv = 0;
        if (Temperature <= b) Power += 1;
        else Power -= 1;
      }
      else rv++;
      break;

    case 2:
      if (Temperature > b)
      {
        b = Temperature - b;
        switch (b)
        { case 1: Power -= 4; break;
          case 2: Power -= 5; break;
          case 3: Power -= 8; break;
          case 4: Power -= 8; break;
          case 5: Power -= 20; break;
          default: Power -= 22;
        }
      }
      else {
        Power += 4;
        if (Temperature < (t - 20)) Power += 30;
      }
      break;

    case 5:
      if (Temperature <= (b + 30)) Power += 20; if (Power > 200) Power = 200;
      break;

    case 3:
      if (Temperature < t) Power = 30;
      break;
  }

  b = Temperature;                    //промежуточное значение температуры
  if (Temperature > t + 7) Power -= 20; 
  Power = constrain(Power, 8, 200); //ограничение мощности 50%
  if ((Power == 8) && (Temperature > t + 3)) Power = 0; //выключение нагревателя 
  if (!menu) {lcd.setCursor(0, 1); lcd.print("Pow"); i = map(Power, 0, 200, 1, 99); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");}//мощность на нагрев
  analogWrite(3, Power);
}


void parametri()                //изменение параметров  в зависимости от дня инкубации, сюда можно внести свои данные режима инкубации
{
  switch(avtomatik){
   case 1:   //для кур
      if (day < 2) {t = 380; powerTurn = true; r_ventilator = false;  H = 60;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 14) {t = 377; H = 60; r_ventilator = true; EEPROM.write(7, 18); vrema_raboti_ventilatora = 18; powerTurn = true; }
      if (day > 17) {t = 370; H = 80; powerTurn = false; r_ventilator = true;}
    break;
   case 2:   //для перепелов
      if (day < 2) {t = 380; powerTurn = true;  r_ventilator = false; H = 60;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 7) {t = 377; H = 60; r_ventilator = true; EEPROM.write(7, 10); vrema_raboti_ventilatora = 10; powerTurn = true;}
      if (day > 13) {t = 377; H = 60; powerTurn = true; r_ventilator = true;}
      if (day > 14) {t = 370; H = 80; powerTurn = false; r_ventilator = true;}
    break;
    case 3: //утка
         if (day < 2) {t = 385; powerTurn = true;  r_ventilator = false; H = 70;} 
         else {t = 380; powerTurn = true; r_ventilator = false; H = 70;}
      if (day > 7) {t = 378; H = 60; r_ventilator = false; EEPROM.write(7, 15); vrema_raboti_ventilatora = 15; powerTurn = true;}
      if (day > 14) {t = 378; H = 60; powerTurn = true; r_ventilator = true;}
      if (day > 25) {t = 375; H = 90; powerTurn = false; r_ventilator = false;}
    break;
    case 4: //гуси
      if (day < 6) {t = 380; powerTurn = true;  r_ventilator = false; H = 70;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 13) {t = 378; H = 70; r_ventilator = true; EEPROM.write(7, 25); vrema_raboti_ventilatora = 25; powerTurn = true;}
      if (day > 26) {t = 375; H = 85; powerTurn = true; r_ventilator = true;}
      if (day > 28) {t = 375; H = 85; powerTurn = false; r_ventilator = false;}
    break;
  }
}



void displayPrint(){
  lcd.setCursor(0, 0); if (hTimer < 10) lcd.print("0"); lcd.print(hTimer); lcd.setCursor(3, 0); if (mTimer < 10) lcd.print("0"); lcd.print(mTimer);
  lcd.setCursor(6, 0); lcd.print("Day"); lcd.print(dTimer);
  lcd.setCursor(12, 0); lcd.print("H"); lcd.print(h, 0); lcd.print("%");
  
  lcd.setCursor(0, 1); lcd.print("Pw"); i = map(Power, 0, 200, 1, 99); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");//мощность на нагрев
  lcd.setCursor(6, 1); lcd.print(avtomatik);
  lcd.setCursor(7, 1); if (powerTurn) lcd.print("|"); else lcd.print("0"); lcd.print(accountTurn);
  lcd.setCursor(8, 1);  if (ventilation) lcd.print("*"); else lcd.print("-");
  lcd.setCursor(9, 1);  if (digitalRead(ventilatorH)) lcd.print("#"); else lcd.print("-");
  lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1); lcd.print("\1");
  
}

//на второй строке дисплея отображаются: мощность на нагеревателе Pw 1-99%;
//                                       режим инкубации 0 - ручной; 1 - куры; 2 - перепелки; 3 - утки; 4 - гуси;
//                                       включен поворот - |; выключен поворот - 0
//                                       вентиляция работает - *; выключена - -;
//                                       работает вентилятор влажности - #; выключен - -;
//                                       текущее значение температуры



void errorMelody() { //8 пин на буззер
  //noTone(8);  tone(6, 440, 200);  delay(200);
  //noTone(6);  tone(7, 494, 500);  delay(500);
  tone(8, 523, 300);  
}

В первом подключения осуществить как на картинке. Питание осуществляется от напряжения в 12 вольт, ток должен соответствовать вашим потребностям. Регулировка мощности по средствам ШИМ модуляции. В данной версии прошивки в автоматический режим заложены параметры инкубации цыплят кур, перепелок, гусей и уток. Выбор режима осуществляется в 4 пункте меню "avtom setap". Если не выбран ни один из режимов на табло выводится "not avtom" - в этом режиме возможно самостоятельно настраивать значение температуры в диапазоне (36,2 - 38,5 С), значение влажности (30 - 90% с шагом в 5%), включать/отключать поворот.

Перед запуском цикла инкубации, после установки нужного режима, необходимо сбросить значения времени и дня.  Сделать это возможно в пункте 2 меню - "reset time?". При нажатии клавиши + или - время обнулится.

В пункте 1 меню возможно произвести калибровку датчика температуры в диапазоне (-1.5 - +1.5 градуса). Производить калибровку необходимо в установившемся режиме с помощью ртутного или цифрового термометра. Установившийся режим - это режим при котором на дисплее в течении длительного времени отображается одинаковая температура (допускается колебание 0,2 градуса). Как правило, цифровые датчики очень точные. По их даташиту погрешность не более 0,2 градуса в некотором диапазоне. (т.е. калибровка не обязательна).

В пункте 5 меню возможно произвести тестовый пуск поворота, нажав клавишу + или -. На дисплее отобразится условное направление поровота - “left”, “right”. Поворот в прошивке - это переключение реле - два часа оно включено, два выключено.

В пункте 6 меню (ЭТО САМЫЙ ВАЖНЫЙ ПУНКТ) необходимо установить значение мощности нагревателя поумолчанию.  Т.е. мощность, которая требуется для поддержания температуры в установившемся значении и от этого значения отнять 5%. Это производится один раз при при тестовом пуске инкубатора. Данное значение индивидуально для каждого размера и значения мощности нагревательных элементов. При установке завышенного значения возможна некорректная работа инкубатора, заниженного - значительные коллебания температуры в момент переходных процессов. Необходимо учесть, что после 7 - 15 дня инкубации (в зависимости от вида инкубируемого яйца) яйца начинают сами выделять тепло, поэтому значению мощности необходимо взять немного ниже, чем мошность расходуемоя для поддержания установившегося режима.

Время считает сам микроконтроллер. При этом он каждый час сохраняет значение времени и каждый день - значение дня. Значение секунд и минут при каждом рестарте обнуляются.

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

Работа дисплея: на второй строке дисплея отображаются: мощность на нагеревателе Pw 1-99%;  режим инкубации 0 - ручной; 1 - куры; 2 - перепелки; 3 - утки; 4 - гуси; включен поворот - |; выключен поворот - 0; // вентиляция работает - *; выключена - -; // работает вентилятор влажности - #; выключен - -;// текущее значение температуры //в меню появились два новых пункта: пункт 7 - включение подсветки дисплея - постоянно горит или выключение - погасает по истечении 4 минут после нажатия клавиши "меню"//пункт 8 - выбор датчика температуры - DHT22 или 18B20 //ШИМ модуляция на пин 3 со значением 8-200 //работа сценария ошибки: - если по истечении 6 часов после включения температура меньше установленной на 2 градуса срабатывает ошибка// - при достижении температуры установленного значения включается функция определения ошибки - если в течении 20 минут температура// превышает или ниже установленных пределов срабатывает ошибка//- если спустя 2 часа после проветривания температура меньше установленной на 2 градуса включается ошибка.

Вторая версия прошивки более сложная и интересная. В ней мощность регулируется симмистором, т.е. питается инкубатор от розетки 220 В. Время зависимо от синусоиды сети - т.е. для регулировки мощности симмистором, необходимо определять момент перехода синусоиды через ноль - а так как это значение известно (50 Гц), то при 100-м переходе синусоиды получается одна секунда. Исходя из этого, если включить инкубатор не в сеть переменного тока или в сеть с частотой 60 Гц, время не пойдет/пойдет не правильно - инкубатор работать не будет. Поворот лотка осуществляется шаговым двигателем 28BYJ-48, поэтому требуется еще и драйвер для него. Стоит это все очень дешево 2$ и 1$. Взял ШД, потому что он сразу с редуктором, он очень мал, очень мощный момент, относительно маленькая скорость поворота. Для работы данного двигателя задействован таймер-счетчик, который по прерыванию переключает пин с высокого на низкий уровень.  Во всем остальном работа похожа на первый вариант.

#include <avr/wdt.h> //сторожевой пёс
#include <CyberLib.h> //Библиотека от Cyber-Place.ru
#include <OneWire.h>
#include <DallasTemperature.h>
#include "DHT.h"
#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>  // Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define DHTPIN 12 // номер пина, к которому подсоединен датчик // Раскомментируйте в соответствии с используемым датчиком// Инициируем датчик
DHT dht(DHTPIN, DHT22);//DHT dht(DHTPIN, DHT11);// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 11 // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

#define dirpin 9       //пины для подключения драйвера ШД перемещения принтера влево вправо
#define steppin 10
#define enable 8
#define kuler 5
#define ventilator 4
#define nasos 6
#define buttonPlus 15
#define buttonMenu 16
#define buttonMinus 17

volatile uint8_t tic, Power = 200, msTimer, sTimer;
boolean r_povorota, r_povorot, r_ventilator, r_nasosa, proverka, turn;
long prevmicros;
byte Power_promeg, ventilar, H, dlit_raboti_nasosa = 5, vrema_raboti_ventilatora;  //0-255 vrema_roboti_ventilatora

byte gradus[8] = {B01000, B10100, B01000, B00111, B01000, B01000, B00111, B00000,};

boolean dhT, counter, r_ventilatora, step, powerTurn, exitMenu, left, right, turnStartPosition, ventilation;
byte hi, sec, mi, mTimer, hTimer, dTimer = 1, day = 1;
byte backLight, menu;
byte avtomatik, timeTurn, setPower, timeTurnExperement, periodTurn, accountTurn;
byte n, rn, rv, m, i;
unsigned int t, Temperature, b;          //0-65535
float e, h, tt, kof, tSetprintDisplay;
//signed char kof; //-128 - 127

void setup() {
  wdt_disable();
  D13_Out;                                      //Настраиваем порты на выход
  D13_Low;                                      //установить на выходах низкий уровень сигнала
  D2_In;                                        //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  //CHANGE – прерывание вызывается при любом изменении значения на входе;
  //RISING – вызов прерывания при изменении уровня напряжения с низкого (Low) на высокий(HIGH)
  //FALLING – вызов прерывания при изменении уровня напряжения с высокого (HIGH) на низкий (Low)
  attachInterrupt(0, detect_up, LOW);         // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
  StartTimer1(halfcycle, 40);                 //время для одного разряда ШИМ
  StopTimer1();                               //остановить таймер

  sensors.begin();
  dht.begin();

  lcd.begin(); lcd.clear();
  lcd.createChar(1, gradus);  //Регистрируем собственные символы с кодами 1 и ...
  DDRD |= (1 << 5)|(1 << 4)|(1 << 6); //кулер, вентилятор, насос
  PORTD |= (1 << 5);        
  PORTD &= ~(1 << 4)|(1 << 6);
  //pinMode(ventilator, OUTPUT); digitalWrite(ventilator, LOW);  pinMode(nasos, OUTPUT); digitalWrite(nasos, LOW); (pinMode(kuler, OUTPUT); digitalWrite(kuler, HIGH);
  DDRB |= (1 << 0)|(1 << 1)|(1 << 2);
  PORTB &= ~(1 << 1)|(1 << 2); 
  PORTB |= (1 << 0); 
  //pinMode(dirpin, OUTPUT);  pinMode(steppin, OUTPUT);  pinMode(enable, OUTPUT); digitalWrite(dirpin, LOW); digitalWrite(enable, HIGH);
  DDRC &= ~(1 << 1)|(1 << 2)|(1 << 3);
  PORTC |= (1 << 1)|(1 << 2)|(1 << 3);
  //pinMode(buttonPlus, INPUT_PULLUP); pinMode(buttonMinus, INPUT_PULLUP); pinMode(buttonMenu, INPUT_PULLUP);
  
  hTimer = EEPROM.read(20);  dTimer = EEPROM.read(3); day = dTimer;
  timeTurn = EEPROM.read(4); powerTurn = EEPROM.read(11); 
  kof = EEPROM.read(5); 
  dlit_raboti_nasosa = EEPROM.read(6); vrema_raboti_ventilatora = EEPROM.read(7);
  avtomatik = EEPROM.read(8); t = EEPROM.read(9) + 370; H = EEPROM.read(10); // r_ventilator = EEPROM.read(12); 
  setPower = EEPROM.read(13); 
  left = EEPROM.read(16); right = EEPROM.read(17);

  if (avtomatik) parametri();    
  setTimer();
  lcd.backlight(); //noBacklight();
  backLight = 60;
  wdt_enable (WDTO_4S);

  sensors.requestTemperatures();  e = sensors.getTempCByIndex(0);   // Считываем температуру
  h = dht.readHumidity(); tt = dht.readTemperature(); Temperature = (10 * tt) + kof; tt += kof * 0.1;
  displayPrint();      
}

void setTimer()
{
  cli();
  TCCR2A |= (1 << WGM21);  //CTC to OCR2A
  TCCR2A &= ~(1 << WGM20);
  TCCR2B &= ~(1 << WGM22);
  TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);  //1024
  TIMSK2 &= ~(1 << OCIE2A);
  OCR2A = 50;
  sei();
}

ISR (TIMER2_COMPA_vect)
{
  PORTB = PORTB ^ (1 << 2); //шаги для двигателя поворота (шаговый двинатель типа 28BYJ-49) - времени на шаг 0,0032 сек
}

//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{
  tic++;  //счетчик
  if (Power < tic ) D13_High; //управляем выходом
}

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

void detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту
{
  StopTimer1(); //остановить таймер
  D13_Low; //логический ноль на выходы
  tic = 0;       //обнулить счетчик
  attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
  msTimer++; if (msTimer > 99) {sTimer++; msTimer = 0;} //счет времени
}
//*************************************************************************


void loop() {Start

if (menu){
switch (menu){
   case 1: lcd.setCursor(4, 0); lcd.print("- turn -"); lcd.setCursor(6, 1); lcd.print(timeTurnExperement); lcd.print(" sec");
      if (!digitalRead(buttonPlus) || !digitalRead(buttonMinus)) { backLight = 20;
      EEPROM.write(16, 0); EEPROM.write(17, 0); left = false; right = false;
      PORTB &= ~(1 << 0); TIMSK2 |= (1 << OCIE2A); 
      if (!digitalRead(buttonMinus)) PORTB &= ~(1 << 1);  
      if (!digitalRead(buttonPlus)) PORTB |= (1 << 1);
      exitMenu = true;
    } 
    else {PORTB |= (1 << 0); TIMSK2 &= ~(1 << OCIE2A); timeTurnExperement = 0;}
      break;

   case 2:
      lcd.setCursor(0, 1); lcd.print("Temp calibration");
      lcd.setCursor(2, 0); lcd.print(kof/10); lcd.print("\1"); lcd.print(" daL "); lcd.print(e, 1);
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); kof++; EEPROM.write(5, kof); if (kof > 15) {kof = -15; lcd.clear();} exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); kof--; EEPROM.write(5, kof); if (kof < -15) {kof = 15; lcd.clear();} exitMenu = true;}
     break;

   case 3: 
      lcd.setCursor(2, 1); lcd.print("Turning time"); timeTurn = EEPROM.read(4);
      lcd.setCursor(3, 0); lcd.print(timeTurn); lcd.print(" sec "); lcd.print("l"); lcd.print(left); lcd.print(" r"); lcd.print(right);  
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); timeTurn--; EEPROM.write(4, timeTurn); exitMenu = true;} 
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); timeTurn++; EEPROM.write(4, timeTurn); exitMenu = true;} 
     break;   

   case 4: lcd.setCursor(2, 0); lcd.print("reset time?");
      if (!digitalRead(buttonMinus) || !digitalRead(buttonPlus)) {backLight = 20; delay(200); hTimer = 0; dTimer = 1; day = 1; mTimer = 0;
      EEPROM.write(3, day); EEPROM.write(2, hTimer); menu = 0; lcd.clear(); displayPrint();}
     break;

   case 5: lcd.setCursor(1, 0); lcd.print("setPowerDefaul");
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); setPower--; EEPROM.write(13, setPower); lcd.clear(); exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); setPower++; EEPROM.write(13, setPower); lcd.clear(); exitMenu = true;}
      lcd.setCursor(5, 1);  i = map(setPower, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");  
     break;    

   case 6: lcd.setCursor(3, 0); lcd.print("avtom setap");
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); avtomatik++; lcd.clear(); EEPROM.write(8, avtomatik); if (avtomatik > 2) avtomatik = 2; exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); avtomatik--; lcd.clear(); EEPROM.write(8, avtomatik); if (avtomatik > 2) avtomatik = 0; exitMenu = true;}  
      lcd.setCursor(2, 1); 
      if (!avtomatik) lcd.print("not avtom"); if (avtomatik == 1) lcd.print("kuri"); if (avtomatik == 2) lcd.print("perepelki"); 
      if (avtomatik) parametri();
     break;

    case 7: lcd.setCursor(3, 0); lcd.print("DHT "); lcd.setCursor(10, 0); lcd.print(" 18B20");
             lcd.setCursor(8, 0); if (dhT) lcd.print("<"); else lcd.print(">");
      if (!digitalRead(buttonPlus)) {delay(300); dhT = !dhT; exitMenu = true; EEPROM.write(16, dhT);}
      if (!digitalRead(buttonMinus)) {delay(300); dhT = !dhT; exitMenu = true; EEPROM.write(16, dhT);}
    break;  
      
    case 8: lcd.setCursor(3, 0); lcd.print("test turn"); lcd.setCursor(0, 1); lcd.print(timeTurn); lcd.print(" l"); lcd.print(left); lcd.print(" r"); lcd.print(right);  
      if (!digitalRead(buttonPlus)) {backLight = 40; turn = true; exitMenu = true; timeTurn = EEPROM.read(4);}
      if (!digitalRead(buttonMinus)) {backLight = 40; turn = true; exitMenu = true; timeTurn = EEPROM.read(4);}
     break;  

 //   case 8: lcd.setCursor(0, 0); lcd.print("Stop Pow - Ventil"); lcd.setCursor(2, 1); lcd.print(vrema_raboti_ventilatora); lcd.print(":"); lcd.print(59 - sTimer);
 //     if (!digitalRead(buttonPlus)) {delay(200); backLight = 200; m = 0; ventilation = true;}
 //     if (!digitalRead(buttonMinus)) {delay(200); backLight = 200; m = 0; ventilation = true;}
 //    break; 
     
    case 9: lcd.setCursor(3, 0); lcd.print("Set Power"); lcd.setCursor(2, 1); i = map(Power, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");
      if (!digitalRead(buttonPlus)) {backLight = 40; delay(200); Power--; if (Power < 60) Power = 60; exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 40; delay(200); Power++; if (Power > 200) Power = 200; exitMenu = true;}
     break;

    case 10: if (avtomatik) {menu = 0; displayPrint(); backLight = 240;}
            else {lcd.setCursor(1, 0); lcd.print("Set t = "); lcd.setCursor(10, 0); tSetprintDisplay = 370+t; lcd.print(tSetprintDisplay/10); lcd.print("\1");  
                if (!digitalRead(buttonPlus)) {delay(200); t++; if (t > 15) t = 15; EEPROM.write(9, t); exitMenu = true;}
                if (!digitalRead(buttonMinus)) {delay(200); t--; if (t > 8) t = 0; EEPROM.write(9, t); exitMenu = true;}
      }
     break;   

    case 11: lcd.setCursor(1, 0); lcd.print("Set H = "); lcd.setCursor(10, 0); lcd.print("37,"); lcd.print(t); lcd.print("\1");
      if (!digitalRead(buttonPlus)) {delay(200); H += 5; if (H > 90) H = 90; EEPROM.write(10, H); exitMenu = true;} 
      if (!digitalRead(buttonMinus)) {delay(200); H -= 5; if (H < 30) H = 30; EEPROM.write(10, H); exitMenu = true;}
     break;

    case 12: lcd.setCursor(1, 0); lcd.print("Set ventilation"); lcd.setCursor(1, 1); lcd.print(vrema_raboti_ventilatora); lcd.print(" min");
      if (!digitalRead(buttonPlus)) {backLight = 20; exitMenu = true; delay(200); vrema_raboti_ventilatora++; if (vrema_raboti_ventilatora > 30) vrema_raboti_ventilatora = 30; EEPROM.write(7, vrema_raboti_ventilatora);}
      if (!digitalRead(buttonMinus)) {backLight = 20; exitMenu = true; delay(200); vrema_raboti_ventilatora--; if (vrema_raboti_ventilatora > 30) vrema_raboti_ventilatora = 0; EEPROM.write(7, vrema_raboti_ventilatora);}
     break;




        
  }
}

 if (sTimer > 59) {mTimer++; if (mTimer > 59) {hTimer++; if (hTimer > 23) {dTimer++; hTimer = 0;} mTimer = 0;} sTimer = 0;}

 if (mi != mTimer){
        h = dht.readHumidity();     //влажность с DHT22
  if (!menu) {lcd.setCursor(3, 0); if (mTimer < 10) lcd.print("0"); lcd.print(mTimer);
  lcd.setCursor(12, 0); lcd.print("H"); lcd.print(h, 0); lcd.print("%"); }      //влажность
  mi = mTimer;

  if (ventilation) {vrema_raboti_ventilatora--; PORTD |= (1 << 4); if (!vrema_raboti_ventilatora) {PORTD &= ~(1 << 4); ventilation = false; vrema_raboti_ventilatora = EEPROM.read(7); Power = EEPROM.read(13);}}        //включение/выключение работа вентилятора проветривания
 }


 if (sec != sTimer){
  counter = !counter; if (!menu) {if (counter) {lcd.setCursor(2, 0); lcd.print(":");} else {lcd.setCursor(2, 0); lcd.print(" ");}}
  
  if (backLight) {backLight--; lcd.backlight(); if (!backLight) {lcd.noBacklight(); menu = 0; lcd.clear(); displayPrint();}}
  
  if (menu == 1) timeTurnExperement++;
  
  if (turn) {PORTB &= ~(1 << 0); TIMSK2 |= (1 << OCIE2A); 
      if (left) PORTB &= ~(1 << 1);
      else if (right) PORTB |= (1 << 1);
      else {timeTurn /= 2; PORTB &= ~(1 << 1); left = true; EEPROM.write(16, left);} 
      timeTurn--; 
      if (turnStartPosition) {timeTurn /= 2; turnStartPosition = false;}
      if (!timeTurn) {PORTB |= (1 << 0); TIMSK2 &= ~(1 << OCIE2A); left = !left; right = !right; EEPROM.write(16, left); EEPROM.write(17, right); turn = false; accountTurn++; if (accountTurn > 99) accountTurn = 0;
          if (!menu) {lcd.setCursor(7, 1); if (right) lcd.print("R"); if (left) lcd.print("L"); lcd.print(accountTurn);} timeTurn = EEPROM.read(4);} }  //поворот на установленный промежуток времени
  
   if (!ventilation) m++; //если не работает проветривание, то считаем 20 сек для работы нагревателя
   else {if (dhT) {tt = dht.readTemperature();  Temperature = (10 * tt) + kof;}                                 //температура с DHT22  
          else {sensors.requestTemperatures(); tt = sensors.getTempCByIndex(0); Temperature = (10 * tt) + kof;}  //температура с 18B20
          if (!menu) {lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1);}} 
            
   sec = sTimer;
     }



  if (hi != hTimer){ if (!menu) {lcd.setCursor(0, 0); if (hTimer < 10) lcd.print("0"); lcd.print(hTimer);} EEPROM.write(20, hTimer); //сохранение текущего часа
      
      if (powerTurn && timeTurn) {periodTurn++; if (periodTurn > 1) {periodTurn = 0; turn = true; timeTurn = EEPROM.read(4);}}
      if (turnStartPosition && hTimer == 1) turn = true; 

      sensors.requestTemperatures(); // температура с 18b20
      e = sensors.getTempCByIndex(0);

      if ((r_ventilator || (!avtomatik && vrema_raboti_ventilatora)) && (hTimer == 6 || hTimer == 18)) {ventilation = true; Power = 200;}//включение вентиляции
      
      hi = hTimer;
     }

  if (day != dTimer) {if (!menu) {lcd.setCursor(6, 0); lcd.print("Day"); lcd.print(dTimer);} EEPROM.write(3, day); parametri();
      day = dTimer;
     }

  if (m > 20) {
     if (dhT) {tt = dht.readTemperature();  Temperature = (10 * tt) + kof;}                                 //температура с DHT22  
     else {sensors.requestTemperatures(); tt = sensors.getTempCByIndex(0); Temperature = (10 * tt) + kof;}  //температура с 18B20
    
     //tt = dht.readTemperature();  //температура с DHT22  // sensors.requestTemperatures(); //  e = sensors.getTempCByIndex(0);
     //Temperature = (10 * tt) + kof;
     if (!menu) {lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1);}   
     if (Temperature != t) nagrev(); m = 0;} //работа нагревателя, выполнется 3 разa в 1 минуту(ы), чтобы не мешать инерционности процессу
  
   
  if (!digitalRead(buttonMenu)) {delay(250); menu++; lcd.clear(); if (exitMenu) {menu = 0; displayPrint(); exitMenu = false;} 
      if (menu > 12) {menu = 0; displayPrint();} backLight = 30;}
        
  wdt_reset();
  End
}









void parametri()                //изменение параметров  в зависимости от дня инкубации, сюда можно внести свои данные режима инкубации
{
  switch(avtomatik){
   case 1:   //для кур
      if (day < 2) {t = 380; powerTurn = true;  r_ventilator = false; H = 60;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 14) {t = 377; H = 60; r_ventilator = true; EEPROM.write(7, 18); vrema_raboti_ventilatora = 18; powerTurn = true; }
      if (day > 17) {t = 370; H = 80; powerTurn = false; if (hTimer == 0) turnStartPosition = true; r_ventilator = true;}
    break;
   case 2:   //для перепелов
      if (day < 2) {t = 380; powerTurn = true;  r_ventilator = false; H = 60;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 7) {t = 377; H = 60; r_ventilator = true; EEPROM.write(7, 10); vrema_raboti_ventilatora = 10; powerTurn = true;}
      if (day > 13) {t = 377; H = 60; powerTurn = true; r_ventilator = true;}
      if (day > 14) {t = 370; H = 80; powerTurn = false; if (hTimer == 0) turnStartPosition = true; r_ventilator = true;}
    break;
      case 3: //утка
         if (day < 2) {t = 385; powerTurn = true;  r_ventilator = false; H = 70;} 
         else {t = 380; powerTurn = true; r_ventilator = false; H = 70;}
      if (day > 7) {t = 378; H = 60; r_ventilator = false; EEPROM.write(7, 15); vrema_raboti_ventilatora = 15; powerTurn = true;}
      if (day > 14) {t = 378; H = 60; powerTurn = true; r_ventilator = true;}
      if (day > 25) {t = 375; H = 90; powerTurn = false; r_ventilator = false;}
    break;
    case 4: //гуси
      if (day < 6) {t = 380; powerTurn = true;  r_ventilator = false; H = 70;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 13) {t = 378; H = 70; r_ventilator = true; EEPROM.write(7, 25); vrema_raboti_ventilatora = 25; powerTurn = true;}
      if (day > 26) {t = 375; H = 85; powerTurn = true; r_ventilator = true;}
      if (day > 28) {t = 375; H = 85; powerTurn = false; r_ventilator = false;}
    break;
  }
}

void nagrev()                              //работа нагревателя, 200 - минимальная мощность, 0 - максимальная
{
    if (Temperature > t + 5) {rn += 4; Power = EEPROM.read(13);}

  if (Temperature < t)      n = 1;
  if (Temperature < t - 10) n = 2;
  if (Temperature < t - 40) n = 5;
  if (Temperature > t)      n = 4;
  if (Power == 255)         n = 3;

  switch (n) {
    case 4: if (rn > 14) {rn = 0; if (Temperature >= b) Power += 1;
              else Power -= 1; if (Temperature > t + 3) Power += 6;}
            else rn++;
      break;

    case 1:
      if (rv > 15) {rv = 0; if (Temperature <= b) Power -= 1;
                            else Power += 1;}
      else rv++;
      break;

    case 2:
      if (Temperature > b)
      {
        b = Temperature - b;
        switch (b)
        { case 1: Power += 4; break;
          case 2: Power += 5; break;
          case 3: Power += 8; break;
          case 4: Power += 8; break;
          case 5: Power += 20; break;
          default: Power += 22;
        }
      }
      else {
        Power -= 4;
        if (Temperature < (t - 20)) Power -= 30;
      }
      break;

    case 5:
      if (Temperature <= (b + 30)) Power -= 20; if (Power < 60) Power = 60;
      break;

    case 3:
      if (Temperature < t) Power = 145;
      break;
  }

  b = Temperature;                    //промежуточное значение температуры
  if (Temperature > t + 7) Power += 20; 
  Power = constrain(Power, 60, 200); //ограничение мощности 50%
  if ((Power == 200) && (Temperature > t + 3)) Power = 255; //выключение нагревателя 
  if (!menu) {lcd.setCursor(0, 1); lcd.print("Pow"); i = map(Power, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");}//мощность на нагрев
}


void displayPrint(){
  lcd.setCursor(0, 0); if (hTimer < 10) lcd.print("0"); lcd.print(hTimer); lcd.setCursor(3, 0); if (mTimer < 10) lcd.print("0"); lcd.print(mTimer);
  lcd.setCursor(6, 0); lcd.print("Day"); lcd.print(dTimer);
  lcd.setCursor(0, 1); lcd.print("Pow"); i = map(Power, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");//мощность на нагрев
  lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1); lcd.print("\1");
  lcd.setCursor(12, 0); lcd.print("H"); lcd.print(h, 0); lcd.print("%");
  lcd.setCursor(7, 1); if (right) lcd.print("R"); if (left) lcd.print("L"); lcd.print(accountTurn);
}

Новых разработок на данную тему не планирую. Возможна техническая поддержка, если возникаю вопросы у вас. Если вопрос: как подключить транзистор?, то ознакомтесь с прекрасной литературой: "1000 и одна микроконтроллерная схема. Рюмик С.М. в двух выпусках". В ней исчерпывающая информация. Если вам необходим индивидуальный проект и индивидуальная прошивка (имеется ввиду не только инкубатор), пишите мне, договоримся.

Проект довольно сложный, но если вы его сделаете, то приобретете бесценный опыт. Удачи во всех начинаниях, надеюсь был полезен!

 

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

Владимир, огромное спасибо за проект!!!

Будьте добры ответьте на несколько вопросов :)

Есть ли схема второго варианта?

можно ссылку на эту библиотеку?

#include <avr/wdt.h> //сторожевой пёс

Спасибо.

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

силовая часть схемы взята из материала http://cyber-place.ru/showthread.php?t=525

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

Emeljanowich пишет:

силовая часть схемы взята из материала http://cyber-place.ru/showthread.php?t=525

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

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Это же лютый писец из клубка проводов. Не страшно таким инкубировать? Привод поворота - бомба! 

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

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

НиколаМастер
Offline
Зарегистрирован: 06.10.2017
ВНИМАНИЕ: Категория '' в библиотеке UIPEthernet не является действительной. Установка на 'Uncategorized'
C:\Nik\Arduina\Inrubator\Vlad\1\sketch_aug06a\sketch_aug06a.ino: In function 'void setup()':

sketch_aug06a:71: error: no matching function for call to 'LiquidCrystal_I2C::begin()'

   lcd.begin(); lcd.clear();

             ^

C:\Nik\Arduina\Inrubator\Vlad\1\sketch_aug06a\sketch_aug06a.ino:71:13: note: candidate is:

In file included from C:\Nik\Arduina\Inrubator\Vlad\1\sketch_aug06a\sketch_aug06a.ino:30:0:

C:\Program Files (x86)\Arduino\libraries\LiquidCrystal_I2C/LiquidCrystal_I2C.h:58:8: note: void LiquidCrystal_I2C::begin(uint8_t, uint8_t, uint8_t)

   void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS );

        ^

C:\Program Files (x86)\Arduino\libraries\LiquidCrystal_I2C/LiquidCrystal_I2C.h:58:8: note:   candidate expects 3 arguments, 0 provided

sketch_aug06a:87: error: 'parametri' was not declared in this scope

   if (avtomatik) parametri();    

                            ^

sketch_aug06a:97: error: 'displayPrint' was not declared in this scope

   displayPrint();      

                ^

C:\Nik\Arduina\Inrubator\Vlad\1\sketch_aug06a\sketch_aug06a.ino: In function 'void loop()':

sketch_aug06a:135: error: 'displayPrint' was not declared in this scope

       EEPROM.write(3, day); EEPROM.write(2, hTimer); menu = 0; lcd.clear(); displayPrint();}

                                                                                          ^

sketch_aug06a:149: error: 'parametri' was not declared in this scope

       if (avtomatik) parametri();

                                ^

sketch_aug06a:178: error: 'displayPrint' was not declared in this scope

     case 9: if (avtomatik) {menu = 0; displayPrint(); backLight = 240;}

                                                    ^

sketch_aug06a:211: error: 'displayPrint' was not declared in this scope

   if ((Temperature > t - 5) && (Temperature < t + 5) && (h < H - 3)) {digitalWrite(ventilatorH, HIGH);  displayPrint();}  //вентилятор для поддержания влажности

                                                                                                                      ^

sketch_aug06a:212: error: 'displayPrint' was not declared in this scope

   else {digitalWrite(ventilatorH, LOW); displayPrint();}  

                                                      ^

sketch_aug06a:214: error: 'displayPrint' was not declared in this scope

   if (ventilation) {displayPrint(); vrema_raboti_ventilatora--; PORTD |= (1 << 4); if (!vrema_raboti_ventilatora) {PORTD &= ~(1 << 4); ventilation = false; vrema_raboti_ventilatora = EEPROM.read(7);  displayPrint();  //включение/выключение работа вентилятора проветривания

                                  ^

sketch_aug06a:222: error: 'displayPrint' was not declared in this scope

       if (backLight) {backLight--; lcd.backlight(); if (!backLight) {if (!PowerLigt) lcd.noBacklight(); menu = 0; lcd.clear(); displayPrint();}}

                                                                                                                                             ^

sketch_aug06a:244: error: 'parametri' was not declared in this scope

      EEPROM.write(3, dTimer); day = dTimer; parametri(); }

                                                       ^

sketch_aug06a:252: error: 'errorMelody' was not declared in this scope

     if ((!errorHour) && (Temperature < t - 20)) {error = true; menu = 15; errorMelody();}          //включение мелодии ошибки

                                                                                       ^

sketch_aug06a:256: error: 'errorMelody' was not declared in this scope

     if (errorTime > TimeError) {errorMelody(); error = true; menu = 15; errorTime = 20;}

                                             ^

sketch_aug06a:260: error: 'nagrev' was not declared in this scope

     if (Temperature != t) nagrev(); m = 0;} //работа нагревателя, выполнется 3 разa в 1 минуту(ы), чтобы не мешать инерционности процессу

                                  ^

sketch_aug06a:263: error: 'displayPrint' was not declared in this scope

   if (!digitalRead(buttonMenu)) {delay(250); menu++; lcd.clear(); if (exitMenu) {menu = 0; displayPrint(); exitMenu = false;} 

                                                                                                         ^

sketch_aug06a:264: error: 'displayPrint' was not declared in this scope

       if (menu > 11) {noTone(8); menu = 0; displayPrint(); backLight = 240;}}

                                                         ^

sketch_aug06a:283: error: a function-definition is not allowed here before '{' token

 {

 ^

sketch_aug06a:418: error: expected '}' at end of input

 }

 ^

Несколько библиотек найдено для "EEPROM.h"
 Используется: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM
Не используется: C:\Program Files (x86)\Arduino\libraries\EEPROM
Несколько библиотек найдено для "LiquidCrystal_I2C.h"
 Используется: C:\Program Files (x86)\Arduino\libraries\LiquidCrystal_I2C
Не используется: C:\Program Files (x86)\Arduino\libraries\Arduino-LiquidCrystal-I2C-library-master
Несколько библиотек найдено для "OneWire.h"
 Используется: C:\Program Files (x86)\Arduino\libraries\OneWire
Не используется: C:\Program Files (x86)\Arduino\libraries\Arduino-Temperature-Control-Library-master
Несколько библиотек найдено для "DallasTemperature.h"
 Используется: C:\Program Files (x86)\Arduino\libraries\DallasTemperature
Не используется: C:\Program Files (x86)\Arduino\libraries\Arduino-Temperature-Control-Library-master
exit status 1
no matching function for call to 'LiquidCrystal_I2C::begin()'

Invalid version found: 1.04
Invalid version found: 1.04
Invalid version found: 1.04
Invalid version found: 1.04
Invalid version found: 1.04
Invalid version found: 1.04
Invalid version found: 1.04
Invalid version found: 1.04

 

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

Не компилируется, помогите пожалуйста

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Колдуны есть свободные? Узнайте, нсколько у него древняя IDE?

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

1,8,5 прога

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

Скажите по ошибкам :)

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

ИДЕ работает, другие скетчи грузятся, что то не так с библиотеками или ссылками в тексте скетча.

Посмотрите пожалуйста

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Ну да, и у меня много ошибок. И не только в либах. Это к автору вопросы. 

b707
Онлайн
Зарегистрирован: 26.05.2017

Umka пишет:

Ну да, и у меня много ошибок. И не только в либах. Это к автору вопросы. 

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

Добавка - посмотрел код. Все так и есть. картинку с проводами видели? - код такой же...

Емельянович - даже лично для себя стыдно так писать. А уж на общее обозрение выкладывать...

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

Блин, а я так надеялся на этот проект... может скинете работающий проект инкубатора?

Хорошо бы такой же экран 1602,  переворот любой еще не делал.

Спасибо

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

У нас вот так если что.

https://photos.app.goo.gl/DMthMFBLDmo5LJFaA

https://photos.app.goo.gl/QAqzatT5oMnHsBXW7

https://photos.app.goo.gl/SGPM5Fyqmz2psYAJA

Код не раздаю на именно этот, но есть тут другой открытый проект. И не только мой. Пошукайте :)

b707
Онлайн
Зарегистрирован: 26.05.2017

Umka пишет:

У нас вот так если что.

Красота!

Неужели самоделка? - даже не верится...

Плату в китае заказывали?

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Да, самоделка. В Китае плату заказывали. Это самый простой вариант. Есть и полохмаче https://photos.app.goo.gl/SQtwdn7xQ41RMzdC7

https://photos.app.goo.gl/x39ZCPZAGr1tVbD4A

b707
Онлайн
Зарегистрирован: 26.05.2017

Umka пишет:

 Есть и полохмаче https://photos.app.goo.gl/SQtwdn7xQ41RMzdC7

 

да ладно, не прибедняйтесь :) отлично выглядит, у китайцнв на Али фабричные платы хуже качеством :)

Молодцы.

 

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

Попробуйте этот, вторая версия прошивки

#include <avr/wdt.h> //сторожевой пёс
#include <CyberLib.h> //Библиотека от Cyber-Place.ru
#include <OneWire.h>
#include <DallasTemperature.h>
#include "DHT.h"
#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>  // Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define DHTPIN 12 // номер пина, к которому подсоединен датчик // Раскомментируйте в соответствии с используемым датчиком// Инициируем датчик
DHT dht(DHTPIN, DHT22);//DHT dht(DHTPIN, DHT11);// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 11 // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

#define dirpin 9       //пины для подключения драйвера ШД перемещения принтера влево вправо
#define steppin 10
#define enable 8
#define kuler 5
#define ventilator 4
#define nasos 6
#define buttonPlus 15
#define buttonMenu 16
#define buttonMinus 17

volatile uint8_t tic, Power = 200, msTimer, sTimer;
boolean r_povorota, r_povorot, r_ventilator, r_nasosa, proverka, turn;
long prevmicros;
byte Power_promeg, ventilar, H, dlit_raboti_nasosa = 5, vrema_raboti_ventilatora;  //0-255 vrema_roboti_ventilatora

byte gradus[8] = {B01000, B10100, B01000, B00111, B01000, B01000, B00111, B00000,};

boolean dhT, counter, r_ventilatora, step, powerTurn, exitMenu, left, right, turnStartPosition, ventilation;
byte hi, sec, mi, mTimer, hTimer, dTimer = 1, day = 1;
byte backLight, menu;
byte avtomatik, timeTurn, setPower, timeTurnExperement, periodTurn, accountTurn;
byte n, rn, rv, m, i;
unsigned int t, Temperature, b;          //0-65535
float e, h, tt, kof, tSetprintDisplay;
//signed char kof; //-128 - 127


void parametri()                //изменение параметров  в зависимости от дня инкубации, сюда можно внести свои данные режима инкубации
{
  switch(avtomatik){
   case 1:   //для кур
      if (day < 2) {t = 380; powerTurn = true;  r_ventilator = false; H = 60;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 14) {t = 377; H = 60; r_ventilator = true; EEPROM.write(7, 18); vrema_raboti_ventilatora = 18; powerTurn = true; }
      if (day > 17) {t = 370; H = 80; powerTurn = false; if (hTimer == 0) turnStartPosition = true; r_ventilator = true;}
    break;
   case 2:   //для перепелов
      if (day < 2) {t = 380; powerTurn = true;  r_ventilator = false; H = 60;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 7) {t = 377; H = 60; r_ventilator = true; EEPROM.write(7, 10); vrema_raboti_ventilatora = 10; powerTurn = true;}
      if (day > 13) {t = 377; H = 60; powerTurn = true; r_ventilator = true;}
      if (day > 14) {t = 370; H = 80; powerTurn = false; if (hTimer == 0) turnStartPosition = true; r_ventilator = true;}
    break;
      case 3: //утка
         if (day < 2) {t = 385; powerTurn = true;  r_ventilator = false; H = 70;} 
         else {t = 380; powerTurn = true; r_ventilator = false; H = 70;}
      if (day > 7) {t = 378; H = 60; r_ventilator = false; EEPROM.write(7, 15); vrema_raboti_ventilatora = 15; powerTurn = true;}
      if (day > 14) {t = 378; H = 60; powerTurn = true; r_ventilator = true;}
      if (day > 25) {t = 375; H = 90; powerTurn = false; r_ventilator = false;}
    break;
    case 4: //гуси
      if (day < 6) {t = 380; powerTurn = true;  r_ventilator = false; H = 70;} 
         else {t = 378; powerTurn = true; r_ventilator = false; H = 60;}
      if (day > 13) {t = 378; H = 70; r_ventilator = true; EEPROM.write(7, 25); vrema_raboti_ventilatora = 25; powerTurn = true;}
      if (day > 26) {t = 375; H = 85; powerTurn = true; r_ventilator = true;}
      if (day > 28) {t = 375; H = 85; powerTurn = false; r_ventilator = false;}
    break;
  }
}

void nagrev()                              //работа нагревателя, 200 - минимальная мощность, 0 - максимальная
{
    if (Temperature > t + 5) {rn += 4; Power = EEPROM.read(13);}

  if (Temperature < t)      n = 1;
  if (Temperature < t - 10) n = 2;
  if (Temperature < t - 40) n = 5;
  if (Temperature > t)      n = 4;
  if (Power == 255)         n = 3;

  switch (n) {
    case 4: if (rn > 14) {rn = 0; if (Temperature >= b) Power += 1;
              else Power -= 1; if (Temperature > t + 3) Power += 6;}
            else rn++;
      break;

    case 1:
      if (rv > 15) {rv = 0; if (Temperature <= b) Power -= 1;
                            else Power += 1;}
      else rv++;
      break;

    case 2:
      if (Temperature > b)
      {
        b = Temperature - b;
        switch (b)
        { case 1: Power += 4; break;
          case 2: Power += 5; break;
          case 3: Power += 8; break;
          case 4: Power += 8; break;
          case 5: Power += 20; break;
          default: Power += 22;
        }
      }
      else {
        Power -= 4;
        if (Temperature < (t - 20)) Power -= 30;
      }
      break;

    case 5:
      if (Temperature <= (b + 30)) Power -= 20; if (Power < 60) Power = 60;
      break;

    case 3:
      if (Temperature < t) Power = 145;
      break;
  }

  b = Temperature;                    //промежуточное значение температуры
  if (Temperature > t + 7) Power += 20; 
  Power = constrain(Power, 60, 200); //ограничение мощности 50%
  if ((Power == 200) && (Temperature > t + 3)) Power = 255; //выключение нагревателя 
  if (!menu) {lcd.setCursor(0, 1); lcd.print("Pow"); i = map(Power, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");}//мощность на нагрев
}


void displayPrint(){
  lcd.setCursor(0, 0); if (hTimer < 10) lcd.print("0"); lcd.print(hTimer); lcd.setCursor(3, 0); if (mTimer < 10) lcd.print("0"); lcd.print(mTimer);
  lcd.setCursor(6, 0); lcd.print("Day"); lcd.print(dTimer);
  lcd.setCursor(0, 1); lcd.print("Pow"); i = map(Power, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");//мощность на нагрев
  lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1); lcd.print("\1");
  lcd.setCursor(12, 0); lcd.print("H"); lcd.print(h, 0); lcd.print("%");
  lcd.setCursor(7, 1); if (right) lcd.print("R"); if (left) lcd.print("L"); lcd.print(accountTurn);
}



void setup() {
  wdt_disable();
  D13_Out;                                      //Настраиваем порты на выход
  D13_Low;                                      //установить на выходах низкий уровень сигнала
  D2_In;                                        //настраиваем порт на вход для отслеживания прохождения сигнала через ноль
  //CHANGE – прерывание вызывается при любом изменении значения на входе;
  //RISING – вызов прерывания при изменении уровня напряжения с низкого (Low) на высокий(HIGH)
  //FALLING – вызов прерывания при изменении уровня напряжения с высокого (HIGH) на низкий (Low)
  attachInterrupt(0, detect_up, LOW);         // настроить срабатывание прерывания interrupt0 на pin 2 на низкий уровень
  StartTimer1(halfcycle, 40);                 //время для одного разряда ШИМ
  StopTimer1();                               //остановить таймер

  sensors.begin();
  dht.begin();

  lcd.begin(); lcd.clear();
  lcd.createChar(1, gradus);  //Регистрируем собственные символы с кодами 1 и ...
  DDRD |= (1 << 5)|(1 << 4)|(1 << 6); //кулер, вентилятор, насос
  PORTD |= (1 << 5);        
  PORTD &= ~(1 << 4)|(1 << 6);
  //pinMode(ventilator, OUTPUT); digitalWrite(ventilator, LOW);  pinMode(nasos, OUTPUT); digitalWrite(nasos, LOW); (pinMode(kuler, OUTPUT); digitalWrite(kuler, HIGH);
  DDRB |= (1 << 0)|(1 << 1)|(1 << 2);
  PORTB &= ~(1 << 1)|(1 << 2); 
  PORTB |= (1 << 0); 
  //pinMode(dirpin, OUTPUT);  pinMode(steppin, OUTPUT);  pinMode(enable, OUTPUT); digitalWrite(dirpin, LOW); digitalWrite(enable, HIGH);
  DDRC &= ~(1 << 1)|(1 << 2)|(1 << 3);
  PORTC |= (1 << 1)|(1 << 2)|(1 << 3);
  //pinMode(buttonPlus, INPUT_PULLUP); pinMode(buttonMinus, INPUT_PULLUP); pinMode(buttonMenu, INPUT_PULLUP);
  
  hTimer = EEPROM.read(20);  dTimer = EEPROM.read(3); day = dTimer;
  timeTurn = EEPROM.read(4); powerTurn = EEPROM.read(11); 
  kof = EEPROM.read(5); 
  dlit_raboti_nasosa = EEPROM.read(6); vrema_raboti_ventilatora = EEPROM.read(7);
  avtomatik = EEPROM.read(8); t = EEPROM.read(9) + 370; H = EEPROM.read(10); // r_ventilator = EEPROM.read(12); 
  setPower = EEPROM.read(13); 
  left = EEPROM.read(16); right = EEPROM.read(17);

  if (avtomatik) parametri();    
  setTimer();
  lcd.backlight(); //noBacklight();
  backLight = 60;
  wdt_enable (WDTO_4S);

  sensors.requestTemperatures();  e = sensors.getTempCByIndex(0);   // Считываем температуру
  h = dht.readHumidity(); tt = dht.readTemperature(); Temperature = (10 * tt) + kof; tt += kof * 0.1;
  displayPrint();      
}

void setTimer()
{
  cli();
  TCCR2A |= (1 << WGM21);  //CTC to OCR2A
  TCCR2A &= ~(1 << WGM20);
  TCCR2B &= ~(1 << WGM22);
  TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20);  //1024
  TIMSK2 &= ~(1 << OCIE2A);
  OCR2A = 50;
  sei();
}

ISR (TIMER2_COMPA_vect)
{
  PORTB = PORTB ^ (1 << 2); //шаги для двигателя поворота (шаговый двинатель типа 28BYJ-49) - времени на шаг 0,0032 сек
}

//********************обработчики прерываний*******************************
void halfcycle()  //прерывания таймера
{
  tic++;  //счетчик
  if (Power < tic ) D13_High; //управляем выходом
}

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

void detect_down()  // обработка внешнего прерывания. Сработает по заднему фронту
{
  StopTimer1(); //остановить таймер
  D13_Low; //логический ноль на выходы
  tic = 0;       //обнулить счетчик
  attachInterrupt(0, detect_up, LOW); //перепрограммировать прерывание на другой обработчик
  msTimer++; if (msTimer > 99) {sTimer++; msTimer = 0;} //счет времени
}
//*************************************************************************


void loop() {Start

if (menu){
switch (menu){
   case 1: lcd.setCursor(4, 0); lcd.print("- turn -"); lcd.setCursor(6, 1); lcd.print(timeTurnExperement); lcd.print(" sec");
      if (!digitalRead(buttonPlus) || !digitalRead(buttonMinus)) { backLight = 20;
      EEPROM.write(16, 0); EEPROM.write(17, 0); left = false; right = false;
      PORTB &= ~(1 << 0); TIMSK2 |= (1 << OCIE2A); 
      if (!digitalRead(buttonMinus)) PORTB &= ~(1 << 1);  
      if (!digitalRead(buttonPlus)) PORTB |= (1 << 1);
      exitMenu = true;
    } 
    else {PORTB |= (1 << 0); TIMSK2 &= ~(1 << OCIE2A); timeTurnExperement = 0;}
      break;

   case 2:
      lcd.setCursor(0, 1); lcd.print("Temp calibration");
      lcd.setCursor(2, 0); lcd.print(kof/10); lcd.print("\1"); lcd.print(" daL "); lcd.print(e, 1);
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); kof++; EEPROM.write(5, kof); if (kof > 15) {kof = -15; lcd.clear();} exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); kof--; EEPROM.write(5, kof); if (kof < -15) {kof = 15; lcd.clear();} exitMenu = true;}
     break;

   case 3: 
      lcd.setCursor(2, 1); lcd.print("Turning time"); timeTurn = EEPROM.read(4);
      lcd.setCursor(3, 0); lcd.print(timeTurn); lcd.print(" sec "); lcd.print("l"); lcd.print(left); lcd.print(" r"); lcd.print(right);  
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); timeTurn--; EEPROM.write(4, timeTurn); exitMenu = true;} 
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); timeTurn++; EEPROM.write(4, timeTurn); exitMenu = true;} 
     break;   

   case 4: lcd.setCursor(2, 0); lcd.print("reset time?");
      if (!digitalRead(buttonMinus) || !digitalRead(buttonPlus)) {backLight = 20; delay(200); hTimer = 0; dTimer = 1; day = 1; mTimer = 0;
      EEPROM.write(3, day); EEPROM.write(2, hTimer); menu = 0; lcd.clear(); displayPrint();}
     break;

   case 5: lcd.setCursor(1, 0); lcd.print("setPowerDefaul");
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); setPower--; EEPROM.write(13, setPower); lcd.clear(); exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); setPower++; EEPROM.write(13, setPower); lcd.clear(); exitMenu = true;}
      lcd.setCursor(5, 1);  i = map(setPower, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");  
     break;    

   case 6: lcd.setCursor(3, 0); lcd.print("avtom setap");
      if (!digitalRead(buttonPlus)) {backLight = 20; delay(200); avtomatik++; lcd.clear(); EEPROM.write(8, avtomatik); if (avtomatik > 2) avtomatik = 2; exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 20; delay(200); avtomatik--; lcd.clear(); EEPROM.write(8, avtomatik); if (avtomatik > 2) avtomatik = 0; exitMenu = true;}  
      lcd.setCursor(2, 1); 
      if (!avtomatik) lcd.print("not avtom"); if (avtomatik == 1) lcd.print("kuri"); if (avtomatik == 2) lcd.print("perepelki"); 
      if (avtomatik) parametri();
     break;

    case 7: lcd.setCursor(3, 0); lcd.print("DHT "); lcd.setCursor(10, 0); lcd.print(" 18B20");
             lcd.setCursor(8, 0); if (dhT) lcd.print("<"); else lcd.print(">");
      if (!digitalRead(buttonPlus)) {delay(300); dhT = !dhT; exitMenu = true; EEPROM.write(16, dhT);}
      if (!digitalRead(buttonMinus)) {delay(300); dhT = !dhT; exitMenu = true; EEPROM.write(16, dhT);}
    break;  
      
    case 8: lcd.setCursor(3, 0); lcd.print("test turn"); lcd.setCursor(0, 1); lcd.print(timeTurn); lcd.print(" l"); lcd.print(left); lcd.print(" r"); lcd.print(right);  
      if (!digitalRead(buttonPlus)) {backLight = 40; turn = true; exitMenu = true; timeTurn = EEPROM.read(4);}
      if (!digitalRead(buttonMinus)) {backLight = 40; turn = true; exitMenu = true; timeTurn = EEPROM.read(4);}
     break;  

 //   case 8: lcd.setCursor(0, 0); lcd.print("Stop Pow - Ventil"); lcd.setCursor(2, 1); lcd.print(vrema_raboti_ventilatora); lcd.print(":"); lcd.print(59 - sTimer);
 //     if (!digitalRead(buttonPlus)) {delay(200); backLight = 200; m = 0; ventilation = true;}
 //     if (!digitalRead(buttonMinus)) {delay(200); backLight = 200; m = 0; ventilation = true;}
 //    break; 
     
    case 9: lcd.setCursor(3, 0); lcd.print("Set Power"); lcd.setCursor(2, 1); i = map(Power, 60, 200, 99, 1); if (i < 10) lcd.print(" "); lcd.print(i); lcd.print("%");
      if (!digitalRead(buttonPlus)) {backLight = 40; delay(200); Power--; if (Power < 60) Power = 60; exitMenu = true;}
      if (!digitalRead(buttonMinus)) {backLight = 40; delay(200); Power++; if (Power > 200) Power = 200; exitMenu = true;}
     break;

    case 10: if (avtomatik) {menu = 0; displayPrint(); backLight = 240;}
            else {lcd.setCursor(1, 0); lcd.print("Set t = "); lcd.setCursor(10, 0); tSetprintDisplay = 370+t; lcd.print(tSetprintDisplay/10); lcd.print("\1");  
                if (!digitalRead(buttonPlus)) {delay(200); t++; if (t > 15) t = 15; EEPROM.write(9, t); exitMenu = true;}
                if (!digitalRead(buttonMinus)) {delay(200); t--; if (t > 8) t = 0; EEPROM.write(9, t); exitMenu = true;}
      }
     break;   

    case 11: lcd.setCursor(1, 0); lcd.print("Set H = "); lcd.setCursor(10, 0); lcd.print("37,"); lcd.print(t); lcd.print("\1");
      if (!digitalRead(buttonPlus)) {delay(200); H += 5; if (H > 90) H = 90; EEPROM.write(10, H); exitMenu = true;} 
      if (!digitalRead(buttonMinus)) {delay(200); H -= 5; if (H < 30) H = 30; EEPROM.write(10, H); exitMenu = true;}
     break;

    case 12: lcd.setCursor(1, 0); lcd.print("Set ventilation"); lcd.setCursor(1, 1); lcd.print(vrema_raboti_ventilatora); lcd.print(" min");
      if (!digitalRead(buttonPlus)) {backLight = 20; exitMenu = true; delay(200); vrema_raboti_ventilatora++; if (vrema_raboti_ventilatora > 30) vrema_raboti_ventilatora = 30; EEPROM.write(7, vrema_raboti_ventilatora);}
      if (!digitalRead(buttonMinus)) {backLight = 20; exitMenu = true; delay(200); vrema_raboti_ventilatora--; if (vrema_raboti_ventilatora > 30) vrema_raboti_ventilatora = 0; EEPROM.write(7, vrema_raboti_ventilatora);}
     break;




        
  }
}

 if (sTimer > 59) {mTimer++; if (mTimer > 59) {hTimer++; if (hTimer > 23) {dTimer++; hTimer = 0;} mTimer = 0;} sTimer = 0;}

 if (mi != mTimer){
        h = dht.readHumidity();     //влажность с DHT22
  if (!menu) {lcd.setCursor(3, 0); if (mTimer < 10) lcd.print("0"); lcd.print(mTimer);
  lcd.setCursor(12, 0); lcd.print("H"); lcd.print(h, 0); lcd.print("%"); }      //влажность
  mi = mTimer;

  if (ventilation) {vrema_raboti_ventilatora--; PORTD |= (1 << 4); if (!vrema_raboti_ventilatora) {PORTD &= ~(1 << 4); ventilation = false; vrema_raboti_ventilatora = EEPROM.read(7); Power = EEPROM.read(13);}}        //включение/выключение работа вентилятора проветривания
 }


 if (sec != sTimer){
  counter = !counter; if (!menu) {if (counter) {lcd.setCursor(2, 0); lcd.print(":");} else {lcd.setCursor(2, 0); lcd.print(" ");}}
  
  if (backLight) {backLight--; lcd.backlight(); if (!backLight) {lcd.noBacklight(); menu = 0; lcd.clear(); displayPrint();}}
  
  if (menu == 1) timeTurnExperement++;
  
  if (turn) {PORTB &= ~(1 << 0); TIMSK2 |= (1 << OCIE2A); 
      if (left) PORTB &= ~(1 << 1);
      else if (right) PORTB |= (1 << 1);
      else {timeTurn /= 2; PORTB &= ~(1 << 1); left = true; EEPROM.write(16, left);} 
      timeTurn--; 
      if (turnStartPosition) {timeTurn /= 2; turnStartPosition = false;}
      if (!timeTurn) {PORTB |= (1 << 0); TIMSK2 &= ~(1 << OCIE2A); left = !left; right = !right; EEPROM.write(16, left); EEPROM.write(17, right); turn = false; accountTurn++; if (accountTurn > 99) accountTurn = 0;
          if (!menu) {lcd.setCursor(7, 1); if (right) lcd.print("R"); if (left) lcd.print("L"); lcd.print(accountTurn);} timeTurn = EEPROM.read(4);} }  //поворот на установленный промежуток времени
  
   if (!ventilation) m++; //если не работает проветривание, то считаем 20 сек для работы нагревателя
   else {if (dhT) {tt = dht.readTemperature();  Temperature = (10 * tt) + kof;}                                 //температура с DHT22  
          else {sensors.requestTemperatures(); tt = sensors.getTempCByIndex(0); Temperature = (10 * tt) + kof;}  //температура с 18B20
          if (!menu) {lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1);}} 
            
   sec = sTimer;
     }



  if (hi != hTimer){ if (!menu) {lcd.setCursor(0, 0); if (hTimer < 10) lcd.print("0"); lcd.print(hTimer);} EEPROM.write(20, hTimer); //сохранение текущего часа
      
      if (powerTurn && timeTurn) {periodTurn++; if (periodTurn > 1) {periodTurn = 0; turn = true; timeTurn = EEPROM.read(4);}}
      if (turnStartPosition && hTimer == 1) turn = true; 

      sensors.requestTemperatures(); // температура с 18b20
      e = sensors.getTempCByIndex(0);

      if ((r_ventilator || (!avtomatik && vrema_raboti_ventilatora)) && (hTimer == 6 || hTimer == 18)) {ventilation = true; Power = 200;}//включение вентиляции
      
      hi = hTimer;
     }

  if (day != dTimer) {if (!menu) {lcd.setCursor(6, 0); lcd.print("Day"); lcd.print(dTimer);} EEPROM.write(3, day); parametri();
      day = dTimer;
     }

  if (m > 20) {
     if (dhT) {tt = dht.readTemperature();  Temperature = (10 * tt) + kof;}                                 //температура с DHT22  
     else {sensors.requestTemperatures(); tt = sensors.getTempCByIndex(0); Temperature = (10 * tt) + kof;}  //температура с 18B20
    
     //tt = dht.readTemperature();  //температура с DHT22  // sensors.requestTemperatures(); //  e = sensors.getTempCByIndex(0);
     //Temperature = (10 * tt) + kof;
     if (!menu) {lcd.setCursor(11, 1); lcd.print(Temperature * 0.1, 1);}   
     if (Temperature != t) nagrev(); m = 0;} //работа нагревателя, выполнется 3 разa в 1 минуту(ы), чтобы не мешать инерционности процессу
  
   
  if (!digitalRead(buttonMenu)) {delay(250); menu++; lcd.clear(); if (exitMenu) {menu = 0; displayPrint(); exitMenu = false;} 
      if (menu > 12) {menu = 0; displayPrint();} backLight = 30;}
        
  wdt_reset();
  End
}


b707, буду очень признателен, если подскажете литературу с правилами оформления кода. Много других интересных дел, нет много времени, что бы уделить его ЭСТЕТИКЕ инкубатора. По пайке у меня 5) все паянные соединения зафиксированы термоусадкой и термоклеем.

 

b707
Онлайн
Зарегистрирован: 26.05.2017

Emeljanowich пишет:

b707, буду очень признателен, если подскажете литературу с правилами оформления кода. Много других интересных дел, нет много времени, что бы уделить его ЭСТЕТИКЕ инкубатора

Emeljanowich - дело не в эстетике, дело в неких простых правилах, повышающих понятность кода.

а) Выбирать мнемонические имена переменных. вместо "слепых" n или  b

б) Избегать обращения к EEPROM или пинам по цифровым адресам - использовать константы define

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

г) не писать несколько операторв в одну строку

д) не обьявлять много переменных через запятую

У вас скетч выглядит так, как будто это писано не человеком, а автоматическим генератором кода типа FLProg. Такой код крайне сложно модифицировать. Я абсолютно уверен, что вы и сами в нем путаетесь. А уж другому человеку тут что-то поправить - почти непосильная задача. Может, конечно, вы этого и хотели - этакая уловка ради охраны своего копирайта - тогда поздравляю, получилось неплохо.

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

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

b707 спасибо!

По математике у меня было 10, но учитель всегда ругал, что у доски сразу ответ писал, а не последовательность решения.  Код писал не для всеобщего понимания, как мне удобно. Есть в среде ардуино фукция ctrl+T, тогда все операторы выстроятся в строки, если так будет понятнее. Для этого и предлагаю техническую помощь, но суть темы подразумевает копировать рабочий проект. Если вы обратили внимание, в первой версии прошивки для инкубатора используется миллис, но это время далеко не точное. Одно прерывание контролирует ноль синусоиды, второе включается при повороте - для ШД. Десятки переменных позволяют делать кучу настроек, и тогда программа получается немножечько сложной.

b707
Онлайн
Зарегистрирован: 26.05.2017

Emeljanowich пишет:

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

вы меня смешите :)

  А ваш таймер по прерыванию, думаете, не от того же кварца тактируется, что миллис? :) откуда ему точнее быть? - ровно та же ошибка и будет

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

Ну что ж, удачи.

Emeljanowich
Emeljanowich аватар
Offline
Зарегистрирован: 30.04.2015

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

Ну что ж, спасибо! :)

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

DHT в инкубаторе использовать решительно не советую. Я вывел больше 50 000 цыплят на самодельных контроллерах за 9 лет птицеводства. DHT это для дома или подвала. В инкубатор лучше емкостные брать - дольше проживут. 

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

Емкостные это что? Можно ссылку для чайников

 

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

А проект Грачика нормальный?

 

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Senserion например или клоны.

Alex547000
Offline
Зарегистрирован: 23.04.2020

Здравствуйте, собрал инкубатор по вашей схеме все работает за исключением одной проблемы. Постоянно выходит из строя силовой транзистор, он остается постоянно открытым. Уже сгорело 4 irf820 и один GT30j322. Подскажите в чем может быть проблема. Пробовал ставить резистор на 680Ом в цепь управления транзистором, после оптопары. Результата ноль.

НиколаМастер
Offline
Зарегистрирован: 06.10.2017

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

посмотрите мое творение :) https://youtu.be/9e8OgkIHHA4