таймер для ардуины

medossa
Offline
Зарегистрирован: 10.07.2012

Требуется каждые 15 секунд включать реле и на 240 секунд отключать. Что сделать с millis() чтоб такое реализовать? К примеру, вот такой код:

int relay = 10;

void setup() {
 Serial.begin(9600);
 pinMode(relay, OUTPUT); 
}

void loop() {
int time = 0;
if((millis() - time) == 12000) {
 digitalWrite(relay, HIGH);
 time = millis(); 
}  
}

Он конечно не работает)) Что исправить и чем дополнить?

maksim
Offline
Зарегистрирован: 12.02.2012
#define relay 10

void setup() 
{
  Serial.begin(9600);
  pinMode(relay, OUTPUT); 
}

void loop() 
{ 
  static bool state;
  static unsigned long time;
  if((millis() - time) > (state ? 1000 : 3000)) 
  {
    state = !state;
    digitalWrite(relay, state);
    time = millis();
  }  
}

1 секунду вкл. 3 секунды выкл.

medossa
Offline
Зарегистрирован: 10.07.2012

отлично! всё путём.

nikolaki
nikolaki аватар
Offline
Зарегистрирован: 14.02.2013

Вот спасибки. Просто и красиво . Это почти ТО что я хотел .

Я добавил в ваш код кнопку и получил искомое.

#define relay 6

void setup() 
{
 Serial.begin(9600);
  pinMode(relay, OUTPUT);
  
}

void loop() {
  static bool state;
     
  if(digitalRead(2)==LOW)
   {  
    digitalWrite(relay, LOW); 
     }

  else(digitalRead(2)==HIGH);
    
      { 

  static unsigned long time;
  if((millis() - time) > (state ? 1000 : 3000)) 
  {
    state = !state;
    digitalWrite(relay, state);
    time = millis();
  }
}  

}

Так код работает . Но почему он не работает вот так:

#define relay 6

void setup() 
{
 Serial.begin(9600);
  pinMode(relay, OUTPUT);
  
}

void loop() {
  static bool state;
     
  if(digitalRead(2)==HIGH)
   { 

  static unsigned long time;
  if((millis() - time) > (state ? 100 : 800)) 
  {
    state = !state;
    digitalWrite(relay, state);
    time = millis();
  }
}  

  else(digitalRead(2)==LOW);
    {  
    digitalWrite(relay, LOW); 
     }
}

 

medossa
Offline
Зарегистрирован: 10.07.2012

else не может быть с условием, либо if, либо else if

nikolaki
nikolaki аватар
Offline
Зарегистрирован: 14.02.2013

СПАСИБО!!!Точно!Теперь я знаю почему не работали и некоторые другие скетчи.Пошел исправлять их.

souchkov
Offline
Зарегистрирован: 04.02.2013

сходная проблема... есть mega2560, millis() и вывод на жк индикатор.. когда ардуина насчитывает минуту, проходит примерно 45 секунд реального времени.... кварц врет или по жизни такая точность?

примерный код...

long int intervalTimer = 500;

void loop()  {
 unsigned long currentMillis = millis();

 if(currentMillis - previousMillis > interval)  {
    previousMillis = currentMillis;
    CheckStatus();  //опрос кнопок каждый interval
    }
    
 if(currentMillis - PrevTimeMillis > intervalTimer)  {
    PrevTimeMillis  = currentMillis;
    Timer(); 
    }

 if(TimerUpdate == HIGH) //
    TimeUpdate();  
}

void Timer()  {
  TimerUpdate = HIGH;
  halfsecond ++;
  if(halfsecond == 2){
    second ++;
    if(second == 60)
    {
      minute ++;
      if(minute == 60)
      {
        hour ++;
        if(hour == 24)hour = 0;
        minute = 0;
      }
      second = 0;
    }
    halfsecond = 0;  
  }
}

void TimeUpdate(void)  {
    if(DyspColonState==LOW) {
      DyspColonState=HIGH;
      DyspDecimalControl(DISPLAY_ADDRESS1,0b00000000);
      Timer(); 
   }
      else {
        DyspColonState=LOW;
        DyspDecimalControl(DISPLAY_ADDRESS1,0b00010000);
      }
// TimeDisp[0] = hour / 10;
//  TimeDisp[1] = hour % 10;
//  TimeDisp[2] = minute / 10;
//  TimeDisp[3] = minute % 10;
  TimeDisp[0] = minute / 10;
  TimeDisp[1] = minute % 10;
  TimeDisp[2] = second / 10;
  TimeDisp[3] = second % 10;
  DyspSendString(DISPLAY_ADDRESS1,TimeDisp);
  TimerUpdate = LOW;
}

 

souchkov
Offline
Зарегистрирован: 04.02.2013

сорри нашел косяк... строка 44, лишний вызов, остался от переноса кода....

maksim
Offline
Зарегистрирован: 12.02.2012

Думаю что и не кварце дело, и не в яйцах, и не в бабине ) Так попробуйте:

int second, minute, hour;
bool TimerUpdate;

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
  Timer();
  if(TimerUpdate == HIGH)
  {
    if(hour < 10) Serial.print("0");
    Serial.print(hour);
    Serial.print(":");
    if(minute < 10) Serial.print("0");
    Serial.print(minute);
    Serial.print(":");
    if(second < 10) Serial.print("0");
    Serial.println(second);
    TimerUpdate = 0;
  }
}

void Timer()  
{
  static unsigned long millis_prev;
  if(millis()-millis_prev > 500)
  {
    TimerUpdate = HIGH;
    second = (millis()/1000)%60;
    minute = (millis()/60000UL)%60;
    hour = (millis()/3600000UL)%24;
    millis_prev = millis();
  }
}

 

souchkov
Offline
Зарегистрирован: 04.02.2013

спасиб ) кое что у себя поправил...

Милана
Offline
Зарегистрирован: 05.04.2013

Ребята помогите пожалуйста. Вопрос в следующем:

Есть Ожидание приема с порта 

while(Serial.available()== 0);

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

Как сделать таймер, что бы при отсутствии входящих данных, через 20 секунд происходило {какое то действие}

а при поступлении таймер сбрасывался.

Извините за неподобающее оформление, ну и надеюсь не будете сильно критиковать.

Перекопала интернет, но так и не поняла.

П.с. Я не программистка, просто пришлось...

maksim
Offline
Зарегистрирован: 12.02.2012
  unsigned long millis_prev = millis();
  while(!Serial.available() && millis()-millis_prev < 20000);
  if(millis()-millis_prev >= 20000)
  {
    // прошло 20 секунд
  }
  else
  {
    // что-то прилетело в буфер порта
  }

 

Милана
Offline
Зарегистрирован: 05.04.2013

maksim пишет:

  unsigned long millis_prev = millis();
  while(!Serial.available() && millis()-millis_prev < 20000);
  if(millis()-millis_prev >= 20000)
  {
    // прошло 20 секунд
  }
  else
  {
    // что-то прилетело в буфер порта
  }

 

спасибо Максим Вам огромное, а обнуление таймера тут ненужно?

maksim
Offline
Зарегистрирован: 12.02.2012

Не знаю что вы имеете ввиду под словами "обнуление таймера тут ненужно?" ...  Если вы про отсчет 20 секунд, то нет не нужно, а что бы знать точно это вам нужно или нет покажите весь код и напишите что хотите.

Милана
Offline
Зарегистрирован: 05.04.2013
void setup()
{
  Serial.begin(9600);
  maxServo1.attach(servoPin1);
  maxServo2.attach(servoPin2);
  
}

void loop ()
{
  
  while(Serial.available()== 0);
  int data = Serial.read() - '0';
  Serial.println(Serial.read());
  if (data == 20 && i==0)
    {
 i=i+1;
 
 Serial.flush();
  }
  else if (data >=0 && i==1)
  {
  int pos1 = map(data, 0, 9, 20, 160);
  pos1 = constrain(pos1, 20, 160);
    maxServo1.write(pos1);
    
  i=i+1;
  
  Serial.flush();
  }
  else if (data >=0 && i==2)
  {
  int pos2 = map(data, 0, 9, 80, 140);
  pos2 = constrain(pos2, 80, 140);
   maxServo2.write(pos2);
  Serial.flush();
  i=i-2;
  }
  else 
  { Serial.flush();
    i=0;
  
    
  }
  
  }

На порт приходит символ, за ним 2 значения от 0 до 9. Оба значения переводятся в положение сервоприводов. Если задержка более 20 секунд, нужно вернуть их в первоначальное положение. Как то так.

step962
Offline
Зарегистрирован: 23.05.2011

Милана пишет:
На порт приходит символ, за ним 2 значения от 0 до 9. Оба значения переводятся в положение сервоприводов. Если задержка более 20 секунд, нужно вернуть их в первоначальное положение. Как то так.

Задержка, как правило, отсчитывается от определенного момента. Что таким моментом является у вас? Рискну предположить, что момент последнего поступления символа по UART.

В общем, чуть конкретизируйте этот пункт.

maksim
Offline
Зарегистрирован: 12.02.2012

Ну так опишите что делает этот код или точнее что вы от него хотите. Потому как символ никак не учавствует или запутывает работу кода, а Serial.flush() уже давно (с версии IDE1.0) не очищает буффер.

Милана
Offline
Зарегистрирован: 05.04.2013

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

maksim
Offline
Зарегистрирован: 12.02.2012

Символ этот D,  угадал? Надо ж было так зашифровать... Често говоря из кода не ясно что вы хотите от него, опишите что хотите... какой формат команды,  которую вы отправляете? D12 ?

Милана
Offline
Зарегистрирован: 05.04.2013

Вся проблема в том, что я подстраиваюсь под передатчик, к которому доступа не имею. Передатчик выдает следующее символ D, вы все верно поняли, далее два значения от 0 до 9. Тоесть типа D19. Приемник должен принять не перепутав очередности, в принципе это работает хорошо. Далее первое значение от 0 до 9 перевести в угол поворота сервопривода в диапазоне от 20 до 160 градусов

int pos1 = map(data, 0, 9, 20, 160);

аналогично со вторым.

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

leshak
Offline
Зарегистрирован: 29.09.2011

Не, все эти бесконечные while ждущие чего-то  - это зло. Так же как и delay().

У вас же потом будет постоянная проблема с одновременностями и проч.

void loop(){
  static unsigned long lastReceived=1; // если один - таймер запустится сразу, если 0 - при первом получении данных
  
  if(Serial.available())lastReceived=millis();
  if( lastReceived && (millis()-lastReceived>=20000)){
     // тут что-то делаем по поводу "20 сек небыло данных"
     
     lastReceived=0; // и вырубаем таймер что-бы небыло повторных сработок
  }
  
  // а тут пишите вашу остальную логику обработки данных из Serial
  
  
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Если "все остальная логика работает" (не вникал в нее), то теоретически вместе это будет так выглядить (просто скоировал ее, убраз вот только "стоять ждать")

 

void loop ()
{

  static unsigned long lastReceived=0; // если один - таймер запустится сразу, если 0 - при первом получении данных

  if(Serial.available())lastReceived=millis();
  if( lastReceived && (millis()-lastReceived>=20000)){
    // тут что-то делаем по поводу "20 сек небыло данных"
    maxServo1.write(90);
    maxServo2.write(90);

    //lastReceived=0; // и вырубаем таймер что-бы небыло повторных сработок
  }

  // пошла ваша логика

  if(Serial.available()== 0){
    int data = Serial.read() - '0';
    Serial.println(Serial.read());
    if (data == 20 && i==0)
    {
      i=i+1;

      Serial.flush();
    }
    else if (data >=0 && i==1)
    {
      int pos1 = map(data, 0, 9, 20, 160);
      pos1 = constrain(pos1, 20, 160);
      maxServo1.write(pos1);

      i=i+1;

      Serial.flush();
    }
    else if (data >=0 && i==2)
    {
      int pos2 = map(data, 0, 9, 80, 140);
      pos2 = constrain(pos2, 80, 140);
      maxServo2.write(pos2);
      Serial.flush();
      i=i-2;
    }
    else 
    { 
      Serial.flush();
      i=0;


    }
  }

}

 

maksim
Offline
Зарегистрирован: 12.02.2012

Тогда уж наверное так должно быть:

  // пошла ваша логика

  if(Serial.available()){
    int data = Serial.read() - '0';
.......

иначе будем читать буффер, когда в нем ничего нет и не читать когда что то есть.

И странно как у вас все работает... если например приходит D23, то D попадает в переменную data, 2 просто обратно отправляется, 3 опять в data. у следующей команды D отправляется обратно и т.д. 

....
    int data = Serial.read() - '0';
    Serial.println(Serial.read());
.....

Я бы сделал так

#include <Servo.h>
#define servoPin1 3
#define servoPin2 4

Servo maxServo1;
Servo maxServo2;

void setup()
{
  Serial.begin(9600);
  maxServo1.attach(servoPin1);
  maxServo2.attach(servoPin2);
}

void loop ()
{
  static unsigned long millis_prev;
  if(Serial.available() >= 3)
  {
    if(Serial.read() == 'D')
    {
      int data = Serial.read() - '0';
      int pos1 = map(data, 0, 9, 20, 160);
      pos1 = constrain(pos1, 20, 160);
      maxServo1.write(pos1);

      data = Serial.read() - '0';
      int pos2 = map(data, 0, 9, 80, 140);
      pos2 = constrain(pos2, 80, 140);
      maxServo2.write(pos2);
    }
    millis_prev = millis();
  }
  else if(millis_prev && (millis()-millis_prev > 20000))
  {
    maxServo1.write(90);
    maxServo2.write(90);
    millis_prev = 0;
  }
}

Интересный приемчик использовать переменную lastReceived и как переменную для хранения миллисекунд и как флаг.

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

Тогда уж наверное так должно быть:

Да конечно. if(Serial.available()) . Просто пока вставлял, пару раз форматирование вставало раком, "собирал " знаново и на третьей попытке - забыл ==0 убрать.


maksim пишет:

Я бы сделал так

Конечно, на вкус и цвет - фломастеры разные, но... 
1. В моем варианте "логика работы" и "логика таймера" - полностью разделены. Легче поддерживать. И переписывать логику чтения/парсинга не отвлекаясь про "не забыть про таймер".
2. available() >= 3 - , а если первое чтение произойдет во не с самого начала пакета? Синхронизация сбилась навсегда?

maksim пишет:

Интересный приемчик использовать переменную lastReceived и как переменную для хранения миллисекунд и как флаг.

Да. Я обычно его и использую. Для того что-бы событие отработало один раз. Вначале я так и сделал, но когда стало ясно что у по событию нам нужно "ставить сервы в 90-то" -  я закоментил строку "останавливаем таймер". Так как ничего страшного в многократном выставлянии серв в 90-то - нет. Следовательно можно обойтись без флага (без выключения таймера после сработки).

leshak
Offline
Зарегистрирован: 29.09.2011

IMHO на такие вещи хорошо ложатся на паттерн "машина состояний"


#define START_MARKER 'D' // символ означающий начало пакет

// перечень возможных режимов сотояний
enum mode_t{
  WAIT_START,
  WAIT_DATA1,
  WAIT_DATA2
};


mode_t mode=WAIT_START; // тут храним что мы сейчас ждем из Serial


// возвращает true если ch - код символа какой-то цифры
bool IsDigit(char ch){
  return ch>='0' && ch<='9';
}



void loop ()
{

   checkTimeOut(); // унесли таймерную логику в отдельную функцию
   
   
  // пошла ваша логика

  if(Serial.available()){

    char ch=Serial.read(); // читаем из Serial

    if(mode==WAIT_START && ch==START_MARKER){ // мы ждали 'D' и он пришел
      mode=WAIT_DATA1; // теперь будем ждать данные для первой сервы
    } 
    else if (IsDigit(ch)){ // что-то делаем только если это у нас пришла цифра
      byte data=ch-'0'; // конвертим код в цифру


      switch(mode){// решаем какую серву нужно выставить
          case WAIT_DATA1:
            maxServo1.write(map(data,0,9,20,160)); // ставим серву
            mode=WAIT_DATA2; // и начинаем ждать данные для второй сервы
            break;
          case WAIT_DATA2:
            maxServo2.write(map(data,0,9,80,140)); // ставим серву
            mode=WAIT_START; // и опять ждем стартовый маркер
            break;
          default:  
            Serial.println("ERROR!!! Unknown mode");// какая-то фигня, никогда не должны сюда попадать. Сообщаем об этом

      } // switch
    } // IsDigit
  } // Serial.available
}


void checkTimeOut(){ // проверяем прошло ли 20-сек и ставит сервы в 90 то если да
  static unsigned long lastReceived=millis(); // сразу запускаем таймер

  if(Serial.available())lastReceived=millis();
  if(  millis()-lastReceived>=20000){     // 20 секунд ничего не просиходло

    // ставим сервы
    maxServo1.write(90);
    maxServo2.write(90);

    // начинаем ожиать букву 'D'
    mode=WAIT_START;
  }

}

 

maksim
Offline
Зарегистрирован: 12.02.2012

Да, на вкус и цвет. С синхронизацией при глюке будет проблема, но это решается очисткой буфера.

leshak
Offline
Зарегистрирован: 29.09.2011

Коль завел речь про "машину состояний", то лично я полюбляю (да, фломастеры :), делать ее не через switch, а через указатели и таблицу обработчиков. Тогда каждый режим - в отдельной функции и ковыряешься с ним потихоньку, а не в одной общей простыне. И добавить новый режим легче, без ветвистых if-фов.

 


#define START_MARKER 'D' // символ означающий начало пакет

// перечень возможных режимов сотояний
enum mode_t{
  WAIT_START=0,
  WAIT_DATA1,
  WAIT_DATA2
};


mode_t mode=WAIT_START; // тут храним что мы сейчас ждем из Serial


// описываем тим обработчика режимов, возвращает режим в который нужно перейти
typedef byte  (*StateHandlerT)(byte code, byte digit); 
// и создаем таблицу обработчиков, ее индексы - совпадают с возможными значениями переменной mode
StateHandlerT handlers[]={start_handler,data1_handler,data2_handler};


void loop ()
{
   checkTimeOut(); // унесли таймерную логику в отдельную функцию
  // пошла ваша логика
  if(Serial.available()){

    char ch=Serial.read(); // читаем из Serial
    byte digit=ch-'0'; // конвертируем в цифру
    
    mode=(mode_t)handlers[mode](ch,digit); // вызываем нужный обработчик и устанавливаем новый режим (который вернул обработчик)
  } 
}


void checkTimeOut(){ // проверяем прошло ли 20-сек и ставит сервы в 90 то если да
  static unsigned long lastReceived=millis(); // сразу запускаем таймер

  if(Serial.available())lastReceived=millis();
  if(  millis()-lastReceived>=20000){     // 20 секунд ничего не просиходло

    // ставим сервы
    maxServo1.write(90);
    maxServo2.write(90);

    // начинаем ожиать букву 'D'
    mode=WAIT_START;
  }

}



// возвращает true если ch - код символа какой-то цифры
bool IsDigit(char ch){
  return ch>='0' && ch<='9';
}



// ============обработчики режимов  ===========
byte start_handler(byte code, byte digit){ // ожидает 'D'
  if(code==START_MARKER)return WAIT_DATA1; // / мы ждали 'D' и он пришел теперь будем ждать данные для первой сервы
  else return WAIT_START; // иначе - продолжаем ждать  'D'
}

byte data1_handler(byte code, byte digit){
  if(!isDigit(code))return WAIT_START; // если это было не цифра - начинаем ждать опять 'D', но можно было и "ждать цифру до победы", тогде вернуть mode - оставить текущий режим
  
     maxServo1.write(map(digit,0,9,20,160)); // ставим серву
     return WAIT_DATA2; // и начинаем ждать данные для второй сервы
}

byte data2_handler(byte code, byte digit){
  if(!isDigit(code))return WAIT_START; // если это было не цифра - начинаем ждать опять 'D'
  
     maxServo2.write(map(digit,0,9,80,140)); // ставим серву
    return WAIT_START; // и опять ждем стартовый маркер
}

 

souchkov
Offline
Зарегистрирован: 04.02.2013

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

souchkov
Offline
Зарегистрирован: 04.02.2013

Есть вопросы про атоматное програмирование:

пример, обратный таймер, кнопка старт,она же стоп, кнопка, плюс, кнопка минус.

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

вопрос в следующем куда запихивать эту обработку? в функции останова, старта или таймера,а может лучше отдельный автомат на изменение счетчика от кнопок?

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

А зачем его вообще пихать в состояния? состояния "ждет старта, таймер идет, таймер закончился" - это понятно. А "увеличивать/уменьшать" счетчик - нет таких состояний. Ну можно, конечно притянуть за уши, но зачем? Это же просто переменные которые нужно менять при нажатии кнопок, судя по вашему описанию в любых состояниях.  Так что IMHO их просто вынесте в loop() скажем и обрабатывать эти кнопки паралельно с машиной состояний.

В качестве примера - смотрите как timeout() я обрабатываю. Его я не пихал в состояния (хотя и может он на них воздействовать). 

souchkov
Offline
Зарегистрирован: 04.02.2013

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

leshak
Offline
Зарегистрирован: 29.09.2011

Ну дык а вам никто и не обещал, что "машина состояний" приминима всегда и везде. "Серебрянной пули" для того что-бы сделать любой код красивым - не существует. switch/if - существовали до нее, так и после (кстати "машина состояний" - это скорее "идея", которая может реализованно быть по разному. через таблицу указателей, через switch, через if...)

Ну а вашими "+"/"-" - тут абстрактно не скажешь. Уже от задачи зависит. Может просто параметры в массив загнать, может сделать еще одну табличку обработчиков (только уже не "состояний", а кнопок)...

souchkov
Offline
Зарегистрирован: 04.02.2013

но все равно спасибо, кое  что применил...

Милана
Offline
Зарегистрирован: 05.04.2013

Ребят спасибо огромное всем кто откликнулся, заработало все. Дело в другом немного крылось оказалось, что передатчик посылает в эфир "D" потом отправляет от 0 - 9 состояние первого сервопривода, далее отправляет состояние второго. В целом все заработало. Удачи вам в проектах!

ilyaplot
Offline
Зарегистрирован: 26.06.2014

Попробуйте вот эту библиотеку http://blog.ilyaplot.ru/arduino_setinterval/. Мне кажется, отлично упростит задачу.