Как выполнить код только 1 раз в цикле loop?

NeXan
Offline
Зарегистрирован: 03.06.2019

Всем привет. Решил переделать автоматический освежитель воздуха. Идея такова, что после выключения света в туалете освежитель пшикает один раз и переходит в режим ожидания до следующего включения и выключения света. Для этого в схеме использовал фоторезистор и реле (потом заменю на транзистор), которое приводит в движение привод.

Так вот, я хочу, чтобы после выключения света, реле срабатывало только 1 раз (1 пшик), а затем переходило в режим ожидания.

Написал такой простенький код, однако после выключения света код в цикле loop не останавливается, реле, то включается, то выключается. Подскажите, пожалуйта, как реализовать эту затею? Спасибо!

const int photo = A0;
const int motor = 13;
int raw = 0;

void setup() {
  pinMode( photo, INPUT );
  pinMode( motor, OUTPUT );
}

void loop() {
  raw = analogRead( photo );
  if( raw < 500)
    digitalWrite( motor, HIGH );
    delay(1000);
    digitalWrite( motor, LOW );
    delay(1000);
}

 

 

Morroc
Offline
Зарегистрирован: 24.10.2016

Например завести еще одну переменную-флаг и добавить в условие. Если флаг false - щелкаем реле и делаем флаг = true, после этого условие перестанет выполняться. Чтобы это все сработало в следующий раз при включении/выключении света придется добавить еще одно отдельное условие, которое будет делать флаг = false когда светло.

NeXan
Offline
Зарегистрирован: 03.06.2019

Изменил код с условием true/false, теперь реле срабатывает 1 раз как положено, но второй раз отказывается. После перезагрузки все повторяется. Подскажите, как включать реле подобным образом без перезагрузки?

const int photo = A0;
const int motor = 13;
int raw = 0;
boolean is_once /* = true*/;


void setup() {
  pinMode( photo, INPUT );
  pinMode( motor, OUTPUT );
  is_once = true;
}

void loop() {
  raw = analogRead( photo );
  if( raw < 500)
  if (is_once) {
    digitalWrite( motor, HIGH );
    delay(1000);
    digitalWrite( motor, LOW );
    delay(1000);
    is_once = false;
}}

 

 

Pyotr
Offline
Зарегистрирован: 12.03.2014

while(analogRead( photo ) > 500){}  //тормозим пока свет включен

while(analogRead( photo ) < 500){}  //тормозим пока свет вЫключен

Эти строки (одну или две) нужно вставить в loop(). Пробуйте.

sadman41
Offline
Зарегистрирован: 19.10.2016
if (raw > 700) { readyToRun = true; }
if (readyToRun && raw < 500) { ...; readyToRun = false; }
Morroc
Offline
Зарегистрирован: 24.10.2016

NeXan пишет:

Подскажите, как включать реле подобным образом без перезагрузки?

Может... прочитать совет до конца ? :)

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

NeXan пишет:

Изменил код с условием true/false, теперь реле срабатывает 1 раз как положено, но второй раз отказывается. После перезагрузки все повторяется. Подскажите, как включать реле подобным образом без перезагрузки?

const int photo = A0;
const int motor = 13;
int raw = 0;
boolean is_once /* = true*/;


void setup() {
  pinMode( photo, INPUT );
  pinMode( motor, OUTPUT );
  is_once = true;
}

void loop() {
  raw = analogRead( photo );
  if( raw < 500)
  if (is_once) {
    digitalWrite( motor, HIGH );
    delay(1000);
    digitalWrite( motor, LOW );
    delay(1000);
    is_once = false;
}}

чуть чуть не хватило, я думаю если бы расставили комментарии сами дописали как надо.

const int photo = A0;
const int motor = 13;
int raw = 0;
boolean is_once /* = true*/;


void setup() {
  pinMode( photo, INPUT );
  pinMode( motor, OUTPUT );
  is_once = true;
}

void loop() {
  raw = analogRead( photo );         // читаем датчик
  if ( raw < 500) {                  // света - нет
    if (is_once) {                   // уже пшикали ?
      digitalWrite( motor, HIGH );   // нет, пшикаем
      delay(1000);
      digitalWrite( motor, LOW );
      delay(1000);
      is_once = false;               // очищаем флаг, пшикнули
    }
  } else {
    is_once = true;                 // зажгли свет, взводим флаг
  }
}

 

NeXan
Offline
Зарегистрирован: 03.06.2019

Спасибо большое! Все работает практически как надо. Вот только иногда случаются ложные срабатывания, все дело в отсутствии гистерезиса. Я думал, что проще всего в код, приведенный выше, добавить опрос фоторезистора не постоянно, а с задержкой, например, раз в 5 секунд. Delay не подходит, так как начинает тормозить другой код программы.

В интернете похожей реализации нигде не нашел (((

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

Такую сложную и редкую задачу в интернетах не рассматривают. 

NeXan
Offline
Зарегистрирован: 03.06.2019

Значит добавить опрос будет сложно. А что если добавить задержку, (кроме delay), например, если свет включен более 5 секунд, то после выключения света, включить реле. Если менее 5 секунд, то ничего не делать.

Pyotr
Offline
Зарегистрирован: 12.03.2014

NeXan пишет:

... Delay не подходит, так как начинает тормозить другой код программы.

 

А вот про это в первом посте не было...

NeXan
Offline
Зарегистрирован: 03.06.2019

Pyotr пишет:

А вот про это в первом посте не было...

Сам участок кода с delay

digitalWrite( motor, HIGH );
delay(1000);
digitalWrite( motor, LOW );
delay(1000);

меня устраивает. Хотелось бы добавить что то наподобие гистерезиса - опроса датчика раз в 5 секунд, а не постоянное((

 

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

Что подразумевается под гистерезисом?

NeXan
Offline
Зарегистрирован: 03.06.2019

Срабатывание реле не сразу после того, как значение датчика опустилось, например до 300. А если значение равно 300 и меньше в течение 5 сек, то включить реле. Если менее, то не включать. И наоборот.

Pyotr
Offline
Зарегистрирован: 12.03.2014

Это я про свой пример выше - аналог delay()

Вот почти готовый пример. Попробуйте вставить в свой код. Название функции и переменных  поменяйте.

//=========== вызов каждые 100-300 мс 
byte controlFireGas(){//возвращ. 0 если нет пламени 
  byte static stateFlame = 1;
  stateFlame <<= 1;
  if(analogRead(PIN_FIRE_GAZ) > 50){//есть запал
    stateFlame |= 1;
  }
  return stateFlame;
}

в loop()

  if(stateFlame == 0){//потухло пламя-//нет пламени за 8 чтений

     //что-то делаем
  }else if(stateFlame == 0xFF){//есть пламя стабильное
    //что-то делаем
    
  }else{ //есть пламя НЕстабильное    
    //что-то делаем
  }

 

NeXan
Offline
Зарегистрирован: 03.06.2019

Спасибо! Кстати, в моем коде вот эта функция считывает показания порта, к которому подключен фоторезистор:

raw = analogRead( photo );

И считывает постоянно, можно ли сделать опросы периодами? Например, те же раз 5 сек.

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

тока дай палец...

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

NeXan пишет:

Спасибо! Кстати, в моем коде вот эта функция считывает показания порта, к которому подключен фоторезистор:

raw = analogRead( photo );

И считывает постоянно, можно ли сделать опросы периодами? Например, те же раз 5 сек.

Можно. Используйте millis()

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014
Как по мне то нужен обычный Антидребезг
Если Вас устраюет delay то Используя код из #6 попробуйте так:
const int photo = A0;
const int motor = 13;
int raw = 0;
boolean is_once /* = true*/;


void setup() {
  pinMode( photo, INPUT );
  pinMode( motor, OUTPUT );
  is_once = true;
}

void dataRaw(){
  raw = analogRead( photo );         // читаем датчик
}

void loop() {
  dataRaw();
  if (is_once && raw < 500) {        // света - нет
    delay(100);                     // Типа Антидребезг
    dataRaw();                      // читаем датчик
    if(raw < 500)
    {
      digitalWrite( motor, HIGH );   // нет, пшикаем
      delay(1000);
      digitalWrite( motor, LOW );
      delay(1000);
      is_once = false;               // очищаем флаг, пшикнули
    }  
  } 
  if (raw > 500 && !is_once) {
    delay(100);                      // Типа Антидребезг
    dataRaw();                       // читаем датчик
    if(raw > 500) is_once = true;     // зажгли свет, взводим флаг
  }
}

 

 
ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

делей надо искоренить!! когда же вы начнете использовать хотябы миллис вместо делеев!!

Morroc
Offline
Зарегистрирован: 24.10.2016

Долой delay() ! Хватит это терпеть ! :)

NeXan пишет:

И считывает постоянно, можно ли сделать опросы периодами? Например, те же раз 5 сек.

Лучше сделать усреднение, но считывать и правда можно пореже.

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

както так на вскидку

#define photo A0
#define motor 13
uint8_t raw = 0;

boolean is_once = 1; /* = true*/

uint32_t cMs , pMs1;    // таймеры

void setup() {
  pinMode( photo, INPUT );
  pinMode( motor, OUTPUT );
}

void Pshik(){
      digitalWrite( motor, HIGH );   // нет, пшикаем
      delay(1000);
      digitalWrite( motor, LOW );
      delay(1000);
}

void loop() {
 cMs = millis(); //текущее время
 /******************** опрос датчикa *********************/
  if ( pMs1 + 5000UL <= cMs ){   pMs1 = cMs; // срабатывает каждые 5 секунд
     raw = analogRead( photo );         // читаем датчик
  }
 /*******************************************************/ 
 if(raw < 500) { // свет
    if (is_once) {Pshik(); !is_once; }
  } else {  // нет света - сброс ключа
    !is_once;
  }
  

}

 

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

ну или даже так 


#define photo A0
#define motor 13
#define svet 5 //лампочка в туалете
uint32_t raw = 0; uint32_t cMs , pMs1; // счетчики времени 

void setup() 
{ pinMode( photo, INPUT );

  pinMode( motor, OUTPUT );
  pinMode( svet , OUTPUT ); //реле на отключение лампочки
} 

void Pshik(){ // ваша пшикалка 
  digitalWrite( motor, HIGH ); // нет, пшикаем 
  delay(1000); 
  digitalWrite( motor, LOW ); 
  delay(1000); } 
void loop() { cMs = millis(); //текущее время, если надо несколько таймеров будет... 
  /******************** опрос датчикa *********************/ 
  if ( cMs - pMs1 > 1000UL ){ pMs1 = cMs; // срабатывает каждые 1 секунду 
  if (analogRead( photo ) > 500 ) raw++; else raw=0; //считает число срабатываний }
  /*******************************************************/
  if(raw == 5 || raw == 600 ) { // свет включен 5 секунд и повтор через 600 секунд (10 мин) 
    Pshik(); 
  } 

  if(raw > 1000) digitalWrite( svet, 1);  //ибо нефик так долго занимать помещение!

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

Жесть какая-то. ТС гистерезис не может даже в этом посте найти, как он в ваших скетчах-то разберётся...

Pyotr
Offline
Зарегистрирован: 12.03.2014

if ( pMs1 + 5000UL <= cMs ){   pMs1 = cMs; // срабатывает каждые 5 секунд

Щас шо будеть...))

Morroc
Offline
Зарегистрирован: 24.10.2016

ПЕРЕПОЛНЕНИЕ ! Всем в укрытие ! oO

 

:) :) :)

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

да, переполнение - а предложите свой вариант? )

Morroc
Offline
Зарегистрирован: 24.10.2016

Минуснуть противоположную сторону.

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

супер - и получить раз в 65 секунд переполнение миллеса... и глюк на следующие 65 сукунд (а то и на всегда до рестарта)

Morroc
Offline
Зарегистрирован: 24.10.2016

Уже попробовали ? 

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

ELITE пишет:

супер - и получить раз в 65 секунд переполнение миллеса... 

Свидетели секты святого переполнения миилиса опять на форуме...

P.S. А вот нехер складывать, чтобы переполнения не было. Отнимать надо!

P.P.S. Обязательно к прочтению - http://arduino.ru/forum/programmirovanie/velikoe-perepolnenie-millis

Morroc
Offline
Зарегистрирован: 24.10.2016

Я в ней был, там хорошо :)

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

