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 }