Automatic Servo with Manual Override (delay problems)

Hello I am new to the forum, I couldn't find any thread that delt with these problems as a whole, and they act very differently when they are put together in one program.

I have a design expo on saturday and I am in some urgent help.

BACKGROUND:
I am building a camera mechanism that is automatically panning and controlled by a two button controlled servo. I want this device to automatically pan to 180 and back to 0 continuously until a button is pressed. Once a button is pressed(left or right) I want the servo to pan in the coinciding direction. Then i want the position of the servo to stop if the button was not pressed anymore, and then after a certain amount of time, have the automatic panning start back up.

OVERVIEW:
My code is in a case structure format. Case 0 is the automatic panning to the right, Case 1 is the automatic panning to the left. During both of these cases the code looks to see if any of the buttons are being pressed If they arent, they switch between each other, if a button is being pressed then the code changes the casenumber and breaks the loop.

Case 2 is for when the leftbutton is pressed and it pans left for that amount of time, Case 3 is the same thing but for the right button. In both of these cases I am looking to see when the button stops being pressed. If it is the servo should stay in its current position for some arbitrary time. I made an if statement that test to see whether the
"current time" - the "previous time" is greater than the interval and if it is it should break to the automatic.

However, my code seems to think this is always true and immediately breaks out of the loop, essentially making not delay

CODE:
Here's my code,
fast help is greatly appreciated

#include <Servo.h> 
 
Servo myservo;
const int leftbutton = 2;  //variable to represent the pin that the left button is on
const int rightbutton = 4;  //variable to represent the pin that the right button is on
int casenum = 0;  //case number to tell the servo which case to run
int pos = 0;  //position
int delaytime = 20;  // increasing this slows down the servo movement
unsigned long previousmillis = 0; // last time update
int interval = 2000; // interval at which to do something (milliseconds)
 
void setup()
{   
  
  
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
  
  pinMode(leftbutton, INPUT);  // the buttons are the inputs
  pinMode(rightbutton, INPUT);

} 

void loop()
{
  unsigned long currentmillis = millis();
  if (digitalRead(leftbutton))  //checks to see if each button is being pressed
        {
          casenum = 2;  //if so it switches the casenum
        }
        if (digitalRead(rightbutton))
        {
          casenum = 3;
        }
  
  switch (casenum)  //switch switches the case based on the value of casenum
  {
    case 0:
    for (pos = pos; pos < 180; pos += 1)  // pan right
      {
        
        myservo.write(pos);  // Move to next position
        delay(delaytime);  // Short pause to allow it to move
        if (digitalRead(leftbutton))  //checks to see if each button is being pressed
        {
          casenum = 2;  //if so it switches the casenum
          break;
        }
        if (digitalRead(rightbutton))
        {
          casenum = 3;
          break;
        }
      }

    casenum = 1;  //if none of the buttons are pressed then it goes to pan left
    break;
   
    case 1:
    for(pos = pos; pos >= 0; pos -= 1)  // pan left
      {
     
        myservo.write(pos);  // Move to next position
        delay(delaytime);  // Short pause to allow it to move
        if (digitalRead(leftbutton))  //checks to see if each button is being pressed
        {
          casenum = 2;  //if so it switches the casenum
          break;
        }
        if (digitalRead(rightbutton))
        {
          casenum = 3;
          break;
        }
        
      }
    casenum = 0;
    break;

    case 2:
    while (digitalRead(leftbutton))  //while the button is being pressed
    {
        myservo.write(pos);  //rotates servo to its current positon
        delay(delaytime);
        pos-=1;  //position becomes one less
    }
        myservo.write(pos);  //after the button stops being pressed the servo is at pos
        if(currentmillis - previousmillis >= interval)  //if the total time - the start time is greater than 3 sec 
        {          
          casenum = 1;                //this is where the code goes wrong, it seems like it reads this if statement 
                                      //and immedietly say thinks its true
          previousmillis = currentmillis;
          break;
        }
        if (digitalRead(rightbutton))
        {
        casenum = 3;
        break;
        }
        if (digitalRead(leftbutton))
        {
          casenum = 2;
          break;
        } 
      
      
    case 3: 
    while (digitalRead(rightbutton))
        {
          myservo.write(pos);
          delay(delaytime);
          pos +=1;
        }
        myservo.write(pos);
        if(currentmillis - previousmillis >= interval)  //if the total time - the start time is greater than 3 sec 
        {          
          casenum = 1;  //this is where the code goes wrong, it seems like it reads this if statement 
                        //and immedietly say thinks its true
          previousmillis = currentmillis;
          break;
        }
        if (digitalRead(rightbutton))
        {
          casenum = 3;
          break;
        }
        if (digitalRead(rightbutton))
        {
          casenum = 2;
          break;
        }

        default:
        casenum = 0;
        break;
  }  
}

This problem is in Case 2 & 3, sorry for not mentioning

Your problem is that you are only ever setting previousMillis in the beginning of the code and when the 3 seconds have been reached, so rather than measuring the time since the button was released, you're measuring the time since the start of the program the first time, and the time since you went back to your automatic mode each subsequent time. I would change the name of previousMillis to lastTimeButtonWasPressed. That should give you a better idea of where you should be updating it.

I understand that I am setting previousmillis in the beginning to 0. however isn't it changing in my loops when I say previousmillis = currentmillis

And can you elaborate on the lastTimeButtonWasPressed, I think I might get what your saying but its cloudy

landis:
however isn't it changing in my loops when I say previousmillis = currentmillis

Because you only ever set it when you say "the button has been released long enough for me to go back to state 0 and state 1".

And can you elaborate on the lastTimeButtonWasPressed, I think I might get what your saying but its cloudy

In how you want it to work, previousMillis should be set based on the state of the button (or more specifically, you want it to be set on the transition). As it stands now, there is no link between when previousMillis is set and the state of the button. I suggested changing the name to show that they should be related, as well as give you a hint on where it should be being set.

So now that I want my function to know when the last time I pushed the button, I should put it under the my while "button is being pressed loop" So then once the button stops being pressed it takes the time at that point.
Something like this?
Top Code:

#include <Servo.h> 
 
Servo myservo;
const int leftbutton = 2;  //variable to represent the pin that the left button is on
const int rightbutton = 4;  //variable to represent the pin that the right button is on
int casenum = 0;  //case number to tell the servo which case to run
int pos = 0;  //position
int delaytime = 20;  // increasing this slows down the servo movement
unsigned long LastTimeButtonWasPressed; // last time update
int endTime = 2000; // interval at which to do something (milliseconds)
 
void setup()

...

Case 2 Code:

 case 2:
    while (digitalRead(leftbutton))  //while the button is being pressed
    {
        myservo.write(pos);  //rotates servo to its current positon
        delay(delaytime);
        pos-=1;  //position becomes one less
    }
 LastTimeButtonWasPressed = millis();
        myservo.write(pos);  //after the button stops being pressed the servo is at pos
        if(currentmillis - LastTimeButtonWasPressed >= endTime)  //if the total time - the start time is greater than 3 sec 
        {          
          //casenum = 1;  //this is where the code goes wrong, it seems like it reads this if statement 
                        //and immedietly say thinks its true
          //previousmillis = currentmillis;
          //interval = millis() +;
          break;
        }

The comments are there because I am not sure what to do with that code.

With the current code it is not pausing at all.

Try moving the assignment statement inside the while loop. Outside of it, its still not being affected by the button state.

by putting the statement inside the while loop like so

case 2:
    while (digitalRead(leftbutton))  //while the button is being pressed
    {
        myservo.write(pos);  //rotates servo to its current positon
        delay(delaytime);
        pos-=1;  //position becomes one less
        LastTimeButtonWasPressed = millis();
    }
    myservo.write(pos);  //after the button stops being pressed the servo is at pos

It still does not delay.

However, I was playing around with this code and when I did this

 case 2:
    while (digitalRead(leftbutton))  //while the button is being pressed
    {
        myservo.write(pos);  //rotates servo to its current positon
        delay(delaytime);
        pos-=1;  //position becomes one less
    }
        myservo.write(pos);  //after the button stops being pressed the servo is at pos
        previousmillis = millis();
        
        if(millis() - 5000 >= 3000UL)  //if the total time - the start time is greater than 3 sec 
        {          
          //casenum = 1;  //this is where the code goes wrong, it seems like it reads this if statement 
                        //and immedietly say thinks its true
          //previousmillis = currentmillis;
          //interval = interval + millis();
          break;
        }

The code paused. But only for around 5 seconds.
There is also a time slot where it will delay and then it will just stand still after I press the button
and I am trying to get the significance of this, but I just can't figure it out

In the first piece of code, where is the if statement that determines how long it has been since LastTimeButtonWasPressed?

In the second piece of code, simply algebra would give you

if (millis() >= 8000)

Which says "if it has been 8 seconds since the processor was turned on or reset..."

I don't see how that's useful...

for the first code

while (digitalRead(leftbutton))  //while the button is being pressed
    {
        myservo.write(pos);  //rotates servo to its current positon
        delay(delaytime);
        pos-=1;  //position becomes one less
    }
        myservo.write(pos);  //after the button stops being pressed the servo is at pos
        previousmillis = millis();
        
        if(millis() >= 8000UL)  //if the total time - the start time is greater than 3 sec 
        {          
          //casenum = 1;  //this is where the code goes wrong, it seems like it reads this if statement 
                        //and immedietly say thinks its true
          //previousmillis = currentmillis;
          //interval = interval + millis();
          break;
        }

but for the second part, I thought the same thing, but with the -5000 on the left side of the inequality delays the panning for 5secs which doesn't make since to me

landis:
for the first code

while (digitalRead(leftbutton))  //while the button is being pressed

{
        myservo.write(pos);  //rotates servo to its current positon
        delay(delaytime);
        pos-=1;  //position becomes one less
    }




What happened to LastTimeButtonWasPressed ?



previousmillis = millis();




And/or why did you brink previousmillis back?



if(millis() >= 8000UL)  //if the total time - the start time is greater than 3 sec
        { 
          break;
        }




In English, what are you expecting this if statement to do? Is it your intention that your sketch behaves differently based on how long it has been running?

I'm sorry, this is not that clear but this is for the first code.

 case 2:
    while (digitalRead(leftbutton))  //while the button is being pressed
    {
        myservo.write(pos);  //rotates servo to its current positon
        delay(delaytime);
        pos-=1;  //position becomes one less
        LastTimeButtonWasPressed = millis();
    }
        
        if(millis() - LastTimeButtonWasPressed >= 3000UL)  //if the total time - the start time is greater than 3 sec 
        {          
          //casenum = 1;  //this is where the code goes wrong, it seems like it reads this if statement 
                        //and immedietly say thinks its true
          //LastTimeButtonWasPressed = millis();
          //interval = millis() +;
          break;
        }
        //if (digitalRead(rightbutton))
        //{
        //casenum = 3;
        //break;
        //}
        if (digitalRead(leftbutton))
        {
        casenum = 2;
        break;
        }

I've been accidentally pulling from different code files.

forget the other code. I showed you that to see if it sparked an idea. Because when that code was entered, for some odd reason, it delayed the panning again

        if(millis() - LastTimeButtonWasPressed >= 3000UL)  //if the total time - the start time is greater than 3 sec 
        {   
          break;
        }

Commented out code removed. Again, What are you expecting this to do?

if (digitalRead(leftbutton))
        {
        casenum = 2;
        break;
        }

And this? Why are your break statements conditional? 99.9% of the time, the break statement within a case statement should be the last thing done, and it should be done unconditionally.

I want the first code to become a timer. When this the total time - the time from when the button was last pressed is greater or equal to 3secs I want the code to pan automatically.

My overall idea with the conditionally breaks is that if the code reads that the leftbutton is being pushed it should rotate left and stay left once the button is not pushed. However, if either of the buttons are pushed again, I want it to restart the case and essentially restart the "delay" time.

landis:
I want the first code to become a timer. When this the total time - the time from when the button was last pressed is greater or equal to 3secs I want the code to pan automatically.

So you have the conditional part down, but if the condition fires, all you do is break out of the code to just go right back into it. How does that break statement make "the code pan automatically"?

My overall idea with the conditionally breaks is that if the code reads that the leftbutton is being pushed it should rotate left and stay left once the button is not pushed. However, if either of the buttons are pushed again, I want it to restart the case and essentially restart the "delay" time.

Think about this part:

if (digitalRead(leftbutton))
        {
        casenum = 2;
        break;
        }

casenum is already 2, since you're still within the "case 2:" part of the switch statement, so this entire piece of code does nothing for you.

Okay that lightens up the code content, thank you. But the problem is still with the delay. It will not stop for me, it just keeps panning.

Updated code? (the whole sketch, not snippits)

CODE:

#include <Servo.h> 
 
Servo myservo;
const int leftbutton = 2;  //variable to represent the pin that the left button is on
const int rightbutton = 4;  //variable to represent the pin that the right button is on
int casenum = 0;  //case number to tell the servo which case to run
int pos = 0;  //position
int delaytime = 20;  // increasing this slows down the servo movement
unsigned long LastTimeButtonWasPressed; // last time update
long interval = 3000UL; // interval at which to do something (milliseconds)
 
void setup()
{   
  
  
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
  
  pinMode(leftbutton, INPUT);  // the buttons are the inputs
  pinMode(rightbutton, INPUT);

} 

void loop()
{
  unsigned long StartVoidLoopTime = millis();
  if (digitalRead(leftbutton))  //checks to see if each button is being pressed
        {
          casenum = 2;  //if so it switches the casenum
        }
        if (digitalRead(rightbutton))
        {
          casenum = 3;
        }
  
  switch (casenum)  //switch switches the case based on the value of casenum
  {
    case 0:
    for (pos = pos; pos < 180; pos += 1)  // pan right
      {
        
        myservo.write(pos);  // Move to next position
        delay(delaytime);  // Short pause to allow it to move
        if (digitalRead(leftbutton))  //checks to see if each button is being pressed
        {
          casenum = 2;  //if so it switches the casenum
          break;
        }
        if (digitalRead(rightbutton))
        {
          casenum = 3;
          break;
        }
      }

    casenum = 1;  //if none of the buttons are pressed then it goes to pan left
    break;
   
    case 1:
    for(pos = pos; pos >= 0; pos -= 1)  // pan left
      {
     
        myservo.write(pos);  // Move to next position
        delay(delaytime);  // Short pause to allow it to move
        if (digitalRead(leftbutton))  //checks to see if each button is being pressed
        {
          casenum = 2;  //if so it switches the casenum
          break;
        }
        if (digitalRead(rightbutton))
        {
          casenum = 3;
         break;
        }
        
      }
    casenum = 0;
    break;

    case 2:
    while (digitalRead(leftbutton))  //while the button is being pressed
    {
        myservo.write(pos);  //rotates servo to its current positon
        delay(delaytime);
        pos-=1;  //position becomes one less
        LastTimeButtonWasPressed = millis();
    }
        
        if(StartVoidLoopTime - LastTimeButtonWasPressed >= interval)  //if the total time - the start time is greater than 3 sec 
        {    
          myservo.write(pos);      
          casenum = 0;  //this is where the code goes wrong, it seems like it reads this if statement 
                        //and immedietly say thinks its true
          //LastTimeButtonWasPressed = millis();
          LastTimeButtonWasPressed = StartVoidLoopTime;
          break;
        }
        if (digitalRead(rightbutton))
        {
        casenum = 3;
        break;
        }
      
      
    case 3: 
    while (digitalRead(4))
        {
          myservo.write(pos);
          delay(delaytime);
          pos +=1;
          LastTimeButtonWasPressed = millis();
        }
        myservo.write(pos);
        if(StartVoidLoopTime - LastTimeButtonWasPressed >= interval)  //if the total time - the start time is greater than 3 sec 
        {          
          casenum = 1;  //this is where the code goes wrong, it seems like it reads this if statement 
                       //and immedietly say thinks its true
          //previousmillis = currentmillis;
          break;
        }
        if (digitalRead(leftbutton))
        {
        casenum = 2;
        break;
        }
                
       default:
       casenum = 0;
       break;
  }  
}
LastTimeButtonWasPressed = StartVoidLoopTime;

You're clinging to hard to Blink Without Delay example here. LastTimeButtonWasPressed shouldn't be set while the button is not pressed (If it was pressed, it would still be in the while loop).

You're problem is still with your understanding of the break statements. Unless you know exactly what you are doing, you should be using an UNCONDITIONAL break at the end of every case statement. Since none of your break conditions are met in case 2 or case 3, as soon as the left button is released, it will fall through all the way to the default section, which sets the casenum to 0.

Okay,its making more sense. So because my if statement for the "delay" is not being accomplished in the first pass it is jumping out and going to the default case? How do I make it reloop to accomplish that if statement?

I don't know a way to get rid of the conditional breaks.