ネストされたループでのmillis()の使用

私はmillis()を使ってarduinoプロミニでいくつかのライト(ネオピクセル)を点滅させています。問題は、私が希望の期間ライトを点滅させることができないということです。より具体的には、ライトは約250ミリ秒間点滅しますが、私は9秒間点滅します。私がやっていることを簡略化したものがここにあります。

loop()では、whileループ(ループ1と呼ぶ)を250ms実行する必要があります。ループ1の中で、関数flash()を呼び出します。これは9秒間実行する必要があります。私は別のループ(ループ2)をループ1の後に置く。それは3秒間実行する。

ここに大きな写真があります...私は実際にラジオが250ミリ秒間起きたがっています。その時間間隔に何かを受け取った場合、flash()が呼び出され、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 loop()。

デバッグのために、私はループに入った後にシリアルプリントを入れます。 2つのループ(私の場合は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
追加された
@タッセンは必要ありません。
追加された 著者 CTKeane,
@あなたはちょうど経過時間よりも状態を変える他の方法を選択することができます。 ifの条件を変更するだけです。さらに多くの状態を持ち、ブール値を列挙型に変更し、if/elseをスイッチに変更することもできます。
追加された 著者 CTKeane,
これは本当に良いように見える...それを試してみる。しかし、コードの最初のブロックにある previousMillis = millis(); という行は本当に必要ではありません。
追加された 著者 Demi,
これはスマートですが、ライトを9秒間点滅させることはありませんが、わずか250ミリ秒(つまりawake_interval)です。ここに大きな写真があります...私は実際にラジオが250ミリ秒間起きたがっています。その時間間隔に何かを受け取った場合、flash()が呼び出され、9秒間ライトが点滅します。それが何も受け取らないならば、それは単に3秒間スリープ状態になり、250ミリ秒間生きて戻ってきます。
追加された 著者 Demi,

ここにあなたを始めるためのスケッチがあります。遅延やライブラリは使用しません。私はNeoPixelデバイスを持っていないので、テスト目的でArduinoをLEDに組み込んだ。 "myTimer4"関数と関連するコード/変数を削除して点滅しているLEDを止めてください。私はあなたが使用している "ラジオ"の種類を知らないので、受信している無線信号をシミュレートするためにシリアルモニタに入力された文字を使用しました。テスト目的のために、コード全体にいくつかのシリアル印刷ステートメントがあります。

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
追加された

ライトが約250ms点滅しますが、9秒間点滅します!

その論理は意味をなさない。本質的には、5秒以内に2時間の食事を食べたい。

あなたのコードはその種のロジックを反映しています:実行が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!");
    }
}

したがって、ループ1が250ミリ秒実行され、ループ2(ループ1の内部)が9秒間実行されても、終了条件を判断するためにmillis()を使用するネストされたループはうまく機能します。

私はあまりにも多くの人の時間を無駄にした場合には謝罪します。

0
追加された

あなたのロジックは間違っています。最初の行でmillis()の値でpreviousMillisを更新すると、コードの最初と2番目の行(flash()内のループで実行しているものと同じ)の時間差が常に減算されます。必要なのは、プログラムが起動してから時間が経過することです。ループ()で毎回更新するのではなく、setup()で固定変数を持つ必要があります。

それでも、私はなぜあなたの場合にミリリス()が必要なのかわかりません。 delay()を使用するほうがはるかに簡単で、まだ仕事を完了させるでしょう。例:

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()に入れるという考えをもう少し広げてください。私のwhileループは、内部に滞在する時間を決定しようとしています。
追加された 著者 Demi,
私はflash()でそれを使ってライトの効果をオン/オフしました。しかし、実際のwhileループで使用すると、将来組み込むことを望む他のものは動作しません。たとえば、私は250ミリ秒間目覚めるラジオを使いたいと思っています。コマンドがあればそれを見てから3秒間スリープ状態になります。その250ミリ秒の間隔でコマンドを受信した場合、arduinoは9秒間ライトを点滅させます
追加された 著者 Demi,
まさに...私はあなたが "それがブロックされている"ということを理解していませんでした。あなたがそれを使用できない理由は何ですか?
追加された 著者 milton,