Pushbutton as an on/off switch

Hi, I'm still new to arduino and its programming language, I'm still trying to figure the ins and outs of the coding but I don't know if its something with the programming or the circuitry. I'm trying to create it like a button that turns on a LED when pushed(not held) and turns of when pushed(not when its let go). This should happen over and over again but all I've gotten was sometimes turns off sometimes dims the light instead.

byte redpin = 8;
byte buttonpin = 4;
byte state = 0;

void setup() {
  pinMode(redpin, OUTPUT);
  pinMode(buttonpin, INPUT_PULLUP);
}

void loop() 
{
  if(digitalRead(buttonpin) == LOW && state == 0){
    digitalWrite(redpin, HIGH);
    state = state + 1;
  }
  if(digitalRead(buttonpin) == LOW && state == 1){
    digitalWrite(redpin, LOW);
    state = state - 1;
  }
}

Any help that is given is greatly appreciated,

Thanks.

Welcome to the forum

Look carefully at the code and you will see that you are reading the state of the input twice (actually more) in very quick succession

Change the second if into an else if

Also, take a look at the StateChangeDetection example in the IDE

…and look into debouncing the switch

1 Like

Thanks everyone for your advice, UKHeliBob, I ended up basing my code around the StateChangeDectection example in the IDE and I probably will look into debouncing the switch lastchancename.

I am glad that you sorted it out

Yes, sooner or later.

The state change detection examlple I see has

    // Delay a little bit to avoid bouncing
    delay(50);
  }

buried within the state change detecting logic; an alternative simple and sometimes adequate place for such a delay is as the last step in the loop() function.

There, it means effectively all your buttons are debounced as they are sim,y not being read very often, 20 milliseconds is enough for most switches.

And it is the crude introduction to one debouncing method, which is to not look at a switch again if you've recently (within 20 ms, e.g.) read and acted upon it.

For which you will find millis() and the paradigmatic "blink without delay" coming across your radar screen.

a7

1 Like

99% of the time, all that is needed is to read the switches every 50ms using a non blocking 50ms TIMER.

thanks, I was going to buy a schmitt trigger to debounce because I thought it was the only method thanks for showing me another way

Here's a bit of code based on BWOD, which can be parked as the very first lines of the loop() function. There it will blink regularly and offer proof that your code is functioning, and that no part of it hogs the resource which is processor time, or attention if you will:

  //******       @LarryD heartbeatLED ™ 
  //is it time to toggle the heartbeat LED ?
  static unsigned long heartbeatTime;
  if (millis() - heartbeatTime >= 500)
  {
    //restart this TIMER
    heartbeatTime = millis();

    //toggle the LED
    digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == LOW ? HIGH : LOW);
  }

The 500 in there is milliseconds, and means the lamp flashes once a second, on for 0.5 seconds, off for the same.

Buttons can be debounced by looking at them slower than thousands of times a second; a straight-ahead adaptation of the heartbeat:

  //is it time to look at the buttons?
  static unsigned long buttonReadTime;
  if (millis() - buttonReadTime >= 20)
  {
    //restart this TIMER
    buttonReadTime = millis();

// here read all your buttons into variables, and use those variables subsequently, e.g.

    someButtonReading = digitalRead(someInputPin) == LOW;  // assuming input pulled up mode
  }

This fits well with a code structure that follows the IPO model, where all inputs are grabbed once per loop, or here once per loop as long as it is time to do.

I am contractually obligated to give a shout out to @paulpaulson, relentless promoter of the IPO model and how I finally learned a name for something I'd been doing for years. Read about it in the abstract here:

The IPO Model.

HTH

a7

1 Like

e pluribus unum...

  digitalWrite(heartbeatLED, !digitalRead(heartbeatLED); // invert present value

BWOD? I gots to know..what is BYOD?

:thinking:




  • Do you understand what is happening below ?
  //is it time to toggle the heartbeat LED ?
  static unsigned long heartbeatTime;
  if (millis() - heartbeatTime >= 500)
  {
    //restart this TIMER
    heartbeatTime = millis();

    //toggle the LED
    digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == LOW ? HIGH : LOW);
  }

Yeah...in my head I want to read this as "Bring Your Own Delay" but that doesn't work since it's "BWOD", which just occurred to me I actually misquoted from @alto777 .
I'll see myself out of this thread now... :joy:

