Управление обогревом комнаты при помощи Arduino и Dallas18B20

pencraft
Offline
Зарегистрирован: 17.01.2012

Идея проекта

В нашем дачном домике установлена стальная конвекционная печь. Греет она очень хорошо, но вот под утро остывает. Домик старый и не очень хорошо утепленный, так что в условиях «сурового северного лета» ложишься спать в тепле, а просыпаешься – вроде как бы уже и нет ;))

В хозяйстве нашелся электрический тепловентилятор, оставалось только сделать автомат, который включал бы его при похолодании в комнате. Устройство было сделано за один вечер буквально из подручных материалов и уже в ближайшие выходные было испытано в «боевых условиях». Испытания прошли удачно ;)) так что теперь в течение всей ночи температура в спальне поддерживается на постоянном уровне. Первые шаги к «умному дому» ;)))

Аппаратная часть

Итак, для создания мегадевайса были использованы:

  1. Плата Arduino Uno
  2. Шильд LCD+Keypad от DFRobot, состоящий из дисплея 16х2 и т. н. «аналоговой клавиатуры» на 5 кнопок.
  3. Термодатчик на микросхеме Dallas DS18B20. Был использован вот такой датчик в герметичном корпусе:

    Но можно применить и просто микросхему:
  4. Твердотельное реле SSR-25 на 25А
  5. Две коробки для розеток-выключателей для открытой проводки. Например, такие:
  6. Одна розетка, любая подходящая для вышеупомянутой коробки
  7. Провод на 10А с вилкой (был использован стандартный «хвост» от компьютерного БП)
  8. Кусок провода для соединения контроллера и реле (обычный электрический или монтажный)
  9. 1 резистор на 4,5-5кОм
  10. Блок питания для Arduino (использовался обычный блок питания 12В 1А)

Немного подробнее об LCD-шильде

LCD-шильд представляет собой довольно удобное решение для несложных устройств, требующих отображения 1-2 параметров, а также несложных настроек. Он состоит из традиционного дисплея – 2 строки по 16 символов с контроллером Hitachi HD44780, подключенного по параллельному 4-битовому интерфейсу и «аналоговой» клавиатуры, подключенной к одному из аналоговых входов. Клавиатура основывается на делителе напряжения из нескольких резисторов, который выдает различное напряжение при нажатии той или иной кнопки.

Дисплей занимает пины D4-D7 (шина данных), D8 (сигнал RS), D9 (сигнал E) и D10 (включение-выключение подсветки). Контраст дисплея регулируется подстроечным резистором, расположенным в левом верхнем углу шильда. Для работы с таким дисплеем давно и успешно используется стандартная библиотека LiquidCrystal.h.

Строка инициализации для моего экземпляра шильда выглядит так:

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

Клавиатура занимает аналоговый пин A0 и состоит из 5 кнопок. Прежде чем начать работать с такой клавиатурой желательно определить значения на аналоговом входе Arduino, соответствующие нажатию каждой из кнопок. Разные экземпляры клавиатур могут выдавать разные значения. Это можно сделать при помощи следующего тестового скетча:

int val = 0; // переменная для хранения считываемого значения

void setup()
{
  Serial.begin(9600); // установка связи по serial
}

void loop()
{
  val = analogRead(A0); // считываем значение
  Serial.println(val); // выводим полученное значение
}

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

#define ButtonUp_LOW 90
#define ButtonUp_HIGH 100
#define ButtonDown_LOW 240
#define ButtonDown_HIGH 280
#define ButtonLeft_LOW 390
#define ButtonLeft_HIGH 450
#define ButtonRight_LOW 0
#define ButtonRight_HIGH 50
#define ButtonSelect_LOW 620
#define ButtonSelect_HIGH 650

Еще немного про Dallas DS18B20

Про термометр на этой микросхеме написано уже достаточно, поэтому буквально несколько слов.

Схема включения – типовая, сигнальный провод подтянут к питанию резистором на 4,5-5кОм:

Для подключения к контроллеру был выбран один из свободных после подключения LCD-шильда пинов, а именно D2. Для работы с датчиком существует библиотека DallasTemperature.h, которую можно скачать, например, отсюда: http://download.milesburton.com/Arduino/MaximTemperature/DallasTemperature_372Beta.zip

Также потребуется библиотека OneWire.h, которую можно взять здесь:  http://www.pjrc.com/teensy/arduino_libraries/OneWire.zip

И еще о твердотельном реле

Использование твердотельного реле – отличный способ управления мощной нагрузкой при помощи микроконтроллера. В отличие от механических реле, оно не обладает подгорающими или приваривающимися при больших токах контактами, не щелкает при переключении, потребляет ток по цепи управления не более 25мА. Практика показала, что при мощностях 500-800 Ватт реле вполне может обойтись без радиатора, будучи установлено в коробку от розетки оно практически не нагревалось даже при длительной работе нагревателя. Реле имеет 4 винтовых клеммы. Две из них, помеченные знаком «~» включаются в разрыв цепи нагрузки (как обычный выключатель). Управляющий вход реле, обозначенный знаком «+» подключается напрямую к выходу Arduino. Для подключения был использован свободный пин D11. Вход, обозначенный знаком «–» подключается к контакту GND Arduino.

Схема подключения реле выглядит так:

Собранная силовая часть помещается в двух коробках от розеток для наружной проводки. В одной установлена розетка, в другой помещается реле. Кабель с вилкой подключается к источнику напряжения 220В, управляющий кабель подключается к контроллеру:

Собранная управляющая часть (пока без корпуса) выглядит вот таким образом (резистор и провода, которыми подключен термодатчик располагаются между «этажами» под платой LCD-keypadShield). Контроллер питается от отдельного блока питания 9-12В 1A. Датчик крепится в любом удобном месте, обеспечивающем адекватный замер температуры в комнате:

Немного о функциональности

Логика работы устройства предельно проста. Задаем некое значение температуры, которое хотим поддерживать, регулярно измеряем температуру в комнате, сравниваем значение с заданным – если текущая температура ниже заданной включаем реле, если выше – выключаем. Если температура равна заданной – ничего не делаем ;)) Измерения будем проводить с частотой 1 раз в секунду (в принципе можно и реже, это дело индивидуальное).

На дисплее будет постоянно отображаться текущая температура в комнате. Заданное значение температуры будет выводиться на дисплей по нажатию клавиши Select. При этом его можно будет редактировать клавишами (Up – увеличить на единицу, Down – уменьшить на единицу). Повторное нажатие Select – сохранение изменений, клавиша Right – отмена изменений. Для того, чтобы при отключении питания заданное значение не потерялось будем хранить его в EEPROM микроконтроллера. Для этого нам понадобится библиотека EEPROM2.h, которую можно скачать здесь:

http://www.freeduino.ru/arduino/files/EEPROM2.zip

Чтобы подсветка дисплея ночью не мешала спать сделаем ее отключаемой с клавиатуры (например при нажатии клавиши Up подсветка включается, при нажатии Down – выключается). Для управления подсветкой в LCD-keypad-Shield используется пин D10.

Вот, вроде бы, и все.

Программная часть

Разберем исходный текст программы:

