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

<< 2345678 101112131415 >>

Свой на 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

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




251 27
Москва
12 мес. назад
Теперь ясно. Это слишком не точный способ дозировки корма, я не рискну такое применить у себя. rondomatic 400 понравилась больше всех, но ее надо наполнять, а это лениво
2014-04-21 добавлено 21/04/2014 11:32:34#1965517

Завсегдатай




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

Для однородного корма в виде мелких гранул очень точный. Но такой корм обычно для донных рыб.
Есть еще вариант по принципу шнека, но вместо архимедова винта по трубке ходит поршень (его сервой можно привести в действие). Плохо, что все равно слежавшийся корм не провалится в подаватель.

Насчет наполнения рондоматика - Зато можно разнообразить корм, и спокойно давать как хлопья, так и гранулы, так и таблетки в одной порции. Плохо, что порции не герметичны. Для меня это проблема - корм отсыревает.

По тому же принципу, как и рондоматик есть вариант не кругового, а линейного конвейера. но крышечки вызывают проблему сложностью изготовления и способа подачи. Хоть робота строй.
2014-04-21 добавлено 21/04/2014 11:43:34#1965518

Новичок




46
Germany
2 г. назад
сообщение SilverSwift
Вопрос: почему появляется вспышка.

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

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


Подскажите где именно


#include <Wire.h>//Подключаем библиотеку для использования I2C интерфейса с модулем RTC
#include "RTClib.h"//Подключаем библиотеку для использования модуля часов реального времени RTC

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

#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 < 50) return btnRIGHT;
if (adc_key_in < 200) return btnUP;
if (adc_key_in < 400) return btnDOWN;
if (adc_key_in < 600) return btnLEFT;
if (adc_key_in < 850) return btnSELECT;
return btnNONE; // when all others fail, return this...
}


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

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

#define PWM_LB_MIN 0 //Если необходим ток покоя на LED - изменить эту константу
#define PWM_LB_MAX 220 //Если необходимо ограничить максимальную яркость - уменьшить значение
#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 = 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);
if (! RTC.isrunning()) {
Serial.println("RTC is NOT running!");
RTC.adjust(DateTime(__DATE__, __TIME__));
}//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()<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); //Пишем в порт вычисленное значение
}

byte i;
byte type_s;
byte data[12];
byte addr[8];
float celsius, fahrenheit;

// Ищем алрес датчика
if ( !ds.search(addr)) {
Serial.println("No more addresses.");
ds.reset_search();
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 < 9; i++) {
data[i] = ds.read(); // Заполняем массив считанными данными
}

// Данные о температуре содержатся в первых двух байтах, переведем их в одно значение и преобразуем в шестнадцатиразрядное число
unsigned int raw = (data[1] << 8) | data[0];
// Переводим температуру в шкалы по Цельсию и Фаренгейту
if (type_s)
{
raw = raw << 3;
}
if (data[7] == 0x10)
{
raw = (raw & 0xFFF0) + 12 - data[6];
} else {
byte cfg = (data[4] & 0x60);
if (cfg == 0x00)
raw = raw << 3;
else if (cfg == 0x20)
raw = raw << 2;
else if (cfg == 0x40) \
raw = raw << 1;
}

// Вывод текущего значения температуры на дисплей
celsius = (float)raw / 16.0;
lcd.setCursor(0, 0);
lcd.print("T=");
lcd.setCursor(2, 0);
lcd.print(celsius);
delay (10);

if (reg==0){ // Если регулирование автоматическое -регулируем по температуре
lcd.setCursor(15, 1);
lcd.print("A");

// Если температура достигает 28,8 (с погрешностью), отключаем кипятильник Н1
if (celsius > 28.8)
{
digitalWrite(40, Relay_Off);
lcd.setCursor(0, 1);
lcd.print("H1-Off");
}

// Если температура достигает 29 (с погрешностью), отключаем кипятильник Н2
if (celsius > 29.0)
{
digitalWrite(41, Relay_Off);
lcd.setCursor(7, 1);
lcd.print("H2-Off");
}
// Если температура падает до 28,5 (с погрешностью), включаем оба кипятильника
if (celsius < 28.5)
{
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.setCursor(9, 0);
lcd.print("L");
for(int fadeValue = 0 ; fadeValue <= PWM_LW_MAX; fadeValue +=1)
{ analogWrite(PWM_LR_PIN, fadeValue);
analogWrite(PWM_LW_PIN, fadeValue);
delay(200);}
break;
}
case btnDOWN:
{
lcd.setCursor(9, 0);
lcd.print("0");
for(int fadeValue = PWM_LW_MAX ; fadeValue >= 0; fadeValue -=1)
{ analogWrite(PWM_LW_PIN, fadeValue);
analogWrite(PWM_LR_PIN, fadeValue);
delay(150);}
break;
}
case btnSELECT:
{
lcd.setCursor(9, 0);
lcd.print(" ");
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;
}
}
}
//------------Конец ЦИКЛА-----------------------------
2014-04-23 добавлено 23/04/2014 16:57:06#1966225

Завсегдатай




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

Я нашел шнек из пластика! Они водятся в катриджах лазерных принтеров.
Добыл уже. В нашем офисном куосейро аж 2 диаметра шнека на одной оси.

Теперь проблемка в подключении шагового двигателя. Почему шагового? Потому что откопал в закромах, плюс он достаточно удобный в плане монтажа и судя по даташиту даже не требует редуктора и работает от 5v.
Единственно - подключение. Драйвер из китая долго ждать - минус. Зато в закромах, где я нарыл мотор была еще и платка.
Разобрался, что двигатель (однополярный) - 6 выводов. Был подключен через транзисторы - 4 шт. Судя по даташиту на них это не совсем транзисторы, а аж 2 транзистора в одном - каскадом.
В инете видел несколько видео, где такое подключение реализуется, а вот схемку внятную нарыть не могу.
2014-04-24 добавлено 24/04/2014 15:32:50#1966555

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




251 27
Москва
12 мес. назад
Подскажите где именно


Все оказалось проще - банальная опечатка из-за копипаста кода.
Смотрим внимательно и видим:

//*********************************************************************************************
// обработка интервала заката
//*********************************************************************************************
}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;



Для pwm_LW получается что в первой скобке значение от 0 до 180, во второй всегда 140 и все это всегда делится на 120. В итоге при значении 180 в первой скобке получается pwm_LW получается 210, при том, что максимальное значение pwm_LW вы задаете как 140.

Я нашел шнек из пластика! Они водятся в катриджах лазерных принтеров.
Добыл уже. В нашем офисном куосейро аж 2 диаметра шнека на одной оси.

Поздравляю! А где в самом картридже шнек? Там разве не тупо банка с чернилами + сопла? В общем поясните сей момент

Теперь проблемка в подключении шагового двигателя.

l239d + любой мотор из китайской игрушки не проще ли взять и не мучиться? Из нее же можно выковырнуть редуктор или червяк.
2014-04-24 добавлено 24/04/2014 16:30:27#1966574

Новичок




46
Germany
2 г. назад
сообщение SilverSwift
Подскажите где именно


Все оказалось проще - банальная опечатка из-за копипаста кода.
Смотрим внимательно и видим:

//*********************************************************************************************
// обработка интервала заката
//*********************************************************************************************
}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;



Для pwm_LW получается что в первой скобке значение от 0 до 180, во второй всегда 140 и все это всегда делится на 120. В итоге при значении 180 в первой скобке получается pwm_LW получается 210, при том, что максимальное значение pwm_LW вы задаете как 140.


Если честно, то не совсем понял где именно ошибка и как её исправить.
Подскажите пожалуйста где и как.
2014-04-24 добавлено 24/04/2014 18:46:06#1966629

Завсегдатай




460 39
Москва
3 мес. назад
SilverSwift
Поздравляю! А где в самом картридже шнек? Там разве не тупо банка с чернилами + сопла? В общем поясните сей момент

Ключевое слово: ЛАЗЕРНЫЙ. Из бункера порошек подает.

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

Вот NPN транзисторы: http://doc.chipfind....
Вот моторчик KP39EM2-801: http://www.motorbank...
Вот вроде подходящая схема:


Единственно непонятно зачем реле по питанию? И его я просто проигнорирую.
Интересно нигде не накосячил?
2014-04-24 добавлено 24/04/2014 18:56:56#1966633

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




251 27
Москва
12 мес. назад
Вместо

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;


надо написать
pwm_LW = ((sunset_start+sunset_duration - Day_time)*(PWM_LW_MAX-PWM_LW_MIN)) / sunset_duration; //Вычисляем для заката величину для записи в порт ШИМ
pwm_LR = ((sunset_start+sunset_duration - Day_time)*(PWM_LR_MAX-PWM_LR_MIN)) / sunset_duration;


Изменено 24.4.14 автор SilverSwift
2014-04-24 добавлено 24/04/2014 18:57:23#1966634

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




251 27
Москва
12 мес. назад
Ключевое слово: ЛАЗЕРНЫЙ. Из бункера порошек подает.


Не разбирал никогда, надо будет поискать использованные.

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

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

Единственно непонятно зачем реле по питанию? И его я просто проигнорирую.
Интересно нигде не накосячил?

С шаговиками не игрался, не скажу что там и как, но вот статья http://www.azega.com... может как поможет
2014-04-24 добавлено 24/04/2014 19:12:24#1966636

Завсегдатай




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

Заработал мотор! Слаботочный и слабенький. Зато тихий!
Благо от него шестерни редуктора живы. Буду с ними собирать. Реальный стимпанк получится.
Схема живая. Я так понял реле там тупо для отключения мотора стоит.

Использовал стандартную библиотеку Stepper. Файлы примеров заработали.

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

P.P.S. Я итак до того как Z0RS втянул меня в ардуиностроительство заказал LED свет с БТ контроллером у DNK. Не кошерно конечно, но не выкидывать же. Тем более там программа настроек из телефона удобная. С рассветами - закатами отдельно по каждому каналу. Да и денег этот контроллер стоит немалых. Так что ардуино проект не такой уж крупный .

Изменено 24.4.14 автор arch07
2014-04-24 добавлено 24/04/2014 20:41:51#1966660

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




251 27
Москва
12 мес. назад
Заработал мотор!

Поздравляю!

Я итак до того как Z0RS втянул меня в ардуиностроительство заказал LED свет с БТ контроллером у DNK. Не кошерно конечно, но не выкидывать же. Тем более там программа настроек из телефона удобная. С рассветами - закатами отдельно по каждому каналу.

Что за контроллер? Не слышал про такой.

Сегодня начал делать новую раму для света из алюминиевого профиля. Хочу поднять лампы повыше, чтобы не грели воду, заодно впилю охлаждение и возможно какое-нибудь программное управление забацаю. Так что слово за слово я втягиваюсь в свой домашний проект по разработке цифрового модуля для аквариума =)
2014-04-24 добавлено 24/04/2014 21:32:28#1966669

Завсегдатай




460 39
Москва
3 мес. назад
SilverSwift
Что за контроллер? Не слышал про такой.


Он пропритарный - спецом разработан для проекта DNK. И заточен под его сборки. Зато поставляется сразу с софтом управления.
Вот сайт, где он своим светом торгует: http://reefll.com/in...

Я пока буду проверять наличие света фоторезистором для включения СО2 и при подаче удобрений.

А быстро ты ошибку нашел... респект.
2014-04-24 добавлено 24/04/2014 22:16:33#1966679
Нравится SilverSwift

Свой на Aqa.ru




1149 188
Ростов-на-Дону
2 дн. назад
Намедни собрал основную часть контроллера, используя в качестве "шилда" монтажную платку. На ней расположен модуль RTC, развязка для термодатчика, выводы для подключения релейного модуля и 2-х каналов ШИМ.

Скетч можно скачать по ссылке http://my-files.ru/d...
Скетч создан на основе рекомендаций ZORS. Критика, внесение изменений для конкретных задач приветствуются, скетч не отличается изяществом или оригинальностью, но вполне работоспособен. Программа умеет:
1. Отображать на экране время, дату, температуру, включение кулеров (загорается значок "*"), отображение включение/выключение СО2.
2. Работает с тремя реле, в данном случае с реле температуры (включает кулеры, но только в определенное время, ночью не включаются чтобы не жужжали), реле СО2 и реле, выключающим питание драйверов (ну вот так я хочу).
3. ШИМ по 2-м каналам
4.Коррекция "убегания" RTC
Пока еще нет самих драйверов (еще идут с гонконга), но схема работает напрямую с 2-мя маломощными диодами через 200 ом на макете. Драйверы расположу на отдельной плате (придется травить платку, давненько не делал этого). Как будет все "в коробочке" - выложу.
ZORS - еще раз спасибо за реальную помощь.
ЗЫ. Скетч не окончательный. Сейчас думаю над работой с тактовыми кнопками (вкл/выкл СО2 в ручном режиме, ручное включение небольшой ночной подсветки и пр.)

Изменено 25.4.14 автор Дядька-рыбомор
2014-04-25 добавлено 25/04/2014 18:33:05#1966898

Свой на Aqa.ru




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

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

Но пару замечаний по оформлению кода все же сдалаю, и позволю себе привести цитату из культовой книги Кернигана и Ритчи:

