-1

There's Latest Update!

So i'm workin' on a counter just for fun. How it works is when you press BUTTON1 the LED Lights up red, the buzzer beeps and the variable "counter" adds one to itself; After counting, u can press BUTTON2 then it will first show a transition animation then blink&beep while taking away the value of "counter". After the value of "counter" is less than 1, everything will start from the beginning.

But what actually happens is after the transition animation played, the LED and the buzzer just won't do any thing. I'm sure my phisical connection is all correct, and the buttons are also pulled to ground with a resistor& a 100nf cap to prevent accidental pressing and vibration.


Note that I'm usin' a common anode RGB LED, so the anode pin is always HIGH but to light up individual colors i'll have to pull the cathode pins LOW (remember how electrons flow?)

Update @Tom Carpenter's latest answer still didn't solve the problem. I found the "else" function inside the "if" didn't work at all. So if i put the counter code inside the "else" function, the result would be none. But if i put the counter code in the "if" inside the primary "if", it would, again, count at clock speed, which results blinking forever! So is there anyway for the code to only count once per press&release cycle without losing?

The latest code:


#define RLED 10
#define GLED 11
#define BLED 12

#define B1 4
#define B2 5

#define BZZ 3

boolean OB1V;

int counter;

void setup() {

  pinMode(RLED, OUTPUT);
  pinMode(GLED, OUTPUT);
  pinMode(BLED, OUTPUT);

  pinMode(B1, INPUT);
  pinMode(B2, INPUT);

  pinMode(BZZ, OUTPUT);

  OB1V = digitalRead(B1);

}

void loop() {

  digitalWrite(RLED, HIGH);
  digitalWrite(GLED, HIGH);
  digitalWrite(BLED, HIGH);

  boolean NB1V = digitalRead(B1);

  if (NB1V != OB1V)
    {
      digitalWrite(RLED, LOW);
      digitalWrite(BZZ, HIGH);
      counter = 0;
    }
  else
    {
      digitalWrite(RLED, HIGH);
      digitalWrite(BZZ, LOW);
      counter = counter +1;
    }

  if ( digitalRead(B2) == HIGH )
    {
      digitalWrite(GLED, LOW);
      delay(300);
      digitalWrite(GLED, HIGH);
      digitalWrite(RLED, LOW);
      delay(300);
      digitalWrite(RLED, HIGH);
      digitalWrite(GLED, LOW);
      delay(300);
      digitalWrite(GLED, HIGH);
      digitalWrite(BLED, LOW);
      delay(100);
      digitalWrite(BLED, HIGH);
      delay(300);

      for ( counter; counter >= 1; counter -- )
        {
          digitalWrite(GLED, LOW);
          digitalWrite(BZZ, HIGH);
          delay(200);
          digitalWrite(GLED, HIGH);
          digitalWrite(BZZ, LOW);
          delay(200);
        }
    }
}

  • 1
    How are your buttons wired? Typically you would use a pullup resistor and a button switching to ground, so pushed is "low". Also your non-delay counter case would probably be subject to contact bounce, except for the fact that "counter +1" doesn't actually count as it does not save the result of the add. Your other path with delays might be somewhat immune to bounce on press, but can suffer it on release unless the user releases the button enough before the end of the sequence of operations there. – Chris Stratton May 21 '16 at 23:47
  • I pulled the button to ground so a press event is HIGH. – user56336 May 22 '16 at 0:01
  • Also if i change "counter+1" above into "counter = counter+1", the green LED will keep flashing as a result – user56336 May 22 '16 at 0:03
  • Go out, walk around some, socialize with people, come home, sleep, and look at it tomorrow. – Ecnerwal May 22 '16 at 0:43
  • 1
    counter +1; doesn't do anything as Chris says. As a result the value counter is always 0, and so the for loop will never do anything (and will probably be removed as dead code by the compiler). If correcting the line causes something else to break, it means something else in the code is broken. – Tom Carpenter May 22 '16 at 1:03
1

Your problems come down to the fact that in the first code you are trying to do things on level sensitivity rather than edge sensitivity.

To try and understand the difference, some examples. Level sensitivity is effectively:

Do 'x' while 'y'

In other words, if 'y' is true (for example an input is high), then keep doing 'x' (e.g. adding to a counter). This is what your code is doing at the moment, but not what you want it to be doing.

Edge sensitivity on the other hand is:

Do 'x' when 'y changed'

In other words, when the state of 'y' changes (for example from true to false), then doing 'x' once.

The key difference between the two is the difference between 'while' and 'when'.


So how do you do edge sensitivity in code?

The way you have done it requires that the button be high and then low at exactly the right moment for the code to catch it. What happens if the button goes low at the end of the loop? Your code will miss the edge because at the start of the next loop it is not high, so doesn't get in to the first if statement.

Lets think about what 'changes' means. It means when the current value is different from the previous value. That implies that we need to keep track of what the previous value is in order to compare it to the current value. In fact that is exactly what we do:

boolean oldButtonValue; //It would be better to use a static variable in loop, but for simplicity of the example lets use a global.

void setup() {
    oldButtonValue = digitalRead(buttonPin); //This line is executed once to initialise the variable.
}

void loop() {

    //Each loop we read our current value
    boolean newButtonValue = digitalRead(buttonPin);

    //Has it changed?
    if (newButtonValue != oldButtonValue) {

        //Do something on edge.

    }

    //Update the old button value to match the new one (so we can check it again next loop
    oldButtonValue = newButtonValue;
}

What this does is each loop it compares the current state of the button with the state of it from the previous loop. If the value changes, it does something.


Now the trouble with that code is it executes on any edge. That means that each time you press the button, it will run twice, once on press and again on release. So lets modify it slightly:

    ...

    //Has it changed?
    if (newButtonValue != oldButtonValue) {

        //Was it a rising edge?
        if (newButtonValue) {
            //If the button value is now high, it must have been a rising edge
        } else {
            //Otherwise it was a falling edge

        }
    }

    ...

The extra comparison allows us to determine whether it was a rising edge or a falling edge. So you can now do something either when the button is pressed, or when it is released (or both if you want).


You can have a second boolean variable to keep track of the old state for the second button. Then you use the same approach to do edge sensitivity detection approach for the second button.

  • i changed my code into this: link Because that the sencond button always works fine, i didn't touch that piece of code. But what happens now is when it is supposed to show the results, it showed nothing! What's wrong now? – Samm May 22 '16 at 21:12
  • @user56336 Try it, and if it works, there you go. It looks basically the same as what I put just with much harder to follow variable names. Except you are missing the bit where you set OB1V = NB1V; at the end of the loop which is a somewhat critical part. And really you should do the same with both buttons, otherwise you will end up with the same problem with the second one - it will execute the code multiple times if you keep the button held down. – Tom Carpenter May 22 '16 at 21:21
1

There are a number of problems with your code. Your code does not deal with releasing the button. Once you press B1 (assuming pressing B1 causes the input to go high) the counter (if correctly implemented (that is, it should be counter++)) would count up to 32K then start over at zero and count up again. Only stopping when you release B1. The value of counter could be any number between 0 and 32K.

Working with buttons is difficult. And the 100nF capacitor will do little to help. It would be better if you learned how to debounce buttons and treat both the press and the release with equal importance.

There is an Arduino library called Bounce that will do some of this for you. It will not help with your logical errors. You still need to treat pressing and releasing the button with equal importance. But it will help debounce button presses so your results will more closely match your expectation.

added later...

Your altered code will still not work. You need to keep track of the state of the button in order to increment the counter by one for each button press. You can call it edge detection code or state machine code. But the code needs history to decide what to do next. Here is an example of code which does this.

  • the cap does help because i've tried to print messgaes on the serial monitor with "caped" buttons--perfectly one result each press. Also to deal with release, i put an "else" function at the end which turns the LED and the buzzer off. So it should trigger counter +1 (or counter ++ if u like) each time i press the button – Samm May 22 '16 at 3:39
  • @user56336 remember that it is in a loop. The whole loop() function will execute thousands of times per second, so unless you release the button in much less than a millisecond, it will repeatedly add 1 to the counter until the button is released. Try adding the print statement I suggested and you will see that. – Tom Carpenter May 22 '16 at 12:52
  • In most cases when using a button, the software keeps track of 4 states. 1) Button released and action already taken, 2) Button pressed and action not taken yet, 3) Button pressed and action already taken & 4) Button released and action not taken yet. You need to keep track of these 4 states if you want to count up by one for each button press. – st2000 May 22 '16 at 12:53
  • well, what my code does is just add 1 to the "counter" value every time there's a HIGH signal from the button pin untill the second button is pushed, it'll show the results. So i don't think i'll have to listen all four status. The problem now is, after i changed "counter +1;" above into "counter = counter +1;" so that it'll keep the result as mentioned above, the LED keeps flashing when supposed to show the result. So there must be something wrong with adding or minusing. – Samm May 22 '16 at 13:57
  • Perhaps this would be more obvious in a real C code environment instead of in an Arduino environment. In real C you would have to put your button press inside of an infinite loop. Because you would want to use your button over an over again. Not just once. While inside an infinite loop, you would see that when the button was pressed, the computer would simply be adding 1 to counter over and over again as fast as it could. Do this, after adding 1 to counter print out the value of counter. I think you will see, when you press the button, an endless stream of " 1", " 2", " 3", ... – st2000 May 22 '16 at 14:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.