We had a long debug session today with @bschwind and we found out the cause. This PR tries to solve it but it's a blind shot - I did not test this yet and the timeout duration is guesstimated.
Cause
Our rotary encoder reports 4 increments per one physical detent and so in the firmware we accumulate the increments and only report a tick when the accumulated value reaches 4.
Unfortunately the HW is little noisy and sometimes the detents' position drifts in relation to the increments. This causes our accumulator to be non-zero even though the dial is resting on a detent. When this happens rotating the dial in one direction has no effect even if the rotation causes 4 increments.
Example (variable names match the code linked above):
start with last_count = 0
dial is rotated one tick to the right, quei.count reports 4, in turn we report a tick and set last_count = 4
because *noise*, one more increment comes in and quei.count reports 5 as the dial settles on the detent position
the button is rotated left, which makes quei.count to report 1 (the expected number since we expect 4 increments per detent) but we do not report anything because last_count - count is 3 (4 - 1)
rotating right again, quei.count reports 5, which again doesn't trigger a tick because last_count - count is -1
i.e. unless the count leaves the (0, 8) range we report no ticks but the two adjacent detents are located at 1 and 5.
Solution
There's no way for us to know where exactly the detents are located in relation to count. The only other dimension we can work with is time.
We can take advantage of the fact that users usually (and detented dials in general) stop for a brief while on the detents. In other words whenever there's no rotation for a while, the dial is probably sitting on a detent and we should reset our accumulator.
This has two caveats:
1) When the dial stops in between dials we assume it's a detent. However, this should seldom happen and when it does it will be corrected the next time the dials slips to a detented position.
2) If the user rotates the button sufficiently slow between detents the rotation will not be registered as the accumulator will be reset before it has chance to accumulate 4 increments. This depends on how we tune the timeout duration. I believe it can be set to value such that reasonably slow ticks are safely registered.
We had a long debug session today with @bschwind and we found out the cause. This PR tries to solve it but it's a blind shot - I did not test this yet and the timeout duration is guesstimated.
Cause
Our rotary encoder reports 4 increments per one physical detent and so in the firmware we accumulate the increments and only report a tick when the accumulated value reaches 4.
Unfortunately the HW is little noisy and sometimes the detents' position drifts in relation to the increments. This causes our accumulator to be non-zero even though the dial is resting on a detent. When this happens rotating the dial in one direction has no effect even if the rotation causes 4 increments.
Example (variable names match the code linked above):
last_count = 0
quei.count
reports 4, in turn we report a tick and setlast_count = 4
quei.count
reports 5 as the dial settles on the detent positionquei.count
to report 1 (the expected number since we expect 4 increments per detent) but we do not report anything becauselast_count - count
is 3 (4 - 1
)quei.count
reports 5, which again doesn't trigger a tick becauselast_count - count
is-1
(0, 8)
range we report no ticks but the two adjacent detents are located at1
and5
.Solution
There's no way for us to know where exactly the detents are located in relation to
count
. The only other dimension we can work with is time.We can take advantage of the fact that users usually (and detented dials in general) stop for a brief while on the detents. In other words whenever there's no rotation for a while, the dial is probably sitting on a detent and we should reset our accumulator.
This has two caveats: 1) When the dial stops in between dials we assume it's a detent. However, this should seldom happen and when it does it will be corrected the next time the dials slips to a detented position. 2) If the user rotates the button sufficiently slow between detents the rotation will not be registered as the accumulator will be reset before it has chance to accumulate 4 increments. This depends on how we tune the timeout duration. I believe it can be set to value such that reasonably slow ticks are safely registered.
This needs throughout testing.
Fixes https://github.com/tonarino/portal/issues/2666