gobot.io/x/gobot@v1.16.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" 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 }