Controlling Micro Servo with 2 Buttons and 4 Button States

Hi! I'm very very new to Arduino so I apologize if my question seems very basic. I am trying to control a micro-servo to go to 4 different positions (10, 60, 120, 170) with 2 buttons attached to separate digital pins (5+6). I understand how to get the micro servo to go to a position when just one button is HIGH/LOW but I don't know how to make it go to a specific position if say button 1=HIGH and button 2=HIGH. When I tried to use if statements the micro servo just kept stuttering as if it was struggling to go between two different positions. I would also like to write this information on an LCD.
In summary...

  1. Four Different Positions linked to Four Different Button Mvts
    a. 10=Button 1 (HIGH), Button 2(HIGH)
    b. 60=Button 1(LOW), BUtton 2(HIGH)
    c. 120=Button 1(HIGH), Button 2(LOW)
    d. 170=Button1(LOW), Button2(LOW)

  2. Get LCD to display the "position" of the micro-servo (10,60,120,170)

Thanks for your help!

Code:
#include <Servo.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,7,4,3,2);
int x;
int prox=A2;
int val;

int button1 = 6; //button pin, connect to ground to move servo
int press1 = 0;
int button2 = 5; //button pin, connect to ground to move servo
int press2 = 0;
int press3=0;
int press4=0;
Servo servo1;

void setup()
{
lcd.begin(16,2);
pinMode(A2, INPUT);
Serial.begin(9600);
pinMode(9, OUTPUT);

pinMode(button1, INPUT);
pinMode(button2, INPUT);
servo1.attach(9);
digitalWrite(6, HIGH); //enable pullups to make pin high
digitalWrite(5, HIGH); //enable pullups to make pin high
}

void loop()
{

press1 = digitalRead(button1);
if (press1 == HIGH && press2==HIGH)
{
servo1.write(170);
}

press2 = digitalRead(button2);
if (press2 == LOW && press2==LOW)
{
servo1.write(10);

}

}

How are you buttons wired?

What is happening here?
if (press2 == LOW && press2==LOW)

Try:
press1 = digitalRead(button1);
press2 = digitalRead(button2);

then do your if conditions
going to need some delay for servos to get there.

Thanks for your help! Button 1 is wired to GND and digital pin 6. Button 2 is wired to GND and digital pin 5. I am confused how you would create more than just 2 button states. Could you possibly use a switch code of some sort?

Something like this:

//00
if (press2 == LOW && press1==LOW)
{
  servo1.write(10);
  //need some time for servo to get there
  delay(1000);
}
//01
else if (press2 == LOW && press1==HIGH)
{
  servo1.write(60);
  //need some time for servo to get there
  delay(1000);
}
//10
else if (press2 == HIGH && press1==LOW)
{
  servo1.write(120);
  //need some time for servo to get there
  delay(1000);
}
//11
else if (press2 == LOW && press1==HIGH)
{
  servo1.write(170);
  //need some time for servo to get there
  delay(1000);
}

For something like this, you can use bit shifting.
I am making the buttons use the internal pull-up resistors, so when I do a digitalRead of the pin, I need to have ! in front of it. Otherwise it will start at 00000011 and go down from there instead of up.

void setup() 
{
  // put your setup code here, to run once:
  pinMode(2, INPUT_PULLUP); // enable internal pull-up resistors
  pinMode(3, INPUT_PULLUP); // enable internal pull-up resistors
  Serial.begin(115200); // begin the serial monitor at 115200 baud, (very fast), but 9600 is ok too
}

void loop() 
{
  // put your main code here, to run repeatedly:

  byte result; // make a variable of type byte. 
  // I put it here due to force of habit, but you can make it global (top of code, used through out the code) 
  // or in a function where it is only created, seen, used and destroyed by that function.

  result = (!digitalRead(2) << 1) | !digitalRead(3);
  // Not sure where to start with this one...
  // Since I am using the internal pull-up resistors, the digitalReads will always return 1
  // when the buttons are NOT pressed and a 0 when they ARE pressed.
  // To get the proper result of 1 when pressed, I need to add the NOT(!) operator in 
  // front of the digitalRead() functions.
  // (!digitalRead(2) "<< 1") the << 1, will take the value returned from the digitalRead and shift it by 1 to the left.
  // So if the byte is 00000001, doing a << 1, will temporarily make it 00000010, until stored in a variable.
  // "| !digitalRead(3)" the | symbol is logic OR, and with this I can take the value from !digitalRead(3)
  // (00000001) and combine it with the value from "!digitalRead(2) << 1" -> (00000010) to produce 00000011

  Serial.println(result, BIN); // show the "result" in a binary representation, 1's and 0's, (leading zeros will be taken off)
}

From here you can use either case statements or IF statements for your servos.

switch( result )
{
case 0:
  servo1.write(10);
  break;

case 1:
  servo1.write(60);
  break;

case 2:
  servo1.write(120);
  break;

case 3:
  servo1.write(170);
  break;

default: break; // In case of a weird result
}

Sorry,
//11
else if (press2 == LOW && press1== HIGH)

should be

//11
else if (press2 == HIGH && press1== HIGH)

Thank you so so much this was so helpful! :slight_smile:
I have one other quick question. I wanted to add a proximity sensor to my project, the values of which would be constantly mapped to the servo-motor. What kind of code/Interrupt would i have to have so that when a button/buttons were pushed it would stop mapping and just go to the position set by the if and else if statements.

Updated Code:

#include <Servo.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,7,4,3,2);
int x;
int prox=A2;
int val;

int button1 = 6; //button pin, connect to ground to move servo
int press1 = 0;
int button2 = 5; //button pin, connect to ground to move servo
int press2 = 0;
Servo servo1;

void setup()
{
lcd.begin(16,2);
pinMode(A2, INPUT);
Serial.begin(9600);
pinMode(9, OUTPUT);

pinMode(button1, INPUT);
pinMode(button2, INPUT);
servo1.attach(9);
digitalWrite(6, HIGH); //enable pullups to make pin high
digitalWrite(5, HIGH); //enable pullups to make pin high
}

void loop()
{
x=analogRead(A2);
Serial.println(x);
val=analogRead(prox);
val=constrain(val, 10, 500);
val=map(val,10,500,0,179);
servo1.write(val);
delay(500);

lcd.setCursor(0,0);
lcd.print("Position");

lcd.setCursor(0,1);
lcd.print(x);
lcd.print(" ");
delay(500);

press1 = digitalRead(button1);
press2 = digitalRead(button2);

if (press2 == LOW && press1==LOW)
{
servo1.write(0);
delay(100);
//need some time for servo to get there

}
//01
else if (press2 == LOW && press1==HIGH)
{
servo1.write(60);
delay(100);
//need some time for servo to get there
}
//10
else if (press2 == HIGH && press1==LOW)
{
servo1.write(120);
delay(100);
//need some time for servo to get there

}
//11
else if (press2 == HIGH && press1==HIGH)
{
servo1.write(170);
delay(100);
//need some time for servo to get there

}
}

geysen:
Hi! I'm very very new to Arduino

HazardsMind:
For something like this, you can use bit shifting

Not really a method for a beginner HM, and I think it would be fair if you offered a bit of an explanation of what that approach does. Yeah, he can Google that of course, but it's really worthy of a few words of explanation from you, imo.

What kind of code/Interrupt would i have to have so that when a button/buttons were pushed it would stop mapping and just go to the position set by the if and else if statements.

Have another button, and give it an if-else too. Then put all of what you have so far in one half of the if-else, and the new stuff in the other half.

That way the new button will control which of the two methods is active. (That's very much off the top of my head, YMMV.)

Yes you would have to think of a third button.

Since no presses gives:
press1 = HIGH and press2 = HIGH
So you would never get to 170

When you attach a sketch for us to look at use the insert code icon.

2014-11-19_21-59-49.jpg

Not really a method for a beginner HM, and I think it would be fair if you offered a bit of an explanation of what that approach does.

I added comments, hopefully they help. If not, then a moderator can delete it.

I'm trying to get into the habit of not wasting RAM :cry: .

HazardsMind:
I added comments, hopefully they help.

That's cool... maybe I should look at them too.

I'm trying to get into the habit of not wasting RAM

Good 8)
Could read the port and mask it also in one step but that's a whole other thing.

Thank you all so much for your help it was so instructive! I was wondering if there was way of having the proximity sensor modify the micro-servo position established by the buttons rather than replace it. In essence, a way for them to work together rather than separately. If you have any ideas they would be greatly appreciated and thanks again for all your help.

Thank you all so much for your help it was so instructive! I was wondering if there was way of having the proximity sensor modify the micro-servo position established by the buttons rather than replace it. In essence, a way for them to work together rather than separately. If you have any ideas they would be greatly appreciated and thanks again for all your help.

#include <Servo.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,7,4,3,2);
int x;
int prox=A2;
int val;

int button1 = 6; //button pin, connect to ground to move servo
int press1 = 0;
int button2 = 5; //button pin, connect to ground to move servo
int press2 = 0;
Servo servo1;



void setup()
{
  lcd.begin(16,2);
  pinMode(A2, INPUT);
  Serial.begin(9600);
  pinMode(9, OUTPUT);
  
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  servo1.attach(9);
  digitalWrite(6, HIGH); //enable pullups to make pin high
  digitalWrite(5, HIGH); //enable pullups to make pin high
}

void loop()
{
  Serial.println(val);
  val=analogRead(prox);
  val=constrain(val, 10, 500);
  val=map(val,10,500,0,179);
  servo1.write(val);
  delay(500);
  
  lcd.setCursor(0,0);
  lcd.print("Position");
 
  lcd.setCursor(0,1);
  lcd.print(x);
  lcd.print("     ");
  delay(500);
  
  press1 = digitalRead(button1);
press2 = digitalRead(button2);

if (press2 == LOW && press1==LOW)
{
  servo1.write(0);
  delay(1000);
  //need some time for servo to get there

}
//01
else if (press2 == LOW && press1==HIGH)
{
  servo1.write(60);
   delay(1000);
  //need some time for servo to get there
}
//10
else if (press2 == HIGH && press1==LOW)
{
  servo1.write(120);
   delay(1000);
  //need some time for servo to get there

}
//11
else if (press2 == HIGH && press1==HIGH)
{
  servo1.write(170);
   delay(1000);
  //need some time for servo to get there

}
}

How often does the proximity sensor value change?

In essence, a way for them to work together rather than separately.

What do you mean? They both move the servo, the only issue comes from the Prox sensor per loop. Regardless of what you set the servo to by the buttons, the Prox sensor will overwrite it. The way to fix this is to see if the value from the Prox sensor ever changes or not. If it does not change then you are free to use the buttons, but if it does change then it will take presidence.

I know my wording was very difficult to understand but heres what im trying to make it do. When a button (or button combination) is pushed the proximity sensor can change the value of the micro servo slightly: increasing slightly as an object gets closer and decreasing slightly when an object gets further away. When a different button is pushed it will have a different range of possible proximity values that it can vary between. Is that clearer?

here is my updated code

#include <Servo.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,5,4,3,2);
int prox=A2;
int val;
int x;
int val2;

int button1 = 6; //button pin, connect to ground to move servo
int press1 = 0;
int button2 = 7; //button pin, connect to ground to move servo
int press2 = 0;
Servo servo1;

void setup()
{
lcd.begin(16,2);
pinMode(A2, INPUT);
Serial.begin(9600);
pinMode(9, OUTPUT);

pinMode(button1, INPUT);
pinMode(button2, INPUT);
servo1.attach(9);
digitalWrite(6, HIGH); //enable pullups to make pin high
digitalWrite(7, HIGH); //enable pullups to make pin high
}

void loop()
{

val= analogRead(prox);
Serial.println(val);
val=constrain(val, 10, 500);

press1 = digitalRead(button1);
press2 = digitalRead(button2);

if (press2 == LOW && press1==LOW)
{ lcd.setCursor(0,0);

lcd.print("ZOOM! ");
delay(100);

if (val <= 150 && val>=10) {
val2=analogRead(prox);
val2=constrain(val2, 10, 150);
val2 = map(val2, 10, 150, 40, 10);
servo1.write(val2); }

else {
servo1.write(20);
delay(100);
}

//need some time for servo to get there

}
//01
else if (press2 == LOW && press1==HIGH)
{
lcd.setCursor(0,0);
lcd.print("Almost There! ");

if (val <= 250 && val >= 150) {
val2=analogRead(prox);
val2=constrain(val2, 150, 250);
val2 = map(val2, 150, 250, 80, 50);
servo1.write(val2);
}

else
{servo1.write(60);
delay(100);
}

//need some time for servo `to get there
}
//10
else if (press2 == HIGH && press1==LOW)
{
lcd.setCursor(0,0);
lcd.print("Not Quite! ");

if (val <= 400 && val >= 250) {

val2=analogRead(prox);
val2=constrain(val2, 250, 400);
val2 = map(val2, 250, 400, 130, 90);
servo1.write(val2);
}

else {
servo1.write(120);
delay(100);

}

//need some time for servo to get there

}
//11
else if (press2 == HIGH && press1==HIGH)
{
lcd.setCursor(0,0);
lcd.print("Press the Gas!");

if (val <= 500 && val >= 400) {

val2=analogRead(prox);
val2=constrain(val2, 400, 500);
val2 = map(val2, 400, 500, 179, 135);
servo1.write(val2);
}

else {
servo1.write(170);
delay(100);

}

//need some time for servo to get there

}
}

You can do it that way or you can make 2 arrays with 4 indexes.

High[4] = {150, 250, 400, 500 };
Low[4] ={10, 150, 250, 400 };

You then would use the buttons to change the index value.
(This is where my code will be the most helpful, because it already returned the correct index values when the buttons were pressed)

You may also want to have two arrays for your servo high and low ranges too.

Put that all together and your code will shrink tremendously.