Servo Directional Control Using Two Button Inputs

There are many examples that allow users to rotate a standard servo 180 degrees by pressing a button, and then back to the initial position by pressing another.

However, I need a standard servo to rotate 180 degrees on its own once a button is pressed (without having to hold down a button), then rotate back to the initial position once the second button is pressed (again, not having to continuously keep the input high).

The latest example I'm using is from Instructables and seen below:

#include <Servo.h>

const int buttonPin = 2;

const int buttonPin2 = 3;

int buttonState = 0;

int buttonState2 = 0;

Servo servoA;

int position = 0;

void setup() {

servoA.attach(9);

pinMode(buttonPin, INPUT);

pinMode(buttonPin2,INPUT);

}

void loop() {

buttonState = digitalRead(buttonPin);

buttonState2 = digitalRead(buttonPin2);

if(buttonState ==HIGH && position < 180){

servoA.write(position++);

delay(5);

}

if(buttonState2 == HIGH && position > 3){

servoA.write(position--);

delay(5);

}

}

I have spent the past few days trying to research (Arduino forums, internet sites, YouTube, etc.) how standard servos (and continuous) behave and have made failed attempts at trying to write and modify my own sketches. Any help at trying to solve this problem would be greatly appreciated.

Take the for loops that move the servos out of your code and simply write the final destination angle to them.

I'm sure it's not what you meant, but I unsuccessfully tried the following with the void loop:

void loop() {

buttonState = digitalRead(buttonPin);

buttonState2 = digitalRead(buttonPin2);

if(buttonState ==HIGH){

servoA.write(180);

delay(5);

}

if(buttonState2 == HIGH){

servoA.write(0);

delay(5);

}
}

In the future, hit [CTRL] T in the IDE. This autoformats the code. Makes it easier to read. Helps identify incorrect use of brackets.

But your code looks good on the first glance. Can you describe the behavior of this latest sketch and its shortcomings?

I just wired up this piece and with some small tweaks, it works as I understand it.

#include <Servo.h>
const int buttonPin = 2;
const int buttonPin2 = 3;
int buttonState = 0;
int buttonState2 = 0;
Servo servoA;
int position = 0;

void setup() {
  servoA.attach(9);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
}

void loop() {
  buttonState = digitalRead(buttonPin);
  buttonState2 = digitalRead(buttonPin2);
  if (buttonState == LOW) {
    servoA.write(180);
    delay(5);
  }
  if (buttonState2 == LOW) {
    servoA.write(0);
    delay(5);
  }
}

I pulled up the inputs. I tested for LOW.
Without the pullups, when the button is not pushed, it floats, leading to all kinds of head scratching.
Unless you had external resistors to pull the input low.

Here is the working piece. Note that I violated the rule - ALWAYS POWER THE SERVOS FROM AN ADEQUATE SOURCE AND NOT FROM THE ARDUINO
But I chose one of my smaller servos for the test.

edit - I just looked at the instructable and it does indeed have external resistors to pull low.
So if wired properly, I would expect the same good behavior.

Can you snap a pic of your breadboard and attach it?

Thanks for the video...."I don't always code, but when I do, I drink Dos Equis".

When I use my modified code, my servo just seems to lock up at the 180 position.

When I used your latest, the servo doesn't even seem to to be powered (or attached, I guess).

Love the dos equis line! The Don Julio was out of the frame.

Can you fire up the serial monitor and print buttonstate and buttonstate2?

The button states seem to be working.

The serial monitor shows 0's for both button states when the input is low, and then 1's when either button is pressed.

I really appreciate the help, but I will have to revisit this in the morning, I'm about to pass out.

Too much dos equips?
When you circle back round, tell us more about the button values.
Both zeros when neither pressed.
And what when one is pressed? Your description is not clear.
I would expect a 0 and a 1
Then when you release, two 0 again
Then when you push the other button, a 1 and a 0

That was one long Dos Equips stupor, but back at it now.

I got the servo to rotate in both directions with push button releases with the code below:

#include <Servo.h>
Servo myservo;

const int buttonPin = 2;
const int buttonPin2 = 3;
int buttonState = 0;
int buttonState2 = 0;

void setup()
{
  myservo.attach(9);

  pinMode(buttonPin, INPUT);
  pinMode(buttonPin2,INPUT);

}
void loop()

{

  buttonState = digitalRead(buttonPin);
  buttonState2 = digitalRead(buttonPin2); 

  if (buttonState == HIGH) {

    myservo.write(180);    

  }

  if (buttonState2 == HIGH) {

    myservo.write(0); 

  } 

}

But now I'm going to throw a wrench in the gear.

I want the servo to rotate on its own by 180 degrees with a press and release of button1 (as the latest code allows). But I want it the servo to rotate in the opposite direction only while button 2 is being held high (as the initial code allowed).

Currently, I'm basically trying to combine the conditions from the past two sketches for button 2 but not having any success.

I'm basically trying to combine the conditions from the past two sketches for button 2 but not having any success.

Show us what you tried.

You need to change the reaction to the button 2 input being HIGH from an if to a while and move the servo a little, pause, move a little, pause and so on until either the button is released or the servo reaches its destination.

so write() sends the servo to that destination, period.

If you want to sweep while pressed, you will need to create a new function to do that and decide what happens after you release the switch (i.e. does it return to a position or does it just stop).

you will have to consider the speed of the sweep as well.

Thank you both.

This is the most current sketch I've been working with:

#include <Servo.h>
Servo myservo;

const int buttonPin = 2;
const int buttonPin2 = 3;
int buttonState = 0;
int buttonState2 = 0;

void setup()
{
  myservo.attach(9);

  pinMode(buttonPin, INPUT);
  pinMode(buttonPin2,INPUT);

}
void loop()

{

  buttonState = digitalRead(buttonPin);
  buttonState2 = digitalRead(buttonPin2); 

  if (buttonState == HIGH) {

    myservo.write(180);    

  }

  while (buttonState2 == HIGH) {

    myservo.write(0); 

  } 

}

Currently the servo only rotates when button one 1 is pressed, button two has no effect.

I'm afraid creating a new function is outside my scope, this will take more servo research on my part.

wickedhurricane:
Currently the servo only rotates when button one 1 is pressed, button two has no effect.

yes, I can see how it may not work as you expect.

wickedhurricane:
I'm afraid creating a new function is outside my scope, this will take more servo research on my part.

Did you at least take the advice of looking at the state change example provided (at no cost to you) in the examples in the IDE?

File -> Examples -> Digital -> StateChangeExample

I've looked at it and will have to think it over. Also have to hit the road so I'll revisit this in a couple of hours. Again, thank you both for the consideration.

Are you suggesting that button 1 (the push and release button) be contingent on the last button state?

I unsuccessfully put together the following code:

#include <Servo.h> 

Servo myservo;
int pos = 0; 

const int  button1Pin = 2;   
const int  button2Pin = 3;      

int button1PushCounter = 0;   
int button1State = 0;         
int button2State = 0;
int lastButtonState = 0;     

void setup() {  
  myservo.attach(9); 
  pinMode(button1Pin, INPUT);
  pinMode(button2Pin, INPUT);
  Serial.begin(9600);
}

void loop() {
  button1State = digitalRead(button1Pin);
  button2State = digitalRead(button2Pin);

  if (button1State != lastButtonState) {
    if (button1State == HIGH) {
      button1PushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes:  ");
      Serial.println(button1PushCounter);
    } 
    else {
      Serial.println("off"); 
    }
  }

  if(button1PushCounter % 1 == 0){
    for(pos = 0; pos <= 180; pos += 1){                                   
      {
        myservo.write(pos);                                        
      }
    }
  }

  if(button2State == HIGH){
    for(pos = 180; pos>=0; pos-=1){ 
      {    
        myservo.write(pos);              
      }
    }
  }

}

When neither button is pressed, the servo shakes. When button 1 is pressed, the shaking increases but there is no rotation. And when button 2 is pressed and held down, it rotates while shaking.

  if (button1PushCounter % 1 == 0)Are you sure this is correct ? What remainder will you get if you divide any number by 1 ?

Another question. How are your buttons wired ? Do you have any pulldown resistors in place to keep them LOW except when pressed or are they floating at an uncertain voltage ?

Thank you for your reply.

I do have pulldown resistors keeping the push buttons LOW until an input is received.

I wasn't sure about that last bit of code...it was just an attempt to keep button 1 HIGH once pressed and released.

Here is some psuedo code with some ideas of how I would do what you want. NOTE : Not tested in any way

returning = false
position = 180

start of loop()
  if button1 has become pressed
    servo.write(180)
    returning = false
  end if
  
  if button2 has become pressed
    returning = true
    position = 180
  end if

  if returning == true
    servo.write(position)
    delay a while
    decrement position
    if position == 0
      returning = false
    end if
  end if
end of loop()

Note that the delay may be better implemented using millis() if the program needs to be doing anything else other than moving the servo.