Waiting on a Switch to Stop Moving

Hi Guys,

I'm working on a (fairly) simple notification project involving an arduino/wifi shield and a 6-position switch (connecting each position to an input pin). The arduino is a web client that will send a query string based on the position of the switch. While the switch is being shipped, I'm brainstorming the logic. The only issue I see on the horizon is checking for the "final" position of the switch.

For example. If the switch is on the OFF position and needs to be moved to position 3, then it will have to step through positions 1 and 2 to get there. I do not want a query sent when the switch hits positions 1 and 2; only position 3.

My thinking is an array of the inputPins that is checked in the loop. The inputPins state (LOW or HIGH) is recorded into a variable/array (previousState). When the state of the inputPins changes, delay for, say 1 second, then gets recorded into another variable/array (currentState). The currentState is checked against the previousState. If they do not match (meaning the switch is still being moved to its final position), then delay and restart the loop. If they do match (meaning the switch is in its final position), then send the query string.

Am I thinking about this properly? Am I over-thinking this? Just looking for some ideas, critics, and general feedback.

Thanks in advance.

Scan through the switch pins looking for the current position.
If the current position is different from the previous position:
Start a timer.
Set an UNUSED flag.
If the timer reaches "N" and the UNUSED flag is set:
Act on the current position.
Clear the UNUSED flag.

I assume you can read the inputs and work out somehow which position the switch is in. Remember that when the switch is being moved between positions, there may not be anything connected.

In that case you just need to work out which position the switch is in, and wait for that number to settle (using the debounce algorithm you outlined). But you only need to debounce one value (the switch position) not the state of all the inputs.

Do you really need a separate input pin per switch position? If the switch is not connected to anything else then you could connect each position to a different resistance value and use a single analog pin as a voltage divider to determine the resistance and hence which position the switch is in.

Thanks for the replies.

This is an industrial application so the switch being used is a cam switch. It's a high quality switch that provides 6 individual outputs. I looked at the resistance/voltage divider idea from another post but I decided to use 6 pins initially for simplicity. I may end up doing the single analog pin setup as this project grows but this is a simple proof of concept. The logic of the debounce will have to address the same data rather its an array of inputs or an array of analog pin values.

I'm thinking about your single input debounce suggestion. Let me know if I'm understanding this correctly. When the switch is moved and a pin goes HIGH, the debounce will see it and delay 1 sec. After the debounce the pin that was HIGH is checked. If the pin that was checked is no longer HIGH (meaning the switch moved to it and then moved again to another position), the loop continues to check pins until it finds another pin that is now HIGH, then debounces that one. If the new pin is checked and is still HIGH (meaning the final position is set), then the correct action is taken. I'm thinking this will populate a variable that is run through a CASE statement which will send the appropriate query string.

Am I following you thoughts correctly here?

Thanks again.

From the software point of view it doesn't particularly matter whether you have a single analog input or multiple digital inputs. All that matters is that you can look at the instantaneous state of the inputs and determine which position the switch is in at a given instant. What you're debouncing is the currently-select input number, NOT the state of all the individual inputs.

Given that, all you need to do is read the current position and compare that to the previous position. If the two are different, store the current time i.e. the time at which the change occurred. If the two are the same, work out how long ago the last change was; if it exceeds your threshold then the switch has settled in the new position so store that as the debounced position.

It only needs the following variables:
the debounced position (i.e. input number selected by the switch)
the previous position
the current position
the value of millis() when the previous position was changed.

Remember to use pull-up resistors (and wire the switch so it pulls the inputs down) since the switch contacts will be isolated while it is moving.

OR - Require some fixed number of identical readings before you respond. 3 to 5 might be a good place to start. If the switch is being turned the readings should be changing. When the position stabilizes then use that position. It is a sort of debounce that is looking for more than just 1 position.

Okay, that simplifies it. I'll get started on coding this logic. This is my first "real" arduino project. Other than blinking LEDs, running a WebServer Thermometer, and sending text to an LCD. This is an opportunity for one of my hobbies to mesh with my job. :slight_smile:

I'll post any other questions I run into.

Thanks again.

kf2qd:
OR - Require some fixed number of identical readings before you respond. 3 to 5 might be a good place to start. If the switch is being turned the readings should be changing. When the position stabilizes then use that position. It is a sort of debounce that is looking for more than just 1 position.

Won't it then repeatedly act on that switch setting? I was under the impression that each switch setting should only be acted on once when the switch is left in that position.

const int pinCount = 6;
const int pinList[pinCount] = {2, 3, 4, 5, 7, 8};

void setup() {
    for (int i=0; i<pinCount; i++) {
        pinMode(pinList[i], INPUT_PULLUP);  // Active low, connect switch common to Ground
    }
}

void loop() {
    static int previousPosition = -1;
    static boolean unused = false;
    static unsigned long timer = 0;
    int currentPosition = -1;

    // Scan through the switch pins looking for the current position.
    for (int i=0; i<pinCount; i++) {
        if (digitalRead(pinList[i]) == LOW)
            currentPosition = i;
    }

    // If the current position is different from the previous position:
    if (currentPosition != previousPosition) {
        timer = millis();  // Start a timer.
        unused = true;  // Set an UNUSED flag.
        previousPosition = currentPosition;
    }

    // If the timer reaches "N" and the UNUSED flag is set:
    if (millis() - timer > 1000  && unused  && currentPosition != -1) { // One second
        // Act on the current position. (Your code goes here)
        unused = false; // Clear the UNUSED flag.
    }
}