// Подключаем библиотеку для работы с шиной OneWire
// Термометр будет подключен на Pin2
#include <OneWire.h>
OneWire oneWire(2);

//Подключаем библиотеку для работы с термометром
#include <DallasTemperature.h>

//Создаем объект sensors, подключенный по OneWire
DallasTemperature sensors(&oneWire);

//Создаем переменные для работы с термометром
DeviceAddress tempDeviceAddress;  //переменная для хранения адреса датчика
float temp1=0; //переменная для текущего значения температуры
int setTmp=0; // переменная для заданного значения температуры

//Подключаем LCD-дисплей
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//Подсветка управляется через пин D10
#define BACKLIGHT_PIN 10

//Создаем переменную для хранения состояния подсветки
boolean backlightStatus = 1;

// Подключаем библиотеку для работы с ARDUINO EEPROM
//Заданная температура будет храниться по адресу 0
#include <EEPROM2.h>

//Реле подключено к пину D11
#define RELAY_PIN 11

//Объявим переменную для хранения состояния реле
boolean relayStatus1=LOW;

//Объявим переменные для задания задержки
long previousMillis1 = 0;
long interval1 = 1000; // интервал опроса датчиков температуры

//Аналоговая клавиатура подключена к пину A0
#define KEYPAD_PIN A0
//Определим значения на аналоговом входе для клавиатуры 
#define ButtonUp_LOW 90
#define ButtonUp_HIGH 100
#define ButtonDown_LOW 240
#define ButtonDown_HIGH 280
#define ButtonLeft_LOW 390
#define ButtonLeft_HIGH 450
#define ButtonRight_LOW 0
#define ButtonRight_HIGH 50
#define ButtonSelect_LOW 620
#define ButtonSelect_HIGH 650

void setup() {

//Настроим пин для управления реле
  pinMode(RELAY_PIN,OUTPUT);
  digitalWrite(RELAY_PIN,LOW);

//Считаем из постоянной памяти заданную температуру
  setTmp=EEPROM_read_byte(0);

//Инициализируем термодатчик и установим разрешающую способность 12 бит (обычно она установлена по умолчанию, так что последнюю строчку можно опустить)
  sensors.begin();
  sensors.getAddress(tempDeviceAddress, 0);
  sensors.setResolution(12);
    
//Настроим подсветку дисплея
  pinMode(BACKLIGHT_PIN, OUTPUT);
  digitalWrite(BACKLIGHT_PIN, backlightStatus);

//Выведем на дисплей стартовое сообщение на 2 секунды
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("Temp. Controller");
  lcd.setCursor(0, 1);
  lcd.print("      v1.0      ");
  delay(2000);

// выведем на дисплей заданное значение температуры на 2 секунды
  lcd.setCursor(0, 1);
  lcd.print("  Set temp:     ");
  lcd.setCursor(12,1);
  lcd.print(setTmp);
  delay(2000);

//Очистим дисплей
  lcd.begin(16, 2);
}



//Определим функцию для опроса аналоговой клавиатуры
//Функция опроса клавиатуры, принимает адрес пина, к которому подключена клавиатура, и возвращает код клавиши:
// 1 - UP
// 2 - DOWN
// 3 - LEFT
// 4 - RIGHT
// 5 - SELECT

int ReadKey(int keyPin)
{
 int KeyNum=0;
 int KeyValue1=0;
 int KeyValue2=0;

//Читаем в цикле аналоговый вход, для подавления дребезга и нестабильности читаем по два раза подряд, пока значения не будут равны.
//Если значения равны 1023 – значит не была нажата ни  одна клавиша.

do {
KeyValue1=analogRead(keyPin);
 KeyValue2=analogRead(keyPin);
 } while (KeyValue1==KeyValue2&&KeyValue2!=1023);

//Интерпретируем полученное значение и определяем код нажатой клавиши
 if (KeyValue2<ButtonUp_HIGH&&KeyValue2>ButtonUp_LOW) {KeyNum=1;}//Up
 if (KeyValue2<ButtonDown_HIGH&&KeyValue2>ButtonDown_LOW) {KeyNum=2;}//Down
 if (KeyValue2<ButtonLeft_HIGH&&KeyValue2>ButtonLeft_LOW) {KeyNum=3;}//Left
 if (KeyValue2<ButtonRight_HIGH&&KeyValue2>ButtonRight_LOW) {KeyNum=4;}//Right
 if (KeyValue2<ButtonSelect_HIGH&&KeyValue2>ButtonSelect_LOW) {KeyNum=5;}//Select

//Возвращаем код нажатой клавиши
return KeyNum;
}

//Определим процедуру редактирования заданной температуры
//Вызывается по нажатию клавиши Select, отображает на дисплее заданную температуру и позволяет изменять ее клавишами Up и Down

void setTemperature() {

  int keyCode=0;

//выводим на дисплей заданное значение температуры  
  lcd.begin(16,2);
  lcd.setCursor(0, 0);
  lcd.print("  Setting temp  ");
  lcd.setCursor(7, 1);
  lcd.print(setTmp);

//Опрашиваем клавиатуру, если нажата клавиша Up увеличиваем значение на 1, если Down – уменьшаем на 1
//Если нажаты клавиши Select или Right – цикл опроса прерывается
//Задержки введены для борьбы с дребезгом, если клавиши срабатывают четко – можно уменьшить время задержек или вообще их убрать
do {
  keyCode=ReadKey(KEYPAD_PIN);
  if (keyCode==1){setTmp++;delay(200);lcd.setCursor(7, 1);lcd.print(setTmp);}
  if (keyCode==2){setTmp--;delay(200);lcd.setCursor(7, 1);lcd.print(setTmp);}
} while (keyCode!=5 && keyCode!=4);
  delay(200);

//По клавише Select – созраняем в EEPROM измененное значение
//По клавише Right – восстанавливаем старое значение
if (keyCode==5) {EEPROM_write_byte(0, setTmp);}
if (keyCode==4) {setTmp = EEPROM_read_byte(0);}
}

void loop() {

//Модуль опроса датчиков и получения сведений о температуре
//Вызывается 1 раз в секунду
  unsigned long currentMillis1 = millis();
if(currentMillis1 - previousMillis1 > interval1) {
    previousMillis1 = currentMillis1;  

//Запуск процедуры измерения температуры
  sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  sensors.setWaitForConversion(true);

Delay(750) // задержка для обработки информации внутри термометра, в данном случае можно не задавать

//Считывание значения температуры
  sensors.getAddress(tempDeviceAddress, 0);
  temp1=sensors.getTempC(tempDeviceAddress);

// Вывод текущего значения температуры на дисплей
  lcd.setCursor(0, 0);
  lcd.print("  Current temp  ");
  lcd.setCursor(5, 1);
  lcd.print(temp1);
//  Serial.println(temp1,4);
}

//Проверка условия включения/выключения нагревателя
if (temp1<setTmp&&relayStatus1==LOW){relayStatus1=HIGH; digitalWrite(RELAY_PIN,HIGH);}
if (temp1>setTmp&&relayStatus1==HIGH){relayStatus1=LOW; digitalWrite(RELAY_PIN,LOW);}

// Опрос клавиатуры 
int Feature = ReadKey(KEYPAD_PIN);
if (Feature==1 ) {backlightStatus=1;digitalWrite(BACKLIGHT_PIN, backlightStatus);} //Включение подсветки
if (Feature==2 ) {backlightStatus=0;digitalWrite(BACKLIGHT_PIN, backlightStatus);} //Отключение подсветки
if (Feature==5 ) {delay(200);setTemperature();} //Переход к редактированию заданной температуры
}

