im triggering callback function witch reads encoder output
with GPIO.add_event_detect
so the global counter
has current value, but i cant pass it to tkinter like i did with listbox.insert(END,counter)
import RPi.GPIO as GPIO
from tkinter import ttk
import tkinter as tk
counter = 0
# GPIO Pins #
A = 17
B = 27
#-----------#
# GPIO settings
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
#
GPIO.setup(A, GPIO.IN)
GPIO.setup(B, GPIO.IN)
def Enc(A):
global counter
sleep(0.002)
input_A = GPIO.input(A)
input_B = GPIO.input(B)
if (input_A == 1) and (input_B == 0):
counter += 1
print (counter)
#listbox.insert(END,counter)
elif (input_A == 1) and (input_B == 1):
counter -= 1
print (counter)
#listbox.insert(END,counter)
else:
return
# GPIO output detection
GPIO.add_event_detect(A, GPIO.RISING, callback=Enc, bouncetime=10)
# window settings
window = tk.Tk()
window.title("encoder_value")
# string value?
window.Enc_counter_str = tk.StringVar(value=counter)
# label
tk.Label(window, text='Enc:',fg = "medium violet red", bg = "light grey", font = "Helvetica 16 bold italic").grid(column=0, row=0, **padding)
# output label
window.Enc_label = tk.Label(window)
window.Enc_label.grid(column=0, row=2, columnspan=1, **padding)
window.Enc_label.config(text=window.Enc_counter_str.get(),fg = "medium violet red", bg = "light grey", font = "Helvetica 16 bold italic")
# tk window loop
window.mainloop()
Improved version:
import RPi.GPIO as GPIO
import tkinter as tk
from time import sleep
ENCODER_PINS = [
(17, 27), # Encoder 1
(22, 23), # Encoder 2
(24, 25), # Encoder 3
(5, 6) # Encoder 4
]
class EncoderApp:
def __init__(self, master):
self.master = master
self.master.title("Encoder Values")
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
# Initialize counters and UI elements
self.counters = [0, 0, 0, 0]
self.setup_ui()
# Setup GPIOs and event detection for each encoder
for idx, (A, B) in enumerate(ENCODER_PINS):
GPIO.setup(A, GPIO.IN)
GPIO.setup(B, GPIO.IN)
GPIO.add_event_detect(A, GPIO.BOTH, callback=lambda ch, idx=idx: self.enc_handler(ch, idx), bouncetime=10)
GPIO.add_event_detect(B, GPIO.BOTH, callback=lambda ch, idx=idx: self.enc_handler(ch, idx), bouncetime=10)
def setup_ui(self):
padding = {"padx": 10, "pady": 10}
self.enc_value_strs = [tk.StringVar(value=counter) for counter in self.counters]
for idx, enc_str in enumerate(self.enc_value_strs):
tk.Label(self.master, text=f'Enc {idx + 1}:', fg="medium violet red", bg="light grey",
font="Helvetica 16 bold italic").grid(column=0, row=idx, **padding)
enc_label = tk.Label(self.master, textvariable=enc_str, fg="medium violet red",
bg="light grey", font="Helvetica 16 bold italic")
enc_label.grid(column=1, row=idx, **padding)
self.master.protocol("WM_DELETE_WINDOW", self.close_app)
def enc_handler(self, channel, idx):
sleep(0.002)
A, B = ENCODER_PINS[idx]
input_A = GPIO.input(A)
input_B = GPIO.input(B)
# Logic for counting all cases (4 transitions) for rotary encoders
if (input_A == 1) and (input_B == 0):
self.counters[idx] += 1
elif (input_A == 1) and (input_B == 1):
self.counters[idx] -= 1
elif (input_A == 0) and (input_B == 1):
self.counters[idx] += 1
elif (input_A == 0) and (input_B == 0):
self.counters[idx] -= 1
self.enc_value_strs[idx].set(self.counters[idx])
def close_app(self):
GPIO.cleanup()
self.master.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = EncoderApp(root)
root.mainloop()
and version without GUI:
import RPi.GPIO as GPIO
from time import sleep
ENCODER_PINS = [
(17, 27), # Encoder 1
(22, 23), # Encoder 2
(24, 25), # Encoder 3
(5, 6) # Encoder 4
]
class EncoderReader:
def __init__(self):
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
# Initialize counters
self.counters = [0, 0, 0, 0]
# Setup GPIOs and event detection for each encoder
for idx, (A, B) in enumerate(ENCODER_PINS):
GPIO.setup(A, GPIO.IN)
GPIO.setup(B, GPIO.IN)
GPIO.add_event_detect(A, GPIO.BOTH, callback=lambda ch, idx=idx: self.enc_handler(ch, idx), bouncetime=10)
GPIO.add_event_detect(B, GPIO.BOTH, callback=lambda ch, idx=idx: self.enc_handler(ch, idx), bouncetime=10)
def enc_handler(self, channel, idx):
sleep(0.002)
A, B = ENCODER_PINS[idx]
input_A = GPIO.input(A)
input_B = GPIO.input(B)
# Logic for counting all cases (4 transitions) for rotary encoders
if (input_A == 1) and (input_B == 0):
self.counters[idx] += 1
direction = "increment"
elif (input_A == 1) and (input_B == 1):
self.counters[idx] -= 1
direction = "decrement"
elif (input_A == 0) and (input_B == 1):
self.counters[idx] += 1
direction = "increment"
elif (input_A == 0) and (input_B == 0):
self.counters[idx] -= 1
direction = "decrement"
else:
return
print(f"Encoder {idx + 1} {direction} to {self.counters[idx]}")
def run(self):
try:
while True:
sleep(0.1)
except KeyboardInterrupt:
GPIO.cleanup()
if __name__ == "__main__":
reader = EncoderReader()
reader.run()
also keep in mind that, because of Python's Global Interpreter Lock (GIL), even though the GPIO callbacks use multiple threads, they don't execute concurrently. This could lead to minor update lags if all encoders generate events at the same time and very quickly.