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

     1  package gpio
     2  
     3  import (
     4  	"errors"
     5  	"math"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"gobot.io/x/gobot/v2"
    12  )
    13  
    14  type phase [][4]byte
    15  
    16  // StepperModes to decide on Phase and Stepping
    17  var StepperModes = struct {
    18  	SinglePhaseStepping [][4]byte
    19  	DualPhaseStepping   [][4]byte
    20  	HalfStepping        [][4]byte
    21  }{
    22  	//1 cycle = 4 steps with lesser torque
    23  	SinglePhaseStepping: [][4]byte{
    24  		{1, 0, 0, 0},
    25  		{0, 1, 0, 0},
    26  		{0, 0, 1, 0},
    27  		{0, 0, 0, 1},
    28  	},
    29  	//1 cycle = 4 steps with higher torque and current
    30  	DualPhaseStepping: [][4]byte{
    31  		{1, 0, 0, 1},
    32  		{1, 1, 0, 0},
    33  		{0, 1, 1, 0},
    34  		{0, 0, 1, 1},
    35  	},
    36  	//1 cycle = 8 steps with lesser torque than full stepping
    37  	HalfStepping: [][4]byte{
    38  		{1, 0, 0, 1},
    39  		{1, 0, 0, 0},
    40  		{1, 1, 0, 0},
    41  		{0, 1, 0, 0},
    42  		{0, 1, 1, 0},
    43  		{0, 0, 1, 0},
    44  		{0, 0, 1, 1},
    45  		{0, 0, 0, 1},
    46  	},
    47  }
    48  
    49  // StepperDriver object
    50  type StepperDriver struct {
    51  	name        string
    52  	pins        [4]string
    53  	connection  DigitalWriter
    54  	phase       phase
    55  	stepsPerRev uint
    56  	moving      bool
    57  	direction   string
    58  	stepNum     int
    59  	speed       uint
    60  	mutex       *sync.Mutex
    61  	gobot.Commander
    62  }
    63  
    64  // NewStepperDriver returns a new StepperDriver given a
    65  // DigitalWriter
    66  // Pins - To which the stepper is connected
    67  // Phase - Defined by StepperModes {SinglePhaseStepping, DualPhaseStepping, HalfStepping}
    68  // Steps - No of steps per revolution of Stepper motor
    69  func NewStepperDriver(a DigitalWriter, pins [4]string, phase phase, stepsPerRev uint) *StepperDriver {
    70  	s := &StepperDriver{
    71  		name:        gobot.DefaultName("Stepper"),
    72  		connection:  a,
    73  		pins:        pins,
    74  		phase:       phase,
    75  		stepsPerRev: stepsPerRev,
    76  		moving:      false,
    77  		direction:   "forward",
    78  		stepNum:     0,
    79  		speed:       1,
    80  		mutex:       &sync.Mutex{},
    81  		Commander:   gobot.NewCommander(),
    82  	}
    83  	s.speed = s.GetMaxSpeed()
    84  
    85  	s.AddCommand("Move", func(params map[string]interface{}) interface{} {
    86  		steps, _ := strconv.Atoi(params["steps"].(string))
    87  		return s.Move(steps)
    88  	})
    89  	s.AddCommand("Run", func(params map[string]interface{}) interface{} {
    90  		return s.Run()
    91  	})
    92  	s.AddCommand("Halt", func(params map[string]interface{}) interface{} {
    93  		return s.Halt()
    94  	})
    95  
    96  	return s
    97  }
    98  
    99  // Name of StepperDriver
   100  func (s *StepperDriver) Name() string { return s.name }
   101  
   102  // SetName sets name for StepperDriver
   103  func (s *StepperDriver) SetName(n string) { s.name = n }
   104  
   105  // Connection returns StepperDriver's connection
   106  func (s *StepperDriver) Connection() gobot.Connection { return s.connection.(gobot.Connection) }
   107  
   108  // Start implements the Driver interface and keeps running the stepper till halt is called
   109  func (s *StepperDriver) Start() (err error) { return }
   110  
   111  // Run continuously runs the stepper
   112  func (s *StepperDriver) Run() (err error) {
   113  	//halt if already moving
   114  	if s.moving {
   115  		s.Halt()
   116  	}
   117  
   118  	s.mutex.Lock()
   119  	s.moving = true
   120  	s.mutex.Unlock()
   121  
   122  	delay := s.getDelayPerStep()
   123  
   124  	go func() {
   125  		for {
   126  			if !s.moving {
   127  				break
   128  			}
   129  			s.step()
   130  			time.Sleep(delay)
   131  		}
   132  	}()
   133  
   134  	return
   135  }
   136  
   137  // Halt implements the Driver interface and halts the motion of the Stepper
   138  func (s *StepperDriver) Halt() (err error) {
   139  	s.mutex.Lock()
   140  	s.moving = false
   141  	s.mutex.Unlock()
   142  	return nil
   143  }
   144  
   145  // SetDirection sets the direction in which motor should be moving, Default is forward
   146  func (s *StepperDriver) SetDirection(direction string) error {
   147  	direction = strings.ToLower(direction)
   148  	if direction != "forward" && direction != "backward" {
   149  		return errors.New("Invalid direction. Value should be forward or backward")
   150  	}
   151  
   152  	s.mutex.Lock()
   153  	s.direction = direction
   154  	s.mutex.Unlock()
   155  	return nil
   156  }
   157  
   158  // IsMoving returns a bool stating whether motor is currently in motion
   159  func (s *StepperDriver) IsMoving() bool {
   160  	return s.moving
   161  }
   162  
   163  // Step moves motor one step in giving direction
   164  func (s *StepperDriver) step() error {
   165  	if s.direction == "forward" {
   166  		s.stepNum++
   167  	} else {
   168  		s.stepNum--
   169  	}
   170  
   171  	if s.stepNum >= int(s.stepsPerRev) {
   172  		s.stepNum = 0
   173  	} else if s.stepNum < 0 {
   174  		s.stepNum = int(s.stepsPerRev) - 1
   175  	}
   176  
   177  	r := int(math.Abs(float64(s.stepNum))) % len(s.phase)
   178  
   179  	for i, v := range s.phase[r] {
   180  		if err := s.connection.DigitalWrite(s.pins[i], v); err != nil {
   181  			return err
   182  		}
   183  	}
   184  
   185  	return nil
   186  }
   187  
   188  // Move moves the motor for given number of steps
   189  func (s *StepperDriver) Move(stepsToMove int) error {
   190  	if stepsToMove == 0 {
   191  		return s.Halt()
   192  	}
   193  
   194  	if s.moving {
   195  		//stop previous motion
   196  		s.Halt()
   197  	}
   198  
   199  	s.mutex.Lock()
   200  	s.moving = true
   201  	s.direction = "forward"
   202  
   203  	if stepsToMove < 0 {
   204  		s.direction = "backward"
   205  	}
   206  	s.mutex.Unlock()
   207  
   208  	stepsLeft := int64(math.Abs(float64(stepsToMove)))
   209  	delay := s.getDelayPerStep()
   210  
   211  	for stepsLeft > 0 {
   212  		if err := s.step(); err != nil {
   213  			return err
   214  		}
   215  		stepsLeft--
   216  		time.Sleep(delay)
   217  	}
   218  
   219  	s.moving = false
   220  	return nil
   221  }
   222  
   223  // getDelayPerStep gives the delay per step
   224  func (s *StepperDriver) getDelayPerStep() time.Duration {
   225  	//Do not remove *1000 and change duration to time.Millisecond. It has been done for a reason
   226  	return time.Duration(60000*1000/(s.stepsPerRev*s.speed)) * time.Microsecond
   227  }
   228  
   229  // GetCurrentStep gives the current step of motor
   230  func (s *StepperDriver) GetCurrentStep() int {
   231  	return s.stepNum
   232  }
   233  
   234  // GetMaxSpeed gives the max RPM of motor
   235  func (s *StepperDriver) GetMaxSpeed() uint {
   236  	//considering time for 1 rev as no of steps per rev * 1.5 (min time req between each step)
   237  	return uint(60000 / (float64(s.stepsPerRev) * 1.5))
   238  }
   239  
   240  // SetSpeed sets the rpm
   241  func (s *StepperDriver) SetSpeed(rpm uint) error {
   242  	if rpm <= 0 {
   243  		return errors.New("RPM cannot be a zero or negative value")
   244  	}
   245  
   246  	m := s.GetMaxSpeed()
   247  	if rpm > m {
   248  		rpm = m
   249  	}
   250  
   251  	s.speed = rpm
   252  	return nil
   253  }