i understand most of that except the whole LOW ? HIGH : LOW thing can you elaborate on that further?

See post #10

  • The above is saying:
    When the level on the heartbeatLED pin is LOW, we will make the heartbeatLED HIGH.
    When the level on the heartbeatLED pin is HIGH, we will make the heartbeatLED LOW.

  • Hence we toggle the LED.




https://cplusplus.com/forum/articles/14631/

1 Like

Yes. and although @xfpd's version works, and indeed I use what it exploits myself, Ive been trying to get into the habit of respecting the stupid API that says there are these concepts HIGH and LOW that are not integers or booleans or anything, srsly check that out.

It has been the subject of interminable and inconclusive discussion, TBH I've just given up.

The odd thing that escapes the innocent is that digitalRead() can be used on an output pin, and will return the value HIGH or LOW that was last written by digitalWrite() on that pin, so the pin itself is the thing that is "remembering" the state.

I use it but don't much see that saving a variable is worth doing something that depends on how those two functions work.

  static unsigned long heartbeatTime;
  static bool ledState;
  if (millis() - heartbeatTime >= 500)
  {
    //restart this TIMER
    heartbeatTime = millis();

    //toggle the LED state true <-> false
    ledState = !ledState;
    // and publish it
    digitalWrite(heartbeatLED, ledState ? HIGH : LOW);
  }

again with the obtuse but correct adherence to the stupid API.

a7

3 Likes

There is a little tiny problem with reading a button pin once to get the state. You don't know if you are reading the button as it is bouncing! The chance is tiny and half of that tiny interval you still get a correct state but for about 2 ms after the button changes state it keep changing hence "bounce".

That is why the better software debouncers watch for change + stability however they do it.

I wrote a series of evolving debouncers since 2011 to shorten the stability time (when reads are all the same new state) and then use less RAM on time-holders and read less often and use less code over the years and hit on using regular reads closer together than what oscilloscopes show as 2 ms or more bounce period depending on how dirty the button is -- to catch the bouncing itself and then a very short but longer than the longest bounce stable time after that without a lot of logic.

I hit on reading the input every 1/2 to 1 ms and keeping a running history of the last 8 reads as bits in a pin-history byte. It changes with every new read and when the last detected bounce is in the oldest bit and the other 7 bits are all same opposite state, the history value shows press+stable or release+stable with every other vale being held-down or left-up or bouncing and not stable yet.
With the pin mode INPUT_PULLUP; binary values give the picture.
0b00000000 is button held down
0b11111111 is button left up
0b10000000 is button pressed+stable for 7 reads
0b01111111 is button released+stable for 7 reads

The history can be logically masked to shorten the stability time which does use one extra line of code in the read function but worth it if you want a quicker response to change detect.

Last week I saw a way to use less code by reading every 1024 micros using the fact that millis() changes every 1024 us... heheh.

I'll write and post the pin history read function in a bit (haven't yet) as writing an example takes longer than I want to keep the edit window open. Yinz can decide how you want to shoot it down if you see this post first. It takes me a while to code now due to eyesight problem and my dinner is ready but I'll Be Back!

It doesn't matter, at least not with pushbuttons.

A pushbutton won't read closed unless it is, or is on its way to being, fully closed and stable.

A pushbutton won't read open unless it is, or is on its way to being, fully open and stable.

If your pushbuttons act differently, get better pushbuttons.

Deglitching a digital signal is a different matter, where a signal may spuriously read an opposite state, and there is a desire to ride across such excursions. Here we need to know how long such a glitch might be, and code accordianly.

For regular push buttons, looking every 20 milliseconds or for crappy switches sometimes somewhat longer between, or ignoring a pushbutton for a similar time after a state change has been observed, is entirely adequate.

a7

1 Like

If the pin is read While It Is Bouncing, the state is effectively random.
This should happen almost never but is possible.
It makes little difference anyway if you read the pin often.

I would also like anyone to note that while they can read the state every so often that they do also need to compare that state to a saved previous state while pin state history includes previous states though once change detection is used, history should be changed to reflect that by clearing if press detected or setting to max if release detected.
In addition, history detects stable pressed or released states without need to compare current to previous states.

Prior to saving extended pin state history I had been combining read and previos read and bounce into a 3 bit status byte just to save keeping prev read and comparing to current state, but that was prior to 2018. Thanks for reminding me of my previous time and thought behind what I do now!