1+ //go:build tinygo && (rp2040 || rp2350)
2+
3+ package tinygo_servo
4+
5+ import (
6+ "time"
7+
8+ "machine"
9+
10+ tinygotypes "github.com/ralvarezdev/tinygo-types"
11+ tinygodriversservo "tinygo.org/x/drivers/servo"
12+ tinygologger "github.com/ralvarezdev/tinygo-logger"
13+ )
14+
15+ type (
16+ // DefaultHandler is the default implementation of the Servo interface
17+ DefaultHandler struct {
18+ afterSetAngleFunc func (angle uint16 )
19+ isMovementEnabled func () bool
20+ isDirectionInverted bool
21+ frequency uint16
22+ minPulseWidth uint16
23+ halfPulseWidth uint16
24+ maxPulseWidth uint16
25+ rangePulseWidth uint16
26+ centerAngle uint16
27+ maxAngle uint16
28+ servo tinygodriversservo.Servo
29+ angle uint16
30+ logger tinygologger.Logger
31+ }
32+ )
33+
34+ var (
35+ // setAnglePrefix is the prefix message for new angle setting
36+ setAnglePrefix = "Set servo angle degrees to:"
37+ )
38+
39+ // NewDefaultHandler creates a new instance of DefaultHandler
40+ //
41+ // Parameters:
42+ //
43+ // pwm: The PWM interface to control the servo
44+ // pin: The pin connected to the servo
45+ // afterSetAngleFunc: A callback function to be called after setting the angle
46+ // isMovementEnabled: A function to check if movement is enabled
47+ // frequency: The frequency of the PWM signal
48+ // minPulseWidth: The minimum pulse width for the servo motor
49+ // maxPulseWidth: The maximum pulse width for the servo motor
50+ // centerAngle: The center angle of the servo motor
51+ // maxAngle: The maximum angle the servo can move from the center
52+ // isDirectionInverted: Whether the direction of the servo motor is inverted
53+ // logger: The logger instance for logging messages
54+ //
55+ // Returns:
56+ //
57+ // An instance of DefaultHandler and an error if any occurred during initialization
58+ func NewDefaultHandler (
59+ pwm tinygodriversservo.PWM ,
60+ pin machine.Pin ,
61+ afterSetAngleFunc func (angle uint16 ),
62+ isMovementEnabled func () bool ,
63+ frequency uint16 ,
64+ minPulseWidth uint16 ,
65+ maxPulseWidth uint16 ,
66+ centerAngle uint16 ,
67+ maxAngle uint16 ,
68+ isDirectionInverted bool ,
69+ logger tinygologger.Logger ,
70+ ) (* DefaultHandler , tinygotypes.ErrorCode ) {
71+ // Configure the PWM
72+ if err := pwm .Configure (
73+ machine.PWMConfig {
74+ Period : uint64 (time .Second / time .Duration (frequency )),
75+ },
76+ ); err != nil {
77+ return nil , ErrorCodeServoFailedToConfigurePWM
78+ }
79+
80+ // Create a new instance of the servo
81+ servo , err := tinygodriversservo .New (pwm , pin )
82+ if err != nil {
83+ return nil , ErrorCodeServoFailedToInitializeServo
84+ }
85+
86+ // Calculate the half pulse and range pulse
87+ halfPulseWidth := (maxPulseWidth + minPulseWidth ) / 2
88+ rangePulseWidth := maxPulseWidth - minPulseWidth
89+
90+ // Initialize the servo with the provided parameters
91+ handler := & DefaultHandler {
92+ afterSetAngleFunc : afterSetAngleFunc ,
93+ isMovementEnabled : isMovementEnabled ,
94+ isDirectionInverted : isDirectionInverted ,
95+ frequency : frequency ,
96+ minPulseWidth : minPulseWidth ,
97+ halfPulseWidth : halfPulseWidth ,
98+ maxPulseWidth : maxPulseWidth ,
99+ rangePulseWidth : rangePulseWidth ,
100+ servo : servo ,
101+ angle : centerAngle ,
102+ centerAngle : centerAngle ,
103+ logger : logger ,
104+ }
105+
106+ // Center the servo on initialization
107+ _ = handler .SetAngleToCenter ()
108+ return handler , tinygotypes .ErrorCodeNil
109+ }
110+
111+ // GetAngle returns the current angle of the servo motor
112+ //
113+ // Returns:
114+ //
115+ // The current angle of the servo motor
116+ func (h * DefaultHandler ) GetAngle () uint16 {
117+ return h .angle
118+ }
119+
120+ // SetAngle sets the angle of the servo motor
121+ //
122+ // Parameters:
123+ //
124+ // angle: The angle to set the servo motor to, must be between 0 and the actuation range
125+ func (h * DefaultHandler ) SetAngle (angle uint16 ) tinygotypes.ErrorCode {
126+ // Check if the angle is within the valid range
127+ if angle < h .centerAngle - h .maxAngle || angle > h .centerAngle + h .maxAngle {
128+ return ErrorCodeServoAngleOutOfRange
129+ }
130+ if angle < LeftLimitAngle || angle > RightLimitAngle {
131+ return ErrorCodeServoAngleOutOfRange
132+ }
133+
134+ // Check if the angle is the same as the current angle
135+ if angle == h .angle {
136+ return tinygotypes .ErrorCodeNil
137+ }
138+
139+ // Check if the direction is inverted
140+ if h .isDirectionInverted {
141+ angle = RightLimitAngle - (angle - LeftLimitAngle )
142+ }
143+
144+ // Update the current angle
145+ h .angle = angle
146+
147+ // Set the servo angle
148+ if h .isMovementEnabled == nil || h .isMovementEnabled () {
149+ if err := h .servo .SetAngleWithMicroseconds (
150+ int (angle ),
151+ int (h .minPulseWidth ),
152+ int (h .maxPulseWidth ),
153+ ); err != nil {
154+ return ErrorCodeServoFailedToSetServoAngle
155+ }
156+ }
157+
158+ // Log the new angle if logger is provided
159+ if h .logger != nil {
160+ h .logger .AddMessageWithUint16 (setAnglePrefix , angle , true , true , false )
161+ h .logger .Debug ()
162+ }
163+
164+ // Call the after set angle function if provided
165+ if h .afterSetAngleFunc != nil {
166+ h .afterSetAngleFunc (angle )
167+ }
168+
169+ return tinygotypes .ErrorCodeNil
170+ }
171+
172+ // IsAngleCentered checks if the servo motor angle is centered
173+ //
174+ // Returns:
175+ //
176+ // True if the servo motor is centered, false otherwise
177+ func (h * DefaultHandler ) IsAngleCentered () bool {
178+ return h .angle == h .centerAngle
179+ }
180+
181+ // SetAngleToCenter centers the servo motor to the middle position
182+ //
183+ // Returns:
184+ //
185+ // An error if the servo motor could not be centered
186+ func (h * DefaultHandler ) SetAngleToCenter () tinygotypes.ErrorCode {
187+ return h .SetAngle (h .centerAngle )
188+ }
189+
190+ // SetAngleRelativeToCenter sets the angle of the servo motor relative to the center position
191+ //
192+ // Parameters:
193+ //
194+ // relativeAngle: The relative angle value between -90 and 90 degrees
195+ //
196+ // Returns:
197+ //
198+ // An error if the relative angle is not within the left and right limits
199+ func (h * DefaultHandler ) SetAngleRelativeToCenter (relativeAngle int16 ) tinygotypes.ErrorCode {
200+ // Calculate the absolute angle based on the center angle and relative angle
201+ absoluteAngle := int16 (h .centerAngle ) + relativeAngle
202+
203+ // Check if the absolute angle is within the left and right limits
204+ if absoluteAngle < int16 (LeftLimitAngle ) || absoluteAngle > int16 (RightLimitAngle ) {
205+ return ErrorCodeServoAngleOutOfRange
206+ }
207+
208+ // Set the servo angle
209+ return h .SetAngle (uint16 (absoluteAngle ))
210+ }
211+
212+ // SetAngleToRight sets the servo motor to the right by a specified angle
213+ //
214+ // Parameters:
215+ //
216+ // angle: The angle value to move the servo to the right, must be between 0 and the right limit
217+ //
218+ // Returns:
219+ //
220+ // An error if the angle is not within the right limit
221+ func (h * DefaultHandler ) SetAngleToRight (angle uint16 ) tinygotypes.ErrorCode {
222+ return h .SetAngleRelativeToCenter (- int16 (angle ))
223+ }
224+
225+ // SetAngleToLeft sets the servo motor to the left by a specified angle
226+ //
227+ // Parameters:
228+ //
229+ // angle: The angle value to move the servo to the left, must be between 0 and the left limit
230+ //
231+ // Returns:
232+ //
233+ // An error if the angle is not within the left limit
234+ func (h * DefaultHandler ) SetAngleToLeft (angle uint16 ) tinygotypes.ErrorCode {
235+ return h .SetAngleRelativeToCenter (int16 (angle ))
236+ }
237+
238+ // SetDirectionToCenter sets the direction to center
239+ func (h * DefaultHandler ) SetDirectionToCenter () tinygotypes.ErrorCode {
240+ return h .SetAngleToCenter ()
241+ }
242+
243+ // SetDirectionToRight sets the direction to right
244+ //
245+ // Parameters:
246+ //
247+ // angle: The angle value to move the servo to the left, must be between 0 and the left limit
248+ //
249+ // Returns:
250+ //
251+ // An error if the angle is not within the left limit
252+ func (h * DefaultHandler ) SetDirectionToRight (angle uint16 ) tinygotypes.ErrorCode {
253+ return h .SetAngleToLeft (angle )
254+ }
255+
256+ // SetDirectionToLeft sets the direction to left
257+ //
258+ // Parameters:
259+ //
260+ // angle: The angle value to move the servo to the right, must be between 0 and the right limit
261+ //
262+ // Returns:
263+ //
264+ // An error if the angle is not within the right limit
265+ func (h * DefaultHandler ) SetDirectionToLeft (angle uint16 ) tinygotypes.ErrorCode {
266+ return h .SetAngleToRight (angle )
267+ }
0 commit comments