Кнопка и реле: как сделать паузу c millis и отключение?

Tanatos
Offline
Зарегистрирован: 17.12.2018

Здравствуйте! Вопрос от чайника )

Скетч простой - кнопка включает и выключает реле.

int flag=0;

void setup()    
{ 
pinMode(34, OUTPUT); // реле
digitalWrite(34, LOW);
pinMode(19, OUTPUT); // кнопка 
digitalWrite(19, LOW);         
} 

void loop() 
{         
     if(digitalRead(19)==HIGH&&flag==0)
     {
     digitalWrite(34,!digitalRead(34));
     flag=1;
     }
      
     if(digitalRead(19)==LOW&&flag==1)
     { 
     flag=0;
     } 
} 

Как изменить код, чтобы:

1) реле после включения само отключалось через 60 сек (лучше с millis)

2) если во время работы реле опять нажать кнопку, реле должно отключатся (досрочное отключение)

Спасибо!

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А сами то Вы пытались? Или Вам в лом?

Tanatos
Offline
Зарегистрирован: 17.12.2018

Пытался. А Вам не в лом комменты ни о чём писать?

Муж_Долговой
Муж_Долговой аватар
Offline
Зарегистрирован: 07.10.2018

Ты сжёг Ардуинку. Сам. Специально. Командой:

pinMode(19, OUTPUT); // кнопка

... кнопка не может быть выходом!!!!!!!!!!!!!!!!!

Tanatos
Offline
Зарегистрирован: 17.12.2018

Спасибо. Исправил. Но как ни странно работала (включала) и не сгорела)

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Вот водочки немного принял и добрый стал


int flag=0;
unsigned long time;
void setup()   
{
time=millis();
pinMode(34, OUTPUT); // реле
digitalWrite(34, LOW);
pinMode(19, INPUT); // кнопка
digitalWrite(19, LOW);        
}
void loop()
{   
     if(digitalRead(19)==HIGH&&flag==0)
   {
 
     flag=1;
    time=millis();
    }
 
     if(digitalRead(19)==LOW&&flag==1&&((millis()-time)>60000))
     {
     flag=0;

     }
   digitalWrite(34,flag);
}

НЕ - похоже многовато - исправил программу

Tanatos
Offline
Зарегистрирован: 17.12.2018

Спасибо большое! Таймер работает!

Может ещё рюмочку и по досрочному отключению подскажете? )

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Tanatos пишет:

Спасибо большое! Таймер работает!

Может ещё рюмочку и по досрочному отключению подскажете? )

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Tanatos пишет:

Пытался. 

Пытался - показывайте. Таковы правила форума. Помогают тому, кто сам делает.

Tanatos
Offline
Зарегистрирован: 17.12.2018

Добавил условие (может не туда?)

Нажатие на кнопку стало непредсказуемо включать-выключать реле - когда-то отключает досрочно, когда-то нет, когда-то даже просто включить не может... (

int flag=0;
unsigned long time;
void setup()   
{
time=millis();
pinMode(34, OUTPUT); // реле
digitalWrite(34, 0);
pinMode(19, INPUT); // кнопка
digitalWrite(19, 0);        
}
void loop()
{   
     if(digitalRead(19)==1&&flag==0)
        {
        flag=1;
        time=millis();
        }
 
     if(digitalRead(19)==0&&flag==1&&((millis()-time)>60000)) //таймер 60 сек.
        {
        flag=0;
        }
        
      if(digitalRead(19)==1&&flag==1)
        {
        flag=0;
        }  
          
        digitalWrite(34,flag);
}

Условие простое: если в течение 60 секунд, пока работает реле, нажать на кнопку, то реле должно отключиться

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
/**/
const byte relayPin = 34; // реле
const byte btnPin = 19; //кнопка
bool relay;
unsigned long past;
void stand(bool r) {
  relay = r;
  past = millis();
  switch (relay) {
    case false:
      digitalWrite(relayPin, LOW);
      break;
    case true:
      digitalWrite(relayPin, HIGH);
      break;
  }
}
bool btn = false;

void setup() {
  pinMode(relayPin, OUTPUT); // реле
  stand(false);
  pinMode(btnPin, INPUT_PULLUP); // кнопка
}

void loop() {
  bool tmp = digitalRead(btnPin);
  switch (btn) {
    case false:
      if (!tmp) {
        delay(50);
        btn = true;
        if (relay == false)stand(true);
        else stand(false); //<-- досрочное отключение
      }
      break;
    case true:
      if (tmp) {
        delay(50);
        btn = false;
      }
      break;
  }
  switch (relay) {
    case false:
      break;
    case true:
      if (millis() - past >= 2000) stand(false);/*откл через 2 сек*/
      break;
  }
}
/**/

 

Tanatos
Offline
Зарегистрирован: 17.12.2018

Спасибо! Этот труд мне ещё надо осмыслить.. )

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

После этого всё как надо реагирует на кнопку.

Как отключить это самоуправство в начале?

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

Ни как.Запускается встроеный загрузчик. Скорее инвертивовать работу реле.

ПС: Как вариант притянуть резистором 1кОм к земле выход на включение модуля реле.

Tanatos
Offline
Зарегистрирован: 17.12.2018

Это следующий этап моего развития в ардуино ))

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

Муж_Долговой
Муж_Долговой аватар
Offline
Зарегистрирован: 07.10.2018

"....После этого всё как надо реагирует на кнопку.

Как отключить это самоуправство в начале? ...."

А ты отвлеки Ардуинку чем нибудь.

Отличный вариант первой строчкой после Воид сетап написать delay (6000).

Работе не мешает. Загрузке не мешает. А ногами не шевелит. Пока загрузка.

Tanatos
Offline
Зарегистрирован: 17.12.2018

Ничего не изменилось - после 6 секунд по-прежнему запускется реле..

Delay вроде же не даёт в фоне ничему происходить?

const byte relayPin = 34; // реле
const byte btnPin = 19; //кнопка
bool relay;
unsigned long past;
void stand(bool r) {
  relay = r;
  past = millis();
  switch (relay) {
    case false:
      digitalWrite(relayPin, LOW);
      break;
    case true:
      digitalWrite(relayPin, HIGH);
      break;
  }
}
bool btn = false;

void setup() {
  delay (6000);
  pinMode(relayPin, OUTPUT); // реле
  stand(false);
  pinMode(btnPin, INPUT_PULLUP); // кнопка
}

void loop() {
  bool tmp = digitalRead(btnPin);
  switch (btn) {
    case false:
      if (!tmp) {
        delay(50);
        btn = true;
        if (relay == false)stand(true);
        else stand(false); //<-- досрочное отключение
      }
      break;
    case true:
      if (tmp) {
        delay(50);
        btn = false;
      }
      break;
  }
  switch (relay) {
    case false:
      break;
    case true:
      if (millis() - past >= 60000) stand(false);/*откл через 60 сек*/
      break;
  }
}

 

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

Муж_Долговой пишет:

Ты сжёг Ардуинку. Сам. Специально. Командой:

pinMode(19, OUTPUT); // кнопка

... кнопка не может быть выходом!!!!!!!!!!!!!!!!!

 

Все в этой жизни отночител но. Иногда надо подать сигнал на выход что бы считать кнопку.

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

При сбросе выводы МК переходят в Z состояние или становятся входами. И это происходит до тех пор пока программист не сделает ее выходом и не подаст на нее LOW. Вот это начальное ошибочное состояние Вы наблюдаете. Так что проще  бороться с этой проблемой аппаратно с помощью дополнительного резистора, который подтягивает вывод идущий на модуль реле в ноль.

Bi11i
Offline
Зарегистрирован: 10.03.2019

Tanatos, спасибо тебе!
qwone, спасибище огромное!!!
Я собрался сделать маленькое скромное реле, удлиняющее время работы лампы на три секунды после подачи сигнала. Дело в том, что столь простая задача на деле оказалась очень непростой. Импульс бывает разной длины, а считать надо уже от фронта этого импульса. Сработку по фронту длинного импульса не объясняет ни один пример.
С помощью сторонней библиотеки я задачу вроде решил, но по ТЗ всё будет собрано на AtTiny85. Библиотека с ней не дружит и пришлось искать решение с поддержкой инструкций для Тиньки.
Я сломал голову себе, пытаясь увязать столько условий. Вот тебе и простая задача... Удлинил импульс, называется.
Слизал Ваш код, чутка подправил - миссия выполнена. Почти.
По хорошему надо чтобы выход не сбрасывался в ноль по истечении времени, но при наличии импульса на входе. Но я уже третью неделю бьюсь над этой программкой, задолбался, и мне проще реализовать простейшее "ИЛИ" на паре диодов.
Ещё раз, qwone, спасибо.

з.ы. Надо всё-же добавить пример с обработкой длинного импульса, а то всё кнопки, кнопки...

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

Bi11i пишет:
Надо всё-же добавить пример с обработкой длинного импульса, а то всё кнопки, кнопки...

в чем принципиальная разница между "длинным импульсом" и удержанием кнопки? - ни в чем. Примеров достаточно.

Bi11i
Offline
Зарегистрирован: 10.03.2019

Принципиальная разница в начале интерпретации длинного нажатия. Всё, что мне попадалось было написано на так или иначе реализованном подсчёте времени после появления высокого логического уровня, что приводило к задержке включения таймера на величину этой задержки. Половина найденных примеров выполняет инструкцию по обратному перепаду уровня в "ноль" и по посчитанной длительности до этого перепада выполняет инструкцию. Попытка перевернуть такой пример на реакцию по фронту приводит к бесконечному циклу. По крайней мере у меня, зелёного.
qwone в своём варианте тоже инструкцию по обратному спаду выполняет, разве что у него это всё в отдельные циклы вынесено и можно легко инвертировать без последствий.
Проблема в том, что если импульс длинней выдержки - таймер перезапускался, а должен был посчитать однократно. Со сработкой по спаду таймер вообще не запускался - он ждал спад.
А существует-ли в Ардуино ИДЕ библиотека, имитирующая одновибратор? Если есть - ткните, пожалуйста, носом.

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

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

Ну или мониторите, пока не поймаете спад.

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

Bi11i пишет:
Принципиальная разница в начале интерпретации длинного нажатия. Всё, что мне попадалось было написано на так или иначе реализованном подсчёте времени после появления высокого логического уровня, что приводило к задержке включения таймера на величину этой задержки. Половина найденных примеров выполняет инструкцию по обратному перепаду уровня в "ноль" и по посчитанной длительности до этого перепада выполняет инструкцию. Попытка перевернуть такой пример на реакцию по фронту приводит к бесконечному циклу...
Такая попытка может приводить к чему угодно, но только никак не к правильной работе, т.к. на момент нажатия принципиально невозможно предвидеть, длинное будет нажатие или короткое.

Правильная реакция на длинное нажатие может быть в одной из двух реализаций:

- по отпусканию кнопки, если время нажатия превосходит интервал, определенный как длительное нажатие,

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

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

Bi11i
Offline
Зарегистрирован: 10.03.2019

Я уже пришёл к такому-же выводу.
Но повторюсь. Там ни разу не кнопка.
Как и сказано выше, флаг я заводил и он эрегировал...
Я упёрся в то, что пока стоит флаг наличия высокого уровня на входе - счётчик при каждом цикле начинает отсчёт заново и до конца не досчитывает. Перманентное начало счёта. Мне нужно было считать и при наличии уровня на входе. Если досчитал, а уровень входа высокий - не начинать счёт сначала.
Потом был добавлен ещё флаг наличия счёта в таймере на миллис и таймер стал досчитывать, но стал перезапускаться при высоком уровне. Теперь надо было дать понять таймеру, что "всё, хватит". Я завёл третий флаг и в них запутался. Вот тут моск со мной попрощался вверг меня в сплин.
Я пытался разными способами заставить счётчик считать один раз на один фронт сигнала, но я ещё не постиг такой магии, а в примерах однократного выполнения процедуры по фронту я не нашёл.

Формально, я мог-бы это родить на паре транзисторов и конденсаторе, но решил вкатиться в Ардуино и прифигел от невозможности просто решить такую задачу.

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

Bi11i пишет:
Я пытался разными способами заставить счётчик считать один раз на один фронт сигнала, но я ещё не постиг такой магии, а в примерах однократного выполнения процедуры по фронту я не нашёл. Формально, я мог-бы это родить на паре транзисторов и конденсаторе, но решил вкатиться в Ардуино и прифигел от невозможности просто решить такую задачу.

"просто" - понятие относительное. Оно приходит с опытом. Опытному столяру табуретку собрать  - нефиг делать, а я 2 дня буду корпеть и вряд ли соберу.

В вашей задаче нет ничего сложного. Похоже, что вы просто отслеживали только уровень сигнала - а этого мало. Чтобы отследить именно фронт - нужно поймать такой момент, когда текущее значение сигнала 1, а предыдущее = 0. Вот в этот то момент вы и запускаете свой таймер. Совершенно очевидно, что при таком условии таймер не будет начинать счет сначала ни при каждом проходе, ни при окончании счета - ведь "фронта" там нет. И флаги в простейшем случае вообще не нужны.

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

