Прошу помочь с millis

jonsvl
Offline
Зарегистрирован: 27.05.2018
Здравствуйте господа.
 
Прошу вашей помощи. для управления сервой сделал такую програмку. при подключении питания серва выставляется в определённое положение(заданое в программе), при нажатии кнопки и её удержании серва через 2 сек. поворачивается на определённый угол и стоит там пока нажата кнопка, после отпускании кнопки серва сразу возвращается назад. С движением сервы проблем нет, а вот с паузой непонятки, бывает что сразу по нажатию срабатывает, бывает больше трёх сек пауза, с возвратом тоже ерунда какая-то, должна сразу возвращаться, а она то нормально вернётся то с задержкой( от одной до трёх сек.). Подскажите пожалуйста, где ошибка, нифига не могу понять.

#include <Servo.h> // подключаем библиотеку для работы с сервоприводом
    Servo servo; // объявляем переменную servo типа "servo"
    int button_pin = 4;     // пин кнопки
    int buttonState;   // переменная для хранения состояния кнопки
    unsigned long timing;

void setup() { 
   pinMode(button_pin, INPUT); // Инициализируем цифровой вход.
   servo.attach(5); // привязываем сервопривод к аналоговому выходу 5
   servo.write(170); //ставим вал под 0
}
void loop() {
   if (millis() - timing > 2000){ // Вместо 2000 ставим нужное значение паузы 
     timing = millis();
     buttonState = digitalRead(button_pin);// считываем значения с входа кнопки
   }
   if (buttonState == HIGH) { 
     servo.write(90); //ставим вал на 125
   }
   else {
     servo.write(170); //ставим вал на 0
   }
}

 

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

так у вас в коде написано совсем не то, что вы хотите. Сначала четко пропишите алгоритм. Сейчас у вас проблемы с логикой. Посмотрите внимательно на loop - в первом условии вы зачем-то сначала ждете 2 сек, а только потом читаете пин кнопки. какой в этом смысл? А во втором включаете серву сразу. как-только переменная пина кнопки стала ХАЙ. И где задержка?

 

Нужно сделать примерно так - в цикле читаете кнопку, если она ХАЙ - только с этого момента начинаете отсчитывать 2 сек. При этом продолжаете проверять кнопку. Если к концу 2 сек кнопка все еще нажата - поворачиваете серву...

5N62V
Offline
Зарегистрирован: 25.02.2016

У Вас в коде отсутствует "через 2 секунды". Вы раз в две секунды опрашиваете кнопку, поэтому логично, что поворот  вала начинается сразу  после того, как состояние кнопки определилось как HIGH.   Я бы на Вашем месте все время бы опрашивал кнопку, в каждом проходе лупа. И тогда уже от ее состояния либо инициировал задержку с последующим поворотом вала, либо возвращал вал в исходное.

jonsvl
Offline
Зарегистрирован: 27.05.2018
#include <Servo.h> // подключаем библиотеку для работы с сервоприводом
    Servo servo; // объявляем переменную servo типа "servo"
    int button_pin = 4;     // пин кнопки
    int buttonState;   // переменная для хранения состояния кнопки
    unsigned long timing;

void setup() { 
   pinMode(button_pin, INPUT); // Инициализируем цифровой вход.
   servo.attach(5); // привязываем сервопривод к аналоговому выходу 5
   servo.write(170); //ставим вал под 0
}
void loop() {
   
     buttonState = digitalRead(button_pin);// считываем значения с входа кнопки
   
   if (buttonState == HIGH) { 
     if (millis() - timing > 2000){ // Вместо 2000 ставим нужное значение паузы 
     timing = millis();
      
     servo.write(90); //ставим вал на 90
     }
   }
   else {
     servo.write(170); //ставим вал на 0
   }
}

вот сначала сделал опрос кнопки, если она хай то ждём 2 сек. поворачиваем серву. (хотя я так и не понял как этот millis работает, поэтому действую больше наугад:)), в таком варианте она поворачивается без пауз.

 

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

