Включать реле по таймеру RTC 1307
- Войдите на сайт для отправки комментариев
Вс, 10/06/2018 - 16:46
Схема такая:
- Arduino Nano
- RTC 1307 (i2c)
- AM2320 (i2c) датчик температуры и влажности воткнут по i2c
- 4 relay modules
Как включать реле на пине D4 каждые 2 часа по часу.
То есть включаем в 12.00 выключаем в 13.00, включаем в 14.00, выключаем в 15.00.
Не понимаю как выдрать из RTC 1307 значения времени и использовать их для включения реле.
На данный момент код работает так:
Нашли часы реального времени DS1307 по адресу 0x68
--------Начинаем работать
Температура: °C 24.50
Влажность: % 43.00
Температура: °C 24.50
Влажность: % 43.00
Дата и время: 2018-06-10T15:11:21
--------=====---------------------
Температура: °C 24.40
Влажность: % 43.00
Дата и время: 2018-06-10T15:11:25
--------=====---------------------
Температура: °C 24.40
Влажность: % 43.10
Дата и время: 2018-06-10T15:11:29
--------=====---------------------
Температура: °C 24.40
Влажность: % 43.10
Дата и время: 2018-06-10T15:11:33
--------=====---------------------
И собственно сам код:
/*
* On start-up the autoprobe feature is used to search the I2C bus to
* find the first available real-time clock. The sketch indicates
* which clock type (if any) has been found. If the clock is an
* MCP7941x device the current calibration setting is printed.
*
* The clock can be set by writing the current unixtime (i.e., seconds
* since 1970-01-01 00:00:00Z) prefixed by the character 'T' to
* Serial. The string must be terminated by a NULL character, line
* feed, or carriage return. E.g., "T1343779200" sets the clock to
* 2012-08-01 00:00:00Z".
*
*/
#include <Wire.h>
#include <RTCx.h> // Библиотека часов реального времени 1307
#include <AM2320.h> // Библиотека датчика AM2320 по шине i2c
AM2320 sensor; // Обьявляем sensor
void setup(void)
{
pinMode(6, OUTPUT); // Пин реле увлажнителя
pinMode(5, OUTPUT); // Пин реле обогревателя
pinMode(4, OUTPUT); // Пин реле вентилятора
pinMode(3, OUTPUT); // Пин реле управляемого таймером- свет
Serial.begin(9600);
Wire.begin();
sensor.begin();
// Автопоиск устройства на шине i2c.
if (rtc.autoprobe()) {
// Кажется нашли часы на шине i2c.
Serial.print("Нашли часы реального времени ");
Serial.print(rtc.getDeviceName());
Serial.print(" по адресу 0x");
Serial.println(rtc.getAddress(), HEX);
Serial.println("--------Начинаем работать");
}
else {
// Если ничё не найдено на i2c так и напишем что не найдено.
Serial.println("No RTCx found, невозможно работать с часами");
while (1)
;
}
// Enable the battery backup. Ато случается на DS1307
rtc.enableBatteryBackup();
rtc.startClock(); // Убедимся что oscillator запущен
rtc.setSQW(RTCx::freq4096Hz);
}
const uint8_t bufLen = 30;
char buffer[bufLen + 1] = {'\0'};
uint8_t bufPos = 0;
unsigned long last = 0;
void loop(void)
{
sensor.measure();
float h = sensor.getHumidity(); float t = sensor.getTemperature();
if (t>24) digitalWrite(5, HIGH); // Включаем реле обогревателя / он находитя в NO
else digitalWrite(5, LOW); // Выключаем реле обогревателя если температура достигнута
if (h>80) digitalWrite(6, HIGH); // Включаем реле туманогенератора / он находитя в NO
else digitalWrite(6, LOW); // Выключаем реле туманогенератора. Влажность достигнута
Serial.print("Температура: °C ");
Serial.println(t);
Serial.print("Влажность: % ");
Serial.println(h);
delay(1000);
struct RTCx::tm tm;
if (millis() - last > 2000) {
last = millis();
rtc.readClock(tm);
Serial.print("Дата и время: ");
RTCx::printIsotime(Serial, tm).println();
// RTCx::time_t t = RTCx::mktime(&tm); // Закомментировал- не знаю для чего строка
// Serial.print("unixtime = ");
// Serial.println(t);
Serial.println("--------=====----------------------");
delay(3000);
}
}
https://github.com/stevemarple/RTCx/blob/master/src/RTCx.h
struct tm { int tm_sec; // Seconds [0..59] int tm_min; // Minutes [0..59]. int tm_hour; // Hour [0..23]. int tm_mday; // Day of month [1..31]. int tm_mon; // Month of year [0..11]. int tm_year; // Years since 1900. int tm_wday; // Day of week [0..6] (Sunday=0). int tm_yday; // Day of year [0..365]. (-1=unset). int tm_isdst; // Daylight Savings flag (ignored). };ого!! нужное направление.. Спасибо.
В итоге хочу получить что-то типа этого:
//------------------------------------------------------------------058059RTC_DS1307 RTC;060061//------------------------------------------------------------------062time cur_time;063time relay_1_on(07, 00, 00);// Время включения 1-ого реле064time relay_1_off(8, 00, 00);// Время отключения 1-ого реле065relay relay_1(RelayChn1);// Реле 1066//------------------------------------------------------------------Ну где то так
/**/ //---------------------- typedef void(*pDo)(); struct schedule { const int hour; const int minute; const int sec; bool flag; const pDo Do; schedule(int h, int m, int s, const pDo D): hour(h), minute(m), sec(s), flag(0), Do(D) {} } ; //---------------------------------- void RelON() { Serial.println("RelON"); } void RelOFF() { Serial.println("RelOFF"); } void LedON() { Serial.println("LedON"); } void LedOFF() { Serial.println("LedOFF"); } schedule TABLE[] = { {/*час*/0,/*минута*/10,/*секунда*/0,/*обработчик*/&RelON }, {/*час*/0,/*минута*/20,/*секунда*/0,/*обработчик*/&RelOFF}, {/*час*/0,/*минута*/30,/*секунда*/0,/*обработчик*/&LedON }, {/*час*/0,/*минута*/40,/*секунда*/0,/*обработчик*/&RelON }, {/*час*/0,/*минута*/50,/*секунда*/0,/*обработчик*/&RelOFF}, {/*час*/1,/*минута*/0,/*секунда*/0,/*обработчик*/&LedOFF} }; //---------------------------------- void setup() { Serial.begin(9600); } void loop() { } /**/В вашем случае можно поступить так:
В вашем случае можно поступить так:
andriano
Точно, так тоже будет работать.
andriano
Шикарная запись. Нужно было оставить оба варианта чтоб другим было понятно.
Не знаю. В разных языках различный приоритет операций. Чтобы не путаться, я предпочитаю ставить с запасом.
Вот так: отвечал на один вопрос, а пока ответил, он уже изменился.
Первоначально вопрос был о необходимости скобок.
andriano
А я посмотрел что скобки уже стоят и решил удалить вопрос)
Не знаю. В разных языках различный приоритет операций. Чтобы не путаться, я предпочитаю ставить с запасом.
Вот так: отвечал на один вопрос, а пока ответил, он уже изменился.
Первоначально вопрос был о необходимости скобок.
ты жесам пост подправил, было
xDriver
Побитовый вариант пусть тоже будет.
ты жесам пост подправил, было
А подправить решил потому, что деление (равно как и нахождение остатка) - "тяжелая" операция. Может, конечно, оптимизатор ее и сам на коньюнкцию исправит, но лучше указать явно.
Вот это да, я думал топик будет заброшен, а тут как подключились да ещё такие коды понаписали!! действительно шикарно!
Если б я ещё это в код всё мог правильно впихать ))))
В вашем случае можно поступить так:
qwone
Ну тут легко это решаемо так:
/**/ unsigned long mill;// переменная для millis() typedef void(*pDo)(); //---------------------- // класс светодиод class Cl_Led { protected: const byte pin; bool led; unsigned long past, time; byte state; //0 выкл/ 1 вкл / 2 мигать /*установить в состояние*/ void stand(byte state_) { past = mill; state = state_; switch (state) { case 0: // выкл digitalWrite(pin, led = LOW); break; case 1: // вкл digitalWrite(pin, led = HIGH); break; case 2:// мигать digitalWrite(pin, led = !led); break; case 3:// короткое выключение digitalWrite(pin, led = LOW); break; case 4:// короткое включение digitalWrite(pin, led = HIGH); break; } } public: /*конструктор*/ Cl_Led(byte pin_): pin(pin_) {} /*инициализация-вставить в setup()*/ void init() { pinMode(pin, OUTPUT); OFF(); } /*работа-вставить в loop()*/ void run() { if (state == 2 && mill - past >= time)stand(2); if (state == 3 && mill - past >= time)stand(1); if (state == 4 && mill - past >= time)stand(0); } /*включить*/ void ON() { stand(1); } /*коротко включить*/ void ON(unsigned long time_) { time = time_; stand(4); } /*выключить*/ void OFF() { stand(0); } /*коротко выключить*/ void OFF(unsigned long time_) { time = time_; stand(3); } /*мигать*/ void blink(unsigned long time_ = 200) { time = time_; stand(2); } }; //---------------------- struct schedule { const int hour; const int minute; const int sec; bool flag; const pDo Do; schedule(int h, int m, int s, const pDo D): hour(h), minute(m), sec(s), flag(0), Do(D) {} } ; class Cl_Timer { protected: byte num; schedule *TABLE; public: Cl_Timer(schedule *T, byte n): TABLE(T), num(n) {} void init() {} void run() {} }; //-----------Компоновка----------------------- Cl_Led Led1(/*пин*/13); Cl_Led Led2(/*пин*/12); Cl_Led Led3(/*пин*/11); void Do1() { Led1.ON(); Serial.println("Led1.ON"); } void Do2() { Led1.OFF(); Serial.println("Led1.OFF"); } void Do3() { Led2.ON(); Serial.println("Led2.ON();"); } void Do4() { Led2.OFF(); Serial.println("Led2.OFF"); } void Do5() { Led3.ON(); Serial.println("Led3.ON();"); } void Do6() { Led3.OFF(); Serial.println("Led3.OFF"); } const int num = 6; schedule TABLE[num] = { {/*час*/0,/*минута*/10,/*секунда*/0,/*обработчик*/&Do1 }, {/*час*/0,/*минута*/20,/*секунда*/0,/*обработчик*/&Do2}, {/*час*/0,/*минута*/30,/*секунда*/0,/*обработчик*/&Do3 }, {/*час*/0,/*минута*/40,/*секунда*/0,/*обработчик*/&Do4 }, {/*час*/0,/*минута*/50,/*секунда*/0,/*обработчик*/&Do5}, {/*час*/1,/*минута*/ 0,/*секунда*/0,/*обработчик*/&Do6} }; Cl_Timer Timer(TABLE, num); //---------------------------------- void setup() { Serial.begin(9600); Led1.init(); Led2.init(); Led3.init(); Timer.init(); } void loop() { mill = millis(); Led1.run(); Led2.run(); Led3.run(); Timer.run(); } /**/Разумеется туда надо поставить реальные часы
Ну тут легко это решаемо так:
qwone
Я с этой библиотекой не знаком, если это функция то да надо.
Rele1 = (cur_time>=Rele1_on) && (cur_time<Rele1_off);
#include <Wire.h> #include <RTClib.h> // Библиотека часов реального времени 1307 #define PWM_MIN 0 //Если необходим ток покоя на LED - изменить эту константу #define PWM_MAX 20 //Если необходимо ограничить максимальную яркость - уменьшить значение #define PWM_PIN 7 #define rele1 3 #define ReleChn1 8//Пин порта, где будет ШИМ //#define Rele1 3 //#define Rele2 4 #define ReleObogrev 5 #define ReleTuman 6 #define mn 60UL //Дополнительные константы для удобства #define hr 3600UL //Отражают соответствующие количества секунд #define d 86400UL #include <AM2320.h> // Библиотека датчика AM2320 по шине i2c AM2320 sensor; // Обьявляем sensor //------------------------------------------------------------------ class time{ public: time(unsigned char h=0, unsigned char m=0, unsigned char s=0){ set_time(h, m, s); } time& operator = (time &t){hour = t.hour; min = t.min; sec = t.sec; seconds = t.seconds; return *this;} boolean operator ==(time &t){return seconds==t.get_time();} boolean operator !=(time &t){return !(*this==t);} boolean operator > (time &t){return seconds>t.get_time();} boolean operator < (time &t){return seconds<t.get_time();} boolean operator >=(time &t){return seconds>=t.get_time();} boolean operator <=(time &t){return seconds<=t.get_time();} void set_time(unsigned char h, unsigned char m, unsigned char s){ hour=h; min=m; sec=s; seconds=(unsigned long)h*1440 + (unsigned long)m*60 + s; } unsigned long get_time(){return seconds;}; void print() {Serial.print(hour); Serial.print(':');Serial.print(min); Serial.print(':');Serial.println(sec);} private: unsigned char hour; unsigned char min; unsigned char sec; unsigned long seconds; }; //------------------------------------------------------------------ class Rele{ public: Rele(unsigned char pin){ pin_num = pin; pinMode(pin_num, OUTPUT); digitalWrite(pin_num, 1); state=0; } void operator =(boolean val) {digitalWrite(pin_num, !val); state=val;} operator boolean() {return state;} private: unsigned char pin_num; boolean state; }; //------------------------------------------------------------------ RTC_DS1307 RTC; time cur_time; time Rele1_on(07, 00, 00); // Время включения 1-ого реле time Rele1_off(22, 00, 00); // Время отключения 1-ого реле Rele Rele1(ReleChn1); // Реле 1 char daysOfTheWeek[7][12] = {"Воскр", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; void setup(void) { Wire.begin(); //Инициируем I2C интерфейс RTC.begin(); //Инициирум RTC модуль analogWrite(PWM_PIN, PWM_MIN); //Пишем в порт минимальное значение Serial.begin(9600); //Запускаем сом-порт delay(3000); //Ожидаем открытия консоли if (! RTC.begin()) { Serial.println("Couldn't find RTC"); while (1); } // following line sets the RTC to the date & time this sketch was compiled // RTC.adjust(DateTime(F(__DATE__), F(__TIME__))); // This line sets the RTC with an explicit date & time, for example to set // January 21, 2014 at 3am you would call: // rtc.adjust(DateTime(2018, 6, 11, 15, 15, 15)); // для установки времени ввести сюда время и расскоментировать pinMode(ReleTuman, OUTPUT); // Пин реле увлажнителя pinMode(ReleObogrev, OUTPUT); // Пин реле обогревателя // pinMode(Rele2, OUTPUT); // Пин реле вентилятора pinMode(Rele1, OUTPUT); // Пин реле управляемого таймером- свет Serial.begin(9600); Wire.begin(); sensor.begin(); } void loop(void){ // Здесь мы будем считывать с датчика AM2320 температуру и влажность // и контролировать 2 пина- 5и6 // Влажность и температура регулируются здесь этими 2мя релюхами sensor.measure(); float h = sensor.getHumidity(); float t = sensor.getTemperature(); if (t>24) digitalWrite(5, HIGH); // Включаем реле обогревателя / он находитя в NO else digitalWrite(5, LOW); // Выключаем реле обогревателя если температура достигнута if (h>80) digitalWrite(6, HIGH); // Включаем реле туманогенератора / он находитя в NO else digitalWrite(6, LOW); // Выключаем реле туманогенератора. Влажность достигнута Serial.print("Температура: °C "); Serial.println(t); Serial.print("Влажность: % "); Serial.println(h); delay(1000); // Тут мы окончили опрос датчика и выставили нужные значения ВКЛ и ВЫКЛ на 5и6 пины. // // Переходим к часам реального времени long pwm; DateTime myTime = RTC.now(); //Читаем данные времени из RTC при каждом выполнении цикла long Day_time = myTime.unixtime() % 86400; //сохраняем в переменную - время в формате UNIX DateTime now = RTC.now(); Serial.print(now.day(), DEC); Serial.print('.'); Serial.print(now.month(), DEC); Serial.print('.'); Serial.print(now.year(), DEC); Serial.print(" ("); Serial.print(daysOfTheWeek[now.dayOfTheWeek()]); Serial.print(") "); Serial.print(now.hour(), DEC); Serial.print(':'); Serial.print(now.minute(), DEC); Serial.print(':'); Serial.print(now.second(), DEC); Serial.println(); delay(1000); // digitalWrite(Rele1,time.Hours%2 == 0); if (time.minutes == 00) digitalWrite(Rele1, !(time.Hours & 1)); // static bool state; // static unsigned long time; // if((millis() - time) > (state ? 10000 : 20000)) // первое число отображает время одного положения в мск, второе число отображает длительность второго состояния реле // { // state = !state; // digitalWrite(Rele1, state); // time = millis(); // } // // cur_time.set_time(myTime.hour(), myTime.minute(), myTime.second()); // Устанавливаем часы, минут, секунды в cur_tim //------------КАНАЛ 1 (Контроль температуры по времени) ------- // sensor.getTemperature(); // // Rele1 = (cur_time>=Rele1_on) && (cur_time<Rele1_off); // Состояние реле 1 = нахождение текущего времени в диапазоне от relay_1_on до relay_1_off и в диапазоне заданных температур }//------------Конец ЦИКЛА-----------------------------выдаёт ошибку exit status 1
И этот код можно сократить, почистить?
Мне pwm не нужен- переделывал другой проект где pwm нужен был- там рассвет, закат был.. там планвно свет зажигался и потухал..
А мне нужно чтоб
1) реле1 (пин3) не совсем так уж строго, но по времени чаз через час работал.
Переменные и классы переименовать бы нормально.. Может кто за пару минут в красивый вид привести - причесать код ))
Ну смотрите для вашей библиотеки получается так.
Вам же привели пример записи.
Но пока пишите так.
Потому что нужно вводить еще один флаг для первоначального включения т.к. минуты могут быть не в нуле и следовательно вы пропустите один цикл.
В вашем случае можно поступить так:
В подавляющем большинстве случаев это ни на что не влияет. А в тех немногих случаях, когда это оказывается существенным, digitalWrite следует заменять на что-то менее ресурсоемкое.
И после такой замены это уже точно ни на что вличть не будет, т.к. мы не на intel работаем - здесь запись в регистр ничем не отличается от записи в ячейку памяти.
qwone
Ну тут легко это решаемо так:
Т.е. когда будет "беспрерывно долбиться" в течении целой минуты - это ничего страшного. :)
andriano
Лично мое мнение пусть хоть весь час "долбит", контроллер должен же чем то быть занят.
А это ~50000000000 долбежок)
andriano
Лично мое мнение пусть хоть весь час "долбит", контроллер должен же чем то быть занят.
Поэтому я и не стал править этот фрагмент.
А откуда взялось time.Hours и time.minutes ? Я не вижу их нигде ранее в коде
И не критично попасть именно в 00 минут. Может лучше цикл while задать или лучше ( (time>12) && (time<13) ) тогда с 12 до 13 будет включено
А откуда взялось time.Hours и time.minutes ? Я не вижу их нигде ранее в коде
Да это заначение времени возращаемое библиотекой, чтобы работало я выше написал на что заменить в вашем случае.
'tm_hour' was not declared in this scope
попробовал как в class описано :
digitalWrite(Rele1, !(t.hour & 1));
получил: request for member 'hour' in 't', which is of non-class type 'float'
Работает таким образом:
digitalWrite(Rele1, !(now.hour() & 1));
Топустим сейчас 8 часов, получается на пин Rele1 пойдет !(8 & 1);
Расшифруйте &
Работает таким образом:
digitalWrite(Rele1, !(now.hour() & 1));
Топустим сейчас 8 часов, получается на пин Rele1 пойдет !(8 & 1);
Расшифруйте &
Как я почитал:
Bitwise AND (&)
The bitwise AND operator in C++ is a single ampersand, &, used between two other integer expressions. Bitwise AND operates on each bit position of the surrounding expressions independently, according to this rule: if both input bits are 1, the resulting output is 1, otherwise the output is 0. Another way of expressing this is:
0 0 1 1 operand1 0 1 0 1 operand2 ---------- 0 0 0 1 (operand1 & operand2) - returned resultЭто же не для меня! Получается что если обе цифры равны 1, то результат будет 1, в противном случае 0.
Значит реле у меня будет включено всего в час ночи )))
А восклицательный знак перед этим означает что Только не в час ночи, но все сутки.. так чтоль?
Мне кажется надо сделать так: Если ЧАСЫ делятся на 2 без остатка, то (Rele1,HIGH) включено. В противном случае (Rele1,LOW)
Напишите в столбик 8 и 1 двоичном представлении. Поразрядно сделайте им & и напишите его ниже. Что получилось? Теперь проделайте то же самое с 9 и 1, 10 и 1, 11 и 1.
Или явно указать часы раоты:
if (now.hour()= 0 || now.hour() =2 || now.hour() =4 || now.hour() =6 || now.hour() =8 || now.hour() =10 || now.hour() =12 || now.hour() =14 || now.hour() =16 || now.hour() =18 || now.hour() =20 || now.hour() =22) {digitalWrite(Rele1, HIGH); else digitalWrite(Rele1, LOW);
}
Вот и готовый код:
Serial.print(" сейчас "); Serial.print(now.hour(), DEC); Serial.println(" часов. Реле должно быть включено "); Serial.println(); if ( now.hour()== 0 || now.hour() == 2 || now.hour() == 4 || now.hour() == 6 || now.hour() == 8 || now.hour() == 10 || now.hour() == 12 || now.hour() == 14 || now.hour() == 16 || now.hour() == 18 || now.hour() == 20 || now.hour() == 22) digitalWrite(Rele1, HIGH); else digitalWrite(Rele1, LOW);Выдаёт следующее:
Бляааа, работать-то удет.. но вот в 9 часов будет писать: Сейчас 9 часов реле должно быть ВКЛючено. ВЫКлючено должно быть ))
это мне больше всего по душе пришлось видимо
Вот это:
легко переписывается так:
Зацените, насколько короче ;) Перевожу: если текущий час делится на два без остатка - он чётный, или 0. Всё это у вас было расписано простынёй.
Как я почитал:
Bitwise AND (&)
The bitwise AND operator in C++ is a single ampersand, &, used between two other integer expressions. Bitwise AND operates on each bit position of the surrounding expressions independently, according to this rule: if both input bits are 1, the resulting output is 1, otherwise the output is 0. Another way of expressing this is:
0 0 1 1 operand1 0 1 0 1 operand2 ---------- 0 0 0 1 (operand1 & operand2) - returned resultЭто же не для меня! Получается что если обе цифры равны 1, то результат будет 1, в противном случае 0.
Значит реле у меня будет включено всего в час ночи )))
А восклицательный знак перед этим означает что Только не в час ночи, но все сутки.. так чтоль?
Все верно вы понимаете, только не все сутки, а час через час. Если хотите инвертировать час включения то просто уберите восклицательный знак.
Update:
Проект бросаю,спалил плату. Всё работает, но компом перестало отображать.
Кстати, этот код не запускает реле- ждал 9 и 10 часов- не запустилось.
Какой трагический финал.
щас бы попробовал
if( !(now.hour() % 2)) но , увы залить не могу )Какой трагический финал.
У тебя ник как раз под эту ситуацию подходит ))
Всем спасибо! не только за помощь в данном проекте, самое главное- выучил несколько операторов && || и даже % понял !!! Спасибо!!!
топик- в топку. пока ардуинка новая не придёт.
А спалил, как ни странно, по инструкции.
Подал на гнездо VIN 9 вольт с блока питания.
Запахло чуток жареным, отрубил всё, включаю просто в usb - нет в компе никакой реакции.. даже порта.. а релюхи продолжают мигать в зависимости от влажности и температуры.
А та релюха по таймеру не включается..
dinovasya
Это потому что у вас строка с дефайном для Rele1 закомментированная.
Пишите так если не умеете пользоватся:
#define PWM_PIN 7#define rele1 3#define ReleChn1 8//Пин порта, где будет ШИМ//#define Rele1 3//#define Rele2 4Но ведь на 2 строки выше он не комментированный.
Пользоваться я умею, просто на будущее сразу смотрю, называть всё своими именами, дабы не искать по пинам..