"Наводнять программу явными числовыми константами типа 200 или 30 - это дурной тон. поскольку они практически ничего не говорят постороннему человеку, взявшемуся читать такую программу. К тому же эти константы трудно выискивать и изменять по всему тексту единообразно, в случае необходимости. Один из подходов к решению проблемы числовых констант состоит в том, чтобы давать им значащие имена. Строка #define определяет символическое имя или символическую константу...."

И напомню еще раз, если самому лениво делать отступы в оформлении кода - есть в среде программирования в меню пункт - АВТОФОРМАТИРОВАНИЕ. Это действие - делает код читабельнее....
2014-04-26 добавлено 25/04/2014 23:09:26#1966996

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




251 27
Москва
12 мес. назад
Если по существу, то код можно оптимизировать, например, вместо:

if (myTime.month() == 1) lcd.print ("Jan");
if (myTime.month() == 2) lcd.print ("Feb");
if (myTime.month() == 3) lcd.print ("Mar");
if (myTime.month() == 4) lcd.print ("Apr");
if (myTime.month() == 5) lcd.print ("May");
if (myTime.month() == 6) lcd.print ("Jun");
if (myTime.month() == 7) lcd.print ("Jul");
if (myTime.month() == 8) lcd.print ("Aug");
if (myTime.month() == 9) lcd.print ("Sep");
if (myTime.month() == 10) lcd.print ("Oct");
if (myTime.month() == 11) lcd.print ("Nov");
if (myTime.month() == 12) lcd.print ("Dec");

Просится объявление в глобальной области массива:

const char* months[] = {"Jan", "Feb", "Mar",
"Apr", "May", "Jun",
"Jul","Aug", "Sep",
"Oct", "Nov", "Dec"};

И вызов
[q] lcd.print (months[myTime.month()-1]);[/q]
Вместо десятка одинаковых ифов.

Вместо
//Установки для отображения реального времени на дисплее
lcd.setCursor(0, 0);
if (myTime.hour() < 10) lcd.print ("0");
lcd.print(myTime.hour());
lcd.print(':');
if (myTime.minute() < 10) lcd.print ("0");
lcd.print(myTime.minute());
lcd.print(':');
if (myTime.second() < 10) lcd.print ("0");
lcd.print(myTime.second());
lcd.setCursor(0, 1);
if (myTime.day() < 10) lcd.print ("0");
lcd.print(myTime.day());
lcd.setCursor(3, 1);


Можно добавить в начало кода:
#include <stdio.h>


а сами вызовы по установке времени заменить на:
[q]//Установки для отображения реального времени на дисплее
lcd.setCursor(0, 0);
char time[8];
sprintf(time,"%02d:%02d:%02d",myTime.hour(), myTime.minute(),myTime.second());
lcd.print(time);
lcd.setCursor(0, 1);
char day[2];
sprintf(day,"%02d",myTime.day());
lcd.print(day);

lcd.setCursor(3, 1);//нужен ли этот вызов в этом месте?[/q]

В целом управление кулерами, светом и вывод на дисплей лучше вынести в отдельные функции, а в loop оставить только вызовы этих функций. Код станет проще для восприятия и работы с ним.
"Магические числа" типа 86400 лучше описать в виде констант, чтобы потом через месяц самому не вспоминать откуда это число взялось.
Например:
int hr = 3600;
int min = 60;
int startTime = 15*hr+25*min;

много понятнее чем
int startTime = 55500ж

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

Сейчас думаю над работой с тактовыми кнопками (вкл/выкл СО2 в ручном режиме, ручное включение небольшой ночной подсветки и пр.)

С этим проблем быть не должно главное подавить дребезг. Есть варианты, от банального dealy() в конце каждой итерации loop, до обработки прерываний контроллера (ардуязыком не поддерживается, но может быть описано на сишке).

Изменено 25.4.14 автор SilverSwift
2014-04-26 добавлено 26/04/2014 00:56:32#1967007

Свой на Aqa.ru




1149 188
Ростов-на-Дону
2 дн. назад
сообщение Z0RS
Для начала - очень не плохо, хоть это практически стопроцентный копипейст ...

АГА. И я в этом сознаюсь. Например, управление ШИМ просто тупо содрано. Но есть и "оригинальные авторские наработки" . Первые в своей жизни строки скетча написал лишь 2 мес назад. И потом, у меня же нет задачи стать профессиональным программером (да и времени нет, еще есть работа, дом, семья и пр.), а так, побаловаться для души, собрать очередную прибамбасину для акваса, включить - и забыть.

