A MicroPython driver to read a rotary encoder. Works with Pyboard, Raspberry Pi Pico, ESP8266, and ESP32 development boards. This is a robust implementation providing effective debouncing of encoder contacts. It uses two GPIO pins configured to trigger interrupts, following Ben Buxton's implementation:
Whenever encoder pins DT and CLK change value a hardware interrupt is generated. This interrupt causes a python-based interrupt service routine (ISR) to run. The ISR interrupts normal code execution to process state changes in the encoder pins.
A gray code based transition state table is used to process the DT and CLK changes. The use of the state table leads to accurate encoder counts and effective switch debouncing. Credit: Ben Buxton
Two files are needed to use this module
rotary.py
- a core file for all development boardsrotary_irq_esp.py
Platform-specific code for ESP8266 and ESP32 development boardsrotary_irq_pyb.py
Platform-specific code for Pyboard development boardsrotary_irq_rp2.py
Platform-specific code for Raspberry Pi Pico development boardsCopy files to the internal MicroPython filesystem using a utility such as ampy
or rshell
Ampy example below for Pyboards. Note: -d1
option is often needed for ESP8266 boards
ampy -pCOMx put rotary.py
ampy -pCOMx put rotary_irq_pyb.py
Starting with MicroPython 1.20.0, it can be installed from mip via:
>>> import mip
>>> mip.install("github:miketeachman/micropython-rotary")
Or from mpremote via:
mpremote mip install github:miketeachman/micropython-rotary
RotaryIRQ
RotaryIRQ(
pin_num_clk,
pin_num_dt,
min_val=0,
max_val=10,
incr=1,
reverse=False,
range_mode=RotaryIRQ.RANGE_UNBOUNDED,
pull_up=False,
half_step=False,
invert=False)
argument | description | value |
---|---|---|
pin_num_clk | GPIO pin connected to encoder CLK pin | integer |
pin_num_dt | GPIO pin connected to encoder DT pin | integer |
min_val | minimum value in the encoder range. Also the starting value | integer |
max_val | maximum value in the encoder range (not used when range_mode = RANGE_UNBOUNDED) | integer |
incr | amount count changes with each encoder click | integer (default=1) |
reverse | reverse count direction | True or False(default) |
range_mode | count behavior at min_val and max_val | RotaryIRQ.RANGE_UNBOUNDED(default) RotaryIRQ.RANGE_WRAP RotaryIRQ.RANGE_BOUNDED |
pull_up | enable internal pull up resistors. Use when rotary encoder hardware lacks pull up resistors | True or False(default) |
half_step | half-step mode | True or False(default) |
invert | invert the CLK and DT signals. Use when encoder resting value is CLK, DT = 00 | True or False(default) |
range_mode | description |
---|---|
RotaryIRQ.RANGE_UNBOUNDED | encoder has no bounds on the counting range |
RotaryIRQ.RANGE_WRAP | encoder will count up to max_val then wrap to minimum value (similar behaviour for count down) |
RotaryIRQ.RANGE_BOUNDED | encoder will count up to max_val then stop. Count down stops at min_val |
value()
Return the encoder value
set(value=None, min_val=None, max_val=None, incr=None, reverse=None, range_mode=None)
Set encoder value and internal configuration parameters. See constructor for argument descriptions. None
indicates no change to the configuration parameter
Examples:
set(min_val=0, max_val=59)
change encoder bounds - useful to set minutes on a clock displayset(value=6)
change encoder value to 6
. calling value()
will now return 6
reset()
set encoder value to min_val
. Redundant with the addition of the set()
method. Retained for backwards compatibility)
add_listener(function)
add a callback function that will be called on each change of encoder count
remove_listener(function)
remove a previously added callback function
close()
deactivate microcontroller pins used to read encoder
Note: None of the arguments are checked for configuration errors.
import time
from rotary_irq_esp import RotaryIRQ
r = RotaryIRQ(pin_num_clk=12,
pin_num_dt=13,
min_val=0,
max_val=5,
reverse=False,
range_mode=RotaryIRQ.RANGE_WRAP)
val_old = r.value()
while True:
val_new = r.value()
if val_old != val_new:
val_old = val_new
print('result =', val_new)
time.sleep_ms(50)
Encoder Pin | Connection |
---|---|
+ | 3.3V |
GND | Ground |
DT | GPIO pin |
CLK | GPIO pin |
This Rotary module requires pins that support interrupts. The following ESP8266 GPIO pins are recommended for this rotary encoder module
The following ESP8266 GPIO pins should be used with caution. There is a risk that the state of the CLK and DT signals can affect the boot sequence. When possible, use other GPIO pins.
One pin does not support interrupts.
This Rotary module requires pins that support interrupts. All ESP32 GPIO pins support interrupts.
The following ESP32 GPIO strapping pins should be used with caution. There is a risk that the state of the CLK and DT signals can affect the boot sequence. When possible, use other GPIO pins.
MicroPython example code is contained in the Examples folder
simple example
uasyncio example
uasyncio with classes example
CLK and DT transitions captured on an oscilloscope. CLK = Yellow. DT = Blue
One clockwise step
One counter-clockwise step
Testing with Pyboard D, Pyboard v1.1, TinyPico, and Raspberry Pi Pico development boards
This MicroPython implementation is an adaptation of Ben Buxton's C++ work:
Other implementation ideas and techniques taken from: