1 (edited by jaymzys 2017-05-29 08:46:03)

Topic: Выполнение скрипта с задержками выполнения программы

Добрый день. В рамках реализации проекта удаленного управления водонагревателем, написал скрипт автовключения водонагревателя. Если включен переключатель ВКЛ.таймер и в поле ввода введено время и температура нагрева в формате HH:MM:TT - каждый цикл производится вызов одной из функций автовключения.  Скриншот интерфейса ниже.

http://s46.radikal.ru/i111/1705/b6/08711c0758f2.png

В теле функции schedule_func_ON()  необходимо совершать несколько последовательных действий. Чтобы установить температуру нагрева, допустим с текущего значения 35 градусов на значение 75 градусов, нужно 9 раз замкнуть и разомкнуть реле temp_relay (первое замыкание - холостое, а далее температура меняется при нажатии с инкрементом 5 градусов). Каждое замыкание-размыкание реле имитирует нажатие человеком на кнопку на передней панели водонагревателя.  После замыкания и размыкания необходимо на некоторое время остановить выполнение программы для того, чтобы реле успевало реагировать и не "залипало". 
Когда я писал программу без использования remoteXY - просто использовал delay(). С RemoteXY использование delay() недопустимо, как понял из документации. Попробовал с пустым циклом while() - программа ведет себя не корректно. Видимо в силу той же причины - пока закончится несколько циклов while() - RemoteXY_Handler теряет связь.
В  void loop() у меня уже есть задержки включения-выключения реле. Но там на каждое действие - свой условный оператор if. Поэтому программа работает по циклу корректно и без задержек. Пример ниже:
___________________________________________________________
  if ((RemoteXY.button_1 !=0) && (millis() - button_1_time > 500))
  // если кнопка нажата (состояние 1) и с момента последнего нажатия прошло не менее 500 мс (исключение многократного срабатывания при долгом нажатии на кнопку) выполняем код ниже
  { 
    digitalWrite(ON_OFF_relay, LOW);
    button_1_time = millis();       //актуализируем значение button_1_time
  } 
 
  //через 200-300 мс после замыкания реле (может выполниться несколько раз за 100 мс) выполняем код ниже
  if ((millis() - button_1_time > 200) && (millis() - button_1_time < 300))
  {
    digitalWrite(ON_OFF_relay, HIGH);
  }
___________________________________________________________________________


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

Ниже код. В функции schedule_func_ON() для простоты показан код только на 1 замыкание-размыкание реле.
________________________________________________________________________
void loop()
{
   ..........

  if ((RemoteXY.switch_1 != 0) && (strlen(RemoteXY.edit_1) == 8)) //если переключатель таймера в положении ВКЛ и в строку таймера вписаны значения в формате HH:MM:TT - выполняем код ниже
  {
    if (ON_OFF_state == 1)
    {
      //вызов функции включения водонагревателя по расписанию с изначально включенным водонагревателем
      schedule_func_ON();   
    }
    else
    {
      //вызов функции включения водонагревателя по расписанию с изначально выключенным водонагревателем
      schedule_func_OFF();   
    }
  }
}

void schedule_func_ON()

  int Hrs = (RemoteXY.edit_1[0] - 48)*10 + (RemoteXY.edit_1[1] - 48); 
  int Min = (RemoteXY.edit_1[3] - 48)*10 + (RemoteXY.edit_1[4] - 48);
  int Temp = (RemoteXY.edit_1[5] - 48)*10 + (RemoteXY.edit_1[6] - 48);
 
  //получаем данные от часов
  get3231Date();                                                                                                   
  if ((Hrs == hours) && (Min == minutes))

  //если настал "час X" - запускаем код ниже                               
  {
    Serial.println("Heater autoON operation start!!");
    Serial.println("IDLE pressing of TEMP button");
    Serial.print("Heat TEMP is: "); Serial.print(temp_state); Serial.println('°С');
    digitalWrite(temp_relay,LOW);                                                                          //замыкаем реле (замкнуто при LOW)   
     
    //бегаем в цикле while, пока не пройдет не менее 300мс                               
    unsigned long relay_time = 0;                             
    while (millis() - relay_time < 300)     
    {   
    }
    relay_time = millis();                                                                                       // актуализируем значение relay_time
    digitalWrite(temp_relay,HIGH);                                                                        // размыкаем реле (разомкнуто при HIGH)

    //Чтобы реле успело произвести размыкание перед следующим замыканием
    // (а у реле SRD-05VDC время release равно 5 мсек) - снова бегаем в цикле while 50 мсек
    while (millis() - relay_time < 50)
    {   
    } 
}
__________________________________________________________________________________

2

Re: Выполнение скрипта с задержками выполнения программы

Скажу вам ответ из своей практики почему использование задержек это плохо.
Как то давно автоматизировали насосную станцию. Программа была написана точно также, при пуске насоса отрабатывался алгоритм пуска с различными переключениями и паузами между ними. Выключатель включили, подождали, задвижку открыли подождали и т.д. Все это привело к тому, что в эти моменты переставала работать программа контроля аварийных ситуаций. Т.е. контроль аварий не работал несколько секунд. Программа аварийных ситуаций анализирует токи, вибрацию установки и еще ряд параметров и может аварийно остановить насос. И вот если в момент пуска, в течении этих нескольких секунд происходит аварийная ситуация, программа ее просто не видит. В итоге пока весь алгоритм старта не отработает, установка не отключается по аварии. А надо признать что аварии чаще всего бывают в момент пуска. И даже если оператор нажимал кнопку экстренного останова - ничего не происходило, ведь устройство контроля на "паузе".
Рекомендую такой подход.
1. Ваша программа должна постоянно крутиться в цикле. чем короче цикл, тем лучше.
2. В каждом цикле должны вызываться соответствующие обработчики задач. Задача контроля аварийных параметров, задача пуска, задача останова и обработчик RemoteXY так же.
3. Каждая задача должна получить текущее время, проанализировать текущее время и состояние выполнения задачи, если пришло время - выполнить задачу (чего нибудь включить, выключить), запомнить свое состояние для следующего вызова и отдать управление (выйти из процедуры).

Как реализовать процедуру обработчика? Например пуска?
1. Ввести переменную состояния процесса пуска.
2. Ввести переменную отсчета временного интервала.

int start_state = 0; //=0 - нет пуска, =1, рубильник замкнули, =2 рубильник разомкнули
long start_time;

void start () {
  if (start_state==0) {
    if (RemoteXY.start==1) { // нажата кнопка пуска
      ... замыкаем рубильник
      start_state = 1;
      start_time = millis ();  // запомнили время замыкания рубильника  
    }
  }
  else  if (start_state==1) {
    if (start_time+1000>millis ()) { // пришло время размыкания рубильника
      ... размыкаем рубильник
      start_state = 2;
      start_time = millis ();  // запомнили время размыкания рубильника  
    }
  }
  else  if (start_state==2) {
    ... установка включена
  }

}