2

I'm trying to write a blink without delay code that will wait 20 seconds, then blink 5 times, then wait another 20 seconds - rinse and repeat.

Here's the minimal code I wrote:

if (currentTime % 20000 == 0) {
  for (int x = 0; x <= 5; x++) {
    if (currentTime % 500 == 0) {
      if (solenoid == LOW) {
        solenoid = HIGH;
      }   else {
        solenoid = LOW;
      }
      digitalWrite(solenoidpin, solenoid);
    }
  }
}
}

All this seems to do is set solenoid to HIGH and keep it there. If I get rid of the for loop and the second if statement, I can get it to switch between low and high every 20 seconds, but adding the if and for loops seems to kill it. Any thoughts on how to make this work?

  • Why are you executing it in a for loop 6 times? If currentTime % 500 == 0 is true, it will flip the solenoid state 6 times (i.e., not change it). – Maximilian Gerhardt Jan 30 '18 at 16:29
  • 3
    Please edit your question to include a Minimal, Complete, and Verifiable example of code, not just snippets. Minimal means you've stripped away irrelevant stuff, just leaving what's needed to show the problem. Complete means all the library names are shown, all the variable declarations, and all the function definitions – so people don't have to waste time guessing what you did or what you meant. Verifiable means it can be compiled and tested, allowing other people to test their theories about the problem – user31481 Jan 30 '18 at 16:30
  • 1
    By comparing the result of the modulo operation on a micros() value, you will only "trigger" if you happen to approach each if statement on exactly the microsecond boundary. Depending on your other code, you may find that the Arduino doesn't get around to your if statements on every sequential millisecond, and so you may miss some triggers. Better to do the subtract-and-compare operation that the BlinkWithoutDelay actually uses. That way, your if will trigger, even if you happened to miss the time by one millisecond. – jose can u c Jan 30 '18 at 16:38
  • That code just flips the solenoid really fast for a milisecond, then repeats it in 20 seconds. The for loop and the second if are pointless. In the for loop the variable currentTime doesn't change and the second if is always true because 20000 is divisible by 500. – gre_gor Jan 30 '18 at 16:45
3
// assuming blinking is 500ms ON, 500ms OFF, so 4500ms for 5 blinks. 

int tick = (currentTime/500) % 49;
// if one tick is 500ms, that means, 
//    1 tick ON, 1 tick OFF, 1 tick ON, ... , 1 tick ON, 40 ticks OFF (for a total of 49 ticks)
if( ( tick < 5*2 ) && ( tick%2==0 ) )// only turn ON the led on the first 10 ticks (5 ON; 5 OFF)
                                     //  and only if the number of ticks is ODD
    digitalWrite(solenoidpin, HIGH);
else
    digitalWrite(solenoidpin, LOW);

What I did was convert the milliseconds in to ticks of 500 ms. Then based on the tick value, update the led.

PS I tested it on a Arduino Pro Mini

  • converting elapsed time to tick makes all kinds of sense. ... it is so easy to expand the number of controlled pins ... you could use lookup tables to make complex timing patterns like in a drum machine – jsotola Jan 30 '18 at 18:10
  • 1
    This is smart. But note that: 1. The division and modulo operators are computationally expensive, although tick%2 would be cheap if tick was unsigned. 2. The formula for tick is ill-behaved when millis() rolls over. Both issues could be fixed by updating tick within a if (millis() - lastMillis > 500) { ... } construct. – Edgar Bonet Jan 31 '18 at 8:38
3

Here is a solution:

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

#define INTERVAL 20000L
#define BLINK_TIMES 5
void loop()
{
    static unsigned long lastBlink = 0;
    static int count = 0;

    unsigned long now = millis();

    if(count > 0) {
        count -= blink();
        lastBlink = now;
    } else {
        if(now - lastBlink >= INTERVAL) {
            count = BLINK_TIMES;
        }
    }
}

/*
 * Do one blink.
 * Return 1 when blink is done.
 */
int blink()
{
  static unsigned long lastFlip = 0;
  static int count = 0;
  bool last = false;

  unsigned long now = millis();

  if(now - lastFlip >= 500) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    Serial.print(now / 1000); Serial.print(". "); Serial.println("blink");
    lastFlip = now;
    count++;
    if (count == 2) {
      count = 0;
      last = true;
      }
   }

   return (last) ? 1 : 0 ;
}

The count variable controls when to blink and how many times. While it's zero, no blinking.

When not blinking, it keeps comparing actual time with the last time it blinked. When the time exceed 20s, it puts count to five, and in the next loop, the blinking start.

Variables are declared static so they keep their values between loop executions.

  • Since the last blink will end with a 500ms delay, the wait-time of 20 seconds, will become 20.5 seconds (-: – Gerben Jan 30 '18 at 17:08
  • @jsotola. My bad. I correct my answer. – user31481 Jan 30 '18 at 17:45
  • @Gerben. Check the new answer without delays. – user31481 Jan 30 '18 at 17:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.