Термометр на DS18b20 и счетчик на прерываниях для солнечного коллектора

123ksn
Offline
Зарегистрирован: 24.11.2014

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

Решил сделать на ардуино уно управление системой циркуляции теплоносителя в солнечном коллекторе.
Общий алгоритм такой:
1)В главном цикле измеряю температуру с помощью двух датчиков DS18b20. Один установлен в солнечном коллекторе(Т1), второй в бойлере (теплообменнике)(Т2).
2)Вывожу значение температур на индикатор.
3)Если температура Т1>35гр.Ц. и Т1>Т2, включить циркуляционный насос(выставить HIGH на нужном выводе )
4)Если Т1>75гр.Ц., то авария - включить бипер, отправить сообщение.

5)Если выполнено условие п3, т.е. включился циркуляционный насос, то должны пойти импульсы со счетчика воды. Импульсы правильно считать в прерываниях. Если насос генерирует импульсы быстрее, чем проходит цикл измерения температуры, то наступает "забивание" программы в части измерения температуры.

Предполагаю, что я ошибаюсь. Вопрос: Возможно ли организовать гарантированное измерение температуры и подсчет импульсов? Замену датчиков DS18b20 и установку дополнительных элементов к Ардуино не предлагать! 

Спасибо за рабочий пример.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

У вас будет несеолько проблемм, перечислю их по увеличению сложности:

1. написать программу

2. собрать всё в корпусе.

3. сделать питание этому.

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

Ваше..... Если Т1>75гр.Ц., то авария - включить бипер, отправить сообщение......   нисколько не спасёт от закипания бойлера и куда вы тогда будете скидывать избыток энергии? Сливать кипяток в канализацию?

Ваше всё в принципе будет работать, но есть поговорка: Прибор должен работать не в принципе, а в кожухе.

Гляньте вот это, уже всё включенО, и система меню и нужные датчики и все реле-питание-корпус....

http://www.aliexpress.com/item/CE-approved-SR868C9-solar-heat-water-controller-for-heating-system110V-60Hz-220V-50Hz/879076716.html

Панель индикации выносится в любое удобное место по любому четырёхжильному (тонкому сигнализационному )кабелю.

при заказе не забудьте указать 220 Вольт !!!!!!!!!!!

123ksn
Offline
Зарегистрирован: 24.11.2014

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

https://github.com/adafruit/Adafruit-Flow-Meter/blob/master/Adafruit_Flo...

http://habrahabr.ru/post/232903/

http://cxem.net/arduino/arduino66.php

http://www.seeedstudio.com/wiki/index.php?title=G1/2_Water_Flow_sensor

но они меня не устраивают. 

P.S. trembo, не считайте себя самым умным. Это избавит от многих проблем в жизни.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

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

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

123ksn
Offline
Зарегистрирован: 24.11.2014

brokly пишет:

поскольку импульсы со счетчика по сравнению с циклом чтения температуры датчика достаточно редки

Из-за ошибочных предположений рождаются неверные решения.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Я не предполагаю. Я констатирую факт. Переменная лишь одна - исполнитель. Учитесь читать и понимать. А вот вы - 123ksn, явно вопросом не владеете. Так что может правильнее просто помолчать и подумать ?

А за "рабочим примером" вам сначала в карман за кошельком, а потом в "Ищу исполнителя" ;) Могу только лишь намекнуть, что прерывания по инциденту на ноге можно использовать как тригер, и считывать данные из этой псевдозащелки. Но это для понимающих НЕХАЛЯВЩИКОВ :) 

Как то неприятно встретить на просторах сети неконтролирующего себя неврастеника, который сначала просит помощи, а потом пытается укусить любого, кто высказывает хоть какую то мысль. Хрен ли тогда к форуму обращаться, лепил бы все сам и блестел бы в своей "безумной крутоте" :)

123ksn
Offline
Зарегистрирован: 24.11.2014

brokly пишет:

Как то неприятно встретить на просторах сети неконтролирующего себя неврастеника

1)Я Вас не оскорблял.

brokly пишет:

Я не предполагаю. Я констатирую факт.

2)Если бы Вы привели аргументацию, то возможно, был бы факт, а так, с точки зрения простого читателя, явно вопросом не владеющим, голословное утверждение.

brokly пишет:

а потом пытается укусить любого, кто высказывает хоть какую то мысль. 

3)Не надо "хоть какую то мысль".  Помощь - это когда помогают! С точки зрения инквизиторов, они тоже помогали грешникам.

Поэтому научитесь сначала читать вопросы, а потом, если есть, что сказать - говорите. Но уверен, что с этой мыслью Вы не согласны. 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

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

Не ждите от меня рыбы, я вам расскажу как сделать удочку. Кушать то, небось сами, будете ?

Каждый импульс счетчика это 1/1000 куба, иными словами 1 литр. В небольших системах отопления расход больший 50 литров в минуту - уже полный бред, поскольку ТН на больших потоках не сможет осуществлять вменяемый теплообмен, просто будет нагреваться вся система, то есть это никому не нужно. Поэтому циркуляционные насосы для непромышленных применений имеют производительность до 11 кубов в час, что составляет 183 литра в минуту (это насосы с резьбой более 1 дюйма). Примем такой расход за максимально возможный в системе. Таким образом максимальное количество импульсов выдаваемое счетчиком составит 3 в секунду, то есть период 0,34 секунды или 340 милисекунд или 340000 микросекунд. 

Если в задержке (кванте) протокола 1-wire встроить всего ОДИН опрос триггера прерывания, то мы сможем считать импульсы с периодом 15 микросекунд, что в 22500 раз больше. То есть реально можно, без потерь отследить поток 250000 кубов в час !!!!

Я бы смог реализовать это скажем за 300 долларов, а бесплатно могу только потрындеть. Это по вашему не хорошо ?

123ksn
Offline
Зарегистрирован: 24.11.2014

brokly пишет:
Изначально предполагалось, что вопрошающий "знает о чем говорит"

123ksn пишет:

Из-за ошибочных предположений рождаются неверные решения.

brokly пишет:

по "недалекой реплике"

Опять оскорбляете.

brokly пишет:

я понял, что он вовсе не понимает сути. 

Я написал бы "предположил". И опять: Из-за неправильного понимания рождаются неверные решения.

brokly пишет:

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

Это Вы поняли по одному посту? Да Вы, батенька, экстрасенс!

brokly пишет:

Каждый импульс счетчика это 1/1000 куба, иными словами 1 литр. В небольших системах отопления расход больший 50 литров в минуту - уже полный бред, поскольку ТН на больших потоках не сможет осуществлять вменяемый теплообмен, просто будет нагреваться вся система, то есть это никому не нужно. Поэтому циркуляционные насосы для непромышленных применений имеют производительность до 11 кубов в час, что составляет 183 литра в минуту (это насосы с резьбой более 1 дюйма). Примем такой расход за максимально возможный в системе. Таким образом максимальное количество импульсов выдаваемое счетчиком составит 3 в секунду, то есть период 0,34 секунды или 340 милисекунд или 340000 микросекунд.

Если в задержке (кванте) протокола 1-wire встроить всего ОДИН опрос триггера прерывания, то мы сможем считать импульсы с периодом 15 микросекунд, что в 22500 раз больше. То есть реально можно, без потерь отследить поток 250000 кубов в час !!!!

Тоесть, Вы действительно не понимаете русского языка? Читаем еще раз вопрос:

123ksn пишет:

Название темы: "Термометр на DS18b20 и счетчик на прерываниях для солнечного коллектора"

Решил сделать на ардуино уно.....

Вопрос: Возможно ли организовать гарантированное измерение температуры и подсчет импульсов? 

Вопрос предполагает два ответа: 1)На ардуино уно можно реализовать такое измерение

2)Средствами ардуино нельзя реализовать такое измерение.

Если бы кто-то предложил готовый пример, то я заранее сказал СПАСИБО.

В принципе, нужный мне код есть на Си здесь http://kazus.ru/forums/showthread.php?p=180411#post180411

brokly пишет:

бесплатно могу только потрындеть.

А вот и истинное лицо открылось. А столько "громких" слов было!!! 

 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

brokly пишет:

Можно...

Мой ответ на заданный вопрос. Первое слово в первом посте от меня. Так нет же, весь, блин, такой оскорбленный, решил поучить меня жизни. Некуда девать ума ? Или чем там вас распирает !? :) 

123ksn пишет:

Я написал бы "предположил". И опять: Из-за неправильного понимания рождаются неверные решения.

Вот и напишите, хрен ли требовать этого от меня !? Вы кто такой, что пытаетесь учить меня !?

И мое "истинное лицо" не скрывалось. Бесплатно работают только дураки. Я уже и так слишком много времени убил на троля. Плюйся дальше, троль :)

123ksn
Offline
Зарегистрирован: 24.11.2014

brokly пишет:

Бесплатно работают только дураки.

Вы так глобально мыслите, а я задаю такие маленькие конкретные вопросы, что боюсь ошибиться в предположениях, поэтому уточню. Это Вы о нем http://kazus.ru/forums/showthread.php?p=180411#post180411 ?

brokly пишет:

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

хрен ли требовать этого от меня !?

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

Я от Вас никогда ничего не требовал!!!

brokly пишет:

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

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

 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Бестолоч :)

123ksn
Offline
Зарегистрирован: 24.11.2014

Ура! Нашел интересующий меня пример кода для двух счетчиков воды (на прерываниях) и одного DS18b20 на Ардуино:  http://cyber-place.ru/showthread.php?t=1292  "Допилить" для нескольких уже не проблема.  И на этом форуме очень много полезной информации по Ардуино и не только. И денег никто не требует.

Всем успехов!

Скопировал код на всякий



// 1 пин со счетчика воды на +5v ( он белый)
// 3 пин(синий) на землю - без резистора там он уже есть
// 4 пин(красный) на pin2 и pin3 ардуина
/*
D0,D1 - RX TX
D2 - Счетчик горячей воды
D3 - Счетчик холодной воды
D7 - Dallas DS18B20 Датчики температуры
*/

#include <OneWire.h>
//#include <CyberLib.h>



////***********************************Переменные для температуры
OneWire ds(7); // Датчки температуры горячей воды
byte addr[8];
byte i;
byte present = 0;
byte data[12];
float celsius;

////***********************************Переменные для температуры

////***********************************Переменные для счетчика
int Count_Hot = 0;
int Count_Cold = 0;
int pin = 13;
volatile int x = LOW;
int millis_prev_1=254;//время на дребезг контактов (переключателя)с "потолка"
int millis_prev_2=254;//
////***********************************Переменные для счетчика

void setup()
{

Serial.begin(57600);


////***********************************Настройки для счетчика
pinMode(pin, OUTPUT);// пин как выход
attachInterrupt(0, add_Hot,CHANGE ); // привязываем 0-е прерывание к функции add_Hot().
attachInterrupt(1, add_Cold,CHANGE ); // привязываем 1-е прерывание к функции add_Cold().
////***********************************Настройки для счетчика

}

