Fun with Arduino 26 Tune a Servo with a Rotary Encoder

servoWith almost every application of a servo motor we have to find the proper angles to operate the device / construction we are going to drive with it. In video 25 we created code to read a rotary encoder and switch. In this video we are going to use this code to tune the servo angles. The switch is used to move the servo to the MIN or the MAX angle. The rotary encoder is then used to modify these angles to find the right ones for the application. The angles can be read out on the serial monitor and can then be transferred to the specific servo application software.

Read on below the video …

So … the challenge is to add the code to control a servo to the Rotary Encoder code that we created in video 25. We #include <servo.h>, create a servo, add an array with is going to contain the MIN and MAX setpoint angles, we increment or decrement the servo angle if it’s not equal to the setpoint and write it to the servo. The new lines are shown in red.

#define ENC_DATA_PIN       10
#define ENC_CLK_PIN        11
#define ENC_SW_PIN         12
#define INCREMENT           1
#define SERVO_PIN           3
#define SERVO_INTERVAL     30 // [ms] time between servo steps
#define MIN_ANGLE           0
#define MAX_ANGLE         180

byte enc_clk,    enc_clk_old;
byte enc_switch, enc_switch_old;
byte pointer; // pointer to setpoint[pointer]
int angle = (MIN_ANGLE + MAX_ANGLE) / 2;
int setpoint[2] = {angle, angle + 10}; //startup angles
unsigned long timeforservo;

#include <Servo.h>
Servo servo;

void setup() { 
  pinMode (ENC_CLK_PIN,INPUT_PULLUP);
  pinMode (ENC_DATA_PIN,INPUT_PULLUP);
  pinMode (ENC_SW_PIN,INPUT_PULLUP);
  servo.attach(SERVO_PIN);
  Serial.begin (9600);
  Serial.print("SETPOINT = ");
  Serial.println(setpoint[pointer]);
  enc_clk_old    = digitalRead(ENC_CLK_PIN);
  enc_switch_old = digitalRead(ENC_SW_PIN);
}

void loop() {
// READ SWITCH AND CHANGE SERVO SETPOINT
  enc_switch = digitalRead(ENC_SW_PIN);
  if((enc_switch == 0) && (enc_switch_old == 1)) { // 1->0 transition detected
    pointer = (pointer + 1) % 2; // change servo setpoint 
    Serial.print("SETPOINT = ");
    Serial.println(setpoint[pointer]);
    delay(10);
  }
  enc_switch_old = enc_switch;

// READ ROTARY AND MODIFY SERVO SETPOINT MIN, MAX 
  enc_clk = digitalRead(ENC_CLK_PIN);
  if((enc_clk == 1) && (enc_clk_old == 0)) { // 0->1 transition
    if(digitalRead(ENC_DATA_PIN) == 1) setpoint[pointer] = setpoint[pointer] + INCREMENT;
    else setpoint[pointer] = setpoint[pointer] - INCREMENT;
    if(setpoint[pointer] > MAX_ANGLE) setpoint[pointer] = MAX_ANGLE;
    if(setpoint[pointer] < MIN_ANGLE) setpoint[pointer] = MIN_ANGLE;
    Serial.println(setpoint[pointer]);
    delay(10);
  }
  enc_clk_old = enc_clk;

// ROTATE SERVO
  if (millis() > timeforservo) {
    timeforservo = millis() + SERVO_INTERVAL;
    if (angle < setpoint[pointer]) angle++;
    if (angle > setpoint[pointer]) angle--;
    servo.write(angle);
  }
}

Lets test and see if it works. Well … yes it does.

However … there are a couple of things that could improve this servo tuner:

  • With 1 increment it takes a lot of rotating when we have to change the angle many degrees. If we can distinguish between a slow or a fast rotation, we would be able to have 2 different increments, say 1 and 10.
  • Also with the switch we could distinguish between a short and a long press. That way we can toggle between the MIN and MAX angle via short presses, while we can use a long press to go to the 90 degrees midpoint.

In the next video we are going to add these features and in the process we will also eliminate bouncing using a timer, not the delay().

— 0 —

 

 

 

Leave a comment