stirrer: decode and report Pololu SMC status errors instead of guessing
Adds StatusError flags for the STATUS register and get_status_errors(); go() now reports if a fail-safe (safe-start violation or other error) persists after GO instead of failing silently. _on_process uses the same decoding to log any active error and only auto-recovers on a safe-start violation, rather than the old narrow status & limit_status heuristic. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EU3NTwLygh8jkrnqPoHNXF
This commit is contained in:
@@ -48,6 +48,24 @@ class Varid(enum.Enum):
|
|||||||
MOTOR_CURRENT_LIMIT_OCCURRENCE_COUNT = 45
|
MOTOR_CURRENT_LIMIT_OCCURRENCE_COUNT = 45
|
||||||
|
|
||||||
|
|
||||||
|
# Bit flags of the STATUS variable (Varid.STATUS). A non-zero
|
||||||
|
# SAFE_START_VIOLATION is the "fail-safe" state: the controller accepted
|
||||||
|
# the GO command but still refuses to spin the motor. The other bits are
|
||||||
|
# the SMC's other error conditions, which also stop the motor and can
|
||||||
|
# make GO fail to bring it back.
|
||||||
|
class StatusError(enum.IntFlag):
|
||||||
|
SAFE_START_VIOLATION = 0x0001
|
||||||
|
REQUIRED_CHANNEL_INVALID = 0x0002
|
||||||
|
SERIAL_ERROR = 0x0004
|
||||||
|
COMMAND_TIMEOUT = 0x0008
|
||||||
|
LIMIT_KILL_SWITCH = 0x0010
|
||||||
|
LOW_VIN = 0x0020
|
||||||
|
HIGH_VIN = 0x0040
|
||||||
|
OVER_TEMPERATURE = 0x0080
|
||||||
|
MOTOR_DRIVER_ERROR = 0x0100
|
||||||
|
ERR_LINE_HIGH = 0x0200
|
||||||
|
|
||||||
|
|
||||||
class Pololu1376:
|
class Pololu1376:
|
||||||
def __init__(self, serial_port):
|
def __init__(self, serial_port):
|
||||||
# Deferred-open pyserial idiom: serial_port is constructed with no
|
# Deferred-open pyserial idiom: serial_port is constructed with no
|
||||||
@@ -117,8 +135,15 @@ class Pololu1376:
|
|||||||
|
|
||||||
return rsp[1:]
|
return rsp[1:]
|
||||||
|
|
||||||
|
def get_status_errors(self):
|
||||||
|
return StatusError(self.get_variable(Varid.STATUS))
|
||||||
|
|
||||||
def go(self):
|
def go(self):
|
||||||
return self.cmd("GO")
|
rsp = self.cmd("GO")
|
||||||
|
errors = self.get_status_errors()
|
||||||
|
if errors:
|
||||||
|
print(f"Pololu1376: ERROR - still in fail-safe after GO, active errors: {errors!r}")
|
||||||
|
return rsp
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
return self.cmd("X")
|
return self.cmd("X")
|
||||||
|
|||||||
@@ -75,11 +75,11 @@ class StirrerPololu1376(AStirrer):
|
|||||||
if not self.connected:
|
if not self.connected:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
status = self.drv.get_variable(Varid.STATUS)
|
errors = self.drv.get_status_errors()
|
||||||
limit_status = self.drv.get_variable(Varid.STATUS_LIMIT_STATUS)
|
if errors:
|
||||||
if status & 0x01 == 0x01:
|
print(f"StirrerPololu1376: motor controller reports errors: {errors!r}")
|
||||||
if limit_status & 0x01 == 0x01:
|
if StatusError.SAFE_START_VIOLATION in errors:
|
||||||
print("Recover after motor error!")
|
print("Recover after safe start violation!")
|
||||||
self.drv.go()
|
self.drv.go()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"StirrerPololu1376: comm error in _on_process: {e}")
|
print(f"StirrerPololu1376: comm error in _on_process: {e}")
|
||||||
|
|||||||
Reference in New Issue
Block a user