Coding a KY-040 Rotary Encoder on a Raspberry Pi Pico - Detailed Explanation & Step by Step Code
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.
Overview
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.
Detailed Video
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.
MicroPython Code
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.