Delicious Bookmark this on Delicious      HOME > > ARDUINO LANGUAGE COURSE  t.o.c.

An example, using things we know

Further exploration of "switch bounce"

This is one of a collection of pages which, together, attempt to show you "everything" about the Arduino's programming language.

There is a page for you with more information about the project in general, and the way these pages are organized, if you want that.

Please visit my page about power browsing notes sometime.

This page, and the software it references, ©TK Boyd, 1/2010.

Where we are going...

In this tutorial, now that we know how to make functions which return a value, we are going to go back to playing with switches and LEDs.

It didn't matter in the work we've done so far, but switches often suffer from something called "bounce".

This is a good example of an exercise that may be pointless in terms of the program developed. None the less, the path we take to get there should be educational.

We're going to make a program which might be used by a motorist driving on the highway to see how many times he is overtaken by another car, and how many times he himself overtakes someone.

If you were to make this "for real use", you'd need an Arduino equipped with an LCD display. We're going to use the serial monitor. It will display a number. If that's, say, 5-4, it means that we have over-taken 5 cars, and been overtaken by 4.

As we drive along, we press the green switch briefly if someone over-takes us, and the yellow switch briefly if we overtake someone else.

Here we go....

First play with the following, crude, version of the program....

/*FEAa1OvertakingTallyCrude
29 Dec 09

Tracks presses of two buttons,
and sends tally to serial monitor*/

int intWe=0,intThey=0;

const byte bGreenSwitch=4;//Pin switch is connected to
const byte bYellowSwitch=5;//Pin switch is connected to

void setup()
{
  Serial.begin(9600);//Prepare serial port for use
  pinMode(bGreenSwitch,INPUT);
  pinMode(bYellowSwitch,INPUT);
}

void loop()
{
  if (digitalRead(bGreenSwitch)==LOW) {intThey++;};
  if (digitalRead(bYellowSwitch)==LOW) {intWe++;};

  printAns(intWe,intThey);
  delay(200);//So serial port isn't overwhelmed by too-rapidly
           //repeated instances of sending the answer again
           //and again.
}

void printAns(int intLWe, int intLThey)//The "L" in the name is to stress they are local.
       //Even without the "L", even with the same names as used elsewhere these
       //variables would be local.
{
  Serial.print("We have over-taken: ");
  Serial.print(intLWe);
  Serial.print(" times. They  have over-taken: ");
  Serial.print(intLThey);
  Serial.println(" times.");
}

Notice that intWe and intThey are set up as global variables. This is because we need the values in those variables to persist from on call of "loop" to another. Local variables "start again" each time you enter the function to which they are local. Note that in declaring them....

int intWe=0,intThey=0;

.... we took a slight short-cut: We said "int" once, and then listed two variables to be created, each with a first value specified.

Later, in the header for the printAns function, we did NOT take the short-cut... we wrote out "int" twice, once for each of the variables we were declaring. The "L" in the name is just to emphasize the fact that these copies of the tallies are only local, temporary....

void printAns(int intLWe, int intLThey)

---

In case you don't remember from before....

intThey++;

... is just a quick way of accomplishing....

intThey=intThey+1;

---

Humm. A little late in the day, I am realizing that THIS program won't be affected by switch bounce, because of the "delay(200);" at the end of the "loop" function. That delay has to be there to give the serial system time to keep up with everything else that is going on. Sigh. We'll PRETEND that bounce would matter!

What IS "bounce"? When you open or close a switch, it doesn't open or close perfectly. Taking the case of closing a switch, you do NOT have at one moment an open circuit, and then a moment later and forever more a closed circuit. For a few fractions of a second, as the switch closes, it sometimes closes-opens-closes-opens and then finally settles down closed. And a similar thing can happen as the switch is opening.

