Millis () gebruiken, in geneste lussen

Ik gebruik millis() om wat licht (neopixels) te flitsen op een arduino pro mini. Het probleem is dat ik de lichten niet kan laten knipperen voor de gewenste periode. Meer specifiek knipperen de lampjes gedurende ~ 250 ms, terwijl ik 9 seconden wil knipperen! Hier is een vereenvoudigde versie van wat ik aan het doen ben.

In loop() heb ik een while-lus (laten we het lus 1 noemen) die 250 ms moet werken. In loop 1 bel ik een functieflits (), die 9 seconden moet lopen! Ik zette een andere lus (lus 2) na lus 1, die 3 seconden zou lopen.

Hier is het grotere plaatje ... Ik wil eigenlijk dat een radio gedurende 250 ms wakker wordt. Als het in dat tijdsinterval iets ontvangt, wordt flash() aangeroepen dat de lichten gedurende 9 seconden flitst. Als het niets ontvangt, gaat het gewoon gedurende 3 seconden in slaap, komt het 250 m lang levend terug, enzovoort enzovoort

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
}

De flits() -functie ziet er als volgt uit,

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

Ik stop er lus 2 in, want zonder dat knipperen de lampjes voor altijd. Dit is logisch omdat alle code zich in een oneindige lus - ongeldige lus() bevindt.

Voor foutopsporing zet ik seriële afdrukken nadat ik de lussen heb ingevoerd. Het lijkt erop dat het nestelen van twee lussen (in mijn geval flash() binnen lus 1), die beide millis() gebruiken, de oorzaak van het probleem is. Als dat waar is, hoe moet ik anders dit dan doen? Als dat niet het probleem is, worden alle ideeën op prijs gesteld.

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
Plaats uw volledige code.
toegevoegd de auteur Hugo, de bron
uw 'while (millis() - previousMillis
toegevoegd de auteur Juraj, de bron
@Juraj Ik begrijp niet wat u bedoelt met "tijdlijnplanning". En ik kan geen vertraging gebruiken, omdat het blokkeert, en dit zal niet samengaan met de rest van het (grotere) programma
toegevoegd de auteur Demi, de bron

5 antwoord

Gebruik in plaats daarvan een staatsmachine:

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
toegevoegd
@Tahseen nee het is niet nodig.
toegevoegd de auteur CTKeane, de bron
@Tahseen je kunt andere manieren kiezen om de staat te wijzigen dan alleen de tijd die is verstreken. Wijzig gewoon de voorwaarde in de if. U kunt ook meer staten hebben, de bool in een enum wijzigen en de if/else in een switch veranderen.
toegevoegd de auteur CTKeane, de bron
Deze ziet er echt goed uit ... ga het proberen. Maar de regel previousMillis = millis (); in uw eerste codeblok is niet echt nodig, toch?
toegevoegd de auteur Demi, de bron
Dit is slim, maar het licht niet gedurende 9 seconden, maar slechts 250 ms (dat is wakker interval). Hier is het grotere plaatje ... Ik wil eigenlijk dat een radio gedurende 250 ms wakker wordt. Als het in dat tijdsinterval iets ontvangt, wordt flash() aangeroepen dat de lichten gedurende 9 seconden flitst. Als het niets ontvangt, gaat het gewoon gedurende 3 seconden in slaap, komt het 250 m lang levend terug, enzovoort enzovoort
toegevoegd de auteur Demi, de bron

Hier is een schets om je op weg te helpen. Het gebruikt geen vertraging of bibliotheken. Ik heb geen NeoPixel-apparaten, dus ik gebruikte de Arduino ingebouwde LED voor testdoeleinden. Verwijder gewoon de "myTimer4" -functie en bijbehorende code/variabelen om het knipperende LED te stoppen. Ik weet niet welk type "radio" u gebruikt, dus ik gebruikte een teken dat in de seriële monitor is ingevoerd om een ​​radiosignaal te simuleren dat wordt ontvangen. Er zijn ook verschillende seriële afdrukinstructies in de code voor testdoeleinden.

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
toegevoegd

de lampjes knipperen voor ~ 250 ms, terwijl ik 9 seconden wil knipperen!

die logica klopt niet. In essentie wil je een maaltijd van 2 uur binnen 5 seconden eten.

jouw code weerspiegelt dat soort logica: wanneer de uitvoering flash() afsluit, moet deze ook de while-lus in de lus verlaten ().

dus denk precies wat je wilt doen, noteer het en codeer ernaar.

0
toegevoegd

Ik had het fout om te veel vertrouwen te hebben in mijn hele code. Toen mensen voorstelden mijn hele code te plaatsen, had ik het net moeten doen !!!

Hoe dan ook, hier is de fout die ik heb gemaakt. Ik heb de variabele flash_time geïnitialiseerd als een 8-bits geheel getal. Natuurlijk kan een 8 bit maar maximaal 255. Ik probeer 9000 op te slaan !!! De volgende code heeft deze bug opgelost.

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!");
    }
}

Dus geneste lussen die millis() gebruiken om de beëindigingsvoorwaarden te bepalen werken prima, zelfs als lus 1 loopt voor 250 ms en lus 2 (dat is binnen lus 1) werkt veel langer, zoals 9 seconden.

Excuses als ik teveel tijd van mensen heb verspild.

0
toegevoegd

Je logica is verkeerd. Als u vorigeMillis bijwerkt met de waarde millis() op de eerste regel, trekt u altijd het tijdsverschil af tussen de eerste en tweede regel van uw code (wat hetzelfde is als in de lus in de binnenste flits ()). Als u wilt dat de tijd verstreken is sinds het programma is gestart, moet u een vaste variabele hebben in setup() in plaats van deze elke keer in de lus() bij te werken.

Toch zie ik niet waarom millis() in jouw geval noodzakelijk is. Het gebruik van delay() zou veel eenvoudiger zijn en de klus nog klaren. Voorbeeld:

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
toegevoegd
@Tahseen: Je hebt geschreven " Ik kan geen vertraging " gebruiken en toch gebruik je het in flash() . Dus wat is de deal?
toegevoegd de auteur Sprogz, de bron
Ik kan geen vertraging gebruiken, omdat het blokkeert, en dit zal niet samengaan met de rest van het (grotere) programma. Kun je alsjeblieft iets meer uitleggen over je idee om een ​​vaste variabele in setup() te plaatsen? Mijn while-loops proberen de tijdsperiode te bepalen om binnen te blijven.
toegevoegd de auteur Demi, de bron
Ik gebruikte het in flash() om het effect te krijgen dat lichten aan en uit gingen. Maar als ik het in de eigenlijke while-lus gebruik, kunnen andere dingen die ik hoop in de toekomst te integreren niet werken. Ik wil bijvoorbeeld een radio gebruiken die voor 250 ms wakker wordt, ziet of deze een opdracht heeft en dan gedurende 3 seconden gaat slapen. Als het een opdracht in dat interval van 250 ms heeft ontvangen, knippert de arduino de lichten gedurende 9 seconden
toegevoegd de auteur Demi, de bron
Precies ... ik begreep niet wat je bedoelt met "het blokkeert", wat is de reden dat je het niet kunt gebruiken?
toegevoegd de auteur milton, de bron