1use crate::device::PoKeysDevice;
4use crate::error::{PoKeysError, Result};
5use serde::{Deserialize, Serialize};
6
7const PWM_PIN_MAP: [u8; 6] = [22, 21, 20, 19, 18, 17];
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub enum ServoType {
14 OneEighty { pos_0: u32, pos_180: u32 },
16 ThreeSixtyPosition { pos_0: u32, pos_360: u32 },
18 ThreeSixtySpeed {
20 stop: u32,
21 clockwise: u32,
22 anti_clockwise: u32,
23 },
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ServoConfig {
29 pub pin: u8,
30 pub servo_type: ServoType,
31}
32
33impl ServoConfig {
34 pub fn one_eighty(pin: u8, pos_0: u32, pos_180: u32) -> Self {
36 Self {
37 pin,
38 servo_type: ServoType::OneEighty { pos_0, pos_180 },
39 }
40 }
41
42 pub fn three_sixty_position(pin: u8, pos_0: u32, pos_360: u32) -> Self {
44 Self {
45 pin,
46 servo_type: ServoType::ThreeSixtyPosition { pos_0, pos_360 },
47 }
48 }
49
50 pub fn three_sixty_speed(pin: u8, stop: u32, clockwise: u32, anti_clockwise: u32) -> Self {
52 Self {
53 pin,
54 servo_type: ServoType::ThreeSixtySpeed {
55 stop,
56 clockwise,
57 anti_clockwise,
58 },
59 }
60 }
61
62 pub fn set_angle(&self, device: &mut PoKeysDevice, angle: f32) -> Result<()> {
64 let duty = match &self.servo_type {
65 ServoType::OneEighty { pos_0, pos_180 } => {
66 if !(0.0..=180.0).contains(&angle) {
67 return Err(PoKeysError::Parameter(
68 "Angle must be between 0.0 and 180.0 degrees".to_string(),
69 ));
70 }
71 let range = *pos_180 as f32 - *pos_0 as f32;
72 (*pos_0 as f32 + (angle / 180.0) * range) as u32
73 }
74 ServoType::ThreeSixtyPosition { pos_0, pos_360 } => {
75 if !(0.0..=360.0).contains(&angle) {
76 return Err(PoKeysError::Parameter(
77 "Angle must be between 0.0 and 360.0 degrees".to_string(),
78 ));
79 }
80 let range = *pos_360 as f32 - *pos_0 as f32;
81 (*pos_0 as f32 + (angle / 360.0) * range) as u32
82 }
83 ServoType::ThreeSixtySpeed { .. } => {
84 return Err(PoKeysError::Parameter(
85 "Cannot set angle on speed servo. Use set_speed() instead".to_string(),
86 ));
87 }
88 };
89
90 device.set_pwm_duty_cycle_for_pin(self.pin, duty)
91 }
92
93 pub fn set_speed(&self, device: &mut PoKeysDevice, speed: f32) -> Result<()> {
95 let duty = match &self.servo_type {
96 ServoType::ThreeSixtySpeed {
97 stop,
98 clockwise,
99 anti_clockwise,
100 } => {
101 if !(-100.0..=100.0).contains(&speed) {
102 return Err(PoKeysError::Parameter(
103 "Speed must be between -100.0 and 100.0".to_string(),
104 ));
105 }
106
107 if speed == 0.0 {
108 *stop
109 } else if speed > 0.0 {
110 let range = *clockwise as f32 - *stop as f32;
112 (*stop as f32 + (speed / 100.0) * range) as u32
113 } else {
114 let range = *anti_clockwise as f32 - *stop as f32;
116 (*stop as f32 + (speed.abs() / 100.0) * range) as u32
117 }
118 }
119 _ => {
120 return Err(PoKeysError::Parameter(
121 "Cannot set speed on position servo. Use set_angle() instead".to_string(),
122 ));
123 }
124 };
125
126 device.set_pwm_duty_cycle_for_pin(self.pin, duty)
127 }
128
129 pub fn stop(&self, device: &mut PoKeysDevice) -> Result<()> {
131 match &self.servo_type {
132 ServoType::ThreeSixtySpeed { stop, .. } => {
133 device.set_pwm_duty_cycle_for_pin(self.pin, *stop)
134 }
135 _ => Err(PoKeysError::Parameter(
136 "Stop command only applies to speed servos".to_string(),
137 )),
138 }
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct PwmData {
145 pub pwm_period: u32,
147 pub pwm_values: [u32; 6],
149 pub enabled_channels: u8,
151}
152
153impl Default for PwmData {
154 fn default() -> Self {
155 Self::new()
156 }
157}
158
159impl PwmData {
160 pub fn new() -> Self {
161 Self {
162 pwm_period: 0,
163 pwm_values: [0; 6],
164 enabled_channels: 0,
165 }
166 }
167
168 pub fn pin_to_channel(pin: u8) -> Result<usize> {
170 match pin {
171 22 => Ok(0), 21 => Ok(1), 20 => Ok(2), 19 => Ok(3), 18 => Ok(4), 17 => Ok(5), _ => Err(PoKeysError::Parameter(format!(
178 "Pin {} does not support PWM. PWM is only supported on pins 17-22",
179 pin
180 ))),
181 }
182 }
183
184 pub fn channel_to_pin(channel: usize) -> Result<u8> {
186 if channel >= 6 {
187 return Err(PoKeysError::Parameter(format!(
188 "Invalid PWM channel {}. Valid channels are 0-5",
189 channel
190 )));
191 }
192 Ok(PWM_PIN_MAP[channel])
193 }
194
195 pub fn set_channel_enabled(&mut self, channel: usize, enabled: bool) -> Result<()> {
197 if channel >= 6 {
198 return Err(PoKeysError::Parameter(format!(
199 "Invalid PWM channel {}. Valid channels are 0-5",
200 channel
201 )));
202 }
203
204 if enabled {
205 self.enabled_channels |= 1 << channel;
206 } else {
207 self.enabled_channels &= !(1 << channel);
208 }
209 Ok(())
210 }
211
212 pub fn is_channel_enabled(&self, channel: usize) -> bool {
214 if channel >= 6 {
215 return false;
216 }
217 (self.enabled_channels & (1 << channel)) != 0
218 }
219
220 pub fn set_duty_cycle(&mut self, channel: usize, duty: u32) -> Result<()> {
222 if channel >= 6 {
223 return Err(PoKeysError::Parameter(format!(
224 "Invalid PWM channel {}. Valid channels are 0-5",
225 channel
226 )));
227 }
228 self.pwm_values[channel] = duty;
229 Ok(())
230 }
231
232 pub fn get_duty_cycle(&self, channel: usize) -> Result<u32> {
234 if channel >= 6 {
235 return Err(PoKeysError::Parameter(format!(
236 "Invalid PWM channel {}. Valid channels are 0-5",
237 channel
238 )));
239 }
240 Ok(self.pwm_values[channel])
241 }
242}
243
244impl PoKeysDevice {
245 pub fn set_pwm_configuration(&mut self) -> Result<()> {
247 let mut data = [0u8; 32];
248
249 data[0] = self.pwm.enabled_channels;
251
252 for i in 0..6 {
254 let value = self.pwm.pwm_values[i];
255 let base_idx = 1 + (i * 4);
256 data[base_idx] = (value & 0xFF) as u8;
257 data[base_idx + 1] = ((value >> 8) & 0xFF) as u8;
258 data[base_idx + 2] = ((value >> 16) & 0xFF) as u8;
259 data[base_idx + 3] = ((value >> 24) & 0xFF) as u8;
260 }
261
262 let period = self.pwm.pwm_period;
264 data[25] = (period & 0xFF) as u8;
265 data[26] = ((period >> 8) & 0xFF) as u8;
266 data[27] = ((period >> 16) & 0xFF) as u8;
267 data[28] = ((period >> 24) & 0xFF) as u8;
268
269 self.send_request_with_data(0xCB, 1, 0, 0, 0, &data)?;
270 Ok(())
271 }
272
273 pub fn update_pwm_duty_values(&mut self) -> Result<()> {
275 let mut data = [0u8; 32];
276
277 data[0] = self.pwm.enabled_channels;
279
280 for i in 0..6 {
282 let value = self.pwm.pwm_values[i];
283 let base_idx = 1 + (i * 4);
284 data[base_idx] = (value & 0xFF) as u8;
285 data[base_idx + 1] = ((value >> 8) & 0xFF) as u8;
286 data[base_idx + 2] = ((value >> 16) & 0xFF) as u8;
287 data[base_idx + 3] = ((value >> 24) & 0xFF) as u8;
288 }
289
290 self.send_request_with_data(0xCB, 1, 1, 0, 0, &data)?;
291 Ok(())
292 }
293
294 pub fn get_pwm_configuration(&mut self) -> Result<()> {
296 let response = self.send_request(0xCB, 0, 0, 0, 0)?;
297
298 if response.len() >= 38 {
300 self.pwm.enabled_channels = response[9];
302
303 for i in 0..6 {
305 let base_idx = 10 + (i * 4);
306 if base_idx + 3 < response.len() {
307 self.pwm.pwm_values[i] = response[base_idx] as u32
308 | ((response[base_idx + 1] as u32) << 8)
309 | ((response[base_idx + 2] as u32) << 16)
310 | ((response[base_idx + 3] as u32) << 24);
311 }
312 }
313
314 if response.len() >= 38 {
316 self.pwm.pwm_period = response[34] as u32
317 | ((response[35] as u32) << 8)
318 | ((response[36] as u32) << 16)
319 | ((response[37] as u32) << 24);
320 }
321 }
322
323 Ok(())
324 }
325
326 pub fn set_pwm_period(&mut self, period: u32) -> Result<()> {
328 if period == 0 {
329 return Err(PoKeysError::Parameter(
330 "PWM period cannot be zero".to_string(),
331 ));
332 }
333
334 self.pwm.pwm_period = period;
335 self.set_pwm_configuration()
336 }
337
338 pub fn get_pwm_period(&self) -> u32 {
340 self.pwm.pwm_period
341 }
342
343 pub fn set_pwm_duty_cycle_for_pin(&mut self, pin: u8, duty: u32) -> Result<()> {
345 let channel = PwmData::pin_to_channel(pin)?;
346 self.pwm.set_duty_cycle(channel, duty)?;
347 self.update_pwm_duty_values()
348 }
349
350 pub fn get_pwm_duty_cycle_for_pin(&self, pin: u8) -> Result<u32> {
352 let channel = PwmData::pin_to_channel(pin)?;
353 self.pwm.get_duty_cycle(channel)
354 }
355
356 pub fn enable_pwm_for_pin(&mut self, pin: u8, enabled: bool) -> Result<()> {
358 let channel = PwmData::pin_to_channel(pin)?;
359 self.pwm.set_channel_enabled(channel, enabled)?;
360 self.set_pwm_configuration()
361 }
362
363 pub fn is_pwm_enabled_for_pin(&self, pin: u8) -> Result<bool> {
365 let channel = PwmData::pin_to_channel(pin)?;
366 Ok(self.pwm.is_channel_enabled(channel))
367 }
368
369 pub fn set_pwm_duty_cycle_percent_for_pin(&mut self, pin: u8, percent: f32) -> Result<()> {
371 if !(0.0..=100.0).contains(&percent) {
372 return Err(PoKeysError::Parameter(
373 "PWM duty cycle percentage must be between 0.0 and 100.0".to_string(),
374 ));
375 }
376
377 let duty = ((percent / 100.0) * self.pwm.pwm_period as f32) as u32;
378 self.set_pwm_duty_cycle_for_pin(pin, duty)
379 }
380
381 pub fn get_pwm_duty_cycle_percent_for_pin(&self, pin: u8) -> Result<f32> {
383 let duty = self.get_pwm_duty_cycle_for_pin(pin)?;
384 if self.pwm.pwm_period == 0 {
385 return Ok(0.0);
386 }
387 Ok((duty as f32 / self.pwm.pwm_period as f32) * 100.0)
388 }
389}
390
391pub fn simple_pwm(
393 device: &mut PoKeysDevice,
394 pin: u8,
395 frequency_hz: u32,
396 duty_percent: f32,
397) -> Result<()> {
398 PwmData::pin_to_channel(pin)?;
400
401 let period = if frequency_hz > 0 {
404 25_000_000 / frequency_hz } else {
406 return Err(PoKeysError::Parameter(
407 "Frequency must be greater than 0".to_string(),
408 ));
409 };
410
411 device.set_pwm_period(period)?;
413
414 device.enable_pwm_for_pin(pin, true)?;
416
417 device.set_pwm_duty_cycle_percent_for_pin(pin, duty_percent)
419}