Создание таймера с использованием часов реального времени с указанием времени начала и остановки

У меня есть 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), который я бросил, так как моему второму насосу нужно будет справиться с переключением после полуночи.

, 👍3

Обсуждение

Могу я узнать, как изменить этот код, работающий в полуночном диапазоне времени? Например, с 23.30 до 01.30. байт начала часа=8 байт начала минуты=15 конец байта= 14 конечная минута байта= 20 пустых циклов() { Дата и время сейчас = RTC.сейчас(); // Прочитайте, какая у нас текущая метка даты из RTC, если ( ( (сейчас.час()==Начало часа && сейчас.минута()>= Начало минуты) || (сейчас.час()>>Начало часа) && ( (сейчас.час()==Конец часа && сейчас.минута()< Конечная минута) || (сейчас.час(), @SMD


6 ответов


Лучший ответ:

5

У меня была похожая проблема, когда я пытался управлять каким-то источником тепла здесь, дома.Вот как я сделал свои шаги, чтобы заставить его работать. Сначала я предположу, что вы используете библиотечное время.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


3

Все, что вам нужно сделать, это преобразовать время в какое-то стандартное единственное число, а затем сравнить эти числа.

Популярным методом является подсчет количества секунд, прошедших с той или иной эпохи. Эпоха-это произвольный момент времени, с которого вы измеряете время. Microsoft выбрала 01/01/0000 для своей эпохи - Unix выбрала 01/01/1970.

Библиотека Time.h содержит функции для управления значениями времени - одна из функций в ней может преобразовать полную спецификацию даты и времени в количество секунд с 01.01.1970 (эпоха Unix). Если вам нужны идеи, посмотрите на исходный код для этого.

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

Как только у вас есть это единственное значение, его очень легко сравнить с >=>.

,

1
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
 } 
}
,

3

Гербен, спасибо за твой код. Я взял его, добавил секунды к счету и исправил несколько опечаток. Это не так сложно, как другие методы, но новичкам может быть полезно следовать логике. надеюсь, это поможет

#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
  }
,

0

Используйте время в общем количестве секунд.

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


0

Простой и эффективный способ-использовать относительные дни и использовать тот факт, что дни, часы, минуты и секунды являются типами данных размером в байт, которые могут вписываться в один 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