void loop()
{

if (Serial.available() > 0)
{
byte inByte = Serial.read();
switch (inByte)
{
case 49:
get_temp();
break;

case 50:
Serial.print("Cold:");
Serial.print(Count_Cold);
Serial.println();
break;

case 51:
Serial.print("Hot:");
Serial.print(Count_Hot);
Serial.println();
break;



}
}
}

void get_temp()
{
label:
if ( !ds.search(addr)) {
ds.reset_search();
delay(250);
return;
}
Serial.print("ROM=");
for( i = 0; i < 8; i++) {
Serial.print(addr[i], HEX);
}
ds.reset();
ds.select(addr);
ds.write(0x44, 1); // start conversion, with parasite power on at the end
delay(1000);
present = ds.reset();
ds.select(addr);

ds.write(0xBE);
for ( i = 0; i < 9; i++) {
data[i] = ds.read();
}

int16_t raw = (data[1] << 8) | data[0];
byte cfg = (data[4] & 0x60);
if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms
else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms

celsius = (float)raw / 16.0;
Serial.print(":");
Serial.print(celsius);
Serial.println();
goto label;
}

void add_Cold()
{
static unsigned long millis_prev_1;
if(millis()-100 > millis_prev_1)
Count_Cold=Count_Cold++;
millis_prev_1 = millis();

}
void add_Hot()
{
static unsigned long millis_prev_2;
if(millis()-100 > millis_prev_2) Count_Hot=Count_Hot++;
millis_prev_2 = millis();
}
прерывания сделаны с защитой от дребезга, что неправильно. С дребезгом надо бороться не в прерываниях!!! 

перезалил 12.12.2014 код, так как в нём была ошибка:static*unsigned*long*millis_prev_1; и не было объявления переменной millis_prev_1(2)

без борьбы с дребезгом код выглядит так:





//*******************************
void ColdWater() 
{ 
  Cold_Count++;  
} 

void HotWater() 
{ 
  Hot_Count++;
}  

Странно, brokly не плюсанули, а меня не минусонули.  Совсем традиции не хранят!

 

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

Главное - не забыть:

Цитата:
Большинство плат Arduino/Freeduino имеют два внешних прерывания с номерами 0 (на digital pin 2) и 1 (на digital pin 3). Arduino Mega имеет дополнительно ещё четыре:
с номерами 2 (pin 21), 3 (pin 20), 4 (pin 19) и 5 (pin 18).

123ksn
Offline
Зарегистрирован: 24.11.2014

А еще важно знать, что Arduino Mega2560 - это КАСТРАТ!!! Так как обрезали возможности МК atmega2560, не выведя выводы на разъемы в угоду совместимости шилдов и скетчей с УНО. Это право создателей! Но плохо, что об этом молчат при рекламе.

Radjah
Offline
Зарегистрирован: 06.08.2014

Паяльник с тонким жалом и тонки провода.

ВЕРНИ ЧИПУ ЯЙЦЫ!

123ksn
Offline
Зарегистрирован: 24.11.2014

Radjah пишет:

Паяльник с тонким жалом и тонки провода.

ВЕРНИ ЧИПУ ЯЙЦЫ!

1)Тогда зачем Ардуино? 

2)Не я "кастрировал". Читаем с включенными мозгами!!!

Radjah
Offline
Зарегистрирован: 06.08.2014

1) Че ты как неэлектрик ваще?

2) Ты первый.

com
Offline
Зарегистрирован: 06.09.2013

чего-то я не понял...

123ksn пишет:

Вопрос предполагает два ответа: 1)На ардуино уно можно реализовать такое измерение

2)Средствами ардуино нельзя реализовать такое измерение.

...

В принципе, нужный мне код есть на Си здесь http://kazus.ru/forums/showthread.php?p=180411#post180411

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

а чего хотел-то?

123ksn
Offline
Зарегистрирован: 24.11.2014

com пишет:

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

а чего хотел-то?

Неужели непонятно!? Помощи просил.

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

com
Offline
Зарегистрирован: 06.09.2013

понятно, что просил. непонятно зачем. неужели непонятно!?

впрочем, уже и не важно

123ksn
Offline
Зарегистрирован: 24.11.2014

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

Не нашел я пока для Ардуины УНО нормального счетчика с несколькими термометрами DS18b20. Код с моими объяснялками и результатами эксперимента привожу ниже.

Эксперимент:
//если со второй ардуины (скэтч BLINC) подать импульсы (1, 10 и 100Гц) на входы на внешних прерываний (пин2и3) Ардуины УНО, то без кода термометра (//get_temp();) при одном прерывании(пин2) 

//при 500мс(1Гц), счетчик за 2 сек прирастает на 2ед, что правильно.
//при 50мс(10Гц) счетчик прирастает на 20ед.Ок.
//при 5мс(100Гц) счетчик должен прирастать на 200ед.Но часто прирастал на 199

//Тоже, только подадим и на второе прерывание паралельно сигнал
//при 500мс(1Гц), счетчик ГОР за 2 сек прирастает на 2ед, а ХОЛ на 4,что правильно. четкое отличие показаний в два раза.ОК.
//при 50мс(10Гц) счетчик ГОР за 2 сек прирастает на 20ед, а ХОЛ не считал. ОК
//при 5мс(100Гц) счетчик ГОР за 2 сек должен прирастать на 200ед.Но часто прирастал на 199. ХОЛ не считал

//Задействуем два прерывания и термометр (один датчик DS18b20) !!!ПРОВАЛЬНЫЙ ТЕСТ!!!
//при 500мс(1Гц), счетчик ГОР за 2 сек прирастает ЧАСТО на 3ед, а ХОЛ на 6-7. Близкое отличие показаний в два раза. Температура правильная. BAD!!!
//при 50мс(10Гц) счетчик ГОР за 2 сек прирастает на 33ед, ХОЛ не считал. Температура правильная. BAD!!!
//при 5мс(100Гц) счетчик ГОР за 2 сек должен прирастать на 200ед.Но прирастал на 338. ХОЛ не считал. Температура правильная. BAD!!!

// http://cyber-place.ru/showthread.php?t=1292 //Программа отвечает на запросы из вне. Сама в сом-порт ничего не гонит и измерение темп не производит

//Ниже переделанная. Выдает инфу каждые 2 сек
//Проверял на Arduino UNO

/*micros()- Возвращает количество микросекунд с момента начала выполнения текущей программы на плате Arduino. Значение переполняется и сбрасывается на ноль, приблизительно через 70 минут. На 16MHz платах Ардуино (Duemilanove и Nano) функция micros() имеет разрешение 4 микросекунды (возвращаемое значение всегда кратно 4). На 8MHz платах (Arduino Lilypad) разрешение функции 8 микросекунд.

Если счетчик переполняется, то лучше просто обнулять счетчик этих функций после измерения:
timer0_millis для millis()
timer0_overflow_count для micros()

Есть два способа вычисления оборотов:
1. Считать количество срабатываний датчика за интервал времени (способ для меня)
2. Считать время между срабатываниями датчика

revolutions - обороты, а не революции
count - "счетчик"-имя переменной в которой хранится количество того, что мы считаем (суммируем)
rpm - оборотов в минуту
volatile - означает указание компилятору не оптимизировать код, поскольку значение переменной изменяется внутри обработчика прерывания
ISR - это Interrupt Service Routine или Процедура Обработки Прерывания

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

Использование таймера/счетчика 1 в своих целях при программировании плат Ardiono чревато разрушением реализованной в wiring системы реального времени. А таймер/счетчик 2 восьмибитный. подробней здесь
http://www.stepwood.com/avrsuite/2012/02/19/arduino-nastraivaem-taymer-schetchik-2/#more-123
*/

// 2 счетчика (на прерываниях!!!) и DS18b20
//Внешних Прерываний у УНО только два и их входы выведены на пин2 для 0прер и пин3 для 1-го
//Прерывания у Mega2560 пины 2 3 21 20 19 18
// Если датчик DS18B20 лежит на спине (лицом к нам), ногами вниз, то считая слева направо 1-GND, 2-D, 3+U
// Питание на DS18B20 можно подавать как +5в, так и +3.3в. Будет работать.
//Между 2 и 3 выв DS18B20 надо установить резистор 4,7кОм.
// для создания импульсов (при проверке) можно использовать энкодер
// Штатный светодиод УНО пин13 можно использовать для индикации прерываний
//OneWire - универсальная. С ее помощью можно работать с любым устройсвом (вкуснув его даташит), а //DallasTemperatureControl - частный случай для DS18B20
//Arduino Nano V.3 имеет дефект , не поддерживает WDT

/* Выводы счетчика воды
1 пин (красный) +5v с ардуина или внешнего источника
3 пин(черный) на землю ардуина
2 пин(желтый) на pin2 и pin3 ардуина

Выводы Ардуино УНО, а не МК (атмега328)
pin0,pin1 - RX TX
pin2 - Счетчик горячей воды
pin3 - Счетчик холодной воды
pin7 - Dallas DS18B20 Датчики температуры
*/
///////////////////////// Конец всяким пояснялкам //////////////////////////////////
 


#include <OneWire.h>

//#include <CyberLib.h>

////***************Переменные для температуры***************
OneWire ds(7); // Сигнал с Датчка температуры на pin7
byte addr[8];
byte i;
byte present = 0;
byte data[12];
float celsius;
////***********************************Конец Переменные для температуры

////****************Переменные для счетчика***************
volatile unsigned int Count_Hot = 0;//int-это диапазон -32,768 to 32,767 , unsigned int =0...65,535
volatile unsigned int Count_Cold = 0;//volatile - указываем, что значение получено из прерывания
//счетчик воды будет быстро переполнен. Поэтому надо импульсы преобразовывать в литры и сохранять
//в переменных, например, ЛитровВМинуту, ЛитровВЧас, ЛитровВСутки, ЛитровВМесяц, ЛитровВГод, ЛитровВсего
int pin13_LED=13; // переменной "pin13_LED" присвоили тип (int) и номер вывода (pin 13 для светодиода)
//volatile int x = LOW;
int millis_prev_1=2;//время на дребезг контактов (переключателя, кнопки)применил с "потолка"
int millis_prev_2=2;//У счетчика воды на датчике Холла дребезга нет
volatile float time = 0;//продолжительность работы программы, увиденная в текущий момент времени
volatile float time_last = 0;//продолжительность работы программы, полученная в предыдущий момент времени
////***********************************Конец Переменные для счетчика

