Raspberry Pi Pico
Rotary Encoder

A rotary encoder has a knob to rotate a little bit like a rotary potentiometer. One big difference is that the knob can be rotated continuously. There are two main types of output from a rotary encoder, Gray Code and Quadrature. Both are binary codes to represent data from the encoder. Depending on the type of encoding, a rotary encoder's output could indicate the absolute position of the knob or the amount and direction of a rotation of the encoder by the user.

I used a PCB mounting rotary encoder for this that gives 2 bit quadrature output. We can read direction only.

The two pins of the rotary encoder work something like this,

Pico Circuit

The two waves are out of phase. The dotted red line shows us that, when turning clockwise, and the signal from A changes from high to low, the B signal will be a high. When turning anticlockwise, when A changes from high to low, there is a dotted blue line showing us that the B signal will be low.

This image shows my test circuit. The rotary encoder I used also acts as a pushbutton. You press the stalk down to open and close the switch. There are two pins for this that you can wire like a normal pushbutton. I chose not to use those in this test circuit.

Pico Circuit

Here is the Fritzing diagram for the same circuit. The Fritzing part only shows the rotary encoder pins. The two switch pins are on the opposite side of the base to the rotary encoder pins. A lot of rotary encoders I have used have similar arrangements for their pins. The rotary encoder pins are usually A, GND, B on one side of the package, with the two switch pins on the opposite.

Pico Circuit

Using polling, this is code I used to test that I could read turns. As long as I don't turn it too quickly, this will pick up each clicky turn.

from machine import Pin
from time import sleep

a = Pin(14, Pin.IN, Pin.PULL_UP)
b = Pin(15, Pin.IN, Pin.PULL_UP)

counter = 0
lasta = 1

print(counter)

while True:
    aread = a.value()
    bread = b.value()
    if not aread and lasta:
        if bread==1:
            counter += 1
        else:
            counter -= 1
        print(counter)
    lasta = aread
    sleep(0.01)

I tried using interrupts to detect the rotations but could not get it to the point where the changes were usably clean.