Постоянный посетитель
|
ARDUINO для автоматизации аквариума (страница 5) |
Создал отдельную тему по вопросам использования простых и не дорогих плат ARDUINO для целей автоматизации аквариума. Схема коммутации в случае использования платы NANO: Программа для среды программирования Arduino: //*************************************************** // Скетч для управления релейным модулем, в котором * // использовано два канала. Используется также RTC * // Релейный модуль - с инверсной логикой на входе * // Автор: ZORS * // Версия 1. Дата 21.12.2013 02:40 * //*************************************************** //----------ИМПОРТ БИБЛИОТЕК------------------------- #include //Подключаем библиотеку для использования I2C интерфейса с модулем RTC #include //Подключаем библиотеку для использования модуля часов реального времени RTC RTC_DS1307 RTC; //Создаем переменную класса - для использования RTC //----------Объявляем разные переменные------------ const int RelayChn1 = 6; //Используем цифровой ПОРТ 6 для ПЕРВОГО канала релейного модуля const int RelayChn2 = 7; //Используем цифровой ПОРТ 7 для ВТОРОГО канала релейного модуля //----------Настройки времени и продолжительности включения реле //----------ПЕРВЫЙ канал---------------------------- const long StartRelCn_1 = 25200; //Время срабатывания в ПЕРВОМ канале релейного модуля (в секундах от начала суток) //в данном случае 25200 - это 7 часов 00 минут = ( 60секунд *60 минут *7 = 25200) const long DurationCh_1 = 10; //ДЛИТЕЛЬНОСТЬ срабатывания реле в ПЕРВОМ канале (в секундах) //----------ВТОРОЙ канал---------------------------- const long StartRelCn_2 = 37800; //Время срабатывания во ВТОРОМ канале релейного модуля (в секундах от начала суток) //В данном случае 10 часов 30 минут = (60 секунд * 60 минут * 10 часов + 60сек*30мин = 37800) const long DurationCh_2 = 15; //ДЛИТЕЛЬНОСТЬ срабатывания реле во ВТОРОМ канале (в секундах) //----------Модуль инициализации setup() - выполняется один раз при инициализации платы при подаче напряжение (и аналогичных событиях) void setup(){ pinMode(RelayChn1,OUTPUT); //Инициализируем порт для ПЕРВОГО канала как ВЫХОД pinMode(RelayChn2,OUTPUT); //Инициализируем порт для ВТОРОГО канала как ВЫХОД digitalWrite(RelayChn1,HIGH); //Устанавливаем на входах релейного модуля ВЫСОКИЙ уровень digitalWrite(RelayChn2,HIGH); //Т.к. используемый релейный модуль с опторазвязкой - управляется инверсной логикой Wire.begin(); //Инициируем I2C интерфейс RTC.begin(); //Инициирум RTC модуль // RTC.adjust(DateTime(__DATE__, __TIME__)); //С этой строки необходимо убрать комментарии один раз в начале, //для того, чтобы загрузить в RTC дату и время на момент компиляции программы //Иногда необходимо заливать СКЕТЧ на плату со снятым комментарием - для поправки //времени в RTC, НО оставлять такой СКЕТЧ в работе НЕЛЬЗЯ !!!!!!!!!!!!! } // КОНЕЦ ИНИЦИАЛИЗАЦИИ //-------------------------------------------------- void loop() // ПРОГРАММЫй безусловный ЦИКЛ { DateTime myTime = RTC.now(); //Читаем данные времени из RTC при каждом выполнении цикла //----------Раздел обработки реле по времени ---- long utime = myTime.unixtime(); //сохраняем в переменную - время в формате UNIX utime %= 86400; //Сохраняем в этой же переменной остаток деления на кол-во секнд в сутках, //Это дает количество секунд с начала текущих суток //------------КАНАЛ 1------------------------------ if ((utime >= StartRelCn_1) && (utime //Если секунд с начала суток больше, чем задано для включения //Но, одновременно и меньше, чем задано для включения + длительность { digitalWrite(RelayChn1,LOW); //Устанавливаем на ПЕРВОМ входе релейного модуля НИЗКИЙ уровень - реле срабатывает } else //во всех остальных случаях { digitalWrite(RelayChn1,HIGH); //Устанавливаем на ПЕРВОМ входе релейного модуля ВЫСОКИЙ уровень - реле выключается } //------------КАНАЛ 2 - все аналогично ----------- if ((utime >= StartRelCn_2) && (utime { digitalWrite(RelayChn2,LOW); //Устанавливаем на ВТОРОМ входе релейного модуля НИЗКИЙ уровень - реле срабатывает } else { digitalWrite(RelayChn2,HIGH); //Устанавливаем на ВТОРОМ входе релейного модуля ВЫСОКИЙ уровень - реле выключается } }//------------Конец ЦИКЛА----------------------------- (Редактор текста на данном сайте - к сожалению, "ломает" красивое форматирование. PS: Выложил этот скетч на файлообменник : http://my-files.ru/h... В данной программе реализован, довольно-таки простой подход, а простота - основа надежности. Каждое из 2-х реле срабатывают один раз в сутки в указанное время, причем время указывается в количестве секунд от начала суток, и удерживается указанное количество времени в секундах. Такой способ задания времени - несколько сложен для понимания, но за это - не нагружает программу и процессор лишним функционалом, который понадобится всего один раз. Для определения времени наступления события начала включения и выключения реле - используется время в формате UNIX. Такой подход хорош тем, что даже если включение или перезагрузка платы произойдет в промежутке времени включения, то реле включится в оставшееся время. В случае с дозированием, это не много что дает, но в случае, если по аналогии будет сделано управление светом, то при подаче напряжения на плату, например, после пропадания напряжения питания, или после посадки напряжения, или др. аналогичных событий, включение и выключение реле произойдет корректно. У комплектных минутных и секундных таймеров "с розеткой" - об этом можно только мечтать....не реализован такой функционал, и еще у них есть один минус. Реле в таких таймерах - не удерживаемое, а переключаемое, со всеми вытекающими последствиями.... Для программирования нескольких включений каждого реле в течение суток - естественно, потребуется несколько другой подход....спрашивайте когда кому-то потребуется, пока не хочу все усложнять... В принципе, можно добавить константных переменных, содержащих время и длительность срабатывания, и соответственно добавить проверку на условия. Если количество необходимых срабатываний каждого реле в сутках больше чем 2 - лучше оформить проверку условий в отдельной функции. Как, говорил человек, учивший меня программированию - "все, что ты делаешь в программе больше двух раз - оформляй отдельной процедурой, или функцией". Главное ограничение этого подхода - включение и выключение реле - должно произойти в одних сутках. Если необходимо чтобы, реле включилось в одних сутках, а выключилось в других - необходимо разбить это задание на 2 задания, или применить другой метод проверки условий наступления событий включения и выключения. Весь этот пример тестировался на "живом" железе : Для наглядности отладки был еще подключен LCD дисплей 16 сим в 2 строках. Схема итоговая была такая: Обратите внимание, что при использовании I2C последовательного интерфейса - соединения значительно упрощаются. И LCD дисплей подключен транзитом через модуль RTC (часов реального времени). Никаких паяных соединений нет вообще....все скоммутировано - проводами с разъемами. Скетч - естественно, несколько другой использовался с добавлением строк инициализации и использования LCD 16x2. Плата использовалась типа такой: http://www.ebay.com/... цена примерно 350 руб. Релейный модуль http://www.ebay.com/... цена примерно 150 руб. Модуль часов реального времени: http://www.ebay.com/... Цена примерно 55 руб. Для сокращения бюджета можно использовать плату проще: http://www.ebay.com/... Цена примерно 200 руб. Если есть потребность подключить ЖК дисплейчик, то лучше брать ЖК дисплей (LCD) c I2C интерфейсом - это сильно упрощает коммутацию, и сокращает количество использованных портов на микроконтроллере. Например такой можно использовать : http://www.ebay.com/... цена около 180 руб. На ЖК можно выводить статусную информацию, информацию об ошибках, диагностическую информацию. Все это, конечно, можно и в последовательный порт компьютера выводить, но это не всегда удобно (скорее наоборот), поэтому я всегда использую какой-нибудь девайс для вывода информации. Для использования I2C интерфейса - необходима библиотека Wire. Она есть в стандартном дистрибутиве среды программирования Arduino. Для использования модуля RTC на микросхеме DS1307 нужна библиотека RTClib. Если ее не будет в стандартном дистрибутиве, то ее можно взять в интернет. Например здесь : http://arduino-info.... Необходимо, скачать, и разархивировать zip-файл в папку с понятным названием, например RTCLib. Затем эту папку поместить в папку, где среда Arduino хранит свои библиотеки. Если ставили среду программирования с настройками по умолчанию, то этой папкой будет : C:Program FilesArduinolibraries Сюда и помещайте папки с подключаемыми библиотеками. Если на этот момент, среда программирования была запущена - ее необходимо закрыть, и запустить заново. После этого библиотека будет доступна в меню Скетч/Импортировать библиотеку. Микросхема RTC модуля DS1307, как правило использует адрес на шине I2C равный 0x68 поэтому, необходимо проверить в библиотечных файлах, библиотеки RTClib наличие строки типа этой: #define DS1307_ADDRESS 0x68 Если будет использоваться LCD 16x2 с I2C интерфейсом, то - понадобиться библиотека LiquidCrystal_I2C. Можно скачать здесь : http://dvrobot.ru/lc... Как подключить к среде программирования Arduino - см. выше ВАЖНО : при использовании I2C устройств - в скетче, библиотека Wire - должна быть объявлена первой, иначе - будут ошибки компиляции. По этому примеру - все.... в следующем - присоединение термо-датчика DS18B20, и создание несложного терморегулятора..... Изменено 24.12.13 автор Z0RS |
|
#1907148 |
Малёк
46
10 года |
|
сообщение Z0RS Смысл вот в чём: Допустим в автомате свет включается в 17.00 и выключается в 22.00. Иногда, особенно когда на улице пассмурно, необходимо включить свет например на час, для того чтобы поменять воду или покормить рыб. А потом опять в автомат. Тоже самое с обогревом Н2. Когда меняю воду мне его надо выключить, а потом например через пол часа или через час опять включить в автомат. Может быть возврат в автоматический режим кнопкой SELECT? Изменено 28.3.14 автор kirex05 Изменено 28.3.14 автор kirex05 |
|
#1956119 |
|
Постоянный посетитель
|
|
kirex05 Ну Вы уж определитесь, как будете режимы переключать, причем выбор режима АВТОМАТ. / РУЧНОЙ - можно сделать для каждой нагрузки свой, а можно общий для всех нагрузок...определяйтесь. |
|
#1956323 |
Малёк
46
10 года |
|
сообщение Z0RS Выбор режима АВТОМАТ. / РУЧНОЙ общий для всех нагрузок. Переключение режимов кнопкой SELECT. |
|
#1956347 |
Посетитель
|
|
Z0RS День добрый. Я тут копаюсь с проектом - возник вопрос по датчику потока: На крыльчатке одет кольцевой магнитик. Снаружи (вернее через перегородочку от воды) такая платка. Внутри я так и не понял что за треног. (датчик холла? - на геркон не похож) Написано W12 38L4 Подключено слева направо: 1. Красный через резистрор. 2. Черный 3. Желтый Я так понял красный - плюс. черный - минус. Желтый на ардуинку. (цифровой ввод?) А как получить значения от него? что писать в коде? |
|
#1956465 |
Малёк
46
10 года |
|
Z0RS Сделал вот так: #include /******************************************************* This program will test the LCD panel and the buttons Mark Bramwell, July 2010 ********************************************************/ // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); int Relay_Off = HIGH; int Relay_On = LOW; int reg=0;// вводим флаг для автоматического (при значении-0) или ручного регулирования -1 // define some values used by the panel and buttons int lcd_key = 0; int adc_key_in = 0; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result // For V1.1 us this threshold if (adc_key_in if (adc_key_in if (adc_key_in if (adc_key_in if (adc_key_in // For V1.0 comment the other threshold and use the one below: /* if (adc_key_in if (adc_key_in if (adc_key_in if (adc_key_in if (adc_key_in */ return btnNONE; // when all others fail, return this... } void setup() { pinMode(41, OUTPUT); lcd.begin(16, 2); // start the library lcd.setCursor(0,0); lcd.print("Push the buttons"); // print a simple message } void loop() { lcd.setCursor(0,1); // move to the begining of the second line lcd_key = read_LCD_buttons(); // read the buttons switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { digitalWrite(41, Relay_Off); lcd.setCursor(7, 1); lcd.print("H2-Off"); break; } case btnLEFT: { digitalWrite(41, Relay_On); lcd.setCursor(7, 1); lcd.print("H2-On "); break; } case btnUP: { lcd.print("UP "); break; } case btnDOWN: { lcd.print("DOWN "); break; } case btnSELECT: { if(reg==1) {reg=0; lcd.setCursor(13, 1); lcd.print("Aut");} else {reg=1; lcd.setCursor(13, 1); lcd.print("Man");} break; } case btnNONE: { lcd.print("NONE "); break; } } } Так кнопкой RIGHT выключается, а кнопкой LEFT включается. Кнопкой SELECT выбирается режимм. |
|
#1956807 |
|
Постоянный посетитель
|
|
arch07 Ну...то, что это датчик Холла - это точно. Не совсем понятно какая именно модель...а напряжение питания где-нибудь указано? На выходе всего скорее - аналоговый сигнал пропорциональный силе воздействующего магнитного поля...но дискретный вход, по идее должен срабатывать без применения дополнительного усиления. Это будет необходимо проверить....когда придет плата, в худшем случае потребуется дополнительный каскад усиления. А может быть и дискретный выход с TTL уровнями. У датчика Холла - нет дребезга и это огромное преимущество перед герконами. Срабатывание датчика лучше отлавливать прерыванием. В принципе, как оформить простейшее аппаратное прерывание - я уже приводил пример. В прерывании, все что нужно сделать - установить флаг того, что произошел импульс, а дальше этот флаг обрабатывать внутри главного цикла, или в теле прерывания увеличивать переменную для зафиксированных импусльсов, а в теле главного цикла - делать обработку и уменьшать переменную до нуля. Какой вариант лучше - я не знаю, это зависит от многих факторов. Уж что за логику использования данного устройства Вы придумаете я не знаю...можно например, включать клапан подмены воды, затем - считать импульсы до тех пор, пока не насчитаете требуемое количество, после чего выключать клапан...да мало ли чего можно придумать. НО сначала - необходимо четко представить себе что вы хотите...после этого - программировать. |
|
#1956809 |
Постоянный посетитель
|
|
kirex05 Я так понимаю, что ....заработало? Ну и хорошо.... |
|
#1956810 |
Малёк
46
10 года |
|
сообщение Z0RS Да нет не заработало. Это я просто пробовал в отдельном коде, а как это сделать в моём ума пока не проиложу. |
|
#1956811 |
|
Постоянный посетитель
|
|
kirex05 Ну для одного порта это работает? А для остальных потребуется сделать процедуру выбора, т.е. написать меню...о чем я и говорил... А меню вещь своеобразная, тем более для микроконтроллеров. |
|
#1956815 |
Малёк
46
10 года |
|
сообщение Z0RS Ну да работает, только как это впихнуть в мой код я пока не пойму. А меню, это для меня вообще полный лес. |
|
#1956818 |
Посетитель
|
|
Z0RS Ага - понял. Попробую подключить. сайт по счетчику есть, но сильно китайский http://gd-sbs.com/ Логику использования понял. Но пока в моем проекте буду использовать только как датчик - т.к. насос плохо держит давление после суток-других простоя, то в начале надо добиться заполнения системы водой - вот тут и пригодится агрегат. Уже сделал одну позицию с обраткой. Потом мне достаточна точность отмерять полив по времени. Разница между максимальным и минимальным уровнем в резервуаре пренебрежительно мала. По замерам насос дает 200 гр за 10-11 сек. Т.е. по 10-20 секунд на горшок (зависит от размера) самый раз. Немного напрягло, что сервомашинки очень неточно позиционируются, если задавать угол градусами. - 0 и 180 откалибровал, но между ними у меня 11 позиций (по 15 градусов) - так вот гуляет +- 10 градусов. Благо можно задавать позицию напрямую частотой - я пока подбираю частоту-угол, а потом создам массив частот под конкретную машинку и буду в цикле его перебирать. Немного доделаю и выложу тут проект с чертежем, фотками и кодом - на рецензию. Внимательно читаю про кнопки - т.к. планирую сделать проект совсем автономным - корректировку времени RTC модуля и задание время и цикла срабатывания без перепрошивки. |
|
#1956901 |
|
Завсегдатай, Кандидат в Советники
|
|
Z0RS Подскажите, пожалуйста, а можно ли подать ШИМ-сигнал с одного выхода сразу на несколько диммируемых светодиодных драйвера, скажем MW LDD1000(H), для экономии и выходов контроллера, и программного кода? |
|
#1956917 |
Постоянный посетитель
|
|
Дядька-рыбомор Вообще если делать правильно, то необходимо выяснить какой ток на диммируемом входе предусмотрен производителем микросхемы драйвера LED, затем умножить этот ток, на количество драйверов, которое Вы хотите одновременно диммировать с одного выхода платы ARDUINO, и выяснить не превышает ли вычесленный ток значение предусмотренное для того порта микроконтрноллера, на котором Вами организован ШИМ. Это в теории...можете эту нехитрую проверку сами сделать... Но на практике, выяснилось, что диммирующие входа на драйверах - имеют весьма высокое сопротивление, и соответственно не большой ток диммирования от 5 мкА до 1 мА. Т.е. с десяток драйверов можно "не глядя" присоединять к одному выходу микроконтроллера. ПС: Посмотрел в даташите ток на диммирующем входе драйвере = 1 мА. Максимальный ток для одного порта например, для ATMega 328 = 40 мА. Выводы понятны надеюсь.... arch07 Так зачем тогда импульсный датчик потока....делайте по времени... arch07 А Вы сервы то для чего используете? arch07 Вообще, у коллеги, кнопки, про которые вы читаете - не очень типичные - с опросом по аналоговому сигналу, это видимо, придумано для сокращения используемых портов... Лучше использовать что-нибудь стандартное, обычную кнопочную или пленочную клавиатурку, а для сокращения используемых портов - использовать IO экспандеры с I2C интерфейсом, как правило это микросхемы сдвиговых регистров, для клавы 4Х4 подойдет, например - PCF8574 За 50-60 рублей, зато ее можно посадить на уже используемую шину I2C интерфейса. У меня просто сейчас нет свободного железа, скоро подойдут экспандеры PCF8574 и PCF8575 - сделаю пример с клавиатурой на I2C, основами построения меню, и написания собственных библиотек... Задумка уже есть такая... Да и еще о RTC. Как выяснилось RTC на DS1307 - не очень точны...и единственное их преимущество это "смешная" цена. А вот точность хода - очень не большая, и в месяц может до минуты накапливаться погрешность....Но это можно исправить без того, чтобы лазить в меню, и поправлять вручную время. Можно вычислить поправку и вносить ее в промежуток времени, который равен периоду времени когда накапливается погрешность = одна секунда. Т.е. RTC необходимо "эджастить" на одну секунду в период, за который эта секнда накапливается. Это будет правильнее...У меня когда-то были электронные часы наручные, наши Российские с такой функцией...можно было поправки вносить...с поправкой в год накапливалось всего несколько секунд...А это уже совсем другое дело.... kirex05 Поймите простое правило - "закодить" готовый алгоритм работы чего либо, в том числе и меню - это "нет ничто" по сравнению с трудностью разработки самого алгоритма. Поэтому...разработайте и опишите для себя хоть своими словами на бумаге - как должно работать меню и как должны реагировать нагрузки...потом все это закодите за пару тройку часов.. Изменено 30.3.14 автор Z0RS Изменено 30.3.14 автор Z0RS |
|
#1956960 |
Малёк
46
10 года |
|
Z0RS С обогревом разобрался, теперь нужно как-то тоже самое со светом LW и LR проделать. #include //Подключаем библиотеку для использования I2C интерфейса с модулем RTC #include "RTClib.h"//Подключаем библиотеку для использования модуля часов реального времени RTC #include #include #include "LiquidCrystal.h" // библиотека экрана LiquidCrystal lcd(8, 9, 4, 5, 6, 7); OneWire ds(53); // Подключаем датчик к 53 цифровому пину int lcd_key = 0; int adc_key_in = 0; int reg=0;// вводим флаг для автоматического (при значении-0) или ручного регулирования -1 #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 142, 328, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in if (adc_key_in if (adc_key_in if (adc_key_in if (adc_key_in return btnNONE; // when all others fail, return this... } #define PWM_LW_MIN 0 //Если необходим ток покоя на LED - изменить эту константу #define PWM_LW_MAX 128//Если необходимо ограничить максимальную яркость - уменьшить значение #define PWM_LW_PIN 11 //Пин порта, где будет ШИМ LW #define PWM_LR_MIN 0 //Если необходим ток покоя на LED - изменить эту константу #define PWM_LR_MAX 200//Если необходимо ограничить максимальную яркость - уменьшить значение #define PWM_LR_PIN 3 //Пин порта, где будет ШИМ LR #define PWM_LB_MIN 0 //Если необходим ток покоя на LED - изменить эту константу #define PWM_LB_MAX 235 //Если необходимо ограничить максимальную яркость - уменьшить значение #define PWM_LB_PIN 12 //Пин порта, где будет ШИМ LB #define mn 60UL //Дополнительные константы для удобства #define hr 3600UL //Отражают соответствующие количества секунд #define d 86400UL int Relay_Off = HIGH; int Relay_On = LOW; RTC_DS1307 RTC; long sunrise_start = 17*hr+00*mn; //Начало восхода в 9 - 45 long sunrise_duration = 3*mn; //Длительность восхода 30 минут long sunset_start = 22*hr+00*mn; //начало заката в 21-15 long sunset_duration = 3*mn; //Длительность заката 30 минут long moonrise_start = 22*hr+00*mn ;//Начало луны в 9 - 45 long moonrise_duration = 3*mn;//Длительность восхода long moonset_start = 23*hr+00*mn;//Конец луны в 11 long moonset_duration = 3*mn; //Длительность заката луны //******************************************************************************************** void setup(){ Serial.begin(9600); lcd.begin(16, 2); // запускаем библиотеку экрана pinMode(40, OUTPUT); // Включаем кипятильник pinMode(41, OUTPUT); digitalWrite(40, Relay_On); digitalWrite(41, Relay_On); Wire.begin(); //Инициируем I2C интерфейс RTC.begin(); //Инициирум RTC модуль analogWrite(PWM_LW_PIN, PWM_LW_MIN); //Пишем в порт минимальное значение analogWrite(PWM_LR_PIN, PWM_LR_MIN); analogWrite(PWM_LB_PIN, PWM_LB_MIN); if (! RTC.isrunning()) { Serial.println("RTC is NOT running!"); RTC.adjust(DateTime(2014, 7, 12, 22, 48, 1)); }//RTC.adjust(DateTime(__DATE__, __TIME__)); } // КОНЕЦ ИНИЦИАЛИЗАЦИИ //******************************************************************************************** void loop() // ПРОГРАММЫй безусловный ЦИКЛ { analogWrite(10, 100); lcd.setCursor(0, 1); long pwm_LW; long pwm_LR; long pwm_LB; DateTime myTime = RTC.now(); //Читаем данные времени из RTC при каждом выполнении цикла long Day_time = myTime.unixtime() % 86400; //сохраняем в переменную - время в формате UNIX lcd.setCursor(11,0); if(myTime.hour() lcd.print(myTime.hour(), DEC); lcd.print(":"); if(myTime.minute() lcd.print(myTime.minute(), DEC); //********************************************************************************************* // обработка интервала до восхода и после заката //********************************************************************************************* if ((Day_time (Day_time>=sunset_start+sunset_duration)) { //Или больше чем начало заката + длительность pwm_LW = PWM_LW_MIN; //Величина для записи в порт равна минимуму pwm_LR = PWM_LR_MIN; //********************************************************************************************* // обработка интервала восхода //********************************************************************************************* }else if ((Day_time>=sunrise_start) && //Если с начала суток больше чем начало восхода (Day_time pwm_LW = ((Day_time - sunrise_start)*(PWM_LW_MAX-PWM_LW_MIN)) / sunrise_duration; //Вычисляем для рассвета величину для записи в порт ШИМ pwm_LR = ((Day_time - sunrise_start)*(PWM_LR_MAX-PWM_LR_MIN)) / sunrise_duration; //********************************************************************************************* // обработка интервала заката //********************************************************************************************* }else if ((Day_time>=sunset_start) && //Если начала суток больше чем начало заката и меньше чем (Day_time pwm_LW = ((sunset_start+sunset_duration - Day_time)*(PWM_LW_MAX-PWM_LW_MIN)) / sunrise_duration; //Вычисляем для заката величину для записи в порт ШИМ pwm_LR = ((sunset_start+sunset_duration - Day_time)*(PWM_LR_MAX-PWM_LR_MIN)) / sunrise_duration; //******************************************************************************************** // обработка интервала от конца рассвета и до начала заката, // когда свет должен быть включен на максимальную яркость //******************************************************************************************** }else { pwm_LW = PWM_LW_MAX; //Устанавливаем максимальную величину для записи в порт ШИМ pwm_LR = PWM_LR_MAX; } analogWrite(PWM_LW_PIN, pwm_LW);//Пишем в порт вычисленное значение analogWrite(PWM_LR_PIN, pwm_LR); // обработка интервала до восхода луны и после заката //********************************************************************************************* if ((Day_time (Day_time>=moonset_start+moonset_duration)) {//Или больше чем начало заката + длительность pwm_LB = PWM_LB_MIN; //Величина для записи в порт равна минимуму //********************************************************************************************* // обработка интервала восхода луны //********************************************************************************************* }else if ((Day_time>=moonrise_start) &&//Если с начала суток больше чем начало восхода (Day_time pwm_LB = ((Day_time - moonrise_start)*(PWM_LB_MAX-PWM_LB_MIN)) / moonrise_duration; //Вычисляем для рассвета величину для записи в порт ШИМ //********************************************************************************************* // обработка интервала заката луны //********************************************************************************************* }else if ((Day_time>=moonset_start) && //Если начала суток больше чем начало заката и меньше чем (Day_time pwm_LB = ((moonset_start+moonset_duration - Day_time)*(PWM_LB_MAX-PWM_LB_MIN)) / moonrise_duration; //Вычисляем для заката величину для записи в порт ШИМ //******************************************************************************************** // обработка интервала от конца рассвета и до начала заката луны, // когда свет должен быть включен на максимальную яркость //******************************************************************************************** }else { pwm_LB = PWM_LB_MAX; //Устанавливаем максимальную величину для записи в порт ШИМ } analogWrite(PWM_LB_PIN, pwm_LB); //Пишем в порт вычисленное значение byte i; byte type_s; byte data[12]; byte addr[8]; float celsius, fahrenheit; // Ищем алрес датчика if ( !ds.search(addr)) { Serial.println("No more addresses."); Serial.println(); ds.reset_search(); delay(250); return; } // Проверяем не было ли помех при передаче if (OneWire::crc8(addr, 7) != addr[7]) { Serial.println("CRC is not valid!"); return; } Serial.println(); // Определяем серию датчика switch (addr[0]) { case 0x10: Serial.println(" Chip = DS18S20"); type_s = 1; break; case 0x28: Serial.println(" Chip = DS18B20"); type_s = 0; break; case 0x22: Serial.println(" Chip = DS1822"); type_s = 0; break; default: Serial.println("Device is not a DS18x20 family device."); return; } ds.reset(); ds.select(addr); // Выбираем адрес ds.write(0x44, 1); // Производим замер, в режиме паразитного питания delay(750); ds.reset(); ds.select(addr); ds.write(0xBE); // Считываем оперативную память датчика for ( i = 0; i data[i] = ds.read(); // Заполняем массив считанными данными } // Данные о температуре содержатся в первых двух байтах, переведем их в одно значение и преобразуем в шестнадцатиразрядное число unsigned int raw = (data[1] // Переводим температуру в шкалы по Цельсию и Фаренгейту if (type_s) { raw = raw } if (data[7] == 0x10) { raw = (raw & 0xFFF0) + 12 - data[6]; } else { byte cfg = (data[4] & 0x60); if (cfg == 0x00) raw = raw else if (cfg == 0x20) raw = raw else if (cfg == 0x40) raw = raw } // Вывод текущего значения температуры на дисплей celsius = (float)raw / 16.0; lcd.setCursor(0, 0); lcd.print("T="); lcd.setCursor(2, 0); lcd.print(celsius); delay (10); celsius = (float)raw / 16.0; fahrenheit = celsius * 1.8 + 32.0; Serial.print("Temp = "); Serial.print(celsius); Serial.print(" C, "); Serial.print(fahrenheit); Serial.println(" F"); if (reg==0){ // Если регулирование автоматическое -регулируем по температуре lcd.setCursor(15, 1); lcd.print("A"); // Если температура достигает 28,3 (с погрешностью), отключаем кипятильник Н1 if (celsius > 28.3) { digitalWrite(40, Relay_Off); lcd.setCursor(0, 1); lcd.print("H1-Off"); } // Если температура достигает 28,5 (с погрешностью), отключаем кипятильник Н2 if (celsius > 28.5) { digitalWrite(41, Relay_Off); lcd.setCursor(7, 1); lcd.print("H2-Off"); } // Если температура падает до 28 (с погрешностью), включаем оба кипятильника if (celsius { digitalWrite(40, Relay_On); lcd.setCursor(0, 1); lcd.print("H1-On "); digitalWrite(41, Relay_On); lcd.setCursor(7, 1); lcd.print("H2-On "); } } lcd_key = read_LCD_buttons(); // read the buttons switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { digitalWrite(41, Relay_Off); lcd.setCursor(7, 1); lcd.print("H2-Off"); break; } case btnLEFT: { digitalWrite(41, Relay_On); lcd.setCursor(7, 1); lcd.print("H2-On "); break; } case btnUP: { lcd.print("UP"); break; } case btnDOWN: { lcd.print("DOWN"); break; } case btnSELECT: { if(reg==1) {reg=0; lcd.setCursor(15, 1); lcd.print("A");} else {reg=1; lcd.setCursor(15, 1); lcd.print("M");} break; } case btnNONE: { break; } } } //------------Конец ЦИКЛА----------------------------- |
|
#1957092 |
|
Посетитель
|
|
Z0RS, опишу проект - а то задаю вопросы, а народу не понятно. А вот датчик потока на 1-ой лейке. К ней подключена трубка "обратки", идущая обратно в резервуар с водой. Используется для определения того, что система наполнилась водой. Ну и поиграться с датчиком, конечно - потом при удачном его использовании буду применять для контроля автоподмен в аквариуме. |
|
#1957110 |
Постоянный посетитель
|
|
arch07 Слишком уж сложно Вы все затеваете....но "дело ваше". Для полива я применяю систему по-проще. Сердце системы - такие насосики: Это из автомагазина запчастей для ВАЗ. Цена 96 руб за шт. Я их купил уже немеряно...но попадаются и плохие....нужно проверять производителя... Насосы 12 вольтовые, создают приличное давление, и даже опрыскивать можно автомобильными же форсунками для омывания ветрового стекла...только дорабатывать надо, или из головок аэрозолей можно наделать. У насосов будут довольно стабильные показатели если их на выходе воды немного "поджать" вентилем. Расход просчитывается. Никаких дополнительных датчиков, мне например - не требуется....Поливаю и кофейные деревья, и розы и разное другое, полив из разных по составу жидкости емкостей... Система получается простая - блок питания 12 вольт, насосы, плата управления + релейный модуль....все. Никаких датчиков потока. Хотел поставить датчики влажности почвы....может для кофе и поставлю и то, только для того, чтобы сухой период соблюсти, но и не засушить дерево совсем...а для остального - без надобности. |
|
#1957134 |
Посетитель
|
|
Z0RS Система полива больше как "игрушка не на полку" и задача ставилась не сколько полить, а сколько освоить как можно больше девайсов, управляемых ардуиной. Кроме ардуинки и датчиков ничего не покупал и извлек из "закромов" - такое вот "слепило из того, что было". К тому же мне нравится её стимпанковость . А работать она будет не постоянно, а только при долгосрочном отъезде из дома, когда все растения собираются в одном месте - на солнечной стороне. В остальное время все горшки разлетаются по комнатам и поливаются женой в ручную "для души". Для того и требуется подводка часов и настройка Насосы от ВАЗ хорошая мысль для практики - приму на заметку. И напряжение небольшое и цена вкусная. Чего-то у меня часы заводится не хотят на плате. P.S. Завел часы. Получил данные с датчика потока - осталось откалибровать, но нечем точно измерить прошедший объем. Найду мерный кувшин и откалибрую. Настроил механику и серву - позиционирование достаточно точное стало. Решил переставить датчик потока на входную магистраль и отмерять точное количество жидкости в каждую лейку. Заодно проверять наличие воды и отключать все если вода не идет. Задумался над алгоритмом меню. И еще гложит мысль - надо записывать значения измененные в процессе настройки иначе при пропадании напряжения все сбросится на прошитые в скетче, а как это делать я не пока не знаю. Изменено 31.3.14 автор arch07 |
|
#1957146 |
|
Завсегдатай, Кандидат в Советники
|
|
сообщение Z0RS Тоже нашел даташит, действительно - диммирующий ток 1 мА. Придут драйверы с e-bay - проверю миллиамперметром, хотя и правда опасаться нечего. Да и еще о RTC. Как выяснилось RTC на DS1307 - не очень точны... RTC необходимо "эджастить" на одну секунду в период, за который эта секнда накапливается. Это будет правильнее...У меня когда-то были электронные часы наручные, наши Российские с такой функцией...можно было поправки вносить... А как кстати практически организовать такую поправку программным способом? Уже задумался об этом... ЗЫ. У меня тоже такие часики были, еще советские, из серии "Электроника 5" или что-то подобное. Подарили в 1986-87 году на ДР. В инструкции помню было очень подробно расписано, как вычислить поправку по "сигналам точного времени". Лепота! Вот умели же делать, не то что китаёзы. |
|
#1957160 |
Постоянный посетитель
|
|
arch07 Вариантов есть несколько: можно использовать EEPROM (EEPROM микроконтроллера; отдельную EEPROM, подключенную например по I2C; EEPROM какого нибудь модуля - типа RTC) или подключить FLASH в виде стандартной SD карты - тем более, что модулей-слотов для SD на ебее - навалом по цене от 36 рублей...Но в случае с SD - есть сложности. Если к карте обращаться через файловую систему - то подключение и использование библиотеки функций, организующее работу через FAT - съедает очень много программной памяти микропроцессора. А прямой доступ к адресному пространству подключенной карты - не каждый запрограммирует. Кстати SD карта - ничто иное, как микросхема FLASH-памяти с SPI интерфейсом, и ее можно "параллелить" с модулем EtherNet, например разведя только cs (chip select) сигнал на отдельный дискретный порт. Вообще, с EEPROM - проще...Только необходимо помнить про ограничения количества записи. Есть еще варианты, но они все больше экзотические... Дядька-рыбомор Так.... библиотечную функцию RTC.adjust() - необходимо использовать...она вызывается с аргументом в виде указателя на экземпляр класса DateTime. Конструктор класса - это перегруженная функция, т.е. может вызываться с разным набором аргументов. Все это есть в заголовочном файле библиотеки, вот три варианта объявления функции конструктора класса DataTime: DateTime (uint32_t t =0); DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour =0, uint8_t min =0, uint8_t sec =0); DateTime (const char* date, const char* time); Например, вызывая функцию установки времени равному системному времени компьютера, на котором происходит компиляция скетча RTC.adjust(DateTime(__DATE__, __TIME__)); мы используем третий вариант. А для установки времени внутри программного кода с произвольными значениями времени, т.е. для той же поправки времени - необходимо использовать 2 вариант. Можно и первый - но он программно ( в теле реализации функции) сводится ко второму, и ничего, кроме увеличения размера кода, использование этого варианта - не даст. Т.е. для поправки времени нужно сформировать переменные, указанные в аргументах данной функции, и в нужный момент вызвать функцию RTC.adjust(DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour =0, uint8_t min =0, uint8_t sec =0)); ...понятно, что со своими переменными-аргументами. Дядька-рыбомор Вот про эти самые часы я и имел ввиду....Они у меня также примерно в этоже время появились....с введенной поправкой ходили очень точно.... Да...еще помню в 87 сделал свой первый компьютер - Радио 86РК. Потом было собрано еще несколько клонов различных....На первый комп - я потратил денег, соизмеримо со стоимостью хорошего мотоцикла, что за время было....да.....мать меня чуть не убила...правда, потом не пожалела ни разу.... |
|
#1957279 |
Завсегдатай, Кандидат в Советники
|
|
сообщение Z0RS Ну, к примеру, RTC спешат (или отстают) на 1 сек в сутки. Как будет выглядеть команд ? RTC.adjust(DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour =0, uint8_t min =0, uint8_t sec =-1)); это спешат на секунду RTC.adjust(DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour =0, uint8_t min =0, uint8_t sec =1)); а это отстают. Правильно? Изменено 31.3.14 автор Дядька-рыбомор |
|
#1957312 |
|
Постоянный посетитель
|
|
Дядька-рыбомор Неет....Вы уж очень просто захотели.... Сначала необходимо прочитать время, затем сформировать новые переменные, оставив все поля такими как и прочитали, а секунды увеличить, а потом уж установить новое время, вызвав указанную мной конструкцию из 2 функций. Изменено 31.3.14 автор Moderator |
|
#1957323 |
Постоянный посетитель
|
|
Дядька-рыбомор Теперь по подробнее. Попробовал простенькую процедуру коррекции времени - как бы все работает... Принцип простой. Сначала определяем константу макроподстановки - которая определит период времени в секундах, по истечению которого, к текущему времени будет прибавлена еще одна секунда. Я для примера вычисли поправку таким образом, чтобы за один месяц прибавлялась примерно одна минута...так мои часики ходят....Ваши могут ходить по-другому. Вообще данную константу можно и в виде переменной в энергонезависимой памяти оформить, но это будет сложнее, да и нужно ли - это необходимо определять, для конкретного случая. #define TimeAdjustPeriod 43200UL Эта строка должна быть вне функции инициализации и вне функции главного цикла...лучше вверху где-нибудь. Там же где нибудь определим вспомогательну переменную: unsigned long nextAdjustTime =0; Ну в функции setup должны присутствовать стандартные для использования RTC строки: Wire.begin(); RTC.begin(); В функции главного цикла loop() мы проверяем не наступило ли время по-править часы и поправляем на одну секунду вперед....ну да на деле может потребоваться и в другую сторону по-править. И тут же вычисляем и назначаем время следующей по-правки времени. DateTime myTime = RTC.now(); unsigned long UTime = myTime.unixtime(); if (UTime > nextAdjustTime){ nextAdjustTime = UTime+TimeAdjustPeriod; RTC.adjust(DateTime(myTime.year(), myTime.month(), myTime.day(), myTime.hour(), myTime.minute(), myTime.second()+1)); } Вообще...это так...для пробы...процедуру коррекции часов, конечно, можно сделать куда более интеллектуальной... Кстати обратите внимание, какие довольно простые конструкции используются в проверке условий - это потому, что работаем с форматом времени UNIX, и тут же используем формат в виде класса DateTime с набором полей... Вообщем, где как удобно - такой формат и применяйте... Изменено 31.3.14 автор Z0RS |
|
#1957358 |
Завсегдатай, Кандидат в Советники
|
|
сообщение Z0RS Спасибо, все разжевали. Осталось проглотить Т.е. в вашем примере добавляется секунда каждые 12 часов, итого в месяц около 1 мин.. А если наоборот - RTC спешат - то ставим минус. ОК. RTC.adjust(DateTime(myTime.year(), myTime.month(), myTime.day(), myTime.hour(), myTime.minute(), myTime.second()-1)); } |
|
#1957392 |
|
Постоянный посетитель
|
|
Дядька-рыбомор Да...НО...не все так просто если вспомнить, что в минуте всего лишь 60 секунд, а именно от 0 до 59. Вообщем в реальной программе - необходимо чуть усложнить проверкой на 2 условия. Количество секунд после прибавления не должно превышать 59, или если речь идет об убавлении то не должно быть меньше 0. Я думаю, математикой можно эти условия упростить - надо будет подумать на досуге как сделать проверку этого условия очень простой..... |
|
#1957397 |
Посетитель
|
|
Z0RS А обязательно все это в RTC.adjust писать? там юникс формат нельзя использовать? - тогда секунды проверять смысла нет. все равно мы периодом ввода поправок на 1 сек задаем. |
|
#1957554 |