Need Help: Initiating and terminating a program with the press of a pushbutton

Hi, first time poster.
I'm trying to write a program that when a push button is pressed, a series of tasks gets done (reading sensors, performing calculations, actuating motors etc). Then when the same push button is pressed again, all activities are stopped and the device turns off.
I wrote the following program using a push button and LDR first (with 2 LEDs for debugging purposes so I can observe the serial o/p of the sensor as I toggle the push button).
When I first ran the code on an online simulation software (Tinkercad), it worked fine but now it takes several clicks of the button to turn the LDR reading ON and OFF. Its behaving like a bouncing button eventhough I've included the debounce function so I am wondering if its an issue with my code, software or something else altogether. (I'm planning to include separate functions for the remaining sensors and motors under the sensor reading but trying to make this part work reliably first).
Any help and advice will be greatly appreciated!

Code:

const int BUTTON=2;
const int LED=9; //blue
const int LED2=10;//green
const int LDR= A0;
boolean readLDR=false; //not reading by default
  
boolean previousBstate= LOW;  //storing previous button state as low
boolean currentBstate=LOW; //storing current button state as low
boolean LedOn= true; //storing LED ON state as true (when code is run, LED is off and turns on when button is pushed)
boolean Ledon2= false; //greeN LED on soon as code is run, without pushing button
int LDRval=0; //val to store LDR

void setup()
{ Serial.begin(9600);
  pinMode(LED, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(BUTTON, INPUT);
  pinMode(LDR, INPUT);
}

//---------button debounce-----------------------------------------||
boolean debounce(boolean previous)
{boolean current = digitalRead(BUTTON);
 if (previous != current)
 {delay(5);
  current = digitalRead(BUTTON);
  return current;
 }
}
//----------------------------------------------------------------||

void loop()
{ //For turning on LED when BUTTON is pressed
  currentBstate = debounce(previousBstate); 
  if (previousBstate == LOW && currentBstate == HIGH) 
  {LedOn = !LedOn; //off to ON
   Ledon2= !Ledon2;// on to OFF 
   readLDR = !readLDR; //toggle reading the LDR (off to ON)
   }
  
  previousBstate = currentBstate; //reset button value
  digitalWrite(LED, LedOn); //change the LED state
  digitalWrite(LED2, Ledon2); //change the LED state
  
  if (readLDR)
  {LDRval= analogRead(LDR);
   Serial.println(LDRval);
   delay(500);  }
}

Circuit:
image

What does your function return if previous == current?

consider

const byte pinLed = LED_BUILTIN;

const byte pinBut = A1;
byte butLst;

unsigned long msec;

// -----------------------------------------------------------------------------
int
chkButton (void)
{
    byte but = digitalRead (pinBut);
    if (butLst != but)  {
        butLst = but;
        delay (10);         // debounce

        if (LOW == but)      // press
            return 1;
    }

    return 0;
}

// -----------------------------------------------------------------------------
#define Period  500
void
doSomething (void)
{
    static unsigned long msecLst;

    if ( (msec - msecLst) > Period)  {
        msecLst = msec;
        digitalWrite (pinLed, ! digitalRead (pinLed));
    }
}

// -----------------------------------------------------------------------------
void
loop ()
{
    static bool run = false;

    msec = millis ();

    if (run)
        doSomething ();

    if (chkButton ())
        run = !run;
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    pinMode (pinLed, OUTPUT);
    pinMode (pinBut, INPUT_PULLUP);
    butLst = digitalRead (pinBut);
}

I made previous not equal to current ( previous != current) so that I could detect a change in button state ( if its switched on/bouncing), then wait for 5ms to read the value again (once its done bouncing) and then return the actual, current button state. :slightly_smiling_face:

Thank you, I should have mentioned that I'm new to coding and Arduino so I'm a bit confused with some parts of your code :exploding_head:
If you could explain these parts, it would really help:

I'm not sure what this means.Is it defining a time frame of 500ms?

Not sure what is happening here either: :thinking:

And what does return 0 and return 1 do?
Also , do we need to make a function "static", are there specific cases where we should do so?

I asked a very specific question, which you have managed not to answer.

Oh, I misunderstood. If previous==current then it would compare the button states to determine if the button has not been pressed (since previous button state is set to LOW) and if so will return the current button state as not pressed (LOW). Is that right? :thinking:

The right answer is "I don't know what It returns in that case".

I'd rather hope that the compiler warned you of that fact.

1 Like

#define is a c preprocessor directive which are applied before compilation.

in this case, anywhere "Period" is used in the program, it will be replaced by 500.

it's common practice to Capitalize constant and only Capitalize constants

functions can return a value. chkButton returns an "int", doSomething returns nothing (a void).

in loop() chkButton() is the expression inside a conditional (the if ()). if chkButton() return 0, same as false, the condition is not execute. otherwise it is executed and run is toggled

a static variable inside a function maintains it's value between function calls. i could have defined it globally, but it is only used inside the function and can only be referenced inside the function it is defined in

1 Like

Thank you so much! I learnt a lot, will try this out now and use these concepts to make my code more concise & eloquent in the future :smiley:

I tried this code and added an extra ReadLDR() function under your doSomething(); within the if(run) statement and it works perfectly (the in built LED blinks every 500ms & the LDR is read when button is pushed ) so thank you so much!

Just one more question- I didn't change the push button wiring ( kept the pull-down resistor between button pin and ground to give it an initial LOW state when unpressed) but in the code the pull-up resistor has been used which would give it an initial HIGH state when unpressed so I'm a bit confused as to how its working this way? :thinking:

When I removed my pull-down resistor or changed to a pull-up res, the LED didn't respond to the button pushes as it did with the original wiring .

i wouldn't expect it to work w/o the pull-down if the switch is wired between the pin and +5V

the internal pull-up resistor is 50-150k. a smaller pull-down resistor would make the input closer to ground than the mid-point and because it works for you must be below the switching threshold of the pin. a 10k pull-down would make the input ~0.8V or less

I'm afraid I don't understand this part, I was under the impression that we had to use either a pull-down resistor or a pull-up when wiring up a push button. I didn't know we could/should use both. If we use both the 50-150k internal pull-up and the 10k pull down as in the diagram c) below, will that hardwire the button to be logic high by default (when unpressed) ? Thank you for your time.

No. Diagram (c) is wrong - it's always high. Without +5V will work like (b).

shouldn't

as i already explained, they create a voltage divider which results in no more than ~0.8V at the pin which is less than the switching threshold so it looks like a LOW

Oh okay, got it. Thank you!

Thank you for explaining it again, got it now.