Bi11i пишет:
Я уже пришёл к такому-же выводу. Но повторюсь. Там ни разу не кнопка. Как и сказано выше, флаг я заводил и он эрегировал... Я упёрся в то, что пока стоит флаг наличия высокого уровня на входе - счётчик при каждом цикле начинает отсчёт заново и до конца не досчитывает. Перманентное начало счёта. Мне нужно было считать и при наличии уровня на входе. Если досчитал, а уровень входа высокий - не начинать счёт сначала.

Перечитал еще раз. Насколько понял, вас интересует только высокий фронт, а длина импульса может быть любой? Распишите, для начала, все возможные состояния и реакцию на них. Будет легче. Лично я не осознаю всю задачу до конца. Например, что делать, если в течении этих трех секунд уровень поменялся с высокого на низкий, и обратно. Через сколько, от истечения трех секунд, начинать цикл по новой от смены уровней и т.д.

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

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

Bi11i
Offline
Зарегистрирован: 10.03.2019

Спс. Читаю...

ТЗ на самом деле несложное.
Это "тройное моргание" поворотником в авто. Ранее успешно справлялась пара реле времени и пара реле контактных. На них была собрана логика. Проблема в размерах. Реле великоваты, а тут ещё стали подгорать и не включать поворотник вообще. В авто реализация этого режима выбирается переключателем, подающим плюс от прерывателя на цепи с лампами. Там при подключении нагрузки появляется пульсирующий ток.
Я через полевые транзисторы собираюсь коммутировать цепи с пульсирующим током, а сигналы о включении брать с переключателя, куда будет подведён плюс на "общий" контакт.
Задача контроллера - увидеть высокий уровень с переключателя, установить на выходном порту также высокий уровень и держать его таковым три секунды вне зависимости от длины входного импульса. Чрезмерная длительность на выходе реализована на простейшем "или" на диодах, один из которых пропустит сигнал на полевик со входа МК, а другой с выхода МК. То есть я поворачиваю - включаю переключатель на фиксатор. Полевик будет открыт, пока есть высокий лог. уровень на входе каскада. Уровень через диод обходит МК, пока включен переключатель. Это страховка на случай поломки МК, т.к. обычный режим моргания останется всегда. Если я включил переключатель кратковременно - высокий лог- уровень придёт на каскад с выхода МК через свой диод. Три секунды моргания поворотником при перестроении.
При включении "правого" входа - выход левого должен встать в ноль и наоборот, чтобы исключить моргание правых и левых поворотников вместе.
Вот и всё.
Два входа, два выхода. Собирать хочу на АтТини85.

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

алгоритм, по-моему, несложный

1. при появлении высокого уровня сразу запускаем таймер - назовем  этот момент "СТАРТ"

2. если в течении 0.5 сек после СТАРТА высокий уровень пропал = это "короткое включение" = оставляем таймер на 3 сек

3. Если через 0.5 сек с момента СТАРТА все еще высокий уровень = "длинное включение" = останавливаем таймер

4. При появлении высокого уровня на противоложном поворотнике без всяких условий останавливаем таймер

vlad072
Offline
Зарегистрирован: 01.08.2017

Если я правильно понял задачу то так попробуйте



#define relay 34 // реле
#define btn   19 //кнопка

uint32_t timer = 0;

void setup() {
  pinMode(relay, OUTPUT); // реле
  pinMode(btn,   INPUT_PULLUP); // кнопка
  digitalWrite(relay, LOW);
}

void loop() {
  if ( !digitalRead(btn) )
    digitalWrite( relay, !digitalRead(relay) ), timer = millis();
  if ( millis() - timer > 60000ul ) digitalWrite(relay, LOW);
}

 

 

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

Bi11i пишет:
А существует-ли в Ардуино ИДЕ библиотека, имитирующая одновибратор? Если есть - ткните, пожалуйста, носом.

const byte btnPin = 2;
const byte ledPin = 13;
bool state;
const unsigned long time2s = 2000;
unsigned long past;
void set(bool s) {
  state = s;
  past = millis();
  switch (s) {
    case false:
      digitalWrite(ledPin, LOW);
      break;
    case true:
      digitalWrite(ledPin, HIGH);
      break;
  }
}
void setup() {
  pinMode(btnPin, INPUT_PULLUP); // кнопка
  pinMode(ledPin, OUTPUT); // реле
  set(false);
}