ок...

millis 65с до переполнения и с 0, допустим сейчас 30с набежало
timing = 0
if (millis() - timing	> 9){  timing = millis();  }
		30	-	0 	  	> 9 (true) timing = 30
		31 	- 	30		> 9 (false)
			....
		39 	- 	30		> 9 (false)
		40 	- 	30		> 9 (true) timing = 40
			....
		50 	- 	40		> 9 (true) timing = 50
			....
		60 	- 	50		> 9 (true) timing = 60
		61 	- 	60		> 9 (false)
		62 	- 	60		> 9 (false)
		63 	- 	60		> 9 (false)
		65	- 	60		> 9 (false)
		 0 	- 	60		> 9 (false)
		 1 	- 	60		> 9 (false)
		 2 	- 	60		> 9 (false)
		 3 	- 	60		> 9 (false)
		 4 	- 	60		> 9 (false)
		 5 	- 	60		> 9 (false)
			....
			....
			....
		20 	- 	60		> 9 (false)

и что в итоге?... - код больше никогда не сработает

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

ELITE пишет:

и что в итоге?... - код больше никогда не сработает

Обязательно к прочтению - http://arduino.ru/forum/programmirovanie/velikoe-perepolnenie-millis

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

ELITE пишет:

и что в итоге?... - код больше никогда не сработает

