Простой умный инкубатор.
- Войдите на сайт для отправки комментариев
Добрый день. С умным брудером получилось, пришло время заняться инкубатором.
Задача - сделать простое по материалам и стоимости, но надежное и точное устройство для маленьких и средних бытовых инкубаторов. Для замены примитивных аналоговых терморегуляторов в корпусах то мыльниц и розеток. Получилось не совсем дешево, но компактно и функционально.
Что мы имеем:
Термометр - народный DS18B20, с залитыми эпоксидным клеем контактами.
Влагомер и доп. термометр - SHT10.
Arduino ProMini в качестве мозга.
LCD 20х4 с I2C контроллером.
Пара твердотельных Omron-овских реле на плате.
Пищалка пьезо.
Двусторонняя плата 50х50мм.
Блок реле с опторазвязкой.
Что мы умеем:
ПИД регулятор с низкочастотным ШИМ в канале нагревателя.
Измерение и поддержани заданной влажности.
Управление поворотом лотков с периодом 1-12 часов или случайным образом.
Управление аварийным вытяжным вентелятором при перегреве.
Подачей звуковой сигнализации и включение аварийного реле при аварии.
В качестве бонуса вольтметр входного напряжения. Это для случая работы от резервного аккумулятора.
Возможность использовать нагревател, увлажнитель, вентиляторы как с питанием от сети 220в, так и от постоянки 12в.
Экранное меню где настраиваются все вышеперечисленные параметры и сохранение настроек в EEPROM.
Визуализация в графическом виде на компьютере температур для удобной настройки ПИД коэффициентов.
Устройство собрано и проверено. Заказал изготовление плат в Китае, на повестке дня тестирование в реальном инкубаторе. Естественно функционал будет добавляться. Часов нет.
Скетч.
#include <PID_v1.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM2.h>
#include <DallasTemperature.h>
#include <OneWire.h>
//#include <SimpleTimer.h>
#include <Sensirion.h>
int del = 80; // переменная ожидания между выборами меню
unsigned int interval = 100; // интервал сколько будет длиться цикл while, после чего перейдёт к следующему меню.(кол-во итераций)
#define heater_pin 13 // нагреватель
#define humidifer_pin 12 // увлажнитель
#define fan_pin 11 // вентилятор
#define alarm_pin 14 // пин аварии
#define beeper_pin 9 //пищалка по аварии
#define dataPin 5 //SHT10
#define clockPin 6 //SHT10
#define turn_pin 10 // управление поворотом
#define button_minus_pin 2 //пин кнопки "минус"
#define button_plus_pin 3 //пин кнопки "плюс"
#define button_enter_pin 4 //пин кнопки "enter"
#define DS18B20_Pin 7 //пин термометра
#define setSampleTime 1000 //время цикла ПИД
#define voltmeter_pin 1 //вход А1 через делитель (22к/10к) подключен к питанию модуля. Измеряет до 16В.
boolean button_minus;
boolean button_plus;
boolean button_enter;
boolean turnFlag = 0; // флаг поворота для случайного периода
float humidity; // Влажность
float temp1Ink; // Температура DS18B20
float temp2Ink; // Температура SHT10
float dewpoint; // Точка росы
unsigned int rawData;
unsigned long currentTime; // задаем переменные для тайминга поворота
unsigned long loopTime;
unsigned long serialTime; //this will help us know when to talk with processing
unsigned long now;
unsigned long trhMillis = 0; // период опроса датчиков
byte measActive = false;
byte measType = TEMP;
const unsigned long TRHSTEP = 3000UL; // Sensor query period
LiquidCrystal_I2C lcd(0x27, 20, 4); // инициализация библиотеки дисплея
//SHT1x sht1x(dataPin, clockPin);
OneWire oneWire(DS18B20_Pin);
DallasTemperature sensors(&oneWire);
Sensirion sht = Sensirion(dataPin, clockPin);
double Setpoint, Input, Output; //объявляем переменные для ПИД
PID myPID(&Input, &Output, &Setpoint, 10, 4, 3, DIRECT); //Инициализируем ПИД-библиотеку и коэффициенты
int WindowSize = setSampleTime; // ширина окна терморегулятора 1 секунда.
unsigned long windowStartTime;
//SimpleTimer timer;
/* EEPROM1 - tempInk
EEPROM5 (13) - set_humidity
EEPROM7 - alarmTemp
EEPROM9 - fanTemp
EEPROM11 - turnPeriod */
void setup() {
Serial.begin(9600);
lcd.init(); // Старт дисплея
lcd.backlight(); // Включаем подсветку дисплея
windowStartTime = millis();
byte stat;
byte error = 0;
delay(15);
myPID.SetOutputLimits(0, WindowSize); //задаем лимиты ширины ПИД-импульса от 0 до 1 секунды.
myPID.SetMode(AUTOMATIC); //включаем ПИД-регулирование
myPID.SetSampleTime(setSampleTime);
pinMode(8, OUTPUT); //пока не используемый 8 вывод. Переводим в 1 чтобы не включать реле.
digitalWrite(8, HIGH);
pinMode(heater_pin, OUTPUT);
pinMode(turn_pin, OUTPUT); // устанавливаем выводы
digitalWrite(turn_pin, HIGH);
pinMode(humidifer_pin, OUTPUT);
pinMode(fan_pin, OUTPUT);
digitalWrite(fan_pin, HIGH);
pinMode(alarm_pin, OUTPUT);
digitalWrite(alarm_pin, HIGH);
pinMode(button_minus_pin, INPUT_PULLUP); //подтягиваем входы кнопок к плюсу встроенными резисторами
pinMode(button_plus_pin, INPUT_PULLUP);
pinMode(button_enter_pin, INPUT_PULLUP);
sensors.begin();
sensors.setResolution(12); // установить разрешение (точность)
sensors.setWaitForConversion(false); // отключить ожидание
//timer.setInterval(3000, getSensors);
windowStartTime = millis();
currentTime = millis(); // считываем время, прошедшее с момента запуска программы
loopTime = currentTime;
//EEPROM_write(13, 61.0);
}
void loop() {
// Input = getTemp();
unsigned long now = millis();
//timer.run();
button_read();
if (!button_enter) {
delay(del);
lcd.clear();
menu();
}
if (!button_minus) {
delay(del);
lcd.clear();
//действие по нажатии кнопки "-"
}
if (!button_plus) {
delay(del);
lcd.clear();
//действие по нажатии кнопки "+"
}
//send-receive with processing if it's time
if (millis() > serialTime)
{
SerialReceive();
SerialSend();
serialTime += 500;
}
getSensors();
thermostat();
humidifer();
turn();
fan();
alarm();
//unsigned long now1 = millis();
//Serial.println(now1-now);
}
void button_read() {//функция проверки нажатия кнопки
button_minus = digitalRead(button_minus_pin); //запоминаем значение кнопки
button_plus = digitalRead(button_plus_pin); //запоминаем значение кнопки
button_enter = digitalRead(button_enter_pin); //запоминаем значение кнопки
if (!button_minus || !button_plus || !button_enter) beeper(50);
}
//меню
void menu() {
temp_setup();
hum_setup();
turn_setup();
alarm_setup();
vent_setup();
//data_time_setup();
}
//устанавливаем температуру в меню
void temp_setup() {
float tempInk;
lcd.clear();
delay(del);
button_read();
lcd.setCursor(2, 0);
lcd.print("TEMP.INK SETUP");
delay(1000);
int x = 0;
while (1) {
x++;
if (x > interval) {
break;
}
button_read();
//EEPROM_write(1, 37.5);
EEPROM_read(1, tempInk);
if (!button_enter) {
delay(del);
lcd.clear(); //очищаем экран
break;
}
if (!button_minus) {
EEPROM_write(1, tempInk + 0.1);
if (tempInk > 40) { //проверяем, если выше 40,
EEPROM_write(1, 40); //пишем в память 40
}
lcd.clear();
}
if (!button_plus) {
EEPROM_write(1, tempInk - 0.1);
if (tempInk < 30.0) { //проверяем, если выше 30,
EEPROM_write(1, 30.0); //пишем в память 30
}
lcd.clear();
}
lcd.setCursor(2, 0);
lcd.print("TEMP.INK = ");
lcd.print(tempInk, 1);
lcd.setCursor(0, 3);
lcd.print("minus NEXT plus");
delay(del);
}
}
//устанавливаем влажность
void hum_setup() {
float set_humidity;
lcd.clear();
delay(del);
button_read();
lcd.setCursor(3, 0);
lcd.print("HUMIDITY SETUP");
delay(1000);
int x = 0;
while (1) {
x++;
if (x > interval) {
break;
}
button_read();
//EEPROM_write(13, 65.0);
//EEPROM_read_mem(13, &set_humidity, sizeof(set_humidity));
EEPROM_read(13, set_humidity);
if (!button_enter) {
delay(del);
lcd.clear(); //очищаем экран
break;
}
if (!button_minus) {
EEPROM_write(13, set_humidity + 0.5);
if (set_humidity > 100) { //проверяем, если выше 40,
EEPROM_write(13, 100.0); //пишем в память 40
}
lcd.clear();
}
if (!button_plus) {
EEPROM_write(13, set_humidity - 0.5);
if (set_humidity < 20) { //проверяем, если выше 40,
EEPROM_write(13, 20.0); //пишем в память 40
}
lcd.clear();
}
lcd.setCursor(2, 0);
lcd.print("Humidity = ");
lcd.print(set_humidity, 1);
lcd.setCursor(0, 3);
lcd.print("minus NEXT plus");
delay(del);
}
}
//устанавливаем поворот
void turn_setup() {
int turnPeriod;
lcd.clear();
delay(del);
button_read();
lcd.setCursor(4, 0);
lcd.print("TURN SETUP");
delay(1000);
int x = 0;
while (1) {
x++;
if (x > interval) {
break;
}
button_read();
EEPROM_read(11, turnPeriod);
if (!button_enter) {
delay(del);
lcd.clear(); //очищаем экран
break;
}
if (!button_minus) {
EEPROM_write(11, turnPeriod + 1);
if (turnPeriod > 13) { //проверяем, если выше 40,
EEPROM_write(11, 13); //пишем в память 40
}
lcd.clear();
}
if (!button_plus) {
EEPROM_write(11, turnPeriod - 1);
if (turnPeriod < 0) { //проверяем, если выше 40,
EEPROM_write(11, 0); //пишем в память 40
}
lcd.clear();
}
EEPROM_read(11, turnPeriod);
lcd.setCursor(2, 0);
lcd.print("PERIOD = ");
lcd.print(turnPeriod);
lcd.print(" Hour");
lcd.setCursor(0, 3);
lcd.print("minus NEXT plus");
delay(del);
}
}
//устанавливаем сигнализацию
void alarm_setup() {
int alarmTemp;
lcd.clear();
delay(del);
button_read();
lcd.setCursor(4, 0);
lcd.print("ALARM SETUP");
delay(1000);
int x = 0;
while (1) {
x++;
if (x > interval) {
break;
}
button_read();
EEPROM_read(5, alarmTemp);
if (!button_enter) {
delay(del);
lcd.clear(); //очищаем экран
break;
}
if (!button_minus) {
EEPROM_write(5, alarmTemp + 1);
if (alarmTemp > 10) { //проверяем, если выше 40,
EEPROM_write(5, 10); //пишем в память 40
}
lcd.clear();
}
if (!button_plus) {
EEPROM_write(5, alarmTemp - 1);
if (alarmTemp < 1) { //проверяем, если выше 40,
EEPROM_write(5, 1); //пишем в память 40
}
lcd.clear();
}
lcd.setCursor(2, 0);
lcd.print("T.Alarm + - ");
lcd.print(alarmTemp);
lcd.print((char)223);
lcd.print("C");
lcd.setCursor(0, 3);
lcd.print("minus NEXT plus");
delay(del);
}
}
//устанавливаем вентиляцию
void vent_setup() {
int fanTemp;
lcd.clear();
delay(del);
button_read();
lcd.setCursor(4, 0);
lcd.print("FAN SETUP");
delay(1000);
int x = 0;
while (1) {
x++;
if (x > interval) {
break;
}
button_read();
EEPROM_read(9, fanTemp);
if (!button_enter) {
delay(del);
lcd.clear(); //очищаем экран
break;
}
if (!button_minus) {
EEPROM_write(9, fanTemp + 1);
if (fanTemp > 40) { //проверяем, если выше 40,
EEPROM_write(9, 40); //пишем в память 40
}
lcd.clear();
}
if (!button_plus) {
EEPROM_write(9, fanTemp - 1);
if (fanTemp < 20) { //проверяем, если выше 40,
EEPROM_write(9, 20); //пишем в память 40
}
lcd.clear();
}
lcd.setCursor(4, 0);
lcd.print("T.Fan = ");
lcd.print(fanTemp);
lcd.print((char)223);
lcd.print("C");
lcd.setCursor(0, 3);
lcd.print("minus NEXT plus");
delay(del);
}
}
void getSensors(){
unsigned long curMillis = millis(); // Get current time
sensors.requestTemperatures();
// Demonstrate non-blocking calls
if (curMillis - trhMillis >= TRHSTEP) { // Time for new measurements?
measActive = true;
measType = TEMP;
sht.meas(TEMP, &rawData, NONBLOCK); // Start temp measurement
trhMillis = curMillis;
}
if (measActive && sht.measRdy()) { // Check measurement status
if (measType == TEMP) { // Process temp or humi?
measType = HUMI;
temp2Ink = sht.calcTemp(rawData); // Convert raw sensor data
temp1Ink = sensors.getTempCByIndex(0);
sht.meas(HUMI, &rawData, NONBLOCK); // Start humi measurement
}
else {
measActive = false;
humidity = sht.calcHumi(rawData, temp2Ink); // Convert raw sensor data
dewpoint = sht.calcDewpoint(humidity, temp2Ink);
}
}
// logData();
}
//void logData() {
// Serial.print("temp1Ink = ");
// Serial.print(temp1Ink);
// Serial.print("temp2Ink = ");
// Serial.print(temp2Ink);
// Serial.print(" C, Humidity = ");
// Serial.print(humidity);
// Serial.print(" %, Dewpoint = ");
// Serial.print(dewpoint);
// Serial.println(" C");
//}
//используем терморегулятор
void thermostat() {
now = millis();
float tempPoint;
EEPROM_read_mem(1, &tempPoint, sizeof(tempPoint));
EEPROM_read(1, tempPoint);
Setpoint = tempPoint;
myPID.Compute();
if (now - windowStartTime > WindowSize) { //время для перещелкивания периода окна
windowStartTime = windowStartTime + WindowSize;
voltmeter(); //запускаем функцию измерения напряжения
Input = temp1Ink;
lcd.setCursor(1, 0); // устанавливаем курсор в 0-ом столбце, 0 строка (начинается с 0)
lcd.print("T1= ");
lcd.print(temp1Ink, 1); // печать температуры на дисплей
lcd.print((char)223);
lcd.print(" (");
lcd.print(Setpoint, 1);
lcd.print(")");
lcd.setCursor(1, 1);
lcd.print("T2= ");
lcd.print((temp2Ink - 0.4), 1); // печать температуры на дисплей
lcd.print((char)223);
lcd.setCursor(11, 1);
lcd.print(" (*");
lcd.print(temp1Ink - temp2Ink, 1);
lcd.print(")");
//lcd.print((char)223);
//lcd.print(" (");
//lcd.print(Setpoint, 1);
//lcd.print(")");
}
if (Output > (now - windowStartTime)) digitalWrite(heater_pin, HIGH);
else digitalWrite(heater_pin, LOW);
}
//управляем влажностью
void humidifer() {
//float humidity;
float set_humidity;
unsigned long humMillis = 0;
unsigned long curMillis = millis();
// if (curMillis - humMillis >= humStep) { // Time to toggle the LED state?
// humMillis = curMillis;
// //humidity = sht1x.readHumidity();
// }
EEPROM_read_mem(13, &set_humidity, sizeof(set_humidity));
EEPROM_read(13, set_humidity);
lcd.setCursor(1, 2); // устанавливаем курсор в 0-ом столбце, 1 строка (начинается с 0)
lcd.print(" H= ");
lcd.print(humidity, 1); // печать влажности на дисплей
lcd.print("%");
lcd.print(" (");
lcd.print(set_humidity, 1);
lcd.print(")");
if (set_humidity > humidity) digitalWrite(humidifer_pin, HIGH); //сравниваем измеренную влажность с заданной
else digitalWrite(humidifer_pin, LOW); //если влажность низкая, включаем увлажнитель
}
//управляем поворотом
void turn() {
int turnPeriod; //период поворота лотков в часах
int turnCommand;
EEPROM_read(11, turnPeriod);
lcd.setCursor(12, 3);
lcd.print("P");
lcd.print(turnPeriod);
if (turnPeriod == 0) return; //если нулевой период поворота, то не поворачиваем яйца.
if (turnPeriod < 13) turnCommand = turnPeriod;
else if (turnPeriod > 12 && turnFlag == 0) { //если произошел поворот (сброшен флаг) и значение в памяти 13, то
turnCommand = random(1, 6); //берем случайное значение часов 1-6
turnFlag = 1; //защелкиваем флаг вычисления случайных значений до следующего поворота
}
currentTime = millis();
if (currentTime >= (loopTime + turnCommand * 3600000)) { // 3600000 сравниваем текущий таймер с переменной loopTime + период поворота в часах.
digitalWrite(turn_pin, !digitalRead(turn_pin)); // включаем/выключаем реле поворота
loopTime = currentTime; // в loopTime записываем новое значение
turnFlag = 0; //сбрасываем флаг поворота
}
}
//управляем авариями
void alarm() {
float tempInk = sensors.getTempCByIndex(0);
int alarmTemp;
float setTemp;
EEPROM_read(5, alarmTemp);
EEPROM_read_mem(1, &setTemp, sizeof(setTemp));
EEPROM_read(1, setTemp);
lcd.setCursor(0, 3);
lcd.print("A");
lcd.print(setTemp + alarmTemp, 1);
if (millis() > 1800000) {
if (tempInk > (setTemp + alarmTemp) || tempInk < (setTemp - alarmTemp)) {
beeper(10);
digitalWrite(alarm_pin, LOW); //если измеренная температура выше заданной на величину аварии
}
else digitalWrite(alarm_pin, HIGH); //то включаем аварийный сигнал.
}
}
void beeper(int duration) {
tone(beeper_pin, 2000, duration);
}
//управляем вентиляторами
void fan() {
//float tempInk = sht1x.readTemperatureC();
float fanTemp;
float setTemp;
EEPROM_read(9, fanTemp);
EEPROM_read_mem(1, &setTemp, sizeof(setTemp));
EEPROM_read(1, setTemp);
lcd.setCursor(6, 3);
lcd.print("F");
lcd.print(setTemp + fanTemp, 1);
if (temp1Ink > (setTemp + fanTemp)) digitalWrite(fan_pin, LOW); //если измеренная температура выше заданной на величину аварии
else digitalWrite(fan_pin, HIGH); //то включаем аварийный сигнал.
}
void voltmeter(){
float outputValue = 0;
outputValue = float(analogRead(voltmeter_pin))/63,9;
//Serial.print("Voltage = " );
//Serial.println(outputValue);
lcd.setCursor(15, 3);
lcd.print("V");
lcd.print(outputValue, 1);
}
/********************************************
* 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");
}
Плата центрального модуля и плата датчика влажности с обвязкой.
Схему не рисовал, вроде и так понятно все и подписано. Дисплей, кнопки, блок реле подключаются к штыревым разъемам на мамке. Вместо двухамперных твердотельных релюшек на борту можно использовать внешние твердотельные реле или симисторы с опторазвязкой. Для этого сигналы управления выведены на отдельный разъем. Вместо стандатртного блока реле можно подвесить свой блок управления нагрузкой, например на силовых MOSFET ключах для 12В нагрузки. Термометр и гигрометр подключаются в телефонные RJ11 розетки. При подключении их не в свое гнездо ничего не сгорит т.к. контакты питания симметричны.
Вот так выглядит прототип устройства в корпусе из электрокоробки. Только там еще принт с надписями сверху приклеен, позже сфотографирую.
Ну вот как-то так. Не знаю что еще писать. Спрашивайте если интересно. Про инкубацию знаю много, сам птицевод с xx-летним стажем. Паять тоже умею, с Ардуиной пока учусь. Вот.
Да, саоме важное забыл сказать. Тут не Даллас не Сенсерион не дают задержки в библиотеках при измерении и скетч за 50 миллисекунд весь пробегает. Это важно для правильной работы ПИД. Пришлось замысловатую либу раскопать для SHT10, зато она умеет точку росы (!) вычислять. Не знаю только для чего мне в инкубаторе точка росы?
Качественно реализовано. Респект автору.
Спасибо. Я уж думал никто не зайдет сюда, всем веселее про загрузчик и программатор меня пинать.
Вот визуализатор - стандартный PID-Fronend на процессинге из ПИД библиотеки. Зеленая линия - заданная температура, красная - измеренная. Синяя - пропорциональный коэффициент. Обратите внимание как он бодро реагирует на изменения температуры (я пальцами термометр трогал).
А вот так пока внутренности выглядят.
Теперь вот так оно выглядит. В нижней строчке Т. аварии, Т. включения вентилятора, периодичность поворота и вольтметр. Во второй строке в скобочках разница в температуре между термометрами.
http://www.youtube.com/watch?v=ZGWFOssu-Bs&feature=youtu.be
Видео на котором почти ничего не понятно. Для интриги :)))
Код подправил немного:
#include <PID_v1.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <EEPROM2.h> #include <DallasTemperature.h> #include <OneWire.h> //#include <SimpleTimer.h> #include <Sensirion.h> int del = 80; // переменная ожидания между выборами меню unsigned int interval = 100; // интервал сколько будет длиться цикл while, после чего перейдёт к следующему меню.(кол-во итераций) #define heater_pin 13 // нагреватель #define humidifer_pin 12 // увлажнитель #define fan_pin 11 // вентилятор #define alarm_pin 14 // пин аварии #define beeper_pin 9 //пищалка по аварии #define dataPin 5 //SHT10 #define clockPin 6 //SHT10 #define turn_pin 10 // управление поворотом #define button_minus_pin 2 //пин кнопки "минус" #define button_plus_pin 3 //пин кнопки "плюс" #define button_enter_pin 4 //пин кнопки "enter" #define DS18B20_Pin 7 //пин термометра #define setSampleTime 1000 //время цикла ПИД #define voltmeter_pin 1 //вход А1 через делитель (22к/10к) подключен к питанию модуля. Измеряет до 16В. boolean button_minus; boolean button_plus; boolean button_enter; boolean turnFlag = 0; // флаг поворота для случайного периода float humidity; // Влажность float temp1Ink; // Температура DS18B20 float temp2Ink; // Температура SHT10 float dewpoint; // Точка росы unsigned int rawData; unsigned long currentTime; // задаем переменные для тайминга поворота unsigned long loopTime; unsigned long serialTime; //this will help us know when to talk with processing unsigned long now; unsigned long trhMillis = 0; // период опроса датчиков byte measActive = false; byte measType = TEMP; const unsigned long TRHSTEP = 3000UL; // Sensor query period LiquidCrystal_I2C lcd(0x27, 20, 4); // инициализация библиотеки дисплея //SHT1x sht1x(dataPin, clockPin); OneWire oneWire(DS18B20_Pin); DallasTemperature sensors(&oneWire); Sensirion sht = Sensirion(dataPin, clockPin); double Setpoint, Input, Output; //объявляем переменные для ПИД PID myPID(&Input, &Output, &Setpoint, 10, 4, 3, DIRECT); //Инициализируем ПИД-библиотеку и коэффициенты int WindowSize = setSampleTime; // ширина окна терморегулятора 1 секунда. unsigned long windowStartTime; //SimpleTimer timer; /* EEPROM1 - tempInk EEPROM5 (13) - set_humidity EEPROM7 - alarmTemp EEPROM9 - fanTemp EEPROM11 - turnPeriod */ void setup() { Serial.begin(9600); lcd.init(); // Старт дисплея lcd.backlight(); // Включаем подсветку дисплея windowStartTime = millis(); byte stat; byte error = 0; delay(15); myPID.SetOutputLimits(0, WindowSize); //задаем лимиты ширины ПИД-импульса от 0 до 1 секунды. myPID.SetMode(AUTOMATIC); //включаем ПИД-регулирование myPID.SetSampleTime(setSampleTime); pinMode(8, OUTPUT); //пока не используемый 8 вывод. Переводим в 1 чтобы не включать реле. digitalWrite(8, HIGH); pinMode(heater_pin, OUTPUT); pinMode(turn_pin, OUTPUT); // устанавливаем выводы digitalWrite(turn_pin, HIGH); pinMode(humidifer_pin, OUTPUT); pinMode(fan_pin, OUTPUT); digitalWrite(fan_pin, HIGH); pinMode(alarm_pin, OUTPUT); digitalWrite(alarm_pin, HIGH); pinMode(button_minus_pin, INPUT_PULLUP); //подтягиваем входы кнопок к плюсу встроенными резисторами pinMode(button_plus_pin, INPUT_PULLUP); pinMode(button_enter_pin, INPUT_PULLUP); sensors.begin(); sensors.setResolution(12); // установить разрешение (точность) sensors.setWaitForConversion(false); // отключить ожидание //timer.setInterval(3000, getSensors); windowStartTime = millis(); currentTime = millis(); // считываем время, прошедшее с момента запуска программы loopTime = currentTime; //EEPROM_write(13, 61.0); } void loop() { // Input = getTemp(); unsigned long now = millis(); //timer.run(); button_read(); if (!button_enter) { delay(del); lcd.clear(); menu(); } if (!button_minus) { delay(del); lcd.clear(); //действие по нажатии кнопки "-" } if (!button_plus) { delay(del); lcd.clear(); //действие по нажатии кнопки "+" } //send-receive with processing if it's time if (millis() > serialTime) { SerialReceive(); SerialSend(); serialTime += 500; } getSensors(); thermostat(); humidifer(); turn(); fan(); alarm(); //unsigned long now1 = millis(); //Serial.println(now1-now); } void button_read() {//функция проверки нажатия кнопки button_minus = digitalRead(button_minus_pin); //запоминаем значение кнопки button_plus = digitalRead(button_plus_pin); //запоминаем значение кнопки button_enter = digitalRead(button_enter_pin); //запоминаем значение кнопки if (!button_minus || !button_plus || !button_enter) beeper(50); } //меню void menu() { temp_setup(); hum_setup(); turn_setup(); alarm_setup(); vent_setup(); //data_time_setup(); } //устанавливаем температуру в меню void temp_setup() { float tempInk; lcd.clear(); delay(del); button_read(); lcd.setCursor(2, 0); lcd.print("TEMP.INK SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); //EEPROM_write(1, 37.5); EEPROM_read(1, tempInk); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(1, tempInk + 0.1); if (tempInk > 40) { //проверяем, если выше 40, EEPROM_write(1, 40); //пишем в память 40 } lcd.clear(); } if (!button_plus) { EEPROM_write(1, tempInk - 0.1); if (tempInk < 30.0) { //проверяем, если выше 30, EEPROM_write(1, 30.0); //пишем в память 30 } lcd.clear(); } lcd.setCursor(2, 0); lcd.print("TEMP.INK = "); lcd.print(tempInk, 1); lcd.setCursor(0, 3); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем влажность void hum_setup() { float set_humidity; lcd.clear(); delay(del); button_read(); lcd.setCursor(3, 0); lcd.print("HUMIDITY SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); //EEPROM_write(13, 65.0); //EEPROM_read_mem(13, &set_humidity, sizeof(set_humidity)); EEPROM_read(13, set_humidity); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(13, set_humidity + 0.5); if (set_humidity > 100) { //проверяем, если выше 40, EEPROM_write(13, 100.0); //пишем в память 40 } lcd.clear(); } if (!button_plus) { EEPROM_write(13, set_humidity - 0.5); if (set_humidity < 20) { //проверяем, если выше 40, EEPROM_write(13, 20.0); //пишем в память 40 } lcd.clear(); } lcd.setCursor(2, 0); lcd.print("Humidity = "); lcd.print(set_humidity, 1); lcd.setCursor(0, 3); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем поворот void turn_setup() { int turnPeriod; lcd.clear(); delay(del); button_read(); lcd.setCursor(4, 0); lcd.print("TURN SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); EEPROM_read(11, turnPeriod); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(11, turnPeriod + 1); if (turnPeriod > 13) { //проверяем, если выше 40, EEPROM_write(11, 13); //пишем в память 40 } lcd.clear(); } if (!button_plus) { EEPROM_write(11, turnPeriod - 1); if (turnPeriod < 0) { //проверяем, если выше 40, EEPROM_write(11, 0); //пишем в память 40 } lcd.clear(); } EEPROM_read(11, turnPeriod); lcd.setCursor(2, 0); lcd.print("PERIOD = "); lcd.print(turnPeriod); lcd.print(" Hour"); lcd.setCursor(0, 3); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем сигнализацию void alarm_setup() { int alarmTemp; lcd.clear(); delay(del); button_read(); lcd.setCursor(4, 0); lcd.print("ALARM SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); EEPROM_read(5, alarmTemp); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(5, alarmTemp + 1); if (alarmTemp > 10) { //проверяем, если выше 40, EEPROM_write(5, 10); //пишем в память 40 } lcd.clear(); } if (!button_plus) { EEPROM_write(5, alarmTemp - 1); if (alarmTemp < 1) { //проверяем, если выше 40, EEPROM_write(5, 1); //пишем в память 40 } lcd.clear(); } lcd.setCursor(2, 0); lcd.print("T.Alarm + - "); lcd.print(alarmTemp); lcd.print((char)223); lcd.print("C"); lcd.setCursor(0, 3); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем вентиляцию void vent_setup() { int fanTemp; lcd.clear(); delay(del); button_read(); lcd.setCursor(4, 0); lcd.print("FAN SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); EEPROM_read(9, fanTemp); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(9, fanTemp + 1); if (fanTemp > 40) { //проверяем, если выше 40, EEPROM_write(9, 40); //пишем в память 40 } lcd.clear(); } if (!button_plus) { EEPROM_write(9, fanTemp - 1); if (fanTemp < 20) { //проверяем, если выше 40, EEPROM_write(9, 20); //пишем в память 40 } lcd.clear(); } lcd.setCursor(4, 0); lcd.print("T.Fan = "); lcd.print(fanTemp); lcd.print((char)223); lcd.print("C"); lcd.setCursor(0, 3); lcd.print("minus NEXT plus"); delay(del); } } void getSensors(){ unsigned long curMillis = millis(); // Получаем текущее время работы sensors.requestTemperatures(); // Demonstrate non-blocking calls if (curMillis - trhMillis >= TRHSTEP) { // время для нового измерения? measActive = true; measType = TEMP; sht.meas(TEMP, &rawData, NONBLOCK); // измеряем температуру. trhMillis = curMillis; } if (measActive && sht.measRdy()) { // проверяем статус измерения if (measType == TEMP) { // обрабатываем температуру или влажность? measType = HUMI; temp2Ink = sht.calcTemp(rawData); // Конвертируем сырые данные с сенсора temp1Ink = sensors.getTempCByIndex(0); sht.meas(HUMI, &rawData, NONBLOCK); // измеряем влажность } else { measActive = false; humidity = sht.calcHumi(rawData, temp2Ink); // конвертируем данные с сенсора dewpoint = sht.calcDewpoint(humidity, temp2Ink); } } // logData(); } //void logData() { // Serial.print("temp1Ink = "); // Serial.print(temp1Ink); // Serial.print("temp2Ink = "); // Serial.print(temp2Ink); // Serial.print(" C, Humidity = "); // Serial.print(humidity); // Serial.print(" %, Dewpoint = "); // Serial.print(dewpoint); // Serial.println(" C"); //} //используем терморегулятор void thermostat() { now = millis(); float tempPoint; EEPROM_read_mem(1, &tempPoint, sizeof(tempPoint)); EEPROM_read(1, tempPoint); Setpoint = tempPoint; myPID.Compute(); if (now - windowStartTime > WindowSize) { //время для перещелкивания периода окна windowStartTime = windowStartTime + WindowSize; voltmeter(); //запускаем функцию измерения напряжения Input = temp1Ink; lcd.setCursor(1, 0); // устанавливаем курсор в 0-ом столбце, 0 строка (начинается с 0) lcd.print("T1= "); lcd.print(temp1Ink, 1); // печать температуры на дисплей lcd.print((char)223); lcd.print(" ("); lcd.print(Setpoint, 1); lcd.print(")"); lcd.setCursor(1, 1); lcd.print("T2= "); lcd.print((temp2Ink - 0.4), 1); // печать температуры на дисплей lcd.print((char)223); lcd.setCursor(11, 1); lcd.print(" (*"); lcd.print(temp1Ink - temp2Ink, 1); lcd.print(")"); //lcd.print((char)223); //lcd.print(" ("); //lcd.print(Setpoint, 1); //lcd.print(")"); } if (Output > (now - windowStartTime)) digitalWrite(heater_pin, HIGH); else digitalWrite(heater_pin, LOW); } //управляем влажностью void humidifer() { //float humidity; float set_humidity; unsigned long humMillis = 0; unsigned long curMillis = millis(); // if (curMillis - humMillis >= humStep) { // humMillis = curMillis; // //humidity = sht1x.readHumidity(); // } EEPROM_read_mem(13, &set_humidity, sizeof(set_humidity)); EEPROM_read(13, set_humidity); lcd.setCursor(1, 2); // устанавливаем курсор в 0-ом столбце, 1 строка (начинается с 0) lcd.print(" H= "); lcd.print(humidity, 1); // печать влажности на дисплей lcd.print("%"); lcd.print(" ("); lcd.print(set_humidity, 1); lcd.print(")"); if (set_humidity > humidity) digitalWrite(humidifer_pin, HIGH); //сравниваем измеренную влажность с заданной else digitalWrite(humidifer_pin, LOW); //если влажность низкая, включаем увлажнитель } //управляем поворотом void turn() { int turnPeriod; //период поворота лотков в часах int turnCommand; EEPROM_read(11, turnPeriod); lcd.setCursor(10, 3); lcd.print("P"); lcd.print(turnPeriod); if (turnPeriod == 0) return; //если нулевой период поворота, то не поворачиваем яйца. if (turnPeriod < 13) turnCommand = turnPeriod; else if (turnPeriod > 12 && turnFlag == 0) { //если произошел поворот (сброшен флаг) и значение в памяти 13, то turnCommand = random(1, 6); //берем случайное значение часов 1-6 turnFlag = 1; //защелкиваем флаг вычисления случайных значений до следующего поворота } currentTime = millis(); if (currentTime >= (loopTime + turnCommand * 3600000)) { // 3600000 сравниваем текущий таймер с переменной loopTime + период поворота в часах. digitalWrite(turn_pin, !digitalRead(turn_pin)); // включаем/выключаем реле поворота loopTime = currentTime; // в loopTime записываем новое значение turnFlag = 0; //сбрасываем флаг поворота } } //управляем авариями void alarm() { float tempInk = sensors.getTempCByIndex(0); int alarmTemp; float setTemp; EEPROM_read(5, alarmTemp); EEPROM_read_mem(1, &setTemp, sizeof(setTemp)); EEPROM_read(1, setTemp); lcd.setCursor(0, 3); lcd.print("A"); lcd.print(setTemp + alarmTemp, 1); if (millis() > 1800000) { if (tempInk > (setTemp + alarmTemp) || tempInk < (setTemp - alarmTemp)) { beeper(10); digitalWrite(alarm_pin, LOW); //если измеренная температура выше заданной на величину аварии } else digitalWrite(alarm_pin, HIGH); //то включаем аварийный сигнал. } } void beeper(int duration) { tone(beeper_pin, 2000, duration); } //управляем вентиляторами void fan() { //float tempInk = sht1x.readTemperatureC(); int fanTemp; EEPROM_read(9, fanTemp); lcd.setCursor(6, 3); lcd.print("F"); lcd.print(fanTemp); if (temp1Ink > fanTemp) digitalWrite(fan_pin, LOW); //если измеренная температура выше заданной на величину аварии else digitalWrite(fan_pin, HIGH); //то включаем аварийный сигнал. } void voltmeter(){ float outputValue = 0; outputValue = float(analogRead(voltmeter_pin))/63,9; //Serial.print("Voltage = " ); //Serial.println(outputValue); lcd.setCursor(14, 3); lcd.print("V"); lcd.print(outputValue, 1); } /******************************************** * 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"); }Здравствуйте в начале указано что заказаны платы в Китае ,тоесть это будет шилд своего рода?Если да то какая цена?Сделал что-то похожее но пока до запуска руки не дошли.
Добрый. Ну вон же плата, чуть выше картинкой опубликована. Выглядеть будет как и эта самоделка, только заводского исполнения. Образец можно в моей прошлой теме про брудер посмотреть. http://arduino.ru/forum/proekty/bruder-avtomat
шилом на верное не совсем правильно называть плату для Про Мини.
Здравствуйте! Не подскажете какой датчик влажности используете, делаю себе подобный инкубатор но для паучьих коконов, там все тоже только параметры влажности и температуры отличаются, количество поворотов 4-6 в сутки... мне и код написали , но все равно куча проблем... датчик влажности завис на 99% иногда после отключения света не может проинициализироватся и вообще виснет...
Можно ли ваш код както подпилить под мое железо?
Здравствуйте. Датчик самый дешевый из хороших - SHT10. Мой код подпилить можно, не вижу проблем.
Вод код с автоматической программой. Периодичность поворота задается в меню.
#include <PID_v1.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <EEPROM2.h> #include "RTClib.h" #include <DallasTemperature.h> #include <OneWire.h> #include <Sensirion.h> //#include <avr/wdt.h> int del = 80; // переменная ожидания между выборами меню unsigned int interval = 100; // интервал сколько будет длиться цикл while, после чего перейдёт к следующему меню.(кол-во итераций) //#define EXT_HEATING // ИСПОЛЬЗУЕМ ДОП.НАГРЕВАТЕЛЬ ВМЕСТО УВЛАЖНИТЕЛЯ. Если нужен увлажнитель, просто закомментируйте эту строку. #define heater_pin 13 // нагреватель #define humidifer_pin 12 // увлажнитель #define fan_pin 11 // вентилятор #define alarm_pin 14 // пин аварии #define beeper_pin 9 //пищалка по аварии #define dataPin 5 //SHT10 #define clockPin 6 //SHT10 #define turn_pin 10 // управление поворотом #define extend_heater_pin 8 // дополнительный нагреватель #define button_minus_pin 2 //пин кнопки "минус" #define button_plus_pin 3 //пин кнопки "плюс" #define button_enter_pin 4 //пин кнопки "enter" #define DS18B20_Pin 7 //пин термометра #define setSampleTime 1000 //время цикла ПИД #define voltmeter_pin 1 //вход А1 через делитель (22к/10к) подключен к питанию модуля. Измеряет до 16В. #define T_correction -0.4 // коррекция температуры SHT10 boolean button_minus; // статус нажатия кнопок boolean button_plus; boolean button_enter; boolean turnFlag = 0; // флаг поворота для случайного периода float humidity; // Влажность float temp1Ink; // Температура DS18B20 float temp2Ink; // Температура SHT10 //float dewpoint; // Точка росы unsigned int rawData; unsigned long currentTime; // задаем переменные для тайминга поворота unsigned long loopTime; unsigned long serialTime; //this will help us know when to talk with processing unsigned long now; unsigned long trhMillis = 0; // период опроса датчиков byte measActive = false; byte measType = TEMP; const unsigned long TRHSTEP = 300UL; // Период опроса датчиков unsigned int currentDay; // текущий день в юникс-формате LiquidCrystal_I2C lcd(0x27, 20, 4); // инициализация библиотеки дисплея //SHT1x sht1x(dataPin, clockPin); OneWire oneWire(DS18B20_Pin); DallasTemperature sensors(&oneWire); Sensirion sht = Sensirion(dataPin, clockPin); double Setpoint, Input, Output; //объявляем переменные для ПИД PID myPID(&Input, &Output, &Setpoint, 5, 4, 3, DIRECT); //Инициализируем ПИД-библиотеку и коэффициенты int WindowSize = setSampleTime; // ширина окна терморегулятора 1 секунда. unsigned long windowStartTime; unsigned long alarmDelay; RTC_DS1307 RTC; //SimpleTimer timer; /* EEPROM1 - startDayUnixtime EEPROM5 - EEPROM7 - alarmTemp EEPROM9 - fanTemp EEPROM11 - turnPeriod EEPROM13 - Humidity EEPROM15 - isRunning */ void setup() { Serial.begin(9600); RTC.begin(); // Старт часов lcd.init(); // Старт дисплея Wire.begin(); // старт I2C lcd.backlight(); // Включаем подсветку дисплея windowStartTime = millis(); byte stat; byte error = 0; delay(15); // wdt_enable (WDTO_8S); //взводим сторожевой таймер на 8 секунд. myPID.SetOutputLimits(0, WindowSize); //задаем лимиты ширины ПИД-импульса от 0 до 1 секунды. myPID.SetMode(AUTOMATIC); //включаем ПИД-регулирование myPID.SetSampleTime(setSampleTime); pinMode(extend_heater_pin, OUTPUT); //пин дополнительного нагревателя. Переводим в 1 чтобы не включать реле. digitalWrite(extend_heater_pin, HIGH); pinMode(heater_pin, OUTPUT); pinMode(turn_pin, OUTPUT); // устанавливаем выводы digitalWrite(turn_pin, HIGH); pinMode(humidifer_pin, OUTPUT); pinMode(fan_pin, OUTPUT); digitalWrite(fan_pin, HIGH); pinMode(alarm_pin, OUTPUT); digitalWrite(alarm_pin, HIGH); pinMode(button_minus_pin, INPUT_PULLUP); //подтягиваем входы кнопок к плюсу встроенными резисторами pinMode(button_plus_pin, INPUT_PULLUP); pinMode(button_enter_pin, INPUT_PULLUP); alarmDelay = millis(); sensors.begin(); sensors.setResolution(12); // установить разрешение (точность) sensors.setWaitForConversion(false); // отключить ожидание //RTC.adjust(DateTime(__DATE__, __TIME__)); //раскоментируйте для установки системмных даты и времени } void loop() { // Input = getTemp(); // unsigned long now1 = millis(); unsigned int startDayUnixtime; // хранящийся в памяти день старта программы в юникс-формате float needTemp = 37.6; // нужная для текущего дня температура инкубации (по умолчанию) float needHum = 60.5; // ---- влажность int ventTime = 2; // длительность проветривания boolean needTurn = false; // нужен ли поворот яиц? unsigned int currentTime_day; //текущий день в юникс-формате (сколько дней минуло с 1 января 1970) DateTime now = RTC.now(); currentTime_day = (now.unixtime() / 86400L); lcd.setCursor(0, 3); if (now.hour() < 10) lcd.print(" "); lcd.print(now.hour(), DEC); lcd.print(":"); if (now.minute() < 10)lcd.print(0); lcd.print(now.minute(), DEC); lcd.print(" "); if (now.day() < 10)lcd.print(" "); lcd.print(now.day(), DEC); lcd.print("."); if (now.month() < 10)lcd.print(0); lcd.print(now.month(), DEC); lcd.print("."); lcd.print(now.year()-2000, DEC); //timer.run(); button_read(); if (!button_enter) { delay(del); lcd.clear(); menu(); } if (!button_minus) { delay(del); lcd.clear(); alarmDelay = millis(); // задержка аварии по нажатии кнопки Минус } if (!button_plus) { delay(del); lcd.clear(); digitalWrite(turn_pin, !digitalRead(turn_pin)); // включаем/выключаем реле поворота по кнопке Плюс } //send-receive with processing if it's time if (millis() > serialTime) { SerialReceive(); SerialSend(); serialTime += 500; } EEPROM_read(1, startDayUnixtime); currentDay = (currentTime_day - startDayUnixtime); // ЭТА СЕКЦИЯ ХРАНИТ РЕЖИМЫ АВТОМАТИЧЕСКОЙ ИНКУБАЦИИ. ИХ МОЖНО НАСТРАИВАТЬ ПОД СВОИ ХОТЕЛКИ!!! //проще всего это сделать, предварительно нарисовав таблицу инкубации для любого типа яиц //Настройки идут с конца таблицы к началу т.к. последующие режимы перезаписываются предыдущими //после данные по температуре, влажности, длительности вентиляции и необходимости поворота передаются в функции. if (currentDay < 18) { // если день инкубации до 18-го (если дней больше 18-ти, то сработают назначенные выше параметры по умолчанию) needTemp = 37.0; // то температура 37 needHum = 85.5; // влажность 85,5% ventTime = 10; // время вентиляции 10 минут (включается в 7 и 19 часов ежедневно) needTurn = false; // поворот лотков отключен } if (currentDay < 15) { // если день инкубации до 15-го, то задаем новые значения тем же переменным. needTemp = 37.4; // если же день инкубации больше 15, но меньше 18, то условие не сработают и needHum = 48.0; // значения останутся прежними. И так до самого начала инкубации. ventTime = 10; // Можно назначать разное количество временных промежутков и наборов параметров для них. needTurn = true; } if (currentDay < 12) { needTemp = 37.6; needHum = 60.0; ventTime = 3; needTurn = true; } if (currentDay < 2) { needTemp = 38.2; needHum = 60.0; ventTime = 0; needTurn = true; } // конец секции задания режима автоматической инкубации. getSensors(); thermostat(needTemp, needHum); // влажность передаем только для вывода на дисплей ))) humidifer(needHum); turn(needTurn); fan(ventTime); alarm(needTemp); ext_heater(needTemp); //outpuPower(); // wdt_reset(); //unsigned long now1 = millis(); //Serial.println(now1-now); } void button_read() {//функция проверки нажатия кнопки button_minus = digitalRead(button_minus_pin); //запоминаем значение кнопки button_plus = digitalRead(button_plus_pin); //запоминаем значение кнопки button_enter = digitalRead(button_enter_pin); //запоминаем значение кнопки if (!button_minus || !button_plus || !button_enter) beeper(50); //wdt_reset(); } //меню void menu() { turn_setup(); alarm_setup(); vent_setup(); startInk(); //data_time_setup(); } // записываем в память день начала инкубации в юникс-формате void startInk() { unsigned int currentTime_day; unsigned int memoryDay; DateTime now = RTC.now(); currentTime_day = (now.unixtime() / 86400L); lcd.clear(); delay(del); button_read(); lcd.setCursor(4, 0); lcd.print("START INK"); delay(1000); int x = 0; while (1) { x++; if (x > interval) break; button_read(); EEPROM_read(1, memoryDay); //Serial.print(currentTime_day); //Serial.print("-"); //Serial.print(memoryDay); //Serial.print("="); //Serial.println(currentTime_day-memoryDay); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(1, currentTime_day); lcd.clear(); } if (!button_plus) { EEPROM_write(1, memoryDay + 1); lcd.clear(); } EEPROM_read(1, memoryDay); lcd.setCursor(5, 0); lcd.print("DAY = "); lcd.print(currentTime_day - memoryDay); lcd.setCursor(0, 3); lcd.print("START NEXT +1"); delay(del); } } //устанавливаем поворот void turn_setup() { int turnPeriod; lcd.clear(); delay(del); button_read(); lcd.setCursor(4, 0); lcd.print("TURN SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); EEPROM_read(11, turnPeriod); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(11, turnPeriod + 1); if (turnPeriod > 13) { //проверяем, если выше 13, EEPROM_write(11, 13); //пишем в память 13 } lcd.clear(); } if (!button_plus) { EEPROM_write(11, turnPeriod - 1); if (turnPeriod < 0) { //проверяем, если ниже 0, EEPROM_write(11, 0); //пишем в память 0 } lcd.clear(); } EEPROM_read(11, turnPeriod); lcd.setCursor(2, 0); lcd.print("PERIOD = "); if (turnPeriod < 13)lcd.print(turnPeriod); if (turnPeriod > 12) lcd.print("RND"); lcd.print(" Hour"); lcd.setCursor(0, 3); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем сигнализацию void alarm_setup() { int alarmTemp; lcd.clear(); delay(del); button_read(); lcd.setCursor(4, 0); lcd.print("ALARM SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); EEPROM_read(5, alarmTemp); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(5, alarmTemp + 1); if (alarmTemp > 10) { //проверяем, если выше 40, EEPROM_write(5, 10); //пишем в память 40 } lcd.clear(); } if (!button_plus) { EEPROM_write(5, alarmTemp - 1); if (alarmTemp < 1) { //проверяем, если выше 40, EEPROM_write(5, 1); //пишем в память 40 } lcd.clear(); } lcd.setCursor(2, 0); lcd.print("T.Alarm + - "); lcd.print(alarmTemp); lcd.print((char)223); lcd.print("C"); lcd.setCursor(0, 3); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем вентиляцию void vent_setup() { int fanTemp; lcd.clear(); delay(del); button_read(); lcd.setCursor(3, 0); lcd.print("A.FAN SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); EEPROM_read(9, fanTemp); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { EEPROM_write(9, fanTemp + 1); if (fanTemp > 40) { //проверяем, если выше 40, EEPROM_write(9, 40); //пишем в память 40 } lcd.clear(); } if (!button_plus) { EEPROM_write(9, fanTemp - 1); if (fanTemp < 20) { //проверяем, если выше 40, EEPROM_write(9, 20); //пишем в память 40 } lcd.clear(); } lcd.setCursor(4, 0); lcd.print("T.Fan = "); lcd.print(fanTemp); lcd.print((char)223); lcd.print("C"); lcd.setCursor(0, 3); lcd.print("minus NEXT plus"); delay(del); } } void getSensors() { unsigned long curMillis = millis(); // Получаем текущее время работы if (curMillis - trhMillis >= TRHSTEP) { // время для нового измерения? sensors.requestTemperatures(); temp1Ink = sensors.getTempCByIndex(0); } // Demonstrate non-blocking calls if (curMillis - trhMillis >= TRHSTEP * 4) { // время для нового измерения? measActive = true; measType = TEMP; sht.meas(TEMP, &rawData, NONBLOCK); // измеряем температуру. trhMillis = curMillis; } if (measActive && sht.measRdy()) { // проверяем статус измерения if (measType == TEMP) { // обрабатываем температуру или влажность? measType = HUMI; temp2Ink = sht.calcTemp(rawData); // Конвертируем сырые данные с сенсора temp2Ink = (temp2Ink + (T_correction)); // Корректируем показания текрмометра sht.meas(HUMI, &rawData, NONBLOCK); // измеряем влажность } else { measActive = false; humidity = sht.calcHumi(rawData, temp2Ink); // конвертируем данные с сенсора //dewpoint = sht.calcDewpoint(humidity, temp2Ink); } } } //используем терморегулятор void thermostat(float tempPoint, float set_humidity) { double now1; now1 = millis(); Setpoint = tempPoint; myPID.Compute(); if (now1 - windowStartTime > WindowSize) { //время для перещелкивания периода окна windowStartTime = windowStartTime + WindowSize; //voltmeter(); //запускаем функцию измерения напряжения Input = temp1Ink; lcd.setCursor(1, 0); // устанавливаем курсор в 0-ом столбце, 0 строка (начинается с 0) lcd.print("T1= "); lcd.print(temp1Ink, 1); // печать температуры на дисплей lcd.print((char)223); lcd.print(" ("); lcd.print(Setpoint, 1); lcd.print(")"); lcd.setCursor(1, 1); lcd.print("T2= "); lcd.print(temp2Ink, 1); // печать температуры на дисплей lcd.print((char)223); lcd.setCursor(11, 1); lcd.print(" (*"); lcd.print(temp1Ink - temp2Ink, 1); lcd.print(") "); lcd.setCursor(1, 2); // устанавливаем курсор в 2-ом столбце, 3 строка (начинается с 0) lcd.print(" H= "); lcd.print(humidity, 1); // печать влажности на дисплей lcd.print("%"); lcd.print(" ("); lcd.print(set_humidity, 1); lcd.print(")"); lcd.setCursor(15, 3); lcd.print("D"); lcd.print(currentDay); // текущий день инкубации } if (Output > (now1 - windowStartTime)) digitalWrite(heater_pin, HIGH); else digitalWrite(heater_pin, LOW); } //управляем влажностью void humidifer(float set_humidity) { //float humidity; unsigned long humMillis = 0; unsigned long curMillis = millis(); // if (curMillis - humMillis >= humStep) { // humMillis = curMillis; // //humidity = sht1x.readHumidity(); // } if (set_humidity > humidity) digitalWrite(humidifer_pin, HIGH); //сравниваем измеренную влажность с заданной else digitalWrite(humidifer_pin, LOW); //если влажность низкая, включаем увлажнитель } //управляем поворотом void turn(boolean needTurn) { int turnPeriod; //период поворота лотков в часах int turnCommand; EEPROM_read(11, turnPeriod); //lcd.setCursor(10, 3); //lcd.print("P"); //lcd.print(turnPeriod); if (turnPeriod == 0) return; //если нулевой период поворота, то не поворачиваем яйца. if (turnPeriod < 13) turnCommand = turnPeriod; else if (turnPeriod > 12 && turnFlag == 0) { //если произошел поворот (сброшен флаг) и значение в памяти 13, то turnCommand = random(1, 6); //берем случайное значение часов 1-6 turnFlag = 1; //защелкиваем флаг вычисления случайных значений до следующего поворота } currentTime = millis(); if (needTurn == true) { if (currentTime >= (loopTime + turnCommand * 3600000)) { // 3600000 сравниваем текущий таймер с переменной loopTime + период поворота в часах. digitalWrite(turn_pin, !digitalRead(turn_pin)); // включаем/выключаем реле поворота loopTime = currentTime; // в loopTime записываем новое значение turnFlag = 0; //сбрасываем флаг поворота } } } //управляем авариями void alarm(float needTemp) { float tempInk = sensors.getTempCByIndex(0); int alarmTemp; EEPROM_read(5, alarmTemp); //lcd.setCursor(0, 3); //lcd.print("A"); //lcd.print(needTemp + alarmTemp, 1); if ((millis() - alarmDelay) > 1800000) { if (tempInk > (needTemp + alarmTemp) || tempInk < (needTemp - alarmTemp)) { beeper(10); digitalWrite(alarm_pin, LOW); //если измеренная температура выше заданной на величину аварии } else digitalWrite(alarm_pin, HIGH); //то включаем аварийный сигнал. } } void beeper(int duration) { tone(beeper_pin, 2000, duration); } //управляем вентиляторами void fan(int fanTime) { //float tempInk = sht1x.readTemperatureC(); DateTime now = RTC.now(); int fanTemp; EEPROM_read(9, fanTemp); //lcd.setCursor(6, 3); //lcd.print("F"); //lcd.print(fanTemp); if ((now.hour() == 7 && now.minute() < fanTime) || (now.hour() == 19 && now.minute() < fanTime) || (temp1Ink > fanTemp)) { digitalWrite(fan_pin, LOW); //если наступило время проветривания или измеренная температура выше заданной на величину авариито включаем продувку. digitalWrite(extend_heater_pin, HIGH); // при этом отключаем обогрев digitalWrite(heater_pin, LOW); } else digitalWrite(fan_pin, HIGH); //иначе выключаем. } // вольтметр //void voltmeter() { // float outputValue = 0; // outputValue = float(analogRead(voltmeter_pin)) / 63, 9; // //if(outputValue < 4.5) beeper(50); // //Serial.print("Voltage = " ); // //Serial.println(outputValue); // lcd.setCursor(14, 3); // lcd.print("V"); // lcd.print(outputValue, 1); //} // //// Печать мощности нагрвателя //void outpuPower() { // lcd.setCursor(14, 3); // lcd.print("W"); // lcd.print(Output, 0); // lcd.print(" "); //} void ext_heater(float needTemp) { // управление дополнительным нагревателем на 8 ножке через блок реле. float tempInk = sensors.getTempCByIndex(0); if (tempInk < (needTemp - 3)) digitalWrite(extend_heater_pin, LOW); else digitalWrite(extend_heater_pin, HIGH); } /******************************************** * 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(millis() / 1000); 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"); }В алгоритме заложено недостаточное кол-во проветриваний.
За 1 час весь объем воздуха нужно менять не менее 2-х раз. Для этого вентиляцию включают каждые 10 минут на 10 секунд. Иначе эмбрионы могут задохнуться (задохлики).
Выбор датчика температуры не ахти, его точность 0,5 градуса - это много. Нужно калибровать и вносить корректировки, чтобы на рабочем режиме (37-38 градусов) значения температуры были правильные.
Ну и главное - это конструкция самого инкубатора. Если сделать от балды - будет разброс по температуре внутри рабочей камеры. Правильный температурный гистерезис должен быть не более 0,3 градусов.
Спасибо. Я инкубирую яйца постоянно и сотнями. В курсе. Воздухообмен идет постоянно как и работа внутреннего вентилятора. Но проветривание это другое. Это отключение обогрева и охлаждние яиц. Имитация вставания наседеи с гнезда. Яйцо охлаждается, обьем содержимого уменьшается, через скорлупу в яйцо проникает воздух...
это особено актуально для крупных яиц.
По калибровке термометров - у меня есть доступ к поверенному лабораторному оборудованию и имею возможность точно откалибровать Далласы. С разбросом больше 0,3 мне еще не попадались.
гистерезиса нет, есть ПИД.
>>Но проветривание это другое.
>>Это отключение обогрева и охлаждние яиц.
НЕТ. Речь не об Охлаждении, а об Проветривании.
Проветривание - удаление углекислых газов выделяемых яйцом.
Охлаждать яйца совсем не обязательно, главное Проветривание. Для примера в промышленных инкубаторах яйца никогда не охлаждают, только проветривание.
>>гистерезиса нет, есть ПИД.
Вы не поняли. Я про разницу температуры внутри инкубатора.
В промышленных инкубаторах идет постоянная загрузка яйца партиями и один средний режим поддерживается постоянно. Поэтому и не охлаждают. Гусиные и страусиные с охлаждением лучше выводимость демонстрируют. Воздухообмен 6-кратный я при конструировании инкубатора сразу предусматриваю. 2 раза в час слишком мало, особенно для бройлеров. Вы вообще много сами вывели циплят?
Можете поястинить про переворот. Я так понал что переворот лотков включается через определенное настраеваемое время, но я не понял на какое время поворот включается? Когда реле поворота должно выключаться?
Концевик в конце хода лотков. Системы поворота разные могут быть. Есть редукторные асинхронники, шаговые двигатели, моторы постоянного тока и коллекторные. Актуаторы тоже можно. Соответственно и схема управления реверсом разная. Это не проблемы автоматики на самом деле.
>>2 раза в час слишком мало, особенно для бройлеров
Много/мало определяется количеством яиц и объемом камеры, производительностью вентилятора. У вас, как я понял, бытовой на 100 яиц. Проветривание каждый 10 минут по 10 секунд - хватит с головой.
>>Вы вообще много сами вывели циплят?
Мне много не нужно. Больше сотни за вывод не вывожу, только для себя.
Полезную информацию по конструкции и режимам инкубации можно посмотреть тут http://fermer.ru/forum/samodelnye-inkubatory/163938
Особено полезно видео с объяснениями насчет проветривания.
Можете поястинить про переворот. Я так понал что переворот лотков включается через определенное настраеваемое время, но я не понял на какое время поворот включается? Когда реле поворота должно выключаться?
Нет, не бытовой и не на 100. Скажем от 1000 и самострой. Не вижу смысла в цикличном проветривании при наличии постоянного притока свежего воздуха и оттока отработаного. В промышленных инкубаторах управляемые заслрнки видели?
Не дороговато ли с такой доставкой?
Народные РД09 я на Авите по 100-200р беру и от 220 работает с одним реле и 2 герконами.
позвольте людям иметь свое мнение пожалуйста. Вот так работает поворот в инкубаторе на 1000 яиц на РД09 http://m.youtube.com/watch?v=FydII7kDRq0
Ну, на вкус и цвет...
Чем больше в системе узлов и агрегатов - тем меньше надежность.
Для сравнения как это с применением линейных двигателей работает.
http://www.youtube.com/watch?v=E86fIIEwFik
Я же в небольших инкубаторах использую шаговые двигатели на 12V
Umka. Выше вы писали, что датчики не дают задержки. Собственно меня интересует DS18B20. Каким образом убрана задержка? Специальной библиотекой или вот этим
Самому датчику в любом случае нужно время для вычисления температуры. Этими строчками отключается delay в библиотеке и датчики опрашиваются по таймеру. Просто работа мк не останавливается на время конверсии. Иначе ПИД лажает.
Я видел инкубаторы Кравчика. Он их по 60тр и больше продает. Можно конечно купить и не морочится с Ардуинами. С другой стороны можно купить ципленка, курицу несушку или готовое мясо и не иметь сношений с инкубацией. Я жадный. Мне 200р дешевле 6000 за привод поворота.
В точку. +1000 )))
Доброно времени суток! Извените за дремучесть. очень хочется посмотреть схему устройства полностью. Я не профи в смысле электроники. Заранее благодарен
Вам придется сходить на перепел ком, найти не сложно. Без регистрации никак.
Да нету схемы, всё видно по описанию пинов и печатке. Не вижу смысла рисовать еще и схему. Плата по сути соединение в кучу двух датчиков, релюшек и Ардуины и диплеем.
Umke уважуха!!!
Суппер!!!!
Я этим летом для пробы взял 7 кур подрощеных, несушек. Очень понравилось. Планирую на следующий год стационарный курятник ставить и взять еще цесарок на яйца и разведение.
Ну и понятное дело потребуется инкубатор. Так что спасибо за труд.
Вот один в один ктото продает.
https://u-m.biz/shop/index.php?route=product/product&path=59&product_id=51
Но есть готовый и дешевле
http://ru.aliexpress.com/item/100-New-Dual-Digital-F-C-PID-Temperature-Controller-XM-26/608847660.html?spm=2114.031020208.3.1.m9jDY1&ws_ab_test=searchweb201556_6_71_72_73_74_75,searchweb201527_1,searchweb201560_9
Первый мой, а второй без ПИД и с реле электромагнитным на нагрев. Они на все пишут ПИД, но не в одном нет настроек этих коэффициентов. Разве что для паяльной станции термостат, там есть, но он не инкубаторный.
А xm-18 в остальном ничего блочек. Там 2 ступени нагрева, управление влажностью и поворотом, задумка нормальная, их даже в промышленные китайские шкафы ставят. Но вот реле я бы сразу на твердотельные поменял. Контроллер там свой китайский и доки только на китайском и прошивок я не встречал.
Благодарю за информацию. Но для меня 80 зелёных - дороговато. Наверное, придётся самому ковыряться.
Благодарю за информацию. Но для меня 80 зелёных - дороговато. Наверное, придётся самому ковыряться.
Так там вроде и голую плату за недорого предлагают. Детали в магазине, схема-скетч бесплатно. Куда уж демократичней.
Ну да плата 5 баксиков.
Чем отличается ПИД регулятор от релейного в эксплуатации для меня (эксплуатанта).
Так там вроде и голую плату за недорого предлагают. Детали в магазине, схема-скетч бесплатно. Куда уж демократичней.
Там не только голые платы, но и КИТ-наборы есть. И услуга по установке SMD-компоненов присутствует в дополнительных услугах. Если кого пугает. И не только по инкубаторам.
Ну не знаю, купить тестолит, пару дней на разводку потратить, повозиться с двухсторонкой и сэкономить 350р?
Практически ничем, не считая того, что реле с течением времени прикажет долго жить и искать аналогичное может оказаться еще тем геммороем. Пределы регулирования будут более грубыми. На всех подобных устройствах наиболее удобны электронные ключи.
Релейные регуляторы удобны на термостатировании мощных нагрузок, имеющих большую инерцию, типа бойлеров, систем отопления и т.п.
У Носова и вовсе обходились лампочкой и термометром.))))
Так человек сказал, что ему дорого. Хотя я плату за такие деньги поленился бы делать. ИМХО.
Чем отличается ПИД регулятор от релейного в эксплуатации для меня (эксплуатанта).
С ПИД температура поддерживается точнее, нет перегрева, это очень важно. На пальцах обЪясняю. Допустим в инкубаторе температура 37,6 градусов, соответственно ее нужно поднять. Если схема на реле то при его включении вся мошность обогревателей греет воздух к примеру +6 градусов, и на выходе получаем прегрев 43,6. При такой температуре яйца сварятся. Теперь про ПИД. Там все по другому температура 37,6 ПИД регулятор вычисляет нужную мощность и включает нагреватель не на всю мощность а только на +0,2 градуса. На выходе 37,8.
Благодарю за ответ.
скажите пожалуйста как в этом коде заменить sht10
на dth11
Категорически не советую использовать DHT11. Хотяб DHT22 возьмите или AM2123 например. https://drive.google.com/open?id=0B0vRuSZ7oRUeRGgwZlNnY3VVQzQ
Категорически не советую использовать DHT11. Хотяб DHT22 возьмите или AM2123 например. https://drive.google.com/open?id=0B0vRuSZ7oRUeRGgwZlNnY3VVQzQ
я очень уважаю ваше мнение
но уменя инкубатор на 25-30 яиц
неохота мне платить большие деньги
темболее что DHT11 уже имееться в наличии
вот с кодом проблема
а так бы уже все запустил время всетаки
мне вообщето таких наворотов как у вас ненужно
мне контроль темп. и автомат. переворот яиц
но поскольку с ардуино пока оочень далек
пытаюсь ваш код приспособить но ничего путнего не получаеться
Денег нет, наворотов не нужно, код не получается... даже не знаю что тут посоветовать. Может китайский термостат за 200р?
#include <PID_v1.h> #include <Wire.h> #include "RTClib.h" #include <LiquidCrystal_I2C.h> #include <EEPROM2.h> #include <DallasTemperature.h> #include <OneWire.h> #include <SimpleTimer.h> #include <avr/wdt.h> #include "DHT.h" int del = 80; // переменная ожидания между выборами меню unsigned int interval = 300; // интервал сколько будет длиться цикл while, после чего перейдёт к следующему меню.(кол-во итераций) //#define EXT_HEATING // ИСПОЛЬЗУЕМ ДОП.НАГРЕВАТЕЛЬ ВМЕСТО УВЛАЖНИТЕЛЯ. Если нужен увлажнитель, просто закомментируйте эту строку. #define heater_pin 13 // нагреватель //#define humidifer_pin 12 // увлажнитель #define fan_pin 11 // вентилятор #define alarm_pin 14 // пин аварии #define beeper_pin 9 //пищалка по аварии #define turn_pin 10 // управление поворотом #define extend_heater_pin 8 // дополнительный нагреватель #define button_minus_pin 2 //пин кнопки "минус" #define button_plus_pin 3 //пин кнопки "плюс" #define button_enter_pin 4 //пин кнопки "enter" #define DS18B20_Pin 7 //пин термометра #define setSampleTime 1000 //время цикла ПИД #define voltmeter_pin 1 //вход А1 через делитель (22к/10к) подключен к питанию модуля. Измеряет до 16В. //#define T_correction -0.0 // коррекция температуры SHT10 //#define h_histeresis 5.0 // гистерезис влажности #define DHTPIN 5 // what pin we're connected to #define DHTTYPE DHT11 // DHT 22 (AM2302) boolean button_minus; boolean button_plus; boolean button_enter; boolean bottomView = 0; // флаг индикации нижней строки float humidity; // Влажность float temp1Ink; // Температура DS18B20 float temp2Ink; // Температура SHT10 float dewpoint; // Точка росы unsigned int rawData; unsigned long currentTime; // задаем переменные для тайминга поворота unsigned long serialTime; //this will help us know when to talk with processing unsigned long now; unsigned long trhMillis = 0; // период опроса датчиков const unsigned long TRHSTEP = 300UL; // Период опроса датчиков LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display RTC_DS1307 rtc; OneWire oneWire(DS18B20_Pin); DallasTemperature sensors(&oneWire); DHT dht(DHTPIN, DHTTYPE); double Setpoint, Input, Output; //объявляем переменные для ПИД PID myPID(&Input, &Output, &Setpoint, 10000.0, 0.5, 10.5, DIRECT); //Инициализируем ПИД-библиотеку и коэффициенты int WindowSize = setSampleTime; // ширина окна терморегулятора 1 секунда. unsigned long windowStartTime; unsigned long alarmDelay; //SimpleTimer timer; /* EEPROM1 - tempInk (float) EEPROM(13) - set_humidity (float) EEPROM5 - +-alarmTemp (float) EEPROM9 - fanTemp (int) EEPROM11 - turnPeriod (int) */ //////////////////////////////////////////////////////////////////////////////////////////////////////////// void setup() { Serial.begin(9600); lcd.init(); // Старт дисплея lcd.backlight(); // Включаем подсветку дисплея windowStartTime = millis(); byte stat; byte error = 0; float tempInk; float set_humidity; float alarmTemp; rtc.begin(); // Старт часов //int fanTemp; //int turnPeriod; rtc.adjust(DateTime(__DATE__, __TIME__)); // EEPROM_read(1, tempInk); // tempInk = constrain(tempInk, 36.7, 38.7); // EEPROM_write(1, tempInk); // EEPROM_read(13, set_humidity); // set_humidity = constrain(set_humidity, 40.0, 90.0); // EEPROM_write(13, set_humidity); // EEPROM_read(9, fanTemp); // fanTemp = constrain(fanTemp, 38, 39); // EEPROM_write(9, fanTemp); // EEPROM_read(5, alarmTemp); // alarmTemp = constrain(alarmTemp, 1.0, 4.0); // EEPROM_write(5, alarmTemp); // EEPROM_read(11, turnPeriod); // alarmTemp = constrain(turnPeriod, 0, 13); // EEPROM_write(11, turnPeriod); delay(15); wdt_enable (WDTO_8S); //взводим сторожевой таймер на 8 секунд. myPID.SetOutputLimits(0, WindowSize); //задаем лимиты ширины ПИД-импульса от 0 до 1 секунды. myPID.SetMode(AUTOMATIC); //включаем ПИД-регулирование myPID.SetSampleTime(setSampleTime); pinMode(extend_heater_pin, OUTPUT); //пин дополнительного нагревателя. Переводим в 1 чтобы не включать реле. digitalWrite(extend_heater_pin, LOW); pinMode(heater_pin, OUTPUT); pinMode(turn_pin, OUTPUT); // устанавливаем выводы digitalWrite(turn_pin, LOW); pinMode(humidifer_pin, OUTPUT); pinMode(fan_pin, OUTPUT); digitalWrite(fan_pin, LOW); pinMode(alarm_pin, OUTPUT); digitalWrite(alarm_pin, HIGH); pinMode(button_minus_pin, INPUT_PULLUP); //подтягиваем входы кнопок к плюсу встроенными резисторами pinMode(button_plus_pin, INPUT_PULLUP); pinMode(button_enter_pin, INPUT_PULLUP); alarmDelay = millis(); dht.begin(); sensors.begin(); sensors.setResolution(12); // установить разрешение (точность) sensors.setWaitForConversion(false); // отключить ожидание //timer.setInterval(3000, getSensors); windowStartTime = millis(); } //////////////////////////////////////////////////////////////////////////////////////////////////////// void loop() { // Input = getTemp(); unsigned long now = millis(); { DateTime now = rtc.now(); lcd.setCursor(11, 0); lcd.print(now.hour(), DEC); lcd.print(':'); lcd.print(now.minute(), DEC); } //timer.run(); button_read(); if (!button_enter) { delay(del); lcd.clear(); menu(); } if (!button_minus) { delay(del); lcd.clear(); alarmDelay = millis(); // задержка аварии по нажатии кнопки Минус } if (!button_plus) { delay(del); lcd.clear(); digitalWrite(turn_pin, !digitalRead(turn_pin)); // включаем/выключаем реле поворота по кнопке Плюс } if (!button_plus && !button_plus) { delay(del); lcd.clear(); // bottomView = !bottomView; // переключаем режим показа нижней строки. } //send-receive with processing if it's time if (millis() > serialTime) { SerialReceive(); SerialSend(); serialTime += 500; } getSensors(); thermostat(); //#ifdef EXT_HEATING extend_heater(); //#else humidifer(); //#endif turn(); fan(); alarm(); //outpuPower(); wdt_reset(); //unsigned long now1 = millis(); //Serial.println(now1-now); } /////////////////////////////////////////////////////////////////////////////////////////////// void button_read() {//функция проверки нажатия кнопки button_minus = digitalRead(button_minus_pin); //запоминаем значение кнопки button_plus = digitalRead(button_plus_pin); //запоминаем значение кнопки button_enter = digitalRead(button_enter_pin); //запоминаем значение кнопки if (!button_minus || !button_plus || !button_enter) beeper(50); wdt_reset(); } //меню//////////////////////////////////////////////////////////////////////////////////////// void menu() { temp_setup(); hum_setup(); turn_setup(); alarm_setup(); vent_setup(); //data_time_setup(); } //устанавливаем температуру в меню/////////////////////////////////////////////////////////// void temp_setup() { float tempInk; lcd.clear(); delay(del); button_read(); lcd.setCursor(0, 0); lcd.print("TEMP.INK SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); //EEPROM_write(1, 37.7); EEPROM_read(1, tempInk); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { x = 0; EEPROM_write(1, tempInk + 0.1); if (tempInk > 40) { //проверяем, если выше 40, EEPROM_write(1, 40); //пишем в память 40 } lcd.clear(); } if (!button_plus) { x = 0; EEPROM_write(1, tempInk - 0.1); if (tempInk < 30.0) { //проверяем, если выше 30, EEPROM_write(1, 30.0); //пишем в память 30 } lcd.clear(); } lcd.setCursor(0, 0); lcd.print("TEMP.INK = "); lcd.print(tempInk, 1); lcd.setCursor(0, 1); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем влажность//////////////////////////////////////////////////////////////////////// void hum_setup() { float set_humidity; lcd.clear(); delay(del); button_read(); lcd.setCursor(0, 0); lcd.print("HUMIDITY SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); //EEPROM_write(13, 65.0); //EEPROM_read_mem(13, &set_humidity, sizeof(set_humidity)); EEPROM_read(13, set_humidity); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { x = 0; EEPROM_write(13, set_humidity + 0.5); if (set_humidity > 100) { //проверяем, если выше 40, EEPROM_write(13, 100.0); //пишем в память 40 } lcd.clear(); } if (!button_plus) { x = 0; EEPROM_write(13, set_humidity - 0.5); if (set_humidity < 20) { //проверяем, если выше 40, EEPROM_write(13, 20.0); //пишем в память 40 } lcd.clear(); } lcd.setCursor(0, 0); lcd.print("Humidity = "); lcd.print(set_humidity, 1); lcd.setCursor(0, 1); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем поворот///////////////////////////////////////////////////////////////////////// void turn_setup() { int turnPeriod; lcd.clear(); delay(del); button_read(); lcd.setCursor(0, 0); lcd.print("TURN SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); EEPROM_read(11, turnPeriod); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { x = 0; EEPROM_write(11, turnPeriod + 1); if (turnPeriod > 13) { //проверяем, если выше 40, EEPROM_write(11, 13); //пишем в память 40 } lcd.clear(); } if (!button_plus) { x = 0; EEPROM_write(11, turnPeriod - 1); if (turnPeriod < 0) { //проверяем, если выше 40, EEPROM_write(11, 0); //пишем в память 40 } lcd.clear(); } EEPROM_read(11, turnPeriod); lcd.setCursor(0, 0); lcd.print("PERIOD = "); lcd.print(turnPeriod); lcd.print(" Hour"); lcd.setCursor(0, 1); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем сигнализацию///////////////////////////////////////////////////////////////////// void alarm_setup() { float alarmTemp; lcd.clear(); delay(del); button_read(); lcd.setCursor(0, 0); lcd.print("ALARM SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); //EEPROM_write(5, 2.5); EEPROM_read(5, alarmTemp); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { x = 0; EEPROM_write(5, alarmTemp + 0.1); if (alarmTemp > 10.0) { //проверяем, если ниже 10, EEPROM_write(5, 10.0); //пишем в память 10 } lcd.clear(); } if (!button_plus) { x = 0; EEPROM_write(5, alarmTemp - 0.1); if (alarmTemp < 1.0) { //проверяем, если выше 40, EEPROM_write(5, 1.0); //пишем в память 40 } lcd.clear(); } lcd.setCursor(0, 0); lcd.print("T.Alarm + - "); lcd.print(alarmTemp, 1); lcd.print((char)223); lcd.print("C"); lcd.setCursor(0, 1); lcd.print("minus NEXT plus"); delay(del); } } //устанавливаем вентиляцию///////////////////////////////////////////////////////////////// void vent_setup() { int fanTemp; lcd.clear(); delay(del); button_read(); lcd.setCursor(0, 0); lcd.print("FAN SETUP"); delay(1000); int x = 0; while (1) { x++; if (x > interval) { break; } button_read(); EEPROM_read(9, fanTemp); if (!button_enter) { delay(del); lcd.clear(); //очищаем экран break; } if (!button_minus) { x = 0; EEPROM_write(9, fanTemp + 1); if (fanTemp > 40) { //проверяем, если выше 40, EEPROM_write(9, 40); //пишем в память 40 } lcd.clear(); } if (!button_plus) { x = 0; EEPROM_write(9, fanTemp - 1); if (fanTemp < 20) { //проверяем, если выше 40, EEPROM_write(9, 20); //пишем в память 40 } lcd.clear(); } lcd.setCursor(0, 0); lcd.print("T.Fan = "); lcd.print(fanTemp); lcd.print((char)223); lcd.print("C"); lcd.setCursor(0, 1); lcd.print("minus NEXT plus"); delay(del); } } ///////////////////////////////////////////////////////////////////////////////////////////////// void getSensors() { unsigned long curMillis = millis(); // Получаем текущее время работы if (curMillis - trhMillis >= TRHSTEP) { // время для нового измерения? sensors.requestTemperatures(); temp1Ink = sensors.getTempCByIndex(0); } if (curMillis - trhMillis >= TRHSTEP * 4) { // время для нового измерения? temp2Ink = dht.readTemperature(); // temp2Ink = (temp2Ink + (T_correction)); // Корректируем показания текрмометра humidity = dht.readHumidity(); trhMillis = curMillis; } // logData(); } ////////////////////////////////////////////////////////////////////////////////////////////////// //void logData() { // Serial.print("temp1Ink = "); // Serial.print(temp1Ink); // Serial.print("temp2Ink = "); // Serial.print(temp2Ink); // Serial.print(" C, Humidity = "); // Serial.print(humidity); // Serial.print(" %, Dewpoint = "); // Serial.print(dewpoint); // Serial.println(" C"); //} //используем терморегулятор void thermostat() { now = millis(); float tempPoint; float set_humidity; EEPROM_read_mem(1, &tempPoint, sizeof(tempPoint)); EEPROM_read(1, tempPoint); EEPROM_read_mem(13, &set_humidity, sizeof(set_humidity)); EEPROM_read(13, set_humidity); Setpoint = tempPoint; myPID.Compute(); if (now - windowStartTime > WindowSize) { //время для перещелкивания периода окна windowStartTime = windowStartTime + WindowSize; voltmeter(); //запускаем функцию измерения напряжения Input = temp1Ink; lcd.setCursor(0, 0); // устанавливаем курсор в 0-ом столбце, 0 строка (начинается с 0) lcd.print("T"); lcd.print(temp1Ink, 1); // печать температуры на дисплей //lcd.print((char)223); //lcd.print(" ("); //lcd.print(Setpoint, 1); //lcd.print(")"); lcd.setCursor(5, 0); lcd.print("T"); lcd.print(temp2Ink, 1); // печать температуры на дисплей //lcd.print((char)223); //lcd.setCursor(12, 0); //lcd.print(" "); //lcd.print(temp1Ink - temp2Ink, 1); //lcd.print(" "); //lcd.print((char)223); //lcd.print(" ("); //lcd.print(Setpoint, 1); //lcd.print(")"); lcd.setCursor(0, 1); // устанавливаем курсор в 2-ом столбце, 3 строка (начинается с 0) lcd.print("H"); lcd.print(humidity, 1); // печать влажности на дисплей } if (Output > (now - windowStartTime)) digitalWrite(heater_pin, HIGH); else digitalWrite(heater_pin, LOW); } //управляем влажностью///////////////////////////////////////////////////////////////////////// void humidifer() { //float humidity; float set_humidity; // if (curMillis - humMillis >= humStep) { // humMillis = curMillis; // //humidity = sht1x.readHumidity(); // } //EEPROM_read_mem(13, &set_humidity, sizeof(set_humidity)); EEPROM_read(13, set_humidity); if (set_humidity > humidity) digitalWrite(humidifer_pin, HIGH); //сравниваем измеренную влажность с заданной lcd.setCursor(5, 1); lcd.print("#");//вывод инфо состояния на LCD // if (set_humidity < humidity + h_histeresis) digitalWrite(humidifer_pin, LOW); lcd.setCursor(5, 1); lcd.print("-");//вывод инфо состояния } //управляем поворотом////////////////////////////////////////////////////////////////////////// void turn() { int turnPeriod; //период поворота лотков в часах int turnCommand; boolean turnFlag = 0; // флаг поворота для случайного периода static unsigned long loopTime; EEPROM_read(11, turnPeriod); if (turnPeriod == 0) return; //если нулевой период поворота, то не поворачиваем яйца. if (turnPeriod < 13) turnCommand = turnPeriod; else if (turnPeriod > 12 && turnFlag == 0) { //если произошел поворот (сброшен флаг) и значение в памяти 13, то turnCommand = random(1, 6); //берем случайное значение часов 1-6 turnFlag = 1; //защелкиваем флаг вычисления случайных значений до следующего поворота } currentTime = millis()/1000; //lcd.setCursor(6, 1); //lcd.print("P"); Serial.println(currentTime); Serial.println(loopTime + turnCommand * 3600UL); if (currentTime > (loopTime + turnCommand * 3600UL)) { // 3600000 сравниваем текущий таймер с переменной loopTime + период поворота в часах. digitalWrite(turn_pin, !digitalRead(turn_pin)); // включаем/выключаем реле поворота loopTime = currentTime; // в loopTime записываем новое значение turnFlag = 0; //сбрасываем флаг поворота } //lcd.print((loopTime - currentTime + turnCommand * 3600UL) / 60UL); //lcd.print(" "); } //управляем авариями///////////////////////////////////////////////////////////////////////////// void alarm() { float tempInk = sensors.getTempCByIndex(0); float alarmTemp; float setTemp; EEPROM_read(5, alarmTemp); EEPROM_read_mem(1, &setTemp, sizeof(setTemp)); EEPROM_read(1, setTemp); //lcd.setCursor(0, 3); //lcd.print("A"); //lcd.print(setTemp + alarmTemp, 1); if ((millis() - alarmDelay) > 1800000) { if (tempInk > (setTemp + alarmTemp) || tempInk < (setTemp - alarmTemp)) { beeper(10); digitalWrite(alarm_pin, LOW); //если измеренная температура выше заданной на величину аварии } else digitalWrite(alarm_pin, HIGH); //то включаем аварийный сигнал. } } //////////////////////////////////////////////////////////////////////////////////////////////////// void beeper(int duration) { tone(beeper_pin, 2000, duration); } //управляем вентиляторами//////////////////////////////////////////////////////////////////////////// void fan() { //float tempInk = sht1x.readTemperatureC(); int fanTemp; EEPROM_read(9, fanTemp); //lcd.setCursor(6, 3); //lcd.print("F"); //lcd.print(fanTemp); if (temp1Ink > fanTemp) digitalWrite(fan_pin, HIGH); //если измеренная температура выше заданной на величину аварии else digitalWrite(fan_pin, LOW); //то включаем аварийный сигнал. } // вольтметр///////////////////////////////////////////////////////////////////////////////////////// void voltmeter() { float outputValue = 0; outputValue = float(analogRead(voltmeter_pin)) / 63, 9; //if(outputValue < 4.5) beeper(50); //Serial.print("Voltage = " ); //Serial.println(outputValue); lcd.setCursor(6, 1); lcd.print("V"); lcd.print(outputValue, 1); } // Печать мощности нагрвателя/////////////////////////////////////////////////////////////////////// void outpuPower() { //lcd.setCursor(15, 3); //lcd.print("W"); //lcd.print(Output, 0); //lcd.print(" "); } void extend_heater() { // управление допnnnnnnnnnnm,.kkkолнительным нагревателем на 8 ножке через блок реле./////////// float tempInk = sensors.getTempCByIndex(0); float setTemp; EEPROM_read_mem(1, &setTemp, sizeof(setTemp)); EEPROM_read(1, setTemp); if (tempInk < (setTemp - 3)) //{ digitalWrite(extend_heater_pin, HIGH); //#ifdef EXT_HEATING //digitalWrite(humidifer_pin, LOW); //#endif //} else digitalWrite(extend_heater_pin, LOW); //#ifdef EXT_HEATING //digitalWrite(humidifer_pin, HIGH); //#endif } /******************************************** * 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 } /////////////////////////////////////////////////////////////////////////////////////////////// void SerialSend(){ Serial.print(millis() / 1000); 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"); }в начале все показывает, а вот в настройках пишет humidity:ноу
темп:ноу и т.д
в eeprom надо данные вбить.
а как часы выставить
и дату?
а то кнопками
немогу нет функции такой
часы и дату? Этого нет в основном скетче. А в автопрограммах есть закоментированная строчка вначале, установка с компа времени при компиляции. А еще я где-то делал по нажатию 2 кнопок меню установки даты-времени.
Кстати, если нужно подобрать коэффициенты ПИД регулятора можно воспользоваться библиотекой PID_AutoTune_v0.h.
Подбор коэффициентов происходит очень долго, несколько часов. У меня за ночь подобрались коэффициенты.
#include <PID_v1.h> #include <PID_AutoTune_v0.h> #include <OneWire.h> int DS18B20_Pin = 9; //DS18S20 Signal pin on digital 9 //Temperature chip i/o OneWire ds(DS18B20_Pin); // on digital pin 9 #define RelayPin 10 int relayPin = 10; int WindowSize = 3000; unsigned long windowStartTime; byte ATuneModeRemember=2; double input=80, output=50, setpoint=37.81; double kp=2,ki=0.5,kd=2; double kpmodel=1.5, taup=100, theta[50]; double outputStart=5; //5 double aTuneStep=500, aTuneNoise=1, aTuneStartValue=500; unsigned int aTuneLookBack=20; //200 boolean tuning = true; unsigned long modelTime, serialTime; PID myPID(&input, &output, &setpoint,kp,ki,kd, DIRECT); PID_ATune aTune(&input, &output); //set to false to connect to the real world boolean useSimulation = false; void setup() { //Setup the pid myPID.SetOutputLimits(0, WindowSize); myPID.SetMode(AUTOMATIC); if(useSimulation) { for(byte i=0;i<50;i++) { theta[i]=outputStart; } modelTime = 0; } if(tuning) { tuning=false; changeAutoTune(); tuning=true; } serialTime = 0; Serial.begin(9600); windowStartTime = millis(); //from pid test Relay pinMode(RelayPin,OUTPUT); //from pid test Relay } void loop() { unsigned long now = millis(); float temperature = getTemp(); float tempF = temperature;// * 9/5 + 32; if(!useSimulation) { //pull the input in from the real world input = tempF; } if(tuning) { byte val = (aTune.Runtime()); if (val!=0) { tuning = false; } if(!tuning) { //we're done, set the tuning parameters kp = aTune.GetKp(); ki = aTune.GetKi(); kd = aTune.GetKd(); myPID.SetTunings(kp,ki,kd); AutoTuneHelper(false); } } else myPID.Compute(); if(useSimulation) { theta[30]=output; if(now>=modelTime) { modelTime +=100; DoModel(); } } else { // analogWrite(0,output); // commented out by me unsigned long now = millis(); if(now - windowStartTime>WindowSize) { //time to shift the Relay Window windowStartTime += WindowSize; } if(output > now - windowStartTime) digitalWrite(RelayPin,HIGH); else digitalWrite(RelayPin,LOW); } //send-receive with processing if it's time if(millis()>serialTime) { SerialReceive(); SerialSend(); serialTime+=1000; } } void changeAutoTune() { if(!tuning) { //Set the output to the desired starting frequency. output=aTuneStartValue; aTune.SetNoiseBand(aTuneNoise); aTune.SetOutputStep(aTuneStep); aTune.SetLookbackSec((int)aTuneLookBack); AutoTuneHelper(true); tuning = true; } else { //cancel autotune aTune.Cancel(); tuning = false; AutoTuneHelper(false); } } void AutoTuneHelper(boolean start) { if(start) ATuneModeRemember = myPID.GetMode(); else myPID.SetMode(ATuneModeRemember); } void SerialSend() {Serial.print("Temprege: ");Serial.println(input); Serial.print("setpoint: ");Serial.print(setpoint); Serial.print(" "); Serial.print("input: ");Serial.print(input); Serial.print(" "); Serial.print("output: ");Serial.print(output); Serial.print(" "); if(tuning){ Serial.println(" tuning mode "); } else { Serial.print("Temprege: ");Serial.println(input); Serial.print("kp: ");Serial.print(myPID.GetKp());Serial.print(" "); Serial.print("ki: ");Serial.print(myPID.GetKi());Serial.print(" "); Serial.print("kd: ");Serial.print(myPID.GetKd());Serial.println(); } } void SerialReceive() { if(Serial.available()) { char b = Serial.read(); Serial.flush(); if((b=='1' && !tuning) || (b!='1' && tuning))changeAutoTune(); } } void DoModel() { //cycle the dead time for(byte i=0;i<49;i++) { theta[i] = theta[i+1]; } //compute the input input = (kpmodel / taup) *(theta[0]-outputStart) + input*(1-1/taup) + ((float)random(-10,10))/100; } float getTemp(){ //returns the temperature from one DS18S20 in DEG Celsius byte data[12]; byte addr[8]; if ( !ds.search(addr)) { //no more sensors on chain, reset search ds.reset_search(); return -1000; } if ( OneWire::crc8( addr, 7) != addr[7]) { Serial.println("CRC is not valid!"); return -1000; } if ( addr[0] != 0x10 && addr[0] != 0x28) { Serial.print("Deviceot recognized"); return -1000; } ds.reset(); ds.select(addr); ds.write(0x44,1); // start conversion, with parasite power on at the end byte present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad for (int i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } ds.reset_search(); byte MSB = data[1]; byte LSB = data[0]; float tempRead = ((MSB << 8) | LSB); //using two's compliment float TemperatureSum = tempRead / 16; return TemperatureSum; }Категорически не советую использовать DHT11. Хотяб DHT22 возьмите или AM2123 например. https://drive.google.com/open?id=0B0vRuSZ7oRUeRGgwZlNnY3VVQzQ
Здравствуйте, чтобы не плодить темы, продолжу в этой, тем более, что взял за основу скеч от Umka, но так как с микроконтроллерами недавно занимаюсь, вопрос по датчику влажности Si7021, как правильно с ним работать? у него есть внутренний нагреватель, в демо примере он 30 сек включен, 20 выключен, измерения делаются раз в секунду. Сам вопрос: надо включать этот внутренний нагреватель или нет
#include <Wire.h> #include <Si7021.h> SI7021 si7021; // BASIC DEMO // ---------- // Print humidity and temperature to the serial monitor while toggling the heater on and off // periodically. // NOTE ON RESOLUTION: // ------------------- // Changing the resolution of the temperature will also change the humidity resolution. // Likewise, changing the resolution of the humidity will also change temperature resolution. // Two functions are provided to change the resolution, both functions are similar except they have // different masks to the registers, so setTempRes(14) will change the temp resolution to 14-bit // but humidity will also be set to 12-bit. Setting the humidity resolution to 8-bit setHumidityRes(8) // will change the temperature resolution to 12-bit. // Resolution Table: // 14-bit Temp <-> 12-bit Humidity // 13-bit Temp <-> 10-bit Humidity // 12-bit Temp <-> 8-bit Humidity // 11-bit Temp <-> 11-bit Humidity // NOTE ON HEATER: // --------------- // The HTRE bit in the user register is what turns the heater on and off. This register // is stored on non-volatile memory to it will keep its state when power is removed. If // the heater is enabled and power is removed before the program had chance to turn it off // then the heater will remain enabled the next time it is powered on. The heater has to // be explicitly turned off. In the begin() function for this library, a reset procedure // will take place and reset the user register to default, so the heater will be turned off // and the resolution will also be set to default, 14-bit temperature and 12-bit humidity. // Keep this in mind if testing and swapping sensors/libraries. void setup() { Serial.begin(115200); si7021.begin(); // Runs : Wire.begin() + reset() while(!Serial); // Wait for serial monitor to open Serial.println("BASIC DEMO"); Serial.println("------------------------------------------"); si7021.setHumidityRes(12); // Humidity = 12-bit / Temperature = 14-bit } void loop() { static uint8_t heaterOnOff; // Create static variable for heater control si7021.setHeater(heaterOnOff); // Turn heater on or off Serial.print("Heater Status = "); Serial.println(si7021.getHeater() ? "ON" : "OFF"); for(int i = (heaterOnOff ? 20 : 30); i>0; i--) { Serial.print("Humidity : "); Serial.print(si7021.readHumidity()); // Read humidity and print to serial monitor Serial.print(" %\t"); Serial.print("Temp : "); Serial.print(si7021.readTemp()); // Read temperature and print to serial monitor Serial.print(" C\t"); Serial.println(i); // Print count down for heater change delay(500); } heaterOnOff = !heaterOnOff; // Toggle heater on/off variable }?