|
7 | 7 | from machine import Pin
|
8 | 8 |
|
9 | 9 | class Encoder:
|
10 |
| - delay = 100 # Pause (ms) for motion to stop |
| 10 | + delay = 100 # Pause (ms) for motion to stop/limit callback frequency |
11 | 11 |
|
12 |
| - def __init__(self, pin_x, pin_y, v=0, vmin=None, vmax=None, div=1, |
13 |
| - callback=lambda a, b : None, args=(), mod=0): |
| 12 | + def __init__(self, pin_x, pin_y, v=0, div=1, vmin=None, vmax=None, |
| 13 | + mod=None, callback=lambda a, b : None, args=()): |
14 | 14 | self._pin_x = pin_x
|
15 | 15 | self._pin_y = pin_y
|
16 | 16 | self._x = pin_x()
|
17 | 17 | self._y = pin_y()
|
18 |
| - self._v = 0 # Hardware value always starts at 0 |
| 18 | + self._v = v * div # Initialise hardware value |
19 | 19 | self._cv = v # Current (divided) value
|
20 | 20 | if ((vmin is not None) and v < vmin) or ((vmax is not None) and v > vmax):
|
21 | 21 | raise ValueError('Incompatible args: must have vmin <= v <= vmax')
|
@@ -44,33 +44,31 @@ def _y_cb(self, pin_y):
|
44 | 44 | self._v -= 1 if y ^ self._pin_x() else -1
|
45 | 45 | self._tsf.set()
|
46 | 46 |
|
47 |
| - async def _run(self, vmin, vmax, div, modulo, cb, args): |
| 47 | + async def _run(self, vmin, vmax, div, mod, cb, args): |
48 | 48 | pv = self._v # Prior hardware value
|
49 |
| - cv = self._cv # Current divided value as passed to callback |
50 |
| - pcv = cv # Prior divided value passed to callback |
| 49 | + pcv = self._cv # Prior divided value passed to callback |
| 50 | + lcv = pcv # Current value after limits applied |
| 51 | + plcv = pcv # Previous value after limits applied |
51 | 52 | delay = self.delay
|
52 | 53 | while True:
|
53 | 54 | await self._tsf.wait()
|
54 |
| - await asyncio.sleep_ms(delay) # Wait for motion to stop |
55 |
| - new = self._v # Sample hardware (atomic read) |
56 |
| - a = new - pv # Hardware change |
57 |
| - # Ensure symmetrical bahaviour for + and - values |
58 |
| - q, r = divmod(abs(a), div) |
59 |
| - if a < 0: |
60 |
| - r = -r |
61 |
| - q = -q |
62 |
| - pv = new - r # Hardware value when local value was updated |
63 |
| - cv += q |
64 |
| - if vmax is not None: |
65 |
| - cv = min(cv, vmax) |
66 |
| - if vmin is not None: |
67 |
| - cv = max(cv, vmin) |
68 |
| - if modulo: |
69 |
| - cv %= modulo |
70 |
| - self._cv = cv # For value() |
71 |
| - if cv != pcv: |
72 |
| - cb(cv, cv - pcv, *args) # User CB in uasyncio context |
| 55 | + await asyncio.sleep_ms(delay) # Wait for motion to stop. |
| 56 | + hv = self._v # Sample hardware (atomic read). |
| 57 | + if hv == pv: # A change happened but was negated before |
| 58 | + continue # this got scheduled. Nothing to do. |
| 59 | + pv = hv |
| 60 | + cv = round(hv / div) # cv is divided value. |
| 61 | + if not (dv := cv - pcv): # dv is change in divided value. |
| 62 | + continue # No change |
| 63 | + lcv += dv # lcv: divided value with limits/mod applied |
| 64 | + lcv = lcv if vmax is None else min(vmax, lcv) |
| 65 | + lcv = lcv if vmin is None else max(vmin, lcv) |
| 66 | + lcv = lcv if mod is None else lcv % mod |
| 67 | + self._cv = lcv # update ._cv for .value() before CB. |
| 68 | + if lcv != plcv: |
| 69 | + cb(lcv, lcv - plcv, *args) # Run user CB in uasyncio context |
73 | 70 | pcv = cv
|
| 71 | + plcv = lcv |
74 | 72 |
|
75 | 73 | def value(self):
|
76 | 74 | return self._cv
|
0 commit comments