diff --git a/components/motor/dimensionengineering/sabertooth.go b/components/motor/dimensionengineering/sabertooth.go index 37123ca2d8c..f50ec216b98 100644 --- a/components/motor/dimensionengineering/sabertooth.go +++ b/components/motor/dimensionengineering/sabertooth.go @@ -397,6 +397,10 @@ func (m *Motor) GoFor(ctx context.Context, rpm, revolutions float64, extra map[s return motor.NewZeroRPMError() } + if err := motor.CheckRevolutions(revolutions); err != nil { + return err + } + powerPct, waitDur := goForMath(m.maxRPM, rpm, revolutions) err := m.SetPower(ctx, powerPct, extra) if err != nil { diff --git a/components/motor/errors.go b/components/motor/errors.go index 4fe79b04c8c..63d8283a10e 100644 --- a/components/motor/errors.go +++ b/components/motor/errors.go @@ -20,6 +20,12 @@ func NewZeroRPMError() error { return errors.New("Cannot move motor at an RPM that is nearly 0") } +// NewZeroRPMError returns an error representing a request to move a motor at +// zero speed (i.e., moving the motor without moving the motor). +func NewZeroRevsError() error { + return errors.New("Cannot move motor for 0 revolutions") +} + // NewGoToUnsupportedError returns error when a motor is required to support GoTo feature. func NewGoToUnsupportedError(motorName string) error { return errors.Errorf("motor with name %s does not support GoTo", motorName) diff --git a/components/motor/fake/motor.go b/components/motor/fake/motor.go index 2e6bf8a24a2..6081512aa2e 100644 --- a/components/motor/fake/motor.go +++ b/components/motor/fake/motor.go @@ -32,6 +32,7 @@ var ( ) const defaultMaxRpm = 100 +const defaultNonZeroRevs = -1 // PinConfig defines the mapping of where motor are wired. type PinConfig struct { @@ -300,6 +301,10 @@ func (m *Motor) GoFor(ctx context.Context, rpm, revolutions float64, extra map[s return err } + if err := motor.CheckRevolutions(revolutions); err != nil { + return err + } + powerPct, waitDur, dir := goForMath(m.MaxRPM, rpm, revolutions) var finalPos float64 @@ -347,8 +352,9 @@ func (m *Motor) GoTo(ctx context.Context, rpm, pos float64, extra map[string]int if err != nil { return err } - if curPos == pos { - return nil + + if err := motor.CheckRevolutions(pos - curPos); err != nil { + return err } revolutions := pos - curPos diff --git a/components/motor/gpio/basic.go b/components/motor/gpio/basic.go index 813d7c83458..298a0b9d93e 100644 --- a/components/motor/gpio/basic.go +++ b/components/motor/gpio/basic.go @@ -278,6 +278,10 @@ func (m *Motor) GoFor(ctx context.Context, rpm, revolutions float64, extra map[s return err } + if err := motor.CheckRevolutions(revolutions); err != nil { + return err + } + powerPct, waitDur := goForMath(m.maxRPM, rpm, revolutions) err = m.SetPower(ctx, powerPct, extra) if err != nil { diff --git a/components/motor/gpio/controlled.go b/components/motor/gpio/controlled.go index 13b90fb109d..06d74a8d77e 100644 --- a/components/motor/gpio/controlled.go +++ b/components/motor/gpio/controlled.go @@ -318,6 +318,10 @@ func (cm *controlledMotor) GoFor(ctx context.Context, rpm, revolutions float64, return err } + if err := motor.CheckRevolutions(revolutions); err != nil { + return err + } + currentTicks, _, err := cm.enc.Position(ctx, encoder.PositionTypeTicks, extra) if err != nil { return err diff --git a/components/motor/gpio/encoded.go b/components/motor/gpio/encoded.go index 2c7b3d1728b..fba4387e954 100644 --- a/components/motor/gpio/encoded.go +++ b/components/motor/gpio/encoded.go @@ -259,6 +259,10 @@ func (m *EncodedMotor) GoFor(ctx context.Context, rpm, revolutions float64, extr return err } + if err := motor.CheckRevolutions(revolutions); err != nil { + return err + } + goalPos, goalRPM, direction := encodedGoForMath(rpm, revolutions, currentTicks, m.ticksPerRotation) if err := m.goForInternal(goalRPM, goalPos, direction); err != nil { diff --git a/components/motor/gpiostepper/gpiostepper.go b/components/motor/gpiostepper/gpiostepper.go index 808fa130a50..0a329e60b64 100644 --- a/components/motor/gpiostepper/gpiostepper.go +++ b/components/motor/gpiostepper/gpiostepper.go @@ -357,6 +357,10 @@ func (m *gpioStepper) goForInternal(ctx context.Context, rpm, revolutions float6 return m.Stop(ctx, nil) } + if err := motor.CheckRevolutions(revolutions); err != nil { + return err + } + var d int64 = 1 if math.Signbit(revolutions) != math.Signbit(rpm) { d = -1 diff --git a/components/motor/motor.go b/components/motor/motor.go index ec8ed554706..19706b7685f 100644 --- a/components/motor/motor.go +++ b/components/motor/motor.go @@ -199,3 +199,11 @@ func CheckSpeed(rpm, max float64) (string, error) { return "", nil } } + +// CheckRevolutions checks if the input revolutions is non-zero +func CheckRevolutions(revs float64) error { + if revs == 0 { + return NewZeroRevsError() + } + return nil +} diff --git a/components/motor/roboclaw/roboclaw.go b/components/motor/roboclaw/roboclaw.go index 613c13c911b..bf7d6cd25ff 100644 --- a/components/motor/roboclaw/roboclaw.go +++ b/components/motor/roboclaw/roboclaw.go @@ -260,6 +260,10 @@ func (m *roboclawMotor) GoFor(ctx context.Context, rpm, revolutions float64, ext return err } + if err := motor.CheckRevolutions(revolutions); err != nil { + return err + } + // If no encoders present, distance traveled is estimated based on max RPM. if m.conf.TicksPerRotation == 0 { if math.Abs(rpm) > maxRPM {