И напомню еще раз, если самому лениво делать отступы в оформлении кода - есть в среде программирования в меню пункт - АВТОФОРМАТИРОВАНИЕ. Это действие - делает код читабельнее..

А за это отдельное спасибо. Мучался. Не знал.

SilverSwift

Отлично! У самого ума не хватило разделаться с "ифами", так добрый человек подсказал.

По кнопкам мне кажется тут неплохая информация есть. На макете уже собирал с коротеньким скетчем, вроде работает без дребезга. Кстати, рекомендация от начинающего для еще более начинающих: очень удобно работать с "тренирующими" короткими скетчами, например для управления ЛСД, или часиками и пр. Потом эти "наброски" можно с успехом использовать в "большой" программе, главное правильно вставить.

"Магические числа" типа 86400 лучше описать в виде констант, чтобы потом через месяц самому не вспоминать откуда это число взялось.

Сделаем. Есть второй рабочий вариант этого скетча с константами типа (x*hr+y*mn) для "опытов". Просто решил попробовать в секундами UNIX поработать.
И кстати, сразу вопрос по времени. Несмотря на все коррекции, часики немного, но убегают. Попытался сделать скетч, когда нажатие кнопки приводит время к 21:00:00 (по часикам программы "Время", к примеру), но он не заработал, вернее отсылал меня к февралю 2099 года.
2014-04-26 добавлено 26/04/2014 07:18:04#1967014

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
SilverSwift
Использовать макрос #define не рекомендую - потенциальный источник неуловимых ошибок, связанных с не явным преобразованием типов, лучше константные переменные нужного типа.

Что значит "преобразования типов"? При применении директивы препроцессора #define происходит банальное - замена символьного имени на символьное значение указанное в левой части директивы, и сам препроцессор - никаких преобразований типов не осуществляет, т.к. это всего лишь программа символьной обработки текста. А вот уже вслед за препроцессором с кодом начинает работать компилятор.
Т.е. компилятор будет работать уже с результирующей строкой, которую создаст препроцессор с учетом директив. И если Вы не правильно использовали данную директиву, также как и другие директивы препроцессора, то компилятор может получить на входе строку с какой либо неоднозначностью в применении значения того или иного типа. Таким образом ошибка при использовании данной директивы - это ошибка программиста в чистом виде.
При описании числовых констант - необходимо использовать форматирующие модификаторы U, L и правильно выбирать формат описания констант. Тогда ошибок при компиляции не будет.
А без данной директивы написание некоторых программ, требующих постоянной адаптации - очень сложное дело.


SilverSwift
Есть варианты, от банального dealy() в конце каждой итерации loop, до обработки прерываний контроллера (ардуязыком не поддерживается, но может быть описано на сишке).

Я не понимаю, что Вы здесь высказали. Вообще, "ардуязык" - как вы его назвали - это один из диалектов Си и есть.
И прерывания прекрасно поддерживаются. Как вариант - можно "отобрать" прерывание по одному из 3-х системных таймеров, который можно запрограммировать на период, который требуется. Но в замен придется отказаться от ШИМ на некоторых выводах. Придет железо - я сделаю пример, где будет матричная клавиатурка, а дребезг будет подавляться периодическим опросом по прерыванию системного таймера.

Дядька-рыбомор
Попытался сделать скетч, когда нажатие кнопки приводит время к 21:00:00 (по часикам программы "Время", к примеру), но он не заработал, вернее отсылал меня к февралю 2099 года.

Не вдаваясь в детали использования кнопки, я так понимаю, что у Вас проблема сформировать время в Формате Unix которое бы отражало текущую дату и время 21:00:00 в текущей дате?

Вообще, если прочитать в некоторую переменную текущее время в формате Unix, затем вычесть из нее остаток от деления этой переменной на количество секунд в сутках, то получим в текущих сутках время 00:00:00. Затем если к полученному значению добавить количество секунд в 21 часе, то получим в текущих сутках время 21:00:00, что и требовалось.
Останется только применить команду установки часов.



Изменено 27.4.14 автор Z0RS
2014-04-27 добавлено 27/04/2014 02:58:25#1967197

Новичок




46
Germany
2 г. назад
сообщение SilverSwift
Вместо

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;


надо написать
pwm_LW = ((sunset_start+sunset_duration - Day_time)*(PWM_LW_MAX-PWM_LW_MIN)) / sunset_duration; //Вычисляем для заката величину для записи в порт ШИМ
pwm_LR = ((sunset_start+sunset_duration - Day_time)*(PWM_LR_MAX-PWM_LR_MIN)) / sunset_duration;


