Initial complete commit

This commit is contained in:
2026-04-04 16:47:39 +02:00
parent 2fa0f1c0d0
commit 9cc1b695bd
8 changed files with 1161 additions and 0 deletions

187
gauge_vid6008.py Normal file
View File

@@ -0,0 +1,187 @@
"""
gauge_vid6008.py — Automotive stepper gauge driver for ESP32 / MicroPython
Supports two driver modes:
1. STEP/DIR mode (e.g. VID-6008): 2 pins — DIR and STEP
Each step requires a pulse on STEP; DIR sets direction.
2. 4-PHASE mode (e.g. VID28/BKA30D/Switec X25): 4 pins — IN1..IN4
Driven via two H-bridge pairs (ULN2003 or direct GPIO).
"""
from machine import Pin
import utime
_TOTAL_STEPS = 3780
_OVERRUN_STEPS = 200
_FULL_SEQUENCE = [
(1, 0, 1, 0),
(0, 1, 1, 0),
(0, 1, 0, 1),
(1, 0, 0, 1),
]
class Gauge:
"""
Analog-style stepper gauge driver.
Parameters
----------
pins : tuple[int, ...]
2 pins for STEP/DIR mode: (DIR, STEP)
4 pins for 4-phase mode: (IN1, IN2, IN3, IN4)
mode : str
"stepdir" for VID-6008 style (2 pins), "4phase" for VID28/X25 (4 pins).
min_val : float
Value that corresponds to the physical lower stop.
max_val : float
Value that corresponds to the physical upper stop.
step_us : int
Delay between steps in microseconds (default 200).
"""
def __init__(self, pins, mode="4phase", min_val=0, max_val=100, step_us=200):
self._mode = mode
self._step_us = step_us
self._min_val = min_val
self._max_val = max_val
if mode == "stepdir":
if len(pins) != 2:
raise ValueError("stepdir mode requires 2 pins: (DIR, STEP)")
self._pin_dir = Pin(pins[0], Pin.OUT)
self._pin_step = Pin(pins[1], Pin.OUT)
self._total_steps = _TOTAL_STEPS
self._phase = 0
elif mode == "4phase":
if len(pins) != 4:
raise ValueError("4phase mode requires 4 pins: (IN1, IN2, IN3, IN4)")
self._pins = [Pin(p, Pin.OUT) for p in pins]
self._total_steps = _TOTAL_STEPS
self._sequence = _FULL_SEQUENCE
self._phase = 0
else:
raise ValueError("mode must be 'stepdir' or '4phase'")
self._current_step = 0
self._zeroed = False
def zero(self):
overrun = _OVERRUN_STEPS
if self._mode == "stepdir":
self._pin_dir.value(0)
utime.sleep_us(10)
for _ in range(_TOTAL_STEPS + overrun):
self._pulse_step(500)
else:
for _ in range(_TOTAL_STEPS + overrun):
self._step(-1)
utime.sleep_ms(2)
self._current_step = 0
self._zeroed = True
self._release()
def set(self, value, release=True):
if not self._zeroed:
raise RuntimeError("Call zero() before set()")
value = max(self._min_val, min(self._max_val, value))
target_step = self._val_to_step(value)
delta = target_step - self._current_step
if delta == 0:
return
if self._mode == "stepdir":
self._move_steps(delta)
else:
for _ in range(abs(delta)):
self._step(1 if delta > 0 else -1)
utime.sleep_ms(self._step_us)
self._current_step = target_step
if release:
self._release()
def _move_steps(self, delta):
direction = 1 if delta > 0 else 0
self._pin_dir.value(direction)
utime.sleep_us(10)
for _ in range(abs(delta)):
self._pulse_step(self._step_us)
def _pulse_step(self, delay_us):
self._pin_step.value(1)
utime.sleep_us(2)
self._pin_step.value(0)
utime.sleep_us(delay_us)
def get(self):
return self._step_to_val(self._current_step)
def release(self):
self._release()
def step(self, direction):
if self._mode == "stepdir":
self._pin_dir.value(1 if direction > 0 else 0)
utime.sleep_us(10)
self._pulse_step(self._step_us)
self._current_step += direction
else:
self._step(direction)
def _step(self, direction):
steps_in_seq = len(self._sequence)
self._phase = (self._phase + direction) % steps_in_seq
seq = self._sequence[self._phase]
for pin, state in zip(self._pins, seq):
pin.value(state)
self._current_step += direction
def _release(self):
if self._mode == "stepdir":
self._pin_step.value(0)
else:
for pin in self._pins:
pin.value(0)
def _val_to_step(self, value):
frac = (value - self._min_val) / (self._max_val - self._min_val)
return int(round(frac * self._total_steps))
def _step_to_val(self, step):
frac = step / self._total_steps
return self._min_val + frac * (self._max_val - self._min_val)
if __name__ == "__main__":
g = Gauge(
pins=(12, 13),
mode="stepdir",
min_val=0,
max_val=100,
step_us=200,
)
print("Zeroing gauge...")
g.zero()
print("Zero complete.")
for target in [25, 50, 75, 100, 50, 0]:
print("Moving to {} (currently at {:.1f})".format(target, g.get()))
g.set(target)
utime.sleep_ms(500)
print("Done.")