|
| 1 | +package servo |
| 2 | + |
| 3 | +import "machine" |
| 4 | + |
| 5 | +// PWM is the interface necessary for controlling typical servo motors. |
| 6 | +type PWM interface { |
| 7 | + Configure(config machine.PWMConfig) error |
| 8 | + Channel(pin machine.Pin) (channel uint8, err error) |
| 9 | + Top() uint32 |
| 10 | + Set(channel uint8, value uint32) |
| 11 | +} |
| 12 | + |
| 13 | +// Array is an array of servos controlled by a single PWM peripheral. On most |
| 14 | +// chips, one PWM peripheral can control multiple servos (usually two or four). |
| 15 | +type Array struct { |
| 16 | + pwm PWM |
| 17 | +} |
| 18 | + |
| 19 | +// Servo is a single servo (connected to one PWM output) that's part of a servo |
| 20 | +// array. |
| 21 | +type Servo struct { |
| 22 | + pwm PWM |
| 23 | + channel uint8 |
| 24 | +} |
| 25 | + |
| 26 | +const pwmPeriod = 20e6 // 20ms |
| 27 | + |
| 28 | +// NewArray returns a new servo array based on the given PWM, for if you want to |
| 29 | +// control multiple servos from a single PWM peripheral. Using a single PWM for |
| 30 | +// multiple servos saves PWM peripherals for other uses and might use less power |
| 31 | +// depending on the chip. |
| 32 | +// |
| 33 | +// If you only want to control a single servo, you could use the New shorthand |
| 34 | +// instead. |
| 35 | +func NewArray(pwm PWM) (Array, error) { |
| 36 | + err := pwm.Configure(machine.PWMConfig{ |
| 37 | + Period: pwmPeriod, |
| 38 | + }) |
| 39 | + if err != nil { |
| 40 | + return Array{}, err |
| 41 | + } |
| 42 | + return Array{pwm}, nil |
| 43 | +} |
| 44 | + |
| 45 | +// Add adds a new servo to the servo array. Please check the chip documentation |
| 46 | +// which pins can be controlled by the given PWM: depending on the chip this |
| 47 | +// might be rigid (only a single pin) or very flexible (you can pick any pin). |
| 48 | +func (array Array) Add(pin machine.Pin) (Servo, error) { |
| 49 | + channel, err := array.pwm.Channel(pin) |
| 50 | + if err != nil { |
| 51 | + return Servo{}, err |
| 52 | + } |
| 53 | + return Servo{ |
| 54 | + pwm: array.pwm, |
| 55 | + channel: channel, |
| 56 | + }, nil |
| 57 | +} |
| 58 | + |
| 59 | +// New is a shorthand for NewArray and array.Add. This is useful if you only |
| 60 | +// want to control just a single servo. |
| 61 | +func New(pwm PWM, pin machine.Pin) (Servo, error) { |
| 62 | + array, err := NewArray(pwm) |
| 63 | + if err != nil { |
| 64 | + return Servo{}, err |
| 65 | + } |
| 66 | + return array.Add(pin) |
| 67 | +} |
| 68 | + |
| 69 | +// SetMicroseconds sets the output signal to be high for the given number of |
| 70 | +// microseconds. For many servos the range is normally between 1000µs and 2000µs |
| 71 | +// for 90° of rotation (with 1500µs being the 'neutral' middle position). |
| 72 | +// |
| 73 | +// In many cases they can actually go a bit further, with a wider range of |
| 74 | +// supported pulse ranges. For example, they might allow pulse widths from 500µs |
| 75 | +// to 2500µs, but be warned that going outside of the 1000µs-2000µs range might |
| 76 | +// break the servo as it might destroy the gears if it doesn't support this |
| 77 | +// range. Therefore, to be sure check the datasheet before you try values |
| 78 | +// outside of the 1000µs-2000µs range. |
| 79 | +func (s Servo) SetMicroseconds(microseconds int16) { |
| 80 | + value := uint64(s.pwm.Top()) * uint64(microseconds) / (pwmPeriod / 1000) |
| 81 | + s.pwm.Set(s.channel, uint32(value)) |
| 82 | +} |
0 commit comments