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
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.
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!
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
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.