jonsvl пишет:
я так и не понял как этот millis работает, поэтому действую больше наугад:))

Пока не поймете миллис (что там понимать-то?) - толку не будет.

В данном коде ваша ошибка в том, что вы в начале периода в 2 сек не запоминаете время, поэтому задержка не срабатывает.

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

 

5N62V
Offline
Зарегистрирован: 25.02.2016

а именно строки 17 и 18 у Вас написаны не правильно. Вам СНАЧАЛА надо запомнить текущее милис, а потом ничего не делать, или делать, как реализуете, в течении какого-то периода.

jonsvl
Offline
Зарегистрирован: 27.05.2018

ок. спасибо, буду пробовать.

jonsvl
Offline
Зарегистрирован: 27.05.2018

я понимаю что в этих строчках ошибка, но чтото не могу разобраться. буду по новой изучать:)

5N62V
Offline
Зарегистрирован: 25.02.2016

jonsvl пишет:

я понимаю что в этих строчках ошибка, но чтото не могу разобраться. буду по новой изучать:)

До что ж там изучать-то! Смотрите: сначала присваиваете переменной значание текущего милис:

timing = millis();

Вы запомнили то значение милис, которое было на момент присваивания. А милис между тем дальше себе побежал, вперед. И Вам надо подождать, пока он убежит от запомненного значения на 2 сек. То есть Вам , например, надо ничего не делать, пока милис не убежит вперед на значение 2000. То есть словами это звучит :

до тех пор, пока разница миллис минус запомненное значение будет менее 2000 - ничего не делать. Вам остается эту фразу  просто реализовать в коде.

freeman86
Offline
Зарегистрирован: 07.09.2016
Чтобы не плодить темы, задам вопрос тут. У меня тоже есть некоторые сложности с таймерами на millis(). Вот кусок кода, который ведет себя странным образом. По логике(моей), он должен работать так: 
СНачала две секунды работает событие TONE2500_STATE, затем запускается событие TONE1000_STATE, а TONE2500_STATE выключатеся. Затем спустя еще две секунды запускается TONE500_STATE, а TONE1000_STATE выключается.
И все повторяется пока gotByte = true. На практике сначала запускается TONE2500_STATE. Заметьте, не ждет две секунды, а работает две секунды! Затем запускается и работает TONE1000_STATE две секунды. 
Затем опять почему-то на две секунды запускается TONE2500_STATE, и только потом TONE500_STATE работат две секунды. Я тоже непонимаю почему присваивание  time_tone2500 = millis(); происходит уже после условия, а не до.
Это никак не вписывается в мою логику, но только так это работает. Если вынести присваивания ДО  if(millis() - time_tone2500 > 2000), код вообще не работает.
 
void Select_State(){
  if(!gotByte){
    state = WAITING_STATE;
  }
  if(gotByte){
    if(millis() - time_tone2500 > 2000){
        time_tone2500 = millis();
        state = TONE2500_STATE;
      if(millis() - time_tone1000 > 4000){
          time_tone1000 = millis();
          state = TONE1000_STATE;
        if(millis() - time_tone500 > 6000){
            time_tone500 = millis();
            state = TONE500_STATE;
        }
      }
    }
  }
}
Код ниже работает как задумано, я его привожу как пример, что присваивать нужно после условия. Если вынести присваивания ДО  if(millis() - last_time_tone2500 > 250), зуммер молчит. Может кто-нибудь это объяснить? 
 
Зуммер пищит 250мс, затем молчит 250мс, и т.д....
 
 
void Tone_2500() {
         if(millis() - last_time_tone2500 > 250){
         last_time_tone2500 = millis();
         digitalWrite(led, HIGH);
          tone(5, 2500);
            if(millis() - last_time_tone2500_1 > 500){
            last_time_tone2500_1 = millis();
           digitalWrite(led, LOW);
              noTone(5);
              }
             }
            }

 

 

 

 

 

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

freeman86,Что бы не жевать одно и тоже, то вы не организовали флаг и дальше if(флаг==событию &&<время истекло>)#23

/**/
unsigned long mill;
//--------------------------------------
enum {TONE2500_STATE, TONE1000_STATE, TONE500_STATE};
byte state;
unsigned long past;
//----main----------------------------------
void setup() {
  state = TONE2500_STATE;
  past = mill;
  /*запуск события TONE2500_STATE */
}

void loop() {
  mill = millis();
  if (state == TONE2500_STATE && mill - past >= 2000) {
    state = TONE1000_STATE;
    past = mill;
    /*остановка события TONE2500_STATE */
    /*запуск события TONE1000_STATE */
  }
  if (state == TONE1000_STATE && mill - past >= 2000) {
    state = TONE500_STATE;
    past = mill;
    /*остановка события TONE1000_STATE */
    /*запуск события TONE500_STATE */
  }
  if (state == TONE500_STATE && mill - past >= 2000) {
    /*остановка события TONE500_STATE */

  }
}
/**/

 

freeman86
Offline
Зарегистрирован: 07.09.2016

Получается еще нужно где-то написать функцию, которая будет входить в состояние TONE2500_STATE? Иначе условие не выполнится..

mill = millis();
16
  if (state == TONE2500_STATE && mill - past >= 2000) {
17
    state = TONE1000_STATE;
18
    past = mill;

 

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

Разумеется . я в setup это сделал.

freeman86
Offline
Зарегистрирован: 07.09.2016

Просто у меня это не начальное состояние, и программа вероятно будет разростаться, поэтому я каждое состояние писал в виде функции, и swith-case эти функции будет крутить, а функция типа select_state подставлять в swith-case нужные номера состояний :) 

Вообще, не очень понимаю с точки зрения логики, почему обязательно должно быть уже какое-то состояние, чтобы таймер работатл правильно ) точнее, не так. swith-case может иметь 30-50-100 case`s. И как быть, если надо из любого кейса переходить в любой другой...? 

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

я эту тему уже здесь на форуме жевал 

/**/
unsigned long mill;
//--------------------------------------
enum {sOFF, TONE2500_STATE, TONE1000_STATE, TONE500_STATE};
unsigned long past;
byte state;
void stand(byte s) {
  state = s;
  past = mill;
  switch (state) {
    case sOFF:
      /*все выключить*/
      break;
    case TONE2500_STATE:
      /*запуск события TONE2500_STATE */
      break;
    case TONE1000_STATE:
      /*запуск события TONE2500_STATE */
      break;
    case TONE500_STATE:
      /*запуск события TONE2500_STATE */
      break;
  }
}
//----main----------------------------------
void setup() {
  stand(TONE2500_STATE);
}

void loop() {
  mill = millis();
  if (state == TONE2500_STATE && mill - past >= 2000) stand(TONE1000_STATE);
  if (state == TONE1000_STATE && mill - past >= 2000) stand(TONE500_STATE);
  if (state == TONE500_STATE  && mill - past >= 2000) stand(sOFF);
}
/**/

 

freeman86
Offline
Зарегистрирован: 07.09.2016

А где почитать ваши объяснения? ) Я хочу сам понять :)

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

попробуй от сюда #254

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

freeman86 пишет:

swith-case может иметь 30-50-100 case`s. И как быть, если надо из любого кейса переходить в любой другой...? 

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

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

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

b707 пишет:

freeman86 пишет:

swith-case может иметь 30-50-100 case`s. И как быть, если надо из любого кейса переходить в любой другой...? 

никак. Не бывает таких систем, которым надо переходить "из любого кейса в любой".

Вообще-то именно "из любого в любое".

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

Цитата:

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

Естественно, ограничено. Если у нас N состояний, то в матрице переходов N*N клеток (включая диагональ). 

2ТС: Другое дело, что "обязательно должно быть уже какое-то состояние" - это как бы по определению. Коль скоро мы вводим конечный автомат, то он обязательно находится в каком-то состоянии. Это как переключатель - он просто физически не может не находиться в одном из положений. Разве что его разломать.

jonsvl
Offline
Зарегистрирован: 27.05.2018
#include <Servo.h> // подключаем библиотеку для работы с сервоприводом
    Servo servo; // объявляем переменную servo типа "servo"
    int button_pin = 4;     // пин кнопки
    int buttonState;   // переменная для хранения состояния кнопки
    unsigned long timing;

void setup() { 
   pinMode(button_pin, INPUT); // Инициализируем цифровой вход.
   servo.attach(5); // привязываем сервопривод к аналоговому выходу 5
   servo.write(170); //ставим вал под 0
}
void loop() {
    buttonState = digitalRead(button_pin);// считываем значения с входа кнопки
   if (buttonState == HIGH) { 
   if (millis() - timing > 2000){ // Вместо 2000 ставим нужное значение паузы 
    servo.write(90); //ставим вал на 90
   }
    }
   else {
    servo.write(170); //ставим вал на 0
    timing = millis();
   }
}

вот так заработало, только всё равно непонятно почему работает когда эта строка "timing = millis();" именно в этом месте, почему если её поставить перед "else" то не работает?

freeman86
Offline
Зарегистрирован: 07.09.2016

В таком варианте состояния бегают по кругу, пока А =true, но засада с таймерами. 

If(A) {
    If(millis() - time > 2000) {
        State = one;
        time =millis();
          If(millis() - time > 2000) {
             State = two;
             time =millis();
            If(millis() - time > 2000) {
               State = three;
               time =millis();
                }
            }
      }
}

В таком варианте таймеры работают как надо, но все застывает на последнем состоянии. Как сделать так, чтобы они бегали по кругу, пока А = true?

 

if(A) { 
       mill = millis();
       if(state = one && mill - past > 2000) {
            state = two;
            past = mill;
                  }
            if(state =two && mill - past > 2000) {
                  state = tree;
                   }
     }
                 

Когда я попытался вместо if(A) сделать while(A), прога перестала работать. Почему так? 

 

Разобрался только что. Чтобы состояния бегали по кругу, надо в последнем состоянии сделать переход на начальное. Но не будет ли конфликта в при определенном стечении обстоятельств? Ведь если например if(!A) {state = one) и при условии if(A) в конец всех состояний будет переход к состоянию one, то непонятно в какой момент такая программа может рухнуть? Или тут все нормально?

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

jonsvl пишет:
вот так заработало, только всё равно непонятно почему работает когда эта строка "timing = millis();" именно в этом месте, почему если её поставить перед "else" то не работает?

все равно неверно. У вас таймер запускается не в момент нажатия кнопки, а черти когда.

Неужели так сложно разобраться один раз, как работает миллис???

Всего два действия

1. при нажатии кнопки запустили тацмер

2. Если таймер истек и кнопка нажата - поворачиваем серву

И где у вас первый шаг? - как не было, так и нет.

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

freeman86, Что здесь сложного.

  If(A) { /*если А true*/
    If(State == three && millis() - time > 2000) {//<-
      State = one;
      time = millis();
    }// <-
    If(State == one && millis() - time > 2000) {// <-
      State = two;
      time = millis();
    }// <-
    If(State == two && millis() - time > 2000) {// <-
      State = three;
      time = millis();
    }// <-
  }

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Пух, по учебнику (да и по здравому смыслу), состояния перебираюца через switch 

freeman86
Offline
Зарегистрирован: 07.09.2016

Они и перебираются с помощью switch. Это просто кусок кода, и эта функция отправляет в switch номера состояний. 

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

freeman86 пишет:

Они и перебираются с помощью switch. Это просто кусок кода, и эта функция отправляет в switch номера состояний. 

ну это очень неоптимальная функция. Думаю, квон так написал для примера и чтоб новичков не баловать :)

jonsvl
Offline
Зарегистрирован: 27.05.2018

b707 пишет:

jonsvl пишет:
вот так заработало, только всё равно непонятно почему работает когда эта строка "timing = millis();" именно в этом месте, почему если её поставить перед "else" то не работает?

все равно неверно. У вас таймер запускается не в момент нажатия кнопки, а черти когда.

Неужели так сложно разобраться один раз, как работает миллис???

Всего два действия

1. при нажатии кнопки запустили тацмер

2. Если таймер истек и кнопка нажата - поворачиваем серву

И где у вас первый шаг? - как не было, так и нет.

я не знаю как запустить таймер

#include <Servo.h> // подключаем библиотеку для работы с сервоприводом
    Servo servo; // объявляем переменную servo типа "servo"
    int button_pin = 4;     // пин кнопки
    int buttonState;   // переменная для хранения состояния кнопки
    unsigned long timing;
    
void setup() { 
   pinMode(button_pin, INPUT); // Инициализируем цифровой вход.
   servo.attach(5); // привязываем сервопривод к аналоговому выходу 5
   servo.write(170); //ставим вал под 0
 
}
void loop() {
   
     buttonState = digitalRead(button_pin);// считываем значения с входа кнопки
   if (millis() - timing > 2000) 
   if (buttonState == HIGH) { 
     servo.write(90); //ставим вал на 90
     
   }
   else {
     servo.write(170); //ставим вал на 0
     timing = millis();
   }  
}

сделал так, не знаю правильно или нет, но вроде работает.

P.S. не, так неправильно работает.

мда, походу программирование это не моё.

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

jonsvl пишет:

мда, походу программирование это не моё.

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

================

Тебе написали ДВА пункта. Можно ли увидеть, что ты верно прочитал написанное и перевел на язык С?

1. при нажатии кнопки запустили тацмер

if (КНОПКА_НАЖАТА) timing = millis();

2. Если таймер истек и кнопка нажата - поворачиваем серву

if ( (millis() - timing > ВРЕМЯ_ОЖИДАНИЯ) && КНОПКА_НАЖАТА) ПОВЕРНУТЬ_СЕРВУ();

----------------

Не вникал в проблему, когда тебе нужно обратно серву вернуть, но вот прямо так и пиши, как думаешь.

Самоуничижения для вызова жалости - это низко. Нужно себя уважать, кому же еще можно доверить столь ответственное дело???

jonsvl
Offline
Зарегистрирован: 27.05.2018

wdrakula пишет:

jonsvl пишет:

мда, походу программирование это не моё.

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

================

Тебе написали ДВА пункта. Можно ли увидеть, что ты верно прочитал написанное и перевел на язык С?

1. при нажатии кнопки запустили тацмер

if (КНОПКА_НАЖАТА) timing = millis();

2. Если таймер истек и кнопка нажата - поворачиваем серву

if ( (millis() - timing > ВРЕМЯ_ОЖИДАНИЯ) && КНОПКА_НАЖАТА) ПОВЕРНУТЬ_СЕРВУ();

----------------

Не вникал в проблему, когда тебе нужно обратно серву вернуть, но вот прямо так и пиши, как думаешь.

Самоуничижения для вызова жалости - это низко. Нужно себя уважать, кому же еще можно доверить столь ответственное дело???

да я уже весь гугл перечитал за неделю ( по вечерам) везде один и тотже пример описывают, причём слово в слово, но подробностей ни где нет. Я не знал что таймер запускается именно так: timing = millis(); , в примерах эта строка всегда идёт в конце.

if ( (millis() - timing > ВРЕМЯ_ОЖИДАНИЯ) && КНОПКА_НАЖАТА) ПОВЕРНУТЬ_СЕРВУ(); 

так я тоже пробовал, только немного иначе

if ((кнопка нажата) && (millis() - timing>2000)) {

повернуть серву

}

как понимал так и делал (я не программист, пару недель назад решил попробовать).

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

ну всётаки благодаря вам и всем кто подсказывал я теперь примерно понимаю как она работает.

Спасибо всем.  

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

jonsvl пишет:

да я уже весь гугл перечитал за неделю ( по вечерам) везде один и тотже пример описывают, причём слово в слово, но подробностей ни где нет. Я не знал что таймер запускается именно так: timing = millis(); , в примерах эта строка всегда идёт в конце.

да ладно, почему установка времени в конце?

Неужели ничего подобного не попадалось?

uint8_t flag =0;
uint32_t  timing;

void loop() {
if ((кнопка нажата) && ! flag) {
flag =1;
timing = millis();        // начинаем отсчет времени
}

if (!(кнопка нажата) ) flag =0;

if (flag && (millis() - timing > 2000 ) ) {
serva;
flag = 0;
} 
}

 

jonsvl
Offline
Зарегистрирован: 27.05.2018

b707 пишет:

jonsvl пишет:

да я уже весь гугл перечитал за неделю ( по вечерам) везде один и тотже пример описывают, причём слово в слово, но подробностей ни где нет. Я не знал что таймер запускается именно так: timing = millis(); , в примерах эта строка всегда идёт в конце.

да ладно, почему установка времени в конце?

Неужели ничего подобного не попадалось?

uint8_t flag =0;
uint32_t  timing;

void loop() {
if ((кнопка нажата) && ! flag) {
flag =1;
timing = millis();        // начинаем отсчет времени
}

if (!(кнопка нажата) ) flag =0;

if (flag && (millis() - timing > 2000 ) ) {
serva;
flag = 0;
} 
}

 

нет, не видел, может проглядел.

везде примерно это:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned long timing; // Переменная для хранения точки отсчета
void setup() {
 Serial.begin(9600);
}
 
void loop() {
/*
 В этом месте начинается выполнение аналога delay()
 Вычисляем разницу между текущим моментом и ранее сохраненной точкой отсчета.
 Если разница больше нужного значения, то выполняем код.
 Если нет - ничего не делаем
*/
 if (millis() - timing > 10000){ // Вместо 10000 подставьте нужное вам значение паузы
  timing = millis();
  Serial.println ("10 seconds");
 }
}

 

jonsvl
Offline
Зарегистрирован: 27.05.2018

всё равно не работает

#include <Servo.h> // подключаем библиотеку для работы с сервоприводом
    Servo servo; // объявляем переменную servo типа "servo"
    int button_pin = 4;     // пин кнопки
    int buttonState;   // переменная для хранения состояния кнопки
    unsigned long timing;
    
void setup() { 
   pinMode(button_pin, INPUT); // Инициализируем цифровой вход.
   servo.attach(5); // привязываем сервопривод к аналоговому выходу 5
   servo.write(170); //ставим вал под 0
 
}
void loop() {
   
     buttonState = digitalRead(button_pin);// считываем значения с входа кнопки
    
   if (buttonState == HIGH)timing = millis();
   if ((millis() - timing > 2000) && buttonState == HIGH) servo.write(90); //ставим вал на 90
   
   
   else {
     servo.write(170); //ставим вал на 0
     
   }  
}

 

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

jonsvl пишет:

всё равно не работает

похоже, программирование и правда не ваше :(

Я уже буквальный пример вам дал, как делать - а вы зачем-то половину кода из него выкинули. Думаете, лишнее? - а без него не сработает.

Если не в силах разобраться сами - то хоть повторяйте внимательно.

jonsvl
Offline
Зарегистрирован: 27.05.2018

да вроде всё сделал так как вы написали.

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

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

freeman86
Offline
Зарегистрирован: 07.09.2016

qwone пишет:

 

/**/
unsigned long mill;
//--------------------------------------
enum {sOFF, TONE2500_STATE, TONE1000_STATE, TONE500_STATE};
unsigned long past;
byte state;
void stand(byte s) {
  state = s;
  past = mill;
  switch (state) {
    case sOFF:
      /*все выключить*/
      break;
    case TONE2500_STATE:
      /*запуск события TONE2500_STATE */
      break;
    case TONE1000_STATE:
      /*запуск события TONE2500_STATE */
      break;
    case TONE500_STATE:
      /*запуск события TONE2500_STATE */
      break;
  }
}
//----main----------------------------------
void setup() {
  stand(TONE2500_STATE);
}

void loop() {
  mill = millis();
  if (state == TONE2500_STATE && mill - past >= 2000) stand(TONE1000_STATE);
  if (state == TONE1000_STATE && mill - past >= 2000) stand(TONE500_STATE);
  if (state == TONE500_STATE  && mill - past >= 2000) stand(sOFF);
}
/**/

 

 

qwone, сегодня переделал подглядывая в ваш пример, который выше. Спасибо, все работает. Единственно чего я не понимаю, для чего нужно past = mill в функции void stand?

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

freeman86, а подумать!! В ардуино в большинстве случаев перехода по состоянию надо еще и фиксировать время перехода. Вот я это и совместил.

freeman86
Offline
Зарегистрирован: 07.09.2016

мне не очень понятен порядок работы. mill = millis() написано ниже. Компилятор это сам расставляет, или скетч как пример, но он не рабочий? 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
freeman86, а подумать!! В ардуино в большинстве случаев перехода по состоянию надо еще и фиксировать время перехода. Вот я это и совместил.

А теперь сравните

/**/
unsigned long mill;
//--------------------------------------
enum {sOFF, TONE2500_STATE, TONE1000_STATE, TONE500_STATE};
unsigned long past;
byte state;
void stand(byte s) {
  state = s;
  //   уберем отсюда past = mill;
  switch (state) {
    case sOFF:
      /*все выключить*/
      break;
    case TONE2500_STATE:
      /*запуск события TONE2500_STATE */
      break;
    case TONE1000_STATE:
      /*запуск события TONE2500_STATE */
      break;
    case TONE500_STATE:
      /*запуск события TONE2500_STATE */
      break;
  }
}
//----main----------------------------------
void setup() {
  stand(TONE2500_STATE);
}

void loop() {
  mill = millis();
  if (state == TONE2500_STATE && mill - past >= 2000) {
    stand(TONE1000_STATE);
    past = mill;//<-- и придется воткнуть сюда
  }
  if (state == TONE1000_STATE && mill - past >= 2000) {
    stand(TONE500_STATE);
    past = mill;//<-- и придется воткнуть сюда
  }
  if (state == TONE500_STATE  && mill - past >= 2000) {
    stand(sOFF);
    past = mill;//<-- и придется воткнуть сюда
  }
}
/**/
Вот мне предыдущий вариант больше нравится. Ну а то что иногда при переходе не надо фиксировать время. За то там где надо зафиксировать не будет пропущено.

 

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

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

/**/
unsigned long mill;
//--------------------------------------
enum {sOFF, TONE2500_STATE, TONE1000_STATE, TONE500_STATE};
unsigned long past;
byte state;
void stand(byte s) {
  state = s;
  past = mill;
  switch (state) {
    case sOFF:
      /*все выключить*/
      Serial.println("sOFF");
      break;
    case TONE2500_STATE:
      /*запуск события TONE2500_STATE */
      Serial.println("TONE2500_STATE");
      break;
    case TONE1000_STATE:
      /*запуск события TONE1000_STATE */
      Serial.println("TONE1000_STATE");
      break;
    case TONE500_STATE:
      /*запуск события TONE500_STATE */
      Serial.println("TONE500_STATE");
      break;
  }
}
//----main----------------------------------
void setup() {
  Serial.begin(9600);
  stand(TONE2500_STATE);
}

void loop() {
  mill = millis();
  if (state == TONE2500_STATE && mill - past >= 2000) stand(TONE1000_STATE);
  if (state == TONE1000_STATE && mill - past >= 2000) stand(TONE500_STATE);
  if (state == TONE500_STATE  && mill - past >= 2000) stand(sOFF);
}
/**/

Может вы до конца не понимаете как программировать с помощью цифрового автомата.

freeman86
Offline
Зарегистрирован: 07.09.2016

разумеется я до конца не понимаю, иначе бы я не задавал глупых вопросов в этой теме :) 

 

void Tone_2500() {

         if(millis() - last_time_tone2500 > 250){

         last_time_tone2500 = millis();

         digitalWrite(led, HIGH);

         tone(5, 2500);

            if(millis() - last_time_tone2500_1 > 500){

            last_time_tone2500_1 = millis();

           digitalWrite(led, LOW);

              noTone(5);

              }

             }

            }

Можете объяснить, как эта функция может работать, если  присваивание времени переменной происходит после условия? она работает: 250 мс пищит, 250мс молчит. Ну то есть работает как задумано...я не понимаю. 

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

Вот как вы варите яйца. Ждете когда закипит вода. Как закипела вода вы кладете яйца в кипяток и засекаете время (фиксируете время ). Потом ждете немного и смотрите на время теперешнее . Разница времени и есть время когда яйца находятся в кипятке. Работаете в таком режиме некоторое время когда разница не станет 10 минут. Все яйца надо извлекать. Так и здесь . millis() это часы. переменная past это блокнот куда вы пишете время укладки яиц.  Флаг это яйца на столе или в кипятке. 

Итак простой алгоритм Когда кладете яйца в кипяток вы взводите флаг-"яйца в кипятке" и засекаете время past=millis().

А теперь регулярно  если "яйца в кипятке" И разница больше 10 минут, то флаг "яйца на столе" и извлечь яйца. Можно закладывать новую партию яиц в кипяток.

 

ПС: Функция бредовая. 

freeman86
Offline
Зарегистрирован: 07.09.2016

 

сам алгоритм я понимаю. Он очень простой. Но я не понимаю как это выразить в С++. Вы говорите что функция бредовая. Я согласен. Но она рабоатет, причем работает как задумано. Либо тут ошибка накладывается на ошибку, либо непонятно почему она работает? 

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

freeman86 пишет:

сам алгоритм я понимаю. Он очень простой. Но я не понимаю как это выразить в С++.

Можно воспользоваться простым приемом:

1. Записать алгоритм по-русски.

2. Перевести с русского на Си++.

freeman86
Offline
Зарегистрирован: 07.09.2016

человеческий язык сильно отличается от языка программирования )

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

freeman86 пишет:

человеческий язык сильно отличается от языка программирования )

Особенно если таким образом говорить. https://www.youtube.com/watch?v=MmY4nQpk4sE

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

freeman86 пишет:

человеческий язык сильно отличается от языка программирования )

В данном случае это несущественно.

Я так понимаю, Вы не можете написать алгоритм по-русски?

freeman86
Offline
Зарегистрирован: 07.09.2016

Могу конечно :)

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

Ну так напишите. За чем дело стало?

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

freeman86 пишет:

человеческий язык сильно отличается от языка программирования )

Уху, а русский от аглицкого, а уж китайский.(((( Но ведь обсчаются как то?
На мой взгляд, разница между СИ и русским на порядки ниже, чем между русским и аглицким. ИМХО.

jonsvl
Offline
Зарегистрирован: 27.05.2018
#include <Servo.h> // подключаем библиотеку для работы с сервоприводом
    Servo servo; // объявляем переменную servo типа "servo"
    int button_pin = 4;     // пин кнопки
    int buttonState;   // переменная для хранения состояния кнопки
    unsigned long timing;
void setup() { 
   pinMode(button_pin, INPUT); // Инициализируем цифровой вход.
   servo.attach(5); // привязываем сервопривод к аналоговому выходу 5
   servo.write(170); //ставим вал под 0
  }
void loop() {
   buttonState = digitalRead(button_pin);// считываем значения с входа кнопки
  if (buttonState == HIGH) {
  if ((buttonState == HIGH)&&(millis() - timing > 2000)) {
    servo.write(90);
    delay(100);
   }
  }
  else {
    servo.write(170);
    delay(100);
    timing = millis();
 }
}    

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

всем спасибо.