Помогите уйти от delay

b707
Offline
Зарегистрирован: 26.05.2017

evgta пишет:

так получше читается ?  хотя новичку с #24 по мне так еще хуже читается

да, на мой взгляд так лучше. В том коде было. на мой взгляд, два основных недостатка -  двойное вычисление миллис на каждой стадии  - это вы убрали. А второе - два условия на каждый интервал, снизу и сверху. что затрудняет отладку (при изменении интервала надо исправлять его в двух местах).

Я бы завязал все условия через else. сделав по одной проверке на каждый интервал

    if(flag == 3){
       
       interval=millis()-timeMillis;
      
       if (interval<=300 )  digitalWrite(reley1, LOW);
       else if (interval<=1300)  digitalWrite(reley1, HIGH);
       else if (interval<=1600) digitalWrite(reley2, LOW);
       else if (interval<=2600) digitalWrite(reley2, HIGH);
       else if (interval<=2900) digitalWrite(reley1, LOW);
       else if ( interval<=3900) digitalWrite(reley1, HIGH);
       else if (interval > 600000)  flag = 0;
     
     }

Но вообще, такой подход мне в принципе не нравится - в нем при каждом проходе ЛУП будет выполнятся digitalWrite(). В данном случае это не важно - но представьте, что у вас вместо digitalWrite, например, отправка СМС - сообщение будет отправляться на каждом проходе ЛУП. В этом смысле подход со стадиями, как у BWN - значительно лучше, там каждая команда на реле отправляется строго один раз.

bwn
Offline
Зарегистрирован: 25.08.2014

saltysoff пишет:

evgta пишет:

хотя новичку с #24 по мне так еще хуже читается

100% хуже! :)

Здесь думаю уже от индивидуального восприятия зависит. Лично меня всегда вымораживает продираться через сложносочиненные ифы, стараюсь обходиться одним условием, ну может еще тру-флажок добавить. ИМХО.

P/S В сущности и гора ифов в моих примерах исключительно для более подробного представления, там прямо просятся кэйсы, как и сделал далее b707.

evgta
Offline
Зарегистрирован: 02.09.2016

можно опятьже флажков поставить чтобы постоянно  digitalWrite() небыло.

     if(flag == 3){
   
   interval=millis()-timeMillis;
  
   if (interval<=300 && flag2 == 0)  {digitalWrite(reley1, LOW); flag2=1;}
   else if (interval<=1300 && flag2 == 1)  {digitalWrite(reley1, HIGH); flag2=2;}
   else if (interval<=1600 && flag2 == 2) {digitalWrite(reley2, LOW);flag2=3;}
   else if (interval<=2600 && flag2 == 3) {digitalWrite(reley2, HIGH);flag2=4;}
   else if (interval<=2900 && flag2 == 4) {digitalWrite(reley1, LOW); flag2=5;}
   else if ( interval<=3900 && flag2 == 5) {digitalWrite(reley1, HIGH);flag2=0;}
   else if (interval > 600000)  {flag = 0;}

 

bwn
Offline
Зарегистрирован: 25.08.2014

Все таки неудобно, оказалась последовательность неверная и понадобилось добавить или убрать условие, придется пересчитать все интервалы ниже, перенумеровать все флаги (скорее все таки счетчик) в двух местах, фактически заново перелопатить весь  кусок ниже. С кэйсами: добавить-удалить строчку , перенумеровать кэйсы ниже, добавить-удалить интервал в массиве (в одном месте), быстро и практически исключен человеческий фактор (с массивом только пальцы правильно загибать). ИМХО.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Разумеется хуже. У новичка то нет даже необходимого минимума. Это как зная только половину букв научить читать и писать тексты. А если новичек будет выть, что научите читать, но простым языком, например при помощи 10 букв, а еще лучше при помощи 5.  А теперь по теме. Использование миллис  имеет свою специфику. И не зная и не привыкнув к ней вы не сможете банально читать программу с использованием millis(). Так что напрягитесть и разберитесь. Иначе все время будете "читать тескст при помощи 5 букв" и жаловаться на  непонятки в тексте.

ПС: bwn, вы тоже не привыкли к miilis. А так не сложно. 

b707
Offline
Зарегистрирован: 26.05.2017

qwone пишет:

ПС: bwn, вы тоже не привыкли к miilis. А так не сложно. 

а как это проявилось в коде?

saltysoff
Offline
Зарегистрирован: 05.02.2018

qwone пишет:

Это как зная только половину букв научить читать и писать тексты. 

Научиться ездить на велосипеде без велосипеда тоже невозможно, даже если зазубрить пдд и зная "на зубок" ттх велосипеда. А посуществу, новичок потому и новичок, что только пытается разобраться в "буквах", "нотах", "велосипедах" и т.п. 

bwn
Offline
Зарегистрирован: 25.08.2014

qwone пишет:

ПС: bwn, вы тоже не привыкли к miilis. А так не сложно. 

Обоснуйте, плиз. Что я делаю не так с бабушкой Миллис? 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Все банально просто. И нет никаких заморочек. С delay()

void setup() {


}

void loop() {
  int a = 5; /*условие*/
  if (a == 5) { /*если это условие выполнилось*/
    /*Сделать A*/
    delay(100);
    /*Сделать B*/
    delay(200);
    /*Сделать C*/
    delay(300);
    /*Сделать D*/
    delay(400);
    /*Сделать E*/
  }
}
/*Скетч использует 682 байт (2%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
*/

c millis()

/**/
unsigned long mill, past;
byte state = 0;
void stand(byte s) {
  state = s;
  past = mill;
  switch (state) {
    case 1:
      /*Сделать A*/
      break;
    case 2:
      /*Сделать B*/
      break;
    case 3:
      /*Сделать C*/
      break;
    case 4:
      /*Сделать D*/
      break;
    case 5:
      /*Сделать E*/
      break;
  }
}
void setup() {
}

void loop() {
  mill = millis();
  int a = 5; /*условие*/
  if (a == 5) { /*если это условие выполнилось*/
    stand(1);
  }
  if (state == 1 && mill - past > 100)stand(2);
  if (state == 2 && mill - past > 200)stand(3);
  if (state == 3 && mill - past > 300)stand(4);
  if (state == 4 && mill - past > 400)stand(5);
}
/*Скетч использует 504 байт (1%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 18 байт (0%) динамической памяти, оставляя 2030 байт для локальных переменных. Максимум: 2048 байт.
*/

 Про обосновать. А зачем?? 

saltysoff. Новичек существо тупое и ленивое, и часто склонное впадать в самобман. Вот для борьбы с этим появились учебные заведения и учители, в крайнем случае репититоры. 

bwn
Offline
Зарегистрирован: 25.08.2014

qwone пишет:

c millis()

/**/
unsigned long mill, past;
byte state = 0;
void stand(byte s) {
  state = s;
  past = mill;
  switch (state) {
    case 1:
      /*Сделать A*/
      break;
    case 2:
      /*Сделать B*/
      break;
    case 3:
      /*Сделать C*/
      break;
    case 4:
      /*Сделать D*/
      break;
    case 5:
      /*Сделать E*/
      break;
  }
}
void setup() {
}

void loop() {
  mill = millis();
  int a = 5; /*условие*/
  if (a == 5) { /*если это условие выполнилось*/
    stand(1);
  }
  if (state == 1 && mill - past > 100)stand(2);
  if (state == 2 && mill - past > 200)stand(3);
  if (state == 3 && mill - past > 300)stand(4);
  if (state == 4 && mill - past > 400)stand(5);
}
/*Скетч использует 504 байт (1%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 18 байт (0%) динамической памяти, оставляя 2030 байт для локальных переменных. Максимум: 2048 байт.
*/

Фак его знает, глобальной разницы, честно говоря, не улавливаю. Может так и правильней.
Для себя, отладил функцию, забыл про нее и остался в лупе один единственный вызов. Нужно войти из другой функции, аналогично. ИМХО.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

 bwn.Вот с глобальной функцией . Но я не об этом . Наличие переменной state и функцией stand  очень облегчает программирование при помощи millis. Так у меня организованы большинство классов. Да и отлаживать легко. Разумеется новичкам этого не понять они буков всех не знают.

/**/
unsigned long mill, past;
byte state = 0;
void stand(byte s) {
  state = s;
  past = mill;
  switch (state) {
    case 1:
      /*Сделать A*/
      break;
    case 2:
      /*Сделать B*/
      break;
    case 3:
      /*Сделать C*/
      break;
    case 4:
      /*Сделать D*/
      break;
    case 5:
      /*Сделать E*/
      break;
  }
}
void run() {
  if (state == 1 && mill - past > 100)stand(2);
  if (state == 2 && mill - past > 200)stand(3);
  if (state == 3 && mill - past > 300)stand(4);
  if (state == 4 && mill - past > 400)stand(5);
}
void setup() {
}

void loop() {
  mill = millis();
  int a = 5; /*условие*/
  if (a == 5) { /*если это условие выполнилось*/
    stand(1);
  }
  run();//<-- глобальная функция
}
/*Скетч использует 504 байт (1%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 18 байт (0%) динамической памяти, оставляя 2030 байт для локальных переменных. Максимум: 2048 байт.
*/

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Осталось только понять зачем вызывать одну глобальную функцию из другой без параметров. Ну или по крайней мере - почему она не inline. Затем выпить Семёновского срецтва и переписать это всё в нормальный конечный автомат. 

bwn
Offline
Зарегистрирован: 25.08.2014

sadman41, про автомат тут уже давно забыли, а ТС спрятался.))))  Теперь за разные подходы речь.

