flashing LED's 8 times with button Trigger

Hello

I am working with a group of students who are creating a warning circuit that will flash two LEDs 8 times when a button is pressed. If the button is pressed a second time it needs to restart the flash sequence and repeat each time it is pressed. Example: when the button is pressed the sequence of 8 flashes begins, if the button were to be pressed again it needs to flash 8 times after the second button press. I am no expert at using the Arduino but working with the students and using this site we have gotten to the point we are at. We started with delays to get our two LEDs to blink 8 times. We then realized that we probably could not complete our circuit with delays. Using the information we have found here we have come up with the following code. We built the circuit and have it working for the 8 flashes but are struggling with the restart at 8. I need some help guiding the students on what path to follow. We have looked at button push counts and some students are working on that. Others are working on a second circuit using an additional trigger and extended delays. Student come in each day with possible solutions but none are currently working.
Here is the code we are currently using to flash our LEDs

const byte buttonPin = 2;  
const byte ledPinA = 13; 
const byte ledPinB = 12;   
unsigned long startTime;   
unsigned long period = 500;  
boolean flashing = false;   
byte count = 0;
byte previousButtonState;


void setup()
{
  Serial.begin(115200);
  pinMode(ledPinA, OUTPUT);
  pinMode (ledPinB, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  digitalWrite(ledPinA,LOW);
  digitalWrite(ledPinB,LOW);
}

void loop()
{
  if (!flashing)
  {
    byte currentButtonState = digitalRead(buttonPin);
    if (currentButtonState != previousButtonState && currentButtonState == LOW)
    {
      flashing = true;
      count = 0;
      startTime = millis();
    }
    previousButtonState = currentButtonState;
  }
  else
  {
    unsigned long currentTime = millis();
    if (currentTime - startTime >= period)
    {
      digitalWrite(ledPinA, !digitalRead(ledPinA));
      digitalWrite(ledPinB, !digitalRead(ledPinB));
      startTime = currentTime;
      count++;
      if (count == 16)
      {
        flashing = false;
        digitalWrite(ledPinA, LOW);
        digitalWrite(ledPinB, LOW);
      }
    }
  }
  
}

Any assistance will be greatly appreciated.

You are over thinking it. Just use a state machine that has 8 states, start with the state variable equal to 8. Only call the flash task when the state is not zero and it is time to call it. Decrement the state variable every time you turn the LED off. Then on detecting a button push just set the state back to 8 again.

For more on a state machine:-
See my
http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html
Or Robin2's several things at once
http://forum.arduino.cc/index.php?topic=223286.0

Thank you Mike

I will pass this on to the students. I was thinking state machine and I had seen and passed on Robin2's "Several things at once" but we were having trouble putting that together. As I said I am new to the Arduino and I am trying to help these students all by trial and error. They are working on a design project for a warning light at a cross road. At the intersection from north to south you can not see over the hill coming up from the east and they are coming up with designs to solve a problem. The design is a warning system telling drivers that a vehicle is coming up over the hill. They are planning to use pressure switch in road (simulated by a button). Another group is trying 2 IR proximity sensors as simulator instead of switch. Your info may help both groups. Thank you for your assistance!

Jeff

This probably more complicated than it needs to be but it introduces a few new concepts like interrupts, debouncing, XOR etc.

const byte buttonPin = 2;  
const byte ledPinA = 13; 
const byte ledPinB = 12;   

#define ISR_BLANKING_TIME   250         //mS time after we see an edge that we ignore additional edges
#define ISR_DEBOUNCE_TIME   20          //mS time after we see a falling edge before checking for debounce
//
#define FLASH_TOGGLE_PERIOD 250         //full period is 2x this
//
#define ISRFLG_FALLING_EDGE 0x01        //flag - indicates a falling edge was seen
#define ISRFLG_BLANKING     0x02        //flag - when set additional falling edges are ignored
#define ISRFLG_DEBOUNCE     0x04        //flag - when set we're timing a debounce

#define LED_NUM_BLINKS      8           //numer of times to blink per button press

int
    nNumFlashes,
    nNumFlashesSet;
byte
    bFlashFlag,
    bISRFlag;

void setup()
{
    Serial.begin(115200);
    //
    pinMode(ledPinA, OUTPUT);
    pinMode (ledPinB, OUTPUT);
    digitalWrite(ledPinA,LOW);
    digitalWrite(ledPinB,LOW);
    
    pinMode(buttonPin, INPUT_PULLUP);
    //set up to interrupt and run the function ISR_ButtonHandler() when
    //a falling edge of buttonPin is seen
    attachInterrupt(digitalPinToInterrupt(buttonPin), ISR_ButtonHandler, FALLING);

    //initialize our flag and the number of flashes counter to known values (0)
    bISRFlag = 0;
    bFlashFlag = 0;
    nNumFlashes = 0;
    
}//setup
    
void loop()
{
    //look to see if a valid button press was detected
    ProcessButton();

    //flash the LEDs
    DoFlashing();
  
}//loop

//////////////////////////////////////////////////////////////////////////
// ISR_ButtonHandler
//      - runs on a falling edge of buttonPin
//      - detects a button press
//
//      - ISRs (interrupt service routines) should be kept very short so all
//        we do here is set a couple of flags that the mainline code can
//        handle
//
//////////////////////////////////////////////////////////////////////////
void ISR_ButtonHandler( void )
{
    if( bISRFlag & ISRFLG_BLANKING )
        return;

    //tell the mainline a falling edge was seen and set a flag to block
    //further edge ISRs for a period of time
    bISRFlag = ISRFLG_FALLING_EDGE | ISRFLG_BLANKING;
    
}//ISR_ButtonHandler

//////////////////////////////////////////////////////////////////////////
// ProcessButton
//      - this is called very frequently by the mainline
//      - it does a few things:
//          - checks to see if the ISR detected a press
//              - if so it:
//                  - sets the blanking flag so more edges are ignored for a bit
//                  - sets a flag indicating we're timing a debounce
//                  - initializes timers to clear the blanking and debounce flags
//
//////////////////////////////////////////////////////////////////////////
void ProcessButton( void )
{
    static unsigned long        //static variables stay around when the function exits
        timerISRBlanking,       //so we can maintain the count or state of something
        timerISRDebounce;       //when we come back they're still there
            
    //has a falling edge been seen in the ISR?
    if( bISRFlag & ISRFLG_FALLING_EDGE )
    {
        //yes; stop interrupts while we modify the flag byte
        //this is good practise; without this an interrupt could happen that might result in the byte
        //being changed there while we're changing it here; that would be bad
        noInterrupts();
        bISRFlag &= ~ISRFLG_FALLING_EDGE;   //this clears the edge-detected flag
        bISRFlag |= ISRFLG_DEBOUNCE;        //this sets the debounce flag
        interrupts();
        
        //set debounce & blanking timers
        timerISRBlanking = millis();
        timerISRDebounce = timerISRBlanking;               
        return;

    }//if
        
    if( bISRFlag & ISRFLG_DEBOUNCE )
    {
        //if here, we've seen an edge and are now debouncing
        //we detect falling edges in the ISR so after some period of time, after
        //all the mechanical bouncing is done, a read of the button pin
        //should show LOW for a valid switch press
        //I set the debounce time to a "typical" 20mS (see ISR_DEBOUNCE_TIME
        //definition above)
        if( (millis() - timerISRDebounce) >= ISR_DEBOUNCE_TIME )
        {
            //our debounce timer has expired; clear the flag (mindful of the
            //ISR, like before)
            noInterrupts();
            bISRFlag &= ~ISRFLG_DEBOUNCE;
            interrupts();

            //and read the pin. If still low, it's a valid button press so
            //reset the flash number and sequence
            if( digitalRead(buttonPin) == LOW )
            {
                //restart the sequence
                bFlashFlag = HIGH;
                digitalWrite( ledPinA, LOW );
                digitalWrite( ledPinB, LOW );
                nNumFlashes = LED_NUM_BLINKS;
                
            }//if
                                        
        }//if                
        
    }//if

    //here we check the blanking timer
    //this is used to allow the ISR to ignore switch presses for some period of
    //after a press. I set it to 250mS (see ISR_BLANKING_TIME definition above)
    if( bISRFlag & ISRFLG_BLANKING )
    {
        //has blanking time passed?
        if( (millis() - timerISRBlanking) >= ISR_BLANKING_TIME )
        {
            //yes; clear flag
            //this will allow ISR to detect another falling edge
            noInterrupts();
            bISRFlag &= ~ISRFLG_BLANKING;
            interrupts();
            
        }//if                
        
    }//if
           
}//CheckForEvent

//////////////////////////////////////////////////////////////////////////
// DoFlashing
//      - this is called very frequently by the mainline
//      - all this does is flash LEDs if:
//          - nNumFlashes is greater than zero
//      - it constantly toggles a bit in a flag byte and uses that to 
//        set the state of the LEDs
//
//////////////////////////////////////////////////////////////////////////
void DoFlashing( void )
{
    static unsigned long
        timeFlashing = millis();
    unsigned long
        timeNow;

    //flashing and LED updates occur at twice the flash period
    //we use this to time the 'low' state and the 'high' state
    timeNow = millis();   
    if( (timeNow - timeFlashing) < FLASH_TOGGLE_PERIOD )
        return;
        
    timeFlashing = timeNow;

    //if there are flashes to do...
    if( nNumFlashes > 0 )
    {
        //set the LEDs to the current state (on or off)
        digitalWrite( ledPinA, bFlashFlag );
        digitalWrite( ledPinB, bFlashFlag );
        //toggle the state from off-to-on or on-to-off
        //this ('^') is an exclusive-OR (XOR)
        //  - 1 XOR 1 = 0
        //  - 0 XOR 1 = 1
        //very handy for toggling a state or bit
        bFlashFlag ^= HIGH;
        if( bFlashFlag )
            nNumFlashes--;

    }//if
        
}//DoFlashing

This probably more complicated than it needs to be

Well you are definitely not wrong about that statement.

Blackfin:
This probably more complicated than it needs to be but it introduces a few new concepts like interrupts, ...

So it is rubbish! :astonished:

Common "newbie" trap to introduce interrupts entirely inappropriately, notably when de-bouncing switches. :roll_eyes:

Griffjd:
If the button is pressed a second time it needs to restart the flash sequence and repeat each time it is pressed. Example: when the button is pressed the sequence of 8 flashes begins, if the button were to be pressed again it needs to flash 8 times after the second button press.

When the button *becomes pressed *load a counter with '8'. As long as the counter is non-zero call a function which does the flashing. When each flash cycle ends decrement the counter.

Paul__B:
So it is rubbish! :astonished:

Common "newbie" trap to introduce interrupts entirely inappropriately, notably when de-bouncing switches. :roll_eyes:

:shrug: The OPs code suggests they're not total noObs. My code's not that complicated.

To each his own. Cheers.

Blackfin:
:shrug: The OPs code suggests they're not total noObs. My code's not that complicated.


The term "Heath Robinson" springs to mind. :grinning:

Or a little shorter using the Bounce2 library.

#include <Bounce2.h>

const byte buttonPin = 2;
const byte ledPinA = 13;
const byte ledPinB = 12;
const unsigned long period = 500;
unsigned long startTime;
byte count = 0;
Bounce key;

void setup() {
  pinMode(ledPinA, OUTPUT);
  pinMode (ledPinB, OUTPUT);
  key.attach(buttonPin, INPUT_PULLUP);
}

void loop() {
  if (key.update() && key.fell()) {
    count = 8;
    digitalWrite(ledPinA, HIGH);
    digitalWrite(ledPinB, HIGH);
    startTime = millis();
  }
  if (count) {
    unsigned long currentTime = millis();
    if (currentTime - startTime >= period) {
      if (!digitalRead(ledPinA)) {
        count--;
      }
      if (count) {
        digitalWrite(ledPinA, !digitalRead(ledPinA));
        digitalWrite(ledPinB, !digitalRead(ledPinB));
        startTime = currentTime;
      }
    }
  }
}