////****************************** процедура настройки Ардуино ************************
void setup()
{

Serial.begin(9600);

pinMode(pin13_LED, OUTPUT);// пин настраиваем как выход (у Уно на 13 выв светодиод)
//digitalWrite(pin13_LED, HIGH);//HIGH, LOW - обязательно большимим буквами. включим ("Зажгем") светодиод

////***********************************Настройки для счетчика
// привязываем 0-е прерывание к функции add_Hot() пин2, CHANGE-прерывание по перепаду уровня сигнала вверх или вниз. Наверно??? можно пропустить короткий импульс во время обработки прерывания
attachInterrupt(0, add_Hot,FALLING );

// привязываем 1-е прерывание к функции add_Cold()пин3. Для относительно медленного сч.воды правильнее использовать FALLING(уровень лог "1"). Но тогда пропустим некоторе количество воды, если счетчик остановился в положении лог "1", т.е. поток сначала установит датчик в "0" и только потом в "1"
attachInterrupt(1, add_Cold,CHANGE );
////***********************************Конец Настройки для счетчика

}
////**************** КОНЕЦ процедуру настройки Ардуино ********************
//*******************************************************************************************


//************************ НАЧАЛО главного цикла ***********************************

//Отправка информации каждую delay(2000)милисек
void loop()
{
digitalWrite(pin13_LED, HIGH);

get_temp();

Serial.print("Cold:");
Serial.print(Count_Cold);
Serial.println();
//break;

//case 51:
Serial.print("Hot:");
Serial.print(Count_Hot, DEC);//в десятичном виде
Serial.println();
//break;
delay(2000);//задаем паузу ?сек

}
//************************ конец главного цикла ***********************************
//******************************************************************************************


//*************** Процедура измерения температуры ************************
void get_temp()
{

label:

if ( !ds.search(addr)) {
ds.reset_search();
delay(250);
return;
}
//Serial.print("ROM=");

for( i = 0; i < 8; i++) {
//Serial.print(addr[i], HEX);
}
ds.reset();
ds.select(addr);
ds.write(0x44, 1); // start conversion, with parasite power on at the end
delay(1000);
present = ds.reset();
ds.select(addr);

ds.write(0xBE);
for ( i = 0; i < 9; i++) {
data[i] = ds.read();
}

int16_t raw = (data[1] << 8) | data[0];
byte cfg = (data[4] & 0x60);
if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms
else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms

celsius = (float)raw / 16.0;
Serial.print("t=");
Serial.print(celsius);
Serial.println();
goto label;
}


//****************процедуры обработки внешних прерываний**************************

//1)Процедура обработки импульса(прерывания) от датчика холодной воды
void add_Cold()
{
static unsigned long millis_prev_1;
if(millis()-100 > millis_prev_1)
Count_Cold=Count_Cold++;
millis_prev_1 = millis();
}
//2)Процедура обработки импульса(прерывания) от датчика горячей воды, который без дребезга
void add_Hot()
{
Count_Hot=Count_Hot++;//к текущему значению счетчика Count_Hot прибавляем 1. Можно так: ++Count_Hot

//Так можно поменять состояние светодиода, что бы увидеть, что прерывание сработало
//НО делать этого в прерывании не надо, что бы не пропустить вх.импульс
digitalWrite(pin13_LED, LOW);

//Так можно узнать время между импульсами (прерываниями). Лжет, в момент переполнение (раз в 50 дней)
//time = (micros() - time_last);
//time_last = micros();
}

 

pav2000
Offline
Зарегистрирован: 15.12.2014

Добрый вечер -))

Тоже начиная с субботы пишу программку как у ТС (123ksn). Счетчик с датчиком Холла и  два датчика 1820.

Устройство будут использоваться с тепловым насосом, для определения его детальных характеристики. Поток 2-3 куба в час температуры 0 до+60 градусов.

Пока не тестировал на системе.

Готов объеденить усилия для доводки программу до ума. Мой код:

// Дополнительные библиотеки
#include <TimerOne.h>          // Программный таймер
#include <LiquidCrystal.h>     // Индикатор LCD 20x04
#include <OneWire.h>           // OneWire библиотека
#include <DallasTemperature.h> // Температурный датчик
#include <EEPROM.h>            // Запись во флеш

const char ver[] = "Version 1.32";          // Текущая версия
const byte statusLed  = 13;                 // Индикация потока нога - светодиод и реле к ТП 1-2
const byte sensorInterrupt = 0;             // Первый датчик потока адрес прерывания
const byte sensorPin       = 2;             // Первый датчик потока ножка на которую вешается датчик
const float calibrationFactor = 4.5;        // Калибровочный коэффициент датчика потока
const int  ONE_WIRE_BUS = 10;               // Нога на которой весят датчики температуры
const int  TEMPERATURE_PRECISION =12;       // разрешение датчика температуры в битах 9-12 бит
const int totalLitresAdr=0;                 // Адрес объема в литрах  во флеше 
volatile byte pulseCount;                   // Счетчик импульсов первого датчика
volatile float flowRate;                    // Поток литров в минуту измеряется раз в минуту
volatile float totalLitres;                 // Общий объем прошедшей жидкости в литрах Пишется во флеш каждый час
volatile int HZ       = 0;                  // Частота датчика
int hour =0;

LiquidCrystal lcd(12, 11, 5, 4, 3, 6); // Инициализаия индикатора

// Адрес герметичного датчика  28 FF 37 7F 4A 04 00 CF
// Адрес микросхемы датчика    28 FF 59 15 3C 04 00 C9
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress Thermometer1 = {0x28, 0xff, 0x37, 0x7f, 0x4a, 0x04, 0x00, 0xcf };  // адрес датчика DS18B20 28 FF 37 7F 4A 04 00 CF - герметичный
DeviceAddress Thermometer2 = {0x28, 0xff, 0x59, 0x15, 0x3c, 0x04, 0x00, 0xc9 };  // адрес датчика DS18B20 28 FF 59 15 3C 04 00 C9 - не герметичный