Дак всё правильно. Название топика какое? "Как выполнить код только 1 раз в цикле loop". Всё в соответствии с требованиям.  

Morroc
Offline
Зарегистрирован: 24.10.2016

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

вся разница в том, что переполнение 50 суток а не 65 секунд, ну перепутал с микрос...

читал я те изыскания и пробовал - лучше лишнее срабатывание , чем полный отвел....

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

sadman41 пишет:

ELITE пишет:

и что в итоге?... - код больше никогда не сработает

Дак всё правильно. Название топика какое? "Как выполнить код только 1 раз в цикле loop". Всё в соответствии с требованиям.  

ну да, но ему надо и "сброс" иметь, а не вечное зависание...

хотя можно и софтресет делать при выключении свет... asm("JMP 0");

 

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

ELITE - родолжаете блистать как знаток Си? :)

Запустите свой же код в ардуине и убедитесь, что переполнения нет

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

ELITE пишет:

ну да, но ему надо и "сброс" иметь, а не вечное зависание...

Это требование противоречит основному . Придётся что-то одно выбрать.

ELITE пишет:

хотя можно и софтресет делать при выключении свет... asm("JMP 0");

Лучше аппаратный WatchDog - он надёжней. 

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

sadman41 пишет:

ELITE пишет:

хотя можно и софтресет делать при выключении свет... asm("JMP 0");

Лучше аппаратный WatchDog - он надёжней. 

можно и пин дергать и на 0 делить... вариантор много... но это уже совсем другая пьесса...

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

b707 пишет:

ELITE - родолжаете блистать как знаток Си? :)

Запустите свой же код в ардуине и убедитесь, что переполнения нет

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

