Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slow response time when implementing multiple encoders #17

Open
coreyblosser opened this issue Jun 12, 2021 · 9 comments
Open

Slow response time when implementing multiple encoders #17

coreyblosser opened this issue Jun 12, 2021 · 9 comments

Comments

@coreyblosser
Copy link

coreyblosser commented Jun 12, 2021

Hi. I'll start with I'm very new to Python so I may be overlooking a simple solution. I'm having difficulty with using threading to implement multiple rotary encoders. I'm using a Raspberry Pi4 and my implementation of threading is based on the multiple encoder example found here: https://github.com/raphaelyancey/pyKY040. I'm assuming since pigpio_encoder is similar, I can use the same method for multiple encoders.

With my current script below, the response time to encoder input is noticeably slow. It appears to not be able to keep up with the rotation of the encoder. If I comment out the lines under #Rotary one or #Rotary two, the response time is better but a bit more performance would be nice. When I use the multiple encoder example from the link above, response time is even faster. I'm assuming that may be due to it being a simpler module.

Would you have any suggestions for how I could optimize this code for faster performance? I prefer pigpio_encoder over pyKY040 because of the additional features such as long press.

Thanks

My Current Script
from pigpio_encoder import pigpio_encoder
import threading
import time
import datetime

def rotary_callback(counter):
print(datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3] + " - " + "Counter value: ", counter)
def sw_short():
print("Switch short press")
def sw_long():
print("Switch long press")

def rotary_callback_two(counter):
print("Counter2 value: ", counter)
def sw_short_two():
print("Switch2 short press")
def sw_long_two():
print("Switch2 long press")

#Rotary one
my_rotary = pigpio_encoder.Rotary(clk=18, dt=17, sw=19)
my_rotary.setup_rotary(min=0, max=100, scale=1, debounce=100, rotary_callback=rotary_callback)
my_rotary.setup_switch(debounce=200, long_press=True, sw_short_callback=sw_short, sw_long_callback=sw_long)
my_thread = threading.Thread(target=my_rotary.watch)
my_thread.start()

#Rotary two
my_rotary_two = pigpio_encoder.Rotary(clk=13, dt=12, sw=16)
my_rotary_two.setup_rotary(min=0, max=100, scale=1, debounce=100, rotary_callback=rotary_callback_two)
my_rotary_two.setup_switch(debounce=200, long_press=True, sw_short_callback=sw_short_two, sw_long_callback=sw_long_two)
my_thread_two = threading.Thread(target=my_rotary_two.watch)
my_thread_two.start()

#Do other stuff
print('Other stuff...')
while True:
print('LoopedResponse tim stuff...')
time.sleep(1000)

This is output for my current script with two encoders enabled. I'm rotating the encoder clockwise with my fingers.
Other stuff...
Looped stuff...
14:15:46.059 - Counter value: 1
14:15:48.166 - Counter value: 2
14:15:48.210 - Counter value: 3
14:15:48.269 - Counter value: 4
14:15:48.640 - Counter value: 5
14:15:48.782 - Counter value: 6
14:15:49.968 - Counter value: 7
14:15:49.995 - Counter value: 6
14:15:50.035 - Counter value: 7
14:15:50.462 - Counter value: 8
14:15:51.039 - Counter value: 9
14:15:51.569 - Counter value: 10
14:15:51.592 - Counter value: 11
14:15:51.615 - Counter value: 12
14:15:51.637 - Counter value: 13
14:15:51.671 - Counter value: 15
14:15:51.693 - Counter value: 16
14:15:52.189 - Counter value: 17

This is output for my current script with the second encoder commented out. The script detects more movement.
Other stuff...
Looped stuff...
14:16:52.406 - Counter value: 1
14:16:52.489 - Counter value: 2
14:16:52.611 - Counter value: 3
14:16:53.023 - Counter value: 4
14:16:53.049 - Counter value: 5
14:16:53.145 - Counter value: 6
14:16:53.535 - Counter value: 7
14:16:53.598 - Counter value: 8
14:16:53.668 - Counter value: 9
14:16:54.059 - Counter value: 10
14:16:54.149 - Counter value: 11
14:16:54.229 - Counter value: 12
14:16:54.640 - Counter value: 13
14:16:54.728 - Counter value: 14
14:16:54.755 - Counter value: 15
14:16:55.163 - Counter value: 16
14:16:55.235 - Counter value: 17
14:16:55.574 - Counter value: 18
14:16:55.611 - Counter value: 17
14:16:55.689 - Counter value: 18
14:16:56.038 - Counter value: 19
14:16:56.081 - Counter value: 18
14:16:56.098 - Counter value: 19
14:16:56.115 - Counter value: 20

This is output for pyky040 script I'm using (code below) two encoders enabled. It's very fast.
Other stuff...
Looped stuff...
14:19:19.438 - Hello world! The scale position is 1
14:19:19.468 - Hello world! The scale position is 2
14:19:19.487 - Hello world! The scale position is 3
14:19:19.503 - Hello world! The scale position is 4
14:19:19.512 - Hello world! The scale position is 5
14:19:19.525 - Hello world! The scale position is 6
14:19:19.531 - Hello world! The scale position is 7
14:19:19.541 - Hello world! The scale position is 8
14:19:19.547 - Hello world! The scale position is 9
14:19:19.557 - Hello world! The scale position is 10
14:19:19.562 - Hello world! The scale position is 11
14:19:19.571 - Hello world! The scale position is 12
14:19:19.576 - Hello world! The scale position is 13
14:19:19.586 - Hello world! The scale position is 14
14:19:19.593 - Hello world! The scale position is 15
14:19:19.603 - Hello world! The scale position is 16
14:19:19.611 - Hello world! The scale position is 17
14:19:19.626 - Hello world! The scale position is 18
14:19:20.003 - Hello world! The scale position is 19
14:19:20.023 - Hello world! The scale position is 20
14:19:20.031 - Hello world! The scale position is 21
14:19:20.043 - Hello world! The scale position is 22
14:19:20.048 - Hello world! The scale position is 23
14:19:20.057 - Hello world! The scale position is 24

My Pyky040 Script
#https://pypi.org/project/pyky040/
#https://github.com/raphaelyancey/pyKY040
#Import the module and threading
from pyky040 import pyky040
import threading
import time
import datetime

def my_callback(scale_position):
print(datetime.datetime.now().strftime('%H:%M:%S.%f')[:-3] + ' - ' + 'Hello world! The scale position is {}'.format(scale_position))
def button():
print('Button pressed')

def my_callback_two(scale_position):
print('Hello world! The scale position2 is {}'.format(scale_position))
def button_two():
print('Button2 pressed')

my_encoder = pyky040.Encoder(CLK=17, DT=18, SW=19)
my_encoder.setup(scale_min=0, scale_max=100, step=1, chg_callback=my_callback, sw_callback=button)
my_thread = threading.Thread(target=my_encoder.watch)
my_thread.start()

my_encoder_two = pyky040.Encoder(CLK=12, DT=13, SW=16)
my_encoder_two.setup(scale_min=0, scale_max=100, step=1, chg_callback=my_callback_two, sw_callback=button_two)
my_thread_two = threading.Thread(target=my_encoder_two.watch)
my_thread_two.start()

#Do other stuff
print('Other stuff...')
while True:
print('Looped stuff...')
time.sleep(1000)

@volkerjaenisch
Copy link
Collaborator

@coreyblosser Thank you for choosing pigpio_encoder!
I think the problem is that you are using the watch function. This function is there only for backward compatibility but should NOT be used.
The pigpio_ecoder lib is interrupt driven so you need no loop. I have not tried but I am quite sure that you even do not need two threads to operate two encoders in parallel.

So my suggestion is to drop the treads and the watch func and look what happens.

You will just need to have a single final loop for your code to wait, or a thread or so.

Cheers,
Volker

@volkerjaenisch
Copy link
Collaborator

volkerjaenisch commented Jun 12, 2021

@coreyblosser I also noticed that you have set the debounce time to 100ms. This gives you a general 100ms delay. Before using debounce at all - try without. If the noise is to strong (e.g. you will see more than one step per step or missing steps) crank up the debounce time in 10ms increments till you reach a stable operation. Different encoders (or encoder models) have quite different characteristic so for each one the debounce has to be adjusted. But at first try without . I have some cheap china encoders that run really fine without debouncing.

Cheers,
Volker

@coreyblosser
Copy link
Author

@volkerjaenisch Thanks for your guidance. I started a new script, based on the "example using all features" found here: https://pypi.org/project/pigpio-encoder. I removed the debounce and .watch(). If understood correctly, I believe this is the direction you suggested.

The good news is the button presses for both encoders work but there is no output when turning the encoders. If I replace the while loop with my_rotary.watch(), I can see output for button presses and turning the both encoders. Am I missing a detail?

Corey

from pigpio_encoder import pigpio_encoder

def rotary_callback(counter):
print("Counter value: ", counter)
def sw_short():
print("Switch short press")
def sw_long():
print("Switch long press")

def rotary_callback_two(counter):
print("Counter value2: ", counter)
def sw_short_two():
print("Switch2 short press")
def sw_long_two():
print("Switch2 long press")

my_rotary = pigpio_encoder.Rotary(clk=18, dt=17, sw=19)
my_rotary.setup_rotary(min=0, max=100, scale=1, rotary_callback=rotary_callback)
my_rotary.setup_switch(long_press=True, sw_short_callback=sw_short, sw_long_callback=sw_long)

my_rotary_two = pigpio_encoder.Rotary(clk=13, dt=12, sw=16)
my_rotary_two.setup_rotary(min=0, max=100, scale=1, rotary_callback=rotary_callback_two)
my_rotary_two.setup_switch(long_press=True, sw_short_callback=sw_short_two, sw_long_callback=sw_long_two)

while(True):
pass

@volkerjaenisch
Copy link
Collaborator

@coreyblosser The code looks perfect to me. What version of pigpio_encoder are you using?
If you comment out the second encoder, does the first one work?

@coreyblosser
Copy link
Author

@volkerjaenisch pigpiod -v shows version 79 and I'm using Python 3.7.3

When I comment out the second encoder, the behavior does not change. Short and long button presses work but no output when turning the first encoder. With the second encoder commented out, if I also comment out the while loop and uncomment the my_rotary.watch(), I see output for button presses and turning the encoder.

@coreyblosser
Copy link
Author

@volkerjaenisch I notice the distribution version of pigpio_encoder I have is 0.2.2 but I see 0.2.5 on Github. Could that be significant?

@volkerjaenisch
Copy link
Collaborator

@coreyblosser

I notice the distribution version of pigpio_encoder I have is 0.2.2 but I see 0.2.5 on Github. Could that be significant?

Therefore my question for the version. Please use the version from GH. I will deploy the 0.2.5 version as Python package on PyPI in the next hours

With the second encoder commented out, if I also comment out the while loop and uncomment the my_rotary.watch(), I see output for button presses and turning the encoder.

This is really strange. If you please have a look at .watch() :

@staticmethod
def watch():
"""
A simple convenience function to have a waiting loop
"""
while True:
time.sleep(10)

It is no more than a loop. But the problem may be that your loop does eat up all CPU since it has no wait in it.

Can you please give your loop a wait and test again. Mainwhile I do the deployment to PyPI.

Cheers,
Volker

@volkerjaenisch
Copy link
Collaborator

Current Version 0.2.4 is now available on PyPI.

@coreyblosser
Copy link
Author

@volkerjaenisch I was able to install the 0.2.4 version. All works with both encoders. This is great! Thanks for all of your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants