ARDUINO для автоматизации аквариума

<< 1234567 9101112131415 >>

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
ARDUINO для автоматизации аквариума
Создал отдельную тему по вопросам использования простых и не дорогих плат ARDUINO для целей автоматизации аквариума.
Идея такая - довести это устройство до полнофункционального контроллера для аквариума, в итоге - с WEB сервером для управления по сети в т.ч. Интернет, и GSM модулем с отправкой SMS.
Начало обсуждения в теме про дозатор :
http://www.aqa.ru/fo...

Информация будет, в основном, в виде конкретных примеров.

Пример 1. Использоание плат Arduino UNO и Arduino NANO для управления по времени суток устройствами в 2-х каналах посредством релейного модуля.
Предполагается использование для создания 2-х канального дозатора, но и для включения света, например подойдет, и для многих других целей также.

Используется модуль часов реального времени, для точного планирования по времени суток, и релейный модуль. В данном примере используется 4-канальный, но 2 канала не используются...

Схема коммутации в случае использования платы UNO R3:


Схема коммутации в случае использования платы NANO:


Программа для среды программирования Arduino:
//***************************************************
// Скетч для управления релейным модулем, в котором *
// использовано два канала. Используется также RTC *
// Релейный модуль - с инверсной логикой на входе *
// Автор: ZORS *
// Версия 1. Дата 21.12.2013 02:40 *
//***************************************************
//----------ИМПОРТ БИБЛИОТЕК-------------------------
#include <Wire.h> //Подключаем библиотеку для использования I2C интерфейса с модулем RTC
#include <RTClib.h> //Подключаем библиотеку для использования модуля часов реального времени 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 < (StartRelCn_1+DurationCh_1)))
//Если секунд с начала суток больше, чем задано для включения
//Но, одновременно и меньше, чем задано для включения + длительность
{
digitalWrite(RelayChn1,LOW); //Устанавливаем на ПЕРВОМ входе релейного модуля НИЗКИЙ уровень - реле срабатывает
}
else //во всех остальных случаях
{
digitalWrite(RelayChn1,HIGH); //Устанавливаем на ПЕРВОМ входе релейного модуля ВЫСОКИЙ уровень - реле выключается
}

//------------КАНАЛ 2 - все аналогично -----------
if ((utime >= StartRelCn_2) &&
(utime < (StartRelCn_2+DurationCh_2)))
{
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 Files\Arduino\libraries
Сюда и помещайте папки с подключаемыми библиотеками. Если на этот момент, среда программирования была запущена - ее необходимо закрыть, и запустить заново. После этого библиотека будет доступна в меню Скетч/Импортировать библиотеку.

Микросхема 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
2013-12-21 добавлено 21/12/2013 15:57:48#1907148

Посетитель




93 1
Москва
2 дн. назад
arch07


Все добавляю. как установить билиотеки я нашел. Только вот при добавлении библиотвеки LiquidCrystal_I2C из первого поста, ругается:

In file included from SunRiseSunSet.ino
/Users/Mac/Documents/Arduino/libraries/LiquidCrystal_I2C/LiquidCrystal_I2C.h error: conflicting return type specified for 'virtual void LiquidCrystal_I2C::write(uint8_t)'
/Users/Mac/Downloads/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/cores/arduino/Print.h error: overriding 'virtual size_t Print::write(uint8_t)'
2014-04-07 добавлено 07/04/2014 08:04:52#1960000

Посетитель




93 1
Москва
2 дн. назад
Как проверить жк диспей самым простым скетчем при схеме подключения из первого поста?
2014-04-07 добавлено 07/04/2014 08:09:05#1960001

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
Bwzs
Все работает.ну всмысле мигает на 13 пине. Как я понял в данном скетче нет обращения к модулю дисплея, правильно?

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

Bwzs
Все добавляю. как установить билиотеки я нашел. Только вот при добавлении библиотвеки LiquidCrystal_I2C из первого поста, ругается:


Всего скорее, эта библиотека не подойдет к вашей среде программирования. На Платформах Windows и Linux в среде программирования ARDUINO - используется компилятор имеющий одного предка, а на MacOS - другой компилятор, который может некоторые определения не понимать. Что и происходит всего скорее. Я бы конечно, в любом случае - адаптировал библиотеку, или написал бы свою.
А так, даже не знаю что Вам сказать, я не могу ответить точнее, не имея данную платформу на руках.

Можно конечно, под MacOS использовать более продвинутый и сложный продукт для программирования AVR процессоров - Atmel Studio. Компилятор в этом продукте всего скорее эту библиотеку поймет. Этот продукт можно скачать с сайта ATMEL.

Но это, все равно, что "феррари" посоветовать новичку водителю.
2014-04-07 добавлено 07/04/2014 10:57:55#1960050

Посетитель




93 1
Москва
2 дн. назад
Немного решил проблему) Поставил на мак винду (боже какая она сложная) ну да ладно.
Взял скетч автора kirex05 с этого поста http://www.aqa.ru/fo...
и в итоге он ругается выдал такую картину
2014-04-07 добавлено 07/04/2014 11:04:03#1960056

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
Bwzs

Ну компилятор ругается на отсутствие библиотеки. У Вас файл RTClib.h где располагается?
2014-04-07 добавлено 07/04/2014 15:19:31#1960180

Посетитель




93 1
Москва
2 дн. назад
с библиотекой разобрался) Теперь проблемы с инициализацией дисплея,
ругается на эту строчку
LiquidCrystal_I2C lcd(0x27,16,2); // устанавливаем адрес 0x27, и дисплей 16 символов в 2 строки (16х2)
2014-04-07 добавлено 07/04/2014 15:28:41#1960186

Посетитель




93 1
Москва
2 дн. назад
Ладно .пока,я так понял, я пришел не тот форум по этом теме) пойду на специализурующий форум по ардуино. Жаль только что покупал детали из первого поста и качал библиотеки из первого, и не работает ничего
2014-04-07 добавлено 07/04/2014 15:41:28#1960193

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
сообщение Bwzs
с библиотекой разобрался)

Да не разобрались Вы...если бы разобрались то
Вот этих проблем не было бы
сообщение Bwzs
Теперь проблемы с инициализацией дисплея, ругается на эту строчку
LiquidCrystal_I2C lcd(0x27,16,2); // устанавливаем адрес 0x27, и дисплей 16 символов в 2 строки (16х2)


Эта строчка - вызывает функцию, так называемый "конструктор" - которая создает объект класса LiquidCrystal_I2C. Этот класс объявлен в вышеуказанной библиотеке. Если по каким-то причинам компилятор не может получит доступ к библиотечным файлам, то при компиляции этой строки - он просто не поймет о чем идет речь в тексте кода, и соответственно выдаст ошибку, о том, что данный класс ему не известен. Потом, если в библиотечных файлах компилятор встретит переменные таких типов, которые ему не известны - также будет ошибка. Т.е. необходимо изучать ошибки, которые говорят о несовместимости типов, и менять типы переменных, на совместимые.
Потом, может и строка инициализации не подойти, т.к. не все дисплеи имеют адрес на I2С шине равный 0х27. Но эта ошибка не на этапе компиляции, т.к. компилятору вообще все равно, какой будет адрес у интерфейсного модуля для дисплея, о в любом случае откомпиллировал бы код.

У меня есть скетч, который перебирает адреса от 1 до 255, и когда приобретается дисплей с интерфейсом I2C и неизвестным адресом, то я запускаю этот скетч, и когда адрес совпадет, то дисплей инициализируется и значение адреса выводится на дисплей.

сообщение Bwzs
Ладно .пока,я так понял, я пришел не тот форум по этом теме) пойду на специализурующий форум по ардуино. Жаль только что покупал детали из первого поста и качал библиотеки из первого, и не работает ничего


Что я Вам могу сказать, я много программировал разные устройства промышленной автоматизации, и среды программирования использовал под разные операционные системы. И могу сказать, что в разных операционных системах, компиляторы, с виду одни и теже - ведут себя по-разному. Прежде всего, это из-за разницы архитектуры. Почему например, компиллеры под Windows и Linux на интеловой платформе - наиболее родственны.

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

Действительно....наверное, не нужно превращать аквариумистический форум в форум по программированию...

Изменено 7.4.14 автор Z0RS
2014-04-07 добавлено 07/04/2014 16:33:35#1960229

Посетитель




93 1
Москва
2 дн. назад
Z0RS


