a8mini_camera_rs/
lib.rs

1#![allow(non_snake_case)]
2
3use bincode::deserialize;
4use std::error::Error;
5use tokio::{net::UdpSocket, time::timeout};
6
7pub mod checksum;
8pub mod constants;
9pub mod control;
10
11#[derive(Debug)]
12pub struct A8Mini {
13    pub command_socket: UdpSocket,
14    pub http_socket: UdpSocket,
15}
16
17impl A8Mini {
18    pub async fn connect() -> Result<Self, Box<dyn Error>> {
19        Ok(Self::connect_to(
20            constants::CAMERA_IP,
21            constants::CAMERA_COMMAND_PORT,
22            constants::CAMERA_HTTP_PORT,
23            "8080",
24            "8088",
25        )
26        .await?)
27    }
28
29    pub async fn connect_to(
30        camera_ip: &str,
31        camera_command_port: &str,
32        camera_http_port: &str,
33        local_command_port: &str,
34        local_http_port: &str,
35    ) -> Result<A8Mini, Box<dyn Error>> {
36        let camera: A8Mini = A8Mini {
37            command_socket: UdpSocket::bind(format!("0.0.0.0:{}", local_command_port)).await?,
38            http_socket: UdpSocket::bind(format!("0.0.0.0:{}", local_http_port)).await?,
39        };
40
41        camera
42            .command_socket
43            .connect(format!("{}:{}", camera_ip, camera_command_port))
44            .await?;
45        camera
46            .http_socket
47            .connect(format!("{}:{}", camera_ip, camera_http_port))
48            .await?;
49        Ok(camera)
50    }
51
52    pub async fn send_command_blind<T: control::Command>(
53        &self,
54        command: T,
55    ) -> Result<(), Box<dyn Error>> {
56        println!(
57            "[COMMAND] Sending command with bytes: {:?}",
58            command.to_bytes()
59        );
60        println!(
61            "[COMMAND] Sending command with DATA_LEN: {:?} | CMD_ID: {:?}",
62            command.to_bytes()[3],
63            command.to_bytes()[7]
64        );
65
66        if self
67            .command_socket
68            .send(command.to_bytes().as_slice())
69            .await?
70            == 0
71        {
72            println!("[COMMAND] No bytes sent.");
73            return Err("No bytes sent.".into());
74        }
75
76        println!("[COMMAND] Command sent successfully.");
77
78        Ok(())
79    }
80
81    pub async fn send_command<T: control::Command>(
82        &self,
83        command: T,
84    ) -> Result<[u8; constants::RECV_BUFF_SIZE], Box<dyn Error>> {
85        self.send_command_blind(command).await?;
86        let mut recv_buffer = [0; constants::RECV_BUFF_SIZE];
87
88        println!("[COMMAND] Waiting for response.");
89
90        let recv_len = timeout(
91            constants::RECV_TIMEOUT,
92            self.command_socket.recv(&mut recv_buffer),
93        )
94        .await??;
95        if recv_len == 0 {
96            println!("[COMMAND] No bytes received.");
97            return Err("No bytes received.".into());
98        }
99
100        println!(
101            "[COMMAND] Response of size {} received successfully: {:?}",
102            recv_len, recv_buffer
103        );
104        Ok(recv_buffer)
105    }
106
107    pub async fn get_attitude_information(
108        &self,
109    ) -> Result<control::A8MiniAtittude, Box<dyn Error>> {
110        let attitude_bytes = self
111            .send_command(control::A8MiniSimpleCommand::AttitudeInformation)
112            .await?;
113        let attitude_info: control::A8MiniAtittude = deserialize(&attitude_bytes)?;
114        Ok(attitude_info)
115    }
116
117    pub async fn send_http_query<T: control::HTTPQuery>(
118        &self,
119        query: T,
120    ) -> Result<control::HTTPResponse, Box<dyn Error>> {
121        let response = reqwest::get(query.to_string()).await?;
122        println!("[HTTP] Waiting for response.");
123
124        let json = response.json::<control::HTTPResponse>().await?;
125        Ok(json)
126    }
127
128    pub async fn send_http_image_query<T: control::HTTPQuery>(
129        &self,
130        query: T,
131    ) -> Result<Vec<u8>, Box<dyn Error>> {
132        let response = reqwest::get(query.to_string()).await?;
133        println!("[HTTP] Waiting for response.");
134
135        let image_bytes = response.bytes().await?;
136        Ok(image_bytes.to_vec())
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use tokio::fs::File;
143    use tokio::io::AsyncWriteExt;
144
145    use super::*;
146    use std::thread::sleep;
147    use std::time::Duration;
148
149    #[tokio::test]
150    async fn test_control_lock() -> Result<(), Box<dyn Error>> {
151        let cam: A8Mini = A8Mini::connect().await?;
152
153        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, 0))
154            .await?;
155        sleep(Duration::from_millis(1000));
156
157        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, -900))
158            .await?;
159        sleep(Duration::from_millis(1000));
160
161        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, 250))
162            .await?;
163        sleep(Duration::from_millis(1000));
164
165        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, 0))
166            .await?;
167        sleep(Duration::from_millis(2500));
168
169        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, -900))
170            .await?;
171        sleep(Duration::from_millis(1000));
172
173        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, 250))
174            .await?;
175        sleep(Duration::from_millis(1000));
176
177        cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
178            .await?;
179        Ok(())
180    }
181
182    #[tokio::test]
183    async fn test_take_and_download_photo() -> Result<(), Box<dyn Error>> {
184        let cam: A8Mini = A8Mini::connect().await?;
185
186        cam.send_command_blind(control::A8MiniSimpleCommand::TakePicture)
187            .await?;
188        sleep(Duration::from_millis(500));
189        let num_pictures = cam
190            .send_http_query(control::A8MiniSimpleHTTPQuery::GetMediaCountPhotos)
191            .await?
192            .data
193            .count
194            .unwrap();
195        dbg!(num_pictures);
196        let picture_bytes = cam
197            .send_http_image_query(control::A8MiniComplexHTTPQuery::GetPhoto(num_pictures as u8))
198            .await?;
199        File::create("tmp.jpeg")
200            .await?
201            .write_all(&picture_bytes)
202            .await?;
203
204        Ok(())
205    }
206
207    #[tokio::test]
208    async fn test_send_simple_commands_blind() -> Result<(), Box<dyn Error>> {
209        let cam: A8Mini = A8Mini::connect().await?;
210
211        cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft)
212            .await?;
213        sleep(Duration::from_millis(500));
214
215        cam.send_command_blind(control::A8MiniSimpleCommand::RotateRight)
216            .await?;
217        sleep(Duration::from_millis(1000));
218
219        cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft)
220            .await?;
221        sleep(Duration::from_millis(500));
222
223        cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation)
224            .await?;
225
226        cam.send_command_blind(control::A8MiniSimpleCommand::RotateUp)
227            .await?;
228        sleep(Duration::from_millis(500));
229
230        cam.send_command_blind(control::A8MiniSimpleCommand::RotateDown)
231            .await?;
232        sleep(Duration::from_millis(500));
233
234        cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation)
235            .await?;
236        sleep(Duration::from_millis(1000));
237
238        cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
239            .await?;
240        Ok(())
241    }
242
243    #[tokio::test]
244    async fn test_send_complex_commands_blind() -> Result<(), Box<dyn Error>> {
245        let cam: A8Mini = A8Mini::connect().await?;
246
247        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 50))
248            .await?;
249        sleep(Duration::from_millis(1000));
250
251        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 10))
252            .await?;
253        sleep(Duration::from_millis(1000));
254
255        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(-25, -15))
256            .await?;
257        sleep(Duration::from_millis(6000));
258
259        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(0, 0))
260            .await?;
261        sleep(Duration::from_millis(1000));
262
263        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, 0))
264            .await?;
265        sleep(Duration::from_millis(1000));
266
267        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, -90))
268            .await?;
269        sleep(Duration::from_millis(1000));
270
271        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, -90))
272            .await?;
273        sleep(Duration::from_millis(1000));
274
275        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, 0))
276            .await?;
277        sleep(Duration::from_millis(1000));
278
279        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(0, 0))
280            .await?;
281        sleep(Duration::from_millis(1000));
282
283        cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
284            .await?;
285        Ok(())
286    }
287
288    #[tokio::test]
289    async fn test_send_command_with_ack() -> Result<(), Box<dyn Error>> {
290        let cam: A8Mini = A8Mini::connect().await?;
291        cam.get_attitude_information().await?;
292        Ok(())
293    }
294}