11package tinygo_servo
22
33import (
4- "time"
5-
64 "machine"
75
86 tinygoerrors "github.com/ralvarezdev/tinygo-errors"
97 tinygologger "github.com/ralvarezdev/tinygo-logger"
10- tinygodriversservo "tinygo.org/x/drivers/servo "
8+ tinygopwm "github.com/ralvarezdev/tinygo-pwm "
119)
1210
1311type (
@@ -17,19 +15,24 @@ type (
1715 isMovementEnabled func () bool
1816 isDirectionInverted bool
1917 frequency uint16
20- minPulseWidth uint16
21- halfPulseWidth uint16
22- maxPulseWidth uint16
23- rangePulseWidth uint16
18+ minPulseWidth uint32
19+ maxPulseWidth uint32
2420 centerAngle uint16
25- maxAngle uint16
26- servo tinygodriversservo.Servo
21+ actuationRange uint16
22+ leftLimitAngle uint16
23+ rightLimitAngle uint16
2724 angle uint16
2825 logger tinygologger.Logger
26+ pwm tinygopwm.PWM
27+ channel uint8
28+ period uint32
2929 }
3030)
3131
3232var (
33+ // setPeriodPrefix is the prefix for the log message when setting the PWM period
34+ setPeriodPrefix = []byte ("Set Servo PWM period to:" )
35+
3336 // setAnglePrefix is the prefix message for new angle setting
3437 setAnglePrefix = []byte ("Set servo angle degrees to:" )
3538)
@@ -46,44 +49,95 @@ var (
4649// minPulseWidth: The minimum pulse width for the servo motor
4750// maxPulseWidth: The maximum pulse width for the servo motor
4851// centerAngle: The center angle of the servo motor
49- // maxAngle: The maximum angle the servo can move from the center
52+ // maxLeftAngle: The maximum left angle from the center
53+ // maxRightAngle: The maximum right angle from the center
5054// isDirectionInverted: Whether the direction of the servo motor is inverted
5155// logger: The logger instance for logging messages
5256//
5357// Returns:
5458//
5559// An instance of DefaultHandler and an error if any occurred during initialization
5660func NewDefaultHandler (
57- pwm tinygodriversservo .PWM ,
61+ pwm tinygopwm .PWM ,
5862 pin machine.Pin ,
5963 afterSetAngleFunc func (angle uint16 ),
6064 isMovementEnabled func () bool ,
6165 frequency uint16 ,
62- minPulseWidth uint16 ,
63- maxPulseWidth uint16 ,
66+ minPulseWidth uint32 ,
67+ maxPulseWidth uint32 ,
68+ actuationRange uint16 ,
6469 centerAngle uint16 ,
65- maxAngle uint16 ,
70+ maxLeftAngle uint16 ,
71+ maxRightAngle uint16 ,
6672 isDirectionInverted bool ,
6773 logger tinygologger.Logger ,
6874) (* DefaultHandler , tinygoerrors.ErrorCode ) {
75+ // Check if the frequency is zero
76+ if frequency == 0 {
77+ return nil , ErrorCodeServoZeroFrequency
78+ }
79+
6980 // Configure the PWM
81+ period := 1e9 / float64 (frequency )
7082 if err := pwm .Configure (
7183 machine.PWMConfig {
72- Period : uint64 (time . Second / time . Duration ( frequency ) ),
84+ Period : uint64 (period ),
7385 },
7486 ); err != nil {
7587 return nil , ErrorCodeServoFailedToConfigurePWM
7688 }
7789
78- // Create a new instance of the servo
79- servo , err := tinygodriversservo .New (pwm , pin )
90+ // Log the configured period
91+ if logger != nil {
92+ logger .AddMessageWithUint32 (
93+ setPeriodPrefix ,
94+ uint32 (period ),
95+ true ,
96+ true ,
97+ false ,
98+ )
99+ logger .Debug ()
100+ }
101+
102+ // Get the channel from the pin
103+ channel , err := pwm .Channel (pin )
80104 if err != nil {
81- return nil , ErrorCodeServoFailedToInitializeServo
105+ return nil , ErrorCodeServoFailedToGetPWMChannel
106+ }
107+
108+ // Check if the min pulse width is valid
109+ if minPulseWidth == 0 || minPulseWidth >= uint32 (period ) {
110+ return nil , ErrorCodeServoInvalidMinPulseWidth
111+ }
112+
113+ // Check if the max pulse width is valid
114+ if maxPulseWidth == 0 || maxPulseWidth >= uint32 (period ) {
115+ return nil , ErrorCodeServoInvalidMaxPulseWidth
116+ }
117+
118+ // Check if the actuation range is valid
119+ if actuationRange == 0 || actuationRange > 360 {
120+ return nil , ErrorCodeServoInvalidActuationRange
82121 }
83122
84- // Calculate the half pulse and range pulse
85- halfPulseWidth := (maxPulseWidth + minPulseWidth ) / 2
86- rangePulseWidth := maxPulseWidth - minPulseWidth
123+ // Check if the center angle is valid
124+ if centerAngle < 0 || centerAngle > actuationRange {
125+ return nil , ErrorCodeServoInvalidCenterAngle
126+ }
127+
128+ // Calculate the left and right limit angles
129+ leftLimitAngle := centerAngle - maxLeftAngle
130+ rightLimitAngle := centerAngle + maxRightAngle
131+
132+ // Check if the left limit angle is valid
133+ if leftLimitAngle < 0 {
134+ leftLimitAngle = 0
135+ }
136+
137+ // Check if the right limit angle is valid
138+ if rightLimitAngle > actuationRange {
139+ rightLimitAngle = actuationRange
140+ }
87141
88142 // Initialize the servo with the provided parameters
89143 handler := & DefaultHandler {
@@ -92,14 +146,17 @@ func NewDefaultHandler(
92146 isDirectionInverted : isDirectionInverted ,
93147 frequency : frequency ,
94148 minPulseWidth : minPulseWidth ,
95- halfPulseWidth : halfPulseWidth ,
96149 maxPulseWidth : maxPulseWidth ,
97- rangePulseWidth : rangePulseWidth ,
98- servo : servo ,
99150 angle : centerAngle ,
100151 centerAngle : centerAngle ,
152+ actuationRange : actuationRange ,
101153 logger : logger ,
102- maxAngle : maxAngle ,
154+ pwm : pwm ,
155+ channel : channel ,
156+ leftLimitAngle : leftLimitAngle ,
157+ rightLimitAngle : rightLimitAngle ,
158+ period : uint32 (period ),
159+
103160 }
104161
105162 // Center the servo on initialization
@@ -123,10 +180,7 @@ func (h *DefaultHandler) GetAngle() uint16 {
123180// angle: The angle to set the servo motor to, must be between 0 and the actuation range
124181func (h * DefaultHandler ) SetAngle (angle uint16 ) tinygoerrors.ErrorCode {
125182 // Check if the angle is within the valid range
126- if angle < h .centerAngle - h .maxAngle || angle > h .centerAngle + h .maxAngle {
127- return ErrorCodeServoAngleOutOfRange
128- }
129- if angle < LeftLimitAngle || angle > RightLimitAngle {
183+ if angle < h .centerAngle - h .leftLimitAngle || angle > h .centerAngle + h .rightLimitAngle {
130184 return ErrorCodeServoAngleOutOfRange
131185 }
132186
@@ -137,21 +191,24 @@ func (h *DefaultHandler) SetAngle(angle uint16) tinygoerrors.ErrorCode {
137191
138192 // Check if the direction is inverted
139193 if h .isDirectionInverted {
140- angle = RightLimitAngle - (angle - LeftLimitAngle )
194+ angle = h . rightLimitAngle - (angle - h . leftLimitAngle )
141195 }
142196
143197 // Update the current angle
144198 h .angle = angle
145199
200+ // Calculate the pulse
201+ pulse := uint32 (h .minPulseWidth ) + uint32 (float64 (h .maxPulseWidth - h .minPulseWidth ) * float64 (angle ) / float64 (h .actuationRange ))
202+
203+
146204 // Set the servo angle
147205 if h .isMovementEnabled == nil || h .isMovementEnabled () {
148- if err := h .servo .SetAngleWithMicroseconds (
149- int (angle ),
150- int (h .minPulseWidth ),
151- int (h .maxPulseWidth ),
152- ); err != nil {
153- return ErrorCodeServoFailedToSetServoAngle
154- }
206+ tinygopwm .SetDuty (
207+ h .pwm ,
208+ h .channel ,
209+ pulse ,
210+ h .period ,
211+ )
155212 }
156213
157214 // Log the new angle if logger is provided
@@ -200,7 +257,7 @@ func (h *DefaultHandler) SetAngleRelativeToCenter(relativeAngle int16) tinygoerr
200257 absoluteAngle := int16 (h .centerAngle ) + relativeAngle
201258
202259 // Check if the absolute angle is within the left and right limits
203- if absoluteAngle < int16 (LeftLimitAngle ) || absoluteAngle > int16 (RightLimitAngle ) {
260+ if absoluteAngle < int16 (h . leftLimitAngle ) || absoluteAngle > int16 (h . rightLimitAngle ) {
204261 return ErrorCodeServoAngleOutOfRange
205262 }
206263
@@ -218,23 +275,16 @@ func (h *DefaultHandler) SetAngleRelativeToCenter(relativeAngle int16) tinygoerr
218275//
219276// An error if the angle is not within the right limit
220277func (h * DefaultHandler ) SetAngleToRight (angle uint16 ) tinygoerrors.ErrorCode {
221- return h .SetAngleRelativeToCenter (int16 (angle ))
222- }
278+ // Check if the angle is negative
279+ if angle < 0 {
280+ angle = 0
281+ }
223282
224- // SafeSetAngleToRight sets the servo motor to the right by a specified angle without exceeding limits
225- //
226- // Parameters:
227- //
228- // angle: The angle value to move the servo to the right, must be between 0 and the right limit
229- //
230- // Returns:
231- //
232- // An error if the angle is not within the right limit
233- func (h * DefaultHandler ) SafeSetAngleToRight (angle uint16 ) tinygoerrors.ErrorCode {
234- if angle > h .maxAngle {
235- angle = h .maxAngle
283+ // Check if the angle is within the right limit
284+ if angle > h .rightLimitAngle - h .centerAngle {
285+ angle = h .rightLimitAngle - h .centerAngle
236286 }
237- return h .SetAngleToRight ( angle )
287+ return h .SetAngleRelativeToCenter ( int16 ( angle ) )
238288}
239289
240290// SetAngleToLeft sets the servo motor to the left by a specified angle
@@ -247,21 +297,14 @@ func (h *DefaultHandler) SafeSetAngleToRight(angle uint16) tinygoerrors.ErrorCod
247297//
248298// An error if the angle is not within the left limit
249299func (h * DefaultHandler ) SetAngleToLeft (angle uint16 ) tinygoerrors.ErrorCode {
250- return h .SetAngleRelativeToCenter (- int16 (angle ))
251- }
300+ // Check if the angle is negative
301+ if angle < 0 {
302+ angle = 0
303+ }
252304
253- // SafeSetAngleToLeft sets the servo motor to the left by a specified angle without exceeding limits
254- //
255- // Parameters:
256- //
257- // angle: The angle value to move the servo to the left, must be between 0 and the left limit
258- //
259- // Returns:
260- //
261- // An error if the angle is not within the left limit
262- func (h * DefaultHandler ) SafeSetAngleToLeft (angle uint16 ) tinygoerrors.ErrorCode {
263- if angle > h .maxAngle {
264- angle = h .maxAngle
305+ // Check if the angle is within the left limit
306+ if angle > h .centerAngle - h .leftLimitAngle {
307+ angle = h .centerAngle - h .leftLimitAngle
265308 }
266- return h .SetAngleToLeft ( angle )
267- }
309+ return h .SetAngleRelativeToCenter ( - int16 ( angle ) )
310+ }
0 commit comments