2018-09-12 23:46:59 -04:00
|
|
|
package gpio
|
|
|
|
|
|
|
|
import (
|
2023-10-31 18:12:07 +01:00
|
|
|
"fmt"
|
2023-11-09 20:31:18 +01:00
|
|
|
"strings"
|
2018-09-12 23:46:59 -04:00
|
|
|
"time"
|
|
|
|
|
2023-05-20 14:25:21 +02:00
|
|
|
"gobot.io/x/gobot/v2"
|
2018-09-12 23:46:59 -04:00
|
|
|
)
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
const easyDriverDebug = false
|
|
|
|
|
|
|
|
// EasyDriver is an driver for stepper hardware board from SparkFun (https://www.sparkfun.com/products/12779)
|
|
|
|
// This should also work for the BigEasyDriver (untested). It is basically a wrapper for the common StepperDriver{}
|
|
|
|
// with the specific additions for the board, e.g. direction, enable and sleep outputs.
|
2018-09-12 23:46:59 -04:00
|
|
|
type EasyDriver struct {
|
2023-11-09 20:31:18 +01:00
|
|
|
*StepperDriver
|
|
|
|
|
|
|
|
stepPin string
|
|
|
|
dirPin string
|
|
|
|
enPin string
|
|
|
|
sleepPin string
|
|
|
|
anglePerStep float32
|
|
|
|
|
|
|
|
sleeping bool
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
// NewEasyDriver returns a new driver
|
2018-09-12 23:46:59 -04:00
|
|
|
// TODO: Support selecting phase input instead of hard-wiring MS1 and MS2 to board truth table
|
|
|
|
// A - DigitalWriter
|
2023-11-09 20:31:18 +01:00
|
|
|
// anglePerStep - Step angle of motor
|
2018-09-12 23:46:59 -04:00
|
|
|
// stepPin - Pin corresponding to step input on EasyDriver
|
|
|
|
// dirPin - Pin corresponding to dir input on EasyDriver. Optional
|
|
|
|
// enPin - Pin corresponding to enabled input on EasyDriver. Optional
|
|
|
|
// sleepPin - Pin corresponding to sleep input on EasyDriver. Optional
|
2023-10-31 18:12:07 +01:00
|
|
|
func NewEasyDriver(
|
|
|
|
a DigitalWriter,
|
2023-11-09 20:31:18 +01:00
|
|
|
anglePerStep float32,
|
2023-10-31 18:12:07 +01:00
|
|
|
stepPin string,
|
|
|
|
dirPin string,
|
|
|
|
enPin string,
|
|
|
|
sleepPin string,
|
|
|
|
) *EasyDriver {
|
2023-11-09 20:31:18 +01:00
|
|
|
if anglePerStep <= 0 {
|
|
|
|
panic("angle per step needs to be greater than zero")
|
2023-10-31 18:12:07 +01:00
|
|
|
}
|
2018-09-12 23:46:59 -04:00
|
|
|
// panic if step pin isn't set
|
|
|
|
if stepPin == "" {
|
|
|
|
panic("Step pin is not set")
|
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
stepper := NewStepperDriver(a, [4]string{}, nil, 1)
|
|
|
|
stepper.name = gobot.DefaultName("EasyDriver")
|
|
|
|
stepper.stepperDebug = easyDriverDebug
|
|
|
|
stepper.haltIfRunning = false
|
|
|
|
stepper.stepsPerRev = 360.0 / anglePerStep
|
|
|
|
d := &EasyDriver{
|
|
|
|
StepperDriver: stepper,
|
|
|
|
stepPin: stepPin,
|
|
|
|
dirPin: dirPin,
|
|
|
|
enPin: enPin,
|
|
|
|
sleepPin: sleepPin,
|
|
|
|
anglePerStep: anglePerStep,
|
2023-10-31 18:12:07 +01:00
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
sleeping: false,
|
2023-10-31 18:12:07 +01:00
|
|
|
}
|
2023-11-09 20:31:18 +01:00
|
|
|
d.stepFunc = d.onePinStepping
|
|
|
|
d.sleepFunc = d.sleepWithSleepPin
|
|
|
|
d.beforeHalt = d.shutdown
|
2023-10-31 18:12:07 +01:00
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
// 1/4 of max speed. Not too fast, not too slow
|
|
|
|
d.speedRpm = d.MaxSpeed() / 4
|
2023-10-31 18:12:07 +01:00
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
return d
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
// SetDirection sets the direction to be moving.
|
|
|
|
func (d *EasyDriver) SetDirection(direction string) error {
|
|
|
|
direction = strings.ToLower(direction)
|
|
|
|
if direction != StepperDriverForward && direction != StepperDriverBackward {
|
|
|
|
return fmt.Errorf("Invalid direction '%s'. Value should be '%s' or '%s'",
|
|
|
|
direction, StepperDriverForward, StepperDriverBackward)
|
2023-10-31 18:12:07 +01:00
|
|
|
}
|
|
|
|
|
2018-09-12 23:46:59 -04:00
|
|
|
if d.dirPin == "" {
|
2023-10-31 18:12:07 +01:00
|
|
|
return fmt.Errorf("dirPin is not set for '%s'", d.name)
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
writeVal := byte(0) // low is forward
|
|
|
|
if direction == StepperDriverBackward {
|
|
|
|
writeVal = 1 // high is backward
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-11-15 20:51:52 +01:00
|
|
|
//nolint:forcetypeassert // type safe by constructor
|
2023-11-09 20:31:18 +01:00
|
|
|
if err := d.connection.(DigitalWriter).DigitalWrite(d.dirPin, writeVal); err != nil {
|
|
|
|
return err
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
// ensure that write of variable can not interfere with read in step()
|
|
|
|
d.valueMutex.Lock()
|
|
|
|
defer d.valueMutex.Unlock()
|
|
|
|
d.direction = direction
|
2018-09-12 23:46:59 -04:00
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
return nil
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enable enables all motor output
|
2023-06-12 19:51:25 +02:00
|
|
|
func (d *EasyDriver) Enable() error {
|
2018-09-12 23:46:59 -04:00
|
|
|
if d.enPin == "" {
|
2023-11-09 20:31:18 +01:00
|
|
|
d.disabled = false
|
2023-10-31 18:12:07 +01:00
|
|
|
return fmt.Errorf("enPin is not set - board '%s' is enabled by default", d.name)
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-06-12 19:51:25 +02:00
|
|
|
// enPin is active low
|
2023-11-15 20:51:52 +01:00
|
|
|
//nolint:forcetypeassert // type safe by constructor
|
2023-10-31 18:12:07 +01:00
|
|
|
if err := d.connection.(DigitalWriter).DigitalWrite(d.enPin, 0); err != nil {
|
2023-06-12 19:51:25 +02:00
|
|
|
return err
|
|
|
|
}
|
2018-09-12 23:46:59 -04:00
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
d.disabled = false
|
2023-06-12 19:51:25 +02:00
|
|
|
return nil
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Disable disables all motor output
|
2023-06-12 19:51:25 +02:00
|
|
|
func (d *EasyDriver) Disable() error {
|
2018-09-12 23:46:59 -04:00
|
|
|
if d.enPin == "" {
|
2023-10-31 18:12:07 +01:00
|
|
|
return fmt.Errorf("enPin is not set for '%s'", d.name)
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
_ = d.stopIfRunning() // drop step errors
|
2018-09-12 23:46:59 -04:00
|
|
|
|
2023-06-12 19:51:25 +02:00
|
|
|
// enPin is active low
|
2023-11-15 20:51:52 +01:00
|
|
|
//nolint:forcetypeassert // type safe by constructor
|
2023-11-09 20:31:18 +01:00
|
|
|
if err := d.connection.(DigitalWriter).DigitalWrite(d.enPin, 1); err != nil {
|
|
|
|
return err
|
2023-06-12 19:51:25 +02:00
|
|
|
}
|
2023-11-09 20:31:18 +01:00
|
|
|
d.disabled = true
|
2018-09-12 23:46:59 -04:00
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
return nil
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsEnabled returns a bool stating whether motor is enabled
|
|
|
|
func (d *EasyDriver) IsEnabled() bool {
|
2023-11-09 20:31:18 +01:00
|
|
|
return !d.disabled
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wake wakes up the driver
|
2023-06-12 19:51:25 +02:00
|
|
|
func (d *EasyDriver) Wake() error {
|
2018-09-12 23:46:59 -04:00
|
|
|
if d.sleepPin == "" {
|
2023-10-31 18:12:07 +01:00
|
|
|
return fmt.Errorf("sleepPin is not set for '%s'", d.name)
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-06-12 19:51:25 +02:00
|
|
|
// sleepPin is active low
|
2023-11-15 20:51:52 +01:00
|
|
|
//nolint:forcetypeassert // type safe by constructor
|
2023-10-31 18:12:07 +01:00
|
|
|
if err := d.connection.(DigitalWriter).DigitalWrite(d.sleepPin, 1); err != nil {
|
2023-06-12 19:51:25 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-09-12 23:46:59 -04:00
|
|
|
d.sleeping = false
|
|
|
|
|
|
|
|
// we need to wait 1ms after sleeping before doing a step to charge the step pump (according to data sheet)
|
|
|
|
time.Sleep(1 * time.Millisecond)
|
|
|
|
|
2023-06-12 19:51:25 +02:00
|
|
|
return nil
|
2018-09-12 23:46:59 -04:00
|
|
|
}
|
|
|
|
|
2023-10-31 18:12:07 +01:00
|
|
|
// IsSleeping returns a bool stating whether motor is sleeping
|
2018-09-12 23:46:59 -04:00
|
|
|
func (d *EasyDriver) IsSleeping() bool {
|
|
|
|
return d.sleeping
|
|
|
|
}
|
2023-10-31 18:12:07 +01:00
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
func (d *EasyDriver) onePinStepping() error {
|
|
|
|
// ensure that read and write of variables (direction, stepNum) can not interfere
|
|
|
|
d.valueMutex.Lock()
|
|
|
|
defer d.valueMutex.Unlock()
|
2023-10-31 18:12:07 +01:00
|
|
|
|
|
|
|
// a valid steps occurs for a low to high transition
|
2023-11-15 20:51:52 +01:00
|
|
|
//nolint:forcetypeassert // type safe by constructor
|
2023-10-31 18:12:07 +01:00
|
|
|
if err := d.connection.(DigitalWriter).DigitalWrite(d.stepPin, 0); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-11-09 20:31:18 +01:00
|
|
|
|
|
|
|
time.Sleep(d.getDelayPerStep())
|
2023-11-15 20:51:52 +01:00
|
|
|
//nolint:forcetypeassert // type safe by constructor
|
2023-10-31 18:12:07 +01:00
|
|
|
if err := d.connection.(DigitalWriter).DigitalWrite(d.stepPin, 1); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
if d.direction == StepperDriverForward {
|
|
|
|
d.stepNum++
|
|
|
|
} else {
|
|
|
|
d.stepNum--
|
|
|
|
}
|
2023-10-31 18:12:07 +01:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
// sleepWithSleepPin puts the driver to sleep and disables all motor output. Low power mode.
|
|
|
|
func (d *EasyDriver) sleepWithSleepPin() error {
|
|
|
|
if d.sleepPin == "" {
|
|
|
|
return fmt.Errorf("sleepPin is not set for '%s'", d.name)
|
2023-10-31 18:12:07 +01:00
|
|
|
}
|
|
|
|
|
2023-11-09 20:31:18 +01:00
|
|
|
_ = d.stopIfRunning() // drop step errors
|
|
|
|
|
|
|
|
// sleepPin is active low
|
2023-11-15 20:51:52 +01:00
|
|
|
//nolint:forcetypeassert // type safe by constructor
|
2023-11-09 20:31:18 +01:00
|
|
|
if err := d.connection.(DigitalWriter).DigitalWrite(d.sleepPin, 0); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
d.sleeping = true
|
|
|
|
|
|
|
|
return nil
|
2023-10-31 18:12:07 +01:00
|
|
|
}
|