Попытки разобраться в программировании с++ не удались успехом хотел собрать код для управления светом и со2 но тут я даже не могу модули подружить, что будет дальше... А вам реально надо медаль вешать, так не расписывают даж на спец форумах.
2014-04-07 добавлено 07/04/2014 18:43:05#1960293

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
Bwzs

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

Вообще, на группе предприятий (металлургия) где я много лет работал - тысячи программируемых контроллеров. И сколько из них имеют дисплеи - знаете? НИСКОЛЬКО.
Сделать, автоматизацию света, дозаторов и т.д. - можно без дисплея.

Главное, одна мелкая проблема - не должна Вас останавливать, тогда Вы точно добьетесь определенного успеха...
2014-04-08 добавлено 08/04/2014 00:48:58#1960455

Свой на Aqa.ru




1149 188
Ростов-на-Дону
2 час. назад
сообщение Z0RS
То, что я здесь, разжевываю материал как для детей 4-5 класса средней школы - больше никто не делает....да и я это делать скоро прекращу.....

А-а-а-а-а, только не это!!!1111
Благодаря вашей теме я почти собрал (на макете пока) полностью работающий контроллер. До этого около года засматривался на Arduino, но не решался, т.к. не программист и не математик, а гуманитарий. А тут вы все разжевали, как для тупых (это хорошо!), ну а как положить в рот - это пусть каждый форумчанин решает сам, это не так трудно после ТАКИХ объяснений.
ZORS, не бросайте тему, вы еще нужны .
2014-04-10 добавлено 10/04/2014 15:39:40#1961586

Завсегдатай




460 39
Москва
3 мес. назад
Дядька-рыбомор
ZORS, не бросайте тему, вы еще нужны

Да-да! полностью поддерживаю.
2014-04-10 добавлено 10/04/2014 18:56:56#1961664

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
Дядька-рыбомор
не бросайте тему

Да... пока и не собираюсь.
2014-04-11 добавлено 11/04/2014 00:50:00#1961769

Завсегдатай




460 39
Москва
3 мес. назад
А я похвастаюсь дозатором удобрений:

Z0RS

Я пока тут разобраться хочу, но не могу.
Требуется функция аргументом которой будет 1. серва, 2. кол-во мл (а вернее качков - так откалибровано)
Проблема в том, что у каждой сервы свои значения крайних положений.

for (int i=0; i<10; i++){
myservo.writeMicroseconds(1120);
delay (1000);
myservo.writeMicroseconds(500);
delay (1000);
}

Вот это кусок кода калибровки для 1-й сервы. У второй совсем другие значения:

for (int i=0; i<10; i++){
myservo.writeMicroseconds(815);
delay (1000);
myservo.writeMicroseconds(1500);
delay (1000);
}

Планирую возможность расширения до 4 дозаторов, но как это грамотно реализовать?
Ардуино двумерные массивы поддерживает? А то в документации не найду никак про это. И наверно от задержек тут отказаться не удастся.
И еще вопросик - так как сервы всегда используются по очереди стоит ли городить огород с раздельным питанием?

Изменено 13.4.14 автор arch07
2014-04-13 добавлено 13/04/2014 21:54:49#1962874

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
arch07
Требуется функция аргументом которой будет 1. серва, 2. кол-во мл (а вернее качков - так откалибровано)
Проблема в том, что у каждой сервы свои значения крайних положений.

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

arch07
Планирую возможность расширения до 4 дозаторов, но как это грамотно реализовать?


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

arch07
Ардуино двумерные массивы поддерживает? А то в документации не найду никак про это.

Да компилятор поддерживает двумерные массивы, но в данном случае применение их не оправдано. Многомерные массивы, как правило, используются для сложной мат.обработки однородных данных. Тем более, что хранить данные заданий - лучше во флеш-памяти, а в случае, когда данные заданий просто находятся в программном коде, они будут продублированы и во флеш-памяти, и при загрузке программы в RAM - они будут скопированы в оперативку, отнимая ее полезный объем. Конечно, пока Вы не упираетесь в объем доступной RAM - об этом можно не беспокоится....

arch07
И наверно от задержек тут отказаться не удастся.

Это всегда не просто. Но всегда возможно не использовать функцию delay(). Но это - программирование на уровне очень хорошего знания Си.

arch07
И еще вопросик - так как сервы всегда используются по очереди стоит ли городить огород с раздельным питанием?

Ну это равноценные варианты....и так и эдак, особой сложности при программировании не должно вызывать.
2014-04-14 добавлено 14/04/2014 00:35:29#1962925

Постоянный посетитель




251 27
Москва
12 мес. назад
Я пока тут разобраться хочу, но не могу.

Требуется функция аргументом которой будет 1. серва, 2. кол-во мл (а вернее качков - так откалибровано)

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

for (int i=0; i<10; i++){

myservo.writeMicroseconds(1120);

delay (1000);

myservo.writeMicroseconds(500);

delay (1000);

}

Вот это кусок кода калибровки для 1-й сервы. У второй совсем другие значения:


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

struct ServoData{
int min;
int max;
Servo myservo;
int count;
};

Один раз в setup, например инициализируете нужное количество экземпляров структур, а дальше используете в функции:

void pumpTask(ServoData sData){


for (int i=0; i<sData.count; i++){
sData.myservo.writeMicroseconds(sData.min);
delay (1000);
sData.myservo.writeMicroseconds(sData.max);
delay (1000);
}

Планирую возможность расширения до 4 дозаторов, но как это грамотно реализовать?

Реализовал вам чуть выше, пользуйтесь

Ардуино двумерные массивы поддерживает? А то в документации не найду никак про это.

И не найдете, массивы реализованны средствами языка С. Массив не нужен - объявите глобально по экземпляру структуры ServoData на каждую серву.

И наверно от задержек тут отказаться не удастся.

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

И еще вопросик - так как сервы всегда используются по очереди стоит ли городить огород с раздельным питанием?

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

Изменено 14.4.14 автор SilverSwift
2014-04-14 добавлено 14/04/2014 10:25:06#1962980
Нравится arch07

Завсегдатай




460 39
Москва
3 мес. назад
SilverSwift

Спасибо, но я немного не понял принцип. А может понял... Если я хочу хранить переменную count не в структуре, то тогда функция будет вида:

void pumpTask(ServoData sData, count){
for (int i=0; i<count; i++){
sData.myservo.writeMicroseconds(sData.min);
delay (1000);
sData.myservo.writeMicroseconds(sData.max);
delay (1000);
}

А вызов для 4-й сервы на 3 качка будет выглядеть так:

pumpTask(ServoData4, 3);

где ServoData4 - структура для 4-й сервы:

struct ServoData4{
int min = 500;
int max = 1250;
Servo myservo4;
};

а в:
setup {
myservo4.attach(7); //седьмой pin к 4 серве.
}

Нигде не напутал?

p.s. где-то напутал. myservo из функции в структуре нету.... как правильно объявить конкретную серву в структуре, что бы ее использовать в функции?

Servo myservo = myservo4; - так?

Изменено 16.4.14 автор arch07
2014-04-16 добавлено 16/04/2014 21:05:47#1964104

Постоянный посетитель




251 27
Москва
12 мес. назад
Если я хочу хранить переменную count не в структуре

Чем обосновано такое желание? В большинстве случаев это не рационально.

void pumpTask(ServoData sData, count){

for (int i=0; i<count; i++){

sData.myservo.writeMicroseconds(sData.min);

delay (1000);

sData.myservo.writeMicroseconds(sData.max);

delay (1000);

}

Правильно, но см. выше.


А вызов для 4-й сервы на 3 качка будет выглядеть так:

pumpTask(ServoData4, 3);

где ServoData4 - структура для 4-й сервы:

struct ServoData4{

int min = 500;

int max = 1250;

Servo myservo4;

};


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

Если говорить доступным языком, отбрасывая некоторые детали, то структура ServoData - это что-то вроде типа данных, который мы определили сами. Структуру достаточно определить один раз глобально (в начале файла вне функций) и далее ее можно использовать для создания переменных типа ServoData, такие переменные (определенных пользователем типов) называют экземплярами.
Структура имеет так называемые поля (min, max, myservo, count) различных типов (int, Servo). Обратиться к каждому полю можно по имени экземпляра с помощью оператора ".". Например:


//объявление структуры
struct ServoData{
int min;
int max;
Servo myservo;
int count;
};
//создаем экземпляр sData структуры ServoData
ServoData sData;

sData.min = 10; // получаем доступ к полю min, экземпляра структуры sData

Так вот, чтобы работать с несколькими сервами надо:
* объявить структуру;
* создать нужное количество экземпляров структур;
* инициализировать каждый экземпляр.

Для примера, работа с двумя сервами:

//объявление структуры
struct ServoData{
int min;
int max;
Servo myservo;
int count;
};

ServoData sData1; //создаем экземпляр для первой сервы
ServoData sData2; //создаем экземпляр для второй сервы

void pumpTask(ServoData sData){

for (int i=0; i<sData.count; i++){
sData.myservo.writeMicroseconds(sData.min);
delay (1000);
sData.myservo.writeMicroseconds(sData.max);
delay (1000);

}

void setup(){
sData1.min = 815; //задаем минимальную частоту ШИМ
sData1.max = 1500; //задаем максимальную частоту ШИМ
sData1.myservo.attach(servo1Pin); //servo1Pin - номер ноги контроллера к которой подцеплен сигнальный шнурок сервы
sData1.count = 5; //покачать 5 раз

sData2.min = 500; //задаем минимальную частоту ШИМ
sData1.max = 1120; //задаем максимальную частоту ШИМ
sData1.myservo.attach(servo2Pin); //servo2Pin - номер ноги контроллера к которой подцеплен сигнальный шнурок сервы
sData1.count = 3; //покачать 3 раза
}

void loop(){

//некоторое условие при выполнении которого надо покачать первой сервой
......
.....
{
pumpTask(sData1); //качаем так как описано в структуре
}

//некоторое условие при выполнении которого надо покачать второй сервой
//и изменить начальное число качков на 10
......
.....
{
sData2.count = 10; //записываем 10 качков вместо старого значения
pumpTask(sData2); //качаем
}

}


Может компилятор ругнется на что-то, пришлите листинг ошибки - решим.
2014-04-17 добавлено 17/04/2014 09:53:21#1964233

Завсегдатай




460 39
Москва
3 мес. назад
SilverSwift

Ага - понял... теперь все по полочкам разложилось в мозге...

А насчет кол-ва качков - собираюсь не хранить их в структуре, потому как структура пишется в проге и описывает конкретное аппаратное устройство.
Изменение требуется только при физической замены сервы (ну тогда можно и новую прошивку залить). А кол-во качков (доза удобрений) величина переменная и зависит от многих факторов - света, кол-ва растений, и пр. Я собираюсь делать эти значения настраиваемые и хранить либо на флешке либо энергонезависимой памяти.

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

И прошу у всех прощения, что подолгу не могу ответить всем, кто мне помогает - весна, строительное обострение. Спать то некогда
2014-04-17 добавлено 17/04/2014 11:52:13#1964275

Новичок




46
Germany
2 г. назад
Собранный контроллер работает без проблем уже 3-ю неделю. Я всем доволен, единственное есть один нюанс. В установках #define PWM_LW_MAX 120, т.е. убавлен почти наполовину. При выключении света в автоматическом режиме он сначала вспыхивает почти до максимума, а потом начинает как и положено медленно гаснуть. Вопрос: почему появляется вспышка.
Вот кусок кода:

#define PWM_LW_MIN 0 //Если необходим ток покоя на LED - изменить эту константу
#define PWM_LW_MAX 120//Если необходимо ограничить максимальную яркость - уменьшить значение
#define PWM_LW_PIN 11 //Пин порта, где будет ШИМ LW

#define PWM_LR_MIN 0 //Если необходим ток покоя на LED - изменить эту константу
#define PWM_LR_MAX 150//Если необходимо ограничить максимальную яркость - уменьшить значение
#define PWM_LR_PIN 3 //Пин порта, где будет ШИМ LR

#define PWM_LB_MIN 0 //Если необходим ток покоя на LED - изменить эту константу
#define PWM_LB_MAX 230 //Если необходимо ограничить максимальную яркость - уменьшить значение
#define PWM_LB_PIN 12 //Пин порта, где будет ШИМ LB

#define mn 60UL //Дополнительные константы для удобства
#define hr 3600UL //Отражают соответствующие количества секунд
#define d 86400UL

long sunrise_start = 13*hr+00*mn; //Начало восхода в 13-00
long sunrise_duration = 2*mn; //Длительность восхода 2 минуты
long sunset_start = 22*hr+00*mn; //Начало заката в 22-00
long sunset_duration = 3*mn; //Длительность заката 3 минуты
long moonrise_start = 22*hr+00*mn ;//Начало луны в 22-00
long moonrise_duration = 3*mn;//Длительность восхода луны 3 минуты
long moonset_start = 23*hr+00*mn;//Конец луны в 23-00
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);

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()<10)lcd.print(0);
lcd.print(myTime.hour(),DEC);
lcd.print(":");
if(myTime.minute()<10)lcd.print(0);
lcd.print(myTime.minute(),DEC);

if (reg==0){
//*********************************************************************************************
// обработка интервала до восхода и после заката
//*********************************************************************************************
if ((Day_time<sunrise_start) ||//Если с начала суток меньше чем начало восхода
(Day_time>=sunset_start+sunset_duration)) { //Или больше чем начало заката + длительность
pwm_LW = PWM_LW_MIN; //Величина для записи в порт равна минимуму
pwm_LR = PWM_LR_MIN;

//*********************************************************************************************
// обработка интервала восхода
//*********************************************************************************************
}else if ((Day_time>=sunrise_start) && //Если с начала суток больше чем начало восхода
(Day_time<sunrise_start+sunrise_duration)){ //И меньше чем начало восхода + длительность

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<sunset_start+sunset_duration)){//начало заката плюс длительность

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<moonrise_start) || //Если с начала суток меньше чем начало восхода
(Day_time>=moonset_start+moonset_duration)) {//Или больше чем начало заката + длительность
pwm_LB = PWM_LB_MIN; //Величина для записи в порт равна минимуму

//*********************************************************************************************
// обработка интервала восхода луны
//*********************************************************************************************
}else if ((Day_time>=moonrise_start) &&//Если с начала суток больше чем начало восхода
(Day_time<moonrise_start+moonrise_duration)){ //И меньше чем начало восхода + длительность

pwm_LB = ((Day_time - moonrise_start)*(PWM_LB_MAX-PWM_LB_MIN)) / moonrise_duration; //Вычисляем для рассвета величину для записи в порт ШИМ

//*********************************************************************************************
// обработка интервала заката луны
//*********************************************************************************************
}else if ((Day_time>=moonset_start) && //Если начала суток больше чем начало заката и меньше чем
(Day_time<moonset_start+moonset_duration)){//начало заката плюс длительность

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); //Пишем в порт вычисленное значение
}
2014-04-17 добавлено 17/04/2014 15:16:08#1964352

Завсегдатай




460 39
Москва
3 мес. назад
Пока есть время - делается крышка аквариума, сижу прорабатываю концепцию контроллера.
Столкнулся с тем, что дисплей 20х4 уже маловат.
Неохота делать кнопки для прокрутки информации - решил добавить еще дисплейчик:
Нокиа 5110 (3110) - болтался у меня такой, но пожег при пайке, заказал еще парочку по $2,6.
Как графический - фигня, но как текстовый 6 строк по 14 символов. Подключал через микруху 4050 - для подачи 3,3 в.
Как описано В Adafruit. Единственно мой вариант имеет другую распиновку и подсветка подключается к земле.
И торетически можно русский прикрутить, но не уверен что надо - т.к. по русски больше букв требуется.

В принципе, контроллер готов. На макетке с кучей проводов. Работает. Но для смены настроек надо перешивать.
Нагрузка пока тоже "на весу". Умеет включать/выключать насос по таймеру, поддерживать температуру радиатора включением/выключением вентиляторов. Поддерживать температуру аквариума включением/выключением нагревателя. Включать/выключать СО2 по времени и сверяясь с наличием/отсутствием света (по фоторезистору). Подавать удобрения по расписанию.

Пока не умеет, но будет кормить рыбу - автокормушка в проекте, причем по концепции она должна кормить за полчаса до заката, если только я не покормил "вручную".
Еще осталось прикрутить контроль уровня воды (жду датчик), контроль объема воды. И уговорить жену дать проложить шланг для автоподмены

Пока смотрю в сторону web интерфейса для настроек. Клавиатура хотя и едет из китая, но по-моему менее удобна. Пока оставил только кнопки непосредственного управления - прокачать удобрение, пропустить кормление.
2014-04-20 добавлено 20/04/2014 12:27:21#1965189

Постоянный посетитель




251 27
Москва
12 мес. назад
Вопрос: почему появляется вспышка.

Потому что есть ошибка.
Вот кусок кода:

Но не в приведенном "куске кода".

Пока смотрю в сторону web интерфейса для настроек.


Придется впиливать отдельное устройство с поддержкой TCP/IP, чтобы поднять на нем сервер. Я бы реализовал конфигурацию по UART и написал ПО для ПК. Учитывая, что настройки менять придется редко, можно ограничиться конфигурацией по UART через эмулятор терминала типа putty.
2014-04-20 добавлено 20/04/2014 20:06:52#1965354

Завсегдатай




460 39
Москва
3 мес. назад
SilverSwift
Придется впиливать отдельное устройство с поддержкой TCP/IP, чтобы поднять на нем сервер. Я бы реализовал конфигурацию по UART и написал ПО для ПК. Учитывая, что настройки менять придется редко, можно ограничиться конфигурацией по UART через эмулятор терминала типа putty.

