-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Copy pathmotor_driver.go
304 lines (250 loc) · 7.34 KB
/
motor_driver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
package gpio
import (
"fmt"
"gobot.io/x/gobot/v2"
)
// motorOptionApplier needs to be implemented by each configurable option type
type motorOptionApplier interface {
apply(cfg *motorConfiguration)
}
// motorConfiguration contains all changeable attributes of the driver.
type motorConfiguration struct {
modeIsAnalog bool
directionPin string
forwardPin string
backwardPin string
}
// motorModeIsAnalogOption is the type for applying analog mode to the configuration
type motorModeIsAnalogOption bool
// motorDirectionPinOption is the type for applying a direction pin to the configuration
type motorDirectionPinOption string
// motorForwardPinOption is the type for applying a forward pin to the configuration
type motorForwardPinOption string
// motorBackwardPinOption is the type for applying a backward pin to the configuration
type motorBackwardPinOption string
// MotorDriver Represents a Motor
type MotorDriver struct {
*driver
motorCfg *motorConfiguration
currentState byte
currentSpeed byte
currentDirection string
}
// NewMotorDriver return a new MotorDriver given a DigitalWriter and pin. This defaults to digital mode and just switch
// on and off in forward direction. Optional pins can be given, depending on your hardware. So the direction can be
// changed with one pin or by using separated forward and backward pins.
//
// If the given pin supports the PwmWriter the motor can be used/switched to analog mode by writing once to SetSpeed()
// or by calling SetAnalogMode(). The optional pins can be used for direction control.
//
// Supported options:
//
// "WithName"
// "WithMotorAnalog"
// "WithMotorDirectionPin"
// "WithMotorForwardPin"
// "WithMotorBackwardPin"
func NewMotorDriver(a DigitalWriter, speedPin string, opts ...interface{}) *MotorDriver {
//nolint:forcetypeassert // no error return value, so there is no better way
d := &MotorDriver{
driver: newDriver(a.(gobot.Connection), "Motor", withPin(speedPin)),
motorCfg: &motorConfiguration{},
currentDirection: "forward",
}
for _, opt := range opts {
switch o := opt.(type) {
case optionApplier:
o.apply(d.driverCfg)
case motorOptionApplier:
o.apply(d.motorCfg)
default:
panic(fmt.Sprintf("'%s' can not be applied on '%s'", opt, d.driverCfg.name))
}
}
return d
}
// WithMotorAnalog change the default mode "digital" to analog for the motor.
func WithMotorAnalog() motorOptionApplier {
return motorModeIsAnalogOption(true)
}
// WithMotorDirectionPin introduces a pin for change the direction of the motor.
func WithMotorDirectionPin(pin string) motorOptionApplier {
return motorDirectionPinOption(pin)
}
// WithMotorForwardPin introduces a pin for setting the direction to forward.
func WithMotorForwardPin(pin string) motorOptionApplier {
return motorForwardPinOption(pin)
}
// WithMotorBackwardPin introduces a pin for setting the direction to backward.
func WithMotorBackwardPin(pin string) motorOptionApplier {
return motorBackwardPinOption(pin)
}
// Off turns the motor off or sets the motor to a 0 speed.
func (d *MotorDriver) Off() error {
if d.IsDigital() {
return d.changeState(0)
}
return d.SetSpeed(0)
}
// On turns the motor on or sets the motor to a maximum speed.
func (d *MotorDriver) On() error {
if d.IsDigital() {
return d.changeState(1)
}
if d.currentSpeed == 0 {
d.currentSpeed = 255
}
return d.SetSpeed(d.currentSpeed)
}
// RunMin sets the motor to the minimum speed.
func (d *MotorDriver) RunMin() error {
return d.Off()
}
// RunMax sets the motor to the maximum speed.
func (d *MotorDriver) RunMax() error {
return d.SetSpeed(255)
}
// Toggle sets the motor to the opposite of it's current state.
func (d *MotorDriver) Toggle() error {
if d.IsOn() {
return d.Off()
}
return d.On()
}
// SetSpeed change the speed of the motor, without change the direction.
func (d *MotorDriver) SetSpeed(value byte) error {
if writer, ok := d.connection.(PwmWriter); ok {
WithMotorAnalog().apply(d.motorCfg)
d.currentSpeed = value
return writer.PwmWrite(d.driverCfg.pin, value)
}
return ErrPwmWriteUnsupported
}
// Forward runs the motor forward with the specified speed.
func (d *MotorDriver) Forward(speed byte) error {
if err := d.SetDirection("forward"); err != nil {
return err
}
if err := d.SetSpeed(speed); err != nil {
return err
}
return nil
}
// Backward runs the motor backward with the specified speed.
func (d *MotorDriver) Backward(speed byte) error {
if err := d.SetDirection("backward"); err != nil {
return err
}
if err := d.SetSpeed(speed); err != nil {
return err
}
return nil
}
// Direction sets the direction pin to the specified direction.
func (d *MotorDriver) SetDirection(direction string) error {
d.currentDirection = direction
if d.motorCfg.directionPin != "" {
var level byte
if direction == "forward" {
level = 1
} else {
level = 0
}
return d.digitalWrite(d.motorCfg.directionPin, level)
}
var forwardLevel, backwardLevel byte
switch direction {
case "forward":
forwardLevel = 1
backwardLevel = 0
case "backward":
forwardLevel = 0
backwardLevel = 1
case "none":
forwardLevel = 0
backwardLevel = 0
}
if d.motorCfg.forwardPin != "" {
if err := d.digitalWrite(d.motorCfg.forwardPin, forwardLevel); err != nil {
return err
}
}
if d.motorCfg.backwardPin != "" {
return d.digitalWrite(d.motorCfg.backwardPin, backwardLevel)
}
return nil
}
// IsAnalog returns true if the motor is in analog mode.
func (d *MotorDriver) IsAnalog() bool {
return d.motorCfg.modeIsAnalog
}
// IsDigital returns true if the motor is in digital mode.
func (d *MotorDriver) IsDigital() bool {
return !d.motorCfg.modeIsAnalog
}
// IsOn returns true if the motor is on.
func (d *MotorDriver) IsOn() bool {
if d.IsDigital() {
return d.currentState == 1
}
return d.currentSpeed > 0
}
// IsOff returns true if the motor is off.
func (d *MotorDriver) IsOff() bool {
return !d.IsOn()
}
// Direction returns the current direction ("forward" or "backward") of the motor.
func (d *MotorDriver) Direction() string {
return d.currentDirection
}
// Speed returns the current speed of the motor.
func (d *MotorDriver) Speed() byte {
return d.currentSpeed
}
func (d *MotorDriver) changeState(state byte) error {
d.currentState = state
if state == 1 {
d.currentSpeed = 255
} else {
d.currentSpeed = 0
}
if d.motorCfg.forwardPin == "" {
return d.digitalWrite(d.driverCfg.pin, state)
}
if state != 1 {
return d.SetDirection("none")
}
if err := d.SetDirection(d.currentDirection); err != nil {
return err
}
if d.driverCfg.pin != "" {
if err := d.SetSpeed(d.currentSpeed); err != nil {
return err
}
}
return nil
}
func (o motorModeIsAnalogOption) String() string {
return "motor mode (analog, digital) option"
}
func (o motorDirectionPinOption) String() string {
return "direction pin option for motors"
}
func (o motorForwardPinOption) String() string {
return "forward pin option for motors"
}
func (o motorBackwardPinOption) String() string {
return "backward pin option for motors"
}
func (o motorModeIsAnalogOption) apply(cfg *motorConfiguration) {
cfg.modeIsAnalog = bool(o)
}
func (o motorDirectionPinOption) apply(cfg *motorConfiguration) {
cfg.directionPin = string(o)
}
func (o motorForwardPinOption) apply(cfg *motorConfiguration) {
cfg.forwardPin = string(o)
}
func (o motorBackwardPinOption) apply(cfg *motorConfiguration) {
cfg.backwardPin = string(o)
}