KD Technology
KD Technology

Reputation: 35

How to send a variable's value only once when a push button is released?

So I have a push button that changes a variable value, which controls a motor's strength, when pressed. It goes through a loop back and forth and stops when I release it.

The problem is that whenever the value changes it's being sent to the motor. I want the value to change when I press the button (without sending it to the motor) and when I'll release the button the value will be sent once to the motor (instead of the motor will change its strength very fast all the time and shake).

Here's what I have:

increment = 1;
        void update() {
            if (millis() - last_update > 300) {
                last_update = millis();
                strength += increment;
                analogWrite(LAUNCHER_PIN, strength);
                Serial.print("Canon strength: ");
                Serial.print(strength);
                if (strength <= CANON_MIN || strength >= CANON_MAX) increment = -increment;
            }
        }
if (!digitalRead(MOTOR_BTN_PIN)) update();

Upvotes: 0

Views: 2645

Answers (4)

Breno Teodoro
Breno Teodoro

Reputation: 483

You need to get the "edge" of the signal when it's going down.

You can think of a buttons signal as a square. When the button is not pressed there is nothing, when you press it, the voltage flows, so it looks like this (this image illustrates a system where you connect your button in pulldown):

enter image description here

So, in total, there are four states of a given digital signal:

  1. There is nothing (this can either be a LOW or a HIGH, depending on your connection; in the example I brought, the nothing relates to a HIGH signal, as I turned on the built-in PULLUP resistor of the arduino digital pin);
  2. There is a signal (which is always the inverse of point 1;
  3. There is a ramp from nothing to signal;
  4. There is a ramp from signal to nothing

In your case, you'll make use of point 4, as this is the state where the button is no longer pressed, and the microcontroller stops receiving signal.

Try this:

#define btnPin 2

uint8_t btn_prev;

void setup() {
  pinMode(btnPin, INPUT_PULLUP);

  // here the previous button signal is set to HIGH, as we enabled the pullup
  btn_prev = digitalRead(btnPin);
}

void loop() {
 //for each iteration we read the button singal
 uint8_t btn = digitalRead(btnPin);

 // we call your update function when the button was low and is now high: a.k.a. falling edge
 if (btn == LOW && btn_prev == HIGH)
  {
    update();
  }

  // at last, we update the previous state of the button, so that we can check again for a change in the next iteration
  btn_prev = digitalRead(btnPin);
}

Hope this makes sense.

Upvotes: 0

Frodyne
Frodyne

Reputation: 3973

If you don't need your main process to continue doing stuff while you press buttons, then you can do something like:

while (!digitalRead(MOTOR_BTN_PIN))
{
    if (digitalRead(MOTOR_BTN_PIN))
        update();
}

When you press the button you get trapped in the while, and when you finally let go update() gets called (once).

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409356

This is could easily be solved with a simple state machine.

You could have two states: NORMAL, BUTTON_DOWN.

Normally you should be in the NORMAL state, and the loop function checks if the button is pressed or not. If the button is pressed and you're in state NORMAL, then you set the state to BUTTON_DOWN and do whatever other processing you need to do when the button is pressed.

If the button is not pressed, and the current state is BUTTON_DOWN you know that the button have been released and you go back to the NORMAL state and do the button-release processing that needs to be done.

Since you only have two states, you could represent that as a single bool variables, where (for example) false means NORMAL and true means BUTTON_DOWN.

In pseudo code it could be something like

void loop()
{
    if (state == NORMAL)
    {
        // Do normal processing

        if (is_button_pressed())
        {
            // Do buttown-down processing

            state = BUTTON_DOWN;
        }
    }
    else if (state == BUTTON_DOWN)
    {
        if (!is_button_pressed())
        {
            // Do button-release processing

            state = NORMAL;
        }
    }
}

By adding a third state, BUTTON_DEBOUNCE, you could easily handle debounce of the button.

When in state NORMAL and you detect the button being pressed, you enter the BUTTON_DEBOUNCE state. If you have been in the BUTTON_DEBOUNCE state for a certain amount of time (say a few milliseconds) and the button is still pressed then you enter the BUTTON_PRESSED state and to the button-pressed processing.

If after the short time the button is not pressed, you go back to the NORMAL state.

If you add a third state like this, then you can no longer have a simple bool variable representing the state. An enumeration and a variable of that enumeration type might be a better choice then.

Upvotes: 2

amaldec23
amaldec23

Reputation: 245

You can use flag to solve this issue,create a flag variable boolean and set it true and false when event occurs and only pass the value when flag is true

Upvotes: 1

Related Questions