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        println!("[HTTP] Received response.");
126        Ok(json)
127    }
128
129    pub async fn send_http_image_query<T: control::HTTPQuery>(
130        &self,
131        query: T,
132    ) -> Result<Vec<u8>, Box<dyn Error>> {
133        let response = reqwest::get(query.to_string()).await?;
134        println!("[HTTP] Waiting for response.");
135
136        let image_bytes = response.bytes().await?;
137        println!("[HTTP] Received response.");
138        Ok(image_bytes.to_vec())
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use tokio::fs::File;
145    use tokio::io::AsyncWriteExt;
146
147    use super::*;
148    use std::thread::sleep;
149    use std::time::Duration;
150
151    #[ignore]
152    #[tokio::test]
153    async fn test_control_lock() -> Result<(), Box<dyn Error>> {
154        let cam: A8Mini = A8Mini::connect().await?;
155
156        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, 0))
157            .await?;
158        sleep(Duration::from_millis(1000));
159
160        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, -900))
161            .await?;
162        sleep(Duration::from_millis(1000));
163
164        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(900, 250))
165            .await?;
166        sleep(Duration::from_millis(1000));
167
168        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, 0))
169            .await?;
170        sleep(Duration::from_millis(2500));
171
172        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, -900))
173            .await?;
174        sleep(Duration::from_millis(1000));
175
176        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-900, 250))
177            .await?;
178        sleep(Duration::from_millis(1000));
179
180        cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
181            .await?;
182        Ok(())
183    }
184
185    #[ignore]
186    #[tokio::test]
187    async fn test_take_and_download_photo() -> Result<(), Box<dyn Error>> {
188        let cam: A8Mini = A8Mini::connect().await?;
189
190        cam.send_command(control::A8MiniSimpleCommand::TakePicture)
191            .await?;
192        sleep(Duration::from_millis(500));
193        let num_pictures = cam
194            .send_http_query(control::A8MiniSimpleHTTPQuery::GetMediaCountPhotos)
195            .await?
196            .data
197            .count
198            .unwrap();
199        let picture_bytes = cam
200            .send_http_image_query(control::A8MiniComplexHTTPQuery::GetPhoto(num_pictures as u8))
201            .await?;
202        File::create("tmp.jpeg")
203            .await?
204            .write_all(&picture_bytes)
205            .await?;
206
207        Ok(())
208    }
209
210    #[ignore]
211    #[tokio::test]
212    async fn test_send_simple_commands_blind() -> Result<(), Box<dyn Error>> {
213        let cam: A8Mini = A8Mini::connect().await?;
214
215        cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft)
216            .await?;
217        sleep(Duration::from_millis(500));
218
219        cam.send_command_blind(control::A8MiniSimpleCommand::RotateRight)
220            .await?;
221        sleep(Duration::from_millis(1000));
222
223        cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft)
224            .await?;
225        sleep(Duration::from_millis(500));
226
227        cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation)
228            .await?;
229
230        cam.send_command_blind(control::A8MiniSimpleCommand::RotateUp)
231            .await?;
232        sleep(Duration::from_millis(500));
233
234        cam.send_command_blind(control::A8MiniSimpleCommand::RotateDown)
235            .await?;
236        sleep(Duration::from_millis(500));
237
238        cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation)
239            .await?;
240        sleep(Duration::from_millis(1000));
241
242        cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
243            .await?;
244        Ok(())
245    }
246
247    #[ignore]
248    #[tokio::test]
249    async fn test_send_complex_commands_blind() -> Result<(), Box<dyn Error>> {
250        let cam: A8Mini = A8Mini::connect().await?;
251
252        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 50))
253            .await?;
254        sleep(Duration::from_millis(1000));
255
256        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 10))
257            .await?;
258        sleep(Duration::from_millis(1000));
259
260        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(-25, -15))
261            .await?;
262        sleep(Duration::from_millis(6000));
263
264        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(0, 0))
265            .await?;
266        sleep(Duration::from_millis(1000));
267
268        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, 0))
269            .await?;
270        sleep(Duration::from_millis(1000));
271
272        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, -90))
273            .await?;
274        sleep(Duration::from_millis(1000));
275
276        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, -90))
277            .await?;
278        sleep(Duration::from_millis(1000));
279
280        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, 0))
281            .await?;
282        sleep(Duration::from_millis(1000));
283
284        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(0, 0))
285            .await?;
286        sleep(Duration::from_millis(1000));
287
288        cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter)
289            .await?;
290        Ok(())
291    }
292
293    #[ignore]
294    #[tokio::test]
295    async fn test_send_command_with_ack() -> Result<(), Box<dyn Error>> {
296        let cam: A8Mini = A8Mini::connect().await?;
297        cam.get_attitude_information().await?;
298        Ok(())
299    }
300}