Jump to content

Rotary Encoder Module

From cognifront

Arduino Rotary Encoder Position and Angle Measurement Using M274 / KY-040

Arduino Rotary Encoder Position and Angle Measurement is a rotary encoder interfacing setup using an Arduino UNO and an M274 / KY-040 rotary encoder.

The encoder provides two digital signals, CLK and DT, which are used by the Arduino to determine the direction and position of rotation.

The encoder also includes a push button SW. Pressing the button resets the encoder position to zero.

The program uses a typical encoder resolution of 20 pulses per revolution to calculate the approximate shaft angle.

Rotary Encoder → Arduino UNO → Position + Angle + Direction

Connections

Device Pin Arduino UNO Function
M274 / KY-040 + / VCC 5V Power
M274 / KY-040 GND GND Ground
M274 / KY-040 CLK D2 Encoder clock signal
M274 / KY-040 DT D3 Encoder direction signal
M274 / KY-040 SW D4 Push button / reset

Rotary Encoder

The M274 / KY-040 rotary encoder provides two output signals:

  • CLK – Clock signal
  • DT – Direction signal

The Arduino reads the relationship between the CLK and DT signals to determine whether the encoder is rotating clockwise or counter-clockwise.

The encoder also provides a push button through the SW pin.

The program assumes:

20 pulses = 1 revolution

Encoder Resolution

The program defines the encoder resolution as:

PULSES_PER_REV = 20

Therefore, one complete revolution corresponds to approximately 20 encoder pulses.

The angle corresponding to the encoder position is calculated using:

Angle = (Pulses in One Revolution × 360) / 20

Therefore:

1 pulse ≈ 18°

Encoder Direction

The Arduino detects a falling edge of the CLK signal.

At the falling edge, the Arduino checks the state of the DT signal.

If DT is HIGH:

Clockwise → Position increases

If DT is LOW:

Counter-clockwise → Position decreases

The encoder position is stored in:

volatile long encoderPosition = 0;

Clockwise rotation increments the position, while counter-clockwise rotation decrements it.

Push Button Reset

The SW pin is connected to Arduino D4.

The input uses the Arduino's internal pull-up resistor:

pinMode(SW_PIN, INPUT_PULLUP);

When the button is pressed, the SW pin becomes LOW.

The program then resets:

Position = 0
Angle = 0°

A debounce time of 200 ms is used to prevent multiple resets caused by button bouncing.

Working

The Arduino continuously performs the following operations:

  1. Reads the CLK and DT signals from the rotary encoder.
  2. Detects changes in the CLK signal using an interrupt.
  3. Determines the direction of rotation using the DT signal.
  4. Increments or decrements the encoder position.
  5. Calculates the angle corresponding to the current position.
  6. Displays the position, angle and direction on the Serial Monitor.
  7. Checks the push button on D4.
  8. Resets the encoder position to zero when the button is pressed.

The overall operation is:

CLK + DT → Direction → Position → Angle

Interrupt Operation

The CLK signal is connected to Arduino D2.

On the Arduino UNO, D2 supports an external interrupt.

The program attaches the interrupt using:

attachInterrupt(
  digitalPinToInterrupt(CLK_PIN),
  encoderISR,
  CHANGE
);

The encoder interrupt function reads both CLK and DT signals and determines the direction of rotation.

The interrupt is triggered whenever the CLK signal changes.

Angle Calculation

The program calculates the position within one complete revolution using the modulo operation:

long pulsesInOneRev = position % PULSES_PER_REV;

For negative positions, the program adjusts the value so that the angle remains within the range of 0° to 360°.

The angle is then calculated as:

Angle = (pulsesInOneRev × 360.0) / PULSES_PER_REV

With 20 pulses per revolution:

20 pulses → 360°
10 pulses → 180°
5 pulses → 90°
1 pulse → 18°

Serial Monitor

The Serial Monitor is set to 115200 baud.

At startup, the program displays:

M274 Rotary Encoder Test
------------------------
Rotate the encoder...

During rotation, the program displays the encoder position, angle and direction.

Example:

Position: 1 | Angle: 18.0 deg | Direction: CLOCKWISE
Position: 2 | Angle: 36.0 deg | Direction: CLOCKWISE
Position: 3 | Angle: 54.0 deg | Direction: CLOCKWISE
Position: 2 | Angle: 36.0 deg | Direction: COUNTER-CLOCKWISE

