simple soft-button issue.

I'm sorry if this is a little stupid, I'm totally new to this (I got the board yesterday ;D )
I'm trying to code a "soft button". Press once the circuit turns on (a LED in this case) press another time and it turns off... It actually works, but only partially. Most of the times when I hit the button the LED will light up until I release the button. If I press many times in a row it will however produce the desired effect and turn on or off the LED.

int flashLED    = 11;
int statusLED   = 13;
int actionBTN   = 12;
int btnValue    = 0;
int softValue   = 0;
 
void setup(void) {
  // initialize inputs/outputs
  pinMode(flashLED, OUTPUT);
  pinMode(statusLED, OUTPUT);
  pinMode(actionBTN, INPUT);
  digitalWrite(statusLED, HIGH); // I just want to know when the AVR is done booting
}
 
void loop(void) {
  btnValue = digitalRead(actionBTN);

  if (btnValue == LOW) {
    if (softValue == 0) {
      softValue = 1;
    } else {
      softValue = 0;
    }
  }
 
  if (softValue == 1) {
    digitalWrite(flashLED, HIGH);
  } else {
    digitalWrite(flashLED, LOW);
  }
}

Well, adding a bit of delay at the bottom of the (btnValue == LOW) statement seems to help. It works nicely with a "static" LED. If I use a flashing LED though, it will turn it on, but the problem is the same when trying to turn it off.

When I read your code literally, it looks like you are looping as fast as possible, polling digital input pin 12. If that pin is LOW, you are rapidly toggling the output pin 11 HIGH/LOW. In fact, it is probably so rapid that it appears to be on all the time. Two things occur to me:

  1. Instead of toggling every time you see pin 12 low, you probably want to toggle only when it goes from high to low.
  2. Even that probably won't work right, because real mechanical switches have a tendency to produce noise for a millisecond or so while they are transitioning between open and closed, or closed and open. So you may want to add a delay(1) on each loop, notice when you are transitioning from input pin 12 being high to low, and then start a countdown of maybe 50 loops (or 0.05 seconds). Only when pin 12 has been low for 0.05 seconds, immediately after having just been high, should you toggle the output state.

Oh, and instead of doing this:

if (softValue == 0) {
softValue = 1;
} else {
softValue = 0;
}

you can do this, which will do the same thing:

softvalue = !softvalue;

Likewise, because HIGH==1 and LOW==0 (see lib\targets\arduino\wiring.h), you can replace:

if (softValue == 1) {
digitalWrite(flashLED, HIGH);
} else {
digitalWrite(flashLED, LOW);
}

with:

digitalWrite (flashLed, softValue);

I'm not sure if my earlier suggestion is phrased in an understandable way. Feel free to post again with questions and I'll do my best to help!

  • Don

Thanks for the pointers! I've been able to make this work. The code was refined. I did not have to take the noise into account with the switch and code I'm using (the switch is the one included on the SparkFun protoshield). The only thing that could still be integrated (noise excluded) is soft debounce, as it's now tracking changes of LOW to HIGH state. I'll have to look into it, but I don't think it would be so hard to do. I'll post an update when I do. Here is what I came up with.

int statusLED  = 13;
int flashLED   = 11;
int actionBtn  = 12;
int btnVal     = 0;
int softVal    = 0;
int lastBtnVal = HIGH;

void setup () {
  pinMode(statusLED, OUTPUT);
  digitalWrite(statusLED, HIGH);
 
  pinMode(flashLED, OUTPUT);
  pinMode(actionBtn, INPUT);
  
  beginSerial(9600); // Connect to the serial port, used for debugging
}

void loop() {
  btnVal = digitalRead(actionBtn);
  if (btnVal == LOW && lastBtnVal == HIGH) {
     lastBtnVal = LOW;
     Serial.println("Going low");
  }
  
  if (btnVal == HIGH && lastBtnVal == LOW) {
    softVal = !softVal;
    lastBtnVal = HIGH;
    Serial.println("Going high");
  }
  
  digitalWrite(flashLED, softVal);
}

Hopefully this will help others too :slight_smile:

I got around implementing soft debounce and figured I could post the code, maybe others will find it useful, maybe others will be able to tell me if I did anything wrong. It works very nicely as far as I can see.

int statusLED  = 13;
int flashLED   = 11;
int actionBtn  = 12;
int btnVal     = 0;
int softVal    = 0;
int bounceTime = 0;
int lastBtnVal = HIGH;

void setup () {
  pinMode(statusLED, OUTPUT);
  digitalWrite(statusLED, HIGH);
 
  pinMode(flashLED, OUTPUT);
  pinMode(actionBtn, INPUT);
  
  Serial.begin(9600);        // connect to the serial port
  Serial.println("Initialized");
}

void loop() {
  btnVal = digitalRead(actionBtn);
  if (btnVal == LOW && lastBtnVal == HIGH && bounceTime != -1) {
     lastBtnVal = LOW;
     Serial.println("Going low");
  }
  
  if (bounceTime == -1 && btnVal == HIGH) {
    bounceTime = 0;
  }
  
  if (btnVal == LOW && lastBtnVal == LOW) {
    bounceTime++;
    if (bounceTime >= 50) {
      bounceTime = -1;
      btnVal = HIGH;
      Serial.println("Action button debounced!");
    }
  }
  
  if (btnVal == HIGH && lastBtnVal == LOW) {
    softVal = !softVal;
    lastBtnVal = HIGH;
    Serial.print("Going high with softVal to: ");
    Serial.println(softVal);
  }
  
  digitalWrite(flashLED, softVal);
}

Here's a little video of the result, including a pulsating LED without using delay()

/* Software Pulsating LED using PWM
 * --------------------------------
 *
 * This example does not use delay() as to not block the cpu from processing other stuff
 *   the down side is it might jerk off when stuff interrupts the cpu.
 *
 * (cleft) 2005 by Matthieu Lalonde
 * <http://smurfturf.net>
 * <mailto:mlalonde(at)smurfturf(dot)net>
 *
 */
// Pins definition
#define statusLED 13
#define actionBtn 12         // The input button
#define protoLED  11         // The output led

/* ** Program variables ** */
// Button variables
int btnVal     = 0;           // Used to store the button's value on cycle
int softVal    = 0;           // The software value of the button
int bounceInterval = 50;      // bounce interval in cycles
int bounceTime = 0;           // Bounce cycle counts
int lastBtnVal = HIGH;        // Last cycle's button value
// Pulsation variables
long previousMillis  = 0;      // Will store last time LED was updated
int interval         = 10;     // interval at which to blink (milliseconds)
int i                = 0;      // Cycle counter for the pulsating effect
int valLED           = 0;      // Stores the current PWM value for the LED
int stopNextRun      = 0;
/* *********************** */

void setup () {
  pinMode(protoLED, OUTPUT);
  pinMode(actionBtn, INPUT);

  Serial.begin(9600);
  reportAVRState(3, 1);
}

void loop() {
  btnVal = digitalRead(actionBtn);

  if (btnVal) { 
    readRemoteSoftVal(); 
  }

  if (btnVal == LOW && lastBtnVal == HIGH && bounceTime != -1) {
    lastBtnVal = LOW;
    Serial.println("Going low");
  }

  if (bounceTime == -1 && btnVal == HIGH) {
    bounceTime = 0;
  }

  if (btnVal == LOW && lastBtnVal == LOW) {
    bounceTime++;
    if (bounceTime >= bounceInterval) {
      bounceTime = -1;
      btnVal = HIGH;
      Serial.println("Action button debounced!");
    }
  }

  if (btnVal == HIGH && lastBtnVal == LOW) {
    softVal = !softVal;
    lastBtnVal = HIGH;
    Serial.print("Going high with softVal to: ");
    Serial.println(softVal);
  }

  // We want the LED to be off!
  if (!softVal) {
    digitalWrite(protoLED, softVal);
    previousMillis = 0;
    valLED = 0;
    i = 0;
  } 
  // Pulsate that shiny thing!
  else {
    // check to see if it's time to blink the LED; that is, is the difference
    // between the current time and last time we blinked the LED bigger than
    // the interval at which we want to blink the LED.
    if (millis() - previousMillis > interval) {
      previousMillis = millis();   // remember the last time we blinked the LED

      i++;
      // Pulsating up
      if (i < 175) {
        interval = 15;
        valLED = i;
      } // Let's light it up for a bit more time in the middle
      else if (i == 175) {
        valLED = 175;
        interval = 20;
      } // Pulsating down
      else if (i < 350) {
        interval = 10;
        valLED = (175 - (i - 175));
      } // The led is off, the loop restarts
      else {
        i = 0;
        valLED = 0;
        interval = 20;
      }

      analogWrite(protoLED, valLED);
    }
  }
}

void readRemoteSoftVal( void ) {
  int incomingByte = 0;

  // send data only when you receive data:
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();

    if (incomingByte == 49) {
      btnVal&= LOW;
    }
  }

  btnVal&= HIGH;
}

// Reports the state of the controller via pin 13's led and serial
void reportAVRState(int howManyTimes, int leaveOn) {
  int i;

  pinMode(statusLED, OUTPUT);

  for (i=0; i< howManyTimes; i++) {
    digitalWrite(statusLED, HIGH);
    delay(200);
    digitalWrite(statusLED, LOW);
    delay(200);  
  }

  if (leaveOn) { 
    digitalWrite(statusLED, HIGH); 
  }

  Serial.println("AVR Initialized");
}