gobot.io/x/gobot/v2@v2.1.0/drivers/gpio/easy_driver.go (about)

     1  package gpio
     2  
     3  import (
     4  	"errors"
     5  	"strconv"
     6  	"time"
     7  
     8  	"gobot.io/x/gobot/v2"
     9  )
    10  
    11  // EasyDriver object
    12  type EasyDriver struct {
    13  	gobot.Commander
    14  
    15  	name       string
    16  	connection DigitalWriter
    17  	stepPin    string
    18  	dirPin     string
    19  	enPin      string
    20  	sleepPin   string
    21  
    22  	angle    float32
    23  	rpm      uint
    24  	dir      int8
    25  	moving   bool
    26  	stepNum  int
    27  	enabled  bool
    28  	sleeping bool
    29  }
    30  
    31  // NewEasyDriver returns a new EasyDriver from SparkFun (https://www.sparkfun.com/products/12779)
    32  // TODO: Support selecting phase input instead of hard-wiring MS1 and MS2 to board truth table
    33  // This should also work for the BigEasyDriver (untested)
    34  // A - DigitalWriter
    35  // stepPin - Pin corresponding to step input on EasyDriver
    36  // dirPin - Pin corresponding to dir input on EasyDriver.  Optional
    37  // enPin - Pin corresponding to enabled input on EasyDriver.  Optional
    38  // sleepPin - Pin corresponding to sleep input on EasyDriver.  Optional
    39  // angle - Step angle of motor
    40  func NewEasyDriver(a DigitalWriter, angle float32, stepPin string, dirPin string, enPin string, sleepPin string) *EasyDriver {
    41  	d := &EasyDriver{
    42  		Commander:  gobot.NewCommander(),
    43  		name:       gobot.DefaultName("EasyDriver"),
    44  		connection: a,
    45  		stepPin:    stepPin,
    46  		dirPin:     dirPin,
    47  		enPin:      enPin,
    48  		sleepPin:   sleepPin,
    49  
    50  		angle:    angle,
    51  		rpm:      1,
    52  		dir:      1,
    53  		enabled:  true,
    54  		sleeping: false,
    55  	}
    56  
    57  	// panic if step pin isn't set
    58  	if stepPin == "" {
    59  		panic("Step pin is not set")
    60  	}
    61  
    62  	// 1/4 of max speed.  Not too fast, not too slow
    63  	d.rpm = d.GetMaxSpeed() / 4
    64  
    65  	d.AddCommand("Move", func(params map[string]interface{}) interface{} {
    66  		degs, _ := strconv.Atoi(params["degs"].(string))
    67  		return d.Move(degs)
    68  	})
    69  	d.AddCommand("Step", func(params map[string]interface{}) interface{} {
    70  		return d.Step()
    71  	})
    72  	d.AddCommand("Run", func(params map[string]interface{}) interface{} {
    73  		return d.Run()
    74  	})
    75  	d.AddCommand("Stop", func(params map[string]interface{}) interface{} {
    76  		return d.Stop()
    77  	})
    78  
    79  	return d
    80  }
    81  
    82  // Name of EasyDriver
    83  func (d *EasyDriver) Name() string { return d.name }
    84  
    85  // SetName sets name for EasyDriver
    86  func (d *EasyDriver) SetName(n string) { d.name = n }
    87  
    88  // Connection returns EasyDriver's connection
    89  func (d *EasyDriver) Connection() gobot.Connection { return d.connection.(gobot.Connection) }
    90  
    91  // Start implements the Driver interface
    92  func (d *EasyDriver) Start() (err error) { return }
    93  
    94  // Halt implements the Driver interface; stops running the stepper
    95  func (d *EasyDriver) Halt() (err error) {
    96  	d.Stop()
    97  	return
    98  }
    99  
   100  // Move the motor given number of degrees at current speed.
   101  func (d *EasyDriver) Move(degs int) (err error) {
   102  	if d.moving {
   103  		// don't do anything if already moving
   104  		return
   105  	}
   106  
   107  	d.moving = true
   108  
   109  	steps := int(float32(degs) / d.angle)
   110  	for i := 0; i < steps; i++ {
   111  		if !d.moving {
   112  			// don't continue to step if driver is stopped
   113  			break
   114  		}
   115  
   116  		d.Step()
   117  	}
   118  
   119  	d.moving = false
   120  
   121  	return
   122  }
   123  
   124  // Step the stepper 1 step
   125  func (d *EasyDriver) Step() (err error) {
   126  	stepsPerRev := d.GetMaxSpeed()
   127  
   128  	// a valid steps occurs for a low to high transition
   129  	d.connection.DigitalWrite(d.stepPin, 0)
   130  	// 1 minute / steps per revolution / revolutions per minute
   131  	// let's keep it as Microseconds so we only have to do integer math
   132  	time.Sleep(time.Duration(60*1000*1000/stepsPerRev/d.rpm) * time.Microsecond)
   133  	d.connection.DigitalWrite(d.stepPin, 1)
   134  
   135  	// increment or decrement the number of steps by 1
   136  	d.stepNum += int(d.dir)
   137  
   138  	return
   139  }
   140  
   141  // Run the stepper continuously
   142  func (d *EasyDriver) Run() (err error) {
   143  	if d.moving {
   144  		// don't do anything if already moving
   145  		return
   146  	}
   147  
   148  	d.moving = true
   149  
   150  	go func() {
   151  		for d.moving {
   152  			d.Step()
   153  		}
   154  	}()
   155  
   156  	return
   157  }
   158  
   159  // Stop running the stepper
   160  func (d *EasyDriver) Stop() (err error) {
   161  	d.moving = false
   162  	return
   163  }
   164  
   165  // SetDirection sets the direction to be moving.  Valid directions are "cw" or "ccw"
   166  func (d *EasyDriver) SetDirection(dir string) (err error) {
   167  	// can't change direct if dirPin isn't set
   168  	if d.dirPin == "" {
   169  		return errors.New("dirPin is not set")
   170  	}
   171  
   172  	if dir == "ccw" {
   173  		d.dir = -1
   174  		d.connection.DigitalWrite(d.dirPin, 1) // high is ccw
   175  	} else { // default to cw, even if user specified wrong value
   176  		d.dir = 1
   177  		d.connection.DigitalWrite(d.dirPin, 0) // low is cw
   178  	}
   179  
   180  	return
   181  }
   182  
   183  // SetSpeed sets the speed of the motor in RPMs.  1 is the lowest and GetMaxSpeed is the highest
   184  func (d *EasyDriver) SetSpeed(rpm uint) (err error) {
   185  	if rpm < 1 {
   186  		d.rpm = 1
   187  	} else if rpm > d.GetMaxSpeed() {
   188  		d.rpm = d.GetMaxSpeed()
   189  	} else {
   190  		d.rpm = rpm
   191  	}
   192  
   193  	return
   194  }
   195  
   196  // GetMaxSpeed returns the max speed of the stepper
   197  func (d *EasyDriver) GetMaxSpeed() uint {
   198  	return uint(360 / d.angle)
   199  }
   200  
   201  // GetCurrentStep returns current step number
   202  func (d *EasyDriver) GetCurrentStep() int {
   203  	return d.stepNum
   204  }
   205  
   206  // IsMoving returns a bool stating whether motor is currently in motion
   207  func (d *EasyDriver) IsMoving() bool {
   208  	return d.moving
   209  }
   210  
   211  // Enable enables all motor output
   212  func (d *EasyDriver) Enable() (err error) {
   213  	// can't enable if enPin isn't set.  This is fine normally since it will be enabled by default
   214  	if d.enPin == "" {
   215  		return errors.New("enPin is not set.  Board is enabled by default")
   216  	}
   217  
   218  	d.enabled = true
   219  	d.connection.DigitalWrite(d.enPin, 0) // enPin is active low
   220  
   221  	return
   222  }
   223  
   224  // Disable disables all motor output
   225  func (d *EasyDriver) Disable() (err error) {
   226  	// can't disable if enPin isn't set
   227  	if d.enPin == "" {
   228  		return errors.New("enPin is not set")
   229  	}
   230  
   231  	// let's stop the motor first
   232  	d.Stop()
   233  
   234  	d.enabled = false
   235  	d.connection.DigitalWrite(d.enPin, 1) // enPin is active low
   236  
   237  	return
   238  }
   239  
   240  // IsEnabled returns a bool stating whether motor is enabled
   241  func (d *EasyDriver) IsEnabled() bool {
   242  	return d.enabled
   243  }
   244  
   245  // Sleep puts the driver to sleep and disables all motor output.  Low power mode.
   246  func (d *EasyDriver) Sleep() (err error) {
   247  	// can't sleep if sleepPin isn't set
   248  	if d.sleepPin == "" {
   249  		return errors.New("sleepPin is not set")
   250  	}
   251  
   252  	// let's stop the motor first
   253  	d.Stop()
   254  
   255  	d.sleeping = true
   256  	d.connection.DigitalWrite(d.sleepPin, 0) // sleepPin is active low
   257  
   258  	return
   259  }
   260  
   261  // Wake wakes up the driver
   262  func (d *EasyDriver) Wake() (err error) {
   263  	// can't wake if sleepPin isn't set
   264  	if d.sleepPin == "" {
   265  		return errors.New("sleepPin is not set")
   266  	}
   267  
   268  	d.sleeping = false
   269  	d.connection.DigitalWrite(d.sleepPin, 1) // sleepPin is active low
   270  
   271  	// we need to wait 1ms after sleeping before doing a step to charge the step pump (according to data sheet)
   272  	// this will ensure that happens
   273  	time.Sleep(1 * time.Millisecond)
   274  
   275  	return
   276  }
   277  
   278  // IsSleeping returns a bool stating whether motor is enabled
   279  func (d *EasyDriver) IsSleeping() bool {
   280  	return d.sleeping
   281  }