User Control AND a Program

There are a variety of ways to code this type of thing, but you are actually trying to solve 2 problems here:

1st, you are trying to “debounce” the signal from the pushbutton. This is necessary because the microprocessor is extremely fast compared to the two contacts being pressed together in the switch. Your code may be able to detect several (perhaps dozens) of individual switch open/closes each time you press or release the button once. This is usually coded around by requiring the switch to show a steady reading for some number of consecutive times through a loop, meaning the signal has settled into one state or the other.

2nd, you are trying to switch between two (or more) modes, advancing to the next mode any time the switch is pressed.

As it turns out, the solution for both of these is fairly similar. It involves using a variable to remember what was happing on previous trips through the loop, and then jumping into action whenever an important state change occurs.

Here is some example code to handle debouncing a button on I/O port 5:

#define DEBOUNCE (50) // Number of consecutive readings that must match.

unsigned char DebounceCounter;  // Counts consecutive matching reedings
unsigned char CurrentButton;    // This is the state of the button now
unsigned char DebouncedButton;  // This is the "debounced" button state

while ( 1==1 )   // Main program loop
{
    CurrentButton = GetDigitalInput(5);
    if ( CurrentButton == DebouncedButton )
    {
        DebounceCounter = 0;
    }
    else
    {
        DebounceCounter++;
        if ( DebounceCounter >= DEBOUNCE )
        {
            DebouncedButton = CurrentButton;
            // DEBOUNCED BUTTON HAS JUST CHANGED STATE
        }
    }
    
    // ... Do all your other program stuff here ...
}

That gets you a debounced pushbutton. Using a smaller value for DEBOUNCE will make the button more responsive, but will also make it more sensitive to bounces. Bumper switches tend to be bouncier than the limit switches, so you can probably use a smaller number to debounce a limit switch.

Now, if you now want to use that debounced input to control your program mode, then you might add some code like so:

#define DEBOUNCE (50) // Number of consecutive readings that must match.
#define MAX_MODE (1)  // Mode number will be between 0 and this number

unsigned char DebounceCounter;  // Counts consecutive matching reedings
unsigned char CurrentButton;    // This is the state of the button now
unsigned char DebouncedButton;  // This is the "debounced" button state
unsigned char Mode = 0;         // Keeps track of your current mode.

while ( 1==1 )   // Main program loop
{
    CurrentButton = GetDigitalInput(5);
    if ( CurrentButton == DebouncedButton )
    {
        DebounceCounter = 0;
    }
    else
    {
        DebounceCounter++;
        if ( DebounceCounter >= DEBOUNCE )
        {
            DebouncedButton = CurrentButton;
            // DEBOUNCED BUTTON HAS JUST CHANGED STATE
            if ( DebouncedButton == 0 )
            {
                // Button was just pressed
                Mode++; // each button press advances to next mode
                if ( Mode > MAX_MODE )
                    Mode = 0; // start back at mode 0 if we go past MAX_MODE
            }
        }
    }
    
    if ( Mode == 0 )
    {
        // ... Do all your Mode 0 stuff here ...
    }

    if ( Mode == 1 )
    {
        // ... Do all your Mode 1 stuff here ...
    }

    // ... Etc ...
}

Hope this helps,

  • Dean

I think this is wrong. You have to do:

#include "Main.h"

void main (void)
{
int VARIABLE

While (1 == 1)
{
VARIABLE = GetDigitalInput (5)
if (VARIABLE == 0)
{
(Maze Program)
}
else
{
(User Control)
}
}
}

Just swap (Maze Program) with your maze function and (User Control) with your controller function. Just put in the jumper to turn on the maze function and take it out to run the user control function. Simple.

Please correct me if I’m wrong.

Technically, you would have to have the includes,void main(), and you could just adjust the order of the maze and user control code but I was just showing the code you would need to add on the graphic interface.GetDigitalInput(1) returns a value indicating the state of the input so, you don’t need to assign it to another variable, you just have to type it in manually.

Oh, sorry.

No problem, I would do the same thing if I thought someones code was wrong.

Thanks a million for the code! Just one more thing…

Your code is for use with a digital input, and I need it for an rx input. Would I just replace


CurrentButton = GetDigitalInput(5);

with


CurrentButton = GetRxInput(5);

?
…or

if ( GetRxInput(5) == 127 )
     CurrentButton = 0
if ( GetRxInput(5) == 255 )
     CurrentButton = 1

would I have to create a statement to compensate for the pwm values?

Oops - for some reason, I had it stuck in my head that you were doing this with a button locally on the robot, rather than from the remote.

The good news is that you can eliminate the debouncing code, since the signal coming from the transmitter has already been debounced.

Your second code fragment would come pretty close to working (but you’d have to swap the 0 and the 1 … my code assumes 0 means “button pressed”).

Here is a simplified example with the debugging goo taken out:


#define MAX_MODE (1)      // Mode will be between 0 and this number

unsigned char Button = 0; // Tracks the state of the button
unsigned char Mode = 0;   // Keeps track of your current mode

while ( 1==1 )   // Main program loop
{
    If ( Button < 192 )
    {
        // Previously, button was not pressed
        Button = GetRxInput(5);
        if ( Button > 192 )
        {
            // But now, button IS pressed
            Mode++; // each button press advances to next mode
            if ( Mode > MAX_MODE )
            {
                Mode = 0; // start back at mode 0 if we go past MAX_MODE
            }
        }
    }
    
    if ( Mode == 0 )
    {
        // ... Do all your Mode 0 stuff here ...
    }

    if ( Mode == 1 )
    {
        // ... Do all your Mode 1 stuff here ...
    }

    // ... Etc ...
}

I use 192 as a threshold because it is about halfway between 127 and 255. I’ve never seen the button channels produce values other than 0/127/255, but this just ensures things don’t go wrong if a 254 sneaks in somehow.

Cheers,

  • Dean

Yeah, that looks great! One last thing: With the MAX_MODE constant, does this mean I can increase the number to include more than 2 modes?

Yep - then you just add more “if ( Mode == # )” blocks for the additional modes.

If you go much past 2 modes, you’ll probably have some trouble figuring out what mode you are in without some feedback. In the past, I’ve used a servo to make a small “meter” that had 5 markings (corresponding with 0,64,128,192,255) for this type of thing. For the indicator, I just used a shaft collar with the set-screw replaced with a 1" long #8-32 coupler (see P/N: COUPLER-832-1000-25). At the top of each “if ( Mode == #1 )” block, just set the meter servo to the appropriate position for that mode.

I want to point out, though, that you should not put long or infinite loops (like while(1)) inside these “if blocks”. The program counts on each of these “if blocks” falling out the bottom so that it can check the ch5 button fairly frequently.

Cheers,

  • Dean

I just got done loading the code, and it works great, until the button is pressed again. It then stays on “Mode 1” code (in my case, SetDigitalOutput (14,1)) no matter how many times the button is pressed. I coppied the code dirrectly, too.

The code that is intended to prevent that from happening is:

        ...
            if ( Mode > MAX_MODE )
            {
                Mode = 0; // start back at mode 0 if we go past MAX_MODE
            }
        ...

Are you sure that your code for “Do all your Mode 1 stuff here” isn’t looping? It needs to fall-through the bottom of the “if” statement frequently so that the button state can be re-checked…

As I played with the code last night, I decided to add a “PrintToScreen” for the Mode variable. No mater where I put the

if ( Mode > MAX_MODE )
            {
                Mode = 0; // start back at mode 0 if we go past MAX_MODE
            }

the variable kept counting up. It’s almost like it just ignores this bit of code.

The only thing I can think of is that the compiler is confused by the definition of MAX_MODE.
Try putting your highest mode number there as a constant:


if ( Mode > 2 ) ...

  • Dean

Well, after all that, I decided to use a few “If” statements and channels 3 and 4 for what I needed. I tried everthing, but still could’t make it work. Thanks so much for all the help. A new Nelson III video might be coming soon.