Coding a KY-040 Rotary Encoder on a Raspberry Pi Pico - Detailed Explanation & Step by Step Code

Our 2nd most popular post is this step by step tutorial on how a KY-040 Rotary Encoder works using MicroPython code on a Raspberry Pi Pico or any Microcontroller of choice.

Some really great content here, you learn how to make Classes in Python, how to use Interrupt Requests, we even have some Binary Shifts. We show a simple circuit to investigate how these work and then add a microcontroller and build the code step by step.

Below is the full video but be sure to check below for more resources, code and information.

You should be able to apply what you have learned here to other rotary encoders. Ours had pull down resistors on the component, but it seems not all components listed as KY-040 have those but you can do that with code. Thanks to everyone who took the time to point that out to us. Including Dan McCreary.

Below is a simple example of how to use the rotary class.

from rotary import Rotary
import utime as time

rotary = Rotary(0,1,2)
val = 0

def rotary_changed(change):
global val
if change == Rotary.ROT_CW:
val = val + 1
print(val)
elif change == Rotary.ROT_CCW:
val = val - 1
print(val)
elif change == Rotary.SW_PRESS:
print('PRESS')
elif change == Rotary.SW_RELEASE:
print('RELEASE')

while True:
time.sleep(0.1)


Here is the code for the class rotary.py

import machine
import utime as time
from machine import Pin
import micropython

class Rotary:

ROT_CW = 1
ROT_CCW = 2
SW_PRESS = 4
SW_RELEASE = 8

def __init__(self,dt,clk,sw):
self.dt_pin = Pin(dt, Pin.IN, Pin.PULL_DOWN)
self.clk_pin = Pin(clk, Pin.IN, Pin.PULL_DOWN)
self.sw_pin = Pin(sw, Pin.IN, Pin.PULL_DOWN)
self.last_status = (self.dt_pin.value() << 1) | self.clk_pin.value()
self.dt_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
self.clk_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
self.sw_pin.irq(handler=self.switch_detect, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
self.handlers = []
self.last_button_status = self.sw_pin.value()

def rotary_change(self, pin):
new_status = (self.dt_pin.value() << 1) | self.clk_pin.value()
if new_status == self.last_status:
return
transition = (self.last_status << 2) | new_status
if transition == 0b1110:
micropython.schedule(self.call_handlers, Rotary.ROT_CW)
elif transition == 0b1101:
micropython.schedule(self.call_handlers, Rotary.ROT_CCW)
self.last_status = new_status

def switch_detect(self,pin):
if self.last_button_status == self.sw_pin.value():
return
self.last_button_status = self.sw_pin.value()
if self.sw_pin.value():
micropython.schedule(self.call_handlers, Rotary.SW_RELEASE)
else:
micropython.schedule(self.call_handlers, Rotary.SW_PRESS)

self.handlers.append(handler)

def call_handlers(self, type):
for handler in self.handlers:
handler(type)