John,

I think you are on to something. All my efforts today have ended with repeated actions on whatever the active pin is currently. I want to run a statement only one time once the switch has stopped moving. Then run it again when the switch settles on the next position, etc. You code works for a couple pins but doesn't seem to work for all. Almost intermittent. Here is my adaptation of your code:

const int pinCount = 5;
const int pinList[pinCount] = {15, 16, 17, 18, 19}; // A1-A5 set as digital

void setup() {
for (int i=0; i<pinCount; i++) {
//pinMode(pinList*, INPUT_PULLUP); // Active low, connect switch common to Ground[/color]*

  • pinMode(pinList[1], INPUT);*
    _ digitalWrite(pinList*, HIGH);_
    _
    }_
    _ Serial.begin(9600);
    Serial.println("Serial running");_

    _
    delay(2000);_
    _
    }_
    void loop() {
    _
    static int previousPosition = -1;_
    _
    static boolean unused = false;_
    _
    static unsigned long timer = 0;_
    _
    int currentPosition = -1;_
    _
    // Scan through the switch pins looking for the current position._
    _
    for (int i=0; i<pinCount; i++) {_
    _ if (digitalRead(pinList) == LOW)
    currentPosition = i;
    }*_

* // If the current position is different from the previous position:*
* if (currentPosition != previousPosition) {*
* timer = millis(); // Start a timer.*
* unused = true; // Set an UNUSED flag.*
* previousPosition = currentPosition;*
* }*
* // If the timer reaches "N" and the UNUSED flag is set:*
* if (millis() - timer > 1000 && unused && currentPosition != -1) { // One second*
* // Act on the current position. (Your code goes here)*
_ Serial.print("status: ");
Serial.println(currentPosition);
* unused = false; // Clear the UNUSED flag.
}
}
[/quote]*
What is missing?
Thanks_

const int pinList[pinCount] = {A1, A2, A3, A4, A5};  // Use names so it will work if you change boards

void setup() {
    for (int i=0; i<pinCount; i++) {
        //pinMode(pinList[i], INPUT_PULLUP);  // Active low, connect switch common to Ground
        pinMode(pinList[i], INPUT);  //////// You put '1'  where you meant to put 'i'
        digitalWrite(pinList[i], HIGH);
    }

Since pins are supposed to default to INPUT that might not be your problem. :frowning:

Intermittent sounds like a floating input and sure enough, we have this:

        digitalWrite(pinList, HIGH);

Which however, doesn't compile in 1.0 and John has correct in his post above. Can you clarify what the latest code is?

Okay, awesome. Typo corrected and the following code works:

boolean debugger = false;  // Verbose output

const int pinCount = 5;
const int pinList[pinCount] = {15, 16, 17, 18, 19};  // A1-A5 set as digital

void setup() {
    for (int i=0; i<pinCount; i++) {
        //pinMode(pinList[i], INPUT_PULLUP);  // Active low, connect switch common to Ground
        pinMode(pinList[i], INPUT);
        digitalWrite(pinList[i], HIGH);
    }

  Serial.begin(9600);
  delay(500);
  Serial.println();
  Serial.println("Serial Activated");

}

void loop() {
    static int previousPosition = -1;
    static boolean unused = false;
    static unsigned long timer = 0;
    int currentPosition = -1;

    // Scan through the switch pins looking for the current position.
    for (byte i=0; i<pinCount; i++) {
        if (digitalRead(pinList[i]) == LOW)
            currentPosition = i;
    }
    
    // Debugger
    if (debugger == true) {
    Serial.print("Running Position: ");
    Serial.println(currentPosition);
    }
    
    // If the current position is different from the previous position:
    if (currentPosition != previousPosition) {
        timer = millis();  // Start a timer.
        unused = true;  // Set an UNUSED flag.
        previousPosition = currentPosition;
    }

    // If the timer reaches "N" and the UNUSED flag is set:
    if (millis() - timer > 1000  && unused  && currentPosition != -1) { // One second
        // Act on the current position. (Your code goes here)
        Serial.print("Current Position: ");
        Serial.println(currentPosition);
        unused = false; // Clear the UNUSED flag.
    }
}

Now the only thing I need is a way to send a once time signal if the switch is in the off position (currentPosition = -1). Any ideas?

Thanks again for all your help.

EDIT: Changed to CODE instead of QUOTE.

wildbill:
Intermittent sounds like a floating input and sure enough, we have this:

        digitalWrite(pinList, HIGH);

Which however, doesn't compile in 1.0 and John has correct in his post above. Can you clarify what the latest code is?

That's an artifact of the posting. Since it was posted as a 'quote' and not 'code' the square brackets and 'i' in the original don't display. The code actually does say:

        digitalWrite(pinList[i], HIGH);

Using the code I posted above, I get the following return:

Position 1 = 0
Position 2 = 1
Position 3 = 2
Position 4 = 3
Position 5 = 4

This code works perfectly. Thank you.

Now to take it a step further, I need to know when the switch is in the 6th position (the OFF position). How can apply the same logic when none of the inputs are active? I got my switch today and it has 5 inputs and the 6th position is indeed off so I cannot add it to another pin.

Thanks in advance.

Just take out the part that says && currentPosition != -1 and you will get position -1 for off.

johnwasser:
Just take out the part that says && currentPosition != -1 and you will get position -1 for off.

That did it! Now to try and figure out this logic so I can commit it to memory. :slight_smile:

Thank you again for all your help.