diff --git a/pico-cp/README.md b/pico-cp/README.md index 67098eb..36dd9d0 100644 --- a/pico-cp/README.md +++ b/pico-cp/README.md @@ -122,9 +122,10 @@ The Kit can drive a **physical metronome pendulum**: a 4-input unipolar stepper power‑cycle. - **Jog / test mode** (hold **A + B** at boot): the joystick sets **direction only** — **L = CCW, R = CW** — and the motor **accelerates to `STEPPER_MAX_RATE`** (reversing decelerates through zero first), with an on‑screen - needle + RGB LED and a **live step counter + rate readout**. *Tuning:* raise `STEPPER_MAX_RATE` until the - motor starts skipping, then back off; if it stalls *starting*, lower `STEPPER_ACCEL` / `STEPPER_JOG_START`. - Power‑cycle (no buttons) to exit. + needle + RGB LED. The **step count + peak‑rate readout updates when you release** (drawing mid‑spin would + stall the step loop and make it jumpy, so the spin itself stays glitch‑free). *Tuning:* hold to spin, release + to read the peak; raise `STEPPER_MAX_RATE` until the motor skips, then back off; if it stalls *starting*, + lower `STEPPER_ACCEL` / `STEPPER_JOG_START`. Power‑cycle (no buttons) to exit. ## programs.json diff --git a/pico-cp/app.py b/pico-cp/app.py index 66a72ee..f6df469 100644 --- a/pico-cp/app.py +++ b/pico-cp/app.py @@ -1179,8 +1179,9 @@ class App: # Joystick = DIRECTION only (no fine speed). Spin at STEPPER_MAX_RATE, reached via an # acceleration ramp (STEPPER_ACCEL) so the motor doesn't stall trying to start at top speed; # reversing decelerates through zero, then accelerates the other way. - spin = 0; cur = 0.0; total = 0; win = 0; peak = 0 + spin = 0; cur = 0.0; total = 0; win = 0; peak = 0; lastrate = 0 now = time.monotonic(); last = now; tsample = now; tjoy = now + gc.collect() # clean heap before spinning (avoid a GC pause mid-spin) while True: # no per-iteration sleep: tight step timing in this mode now = time.monotonic() if now - tjoy >= 0.004: # control update (joystick + accel), off the step hot-path @@ -1194,15 +1195,16 @@ class App: elif cur > target: cur = max(0.0, cur - STEPPER_ACCEL * cdt) # decelerate if cur <= 0.0 and spin != 0 and want != spin: # finished slowing -> stop, or flip direction if self.pend is not None and self.pend.ok: self.pend.release() - if want == 0: spin = 0; set_needle(0) - else: spin = want; cur = float(STEPPER_JOG_START); set_needle(spin) + if want == 0: # stopped -> now safe to draw the readout + tidy the heap + spin = 0; show_stats(total, lastrate, peak); set_needle(0); gc.collect() + else: + spin = want; cur = float(STEPPER_JOG_START); set_needle(spin) if spin != 0 and cur > 0.0 and self.pend is not None and self.pend.ok and now - last >= 1.0 / cur: self.pend.spin(spin > 0); last = now; total += 1; win += 1 - if now - tsample >= 1.0: # step-rate readout, once/s (one tiny refresh) - rate = int(win / (now - tsample)) - if rate > peak: peak = rate - show_stats(total, rate, peak); win = 0; tsample = now - self.display.refresh() + if now - tsample >= 1.0: # measure rate SILENTLY (drawing here is what hitched it) + lastrate = int(win / (now - tsample)) + if lastrate > peak: peak = lastrate + win = 0; tsample = now # ---------- audio + light ---------- def click(self, level):