void loop() {
  // пока кнопка нажата свет горит
  if (!digitalRead(btnPin)) set(true);
  switch (state) {
    case false:
      break;
    case true:
      // истекло 2 секунды после отжатия
      if (millis() - past >= time2s)set(false);
      break;
  }
}

 

Nikolay_XMAO
Offline
Зарегистрирован: 21.10.2022

Всем доброго времени суток, очень нужна помощь, 

Я пытаюсь написать программку для облегчения своей работы, работа не связана с программированием, поэтому в этой сфере я полный НОЛЬ, немного по изучав я наткнулся на программу ардублок, но там отсутствует возможность использовать задержки кроме delay. а мне необходимо отключать или запускать другой процесс а с delay это не получается и приходится постоянно перезагружать ардуинку, попробовав разные варианты с форума и с интернета, у меня ничего не получилось, программа заключается в включении трех реле на 2-3сек через определенный промежуток времени, в программе в место реле описаны светодиоды, так как я пробую на них и так нагляднее, вот пример моего кода.



long x = 0;

#include <Wire.h>
#include "ASOLED.h"


void Start() {
  if (digitalRead(A0) == 0) {
    while (1){
      LD.printString_12x16("Старт 1", 0, 2);
      LD.printString_12x16("Цикл", 0, 6);
      LD.printString_12x16("", 50, 6);
       LD.printNumber((long)x);
      x = x + 1;
      pinMode(10, OUTPUT);
       digitalWrite(10, 1);
      delay(2000);
      pinMode(10, OUTPUT);
       digitalWrite(10, 0);
      delay(6000);
      pinMode(11, OUTPUT);
       digitalWrite(11, 1);
      delay(2000);
      pinMode(11, OUTPUT);
       digitalWrite(11, 0);
      delay(30000);
      pinMode(12, OUTPUT);
       digitalWrite(12, 1);
      delay(2000);
      pinMode(12, OUTPUT);
       digitalWrite(12, 1);
      delay(900000);
      pinMode(13, OUTPUT);
       digitalWrite(13, 1);
      delay(2000);
    }
  }
}

void Start1() {
  if (digitalRead(A1) == 0) {
    while (1){
      LD.printString_12x16("Старт 2", 0, 2);
      LD.printString_12x16("Цикл", 0, 6);
      LD.printString_12x16("", 50, 6);
       LD.printNumber((long)x);
      x = x + 1;
      pinMode(10, OUTPUT);
       digitalWrite(10, 1);
      delay(2000);
      pinMode(10, OUTPUT);
       digitalWrite(10, 0);
      delay(6000);
      pinMode(11, OUTPUT);
       digitalWrite(11, 1);
      delay(2000);
      pinMode(11, OUTPUT);
       digitalWrite(11, 0);
      delay(45000);
      pinMode(12, OUTPUT);
       digitalWrite(12, 1);
      delay(2000);
      pinMode(12, OUTPUT);
       digitalWrite(12, 1);
      delay(900000);
      pinMode(13, OUTPUT);
       digitalWrite(13, 1);
      delay(2000);
    }
  }
}

void Start3() {
  if (digitalRead(A2) == 0) {
    while (1){
      LD.printString_12x16("СТОП", 0, 2);
      LD.printString_12x16("        ", 0, 6);
      pinMode(13, OUTPUT);
       digitalWrite(13, 1);
    }
  }
}


void setup() {
  LD.init();
 LD.clearDisplay();

    LD.printString_12x16("RABOTA", 35, 0);
  pinMode(10, OUTPUT);
   digitalWrite(10, 0);
  pinMode(11, OUTPUT);
   digitalWrite(11, 0);
  pinMode(12, OUTPUT);
   digitalWrite(12, 0);
  pinMode(13, OUTPUT);
   digitalWrite(13, 0);

  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
}

void loop() {
    Start();
    Start1();
    Start3();

}

 

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

все вот это pinMode(11, OUTPUT); не требуется делать каждый раз, только в сетапе. или это ардублок генерит автоматом ?

Nikolay_XMAO пишет:

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

Т.е. вы отказались от ардублок ?

Nikolay_XMAO пишет:

программа заключается в включении трех реле на 2-3сек через определенный промежуток времени, в программе в место реле описаны светодиоды, так как я пробую на них и так нагляднее, вот пример моего кода.

А где в этом вашем ТЗ написано о "запускать другой процесс" ? Тут все укладывается в вариант с delay.

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