Вот, собственно и все. В заключение – как всегда несколько полезных ссылок:

Страница про LCD Keypad Shield от DFRobot:

http://www.dfrobot.com/index.php?route=product/product&product_id=51#.UZn1u0oUjTo

Даташит на Dallas DS18B20 на русском от Геннадия Чернова:

http://invent-systems.narod.ru/DS18B20.htm

Фирменный datasheet на Dallas DS18B20:

http://www.alldatasheet.com/datasheet-pdf/pdf/58557/DALLAS/DS18B20.html

Страничка о библиотеке DallasTemperature.h:

http://milesburton.com/Dallas_Temperature_Control_Library

Страничка о библиотеке OneWire.h:

http://www.pjrc.com/teensy/td_libs_OneWire.html

Параметры твердотельных реле фирмы Fotek:

http://www.fotek.com.hk/solid/SSR-1.htm

Еще одна страничка про твердотельные реле:

http://www.futurlec.com/Relays/SSR25A.shtml

Страничка про EEPROM от Freeduino.ru:

http://freeduino.ru/arduino/sample_EEPROM.html

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Раз в секунду для термометра - достаточно часто, я бы делал раз в 10 секунд...

Что будет делать система,

- если датчик оборвался или вышел из строя

- включили нагрев, а температура - не поднимается

pencraft
Offline
Зарегистрирован: 17.01.2012

Andrey_Y_Ostanovsky пишет:

Раз в секунду для термометра - достаточно часто, я бы делал раз в 10 секунд...

Да, пожалуй стоит увеличить, может быть не каждые 10 сек, а каждые 5. Это делается легко, увеличением значения переменной interval1

Andrey_Y_Ostanovsky пишет:

Что будет делать система,

- если датчик оборвался или вышел из строя

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

Andrey_Y_Ostanovsky пишет:

- включили нагрев, а температура - не поднимается

А вот это интересно... Такая ситуация действительно может возникнуть, но я не очень понимаю что надо делать в этом случае. Есть какие-нибудь идеи?

demon969
Offline
Зарегистрирован: 24.04.2012

Жду следующую версию! Почерпнул для себя кое что из вашего кода.

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

pencraft пишет:

Andrey_Y_Ostanovsky пишет:

- включили нагрев, а температура - не поднимается

А вот это интересно... Такая ситуация действительно может возникнуть, но я не очень понимаю что надо делать в этом случае. Есть какие-нибудь идеи?

Ну, можно попробовать как-то так: создаем массив "нагреватель включен", в который при включении пишется текущий millis() и температура. Через определенный период (минут 5 - 10) проверяем температуру. Если она не отличается от исходной (или отличается, но меньше заданной дельты) - надо что-то делать: либо сразу выходить в аварию, либо подождать еще пару циклов, либо попытаться повторно включить нагреватель... Вобщем тут целое поле для деятельности в зависимости от "уровня значимости объекта". Как минимум, стоит взвести бит ошибки в массиве "нагреватель включен".

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

spa-sam
Offline
Зарегистрирован: 14.12.2012

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

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

spa-sam пишет:

Может я пропустил...но... где зона возврата по темпереатуре?

Гистерезис там и так получится за счет инерции объема и правильно подобранных интервалов замера температуры.

spa-sam
Offline
Зарегистрирован: 14.12.2012

Смутные сомнения по этому поводу...интервал 1 сек...это почти ничего...минута ещё куда не шло...Вы наблюдали за температурой...нет  колебания?...тем более при таком объеме

pencraft
Offline
Зарегистрирован: 17.01.2012

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

pencraft
Offline
Зарегистрирован: 17.01.2012

Andrey_Y_Ostanovsky пишет:

Ну, можно попробовать как-то так: создаем массив "нагреватель включен", в который при включении пишется текущий millis() и температура. Через определенный период (минут 5 - 10) проверяем температуру. Если она не отличается от исходной (или отличается, но меньше заданной дельты) - надо что-то делать: либо сразу выходить в аварию, либо подождать еще пару циклов, либо попытаться повторно включить нагреватель... Вобщем тут целое поле для деятельности в зависимости от "уровня значимости объекта". Как минимум, стоит взвести бит ошибки в массиве "нагреватель включен".

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

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

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

pencraft пишет:

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

Значит надо либо температуру за окном мерять (1-wire это позволяет), либо не отключаться, но подавать звуковой сигнал, например... Тут Вам, как автору проекта, и карта в руки. :)

spa-sam
Offline
Зарегистрирован: 14.12.2012

Вопрос о контроле вкл или выкл обогреватель можно решить установкой датчика тока....вот, например, такие....

http://www.ebay.com/itm/SCT-013-020-20A-Non-invasive-AC-current-sensor-Split-Core-Current-Transformer-/200872512212?pt=BI_Circuit_Breakers_Transformers&hash=item2ec4ef4ad4

или попроще...http://www.ebay.com/itm/ACS712ELCTR-30A-AC-and-DC-30A-range-Current-Sensor-Module-/281089415233?pt=LH_DefaultDomain_0&hash=item41723c1841

 
pencraft
Offline
Зарегистрирован: 17.01.2012

Тогда получается следующее:

Оборудовать контроллер "пишалкой" и выдавать звуковой сигнал в случае неисправности:

1. Если оборвался или не отвечает датчик температуры - отключить нагрев, выдать сообщение "датчик не отвечает/отсутствует" и будить хозяина громким писком.

2. Если, скажем, за полчаса температура не достигнута - продолжать греть, но выдать сообщение об ошщибке и, опять же, пищалку - типа "нагрев недостаточен, проснись и растопи печку, а то замерзнешь" ;))

3. Датчиком тока отслеживать исправность цепи нагревателя. Если сгорел нагреватель - т. е. реле включено, но ток в нагрузке подозрительно мал - отключить реле и сообщить о неисправности в цепи нагревателя. А на датчике тока можно, наверное, сделать еще защиту от перегрузки по току.

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

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

pencraft пишет:

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

Смотря как меряете ток. Обычно, ток меряется шунтом, и основной ток идет именно по шунту...

spa-sam
Offline
Зарегистрирован: 14.12.2012

http://www.ebay.com/itm/SCT-013-020-20A-Non-invasive-AC-current-sensor-Split-Core-Current-Transformer-/200872512212?pt=BI_Circuit_Breakers_Transformers&hash=item2ec4ef4ad4 ... это токовый трансформатор

http://www.ebay.com/itm/ACS712ELCTR-30A-AC-and-DC-30A-range-Current-Sensor-Module-/281089415233?pt=LH_DefaultDomain_0&hash=item41723c1841   ....этот на эффекте Холла...

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

 

vvadim
Offline
Зарегистрирован: 23.05.2012

pencraft молодец - чётко и ясно расписал проект и думаю многим , кто захочет заняться проектами типа умный дом, это пригодится.

