Создание таймера с использованием часов реального времени с указанием времени начала и остановки
У меня есть Arduino Uno с моторным экраном, а также DS1307 RTC, который я использую для управления 2 насосами.
В идеале я хотел бы установить время начала (час начала + минута начала) и время окончания (час окончания + минута окончания) для каждого насоса.
Мне повезло использовать операторы if для управления моим таймером, как показано ниже (адаптировано из Iain Foulds):
byte startHour=8
byte startMinute=15
byte endHour= 14
byte endMinute= 20
byte startHour2=22
byte startMinute2=30
byte endHour2= 2
byte endMinute2= 45
void loop() {
byte checkStartTime1() {
DateTime now = RTC.now();
// Read in what our current datestamp is from RTC
if (now.hour() == startHour && now.minute() == startMinute) {
validStart1 = 1;
} else {
validStart1 = 0;
}
return validStart1;
// Return the status for powering up
}
}
Но меня беспокоит то, что в данный момент это проверяет только эквивалентность, и если бы я потерял мощность, когда подошло время запуска, мой насос не включился бы. Поэтому я надеялся создать оператор if, который может работать с">=", а не просто "==".>
Который я могу использовать, если буду иметь дело только с часовым разделом:
if (now.hour() >= startHour && <= endHour)
Но все усложняется, когда я пытаюсь добавить несколько минут в структуру управления. В итоге я получаю серию вложенных циклов for, которые не работают.
Я надеюсь, что кто-нибудь сможет помочь мне написать заявление if (или
случай переключения
), которое может вместить часы и минуты. Так что в любое время между 8:15-14:20
validStart1=1
и в любое другое время
validStart1=0
И, пожалуйста, обратите внимание на добавление второго набора для этого таймера (StartHour2), который я бросил, так как моему второму насосу нужно будет справиться с переключением после полуночи.
@Vinterwoo, 👍3
Обсуждение6 ответов
Лучший ответ:
У меня была похожая проблема, когда я пытался управлять каким-то источником тепла здесь, дома.Вот как я сделал свои шаги, чтобы заставить его работать. Сначала я предположу, что вы используете библиотечное время.h и DS1307RTC.h (проверьте здесь информацию: https://www.pjrc.com/teensy/td_libs_Time.html).
Предполагая, что вам понадобятся 3 переменные типа tmElements_t. tm сохранит ваше текущее время( то, которое вы получаете от DS1307). Два других являются вспомогательными для определения времени запуска и остановки насоса. Аналогичным образом, вам понадобятся 3 переменные типа time_t по тем же причинам. переменные time_t хранят метки времени с 01.01.1970 (эпоха Unix), с которыми проще работать, но структуры элементов более удобны для человека.
tmElements_t tm, tm_hour_start, tm_hour_end;
time_t now, t_hour_start, t_hour_end;
Теперь в вашем цикле вам нужно получить текущее время от rtc:
time_t now = RTC.get();
после этого используйте функцию "Время перерыва", чтобы преобразовать значение метки времени в структуру tmElements:
breakTime(now, tm);
Следующий шаг-скопировать содержимое tm в другие вспомогательные структуры, используя функцию memcpy:
memcpy(&tm_hour_start, &tm, sizeof(tm));
memcpy(&tm_hour_end, &tm, sizeof(tm));
после этого измените параметры вспомогательных устройств, чтобы они соответствовали вашему правильному расписанию запуска и остановки (важны ли секунды? если да, сделайте то же самое):
tm_hour_start.Hour = startHour;
tm_hour_start.Minute = startMinute;
tm_hour_start.Second = 0;
tm_hour_end.Hour = endHour;
tm_hour_end.Minute = endMinute;
tm_hour_end.Second = 0;
Хорошо, у нас есть более дружественные к человеку начальные и конечные структуры, давайте получим от них временные метки. Для этого мы используем функцию makeTime, которая преобразует структуры tmElements_t в time_t:
t_hour_start = makeTime(tm_hour_start);
t_hour_end = makeTime(tm_hour_end);
Все, что вам нужно сделать сейчас, это проверить, находится ли ваша текущая метка времени (сейчас) между этими вновь созданными метками времени:
if ((t_hour_start <= now) && (now <= t_hour_end)){
//do something is soo...
}else{
// do something if not...
}
Дополнительный совет: В случае, если ваш час остановки перевалил за полночь, добавьте еще один день к отметке времени:
if (startHour > endHour) //past midnight correction
t_hour_end = t_hour_end + SECS_PER_DAY;
чистый код:
#include <time.h>
#include <DS1307RTC.h>
#include <stdio.h>
// other includes etc...
byte startHour=8
byte startMinute=15
byte endHour= 14
byte endMinute= 20
tmElements_t tm, tm_hour_start, tm_hour_end;
time_t now, t_hour_start, t_hour_end;
// setup function and other needed things...
void loop(){
//get current timestamp
time_t now = RTC.get();
// make current date and time structure
breakTime(now, tm);
// make auxiliary structures to be more human editable and friendly
memcpy(&tm_hour_start, &tm, sizeof(tm));
memcpy(&tm_hour_end, &tm, sizeof(tm));
// change auxiliary structures to meet your start and end schedule
tm_hour_start.Hour = startHour;
tm_hour_start.Minute = startMinute;
tm_hour_start.Second = 0;
tm_hour_end.Hour = endHour;
tm_hour_end.Minute = endMinute;
tm_hour_end.Second = 0;
// reverse process to get timestamps
t_hour_start = makeTime(tm_hour_start);
t_hour_end = makeTime(tm_hour_end);
// check if end time is past midnight and correct if needed
if (startHour > endHour) //past midnight correction
t_hour_end = t_hour_end + SECS_PER_DAY;
//final part
if ((t_hour_start <= now) && (now <= t_hour_end)){
//do something is soo...
}else{
// do something if not...
}
}
ИЗМЕНИТЬ: чтобы исправить то, что касается полуночных проверок, замеченных в комментариях. Новый код:
#include <Wire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <stdio.h>
// other includes etc...
byte startHour = 8;
byte startMinute = 15;
byte endHour = 14;
byte endMinute = 20;
tmElements_t tm, tm_hour_start, tm_hour_end;
time_t now_, t_hour_start, t_hour_end;
boolean update_tm = 1;
// setup function and other needed things...
void setup(){};
void loop(){
//get current timestamp
time_t now_ = RTC.get();
// make current date and time structure
breakTime(now_, tm);
// make auxiliary structures to be more human editable and friendly
if(update_tm){
memcpy(&tm_hour_start, &tm, sizeof(tm));
memcpy(&tm_hour_end, &tm, sizeof(tm));
// change auxiliary structures to meet your start and end schedule
tm_hour_start.Hour = startHour;
tm_hour_start.Minute = startMinute;
tm_hour_start.Second = 0;
tm_hour_end.Hour = endHour;
tm_hour_end.Minute = endMinute;
tm_hour_end.Second = 0;
// reverse process to get timestamps
t_hour_start = makeTime(tm_hour_start);
t_hour_end = makeTime(tm_hour_end);
// check if end time is past midnight and correct if needed
if (startHour > endHour) //past midnight correction
t_hour_end = t_hour_end + SECS_PER_DAY;
}
//final part
if ((t_hour_start <= now_) && (now_ <= t_hour_end)){
/* if we got a valid schedule, don't change the tm_hour structures and the
respective t_hour_start and t_hour_end timestamps. They should be updated
after exiting the valid schedule */
if(update_tm)
update_tm = 0;
//do something is soo...
}else{
if(update_tm == 0)
update_tm = 1;
// do something if not...
}
}
Большое спасибо за такой хорошо продуманный, хорошо объясненный и хорошо дополненный код. Но у меня, похоже, возникли проблемы с полуночной коррекцией. Когда я использую приведенный выше код, когда часы бьют полночь, мои насосы останавливаются. Кажется, в полночь происходит какая-то странная математика, поэтому я даже попытался (SECS_PER_DAY*2) убедиться, что я хорошо справляюсь со следующим днем. Но мой насос все еще отключается в полночь. Но, кроме этой проблемы, код работает хорошо, @Vinterwoo
попробуйте заменить SEC_PER_DAY реальным числом (24 часа * 3600 секунд в час = 86400), чтобы убедиться, что с этим макросом нет проблем с ошибками или что-то в этом роде. Сам макрос определяется во времени.h (https://github.com/PaulStoffregen/Time/blob/master/Time.h). Вы напечатали переменные time_t t_hour_start, t_hour_end и сейчас? Они верны?, @brtiberio
@Vinterwoo проигнорируйте мой последний комментарий, я думаю, что понял это, однако в данный момент я не могу это проверить. Я думаю, проблема в том, что мы постоянно обновляем структуры "tmElements_t" в "tm_hour_start" и в "tm_hour_end". Как только мы пройдем за полночь, эти структуры также изменятся и вызовут сбой (потому что " tm_hour_start.день " будет отражать изменение в течение нового дня). Как только мы получим действительный старт, мы должны исправить эту структуру до тех пор, пока не пройдет час выключения. Проверьте редактирование, @brtiberio
Все, что вам нужно сделать, это преобразовать время в какое-то стандартное единственное число, а затем сравнить эти числа.
Популярным методом является подсчет количества секунд, прошедших с той или иной эпохи. Эпоха-это произвольный момент времени, с которого вы измеряете время. Microsoft выбрала 01/01/0000 для своей эпохи - Unix выбрала 01/01/1970.
Библиотека Time.h содержит функции для управления значениями времени - одна из функций в ней может преобразовать полную спецификацию даты и времени в количество секунд с 01.01.1970 (эпоха Unix). Если вам нужны идеи, посмотрите на исходный код для этого.
Аналогичный подход можно использовать для простой работы по часам и минутам в течение дня. Один час равен 60 минутам, поэтому вы можете создать число, представляющее собой количество часов, умноженное на 60 плюс количество минут в текущем часе. Это даст вам количество минут, прошедших с полуночи. Вы даже можете добавить секунды, если хотите еще большей точности.
Как только у вас есть это единственное значение, его очень легко сравнить с >=>
.
byte startHour=8
byte startMinute=15
byte endHour= 14
byte endMinute= 20
byte startHour2=22
byte startMinute2=30
byte endHour2= 2
byte endMinute2= 45
void loop()
{
DateTime now = RTC.now(); // Read in what our current datestamp is from RTC
if (
( (now.hour()==startHour && now.minute()>= startMinute) || (now.hour()>startHour)
&& ( (now.hour()==endHour && now.minute()< endMinute) || (now.hour()<endHour) ) {
validStart1 = 1;
} else {
validStart1 = 0; //
}
return validStart1; // Return the status for powering up
}
}
Гербен, спасибо за твой код. Я взял его, добавил секунды к счету и исправил несколько опечаток. Это не так сложно, как другие методы, но новичкам может быть полезно следовать логике. надеюсь, это поможет
#include <TimeLib.h>
#include <Wire.h>
#include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t
int openHH = 9;
int openMM = 10;
int openSS = 0;
int closeHH = 9;
int closeMM = 40;
int closeSS = 0;
bool isOpenTime()
{
time_t t = now(); // Read in what our current datestamp is from RTC
// DateTime now = RTC.now(); // if using timeRTC lib, i think
int nowHH = hour(t); int nowMM = minute(t); int nowSS = second(t); // put into local variables for easier read
if ((
(nowHH>openHH) // check hours first
|| ((nowHH==openHH) && (nowMM>openMM)) // check minutes if in same hour
|| ((nowHH==openHH) && (nowMM==openMM) && (nowSS>=openSS)) // check seconds if in same hour & minute
)
&& // both conditions must be valid
(
(nowHH<closeHH) // check hours first
|| ((nowHH==closeHH) && (nowMM<closeMM))) // check minutes if in same hour
|| ((nowHH==closeHH) && (nowMM==closeMM) && (nowSS<=closeSS)) // check seconds if in same hour & minute
)
{ return true; } else { return false; }
}
// usage
if (isOpenTime) {
// ... do something, eg open door
} else {
// ... do something, eg close door
}
Используйте время в общем количестве секунд.
ontime=((sethour*60)+(setmin*60)+setsec);
offtime=((ofsethour*60)+(ofsetmin*60)+ofsetsec);
currenttime=((t.hour*60)+(t.min*60)+(t.sec);
if ((currenttime>ontime)&&(currenttime<offtime));
{
pumpon();
}
else
{
pumpoff();
}
delay(1000);
небольшая поправка ... в одном часе 3600 секунд ... итак, время=((sethour*3600)+(setmin*60)+setsec); нерабочее время=((ofsethour*3600)+(ofsetmin*60)+ofsetec); текущее время=((т. час*3600)+(т. мин*60)+(т. сек);, @chemada
Простой и эффективный способ-использовать относительные дни и использовать тот факт, что дни, часы, минуты и секунды являются типами данных размером в байт, которые могут вписываться в один 32-разрядный тип данных.
байт 3 | байт 2 | байт 1 | байт 0 |
---|---|---|---|
День | Час | Минута | Второе |
Это может быть достигнуто с помощью следующей функции:
constexpr uint32_t MakeTimestamp(const byte day, const byte hour, const byte minute, const byte second)
{
// Pack the 4 individual bytes into a single 4-byte data type in order of precedence.
return uint32_t(day) << 24 | uint32_t(hour) << 16 | uint32_t(minute) << 8 | uint32_t(second);
}
Эту 32-разрядную метку времени можно легко сравнить с другими, чтобы проверить, находится ли метка времени в пределах определенного промежутка времени:
bool WithinTimespan(const uint32_t& timestamp, const uint32_t& begin, const uint32_t& end)
{
if (end < begin)
{
// Crossing midnight.
return timestamp >= begin || timestamp <= end;
}
// Single day.
return timestamp >= begin && timestamp <= end;
}
Используя относительные дни для объявления времени запуска и остановки двигателя, сравнение учитывает перенос на завтра:
const byte motor1_start_hour = 8;
const byte motor1_start_minute = 15;
const byte motor1_stop_hour = 14;
const byte motor1_stop_minute = 20;
const byte motor2_start_hour = 22;
const byte motor2_start_minute = 30;
const byte motor2_stop_hour = 2;
const byte motor2_stop_minute = 45;
const uint32_t motor1_start_time = MakeTimestamp(0, motor1_start_hour, motor1_start_minute, 0);
const uint32_t motor1_stop_time = MakeTimestamp(0, motor1_stop_hour, motor1_stop_minute, 0);
const uint32_t motor2_start_time = MakeTimestamp(0, motor2_start_hour, motor2_start_minute, 0);
const uint32_t motor2_stop_time = MakeTimestamp(0, motor2_stop_hour, motor2_stop_minute, 0);
Тогда loop()
просто выглядит примерно так:
void loop()
{
const DateTime now = rtc.now();
const uint32_t timestamp = MakeTimestamp(0, now.hour(), now.minute(), now.second());
bool motor1_on = WithinTimespan(timestamp, motor1_start_time, motor1_stop_time);
bool motor2_on = WithinTimespan(timestamp, motor2_start_time, motor2_stop_time);
UpdateMotor1(motor1_on);
UpdateMotor2(motor2_on);
}
Я рекомендую вам прочитать ответ Майенко, @Juraj
- Разница между «time_t» и «DateTime»
- DS3231 с Arduino Nano для точной синхронизации
- Как сделать очень долгую функцию delay(), несколько часов
- Получение BPM из данного кода
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- Генерация стабильной частоты
- Как исправить ошибку компиляции для tone (), используя тот же таймер, что и другая функция
- Использовать timer0, не влияя на millis() и micros().
Могу я узнать, как изменить этот код, работающий в полуночном диапазоне времени? Например, с 23.30 до 01.30. байт начала часа=8 байт начала минуты=15 конец байта= 14 конечная минута байта= 20 пустых циклов() { Дата и время сейчас = RTC.сейчас(); // Прочитайте, какая у нас текущая метка даты из RTC, если ( ( (сейчас.час()==Начало часа && сейчас.минута()>= Начало минуты) || (сейчас.час()>>Начало часа) && ( (сейчас.час()==Конец часа && сейчас.минута()< Конечная минута) || (сейчас.час(), @SMD