Fun with Arduino 27 Rotary Encoder Recognize Slow or Fast Rotation.

Remark: In case you don’t have a rotary encoder, at the end of the article you can find code to tune a servo by just using a wire, or push buttons.

In this video the goal is to improve the Servo Tuner of video 26 with two features :

  • Distinguish between slow or fast encoder rotation. We can use this to inrement the angle with small or larger steps.
  • Distinguish between a short and a long press of the switch. A short press toggles between the min/max setpoint angles. A long press moves the servo to its mid point.

To get this done we are going to use the millis() function. For the switch, we start a timer as soon as it is pressed. We read out the timer when it is released, and if ((millis() – enc_pressed_time) > PRESS_TIME_LONG) we know it was a long press, else it was short.

For the rotary encoder we start a timer at every LOW to HIGH transition and we read it out at the next LOW to HIGH transition, and if ((millis() – enc_rotated_time) > ROTARY_SLOW) we know if it was a slow rotation, else it was fast.

In both cases we also do a second test, on PRESS_TIME_SHORT and on ROTARY_FAST. If the measured time was shorter or faster than those, we know it was due to bouncing.

Read on below the video …

Let’s add the code that does the time measurement to the servo tune code of the previous video 26. The lines that are added are shown in red.

#define ENC_DATA_PIN       10
#define ENC_CLK_PIN        11
#define ENC_SW_PIN         12
#define SERVO_PIN           3
#define SERVO_INTERVAL     30 // [ms] time between servo steps
#define MIN_ANGLE           1
#define MAX_ANGLE         179
#define PRESS_TIME_SHORT   20 // [ms] short press
#define PRESS_TIME_LONG  2000 // [ms] long  press
#define ROTARY_FAST        10 // [ms] rotary speed fast
#define ROTARY_SLOW       200 // [ms] rotary speed slow
#define INCREMENT_SLOW      1
#define INCREMENT_FAST     10

byte increment;
byte pointer; // pointer to setpoint[pointer]
byte enc_clk,    enc_clk_old;
byte enc_switch, enc_switch_old;
int angle = (MIN_ANGLE + MAX_ANGLE) / 2;
int setpoint[3] = {angle - 5, angle + 5, angle};
unsigned long time_for_servo, enc_pressed_time, enc_rotated_time;

Servo servo;

void setup() { 
  Serial.begin (9600);
  Serial.print("SETPOINT = ");
  enc_clk_old    = digitalRead(ENC_CLK_PIN);
  enc_switch_old = digitalRead(ENC_SW_PIN);

void loop() {
  enc_switch = digitalRead(ENC_SW_PIN);
  if(((enc_switch_old == 1) && enc_switch == 0)) enc_pressed_time = millis(); // 1->0 transition
  if((enc_switch_old == 0) && (enc_switch == 1)) { // 0->1 transition
    if((millis() - enc_pressed_time) > PRESS_TIME_LONG) { // long press
      pointer = 2; // servo to setpoint[2] = mid angle
      Serial.print("Long press: SETPOINT = ");
    else if((millis() - enc_pressed_time) > PRESS_TIME_SHORT) { // short press
      pointer = (pointer + 1) % 2;  
      Serial.print("Short press: SETPOINT = ");
    else Serial.println("SW BOUNCED");
  enc_switch_old = enc_switch;

  enc_clk = digitalRead(ENC_CLK_PIN);
  if((enc_clk == 1) && (enc_clk_old == 0)) { // 0->1 transition
    if((millis() - enc_rotated_time) > ROTARY_SLOW) {
      increment = INCREMENT_SLOW;  
    else if((millis() - enc_rotated_time) > ROTARY_FAST) {
      increment = INCREMENT_FAST;  
    else {
      Serial.println("ROT BOUNCED");
      increment = 0;  
    enc_rotated_time = millis();
    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]);   }   enc_clk_old = enc_clk; // INCREMENT SERVO   if (millis() > time_for_servo) {
    time_for_servo = millis() + SERVO_INTERVAL;
    if (angle < setpoint[pointer]) angle++;     if (angle > setpoint[pointer]) angle--;

Does it work? Yes, the video shows it does.

In the next video we are going to have a look at how we can use an ‘external editor’. The Arduino IDE code editor is just OK, but not more than that. There are much nicer editors around, which are free to use. The Arduino IDE has the feature that lets us use an external editor while we can still easily upload the code to the Arduino and test our software without any copy / paste or file opening actions needed.

In case you don’t have a rotary encoder, this is code to tune a servo using a wire or push buttons to connect A0-A4 GND. The purpose of A0 – A4 is explained in the code.

// RudyB, April 2019 - Servo Tune with Push Buttons
// OPERATION - make a pin LOW (GND) with a pushbutton or a wire:
//   A0: Go to  CW angle ( lower angle values)
//   A1: Go to CCW angle (higher angle values)
//   A2: Go to middle angle (MIN + MAX) / 2
//   A3: Decrease angle of current setpoint, move  CW
//   A4: Increase angle of current setpoint, move CCW

#define SERVO_PIN           3 // pin to which the servo is attached
#define SERVO_INTERVAL     30 // [ms] interval between servo steps, lower number is higher speed
#define SETPOINT_INTERVAL 250 // [ms] interval between setpoint updates if A3,A4 inputs are LOW
#define MAX_ANGLE         180 // may depend on your mechanics OR for 90 degree servo
#define MIN_ANGLE           0 // may depend on your mechanics

#define ON_BOARD_LED 13 // led on the Arduino pcb
Servo servo; //Servo functions included
byte angle = (MIN_ANGLE + MAX_ANGLE) / 2;
byte setpoint[3] = {angle - 5, angle + 5, angle}; //startup angles
byte sp = 2; //setpoint pointer
byte setpoint_old;
unsigned long timeforservo, timetoupdatesetpoint, timetoprint;

void setup() {
  servo.attach(SERVO_PIN, 1000, 2000); //servo pin, us pulse min, us pulse max

void loop() {
  if (!digitalRead(A0)) sp = 0; //go to  CW angle
  if (!digitalRead(A1)) sp = 1; //go to CCW angle
  if (!digitalRead(A2)) sp = 2; //go to middle angle

  if (millis() > timetoupdatesetpoint) {
    timetoupdatesetpoint = millis() + (unsigned long)SETPOINT_INTERVAL;
    if (!digitalRead(A3)) setpoint[sp]--; //  CW
    if (!digitalRead(A4)) setpoint[sp]++; // CCW
    if (setpoint[sp] > MAX_ANGLE) setpoint[sp] = MAX_ANGLE;
    if (setpoint[sp] < MIN_ANGLE) setpoint[sp] = MIN_ANGLE;     if (setpoint[sp] != setpoint_old) {       Serial.println(setpoint[sp]);       setpoint_old = setpoint[sp];     }   }   if (millis() > timeforservo) {
    timeforservo = millis() + SERVO_INTERVAL;
    if (angle < setpoint[sp]) angle++;     if (angle > setpoint[sp]) angle--;
		if (angle == setpoint[sp]) digitalWrite(ON_BOARD_LED,LOW); else digitalWrite(ON_BOARD_LED,HIGH);

— 0 —


2 thoughts on “Fun with Arduino 27 Rotary Encoder Recognize Slow or Fast Rotation.

  1. Hi Rudy
    Great videos. Thank you very much for your great work.
    Will you allready now reveal which external Editor you have selected ?
    Jens J. Larsen


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s