Skip to content

Instantly share code, notes, and snippets.

@brennanMKE
Last active March 16, 2025 03:23
Show Gist options
  • Save brennanMKE/bd94dfd01fe0b8f682eec81624101f09 to your computer and use it in GitHub Desktop.
Save brennanMKE/bd94dfd01fe0b8f682eec81624101f09 to your computer and use it in GitHub Desktop.
Rotatary Encode KY-040 with ESP32-C3

Rotary Encoder KY-040 with ESP32-C3

(via ChatGPT)

Below is a step-by-step guide for wiring a KY-040 rotary encoder to an ESP32-C3-based board on a breadboard, along with a simple example sketch (Arduino-style) showing how to read both rotation and the built-in push button.


1. Rotary Encoder (KY-040) Overview

A typical KY-040 rotary encoder module has five pins:

  1. GND – Ground
  2. + or VCC – Supply voltage (often labeled 5 V on the module, but it will also work fine at 3.3 V for an ESP32-C3)
  3. SW – Push-button switch output
  4. DT – Data signal (sometimes labeled B)
  5. CLK – Clock signal (sometimes labeled A)

When you rotate the shaft, the module toggles CLK and DT out of phase. By detecting which signal changes first, you can figure out rotation direction (clockwise or counterclockwise). The push-button switch is usually triggered by pressing the knob down.


2. Connecting the KY-040 to the ESP32-C3

Below is one example wiring that works well on the ESP32-C3. You can choose other GPIOs if you prefer, but avoid pins dedicated to flash, USB, or boot configuration (i.e. strapping pins) unless you are sure you can free them up.

KY-040 Pin Connect to on ESP32-C3 Notes
GND GND Common ground
+ 3.3 V Supply with 3.3 V (do not use 5 V)
CLK GPIO9 Rotary output channel A (interrupt/input)
DT GPIO8 Rotary output channel B (interrupt/input)
SW GPIO7 Push-button switch (digital input)

Important:

  • On many KY-040 modules, the pin labeled + is sometimes used at 5 V with Arduino boards. However, the ESP32-C3 is a 3.3 V device, so you should power it at 3.3 V to avoid logic-level mismatches.
  • The rotary encoder outputs are open-contact signals. Typically, you enable internal pull-ups on the ESP32-C3 so you don’t need extra resistors (the hardware has small pull-ups built in).
  • The built-in push button (SW) is often pulled up inside the ESP32-C3. When pressed, it goes LOW (to GND).

A typical breadboard arrangement:

   +-----------+ 
   |   KY-040  |
   |           |
   |  SW  DT  CLK  +  GND
   +-----------+
       |   |   |   |   |
       |   |   |   |   |
GPIO7---+   |   |   |   |
           GPIO8--+   |   |
                 GPIO9--+ |
3.3 V--------------------+
GND-------------------------+

3. Example Arduino Sketch

Below is an Arduino-style sketch that uses internal pull-ups and interrupts to detect both turning and button presses. You can paste this into your PlatformIO or Arduino IDE project (making sure your board is set to an ESP32-C3 variant).

// Example code for reading KY-040 Rotary Encoder on an ESP32-C3.
// Author: (Your Name)

#define PIN_CLK  9   // GPIO9 for the rotary encoder CLK
#define PIN_DT   8   // GPIO8 for the rotary encoder DT
#define PIN_SW   7   // GPIO7 for the rotary encoder push button

volatile int16_t encoderValue = 0;
volatile bool   buttonPressed = false;

// Keep track of the last state of CLK to determine direction
volatile int lastCLKState = HIGH;

void IRAM_ATTR handleEncoderCLK()
{
  // Read both pins quickly in the ISR
  int currentCLK = digitalRead(PIN_CLK);
  int currentDT  = digitalRead(PIN_DT);

  // If the CLK signal has changed from high to low
  if (currentCLK == LOW && lastCLKState == HIGH) {
    // Check DT to figure out direction
    if (currentDT == HIGH) {
      // Rotating clockwise
      encoderValue++;
    } else {
      // Rotating counterclockwise
      encoderValue--;
    }
  }
  // Save the last state so we can detect the falling edge next time
  lastCLKState = currentCLK;
}

void IRAM_ATTR handleButton()
{
  // If the SW pin is active-low, it goes LOW when pressed:
  if (digitalRead(PIN_SW) == LOW) {
    buttonPressed = true;
  }
}

void setup() {
  // Configure USB Serial on ESP32-C3
  // For PlatformIO, ensure build flags in platformio.ini:
  //    -DARDUINO_USB_MODE=1
  //    -DARDUINO_USB_CDC_ON_BOOT=1
  Serial.begin(115200);
  delay(1000);

  // Pins for the encoder signals
  pinMode(PIN_CLK, INPUT_PULLUP);
  pinMode(PIN_DT,  INPUT_PULLUP);

  // Store the initial state of CLK
  lastCLKState = digitalRead(PIN_CLK);

  // Attach interrupt on the CLK signal’s falling edge
  attachInterrupt(digitalPinToInterrupt(PIN_CLK), handleEncoderCLK, FALLING);

  // Pin for the push button
  pinMode(PIN_SW, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PIN_SW), handleButton, FALLING);

  Serial.println("KY-040 Rotary Encoder with ESP32-C3 - Starting up.");
}

void loop() {
  // Just show changes once per second, or whenever the value changes significantly
  static int16_t lastValue = 0;
  int16_t currentValue = encoderValue;

  if (currentValue != lastValue) {
    Serial.print("Encoder value: ");
    Serial.println(currentValue);
    lastValue = currentValue;
  }

  if (buttonPressed) {
    Serial.println("Button pressed!");
    buttonPressed = false;
  }

  delay(50); 
}

Notes on the Code

  1. Interrupts: We attach an interrupt to the CLK pin for the falling edge. In the handler, we read both CLK and DT to figure out the direction of rotation.

  2. Pull-ups: The code sets the pins as INPUT_PULLUP, relying on the internal pull-up resistors so external resistors aren’t strictly needed (though many KY-040 modules also have onboard pull-ups).

  3. Bounce: Mechanical rotary encoders and push buttons often need debounce handling. Depending on quality and speed requirements, you may see occasional spurious direction changes. If your application is sensitive, you can do software debouncing or short delays in the interrupt routine. For many use cases, the above approach works well enough.

  4. USB Serial on ESP32-C3:

    • If you’re using Arduino or PlatformIO with an ESP32-C3 DevKit, you may need these build flags to enable serial over USB:
      build_flags = 
        -DARDUINO_USB_MODE=1
        -DARDUINO_USB_CDC_ON_BOOT=1
    • This will let you see Serial.println() outputs via the USB port directly.

4. Testing & Troubleshooting

  1. Open the Serial Monitor at 115200 baud.
  2. Rotate the encoder slowly clockwise. You should see the “Encoder value” increment.
  3. Rotate the encoder counterclockwise. The value should decrement.
  4. Push the built-in button (press the rotary knob down). You should see “Button pressed!”
  5. If you see random increments or bouncing, consider adding a small capacitor or software debounce logic.

5. Wrap-Up

Once wired as shown and loaded with the example sketch, you will have basic rotation and push-button reads from the KY-040. You can adapt this code to your own application (changing how frequently it prints, adding filtering, or using the value to navigate menus, adjust volume, etc.).

Enjoy experimenting with your ESP32-C3 and the KY-040 rotary encoder! If you run into any quirks with pin selection or debugging, remember you can enable debug logs over USB by ensuring the USB CDC build flags are set, or switch to UART logs on dedicated TX/RX pins.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment