Time keeping for blinking an output

Hey guys, i started fiddling with an arduino a while ago trying to learn some of it so i could use it for a project im working on. I've been doing some of the projects out of the project book in the arduino starter kit and i feel like im getting a hang of the basic stuff.

im starting to do some prototyping of the forementioned project im working on and i've run into some issues with the code, which i've posted below. The practical function i wish to achive is this;
With two buttons i want to be able to change the number on a 7-segment display, one for up and one for down. When a third button is pressed, i want the arduino blink an output at the pace of the Bpm displayed on the 7-seg display, 4 times. If the number displayed has'nt been changed since last time the third button is pushed, i dont want it to do anything.
the output should be normally closed (NC) and when its blinked it should open, then close again.
the first part i have working, adjusting the displayed 'bpm' value. The thing im having a problem with is getting it to blink the output when the third button is pressed. I learned from the project book that for time keeping purposes i should use unsigned long and not int, and also reference the millis() function instead of using delays. Im having a hard time trying to figure out how to get this working, although i partly got it working using only delays.
with only delays i could set the delay to be x amount of milliseconds between blinks, proportional to the displayed Bpm. but if i set the bpm higher than 120 or lower than 60 and pressed the third button, the arduino would freeze and i had to reset it.

the 'buttonUP' and 'buttonDown' inputs are the buttons for adjusting the bpm value
and the 'footSwitch' is the third button i mentioned.
the 'pedal' is the output i want the arduino to blink.
I haven't intergrated the part displaying the number on the display yet, but had it print the value to the serial monitor instead.

i appreciate any advice you guys can give me :slight_smile:

[code]
const int ButtonUp = 2;
const int ButtonDown = 3;
const int FootSwitch = 4;
const int Pedal = 5;
int bpm = 60;
int prevBpm;
int SwitchState;
int UpState;
int DownState;
long Blink;
unsigned long prevMillis = 0;

void setup() {
    Serial.begin(9600);
    pinMode(ButtonDown, INPUT);
    pinMode(ButtonUp, INPUT);
    pinMode(FootSwitch, INPUT);
    pinMode(Pedal, OUTPUT);
    digitalWrite(Pedal, HIGH);
}
void loop() {
    unsigned long currentMillis = millis();
    UpState = digitalRead(ButtonUp);
    DownState =  digitalRead(ButtonDown);
    SwitchState = digitalRead(FootSwitch);
    if (UpState == HIGH) {
        bpm++;
        delay(200);
        Serial.println(bpm);
        //print sevseg
    }
    if (DownState == HIGH) {
        bpm--;
        delay(200);
        Serial.println(bpm);
        //print sevseg
    }
    if (SwitchState == HIGH && bpm != prevBpm) {
        Blink = 1/(bpm/60)*1000-100;
        if(currentMillis-prevMillis>=Blink) {
        digitalWrite(Pedal, LOW);
        delay(100);
        digitalWrite(Pedal, HIGH);
        }
         if(currentMillis-prevMillis>=Blink*2) {
        digitalWrite(Pedal, LOW);
        delay(100);
        digitalWrite(Pedal, HIGH);
        }
        delay(1);
      prevMillis = currentMillis;
      prevBpm = bpm;
    }
}

[/code]

This is a good example where using variables to keep track of the state of the system makes things much easier. This pseudo code should give you the idea

void loop() {
   
   // your other code

   if (thirdButtonPressed == true and valueChanged == true and blinkCount >= maxBlinks ) {
       blinkCount = 0;
   }

   blinkLed();
}
   
void blinkLed() {
   if (blinkCount < maxBlinks) {
     digitalWrite(ledPin, ! digitalRead(ledPin)); // toggles the LED pin
     if (digitalRead(ledPin) == LOW) { // maybe this should be HIGH
         blinkCount ++;  // only increment counter after every second change
     }
  }
}

...R

Robin2:
This is a good example where using variables to keep track of the state of the system makes things much easier. This pseudo code should give you the idea

void loop() {

// your other code

if (thirdButtonPressed == true and valueChanged == true and blinkCount >= maxBlinks ) {
      blinkCount = 0;
  }

blinkLed();
}
 