Но в подобных разработках момент номер один - техника безопасности ( ну чтобы утром проснуться ) и здесь надо довести до ума.

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

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Вот что-то с этим у меня не пошло на переменке... То-ли нагрузка (лампочка 100 ватт) для 30 амперника оказалась за пределами чувствительности измерения, то-ли одно из двух... Надо будет попробовать с чем-нибудь более мощным.

Сейчас специально собрал схему с утюгом - не меряет (на выходе все время стоит 2.5 вольта), хотя утюг исправно греется и потребляет, судя по клещам, 4.8 ампера. От 12V аккумулятора с той же схемой видно, что вольтметр пытается изменить показания, т.е. постоянку - меряет...

Bulatovor
Offline
Зарегистрирован: 01.03.2013

Andrey_Y_Ostanovsky пишет:

Вот что-то с этим у меня не пошло на переменке... То-ли нагрузка (лампочка 100 ватт) для 30 амперника оказалась за пределами чувствительности измерения, то-ли одно из двух... Надо будет попробовать с чем-нибудь более мощным.

У меня была аналогичная ситуация - только лампочка на 40 ватт, а датчик на 20. Но пока исправить это руки не дошли - отложил и жду 5 амперные. Но как вариант есть попробоварь AREF подтянуть к встроенному  напряжению 1.1V через analogReference(), чувствительность должна увеличиться в пять раз с таким же уменьшением максимальной силы тока(он не сгорить просто за пределами будет всегда 1024)

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Bulatovor пишет:

У меня была аналогичная ситуация - только лампочка на 40 ватт, а датчик на 20. Но пока исправить это руки не дошли - отложил и жду 5 амперные. Но как вариант есть попробоварь AREF подтянуть к встроенному  напряжению 1.1V через analogReference(), чувствительность должна увеличиться в пять раз с таким же уменьшением максимальной силы тока(он не сгорить просто за пределами будет всегда 1024)

Да не, чувствительность АЦП тут не при чем. Я даже брал вольтметр с тремя знаками после запятой, и на нем видел 2.50[678], т.е. изменение в третьем знаке, что больше похоже на шум...

Snubist
Offline
Зарегистрирован: 18.02.2013

 

Andrey_Y_Ostanovsky пишет:

Да не, чувствительность АЦП тут не при чем. Я даже брал вольтметр с тремя знаками после запятой, и на нем видел 2.50[678], т.е. изменение в третьем знаке, что больше похоже на шум...

Ну с этим то как раз просто. Дело в том что, раз датчик меряет в обоих направлениях, то в нулевой точке он на выходе будет выдавать 2,5В (Vcc/2) Буквально из даташита ( Quiescent output voltage (VIOUT(Q)). The output of the device when the primary current is zero. For a unipolar supply voltage, it nominally remains at VCC ⁄ 2. Thus, VCC = 5 V translates into VIOUT(Q) = 2.5 V.)

Так что при измерении переменки, при одной полуволне, он выдает больше 2,5 а при отрицательной на столько же мВ меньше 2,5. А среднеквадратичное улавливаемое приборами так и будет 2,5.

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

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Вот тема по датчику тока acs712 даже с фрагментами кода под переменку: http://cyber-place.ru/showthread.php?t=570&highlight=acs712

XAM
Offline
Зарегистрирован: 01.03.2014

Я переделал скетч под нагревательный котел с 3 мя тэнами. При включений с интервалом включаюся 3 группы тэнов с интервалом в полсекунды. При наборе заданной температуры выключаются. Как температура падает на 1 градус 1 тэн включается ,на 2-2 тэна ,на 3-3 тэна.И еще подцепил второй датчик для информаций улицы например.  Как ограничить возможную уставку температуры например от 5 до 75 и сделать шаг уставки 5.

// Подключаем библиотеку для работы с шиной OneWire
// Термометр будет подключен на Pin2
#include <OneWire.h>
OneWire oneWire(2);

//Подключаем библиотеку для работы с термометром
#include <DallasTemperature.h>

//Создаем объект sensors, подключенный по OneWire
DallasTemperature sensors(&oneWire);

//Создаем переменные для работы с термометром
DeviceAddress temp1DeviceAddress;//переменная для хранения адреса датчика
DeviceAddress temp2DeviceAddress; //второй информационный датчик 
float temp1=0; //переменная для текущего значения температуры 1
float temp2=0; //переменная для текущего значения температуры 2
int setTmp=0; // переменная для заданного значения температуры

//Подключаем LCD-дисплей
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//Подсветка управляется через пин D10
#define BACKLIGHT_PIN 10

//Создаем переменную для хранения состояния подсветки
boolean backlightStatus = 1;

// Подключаем библиотеку для работы с ARDUINO EEPROM
//Заданная температура будет храниться по адресу 0
#include <EEPROM2.h>

//Реле подключено к пину D11
#define RELAY1 11
#define RELAY2 12
#define RELAY3 13
//Объявим переменную для хранения состояния реле
boolean relayStatus1=LOW;
boolean relayStatus2=LOW;
boolean relayStatus3=LOW;

//Объявим переменные для задания задержки
long previousMillis1 = 0;
long interval1 = 2000; // интервал опроса датчиков температуры

//Аналоговая клавиатура подключена к пину A0
#define KEYPAD_PIN A0
//Определим значения на аналоговом входе для клавиатуры 
#define ButtonUp_LOW 140
#define ButtonUp_HIGH 145
#define ButtonDown_LOW 324
#define ButtonDown_HIGH 330
#define ButtonLeft_LOW 503
#define ButtonLeft_HIGH 509
#define ButtonRight_LOW 0
#define ButtonRight_HIGH 5
#define ButtonSelect_LOW 740
#define ButtonSelect_HIGH 745

void setup() {

//Настроим пин для управления реле
  pinMode(RELAY1,OUTPUT);
  pinMode(RELAY2,OUTPUT);
  pinMode(RELAY3,OUTPUT);
  digitalWrite(RELAY1,LOW);
  digitalWrite(RELAY2,LOW);
  digitalWrite(RELAY3,LOW);

//Считаем из постоянной памяти заданную температуру
  setTmp=EEPROM_read_byte(0);
  
//Инициализируем термодатчик и установим разрешающую способность 12 бит 
//(обычно она установлена по умолчанию, так что последнюю строчку 
//можно опустить)
  sensors.begin();
  sensors.getAddress(temp1DeviceAddress, 0);
  temp1=sensors.getTempC(temp1DeviceAddress);
  sensors.getAddress(temp2DeviceAddress, 1);
  temp2=sensors.getTempC(temp2DeviceAddress);

 // sensors.setResolution(12);
    
//Настроим подсветку дисплея
  pinMode(BACKLIGHT_PIN, OUTPUT);
  digitalWrite(BACKLIGHT_PIN, backlightStatus);

//Выведем на дисплей стартовое сообщение на 2 секунды
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("Temp. Controller");
  lcd.setCursor(0, 1);
  lcd.print("      v1.1      ");
  delay(1500);

// выведем на дисплей заданное значение температуры на 2 секунды
  lcd.setCursor(0, 1);
  lcd.print("  Set temp:     ");
  lcd.setCursor(12,1);
  lcd.print(setTmp);
  delay(1500);

//Очистим дисплей
  lcd.begin(16, 2);
}

