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)]
12/// Represents the A8Mini camera API with a dedicate UDP socket for both `Command`s and `HTTPQuery`s.
13pub struct A8Mini {
14    pub command_socket: UdpSocket,
15    pub http_socket: UdpSocket,
16}
17
18impl A8Mini {
19    /// Connect to and creates a new `A8Mini` using default ip address `192.168.144.25` and default port 37260 and port 82. 
20    /// Remote ports are mapped to port 8080 and port 8088 on local.
21    pub async fn connect() -> Result<Self, Box<dyn Error>> {
22        Ok(Self::connect_to(
23            constants::CAMERA_IP,
24            constants::CAMERA_COMMAND_PORT,
25            constants::CAMERA_HTTP_PORT,
26            "8080",
27            "8088",
28        )
29        .await?)
30    }
31
32    // Connects to and creates a new `A8Mini` given network args.
33    pub async fn connect_to(
34        camera_ip: &str,
35        camera_command_port: &str,
36        camera_http_port: &str,
37        local_command_port: &str,
38        local_http_port: &str,
39    ) -> Result<A8Mini, Box<dyn Error>> {
40        let camera: A8Mini = A8Mini {
41            command_socket: UdpSocket::bind(format!("0.0.0.0:{}", local_command_port)).await?,
42            http_socket: UdpSocket::bind(format!("0.0.0.0:{}", local_http_port)).await?,
43        };
44
45        camera
46            .command_socket
47            .connect(format!("{}:{}", camera_ip, camera_command_port))
48            .await?;
49        camera
50            .http_socket
51            .connect(format!("{}:{}", camera_ip, camera_http_port))
52            .await?;
53        Ok(camera)
54    }
55
56    /// Sends a `control::Command` blind. This should be used for all commands that don't have a ACK.
57    pub async fn send_command_blind<T: control::Command>(
58        &self,
59        command: T,
60    ) -> Result<(), Box<dyn Error>> {
61        println!(
62            "[COMMAND] Sending command with bytes: {:?}",
63            command.to_bytes()
64        );
65        println!(
66            "[COMMAND] Sending command with DATA_LEN: {:?} | CMD_ID: {:?}",
67            command.to_bytes()[3],
68            command.to_bytes()[7]
69        );
70
71        let send_len = self.command_socket.send(command.to_bytes().as_slice()).await?;
72
73        if send_len == 0 {
74            println!("[COMMAND] No bytes sent.");
75            return Err("No bytes sent.".into());
76        }
77
78        println!("[COMMAND] Sent {} bytes successfully.", send_len);
79
80        Ok(())
81    }
82
83    /// Sends a `control::Command` expecting an ACK. Returns received ACK response bytes.
84    pub async fn send_command<T: control::Command>(
85        &self,
86        command: T,
87    ) -> Result<[u8; constants::RECV_BUFF_SIZE], Box<dyn Error>> {
88        self.send_command_blind(command).await?;
89        let mut recv_buffer = [0; constants::RECV_BUFF_SIZE];
90
91        println!("[COMMAND] Waiting for response.");
92
93        let recv_len = timeout(
94            constants::RECV_TIMEOUT,
95            self.command_socket.recv(&mut recv_buffer),
96        )
97        .await??;
98        if recv_len == 0 {
99            println!("[COMMAND] No bytes received.");
100            return Err("No bytes received.".into());
101        }
102
103        println!(
104            "[COMMAND] Response of size {} received successfully: {:?}",
105            recv_len, recv_buffer
106        );
107        Ok(recv_buffer)
108    }
109
110    /// Retrieves attitude information from the camera. 
111    /// Can be used as a system connectivity check.
112    pub async fn get_attitude_information(
113        &self,
114    ) -> Result<control::A8MiniAtittude, Box<dyn Error>> {
115        let attitude_bytes = self
116            .send_command(control::A8MiniSimpleCommand::AttitudeInformation)
117            .await?;
118        let attitude_info: control::A8MiniAtittude = deserialize(&attitude_bytes)?;
119        Ok(attitude_info)
120    }
121
122    /// Sends a `control::HTTPQuery` and returns the corresponding received `control::HTTPResponse`.
123    pub async fn send_http_query<T: control::HTTPQuery>(
124        &self,
125        query: T,
126    ) -> Result<control::HTTPResponse, Box<dyn Error>> {
127        let response = reqwest::get(query.to_string()).await?;
128        println!("[HTTP] Waiting for response.");
129
130        let json = response.json::<control::HTTPResponse>().await?;
131        println!("[HTTP] Received response.");
132        Ok(json)
133    }
134
135    /// Retrieves an image or video (WIP) from the camera.
136    pub async fn send_http_media_query<T: control::HTTPQuery>(
137        &self,
138        query: T,
139    ) -> Result<Vec<u8>, Box<dyn Error>> {
140        let response = reqwest::get(query.to_string()).await?;
141        println!("[HTTP] Waiting for response.");
142
143        let image_bytes = response.bytes().await?;
144        println!("[HTTP] Received response.");
145        Ok(image_bytes.to_vec())
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use crate::control::*;
153
154    use std::thread::sleep;
155    use std::time::Duration;
156    use tokio::fs::File;
157    use tokio::io::AsyncWriteExt;
158
159
160    #[ignore]
161    #[tokio::test]
162    async fn test_take_and_download_photo() -> Result<(), Box<dyn Error>> {
163        let cam: A8Mini = A8Mini::connect().await?;
164
165        cam.send_command(control::A8MiniSimpleCommand::TakePicture)
166            .await?;
167        sleep(Duration::from_millis(500));
168        let num_pictures = cam
169            .send_http_query(control::A8MiniSimpleHTTPQuery::GetMediaCountPhotos)
170            .await?
171            .data
172            .count
173            .unwrap();
174        let picture_bytes = cam
175            .send_http_media_query(control::A8MiniComplexHTTPQuery::GetPhoto(num_pictures as u8))
176            .await?;
177        File::create("tmp.jpeg")
178            .await?
179            .write_all(&picture_bytes)
180            .await?;
181
182        Ok(())
183    }
184
185    #[ignore]
186    #[tokio::test]
187    async fn test_send_simple_commands_blind() -> Result<(), Box<dyn Error>> {
188        let cam: A8Mini = A8Mini::connect().await?;
189
190        cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft).await?;
191        sleep(Duration::from_millis(500));
192
193        cam.send_command_blind(control::A8MiniSimpleCommand::RotateRight).await?;
194        sleep(Duration::from_millis(1000));
195
196        cam.send_command_blind(control::A8MiniSimpleCommand::RotateLeft).await?;
197        sleep(Duration::from_millis(500));
198
199        cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation).await?;
200
201        cam.send_command_blind(control::A8MiniSimpleCommand::RotateUp).await?;
202        sleep(Duration::from_millis(500));
203
204        cam.send_command_blind(control::A8MiniSimpleCommand::RotateDown).await?;
205        sleep(Duration::from_millis(500));
206
207        cam.send_command_blind(control::A8MiniSimpleCommand::StopRotation).await?;
208        sleep(Duration::from_millis(1000));
209
210        cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter).await?;
211        Ok(())
212    }
213
214    #[ignore]
215    #[tokio::test]
216    async fn test_send_complex_commands_blind() -> Result<(), Box<dyn Error>> {
217        let cam: A8Mini = A8Mini::connect().await?;
218
219        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 50)).await?;
220        sleep(Duration::from_millis(1000));
221
222        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(50, 10)).await?;
223        sleep(Duration::from_millis(1000));
224
225        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(-25, -15)).await?;
226        sleep(Duration::from_millis(6000));
227
228        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchSpeed(0, 0)).await?;
229        sleep(Duration::from_millis(1000));
230
231        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, 0)).await?;
232        sleep(Duration::from_millis(1000));
233
234        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(90, -90)).await?;
235        sleep(Duration::from_millis(1000));
236
237        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, -90)).await?;
238        sleep(Duration::from_millis(1000));
239
240        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(-90, 0)).await?;
241        sleep(Duration::from_millis(1000));
242
243        cam.send_command_blind(control::A8MiniComplexCommand::SetYawPitchAngle(0, 0)).await?;
244        sleep(Duration::from_millis(1000));
245
246        cam.send_command_blind(control::A8MiniSimpleCommand::AutoCenter).await?;
247        Ok(())
248    }
249
250    #[ignore]
251    #[tokio::test]
252    async fn test_send_command_with_ack() -> Result<(), Box<dyn Error>> {
253        let cam: A8Mini = A8Mini::connect().await?;
254        println!("{:?}", cam.get_attitude_information().await?);
255        Ok(())
256    }
257
258    #[ignore]
259    #[tokio::test]
260    async fn aarya_tests() -> Result<(), Box<dyn Error>> {
261        let cam: A8Mini = A8Mini::connect().await?;
262        // cam.send_command_blind(A8MiniComplexCommand::SetYawPitchAngle(0, 900)).await?;
263        // cam.send_command_blind(A8MiniSimpleCommand::RecordVideo).await?;
264        // println!("{:?}", cam.send_http_query(A8MiniSimpleHTTPQuery::GetMediaCountVideos).await?);
265
266        // cam.send_command_blind(A8MiniComplexCommand::SetCodecSpecs(0, 2, 1920, 1080, 4000, 0)).await?;
267
268        // cam.send_command_blind(A8MiniSimpleCommand::Resolution4k).await?;
269        cam.send_command_blind(A8MiniSimpleCommand::RecordVideo).await?;
270        // sleep(Duration::from_millis(10000));
271        // cam.send_command_blind(A8MiniSimpleCommand::RecordVideo).await?;
272        
273
274        Ok(())
275    }
276}