void blinkLed() {
  if (blinkCount < maxBlinks) {
    digitalWrite(ledPin, ! digitalRead(ledPin)); // toggles the LED pin
    if (digitalRead(ledPin) == LOW) { // maybe this should be HIGH
        blinkCount ++;  // only increment counter after every second change
    }
  }
}




...R

Hmm i see. This look way more systematic and clean than my attempt :stuck_out_tongue:
I cant see where i would insert the timing variable through. Not sure if i made it clear enough, but the outputs blinking is supposed to be at the pace of the det 'bpm' variable. That would be 1000ms beetween every blink at 60bpm, and 500ms at 120bpm.

Here's one strategy.

Define variables before setup() for number of blinks left and number of times to blink.

const int NUM_BLINKS = 4;
int blinkCount = 0;

When you set blink delay, set number of blinks left to twice the number of blinks (for on and off).

  if (SwitchState == HIGH && bpm != prevBpm) {
    Blink = 1 / (bpm / 60) * 1000 - 100;
    blinkCount = NUM_BLINKS * 2;
    prevBpm = bpm;
    prevMillis = currentMillis;
    currentMillis += Blink;  // start right away
    Serial.print("blink delay = ");
    Serial.println(Blink);
  }

Then before the end of loop if you have more blinks left and the it's been long enough, toggle the LED.

  // if more blinks and it's time to blink
  if ( blinkCount && ((currentMillis - prevMillis) >= Blink) )
  {
    // toggle LED
    digitalWrite(Pedal, !digitalRead(Pedal));

    // decrement count
    blinkCount--;

    // mark blink time
    prevMillis = millis();

    Serial.println("blink");
  }

partysponge:
I cant see where i would insert the timing variable through

My apologies, I completely forgot about that. The following version includes the timing code

void loop() {
    
    // your other code

    if (thirdButtonPressed == true and valueChanged == true and blinkCount >= maxBlinks ) {
        blinkCount = 0;
        lastBlinkTime = 0;   // to make sure the first part of the blink starts immediately
        blinkInterval = valueFromBPM; // this probably needs adjusting to get
                                  //     the equivalent number of millisecs
    }

    blinkLed();
}
    
void blinkLed() {
    if (blinkCount < maxBlinks) {
        if (millis() - lastBlinkTime >= blinkInterval) {
            lastBlinkTime = millis();
            digitalWrite(ledPin, ! digitalRead(ledPin)); // toggles the LED pin
            if (digitalRead(ledPin) == LOW) { // maybe this should be HIGH
                blinkCount ++;  // only increment counter after every second change
            }
        }
    }
}

...R

Aside...
One thing I saw in your OP, was the ability for the display to show a value different from the actual beat-rate.

None of my business, but what I’d add is to use the decimal point to indicate when the display==the_bpm rate.

Just OCD completeness - to tell the user what’s happening before the third button is pressed.

Absolutely a good point! It,s kind of intended though. But the decimal point is a great idea!
i was thinking maybe i would use the decimal point for displaying the displayed tempo, even if its different from the one that was last "blinked".

Im using it for controlling a Effects pedal for electric guitar, which has a input which you can give a tap tempo that controls a built in metronome, so i don't want it blinking the set tempo all the time but only when i 'confirm' it by tapping the third button. Thus the function of the display being able to change without setting the tempo. :slight_smile:

Robin2:
My apologies, I completely forgot about that. The following version includes the timing code

void loop() {

// your other code

if (thirdButtonPressed == true and valueChanged == true and blinkCount >= maxBlinks ) {
        blinkCount = 0;
        lastBlinkTime = 0;  // to make sure the first part of the blink starts immediately
        blinkInterval = valueFromBPM; // this probably needs adjusting to get
                                  //    the equivalent number of millisecs
    }

blinkLed();
}
   
void blinkLed() {
    if (blinkCount < maxBlinks) {
        if (millis() - lastBlinkTime >= blinkInterval) {
            lastBlinkTime = millis();
            digitalWrite(ledPin, ! digitalRead(ledPin)); // toggles the LED pin
            if (digitalRead(ledPin) == LOW) { // maybe this should be HIGH
                blinkCount ++;  // only increment counter after every second change
            }
        }
    }
}