void setup() {
  lcd.begin(20, 4);       // определение размера дисплея
  lcd.setCursor(4, 1);    // Версия программы
  lcd.print(ver);  
  delay (2000);  
  //Чтение общего расхода из флеша 
//  writeEEPROM(totalLitresAdr,0); // Обнулить счетчик
  hour =0; totalLitres=readEEPROM(totalLitresAdr);
  
  lcd.setCursor(4, 1);    // Версия программы
  lcd.print("                "); 
  
  pinMode(statusLed, OUTPUT); // Индикация потока светодиод и реле
  digitalWrite(statusLed, HIGH);  
  pinMode(sensorPin, INPUT);   //  Подключение датчика потока 1
  digitalWrite(sensorPin, HIGH);

  // Установка начальных переменных
  pulseCount        = 0;
  flowRate          = 0.0;
  
  attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
  Timer1.initialize(1000000);         // initialize timer1, and set a 1 second period
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  
  // запуск датчиков температуры
  sensors.begin();
  sensors.setResolution(Thermometer1, TEMPERATURE_PRECISION);
  sensors.setResolution(Thermometer2, TEMPERATURE_PRECISION);
  }

void loop() {
    unsigned int frac;
    float t1,t2,dt,P;
    // Светодиод и реле показывает что поток есть
    if (flowRate > 0) digitalWrite(statusLed, HIGH);
    else              digitalWrite(statusLed, LOW);
    
    // Поток -  1 строка
    lcd.setCursor(0, 0);
    lcd.print("Flow:");
    lcd.setCursor(6, 0);
    lcd.print(int(flowRate));  // Целая часть
    lcd.print(".");             
    frac = (flowRate - int(flowRate)) * 10;
    lcd.print(frac, DEC) ;      // Дробная
    lcd.print(" l/m  ");
    
  // Вывод тепературы 2 строка
    sensors.requestTemperatures();
    t1=sensors.getTempC(Thermometer1);
    t2=sensors.getTempC(Thermometer2);
    dt=t1-t2;
    lcd.setCursor(0, 1);   
    lcd.print("dT:");
    lcd.print(int(dt)); lcd.print("."); frac=(dt-int(dt))*10;lcd.print(frac, DEC); lcd.print(" ");
    lcd.print("t:");
    lcd.print(int(t1)); lcd.print("."); frac=(t1-int(t1))*10;lcd.print(frac, DEC); lcd.print("/");
    lcd.print(int(t2)); lcd.print("."); frac=(t2-int(t2))*10;lcd.print(frac, DEC); lcd.print(" ");    

 // Частота датчика и мощность - 3 строка
    lcd.setCursor(0, 2);   
    lcd.print("Hz: ");
    lcd.print(int(HZ));  
    lcd.print("    ");
    lcd.setCursor(8, 2);   
    lcd.print("P: ");
    P=(flowRate/60) // литры/килограммы в секунду
                   *4.191*dt;
//    P=(0.8) *4.191*2;
    lcd.print(int(P)); lcd.print("."); frac=(P-int(P))*10;lcd.print(frac, DEC); lcd.print("kW  ");    
   
    // Объем и  - 4 строка 
    lcd.setCursor(0, 3);
    lcd.print("Total: ");             
    lcd.print(int(totalLitres));
    lcd.print(" Litres");
    
    // Запись во флеш общего расхода раз в час чтобы не убить флеш
    if (hour>3600) { writeEEPROM(totalLitresAdr,totalLitres);   hour=0;    }
    delay (1000);  
}

// Прерывание подсчет импульсов от первого датчика
void pulseCounter() {   pulseCount++; }

// Прерывание по таймеру для расчета секундного расхода в литрах Вызывается 1 раз в секунду
void callback()
{
 cli();  // Запретить прерывания
 HZ=pulseCount;  pulseCount = 0;
 sei();  // разрешить прерывания
 flowRate = HZ / calibrationFactor; // рассчитать поток в литрах за минуту
 totalLitres = totalLitres + flowRate/60.0; 
 hour++;
}

//----------------------------EEPROM FUNCTION--------------------------------------------------
// http://forum.arduino.cc/index.php?topic=188355.0
//union  value
typedef union{
  float flt;
  byte array[4];
} FloatConverter;
//write
void writeEEPROM(int address, float value)
{
  FloatConverter aConverter; //create a new variable of type FloatConverter
  aConverter.flt = value; //set its value (using the float blueprint) to the value of config
  for(byte i = 0; i < 4; i++){
    EEPROM.write(address+i,aConverter.array[i]); //store each of the 4 bytes of aConverter to the EEPROM, accessing them using the byte[4] blueprint
   };
 }
//read
float readEEPROM(int address)
{
  float value;
  FloatConverter aConverter; //create a new variable of type FloatConverter
  for(byte i = 0; i < 4; i++){
    aConverter.array[i] = EEPROM.read(address+i); //read 4 bytes from the EEPROM to aConverter using the byte[4] blueprint
  }
  value = aConverter.flt; //set the value of config to the value of aConverter using the float blueprint}
  return value;
}
//-------------------------------END EEPROM FUNCTION -----------------------------------------------------

Совет 123ksn минимизируйте  обработчики прерываний. Уберите все лишнее из них.

123ksn
Offline
Зарегистрирован: 24.11.2014

Сейчас скопирую и подвергну испытанию.

123ksn
Offline
Зарегистрирован: 24.11.2014