ELITE
ELITE аватар
Offline
Зарегистрирован: 11.01.2018

ок, тс пусь будет "правильное" ( cMs - pMs1 > 1000UL )

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ELITE пишет:

ок...

millis 65с до переполнения и с 0, допустим сейчас 30с набежало
timing = 0
if (millis() - timing	> 9){  timing = millis();  }
		30	-	0 	  	> 9 (true) timing = 30
		31 	- 	30		> 9 (false)
			....
		39 	- 	30		> 9 (false)
		40 	- 	30		> 9 (true) timing = 40
			....
		50 	- 	40		> 9 (true) timing = 50
			....
		60 	- 	50		> 9 (true) timing = 60
		61 	- 	60		> 9 (false)
		62 	- 	60		> 9 (false)
		63 	- 	60		> 9 (false)
		65	- 	60		> 9 (false)
		 0 	- 	60		> 9 (false)
		 1 	- 	60		> 9 (false)
		 2 	- 	60		> 9 (false)
		 3 	- 	60		> 9 (false)
		 4 	- 	60		> 9 (false)
		 5 	- 	60		> 9 (false)
			....
			....
			....
		20 	- 	60		> 9 (false)

и что в итоге?... - код больше никогда не сработает

;)))))

какой зайчеГ!!!!

В твоем примере - 4 - 60 > 9(Sic!) и 5-60 и 6-60, все они больше 9 (по модулю 66). Неожиданно?

Прочти строчку выше столько раз, сколько нужно для просветления! Может быть тогда поймёшь, как работает вычисление простых операций в кАмпутере... ну там плюсик, минусик и т.п.

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

ELITE пишет:

делей надо искоренить!! когда же вы начнете использовать хотябы миллис вместо делеев!!

Согласен!!! на 199.5 процента, смотрим код примера 
void Pshik(){ // ваша пшикалка 
  digitalWrite( motor, HIGH ); // нет, пшикаем 
  delay(1000); 
  digitalWrite( motor, LOW ); 
  delay(1000); }
Опа, а это что такое. не тот ли delay с каким надо бороться, или это какойто ДРУГОЙ - так сказать ЭЛИТНЫЙ
 
Morroc
Offline
Зарегистрирован: 24.10.2016

Это элитный т.к. ради этих 4х строчек все и задумано :)

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

ELITE пишет:

делей надо искоренить!! когда же вы начнете использовать хотябы миллис вместо делеев!!

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

и тут меня пугает слово "хотябы", что нас ждет в дальнейшем страшно подумать.

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

А что, Вы, хотели? Не кажный удостоился отдельного пункта (3), в "Песочнице".

Гриша
Offline
Зарегистрирован: 27.04.2014

а если рассмотреть хардовый вариант решения вопроса?

использовать несколько фотоэлементов + полевик  и все это  на ногу дога. 

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

тода весь код можно в луп закинуть, а просыпаться когда дог тяфкнет. 

:))) ну мы же не ищем простых решений, а этот вариант жизнеспособный и интересный. ИМХО 

NeXan
Offline
Зарегистрирован: 03.06.2019

Ух как тема разрослась)) Мне подсказали на другом форуме, что для добавления гистерезиса нужно всего лишь добавить простую строку (как ж я не смог догадаться!!), теперь все нормально работает:

if( raw < 450 ) {
 //действие
}

if( raw >= 550 )  {
 //действие
}

Не хочу создавать новую тему. Поэтому хотелось бы поинтересоваться здесь. Как написать код, чтобы включалась блокировка времени повторного срабатывания пшика без delay? Например, зачем пшикать, если свет в туалете часто включается, почему бы не сделать блокировку минут на 10? Спасибо)

Morroc
Offline
Зарегистрирован: 24.10.2016

Писали вроде уже - через millis(). Запоминаете millis() в момент "пшика" в переменную t и ждете, когда millis() - 600000 > t, это условие можно добавить к raw >= 550,