1#[cfg(target_os = "linux")]
2mod raw;
3
4#[cfg(target_os = "linux")]
5use linux_embedded_hal::I2cdev;
6#[cfg(target_os = "linux")]
7use pwm_pca9685::Pca9685;
8#[cfg(target_os = "linux")]
9use raw::dc::DcKit;
10#[cfg(target_os = "linux")]
11use raw::servo::ServoKit;
12#[cfg(target_os = "linux")]
13use raw::{init_motor_pwm, init_servo_pwm, Motor};
14#[cfg(target_os = "linux")]
15use std::ops::Drop;
16
17use std::time::{Duration, Instant};
18
19#[derive(PartialEq, PartialOrd)]
20pub struct Velocity(f32);
21
22impl Velocity {
23 pub fn backward() -> Self {
24 Self(-100.0)
25 }
26
27 pub fn none() -> Self {
28 Self(0.0)
29 }
30
31 pub fn forward() -> Self {
32 Self(100.0)
33 }
34
35 pub fn into_inner(self) -> f32 {
36 self.0
37 }
38}
39
40impl TryFrom<f32> for Velocity {
41 type Error = eyre::Report;
42
43 fn try_from(value: f32) -> eyre::Result<Velocity> {
44 eyre::ensure!(
45 value >= Velocity::backward().0 && value <= Velocity::forward().0,
46 "velocity must be between {} and {}",
47 Velocity::backward().0,
48 Velocity::forward().0,
49 );
50 Ok(Velocity(value))
51 }
52}
53
54impl Default for Velocity {
55 fn default() -> Self {
56 Self(0.0)
57 }
58}
59
60pub struct MotorSocket {
62 #[cfg(target_os = "linux")]
63 dc_motor: DcKit,
64 #[cfg(target_os = "linux")]
65 dc_pwm: Pca9685<I2cdev>,
66 cooldown_since: Instant,
67}
68
69impl MotorSocket {
70 pub async fn open() -> eyre::Result<Self> {
72 #[cfg(target_os = "linux")]
73 let mut dc_pwm = init_motor_pwm(None)?;
74 #[cfg(target_os = "linux")]
75 let dc_motor = DcKit::try_new(&mut dc_pwm, Motor::Motor1)?;
76 Ok(MotorSocket {
77 #[cfg(target_os = "linux")]
78 dc_pwm,
79 #[cfg(target_os = "linux")]
80 dc_motor,
81 cooldown_since: Instant::now(),
82 })
83 }
84
85 pub async fn move_for(&mut self, velocity: Velocity, max_dur: Duration) -> eyre::Result<()> {
89 if let Some(left) = Duration::from_secs(3).checked_sub(self.cooldown_since.elapsed()) {
90 tokio::time::sleep(left).await;
91 }
92 #[cfg(target_os = "linux")]
94 self.dc_motor
95 .set_throttle(&mut self.dc_pwm, -1. * velocity.into_inner() / 100.0)?;
96 #[cfg(not(target_os = "linux"))]
97 let _ = velocity; let actual_dur = std::cmp::min(Duration::from_secs(1), max_dur);
99 tokio::time::sleep(actual_dur).await;
100 #[cfg(target_os = "linux")]
101 self.dc_motor
102 .set_throttle(&mut self.dc_pwm, Velocity::none().into_inner() / 100.0)?;
103 self.cooldown_since = Instant::now();
104 Ok(())
105 }
106
107 pub fn stop(&mut self) -> eyre::Result<()> {
111 #[cfg(target_os = "linux")]
112 self.dc_motor.stop(&mut self.dc_pwm)?;
113 Ok(())
114 }
115}
116
117impl Drop for MotorSocket {
118 fn drop(&mut self) {
119 let _ = self.stop();
120 }
121}
122
123pub struct WheelOrientation {
125 #[cfg(target_os = "linux")]
126 servo: ServoKit,
127 #[cfg(target_os = "linux")]
128 servo_pwm: Pca9685<I2cdev>,
129 current: Angle,
130}
131
132impl WheelOrientation {
133 pub async fn new() -> eyre::Result<Self> {
135 #[cfg(target_os = "linux")]
136 let mut servo_pwm = init_servo_pwm(None)?;
137 #[cfg(target_os = "linux")]
138 let servo = ServoKit::try_new(&mut servo_pwm, Motor::Servo)?;
139 Ok(WheelOrientation {
140 #[cfg(target_os = "linux")]
141 servo_pwm,
142 #[cfg(target_os = "linux")]
143 servo,
144 current: Default::default(),
145 })
146 }
147
148 pub async fn current(self) -> eyre::Result<Angle> {
150 Ok(self.current)
151 }
152
153 pub async fn set(&mut self, angle: Angle) -> eyre::Result<()> {
155 self.current = angle;
156 let raw: f32 = (-90.0 * angle.into_inner() + 90.0).try_into()?;
157 #[cfg(not(target_os = "linux"))]
158 let _ = raw;
159 #[cfg(target_os = "linux")]
160 self.servo.set_angle(&mut self.servo_pwm, raw)?;
161 Ok(())
162 }
163}
164
165impl Drop for WheelOrientation {
166 fn drop(&mut self) {
167 #[cfg(target_os = "linux")]
168 let _ = self.servo.set_angle(&mut self.servo_pwm, 90.0);
169 }
170}
171
172#[derive(PartialEq, PartialOrd, Copy, Clone)]
173pub struct Angle(f32);
174
175impl Angle {
176 pub fn left() -> Self {
177 Self(-1.0)
178 }
179
180 pub fn straight() -> Self {
181 Self(0.0)
182 }
183
184 pub fn right() -> Self {
185 Self(1.0)
186 }
187
188 pub fn into_inner(self) -> f32 {
189 self.0
190 }
191}
192
193impl TryFrom<f32> for Angle {
194 type Error = eyre::Report;
195
196 fn try_from(value: f32) -> eyre::Result<Angle> {
197 eyre::ensure!(
198 value >= Angle::left().0 && value <= Angle::right().0,
199 "angle must be between {} and {}",
200 Angle::left().0,
201 Angle::right().0,
202 );
203 Ok(Angle(value))
204 }
205}
206
207impl Default for Angle {
208 fn default() -> Self {
209 Self::straight()
210 }
211}