When the push button is pressed:

RESET -> Position = 0, Angle = 0 deg

Arduino Code

/*
  M274 / KY-040 Rotary Encoder
  Arduino UNO

  Connections:
  ----------------------------
  M274       Arduino UNO
  ----------------------------
  + / VCC ->  5V
  GND     ->  GND
  CLK     ->  D2
  DT      ->  D3
  SW      ->  D4

  Typical encoder resolution:
  20 cycles/pulses per revolution

  Features:
  - Detects clockwise rotation
  - Detects counter-clockwise rotation
  - Counts encoder position
  - Calculates approximate angle
  - Push button resets position to zero
*/

#define CLK_PIN 2
#define DT_PIN  3
#define SW_PIN  4

// Typical M274/KY-040 specification
const int PULSES_PER_REV = 20;

// Encoder position
volatile long encoderPosition = 0;

// Previous CLK state
volatile bool lastCLKState = HIGH;

// Button debounce
unsigned long lastButtonTime = 0;
const unsigned long debounceTime = 200;

// Encoder interrupt function
void encoderISR()
{
  bool currentCLK = digitalRead(CLK_PIN);
  bool currentDT  = digitalRead(DT_PIN);

  // Detect falling edge of CLK
  if (lastCLKState == HIGH && currentCLK == LOW)
  {
    if (currentDT == HIGH)
    {
      // Clockwise
      encoderPosition++;
    }
    else
    {
      // Counter-clockwise
      encoderPosition--;
    }
  }

  lastCLKState = currentCLK;
}

void setup()
{
  Serial.begin(115200);

  // Internal pull-up resistors
  pinMode(CLK_PIN, INPUT_PULLUP);
  pinMode(DT_PIN, INPUT_PULLUP);
  pinMode(SW_PIN, INPUT_PULLUP);

  // Read initial CLK state
  lastCLKState = digitalRead(CLK_PIN);

  // Interrupt on CLK
  attachInterrupt(
    digitalPinToInterrupt(CLK_PIN),
    encoderISR,
    CHANGE
  );

  Serial.println("M274 Rotary Encoder Test");
  Serial.println("------------------------");
  Serial.println("Rotate the encoder...");
}

void loop()
{
  // Read encoder position safely
  noInterrupts();
  long position = encoderPosition;
  interrupts();

  // Calculate angle
  long pulsesInOneRev = position % PULSES_PER_REV;

  if (pulsesInOneRev < 0)
  {
    pulsesInOneRev += PULSES_PER_REV;
  }

  float angle =
    (pulsesInOneRev * 360.0) / PULSES_PER_REV;

  // Display data
  static long previousPosition = 999999;

  if (position != previousPosition)
  {
    Serial.print("Position: ");
    Serial.print(position);

    Serial.print(" | Angle: ");
    Serial.print(angle, 1);

    Serial.print(" deg | Direction: ");

    if (position > previousPosition)
    {
      Serial.println("CLOCKWISE");
    }
    else
    {
      Serial.println("COUNTER-CLOCKWISE");
    }

    previousPosition = position;
  }

  if (digitalRead(SW_PIN) == LOW)
  {
    if (millis() - lastButtonTime > debounceTime)
    {
      noInterrupts();
      encoderPosition = 0;
      interrupts();

      Serial.println("RESET -> Position = 0, Angle = 0 deg");

      lastButtonTime = millis();
    }
  }
}

Observation

The Serial Monitor is set to 115200 baud.

When the encoder is rotated clockwise, the encoder position increases and the Serial Monitor displays: CLOCKWISE

When the encoder is rotated counter-clockwise, the encoder position decreases and the Serial Monitor displays: COUNTER-CLOCKWISE

The angle is calculated based on the encoder position and the specified 20 pulses per revolution.

Pressing the encoder push button resets the position and angle to zero.

Result

The Arduino UNO successfully interfaces with the M274 / KY-040 rotary encoder and detects clockwise and counter-clockwise rotation.

The program counts the encoder position, calculates the approximate angular position based on 20 pulses per revolution, displays the direction and angle on the Serial Monitor, and resets the position to zero when the push button is pressed.