I am having some difficulty reading input from a contact switch. The workaround I am using involves some delay, and I'm wondering if the board really needs this.
I am using an Arduino Uno. The input pin is connected to the common terminal on a double-pole contact switch; the NO terminal on the switch goes to ground, the NC goes to 5V. I want to take action when the switch closes but wait for it to be released.
In setup(), I call pinMode(MyPin,INPUT_PULLUP).
In the loop, I poll for MyPin to go HIGH, and then wait until the contact switch opens before proceeding. So:
if(digitalRead(MyPin)==HIGH)
{
while(digitalRead(MyPin)==HIGH);
// continue with the action
}
What happens is that when the switch is closed, the program goes to the while() statement, but doesn't go any further. It stops there as if MyPin never goes LOW.
The workaround that appears to work:
if(digitalRead(MyPin)==HIGH)
{
while(1)
{
if(digitalRead(MyPin)==LOW)
break;
delay(25);
}
// continue with the program
}
Is the first method actually incorrect? Are there better workarounds? Thanks for your input.
You may be seeing switch bounce. The micro is reading that pin on a timescale of microseconds and every mechanical switch is going to have some mechanical action where the contact is made and broken a number of times over a short period of time. Look up debounce strategies.
Blackfin, I had not thought of that. What I'm puzzled by is why it doesn't proceed once the contact is released. I can see how bounce might make COM go low prematurely but would that prevent it from correctly responding when the switch is released?
Please, show complete sketches.
Use CTRL T to format your code.
Attach your sketch between code tags [code]Paste your sketch here[/code]
You might get some benefit from reading this discussion.
Also, this code demonstrates Mr. Gammon’s switch manager library.
It makes short work of switch handling.
/*SwitchManager skeleton
This sketch is to introduce new people to the SwitchManager library written by Nick Gammon
The library handles switch de-bouncing and provides timing and state change information in your sketch.
The SwitchManager.h file should be placed in your libraries folder, i.e.
C:\Users\YourName\Documents\Arduino\libraries\SwitchManager\SwitchManager.h
You can download the library at:
http://gammon.com.au/Arduino/SwitchManager.zip Thank you Nick!
In this example we have 2 normally open (N.O.) switches connected to the Arduino - increment and decrement.
The increment switch will also be used as a "Reset" switch if pressed for more than two seconds.
The two switches are connected between GND (0 volts) and an Arduino input pin.
The library enables pull-up resistors for your switch inputs.
Pushing a switch makes its pin LOW. Releasing a switch makes its pin HIGH.
The SwitchManager library provides 10ms de-bounce for switches.
i.e. enum { debounceTime = 10, noSwitch = -1 };
If you need more time, edit the SwitchManager.h file
i.e. enum { debounceTime = 50, noSwitch = -1 }; //here it is changed to 50ms
*/
#include <SwitchManager.h>
//object instantiations
SwitchManager myIncSwitch;
SwitchManager myDecSwitch;
unsigned long currentMillis;
unsigned long heartBeatMillis;
unsigned long heartFlashRate = 500UL; // time the led will change state
unsigned long incShortPress = 500UL; // 1/2 second
unsigned long incLongPress = 2000UL;// 2 seconds
unsigned long decShortPress = 500UL; // 1/2 second
const byte heartBeatLED = 13;
const byte incSwitch = 4; //increment switch is on Arduino pin 4
const byte decSwitch = 5; //decrement switch is on Arduino pin 5
int myCounter;
//======================================================================
void setup()
{
Serial.begin(9600);
//gives a visual indication if the sketch is blocking
pinMode(heartBeatLED, OUTPUT);
myIncSwitch.begin (incSwitch, handleSwitchPresses);
myDecSwitch.begin (decSwitch, handleSwitchPresses);
//the handleSwitchPresses() function is called when a switch changes state
} // E N D O F s e t u p ( )
//======================================================================
void loop()
{
//leave this line of code at the top of loop()
currentMillis = millis();
//***************************
//some code to see if the sketch is blocking
if (CheckTime(heartBeatMillis, heartFlashRate, true))
{
//toggle the heartBeatLED
digitalWrite(heartBeatLED,!digitalRead(heartBeatLED));
}
//***************************
//check to see what's happening with the switches
//"Do not use delay()s" in your sketch as it will make switch changes unresponsive
//Use BlinkWithoutDelay (BWD) techniques instead.
myIncSwitch.check ();
myDecSwitch.check ();
//***************************
//put other non-blocking stuff here
} // E N D O F l o o p ( )
//======================================================================
// F U N C T I O N S
//======================================================================
// C h e c k T i m e ( )
//**********************************************************************
//Delay time expired function
//parameters:
//lastMillis = time we started
//wait = delay in ms
//restart = do we start again
boolean CheckTime(unsigned long & lastMillis, unsigned long wait, boolean restart)
{
//has time expired for this task?
if (currentMillis - lastMillis >= wait)
{
//should this start again?
if(restart)
{
//yes, get ready for the next iteration
lastMillis = millis();
}
return true;
}
return false;
} // E N D o f C h e c k T i m e ( )
// h a n d l e S w i t c h P r e s s e s ( )
//**********************************************************************
void handleSwitchPresses(const byte newState, const unsigned long interval, const byte whichPin)
{
// You get here "ONLY" if there has been a change in a switches state.
//When a switch has changed state, SwitchManager passes this function 3 arguments:
//"newState" this will be HIGH or LOW. This is the state the switch is in now.
//"interval" the number of milliseconds the switch stayed in the previous state
//"whichPin" is the switch pin that we are examining
switch (whichPin)
{
//***************************
//are we dealing with this switch?
case incSwitch:
//has this switch gone from LOW to HIGH (gone from pressed to not pressed)
//this happens with normally open switches wired as mentioned at the top of this sketch
if (newState == HIGH)
{
//The incSwitch was just released
//was this a short press followed by a switch release
if(interval <= incShortPress)
{
Serial.print("My counter value is = ");
myCounter++;
if(myCounter > 1000)
{
//limit the counter to a maximum of 1000
myCounter = 1000;
}
Serial.println(myCounter);
}
//was this a long press followed by a switch release
else if(interval >= incLongPress)
//we could also have an upper limit
//if incLongMillis was 2000UL; we could then have a window between 2-3 seconds
//else if(interval >= incLongMillis && interval <= incLongMillis + 1000UL)
{
//this could be used to change states in a StateMachine
//in this example however, we will just reset myCounter
myCounter = 0;
Serial.print("My counter value is = ");
Serial.println(myCounter);
}
}
//if the switch is a normally closed (N.C.) and opens on a press this section would be used
//the switch must have gone from HIGH to LOW
else
{
Serial.println("The incSwitch was just pushed");
}
break; //End of case incSwitch
//***************************
//are we dealing with this switch?
case decSwitch:
//has this switch gone from LOW to HIGH (gone from pressed to not pressed)
//this happens with normally open switches wired as mentioned at the top of this sketch
if (newState == HIGH)
{
//The decSwitch was just released
//was this a short press followed by a switch release
if(interval <= decShortPress)
{
Serial.print("My counter value is = ");
myCounter--;
if(myCounter < 0)
{
//don't go below zero
myCounter = 0;
}
Serial.println(myCounter);
}
}
//if the switch is a normally closed (N.C.) and opens on a press this section would be used
//the switch must have gone from HIGH to LOW
else
{
Serial.println("The decSwitch switch was just pushed");
}
break; //End of case decSwitch
//***************************
//Put default stuff here
//default:
//break; //END of default
} //End switch (whichPin)
} // E n d o f h a n d l e S w i t c h P r e s s e s ( )
//======================================================================
// E N D O F C O D E
//======================================================================
There is also an example in the IDE that shows how to respond to switch changes rather than switch levels.
“and then wait until the contact switch opens before proceeding”
Waiting for a slow mechanical thing to occur in your sketch makes that sketch lock up and unresponsive.
Don’t wait, when the switch goes from HIGH to LOW do your stuff.
The input pin is connected to the common terminal on a double-pole contact switch; the NO terminal on the switch goes to ground,the NC goes to 5V. I want to take action when the switch closes but wait for it to be released.
Your logic is backwards. Switch closure shows up as a LOW on the pin.
You must use INPUT_PULLUP with that setup, or the switch state will be undefined during a transition. I see no reason to connect NC to 5V.
Larryd, thanks for the link and info. I'm very curious about one thing you said: "Waiting for a slow mechanical thing to occur in your sketch makes that sketch lock up and unresponsive."
Does constant polling cause the sketch to lock up?
However, this does:
while(digitalRead(MyPin)==HIGH);
The following code ‘looks for a change in state’ on a switch, not a level.
If there was a change, we check to see if it went HIGH, if not, it must have gone LOW.
//******************************************
//check if this switch has changed state
switchPosition = digitalRead(ToggleSwitch);
if (switchPosition != lastToggleSwitchState)
{
//update to the new switch state
lastToggleSwitchState = switchPosition;
//This switch position has changed, let's do some stuff
//"HIGH condition code" <-----<<<<<
//Switch went from LOW to HIGH
if (switchPosition == HIGH)
{
//Do some HIGH stuff here
}
//"LOW condition code" <-----<<<<<
//Switch went from HIGH to LOW
else
{
digitalWrite(testLED, !digitalRead(testLED)); //toggle the testLED
}
Larryd, thanks for the response. What I'm not clear on is why execution would not proceed as soon as the switch went LOW. If the switch is LOW when the statement is executed, won't the sketch move on to the next statement immediately? If the switch is HIGH when the statement is first executed, won't the sketch move on as soon as the switch goes LOW? Am I right in assuming that if the program stops on the statement
while(digitalRead(MyPin)==HIGH);
it is only because digitalRead(MyPin) is in fact returning HIGH, regardless of the true state?
Let say you push the switch, i.e. the state is now HIGH.
We now sit doing the ‘while’ as long as the switch is now pushed.
Let’s say you have a process that needs to be updated every 5 milliseconds.
This could be motor movement, scanning for a receiver message from a PC etc.
Now since you are waiting in the ‘while loop’ none of the other time sensitive stuff is being done.
When you the let go of the switch, you move out of the ‘while’ loop and can now process the other important time sensitive stuff.
You might have only held the switch down for 1/2 seconds (500 ms) but this 500ms is an eternity as far as the controller is concerned.
Let say you push the switch, i.e. the state is now HIGH.
Let's not. If the switch is wired as described, and is "pushed", the state will be LOW.
The switch could certainly be wired some other way, or defective.
For those unfamiliar with switch notation, including possibly the OP, "NC" = normally closed and connected to the terminal labeled "C" (for common). That is, when the switch is not pushed.
"NO" = normally open, not connected to C, when the switch is not pushed.
When pushed, NO becomes closed, connected to C, and vice versa.
Ray, that would probably provide some insight. As a practical matter, for the time being I am using this routine, which I call with the pin ID and target state as arguments:
This seems to work, but I remain curious about why constant polling fails.
Larryd, what you describe is just how I expected it to work. But when the while(...) statement is used, execution stops there. When I use the function described above, it proceeds. I'm wondering why the while(...) approach fails.
jremington, the double-pole switch that I use has NO (ground) connected to COM when it is at rest, and NC (5V)connected when it is pressed.
jremington, you're right; I misspoke. FWIW, the confusion here isn't over whether the pin reads LOW or HIGH, but I appreciate you pointing out that I had the designation wrong.
Given this failure of communication, we still do not know for sure how you have wired your switch.
Please get out your multimeter and report the voltage appearing on the COM terminal, and hence the digital input pin (with respect to Arduino ground), once when the switch is pressed, and once when it is not.
If you do not have a multimeter, now is the time to buy one. It is essential for troubleshooting external circuitry, power supplies, etc.