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') rotary.add_handler(rotary_changed) 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) def add_handler(self, handler): self.handlers.append(handler) def call_handlers(self, type): for handler in self.handlers: handler(type)
Links To Download Code
You can download the code at https://github.com/gurgleapps/rotary-encoder
Classes are very useful if you find yourself using the same code in multiple projects. You can seperate out all the code and logic into a really useful class. It also makes your main project code easier to follow.
Interrupt Requests are also useful for time critical changes you want to detect.