Використовуючи millis (), у вкладених циклах

Я використовую millis() для спалахування деяких ліхтарів (neopixels) на arduino pro mini. Проблема полягає в тому, що я не можу спалахувати світло протягом потрібного періоду. Більш конкретно, індикатори спалахують ~ 250 мс, тоді як я хочу спалахнути 9 секунд! Ось спрощена версія того, що я роблю.

У loop (), я маю цикл while (назвемо його циклом 1), який повинен працювати протягом 250 мс. Всередині циклу 1 я називаю функцію flash (), яка повинна працювати протягом 9 секунд! Я поставив інший цикл (петля 2) після циклу 1, який би працював протягом 3 секунд.

Ось велика картина ... Я дійсно хочу радіо, щоб прокинутися протягом 250 мс. Якщо за цей проміжок часу він отримує що-небудь, буде викликатися спалах (), який замикає вогні протягом 9 секунд. Якщо він нічого не отримує, він просто заскнеться на 3 секунди, після чого повернеться живим на 250 мс, і так далі

void loop() { 
    previousMillis = millis();
    while (millis() - previousMillis < awake_interval) {  //awake_interval = 250 ms    
        flash();  //flash for 9 seconds
    }

    previousMillis = millis();
    while (millis() - previousMillis < sleep_interval);  //sleep_interval = 3 secs
}

Функція flash() виглядає наступним чином:

void flash() {
    unsigned long x = millis();
    while(millis() - x < flash_time){  //flash_time = 9 secs 
        lights_on();
        delay(t1);
        lights_off(); 
        delay(t2); 
    }
}

Я поставив цикл 2, тому що без нього вогні спалахують назавжди. Це має сенс, оскільки весь код знаходиться в нескінченному циклі - цикл void ().

Для налагодження я ставлю серійні відбитки після введення циклів. Схоже, що вкладання двох циклів (у моєму випадку flash() всередині циклу 1), обидва з яких використовують millis (), є джерелом проблеми. Якщо це правда, як інакше я повинен це робити? Якщо це не проблема, будь-які ідеї будуть оцінені.

Edit: The suggestions made by @VE7JRO and @ratchet freak are neat but I was still having problems making my thing work. Turns out it was a problem with one my initializations. I am posting my whole code below (that uses LEDs). I guess I should post the correct code as an answer.

const long sleep_interval = 3000; 
const long awake_interval = 250; 
const int ledPin = 3;

const uint8_t flash_time = 9000;  //bug 
const uint16_t t1 = 5; 
const uint16_t t2 = 495;

void flash() {  
    unsigned long x = millis();
    while(millis() - x < flash_time){ 
        digitalWrite(ledPin, HIGH);
        delay(t1);
        digitalWrite(ledPin, LOW);
        delay(t2);
    }   
}   

void setup() {
    Serial.begin(57600);  
    pinMode(ledPin, OUTPUT);
}

void loop() {
    unsigned long x = millis();
    while(millis() - x < awake_interval){
        Serial.println("flash time!");
        flash();  //overflow will occur    
    }

    unsigned long y = millis();; 
    while (millis() - y < sleep_interval) {  //sleep_interval = 3 secs
        Serial.println("Sleep time!");
    }
}
1
Розмістіть повний код.
додано Автор Hugo, джерело
ваш 'while (millis() - previousMillis
додано Автор Juraj, джерело
@Juraj Я не розумію, що ви маєте на увазі під "графіком графіка". І я не можу використовувати затримку, тому що це блокує, і це не піде з рештою (більшою) програмою
додано Автор Demi, джерело

5 Відповіді

Замість цього використовуйте автомат

bool  flashing;
unsigned long lastflashingChange;

void loop() { 
    unsigned long currentMillis = millis;
    if(flashing){
        flash();  //update flash state and change IO pin
        if (lastflashingChange - currentMillis >= awake_interval) {  //awake_interval = 250 ms    
            flahsing = false;
            //turn off the led
            lastflashingChange = currentMillis;
        }
    } else {
        if (lastflashingChange - currentMillis >= sleep_interval) {  //awake_interval = 250 ms    
            flashing = true; 
            //possible initialize flash state
            lastflashingChange = currentMillis;
        }
    }
}

And a similar bit of code inside flash()

bool flashOn;
unsigned long lastFlashLightChange;
void flash() {
    unsigned long x = millis();

    if(flashOn){
        if (lastFlashLightChange - x >= t1) {
            flashOn = false;
            lights_off();
            lastFlashLightChange = currentMillis;
        }
    } else {
        if (lastFlashLightChange - x >= t2) {
            flashOn = true;
            lights_on();
            lastFlashLightChange = currentMillis;
        }
    }

}
1
додано
@Tahseen ні, це не потрібно.
додано Автор CTKeane, джерело
@Tahseen ви можете вибрати інші способи зміни стану, ніж просто час, що минув. Просто змініть умову в if. Ви також можете мати більше станів, змінити bool на enum і змінити if/else на перемикач.
додано Автор CTKeane, джерело
Цей виглядає дуже добре ... збирається спробувати. Але рядок previousMillis = millis (); у вашому першому блоці коду дійсно не потрібний, чи не так?
додано Автор Demi, джерело
Це розумно, але воно не буде спалахувати протягом 9 секунд, а лише 250 мс (тобто awake_interval). Ось велика картина ... Я дійсно хочу радіо, щоб прокинутися протягом 250 мс. Якщо за цей проміжок часу він отримує що-небудь, буде викликатися спалах (), який замикає вогні протягом 9 секунд. Якщо він нічого не отримує, він просто заскнеться на 3 секунди, після чого повернеться живим на 250 мс, і так далі
додано Автор Demi, джерело

Ось ескіз для початку роботи. Він не використовує затримки або бібліотеки. У мене немає ніяких пристроїв NeoPixel, тому я використовував Arduino, побудований у світлодіодах для цілей тестування. Просто видаліть функцію "myTimer4" і пов'язані з нею коди/змінні, щоб зупинити блимання світлодіода. Я не знаю, який тип "радіо" ви використовуєте, тому я використав символ, введений в серійний монітор, щоб імітувати отриманий радіосигнал. Існує декілька послідовних операцій друку в коді для цілей тестування.

const unsigned long delayTime = 3000;
const unsigned long delayTime2 = 250;
const unsigned long delayTime3 = 9000;
const unsigned long delayTime4 = 500;
unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
unsigned long previousMillis3 = 0;
unsigned long previousMillis4 = 0;
byte checkForData = 0;
byte dataReceived = 0;
byte flashLED = 0;
byte doOnce = 0;

void setup(){
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}

void loop(){

 //Get current time.
  unsigned long currentMillis = millis();

 //First event.
  if(checkForData == 0){
    if(myTimer1(delayTime, currentMillis) == 1){
      Serial.println("3 second timer done");
      checkForData = 1;
      previousMillis2 = currentMillis;
    }
  }

 //Second event.
  if(checkForData == 1){

   //Your radio code here to check for data.

   //I'm simulating data input from your device using the serial monitor.
    if(Serial.read() > 0){dataReceived = 1;}

    if(myTimer2(delayTime2, currentMillis) == 1){
      Serial.println("250 ms timer done");
      checkForData = 0;
      previousMillis = currentMillis;
      if(dataReceived == 1){
        flashLED = 1;
        previousMillis3 = currentMillis;
        dataReceived = 0;
      }
    }
  }

 //Third event.
  if(flashLED == 1){
    if(doOnce == 0){
      flash(1);
      doOnce = 1;
    }
    if(myTimer3(delayTime3, currentMillis) == 1){
      Serial.println("9 second timer done");
      flashLED = 0;
      flash(0);
      doOnce = 0;
      digitalWrite(LED_BUILTIN, LOW);
    }
  }

 //Fourth event. 
  if(myTimer4(delayTime4, currentMillis) == 1 && flashLED == 1){
    //Serial.println("500 ms timer done");
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    previousMillis4 = currentMillis;
  }

}

// Function to send data to the NeoPixel device.
void flash(byte startStop){
  if(startStop == 1){Serial.println("flash function started");}
  if(startStop == 0){Serial.println("flash function ended");}  
}

// First event timer.
byte myTimer1(unsigned long delayTime, unsigned long currentMillis){
  if(currentMillis - previousMillis >= delayTime){previousMillis = currentMillis;return 1;}
  else{return 0;}
}

// Second event timer.
byte myTimer2(unsigned long delayTime2, unsigned long currentMillis){
  if(currentMillis - previousMillis2 >= delayTime2){previousMillis2 = currentMillis;return 1;}
  else{return 0;}
}

// Third event timer.
byte myTimer3(unsigned long delayTime3, unsigned long currentMillis){
  if(currentMillis - previousMillis3 >= delayTime3){previousMillis3 = currentMillis;return 1;}
  else{return 0;}
}

// Fourth event timer.
byte myTimer4(unsigned long delayTime4, unsigned long currentMillis){
  if(currentMillis - previousMillis4 >= delayTime4){previousMillis4 = currentMillis;return 1;}
  else{return 0;}
}
1
додано

індикатори спалахують ~ 250 мс, тоді як я хочу спалахнути 9 секунд!

ця логіка не має сенсу. По суті ви хочете з'їсти 2 години їжі протягом 5 секунд.

Ваш код відображає таку логіку: коли виконання виходить з flash (), воно також повинно вийти з циклу while в циклі ().

так що думайте точно, що ви хочете зробити, запишіть його, а потім код до нього.

0
додано

Я помилявся, будучи занадто впевненим у своєму цілому коді. Коли люди запропонували розмістити весь мій код, я повинен був це зробити !!!

У всякому разі, ось помилка, яку я зробив. Я ініціалізував змінну flash_time як 8-бітове ціле число. Звичайно, 8 біт може утримувати до 255. Я намагаюся зберегти 9000 !!! Наступний код має виправлену помилку.

const long sleep_interval = 3000; 
const long awake_interval = 250; 
const int ledPin = 3;

const uint16_t flash_time = 9000;  //bug fixed by using uint16_t instead of uint8_t
const uint16_t t1 = 5; 
const uint16_t t2 = 495;

void flash() {  
    unsigned long x = millis();
    while(millis() - x < flash_time){ 
        digitalWrite(ledPin, HIGH);
        delay(t1);
        digitalWrite(ledPin, LOW);
        delay(t2);
    }   
}   

void setup() {
    Serial.begin(57600);  
    pinMode(ledPin, OUTPUT);
}

void loop() {
    unsigned long x = millis();
    while(millis() - x < awake_interval){
        Serial.println("flash time!");
        flash();    
    }

    unsigned long y = millis();; 
    while (millis() - y < sleep_interval) {  //sleep_interval = 3 secs
        Serial.println("Sleep time!");
    }
}

Отже, вкладені цикли, які використовують millis() для визначення умов завершення роботи, чудово працюють, навіть якщо цикл 1 виконується протягом 250 мс, а контур 2 (це всередині циклу 1) працює довше, як 9 секунд.

Вибачте, якщо я витратив занадто багато часу на людей.

0
додано

Ваша логіка неправильна. Якщо ви оновлюєте попередню Mіllis з millis() значенням у першому рядку, ви завжди віднімаєте різницю в часі між першим і другим рядками вашого коду (це те ж саме, що ви робите в циклі всередині flash ()). Якщо вам потрібно отримати час, що пройшов з моменту запуску програми, вам потрібно мати фіксовану змінну для цього у setup() замість оновлення кожного разу у циклі ().

Незважаючи на це, я не розумію, чому millis() необхідний у вашому випадку. Використання затримки() було б набагато простішим і все одно буде виконано роботу. Приклад:

void setup() {
delay(250);
}

void loop(){

    for(int i = 0; i < 9; i++){
        digitalWrite(LED, HIGH);
        delay(time);
        digitalWrite(LED, LOW);
        delay(time);

        delay(1000 - 2*time); //Delays the loop exactly one second per iteraction
    }

}
0
додано
@Tahseen: Ви написали " Я не можу затримати ", але ви використовуєте його у flash() . Яка угода?
додано Автор Sprogz, джерело
Я не можу використовувати затримку, тому що це блокує, і це не піде з рештою (більшої) програми. Не могли б ви трохи розширити вашу ідею встановлення фіксованої змінної у setup ()? Мої петельки поки намагаються визначити період часу, щоб залишитися всередині.
додано Автор Demi, джерело
Я використовував його у flash() для того, щоб ефект світла включався та вимикався. Але якщо я використовую його у фактичному циклі, тоді інші речі, які я сподіваюся включити в майбутнє, не можуть працювати. Наприклад, я хочу використовувати радіо, яке прокидається протягом 250 мс, бачить, якщо у нього є якась команда, а потім засинає 3 сек. Якщо він отримав команду в такому інтервалі 250 мс, arduino буде спалахувати вогні протягом 9 секунд
додано Автор Demi, джерело
Точно ... Я не розумів, що ви маєте на увазі під "блокуванням", чому ви не можете його використовувати?
додано Автор milton, джерело