Debouncing a button, looping issue (I think)

I'm having an issue I can't figure out with debouncing a button. I'm a programming noob, this is my second project, first was making the flame effect you see in the first video below. I'm learning swiftly, but I'm stuck again. I'm aware of the various debounce hardware solutions, but I'm trying not to spend any more money on this project so I'm attempting to solve this issue with the software. I'm also just trying to learn the programming side of the arduino as much as possible.

I think the issue I'm having is the lastDebounceTime variable (taken from the debounce example) is being looped over and over again because instead of detecting a single button press, I'm trying to code a press and hold button.

The first code I have working is called TCK_grenade and is just for one button press&hold, and works fine.

The second one, TCK_Grenade_2b_deb1 is the code for one button but do two different things. The first button press&hold should spin the motor (1475 u-seconds, 1550 = still) and random the LEDs on and off. The second button press&hold should spin the motor the opposite direction (1600useconds, with 1550 being still/center) doing nothing with the LEDs.

What's happening with the debounce code, is it seems like the debounce timer is looping the debounce delay over and over. So I'll hold the button down, it will spin the servo in the correct direction for 400ms, then try to spin the motor the opposite direction for 400ms, and do that over and over all while still holding the button. What SHOULD happen is when the button is pressed and held the first time, flash the LEDS and spin the servo one way until the button is let go. When the button is pressed again, spin the servo the opposite direction so that the string can be un-wound from the spool without opening the enclosure. See the videos below for a visual.

EDIT: I Should note I'm using an SPST push button switch using the pullup resistor built into the arduino. I don't have a schmitt inverter or a resistor in the circuit other than the pullup on the button and the resistor for the LEDs.

Here's a video explaining what it should do (using TCK_grenade):

Here's a video of it's actually doing (using TCK_Grenade_2b_deb1):

I'm not going to post the TCK_grenade code, as it's just a missing all of the debounce and button press tracking code.

#include <Servo.h> // imported servo library

// Written by Will Dean, master electrician of the
// House Theatre of Chicago
// Show: The Crownless King.  
// Grenade code for Davey Boone's bluff
// September, 2013

Servo myservo;  // assign myservo variable from library Servo.h

// these don't change values, ever
const int buttonInput = 2;
const int lightOutput = 13;  //short end gets resistor = - side

// changing variables
int buttonState;        // current reading from the button input pin (pullup resisto
int ledState = -1;      // I'm using the ledState to track button presses.  
                        // Taken from a video tutorial here: http://www.youtube.com/watch?v=CnHKE_ls7F8

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 400;   // the debounce time; increase if the output flickers


void setup() {
  
  pinMode(lightOutput, OUTPUT);  // setting LED pin to output the random values
  pinMode(buttonInput, INPUT);  // setting buttonInput pin to read Inputs
  digitalWrite(buttonInput, HIGH);  // Use the built in pullup resistor for button
  myservo.attach(9);  // setting myservo to read from pin 9
}
  
void loop() 
{
  buttonState = digitalRead(buttonInput);       // read the state of the switch into a local variable
  
  // check to see if you just pressed the button
  // (ie the input when from HIGH to LOW), and you've waited
  // long enough since the last press to ignore any noise:
  
  if ((millis() - lastDebounceTime) > debounceDelay) { // millis = system time
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    
  // BUTTON ON
  if( (buttonState == LOW) && (ledState < 0) ) { 
    digitalWrite(lightOutput, HIGH);  // Turn on LED
    analogWrite(lightOutput, random(5,190));  //Make LED flash randomly
    myservo.writeMicroseconds(14765);  //spin servo slowly in the direction to wind the wire
    ledState = -ledState;           // inverse LED state
    lastDebounceTime = millis();    // reset debounce timer
   
   //BUTTON OFF
}
   else {                            // otherwise, the button is on, IE in a LOW state
    digitalWrite(lightOutput, LOW);  //turn LED off
    myservo.writeMicroseconds(1550);  //Keep servo at 1550, which is the off position 
                                      //for this particular servo in microseconds    
  } // close else statement
  } // close debounce if statment
  
   // 2nd button press to unwind rope
  if ((millis() - lastDebounceTime) > debounceDelay) {
    //BUTTON ON
  if( (buttonState == LOW) && (ledState > 0) ) {  
    digitalWrite(lightOutput, LOW);   // Keep leds off
    myservo.writeMicroseconds(1500);  // Unwind servo
    ledState = -ledState;             // reverse ledState
    lastDebounceTime = millis();      // reset debounce timer

  } // close if statement
  else {  

    //BUTTON OFF
    myservo.writeMicroseconds(1550);  // keep servo still
    digitalWrite(lightOutput, LOW);   // keep leds off
  
  } // close last else statement
    
  } // close debounce
  } // close void loop