//Определим функцию для опроса аналоговой клавиатуры
//Функция опроса клавиатуры, принимает адрес пина, к которому 
//подключена клавиатура, и возвращает код клавиши:
// 1 - UP
// 2 - DOWN
// 3 - LEFT
// 4 - RIGHT
// 5 - SELECT

int ReadKey(int keyPin)
{
 int KeyNum=0;
 int KeyValue1=0;
 int KeyValue2=0;

//Читаем в цикле аналоговый вход, для подавления дребезга 
//и нестабильности читаем по два раза подряд, пока значения 
//не будут равны.
//Если значения равны 1023 – значит не была нажата ни  одна клавиша.

do {
KeyValue1=analogRead(keyPin);
 KeyValue2=analogRead(keyPin);
 } while (KeyValue1==KeyValue2&&KeyValue2!=1023);

//Интерпретируем полученное значение и определяем код нажатой клавиши
 if (KeyValue2<ButtonUp_HIGH&&KeyValue2>ButtonUp_LOW) {KeyNum=1;}//Up
 if (KeyValue2<ButtonDown_HIGH&&KeyValue2>ButtonDown_LOW) {KeyNum=2;}//Down
 if (KeyValue2<ButtonLeft_HIGH&&KeyValue2>ButtonLeft_LOW) {KeyNum=3;}//Left
 if (KeyValue2<ButtonRight_HIGH&&KeyValue2>ButtonRight_LOW) {KeyNum=4;}//Right
 if (KeyValue2<ButtonSelect_HIGH&&KeyValue2>ButtonSelect_LOW) {KeyNum=5;}//Select

//Возвращаем код нажатой клавиши
return KeyNum;
}

//Определим процедуру редактирования заданной температуры
//Вызывается по нажатию клавиши Select, отображает на дисплее 
//заданную температуру и позволяет изменять ее клавишами Up и Down

void setTemperature() 

{
 lcd.begin(16,2);
  int keyCode=0;

//выводим на дисплей заданное значение температуры  

  lcd.setCursor(0, 0);
  lcd.print("  Setting temp  ");
  lcd.setCursor(7, 1);
  lcd.print(setTmp);

//Опрашиваем клавиатуру, если нажата клавиша Up увеличиваем 
//значение на 1, если Down – уменьшаем на 1
//Если нажаты клавиши Select или Right – цикл опроса прерывается
//Задержки введены для борьбы с дребезгом, если клавиши срабатывают 
//четко – можно уменьшить время задержек или вообще их убрать
  do 
  {
  keyCode=ReadKey(KEYPAD_PIN);
  if (keyCode==1){setTmp++;delay(150);lcd.setCursor(7, 1);lcd.print(setTmp);}
  if (keyCode==2){setTmp--;delay(150);lcd.setCursor(7, 1);lcd.print(setTmp);}
  //lcd.begin(16,2);
} 
  while (keyCode!=5 && keyCode!=4);
  
  delay(200);
//lcd.begin(16, 2);
//По клавише Select – созраняем в EEPROM измененное значение
//По клавише Right – восстанавливаем старое значение
if (keyCode==5) {EEPROM_write_byte(0, setTmp);}
if (keyCode==4) {setTmp = EEPROM_read_byte(0);}
}

void loop() {

//Модуль опроса датчиков и получения сведений о температуре
//Вызывается 1 раз в секунду
  unsigned long currentMillis1 = millis();
if(currentMillis1 - previousMillis1 > interval1) {
    previousMillis1 = currentMillis1;  

//Запуск процедуры измерения температуры
 // sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  //sensors.setWaitForConversion(true);

//Delay(250) // задержка для обработки информации внутри термометра, 
//в данном случае можно не задавать

//Считывание значения температуры
  sensors.getAddress(temp1DeviceAddress, 0);
  temp1=sensors.getTempC(temp1DeviceAddress);
  sensors.getAddress(temp2DeviceAddress, 1);
  temp2=sensors.getTempC(temp2DeviceAddress);

// Вывод текущего значения температуры на дисплей
  lcd.setCursor(0, 0);
  lcd.print("Current");
  lcd.setCursor(0, 1);
  lcd.print("T=");
  lcd.print(temp1);
  lcd.print("C");
  lcd.setCursor(9, 1);
  lcd.print("Ty");
  lcd.print(temp2);

}

//Проверка условия включения/выключения нагревателя
if (temp1<setTmp&&relayStatus1==LOW)
{relayStatus1=HIGH; digitalWrite(RELAY1,HIGH);
lcd.setCursor(10, 0);
lcd.print("P= 35%");}
if (temp1>setTmp&&relayStatus1==HIGH)
{relayStatus1=LOW; digitalWrite(RELAY1,LOW);
lcd.setCursor(10, 0);
lcd.print("P=  0%");
}
delay(500);
if (temp1+1<setTmp&&relayStatus2==LOW)
{relayStatus2=HIGH; digitalWrite(RELAY2,HIGH);
lcd.setCursor(10, 0);
lcd.print("P= 70%");}
if (temp1>setTmp&&relayStatus2==HIGH)
{relayStatus2=LOW; digitalWrite(RELAY2,LOW);
//lcd.begin(10, 2);
}
delay(500);
if (temp1+2<setTmp&&relayStatus3==LOW)
{relayStatus3=HIGH; digitalWrite(RELAY3,HIGH);
lcd.setCursor(10, 0);
lcd.print("P=100%");}
if (temp1>setTmp&&relayStatus3==HIGH)
{relayStatus3=LOW; digitalWrite(RELAY3,LOW);
//lcd.begin(16, 2);
}
delay(200);
// Опрос клавиатуры 
int Feature = ReadKey(KEYPAD_PIN);

//Включение подсветки
if (Feature==1 ) 
{backlightStatus=1;digitalWrite(BACKLIGHT_PIN, backlightStatus);} 

//Отключение подсветки
if (Feature==2 ) 
{backlightStatus=0;digitalWrite(BACKLIGHT_PIN, backlightStatus);} 

 //Переход к редактированию заданной температуры
if (Feature==5 ) 
{delay(200);setTemperature();
}
}

 

 

Diemon
Offline
Зарегистрирован: 18.11.2011

Автор, у меня вопрос по поводу LCD+keypad Shield`а. Засматриваюсь на него.

Смущает одно: есть для него какой-то корпус, или накладки на кнопки? Чтобы как-то красиво было.

XAM
Offline
Зарегистрирован: 01.03.2014

Вопрос хороший! Я как то не задумывался на эту тему поскольку пока всё в отладке. Щас искал не нашел не чего похожего :-(

Diemon
Offline
Зарегистрирован: 18.11.2011

Вот именно. Мне тоже интересно, как будут нажиматься кнопки, когда устройство будет "законченным". Может какие-то "удлинители"?

XAM
Offline
Зарегистрирован: 01.03.2014

На ebay есть кепочки (caps) но они походу короткие , дисплей больше торчит  и эти кнопки будут надо будет перепаять на длинные и надеть их. Смысл? проще на макетке сразу кнопки спаять.

Diemon
Offline
Зарегистрирован: 18.11.2011

XAM пишет:

На ebay есть кепочки (caps) но они походу короткие , дисплей больше торчит  и эти кнопки будут надо будет перепаять на длинные и надеть их. Смысл? проще на макетке сразу кнопки спаять.

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

veer1234
Offline
Зарегистрирован: 07.05.2013

Такой вопрос - почему то интервал задания температуры  setting temp получился у меня от 100 до 990? в вашем скэтче ниего не менял. 

XAM
Offline
Зарегистрирован: 01.03.2014

Там прокрутил как то то-ли вверх то-ли вниз и от 0 стало не могу до конца понять как сделать ограничение например от 0 до 80 понял что скорей сдесь нокак и шаг изменить с 1 на 5

168   if (keyCode==1){setTmp++;delay(150);lcd.setCursor(7, 1);lcd.print(setTmp);}
169   if (keyCode==2){setTmp--;delay(150);lcd.setCursor(7, 1);lcd.print(setTmp);}

 

veer1234
Offline
Зарегистрирован: 07.05.2013

Прокрутка вперед помогла, а так как сет температуры ставится один раз, то не мешает никак. Спасибо огромное.

knack
knack аватар
Offline
Зарегистрирован: 27.03.2012

pencraft пишет:

Andrey_Y_Ostanovsky пишет:

Ну, можно попробовать как-то так: создаем массив "нагреватель включен", в который при включении пишется текущий millis() и температура. Через определенный период (минут 5 - 10) проверяем температуру. Если она не отличается от исходной (или отличается, но меньше заданной дельты) - надо что-то делать: либо сразу выходить в аварию, либо подождать еще пару циклов, либо попытаться повторно включить нагреватель... Вобщем тут целое поле для деятельности в зависимости от "уровня значимости объекта". Как минимум, стоит взвести бит ошибки в массиве "нагреватель включен".

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

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

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

knack
knack аватар
Offline
Зарегистрирован: 27.03.2012

Такой вопрос, если взять отдельное помещение в доме, котел один, комнат несколько. Как правильно организовать подключение батарей через клапан с байпасом или плавное регулирование подачи/обратки в батарее и как боротся с инеркцией?

С плавным регулированием: Допустим отопление включили с 0ля. в помещении +15 а надо +21, открываем батарею на 50%, ждём 15, 30, сколько? мин. Поднялось до 20, расчитываем 20-15=5 10%=1градус, открываем еще на 10%. А вот потдерживание самой температуры, недогоняю алгоритма, у кого есть такой опыт?

С клапанами проще, догнали температуру, закрыли батарею, открыли байпас, только клапанов надо в 2 раза больше.

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

knack пишет:

А вот потдерживание самой температуры, недогоняю алгоритма, у кого есть такой опыт?

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

Т.е. вручную меряем реакцию, например, температура сдвинулась вниз на один градус - открываем клапан 100% на 3 минуты ("экспозиция"), ждем отклика температуры - через сколько минут он появится на термометрах. Зная это отставание для разных по времени "экспозиций" - мы уже можем прогнозировать поведение системы. Можете поэкспериментировать с неполным открытием - но, на мой взгляд, показания будут сильно плавать.

knack пишет:

С клапанами проще, догнали температуру, закрыли батарею, открыли байпас, только клапанов надо в 2 раза больше.

Зачем все время открывать/закрывать байпас - по-уму он должен работать на проток всегда :) Слегка прижмите его вентилем, а ходы на комнаты - открывайте и закрывайте клапанами - за счет разности гидравлических сопротивлений поток горячей воды все равно в комнаты попадет. Я надеюсь, байпас у Вас классический: в конце трубы, а не в ее начале?

knack
knack аватар
Offline
Зарегистрирован: 27.03.2012

Andrey_Y_Ostanovsky пишет:

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

Т.е. вручную меряем реакцию, например, температура сдвинулась вниз на один градус - открываем клапан 100% на 3 минуты ("экспозиция"), ждем отклика температуры - через сколько минут он появится на термометрах. Зная это отставание для разных по времени "экспозиций" - мы уже можем прогнозировать поведение системы. Можете поэкспериментировать с неполным открытием - но, на мой взгляд, показания будут сильно плавать.

Как то всё очень туманно, комната не в вакууме находится, т.е. будет влият температура за бортом, ветер, солнце. Мне кажется есть более универсальный вариант, формула.

Andrey_Y_Ostanovsky пишет:

Зачем все время открывать/закрывать байпас - по-уму он должен работать на проток всегда :) Слегка прижмите его вентилем, а ходы на комнаты - открывайте и закрывайте клапанами - за счет разности гидравлических сопротивлений поток горячей воды все равно в комнаты попадет. Я надеюсь, байпас у Вас классический: в конце трубы, а не в ее начале?

А почему он байпас должен всегда быть открытым? И чем чревато байпас в начале трубы?

Планируется подключение через гребёнку 

вход в гребенку 3/4, выход на батареи 1/2, байпас 1/4(что бы весь теплоноситель не перегонялся через байпас, но и не росло давление на напоре).

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

knack пишет:

Как то всё очень туманно, комната не в вакууме находится, т.е. будет влият температура за бортом, ветер, солнце. Мне кажется есть более универсальный вариант, формула.

Будет формула, просто коэффициенты в ней будут уникальны для Вашего помещения и дельты температур между помещением и улицей. :)

knack пишет:

А почему он байпас должен всегда быть открытым? И чем чревато байпас в начале трубы?

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

knack пишет:

Планируется подключение через гребёнку : вход в гребенку 3/4, выход на батареи 1/2, байпас 1/4(что бы весь теплоноситель не перегонялся через байпас, но и не росло давление на напоре).

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

knack
knack аватар
Offline
Зарегистрирован: 27.03.2012

Так вот дельта бесполезна, т.к. наличие ветера, его скорость могут менять ее и вся эта махина будет неповоротлива и с мегоэнеркцией. Единственное на ум приходит такой же алгоритм как и с клапанами, 100% догнали нужную температуру, закрываем до 30% или ксолько там...

На счёт байпаса, я думал ставить на каждую ветку но с малым диаметром, что бы все открытые байпасы пропускали 3/4 выхода с котла, т.к. при резком потеплении скорее всего перекроются все батареи в доме. А больший диаметр байпаса, теплоноситель будет идти по меньшему сопротивлению, по малому кругу, а остальные ветки будут остывать.

Кстати как поведет себя котел, если всас, напор закольцевать, в ерор не упадёт?

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011
int ReadADCButtons()
{
  int adc_key_in = analogRead(KEYBOARDPIN);      // read the value from the sensor
  if (adc_key_in > 1000) return BUTTONNONE;    // We make this the 1st option for speed reasons since it will be the most likely result
  // Serial.println (adc_key_in);
  if (adc_key_in < 50)   return BUTTONRIGHT;   //   0 My
  if (adc_key_in < 220)  return BUTTONUP;      // 130 My
  if (adc_key_in < 380)  return BUTTONDOWN;    // 307 My
  if (adc_key_in < 555)  return BUTTONLEFT;    // 480 My
  if (adc_key_in < 900)  return BUTTONSELECT;  // 720 My
  return BUTTONNONE;  // when all others fail, return this...
}

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

knack пишет:

Так вот дельта бесполезна, т.к. наличие ветера, его скорость могут менять ее и вся эта махина будет неповоротлива и с мегоэнеркцией. Единственное на ум приходит такой же алгоритм как и с клапанами, 100% догнали нужную температуру, закрываем до 30% или ксолько там...

Подразумевается, что помещение утеплено, и ветер его не продувает уж совсем насквозь. :) А дельта температур нужна для того, чтобы менять наклон кривых, т.е. при одном и том же падении температуры помещения на 1 градус при забортной 0 греем 3 минуты, при забортной минус 10 - 7 минут, ну вот как-то так... Понятно, что если температура помещения близка к заданной - надо вводить дополнительные поправки. Это надо сначала руками сделать, чтобы почувстствовать инерцию...

knack пишет:

Кстати как поведет себя котел, если всас, напор закольцевать, в ерор не упадёт?

А котлу-то какая разница? Проток есть, температура в заданном диапазоне. Это если котел-автомат и он сам умеет выключать и включать горелку.

mummykbf
Offline
Зарегистрирован: 28.03.2014

у меня несколько вопросов к автору проекта:

1. вот Вы приводите ссылку на библиотеку eeprom2, а Вы читали статью про eeprom на этом сайте? http://www.freeduino.ru/arduino/sample_EEPROM.html

можете объяснить почему именно вторая версия библиотеки? я приведу цитатуц из статьи:

  uint8_t counter; //Здесь будем хранить счетчик
  //uint8_t - тип данных, занимающий 1 байт,
  //и хранящий значение от 0 до 255

 counter = EEPROM.read(0);

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

2. использование влагозащищенного исполнения ds18b20 для измерения температуры воздуха тоже мне кажется странным.

3. зачем Вам 12-битное разрешение при измерении? или это копи-паст откуда то?

4. измерение температуры воздуха в помещении 1 раз в секунду (даже 1 раз в 10 секунд) это слишком часто, по сути так часто это не ребуется. сейчас реализую проект управления электрическим котлом на даче, опрос темературы как мне кажется оптимальнее всего производить от 1 раза в минуту до 1 раза в пять минут, этого будет вполне достаточно. И обязательно надо задавать разброс по температуре на включение и выключение, иначе ТТР будет вкл и выкл то и дело - но это дело сугубо индивидуальное и зависит от утепленности дома.

 

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

mummykbf пишет:

2. использование влагозащищенного исполнения ds18b20 для измерения температуры воздуха тоже мне кажется странным.

А что тут странного? Самый дешевый ds18b20 в корпусе TO-92 - стоит около доллара, а влагозащищенный уже собранный в гильзу с метровым проводом - доллар восемьдесят... Тут, скорее, вопрос: во что Вы оцениваете свои трудозатраты по распайке и подготовке датчиков.

У меня, например, на балконе - влагозащищенный датчик, а тот, что с кухни: сам распаивал, т.к. было интересно. Если для следующего проекта потребуется десяток - мне проще заказать готовые.

knack
knack аватар
Offline
Зарегистрирован: 27.03.2012

Andrey_Y_Ostanovsky пишет:

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

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

mummykbf
Offline
Зарегистрирован: 28.03.2014

Andrey_Y_Ostanovsky пишет:

У меня, например, на балконе - влагозащищенный датчик, а тот, что с кухни: сам распаивал, т.к. было интересно. Если для следующего проекта потребуется десяток - мне проще заказать готовые.

Андрей, тогда Вам вопрос - не сравнивали показания датчиков? у меня почему то влагозащищенный врал на 1 градус. В схеме были одновременно подключены ds18b20 и dht22, и вариант два - ds18b20 (влагозащищенный) и dht22. так вот первый вариант показывают оба одинаковую температуру, а второй вариант врал на градус, дхт показывал 25,9 а 18В20 показывал при этом 27 градусов. на сколько это расхождение увеличится при нагреве остается только гадать.

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

mummykbf пишет:

Андрей, тогда Вам вопрос - не сравнивали показания датчиков?

Нет, специально - не сравнивал. Обычно они у меня показывают разную температуру, т.к. балкон снизу от соседей (у них застеклено) греется, а кухонный датчик отстоит от стены на 40 сантиметров и находится в тени здания. Весной и летом - солнце в первой половине дня греет балкон, а во второй половине дня засвечивает кухонный датчик: так например на 16:30 балконный показывает 11.76, а кухонный - 12.15, на 21:10 (солнца нет) балконный 9.49, кухонный - 8.79, китайский термометр там же - 6.6. Я думаю, что разброс между ds18b20 в разумных пределах, а вот китайский термометр на аналоге - врет на пару градусов в минус.

На работе примерно такая же ситуация: ds18b20 показывает температуру серверной правильно (как кондиционер настроен), а аналог - врет на пару градусов, и тоже в минус.

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

knack пишет:

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

Зря. Стоимость внешнего датчика - всего пара долларов.

Зато если уличная температура падает на 10 градусов за час - дом за счет инерции еще какое-то время будет держать, а потом пойдет достаточно резкое снижение температуры. С внешним датчиком - такого "провала" не произойдет. При резком потеплении - это позволит еще и на котле чуть раньше снизить температуру, т.е. экономии будет больше, чем на два доллара.

knack пишет:

А учитывать внешнюю температуру только на котле, как не регулируй радиаторы, а при -25 нужно больше калорий спалить чем при -5, чтоб обогреть дом.

Котел должен просто держать заданную температуру в контуре, и в его управление лучше домашней автоматикой влезать по минимуму.

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

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

Тут ПИД-регулятор должен помочь. Он удобен именно при меняющихся внешних параметрах. При правильно подобранных коэффициентах он быстро набирает заданный параметр и сводит к минимуму расколбас перерегулирования. Есть библиотека, работает, я в инкубаторах использую http://playground.arduino.cc/Code/PIDLibrary .  Инкубатор по сути дом с отоплением, только требования к стабильности температуры выше. Пол градуса уже много.  

XAM
Offline
Зарегистрирован: 01.03.2014

Выложи скетч как используеш. У меня не получается задавать десятые градуса и диференциал не могу меньше единицы сделать :-( вроде делаю числа с запятыми и уставку так два нуля и остаются а целые переключаются

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

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



#include <PID_v1.h>
#include <OneWire.h> 
#include <LiquidCrystal.h>
#include <DallasTemperature.h>

int heater = 13;                         // нагреватель на выводе 13
int nasos = 7;                           // насос на выводе 7
int vent = 10;
int alarm = 8;
int DS18S20_Pin = 2;                     // DS18S20 подключен к входу 2
int povorot = 9;                         // управление поворотом на выводе 9
unsigned long currentTime;// задаем переменные для тайминга поворота
unsigned long currentTime2;

unsigned long loopTime;
unsigned long loopTime2;

unsigned long serialTime; //this will help us know when to talk with processing

LiquidCrystal lcd(12, 11, 6, 5, 4, 3);  // инициализация библиотеки с перечнем задействованных выводов

OneWire oneWire(DS18S20_Pin);                // на цифровом пине 2
// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

DeviceAddress outsideThermometer = { 0x28, 0x7C, 0x08, 0xD0, 0x02, 0x00, 0x00, 0x8B };
DeviceAddress insideThermometer = { 0x28, 0x92, 0xE7, 0x85, 0x02, 0x00, 0x00, 0x5D };
//DeviceAddress dogHouseThermometer = { 0x28, 0x59, 0xBE, 0xDF, 0x02, 0x00, 0x00, 0x9F };
                                        //объявляем переменные для ПИД
double Setpoint, Input, Output;
double Setpoint2, Input2, Output2;
                                        //Инициализируем ПИД-библиотеку и коэффициенты
PID myPID(&Input, &Output, &Setpoint,10,4,3, DIRECT);
PID myPID2(&Input2, &Output2, &Setpoint2,10,4,3, DIRECT);

int WindowSize = 2000;                  // ширина окна терморегулятора 1 секунда.
int WindowSize2 = 6000;
unsigned long windowStartTime;
unsigned long windowStartTime2;



void setup()
{
   Serial.begin(9600);
  
  lcd.begin(16, 2);       // устанавливаем кол-во столбцов и строк
  // Start up the library
  sensors.begin();
  // set the resolution to 10 bit (good enough?)
  sensors.setResolution(insideThermometer, 12);
  sensors.setResolution(outsideThermometer, 12);
 // sensors.setResolution(dogHouseThermometer, 10);
  
  windowStartTime = millis();
  windowStartTime2 = millis();
                        
  //Setpoint = 31.5;        //задаем температуру стабилизации
  
                          //задаем лимиты ширины ПИД-импульса от 0 до 1 секунды.
  myPID.SetOutputLimits(0, WindowSize);
  myPID2.SetOutputLimits(2000, 5000);
                        
  myPID.SetMode(AUTOMATIC); //включаем ПИД-регулирование
  myPID2.SetMode(AUTOMATIC);
  
  myPID.SetSampleTime(2000);
  myPID2.SetSampleTime(6000);
  
   pinMode(povorot, OUTPUT);      // устанавливаем вывод 9 как выход
   pinMode(nasos, OUTPUT);
   
  currentTime = millis();  // считываем время, прошедшее с момента запуска программы
  currentTime2 = millis();
  
  loopTime = currentTime; 
  loopTime2 = currentTime2; 
}

void loop()
{
  
  //вычисляем температуру
 // Input = getTemp();
  myPID.Compute();
  myPID2.Compute();

  unsigned long now = millis();
  
  float temperature = sensors.getTempC(insideThermometer);
  float temperature2 = sensors.getTempC(outsideThermometer);
  Input = temperature;
  Input2 = temperature2;
  
  float resistor1Value = analogRead(A1);
  float resistor2Value = analogRead(A0);
  resistor1Value = map(resistor1Value, 0, 1023, 250, 390);
  resistor2Value = map(resistor2Value, 0, 1023, 250, 390);
     Setpoint = resistor1Value / 10;
     Setpoint2 = resistor2Value / 10;
  
  if(now - windowStartTime>WindowSize)
  {                       //время для перещелкивания периода окна
    windowStartTime += WindowSize;
    
     currentTime = millis();                           // считываем время, прошедшее с момента запуска программы
    
    sensors.requestTemperatures();
    //Serial.println(temperature);
    lcd.setCursor(0, 1);                 // устанавливаем курсор в 0-ом столбце, 1 строка (начинается с 0)
    lcd.print(temperature);              // печать температуры на дисплей
    //lcd.setCursor(9, 1);                // устанавливаем курсор в 9-ом столбце, 1 строка (начинается с 0)
    //lcd.print(Output); 
    lcd.setCursor(0, 0);    // устанавливаем курсор в 0-ом столбце, 1 строка (начинается с 0)
    //
    lcd.print(resistor1Value/10);
    lcd.setCursor(7, 1);
    lcd.print(sensors.getTempC(outsideThermometer)); 
    lcd.setCursor(7, 0);
    lcd.print(resistor2Value/10);
    
    
    
  }
  
  if(now - windowStartTime2>WindowSize2)
  {                       //время для перещелкивания периода окна
    windowStartTime2 += WindowSize2;
    
                                 
    sensors.requestTemperatures(); // считываем время, прошедшее с момента запуска программы
    //Serial.println(temperature);
    
    
   }
  
  
  
  if(currentTime >= (loopTime + 3600)){              // сравниваем текущий таймер с переменной loopTime + 3,6 секунды
    digitalWrite(povorot, !digitalRead(povorot));    // включаем/выключаем реле поворота
    loopTime = currentTime;                          // в loopTime записываем новое значение
    }
     
  if(Output > now - windowStartTime) digitalWrite(heater,HIGH);
  else digitalWrite(heater,LOW);
  
  
    
    if(Output2 > now - windowStartTime2){
     if (temperature>=resistor1Value/10-1) digitalWrite(nasos,HIGH);
    }
    else digitalWrite(nasos,LOW);
    
  
     if(millis()>1800){
     if (temperature>resistor1Value/10+1.2) 
       {
       digitalWrite(vent,HIGH);
       lcd.setCursor(14, 1);
       lcd.print("Vl");
       }
     
     else 
     {
       digitalWrite(vent,LOW);
       lcd.setCursor(14, 1);
       lcd.print("Vo");
     }
     if (temperature>resistor1Value/10+1 || temperature<resistor1Value/10-1)
     { 
       digitalWrite(alarm,HIGH);
       lcd.setCursor(14, 0);
       lcd.print("Al");
     }
     else {
       digitalWrite (alarm,LOW);
       lcd.setCursor(14, 0);
       lcd.print("Ao");
     }
     if (temperature2>resistor2Value/10+3 || temperature<resistor1Value/10-3) digitalWrite(alarm,HIGH);
     else digitalWrite (alarm,LOW);
    }
    else digitalWrite(vent,LOW);
  
  //send-receive with processing if it's time
  if(millis()>serialTime)
  {
    SerialReceive();
    SerialSend();
    serialTime+=500;
  }
  
}


/********************************************
 * 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");
}

 

XAM
Offline
Зарегистрирован: 01.03.2014

Спасибо !!!! Я как понял ты уставки задаеш с компьютерра

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

Не, переменным резистором на аналоговый вход и шкалу мапим в полезный диапазон значений (35-39 градусов). Чтобы шкоды не наделать при любом положении движка резистора.

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

А! Там ещё фронтэнд на процессинге есть, я готовый из комплекта библиотеки использовал, только диапазоны род себя подрегулировал. Вот оттуда можно прямо во время работы П И Д коэффициенты в Ардуину забрасывать. Но они временные конечно. Подобрать самые подходящие и вогнать железно в код. Удобно очень с визуализатором. ПИД то ещё колдунство!

XAM
Offline
Зарегистрирован: 01.03.2014

Прошу прощения за отступление от темы. Я логику работы до конца не могу понять.

Вопрос такой у тебя 2 датчика. 1 наружной температуры 2 внутри инкубатора? А насос как я понял на влажность? а влажность как  регулируется? за счёт чего он включается? и переворот я так понял идет отсчёт времени с момента включения. Я пытаюсь привязать к RTC1307 ни как не получается хотел чтоб после 17 дня переворот оключался