так получше читается ? хотя новичку с #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 - значительно лучше, там каждая команда на реле отправляется строго один раз.
Здесь думаю уже от индивидуального восприятия зависит. Лично меня всегда вымораживает продираться через сложносочиненные ифы, стараюсь обходиться одним условием, ну может еще тру-флажок добавить. ИМХО.
P/S В сущности и гора ифов в моих примерах исключительно для более подробного представления, там прямо просятся кэйсы, как и сделал далее b707.
Все таки неудобно, оказалась последовательность неверная и понадобилось добавить или убрать условие, придется пересчитать все интервалы ниже, перенумеровать все флаги (скорее все таки счетчик) в двух местах, фактически заново перелопатить весь кусок ниже. С кэйсами: добавить-удалить строчку , перенумеровать кэйсы ниже, добавить-удалить интервал в массиве (в одном месте), быстро и практически исключен человеческий фактор (с массивом только пальцы правильно загибать). ИМХО.
Разумеется хуже. У новичка то нет даже необходимого минимума. Это как зная только половину букв научить читать и писать тексты. А если новичек будет выть, что научите читать, но простым языком, например при помощи 10 букв, а еще лучше при помощи 5. А теперь по теме. Использование миллис имеет свою специфику. И не зная и не привыкнув к ней вы не сможете банально читать программу с использованием millis(). Так что напрягитесть и разберитесь. Иначе все время будете "читать тескст при помощи 5 букв" и жаловаться на непонятки в тексте.
ПС: bwn, вы тоже не привыкли к miilis. А так не сложно.
Это как зная только половину букв научить читать и писать тексты.
Научиться ездить на велосипеде без велосипеда тоже невозможно, даже если зазубрить пдд и зная "на зубок" ттх велосипеда. А посуществу, новичок потому и новичок, что только пытается разобраться в "буквах", "нотах", "велосипедах" и т.п.
Все банально просто. И нет никаких заморочек. С 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. Новичек существо тупое и ленивое, и часто склонное впадать в самобман. Вот для борьбы с этим появились учебные заведения и учители, в крайнем случае репититоры.
/**/
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 байт.
*/
Фак его знает, глобальной разницы, честно говоря, не улавливаю. Может так и правильней.
Для себя, отладил функцию, забыл про нее и остался в лупе один единственный вызов. Нужно войти из другой функции, аналогично. ИМХО.
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 байт.
*/
Осталось только понять зачем вызывать одну глобальную функцию из другой без параметров. Ну или по крайней мере - почему она не inline. Затем выпить Семёновского срецтва и переписать это всё в нормальный конечный автомат.
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. Появляется возможность одновременной работы нескольких автоматов. Кстати, последнее легко реализуется созданием нескольких экземпляров класса (которые Вы так любите), конструктору которых передаются лишь адреса двух массивов - времен и функций.
andriano. Мелко вы мыслите. Конечный автомат это не функция run. Я такую ересь считаю безумной. Конечный автомат это прежде всего объект. А у объекта должны быть состояния и методы изменения этого состояния. state это и есть переменая состояния а stand это один из методов ее менять. Но и state и stand должны быть закрыты для пользования. Управляется конечный автомат функциями обертками. так void init_(){stand(0);}void Do2(){stand(1);}void Do2(){stand(5);} Опять же в функциях оберках можно поставить условия когда некоторый запуск цепочки запрещен. Да и классы это один из способов создания объектов. Просто с классов я начинал.
qwone - говорищь ты вроде правильно все - но на практике адриано, кмк. более прав. На словах твои принципы верные, а код в итоге выходит плохой. Что вот этот - в #61, что в твоих "классах для чайников" - бесколнечные повторы, "повисшие" переменные. куча лишних функций-оберток.
Твой код какой-то неживой - точно робот писал. Он очень похож на результат работы генератора кода графических программных сред, типа FLProg
Не знаю, соглашаться ли с Вами - это зависит от Вашей трактовки слова "объект". Если физический объект - нет, если программный - да.
Цитата:
Но и state и stand должны быть закрыты для пользования.
Именно.
Чего как раз в ВАшем коде нет, и это именно то направление, в котором я рекомендую Вам его переделать.
Цитата:
Управляется конечный автомат функциями обертками.
Не обязательно. Я как раз предложил вариант другого подхода к автоматному программированию. С одной стороны он без всяких функций-оберток может работать с разлиными внешними устройствиами. С другой - ограничен единственным типом условия переключения состояний - переключение по расписанию.
Цитата:
Опять же в функциях оберках можно поставить условия когда некоторый запуск цепочки запрещен.
Да. При дополнительных условиях функции-обертки потребуются.
Тем не менее, думаю, что против двух принципиальных моментов, которые я предложил:
1. Инкапсуляция переменной состояния.
2. Применение массивов для задания времен переклдючения и для адресов функций реакции. Особенно для сотни состояний.
Остались при своих. Вы ваши надуманые проблемы решаете надумаными решениями. Я предложил лучший способ эрзац решения перевести с delay на millis. А вы мне втираете что мой вертолет плох, так как он под водой течет, а в стратофере воздух не держит. Разные задачи- разные решения.
b707, код не должен быть живым. Он должен быть - планируемым. Всегда у него должна быть возможность расширится убрав косяки других частей или регресировать- убрать часть ненужного кода,так как эти ситуации исключились на более раних этапах.
/********************************************************
* 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);
}
И потом, как процессор будет понимать полупериоды ~220 если делать на симисторе?
если взять драйвер симистора с контролем нуля типа МОС3061 (5рублей на Али) - то понимать полупериоды будеть МОСька, а процессору останется только дергать управляющий сигнал.
так получше читается ? хотя новичку с #24 по мне так еще хуже читается
да, на мой взгляд так лучше. В том коде было. на мой взгляд, два основных недостатка - двойное вычисление миллис на каждой стадии - это вы убрали. А второе - два условия на каждый интервал, снизу и сверху. что затрудняет отладку (при изменении интервала надо исправлять его в двух местах).
Я бы завязал все условия через else. сделав по одной проверке на каждый интервал
Но вообще, такой подход мне в принципе не нравится - в нем при каждом проходе ЛУП будет выполнятся digitalWrite(). В данном случае это не важно - но представьте, что у вас вместо digitalWrite, например, отправка СМС - сообщение будет отправляться на каждом проходе ЛУП. В этом смысле подход со стадиями, как у BWN - значительно лучше, там каждая команда на реле отправляется строго один раз.
хотя новичку с #24 по мне так еще хуже читается
100% хуже! :)
Здесь думаю уже от индивидуального восприятия зависит. Лично меня всегда вымораживает продираться через сложносочиненные ифы, стараюсь обходиться одним условием, ну может еще тру-флажок добавить. ИМХО.
P/S В сущности и гора ифов в моих примерах исключительно для более подробного представления, там прямо просятся кэйсы, как и сделал далее b707.
можно опятьже флажков поставить чтобы постоянно digitalWrite() небыло.
Все таки неудобно, оказалась последовательность неверная и понадобилось добавить или убрать условие, придется пересчитать все интервалы ниже, перенумеровать все флаги (скорее все таки счетчик) в двух местах, фактически заново перелопатить весь кусок ниже. С кэйсами: добавить-удалить строчку , перенумеровать кэйсы ниже, добавить-удалить интервал в массиве (в одном месте), быстро и практически исключен человеческий фактор (с массивом только пальцы правильно загибать). ИМХО.
Разумеется хуже. У новичка то нет даже необходимого минимума. Это как зная только половину букв научить читать и писать тексты. А если новичек будет выть, что научите читать, но простым языком, например при помощи 10 букв, а еще лучше при помощи 5. А теперь по теме. Использование миллис имеет свою специфику. И не зная и не привыкнув к ней вы не сможете банально читать программу с использованием millis(). Так что напрягитесть и разберитесь. Иначе все время будете "читать тескст при помощи 5 букв" и жаловаться на непонятки в тексте.
ПС: bwn, вы тоже не привыкли к miilis. А так не сложно.
ПС: bwn, вы тоже не привыкли к miilis. А так не сложно.
а как это проявилось в коде?
Это как зная только половину букв научить читать и писать тексты.
Научиться ездить на велосипеде без велосипеда тоже невозможно, даже если зазубрить пдд и зная "на зубок" ттх велосипеда. А посуществу, новичок потому и новичок, что только пытается разобраться в "буквах", "нотах", "велосипедах" и т.п.
ПС: bwn, вы тоже не привыкли к miilis. А так не сложно.
Обоснуйте, плиз. Что я делаю не так с бабушкой Миллис?
Все банально просто. И нет никаких заморочек. С delay()
c millis()
Про обосновать. А зачем??
saltysoff. Новичек существо тупое и ленивое, и часто склонное впадать в самобман. Вот для борьбы с этим появились учебные заведения и учители, в крайнем случае репититоры.
c millis()
Фак его знает, глобальной разницы, честно говоря, не улавливаю. Может так и правильней.
Для себя, отладил функцию, забыл про нее и остался в лупе один единственный вызов. Нужно войти из другой функции, аналогично. ИМХО.
bwn.Вот с глобальной функцией . Но я не об этом . Наличие переменной state и функцией stand очень облегчает программирование при помощи millis. Так у меня организованы большинство классов. Да и отлаживать легко. Разумеется новичкам этого не понять они буков всех не знают.
Осталось только понять зачем вызывать одну глобальную функцию из другой без параметров. Ну или по крайней мере - почему она не inline. Затем выпить Семёновского срецтва и переписать это всё в нормальный конечный автомат.
sadman41, про автомат тут уже давно забыли, а ТС спрятался.)))) Теперь за разные подходы речь.
qwone, ок, поразмышляю.
выпить Семёновского срецтва и переписать это всё в нормальный конечный автомат.
угу - #31
/**/
Ужас!
А если состояний 100+?
1. Изменять переменную состояния в stand - неверно. Не ее это дело. Этим должен заниматься сам конечный автомат, т.е. run.
2. Соответственно state - не глобальная, а локальная статическая переменная функции run. В самом деле, в программе может быть несколько независимых конечных автоматов, и они не должны мешать друг другу через глобальные переменные.
3. Если в конечном автомате все условия однотипные (скажем, сравнение со временем), то нет смысла повторять одно и то же условие 100 раз.
4. Общая функция реакции stand не нужна. Вместо нее столько функций, сколько вариантов действий (на разные моменты времени может повторяться одно и то же действие).
5. В соответствии с 3 и 4 создаются два массива: моментов времни (т.е. (mil - past) сравнивается с элементом этого массива) и адресов функций (подходящая для данного случая функция вызывается по адресу).
Соответствыенно, весь код:
1. Упрощается.
2. Облегчается его поддержка и модификация.
3. Появляется возможность одновременной работы нескольких автоматов. Кстати, последнее легко реализуется созданием нескольких экземпляров класса (которые Вы так любите), конструктору которых передаются лишь адреса двух массивов - времен и функций.
andriano. Мелко вы мыслите. Конечный автомат это не функция run. Я такую ересь считаю безумной. Конечный автомат это прежде всего объект. А у объекта должны быть состояния и методы изменения этого состояния. state это и есть переменая состояния а stand это один из методов ее менять. Но и state и stand должны быть закрыты для пользования. Управляется конечный автомат функциями обертками. так void init_(){stand(0);} void Do2(){stand(1);} void Do2(){stand(5);} Опять же в функциях оберках можно поставить условия когда некоторый запуск цепочки запрещен. Да и классы это один из способов создания объектов. Просто с классов я начинал.
qwone - говорищь ты вроде правильно все - но на практике адриано, кмк. более прав. На словах твои принципы верные, а код в итоге выходит плохой. Что вот этот - в #61, что в твоих "классах для чайников" - бесколнечные повторы, "повисшие" переменные. куча лишних функций-оберток.
Твой код какой-то неживой - точно робот писал. Он очень похож на результат работы генератора кода графических программных сред, типа FLProg
Конечный автомат это прежде всего объект.
Не знаю, соглашаться ли с Вами - это зависит от Вашей трактовки слова "объект". Если физический объект - нет, если программный - да.
Но и state и stand должны быть закрыты для пользования.
Именно.
Чего как раз в ВАшем коде нет, и это именно то направление, в котором я рекомендую Вам его переделать.
Управляется конечный автомат функциями обертками.
Не обязательно. Я как раз предложил вариант другого подхода к автоматному программированию. С одной стороны он без всяких функций-оберток может работать с разлиными внешними устройствиами. С другой - ограничен единственным типом условия переключения состояний - переключение по расписанию.
Опять же в функциях оберках можно поставить условия когда некоторый запуск цепочки запрещен.
Да. При дополнительных условиях функции-обертки потребуются.
Тем не менее, думаю, что против двух принципиальных моментов, которые я предложил:
1. Инкапсуляция переменной состояния.
2. Применение массивов для задания времен переклдючения и для адресов функций реакции. Особенно для сотни состояний.
Вы возражать не станете.
Остались при своих. Вы ваши надуманые проблемы решаете надумаными решениями. Я предложил лучший способ эрзац решения перевести с delay на millis. А вы мне втираете что мой вертолет плох, так как он под водой течет, а в стратофере воздух не держит. Разные задачи- разные решения.
b707, код не должен быть живым. Он должен быть - планируемым. Всегда у него должна быть возможность расширится убрав косяки других частей или регресировать- убрать часть ненужного кода,так как эти ситуации исключились на более раних этапах.
Может ещё подскажете. Насколько сложно будет исключить из схемы РМ-2 и релюхи, и сделать автоматическую регулировку мощности с помощью шим?
Если асинхронник, то овчинка выделки не стоит.
что на РМ-2 висит? какой мощности?
Ректификационная колонна. нагревательный элемент 1,5кВт.
Пуя себе, а что мы там с давлением сочиняли?
Давление в колонне стабилизируется нагревом(объемом пара)
А температурой не проще? У меня по крайней мере так.
ммм, это?
и это вместо
рм-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
Я бы сказал, что невозможно.
Ну в принципе уже ответили, оптопара, симистор и пид.
То что нужно:) Осталось расшифровать манускрипт #77
Setpoint- заданное значение давления
WindowSize - время в миллисекундах длинны периода включсение нагрева(как будто это один период шим)
Kp=2, Ki=5, Kd=1; коэфициэнты ПИД (подбираются опытным путем)
DIRECT прямое регулирование REVERCE обратное
http://playground.arduino.cc/Code/PIDLibrary
Твердотельное реле не расплавится в режиме шим?
И потом, как процессор будет понимать полупериоды ~220 если делать на симисторе?
сдесь нет ШИМ, точнее он есть но очень растянутый во времени(частоте) при WindowSize 1000 часта будет 1HZ
и длинна периода ШИМ задается параметром WindowSize
т.е если WindowSize сделать 4 секунды, то этот скетч будет 1 раз включать и выключать симист в течении этих 4 секунд
Помехи в сети и прочие ппроблемы?
https://www.forumhouse.ru/threads/334671/
их небудет если ТТ реле с зерокросс
И потом, как процессор будет понимать полупериоды ~220 если делать на симисторе?
если взять драйвер симистора с контролем нуля типа МОС3061 (5рублей на Али) - то понимать полупериоды будеть МОСька, а процессору останется только дергать управляющий сигнал.