Using a Momentary Switch as a toggle?

Hi,

I've read alot of threads on this topic, but I keep getting stuck at a key point; the loop. I understand that I'll be flicking a boolean between true and false based on the button being pressed at any point in time; however, I can't seem to find why the edge case example isn't working. This is what the code for said example looks like:

const int buttonPin = 2;
int buttonState = 0;
int currentMode = 0;
int lastState = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(buttonPin, INPUT);
  pinMode(13, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastState) {
    if (buttonState == LOW) {
      digitalWrite(13, HIGH);
      Serial.println("on");
      currentMode++;
      Serial.print("number of button pushes:  ");
      Serial.println(currentMode);
    }
    else {
      currentMode++;
      Serial.println("off");
      digitalWrite(13, LOW);
    }
    delay(50);
  }
  lastState = buttonState;
}

However this just runs forever ticking the numbers up. The codes in this thread also seem to be broken; the first only reads values when the switch is pulled off the Arduino, and the second doesn't do anything at all.

Can anyone help me figure out how to use such a switch as a toggle in laymans terms?

Edit: I've drawn up this, however this doesn't seem to be working. Can anyone help me identify why? Is it a logic error?

bool pinOn = true;
const int buttonPin = 2;
int lastState = 0;
int currentState = 0;
int state = 0;


void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);

}

void loop() {
  currentState = digitalRead(buttonPin);
  if (currentState == HIGH && currentState != lastState) {
    if (state == 0) {
      Serial.println("Button Pressed");
      state = 1;
    }
    else {
      Serial.println("Button Depressed");
      state = 0;
    }
    lastState = 1;
  }
  if (currentState == LOW && currentState != lastState) {
    Serial.println("Button not pressed");
    lastState = currentState;
  }
}

You declare the button pin input, so you use external pull up or down resistors?

And you are not toggling, you just set the output HIGH when a button becomes pushed (assuming active LOW) and LOW when the buttons becomes released. And you increment currentMode both when the button becomes pressed an when it becomes released.

Updated OP as I read similiar comments in another thread; would you please be able to read the new code at bottom of OP?

Now you're just mixing and matching state of the led and the button... The first sketch was a better start. Just open that and look at my comments. And hint, start answering questions :wink:

const int buttonPin = 2;
int buttonState = 0;
int currentMode = 0;
int lastState = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(buttonPin, INPUT);
  pinMode(13, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  buttonState = digitalRead(buttonPin); // current state of button
  if (buttonState != lastState) { // if the state differs
    if (buttonState == HIGH) { // if the button is pushed
      if (currentMode == 0) { // and we are not already in "on" mode
        digitalWrite(13, HIGH);
        Serial.println("on");
        currentMode = 1; // turn us into "on" mode
      }
    }
    else { // button is not pressed but was in last loop
      if (currentMode == 1) { // if we're also in "on" mode
        Serial.println("off");
        digitalWrite(13, LOW);
        currentMode = 0; // take us out
      }

    }
    delay(50); // debounce thing
  }
  lastState = buttonState; // update last state
}

This doesn't work either; the numbers keep climbing to infinity. I feel like I've made it toggle now using your advice; is there anything obviously wrong here? I've put comments as to what I think should be happening, but I'm fairly new to programming in general and not 100% on it all.

septillion:
You declare the button pin input, so you use external pull up or down resistors?

Made it bold, can you now spot the important question?

And almost, but why still do stuff when you release the button? Or as you say it "button is not pressed but was in last loop". Don't you only want to toggle when the button became pressed?

Alright, I was not using external resistors (Just read up on the internal pull up resistors). I also realise what you mean by doing stuff when button isn't pushed; I don't know why I was writing it that way, but I think this should do what I want. I'm currently away from Arduino, but does this look logically correct to you (There might be some syntax mistakes simply because I typed this on mobile, but just want to know if I'm going in the right direction logic wise). Thank you!

const int buttonPin = 2;
int buttonState = 0;
int currentMode = 0;
int lastState = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(13, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  buttonState = digitalRead(buttonPin); // current state of button
  if (buttonState != lastState) { // if the state differs
    if (buttonState == HIGH) { // if the button is pushed
      if (currentMode == 0) { // and we are not already in "on" mode
        digitalWrite(13, HIGH);
        Serial.println("on");
        currentMode = 1; // turn us into "on" mode
      }
      else if (currentMode == 1) {
        digitalWrite(13, LOW);
        currentMode = 0; // take us out
      }
    }
    else { // button is not pressed but was in last loop
      Serial.println("off");

    }
    delay(50); // debounce thing
  }
  lastState = buttonState; // update last state
}

Besides the print of “off” being in a weird spot it will I think work. Although it will toggle when you release the button :wink: Remember, it’s pulled up by default so low when the button is pressed.

But two tips now you’re this far aka understand it:

You can just read the state of the led instead of having to store it. That’s why I like to add

inline void digitalToggle(byte pin){
  digitalWrite(pin, !digitalRead(pin));
}

To a lot of projects :slight_smile:

Now you know what state change detection is and how to implement it it’s easier for the future to use a library to do all that including variables etc. I like to use Bounce2.

Then it would be as clear as:

#include <Bounce2.h>

const byte ButtonPin = 2;
const byte LedPin = 13;

Bounce button;

void setup() {
  // put your setup code here, to run once:
  button.attach(ButtonPin, INPUT_PULLUP);
  pinMode(LedPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  button.update();
  if(button.fell()){
    digitalToggle(LedPin);
    Serial.println(digitalRead(LedPin) ? F("on") : F("off"));
  }
}

inline void digitalToggle(byte pin){
  digitalWrite(pin, !digitalRead(pin));
}

Thank you so much for the help with this; I appreciate it immensely. Two quick last questions;

  1. The above code is toggling when the button is turned off because it should be set to LOW correct (due to the pull up, should of realised)

  2. Is Bounce2 included in the IDE? That looks much more beautiful and streamlined compared to this!

Thank you once again!

  1. Yep, after you detect a change of state check to see if it's LOW (aka pressed) to see if the button became pressed. Now you check if it became released :wink:

  2. Nope, GitHub - thomasfredericks/Bounce2: Debouncing library for Arduino and Wiring

Hi again,

I seem to be running into a physical issue; the PULL_UP leaves its default state as 1, however pressing the button doesn't actually change it's state. This is the button I'm using; it's a normally open button; would I need a pull DOWN resistor for this button to work with the above code (Switching the states from LOW to HIGH respectively)?

" it's a normally open button"

Normally closed?

.

Rip, would I still need a pull down resistor for this button to work properly? Or is the reason its not changing states potentially due to faulty connections/wires?

You need proper levels for change in state to work.
Your switch can be used, logic will be reversed.

Some examples:

.

The current button is wired as following:

5V Power connected to an External Pull Up resistor (resistor before the button), this is then connected to the button, which is then connected on the other side to pin 2 on the Uno. How would I connect it to ground if theres only 2 pins on the button?

So your are wired similarly to S2 in the diagram.
If so, what is your concern?

.

larryd:
So your are wired similarly to S2 in the diagram.
If so, what is your concern?

.

When the button is pressed at any time the state of the digitalRead isn't changing (it stays at 1/HIGH regardless of the press). I'm wondering why this might be the case.

I assume you are wired as S2.
Remove the switch from the cct., check it with a DVM.
It should read, zero ohms released, infinity when pressed.

.

Hey,

So I tested it with a DVM and found tha the resistance doesn't change when the button is pressed, however, the current does. Can I use this current change to make the button state change?

". . . however, the current does. "
This makes no sense.

Are you sure you are using the DVM properly when measuring its resistance (switch not connected to the rest of the circuit.)

If there is no resistance change from pushed to not pushed the switch is bad.

.