Отдельное устройство?
Реализовать с помощью Ethernet шилд W5100 не получится? Он уже есть
В принципе состояние я уже считываю и отображаю.
Сейчас разбираюсь как передать со страницы в контроллер.

У меня проект не для массового воспроизводства, а "для души" с элементами стимпанка

Тут искал интересные решения для автокормушки. Понрвилось:
1. Шнековая кормушка. Массово используется для кормления прудовой рыбы. Но вариант металлического шнека не устраивает (обычно используют сверло), а пластиковый неоткуда взять. Если придумаю как изготовить шнек - реализую.
2. Кормушка типа rondomatic 400, но сложно в изготовлении. Цена родной 20-40 евро отпугивает. Хотя принцип очень нравится.
3. Кормушка на сервомашинке, по принципу переворачиваемой сахарницы. Ограничение корма - трубочкой и прорезью в ней внутри емкости. Очень просто реализуется. Можно дополнить второй машинкой, которая будет открывать крышку аквариума.
4. И вот лидер моего рейтинга, мне прям загорелось. Но боюсь жена такой агрегат в холле не примет категорически.


Проблемы пока одни - высокая влажность в квартире, от которой намокает не закрытый корм. И какие-то жучки, которые благополучно пожрали не закрытые таблетки для сомов. Поэтому кормушка должна быть закрыта от атмосферного воздуха да еще и работать с разными видами корма от таблеток до гранул и хлопьев. По этой причине отпали ротационные, так распрстранненые сейчас кормушки.
2014-04-21 добавлено 20/04/2014 23:49:41#1965440

Постоянный посетитель




251 27
Москва
12 мес. назад
Отдельное устройство?

Реализовать с помощью Ethernet шилд W5100 не получится? Он уже есть

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

Ардуина в принципе не потянет такое ПО. Поэтому нужен софт для конфигурации (с возможностью посмотреть текущие настройки и записать новые) либо через Ethernet, либо через UART. Путь через Ethernet видится избыточным, когда можно тыкнуть шнурок в usb порт ноута и даже через arduino ide читать и писать в UART.

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

О кормушках тоже думал, но вариант 3 не встречал
3. Кормушка на сервомашинке, по принципу переворачиваемой сахарницы. Ограничение корма - трубочкой и прорезью в ней внутри емкости. Очень просто реализуется. Можно дополнить второй машинкой, которая будет открывать крышку аквариума.

Дадите ссылку?
2014-04-21 добавлено 21/04/2014 09:31:54#1965480

Завсегдатай




460 39
Москва
3 мес. назад
SilverSwift
Дадите ссылку?

В принципе готовой конструкции тоже не встречал, но это компиляция из ротационной, сахарницы с дозатором и такого механизма:

или вот еще пример, но там сама баночка без дозатора, зато реализована доп. крышечка, которая может закрывать отверстие в крышке аквариума:
2014-04-21 добавлено 21/04/2014 10:29:39#1965501
Нравится SilverSwift

<< 1234567 9101112131415 >> Создать новую темуБыстрый ответ