qwone, ок, поразмышляю.

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:

 выпить Семёновского срецтва и переписать это всё в нормальный конечный автомат. 

угу - #31

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

qwone пишет:

/**/

unsigned long mill, past;
byte state = 0;
void stand(byte s) {
  state = s;
  past = mill;
  switch (state) {
    case 1:
      /*Сделать A*/
      break;
    case 2:
      /*Сделать B*/
      break;
    case 3:
      /*Сделать C*/
      break;
    case 4:
      /*Сделать D*/
      break;
    case 5:
      /*Сделать E*/
      break;
  }
}
void run() {
  if (state == 1 && mill - past > 100)stand(2);
  if (state == 2 && mill - past > 200)stand(3);
  if (state == 3 && mill - past > 300)stand(4);
  if (state == 4 && mill - past > 400)stand(5);
}
void setup() {
}

void loop() {
  mill = millis();
  int a = 5; /*условие*/
  if (a == 5) { /*если это условие выполнилось*/
    stand(1);
  }
  run();//<-- глобальная функция
}
/*Скетч использует 504 байт (1%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 18 байт (0%) динамической памяти, оставляя 2030 байт для локальных переменных. Максимум: 2048 байт.
*/

 

Ужас!

А если состояний 100+?

1. Изменять переменную состояния в stand - неверно. Не ее это дело. Этим должен заниматься сам конечный автомат, т.е. run.

2. Соответственно state - не глобальная, а локальная статическая переменная функции run. В самом деле, в программе может быть несколько независимых конечных автоматов, и они не должны мешать друг другу через глобальные переменные.

3. Если в конечном автомате все условия однотипные (скажем, сравнение со временем), то нет смысла повторять одно и то же условие 100 раз.

4. Общая функция реакции stand не нужна. Вместо нее столько функций, сколько вариантов действий (на разные моменты времени может повторяться одно и то же действие). 

5. В соответствии с 3 и 4 создаются два массива: моментов времни (т.е. (mil - past) сравнивается с элементом этого массива)  и адресов функций (подходящая для данного случая функция вызывается по адресу).

Соответствыенно, весь код:

1. Упрощается.

2. Облегчается его поддержка и модификация.

3. Появляется возможность одновременной работы нескольких автоматов. Кстати, последнее легко реализуется созданием нескольких экземпляров класса (которые Вы так любите), конструктору которых передаются лишь адреса двух массивов - времен и функций.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

andriano. Мелко вы мыслите. Конечный автомат это не функция run. Я такую ересь считаю безумной. Конечный автомат это прежде всего объект. А у объекта должны быть состояния и методы изменения этого состояния.  state это и есть переменая состояния а stand это один из методов ее менять. Но и state и stand должны быть закрыты для пользования. Управляется конечный автомат функциями обертками. так void init_(){stand(0);}   void Do2(){stand(1);}     void Do2(){stand(5);} Опять же в функциях оберках можно поставить условия когда некоторый запуск цепочки запрещен.    Да и классы это один из способов создания объектов.  Просто с классов я начинал.

b707
Offline
Зарегистрирован: 26.05.2017

qwone - говорищь ты вроде правильно все - но на практике адриано, кмк. более прав.  На словах твои принципы верные, а код в итоге выходит плохой. Что вот этот - в #61, что в твоих "классах для чайников" - бесколнечные повторы, "повисшие" переменные. куча лишних функций-оберток.

Твой код какой-то неживой - точно робот писал. Он очень похож на результат работы генератора кода графических программных сред, типа FLProg

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

qwone пишет:

Конечный автомат это прежде всего объект.

Не знаю, соглашаться ли с Вами - это зависит от Вашей трактовки слова "объект". Если физический объект - нет, если программный - да.

Цитата:

Но и state и stand должны быть закрыты для пользования.

Именно.

Чего как раз в ВАшем коде нет, и это именно то направление, в котором я рекомендую Вам его переделать.

Цитата:

Управляется конечный автомат функциями обертками.

Не обязательно. Я как раз предложил вариант другого подхода к автоматному программированию. С одной стороны он без всяких функций-оберток может работать с разлиными внешними устройствиами. С другой - ограничен единственным типом условия переключения состояний - переключение по расписанию.

Цитата:

Опять же в функциях оберках можно поставить условия когда некоторый запуск цепочки запрещен.

Да. При дополнительных условиях функции-обертки потребуются.

 

Тем не менее, думаю, что против двух принципиальных моментов, которые я предложил:

1. Инкапсуляция переменной состояния.

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

Вы возражать не станете.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Остались при своих. Вы ваши надуманые проблемы решаете надумаными решениями. Я предложил лучший способ эрзац решения перевести с delay на millis.  А вы мне втираете что мой вертолет плох, так как он под водой течет, а в стратофере воздух не держит. Разные задачи- разные решения. 

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

saltysoff
Offline
Зарегистрирован: 05.02.2018

Может ещё подскажете. Насколько сложно будет исключить из схемы РМ-2 и релюхи, и сделать автоматическую регулировку мощности с помощью шим?

bwn
Offline
Зарегистрирован: 25.08.2014

Если асинхронник, то овчинка выделки не стоит.

evgta
Offline
Зарегистрирован: 02.09.2016

что на РМ-2 висит? какой мощности?

saltysoff
Offline
Зарегистрирован: 05.02.2018

Ректификационная колонна. нагревательный элемент 1,5кВт.

bwn
Offline
Зарегистрирован: 25.08.2014

Пуя себе, а что мы там с давлением сочиняли?

saltysoff
Offline
Зарегистрирован: 05.02.2018

Давление в колонне стабилизируется нагревом(объемом пара)

bwn
Offline
Зарегистрирован: 25.08.2014

А температурой не проще? У меня по крайней мере так.

evgta
Offline
Зарегистрирован: 02.09.2016

ммм, это?


/********************************************************
 * PID RelayOutput Example
 * Same as basic example, except that this time, the output
 * is going to a digital pin which (we presume) is controlling
 * a relay.  the pid is designed to Output an analog value,
 * but the relay can only be On/Off.
 *
 *   to connect them together we use "time proportioning
 * control"  it's essentially a really slow version of PWM.
 * first we decide on a window size (5000mS say.) we then
 * set the pid to adjust its output between 0 and that window
 * size.  lastly, we add some logic that translates the PID
 * output into "Relay On Time" with the remainder of the
 * window being "Relay Off Time"
 ********************************************************/

#include <PID_v1.h>

#define PIN_INPUT 0
#define RELAY_PIN 6

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
double Kp=2, Ki=5, Kd=1;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;

void setup()
{
  windowStartTime = millis();

  //initialize the variables we're linked to
  Setpoint = 100;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
}

void loop()
{
  Input = analogRead(PIN_INPUT);
  myPID.Compute();

  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  if (millis() - windowStartTime > WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if (Output < millis() - windowStartTime) digitalWrite(RELAY_PIN, HIGH);
  else digitalWrite(RELAY_PIN, LOW);

}

 

и это вместо 

рм-2

https://ru.aliexpress.com/item/Twtade-SSR-40DA-40A/32846480241.html?s=p&ws_ab_test=searchweb0_0%2Csearchweb201602_1_10152_10151_10065_10344_10068_10342_10343_10340_10341_10543_10084_10083_10618_10630_10307_10301_5711211_10313_10059_5722311_10534_100031_10629_10103_10626_10624_10623_10622_10621_10620_10142_10125%2Csearchweb201603_25%2CppcSwitch_5&algo_expid=d06d424e-5776-4db4-8687-a70bb7882047-2&algo_pvid=d06d424e-5776-4db4-8687-a70bb7882047&priceBeautifyAB=0

saltysoff
Offline
Зарегистрирован: 05.02.2018

Я бы сказал, что невозможно.

bwn
Offline
Зарегистрирован: 25.08.2014

Ну в принципе уже ответили, оптопара, симистор и пид.

saltysoff
Offline
Зарегистрирован: 05.02.2018

То что нужно:) Осталось расшифровать манускрипт #77

evgta
Offline
Зарегистрирован: 02.09.2016

Setpoint- заданное значение давления

WindowSize - время в миллисекундах длинны периода включсение нагрева(как будто это один период шим)

Kp=2, Ki=5, Kd=1; коэфициэнты ПИД (подбираются опытным путем)

DIRECT прямое регулирование REVERCE обратное

http://playground.arduino.cc/Code/PIDLibrary

 

saltysoff
Offline
Зарегистрирован: 05.02.2018

Твердотельное реле не расплавится в режиме шим?

saltysoff
Offline
Зарегистрирован: 05.02.2018

И потом, как процессор будет понимать полупериоды ~220 если делать на симисторе?

evgta
Offline
Зарегистрирован: 02.09.2016

сдесь нет ШИМ, точнее он есть но очень растянутый во времени(частоте) при WindowSize 1000 часта будет 1HZ

и длинна периода ШИМ задается параметром  WindowSize 

т.е если WindowSize сделать 4 секунды, то этот скетч будет 1 раз включать и выключать симист в течении этих 4 секунд

 

 

saltysoff
Offline
Зарегистрирован: 05.02.2018

Помехи в сети и прочие ппроблемы?

https://www.forumhouse.ru/threads/334671/

evgta
Offline
Зарегистрирован: 02.09.2016

их небудет если ТТ реле с зерокросс 

b707
Offline
Зарегистрирован: 26.05.2017

saltysoff пишет:

И потом, как процессор будет понимать полупериоды ~220 если делать на симисторе?

если взять драйвер симистора с контролем нуля типа МОС3061 (5рублей на Али) - то понимать полупериоды будеть МОСька, а процессору останется только дергать управляющий сигнал.