Изменено 24.4.14 автор SilverSwift


Спасибо за помощь, я так и думал что в этом месте косяк, но сомневался.
Может подскажите ещё по одной проблемке:
температура на дисплей выводится в виде 28,85
т.е. два знака после запятой. Проблема в том, что через некоторое время появляется 3-й знак, а потом и 4-й.
И ещё иногда изредка реле срабатывают раньше заданной температуры. Задано отключение Н1 при темп. 28,8 и Н2 при 29,0, а отключается Н1 при 28,7 и Н2 при 28,8. Может проблема с датчиком, может не совсем герметичен?
Заранее благодарен.

Изменено 27.4.14 автор kirex05
2014-04-27 добавлено 27/04/2014 13:58:16#1967276

Свой на Aqa.ru




1149 188
Ростов-на-Дону
2 дн. назад
Наконец удалось реализовать вкл/выкл реле в ручном режиме вне зависимости от установленного времени автоматического включения. С индикацией на экране о состоянии реле и режиме (ручной или автомат) В данном случае - управление реле СО2. Приведу кусочек кода, ответственного за эту функцию контроллера
const int RelCO2 = 4; //задаем подключение реле СО2 на выходе 4
const long CO2On = 14*hr+30*mn;//включение СО2
const long CO2Off = 20*hr+30*mn;//выключение СО2
const int Button = 2;//включаем кнопочку на вход 2
int flagCO2 = 0;
int regimCO2 = 1;
В основном цикле
void loop()
{
if (digitalRead(Button)==HIGH && flagCO2==0)
{
regimCO2++;
flagCO2=1;
if(regimCO2>3)
{
regimCO2=1;
}
}
if (digitalRead(Button)==LOW && flagCO2==1)
{
flagCO2=0;
}
if(regimCO2==1)
{
if (UTime>=CO2On && UTime<CO2Off)
{
digitalWrite (RelCO2, HIGH);
}
else
{
digitalWrite (RelCO2, LOW);
}
lcd.setCursor(9,0);
lcd.print("AUTO");
}
if(regimCO2==2)
{
digitalWrite(RelCO2, HIGH);
lcd.setCursor(9,0);
lcd.print("HAND");
}
if(regimCO2==3)
{
digitalWrite(RelCO2, LOW);
lcd.setCursor(14,0);
lcd.print("HAND");
}
if (digitalRead(RelCO2)==HIGH)
{
lcd.setCursor(0,0);
lcd.print("CO2 ON ");
}
else
{
lcd.setCursor(0,0);
lcd.print("CO2 OFF");
}
}
Код, может быть, и корявый, но вполне рабочий. Написал в соответствии со своим разумением. Дребезга кнопки нет. Оптимизация кода приветствуется.

Изменено 27.4.14 автор Дядька-рыбомор
2014-04-27 добавлено 27/04/2014 14:45:10#1967289

Свой на Aqa.ru




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


По предыдущему вопросу...вот кусок кода, который установит в текущих сутках время 21:00:00 оставив все остальное (день, месяц и год) как есть.

DateTime myTime = RTC.now();
uint32_t UTime = myTime.unixtime();
UTime = 75600 + (UTime-UTime%86400);
RTC.adjust(DateTime(UTime));

Здесь 75600 - это кол-во секунд в 21 часе.....поэтому если есть желание на другое время переделать - меняйте эту константу. Ну 86400 - это наверное уже понятно...это кол-во секунд в сутках..

Дядька-рыбомор
if (digitalRead(Button)==HIGH && flagCO2==0)


Сравнивать с булевой константой значение функции или значения переменных, которые могут принимать только булевые значение - это моветон...и выражения например такие:
if (digitalRead(Button)==HIGH ) и

if (digitalRead(Button))

эти выражения эквивалентны

Также как и
if (flagCO2==0) и
if (!flagCO2)

поэтому можно записать так:
if (digitalRead(Button)&&!flagCO2)
НО возможно, что данное выражение создав чуть более компактный код, будет менее информативно, для последующего прочтения менее подготовленным программистом.

Вот такая структура :
if (UTime>=CO2On && UTime<CO2Off)
{
digitalWrite (RelCO2, HIGH);
}
else
{
digitalWrite (RelCO2, LOW);
}
может быть заменена на :
digitalWrite (RelCO2, (UTime>=CO2On && UTime<CO2Off));

я где-то выше в теме уже показывал такие приемы....

Ну и т.д....