For humans, it doesn't matter. Our senses aren't sharp enough to pick up such things. However, computers are incredibly fast. If your program is "watching" to see a switch close, the effect of bounce can be to make it think the switch has closed several times. Not good if, for instance, you are using the program to count switch closures, as we are in my "over-taking" example.... BUT... in the over-taking example, we aren't going to get multiple counts. The "delay(200)" which is there to keep the service to the serial port happy will be more than enough to ensure that the program does not see the multiple openings and closing which occur even with a very bouncy switch.

However, another problem may occur. What happens if the human "pressing the button" to say "a car just over-took me" holds his finger on the button a little too long, say for 0.3 seconds?

The program will see and count the button press once properly, but if the button is still pressed when the program goes through "loop" again, what was really only one press will get counted as two presses.

In order to keep the bulk of "loop" clear and easy to read, we are going to move the test of whether a button is pressed out of "loop" into a function. The function will return a boolean value. Initially, it will be nearly the same as the code which was originally inside "loop"....

/*FEAa1OvertakingTallyStage2
29 Dec 09

Tracks presses of two buttons,
and sends tally to serial monitor.

Similar to crude version, but reading of
switches moved out to a shared function*/

int intWe=0,intThey=0;

const byte bGreenSwitch=4;//Pin switch is connected to
const byte bYellowSwitch=5;//Pin switch is connected to

void setup()
{
  Serial.begin(9600);//Prepare serial port for use
  pinMode(bGreenSwitch,INPUT);
  pinMode(bYellowSwitch,INPUT);
}

void loop()
{
  if (boSwitchClosed(bGreenSwitch)) {intThey++;};
  if (boSwitchClosed(bYellowSwitch)) {intWe++;};

  printAns(intWe,intThey);
  delay(200);//So serial port isn't overwhelmed by too-rapidly
           //repeated instances of sending the answer again
           //and again.
}

void printAns(int intLWe, int intLThey)//The "L" in the name is to stress they are local.
       //Even without the "L", even with the same names as used elsewhere these
       //variables would be local.
{
  Serial.print("We have over-taken: ");
  Serial.print(intLWe);
  Serial.print(" times. They  have over-taken: ");
  Serial.print(intLThey);
  Serial.println(" times.");
}

boolean boSwitchClosed(byte bWhichSwitch)
{
  boolean boTmp=false;
  if (digitalRead(bWhichSwitch)==LOW) {boTmp=true;};
  return boTmp;
}

That version is only subtly different from the previous, but the differences are important.

Notice how now only ONE bit of code takes care of reading BOTH switches? This is a Good Thing because if it is got right for one of the switches, there is no second bunch of code to bring into alignment with the first bunch.

When we call "boSwitchClosed", we pass to it the pin number for the input we want to check. That's "the trick" of getting the one bit of code to look at whichever switch we want it to look at.

Look at the code in "loop"... that's a little less complex now, so a little easier to follow. Another Good Thing.

Let me talk about a feature within the function boSwitchClosed.

The function could have been written as follows.....

if (digitalRead(bWhichSwitch)==LOW)
   {boTmp=true;}
     else {boTmp=false;};

... having the "else" as well always seems extra "stuff" to deal with, to me.

Instead of using the "else" option, what I did was to set boTmp false before I did the "if (digitalRead..." part. If boTmp didn't need changing to "true", I was done... boTmp was ALREADY false, because of the way I initialized it. Although my system is less explicit, perhaps, it just seems tidier (and cleaner, clearer) to me.

In any case, it is always a good idea to initialize any variable early on. In other words, put something in the variable. Until you do that, you don't really know what is in it, and that could cause you pain.

---

We're now going to make the function that checks the switch a little fancier.

When we enter the function, if it finds the switch is closed, we will pause for a moment, in order to let bounces pass, and then we will enter a loop which will keep us in the function until the switch has been seen to be open not just once, but twice, a few moments apart. That will ensure that bounces arising as the switch opens have passed before we leave the function.

Note that this function for checking if a key is pressed will only be of use in connection with programs where the user is not expected to leave his finger on the button for extended periods. While the switch is closed, the program will be in a loop waiting for the release of that button, and other things will not be dealt with.

In Windows programming, it is dangerous to enter loops that the program may languish in for several seconds or more. Happily, the Arduino is a less complex device, and doesn't mind such things.... depending on what you have programmed it to do! (But by the time it matters, you will know it matters.)

/*FEAa1OvertakingTallyStage2
29 Dec 09

Tracks presses of two buttons,
and sends tally to serial monitor.

Similar to crude version, but reading of
switches moved out to a shared function*/

int intWe=0,intThey=0;

const byte bGreenSwitch=4;//Pin switch is connected to
const byte bYellowSwitch=5;//Pin switch is connected to

void setup()
{
  Serial.begin(9600);//Prepare serial port for use
  pinMode(bGreenSwitch,INPUT);
  pinMode(bYellowSwitch,INPUT);
}

void loop()
{
  if (boSwitchClosed(bGreenSwitch)) {intThey++;};
  if (boSwitchClosed(bYellowSwitch)) {intWe++;};

  printAns(intWe,intThey);
  delay(200);//So serial port isn't overwhelmed by too-rapidly
           //repeated instances of sending the answer again
           //and again.
}

void printAns(int intLWe, int intLThey)//The "L" in the name is to stress they are local.
       //Even without the "L", even with the same names as used elsewhere these
       //variables would be local.
{
  Serial.print("We have over-taken: ");
  Serial.print(intLWe);
  Serial.print(" times. They  have over-taken: ");
  Serial.print(intLThey);
  Serial.println(" times.");
}

boolean boSwitchClosed(byte bWhichSwitch)
{
  boolean boTmp=false;
  if (digitalRead(bWhichSwitch)==LOW) {boTmp=true;};

  delay(4);//wait a moment, to let bounces pass...

  //if the switch was closed, look at it again and again
  //  until it is open again. This "while" loop doesn't DO
  //  anything... except go 'round and 'round 'til the
  //  switch is released.... but that's allowed!
  while (boTmp && (digitalRead(bWhichSwitch)==LOW)) {;};

  delay(4);//Again let bounces pass...

  //Again wait for the switch to be open.
  while (boTmp && (digitalRead(bWhichSwitch)==LOW)) {;};

  return boTmp;
}

Question for you: How can we tell that this is actually doing what I say it is doing? How do we know that the boSwitchClosed function "traps" the computer's "attention" until the switch is replaced?

v

v

Scroll down when ready for answer....

v

v

v

Watch the serial monitor while holding one of the switches closed. Notice that new lines are not sent to the serial monitor as long as the switch is held closed. Previously, not only did the count continue to rise, as if the user was pushing the button again and again, but the stream of messages to the serial monitor wasn't interrupted if the switch was held down for longer than an naive programmer might have expected a user to hold it down.

---

There are all sorts of variations on these themes to consider. What "makes sense" to a human isn't necessarily an adequate view of everything that is happening at computer speeds and inside the electronics. "Bounce" is one "gotcha" to watch out for. People holding buttons down "too long" may happen because a human's idea of "long" is almost unrelated to "long" in electronic terms.

But you know about bounce now, and are ready allow for it when necessary!




   Search this site or the web      powered by FreeFind

Site search Web search
Site Map    What's New    Search

The search engine is not intelligent. It merely seeks the words you specify. It will not do anything sensible with "What does the 'could not compile' error mean?" It will just return references to pages with "what", "does", "could", "not".... etc.

SPELL your search term properly. When I review search logs, it is amazing how many people ask the engine to search for something meaningless.


Why does this site cause a script to run? I have my web-traffic monitored for me by eXTReMe tracker. They offer a free tracker. If you want to try it, check out their site. And if there are Google ads on the page, they are run with scripts, too.


Click here to return to Arduino COURSE table of contents.
Click here to go to the author's home page.

Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!
Click here to visit editor's freeware, shareware page.


Here is how you can contact this page's editor. This page, and the software it references, ©TK Boyd, 1/2010.

Valid HTML 4.01 Transitional Page tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org