...R

Robin2:
My apologies, I completely forgot about that. The following version includes the timing code

void loop() {

// your other code

if (thirdButtonPressed == true and valueChanged == true and blinkCount >= maxBlinks ) {
        blinkCount = 0;
        lastBlinkTime = 0;  // to make sure the first part of the blink starts immediately
        blinkInterval = valueFromBPM; // this probably needs adjusting to get
                                  //    the equivalent number of millisecs
    }

blinkLed();
}
   
void blinkLed() {
    if (blinkCount < maxBlinks) {
        if (millis() - lastBlinkTime >= blinkInterval) {
            lastBlinkTime = millis();
            digitalWrite(ledPin, ! digitalRead(ledPin)); // toggles the LED pin
            if (digitalRead(ledPin) == LOW) { // maybe this should be HIGH
                blinkCount ++;  // only increment counter after every second change
            }
        }
    }
}




...R

So i intergrated this into my code. And it works way better then with delays :slight_smile: thanks a lot. The thing is i still cant get the blinking to work when the bpm tempo is set under 60, or over 119 :s. Is it a problem with the formula i use to find how many mS it should be between blinks, meaning the 'Blink' value?

what i also discovered is that the tempo of the blinking doesn't actually get higher even when i max the bpm to 119, it just as slow as 60. why does this occur? i really cant see the connection as to why this happens.
also heres the updated code in case i subconciously changed something else than the blinking function;
EDIT; i realised my error when i made the arduino Serial print the calculated Blink value as it was always at 1000. I tried changing it out with a map() funtion, and while it worked for making the tempo actually change it's never correctly timed, as a change in bpm isn't directly numerically proportionate to the amount of mS it should be between blinks.
the formula should be correct, and it works when i use it on a calculator. Did i formulate it in a way the arduino can't understand?

const int ButtonUp = 2;
const int ButtonDown = 3;
const int FootSwitch = 4;
const int Pedal = 5;
int bpm = 60;
int prevBpm;
int SwitchState;
int UpState;
int DownState;
int blinkCount = 0;
int maxBlinks = 4;
long Blink;
unsigned long lastBlinkTime;


void setup() {
    Serial.begin(9600);
    pinMode(ButtonDown, INPUT);
    pinMode(ButtonUp, INPUT);
    pinMode(FootSwitch, INPUT);
    pinMode(Pedal, OUTPUT);
    digitalWrite(Pedal, HIGH);
}
void loop() {
    UpState = digitalRead(ButtonUp);
    DownState =  digitalRead(ButtonDown);
    SwitchState = digitalRead(FootSwitch);
    Blink = 1/(bpm/60)*1000;
    if (UpState == HIGH) {
        bpm++;
        delay(200);
        Serial.println(bpm);
        //print sevseg
    }
    if (DownState == HIGH) {
        bpm--;
        delay(200);
        Serial.println(bpm);
        //print sevseg
    }
    if (SwitchState == HIGH && blinkCount >= maxBlinks && bpm != prevBpm) {
        blinkCount = 0;
        lastBlinkTime = 0;
        prevBpm = bpm;
    }
    blinkOutput();
}
void blinkOutput() {
  if(blinkCount < maxBlinks) {
    if(millis() - lastBlinkTime >=  Blink/2) {
      lastBlinkTime = millis();
      digitalWrite(Pedal, ! digitalRead(Pedal));
      if(digitalRead(Pedal) == HIGH) {
        blinkCount++;
      }
    }
  }
}

This line is probably not doing what your think

Blink = 1/(bpm/60)*1000;

because Arduino integer maths is not the same as on a calculator

To start with 1/ anything will probably give you zero.

Try this version

Blink = 1000L * 60 / bpm;

At each stage of the calculation you must be sure that the result can fit into the chosen data type - i.e will neither overflow nor underflow. The L after 1000 tells the compiler to treat it as a Long

Add a print statement so you can check that Blink has the correct value

...R

Ah Great, that did the trick :smiley: this made me learn quite a lot!