Вам необходимо учится работать с булевыми значениями, и не забывать, что для переменных и функций, которые работают с булевыми значениями,
НОЛЬ (0) , false, LOW, !1 - это синонимы.
1 (и любое значение больше единицы), true, HIGH, !0 - это тоже синонимы.



Изменено 27.4.14 автор Z0RS
2014-04-27 добавлено 27/04/2014 15:03:53#1967291

Свой на Aqa.ru




1149 188
Ростов-на-Дону
2 дн. назад
Z0RS

Спасибо. С часиками я почти дошел сам, ошибся по форме написания кода.
поэтому можно записать так:
if (digitalRead(Button)&&!flagCO2)
НО возможно, что данное выражение создав чуть более компактный код, будет менее информативно, для последующего прочтения менее подготовленным программистом.

Просто мне еще сложно писать код, как вы показали. Хоть это и не совсем правильно, но мне так понятнее, чисто для себя.
2014-04-27 добавлено 27/04/2014 16:35:52#1967301

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




251 27
Москва
12 мес. назад
Что значит "преобразования типов"?

Это значит, что переменная одного типа приводится к другому. Если операнды имеют разные типы, будет выполненно неявное (не указанное программистом в явном виде) приведение операндов к одному типу.

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

Вообще любая ошибка в программе является ошибкой программиста в чистом виде. Только некоторые ошибки может распознать компилятор. Простой пример:
uint8_t byte = -999;

На эту строку компилятор выбросит ворнинг.

А вот объявление типа:
#define byte -999U

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

Я не понимаю, что Вы здесь высказали.

Ну вы же знаете, что ардуино это железо (включая всякие шилды), софт для этого железа и "свой язык" со "средой разработки"? Язык обозначают словом wiring, его я и назвал арду-языком.

Вообще, "ардуязык" - как вы его назвали - это один из диалектов Си и есть.

Насчет диалектов не скажу, но по факту wiring - это библиотека из классов и функций на С++, предоставляющая удобную апишку для работы с ограниченным функционалом ограниченного количества типов контроллеров. И исключительно средствам ардуязыка доступен только один тип прерываний на ограниченном числе ног. Чтобы использовать прерывания таймера придется пораскинуть байтиками по регистрам чисто сишными средствами. И если вдруг вам кажется, что wiring это С без плюсов попробуйте компильнуть в arduino IDE код:

class A{
A(int a = 3): m_a(a)
{}
private:
int m_a;
};

void setup(){
}

void loop(){
}


Как вариант - можно "отобрать" прерывание по одному из 3-х системных таймеров, который можно запрограммировать на период, который требуется. Но в замен придется отказаться от ШИМ на некоторых выводах

Об этом я и говорил.

температура на дисплей выводится в виде 28,85
т.е. два знака после запятой. Проблема в том, что через некоторое время появляется 3-й знак, а потом и 4-й.

Никак количество знаков после запятой в коде не ограничивается.

Вместо:
// Вывод текущего значения температуры на дисплей
celsius = (float)raw / 16.0;
lcd.setCursor(0, 0);
lcd.print("T=");
lcd.setCursor(2, 0);
lcd.print(celsius);
delay (10);


Можно написать:
[q]// Вывод текущего значения температуры на дисплей
celsius = (float)raw / 16.0;
lcd.setCursor(0, 0);
char str[7];
sprintf(str, "T=%0.2f", celsius);
lcd.print(str);
delay (10); [/q]

Не стесняемся жать кнопку "спасибо"

И ещё иногда изредка реле срабатывают раньше заданной температуры. Задано отключение Н1 при темп. 28,8 и Н2 при 29,0, а отключается Н1 при 28,7 и Н2 при 28,8. Может проблема с датчиком, может не совсем герметичен?
Заранее благодарен.

А то, что срабатывает раньше Вы видите по термометру или по той температуре, что указана на дисплее?

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


Я бы написал так:

const int RelCO2 = 4; //задаем подключение реле СО2 на выходе 4
const long CO2On = 14*hr+30*mn;//включение СО2
const long CO2Off = 20*hr+30*mn;//выключение СО2
const int Button = 2;//включаем кнопочку на вход 2
bool flagCO2 = false;
int regimCO2 = 1;