Thank you in advance for any help anyone can offer.

-Will

Try this:

#include <Servo.h> // imported servo library

// Written by Will Dean, master electrician of the
// House Theatre of Chicago
// Show: The Crownless King.  
// Grenade code for Davey Boone's bluff
// September, 2013

Servo myservo;  // assign myservo variable from library Servo.h

// these don't change values, ever
#define PIN_BUTTON      2
#define PIN_SERVO       9
#define PIN_LED         13

#define STATE_IDLE      0
#define STATE_WINDING   1
#define STATE_UNWINDING 2

#define SERVO_CENTER    1550
#define SERVO_UNWIND    1500
#define SERVO_WIND      1475

#define DEBOUNCE_DELAY  400

// changing variables
uint8_t btnStateLast = HIGH;
uint8_t currentState = STATE_IDLE;

unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled

void setup()
{
    pinMode(PIN_LED, OUTPUT);  // setting LED pin to output the random values
    pinMode(PIN_BUTTON, INPUT);  // setting PIN_BUTTON pin to read Inputs
    digitalWrite(PIN_BUTTON, HIGH);  // Use the built in pullup resistor for button
    myservo.attach(PIN_SERVO);  // setting myservo to read from pin 9
}

bool handleButton()
{
    uint8_t btnStateCurrent = digitalRead(PIN_BUTTON);
    if (btnStateCurrent != btnStateLast && (millis() - lastDebounceTime) > DEBOUNCE_DELAY)
    {
        if (btnStateCurrent == LOW)
        {
            ++currentState;
            if (currentState > STATE_UNWINDING)
                currentState = STATE_WINDING;
        } else {
            currentState = STATE_IDLE;
        }

        btnStateLast = btnStateCurrent;
        lastDebounceTime = millis();

        return true;
    }

    return false;
}

void flickerLED()
{
    uint8_t value = 0;
    if (currentState == STATE_WINDING)
        value = random(5, 190);
    analogWrite(PIN_LED, value);
}

void setServo()
{
    switch(currentState)
    {
        case STATE_WINDING:
            myservo.writeMicroseconds(SERVO_WIND);
            break;
        case STATE_UNWINDING:
            myservo.writeMicroseconds(SERVO_UNWIND);
            break;
        default:
            myservo.writeMicroseconds(SERVO_CENTER);
            break;
    }
}

void loop() 
{
    if (handleButton())
        setServo();
    flickerLED();
}

Thank you for writing all of that! I don't know if you had a lot of that written or not, but I don't fully understand what's happening there. The button presses are working but the motor is still only spinning in one direction, and the LEDs aren't doing their random chase. I don't understand that code enough to make changes to it.

I'm going to run down to radio shaft and see if I can't find a hardware solution, but I'm definitely interested in other software solutions as well. It's so close, yet so far... :frowning:

Putting each { on a new line, and using Tools + Auto Format can really help improve the readability of your code.

Always using { and } after an if statement ensures that you KNOW what will happen if the statement is true. Absent them C, unlike some other languages, assumes that the body of the statement is the next single statement. Using { and } allows you to demonstrate that you have control.

You probably should be looking at the state change detection example, to see how to trigger an action when the switch BECOMES pressed, rather than IS pressed. I don't think your issue is debounce as much as it is one of assuming the BECOMES presses and IS pressed are the same thing.

PaulS:
Putting each { on a new line, and using Tools + Auto Format can really help improve the readability of your code.

OOH good to know about that! Thanks.

You probably should be looking at the state change detection example, to see how to trigger an action when the switch BECOMES pressed, rather than IS pressed. I don't think your issue is debounce as much as it is one of assuming the BECOMES presses and IS pressed are the same thing.

Yes, I suspected this was a possible issue. I think I had something like this in my code, but then deleted it thinking it was a debounce issue. Let me try to put this in and see where it takes me...

thanks!

PaulS:
You probably should be looking at the state change detection example, to see how to trigger an action when the switch BECOMES pressed, rather than IS pressed. I don't think your issue is debounce as much as it is one of assuming the BECOMES presses and IS pressed are the same thing.

Is there something I should be looking for? I have the basic state change example in my code, but it's still behaving the same way (flipping back and forth when the button becomes pressed).

should I be changing this line to reflect when the button becomes pressed vs when it is pressed? should it be analog read?

buttonState = digitalRead(buttonInput); // read the state of the switch into a local variable

// changing variables
int buttonState = 0;        // current reading from the button input pin (pullup resisto
int lastButtonState = -1;     // previous state of the button

...

Loop
   // BUTTON ON
    // compare buttonState to its previous state
      if ( (buttonState == LOW) && (lastButtonState > 0) ) { 
       
Do stuff

        lastDebounceTime = millis();    // reset debounce timer
        lastButtonState = -lastButtonState;

...

    // 2nd button press to unwind rope
    if ((millis() - lastDebounceTime) > debounceDelay) {
      //BUTTON ON
      if( (buttonState == LOW) && (lastButtonState < 0) ) {  

do stuff

        lastButtonState = -lastButtonState;
        lastDebounceTime = millis();      // reset debounce timer
}
}

Is there something I should be looking for?

The mistakes in your code, of course. If you need help, post it.

should I be changing this line to reflect when the button becomes pressed vs when it is pressed? should it be analog read?

No and no.

OK here it is :frowning: all button presses are still doing the same thing in the video (servo spinning back and forth at the debounceDelay rate, 400ms)

woops, still has analogRead...fixed

#include <Servo.h> // imported servo library

// Written by Will Dean, master electrician of the
// House Theatre of Chicago
// Show: The Crownless King.  
// Grenade code for Davey Boone's bluff
// September, 2013

Servo myservo;  // assign myservo variable from library Servo.h

// these don't change values, ever
const int buttonInput = 2;
const int lightOutput = 13;  //short end gets resistor = - side

// changing variables
int buttonState = 0;        // current reading from the button input pin (pullup resisto
int lastButtonState = -1;     // previous state of the button

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 400;   // the debounce time; increase if the output flickers


void setup() {

  pinMode(lightOutput, OUTPUT);  // setting LED pin to output the random values
  pinMode(buttonInput, INPUT);  // setting buttonInput pin to read Inputs
  digitalWrite(buttonInput, HIGH);  // Use the built in pullup resistor for button
  myservo.attach(9);  // setting myservo to read from pin 9
}

void loop() 
{
  buttonState = digitalRead(buttonInput);       // read the state of the switch into a local variable

  // check to see if you just pressed the button
  // (ie the input when from HIGH to LOW), and you've waited
  // long enough since the last press to ignore any noise:

  if ((millis() - lastDebounceTime) > debounceDelay) { // millis = system time
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // BUTTON ON
    // compare buttonState to its previous state
    if ( (buttonState == LOW) && (lastButtonState > 0) ) { 
      digitalWrite(lightOutput, HIGH);  // Turn on LED
      analogWrite(lightOutput, random(5,190));  //Make LED flash randomly
      myservo.writeMicroseconds(1570);  //spin servo slowly in the direction to wind the wire
      lastDebounceTime = millis();    // reset debounce timer
      lastButtonState = -lastButtonState;
      //BUTTON OFF
    }
    else {                            // otherwise, the button is on, IE in a LOW state
      digitalWrite(lightOutput, LOW);  //turn LED off
      myservo.writeMicroseconds(1550);  //Keep servo at 1550, which is the off position 
      //for this particular servo in microseconds    
    } // close else statement

  } // close debounce if statment

  // 2nd button press to unwind rope
  if ((millis() - lastDebounceTime) > debounceDelay) {
    //BUTTON ON
    if( (buttonState == LOW) && (lastButtonState < 0) ) {  
      digitalWrite(lightOutput, LOW);   // Keep leds off
      myservo.writeMicroseconds(1500);   // Unwind servo
      lastButtonState = -lastButtonState;
      lastDebounceTime = millis();      // reset debounce timer

    } // close if statement
    else {  

      //BUTTON OFF
      myservo.writeMicroseconds(1550);  // keep servo still
      digitalWrite(lightOutput, LOW);   // keep leds off

    } // close last else statement

  } // close debounce
} // close void loop

sourcefour:
Thank you for writing all of that! I don't know if you had a lot of that written or not, but I don't fully understand what's happening there. The button presses are working but the motor is still only spinning in one direction, and the LEDs aren't doing their random chase. I don't understand that code enough to make changes to it.

LOL, of course I don't have code like this laying around :slight_smile:

I've wired it up and found the bug(s). This code is tested and working:

#include <Servo.h> // imported servo library

// Written by Will Dean, master electrician of the
// House Theatre of Chicago
// Show: The Crownless King.  
// Grenade code for Davey Boone's bluff
// September, 2013

Servo myservo;  // assign myservo variable from library Servo.h

// these don't change values, ever
#define PIN_BUTTON      8
#define PIN_SERVO       9
#define PIN_LED         11

#define STATE_IDLE      0
#define STATE_WINDING   1
#define STATE_UNWINDING 2

#define SERVO_CENTER    1550
#define SERVO_UNWIND    1500
#define SERVO_WIND      14750

#define LOOP_DELAY      50
#define DEBOUNCE_DELAY  400

// changing variables
uint8_t btnStateLast = HIGH;
uint8_t currentState = STATE_IDLE;
uint8_t nextState = STATE_WINDING;

unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled

void setup()
{
    pinMode(PIN_BUTTON, INPUT);  // setting PIN_BUTTON pin to read Inputs
    digitalWrite(PIN_BUTTON, HIGH);  // Use the built in pullup resistor for button
    myservo.attach(PIN_SERVO);  // setting myservo to read from pin 9
}

bool handleButton()
{
    uint8_t btnStateCurrent = digitalRead(PIN_BUTTON);
    if (btnStateCurrent != btnStateLast && (millis() - lastDebounceTime) > DEBOUNCE_DELAY)
    {
        if (btnStateCurrent == LOW)
        {
            currentState = nextState++;
            if (nextState > STATE_UNWINDING)
                nextState = STATE_WINDING;
        } else {
            currentState = STATE_IDLE;
        }

        btnStateLast = btnStateCurrent;
        lastDebounceTime = millis();

        return true;
    }

    return false;
}

void flickerLED()
{
    int value = 0;
    if (currentState == STATE_WINDING)
        value = random(1, 255);
    analogWrite(PIN_LED, value);
}

void setServo()
{
    switch(currentState)
    {
        case STATE_WINDING:
            myservo.writeMicroseconds(SERVO_WIND);
            break;
        case STATE_UNWINDING:
            myservo.writeMicroseconds(SERVO_UNWIND);
            break;
        default:
            myservo.writeMicroseconds(SERVO_CENTER);
            break;
    }
}

void loop() 
{
    if (handleButton())
        setServo();
    flickerLED();
    delay(LOOP_DELAY);
}

Please use these exact pins:
#define PIN_BUTTON 8
#define PIN_SERVO 9
#define PIN_LED 11

The LED needs to be on a PWM pin in order to be able to flicker.
Again, this is tested and working.

I'm sure there are new concepts in this code, but I've kept all the functions small and the variable and function names as "english" as possible, hopefully making the code easier to understand.

Holy crap, it does work. Thank you! I will study this and learn from it. Can you tell me what you think I was missing? It seems to me like I had most of the legos there, but they just weren't put together properly.

I'm pouring through your code and trying to figure out what you did differently. I have grasped what you did here; it all makes sense to me. But I don't fully understand what was wrong with my code that caused it to stutter like it did.

Again, thank you. Is there a way you prefer I reference you in this code? I'd like to put it on my website: http://www.willdeandesigns.com/arduino-projects.html

sourcefour:
Holy crap, it does work. Thank you! I will study this and learn from it. Can you tell me what you think I was missing? It seems to me like I had most of the legos there, but they just weren't put together properly.

I think what might be the biggest possible misunderstanding in your code is the "2nd button press" part. The code in the "loop()" function is executed over and over again, many thousand times per second (usually). So the way you had your code laid-out, there is no concept of a "2nd button press" unless you count the number of button presses somehow.

Also, for the de-bouncing, you need to update the "lastDebounceTime" every time state changes, not just then the button is pressed (also on release).

Again, thank you. Is there a way you prefer I reference you in this code? I'd like to put it on my website: http://www.willdeandesigns.com/arduino-projects.html

"IronCreek Software"

Thanks!

To help your understanding (hopefully :slight_smile: ), here's a more annotated version:

//////////////////////////////////////////////////
//
// Grenade code for Davey Boone's bluff
// September, 2013
//
// Written by Will Dean, master electrician of the
// House Theatre of Chicago
//
// Show: The Crownless King.  
//
//////////////////////////////////////////////////

//////////////////////////////////////////////////
//               Libraries
//////////////////////////////////////////////////

#include <Servo.h>

//////////////////////////////////////////////////
//               Constants
//////////////////////////////////////////////////

// Pin assignments
#define PIN_BUTTON      8
#define PIN_SERVO       9
#define PIN_LED         11

// Program states
#define STATE_IDLE      0
#define STATE_WINDING   1
#define STATE_UNWINDING 2

// Servo direction control constants
#define SERVO_CENTER    1550
#define SERVO_UNWIND    1500
#define SERVO_WIND      14750

// Delays
#define LOOP_DELAY      50
#define DEBOUNCE_DELAY  400

//////////////////////////////////////////////////
//               Global Variables
//////////////////////////////////////////////////

// The servo control object
Servo myservo;

// Last button state.
// This is used for de-bouncing to only detect
// changes to the button state.
uint8_t btnStateLast = HIGH;

// The CURRENT state of the program.
// We can eithe be IDLE and do nothing at all or
// WINDING, where the fuse is pulled in and the
// LED flickers. Finally we may be UNWINDING, where
// the fuse is fed back out and the LED stays off.
uint8_t currentState = STATE_IDLE;

// This is the NEXT state of the progam.
// Once a button is pressed, that is the next state
// we're going to enter. This variable makes the 
// difference between WINDING and UNWINDING.
uint8_t nextState = STATE_WINDING;

// Last time the button state changed.
unsigned long lastDebounceTime = 0;

//////////////////////////////////////////////////
// 
// Initialized the ports and attaches the servo
// library to the output pin.
//
//////////////////////////////////////////////////
void setup()
{
    // Set button pin to output and enable pull-up
    pinMode(PIN_BUTTON, INPUT);
    digitalWrite(PIN_BUTTON, HIGH);

    // Attach servo to port
    myservo.attach(PIN_SERVO);
}

//////////////////////////////////////////////////
// 
// This function checks the button input and sets
// the program state as a result. The button inputs
// are debounced to avoid false toggles.
// 
// Returns "true" if the program state has changed
// of "false" if everything is still the same.
// 
//////////////////////////////////////////////////
bool handleButton()
{
    // Get the current button state
    uint8_t btnStateCurrent = digitalRead(PIN_BUTTON);

    // Check if the button state has changed and 
    // if enough time has elapsed since the last
    // button state change.
    if (btnStateCurrent != btnStateLast && (millis() - lastDebounceTime) > DEBOUNCE_DELAY)
    {
        if (btnStateCurrent == LOW)
        {
            // If the button has been pressed, we
            // switch from IDLE mode to the next
            // state (either WINDING, or UNWINDING).
            // "nextState" is also incremented in
            // process.
            currentState = nextState++;

            // Since we incremented "nextState"
            // we must now check if we still have
            // a valid state, if not, wrap back to
            // state "WINDING".
            if (nextState > STATE_UNWINDING)
                nextState = STATE_WINDING;

        } else {

            // If the button is not pressed,
            // go back to IDLE mode.
            currentState = STATE_IDLE;
        }

        // Remember the last button state and
        // the time of the change for de-bouncing.
        btnStateLast = btnStateCurrent;
        lastDebounceTime = millis();

        // Signal that something has changed
        return true;
    }

    // Button state has not changed or button
    // de-bounce time has not passed yet.
    return false;
}

//////////////////////////////////////////////////
// 
// Flickers the LED during the WINDING stage and
// keeps the LED off for all other states.
// 
//////////////////////////////////////////////////
void flickerLED()
{
    // Default value for the LED is 0 or "off"
    int value = 0;

    // If we're in WINDING state, assign a random
    // value to make the LED "flicker"
    if (currentState == STATE_WINDING)
        value = random(1, 255);

    // Assign the value to the pin
    analogWrite(PIN_LED, value);
}

//////////////////////////////////////////////////
// 
// Set the servo action based on the current state.
// In WINDING state, turn one way, in UNWIND state
// turn the other way. In IDLE state, simply center
// the servo.
//
//////////////////////////////////////////////////
void setServo()
{
    switch(currentState)
    {
        case STATE_WINDING:
            myservo.writeMicroseconds(SERVO_WIND);
            break;
        case STATE_UNWINDING:
            myservo.writeMicroseconds(SERVO_UNWIND);
            break;
        default:
            myservo.writeMicroseconds(SERVO_CENTER);
            break;
    }
}

//////////////////////////////////////////////////
// 
// The main application loop. This function gets
// executed many times per second.
//
//////////////////////////////////////////////////
void loop() 
{
    // Check if a button state has changed
    if (handleButton())
    {
        // If so, move the servo to it's new state
        setServo();
    }

    // Even if the button state didn't change (i.e.
    // it's still pressed), we need to flicker the
    // LED.
    flickerLED();

    // Small delay just so that the LED isn't
    // flickering too quickly.
    delay(LOOP_DELAY);
}

Btw. when you write comments in your program, point out non-obvious things. For example:

#include <Servo.h>    // Include Servo library

Comments like that are not helpful, in fact they are redundant and can become outdated and misleading even. Let the code do the talking as to what is done, use the comments more to explain why it's done that way.

Good luck with your production!
This seems to be a really cool project and I'm glad I could help!

Let the code do the talking as to what is done, use the comments more to explain why it's done that way.

In my opinion, the only useful comments are those that start in column 1.

// This code is going to... <--- If the ... is actually useful information, this comment is usefull
if(someCondition)
{
}

Comments that follow code on the same line are usually useless.

int2str:
To help your understanding (hopefully :slight_smile: ), here's a more annotated version:

//////////////////////////////////////////////////

//
// Grenade code for Davey Boone's bluff
// September, 2013
//
// Written by Will Dean, master electrician of the
// House Theatre of Chicago
//
// Show: The Crownless King. 
//
//////////////////////////////////////////////////

//////////////////////////////////////////////////
//               Libraries
//////////////////////////////////////////////////

#include <Servo.h>

//////////////////////////////////////////////////
//               Constants
//////////////////////////////////////////////////

// Pin assignments
#define PIN_BUTTON      8
#define PIN_SERVO       9
#define PIN_LED         11

// Program states
#define STATE_IDLE      0
#define STATE_WINDING   1
#define STATE_UNWINDING 2

// Servo direction control constants
#define SERVO_CENTER    1550
#define SERVO_UNWIND    1500
#define SERVO_WIND      14750

// Delays
#define LOOP_DELAY      50
#define DEBOUNCE_DELAY  400

//////////////////////////////////////////////////
//               Global Variables
//////////////////////////////////////////////////

// The servo control object
Servo myservo;

// Last button state.
// This is used for de-bouncing to only detect
// changes to the button state.
uint8_t btnStateLast = HIGH;

// The CURRENT state of the program.
// We can eithe be IDLE and do nothing at all or
// WINDING, where the fuse is pulled in and the
// LED flickers. Finally we may be UNWINDING, where
// the fuse is fed back out and the LED stays off.
uint8_t currentState = STATE_IDLE;

// This is the NEXT state of the progam.
// Once a button is pressed, that is the next state
// we're going to enter. This variable makes the
// difference between WINDING and UNWINDING.
uint8_t nextState = STATE_WINDING;

// Last time the button state changed.
unsigned long lastDebounceTime = 0;

//////////////////////////////////////////////////
//
// Initialized the ports and attaches the servo
// library to the output pin.
//
//////////////////////////////////////////////////
void setup()
{
    // Set button pin to output and enable pull-up
    pinMode(PIN_BUTTON, INPUT);
    digitalWrite(PIN_BUTTON, HIGH);

// Attach servo to port
    myservo.attach(PIN_SERVO);
}

//////////////////////////////////////////////////
//
// This function checks the button input and sets
// the program state as a result. The button inputs
// are debounced to avoid false toggles.
//
// Returns "true" if the program state has changed
// of "false" if everything is still the same.
//
//////////////////////////////////////////////////
bool handleButton()
{
    // Get the current button state
    uint8_t btnStateCurrent = digitalRead(PIN_BUTTON);

// Check if the button state has changed and
    // if enough time has elapsed since the last
    // button state change.
    if (btnStateCurrent != btnStateLast && (millis() - lastDebounceTime) > DEBOUNCE_DELAY)
    {
        if (btnStateCurrent == LOW)
        {
            // If the button has been pressed, we
            // switch from IDLE mode to the next
            // state (either WINDING, or UNWINDING).
            // "nextState" is also incremented in
            // process.
            currentState = nextState++;

// Since we incremented "nextState"
            // we must now check if we still have
            // a valid state, if not, wrap back to
            // state "WINDING".
            if (nextState > STATE_UNWINDING)
                nextState = STATE_WINDING;

} else {

// If the button is not pressed,
            // go back to IDLE mode.
            currentState = STATE_IDLE;
        }

// Remember the last button state and
        // the time of the change for de-bouncing.
        btnStateLast = btnStateCurrent;
        lastDebounceTime = millis();

// Signal that something has changed
        return true;
    }

// Button state has not changed or button
    // de-bounce time has not passed yet.
    return false;
}

//////////////////////////////////////////////////
//
// Flickers the LED during the WINDING stage and
// keeps the LED off for all other states.
//
//////////////////////////////////////////////////
void flickerLED()
{
    // Default value for the LED is 0 or "off"
    int value = 0;

// If we're in WINDING state, assign a random
    // value to make the LED "flicker"
    if (currentState == STATE_WINDING)
        value = random(1, 255);

// Assign the value to the pin
    analogWrite(PIN_LED, value);
}

//////////////////////////////////////////////////
//
// Set the servo action based on the current state.
// In WINDING state, turn one way, in UNWIND state
// turn the other way. In IDLE state, simply center
// the servo.
//
//////////////////////////////////////////////////
void setServo()
{
    switch(currentState)
    {
        case STATE_WINDING:
            myservo.writeMicroseconds(SERVO_WIND);
            break;
        case STATE_UNWINDING:
            myservo.writeMicroseconds(SERVO_UNWIND);
            break;
        default:
            myservo.writeMicroseconds(SERVO_CENTER);
            break;
    }
}

//////////////////////////////////////////////////
//
// The main application loop. This function gets
// executed many times per second.
//
//////////////////////////////////////////////////
void loop()
{
    // Check if a button state has changed
    if (handleButton())
    {
        // If so, move the servo to it's new state
        setServo();
    }

// Even if the button state didn't change (i.e.
    // it's still pressed), we need to flicker the
    // LED.
    flickerLED();

// Small delay just so that the LED isn't
    // flickering too quickly.
    delay(LOOP_DELAY);
}




Btw. when you write comments in your program, point out non-obvious things. For example:


#include <Servo.h>    // Include Servo library




Comments like that are not helpful, in fact they are redundant and can become outdated and misleading even. Let the code do the talking as to what is done, use the comments more to explain why it's done that way.

Good luck with your production!
This seems to be a really cool project and I'm glad I could help!

You are rocking my world here, thank you. Wish I could buy you a beer.

I'll post a video of it in action in the show, maybe tomorrow if they let me film it on my phone (it's press opening, so it might be too distracting), otherwise in a few weeks when they record the show for archival purposes.

video of the final product: Final Grenade fuse prop - YouTube

sourcefour:
video of the final product...

Well done!
What a smart and inventive idea for a play prop!
I would have never though of something like this in a million years. It's safe, re-usable and I'm sure looks great on stage. Well done!

Top tip for next time:
Instead of putting a full Arduino in there, a ATTiny85 can handle this project :slight_smile: