Code only makes light very dim instead of turning off and on.

Hi, I’m trying to turn an LED on with one click of a button, and off with the same click of the same button. However, with my full code, the light gets very very dim and doesn’t turn ON or OFF as I would like. If I remove the last if-statement in the loop, the light will turn on just fine, but then I’d also like to turn it off with the same click of the button. Any ideas, hopefully keeping it as close to the original code as possible?

int led = 12;
int button = 2;

void setup() {
  Serial.begin(9600);
  pinMode(led, OUTPUT);
  pinMode (button, INPUT);
}

int on = 0;
int buttonPressed = 0;
void loop() {
  buttonPressed = digitalRead(button);
  Serial.println(buttonPressed);
  
  if (buttonPressed && digitalRead(led) == LOW) {
    digitalWrite(led, HIGH);
  }
  if (buttonPressed && digitalRead(led) == HIGH) { // < Deleting this if-statement makes the light turn
    digitalWrite(led, LOW);                                     // on just fine. But then I'd like to turn it off again 
  }                                                                      // with the same button.
}
  1. Is the button wired correctly? That is, is there a pulldown resistor in place to ensure that the button pin has a known state when it is not pressed? If not, the pin will be floating when the button is not pressed, and will have a random state - they often rapidly oscillate between states (in response to ambient electromagnetic noise), leading the the button appearing to be getting pressed rapidly and continuously when not pressed). The usual way to connect a button is to wire the button so pressing it connects it to ground, and set the pin mode to INPUT_PULLUP to use the internal pullup - in this configuration no external resistor is required, and the button will read HIGH when not pressed and LOW when pressed.

  2. Your code will turn the LED on very briefly and then immediately turn it off again. That's not what you want.

  3. You also need to debounce the button. There are a lot of guides here, there and everywhere about various schemes for debouncing buttons, so I'm not going to belabor the process here - the general point is, when a button is pressed, the contact will bounce, leading to it making and breaking the connection a few times in rapid succession before settling in the appropriate state. There are many similar approaches - the basic idea is that you monitor the pin, and only after it has settled in one state for a given length of time do you decide that it has changed states and act on it.

So what I think you want:

  • A new global variable to store the state that you want the LED to have.
  • A debounce routine in loop to determine when the button is actually pressed and avoid reacting multiple times due to contact bounce. When it is, toggle that global variable, and write to the LED.

@DrAzzy Thanks for your reply. The button is grounded through a 220 ohm resistor. I also don’t think the button is bouncing, but I’ll look into it. I think that the program runs so fast, that it’s instantly capturing the “toggle” and switching it on and off really fast, which is why I get the dimming effect. Almost like a manual PWM. Any way to negate that and make it trigger only once per push?

Your logic is flawed. If buttonPressed is true and the LED pin reads LOW you set it to HIGH. Then you check whether it is HIGH and if so write it LOW.

You might try:

  if ( buttonPressed ) {

    if ( digitalRead(led) == LOW) {
      digitalWrite(led, HIGH);
    } else
      digitalWrite(led, LOW);
  }

or more succinctly

 if ( buttonPressed ) digitalWrite(led, !digitalRead(led));

You probably do need to debounce the button, try adding a slight delay if it seems unstable.

Blue eyes, your code wont work, as it will repeatedly turn the code on and off while button is pressed.

You need to track the state of the button so you only do something when it becomes pressed.

This is how I would write it to include button state change detection (only act when the button is first pressed or released) and debouncing (don’t react to glitches immediately after a button press or release).

const byte LEDPin = 12;
const byte ButtonPin = 2;


const unsigned long DebounceTime = 30;


void setup()
{
  Serial.begin(115200);
  pinMode(LEDPin, OUTPUT);
  pinMode (ButtonPin, INPUT_PULLUP);  // Button between Pin and Ground
}


void loop()
{
  unsigned long currentTime = millis();
  boolean buttonPressed = digitalRead(ButtonPin) == LOW;  // Active LOW
  static boolean buttonAlreadyPressed = false;
  static unsigned long buttonStateChangeTime = 0;


  // Serial.println(buttonPressed ? "Button Is Pressed" : "Button is NOT pressed");


  // Check for button change and do debounce
  if (buttonPressed != buttonAlreadyPressed &&
      currentTime -  buttonStateChangeTime > DebounceTime)
  {
    // Button state has changed
    buttonStateChangeTime = currentTime;
    buttonAlreadyPressed = buttonPressed;
    
    if (buttonPressed)
    {
      // Button was just pressed
      Serial.println("Button Was Just Pressed");

      digitalWrite(LEDPin, !digitalRead(LEDPin)); // Toggle the LED
    }
    else
    {
      // Button was just released
      Serial.println("Button Was Just Released");

      // (Put code here if you want something to happen on button release)
    }
  }
}

The reason that the LED is dim is that it is being rapidly switched on and off.

Don't just check for the button being pressed, check for it changing from not pressed to pressed. To do this, you will have to remember the previous state of the button in a global variable.

PaulMurrayCbr:
To do this, you will have to remember the previous state of the button in a global variable.

…or ‘static’ local variable. (Both retain value across function calls, unlike regular local variables.)