В основном цикле
void loop()
{
//вариант 1
if (digitalRead(Button) == HIGH && !flagCO2){
regimCO2++;
flagCO2=true;
if(regimCO2>3)
regimCO2=1;

}
if (digitalRead(Button) == HIGH && flagCO2)
flagCO2=false;
//=============================
//вариант 2
/*
if (digitalRead(Button)==HIGH){
(regimCO2 == 3) ? regimCO2 = 1 : regimCO2++;
delay(250);
}
*/

//=============================
//switch-case в этом случае более уместен
switch(regimCO2){
case 1:{
if (UTime>=CO2On && UTime<CO2Off)
digitalWrite (RelCO2, HIGH);
else
digitalWrite (RelCO2, LOW);

lcd.setCursor(9,0);
lcd.print("AUTO");
break;
}
case 2:{
digitalWrite(RelCO2, HIGH);
lcd.setCursor(9,0);
lcd.print("HAND");
break;
}
case 3:{
digitalWrite(RelCO2, LOW);
lcd.setCursor(14,0);
lcd.print("HAND");
break;
}
default:
//можно обработать ошибку
break;

}

lcd.setCursor(0,0);
if (digitalRead(RelCO2)==HIGH)
lcd.print("CO2 ON ");
else
lcd.print("CO2 OFF");



1. Если хотите использовать flagCO2 как флажок который либо выставлен либо нет, то для этого логично использовать тип bool. Но если этот флажок используется для подавления дребезга, можно сделать это проще и короче. Для этого описал вариант 2, который можно раскомментировать, а вариант 1 закомментировать.
2. Сравнение одной переменной с различными значениями лучше делать не с блоком ифов, а конструкцией switch-case, или хотя бы использовать else if. В противном случае можно словить логическу ошибку, например:

int num = 1;

if (num == 1){
...
num++;
}

if (num == 2){
...
}


Выполнятся блоки под двумя ифами, потому как на момент проверки каждого из условий они будут выполняться.
3. Если в теле if или else всего одна строка - фигурные скобки не обзательны.

1 (и любое значение больше единицы), true, HIGH, !0 - это тоже синонимы.

А если меньше единицы, строка символов или пустое место?)) false = 0, все остальное true - это прописано стандартом.

Забыл добавить:
Вот такая структура :
if (UTime>=CO2On && UTime<CO2Off)
{
digitalWrite (RelCO2, HIGH);
}
else
{
digitalWrite (RelCO2, LOW);
}
может быть заменена на :
digitalWrite (RelCO2, (UTime>=CO2On && UTime<CO2Off));

Не может - внутри каждого ифа реализован вывод на lcd.

Изменено 27.4.14 автор SilverSwift
2014-04-27 добавлено 27/04/2014 21:36:55#1967372

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
SilverSwift
А если меньше единицы, строка символов или пустое место?)) false = 0, все остальное true - это прописано стандартом.

Да....я хотел сказать если значение не равно 0, но сформулировал не правильно.


Забыл добавить:
Вот такая структура :
if (UTime>=CO2On && UTime<CO2Off)
{
digitalWrite (RelCO2, HIGH);
}
else
{
digitalWrite (RelCO2, LOW);
}
может быть заменена на :
digitalWrite (RelCO2, (UTime>=CO2On && UTime<CO2Off));

Не может - внутри каждого ифа реализован вывод на lcd.


Я говорю про конкретно указанную структуру - в ней нет никаких выводов на LCD. И если откомпилировать оба варианта кода - то мой вариант, не только работоспособный, но и много компактнее в резулльтате.
Поэтому, зачем это теоретизированное умничание, с профанацией моих высказываний? Хочется, чего-то большего, чем просто по-учавствовать в теме...ну так и откройте свою...

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

Разбирайте и помогайте коллегам аквариумистам-программистам с конткретными примерами - у Вас хорошо получается, и главное время на это есть....
2014-04-28 добавлено 27/04/2014 23:15:03#1967392

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




251 27
Москва
12 мес. назад
Я говорю про конкретно указанную структуру - в ней нет никаких выводов на LCD. И если откомпилировать оба варианта кода - то мой вариант, не только работоспособный, но и много компактнее в резулльтате.

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

Я лично против вас ничего не имею, наоборот вас и вашу тему поддерживаю, зря вы так...
2014-04-28 добавлено 27/04/2014 23:42:09#1967397

Свой на Aqa.ru




693 77
Выкса
8 мес. назад
SilverSwift
Я лично против вас ничего не имею, наоборот вас и вашу тему поддерживаю, зря вы так...


ОК. Прошу прощения....Просто я не очень хорошо воспринимаю, неоднозначные высказывания...ну есть такой "вредный" черт характера..
Продолжим плодотворное сотрудничество...
2014-04-28 добавлено 28/04/2014 00:06:19#1967398

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