pav2000, не пишите общих фраз!!! Посмотрите код обработчика прерываний!!!

 

 
119 //1)Процедура обработки импульса(прерывания) от датчика холодной воды
120 void add_Cold()
121 {
122 static unsigned long millis_prev_1;
123 if(millis()-100 > millis_prev_1)
124 Count_Cold=Count_Cold++;
125 millis_prev_1 = millis();
126 }
127 //2)Процедура обработки импульса(прерывания) от датчика горячей воды, который без дребезга
128 void add_Hot()
129 {
130 Count_Hot=Count_Hot++;//к текущему значению счетчика Count_Hot прибавляем 1. Можно так: ++Count_Hot
131  
132 //Так можно поменять состояние светодиода, что бы увидеть, что прерывание сработало
133 //НО делать этого в прерывании не надо, что бы не пропустить вх.импульс
134 digitalWrite(pin13_LED, LOW);
135  
136 //Так можно узнать время между импульсами (прерываниями). Лжет, в момент переполнение (раз в 50 дней)
137 //time = (micros() - time_last);
138 //time_last = micros();
139 }
 

Что в нем лишнее?!!!

123ksn
Offline
Зарегистрирован: 24.11.2014

Судя по ругательству компилятора 

sketch_dec13a.ino: In function 'void setup()':
sketch_dec13a:55: error: 'Timer1' was not declared in this scope

и черному цвету надписи #include <TimerOne.h>

У меня нет библиотеки TimerOne. Бросьте ссылочку что бы скачать.

pav2000
Offline
Зарегистрирован: 15.12.2014

Лишее по моему ИХМО digitalWrite(pin13_LED, LOW);

Я конечно на arduino программирую 3  день, но мне кажется что эта функия долгая (это надстройка).

Также я бы не использовал функцию millis() в прерывании - тоже жрет ресурсы

 

Ссылка TimerOne.h

https://code.google.com/p/arduino-timerone/downloads/list

 

 

123ksn
Offline
Зарегистрирован: 24.11.2014

Пока ждал ответ, погуглил. В моих записях есть такая мысль:Использование таймера/счетчика 1 в своих целях при программировании плат Ardiono чревато разрушением реализованной в wiring системы реального времени. 

http://www.stepwood.com/avrsuite/2012/02/19/arduino-nastraivaem-taymer-schetchik-2/#more-123

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

Уверен, что на штатных библиотеках счетчики по прерыванию и DS18b20 "не подружить"(ИМХО).  Весь код надо писать на Си. Обратите внимание сколько отрицательных отзывов на библиотеку TimerOne и опять же нет примера для двух таймингозависимых устройств. Все приводят тепличные варианты.
 

pav2000
Offline
Зарегистрирован: 15.12.2014

У меня датчики темературы считываются корректно. А вот датчик потока у меня вызывает сомнения (хотя он не привязан к таймеру) Как его калибровать и проверить на адекватность.

Программировать на прямую я пробовал результат тот же т.е. работает.

По поводу библиотеки - нашел что не будет работать шим (на каких то ногах) но он мне не нужен. Других отрицательных моментов не нашел.

 

 

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

DS18B20 не зависит от таймингов. Считывание можно вести в любой момент времени после минимальной задержки. Просто в вашей библе напихано много delay(), попробуйте через OneWire напрямую.

123ksn
Offline
Зарегистрирован: 24.11.2014

Результаты моего тестирования отличаются от Ваших с точностью до наоборот

Hz= 100    P= 0
Flow:22.2 l/m  
t1=-127.0/t2=-127.0 
Hz= 100    P= 0
Flow:22.2 l/m  
t1=-127.0/t2=-127.0 
Hz= 99    P= 0
Flow:22.2 l/m  
t1=-127.0/t2=-127.0 
Hz= 100    P= 0
Т.е. ловит прерывание даже с частотой 100Гц, а температуру ни разу не показал. Подключен 1 датчик к 10 пину УНО.
123ksn
Offline
Зарегистрирован: 24.11.2014

bwn пишет:

DS18B20 не зависит от таймингов. Считывание можно вести в любой момент времени после минимальной задержки. Просто в вашей библе напихано много delay(), попробуйте через OneWire напрямую.

Пожалуйста, будте конкретней.

1)Если Вы посмотрите в даташит на DS18B20, то увидите много интервалов. И что бы считать температуру из DS18B20 надо сделать несколько предварительных операций, для которых очень важны тайминги.

2)О какой моей библиотеке идет речь?

3)У меня используется именно OneWire!!!!

123ksn
Offline
Зарегистрирован: 24.11.2014

В Вашем коде для работы с DS18B20 надо вводить его номер? То есть жестко прописывать?

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

У вас согласен,это писал по второму коду.

Но у меня на ciberlib работает два далласа и два диммера (100Гц) на прерывании, друг другу не мешают.

 

pav2000
Offline
Зарегистрирован: 15.12.2014

Может сделать считывание датчика как у меня через доп. библиотеку <DallasTemperature.h>

123ksn
Offline
Зарегистрирован: 24.11.2014

bwn пишет:

Но у меня на ciberlib работает два далласа и два диммера (100Гц) на прерывании, друг другу не мешают.

А кодом поделиться?

123ksn
Offline
Зарегистрирован: 24.11.2014

pav2000 пишет:

Может сделать считывание датчика как у меня через доп. библиотеку <DallasTemperature.h>

Если посмотрите мои заметки к коду, то прочтете: 

"//OneWire - универсальная. С ее помощью можно работать с любым устройсвом (вкуснув его даташит), а //DallasTemperatureControl - частный случай для DS18B20"

Добавлю: Просто надстройка над OneWire

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

Если 1000 строк не лень читать, то вот.

#if defined(ARDUINO) && ARDUINO >= 100 //Определение версии IDE
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

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

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

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

DHT dht(DHTPIN, DHTTYPE);


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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

void vlagClear()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 

123ksn
Offline
Зарегистрирован: 24.11.2014

Спасибо. Я уже не одну тысячу прочитал.

123ksn
Offline
Зарегистрирован: 24.11.2014

А вот и первый подводный булыжник всплыл.

ROM=2881B645600A0
Flow:0.2 l/m  
t1=-127.0/t2=-127.0 
Hz= 1    P= 0
Flow:2.2 l/m  
t1=-127.0/t2=-127.0 
Hz= 10    P= 0
Flow:2.2 l/m  
t1=-127.0/t2=-127.0 
 
Этот отчет я получил, когда добавил в void setup() код поиска датчиков DS18B20 и их номеров
     if ( !ds.search(addr)) {
ds.reset_search();
delay(250);
return;
}
Serial.print("ROM=");
 
for( i = 0; i < 8; i++) {
Serial.print(addr[i], HEX);
Serial.println();
В результате прерывание отработало не так как ожидалось, так как  Hz= 1  
Кроме того странным выглядит полученный номер по сравнению с номерами от pav2000 (DeviceAddress Thermometer2 = {0x28, 0xff, 0x59, 0x15, 0x3c, 0x04, 0x00, 0xc9 };) Код моего датчика явно короче. В своей программе я номер датчика не выводил, но температура у меня определялась. 
Главное, что поиск датчиков DS18B20, нарушает работу прерываний. ИМХО
 

 

pav2000
Offline
Зарегистрирован: 15.12.2014

Я в программе не ищу датчики, я заранее знаю адреса по которым стучаться надо. Определение делается с помощью одного из примеров библиотеки <DallasTemperature.h> что то типа мульти . . .

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

Адрес должен быть как у меня длиной.

123ksn
Offline
Зарегистрирован: 24.11.2014

pav2000 пишет:

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

Принципиально не согласен с таким подходом.

pav2000 пишет:

Чтение температуры по конкретному адресу не припятсвует работе таймера.

О каком таймере речь?

pav2000 пишет:

Адрес должен быть как у меня длиной.

Видимо, да.

123ksn
Offline
Зарегистрирован: 24.11.2014

 bwn,   не могли бы Вы поделиться библиотекой LiquidCrystalCyr_I2C.h. Гугл не находит.

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

Чет я пропустил, при отображении ROM у вас выкинуло нули, адрес 8байт. А если вы проведете определение в Setup и запомните адреса, то потом этой операции не будет и соотв. не должно влиять на прерывания (они delay() ненавидят). У меня диммеры с DS работают без мигания, а вот библиотеку DHT не победил (там все на длинных таймингах), на ней есть помаргивание.

Библу здесь не знаю как выложить, могу скинуть.

 

123ksn
Offline
Зарегистрирован: 24.11.2014

bwn пишет:

А если вы проведете определение в Setup и запомните адреса, то потом этой операции не будет и соотв. не должно влиять на прерывания 

Это я уже понял. А информацию выложил, что бы не быть голословным трепачем. Библиотеку в одном месте нашел. Вот ссылка URL: http://blockduino.org/Libs/LiquidCrystalCyr_I2C.zip

Сегодня ночью "обрезАл" Ваш код, так как у меня нет ни ЖКИ, ни кнопок, ни DHT22, Если возможно, поделитесь дополнительно информацией о том, какие Вы подключали к Ардуино прибамбасы и к каким пинам. Например, сколько кнопок в клавиатуре? Схемой на кнопки. Какую внешнюю память используете? Код у Вас хорошо комментирован, но все-таки описание на словах лишним не будет.

 

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

Кнопок 3 верх, низ, выбор. Подключены на А0 резистивным делителем (там в комментах были номиналы). SDA,SCL(A4,A5) часы и дисплей. D2 вход прерывания от сетевой синусоиды (через оптрон), D3,D4 - диммеры через оптрон-симистор без отлова нуля. D7 - AM2301(DHT22). D8 - DS18B20 2 штуки. D9 - пищалка активная через ключ. D10, 11, 12 - вентиляторы и освещение через оптрон-симистор с отловом нуля (moc3041). D13 - мигалка+собака на NE555.

Память внутренняя на Atmege. Если будете использовать для нее Ciberlib, там был косяк с полем памяти (возможно ужу подправили) byte и long работали с однобайтовой адресацией.

Radjah
Offline
Зарегистрирован: 06.08.2014

По всякому библиотеку ломал. Кое-как на Mega запустил, убрав PROGMEM и сделав тип unsigned char (билеберда полезла), на nano не завелась никак.

Добавил бы кто переключатель страниц знакогенератора для дисплеев МЭЛТ и обработку для юникода. :)

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

Radjah пишет:

По всякому библиотеку ломал. Кое-как на Mega запустил, убрав PROGMEM и сделав тип unsigned char (билеберда полезла), на nano не завелась никак.

Добавил бы кто переключатель страниц знакогенератора для дисплеев МЭЛТ и обработку для юникода. :)

Это вы за Ciberlib?

Radjah
Offline
Зарегистрирован: 06.08.2014

КиберЛиб у меня работает, я про LiquidCrystalCyr_I2C.zip

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

У меня с первого раза без проблем. А дисплей с кирилицей?

Radjah
Offline
Зарегистрирован: 06.08.2014

Да. Патался залить пример в Mega. prog_uchar где объявлено?

Ругань идет при заливке в мега (там PROGMEM нет) и в nano (на prog_uchar)

IDE 1.5.7